quic 0.3.5 → 0.3.6
raw patch · 21 files changed
+402/−11378 lines, 21 filesdep ~cryptonPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: crypton
API changes (from Hackage documentation)
- Network.QUIC.Internal: [supplement] :: Coder -> Maybe Supplement
- Network.QUIC.Internal: data FusionContext
- Network.QUIC.Internal: data Supplement
- Network.QUIC.Internal: fusionDecrypt :: FusionContext -> Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int
- Network.QUIC.Internal: fusionEncrypt :: FusionContext -> Supplement -> Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int
- Network.QUIC.Internal: fusionGetMask :: Supplement -> IO Buffer
- Network.QUIC.Internal: fusionNewContext :: IO FusionContext
- Network.QUIC.Internal: fusionSetSample :: Supplement -> Buffer -> IO ()
- Network.QUIC.Internal: fusionSetup :: Cipher -> FusionContext -> Key -> IV -> IO ()
- Network.QUIC.Internal: fusionSetupSupplement :: Cipher -> Key -> IO Supplement
- Network.QUIC.Internal: isFusionAvailable :: IO Bool
+ Network.QUIC.Internal: makeGcmDecrypt :: Cipher -> Key -> IV -> Maybe NiteDecrypt
+ Network.QUIC.Internal: makeGcmEncrypt :: Cipher -> Key -> IV -> Key -> IO (Maybe (NiteEncrypt, Buffer -> IO (), IO Buffer))
+ Network.QUIC.Internal: takeRetransSTM :: Connection -> EncryptionLevel -> STM (Maybe PlainPacket)
- Network.QUIC.Internal: Coder :: (Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int) -> (Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int) -> Maybe Supplement -> Coder
+ Network.QUIC.Internal: Coder :: (Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int) -> (Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int) -> Coder
Files
- ChangeLog.md +64/−0
- Network/QUIC/Config.hs +0/−6
- Network/QUIC/Connection/Crypto.hs +17/−91
- Network/QUIC/Connection/Queue.hs +27/−0
- Network/QUIC/Connection/Types.hs +0/−2
- Network/QUIC/Crypto.hs +0/−2
- Network/QUIC/Crypto/Fusion.hs +0/−203
- Network/QUIC/Crypto/Nite.hs +121/−4
- Network/QUIC/Recovery/LossRecovery.hs +10/−6
- Network/QUIC/Recovery/Release.hs +1/−1
- Network/QUIC/Recovery/Utils.hs +19/−0
- Network/QUIC/Sender.hs +61/−10
- Network/QUIC/Server/Reader.hs +17/−1
- cbits/fusion.c +0/−2290
- cbits/picotls.c +0/−6647
- cbits/picotls.h +0/−1963
- cbits/picotls/fusion.h +0/−115
- quic.cabal +2/−23
- test/Config.hs +59/−9
- test/IOSpec.hs +4/−1
- test/TLSSpec.hs +0/−4
ChangeLog.md view
@@ -1,5 +1,69 @@ # ChangeLog +## 0.3.6++A security fix and three for a stalled handshake.++* Stop treating a token the server cannot decrypt as a validated address.+ The dispatcher's wildcard caught the decryption failing and passed+ `addrValid = True` on, which turns off the three-times+ anti-amplification limit -- so a peer was better off sending rubbish in+ the Token field than sending nothing, from any source address, with no+ keys and no handshake. RFC 9000 Sec 8.1.3 says to proceed as if the+ address were not validated. A token we did issue in NEW_TOKEN now has+ its lifetime honoured as well; only the Retry path was checking expiry.+ [#114](https://github.com/kazu-yamamoto/quic/pull/114)+* Let the PTO probe reach the retransmission that is waiting for the+ window. A client that sends 1-RTT before the handshake is confirmed+ can deadlock its own handshake: RFC 9001 Sec 5.7 stops the peer+ processing those packets, so they are never acknowledged and never+ leave the congestion window, and the window cannot open until the+ CRYPTO frame the peer is waiting for arrives. The probe may be sent+ past a full window and was being spent on a bare PING, because a packet+ already declared lost has left the sent-packet database and+ `releaseOldest` cannot see it. An ACK-only packet no longer waits for+ the window either (RFC 9002 Sec 7), which was blocking the one sender+ thread and everything queued behind it.+ [#115](https://github.com/kazu-yamamoto/quic/pull/115)+* Spend the PTO probe on what is being held back rather than on a PING,+ when the sender is already holding an ack-eliciting packet at the level+ the timer fired for.+ [#113](https://github.com/kazu-yamamoto/quic/pull/113)+* Count only what is in flight into bytes in flight. RFC 9002 Sec 2:+ a packet is in flight when it is ack-eliciting or contains PADDING.+ Every packet sent was counted, so an ACK-only packet spent congestion+ window it had no business spending -- 177 of 207 such sends in a+ measured run. The predicate was already in `Types.Frame`, unused.+ [#116](https://github.com/kazu-yamamoto/quic/pull/116)++* AES-GCM goes through crypton's one-call interface, and the bundled picotls+ `fusion` engine is gone with the 11,017 lines of C it came in. What that+ engine was for is that crypton rebuilt the AES key schedule and the table+ of multiples of H for every packet, and took the header protection mask in+ a second call after the encryption; crypton 2.1 builds the first once per+ key and hands back the mask from the same call as the ciphertext. Measured+ on an Apple M4, a 100-byte packet goes from 2.25 to 0.16 microseconds and a+ 1440-byte one from 2.50 to 0.45. Against `fusion` itself, measured in C on+ an Intel Haswell where it runs at all, crypton is at 91 to 96 per cent of+ it for the call this makes -- and in Haskell `fusion` needs three foreign+ calls to crypton's one.++* ChaCha20-Poly1305 is in `defaultCiphers` on x86-64 again. It had been left+ out there because `fusion` did not implement it, so a build with the engine+ offered two suites where every other build offered three. The RFC 9001 and+ RFC 9369 test vectors for it, skipped under the same condition, now run+ everywhere.+ [#111](https://github.com/kazu-yamamoto/quic/pull/111)++* The `fusion` cabal flag is gone with the engine. A build passing+ `-f fusion` will now fail on an unknown flag rather than quietly+ selecting something that no longer exists.++* The IOSpec relay no longer latches onto a leftover datagram at the port+ handover, and qlog is kept for a failed CI job. Tests and CI only, but+ the qlog is what made the stalls above findable at all.+ [#112](https://github.com/kazu-yamamoto/quic/pull/112)+ ## 0.3.5 Security fixes. The first four can be reached by a peer that has not
Network/QUIC/Config.hs view
@@ -58,16 +58,10 @@ defaultCiphers :: [Cipher] defaultCiphers =-#ifdef USE_FUSION- [ cipher13_AES_256_GCM_SHA384- , cipher13_AES_128_GCM_SHA256- ]-#else [ cipher13_CHACHA20_POLY1305_SHA256 , cipher13_AES_256_GCM_SHA384 , cipher13_AES_128_GCM_SHA256 ]-#endif ----------------------------------------------------------------
Network/QUIC/Connection/Crypto.hs view
@@ -35,13 +35,6 @@ import Network.QUIC.Imports import Network.QUIC.Types -useFusion :: Bool-#ifdef USE_FUSION-useFusion = True-#else-useFusion = False-#endif- ---------------------------------------------------------------- setEncryptionLevel :: Connection -> EncryptionLevel -> IO ()@@ -106,9 +99,6 @@ ---------------------------------------------------------------- -fusionCiphers :: [Cipher]-fusionCiphers = [cipher13_AES_128_GCM_SHA256, cipher13_AES_256_GCM_SHA384]- initializeCoder :: Connection -> EncryptionLevel -> TrafficSecrets a -> IO () initializeCoder conn lvl sec = do ver <-@@ -116,11 +106,7 @@ then return $ getOriginalVersion conn else getVersion conn cipher <- getCipher conn lvl- avail <- isFusionAvailable- (coder, protector) <-- if useFusion && avail && cipher `elem` fusionCiphers- then genFusionCoder (isClient conn) ver cipher sec- else genNiteCoder (isClient conn) ver cipher sec+ (coder, protector) <- genNiteCoder (isClient conn) ver cipher sec writeArray (coders conn) lvl coder writeArray (protectors conn) lvl protector @@ -128,11 +114,7 @@ initializeCoder1RTT conn sec = do ver <- getVersion conn cipher <- getCipher conn RTT1Level- avail <- isFusionAvailable- (coder, protector) <-- if useFusion && avail && cipher `elem` fusionCiphers- then genFusionCoder (isClient conn) ver cipher sec- else genNiteCoder (isClient conn) ver cipher sec+ (coder, protector) <- genNiteCoder (isClient conn) ver cipher sec let coder1 = Coder1RTT coder sec writeArray (coders1RTT conn) False coder1 writeArray (protectors conn) RTT1Level protector@@ -144,11 +126,7 @@ cipher <- getCipher conn RTT1Level Coder1RTT coder secN <- readArray (coders1RTT conn) (not nextPhase) let secN1 = updateSecret ver cipher secN- avail <- isFusionAvailable- coderN1 <-- if useFusion && avail && cipher `elem` fusionCiphers- then genFusionCoder1RTT (isClient conn) ver cipher secN1 coder- else genNiteCoder1RTT (isClient conn) ver cipher secN1 coder+ coderN1 <- genNiteCoder1RTT (isClient conn) ver cipher secN1 coder let nextCoder = Coder1RTT coderN1 secN1 writeArray (coders1RTT conn) nextPhase nextCoder @@ -163,53 +141,28 @@ Secret sN1 = nextSecret ver cipher $ Secret sN secN1 = (ClientTrafficSecret cN1, ServerTrafficSecret sN1) -genFusionCoder- :: Bool -> Version -> Cipher -> TrafficSecrets a -> IO (Coder, Protector)-genFusionCoder cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) = do- fctxt <- fusionNewContext- fctxr <- fusionNewContext- fusionSetup cipher fctxt txPayloadKey txPayloadIV- fusionSetup cipher fctxr rxPayloadKey rxPayloadIV- supp <- fusionSetupSupplement cipher txHeaderKey- let coder =- Coder- { encrypt = fusionEncrypt fctxt supp- , decrypt = fusionDecrypt fctxr- , supplement = Just supp- }- let protector =- Protector- { setSample = fusionSetSample supp- , getMask = fusionGetMask supp- , unprotect = unp- }- return (coder, protector)- where- txSecret- | cli = Secret c- | otherwise = Secret s- rxSecret- | cli = Secret s- | otherwise = Secret c- txPayloadKey = aeadKey ver cipher txSecret- txPayloadIV = initialVector ver cipher txSecret- txHeaderKey = headerProtectionKey ver cipher txSecret- rxPayloadKey = aeadKey ver cipher rxSecret- rxPayloadIV = initialVector ver cipher rxSecret- rxHeaderKey = headerProtectionKey ver cipher rxSecret- unp = protectionMask cipher rxHeaderKey genNiteCoder :: Bool -> Version -> Cipher -> TrafficSecrets a -> IO (Coder, Protector) genNiteCoder cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) = do- let enc = makeNiteEncrypt cipher txPayloadKey txPayloadIV- dec = makeNiteDecrypt cipher rxPayloadKey rxPayloadIV- (set, get) <- makeNiteProtector cipher txHeaderKey+ -- AES-GCM goes through crypton's one-call interface where it can: the+ -- key schedule and the table of multiples of H are built once for the+ -- key rather than once for every packet, and the header protection mask+ -- comes back from the same call as the ciphertext. ChaCha20-Poly1305+ -- has no equivalent there and takes the path below.+ mgcm <- makeGcmEncrypt cipher txPayloadKey txPayloadIV txHeaderKey+ (enc, set, get) <- case mgcm of+ Just gcm -> return gcm+ Nothing -> do+ (s', g') <- makeNiteProtector cipher txHeaderKey+ return (makeNiteEncrypt cipher txPayloadKey txPayloadIV, s', g')+ let dec = case makeGcmDecrypt cipher rxPayloadKey rxPayloadIV of+ Just d -> d+ Nothing -> makeNiteDecrypt cipher rxPayloadKey rxPayloadIV let coder = Coder { encrypt = enc , decrypt = dec- , supplement = Nothing } let protector = Protector@@ -233,32 +186,6 @@ rxHeaderKey = headerProtectionKey ver cipher rxSecret unp = protectionMask cipher rxHeaderKey -genFusionCoder1RTT- :: Bool -> Version -> Cipher -> TrafficSecrets a -> Coder -> IO Coder-genFusionCoder1RTT cli ver cipher (ClientTrafficSecret c, ServerTrafficSecret s) oldcoder = do- fctxt <- fusionNewContext- fctxr <- fusionNewContext- fusionSetup cipher fctxt txPayloadKey txPayloadIV- fusionSetup cipher fctxr rxPayloadKey rxPayloadIV- let supp = fromJust $ supplement oldcoder- let coder =- Coder- { encrypt = fusionEncrypt fctxt supp- , decrypt = fusionDecrypt fctxr- , supplement = Just supp- }- return coder- where- txSecret- | cli = Secret c- | otherwise = Secret s- rxSecret- | cli = Secret s- | otherwise = Secret c- txPayloadKey = aeadKey ver cipher txSecret- txPayloadIV = initialVector ver cipher txSecret- rxPayloadKey = aeadKey ver cipher rxSecret- rxPayloadIV = initialVector ver cipher rxSecret genNiteCoder1RTT :: Bool -> Version -> Cipher -> TrafficSecrets a -> Coder -> IO Coder@@ -269,7 +196,6 @@ Coder { encrypt = enc , decrypt = dec- , supplement = Nothing } return coder where
Network/QUIC/Connection/Queue.hs view
@@ -5,6 +5,7 @@ import Network.QUIC.Connection.Types import Network.QUIC.Stream+import Network.QUIC.Types ---------------------------------------------------------------- @@ -41,6 +42,32 @@ isEmptyOutputSTM :: Connection -> STM Bool isEmptyOutputSTM conn = isEmptyTQueue $ outputQ conn++-- | Take the oldest queued retransmission at this level, leaving the rest of+-- the queue in order.+--+-- A PTO probe may be sent past a full congestion window, and this is how it+-- reaches the packet that needs sending. Once a packet has been declared+-- lost it is no longer in the sent-packet database, so 'releaseOldest' cannot+-- find it; it is here, waiting for a window that may not open until it has+-- gone out.+takeRetransSTM :: Connection -> EncryptionLevel -> STM (Maybe PlainPacket)+takeRetransSTM conn lvl = do+ outs <- flushTQueue (outputQ conn)+ let (found, rest) = pick outs+ mapM_ (writeTQueue (outputQ conn)) rest+ return found+ where+ pick [] = (Nothing, [])+ pick (o@(OutRetrans ppkt@(PlainPacket hdr _)) : os)+ | levelOf hdr == lvl = (Just ppkt, os)+ | otherwise = let (f, r) = pick os in (f, o : r)+ pick (o : os) = let (f, r) = pick os in (f, o : r)+ levelOf hdr+ | l == RTT0Level = RTT1Level+ | otherwise = l+ where+ l = packetEncryptionLevel hdr ----------------------------------------------------------------
Network/QUIC/Connection/Types.hs view
@@ -129,7 +129,6 @@ data Coder = Coder { encrypt :: Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int , decrypt :: Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int- , supplement :: Maybe Supplement } initialCoder :: Coder@@ -137,7 +136,6 @@ Coder { encrypt = \_ _ _ _ -> return (-1) , decrypt = \_ _ _ _ -> return (-1)- , supplement = Nothing } data Coder1RTT = Coder1RTT
Network/QUIC/Crypto.hs view
@@ -1,5 +1,4 @@ module Network.QUIC.Crypto (- module Network.QUIC.Crypto.Fusion, module Network.QUIC.Crypto.Nite, module Network.QUIC.Crypto.Types, module Network.QUIC.Crypto.Keys,@@ -9,7 +8,6 @@ import Network.TLS hiding (Version) import Network.TLS.QUIC -import Network.QUIC.Crypto.Fusion import Network.QUIC.Crypto.Keys import Network.QUIC.Crypto.Nite import Network.QUIC.Crypto.Types
− Network/QUIC/Crypto/Fusion.hs
@@ -1,203 +0,0 @@-{-# LANGUAGE CPP #-}--module Network.QUIC.Crypto.Fusion (- FusionContext,- fusionNewContext,- fusionSetup,- fusionEncrypt,- fusionDecrypt,- Supplement,- fusionSetupSupplement,- fusionSetSample,- fusionGetMask,- isFusionAvailable,-) where--#ifdef USE_FUSION-import qualified Data.ByteString as BS-import Data.ByteArray (convert)-import Foreign.C.Types-import Foreign.ForeignPtr-import Foreign.Ptr-import Network.TLS.Extra.Cipher--import Network.QUIC.Crypto.Types-import Network.QUIC.Imports-import Network.QUIC.Types--------------------------------------------------------------------data FusionContextOpaque-newtype FusionContext = FC (ForeignPtr FusionContextOpaque)--fusionNewContext :: IO FusionContext-fusionNewContext = FC <$> (c_aead_context_new >>= newForeignPtr p_aead_context_free)--------------------------------------------------------------------fusionSetup :: Cipher -> FusionContext -> Key -> IV -> IO ()-fusionSetup cipher- | cipher == cipher13_AES_128_GCM_SHA256 = fusionSetupAES128- | cipher == cipher13_AES_256_GCM_SHA384 = fusionSetupAES256- | otherwise = error "fusionSetup"--fusionSetupAES128 :: FusionContext -> Key -> IV -> IO ()-fusionSetupAES128 (FC fctx) (Key key) (IV iv) = withForeignPtr fctx $ \pctx ->- withByteString (convert key) $ \keyp ->- withByteString iv $ \ivp -> void $ c_aes128gcm_setup pctx 0 keyp ivp--fusionSetupAES256 :: FusionContext -> Key -> IV -> IO ()-fusionSetupAES256 (FC fctx) (Key key) (IV iv) = withForeignPtr fctx $ \pctx ->- withByteString (convert key) $ \keyp ->- withByteString iv $ \ivp -> void $ c_aes256gcm_setup pctx 0 keyp ivp--------------------------------------------------------------------fusionEncrypt :: FusionContext -> Supplement- -> Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int-fusionEncrypt (FC fctx) (SP fsupp) obuf plaintext (AssDat header) pn =- withForeignPtr fctx $ \pctx -> withForeignPtr fsupp $ \psupp -> do- withByteString plaintext $ \ibuf ->- withByteString header $ \abuf -> do- c_aead_do_encrypt pctx obuf ibuf ilen' pn' abuf alen psupp- return (ilen + 16) -- fixme- where- pn' = fromIntegral pn- ilen = BS.length plaintext- ilen' = fromIntegral ilen- alen = fromIntegral $ BS.length header--fusionDecrypt :: FusionContext- -> Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int-fusionDecrypt (FC fctx) obuf ciphertext (AssDat header) pn =- withForeignPtr fctx $ \pctx ->- withByteString ciphertext $ \ibuf ->- withByteString header $ \abuf ->- fromIntegral <$> c_aead_do_decrypt pctx obuf ibuf ilen pn' abuf alen- where- pn' = fromIntegral pn- ilen = fromIntegral $ BS.length ciphertext- alen = fromIntegral $ BS.length header--------------------------------------------------------------------data SupplementOpaque-newtype Supplement = SP (ForeignPtr SupplementOpaque)--fusionSetupSupplement :: Cipher -> Key -> IO Supplement-fusionSetupSupplement cipher (Key hpkey) = withByteString (convert hpkey) $ \hpkeyp ->- SP <$> (c_supplement_new hpkeyp keylen >>= newForeignPtr p_supplement_free)- where- keylen- | cipher == cipher13_AES_128_GCM_SHA256 = 16- | otherwise = 32--fusionSetSample :: Supplement -> Buffer -> IO ()-fusionSetSample (SP fsupp) p = withForeignPtr fsupp $ \psupp ->- c_supplement_set_sample psupp p--fusionGetMask :: Supplement -> IO Buffer-fusionGetMask (SP fsupp) = withForeignPtr fsupp c_supplement_get_mask--isFusionAvailable :: IO Bool-isFusionAvailable = do- n <- c_ptls_fusion_is_supported_by_cpu- return $ not (n == 0)--------------------------------------------------------------------foreign import ccall unsafe "aead_context_new"- c_aead_context_new :: IO (Ptr FusionContextOpaque)--foreign import ccall unsafe "&aead_context_free"- p_aead_context_free :: FunPtr (Ptr FusionContextOpaque -> IO ())--foreign import ccall unsafe "aes128gcm_setup"- c_aes128gcm_setup :: Ptr FusionContextOpaque- -> CInt -- dummy- -> Ptr Word8 -- key- -> Ptr Word8 -- iv- -> IO CInt--foreign import ccall unsafe "aes256gcm_setup"- c_aes256gcm_setup :: Ptr FusionContextOpaque- -> CInt -- dummy- -> Ptr Word8 -- key- -> Ptr Word8 -- iv- -> IO CInt-{--foreign import ccall unsafe "aesgcm_dispose_crypto"- c_aesgcm_dispose_crypto :: FusionContext -> IO ()--}--foreign import ccall unsafe "aead_do_encrypt"- c_aead_do_encrypt :: Ptr FusionContextOpaque- -> Ptr Word8 -- output- -> Ptr Word8 -- input- -> CSize -- input length- -> CULong -- sequence- -> Ptr Word8 -- AAD- -> CSize -- AAD length- -> Ptr SupplementOpaque- -> IO ()--foreign import ccall unsafe "aead_do_decrypt"- c_aead_do_decrypt :: Ptr FusionContextOpaque- -> Ptr Word8 -- output- -> Ptr Word8 -- input- -> CSize -- input length- -> CULong -- sequence- -> Ptr Word8 -- AAD- -> CSize -- AAD length- -> IO CSize--foreign import ccall unsafe "supplement_new"- c_supplement_new :: Ptr Word8 -> CInt -> IO (Ptr SupplementOpaque)--foreign import ccall unsafe "&supplement_free"- p_supplement_free :: FunPtr (Ptr SupplementOpaque -> IO ())--foreign import ccall unsafe "supplement_set_sample"- c_supplement_set_sample :: Ptr SupplementOpaque -> Ptr Word8 -> IO ()--foreign import ccall unsafe "supplement_get_mask"- c_supplement_get_mask :: Ptr SupplementOpaque -> IO (Ptr Word8)--foreign import ccall unsafe "ptls_fusion_is_supported_by_cpu"- c_ptls_fusion_is_supported_by_cpu :: IO Int--#else-import Network.QUIC.Crypto.Types-import Network.QUIC.Imports-import Network.QUIC.Types--data FusionContext--fusionNewContext :: IO FusionContext-fusionNewContext = undefined--fusionSetup :: Cipher -> FusionContext -> Key -> IV -> IO ()-fusionSetup = undefined--fusionEncrypt :: FusionContext -> Supplement- -> Buffer -> PlainText -> AssDat -> PacketNumber -> IO Int-fusionEncrypt = undefined--fusionDecrypt :: FusionContext- -> Buffer -> CipherText -> AssDat -> PacketNumber -> IO Int-fusionDecrypt = undefined--data Supplement--fusionSetupSupplement :: Cipher -> Key -> IO Supplement-fusionSetupSupplement = undefined--fusionSetSample :: Supplement -> Buffer -> IO ()-fusionSetSample = undefined--fusionGetMask :: Supplement -> IO Buffer-fusionGetMask = undefined--isFusionAvailable :: IO Bool-isFusionAvailable = return False-#endif
Network/QUIC/Crypto/Nite.hs view
@@ -14,21 +14,26 @@ makeNiteEncrypt, makeNiteDecrypt, makeNiteProtector,+ makeGcmEncrypt,+ makeGcmDecrypt, ) where import Crypto.Cipher.AES+import qualified Crypto.Cipher.AES.GCM as GCM import qualified Crypto.Cipher.ChaCha as ChaCha import Crypto.Cipher.ChaChaPoly1305 (aeadChacha20poly1305Init) import Crypto.Cipher.Types hiding (Cipher, IV) import Crypto.Error (maybeCryptoError)-import qualified Data.ByteArray as Byte (convert)+import Data.IORef (IORef, newIORef, readIORef, writeIORef)+import qualified Data.ByteArray as Byte (ByteArrayAccess (..), convert) import qualified Data.ByteString as BS import qualified Data.ByteString.Internal as BS-import Foreign.ForeignPtr (newForeignPtr_, withForeignPtr)+import Foreign.ForeignPtr (ForeignPtr, mallocForeignPtrBytes, newForeignPtr_, withForeignPtr) import Foreign.Marshal.Alloc (mallocBytes) import Foreign.Marshal.Utils (copyBytes)-import Foreign.Ptr (Ptr, nullPtr, plusPtr)-import Foreign.Storable (peek, poke)+import Foreign.Ptr (Ptr, minusPtr, nullPtr, plusPtr)+import Foreign.Storable (peek, poke, pokeByteOff)+import Foreign.Ptr (castPtr) import Network.TLS hiding (Version) import qualified Network.TLS as TLS import Network.TLS.Extra.Cipher@@ -295,3 +300,115 @@ let Mask mask = mkMask $ Sample sample _len <- copyBS dstbuf mask return dstbuf++----------------------------------------------------------------++{-+ - AES-GCM through crypton's one-call interface.+ -+ - What the interface above this costs is not the encryption. A t'Context'+ - is built from the key once, where 'aeadInit' rebuilt the key schedule and+ - the table of multiples of H for every nonce; and the header protection+ - mask comes back from the same call as the ciphertext, riding in a lane of+ - the AES pipeline that the packet length leaves idle, where it used to be a+ - block of its own after the fact.+ -+ - The sample offset the mask is taken from is not fixed: QUIC samples four+ - bytes past the start of the packet number, and the ciphertext starts after+ - it, so the offset is four less the length of the encoded packet number.+ - 'setSample' is handed the address, and the offset is what it is from the+ - output buffer the encryption is given.+ -}++{-+ - The nonce, written into a buffer the connection keeps rather than built+ - fresh. It is the IV with the packet number exclusive-ored into its low+ - eight bytes, and the obvious way -- bytestring64 and bsXORpad -- allocates+ - two ByteStrings for every packet. At these lengths that is a third of+ - what the encryption costs.+ -}+newtype NoncePtr = NoncePtr (ForeignPtr Word8)++instance Byte.ByteArrayAccess NoncePtr where+ length _ = 12+ withByteArray (NoncePtr fp) f = withForeignPtr fp (f . castPtr)++writeNonce :: Ptr Word8 -> Ptr Word8 -> Word64 -> IO ()+writeNonce dst ivp pn = do+ copyBytes dst ivp 4+ go 0+ where+ go :: Int -> IO ()+ go 8 = return ()+ go j = do+ b <- peek (ivp `plusPtr` (4 + j)) :: IO Word8+ pokeByteOff dst (4 + j) (b `xor` fromIntegral (pn `shiftR` (56 - 8 * j)))+ go (j + 1)++-- | Whether this is one of the two AES-GCM suites, which is what the+-- interface below covers. ChaCha20-Poly1305 has no equivalent there and+-- stays with the code above.+gcmKeySize :: Cipher -> Maybe Int+gcmKeySize cipher+ | cipher == cipher13_AES_128_GCM_SHA256 = Just 16+ | cipher == cipher13_AES_256_GCM_SHA384 = Just 32+ | otherwise = Nothing++-- | The encryption side, with the mask. The two extra actions are the+-- 'Protector' halves: they and the encryption share the buffer the mask is+-- written to and the address the sample is taken from.+makeGcmEncrypt+ :: Cipher+ -> Key+ -> IV+ -> Key+ -> IO (Maybe (NiteEncrypt, Buffer -> IO (), IO Buffer))+makeGcmEncrypt cipher (Key key) iv (Key hpkey) = case gcmKeySize cipher of+ Nothing -> return Nothing+ Just _ -> case (mctx, mhk) of+ (Just ctx, Just hk) -> do+ ref <- newIORef nullPtr+ maskBuf <- mallocBytes 16 -- fixme: free+ ivfp <- mallocForeignPtrBytes 12+ noncefp <- mallocForeignPtrBytes 12+ let IV ivbs = iv+ withForeignPtr ivfp $ \p -> void $ copyBS p ivbs+ let enc dst plaintext (AssDat ad) pn = do+ sample <- readIORef ref+ let off = sample `minusPtr` dst+ withForeignPtr noncefp $ \np ->+ withForeignPtr ivfp $ \ivp ->+ writeNonce np ivp (fromIntegral pn)+ ok <-+ GCM.encryptWithMask+ ctx+ hk+ (NoncePtr noncefp)+ ad+ plaintext+ 16+ off+ dst+ maskBuf+ return $ if ok then BS.length plaintext + 16 else -1+ return $ Just (enc, writeIORef ref, return maskBuf)+ _ -> return Nothing+ where+ mctx = maybeCryptoError $ GCM.newContext key+ mhk = maybeCryptoError $ GCM.newHeaderKey hpkey++-- | The decryption side. There is no mask here: the receiver takes its+-- sample from the packet it was given, before anything is decrypted.+makeGcmDecrypt :: Cipher -> Key -> IV -> Maybe NiteDecrypt+makeGcmDecrypt cipher (Key key) iv = case gcmKeySize cipher of+ Nothing -> Nothing+ Just _ -> case maybeCryptoError $ GCM.newContext key of+ Nothing -> Nothing+ Just ctx ->+ let mk = makeNonce iv+ dec dst ciphertext (AssDat ad) pn =+ let Nonce nonce = mk $ bytestring64 $ fromIntegral pn+ in case GCM.decrypt ctx nonce ad ciphertext 16 of+ Nothing -> return (-1)+ Just plain -> copyBS dst plain+ in Just dec
Network/QUIC/Recovery/LossRecovery.hs view
@@ -51,11 +51,9 @@ atomically $ modifyTVar' recoveryCC $ \cc -> cc- { bytesInFlight = bytesInFlight cc + sentBytes+ { bytesInFlight = bytesInFlight cc + inFlightBytes sentPacket , numOfAckEliciting = numOfAckEliciting cc + countAckEli sentPacket }- where- sentBytes = spSentBytes sentPacket ---------------------------------------------------------------- @@ -174,8 +172,14 @@ (bytes, cwin, acked, _, cnt) .+ sp@SentPacket{..} = (bytes', cwin', acked', mode', cnt') where isRecovery = inCongestionRecovery spTimeSent congestionRecoveryStartTime- bytes' = bytes - spSentBytes- ackedA = acked + spSentBytes+ -- What this packet cost the window, which is nothing unless it+ -- was in flight. It has to be the same number that was added+ -- when the packet was sent, or the count drifts -- and it is the+ -- right number for the window to grow by, since a packet that+ -- spent no window is no evidence that more is available.+ sz = inFlightBytes sp+ bytes' = bytes - sz+ ackedA = acked + sz cnt' = cnt - countAckEli sp (cwin', acked', mode') -- Do not increase congestion window in recovery period.@@ -184,7 +188,7 @@ -- limited or flow control limited. -- -- Slow start.- | cwin < ssthresh = (cwin + spSentBytes, acked, SlowStart)+ | cwin < ssthresh = (cwin + sz, acked, SlowStart) -- Congestion avoidance. -- In this implementation, maxPktSiz == spSentBytes. -- spSentBytes is large enough, so we don't care
Network/QUIC/Recovery/Release.hs view
@@ -79,7 +79,7 @@ decreaseCC :: (Functor m, Foldable m) => LDCC -> m SentPacket -> IO () decreaseCC ldcc@LDCC{..} packets = do- let sentBytes = sum' (spSentBytes <$> packets)+ let sentBytes = sum' (inFlightBytes <$> packets) num = sum' (countAckEli <$> packets) metricsUpdated ldcc $ atomically $
Network/QUIC/Recovery/Utils.hs view
@@ -7,6 +7,7 @@ mergeLostCandidatesAndClear, peerCompletedAddressValidation, countAckEli,+ inFlightBytes, inCongestionRecovery, delay, ) where@@ -86,6 +87,24 @@ countAckEli sentPacket | spAckEliciting sentPacket = 1 | otherwise = 0++-- | RFC 9002 section 2: "Packets are considered in flight when they are+-- ack-eliciting or contain a PADDING frame".+--+-- Which is what 'inFlight' on a frame already says -- everything but ACK and+-- the two CONNECTION_CLOSEs -- so a packet is in flight when any frame in it+-- is. That predicate has been sitting in Types.Frame unused.+--+-- Both places that move bytesInFlight ask this, and they ask it of the same+-- SentPacket: the one in the sent-packet database, padding included, since+-- padding is added before 'onPacketSent' stores it. So the two answers+-- cannot disagree and the count cannot drift.+inFlightBytes :: SentPacket -> Int+inFlightBytes sentPacket+ | any inFlight $ plainFrames plain = spSentBytes sentPacket+ | otherwise = 0+ where+ PlainPacket _ plain = spPlainPacket sentPacket ----------------------------------------------------------------
Network/QUIC/Sender.hs view
@@ -43,16 +43,56 @@ where SizedBuffer buf0 bufsiz0 = encryptRes conn ldcc = connLDCC conn+ -- RFC 9002 section 7: "packets containing only ACK frames do not count+ -- toward bytes in flight and are not congestion controlled". Waiting+ -- for the window before sending one is therefore wrong on its own terms,+ -- and it wedges the sender: there is one sender thread, it blocks inside+ -- here until the window opens, and everything queued behind it waits --+ -- including the retransmission of a CRYPTO frame the peer needs before+ -- it can acknowledge anything and let the window open at all.+ --+ -- A stalled handshake caught in IOSpec has exactly that shape. The+ -- client's Finished is declared lost and queued for retransmission, and+ -- the queue never moves again because the sender is holding an ACK-only+ -- Handshake packet against a full window. The PTO keeps firing and+ -- keeps sending a bare PING, because what is being held is not+ -- ack-eliciting and so is not a probe; twenty seconds later the idle+ -- timeout ends it.+ ackOnly = all (not . spAckEliciting) spkts0 go maxSiz = do mx <-- atomically- ( (Just <$> takePingSTM ldcc)- `orElse` (Nothing <$ checkWindowOpenSTM ldcc maxSiz)- )+ if ackOnly+ then return Nothing+ else+ atomically+ ( (Just <$> takePingSTM ldcc)+ `orElse` (Nothing <$ checkWindowOpenSTM ldcc maxSiz)+ ) case mx of- Just lvl | lvl `elem` [InitialLevel, HandshakeLevel] -> do- sendPingPacket conn lvl- go maxSiz+ -- A PTO probe is the one packet allowed past a full congestion+ -- window. Normally sendPingPacket is the right way to spend it:+ -- it retransmits the oldest packet still in flight at this level,+ -- and falls back to a PING when there is none.+ --+ -- But when the packet the peer is waiting for has already been+ -- declared lost, it is no longer in flight -- it is here, in+ -- spkts0, waiting for a window that cannot open. The 1-RTT+ -- packets holding the window shut cannot be acknowledged until+ -- the peer has that very frame, and they are not eligible for+ -- loss detection either, because RFC 9002 section 6.2.1 forbids+ -- arming a timer for Application Data before the handshake is+ -- confirmed. So the bytes stay in flight, the window stays+ -- full, sendPingPacket finds nothing to retransmit and sends a+ -- PING, and the handshake never finishes.+ --+ -- Hold an ack-eliciting packet at this level and it is a probe in+ -- its own right, so send it instead -- the branch below does.+ -- Anything else is not a probe, and the PING still is.+ Just lvl+ | lvl `elem` [InitialLevel, HandshakeLevel]+ , not (any (\p -> spEncryptionLevel p == lvl && spAckEliciting p) spkts0) -> do+ sendPingPacket conn lvl+ go maxSiz _ -> do when (isJust mx) $ qlogDebug conn $ Debug "probe new" (sentPackets, leftsiz) <- buildPackets buf0 bufsiz0 maxSiz spkts0 id@@ -121,13 +161,24 @@ let ldcc = connLDCC conn mp <- releaseOldest ldcc lvl frames <- case mp of- Nothing -> do- qlogDebug conn $ Debug "probe ping"- return [Ping] Just spkt -> do qlogDebug conn $ Debug "probe old" let PlainPacket _ plain0 = spPlainPacket spkt adjustForRetransmit conn $ plainFrames plain0+ Nothing -> do+ -- Nothing in flight at this level to resend. That does not+ -- mean there is nothing to send: a packet already declared+ -- lost has left the sent-packet database and is waiting in+ -- the output queue, behind a congestion window that a probe+ -- is allowed to ignore and it is not. Spend the probe on it.+ mr <- atomically $ takeRetransSTM conn lvl+ case mr of+ Just (PlainPacket _ plain0) -> do+ qlogDebug conn $ Debug "probe retrans"+ adjustForRetransmit conn $ plainFrames plain0+ Nothing -> do+ qlogDebug conn $ Debug "probe ping"+ return [Ping] xs <- construct conn lvl frames False -- Asking for the last one and asking whether there is one at all are -- the same question, so ask it once.
Network/QUIC/Server/Reader.hs view
@@ -264,7 +264,20 @@ | isRetryToken ct -> do ok <- isRetryTokenValid ct if ok then pushToAcceptRetried ct else sendRetry- _ -> pushToAcceptFirst True+ | otherwise -> do+ -- A token we issued in NEW_TOKEN. It carries+ -- a lifetime, so honour it.+ fresh <- isTokenFresh ct+ pushToAcceptFirst fresh+ -- A token we cannot decrypt is not a token. RFC 9000+ -- section 8.1.3: "If the token is invalid, then the+ -- server SHOULD proceed as if the client did not have+ -- a validated address". Treating it as validated+ -- instead left a peer better off for sending+ -- rubbish than for sending nothing, and turned off+ -- the anti-amplification limit for an address that+ -- had proved nothing.+ Nothing -> pushToAcceptFirst False Just conn -> writeRecvQ (connRecvQ conn) $ mkReceivedPacket cpkt tim siz lvl where myVersions = scVersions@@ -336,6 +349,9 @@ } pushToAcceptQ myAuthCIDs peerAuthCIDs True pushToAcceptRetried _ = return ()+ isTokenFresh (CryptoToken _ life etim _) = do+ diff <- getElapsedTimeMicrosecond etim+ return $ diff <= Microseconds (fromIntegral life * 1000000) isRetryTokenValid (CryptoToken _tver life etim (Just (l, r, _))) = do diff <- getElapsedTimeMicrosecond etim return $
− cbits/fusion.c
@@ -1,2290 +0,0 @@-/*- * This source file is licensed under the Apache License 2.0 *and* the MIT- * License. Please agree to *both* of the licensing terms!- *- *- * `transformH` function is a derivative work of OpenSSL. The original work- * is covered by the following license:- *- * Copyright 2013-2020 The OpenSSL Project Authors. All Rights Reserved.- *- * Licensed under the Apache License 2.0 (the "License"). You may not use- * this file except in compliance with the License. You can obtain a copy- * in the file LICENSE in the source distribution or at- * https://www.openssl.org/source/license.html- *- *- * All other work, including modifications to the `transformH` function is- * covered by the following MIT license:- *- * Copyright (c) 2020-2022 Fastly, Kazuho Oku- *- * Permission is hereby granted, free of charge, to any person obtaining a copy- * of this software and associated documentation files (the "Software"), to- * deal in the Software without restriction, including without limitation the- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or- * sell copies of the Software, and to permit persons to whom the Software is- * furnished to do so, subject to the following conditions:- *- * The above copyright notice and this permission notice shall be included in- * all copies or substantial portions of the Software.- *- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS- * IN THE SOFTWARE.- */-#include <stdint.h>--#include <stdlib.h>-#include <string.h>-#include <immintrin.h>-#include <tmmintrin.h>-#include <nmmintrin.h>-#include <wmmintrin.h>-#include "picotls.h"-#include "picotls/fusion.h"--#if defined(__clang__)-#if __has_feature(address_sanitizer)-#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address")))-#endif-#elif __SANITIZE_ADDRESS__ /* gcc */-#define NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))-#endif-#ifndef NO_SANITIZE_ADDRESS-#define NO_SANITIZE_ADDRESS-#endif--#ifdef _WINDOWS-#define aligned_alloc(a, s) _aligned_malloc((s), (a))-#define aligned_free(p) _aligned_free(p)-#else-#define aligned_free(p) free(p)-#endif--struct ptls_fusion_aesgcm_context {- ptls_fusion_aesecb_context_t ecb;- size_t capacity;- size_t ghash_cnt;-};--struct ptls_fusion_aesgcm_context128 {- struct ptls_fusion_aesgcm_context super;- struct ptls_fusion_aesgcm_ghash_precompute128 {- __m128i H;- __m128i r;- } ghash[0];-};--struct ptls_fusion_aesgcm_context256 {- struct ptls_fusion_aesgcm_context super;- union ptls_fusion_aesgcm_ghash_precompute256 {- struct {- __m128i H[2];- __m128i r[2];- };- struct {- __m256i Hx2;- __m256i rx2;- };- } ghash[0];-};--struct ctr_context {- ptls_cipher_context_t super;- ptls_fusion_aesecb_context_t fusion;- __m128i bits;- uint8_t is_ready;-};--struct aesgcm_context {- ptls_aead_context_t super;- ptls_fusion_aesgcm_context_t *aesgcm;- /**- * retains the static IV in the upper 96 bits (in little endian)- */- __m128i static_iv;-};--static const uint64_t poly_[2] __attribute__((aligned(16))) = {1, 0xc200000000000000};-#define poly (*(__m128i *)poly_)-static const uint8_t byteswap_[32] __attribute__((aligned(32))) = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,- 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};-#define byteswap128 (*(__m128i *)byteswap_)-#define byteswap256 (*(__m256i *)byteswap_)-static const uint8_t one_[16] __attribute__((aligned(16))) = {1};-#define one8 (*(__m128i *)one_)-static const uint8_t incr128x2_[32] __attribute__((aligned(32))) = {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2};-#define incr128x2 (*(__m256i *)incr128x2_)--/* This function is covered by the Apache License and the MIT License. The origin is crypto/modes/asm/ghash-x86_64.pl of openssl- * at commit 33388b4. */-static __m128i transformH(__m128i H)-{- // # <<1 twist- // pshufd \$0b11111111,$Hkey,$T2 # broadcast uppermost dword- __m128i t2 = _mm_shuffle_epi32(H, 0xff);- // movdqa $Hkey,$T1- __m128i t1 = H;- // psllq \$1,$Hkey- H = _mm_slli_epi64(H, 1);- // pxor $T3,$T3 #- __m128i t3 = _mm_setzero_si128();- // psrlq \$63,$T1- t1 = _mm_srli_epi64(t1, 63);- // pcmpgtd $T2,$T3 # broadcast carry bit- t3 = _mm_cmplt_epi32(t2, t3);- // pslldq \$8,$T1- t1 = _mm_slli_si128(t1, 8);- // por $T1,$Hkey # H<<=1- H = _mm_or_si128(t1, H);-- // # magic reduction- // pand .L0x1c2_polynomial(%rip),$T3- t3 = _mm_and_si128(t3, poly);- // pxor $T3,$Hkey # if(carry) H^=0x1c2_polynomial- H = _mm_xor_si128(t3, H);-- return H;-}-// end of Apache License code--static __m128i gfmul(__m128i x, __m128i y)-{- __m128i lo = _mm_clmulepi64_si128(x, y, 0x00);- __m128i hi = _mm_clmulepi64_si128(x, y, 0x11);-- __m128i a = _mm_shuffle_epi32(x, 78);- __m128i b = _mm_shuffle_epi32(y, 78);- a = _mm_xor_si128(a, x);- b = _mm_xor_si128(b, y);-- a = _mm_clmulepi64_si128(a, b, 0x00);- a = _mm_xor_si128(a, lo);- a = _mm_xor_si128(a, hi);-- b = _mm_slli_si128(a, 8);- a = _mm_srli_si128(a, 8);-- lo = _mm_xor_si128(lo, b);- hi = _mm_xor_si128(hi, a);-- // from https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf- __m128i t = _mm_clmulepi64_si128(lo, poly, 0x10);- lo = _mm_shuffle_epi32(lo, 78);- lo = _mm_xor_si128(lo, t);- t = _mm_clmulepi64_si128(lo, poly, 0x10);- lo = _mm_shuffle_epi32(lo, 78);- lo = _mm_xor_si128(lo, t);-- return _mm_xor_si128(hi, lo);-}--static inline __m128i gfmul_do_reduce(__m128i hi, __m128i lo, __m128i mid)-{- mid = _mm_xor_si128(mid, hi);- mid = _mm_xor_si128(mid, lo);- lo = _mm_xor_si128(lo, _mm_slli_si128(mid, 8));- hi = _mm_xor_si128(hi, _mm_srli_si128(mid, 8));-- /* fast reduction, using https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf */- __m128i r = _mm_clmulepi64_si128(lo, poly, 0x10);- lo = _mm_shuffle_epi32(lo, 78);- lo = _mm_xor_si128(lo, r);- r = _mm_clmulepi64_si128(lo, poly, 0x10);- lo = _mm_shuffle_epi32(lo, 78);- lo = _mm_xor_si128(lo, r);- lo = _mm_xor_si128(hi, lo);-- return lo;-}--struct ptls_fusion_gfmul_state128 {- __m128i hi, lo, mid;-};--#if defined(__GNUC__) && !defined(__clang__)-static inline __m128i xor128(__m128i x, __m128i y)-{- __m128i ret;- __asm__("vpxor %2, %1, %0" : "=x"(ret) : "x"(x), "xm"(y));- return ret;-}-#else-#define xor128 _mm_xor_si128-#endif--static inline void gfmul_do_step128(struct ptls_fusion_gfmul_state128 *gstate, __m128i X,- struct ptls_fusion_aesgcm_ghash_precompute128 *precompute)-{- __m128i t1 = _mm_clmulepi64_si128(precompute->H, X, 0x00);- __m128i t2 = _mm_clmulepi64_si128(precompute->H, X, 0x11);- __m128i t3 = _mm_shuffle_epi32(X, 78);- t3 = _mm_xor_si128(t3, X);- t3 = _mm_clmulepi64_si128(precompute->r, t3, 0x00);- gstate->lo = xor128(gstate->lo, t1);- gstate->hi = xor128(gstate->hi, t2);- gstate->mid = xor128(gstate->mid, t3);-}--#undef xor128--static inline void gfmul_firststep128(struct ptls_fusion_gfmul_state128 *gstate, __m128i X,- struct ptls_fusion_aesgcm_ghash_precompute128 *precompute)-{- X = _mm_shuffle_epi8(X, byteswap128);- X = _mm_xor_si128(gstate->lo, X);- gstate->lo = _mm_setzero_si128();- gstate->hi = _mm_setzero_si128();- gstate->mid = _mm_setzero_si128();- gfmul_do_step128(gstate, X, precompute);-}--static inline void gfmul_nextstep128(struct ptls_fusion_gfmul_state128 *gstate, __m128i X,- struct ptls_fusion_aesgcm_ghash_precompute128 *precompute)-{- X = _mm_shuffle_epi8(X, byteswap128);- gfmul_do_step128(gstate, X, precompute);-}--static inline void gfmul_reduce128(struct ptls_fusion_gfmul_state128 *gstate)-{- gstate->lo = gfmul_do_reduce(gstate->hi, gstate->lo, gstate->mid);-}--static inline __m128i gfmul_get_tag128(struct ptls_fusion_gfmul_state128 *gstate, __m128i ek0)-{- __m128i tag = _mm_shuffle_epi8(gstate->lo, byteswap128);- tag = _mm_xor_si128(tag, ek0);- return tag;-}--struct ptls_fusion_gfmul_state256 {- __m256i hi, lo, mid;-};--static inline void gfmul_do_step256(struct ptls_fusion_gfmul_state256 *gstate, __m256i X,- union ptls_fusion_aesgcm_ghash_precompute256 *precompute)-{- __m256i t = _mm256_clmulepi64_epi128(precompute->Hx2, X, 0x00);- gstate->lo = _mm256_xor_si256(gstate->lo, t);- t = _mm256_clmulepi64_epi128(precompute->Hx2, X, 0x11);- gstate->hi = _mm256_xor_si256(gstate->hi, t);- t = _mm256_shuffle_epi32(X, 78);- t = _mm256_xor_si256(t, X);- t = _mm256_clmulepi64_epi128(precompute->rx2, t, 0x00);- gstate->mid = _mm256_xor_si256(gstate->mid, t);-}--static inline void gfmul_firststep256(struct ptls_fusion_gfmul_state256 *gstate, __m256i X, int half,- union ptls_fusion_aesgcm_ghash_precompute256 *precompute)-{- X = _mm256_shuffle_epi8(X, byteswap256);- X = _mm256_xor_si256(gstate->lo, X);- if (half)- X = _mm256_permute2f128_si256(X, X, 0x08);- gstate->lo = _mm256_setzero_si256();- gstate->hi = _mm256_setzero_si256();- gstate->mid = _mm256_setzero_si256();- gfmul_do_step256(gstate, X, precompute);-}--static inline void gfmul_nextstep256(struct ptls_fusion_gfmul_state256 *gstate, __m256i X,- union ptls_fusion_aesgcm_ghash_precompute256 *precompute)-{- X = _mm256_shuffle_epi8(X, byteswap256);- gfmul_do_step256(gstate, X, precompute);-}--static inline void gfmul_reduce256(struct ptls_fusion_gfmul_state256 *gstate)-{-#define XOR_256TO128(y) _mm_xor_si128(_mm256_castsi256_si128(y), _mm256_extractf128_si256((y), 1))- __m128i hi = XOR_256TO128(gstate->hi);- __m128i lo = XOR_256TO128(gstate->lo);- __m128i mid = XOR_256TO128(gstate->mid);-#undef XOR_256TO128-- lo = gfmul_do_reduce(hi, lo, mid);- gstate->lo = _mm256_castsi128_si256(lo);-}--static inline __m128i gfmul_get_tag256(struct ptls_fusion_gfmul_state256 *gstate, __m128i ek0)-{- __m128i tag = _mm_shuffle_epi8(_mm256_castsi256_si128(gstate->lo), byteswap128);- tag = _mm_xor_si128(tag, ek0);- return tag;-}--static inline __m128i aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, __m128i v)-{-#define ROUNDKEY(i) (ctx->aesni256 ? _mm256_castsi256_si128(ctx->keys.m256[i]) : ctx->keys.m128[i])-- v = _mm_xor_si128(v, ROUNDKEY(0));- for (size_t i = 1; i < ctx->rounds; ++i)- v = _mm_aesenc_si128(v, ROUNDKEY(i));- v = _mm_aesenclast_si128(v, ROUNDKEY(ctx->rounds));-- return v;--#undef ROUNDKEY-}--// 32-bytes of 0xff followed by 31-bytes of 0x00-static const uint8_t loadn_mask[63] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};-static const uint8_t loadn_shuffle[31] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // first 16 bytes map to byte offsets- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,- 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; // latter 15 bytes map to zero--NO_SANITIZE_ADDRESS-static inline __m128i loadn_end_of_page(const void *p, size_t l)-{- uintptr_t shift = (uintptr_t)p & 15;- __m128i pattern = _mm_loadu_si128((const __m128i *)(loadn_shuffle + shift));- return _mm_shuffle_epi8(_mm_load_si128((const __m128i *)((uintptr_t)p - shift)), pattern);-}--NO_SANITIZE_ADDRESS-static inline __m128i loadn128(const void *p, size_t l)-{- __m128i v, mask = _mm_loadu_si128((__m128i *)(loadn_mask + 32 - l));- uintptr_t mod4k = (uintptr_t)p % 4096;-- if (PTLS_LIKELY(mod4k <= 4096 - 16) || mod4k + l > 4096) {- v = _mm_loadu_si128(p);- } else {- v = loadn_end_of_page(p, l);- }- v = _mm_and_si128(v, mask);-- return v;-}--NO_SANITIZE_ADDRESS-static inline __m256i loadn256(const void *p, size_t l)-{- __m256i v, mask = _mm256_loadu_si256((__m256i *)(loadn_mask + 32 - l));- uintptr_t mod4k = (uintptr_t)p % 4096;-- if (PTLS_LIKELY(mod4k < 4096 - 32) || mod4k + l > 4096) {- v = _mm256_loadu_si256(p);- } else if (l > 16) {- __m128i first16 = _mm_loadu_si128(p), second16 = loadn128((uint8_t *)p + 16, l - 16);- v = _mm256_permute2f128_si256(_mm256_castsi128_si256(first16), _mm256_castsi128_si256(second16), 0x20);- } else if (l == 16) {- v = _mm256_castsi128_si256(_mm_loadu_si128(p));- } else {- v = _mm256_castsi128_si256(loadn_end_of_page(p, l));- }- v = _mm256_and_si256(v, mask);-- return v;-}--static inline void storen128(void *_p, size_t l, __m128i v)-{- uint8_t buf[16], *p = _p;-- *(__m128i *)buf = v;-- for (size_t i = 0; i != l; ++i)- p[i] = buf[i];-}--void ptls_fusion_aesgcm_encrypt(ptls_fusion_aesgcm_context_t *_ctx, void *output, const void *input, size_t inlen, __m128i ctr,- const void *_aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)-{-/* init the bits (we can always run in full), but use the last slot for calculating ek0, if possible */-#define AESECB6_INIT() \- do { \- ctr = _mm_add_epi64(ctr, one8); \- bits0 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits1 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits2 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits3 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits4 = _mm_shuffle_epi8(ctr, byteswap128); \- if (PTLS_LIKELY(srclen > 16 * 5)) { \- ctr = _mm_add_epi64(ctr, one8); \- bits5 = _mm_shuffle_epi8(ctr, byteswap128); \- } else { \- if ((state & STATE_EK0_BEEN_FED) == 0) { \- bits5 = ek0; \- state |= STATE_EK0_BEEN_FED; \- } \- if ((state & STATE_SUPP_USED) != 0 && srclen <= 16 * 4 && (const __m128i *)supp->input + 1 <= dst_ghash) { \- bits4 = _mm_loadu_si128(supp->input); \- bits4keys = ((struct ctr_context *)supp->ctx)->fusion.keys.m128; \- state |= STATE_SUPP_IN_PROCESS; \- } \- } \- __m128i k = ctx->super.ecb.keys.m128[0]; \- bits0 = _mm_xor_si128(bits0, k); \- bits1 = _mm_xor_si128(bits1, k); \- bits2 = _mm_xor_si128(bits2, k); \- bits3 = _mm_xor_si128(bits3, k); \- bits4 = _mm_xor_si128(bits4, bits4keys[0]); \- bits5 = _mm_xor_si128(bits5, k); \- } while (0)--/* aes block update */-#define AESECB6_UPDATE(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenc_si128(bits0, k); \- bits1 = _mm_aesenc_si128(bits1, k); \- bits2 = _mm_aesenc_si128(bits2, k); \- bits3 = _mm_aesenc_si128(bits3, k); \- bits4 = _mm_aesenc_si128(bits4, bits4keys[i]); \- bits5 = _mm_aesenc_si128(bits5, k); \- } while (0)--/* aesenclast */-#define AESECB6_FINAL(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenclast_si128(bits0, k); \- bits1 = _mm_aesenclast_si128(bits1, k); \- bits2 = _mm_aesenclast_si128(bits2, k); \- bits3 = _mm_aesenclast_si128(bits3, k); \- bits4 = _mm_aesenclast_si128(bits4, bits4keys[i]); \- bits5 = _mm_aesenclast_si128(bits5, k); \- } while (0)-- struct ptls_fusion_aesgcm_context128 *ctx = (void *)_ctx;- __m128i ek0, bits0, bits1, bits2, bits3, bits4, bits5 = _mm_setzero_si128();- const __m128i *bits4keys = ctx->super.ecb.keys.m128; /* is changed to supp->ctx.keys when calcurating suppout */- struct ptls_fusion_gfmul_state128 gstate = {0};- __m128i gdatabuf[6];- __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), byteswap128);-- // src and dst are updated after the chunk is processed- const __m128i *src = input;- __m128i *dst = output;- size_t srclen = inlen;- // aad and src_ghash are updated before the chunk is processed (i.e., when the pointers are fed indo the processor)- const __m128i *aad = _aad, *dst_ghash = dst;- size_t dst_ghashlen = srclen;-- struct ptls_fusion_aesgcm_ghash_precompute128 *ghash_precompute = ctx->ghash + (aadlen + 15) / 16 + (srclen + 15) / 16 + 1;--#define STATE_EK0_BEEN_FED 0x3-#define STATE_EK0_INCOMPLETE 0x2-#define STATE_EK0_READY() ((state & STATE_EK0_BEEN_FED) == 0x1)-#define STATE_SUPP_USED 0x4-#define STATE_SUPP_IN_PROCESS 0x8- int32_t state = supp != NULL ? STATE_SUPP_USED : 0;-- /* build counter */- ctr = _mm_insert_epi32(ctr, 1, 0);- ek0 = _mm_shuffle_epi8(ctr, byteswap128);-- /* start preparing AES */- AESECB6_INIT();- AESECB6_UPDATE(1);-- /* build first ghash data (only AAD can be fed at this point, as this would be calculated alongside the first AES block) */- const __m128i *gdata = gdatabuf; // points to the elements fed into GHASH- size_t gdata_cnt = 0;- if (PTLS_LIKELY(aadlen != 0)) {- while (gdata_cnt < 6) {- if (PTLS_LIKELY(aadlen < 16)) {- if (aadlen != 0) {- gdatabuf[gdata_cnt++] = loadn128(aad, aadlen);- aadlen = 0;- }- goto MainLoop;- }- gdatabuf[gdata_cnt++] = _mm_loadu_si128(aad++);- aadlen -= 16;- }- }-- /* the main loop */-MainLoop:- while (1) {- /* run AES and multiplication in parallel */- size_t i;- for (i = 2; i < gdata_cnt + 2; ++i) {- AESECB6_UPDATE(i);- gfmul_nextstep128(&gstate, _mm_loadu_si128(gdata++), --ghash_precompute);- }- for (; i < ctx->super.ecb.rounds; ++i)- AESECB6_UPDATE(i);- AESECB6_FINAL(i);-- /* apply the bit stream to src and write to dest */- if (PTLS_LIKELY(srclen >= 6 * 16)) {-#define APPLY(i) _mm_storeu_si128(dst + i, _mm_xor_si128(_mm_loadu_si128(src + i), bits##i))- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- dst += 6;- src += 6;- srclen -= 6 * 16;- } else {- if ((state & STATE_EK0_BEEN_FED) == STATE_EK0_BEEN_FED) {- ek0 = bits5;- state &= ~STATE_EK0_INCOMPLETE;- }- if ((state & STATE_SUPP_IN_PROCESS) != 0) {- _mm_storeu_si128((__m128i *)supp->output, bits4);- state &= ~(STATE_SUPP_USED | STATE_SUPP_IN_PROCESS);- }- if (srclen != 0) {-#define APPLY(i) \- do { \- if (PTLS_LIKELY(srclen >= 16)) { \- _mm_storeu_si128(dst++, _mm_xor_si128(_mm_loadu_si128(src++), bits##i)); \- srclen -= 16; \- } else if (PTLS_LIKELY(srclen != 0)) { \- bits0 = bits##i; \- goto ApplyRemainder; \- } else { \- goto ApplyEnd; \- } \- } while (0)- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- goto ApplyEnd;- ApplyRemainder:- storen128(dst, srclen, _mm_xor_si128(loadn128(src, srclen), bits0));- dst = (__m128i *)((uint8_t *)dst + srclen);- srclen = 0;- ApplyEnd:;- }- }-- /* next block AES starts here */- AESECB6_INIT();-- AESECB6_UPDATE(1);-- /* setup gdata */- if (PTLS_UNLIKELY(aadlen != 0)) {- gdata_cnt = 0;- while (gdata_cnt < 6) {- if (aadlen < 16) {- if (aadlen != 0) {- gdatabuf[gdata_cnt++] = loadn128(aad, aadlen);- aadlen = 0;- }- goto GdataFillDST;- }- gdatabuf[gdata_cnt++] = _mm_loadu_si128(aad++);- aadlen -= 16;- }- gdata = gdatabuf;- } else if (PTLS_LIKELY(dst_ghashlen >= 6 * 16)) {- gdata = dst_ghash;- gdata_cnt = 6;- dst_ghash += 6;- dst_ghashlen -= 96;- } else {- gdata_cnt = 0;- GdataFillDST:- while (gdata_cnt < 6) {- if (dst_ghashlen < 16) {- if (dst_ghashlen != 0) {- gdatabuf[gdata_cnt++] = loadn128(dst_ghash, dst_ghashlen);- dst_ghashlen = 0;- }- if (gdata_cnt < 6)- goto Finish;- break;- }- gdatabuf[gdata_cnt++] = _mm_loadu_si128(dst_ghash++);- dst_ghashlen -= 16;- }- gdata = gdatabuf;- }- }--Finish:- gdatabuf[gdata_cnt++] = ac;-- /* We have complete set of data to be fed into GHASH. Let's finish the remaining calculation.- * Note that by now, all AES operations for payload encryption and ek0 are complete. This is is because it is necessary for GCM- * to process at least the same amount of data (i.e. payload-blocks + AC), and because AES is at least one 96-byte block ahead.- */- assert(STATE_EK0_READY());- for (size_t i = 0; i < gdata_cnt; ++i)- gfmul_nextstep128(&gstate, gdatabuf[i], --ghash_precompute);-- gfmul_reduce128(&gstate);- _mm_storeu_si128(dst, gfmul_get_tag128(&gstate, ek0));-- /* Finish the calculation of supplemental vector. Done at the very last, because the sample might cover the GCM tag. */- if ((state & STATE_SUPP_USED) != 0) {- size_t i;- if ((state & STATE_SUPP_IN_PROCESS) == 0) {- bits4keys = ((struct ctr_context *)supp->ctx)->fusion.keys.m128;- bits4 = _mm_xor_si128(_mm_loadu_si128(supp->input), bits4keys[0]);- i = 1;- } else {- i = 2;- }- do {- bits4 = _mm_aesenc_si128(bits4, bits4keys[i++]);- } while (i != ctx->super.ecb.rounds);- bits4 = _mm_aesenclast_si128(bits4, bits4keys[i]);- _mm_storeu_si128((__m128i *)supp->output, bits4);- }--#undef AESECB6_INIT-#undef AESECB6_UPDATE-#undef AESECB6_FINAL-#undef STATE_EK0_BEEN_FOUND-#undef STATE_EK0_READY-#undef STATE_SUPP_IN_PROCESS-}--int ptls_fusion_aesgcm_decrypt(ptls_fusion_aesgcm_context_t *_ctx, void *output, const void *input, size_t inlen, __m128i ctr,- const void *_aad, size_t aadlen, const void *tag)-{- struct ptls_fusion_aesgcm_context128 *ctx = (void *)_ctx;- __m128i ek0 = _mm_setzero_si128(), bits0, bits1 = _mm_setzero_si128(), bits2 = _mm_setzero_si128(), bits3 = _mm_setzero_si128(),- bits4 = _mm_setzero_si128(), bits5 = _mm_setzero_si128();- struct ptls_fusion_gfmul_state128 gstate = {0};- __m128i gdatabuf[6];- __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), byteswap128);- struct ptls_fusion_aesgcm_ghash_precompute128 *ghash_precompute = ctx->ghash + (aadlen + 15) / 16 + (inlen + 15) / 16 + 1;-- const __m128i *gdata; // points to the elements fed into GHASH- size_t gdata_cnt;-- const __m128i *src_ghash = input, *src_aes = input, *aad = _aad;- __m128i *dst = output;- size_t nondata_aes_cnt = 0, src_ghashlen = inlen, src_aeslen = inlen;-- /* schedule ek0 and suppkey */- ctr = _mm_add_epi64(ctr, one8);- bits0 = _mm_xor_si128(_mm_shuffle_epi8(ctr, byteswap128), ctx->super.ecb.keys.m128[0]);- ++nondata_aes_cnt;--#define STATE_IS_FIRST_RUN 0x1-#define STATE_GHASH_HAS_MORE 0x2- int state = STATE_IS_FIRST_RUN | STATE_GHASH_HAS_MORE;-- /* the main loop */- while (1) {-- /* setup gdata */- if (PTLS_UNLIKELY(aadlen != 0)) {- gdata = gdatabuf;- gdata_cnt = 0;- while (gdata_cnt < 6) {- if (aadlen < 16) {- if (aadlen != 0) {- gdatabuf[gdata_cnt++] = loadn128(aad, aadlen);- aadlen = 0;- ++nondata_aes_cnt;- }- goto GdataFillSrc;- }- gdatabuf[gdata_cnt++] = _mm_loadu_si128(aad++);- aadlen -= 16;- ++nondata_aes_cnt;- }- } else if (PTLS_LIKELY(src_ghashlen >= 6 * 16)) {- gdata = src_ghash;- gdata_cnt = 6;- src_ghash += 6;- src_ghashlen -= 6 * 16;- } else {- gdata = gdatabuf;- gdata_cnt = 0;- GdataFillSrc:- while (gdata_cnt < 6) {- if (src_ghashlen < 16) {- if (src_ghashlen != 0) {- gdatabuf[gdata_cnt++] = loadn128(src_ghash, src_ghashlen);- src_ghash = (__m128i *)((uint8_t *)src_ghash + src_ghashlen);- src_ghashlen = 0;- }- if (gdata_cnt < 6 && (state & STATE_GHASH_HAS_MORE) != 0) {- gdatabuf[gdata_cnt++] = ac;- state &= ~STATE_GHASH_HAS_MORE;- }- break;- }- gdatabuf[gdata_cnt++] = _mm_loadu_si128(src_ghash++);- src_ghashlen -= 16;- }- }-- /* setup aes bits */- if (PTLS_LIKELY(nondata_aes_cnt == 0))- goto InitAllBits;- switch (nondata_aes_cnt) {-#define INIT_BITS(n, keys) \- case n: \- ctr = _mm_add_epi64(ctr, one8); \- bits##n = _mm_xor_si128(_mm_shuffle_epi8(ctr, byteswap128), keys.m128[0]);- InitAllBits:- INIT_BITS(0, ctx->super.ecb.keys);- INIT_BITS(1, ctx->super.ecb.keys);- INIT_BITS(2, ctx->super.ecb.keys);- INIT_BITS(3, ctx->super.ecb.keys);- INIT_BITS(4, ctx->super.ecb.keys);- INIT_BITS(5, ctx->super.ecb.keys);-#undef INIT_BITS- }-- { /* run aes and ghash */-#define AESECB6_UPDATE(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenc_si128(bits0, k); \- bits1 = _mm_aesenc_si128(bits1, k); \- bits2 = _mm_aesenc_si128(bits2, k); \- bits3 = _mm_aesenc_si128(bits3, k); \- bits4 = _mm_aesenc_si128(bits4, k); \- bits5 = _mm_aesenc_si128(bits5, k); \- } while (0)-- size_t aesi;- for (aesi = 1; aesi <= gdata_cnt; ++aesi) {- AESECB6_UPDATE(aesi);- gfmul_nextstep128(&gstate, _mm_loadu_si128(gdata++), --ghash_precompute);- }- for (; aesi < ctx->super.ecb.rounds; ++aesi)- AESECB6_UPDATE(aesi);- __m128i k = ctx->super.ecb.keys.m128[aesi];- bits0 = _mm_aesenclast_si128(bits0, k);- bits1 = _mm_aesenclast_si128(bits1, k);- bits2 = _mm_aesenclast_si128(bits2, k);- bits3 = _mm_aesenclast_si128(bits3, k);- bits4 = _mm_aesenclast_si128(bits4, k);- bits5 = _mm_aesenclast_si128(bits5, k);--#undef AESECB6_UPDATE- }-- /* apply aes bits */- if (PTLS_LIKELY(nondata_aes_cnt == 0 && src_aeslen >= 6 * 16)) {-#define APPLY(i) _mm_storeu_si128(dst + i, _mm_xor_si128(_mm_loadu_si128(src_aes + i), bits##i))- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- dst += 6;- src_aes += 6;- src_aeslen -= 6 * 16;- } else {- if ((state & STATE_IS_FIRST_RUN) != 0) {- ek0 = bits0;- state &= ~STATE_IS_FIRST_RUN;- }- switch (nondata_aes_cnt) {-#define APPLY(i) \- case i: \- if (PTLS_LIKELY(src_aeslen > 16)) { \- _mm_storeu_si128(dst++, _mm_xor_si128(_mm_loadu_si128(src_aes++), bits##i)); \- src_aeslen -= 16; \- } else { \- bits0 = bits##i; \- goto Finish; \- }- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- }- nondata_aes_cnt = 0;- }- }--Finish:- if (src_aeslen == 16) {- _mm_storeu_si128(dst, _mm_xor_si128(_mm_loadu_si128(src_aes), bits0));- } else if (src_aeslen != 0) {- storen128(dst, src_aeslen, _mm_xor_si128(loadn128(src_aes, src_aeslen), bits0));- }-- assert((state & STATE_IS_FIRST_RUN) == 0);-- /* the only case where AES operation is complete and GHASH is not is when the application of AC is remaining */- if ((state & STATE_GHASH_HAS_MORE) != 0) {- assert(ghash_precompute - 1 == ctx->ghash);- gfmul_nextstep128(&gstate, ac, --ghash_precompute);- }-- gfmul_reduce128(&gstate);- __m128i calctag = gfmul_get_tag128(&gstate, ek0);-- return _mm_movemask_epi8(_mm_cmpeq_epi8(calctag, _mm_loadu_si128(tag))) == 0xffff;--#undef STATE_IS_FIRST_RUN-#undef STATE_GHASH_HAS_MORE-}--static __m128i expand_key(__m128i key, __m128i temp)-{- key = _mm_xor_si128(key, _mm_slli_si128(key, 4));- key = _mm_xor_si128(key, _mm_slli_si128(key, 4));- key = _mm_xor_si128(key, _mm_slli_si128(key, 4));-- key = _mm_xor_si128(key, temp);-- return key;-}--void ptls_fusion_aesecb_init(ptls_fusion_aesecb_context_t *ctx, int is_enc, const void *key, size_t key_size, int aesni256)-{- assert(is_enc && "decryption is not supported (yet)");-- size_t i = 0;-- switch (key_size) {- case 16: /* AES128 */- ctx->rounds = 10;- break;- case 32: /* AES256 */- ctx->rounds = 14;- break;- default:- assert(!"invalid key size; AES128 / AES256 are supported");- break;- }- ctx->aesni256 = aesni256;-- /* load and expand keys using keys.m128 */- ctx->keys.m128[i++] = _mm_loadu_si128((__m128i *)key);- if (key_size == 32)- ctx->keys.m128[i++] = _mm_loadu_si128((__m128i *)key + 1);- while (1) {-#define EXPAND(R) \- { \- ctx->keys.m128[i] = \- expand_key(ctx->keys.m128[i - key_size / 16], \- _mm_shuffle_epi32(_mm_aeskeygenassist_si128(ctx->keys.m128[i - 1], R), _MM_SHUFFLE(3, 3, 3, 3))); \- if (i == ctx->rounds) \- break; \- ++i; \- if (key_size > 24) { \- ctx->keys.m128[i] = \- expand_key(ctx->keys.m128[i - key_size / 16], \- _mm_shuffle_epi32(_mm_aeskeygenassist_si128(ctx->keys.m128[i - 1], R), _MM_SHUFFLE(2, 2, 2, 2))); \- ++i; \- } \- }- EXPAND(0x1);- EXPAND(0x2);- EXPAND(0x4);- EXPAND(0x8);- EXPAND(0x10);- EXPAND(0x20);- EXPAND(0x40);- EXPAND(0x80);- EXPAND(0x1b);- EXPAND(0x36);-#undef EXPAND- }-- /* convert to keys.m256 if aesni256 is used */- if (ctx->aesni256) {- size_t i = ctx->rounds;- do {- ctx->keys.m256[i] = _mm256_broadcastsi128_si256(ctx->keys.m128[i]);- } while (i-- != 0);- }-}--void ptls_fusion_aesecb_dispose(ptls_fusion_aesecb_context_t *ctx)-{- ptls_clear_memory(ctx, sizeof(*ctx));-}--void ptls_fusion_aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, void *dst, const void *src)-{- __m128i v = _mm_loadu_si128(src);- v = aesecb_encrypt(ctx, v);- _mm_storeu_si128(dst, v);-}--/**- * returns the number of ghash entries that is required to handle an AEAD block of given size- */-static size_t aesgcm_calc_ghash_cnt(size_t capacity)-{- // round-up by block size, add to handle worst split of the size between AAD and payload, plus context to hash AC- return (capacity + 15) / 16 + 2;-}--static void setup_one_ghash_entry(ptls_fusion_aesgcm_context_t *ctx)-{- __m128i *H, *r, *Hprev, H0;-- if (ctx->ecb.aesni256) {- struct ptls_fusion_aesgcm_context256 *ctx256 = (void *)ctx;-#define GET_SLOT(i, mem) (&ctx256->ghash[(i) / 2].mem[(i) % 2 == 0])- H = GET_SLOT(ctx->ghash_cnt, H);- r = GET_SLOT(ctx->ghash_cnt, r);- Hprev = ctx->ghash_cnt == 0 ? NULL : GET_SLOT(ctx->ghash_cnt - 1, H);-#undef GET_SLOT- H0 = ctx256->ghash[0].H[1];- } else {- struct ptls_fusion_aesgcm_context128 *ctx128 = (void *)ctx;- H = &ctx128->ghash[ctx->ghash_cnt].H;- r = &ctx128->ghash[ctx->ghash_cnt].r;- Hprev = ctx->ghash_cnt == 0 ? NULL : &ctx128->ghash[ctx->ghash_cnt - 1].H;- H0 = ctx128->ghash[0].H;- }-- if (Hprev != NULL)- *H = gfmul(*Hprev, H0);-- *r = _mm_shuffle_epi32(*H, 78);- *r = _mm_xor_si128(*r, *H);-- ++ctx->ghash_cnt;-}--static size_t calc_aesgcm_context_size(size_t *ghash_cnt, int aesni256)-{- size_t sz;-- if (aesni256) {- if (*ghash_cnt % 2 != 0)- ++*ghash_cnt;- sz = offsetof(struct ptls_fusion_aesgcm_context256, ghash) +- sizeof(union ptls_fusion_aesgcm_ghash_precompute256) * *ghash_cnt / 2;- } else {- sz = offsetof(struct ptls_fusion_aesgcm_context128, ghash) +- sizeof(struct ptls_fusion_aesgcm_ghash_precompute128) * *ghash_cnt;- }- return sz;-}--static ptls_fusion_aesgcm_context_t *new_aesgcm(const void *key, size_t key_size, size_t capacity, int aesni256)-{- ptls_fusion_aesgcm_context_t *ctx;- size_t ghash_cnt = aesgcm_calc_ghash_cnt(capacity), ctx_size = calc_aesgcm_context_size(&ghash_cnt, aesni256);-- if ((ctx = aligned_alloc(32, ctx_size)) == NULL)- return NULL;-- ptls_fusion_aesecb_init(&ctx->ecb, 1, key, key_size, aesni256);-- ctx->capacity = capacity;-- __m128i H0 = aesecb_encrypt(&ctx->ecb, _mm_setzero_si128());- H0 = _mm_shuffle_epi8(H0, byteswap128);- H0 = transformH(H0);- if (ctx->ecb.aesni256) {- ((struct ptls_fusion_aesgcm_context256 *)ctx)->ghash[0].H[1] = H0;- } else {- ((struct ptls_fusion_aesgcm_context128 *)ctx)->ghash[0].H = H0;- }-- ctx->ghash_cnt = 0;- while (ctx->ghash_cnt < ghash_cnt)- setup_one_ghash_entry(ctx);-- return ctx;-}--ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_new(const void *key, size_t key_size, size_t capacity)-{- return new_aesgcm(key, key_size, capacity, 0);-}--ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_set_capacity(ptls_fusion_aesgcm_context_t *ctx, size_t capacity)-{- size_t new_ghash_cnt = aesgcm_calc_ghash_cnt(capacity);-- if (new_ghash_cnt <= ctx->ghash_cnt)- return ctx;-- size_t new_ctx_size = calc_aesgcm_context_size(&new_ghash_cnt, ctx->ecb.aesni256),- old_ctx_size = calc_aesgcm_context_size(&ctx->ghash_cnt, ctx->ecb.aesni256);-- ptls_fusion_aesgcm_context_t *newp;- if ((newp = aligned_alloc(32, new_ctx_size)) == NULL)- return NULL;- memcpy(newp, ctx, old_ctx_size);- ptls_clear_memory(ctx, old_ctx_size);- aligned_free(ctx);- ctx = newp;-- ctx->capacity = capacity;- while (ctx->ghash_cnt < new_ghash_cnt)- setup_one_ghash_entry(ctx);-- return ctx;-}--void ptls_fusion_aesgcm_free(ptls_fusion_aesgcm_context_t *ctx)-{- ptls_clear_memory(ctx, calc_aesgcm_context_size(&ctx->ghash_cnt, ctx->ecb.aesni256));- /* skip ptls_fusion_aesecb_dispose, based on the knowledge that it does not allocate memory elsewhere */-- aligned_free(ctx);-}--static void ctr_dispose(ptls_cipher_context_t *_ctx)-{- struct ctr_context *ctx = (struct ctr_context *)_ctx;- ptls_fusion_aesecb_dispose(&ctx->fusion);- _mm_storeu_si128(&ctx->bits, _mm_setzero_si128());-}--static void ctr_init(ptls_cipher_context_t *_ctx, const void *iv)-{- struct ctr_context *ctx = (struct ctr_context *)_ctx;- _mm_storeu_si128(&ctx->bits, aesecb_encrypt(&ctx->fusion, _mm_loadu_si128(iv)));- ctx->is_ready = 1;-}--static void ctr_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)-{- struct ctr_context *ctx = (struct ctr_context *)_ctx;-- assert((ctx->is_ready && len <= 16) ||- !"CTR transfomation is supported only once per call to `init` and the maximum size is limited to 16 bytes");- ctx->is_ready = 0;-- if (len < 16) {- storen128(output, len, _mm_xor_si128(_mm_loadu_si128(&ctx->bits), loadn128(input, len)));- } else {- _mm_storeu_si128(output, _mm_xor_si128(_mm_loadu_si128(&ctx->bits), _mm_loadu_si128(input)));- }-}--static int aesctr_setup(ptls_cipher_context_t *_ctx, int is_enc, const void *key, size_t key_size)-{- struct ctr_context *ctx = (struct ctr_context *)_ctx;-- ctx->super.do_dispose = ctr_dispose;- ctx->super.do_init = ctr_init;- ctx->super.do_transform = ctr_transform;- ptls_fusion_aesecb_init(&ctx->fusion, 1, key, key_size, 0 /* probably we do not need aesni256 for CTR? */);- ctx->is_ready = 0;-- return 0;-}--static int aes128ctr_setup(ptls_cipher_context_t *ctx, int is_enc, const void *key)-{- return aesctr_setup(ctx, is_enc, key, PTLS_AES128_KEY_SIZE);-}--static int aes256ctr_setup(ptls_cipher_context_t *ctx, int is_enc, const void *key)-{- return aesctr_setup(ctx, is_enc, key, PTLS_AES256_KEY_SIZE);-}--static void aesgcm_dispose_crypto(ptls_aead_context_t *_ctx)-{- struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;-- ptls_fusion_aesgcm_free(ctx->aesgcm);-}--static void aead_do_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)-{- assert(!"FIXME");-}--static size_t aead_do_encrypt_update(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen)-{- assert(!"FIXME");- return SIZE_MAX;-}--static size_t aead_do_encrypt_final(ptls_aead_context_t *_ctx, void *_output)-{- assert(!"FIXME");- return SIZE_MAX;-}--static inline __m128i calc_counter(struct aesgcm_context *ctx, uint64_t seq)-{- __m128i ctr = _mm_setzero_si128();- ctr = _mm_insert_epi64(ctr, seq, 0);- ctr = _mm_slli_si128(ctr, 4);- ctr = _mm_xor_si128(ctx->static_iv, ctr);- return ctr;-}--void aead_do_encrypt(struct st_ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)-{- struct aesgcm_context *ctx = (void *)_ctx;-- if (inlen + aadlen > ctx->aesgcm->capacity)- ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, inlen + aadlen);- ptls_fusion_aesgcm_encrypt(ctx->aesgcm, output, input, inlen, calc_counter(ctx, seq), aad, aadlen, supp);-}--static void aead_do_encrypt_v(struct st_ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,- const void *aad, size_t aadlen)-{- assert(!"FIXME");-}--size_t aead_do_decrypt(ptls_aead_context_t *_ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen)-{- struct aesgcm_context *ctx = (void *)_ctx;-- if (inlen < 16)- return SIZE_MAX;-- size_t enclen = inlen - 16;- if (enclen + aadlen > ctx->aesgcm->capacity)- ctx->aesgcm = ptls_fusion_aesgcm_set_capacity(ctx->aesgcm, enclen + aadlen);- if (!ptls_fusion_aesgcm_decrypt(ctx->aesgcm, output, input, enclen, calc_counter(ctx, seq), aad, aadlen,- (const uint8_t *)input + enclen))- return SIZE_MAX;- return enclen;-}--static inline void aesgcm_get_iv(ptls_aead_context_t *_ctx, void *iv)-{- struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;-- __m128i m128 = _mm_shuffle_epi8(ctx->static_iv, byteswap128);- storen128(iv, PTLS_AESGCM_IV_SIZE, m128);-}--static inline void aesgcm_set_iv(ptls_aead_context_t *_ctx, const void *iv)-{- struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;-- ctx->static_iv = loadn128(iv, PTLS_AESGCM_IV_SIZE);- ctx->static_iv = _mm_shuffle_epi8(ctx->static_iv, byteswap128);-}--static int aesgcm_setup(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, size_t key_size)-{- struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;-- ctx->static_iv = loadn128(iv, PTLS_AESGCM_IV_SIZE);- ctx->static_iv = _mm_shuffle_epi8(ctx->static_iv, byteswap128);- if (key == NULL)- return 0;-- ctx->super.dispose_crypto = aesgcm_dispose_crypto;- ctx->super.do_get_iv = aesgcm_get_iv;- ctx->super.do_set_iv = aesgcm_set_iv;- ctx->super.do_encrypt_init = aead_do_encrypt_init;- ctx->super.do_encrypt_update = aead_do_encrypt_update;- ctx->super.do_encrypt_final = aead_do_encrypt_final;- ctx->super.do_encrypt = aead_do_encrypt;- ctx->super.do_encrypt_v = aead_do_encrypt_v;- ctx->super.do_decrypt = aead_do_decrypt;-- ctx->aesgcm = new_aesgcm(key, key_size, 1500 /* assume ordinary packet size */, 0 /* no support for aesni256 yet */);-- return 0;-}--int aes128gcm_setup(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)-{- return aesgcm_setup(ctx, is_enc, key, iv, PTLS_AES128_KEY_SIZE);-}--int aes256gcm_setup(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)-{- return aesgcm_setup(ctx, is_enc, key, iv, PTLS_AES256_KEY_SIZE);-}--int ptls_fusion_can_aesni256 = 0;-ptls_cipher_algorithm_t ptls_fusion_aes128ctr = {"AES128-CTR",- PTLS_AES128_KEY_SIZE,- 1, // block size- PTLS_AES_IV_SIZE,- sizeof(struct ctr_context),- aes128ctr_setup};-ptls_cipher_algorithm_t ptls_fusion_aes256ctr = {"AES256-CTR",- PTLS_AES256_KEY_SIZE,- 1, // block size- PTLS_AES_IV_SIZE,- sizeof(struct ctr_context),- aes256ctr_setup};-ptls_aead_algorithm_t ptls_fusion_aes128gcm = {"AES128-GCM",- PTLS_AESGCM_CONFIDENTIALITY_LIMIT,- PTLS_AESGCM_INTEGRITY_LIMIT,- &ptls_fusion_aes128ctr,- NULL, // &ptls_fusion_aes128ecb,- PTLS_AES128_KEY_SIZE,- PTLS_AESGCM_IV_SIZE,- PTLS_AESGCM_TAG_SIZE,- {0}, // while it may work, no reason to support TLS/1.2- 0,- 0,- sizeof(struct aesgcm_context),- aes128gcm_setup};-ptls_aead_algorithm_t ptls_fusion_aes256gcm = {"AES256-GCM",- PTLS_AESGCM_CONFIDENTIALITY_LIMIT,- PTLS_AESGCM_INTEGRITY_LIMIT,- &ptls_fusion_aes256ctr,- NULL, // &ptls_fusion_aes256ecb,- PTLS_AES256_KEY_SIZE,- PTLS_AESGCM_IV_SIZE,- PTLS_AESGCM_TAG_SIZE,- {0}, // while it may work, no reason to support TLS/1.2- 0,- 0,- sizeof(struct aesgcm_context),- aes256gcm_setup};--static inline size_t calc_total_length(ptls_iovec_t *input, size_t incnt)-{- size_t totlen = 0;- for (size_t i = 0; i < incnt; ++i)- totlen += input[i].len;- return totlen;-}--static inline void reduce_aad128(struct ptls_fusion_gfmul_state128 *gstate, struct ptls_fusion_aesgcm_ghash_precompute128 *ghash,- const void *_aad, size_t aadlen)-{- struct ptls_fusion_aesgcm_ghash_precompute128 *ghash_precompute;- const uint8_t *aad = _aad;-- while (PTLS_UNLIKELY(aadlen >= 6 * 16)) {- ghash_precompute = ghash + 6;- gfmul_firststep128(gstate, _mm_loadu_si128((void *)aad), --ghash_precompute);- aad += 16;- aadlen -= 16;- for (int i = 1; i < 6; ++i) {- gfmul_nextstep128(gstate, _mm_loadu_si128((void *)aad), --ghash_precompute);- aad += 16;- aadlen -= 16;- }- gfmul_reduce128(gstate);- }-- if (PTLS_LIKELY(aadlen != 0)) {- ghash_precompute = ghash + (aadlen + 15) / 16;- if (PTLS_UNLIKELY(aadlen >= 16)) {- gfmul_firststep128(gstate, _mm_loadu_si128((void *)aad), --ghash_precompute);- aad += 16;- aadlen -= 16;- while (aadlen >= 16) {- gfmul_nextstep128(gstate, _mm_loadu_si128((void *)aad), --ghash_precompute);- aad += 16;- aadlen -= 16;- }- if (PTLS_LIKELY(aadlen != 0))- gfmul_nextstep128(gstate, loadn128(aad, aadlen), --ghash_precompute);- } else {- gfmul_firststep128(gstate, loadn128(aad, aadlen), --ghash_precompute);- }- assert(ghash == ghash_precompute);- gfmul_reduce128(gstate);- }-}--NO_SANITIZE_ADDRESS-static inline uint8_t *load_preceding_unaligned(uint8_t *encbuf, uint8_t **output)-{- uint8_t *encp;-- if ((encp = encbuf + ((uintptr_t)*output & 63)) != encbuf) {- _mm256_store_si256((void *)encbuf, _mm256_load_si256((void *)(*output - (encp - encbuf))));- _mm256_store_si256((void *)(encbuf + 32), _mm256_load_si256((void *)(*output - (encp - encbuf) + 32)));- *output -= encp - encbuf;- }-- return encp;-}--NO_SANITIZE_ADDRESS-static inline void write_remaining_bytes(uint8_t *dst, const uint8_t *src, const uint8_t *end)-{- /* Write in 64-byte chunks, using NT store instructions. Last partial block, if any, is written to cache, as that cache line- * would likely be read when the next TLS record is being built. */-- for (; end - src >= 64; dst += 64, src += 64) {- _mm256_stream_si256((void *)dst, _mm256_load_si256((void *)src));- _mm256_stream_si256((void *)(dst + 32), _mm256_load_si256((void *)(src + 32)));- }- _mm_sfence(); /* weakly ordered writes have to be synced before being passed to NIC */- if (src != end) {- for (; end - src >= 16; dst += 16, src += 16)- _mm_store_si128((void *)dst, _mm_load_si128((void *)src));- if (src != end)- storen128((void *)dst, end - src, loadn128((void *)src, end - src));- }-}--NO_SANITIZE_ADDRESS-static void non_temporal_encrypt_v128(struct st_ptls_aead_context_t *_ctx, void *_output, ptls_iovec_t *input, size_t incnt,- uint64_t seq, const void *aad, size_t aadlen)-{-/* init the bits (we can always run in full), but use the last slot for calculating ek0, if possible */-#define AESECB6_INIT() \- do { \- ctr = _mm_add_epi64(ctr, one8); \- bits0 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits1 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits2 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits3 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits4 = _mm_shuffle_epi8(ctr, byteswap128); \- if (PTLS_LIKELY(srclen > 16 * 5) || src_vecleft != 0) { \- ctr = _mm_add_epi64(ctr, one8); \- bits5 = _mm_shuffle_epi8(ctr, byteswap128); \- } else { \- bits5 = ek0; \- state |= STATE_EK0_READY; \- } \- __m128i k = ctx->super.ecb.keys.m128[0]; \- bits0 = _mm_xor_si128(bits0, k); \- bits1 = _mm_xor_si128(bits1, k); \- bits2 = _mm_xor_si128(bits2, k); \- bits3 = _mm_xor_si128(bits3, k); \- bits4 = _mm_xor_si128(bits4, k); \- bits5 = _mm_xor_si128(bits5, k); \- } while (0)--/* aes block update */-#define AESECB6_UPDATE(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenc_si128(bits0, k); \- bits1 = _mm_aesenc_si128(bits1, k); \- bits2 = _mm_aesenc_si128(bits2, k); \- bits3 = _mm_aesenc_si128(bits3, k); \- bits4 = _mm_aesenc_si128(bits4, k); \- bits5 = _mm_aesenc_si128(bits5, k); \- } while (0)--/* aesenclast */-#define AESECB6_FINAL(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenclast_si128(bits0, k); \- bits1 = _mm_aesenclast_si128(bits1, k); \- bits2 = _mm_aesenclast_si128(bits2, k); \- bits3 = _mm_aesenclast_si128(bits3, k); \- bits4 = _mm_aesenclast_si128(bits4, k); \- bits5 = _mm_aesenclast_si128(bits5, k); \- } while (0)-- struct aesgcm_context *agctx = (void *)_ctx;- uint8_t *output = _output;--#define STATE_EK0_READY 0x1-#define STATE_COPY_128B 0x2- int32_t state = 0;-- /* Bytes are written here first then written using NT store instructions, 64 bytes at a time. */- uint8_t encbuf[32 * 6] __attribute__((aligned(32))), *encp;-- /* `encbuf` should be large enough to store up to 63-bytes of unaligned bytes, 6 16-byte AES blocks, plus AEAD tag that is- * append to the ciphertext before writing the bytes to main memory using NT store instructions. */- PTLS_BUILD_ASSERT(sizeof(encbuf) >= 64 + 6 * 16 + 16);-- /* load unaligned data within same cache line preceding `output`, adjusting pointers accordingly */- encp = load_preceding_unaligned(encbuf, &output);-- /* First write would be 128 bytes (32+6*16), if encbuf contains no less than 32 bytes already. */- if (encp - encbuf >= 32)- state |= STATE_COPY_128B;-- /* setup ctr, retain Ek(0), len(A) | len(C) to be fed into GCM */- __m128i ctr = calc_counter(agctx, seq);- ctr = _mm_insert_epi32(ctr, 1, 0);- __m128i ek0 = _mm_shuffle_epi8(ctr, byteswap128);- __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)calc_total_length(input, incnt) * 8), byteswap128);-- struct ptls_fusion_aesgcm_context128 *ctx = (void *)agctx->aesgcm;- __m128i bits0, bits1, bits2, bits3, bits4, bits5 = _mm_setzero_si128();- struct ptls_fusion_gfmul_state128 gstate = {0};-- /* find the first non-empty vec */- const uint8_t *src = NULL;- size_t srclen = 0, src_vecleft = incnt;- while (srclen == 0 && src_vecleft != 0) {- src = (void *)input[0].base;- srclen = input[0].len;- ++input;- --src_vecleft;- }-- /* Prepare first 6 blocks of bit stream, at the same time calculating ghash of AAD. */- AESECB6_INIT();- AESECB6_UPDATE(1);- AESECB6_UPDATE(2);- reduce_aad128(&gstate, ctx->ghash, aad, aadlen);- for (size_t i = 3; i < ctx->super.ecb.rounds; ++i)- AESECB6_UPDATE(i);- AESECB6_FINAL(ctx->super.ecb.rounds);-- /* Main loop. This loop:- * 1. using current keystream (bits0..bits5), xors a up to 6 * 16 bytes and writes to encbuf,- * 2. then if there is no more data to be encrypted, exit the loop, otherwise,- * 3. calculate ghash of the blocks being written to encbuf,- * 4. calculate next 6 * 16 bytes of keystream,- * 5. writes encbuf in 64-byte blocks- * When exitting the loop, `remaining_ghash_from` represents the offset within `encbuf` from where ghash remains to be- * calculated. */- size_t remaining_ghash_from = encp - encbuf;- if (srclen != 0) {- while (1) {- /* apply the bit stream to input, writing to encbuf */- if (PTLS_LIKELY(srclen >= 6 * 16)) {-#define APPLY(i) _mm_storeu_si128((void *)(encp + i * 16), _mm_xor_si128(_mm_loadu_si128((void *)(src + i * 16)), bits##i))- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- encp += 6 * 16;- src += 6 * 16;- srclen -= 6 * 16;- if (PTLS_UNLIKELY(srclen == 0)) {- if (src_vecleft == 0) {- remaining_ghash_from = (encp - encbuf) - 96;- break;- }- src = (void *)input[0].base;- srclen = input[0].len;- ++input;- --src_vecleft;- }- } else {- /* slow path, load at most 6 * 16 bytes to encbuf then encrypt in-place */- size_t bytes_copied = 0;- do {- if (srclen >= 16 && bytes_copied < 5 * 16) {- _mm_storeu_si128((void *)(encp + bytes_copied), _mm_loadu_si128((void *)src));- bytes_copied += 16;- src += 16;- srclen -= 16;- } else {- encp[bytes_copied++] = *src++;- --srclen;- }- if (PTLS_UNLIKELY(srclen == 0)) {- do {- if (src_vecleft == 0)- break;- src = (void *)input[0].base;- srclen = input[0].len;- ++input;- --src_vecleft;- } while (srclen == 0);- if (srclen == 0)- break;- }- } while (bytes_copied < 6 * 16);-#define APPLY(i) _mm_storeu_si128((void *)(encp + i * 16), _mm_xor_si128(_mm_loadu_si128((void *)(encp + i * 16)), bits##i))- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- encp += bytes_copied;- if (PTLS_UNLIKELY(srclen == 0)) {- /* Calculate amonut of data left to be ghashed, as well as zero-clearing the remainedr of partial block, as it- * will be fed into ghash. */- remaining_ghash_from = (encp - encbuf) - bytes_copied;- if ((bytes_copied & 15) != 0)- _mm_storeu_si128((void *)encp, _mm_setzero_si128());- break;- }- }-- /* Next 96-byte block starts here. Run AES and ghash in while writing output using non-temporal stores in 64-byte- * blocks. */- AESECB6_INIT();- struct ptls_fusion_aesgcm_ghash_precompute128 *ghash_precompute = ctx->ghash + 6;- gfmul_firststep128(&gstate, _mm_loadu_si128((void *)(encp - 6 * 16)), --ghash_precompute);- AESECB6_UPDATE(1);- gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(encp - 5 * 16)), --ghash_precompute);- AESECB6_UPDATE(2);- gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(encp - 4 * 16)), --ghash_precompute);- AESECB6_UPDATE(3);- _mm256_stream_si256((void *)output, _mm256_load_si256((void *)encbuf));- _mm256_stream_si256((void *)(output + 32), _mm256_load_si256((void *)(encbuf + 32)));- AESECB6_UPDATE(4);- gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(encp - 3 * 16)), --ghash_precompute);- AESECB6_UPDATE(5);- gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(encp - 2 * 16)), --ghash_precompute);- AESECB6_UPDATE(6);- gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(encp - 1 * 16)), --ghash_precompute);- AESECB6_UPDATE(7);- if ((state & STATE_COPY_128B) != 0) {- _mm256_stream_si256((void *)(output + 64), _mm256_load_si256((void *)(encbuf + 64)));- _mm256_stream_si256((void *)(output + 96), _mm256_load_si256((void *)(encbuf + 96)));- output += 128;- encp -= 128;- AESECB6_UPDATE(8);- _mm256_store_si256((void *)encbuf, _mm256_load_si256((void *)(encbuf + 128)));- _mm256_store_si256((void *)(encbuf + 32), _mm256_load_si256((void *)(encbuf + 160)));- } else {- output += 64;- encp -= 64;- _mm256_store_si256((void *)encbuf, _mm256_load_si256((void *)(encbuf + 64)));- _mm256_store_si256((void *)(encbuf + 32), _mm256_load_si256((void *)(encbuf + 96)));- AESECB6_UPDATE(8);- }- state ^= STATE_COPY_128B;- AESECB6_UPDATE(9);- if (PTLS_UNLIKELY(ctx->super.ecb.rounds != 10)) {- for (size_t i = 10; PTLS_LIKELY(i < ctx->super.ecb.rounds); ++i)- AESECB6_UPDATE(i);- }- assert(ctx->ghash == ghash_precompute);- gfmul_reduce128(&gstate);- AESECB6_FINAL(ctx->super.ecb.rounds);- }- }-- /* Now, All the encrypted bits are built in encbuf. Calculate AEAD tag and append to encbuf. */-- { /* Run ghash against the remaining bytes, after appending `ac` (i.e., len(A) | len(C)). At this point, we might be ghashing 7- * blocks at once. */- size_t ac_off = remaining_ghash_from + ((encp - encbuf) - remaining_ghash_from + 15) / 16 * 16;- _mm_storeu_si128((void *)(encbuf + ac_off), ac);- size_t blocks = ((encp - encbuf) - remaining_ghash_from + 15) / 16 + 1; /* round up, +1 for AC */- assert(blocks <= 7);- struct ptls_fusion_aesgcm_ghash_precompute128 *ghash_precompute = ctx->ghash + blocks;- gfmul_firststep128(&gstate, _mm_loadu_si128((void *)(encbuf + remaining_ghash_from)), --ghash_precompute);- remaining_ghash_from += 16;- while (ghash_precompute != ctx->ghash) {- gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(encbuf + remaining_ghash_from)), --ghash_precompute);- remaining_ghash_from += 16;- }- gfmul_reduce128(&gstate);- }-- /* Calculate EK0, if in the unlikely case on not been done yet. When encoding in full size (16K), EK0 will be ready. */- if (PTLS_UNLIKELY((state & STATE_EK0_READY) == 0)) {- bits5 = _mm_xor_si128(ek0, ctx->super.ecb.keys.m128[0]);- for (size_t i = 1; i < ctx->super.ecb.rounds; ++i)- bits5 = _mm_aesenc_si128(bits5, ctx->super.ecb.keys.m128[i]);- bits5 = _mm_aesenclast_si128(bits5, ctx->super.ecb.keys.m128[ctx->super.ecb.rounds]);- }-- /* append tag to encbuf */- _mm_storeu_si128((void *)encp, gfmul_get_tag128(&gstate, bits5));- encp += 16;-- /* write remaining bytes */- write_remaining_bytes(output, encbuf, encp);--#undef AESECB6_INIT-#undef AESECB6_UPDATE-#undef AESECB6_FINAL-#undef STATE_EK0_READY-#undef STATE_COPY_128B-}--static size_t non_temporal_decrypt128(ptls_aead_context_t *_ctx, void *_output, const void *_input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen)-{- /* Bail out if the input is too short, or remove tag from range. */- if (inlen < 16)- return SIZE_MAX;- inlen -= 16;- size_t textlen = inlen;--/* init the bits (we can always run in full), but use the last slot for calculating ek0, if possible */-#define AESECB6_INIT() \- do { \- ctr = _mm_add_epi64(ctr, one8); \- bits0 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits1 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits2 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits3 = _mm_shuffle_epi8(ctr, byteswap128); \- ctr = _mm_add_epi64(ctr, one8); \- bits4 = _mm_shuffle_epi8(ctr, byteswap128); \- if (PTLS_LIKELY(inlen > 16 * 5)) { \- ctr = _mm_add_epi64(ctr, one8); \- bits5 = _mm_shuffle_epi8(ctr, byteswap128); \- } else { \- bits5 = ek0; \- state |= STATE_EK0_READY; \- } \- __m128i k = ctx->super.ecb.keys.m128[0]; \- bits0 = _mm_xor_si128(bits0, k); \- bits1 = _mm_xor_si128(bits1, k); \- bits2 = _mm_xor_si128(bits2, k); \- bits3 = _mm_xor_si128(bits3, k); \- bits4 = _mm_xor_si128(bits4, k); \- bits5 = _mm_xor_si128(bits5, k); \- } while (0)--/* aes block update */-#define AESECB6_UPDATE(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenc_si128(bits0, k); \- bits1 = _mm_aesenc_si128(bits1, k); \- bits2 = _mm_aesenc_si128(bits2, k); \- bits3 = _mm_aesenc_si128(bits3, k); \- bits4 = _mm_aesenc_si128(bits4, k); \- bits5 = _mm_aesenc_si128(bits5, k); \- } while (0)--/* aesenclast */-#define AESECB6_FINAL(i) \- do { \- __m128i k = ctx->super.ecb.keys.m128[i]; \- bits0 = _mm_aesenclast_si128(bits0, k); \- bits1 = _mm_aesenclast_si128(bits1, k); \- bits2 = _mm_aesenclast_si128(bits2, k); \- bits3 = _mm_aesenclast_si128(bits3, k); \- bits4 = _mm_aesenclast_si128(bits4, k); \- bits5 = _mm_aesenclast_si128(bits5, k); \- } while (0)-- struct aesgcm_context *agctx = (void *)_ctx;- uint8_t *output = _output;- const uint8_t *input = _input;--#define STATE_EK0_READY 0x1- int32_t state = 0;-- /* setup ctr, retain Ek(0), len(A) | len(C) to be fed into GCM */- __m128i ctr = calc_counter(agctx, seq);- ctr = _mm_insert_epi32(ctr, 1, 0);- __m128i ek0 = _mm_shuffle_epi8(ctr, byteswap128);- __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), byteswap128);-- struct ptls_fusion_aesgcm_context128 *ctx = (void *)agctx->aesgcm;- __m128i bits0, bits1, bits2, bits3, bits4, bits5 = _mm_setzero_si128();- struct ptls_fusion_gfmul_state128 gstate = {0};-- /* Prepare first 6 blocks of bit stream, at the same time calculating ghash of AAD. */- AESECB6_INIT();- AESECB6_UPDATE(1);- AESECB6_UPDATE(2);- reduce_aad128(&gstate, ctx->ghash, aad, aadlen);- for (size_t i = 3; i < ctx->super.ecb.rounds; ++i)- AESECB6_UPDATE(i);- AESECB6_FINAL(ctx->super.ecb.rounds);-- /* Main loop. Operate in full blocks (6 * 16 bytes). */- while (PTLS_LIKELY(inlen >= 6 * 16)) {-#define DECRYPT(i) _mm_storeu_si128((void *)(output + i * 16), _mm_xor_si128(bits##i, _mm_loadu_si128((void *)(input + i * 16))))- DECRYPT(0);- DECRYPT(1);- DECRYPT(2);- DECRYPT(3);- DECRYPT(4);- DECRYPT(5);-#undef DECRYPT-#define GFMUL_NEXT(i) gfmul_nextstep128(&gstate, _mm_loadu_si128((void *)(input + i * 16)), ctx->ghash + 5 - i)- AESECB6_INIT();- AESECB6_UPDATE(1);- AESECB6_UPDATE(2);- AESECB6_UPDATE(3);- gfmul_firststep128(&gstate, _mm_loadu_si128((void *)input), ctx->ghash + 5);- AESECB6_UPDATE(4);- GFMUL_NEXT(1);- AESECB6_UPDATE(5);- GFMUL_NEXT(2);- AESECB6_UPDATE(6);- GFMUL_NEXT(3);- AESECB6_UPDATE(7);- GFMUL_NEXT(4);- AESECB6_UPDATE(8);- GFMUL_NEXT(5);- AESECB6_UPDATE(9);- gfmul_reduce128(&gstate);- if (PTLS_UNLIKELY(ctx->super.ecb.rounds != 10)) {- size_t i = 10;- do {- AESECB6_UPDATE(i);- } while (++i < ctx->super.ecb.rounds);- }- AESECB6_FINAL(ctx->super.ecb.rounds);- output += 6 * 16;- input += 6 * 16;- inlen -= 6 * 16;-#undef GFMUL_NEXT- }-- /* Decrypt the remainder as well as finishing GHASH calculation. */- if (inlen != 0) {- struct ptls_fusion_aesgcm_ghash_precompute128 *ghash_precompute = ctx->ghash + (inlen + 15) / 16 + 1;-#define ONEBLOCK(i) \- do { \- if (inlen != 0) { \- __m128i b = inlen >= 16 ? _mm_loadu_si128((void *)input) : loadn128(input, inlen); \- if (i == 0) { \- gfmul_firststep128(&gstate, b, --ghash_precompute); \- } else { \- gfmul_nextstep128(&gstate, b, --ghash_precompute); \- } \- b = _mm_xor_si128(b, bits##i); \- if (inlen >= 16) { \- _mm_storeu_si128((void *)output, b); \- output += 16; \- input += 16; \- inlen -= 16; \- } else { \- storen128(output, inlen, b); \- output += inlen; \- input += inlen; \- inlen = 0; \- } \- } \- } while (0)- ONEBLOCK(0);- ONEBLOCK(1);- ONEBLOCK(2);- ONEBLOCK(3);- ONEBLOCK(4);- ONEBLOCK(5);-#undef ONEBLOCK- gfmul_nextstep128(&gstate, ac, --ghash_precompute);- assert(ghash_precompute == ctx->ghash);- } else {- gfmul_firststep128(&gstate, ac, ctx->ghash);- }- gfmul_reduce128(&gstate);-- /* Calculate EK0 if not yet available in bits5. */- if ((state & STATE_EK0_READY) == 0) {- bits5 = _mm_xor_si128(ek0, ctx->super.ecb.keys.m128[0]);- for (size_t i = 1; i < ctx->super.ecb.rounds; ++i)- bits5 = _mm_aesenc_si128(bits5, ctx->super.ecb.keys.m128[i]);- bits5 = _mm_aesenclast_si128(bits5, ctx->super.ecb.keys.m128[ctx->super.ecb.rounds]);- }-- /* Calculate GCM tag and compare. */- __m128i calctag = gfmul_get_tag128(&gstate, bits5);- __m128i recvtag = _mm_loadu_si128((void *)input);- if (_mm_movemask_epi8(_mm_cmpeq_epi8(calctag, recvtag)) != 0xffff)- return SIZE_MAX;-- return textlen;--#undef AESECB6_INIT-#undef AESECB6_UPDATE-#undef AESECB6_FINAL-#undef STATE_EK0_READY-}--NO_SANITIZE_ADDRESS-static void non_temporal_encrypt_v256(struct st_ptls_aead_context_t *_ctx, void *_output, ptls_iovec_t *input, size_t incnt,- uint64_t seq, const void *_aad, size_t aadlen)-{-/* init the bits (we can always run in full), but use the last slot for calculating ek0, if possible */-#define AESECB6_INIT() \- do { \- ctr = _mm256_add_epi64(ctr, incr128x2); \- bits0 = _mm256_shuffle_epi8(ctr, byteswap256); \- ctr = _mm256_add_epi64(ctr, incr128x2); \- bits1 = _mm256_shuffle_epi8(ctr, byteswap256); \- ctr = _mm256_add_epi64(ctr, incr128x2); \- bits2 = _mm256_shuffle_epi8(ctr, byteswap256); \- ctr = _mm256_add_epi64(ctr, incr128x2); \- bits3 = _mm256_shuffle_epi8(ctr, byteswap256); \- ctr = _mm256_add_epi64(ctr, incr128x2); \- bits4 = _mm256_shuffle_epi8(ctr, byteswap256); \- ctr = _mm256_add_epi64(ctr, incr128x2); \- bits5 = _mm256_shuffle_epi8(ctr, byteswap256); \- if (PTLS_UNLIKELY(srclen <= 32 * 6 - 16) && src_vecleft == 0) { \- bits5 = _mm256_permute2f128_si256(bits5, ac_ek0, 0x30); \- state |= STATE_EK0_READY; \- } \- __m256i k = ctx->super.ecb.keys.m256[0]; \- bits0 = _mm256_xor_si256(bits0, k); \- bits1 = _mm256_xor_si256(bits1, k); \- bits2 = _mm256_xor_si256(bits2, k); \- bits3 = _mm256_xor_si256(bits3, k); \- bits4 = _mm256_xor_si256(bits4, k); \- bits5 = _mm256_xor_si256(bits5, k); \- } while (0)--/* aes block update */-#define AESECB6_UPDATE(i) \- do { \- __m256i k = ctx->super.ecb.keys.m256[i]; \- bits0 = _mm256_aesenc_epi128(bits0, k); \- bits1 = _mm256_aesenc_epi128(bits1, k); \- bits2 = _mm256_aesenc_epi128(bits2, k); \- bits3 = _mm256_aesenc_epi128(bits3, k); \- bits4 = _mm256_aesenc_epi128(bits4, k); \- bits5 = _mm256_aesenc_epi128(bits5, k); \- } while (0)--/* aesenclast */-#define AESECB6_FINAL(i) \- do { \- __m256i k = ctx->super.ecb.keys.m256[i]; \- bits0 = _mm256_aesenclast_epi128(bits0, k); \- bits1 = _mm256_aesenclast_epi128(bits1, k); \- bits2 = _mm256_aesenclast_epi128(bits2, k); \- bits3 = _mm256_aesenclast_epi128(bits3, k); \- bits4 = _mm256_aesenclast_epi128(bits4, k); \- bits5 = _mm256_aesenclast_epi128(bits5, k); \- } while (0)-- struct aesgcm_context *agctx = (void *)_ctx;- uint8_t *output = _output;- const uint8_t *aad = _aad;--#define STATE_EK0_READY 0x1- int32_t state = 0;-- /* Bytes are written here first then written using NT store instructions, 64 bytes at a time. */- uint8_t encbuf[32 * 9] __attribute__((aligned(32))), *encp;-- /* `encbuf` should be large enough to store up to 63-bytes of unaligned bytes, 6 16-byte AES blocks, plus AEAD tag that is- * append to the ciphertext before writing the bytes to main memory using NT store instructions. */- PTLS_BUILD_ASSERT(sizeof(encbuf) >= 64 + 6 * 32 + 16);-- /* load unaligned data within same cache line preceding `output`, adjusting pointers accordingly */- encp = load_preceding_unaligned(encbuf, &output);-- /* setup ctr, retaining Ek(0), len(A) | len(C) to be fed into GCM */- __m256i ctr = _mm256_broadcastsi128_si256(calc_counter(agctx, seq));- ctr = _mm256_insert_epi32(ctr, 1, 4);- __m256i ac_ek0 = _mm256_permute2f128_si256(- /* first half: ac */- _mm256_castsi128_si256(- _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)calc_total_length(input, incnt) * 8), byteswap128)),- /* second half: ek0 */- _mm256_shuffle_epi8(ctr, byteswap256), 0x30);-- struct ptls_fusion_aesgcm_context256 *ctx = (void *)agctx->aesgcm;- __m256i bits0, bits1, bits2, bits3, bits4, bits5 = _mm256_setzero_si256();- struct ptls_fusion_gfmul_state256 gstate = {0};-- /* find the first non-empty vec */- const uint8_t *src = NULL;- size_t srclen = 0, src_vecleft = incnt;- while (srclen == 0 && src_vecleft != 0) {- src = (void *)input[0].base;- srclen = input[0].len;- ++input;- --src_vecleft;- }-- /* Prepare first 6 blocks of bit stream, at the same time calculating ghash of AAD. */- AESECB6_INIT();- AESECB6_UPDATE(1);- AESECB6_UPDATE(2);- if (PTLS_LIKELY(aadlen != 0)) {- union ptls_fusion_aesgcm_ghash_precompute256 *ghash_precompute;- while (PTLS_UNLIKELY(aadlen >= 6 * 32)) {- ghash_precompute = ctx->ghash + 6;- gfmul_firststep256(&gstate, _mm256_loadu_si256((void *)aad), 0, --ghash_precompute);- aad += 32;- aadlen -= 32;- for (int i = 1; i < 6; ++i) {- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)aad), --ghash_precompute);- aad += 32;- aadlen -= 32;- }- gfmul_reduce256(&gstate);- }- if (PTLS_LIKELY(aadlen != 0)) {- ghash_precompute = ctx->ghash + (aadlen + 31) / 32;- if (PTLS_UNLIKELY(aadlen >= 32)) {- if (aadlen % 32 == 0 || aadlen % 32 > 16) {- gfmul_firststep256(&gstate, _mm256_loadu_si256((void *)aad), 0, --ghash_precompute);- aad += 32;- aadlen -= 32;- } else {- gfmul_firststep256(&gstate, _mm256_loadu_si256((void *)aad), 1, --ghash_precompute);- aad += 16;- aadlen -= 16;- }- while (aadlen >= 32) {- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)aad), --ghash_precompute);- aad += 32;- aadlen -= 32;- }- if (PTLS_LIKELY(aadlen != 0)) {- assert(aadlen > 16);- gfmul_nextstep256(&gstate, loadn256(aad, aadlen), --ghash_precompute);- }- } else {- gfmul_firststep256(&gstate, loadn256(aad, aadlen), aadlen <= 16, --ghash_precompute);- }- assert(ctx->ghash == ghash_precompute);- gfmul_reduce256(&gstate);- }- }- for (size_t i = 3; i < ctx->super.ecb.rounds; ++i)- AESECB6_UPDATE(i);- AESECB6_FINAL(ctx->super.ecb.rounds);-- /* Main loop. This loop:- * 1. using current keystream (bits0..bits5), xors a up to 6 * 16 bytes and writes to encbuf,- * 2. then if there is no more data to be encrypted, exit the loop, otherwise,- * 3. calculate ghash of the blocks being written to encbuf,- * 4. calculate next 6 * 16 bytes of keystream,- * 5. writes encbuf in 64-byte blocks- * When exitting the loop, `remaining_ghash_from` represents the offset within `encbuf` from where ghash remains to be- * calculated. */- size_t remaining_ghash_from = encp - encbuf;- if (srclen != 0) {- while (1) {- /* apply the bit stream to input, writing to encbuf */- if (PTLS_LIKELY(srclen >= 6 * 32)) {-#define APPLY(i) _mm256_storeu_si256((void *)(encp + i * 32), _mm256_xor_si256(_mm256_loadu_si256((void *)(src + i * 32)), bits##i))- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- encp += 6 * 32;- src += 6 * 32;- srclen -= 6 * 32;- if (PTLS_UNLIKELY(srclen == 0)) {- if (src_vecleft == 0) {- remaining_ghash_from = (encp - encbuf) - 6 * 32;- break;- }- src = (void *)input[0].base;- srclen = input[0].len;- ++input;- --src_vecleft;- }- } else {- /* slow path, load at most 6 * 32 bytes to encbuf then encrypt in-place */- size_t bytes_copied = 0;- do {- if (srclen >= 32 && bytes_copied < 5 * 32) {- _mm256_storeu_si256((void *)(encp + bytes_copied), _mm256_loadu_si256((void *)src));- bytes_copied += 32;- src += 32;- srclen -= 32;- } else {- encp[bytes_copied++] = *src++;- --srclen;- }- if (PTLS_UNLIKELY(srclen == 0)) {- do {- if (src_vecleft == 0)- break;- src = (void *)input[0].base;- srclen = input[0].len;- ++input;- --src_vecleft;- } while (srclen == 0);- if (srclen == 0)- break;- }- } while (bytes_copied < 6 * 32);-#define APPLY(i) \- _mm256_storeu_si256((void *)(encp + i * 32), _mm256_xor_si256(_mm256_loadu_si256((void *)(encp + i * 32)), bits##i))- APPLY(0);- APPLY(1);- APPLY(2);- APPLY(3);- APPLY(4);- APPLY(5);-#undef APPLY- encp += bytes_copied;- if (PTLS_UNLIKELY(srclen == 0)) {- /* Calculate amonut of data left to be ghashed, as well as zero-clearing the remainedr of partial block, as it- * will be fed into ghash. */- remaining_ghash_from = (encp - encbuf) - bytes_copied;- if ((bytes_copied & 15) != 0)- _mm_storeu_si128((void *)encp, _mm_setzero_si128());- break;- }- }-- /* Next 96-byte block starts here. Run AES and ghash in parallel while writing output using non-temporal store- * instructions. */- AESECB6_INIT();- union ptls_fusion_aesgcm_ghash_precompute256 *ghash_precompute = ctx->ghash + 6;- gfmul_firststep256(&gstate, _mm256_loadu_si256((void *)(encp - 6 * 32)), 0, --ghash_precompute);- AESECB6_UPDATE(1);- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)(encp - 5 * 32)), --ghash_precompute);- AESECB6_UPDATE(2);- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)(encp - 4 * 32)), --ghash_precompute);- AESECB6_UPDATE(3);- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)(encp - 3 * 32)), --ghash_precompute);- AESECB6_UPDATE(4);- _mm256_stream_si256((void *)output, _mm256_load_si256((void *)encbuf));- _mm256_stream_si256((void *)(output + 32), _mm256_load_si256((void *)(encbuf + 32)));- _mm256_stream_si256((void *)(output + 64), _mm256_load_si256((void *)(encbuf + 64)));- _mm256_stream_si256((void *)(output + 96), _mm256_load_si256((void *)(encbuf + 96)));- _mm256_stream_si256((void *)(output + 128), _mm256_load_si256((void *)(encbuf + 128)));- _mm256_stream_si256((void *)(output + 160), _mm256_load_si256((void *)(encbuf + 160)));- AESECB6_UPDATE(5);- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)(encp - 2 * 32)), --ghash_precompute);- AESECB6_UPDATE(6);- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)(encp - 1 * 32)), --ghash_precompute);- output += 192;- encp -= 192;- AESECB6_UPDATE(7);- _mm256_store_si256((void *)encbuf, _mm256_load_si256((void *)(encbuf + 192)));- AESECB6_UPDATE(8);- _mm256_store_si256((void *)(encbuf + 32), _mm256_load_si256((void *)(encbuf + 224)));- AESECB6_UPDATE(9);- if (PTLS_UNLIKELY(ctx->super.ecb.rounds != 10)) {- for (size_t i = 10; PTLS_LIKELY(i < ctx->super.ecb.rounds); ++i)- AESECB6_UPDATE(i);- }- assert(ctx->ghash == ghash_precompute);- gfmul_reduce256(&gstate);- AESECB6_FINAL(ctx->super.ecb.rounds);- }- }-- /* Now, All the encrypted bits are built in encbuf. Calculate AEAD tag and append to encbuf. */-- { /* Run ghash against the remaining bytes, after appending `ac` (i.e., len(A) | len(C)). At this point, we might be ghashing 7- * blocks at once. */- size_t ac_off = remaining_ghash_from + ((encp - encbuf) - remaining_ghash_from + 15) / 16 * 16;- _mm_storeu_si128((void *)(encbuf + ac_off), _mm256_castsi256_si128(ac_ek0));- size_t blocks = ((encp - encbuf) - remaining_ghash_from + 15) / 16 + 1; /* round up, +1 for AC */- assert(blocks <= 13);- union ptls_fusion_aesgcm_ghash_precompute256 *ghash_precompute = ctx->ghash + blocks / 2;- if (blocks % 2 != 0) {- gfmul_firststep256(&gstate, _mm256_loadu_si256((void *)(encbuf + remaining_ghash_from)), 1, ghash_precompute);- remaining_ghash_from += 16;- } else {- gfmul_firststep256(&gstate, _mm256_loadu_si256((void *)(encbuf + remaining_ghash_from)), 0, --ghash_precompute);- remaining_ghash_from += 32;- }- while (ghash_precompute != ctx->ghash) {- gfmul_nextstep256(&gstate, _mm256_loadu_si256((void *)(encbuf + remaining_ghash_from)), --ghash_precompute);- remaining_ghash_from += 32;- }- gfmul_reduce256(&gstate);- }-- /* Calculate EK0, if in the unlikely case on not been done yet. When encoding in full size (16K), EK0 will be ready. */- if (PTLS_UNLIKELY((state & STATE_EK0_READY) == 0)) {- bits5 = ac_ek0;- bits5 = _mm256_xor_si256(bits5, ctx->super.ecb.keys.m256[0]);- for (size_t i = 1; i < ctx->super.ecb.rounds; ++i)- bits5 = _mm256_aesenc_epi128(bits5, ctx->super.ecb.keys.m256[i]);- bits5 = _mm256_aesenclast_epi128(bits5, ctx->super.ecb.keys.m256[ctx->super.ecb.rounds]);- }-- /* append tag to encbuf */- _mm_storeu_si128((void *)encp,- gfmul_get_tag256(&gstate, _mm256_castsi256_si128(_mm256_permute2f128_si256(bits5, bits5, 0x11))));- encp += 16;-- /* write remaining bytes */- write_remaining_bytes(output, encbuf, encp);-}--static int non_temporal_setup(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, size_t key_size)-{- struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;- int aesni256 = is_enc && ptls_fusion_can_aesni256;-- ctx->static_iv = loadn128(iv, PTLS_AESGCM_IV_SIZE);- ctx->static_iv = _mm_shuffle_epi8(ctx->static_iv, byteswap128);- if (key == NULL)- return 0;-- ctx->super.dispose_crypto = aesgcm_dispose_crypto;- ctx->super.do_get_iv = aesgcm_get_iv;- ctx->super.do_set_iv = aesgcm_set_iv;- ctx->super.do_encrypt_init = NULL;- ctx->super.do_encrypt_update = NULL;- ctx->super.do_encrypt_final = NULL;- if (is_enc) {- ctx->super.do_encrypt = ptls_aead__do_encrypt;- ctx->super.do_encrypt_v = aesni256 ? non_temporal_encrypt_v256 : non_temporal_encrypt_v128;- ctx->super.do_decrypt = NULL;- } else {- assert(!aesni256);- ctx->super.do_encrypt = NULL;- ctx->super.do_encrypt_v = NULL;- ctx->super.do_decrypt = non_temporal_decrypt128;- }-- ctx->aesgcm =- new_aesgcm(key, key_size,- 7 * (ptls_fusion_can_aesni256 ? 32 : 16), // 6 blocks at once, plus len(A) | len(C) that we might append- aesni256);-- return 0;-}--static int non_temporal_aes128gcm_setup(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)-{- return non_temporal_setup(ctx, is_enc, key, iv, PTLS_AES128_KEY_SIZE);-}--static int non_temporal_aes256gcm_setup(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv)-{- return non_temporal_setup(ctx, is_enc, key, iv, PTLS_AES256_KEY_SIZE);-}--ptls_aead_algorithm_t ptls_non_temporal_aes128gcm = {"AES128-GCM",- PTLS_AESGCM_CONFIDENTIALITY_LIMIT,- PTLS_AESGCM_INTEGRITY_LIMIT,- &ptls_fusion_aes128ctr,- NULL, // &ptls_fusion_aes128ecb,- PTLS_AES128_KEY_SIZE,- PTLS_AESGCM_IV_SIZE,- PTLS_AESGCM_TAG_SIZE,- {PTLS_TLS12_AESGCM_FIXED_IV_SIZE, PTLS_TLS12_AESGCM_RECORD_IV_SIZE},- 1,- PTLS_X86_CACHE_LINE_ALIGN_BITS,- sizeof(struct aesgcm_context),- non_temporal_aes128gcm_setup};-ptls_aead_algorithm_t ptls_non_temporal_aes256gcm = {"AES256-GCM",- PTLS_AESGCM_CONFIDENTIALITY_LIMIT,- PTLS_AESGCM_INTEGRITY_LIMIT,- &ptls_fusion_aes256ctr,- NULL, // &ptls_fusion_aes128ecb,- PTLS_AES256_KEY_SIZE,- PTLS_AESGCM_IV_SIZE,- PTLS_AESGCM_TAG_SIZE,- {PTLS_TLS12_AESGCM_FIXED_IV_SIZE, PTLS_TLS12_AESGCM_RECORD_IV_SIZE},- 1,- PTLS_X86_CACHE_LINE_ALIGN_BITS,- sizeof(struct aesgcm_context),- non_temporal_aes256gcm_setup};--#ifdef _WINDOWS-/**- * ptls_fusion_is_supported_by_cpu:- * Check that the CPU has extended instructions for PCMUL, AES and AVX2.- * This test assumes that the CPU is following the x86/x64 architecture.- * A slightly more refined test could check that the cpu_info spells out- * "genuineIntel" or "authenticAMD", but would fail in presence of- * little known CPU brands or some VM */-int ptls_fusion_is_supported_by_cpu(void)-{- uint32_t cpu_info[4];- uint32_t nb_ids;- int is_supported = 0;-- __cpuid(cpu_info, 0);- nb_ids = cpu_info[0];-- if (nb_ids >= 7) {- uint32_t leaf1_ecx;- __cpuid(cpu_info, 1);- leaf1_ecx = cpu_info[2];-- if (/* PCLMUL */ (leaf1_ecx & (1 << 5)) != 0 && /* AES */ (leaf1_ecx & (1 << 25)) != 0) {- uint32_t leaf7_ebx, leaf7_ecx;- __cpuid(cpu_info, 7);- leaf7_ebx = cpu_info[1];- leaf7_ecx = cpu_info[2];-- is_supported = /* AVX2 */ (leaf7_ebx & (1 << 5)) != 0;-- /* enable 256-bit mode if possible */- if (is_supported && (leaf7_ecx & 0x600) != 0 && !ptls_fusion_can_aesni256)- ptls_fusion_can_aesni256 = 1;- }- }-- return is_supported;-}-#else-int ptls_fusion_is_supported_by_cpu(void)-{- unsigned leaf1_ecx, leaf7_ebx, leaf7_ecx;-- { /* GCC-specific code to obtain CPU features */- unsigned leaf_cnt;- __asm__("cpuid" : "=a"(leaf_cnt) : "a"(0) : "ebx", "ecx", "edx");- if (leaf_cnt < 7)- return 0;- __asm__("cpuid" : "=c"(leaf1_ecx) : "a"(1) : "ebx", "edx");- __asm__("cpuid" : "=b"(leaf7_ebx), "=c"(leaf7_ecx) : "a"(7), "c"(0) : "edx");- }-- /* AVX2 */- if ((leaf7_ebx & (1 << 5)) == 0)- return 0;- /* AES */- if ((leaf1_ecx & (1 << 25)) == 0)- return 0;- /* PCLMUL */- if ((leaf1_ecx & (1 << 1)) == 0)- return 0;-- /* enable 256-bit mode if possible */- if ((leaf7_ecx & 0x600) != 0 && !ptls_fusion_can_aesni256)- ptls_fusion_can_aesni256 = 1;-- return 1;-}-#endif--/* ---------------------------------------------------------------- */--// struct aesgcm_context {-// ptls_aead_context_t super;-// ptls_fusion_aesgcm_context_t *aesgcm; <- ptls_fusion_aesgcm_free-- ptls_aead_context_t *aead_context_new() {- ptls_aead_context_t *p = malloc(sizeof(struct aesgcm_context));- return p;- }--void aead_context_free(ptls_aead_context_t *p) {- aesgcm_dispose_crypto(p);- free(p);-}--/* ---------------------------------------------------------------- */--ptls_aead_supplementary_encryption_t *supplement_new(uint8_t *key, unsigned int siz) {- ptls_aead_supplementary_encryption_t *supp = malloc(sizeof(ptls_aead_supplementary_encryption_t));- if (siz == PTLS_AES256_KEY_SIZE) {- supp->ctx = ptls_cipher_new(&ptls_fusion_aes256ctr, 1, key);- } else {- supp->ctx = ptls_cipher_new(&ptls_fusion_aes128ctr, 1, key);- }- return supp;-}--void supplement_free(ptls_aead_supplementary_encryption_t *supp) {- ptls_cipher_free(supp->ctx);- free(supp);-}--void supplement_set_sample(ptls_aead_supplementary_encryption_t *supp, uint8_t *sample) {- supp->input = sample;-}--uint8_t *supplement_get_mask(ptls_aead_supplementary_encryption_t *supp) {- return (supp->output);-}
− cbits/picotls.c
@@ -1,6647 +0,0 @@-/*- * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku- *- * Permission is hereby granted, free of charge, to any person obtaining a copy- * of this software and associated documentation files (the "Software"), to- * deal in the Software without restriction, including without limitation the- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or- * sell copies of the Software, and to permit persons to whom the Software is- * furnished to do so, subject to the following conditions:- *- * The above copyright notice and this permission notice shall be included in- * all copies or substantial portions of the Software.- *- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS- * IN THE SOFTWARE.- */-#ifdef _WINDOWS-#include "winsock.h"-#endif-#include <assert.h>-#include <stddef.h>-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#ifndef _WINDOWS-#include <errno.h>-#include <pthread.h>-#include <unistd.h>-#include <arpa/inet.h>-#include <sys/time.h>-#endif-#include "picotls.h"-#if PICOTLS_USE_DTRACE-#include "picotls-probes.h"-#endif--#define PTLS_MAX_PLAINTEXT_RECORD_SIZE 16384-#define PTLS_MAX_ENCRYPTED_RECORD_SIZE (16384 + 256)--#define PTLS_RECORD_VERSION_MAJOR 3-#define PTLS_RECORD_VERSION_MINOR 3--#define PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC 20-#define PTLS_CONTENT_TYPE_ALERT 21-#define PTLS_CONTENT_TYPE_HANDSHAKE 22-#define PTLS_CONTENT_TYPE_APPDATA 23--#define PTLS_PSK_KE_MODE_PSK 0-#define PTLS_PSK_KE_MODE_PSK_DHE 1--#define PTLS_HANDSHAKE_HEADER_SIZE 4--#define PTLS_EXTENSION_TYPE_SERVER_NAME 0-#define PTLS_EXTENSION_TYPE_STATUS_REQUEST 5-#define PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS 10-#define PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS 13-#define PTLS_EXTENSION_TYPE_ALPN 16-#define PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE 20-#define PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE 27-#define PTLS_EXTENSION_TYPE_PRE_SHARED_KEY 41-#define PTLS_EXTENSION_TYPE_EARLY_DATA 42-#define PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS 43-#define PTLS_EXTENSION_TYPE_COOKIE 44-#define PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES 45-#define PTLS_EXTENSION_TYPE_KEY_SHARE 51-#define PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS 0xfd00-#define PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO 0xfe0d--#define PTLS_SERVER_NAME_TYPE_HOSTNAME 0--#define PTLS_ECH_CONFIG_VERSION 0xfe0d-#define PTLS_ECH_CLIENT_HELLO_TYPE_OUTER 0-#define PTLS_ECH_CLIENT_HELLO_TYPE_INNER 1--#define PTLS_ECH_CONFIRM_LENGTH 8--static const char ech_info_prefix[8] = "tls ech";--#define PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, server CertificateVerify"-#define PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING "TLS 1.3, client CertificateVerify"-#define PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE \- (64 + sizeof(PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING) + PTLS_MAX_DIGEST_SIZE * 2)--#define PTLS_EARLY_DATA_MAX_DELAY 10000 /* max. RTT (in msec) to permit early data */--#ifndef PTLS_MAX_EARLY_DATA_SKIP_SIZE-#define PTLS_MAX_EARLY_DATA_SKIP_SIZE 65536-#endif-#if defined(PTLS_DEBUG) && PTLS_DEBUG-#define PTLS_DEBUGF(...) fprintf(stderr, __VA_ARGS__)-#else-#define PTLS_DEBUGF(...)-#endif--#ifndef PTLS_MEMORY_DEBUG-#define PTLS_MEMORY_DEBUG 0-#endif--#if PICOTLS_USE_DTRACE-#define PTLS_SHOULD_PROBE(LABEL, tls) (PTLS_UNLIKELY(PICOTLS_##LABEL##_ENABLED()) && !(tls)->skip_tracing)-#define PTLS_PROBE0(LABEL, tls) \- do { \- ptls_t *_tls = (tls); \- if (PTLS_SHOULD_PROBE(LABEL, _tls)) \- PICOTLS_##LABEL(_tls); \- } while (0)-#define PTLS_PROBE(LABEL, tls, ...) \- do { \- ptls_t *_tls = (tls); \- if (PTLS_SHOULD_PROBE(LABEL, _tls)) \- PICOTLS_##LABEL(_tls, __VA_ARGS__); \- } while (0)-#else-#define PTLS_PROBE0(LABEL, tls)-#define PTLS_PROBE(LABEL, tls, ...)-#endif--/**- * list of supported versions in the preferred order- */-static const uint16_t supported_versions[] = {PTLS_PROTOCOL_VERSION_TLS13};--static const uint8_t hello_retry_random[PTLS_HELLO_RANDOM_SIZE] = {0xCF, 0x21, 0xAD, 0x74, 0xE5, 0x9A, 0x61, 0x11, 0xBE, 0x1D, 0x8C,- 0x02, 0x1E, 0x65, 0xB8, 0x91, 0xC2, 0xA2, 0x11, 0x16, 0x7A, 0xBB,- 0x8C, 0x5E, 0x07, 0x9E, 0x09, 0xE2, 0xC8, 0xA8, 0x33, 0x9C};--struct st_ptls_traffic_protection_t {- uint8_t secret[PTLS_MAX_DIGEST_SIZE];- size_t epoch;- /* the following fields are not used if the key_change callback is set */- ptls_aead_context_t *aead;- uint64_t seq;- unsigned tls12 : 1;- uint64_t tls12_enc_record_iv;-};--struct st_ptls_record_message_emitter_t {- ptls_message_emitter_t super;- size_t rec_start;-};--struct st_ptls_signature_algorithms_t {- uint16_t list[16]; /* expand? */- size_t count;-};--struct st_ptls_certificate_request_t {- /**- * context.base becomes non-NULL when a CertificateRequest is pending for processing- */- ptls_iovec_t context;- struct st_ptls_signature_algorithms_t signature_algorithms;-};--struct st_decoded_ech_config_t {- uint8_t id;- ptls_hpke_kem_t *kem;- ptls_iovec_t public_key;- ptls_hpke_cipher_suite_t *cipher;- uint8_t max_name_length;- ptls_iovec_t public_name;- ptls_iovec_t bytes;-};--/**- * Properties for ECH. Iff ECH is used and not rejected, `aead` is non-NULL.- */-struct st_ptls_ech_t {- uint8_t offered : 1;- uint8_t offered_grease : 1;- uint8_t accepted : 1;- uint8_t config_id;- ptls_hpke_kem_t *kem;- ptls_hpke_cipher_suite_t *cipher;- ptls_aead_context_t *aead;- uint8_t inner_client_random[PTLS_HELLO_RANDOM_SIZE];- struct {- ptls_iovec_t enc;- uint8_t max_name_length;- char *public_name;- /**- * retains a copy of entire ECH extension so that it can be replayed in the 2nd CH when ECH is rejected via HRR- */- ptls_iovec_t first_ech;- } client;-};--struct st_ptls_t {- /**- * the context- */- ptls_context_t *ctx;- /**- * the state- */- enum en_ptls_state_t {- PTLS_STATE_CLIENT_HANDSHAKE_START,- PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO,- PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO,- PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS,- PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE,- PTLS_STATE_CLIENT_EXPECT_CERTIFICATE,- PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY,- PTLS_STATE_CLIENT_EXPECT_FINISHED,- PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO,- PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO,- PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY,- PTLS_STATE_SERVER_EXPECT_CERTIFICATE,- PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY,- /* ptls_send can be called if the state is below here */- PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA,- PTLS_STATE_SERVER_EXPECT_FINISHED,- PTLS_STATE_POST_HANDSHAKE_MIN,- PTLS_STATE_CLIENT_POST_HANDSHAKE = PTLS_STATE_POST_HANDSHAKE_MIN,- PTLS_STATE_SERVER_POST_HANDSHAKE- } state;- /**- * receive buffers- */- struct {- ptls_buffer_t rec;- ptls_buffer_t mess;- } recvbuf;- /**- * key schedule- */- ptls_key_schedule_t *key_schedule;- /**- * values used for record protection- */- struct {- struct st_ptls_traffic_protection_t dec;- struct st_ptls_traffic_protection_t enc;- } traffic_protection;- /**- * server-name passed using SNI- */- char *server_name;- /**- * result of ALPN- */- char *negotiated_protocol;- /**- * selected key-exchange- */- ptls_key_exchange_algorithm_t *key_share;- /**- * selected cipher-suite- */- ptls_cipher_suite_t *cipher_suite;- /**- * ClientHello.random that appears on the wire. When ECH is used, that of inner CH is retained separately.- */- uint8_t client_random[PTLS_HELLO_RANDOM_SIZE];- /**- * exporter master secret (either 0rtt or 1rtt)- */- struct {- uint8_t *early;- uint8_t *one_rtt;- } exporter_master_secret;- /**- * ECH- */- struct st_ptls_ech_t ech;- /* flags */- unsigned is_server : 1;- unsigned is_psk_handshake : 1;- unsigned send_change_cipher_spec : 1;- unsigned needs_key_update : 1;- unsigned key_update_send_request : 1;- unsigned skip_tracing : 1;- /**- * misc.- */- union {- struct {- ptls_iovec_t legacy_session_id;- uint8_t legacy_session_id_buf[32];- ptls_key_exchange_context_t *key_share_ctx;- unsigned offered_psk : 1;- /**- * if 1-RTT write key is active- */- unsigned using_early_data : 1;- struct st_ptls_certificate_request_t certificate_request;- } client;- struct {- uint8_t pending_traffic_secret[PTLS_MAX_DIGEST_SIZE];- uint32_t early_data_skipped_bytes; /* if not UINT32_MAX, the server is skipping early data */- unsigned can_send_session_ticket : 1;- ptls_async_job_t *async_job;- } server;- };- /**- * certificate verify; will be used by the client and the server (if require_client_authentication is set)- */- struct {- int (*cb)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t signature);- void *verify_ctx;- } certificate_verify;- /**- * handshake traffic secret to be commisioned (an array of `uint8_t [PTLS_MAX_DIGEST_SIZE]` or NULL)- */- uint8_t *pending_handshake_secret;- /**- * user data- */- void *data_ptr;-};--struct st_ptls_record_t {- uint8_t type;- uint16_t version;- size_t length;- const uint8_t *fragment;-};--struct st_ptls_client_hello_psk_t {- ptls_iovec_t identity;- uint32_t obfuscated_ticket_age;- ptls_iovec_t binder;-};--#define MAX_UNKNOWN_EXTENSIONS 16-#define MAX_CERTIFICATE_TYPES 8--struct st_ptls_client_hello_t {- uint16_t legacy_version;- const uint8_t *random_bytes;- ptls_iovec_t legacy_session_id;- struct {- const uint8_t *ids;- size_t count;- } compression_methods;- uint16_t selected_version;- ptls_iovec_t cipher_suites;- ptls_iovec_t negotiated_groups;- ptls_iovec_t key_shares;- struct st_ptls_signature_algorithms_t signature_algorithms;- ptls_iovec_t server_name;- struct {- ptls_iovec_t list[16];- size_t count;- } alpn;- struct {- uint16_t list[16];- size_t count;- } cert_compression_algos;- struct {- ptls_iovec_t all;- ptls_iovec_t tbs;- ptls_iovec_t ch1_hash;- ptls_iovec_t signature;- unsigned sent_key_share : 1;- } cookie;- struct {- uint8_t list[MAX_CERTIFICATE_TYPES];- size_t count;- } server_certificate_types;- unsigned status_request : 1;- /**- * ECH: payload.base != NULL indicates that the extension was received- */- struct {- uint8_t type;- uint8_t config_id;- ptls_hpke_cipher_suite_id_t cipher_suite;- ptls_iovec_t enc;- ptls_iovec_t payload;- } ech;- struct {- const uint8_t *hash_end;- struct {- struct st_ptls_client_hello_psk_t list[4];- size_t count;- } identities;- unsigned ke_modes;- unsigned early_data_indication : 1;- unsigned is_last_extension : 1;- } psk;- ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];- size_t first_extension_at;-};--struct st_ptls_server_hello_t {- uint8_t random_[PTLS_HELLO_RANDOM_SIZE];- ptls_iovec_t legacy_session_id;- int is_retry_request;- union {- ptls_iovec_t peerkey;- struct {- uint16_t selected_group;- ptls_iovec_t cookie;- const uint8_t *ech;- } retry_request;- };-};--struct st_ptls_key_schedule_t {- unsigned generation; /* early secret (1), hanshake secret (2), master secret (3) */- uint8_t secret[PTLS_MAX_DIGEST_SIZE];- size_t num_hashes;- struct {- ptls_hash_algorithm_t *algo;- ptls_hash_context_t *ctx, *ctx_outer;- } hashes[1];-};--struct st_ptls_extension_decoder_t {- uint16_t type;- int (*cb)(ptls_t *tls, void *arg, const uint8_t *src, const uint8_t *const end);-};--struct st_ptls_extension_bitmap_t {- uint64_t bits;-};--static const uint8_t zeroes_of_max_digest_size[PTLS_MAX_DIGEST_SIZE] = {0};--static ptls_aead_context_t *new_aead(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret,- ptls_iovec_t hash_value, const char *label_prefix);-static int server_finish_handshake(ptls_t *tls, ptls_message_emitter_t *emitter, int send_cert_verify,- struct st_ptls_signature_algorithms_t *signature_algorithms);--static int is_supported_version(uint16_t v)-{- size_t i;- for (i = 0; i != PTLS_ELEMENTSOF(supported_versions); ++i)- if (supported_versions[i] == v)- return 1;- return 0;-}--static int extension_bitmap_testandset(struct st_ptls_extension_bitmap_t *bitmap, int hstype, uint16_t extid)-{-#define HSTYPE_TO_BIT(hstype) ((uint64_t)1 << ((hstype) + 1)) /* min(hstype) is -1 (PSEUDO_HRR) */-#define DEFINE_BIT(abbrev, hstype) static const uint64_t abbrev = HSTYPE_TO_BIT(PTLS_HANDSHAKE_TYPE_##hstype)-#define EXT(candext, allowed_bits) \- do { \- if (PTLS_UNLIKELY(extid == PTLS_EXTENSION_TYPE_##candext)) { \- allowed_hs_bits = allowed_bits; \- goto Found; \- } \- ext_bitmap_mask <<= 1; \- } while (0)-- DEFINE_BIT(CH, CLIENT_HELLO);- DEFINE_BIT(SH, SERVER_HELLO);- DEFINE_BIT(HRR, PSEUDO_HRR);- DEFINE_BIT(EE, ENCRYPTED_EXTENSIONS);- DEFINE_BIT(CR, CERTIFICATE_REQUEST);- DEFINE_BIT(CT, CERTIFICATE);- DEFINE_BIT(NST, NEW_SESSION_TICKET);-- uint64_t allowed_hs_bits, ext_bitmap_mask = 1;-- /* clang-format off */- /* RFC 8446 section 4.2: "The table below indicates the messages where a given extension may appear... If an implementation- * receives an extension which it recognizes and which is not specified for the message in which it appears, it MUST abort the- * handshake with an "illegal_parameter" alert.- *- * +-------------------------+---------------+- * + Extension | Allowed |- * +-------------------------+---------------+ */- EXT( SERVER_NAME , CH + EE );- EXT( STATUS_REQUEST , CH + CR + CT );- EXT( SUPPORTED_GROUPS , CH + EE );- EXT( SIGNATURE_ALGORITHMS , CH + CR );- EXT( ALPN , CH + EE );- EXT( SERVER_CERTIFICATE_TYPE , CH + EE );- EXT( KEY_SHARE , CH + SH + HRR );- EXT( PRE_SHARED_KEY , CH + SH );- EXT( PSK_KEY_EXCHANGE_MODES , CH );- EXT( EARLY_DATA , CH + EE + NST );- EXT( COOKIE , CH + HRR );- EXT( SUPPORTED_VERSIONS , CH + SH + HRR );- EXT( COMPRESS_CERTIFICATE , CH + CR ); /* from RFC 8879 */- EXT( ENCRYPTED_CLIENT_HELLO , CH + HRR + EE ); /* from draft-ietf-tls-esni-15 */- EXT( ECH_OUTER_EXTENSIONS , 0 );- /* +-----------------------------------------+ */- /* clang-format on */-- return 1;--Found:- if ((allowed_hs_bits & HSTYPE_TO_BIT(hstype)) == 0)- return 0;- if ((bitmap->bits & ext_bitmap_mask) != 0)- return 0;- bitmap->bits |= ext_bitmap_mask;- return 1;--#undef HSTYPE_TO_BIT-#undef DEFINE_ABBREV-#undef EXT-}--#ifndef ntoh16-static uint16_t ntoh16(const uint8_t *src)-{- return (uint16_t)src[0] << 8 | src[1];-}-#endif--#ifndef ntoh24-static uint32_t ntoh24(const uint8_t *src)-{- return (uint32_t)src[0] << 16 | (uint32_t)src[1] << 8 | src[2];-}-#endif--#ifndef ntoh32-static uint32_t ntoh32(const uint8_t *src)-{- return (uint32_t)src[0] << 24 | (uint32_t)src[1] << 16 | (uint32_t)src[2] << 8 | src[3];-}-#endif--#ifndef ntoh64-static uint64_t ntoh64(const uint8_t *src)-{- return (uint64_t)src[0] << 56 | (uint64_t)src[1] << 48 | (uint64_t)src[2] << 40 | (uint64_t)src[3] << 32 |- (uint64_t)src[4] << 24 | (uint64_t)src[5] << 16 | (uint64_t)src[6] << 8 | src[7];-}-#endif--static void encode64(uint8_t *dst, uint64_t v)-{- for (size_t i = 0; i < 8; ++i)- dst[i] = (uint8_t)(v >> (56 - 8 * i));-}--static char *duplicate_as_str(const void *src, size_t len)-{- char *dst;-- if ((dst = malloc(len + 1)) == NULL)- return NULL;- memcpy(dst, src, len);- dst[len] = '\0';- return dst;-}--void ptls_buffer__release_memory(ptls_buffer_t *buf)-{- ptls_clear_memory(buf->base, buf->off);- if (buf->is_allocated) {-#ifdef _WINDOWS- if (buf->align_bits != 0) {- _aligned_free(buf->base);- } else {- free(buf->base);- }-#else- free(buf->base);-#endif- }-}--int ptls_buffer_reserve(ptls_buffer_t *buf, size_t delta)-{- return ptls_buffer_reserve_aligned(buf, delta, 0);-}--int ptls_buffer_reserve_aligned(ptls_buffer_t *buf, size_t delta, uint8_t align_bits)-{- if (buf->base == NULL)- return PTLS_ERROR_NO_MEMORY;-- if (PTLS_MEMORY_DEBUG || buf->capacity < buf->off + delta ||- (buf->align_bits < align_bits && ((uintptr_t)buf->base & (((uintptr_t)1 << align_bits) - 1)) != 0)) {- void *newp;- size_t new_capacity = buf->capacity;- if (new_capacity < 1024)- new_capacity = 1024;- while (new_capacity < buf->off + delta) {- new_capacity *= 2;- }- if (align_bits != 0) {-#ifdef _WINDOWS- if ((newp = _aligned_malloc(new_capacity, (size_t)1 << align_bits)) == NULL)- return PTLS_ERROR_NO_MEMORY;-#else- if (posix_memalign(&newp, 1 << align_bits, new_capacity) != 0)- return PTLS_ERROR_NO_MEMORY;-#endif- } else {- if ((newp = malloc(new_capacity)) == NULL)- return PTLS_ERROR_NO_MEMORY;- }- memcpy(newp, buf->base, buf->off);- ptls_buffer__release_memory(buf);- buf->base = newp;- buf->capacity = new_capacity;- buf->is_allocated = 1;- buf->align_bits = align_bits;- }-- return 0;-}--int ptls_buffer__do_pushv(ptls_buffer_t *buf, const void *src, size_t len)-{- int ret;-- if (len == 0)- return 0;- if ((ret = ptls_buffer_reserve(buf, len)) != 0)- return ret;- memcpy(buf->base + buf->off, src, len);- buf->off += len;- return 0;-}--int ptls_buffer__adjust_quic_blocksize(ptls_buffer_t *buf, size_t body_size)-{- uint8_t sizebuf[PTLS_ENCODE_QUICINT_CAPACITY];- size_t sizelen = ptls_encode_quicint(sizebuf, body_size) - sizebuf;-- /* adjust amount of space before body_size to `sizelen` bytes */- if (sizelen != 1) {- int ret;- if ((ret = ptls_buffer_reserve(buf, sizelen - 1)) != 0)- return ret;- memmove(buf->base + buf->off - body_size - 1 + sizelen, buf->base + buf->off - body_size, body_size);- buf->off += sizelen - 1;- }-- /* write the size */- memcpy(buf->base + buf->off - body_size - sizelen, sizebuf, sizelen);-- return 0;-}--int ptls_buffer__adjust_asn1_blocksize(ptls_buffer_t *buf, size_t body_size)-{- fprintf(stderr, "unimplemented\n");- abort();-}--int ptls_buffer_push_asn1_ubigint(ptls_buffer_t *buf, const void *bignum, size_t size)-{- const uint8_t *p = bignum, *const end = p + size;- int ret;-- /* skip zeroes */- for (; end - p >= 1; ++p)- if (*p != 0)- break;-- /* emit */- ptls_buffer_push(buf, 2);- ptls_buffer_push_asn1_block(buf, {- if (*p >= 0x80)- ptls_buffer_push(buf, 0);- if (p != end) {- ptls_buffer_pushv(buf, p, end - p);- } else {- ptls_buffer_pushv(buf, "", 1);- }- });- ret = 0;--Exit:- return ret;-}--#if PTLS_FUZZ_HANDSHAKE--static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen,- uint8_t content_type)-{- memcpy(output, input, inlen);- memcpy(output + inlen, &content_type, 1);- return inlen + 1 + 16;-}--static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen)-{- if (inlen < 16) {- return PTLS_ALERT_BAD_RECORD_MAC;- }- memcpy(output, input, inlen - 16);- *outlen = inlen - 16; /* removing the 16 bytes of tag */- return 0;-}--#else--static void build_aad(uint8_t aad[5], size_t reclen)-{- aad[0] = PTLS_CONTENT_TYPE_APPDATA;- aad[1] = PTLS_RECORD_VERSION_MAJOR;- aad[2] = PTLS_RECORD_VERSION_MINOR;- aad[3] = (uint8_t)(reclen >> 8);- aad[4] = (uint8_t)reclen;-}--static size_t aead_encrypt(struct st_ptls_traffic_protection_t *ctx, void *output, const void *input, size_t inlen,- uint8_t content_type)-{- ptls_iovec_t invec[2] = {ptls_iovec_init(input, inlen), ptls_iovec_init(&content_type, 1)};- uint8_t aad[5];-- build_aad(aad, inlen + 1 + ctx->aead->algo->tag_size);- ptls_aead_encrypt_v(ctx->aead, output, invec, PTLS_ELEMENTSOF(invec), ctx->seq++, aad, sizeof(aad));-- return inlen + 1 + ctx->aead->algo->tag_size;-}--static int aead_decrypt(struct st_ptls_traffic_protection_t *ctx, void *output, size_t *outlen, const void *input, size_t inlen)-{- uint8_t aad[5];-- build_aad(aad, inlen);- if ((*outlen = ptls_aead_decrypt(ctx->aead, output, input, inlen, ctx->seq, aad, sizeof(aad))) == SIZE_MAX)- return PTLS_ALERT_BAD_RECORD_MAC;- ++ctx->seq;- return 0;-}--#endif /* #if PTLS_FUZZ_HANDSHAKE */--static void build_tls12_aad(uint8_t *aad, uint8_t type, uint64_t seq, uint16_t length)-{- for (size_t i = 0; i < 8; ++i)- aad[i] = (uint8_t)(seq >> (56 - i * 8));- aad[8] = type;- aad[9] = PTLS_RECORD_VERSION_MAJOR;- aad[10] = PTLS_RECORD_VERSION_MINOR;- aad[11] = length >> 8;- aad[12] = (uint8_t)length;-}--#define buffer_push_record(buf, type, block) \- do { \- ptls_buffer_push((buf), (type), PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR); \- ptls_buffer_push_block((buf), 2, block); \- } while (0)--static int buffer_push_encrypted_records(ptls_buffer_t *buf, uint8_t type, const uint8_t *src, size_t len,- struct st_ptls_traffic_protection_t *enc)-{- int ret = 0;-- while (len != 0) {- size_t chunk_size = len;- if (chunk_size > PTLS_MAX_PLAINTEXT_RECORD_SIZE)- chunk_size = PTLS_MAX_PLAINTEXT_RECORD_SIZE;- if (enc->tls12) {- buffer_push_record(buf, type, {- /* reserve memory */- if ((ret = ptls_buffer_reserve_aligned(- buf, enc->aead->algo->tls12.record_iv_size + chunk_size + enc->aead->algo->tag_size,- enc->aead->algo->align_bits)) != 0)- goto Exit;- /* determine nonce, as well as prepending that walue as the record IV (AES-GCM) */- uint64_t nonce;- if (enc->aead->algo->tls12.record_iv_size != 0) {- assert(enc->aead->algo->tls12.record_iv_size == 8);- nonce = enc->tls12_enc_record_iv++;- encode64(buf->base + buf->off, nonce);- buf->off += 8;- } else {- nonce = enc->seq;- }- /* build AAD */- uint8_t aad[PTLS_TLS12_AAD_SIZE];- build_tls12_aad(aad, type, enc->seq, (uint16_t)chunk_size);- /* encrypt */- buf->off += ptls_aead_encrypt(enc->aead, buf->base + buf->off, src, chunk_size, nonce, aad, sizeof(aad));- ++enc->seq;- });- } else {- buffer_push_record(buf, PTLS_CONTENT_TYPE_APPDATA, {- if ((ret = ptls_buffer_reserve_aligned(buf, chunk_size + enc->aead->algo->tag_size + 1,- enc->aead->algo->align_bits)) != 0)- goto Exit;- buf->off += aead_encrypt(enc, buf->base + buf->off, src, chunk_size, type);- });- }- src += chunk_size;- len -= chunk_size;- }--Exit:- return ret;-}--static int buffer_encrypt_record(ptls_buffer_t *buf, size_t rec_start, struct st_ptls_traffic_protection_t *enc)-{- size_t bodylen = buf->off - rec_start - 5;- uint8_t *tmpbuf, type = buf->base[rec_start];- int ret;-- /* Fast path: do in-place encryption if only one record needs to be emitted. (For simplicity, do not take this path if TLS 1.2- * is used, as this function will be called no more than once per connection, for encrypting an alert.) */- if (!enc->tls12 && bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) {- size_t overhead = 1 + enc->aead->algo->tag_size;- if ((ret = ptls_buffer_reserve_aligned(buf, overhead, enc->aead->algo->align_bits)) != 0)- return ret;- size_t encrypted_len = aead_encrypt(enc, buf->base + rec_start + 5, buf->base + rec_start + 5, bodylen, type);- assert(encrypted_len == bodylen + overhead);- buf->off += overhead;- buf->base[rec_start] = PTLS_CONTENT_TYPE_APPDATA;- buf->base[rec_start + 3] = (encrypted_len >> 8) & 0xff;- buf->base[rec_start + 4] = encrypted_len & 0xff;- return 0;- }-- /* move plaintext to temporary buffer */- if ((tmpbuf = malloc(bodylen)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- memcpy(tmpbuf, buf->base + rec_start + 5, bodylen);- ptls_clear_memory(buf->base + rec_start, bodylen + 5);- buf->off = rec_start;-- /* push encrypted records */- ret = buffer_push_encrypted_records(buf, type, tmpbuf, bodylen, enc);--Exit:- if (tmpbuf != NULL) {- ptls_clear_memory(tmpbuf, bodylen);- free(tmpbuf);- }- return ret;-}--static int begin_record_message(ptls_message_emitter_t *_self)-{- struct st_ptls_record_message_emitter_t *self = (void *)_self;- int ret;-- self->rec_start = self->super.buf->off;- ptls_buffer_push(self->super.buf, PTLS_CONTENT_TYPE_HANDSHAKE, PTLS_RECORD_VERSION_MAJOR, PTLS_RECORD_VERSION_MINOR, 0, 0);- ret = 0;-Exit:- return ret;-}--static int commit_record_message(ptls_message_emitter_t *_self)-{- struct st_ptls_record_message_emitter_t *self = (void *)_self;- int ret;-- if (self->super.enc->aead != NULL) {- ret = buffer_encrypt_record(self->super.buf, self->rec_start, self->super.enc);- } else {- /* TODO allow CH,SH,HRR above 16KB */- size_t sz = self->super.buf->off - self->rec_start - 5;- assert(sz <= PTLS_MAX_PLAINTEXT_RECORD_SIZE);- self->super.buf->base[self->rec_start + 3] = (uint8_t)(sz >> 8);- self->super.buf->base[self->rec_start + 4] = (uint8_t)(sz);- ret = 0;- }-- return ret;-}--#define buffer_push_extension(buf, type, block) \- do { \- ptls_buffer_push16((buf), (type)); \- ptls_buffer_push_block((buf), 2, block); \- } while (0);--#define decode_open_extensions(src, end, hstype, exttype, block) \- do { \- struct st_ptls_extension_bitmap_t bitmap = {0}; \- ptls_decode_open_block((src), end, 2, { \- while ((src) != end) { \- if ((ret = ptls_decode16((exttype), &(src), end)) != 0) \- goto Exit; \- if (!extension_bitmap_testandset(&bitmap, (hstype), *(exttype))) { \- ret = PTLS_ALERT_ILLEGAL_PARAMETER; \- goto Exit; \- } \- ptls_decode_open_block((src), end, 2, block); \- } \- }); \- } while (0)--#define decode_extensions(src, end, hstype, exttype, block) \- do { \- decode_open_extensions((src), end, hstype, exttype, block); \- ptls_decode_assert_block_close((src), end); \- } while (0)--int ptls_decode8(uint8_t *value, const uint8_t **src, const uint8_t *end)-{- if (*src == end)- return PTLS_ALERT_DECODE_ERROR;- *value = *(*src)++;- return 0;-}--int ptls_decode16(uint16_t *value, const uint8_t **src, const uint8_t *end)-{- if (end - *src < 2)- return PTLS_ALERT_DECODE_ERROR;- *value = ntoh16(*src);- *src += 2;- return 0;-}--int ptls_decode24(uint32_t *value, const uint8_t **src, const uint8_t *end)-{- if (end - *src < 3)- return PTLS_ALERT_DECODE_ERROR;- *value = ((uint32_t)(*src)[0] << 16) | ((uint32_t)(*src)[1] << 8) | (*src)[2];- *src += 3;- return 0;-}--int ptls_decode32(uint32_t *value, const uint8_t **src, const uint8_t *end)-{- if (end - *src < 4)- return PTLS_ALERT_DECODE_ERROR;- *value = ntoh32(*src);- *src += 4;- return 0;-}--int ptls_decode64(uint64_t *value, const uint8_t **src, const uint8_t *end)-{- if (end - *src < 8)- return PTLS_ALERT_DECODE_ERROR;- *value = ntoh64(*src);- *src += 8;- return 0;-}--uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end)-{- if (PTLS_UNLIKELY(*src == end))- return UINT64_MAX;-- uint8_t b = *(*src)++;-- if (PTLS_LIKELY(b <= 0x3f))- return b;-- uint64_t v = b & 0x3f;- unsigned bytes_left = (1 << (b >> 6)) - 1;- if (PTLS_UNLIKELY((size_t)(end - *src) < bytes_left))- return UINT64_MAX;- do {- v = (v << 8) | *(*src)++;- } while (--bytes_left != 0);- return v;-}--static void log_secret(ptls_t *tls, const char *type, ptls_iovec_t secret)-{- char hexbuf[PTLS_MAX_DIGEST_SIZE * 2 + 1];-- PTLS_PROBE(NEW_SECRET, tls, type, ptls_hexdump(hexbuf, secret.base, secret.len));- PTLS_LOG_CONN(new_secret, tls, { PTLS_LOG_ELEMENT_SAFESTR(label, type); });-- if (tls->ctx->log_event != NULL)- tls->ctx->log_event->cb(tls->ctx->log_event, tls, type, "%s", ptls_hexdump(hexbuf, secret.base, secret.len));-}--/**- * This function preserves the flags and modes (e.g., `offered`, `accepted`, `cipher`), they can be used afterwards.- */-static void clear_ech(struct st_ptls_ech_t *ech, int is_server)-{- if (ech->aead != NULL) {- ptls_aead_free(ech->aead);- ech->aead = NULL;- }- ptls_clear_memory(ech->inner_client_random, PTLS_HELLO_RANDOM_SIZE);- if (!is_server) {- free(ech->client.enc.base);- ech->client.enc = ptls_iovec_init(NULL, 0);- if (ech->client.public_name != NULL) {- free(ech->client.public_name);- ech->client.public_name = NULL;- }- free(ech->client.first_ech.base);- ech->client.first_ech = ptls_iovec_init(NULL, 0);- }-}--/**- * Decodes one ECHConfigContents (tls-esni-15 section 4). `decoded->kem` and `cipher` may be NULL even when the function returns- * zero, if the corresponding entries are not found.- */-static int decode_one_ech_config(ptls_hpke_kem_t **kems, ptls_hpke_cipher_suite_t **ciphers,- struct st_decoded_ech_config_t *decoded, const uint8_t **src, const uint8_t *const end)-{- char *public_name_buf = NULL;- int ret;-- *decoded = (struct st_decoded_ech_config_t){0};-- if ((ret = ptls_decode8(&decoded->id, src, end)) != 0)- goto Exit;- uint16_t kem_id;- if ((ret = ptls_decode16(&kem_id, src, end)) != 0)- goto Exit;- for (size_t i = 0; kems[i] != NULL; ++i) {- if (kems[i]->id == kem_id) {- decoded->kem = kems[i];- break;- }- }- ptls_decode_open_block(*src, end, 2, {- if (*src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- decoded->public_key = ptls_iovec_init(*src, end - *src);- *src = end;- });- ptls_decode_open_block(*src, end, 2, {- do {- uint16_t kdf_id;- uint16_t aead_id;- if ((ret = ptls_decode16(&kdf_id, src, end)) != 0)- goto Exit;- if ((ret = ptls_decode16(&aead_id, src, end)) != 0)- goto Exit;- if (decoded->cipher == NULL) {- for (size_t i = 0; ciphers[i] != NULL; ++i) {- if (ciphers[i]->id.kdf == kdf_id && ciphers[i]->id.aead == aead_id) {- decoded->cipher = ciphers[i];- break;- }- }- }- } while (*src != end);- });- if ((ret = ptls_decode8(&decoded->max_name_length, src, end)) != 0)- goto Exit;--#define SKIP_DECODED() \- do { \- decoded->kem = NULL; \- decoded->cipher = NULL; \- } while (0)-- /* Decode public_name. The specification requires clients to ignore (upon parsing ESNIConfigList) or reject (upon handshake)- * public names that are not DNS names or IPv4 addresses. We ignore IPv4 and v6 addresses during parsing (IPv6 addresses never- * looks like DNS names), and delegate the responsibility of rejecting non-DNS names to the certificate verify callback. */- ptls_decode_open_block(*src, end, 1, {- if (*src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if ((public_name_buf = duplicate_as_str(*src, end - *src)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- if (ptls_server_name_is_ipaddr(public_name_buf)) {- SKIP_DECODED();- } else {- decoded->public_name = ptls_iovec_init(*src, end - *src);- }- *src = end;- });-- ptls_decode_block(*src, end, 2, {- while (*src < end) {- uint16_t type;- if ((ret = ptls_decode16(&type, src, end)) != 0)- goto Exit;- ptls_decode_open_block(*src, end, 2, { *src = end; });- /* if a critital extension is found, indicate that the config cannot be used */- if ((type & 0x8000) != 0)- SKIP_DECODED();- }- });--#undef SKIP_DECODED--Exit:- free(public_name_buf);- return ret;-}--static int client_decode_ech_config_list(ptls_context_t *ctx, struct st_decoded_ech_config_t *decoded, ptls_iovec_t config_list)-{- const uint8_t *src = config_list.base, *const end = src + config_list.len;- int match_found = 0, ret;-- *decoded = (struct st_decoded_ech_config_t){0};-- ptls_decode_block(src, end, 2, {- do {- const uint8_t *config_start = src;- uint16_t version;- if ((ret = ptls_decode16(&version, &src, end)) != 0)- goto Exit;- ptls_decode_open_block(src, end, 2, {- /* If the block is the one that we recognize, parse it, then adopt if if possible. Otherwise, skip. */- if (version == PTLS_ECH_CONFIG_VERSION) {- struct st_decoded_ech_config_t thisconf;- if ((ret = decode_one_ech_config(ctx->ech.client.kems, ctx->ech.client.ciphers, &thisconf, &src, end)) != 0)- goto Exit;- if (!match_found && thisconf.kem != NULL && thisconf.cipher != NULL) {- *decoded = thisconf;- decoded->bytes = ptls_iovec_init(config_start, end - config_start);- match_found = 1;- }- } else {- src = end;- }- });- } while (src != end);- });- ret = 0;--Exit:- if (ret != 0)- *decoded = (struct st_decoded_ech_config_t){0};- return ret;-}--static int client_setup_ech(struct st_ptls_ech_t *ech, struct st_decoded_ech_config_t *decoded,- void (*random_bytes)(void *, size_t))-{- ptls_buffer_t infobuf;- uint8_t infobuf_smallbuf[256];- int ret;-- /* setup `enc` and `aead` by running HPKE */- ptls_buffer_init(&infobuf, infobuf_smallbuf, sizeof(infobuf_smallbuf));- ptls_buffer_pushv(&infobuf, ech_info_prefix, sizeof(ech_info_prefix));- ptls_buffer_pushv(&infobuf, decoded->bytes.base, decoded->bytes.len);-/*- if ((ret = ptls_hpke_setup_base_s(decoded->kem, decoded->cipher, &ech->client.enc, &ech->aead, decoded->public_key,- ptls_iovec_init(infobuf.base, infobuf.off))) != 0)- goto Exit;-*/- /* setup the rest */- ech->config_id = decoded->id;- ech->kem = decoded->kem;- ech->cipher = decoded->cipher;- random_bytes(ech->inner_client_random, PTLS_HELLO_RANDOM_SIZE);- ech->client.max_name_length = decoded->max_name_length;- if ((ech->client.public_name = duplicate_as_str(decoded->public_name.base, decoded->public_name.len)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }--Exit:- if (ret != 0)- clear_ech(ech, 0);- return ret;-}--static void client_setup_ech_grease(struct st_ptls_ech_t *ech, void (*random_bytes)(void *, size_t), ptls_hpke_kem_t **kems,- ptls_hpke_cipher_suite_t **ciphers, const char *sni_name)-{- static const size_t x25519_key_size = 32;- uint8_t random_secret[PTLS_AES128_KEY_SIZE + PTLS_AES_IV_SIZE];-- /* pick up X25519, AES-128-GCM or bail out */- for (size_t i = 0; kems[i] != NULL; ++i) {- if (kems[i]->id == PTLS_HPKE_KEM_X25519_SHA256) {- ech->kem = kems[i];- break;- }- }- for (size_t i = 0; ciphers[i] != NULL; ++i) {- if (ciphers[i]->id.kdf == PTLS_HPKE_HKDF_SHA256 && ciphers[i]->id.aead == PTLS_HPKE_AEAD_AES_128_GCM) {- ech->cipher = ciphers[i];- break;- }- }- if (ech->kem == NULL || ech->cipher == NULL)- goto Fail;-- /* aead is generated from random */- random_bytes(random_secret, sizeof(random_secret));- ech->aead = ptls_aead_new_direct(ech->cipher->aead, 1, random_secret, random_secret + PTLS_AES128_KEY_SIZE);-- /* `enc` is random bytes */- if ((ech->client.enc.base = malloc(x25519_key_size)) == NULL)- goto Fail;- ech->client.enc.len = x25519_key_size;- random_bytes(ech->client.enc.base, ech->client.enc.len);-- /* setup the rest (inner_client_random is left zeros) */- random_bytes(&ech->config_id, sizeof(ech->config_id));- ech->client.max_name_length = 64;- if ((ech->client.public_name = duplicate_as_str(sni_name, strlen(sni_name))) == NULL)- goto Fail;-- return;--Fail:- clear_ech(ech, 0);-}--#define ECH_CONFIRMATION_SERVER_HELLO "ech accept confirmation"-#define ECH_CONFIRMATION_HRR "hrr ech accept confirmation"-static int ech_calc_confirmation(ptls_key_schedule_t *sched, void *dst, const uint8_t *inner_random, const char *label,- ptls_iovec_t message)-{- ptls_hash_context_t *hash = NULL;- uint8_t secret[PTLS_MAX_DIGEST_SIZE], transcript_hash[PTLS_MAX_DIGEST_SIZE];- int ret;-- /* calc transcript hash using the modified ServerHello / HRR */- if ((hash = sched->hashes[0].ctx->clone_(sched->hashes[0].ctx)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- hash->update(hash, message.base, message.len);- hash->final(hash, transcript_hash, PTLS_HASH_FINAL_MODE_FREE);- hash = NULL;-- /* HKDF extract and expand */- if ((ret = ptls_hkdf_extract(sched->hashes[0].algo, secret, ptls_iovec_init(NULL, 0),- ptls_iovec_init(inner_random, PTLS_HELLO_RANDOM_SIZE))) != 0)- goto Exit;- if ((ret = ptls_hkdf_expand_label(sched->hashes[0].algo, dst, 8, ptls_iovec_init(secret, sched->hashes[0].algo->digest_size),- label, ptls_iovec_init(transcript_hash, sched->hashes[0].algo->digest_size), NULL)) != 0)- goto Exit;--Exit:- ptls_clear_memory(secret, sizeof(secret));- ptls_clear_memory(transcript_hash, sizeof(transcript_hash));- if (hash != NULL)- hash->final(hash, NULL, PTLS_HASH_FINAL_MODE_FREE);- return ret;-}--static void key_schedule_free(ptls_key_schedule_t *sched)-{- size_t i;- ptls_clear_memory(sched->secret, sizeof(sched->secret));- for (i = 0; i != sched->num_hashes; ++i) {- sched->hashes[i].ctx->final(sched->hashes[i].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE);- if (sched->hashes[i].ctx_outer != NULL)- sched->hashes[i].ctx_outer->final(sched->hashes[i].ctx_outer, NULL, PTLS_HASH_FINAL_MODE_FREE);- }- free(sched);-}--static ptls_key_schedule_t *key_schedule_new(ptls_cipher_suite_t *preferred, ptls_cipher_suite_t **offered, int use_outer)-{-#define FOREACH_HASH(block) \- do { \- ptls_cipher_suite_t *cs; \- if ((cs = preferred) != NULL) { \- block \- } \- if (offered != NULL) { \- size_t i, j; \- for (i = 0; (cs = offered[i]) != NULL; ++i) { \- if (preferred == NULL || cs->hash != preferred->hash) { \- for (j = 0; j != i; ++j) \- if (cs->hash == offered[j]->hash) \- break; \- if (j == i) { \- block \- } \- } \- } \- } \- } while (0)-- ptls_key_schedule_t *sched;-- { /* allocate */- size_t num_hashes = 0;- FOREACH_HASH({ ++num_hashes; });- if ((sched = malloc(offsetof(ptls_key_schedule_t, hashes) + sizeof(sched->hashes[0]) * num_hashes)) == NULL)- return NULL;- *sched = (ptls_key_schedule_t){0};- }-- /* setup the hash algos and contexts */- FOREACH_HASH({- sched->hashes[sched->num_hashes].algo = cs->hash;- if ((sched->hashes[sched->num_hashes].ctx = cs->hash->create()) == NULL)- goto Fail;- if (use_outer) {- if ((sched->hashes[sched->num_hashes].ctx_outer = cs->hash->create()) == NULL)- goto Fail;- } else {- sched->hashes[sched->num_hashes].ctx_outer = NULL;- }- ++sched->num_hashes;- });-- return sched;-Fail:- key_schedule_free(sched);- return NULL;--#undef FOREACH_HASH-}--static int key_schedule_extract(ptls_key_schedule_t *sched, ptls_iovec_t ikm)-{- int ret;-- if (ikm.base == NULL)- ikm = ptls_iovec_init(zeroes_of_max_digest_size, sched->hashes[0].algo->digest_size);-- if (sched->generation != 0 &&- (ret = ptls_hkdf_expand_label(sched->hashes[0].algo, sched->secret, sched->hashes[0].algo->digest_size,- ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), "derived",- ptls_iovec_init(sched->hashes[0].algo->empty_digest, sched->hashes[0].algo->digest_size),- NULL)) != 0)- return ret;-- ++sched->generation;- ret = ptls_hkdf_extract(sched->hashes[0].algo, sched->secret,- ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), ikm);- PTLS_DEBUGF("%s: %u, %02x%02x\n", __FUNCTION__, sched->generation, (int)sched->secret[0], (int)sched->secret[1]);- return ret;-}--static int key_schedule_select_cipher(ptls_key_schedule_t *sched, ptls_cipher_suite_t *cs, int reset)-{- size_t found_slot = SIZE_MAX, i;- int ret;-- assert(sched->generation == 1);-- /* find the one, while freeing others */- for (i = 0; i != sched->num_hashes; ++i) {- if (sched->hashes[i].algo == cs->hash) {- assert(found_slot == SIZE_MAX);- found_slot = i;- } else {- sched->hashes[i].ctx->final(sched->hashes[i].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE);- if (sched->hashes[i].ctx_outer != NULL)- sched->hashes[i].ctx_outer->final(sched->hashes[i].ctx_outer, NULL, PTLS_HASH_FINAL_MODE_FREE);- }- }- if (found_slot != 0) {- sched->hashes[0] = sched->hashes[found_slot];- reset = 1;- }- sched->num_hashes = 1;-- /* recalculate the hash if a different hash as been selected than the one we used for calculating the early secrets */- if (reset) {- --sched->generation;- memset(sched->secret, 0, sizeof(sched->secret));- if ((ret = key_schedule_extract(sched, ptls_iovec_init(NULL, 0))) != 0)- goto Exit;- }-- ret = 0;-Exit:- return ret;-}--static void key_schedule_select_outer(ptls_key_schedule_t *sched)-{- /* This function is called when receiving a cleartext message (Server Hello), after the cipher-suite is determined (and hence- * the hash also), if ECH was offered */- assert(sched->generation == 1);- assert(sched->num_hashes == 1);- assert(sched->hashes[0].ctx_outer != NULL);-- sched->hashes[0].ctx->final(sched->hashes[0].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE);- sched->hashes[0].ctx = sched->hashes[0].ctx_outer;- sched->hashes[0].ctx_outer = NULL;-}--void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen, int use_outer)-{- size_t i;-- PTLS_DEBUGF("%s:%zu\n", __FUNCTION__, msglen);- for (i = 0; i != sched->num_hashes; ++i) {- ptls_hash_context_t *ctx = use_outer ? sched->hashes[i].ctx_outer : sched->hashes[i].ctx;- ctx->update(ctx, msg, msglen);- }-}--static void key_schedule_update_ch1hash_prefix(ptls_key_schedule_t *sched)-{- uint8_t prefix[4] = {PTLS_HANDSHAKE_TYPE_MESSAGE_HASH, 0, 0, (uint8_t)sched->hashes[0].algo->digest_size};- ptls__key_schedule_update_hash(sched, prefix, sizeof(prefix), 0);-}--static void key_schedule_extract_ch1hash(ptls_key_schedule_t *sched, uint8_t *hash)-{- assert(sched->hashes[0].ctx_outer == NULL);- sched->hashes[0].ctx->final(sched->hashes[0].ctx, hash, PTLS_HASH_FINAL_MODE_RESET);-}--static void key_schedule_transform_post_ch1hash(ptls_key_schedule_t *sched)-{- size_t digest_size = sched->hashes[0].algo->digest_size;- ptls_hash_context_t *hashes[3] = {sched->hashes[0].ctx, sched->hashes[0].ctx_outer, NULL};- uint8_t ch1hash[PTLS_MAX_DIGEST_SIZE];- uint8_t prefix[4] = {PTLS_HANDSHAKE_TYPE_MESSAGE_HASH, 0, 0, (uint8_t)digest_size};-- for (size_t i = 0; hashes[i] != NULL; ++i) {- hashes[i]->final(hashes[i], ch1hash, PTLS_HASH_FINAL_MODE_RESET);- hashes[i]->update(hashes[i], prefix, sizeof(prefix));- hashes[i]->update(hashes[i], ch1hash, digest_size);- }-- ptls_clear_memory(ch1hash, sizeof(ch1hash));-}--static int derive_secret_with_hash(ptls_key_schedule_t *sched, void *secret, const char *label, const uint8_t *hash)-{- int ret = ptls_hkdf_expand_label(sched->hashes[0].algo, secret, sched->hashes[0].algo->digest_size,- ptls_iovec_init(sched->secret, sched->hashes[0].algo->digest_size), label,- ptls_iovec_init(hash, sched->hashes[0].algo->digest_size), NULL);- PTLS_DEBUGF("%s: (label=%s, hash=%02x%02x) => %02x%02x\n", __FUNCTION__, label, hash[0], hash[1], ((uint8_t *)secret)[0],- ((uint8_t *)secret)[1]);- return ret;-}--static int derive_secret(ptls_key_schedule_t *sched, void *secret, const char *label)-{- uint8_t hash_value[PTLS_MAX_DIGEST_SIZE];-- sched->hashes[0].ctx->final(sched->hashes[0].ctx, hash_value, PTLS_HASH_FINAL_MODE_SNAPSHOT);- int ret = derive_secret_with_hash(sched, secret, label, hash_value);- ptls_clear_memory(hash_value, sizeof(hash_value));- return ret;-}--static int derive_secret_with_empty_digest(ptls_key_schedule_t *sched, void *secret, const char *label)-{- return derive_secret_with_hash(sched, secret, label, sched->hashes[0].algo->empty_digest);-}--static int derive_exporter_secret(ptls_t *tls, int is_early)-{- int ret;-- if (!tls->ctx->use_exporter)- return 0;-- uint8_t **slot = is_early ? &tls->exporter_master_secret.early : &tls->exporter_master_secret.one_rtt;- assert(*slot == NULL);- if ((*slot = malloc(tls->key_schedule->hashes[0].algo->digest_size)) == NULL)- return PTLS_ERROR_NO_MEMORY;-- if ((ret = derive_secret(tls->key_schedule, *slot, is_early ? "e exp master" : "exp master")) != 0)- return ret;-- log_secret(tls, is_early ? "EARLY_EXPORTER_SECRET" : "EXPORTER_SECRET",- ptls_iovec_init(*slot, tls->key_schedule->hashes[0].algo->digest_size));-- return 0;-}--static void free_exporter_master_secret(ptls_t *tls, int is_early)-{- uint8_t *slot = is_early ? tls->exporter_master_secret.early : tls->exporter_master_secret.one_rtt;- if (slot == NULL)- return;- assert(tls->key_schedule != NULL);- ptls_clear_memory(slot, tls->key_schedule->hashes[0].algo->digest_size);- free(slot);-}--static int derive_resumption_secret(ptls_key_schedule_t *sched, uint8_t *secret, ptls_iovec_t nonce)-{- int ret;-- if ((ret = derive_secret(sched, secret, "res master")) != 0)- goto Exit;- if ((ret = ptls_hkdf_expand_label(sched->hashes[0].algo, secret, sched->hashes[0].algo->digest_size,- ptls_iovec_init(secret, sched->hashes[0].algo->digest_size), "resumption", nonce, NULL)) != 0)- goto Exit;--Exit:- if (ret != 0)- ptls_clear_memory(secret, sched->hashes[0].algo->digest_size);- return ret;-}--static int decode_new_session_ticket(ptls_t *tls, uint32_t *lifetime, uint32_t *age_add, ptls_iovec_t *nonce, ptls_iovec_t *ticket,- uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end)-{- uint16_t exttype;- int ret;-- if ((ret = ptls_decode32(lifetime, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode32(age_add, &src, end)) != 0)- goto Exit;- ptls_decode_open_block(src, end, 1, {- *nonce = ptls_iovec_init(src, end - src);- src = end;- });- ptls_decode_open_block(src, end, 2, {- if (src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- *ticket = ptls_iovec_init(src, end - src);- src = end;- });-- *max_early_data_size = 0;- decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, &exttype, {- if (tls->ctx->on_extension != NULL &&- (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, exttype,- ptls_iovec_init(src, end - src)) != 0))- goto Exit;- switch (exttype) {- case PTLS_EXTENSION_TYPE_EARLY_DATA:- if ((ret = ptls_decode32(max_early_data_size, &src, end)) != 0)- goto Exit;- break;- default:- src = end;- break;- }- });-- ret = 0;-Exit:- return ret;-}--static int decode_stored_session_ticket(ptls_t *tls, ptls_key_exchange_algorithm_t **key_share, ptls_cipher_suite_t **cs,- ptls_iovec_t *secret, uint32_t *obfuscated_ticket_age, ptls_iovec_t *ticket,- uint32_t *max_early_data_size, const uint8_t *src, const uint8_t *const end)-{- uint16_t kxid, csid;- uint32_t lifetime, age_add;- uint64_t obtained_at, now;- ptls_iovec_t nonce;- int ret;-- /* decode */- if ((ret = ptls_decode64(&obtained_at, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode16(&kxid, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode16(&csid, &src, end)) != 0)- goto Exit;- ptls_decode_open_block(src, end, 3, {- if ((ret = decode_new_session_ticket(tls, &lifetime, &age_add, &nonce, ticket, max_early_data_size, src, end)) != 0)- goto Exit;- src = end;- });- ptls_decode_block(src, end, 2, {- *secret = ptls_iovec_init(src, end - src);- src = end;- });-- { /* determine the key-exchange */- ptls_key_exchange_algorithm_t **cand;- for (cand = tls->ctx->key_exchanges; *cand != NULL; ++cand)- if ((*cand)->id == kxid)- break;- if (*cand == NULL) {- ret = PTLS_ERROR_LIBRARY;- goto Exit;- }- *key_share = *cand;- }-- { /* determine the cipher-suite */- ptls_cipher_suite_t **cand;- for (cand = tls->ctx->cipher_suites; *cand != NULL; ++cand)- if ((*cand)->id == csid)- break;- if (*cand == NULL) {- ret = PTLS_ERROR_LIBRARY;- goto Exit;- }- *cs = *cand;- }-- /* calculate obfuscated_ticket_age */- now = tls->ctx->get_time->cb(tls->ctx->get_time);- if (!(obtained_at <= now && now - obtained_at < 7 * 86400 * 1000)) {- ret = PTLS_ERROR_LIBRARY;- goto Exit;- }- *obfuscated_ticket_age = (uint32_t)(now - obtained_at) + age_add;-- ret = 0;-Exit:- return ret;-}--static int get_traffic_key(ptls_hash_algorithm_t *algo, void *key, size_t key_size, int is_iv, const void *secret,- ptls_iovec_t hash_value, const char *label_prefix)-{- return ptls_hkdf_expand_label(algo, key, key_size, ptls_iovec_init(secret, algo->digest_size), is_iv ? "iv" : "key", hash_value,- label_prefix);-}--static int get_traffic_keys(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, void *key, void *iv, const void *secret,- ptls_iovec_t hash_value, const char *label_prefix)-{- int ret;-- if ((ret = get_traffic_key(hash, key, aead->key_size, 0, secret, hash_value, label_prefix)) != 0 ||- (ret = get_traffic_key(hash, iv, aead->iv_size, 1, secret, hash_value, label_prefix)) != 0) {- ptls_clear_memory(key, aead->key_size);- ptls_clear_memory(iv, aead->iv_size);- }-- return ret;-}--static int setup_traffic_protection(ptls_t *tls, int is_enc, const char *secret_label, size_t epoch, int skip_notify)-{- static const char *log_labels[2][4] = {- {NULL, "CLIENT_EARLY_TRAFFIC_SECRET", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", "CLIENT_TRAFFIC_SECRET_0"},- {NULL, NULL, "SERVER_HANDSHAKE_TRAFFIC_SECRET", "SERVER_TRAFFIC_SECRET_0"}};- struct st_ptls_traffic_protection_t *ctx = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec;-- if (secret_label != NULL) {- int ret;- if ((ret = derive_secret(tls->key_schedule, ctx->secret, secret_label)) != 0)- return ret;- }-- ctx->epoch = epoch;-- log_secret(tls, log_labels[ptls_is_server(tls) == is_enc][epoch],- ptls_iovec_init(ctx->secret, tls->key_schedule->hashes[0].algo->digest_size));-- /* special path for applications having their own record layer */- if (tls->ctx->update_traffic_key != NULL) {- if (skip_notify)- return 0;- return tls->ctx->update_traffic_key->cb(tls->ctx->update_traffic_key, tls, is_enc, epoch, ctx->secret);- }-- if (ctx->aead != NULL)- ptls_aead_free(ctx->aead);- if ((ctx->aead = ptls_aead_new(tls->cipher_suite->aead, tls->cipher_suite->hash, is_enc, ctx->secret,- tls->ctx->hkdf_label_prefix__obsolete)) == NULL)- return PTLS_ERROR_NO_MEMORY; /* TODO obtain error from ptls_aead_new */- ctx->seq = 0;-- PTLS_DEBUGF("[%s] %02x%02x,%02x%02x\n", log_labels[ptls_is_server(tls)][epoch], (unsigned)ctx->secret[0],- (unsigned)ctx->secret[1], (unsigned)ctx->aead->static_iv[0], (unsigned)ctx->aead->static_iv[1]);-- return 0;-}--static int commission_handshake_secret(ptls_t *tls)-{- int is_enc = !ptls_is_server(tls);-- assert(tls->pending_handshake_secret != NULL);- memcpy((is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec)->secret, tls->pending_handshake_secret,- PTLS_MAX_DIGEST_SIZE);- ptls_clear_memory(tls->pending_handshake_secret, PTLS_MAX_DIGEST_SIZE);- free(tls->pending_handshake_secret);- tls->pending_handshake_secret = NULL;-- return setup_traffic_protection(tls, is_enc, NULL, 2, 1);-}--static void log_client_random(ptls_t *tls)-{- char buf[sizeof(tls->client_random) * 2 + 1];-- PTLS_PROBE(CLIENT_RANDOM, tls, ptls_hexdump(buf, tls->client_random, sizeof(tls->client_random)));- PTLS_LOG_CONN(client_random, tls, { PTLS_LOG_ELEMENT_HEXDUMP(bytes, tls->client_random, sizeof(tls->client_random)); });-}--#define SESSION_IDENTIFIER_MAGIC "ptls0001" /* the number should be changed upon incompatible format change */-#define SESSION_IDENTIFIER_MAGIC_SIZE (sizeof(SESSION_IDENTIFIER_MAGIC) - 1)--static int encode_session_identifier(ptls_context_t *ctx, ptls_buffer_t *buf, uint32_t ticket_age_add, ptls_iovec_t ticket_nonce,- ptls_key_schedule_t *sched, const char *server_name, uint16_t key_exchange_id, uint16_t csid,- const char *negotiated_protocol)-{- int ret = 0;-- ptls_buffer_push_block(buf, 2, {- /* format id */- ptls_buffer_pushv(buf, SESSION_IDENTIFIER_MAGIC, SESSION_IDENTIFIER_MAGIC_SIZE);- /* date */- ptls_buffer_push64(buf, ctx->get_time->cb(ctx->get_time));- /* resumption master secret */- ptls_buffer_push_block(buf, 2, {- if ((ret = ptls_buffer_reserve(buf, sched->hashes[0].algo->digest_size)) != 0)- goto Exit;- if ((ret = derive_resumption_secret(sched, buf->base + buf->off, ticket_nonce)) != 0)- goto Exit;- buf->off += sched->hashes[0].algo->digest_size;- });- /* key-exchange */- ptls_buffer_push16(buf, key_exchange_id);- /* cipher-suite */- ptls_buffer_push16(buf, csid);- /* ticket_age_add */- ptls_buffer_push32(buf, ticket_age_add);- /* server-name */- ptls_buffer_push_block(buf, 2, {- if (server_name != NULL)- ptls_buffer_pushv(buf, server_name, strlen(server_name));- });- /* alpn */- ptls_buffer_push_block(buf, 1, {- if (negotiated_protocol != NULL)- ptls_buffer_pushv(buf, negotiated_protocol, strlen(negotiated_protocol));- });- });--Exit:- return ret;-}--int decode_session_identifier(uint64_t *issued_at, ptls_iovec_t *psk, uint32_t *ticket_age_add, ptls_iovec_t *server_name,- uint16_t *key_exchange_id, uint16_t *csid, ptls_iovec_t *negotiated_protocol, const uint8_t *src,- const uint8_t *const end)-{- int ret = 0;-- ptls_decode_block(src, end, 2, {- if (end - src < SESSION_IDENTIFIER_MAGIC_SIZE ||- memcmp(src, SESSION_IDENTIFIER_MAGIC, SESSION_IDENTIFIER_MAGIC_SIZE) != 0) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- src += SESSION_IDENTIFIER_MAGIC_SIZE;- if ((ret = ptls_decode64(issued_at, &src, end)) != 0)- goto Exit;- ptls_decode_open_block(src, end, 2, {- *psk = ptls_iovec_init(src, end - src);- src = end;- });- if ((ret = ptls_decode16(key_exchange_id, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode16(csid, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode32(ticket_age_add, &src, end)) != 0)- goto Exit;- ptls_decode_open_block(src, end, 2, {- *server_name = ptls_iovec_init(src, end - src);- src = end;- });- ptls_decode_open_block(src, end, 1, {- *negotiated_protocol = ptls_iovec_init(src, end - src);- src = end;- });- });--Exit:- return ret;-}--static size_t build_certificate_verify_signdata(uint8_t *data, ptls_key_schedule_t *sched, const char *context_string)-{- size_t datalen = 0;-- memset(data + datalen, 32, 64);- datalen += 64;- memcpy(data + datalen, context_string, strlen(context_string) + 1);- datalen += strlen(context_string) + 1;- sched->hashes[0].ctx->final(sched->hashes[0].ctx, data + datalen, PTLS_HASH_FINAL_MODE_SNAPSHOT);- datalen += sched->hashes[0].algo->digest_size;- assert(datalen <= PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE);-- return datalen;-}--static int calc_verify_data(void *output, ptls_key_schedule_t *sched, const void *secret)-{- ptls_hash_context_t *hmac;- uint8_t digest[PTLS_MAX_DIGEST_SIZE];- int ret;-- if ((ret = ptls_hkdf_expand_label(sched->hashes[0].algo, digest, sched->hashes[0].algo->digest_size,- ptls_iovec_init(secret, sched->hashes[0].algo->digest_size), "finished",- ptls_iovec_init(NULL, 0), NULL)) != 0)- return ret;- if ((hmac = ptls_hmac_create(sched->hashes[0].algo, digest, sched->hashes[0].algo->digest_size)) == NULL) {- ptls_clear_memory(digest, sizeof(digest));- return PTLS_ERROR_NO_MEMORY;- }-- sched->hashes[0].ctx->final(sched->hashes[0].ctx, digest, PTLS_HASH_FINAL_MODE_SNAPSHOT);- PTLS_DEBUGF("%s: %02x%02x,%02x%02x\n", __FUNCTION__, ((uint8_t *)secret)[0], ((uint8_t *)secret)[1], digest[0], digest[1]);- hmac->update(hmac, digest, sched->hashes[0].algo->digest_size);- ptls_clear_memory(digest, sizeof(digest));- hmac->final(hmac, output, PTLS_HASH_FINAL_MODE_FREE);-- return 0;-}--static int verify_finished(ptls_t *tls, ptls_iovec_t message)-{- uint8_t verify_data[PTLS_MAX_DIGEST_SIZE];- int ret;-- if (PTLS_HANDSHAKE_HEADER_SIZE + tls->key_schedule->hashes[0].algo->digest_size != message.len) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }-- if ((ret = calc_verify_data(verify_data, tls->key_schedule, tls->traffic_protection.dec.secret)) != 0)- goto Exit;- if (!ptls_mem_equal(message.base + PTLS_HANDSHAKE_HEADER_SIZE, verify_data, tls->key_schedule->hashes[0].algo->digest_size)) {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- goto Exit;- }--Exit:- ptls_clear_memory(verify_data, sizeof(verify_data));- return ret;-}--static int send_finished(ptls_t *tls, ptls_message_emitter_t *emitter)-{- int ret;-- ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, {- if ((ret = ptls_buffer_reserve(emitter->buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)- goto Exit;- if ((ret = calc_verify_data(emitter->buf->base + emitter->buf->off, tls->key_schedule,- tls->traffic_protection.enc.secret)) != 0)- goto Exit;- emitter->buf->off += tls->key_schedule->hashes[0].algo->digest_size;- });--Exit:- return ret;-}--static int send_session_ticket(ptls_t *tls, ptls_message_emitter_t *emitter)-{- ptls_hash_context_t *msghash_backup = tls->key_schedule->hashes[0].ctx->clone_(tls->key_schedule->hashes[0].ctx);- ptls_buffer_t session_id;- char session_id_smallbuf[128];- uint32_t ticket_age_add;- int ret = 0;-- assert(tls->ctx->ticket_lifetime != 0);- assert(tls->ctx->encrypt_ticket != NULL);-- ptls_buffer_init(&session_id, session_id_smallbuf, sizeof(session_id_smallbuf));-- { /* calculate verify-data that will be sent by the client */- size_t orig_off = emitter->buf->off;- if (tls->pending_handshake_secret != NULL && !tls->ctx->omit_end_of_early_data) {- assert(tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA);- ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {});- emitter->buf->off = orig_off;- }- ptls_buffer_push_message_body(emitter->buf, tls->key_schedule, PTLS_HANDSHAKE_TYPE_FINISHED, {- if ((ret = ptls_buffer_reserve(emitter->buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)- goto Exit;- if ((ret = calc_verify_data(emitter->buf->base + emitter->buf->off, tls->key_schedule,- tls->pending_handshake_secret != NULL ? tls->pending_handshake_secret- : tls->traffic_protection.dec.secret)) != 0)- goto Exit;- emitter->buf->off += tls->key_schedule->hashes[0].algo->digest_size;- });- emitter->buf->off = orig_off;- }-- tls->ctx->random_bytes(&ticket_age_add, sizeof(ticket_age_add));-- /* build the raw nsk */- ret = encode_session_identifier(tls->ctx, &session_id, ticket_age_add, ptls_iovec_init(NULL, 0), tls->key_schedule,- tls->server_name, tls->key_share->id, tls->cipher_suite->id, tls->negotiated_protocol);- if (ret != 0)- goto Exit;-- /* encrypt and send */- ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET, {- ptls_buffer_push32(emitter->buf, tls->ctx->ticket_lifetime);- ptls_buffer_push32(emitter->buf, ticket_age_add);- ptls_buffer_push_block(emitter->buf, 1, {});- ptls_buffer_push_block(emitter->buf, 2, {- if ((ret = tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 1, emitter->buf,- ptls_iovec_init(session_id.base, session_id.off))) != 0)- goto Exit;- });- ptls_buffer_push_block(emitter->buf, 2, {- if (tls->ctx->max_early_data_size != 0)- buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_EARLY_DATA,- { ptls_buffer_push32(emitter->buf, tls->ctx->max_early_data_size); });- });- });--Exit:- ptls_buffer_dispose(&session_id);-- /* restore handshake state */- tls->key_schedule->hashes[0].ctx->final(tls->key_schedule->hashes[0].ctx, NULL, PTLS_HASH_FINAL_MODE_FREE);- tls->key_schedule->hashes[0].ctx = msghash_backup;-- return ret;-}--static int push_change_cipher_spec(ptls_t *tls, ptls_message_emitter_t *emitter)-{- int ret;-- /* check if we are requested to (or still need to) */- if (!tls->send_change_cipher_spec) {- ret = 0;- goto Exit;- }-- /* CCS is a record, can only be sent when using a record-based protocol. */- if (emitter->begin_message != begin_record_message) {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- goto Exit;- }-- /* emit CCS */- buffer_push_record(emitter->buf, PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC, { ptls_buffer_push(emitter->buf, 1); });-- tls->send_change_cipher_spec = 0;- ret = 0;-Exit:- return ret;-}--static int push_additional_extensions(ptls_handshake_properties_t *properties, ptls_buffer_t *sendbuf)-{- int ret;-- if (properties != NULL && properties->additional_extensions != NULL) {- ptls_raw_extension_t *ext;- for (ext = properties->additional_extensions; ext->type != UINT16_MAX; ++ext) {- buffer_push_extension(sendbuf, ext->type, { ptls_buffer_pushv(sendbuf, ext->data.base, ext->data.len); });- }- }- ret = 0;-Exit:- return ret;-}--static int push_signature_algorithms(ptls_verify_certificate_t *vc, ptls_buffer_t *sendbuf)-{- /* The list sent when verify callback is not registered */- static const uint16_t default_algos[] = {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256,- PTLS_SIGNATURE_RSA_PKCS1_SHA256, PTLS_SIGNATURE_RSA_PKCS1_SHA1, UINT16_MAX};- int ret;-- ptls_buffer_push_block(sendbuf, 2, {- for (const uint16_t *p = vc != NULL ? vc->algos : default_algos; *p != UINT16_MAX; ++p)- ptls_buffer_push16(sendbuf, *p);- });-- ret = 0;-Exit:- return ret;-}--static int decode_signature_algorithms(struct st_ptls_signature_algorithms_t *sa, const uint8_t **src, const uint8_t *end)-{- int ret;-- ptls_decode_block(*src, end, 2, {- do {- uint16_t id;- if ((ret = ptls_decode16(&id, src, end)) != 0)- goto Exit;- if (sa->count < PTLS_ELEMENTSOF(sa->list))- sa->list[sa->count++] = id;- } while (*src != end);- });-- ret = 0;-Exit:- return ret;-}--static int select_cipher(ptls_cipher_suite_t **selected, ptls_cipher_suite_t **candidates, const uint8_t *src,- const uint8_t *const end, int server_preference, int server_chacha_priority)-{- size_t found_index = SIZE_MAX;- int ret;-- while (src != end) {- uint16_t id;- if ((ret = ptls_decode16(&id, &src, end)) != 0)- goto Exit;- for (size_t i = 0; candidates[i] != NULL; ++i) {- if (candidates[i]->id == id) {- if (server_preference && !(server_chacha_priority && id == PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256)) {- /* preserve smallest matching index, and proceed to the next input */- if (i < found_index) {- found_index = i;- break;- }- } else {- /* return the pointer matching to the first input that can be used */- *selected = candidates[i];- goto Exit;- }- }- }- /* first position of the server list matched (server_preference) */- if (found_index == 0)- break;- /* server preference is overridden only if the first entry of client-provided list is chachapoly */- server_chacha_priority = 0;- }- if (found_index != SIZE_MAX) {- *selected = candidates[found_index];- ret = 0;- } else {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- }--Exit:- return ret;-}--static int push_key_share_entry(ptls_buffer_t *buf, uint16_t group, ptls_iovec_t pubkey)-{- int ret;-- ptls_buffer_push16(buf, group);- ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, pubkey.base, pubkey.len); });- ret = 0;-Exit:- return ret;-}--static int decode_key_share_entry(uint16_t *group, ptls_iovec_t *key_exchange, const uint8_t **src, const uint8_t *const end)-{- int ret;-- if ((ret = ptls_decode16(group, src, end)) != 0)- goto Exit;- ptls_decode_open_block(*src, end, 2, {- *key_exchange = ptls_iovec_init(*src, end - *src);- *src = end;- });--Exit:- return ret;-}--static int select_key_share(ptls_key_exchange_algorithm_t **selected, ptls_iovec_t *peer_key,- ptls_key_exchange_algorithm_t **candidates, const uint8_t **src, const uint8_t *const end,- int expect_one)-{- int ret;-- *selected = NULL;-- if (expect_one && *src == end) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }-- while (*src != end) {- uint16_t group;- ptls_iovec_t key;- if ((ret = decode_key_share_entry(&group, &key, src, end)) != 0)- goto Exit;- ptls_key_exchange_algorithm_t **c = candidates;- for (; *c != NULL; ++c) {- if (*selected == NULL && (*c)->id == group) {- *selected = *c;- *peer_key = key;- }- }- if (expect_one) {- ret = *selected != NULL ? 0 : PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }-- ret = 0;--Exit:- return ret;-}--static int emit_server_name_extension(ptls_buffer_t *buf, const char *server_name)-{- int ret;-- ptls_buffer_push_block(buf, 2, {- ptls_buffer_push(buf, PTLS_SERVER_NAME_TYPE_HOSTNAME);- ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, server_name, strlen(server_name)); });- });-- ret = 0;-Exit:- return ret;-}--/**- * Within the outer ECH extension, returns the number of bytes that preceeds the AEAD-encrypted payload.- */-static inline size_t outer_ech_header_size(size_t enc_size)-{- return 10 + enc_size;-}--/**- * Flag to indicate which of ClientHelloInner, EncodedClientHelloInner, ClientHelloOuter is to be generated. When ECH is inactive,- * only ClientHelloInner is used.- */-enum encode_ch_mode { ENCODE_CH_MODE_INNER, ENCODE_CH_MODE_ENCODED_INNER, ENCODE_CH_MODE_OUTER };--static int encode_client_hello(ptls_context_t *ctx, ptls_buffer_t *sendbuf, enum encode_ch_mode mode, int is_second_flight,- ptls_handshake_properties_t *properties, const void *client_random,- ptls_key_exchange_context_t *key_share_ctx, const char *sni_name, ptls_iovec_t legacy_session_id,- struct st_ptls_ech_t *ech, size_t *ech_size_offset, ptls_iovec_t ech_replay,- ptls_iovec_t resumption_secret, ptls_iovec_t resumption_ticket, uint32_t obfuscated_ticket_age,- size_t psk_binder_size, ptls_iovec_t *cookie, int using_early_data)-{- int ret;-- assert(mode == ENCODE_CH_MODE_INNER || ech != NULL);-- ptls_buffer_push_message_body(sendbuf, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, {- /* legacy_version */- ptls_buffer_push16(sendbuf, 0x0303);- /* random_bytes */- ptls_buffer_pushv(sendbuf, client_random, PTLS_HELLO_RANDOM_SIZE);- /* lecagy_session_id */- ptls_buffer_push_block(sendbuf, 1, {- if (mode != ENCODE_CH_MODE_ENCODED_INNER)- ptls_buffer_pushv(sendbuf, legacy_session_id.base, legacy_session_id.len);- });- /* cipher_suites */- ptls_buffer_push_block(sendbuf, 2, {- ptls_cipher_suite_t **cs = ctx->cipher_suites;- for (; *cs != NULL; ++cs)- ptls_buffer_push16(sendbuf, (*cs)->id);- });- /* legacy_compression_methods */- ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push(sendbuf, 0); });- /* extensions */- ptls_buffer_push_block(sendbuf, 2, {- if (mode == ENCODE_CH_MODE_OUTER) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO, {- size_t ext_payload_from = sendbuf->off;- ptls_buffer_push(sendbuf, PTLS_ECH_CLIENT_HELLO_TYPE_OUTER);- ptls_buffer_push16(sendbuf, ech->cipher->id.kdf);- ptls_buffer_push16(sendbuf, ech->cipher->id.aead);- ptls_buffer_push(sendbuf, ech->config_id);- ptls_buffer_push_block(sendbuf, 2, {- if (!is_second_flight)- ptls_buffer_pushv(sendbuf, ech->client.enc.base, ech->client.enc.len);- });- ptls_buffer_push_block(sendbuf, 2, {- assert(sendbuf->off - ext_payload_from ==- outer_ech_header_size(is_second_flight ? 0 : ech->client.enc.len));- if ((ret = ptls_buffer_reserve(sendbuf, *ech_size_offset)) != 0)- goto Exit;- memset(sendbuf->base + sendbuf->off, 0, *ech_size_offset);- sendbuf->off += *ech_size_offset;- *ech_size_offset = sendbuf->off - *ech_size_offset;- });- });- } else if (ech->aead != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO,- { ptls_buffer_push(sendbuf, PTLS_ECH_CLIENT_HELLO_TYPE_INNER); });- } else if (ech_replay.base != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO,- { ptls_buffer_pushv(sendbuf, ech_replay.base, ech_replay.len); });- }- if (mode == ENCODE_CH_MODE_ENCODED_INNER) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS, {- ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push16(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE); });- });- } else {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, {- ptls_buffer_push_block(sendbuf, 2, {- if (key_share_ctx != NULL &&- (ret = push_key_share_entry(sendbuf, key_share_ctx->algo->id, key_share_ctx->pubkey)) != 0)- goto Exit;- });- });- }- if (sni_name != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {- if ((ret = emit_server_name_extension(sendbuf, sni_name)) != 0)- goto Exit;- });- }- if (properties != NULL && properties->client.negotiated_protocols.count != 0) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, {- ptls_buffer_push_block(sendbuf, 2, {- size_t i;- for (i = 0; i != properties->client.negotiated_protocols.count; ++i) {- ptls_buffer_push_block(sendbuf, 1, {- ptls_iovec_t p = properties->client.negotiated_protocols.list[i];- ptls_buffer_pushv(sendbuf, p.base, p.len);- });- }- });- });- }- if (ctx->decompress_certificate != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE, {- ptls_buffer_push_block(sendbuf, 1, {- const uint16_t *algo = ctx->decompress_certificate->supported_algorithms;- assert(*algo != UINT16_MAX);- for (; *algo != UINT16_MAX; ++algo)- ptls_buffer_push16(sendbuf, *algo);- });- });- }- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS, {- ptls_buffer_push_block(sendbuf, 1, {- size_t i;- for (i = 0; i != PTLS_ELEMENTSOF(supported_versions); ++i)- ptls_buffer_push16(sendbuf, supported_versions[i]);- });- });- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, {- if ((ret = push_signature_algorithms(ctx->verify_certificate, sendbuf)) != 0)- goto Exit;- });- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS, {- ptls_key_exchange_algorithm_t **algo = ctx->key_exchanges;- ptls_buffer_push_block(sendbuf, 2, {- for (; *algo != NULL; ++algo)- ptls_buffer_push16(sendbuf, (*algo)->id);- });- });- if (cookie != NULL && cookie->base != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, {- ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, cookie->base, cookie->len); });- });- }- if (ctx->use_raw_public_keys) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE, {- ptls_buffer_push_block(sendbuf, 1, { ptls_buffer_push(sendbuf, PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); });- });- }- if ((ret = push_additional_extensions(properties, sendbuf)) != 0)- goto Exit;- if (ctx->save_ticket != NULL || resumption_secret.base != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES, {- ptls_buffer_push_block(sendbuf, 1, {- if (!ctx->require_dhe_on_psk)- ptls_buffer_push(sendbuf, PTLS_PSK_KE_MODE_PSK);- ptls_buffer_push(sendbuf, PTLS_PSK_KE_MODE_PSK_DHE);- });- });- }- if (resumption_secret.base != NULL) {- if (using_early_data && !is_second_flight)- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {});- /* pre-shared key "MUST be the last extension in the ClientHello" (draft-17 section 4.2.6) */- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY, {- ptls_buffer_push_block(sendbuf, 2, {- ptls_buffer_push_block(sendbuf, 2, {- if (mode == ENCODE_CH_MODE_OUTER) {- if ((ret = ptls_buffer_reserve(sendbuf, resumption_ticket.len)) != 0)- goto Exit;- ctx->random_bytes(sendbuf->base + sendbuf->off, resumption_ticket.len);- sendbuf->off += resumption_ticket.len;- } else {- ptls_buffer_pushv(sendbuf, resumption_ticket.base, resumption_ticket.len);- }- });- uint32_t age;- if (mode == ENCODE_CH_MODE_OUTER) {- ctx->random_bytes(&age, sizeof(age));- } else {- age = obfuscated_ticket_age;- }- ptls_buffer_push32(sendbuf, age);- });- /* allocate space for PSK binder. The space is filled initially filled by a random value (meeting the- * requirement of ClientHelloOuter), and later gets filled with the correct binder value if necessary. */- ptls_buffer_push_block(sendbuf, 2, {- ptls_buffer_push_block(sendbuf, 1, {- if ((ret = ptls_buffer_reserve(sendbuf, psk_binder_size)) != 0)- goto Exit;- ctx->random_bytes(sendbuf->base + sendbuf->off, psk_binder_size);- sendbuf->off += psk_binder_size;- });- });- });- }- });- });--Exit:- return ret;-}--static int send_client_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_handshake_properties_t *properties,- ptls_iovec_t *cookie)-{- ptls_iovec_t resumption_secret = {NULL}, resumption_ticket = {NULL};- uint32_t obfuscated_ticket_age = 0;- const char *sni_name = NULL;- size_t mess_start, msghash_off;- uint8_t binder_key[PTLS_MAX_DIGEST_SIZE];- ptls_buffer_t encoded_ch_inner;- int ret, is_second_flight = tls->key_schedule != NULL;-- ptls_buffer_init(&encoded_ch_inner, "", 0);-- if (tls->server_name != NULL && !ptls_server_name_is_ipaddr(tls->server_name))- sni_name = tls->server_name;-- if (properties != NULL) {- /* try to use ECH (ignore broken ECHConfigList; it is delivered insecurely) */- if (!is_second_flight && sni_name != NULL && tls->ctx->ech.client.ciphers != NULL) {- if (properties->client.ech.configs.len != 0) {- struct st_decoded_ech_config_t decoded;- client_decode_ech_config_list(tls->ctx, &decoded, properties->client.ech.configs);- if (decoded.kem != NULL && decoded.cipher != NULL) {- if ((ret = client_setup_ech(&tls->ech, &decoded, tls->ctx->random_bytes)) != 0)- goto Exit;- }- } else {- /* zero-length config indicates ECH greasing */- client_setup_ech_grease(&tls->ech, tls->ctx->random_bytes, tls->ctx->ech.client.kems, tls->ctx->ech.client.ciphers,- sni_name);- }- }- /* setup resumption-related data. If successful, resumption_secret becomes a non-zero value. */- if (properties->client.session_ticket.base != NULL) {- ptls_key_exchange_algorithm_t *key_share = NULL;- ptls_cipher_suite_t *cipher_suite = NULL;- uint32_t max_early_data_size;- if (decode_stored_session_ticket(tls, &key_share, &cipher_suite, &resumption_secret, &obfuscated_ticket_age,- &resumption_ticket, &max_early_data_size, properties->client.session_ticket.base,- properties->client.session_ticket.base + properties->client.session_ticket.len) == 0) {- tls->client.offered_psk = 1;- /* key-share selected by HRR should not be overridden */- if (tls->key_share == NULL)- tls->key_share = key_share;- tls->cipher_suite = cipher_suite;- if (!is_second_flight && max_early_data_size != 0 && properties->client.max_early_data_size != NULL) {- tls->client.using_early_data = 1;- *properties->client.max_early_data_size = max_early_data_size;- }- } else {- resumption_secret = ptls_iovec_init(NULL, 0);- }- }- if (tls->client.using_early_data) {- properties->client.early_data_acceptance = PTLS_EARLY_DATA_ACCEPTANCE_UNKNOWN;- } else {- if (properties->client.max_early_data_size != NULL)- *properties->client.max_early_data_size = 0;- properties->client.early_data_acceptance = PTLS_EARLY_DATA_REJECTED;- }- }-- /* use the default key share if still not undetermined */- if (tls->key_share == NULL && !(properties != NULL && properties->client.negotiate_before_key_exchange))- tls->key_share = tls->ctx->key_exchanges[0];-- /* instantiate key share context */- assert(tls->client.key_share_ctx == NULL);- if (tls->key_share != NULL) {- if ((ret = tls->key_share->create(tls->key_share, &tls->client.key_share_ctx)) != 0)- goto Exit;- }-- /* initialize key schedule */- if (!is_second_flight) {- tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites, tls->ech.aead != NULL);- if ((ret = key_schedule_extract(tls->key_schedule, resumption_secret)) != 0)- goto Exit;- }-- /* start generating CH */- if ((ret = emitter->begin_message(emitter)) != 0)- goto Exit;- mess_start = msghash_off = emitter->buf->off;-- /* generate true (inner) CH */- if ((ret = encode_client_hello(tls->ctx, emitter->buf, ENCODE_CH_MODE_INNER, is_second_flight, properties,- tls->ech.aead != NULL ? tls->ech.inner_client_random : tls->client_random,- tls->client.key_share_ctx, sni_name, tls->client.legacy_session_id, &tls->ech, NULL,- tls->ech.client.first_ech, resumption_secret, resumption_ticket, obfuscated_ticket_age,- tls->key_schedule->hashes[0].algo->digest_size, cookie, tls->client.using_early_data)) != 0)- goto Exit;-- /* update the message hash, filling in the PSK binder HMAC if necessary */- if (resumption_secret.base != NULL) {- size_t psk_binder_off = emitter->buf->off - (3 + tls->key_schedule->hashes[0].algo->digest_size);- if ((ret = derive_secret_with_empty_digest(tls->key_schedule, binder_key, "res binder")) != 0)- goto Exit;- ptls__key_schedule_update_hash(tls->key_schedule, emitter->buf->base + msghash_off, psk_binder_off - msghash_off, 0);- msghash_off = psk_binder_off;- if ((ret = calc_verify_data(emitter->buf->base + psk_binder_off + 3, tls->key_schedule, binder_key)) != 0)- goto Exit;- }- ptls__key_schedule_update_hash(tls->key_schedule, emitter->buf->base + msghash_off, emitter->buf->off - msghash_off, 0);-- /* ECH */- if (tls->ech.aead != NULL) {- /* build EncodedCHInner */- if ((ret = encode_client_hello(tls->ctx, &encoded_ch_inner, ENCODE_CH_MODE_ENCODED_INNER, is_second_flight, properties,- tls->ech.inner_client_random, tls->client.key_share_ctx, sni_name,- tls->client.legacy_session_id, &tls->ech, NULL, ptls_iovec_init(NULL, 0), resumption_secret,- resumption_ticket, obfuscated_ticket_age, tls->key_schedule->hashes[0].algo->digest_size,- cookie, tls->client.using_early_data)) != 0)- goto Exit;- if (resumption_secret.base != NULL)- memcpy(encoded_ch_inner.base + encoded_ch_inner.off - tls->key_schedule->hashes[0].algo->digest_size,- emitter->buf->base + emitter->buf->off - tls->key_schedule->hashes[0].algo->digest_size,- tls->key_schedule->hashes[0].algo->digest_size);- { /* pad EncodedCHInner (following draft-ietf-tls-esni-15 6.1.3) */- size_t padding_len;- if (sni_name != NULL) {- padding_len = strlen(sni_name);- if (padding_len < tls->ech.client.max_name_length)- padding_len = tls->ech.client.max_name_length;- } else {- padding_len = tls->ech.client.max_name_length + 9;- }- size_t final_len = encoded_ch_inner.off - PTLS_HANDSHAKE_HEADER_SIZE + padding_len;- final_len = (final_len + 31) / 32 * 32;- padding_len = final_len - (encoded_ch_inner.off - PTLS_HANDSHAKE_HEADER_SIZE);- if (padding_len != 0) {- if ((ret = ptls_buffer_reserve(&encoded_ch_inner, padding_len)) != 0)- goto Exit;- memset(encoded_ch_inner.base + encoded_ch_inner.off, 0, padding_len);- encoded_ch_inner.off += padding_len;- }- }- /* flush CHInner, build CHOuterAAD */- emitter->buf->off = mess_start;- size_t ech_payload_size = encoded_ch_inner.off - PTLS_HANDSHAKE_HEADER_SIZE + tls->ech.aead->algo->tag_size,- ech_size_offset = ech_payload_size;- if ((ret = encode_client_hello(tls->ctx, emitter->buf, ENCODE_CH_MODE_OUTER, is_second_flight, properties,- tls->client_random, tls->client.key_share_ctx, tls->ech.client.public_name,- tls->client.legacy_session_id, &tls->ech, &ech_size_offset, ptls_iovec_init(NULL, 0),- resumption_secret, resumption_ticket, obfuscated_ticket_age,- tls->key_schedule->hashes[0].algo->digest_size, cookie, tls->client.using_early_data)) != 0)- goto Exit;- /* overwrite ECH payload */- ptls_aead_encrypt(tls->ech.aead, emitter->buf->base + ech_size_offset, encoded_ch_inner.base + PTLS_HANDSHAKE_HEADER_SIZE,- encoded_ch_inner.off - PTLS_HANDSHAKE_HEADER_SIZE, is_second_flight,- emitter->buf->base + mess_start + PTLS_HANDSHAKE_HEADER_SIZE,- emitter->buf->off - (mess_start + PTLS_HANDSHAKE_HEADER_SIZE));- /* keep the copy of the 1st ECH extension so that we can send it again in 2nd CH in response to rejection with HRR */- if (!is_second_flight) {- size_t len = outer_ech_header_size(tls->ech.client.enc.len) + ech_payload_size;- if ((tls->ech.client.first_ech.base = malloc(len)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- memcpy(tls->ech.client.first_ech.base,- emitter->buf->base + ech_size_offset - outer_ech_header_size(tls->ech.client.enc.len), len);- tls->ech.client.first_ech.len = len;- if (properties->client.ech.configs.len != 0) {- tls->ech.offered = 1;- } else {- tls->ech.offered_grease = 1;- }- }- /* update hash */- ptls__key_schedule_update_hash(tls->key_schedule, emitter->buf->base + mess_start, emitter->buf->off - mess_start, 1);- }-- /* commit CH to the record layer */- if ((ret = emitter->commit_message(emitter)) != 0)- goto Exit;-- if (tls->client.using_early_data) {- assert(!is_second_flight);- if ((ret = setup_traffic_protection(tls, 1, "c e traffic", 1, 0)) != 0)- goto Exit;- if ((ret = push_change_cipher_spec(tls, emitter)) != 0)- goto Exit;- }- if (resumption_secret.base != NULL && !is_second_flight) {- if ((ret = derive_exporter_secret(tls, 1)) != 0)- goto Exit;- }- tls->state = cookie == NULL ? PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO : PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO;- ret = PTLS_ERROR_IN_PROGRESS;--Exit:- ptls_buffer_dispose(&encoded_ch_inner);- ptls_clear_memory(binder_key, sizeof(binder_key));- return ret;-}--ptls_cipher_suite_t *ptls_find_cipher_suite(ptls_cipher_suite_t **cipher_suites, uint16_t id)-{- ptls_cipher_suite_t **cs;- if (cipher_suites == NULL)- return NULL;- for (cs = cipher_suites; *cs != NULL && (*cs)->id != id; ++cs)- ;- return *cs;-}--static int decode_server_hello(ptls_t *tls, struct st_ptls_server_hello_t *sh, const uint8_t *src, const uint8_t *const end)-{- int ret;-- *sh = (struct st_ptls_server_hello_t){{0}};-- /* ignore legacy-version */- if (end - src < 2) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- src += 2;-- /* random */- if (end - src < PTLS_HELLO_RANDOM_SIZE) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- sh->is_retry_request = memcmp(src, hello_retry_random, PTLS_HELLO_RANDOM_SIZE) == 0;- src += PTLS_HELLO_RANDOM_SIZE;-- /* legacy_session_id */- ptls_decode_open_block(src, end, 1, {- if (end - src > 32) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- sh->legacy_session_id = ptls_iovec_init(src, end - src);- src = end;- });-- { /* select cipher_suite */- uint16_t csid;- if ((ret = ptls_decode16(&csid, &src, end)) != 0)- goto Exit;- if ((tls->cipher_suite = ptls_find_cipher_suite(tls->ctx->cipher_suites, csid)) == NULL) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }-- { /* legacy_compression_method */- uint8_t method;- if ((ret = ptls_decode8(&method, &src, end)) != 0)- goto Exit;- if (method != 0) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }-- if (sh->is_retry_request)- sh->retry_request.selected_group = UINT16_MAX;-- uint16_t exttype, found_version = UINT16_MAX, selected_psk_identity = UINT16_MAX;- decode_extensions(src, end, sh->is_retry_request ? PTLS_HANDSHAKE_TYPE_PSEUDO_HRR : PTLS_HANDSHAKE_TYPE_SERVER_HELLO, &exttype,- {- if (tls->ctx->on_extension != NULL &&- (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_SERVER_HELLO,- exttype, ptls_iovec_init(src, end - src)) != 0))- goto Exit;- switch (exttype) {- case PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS:- if ((ret = ptls_decode16(&found_version, &src, end)) != 0)- goto Exit;- break;- case PTLS_EXTENSION_TYPE_KEY_SHARE:- if (sh->is_retry_request) {- if ((ret = ptls_decode16(&sh->retry_request.selected_group, &src, end)) != 0)- goto Exit;- } else {- uint16_t group;- if ((ret = decode_key_share_entry(&group, &sh->peerkey, &src, end)) != 0)- goto Exit;- if (src != end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if (tls->key_share == NULL || tls->key_share->id != group) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }- break;- case PTLS_EXTENSION_TYPE_COOKIE:- assert(sh->is_retry_request);- ptls_decode_block(src, end, 2, {- if (src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- sh->retry_request.cookie = ptls_iovec_init(src, end - src);- src = end;- });- break;- case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY:- assert(!sh->is_retry_request);- if ((ret = ptls_decode16(&selected_psk_identity, &src, end)) != 0)- goto Exit;- break;- case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO:- assert(sh->is_retry_request);- if (!(tls->ech.offered || tls->ech.offered_grease)) {- ret = PTLS_ALERT_UNSUPPORTED_EXTENSION;- goto Exit;- }- if (end - src != PTLS_ECH_CONFIRM_LENGTH) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- sh->retry_request.ech = src;- src = end;- break;- default:- src = end;- break;- }- });-- if (!is_supported_version(found_version)) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- if (!sh->is_retry_request) {- if (selected_psk_identity != UINT16_MAX) {- if (!tls->client.offered_psk) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- if (selected_psk_identity != 0) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- tls->is_psk_handshake = 1;- }- if (sh->peerkey.base == NULL && !tls->is_psk_handshake) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }-- ret = 0;-Exit:- return ret;-}--static int handle_hello_retry_request(ptls_t *tls, ptls_message_emitter_t *emitter, struct st_ptls_server_hello_t *sh,- ptls_iovec_t message, ptls_handshake_properties_t *properties)-{- int ret;-- if (tls->client.key_share_ctx != NULL) {- tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, NULL, ptls_iovec_init(NULL, 0));- tls->client.key_share_ctx = NULL;- }- if (tls->client.using_early_data) {- /* release traffic encryption key so that 2nd CH goes out in cleartext, but keep the epoch at 1 since we've already- * called derive-secret */- if (tls->ctx->update_traffic_key == NULL) {- assert(tls->traffic_protection.enc.aead != NULL);- ptls_aead_free(tls->traffic_protection.enc.aead);- tls->traffic_protection.enc.aead = NULL;- }- tls->client.using_early_data = 0;- }-- if (sh->retry_request.selected_group != UINT16_MAX) {- /* we offer the first key_exchanges[0] as KEY_SHARE unless client.negotiate_before_key_exchange is set */- ptls_key_exchange_algorithm_t **cand;- for (cand = tls->ctx->key_exchanges; *cand != NULL; ++cand)- if ((*cand)->id == sh->retry_request.selected_group)- break;- if (*cand == NULL) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- tls->key_share = *cand;- } else if (tls->key_share != NULL) {- /* retain the key-share using in first CH, if server does not specify one */- } else {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }-- ret = send_client_hello(tls, emitter, properties, &sh->retry_request.cookie);--Exit:- return ret;-}--static int client_ech_select_hello(ptls_t *tls, ptls_iovec_t message, size_t confirm_hash_off, const char *label)-{- uint8_t confirm_hash_delivered[PTLS_ECH_CONFIRM_LENGTH], confirm_hash_expected[PTLS_ECH_CONFIRM_LENGTH];- int ret = 0;-- /* Determine if ECH has been accepted by checking the confirmation hash. `confirm_hash_off` set to zero indicates that HRR was- * received wo. ECH extension, which is an indication that ECH was rejected. */- if (confirm_hash_off != 0) {- memcpy(confirm_hash_delivered, message.base + confirm_hash_off, sizeof(confirm_hash_delivered));- memset(message.base + confirm_hash_off, 0, sizeof(confirm_hash_delivered));- if ((ret = ech_calc_confirmation(tls->key_schedule, confirm_hash_expected, tls->ech.inner_client_random, label, message)) !=- 0)- goto Exit;- tls->ech.accepted = ptls_mem_equal(confirm_hash_delivered, confirm_hash_expected, sizeof(confirm_hash_delivered));- memcpy(message.base + confirm_hash_off, confirm_hash_delivered, sizeof(confirm_hash_delivered));- if (tls->ech.accepted)- goto Exit;- }-- /* dispose ECH AEAD state to indicate rejection, adopting outer CH for the rest of the handshake */- ptls_aead_free(tls->ech.aead);- tls->ech.aead = NULL;- key_schedule_select_outer(tls->key_schedule);--Exit:- PTLS_PROBE(ECH_SELECTION, tls, !!tls->ech.accepted);- PTLS_LOG_CONN(ech_selection, tls, { PTLS_LOG_ELEMENT_BOOL(is_ech, tls->ech.accepted); });- ptls_clear_memory(confirm_hash_expected, sizeof(confirm_hash_expected));- return ret;-}--static int client_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message,- ptls_handshake_properties_t *properties)-{- struct st_ptls_server_hello_t sh;- ptls_iovec_t ecdh_secret = {NULL};- int ret;-- if ((ret = decode_server_hello(tls, &sh, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len)) != 0)- goto Exit;- if (!(sh.legacy_session_id.len == tls->client.legacy_session_id.len &&- ptls_mem_equal(sh.legacy_session_id.base, tls->client.legacy_session_id.base, tls->client.legacy_session_id.len))) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }-- if (sh.is_retry_request) {- if ((ret = key_schedule_select_cipher(tls->key_schedule, tls->cipher_suite, 0)) != 0)- goto Exit;- key_schedule_transform_post_ch1hash(tls->key_schedule);- if (tls->ech.aead != NULL) {- size_t confirm_hash_off = 0;- if (tls->ech.offered) {- if (sh.retry_request.ech != NULL)- confirm_hash_off = sh.retry_request.ech - message.base;- } else {- assert(tls->ech.offered_grease);- }- if ((ret = client_ech_select_hello(tls, message, confirm_hash_off, ECH_CONFIRMATION_HRR)) != 0)- goto Exit;- }- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);- return handle_hello_retry_request(tls, emitter, &sh, message, properties);- }-- if ((ret = key_schedule_select_cipher(tls->key_schedule, tls->cipher_suite,- tls->client.offered_psk && !tls->is_psk_handshake)) != 0)- goto Exit;-- /* check if ECH is accepted */- if (tls->ech.aead != NULL) {- size_t confirm_hash_off = 0;- if (tls->ech.offered) {- confirm_hash_off =- PTLS_HANDSHAKE_HEADER_SIZE + 2 /* legacy_version */ + PTLS_HELLO_RANDOM_SIZE - PTLS_ECH_CONFIRM_LENGTH;- } else {- assert(tls->ech.offered_grease);- }- if ((ret = client_ech_select_hello(tls, message, confirm_hash_off, ECH_CONFIRMATION_SERVER_HELLO)) != 0)- goto Exit;- }-- /* clear sensitive and space-consuming ECH state, now that are done with handling sending and decoding Hellos */- clear_ech(&tls->ech, 0);- if (tls->key_schedule->hashes[0].ctx_outer != NULL) {- tls->key_schedule->hashes[0].ctx_outer->final(tls->key_schedule->hashes[0].ctx_outer, NULL, PTLS_HASH_FINAL_MODE_FREE);- tls->key_schedule->hashes[0].ctx_outer = NULL;- }-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);-- if (sh.peerkey.base != NULL) {- if ((ret = tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, &ecdh_secret, sh.peerkey)) != 0)- goto Exit;- }-- if ((ret = key_schedule_extract(tls->key_schedule, ecdh_secret)) != 0)- goto Exit;- if ((ret = setup_traffic_protection(tls, 0, "s hs traffic", 2, 0)) != 0)- goto Exit;- if (tls->client.using_early_data) {- if ((tls->pending_handshake_secret = malloc(PTLS_MAX_DIGEST_SIZE)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- if ((ret = derive_secret(tls->key_schedule, tls->pending_handshake_secret, "c hs traffic")) != 0)- goto Exit;- if (tls->ctx->update_traffic_key != NULL &&- (ret = tls->ctx->update_traffic_key->cb(tls->ctx->update_traffic_key, tls, 1, 2, tls->pending_handshake_secret)) != 0)- goto Exit;- } else {- if ((ret = setup_traffic_protection(tls, 1, "c hs traffic", 2, 0)) != 0)- goto Exit;- }-- tls->state = PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS;- ret = PTLS_ERROR_IN_PROGRESS;--Exit:- if (ecdh_secret.base != NULL) {- ptls_clear_memory(ecdh_secret.base, ecdh_secret.len);- free(ecdh_secret.base);- }- return ret;-}--static int should_collect_unknown_extension(ptls_t *tls, ptls_handshake_properties_t *properties, uint16_t type)-{- return properties != NULL && properties->collect_extension != NULL && properties->collect_extension(tls, properties, type);-}--static int collect_unknown_extension(ptls_t *tls, uint16_t type, const uint8_t *src, const uint8_t *const end,- ptls_raw_extension_t *slots)-{- size_t i;- for (i = 0; slots[i].type != UINT16_MAX; ++i) {- assert(i < MAX_UNKNOWN_EXTENSIONS);- if (slots[i].type == type)- return PTLS_ALERT_ILLEGAL_PARAMETER;- }- if (i < MAX_UNKNOWN_EXTENSIONS) {- slots[i].type = type;- slots[i].data = ptls_iovec_init(src, end - src);- slots[i + 1].type = UINT16_MAX;- }- return 0;-}--static int report_unknown_extensions(ptls_t *tls, ptls_handshake_properties_t *properties, ptls_raw_extension_t *slots)-{- if (properties != NULL && properties->collect_extension != NULL) {- assert(properties->collected_extensions != NULL);- return properties->collected_extensions(tls, properties, slots);- } else {- return 0;- }-}--static int client_handle_encrypted_extensions(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties)-{- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;- uint16_t type;- static const ptls_raw_extension_t no_unknown_extensions = {UINT16_MAX};- ptls_raw_extension_t *unknown_extensions = (ptls_raw_extension_t *)&no_unknown_extensions;- int ret, skip_early_data = 1;- uint8_t server_offered_cert_type = PTLS_CERTIFICATE_TYPE_X509;-- decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, &type, {- if (tls->ctx->on_extension != NULL &&- (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, type,- ptls_iovec_init(src, end - src)) != 0))- goto Exit;- switch (type) {- case PTLS_EXTENSION_TYPE_SERVER_NAME:- if (src != end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if (!(tls->server_name != NULL && !ptls_server_name_is_ipaddr(tls->server_name))) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- break;- case PTLS_EXTENSION_TYPE_ALPN:- ptls_decode_block(src, end, 2, {- ptls_decode_open_block(src, end, 1, {- if (src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if ((ret = ptls_set_negotiated_protocol(tls, (const char *)src, end - src)) != 0)- goto Exit;- src = end;- });- if (src != end) {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- goto Exit;- }- });- break;- case PTLS_EXTENSION_TYPE_EARLY_DATA:- if (!tls->client.using_early_data) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- skip_early_data = 0;- break;- case PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE:- if (end - src != 1) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- server_offered_cert_type = *src;- src = end;- break;- case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO: {- /* accept retry_configs only if we offered ECH but rejected */- if (!((tls->ech.offered || tls->ech.offered_grease) && !ptls_is_ech_handshake(tls, NULL, NULL, NULL))) {- ret = PTLS_ALERT_UNSUPPORTED_EXTENSION;- goto Exit;- }- /* parse retry_config, and if it is applicable, provide that to the application */- struct st_decoded_ech_config_t decoded;- if ((ret = client_decode_ech_config_list(tls->ctx, &decoded, ptls_iovec_init(src, end - src))) != 0)- goto Exit;- if (decoded.kem != NULL && decoded.cipher != NULL && properties != NULL &&- properties->client.ech.retry_configs != NULL) {- if ((properties->client.ech.retry_configs->base = malloc(end - src)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- memcpy(properties->client.ech.retry_configs->base, src, end - src);- properties->client.ech.retry_configs->len = end - src;- }- src = end;- } break;- default:- if (should_collect_unknown_extension(tls, properties, type)) {- if (unknown_extensions == &no_unknown_extensions) {- if ((unknown_extensions = malloc(sizeof(*unknown_extensions) * (MAX_UNKNOWN_EXTENSIONS + 1))) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- unknown_extensions[0].type = UINT16_MAX;- }- if ((ret = collect_unknown_extension(tls, type, src, end, unknown_extensions)) != 0)- goto Exit;- }- break;- }- src = end;- });-- if (server_offered_cert_type !=- (tls->ctx->use_raw_public_keys ? PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY : PTLS_CERTIFICATE_TYPE_X509)) {- ret = PTLS_ALERT_UNSUPPORTED_CERTIFICATE;- goto Exit;- }-- if (tls->client.using_early_data) {- if (skip_early_data)- tls->client.using_early_data = 0;- if (properties != NULL)- properties->client.early_data_acceptance = skip_early_data ? PTLS_EARLY_DATA_REJECTED : PTLS_EARLY_DATA_ACCEPTED;- }- if ((ret = report_unknown_extensions(tls, properties, unknown_extensions)) != 0)- goto Exit;-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);- tls->state =- tls->is_psk_handshake ? PTLS_STATE_CLIENT_EXPECT_FINISHED : PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE;- ret = PTLS_ERROR_IN_PROGRESS;--Exit:- if (unknown_extensions != &no_unknown_extensions)- free(unknown_extensions);- return ret;-}--static int decode_certificate_request(ptls_t *tls, struct st_ptls_certificate_request_t *cr, const uint8_t *src,- const uint8_t *const end)-{- int ret;- uint16_t exttype = 0;-- /* certificate request context */- ptls_decode_open_block(src, end, 1, {- size_t len = end - src;- if (len > 255) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if ((cr->context.base = malloc(len != 0 ? len : 1)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- cr->context.len = len;- memcpy(cr->context.base, src, len);- src = end;- });-- /* decode extensions */- decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST, &exttype, {- if (tls->ctx->on_extension != NULL &&- (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST, exttype,- ptls_iovec_init(src, end - src)) != 0))- goto Exit;- switch (exttype) {- case PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS:- if ((ret = decode_signature_algorithms(&cr->signature_algorithms, &src, end)) != 0)- goto Exit;- break;- }- src = end;- });-- if (cr->signature_algorithms.count == 0) {- ret = PTLS_ALERT_MISSING_EXTENSION;- goto Exit;- }-- ret = 0;-Exit:- return ret;-}--int ptls_build_certificate_message(ptls_buffer_t *buf, ptls_iovec_t context, ptls_iovec_t *certificates, size_t num_certificates,- ptls_iovec_t ocsp_status)-{- int ret;-- ptls_buffer_push_block(buf, 1, { ptls_buffer_pushv(buf, context.base, context.len); });- ptls_buffer_push_block(buf, 3, {- size_t i;- for (i = 0; i != num_certificates; ++i) {- ptls_buffer_push_block(buf, 3, { ptls_buffer_pushv(buf, certificates[i].base, certificates[i].len); });- ptls_buffer_push_block(buf, 2, {- if (i == 0 && ocsp_status.len != 0) {- buffer_push_extension(buf, PTLS_EXTENSION_TYPE_STATUS_REQUEST, {- ptls_buffer_push(buf, 1); /* status_type == ocsp */- ptls_buffer_push_block(buf, 3, { ptls_buffer_pushv(buf, ocsp_status.base, ocsp_status.len); });- });- }- });- }- });-- ret = 0;-Exit:- return ret;-}--static int default_emit_certificate_cb(ptls_emit_certificate_t *_self, ptls_t *tls, ptls_message_emitter_t *emitter,- ptls_key_schedule_t *key_sched, ptls_iovec_t context, int push_status_request,- const uint16_t *compress_algos, size_t num_compress_algos)-{- int ret;-- ptls_push_message(emitter, key_sched, PTLS_HANDSHAKE_TYPE_CERTIFICATE, {- if ((ret = ptls_build_certificate_message(emitter->buf, context, tls->ctx->certificates.list, tls->ctx->certificates.count,- ptls_iovec_init(NULL, 0))) != 0)- goto Exit;- });-- ret = 0;-Exit:- return ret;-}--static int send_certificate(ptls_t *tls, ptls_message_emitter_t *emitter,- struct st_ptls_signature_algorithms_t *signature_algorithms, ptls_iovec_t context,- int push_status_request, const uint16_t *compress_algos, size_t num_compress_algos)-{- int ret;-- if (signature_algorithms->count == 0) {- ret = PTLS_ALERT_MISSING_EXTENSION;- goto Exit;- }-- { /* send Certificate (or the equivalent) */- static ptls_emit_certificate_t default_emit_certificate = {default_emit_certificate_cb};- ptls_emit_certificate_t *emit_certificate =- tls->ctx->emit_certificate != NULL ? tls->ctx->emit_certificate : &default_emit_certificate;- Redo:- if ((ret = emit_certificate->cb(emit_certificate, tls, emitter, tls->key_schedule, context, push_status_request,- compress_algos, num_compress_algos)) != 0) {- if (ret == PTLS_ERROR_DELEGATE) {- assert(emit_certificate != &default_emit_certificate);- emit_certificate = &default_emit_certificate;- goto Redo;- }- goto Exit;- }- }--Exit:- return ret;-}--static int send_certificate_verify(ptls_t *tls, ptls_message_emitter_t *emitter,- struct st_ptls_signature_algorithms_t *signature_algorithms, const char *context_string)-{- size_t start_off = emitter->buf->off;- int ret;-- if (tls->ctx->sign_certificate == NULL)- return 0;- /* build and send CertificateVerify */- ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY, {- ptls_buffer_t *sendbuf = emitter->buf;- size_t algo_off = sendbuf->off;- ptls_buffer_push16(sendbuf, 0); /* filled in later */- ptls_buffer_push_block(sendbuf, 2, {- uint16_t algo;- uint8_t data[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE];- size_t datalen = build_certificate_verify_signdata(data, tls->key_schedule, context_string);- if ((ret = tls->ctx->sign_certificate->cb(- tls->ctx->sign_certificate, tls, tls->is_server ? &tls->server.async_job : NULL, &algo, sendbuf,- ptls_iovec_init(data, datalen), signature_algorithms != NULL ? signature_algorithms->list : NULL,- signature_algorithms != NULL ? signature_algorithms->count : 0)) != 0) {- if (ret == PTLS_ERROR_ASYNC_OPERATION) {- assert(tls->is_server || !"async operation only supported on the server-side");- assert(tls->server.async_job != NULL);- /* Reset the output to the end of the previous handshake message. CertificateVerify will be rebuilt when the- * async operation completes. */- emitter->buf->off = start_off;- } else {- assert(tls->server.async_job == NULL);- }- goto Exit;- }- assert(tls->server.async_job == NULL);- sendbuf->base[algo_off] = (uint8_t)(algo >> 8);- sendbuf->base[algo_off + 1] = (uint8_t)algo;- });- });-Exit:- return ret;-}--static int client_handle_certificate_request(ptls_t *tls, ptls_iovec_t message, ptls_handshake_properties_t *properties)-{- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;- int ret = 0;-- assert(!tls->is_psk_handshake && "state machine asserts that this message is never delivered when PSK is used");-- if ((ret = decode_certificate_request(tls, &tls->client.certificate_request, src, end)) != 0)- return ret;-- /* This field SHALL be zero length unless used for the post-handshake authentication exchanges (section 4.3.2) */- if (tls->client.certificate_request.context.len != 0)- return PTLS_ALERT_ILLEGAL_PARAMETER;-- tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE;- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);-- return PTLS_ERROR_IN_PROGRESS;-}--static int handle_certificate(ptls_t *tls, const uint8_t *src, const uint8_t *end, int *got_certs)-{- ptls_iovec_t certs[16];- size_t num_certs = 0;- int ret = 0;-- /* certificate request context */- ptls_decode_open_block(src, end, 1, {- if (src != end) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- });- /* certificate_list */- ptls_decode_block(src, end, 3, {- while (src != end) {- ptls_decode_open_block(src, end, 3, {- if (num_certs < PTLS_ELEMENTSOF(certs))- certs[num_certs++] = ptls_iovec_init(src, end - src);- src = end;- });- uint16_t type;- decode_open_extensions(src, end, PTLS_HANDSHAKE_TYPE_CERTIFICATE, &type, {- if (tls->ctx->on_extension != NULL &&- (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, PTLS_HANDSHAKE_TYPE_CERTIFICATE, type,- ptls_iovec_init(src, end - src)) != 0))- goto Exit;- src = end;- });- }- });-- if (tls->ctx->verify_certificate != NULL) {- const char *server_name = NULL;- if (!ptls_is_server(tls)) {- if (tls->ech.offered && !ptls_is_ech_handshake(tls, NULL, NULL, NULL)) {- server_name = tls->ech.client.public_name;- } else {- server_name = tls->server_name;- }- }- if ((ret = tls->ctx->verify_certificate->cb(tls->ctx->verify_certificate, tls, server_name, &tls->certificate_verify.cb,- &tls->certificate_verify.verify_ctx, certs, num_certs)) != 0)- goto Exit;- }-- *got_certs = num_certs != 0;--Exit:- return ret;-}--static int client_do_handle_certificate(ptls_t *tls, const uint8_t *src, const uint8_t *end)-{- int got_certs, ret;-- if ((ret = handle_certificate(tls, src, end, &got_certs)) != 0)- return ret;- if (!got_certs)- return PTLS_ALERT_ILLEGAL_PARAMETER;-- return 0;-}--static int client_handle_certificate(ptls_t *tls, ptls_iovec_t message)-{- int ret;-- if ((ret = client_do_handle_certificate(tls, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len)) != 0)- return ret;-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);-- tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY;- return PTLS_ERROR_IN_PROGRESS;-}--static int client_handle_compressed_certificate(ptls_t *tls, ptls_iovec_t message)-{- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;- uint16_t algo;- uint32_t uncompressed_size;- uint8_t *uncompressed = NULL;- int ret;-- if (tls->ctx->decompress_certificate == NULL) {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- goto Exit;- }-- /* decode */- if ((ret = ptls_decode16(&algo, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode24(&uncompressed_size, &src, end)) != 0)- goto Exit;- if (uncompressed_size > 65536) { /* TODO find a sensible number */- ret = PTLS_ALERT_BAD_CERTIFICATE;- goto Exit;- }- if ((uncompressed = malloc(uncompressed_size)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- ptls_decode_block(src, end, 3, {- if ((ret = tls->ctx->decompress_certificate->cb(tls->ctx->decompress_certificate, tls, algo,- ptls_iovec_init(uncompressed, uncompressed_size),- ptls_iovec_init(src, end - src))) != 0)- goto Exit;- src = end;- });-- /* handle */- if ((ret = client_do_handle_certificate(tls, uncompressed, uncompressed + uncompressed_size)) != 0)- goto Exit;-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);- tls->state = PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY;- ret = PTLS_ERROR_IN_PROGRESS;--Exit:- free(uncompressed);- return ret;-}--static int server_handle_certificate(ptls_t *tls, ptls_iovec_t message)-{- int got_certs, ret;-- if ((ret = handle_certificate(tls, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, &got_certs)) != 0)- return ret;-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);-- if (got_certs) {- tls->state = PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY;- } else {- /* Client did not provide certificate, and the verifier says we can fail open. Therefore, the next message is Finished. */- tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED;- }-- return PTLS_ERROR_IN_PROGRESS;-}--static int handle_certificate_verify(ptls_t *tls, ptls_iovec_t message, const char *context_string)-{- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;- uint16_t algo;- ptls_iovec_t signature;- uint8_t signdata[PTLS_MAX_CERTIFICATE_VERIFY_SIGNDATA_SIZE];- size_t signdata_size;- int ret;-- /* decode */- if ((ret = ptls_decode16(&algo, &src, end)) != 0)- goto Exit;- ptls_decode_block(src, end, 2, {- signature = ptls_iovec_init(src, end - src);- src = end;- });-- signdata_size = build_certificate_verify_signdata(signdata, tls->key_schedule, context_string);- if (tls->certificate_verify.cb != NULL) {- ret = tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, algo, ptls_iovec_init(signdata, signdata_size),- signature);- } else {- ret = 0;- }- ptls_clear_memory(signdata, signdata_size);- tls->certificate_verify.cb = NULL;- if (ret != 0) {- goto Exit;- }-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);--Exit:- return ret;-}--static int client_handle_certificate_verify(ptls_t *tls, ptls_iovec_t message)-{- int ret = handle_certificate_verify(tls, message, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING);-- if (ret == 0) {- tls->state = PTLS_STATE_CLIENT_EXPECT_FINISHED;- ret = PTLS_ERROR_IN_PROGRESS;- }-- return ret;-}--static int server_handle_certificate_verify(ptls_t *tls, ptls_iovec_t message)-{- int ret = handle_certificate_verify(tls, message, PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING);-- if (ret == 0) {- tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED;- ret = PTLS_ERROR_IN_PROGRESS;- }-- return ret;-}--static int client_handle_finished(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message)-{- uint8_t send_secret[PTLS_MAX_DIGEST_SIZE];- int alert_ech_required = tls->ech.offered && !ptls_is_ech_handshake(tls, NULL, NULL, NULL), ret;-- if ((ret = verify_finished(tls, message)) != 0)- goto Exit;- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);-- /* update traffic keys by using messages upto ServerFinished, but commission them after sending ClientFinished */- if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0)- goto Exit;- if ((ret = setup_traffic_protection(tls, 0, "s ap traffic", 3, 0)) != 0)- goto Exit;- if ((ret = derive_secret(tls->key_schedule, send_secret, "c ap traffic")) != 0)- goto Exit;- if ((ret = derive_exporter_secret(tls, 0)) != 0)- goto Exit;-- /* if sending early data, emit EOED and commision the client handshake traffic secret */- if (tls->pending_handshake_secret != NULL) {- assert(tls->traffic_protection.enc.aead != NULL || tls->ctx->update_traffic_key != NULL);- if (tls->client.using_early_data && !tls->ctx->omit_end_of_early_data)- ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA, {});- tls->client.using_early_data = 0;- if ((ret = commission_handshake_secret(tls)) != 0)- goto Exit;- }-- if ((ret = push_change_cipher_spec(tls, emitter)) != 0)- goto Exit;-- if (!alert_ech_required && tls->client.certificate_request.context.base != NULL) {- if ((ret = send_certificate(tls, emitter, &tls->client.certificate_request.signature_algorithms,- tls->client.certificate_request.context, 0, NULL, 0)) == 0)- ret = send_certificate_verify(tls, emitter, &tls->client.certificate_request.signature_algorithms,- PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING);- free(tls->client.certificate_request.context.base);- tls->client.certificate_request.context = ptls_iovec_init(NULL, 0);- if (ret != 0)- goto Exit;- }-- ret = send_finished(tls, emitter);-- memcpy(tls->traffic_protection.enc.secret, send_secret, sizeof(send_secret));- if ((ret = setup_traffic_protection(tls, 1, NULL, 3, 0)) != 0)- goto Exit;-- tls->state = PTLS_STATE_CLIENT_POST_HANDSHAKE;-- /* if ECH was rejected, close the connection with ECH_REQUIRED alert after verifying messages up to Finished */- if (alert_ech_required)- ret = PTLS_ALERT_ECH_REQUIRED;--Exit:- ptls_clear_memory(send_secret, sizeof(send_secret));- return ret;-}--static int client_handle_new_session_ticket(ptls_t *tls, ptls_iovec_t message)-{- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;- ptls_iovec_t ticket_nonce;- int ret;-- { /* verify the format */- uint32_t ticket_lifetime, ticket_age_add, max_early_data_size;- ptls_iovec_t ticket;- if ((ret = decode_new_session_ticket(tls, &ticket_lifetime, &ticket_age_add, &ticket_nonce, &ticket, &max_early_data_size,- src, end)) != 0)- return ret;- }-- /* do nothing if use of session ticket is disabled */- if (tls->ctx->save_ticket == NULL)- return 0;-- /* save the extension, along with the key of myself */- ptls_buffer_t ticket_buf;- ptls_buffer_init(&ticket_buf, "", 0);- ptls_buffer_push64(&ticket_buf, tls->ctx->get_time->cb(tls->ctx->get_time));- ptls_buffer_push16(&ticket_buf, tls->key_share->id);- ptls_buffer_push16(&ticket_buf, tls->cipher_suite->id);- ptls_buffer_push_block(&ticket_buf, 3, { ptls_buffer_pushv(&ticket_buf, src, end - src); });- ptls_buffer_push_block(&ticket_buf, 2, {- if ((ret = ptls_buffer_reserve(&ticket_buf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)- goto Exit;- if ((ret = derive_resumption_secret(tls->key_schedule, ticket_buf.base + ticket_buf.off, ticket_nonce)) != 0)- goto Exit;- ticket_buf.off += tls->key_schedule->hashes[0].algo->digest_size;- });-- if ((ret = tls->ctx->save_ticket->cb(tls->ctx->save_ticket, tls, ptls_iovec_init(ticket_buf.base, ticket_buf.off))) != 0)- goto Exit;-- ret = 0;-Exit:- ptls_buffer_dispose(&ticket_buf);- return ret;-}--static int client_hello_decode_server_name(ptls_iovec_t *name, const uint8_t **src, const uint8_t *const end)-{- int ret = 0;-- ptls_decode_open_block(*src, end, 2, {- do {- uint8_t type;- if ((ret = ptls_decode8(&type, src, end)) != 0)- goto Exit;- ptls_decode_open_block(*src, end, 2, {- switch (type) {- case PTLS_SERVER_NAME_TYPE_HOSTNAME:- if (memchr(*src, '\0', end - *src) != 0) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- *name = ptls_iovec_init(*src, end - *src);- break;- default:- break;- }- *src = end;- });- } while (*src != end);- });--Exit:- return ret;-}--static int select_negotiated_group(ptls_key_exchange_algorithm_t **selected, ptls_key_exchange_algorithm_t **candidates,- const uint8_t *src, const uint8_t *const end)-{- int ret;-- ptls_decode_block(src, end, 2, {- while (src != end) {- uint16_t group;- if ((ret = ptls_decode16(&group, &src, end)) != 0)- goto Exit;- ptls_key_exchange_algorithm_t **c = candidates;- for (; *c != NULL; ++c) {- if ((*c)->id == group) {- *selected = *c;- return 0;- }- }- }- });-- ret = PTLS_ALERT_HANDSHAKE_FAILURE;--Exit:- return ret;-}--static int decode_client_hello(ptls_context_t *ctx, struct st_ptls_client_hello_t *ch, const uint8_t *src, const uint8_t *const end,- ptls_handshake_properties_t *properties, ptls_t *tls_cbarg)-{- const uint8_t *start = src;- uint16_t exttype = 0;- int ret;-- /* decode protocol version (do not bare to decode something older than TLS 1.0) */- if ((ret = ptls_decode16(&ch->legacy_version, &src, end)) != 0)- goto Exit;- if (ch->legacy_version < 0x0301) {- ret = PTLS_ALERT_PROTOCOL_VERSION;- goto Exit;- }-- /* skip random */- if (end - src < PTLS_HELLO_RANDOM_SIZE) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ch->random_bytes = src;- src += PTLS_HELLO_RANDOM_SIZE;-- /* skip legacy_session_id */- ptls_decode_open_block(src, end, 1, {- if (end - src > 32) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ch->legacy_session_id = ptls_iovec_init(src, end - src);- src = end;- });-- /* decode and select from ciphersuites */- ptls_decode_open_block(src, end, 2, {- if ((end - src) % 2 != 0) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ch->cipher_suites = ptls_iovec_init(src, end - src);- src = end;- });-- /* decode legacy_compression_methods */- ptls_decode_open_block(src, end, 1, {- if (src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ch->compression_methods.ids = src;- ch->compression_methods.count = end - src;- src = end;- });-- /* CH defined in TLS versions below 1.2 might not have extensions (or they might, see what OpenSSL 1.0.0 sends); so bail out- * after parsing the main variables. Zero is returned as it is a valid ClientHello. However `ptls_t::selected_version` remains- * zero indicating that no compatible version were found. */- if (ch->legacy_version < 0x0303 && src == end) {- ret = 0;- goto Exit;- }-- /* decode extensions */- ch->first_extension_at = src - start + 2;- decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, &exttype, {- ch->psk.is_last_extension = 0;- if (ctx->on_extension != NULL && tls_cbarg != NULL &&- (ret = ctx->on_extension->cb(ctx->on_extension, tls_cbarg, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, exttype,- ptls_iovec_init(src, end - src)) != 0))- goto Exit;- switch (exttype) {- case PTLS_EXTENSION_TYPE_SERVER_NAME:- if ((ret = client_hello_decode_server_name(&ch->server_name, &src, end)) != 0)- goto Exit;- if (src != end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- break;- case PTLS_EXTENSION_TYPE_ALPN:- ptls_decode_block(src, end, 2, {- do {- ptls_decode_open_block(src, end, 1, {- /* rfc7301 3.1: empty strings MUST NOT be included */- if (src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if (ch->alpn.count < PTLS_ELEMENTSOF(ch->alpn.list))- ch->alpn.list[ch->alpn.count++] = ptls_iovec_init(src, end - src);- src = end;- });- } while (src != end);- });- break;- case PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE:- ptls_decode_block(src, end, 1, {- size_t list_size = end - src;-- /* RFC7250 4.1: No empty list, no list with single x509 element */- if (list_size == 0 || (list_size == 1 && *src == PTLS_CERTIFICATE_TYPE_X509)) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }-- do {- if (ch->server_certificate_types.count < PTLS_ELEMENTSOF(ch->server_certificate_types.list))- ch->server_certificate_types.list[ch->server_certificate_types.count++] = *src;- src++;- } while (src != end);- });- break;- case PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE:- ptls_decode_block(src, end, 1, {- do {- uint16_t id;- if ((ret = ptls_decode16(&id, &src, end)) != 0)- goto Exit;- if (ch->cert_compression_algos.count < PTLS_ELEMENTSOF(ch->cert_compression_algos.list))- ch->cert_compression_algos.list[ch->cert_compression_algos.count++] = id;- } while (src != end);- });- break;- case PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS:- ch->negotiated_groups = ptls_iovec_init(src, end - src);- break;- case PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS:- if ((ret = decode_signature_algorithms(&ch->signature_algorithms, &src, end)) != 0)- goto Exit;- break;- case PTLS_EXTENSION_TYPE_KEY_SHARE:- ch->key_shares = ptls_iovec_init(src, end - src);- break;- case PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS:- ptls_decode_block(src, end, 1, {- size_t selected_index = PTLS_ELEMENTSOF(supported_versions);- do {- size_t i;- uint16_t v;- if ((ret = ptls_decode16(&v, &src, end)) != 0)- goto Exit;- for (i = 0; i != selected_index; ++i) {- if (supported_versions[i] == v) {- selected_index = i;- break;- }- }- } while (src != end);- if (selected_index != PTLS_ELEMENTSOF(supported_versions))- ch->selected_version = supported_versions[selected_index];- });- break;- case PTLS_EXTENSION_TYPE_COOKIE:- if (properties == NULL || properties->server.cookie.key == NULL) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- ch->cookie.all = ptls_iovec_init(src, end - src);- ptls_decode_block(src, end, 2, {- ch->cookie.tbs.base = (void *)src;- ptls_decode_open_block(src, end, 2, {- ptls_decode_open_block(src, end, 1, {- ch->cookie.ch1_hash = ptls_iovec_init(src, end - src);- src = end;- });- uint8_t sent_key_share;- if ((ret = ptls_decode8(&sent_key_share, &src, end)) != 0)- goto Exit;- switch (sent_key_share) {- case 0:- assert(!ch->cookie.sent_key_share);- break;- case 1:- ch->cookie.sent_key_share = 1;- break;- default:- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- });- ch->cookie.tbs.len = src - ch->cookie.tbs.base;- ptls_decode_block(src, end, 1, {- ch->cookie.signature = ptls_iovec_init(src, end - src);- src = end;- });- });- break;- case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY: {- size_t num_identities = 0;- ptls_decode_open_block(src, end, 2, {- do {- struct st_ptls_client_hello_psk_t psk = {{NULL}};- ptls_decode_open_block(src, end, 2, {- psk.identity = ptls_iovec_init(src, end - src);- src = end;- });- if ((ret = ptls_decode32(&psk.obfuscated_ticket_age, &src, end)) != 0)- goto Exit;- if (ch->psk.identities.count < PTLS_ELEMENTSOF(ch->psk.identities.list))- ch->psk.identities.list[ch->psk.identities.count++] = psk;- ++num_identities;- } while (src != end);- });- ch->psk.hash_end = src;- ptls_decode_block(src, end, 2, {- size_t num_binders = 0;- do {- ptls_decode_open_block(src, end, 1, {- if (num_binders < ch->psk.identities.count)- ch->psk.identities.list[num_binders].binder = ptls_iovec_init(src, end - src);- src = end;- });- ++num_binders;- } while (src != end);- if (num_identities != num_binders) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- });- ch->psk.is_last_extension = 1;- } break;- case PTLS_EXTENSION_TYPE_PSK_KEY_EXCHANGE_MODES:- ptls_decode_block(src, end, 1, {- do {- uint8_t mode;- if ((ret = ptls_decode8(&mode, &src, end)) != 0)- goto Exit;- if (mode < sizeof(ch->psk.ke_modes) * 8)- ch->psk.ke_modes |= 1u << mode;- } while (src != end);- });- break;- case PTLS_EXTENSION_TYPE_EARLY_DATA:- ch->psk.early_data_indication = 1;- break;- case PTLS_EXTENSION_TYPE_STATUS_REQUEST:- ch->status_request = 1;- break;- case PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO:- if ((ret = ptls_decode8(&ch->ech.type, &src, end)) != 0)- goto Exit;- switch (ch->ech.type) {- case PTLS_ECH_CLIENT_HELLO_TYPE_OUTER:- if ((ret = ptls_decode16(&ch->ech.cipher_suite.kdf, &src, end)) != 0 ||- (ret = ptls_decode16(&ch->ech.cipher_suite.aead, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode8(&ch->ech.config_id, &src, end)) != 0)- goto Exit;- ptls_decode_open_block(src, end, 2, {- ch->ech.enc = ptls_iovec_init(src, end - src);- src = end;- });- ptls_decode_open_block(src, end, 2, {- if (src == end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ch->ech.payload = ptls_iovec_init(src, end - src);- src = end;- });- break;- case PTLS_ECH_CLIENT_HELLO_TYPE_INNER:- if (src != end) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ch->ech.payload = ptls_iovec_init("", 0); /* non-zero base indicates that the extension was received */- break;- default:- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- src = end;- break;- default:- if (tls_cbarg != NULL && should_collect_unknown_extension(tls_cbarg, properties, exttype)) {- if ((ret = collect_unknown_extension(tls_cbarg, exttype, src, end, ch->unknown_extensions)) != 0)- goto Exit;- }- break;- }- src = end;- });-- ret = 0;-Exit:- return ret;-}--static int rebuild_ch_inner_extensions(ptls_buffer_t *buf, const uint8_t **src, const uint8_t *const end, const uint8_t *outer_ext,- const uint8_t *outer_ext_end)-{- int ret;-- ptls_buffer_push_block(buf, 2, {- ptls_decode_open_block(*src, end, 2, {- while (*src != end) {- uint16_t exttype;- if ((ret = ptls_decode16(&exttype, src, end)) != 0)- goto Exit;- ptls_decode_open_block(*src, end, 2, {- if (exttype == PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS) {- ptls_decode_open_block(*src, end, 1, {- do {- uint16_t reftype;- uint16_t outertype;- uint16_t outersize;- if ((ret = ptls_decode16(&reftype, src, end)) != 0)- goto Exit;- if (reftype == PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- while (1) {- if (ptls_decode16(&outertype, &outer_ext, outer_ext_end) != 0 ||- ptls_decode16(&outersize, &outer_ext, outer_ext_end) != 0) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- assert(outer_ext_end - outer_ext >= outersize);- if (outertype == reftype)- break;- outer_ext += outersize;- }- buffer_push_extension(buf, reftype, {- ptls_buffer_pushv(buf, outer_ext, outersize);- outer_ext += outersize;- });- } while (*src != end);- });- } else {- buffer_push_extension(buf, exttype, {- ptls_buffer_pushv(buf, *src, end - *src);- *src = end;- });- }- });- }- });- });--Exit:- return ret;-}--static int rebuild_ch_inner(ptls_buffer_t *buf, const uint8_t *src, const uint8_t *const end,- struct st_ptls_client_hello_t *outer_ch, const uint8_t *outer_ext, const uint8_t *outer_ext_end)-{-#define COPY_BLOCK(capacity) \- do { \- ptls_decode_open_block(src, end, (capacity), { \- ptls_buffer_push_block(buf, (capacity), { ptls_buffer_pushv(buf, src, end - src); }); \- src = end; \- }); \- } while (0)-- int ret;-- ptls_buffer_push_message_body(buf, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, {- { /* legacy_version */- uint16_t legacy_version;- if ((ret = ptls_decode16(&legacy_version, &src, end)) != 0)- goto Exit;- ptls_buffer_push16(buf, legacy_version);- }-- /* hello random */- if (end - src < PTLS_HELLO_RANDOM_SIZE) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- ptls_buffer_pushv(buf, src, PTLS_HELLO_RANDOM_SIZE);- src += PTLS_HELLO_RANDOM_SIZE;-- ptls_decode_open_block(src, end, 1, {- if (src != end) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- });- ptls_buffer_push_block(buf, 1,- { ptls_buffer_pushv(buf, outer_ch->legacy_session_id.base, outer_ch->legacy_session_id.len); });-- /* cipher-suites and legacy-compression-methods */- COPY_BLOCK(2);- COPY_BLOCK(1);-- /* extensions */- if ((ret = rebuild_ch_inner_extensions(buf, &src, end, outer_ext, outer_ext_end)) != 0)- goto Exit;- });-- /* padding must be all zero */- for (; src != end; ++src) {- if (*src != '\0') {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }--Exit:- return ret;--#undef COPY_BLOCK-}--/* Wrapper function for invoking the on_client_hello callback, taking an exhaustive list of parameters as arguments. The intention- * is to not miss setting them as we add new parameters to the struct. */-static inline int call_on_client_hello_cb(ptls_t *tls, ptls_iovec_t server_name, ptls_iovec_t raw_message,- ptls_iovec_t cipher_suites, ptls_iovec_t *alpns, size_t num_alpns,- const uint16_t *sig_algos, size_t num_sig_algos, const uint16_t *cert_comp_algos,- size_t num_cert_comp_algos, const uint8_t *server_cert_types,- size_t num_server_cert_types, int incompatible_version)-{- if (tls->ctx->on_client_hello == NULL)- return 0;-- ptls_on_client_hello_parameters_t params = {server_name,- raw_message,- cipher_suites,- {alpns, num_alpns},- {sig_algos, num_sig_algos},- {cert_comp_algos, num_cert_comp_algos},- {server_cert_types, num_server_cert_types},- incompatible_version};- return tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, ¶ms);-}--static int check_client_hello_constraints(ptls_context_t *ctx, struct st_ptls_client_hello_t *ch, const void *prev_random,- int ech_is_inner_ch, ptls_iovec_t raw_message, ptls_t *tls_cbarg)-{- int is_second_flight = prev_random != 0;-- /* The following check is necessary so that we would be able to track the connection in SSLKEYLOGFILE, even though it might not- * be for the safety of the protocol. */- if (is_second_flight && !ptls_mem_equal(ch->random_bytes, prev_random, PTLS_HELLO_RANDOM_SIZE))- return PTLS_ALERT_HANDSHAKE_FAILURE;-- /* bail out if CH cannot be handled as TLS 1.3 */- if (!is_supported_version(ch->selected_version)) {- /* ECH: server MUST abort with an "illegal_parameter" alert if the client offers TLS 1.2 or below (draft-15 7.1) */- if (ech_is_inner_ch)- return PTLS_ALERT_ILLEGAL_PARAMETER;- /* fail with PROTOCOL_VERSION alert, after providing the applications the raw CH and SNI to help them fallback */- if (!is_second_flight) {- int ret;- if ((ret = call_on_client_hello_cb(tls_cbarg, ch->server_name, raw_message, ch->cipher_suites, ch->alpn.list,- ch->alpn.count, NULL, 0, NULL, 0, NULL, 0, 1)) != 0)- return ret;- }- return PTLS_ALERT_PROTOCOL_VERSION;- }-- /* Check TLS 1.3-specific constraints. Hereafter, we might exit without calling on_client_hello. That's fine because this CH is- * ought to be rejected. */- if (ch->legacy_version <= 0x0300) {- /* RFC 8446 Appendix D.5: any endpoint receiving a Hello message with legacy_version set to 0x0300 MUST abort the handshake- * with a "protocol_version" alert. */- return PTLS_ALERT_PROTOCOL_VERSION;- }- if (!(ch->compression_methods.count == 1 && ch->compression_methods.ids[0] == 0))- return PTLS_ALERT_ILLEGAL_PARAMETER;- /* pre-shared key */- if (ch->psk.hash_end != NULL) {- /* PSK must be the last extension */- if (!ch->psk.is_last_extension)- return PTLS_ALERT_ILLEGAL_PARAMETER;- } else {- if (ch->psk.early_data_indication)- return PTLS_ALERT_ILLEGAL_PARAMETER;- }-- if (ech_is_inner_ch && ch->ech.payload.base == NULL)- return PTLS_ALERT_ILLEGAL_PARAMETER;- if (ch->ech.payload.base != NULL &&- ch->ech.type != (ech_is_inner_ch ? PTLS_ECH_CLIENT_HELLO_TYPE_INNER : PTLS_ECH_CLIENT_HELLO_TYPE_OUTER))- return PTLS_ALERT_ILLEGAL_PARAMETER;-- return 0;-}--static int vec_is_string(ptls_iovec_t x, const char *y)-{- return strncmp((const char *)x.base, y, x.len) == 0 && y[x.len] == '\0';-}--static int try_psk_handshake(ptls_t *tls, size_t *psk_index, int *accept_early_data, struct st_ptls_client_hello_t *ch,- ptls_iovec_t ch_trunc)-{- ptls_buffer_t decbuf;- ptls_iovec_t ticket_psk, ticket_server_name, ticket_negotiated_protocol;- uint64_t issue_at, now = tls->ctx->get_time->cb(tls->ctx->get_time);- uint32_t age_add;- uint16_t ticket_key_exchange_id, ticket_csid;- uint8_t binder_key[PTLS_MAX_DIGEST_SIZE];- int ret;-- ptls_buffer_init(&decbuf, "", 0);-- for (*psk_index = 0; *psk_index < ch->psk.identities.count; ++*psk_index) {- struct st_ptls_client_hello_psk_t *identity = ch->psk.identities.list + *psk_index;- /* decrypt and decode */- int can_accept_early_data = 1;- decbuf.off = 0;- switch (tls->ctx->encrypt_ticket->cb(tls->ctx->encrypt_ticket, tls, 0, &decbuf, identity->identity)) {- case 0: /* decrypted */- break;- case PTLS_ERROR_REJECT_EARLY_DATA: /* decrypted, but early data is rejected */- can_accept_early_data = 0;- break;- default: /* decryption failure */- continue;- }- if (decode_session_identifier(&issue_at, &ticket_psk, &age_add, &ticket_server_name, &ticket_key_exchange_id, &ticket_csid,- &ticket_negotiated_protocol, decbuf.base, decbuf.base + decbuf.off) != 0)- continue;- /* check age */- if (now < issue_at)- continue;- if (now - issue_at > (uint64_t)tls->ctx->ticket_lifetime * 1000)- continue;- *accept_early_data = 0;- if (ch->psk.early_data_indication && can_accept_early_data) {- /* accept early-data if abs(diff) between the reported age and the actual age is within += 10 seconds */- int64_t delta = (now - issue_at) - (identity->obfuscated_ticket_age - age_add);- if (delta < 0)- delta = -delta;- if (tls->ctx->max_early_data_size != 0 && delta <= PTLS_EARLY_DATA_MAX_DELAY)- *accept_early_data = 1;- }- /* check server-name */- if (ticket_server_name.len != 0) {- if (tls->server_name == NULL)- continue;- if (!vec_is_string(ticket_server_name, tls->server_name))- continue;- } else {- if (tls->server_name != NULL)- continue;- }- { /* check key-exchange */- ptls_key_exchange_algorithm_t **a;- for (a = tls->ctx->key_exchanges; *a != NULL && (*a)->id != ticket_key_exchange_id; ++a)- ;- if (*a == NULL)- continue;- tls->key_share = *a;- }- /* check cipher-suite */- if (ticket_csid != tls->cipher_suite->id)- continue;- /* check negotiated-protocol */- if (ticket_negotiated_protocol.len != 0) {- if (tls->negotiated_protocol == NULL)- continue;- if (!vec_is_string(ticket_negotiated_protocol, tls->negotiated_protocol))- continue;- }- /* check the length of the decrypted psk and the PSK binder */- if (ticket_psk.len != tls->key_schedule->hashes[0].algo->digest_size)- continue;- if (ch->psk.identities.list[*psk_index].binder.len != tls->key_schedule->hashes[0].algo->digest_size)- continue;-- /* found */- goto Found;- }-- /* not found */- *psk_index = SIZE_MAX;- *accept_early_data = 0;- tls->key_share = NULL;- ret = 0;- goto Exit;--Found:- if ((ret = key_schedule_extract(tls->key_schedule, ticket_psk)) != 0)- goto Exit;- if ((ret = derive_secret(tls->key_schedule, binder_key, "res binder")) != 0)- goto Exit;- ptls__key_schedule_update_hash(tls->key_schedule, ch_trunc.base, ch_trunc.len, 0);- if ((ret = calc_verify_data(binder_key /* to conserve space, reuse binder_key for storing verify_data */, tls->key_schedule,- binder_key)) != 0)- goto Exit;- if (!ptls_mem_equal(ch->psk.identities.list[*psk_index].binder.base, binder_key,- tls->key_schedule->hashes[0].algo->digest_size)) {- ret = PTLS_ALERT_DECRYPT_ERROR;- goto Exit;- }- ret = 0;--Exit:- ptls_buffer_dispose(&decbuf);- ptls_clear_memory(binder_key, sizeof(binder_key));- return ret;-}--static int calc_cookie_signature(ptls_t *tls, ptls_handshake_properties_t *properties,- ptls_key_exchange_algorithm_t *negotiated_group, ptls_iovec_t tbs, uint8_t *sig)-{- ptls_hash_algorithm_t *algo = tls->ctx->cipher_suites[0]->hash;- ptls_hash_context_t *hctx;-- if ((hctx = ptls_hmac_create(algo, properties->server.cookie.key, algo->digest_size)) == NULL)- return PTLS_ERROR_NO_MEMORY;--#define UPDATE_BLOCK(p, _len) \- do { \- size_t len = (_len); \- assert(len < UINT8_MAX); \- uint8_t len8 = (uint8_t)len; \- hctx->update(hctx, &len8, 1); \- hctx->update(hctx, (p), len); \- } while (0)-#define UPDATE16(_v) \- do { \- uint16_t v = (_v); \- uint8_t b[2] = {v >> 8, v & 0xff}; \- hctx->update(hctx, b, 2); \- } while (0)-- UPDATE_BLOCK(tls->client_random, sizeof(tls->client_random));- UPDATE_BLOCK(tls->server_name, tls->server_name != NULL ? strlen(tls->server_name) : 0);- UPDATE16(tls->cipher_suite->id);- UPDATE16(negotiated_group->id);- UPDATE_BLOCK(properties->server.cookie.additional_data.base, properties->server.cookie.additional_data.len);-- UPDATE_BLOCK(tbs.base, tbs.len);--#undef UPDATE_BLOCK-#undef UPDATE16-- hctx->final(hctx, sig, PTLS_HASH_FINAL_MODE_FREE);- return 0;-}--static int certificate_type_exists(uint8_t *list, size_t count, uint8_t desired_type)-{- /* empty type list means that we default to x509 */- if (desired_type == PTLS_CERTIFICATE_TYPE_X509 && count == 0)- return 1;- for (size_t i = 0; i < count; i++) {- if (list[i] == desired_type)- return 1;- }- return 0;-}--static int server_handle_hello(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message,- ptls_handshake_properties_t *properties)-{-#define EMIT_SERVER_HELLO(sched, fill_rand, extensions, post_action) \- do { \- size_t sh_start_off; \- ptls_push_message(emitter, NULL, PTLS_HANDSHAKE_TYPE_SERVER_HELLO, { \- sh_start_off = emitter->buf->off - PTLS_HANDSHAKE_HEADER_SIZE; \- ptls_buffer_push16(emitter->buf, 0x0303 /* legacy version */); \- if ((ret = ptls_buffer_reserve(emitter->buf, PTLS_HELLO_RANDOM_SIZE)) != 0) \- goto Exit; \- do { \- fill_rand \- } while (0); \- emitter->buf->off += PTLS_HELLO_RANDOM_SIZE; \- ptls_buffer_push_block(emitter->buf, 1, \- { ptls_buffer_pushv(emitter->buf, ch->legacy_session_id.base, ch->legacy_session_id.len); }); \- ptls_buffer_push16(emitter->buf, tls->cipher_suite->id); \- ptls_buffer_push(emitter->buf, 0); \- ptls_buffer_push_block(emitter->buf, 2, { \- buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_SUPPORTED_VERSIONS, \- { ptls_buffer_push16(emitter->buf, ch->selected_version); }); \- do { \- extensions \- } while (0); \- }); \- }); \- do { \- post_action \- } while (0); \- ptls__key_schedule_update_hash((sched), emitter->buf->base + sh_start_off, emitter->buf->off - sh_start_off, 0); \- } while (0)--#define EMIT_HELLO_RETRY_REQUEST(sched, negotiated_group, additional_extensions, post_action) \- EMIT_SERVER_HELLO((sched), { memcpy(emitter->buf->base + emitter->buf->off, hello_retry_random, PTLS_HELLO_RANDOM_SIZE); }, \- { \- ptls_key_exchange_algorithm_t *_negotiated_group = (negotiated_group); \- if (_negotiated_group != NULL) { \- buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_KEY_SHARE, \- { ptls_buffer_push16(emitter->buf, _negotiated_group->id); }); \- } \- do { \- additional_extensions \- } while (0); \- }, \- post_action)- struct st_ptls_client_hello_t *ch;- struct {- ptls_key_exchange_algorithm_t *algorithm;- ptls_iovec_t peer_key;- } key_share = {NULL};- struct {- uint8_t *encoded_ch_inner;- uint8_t *ch_outer_aad;- ptls_buffer_t ch_inner;- } ech = {NULL};- enum { HANDSHAKE_MODE_FULL, HANDSHAKE_MODE_PSK, HANDSHAKE_MODE_PSK_DHE } mode;- size_t psk_index = SIZE_MAX;- ptls_iovec_t pubkey = {0}, ecdh_secret = {0};- int accept_early_data = 0, is_second_flight = tls->state == PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO, ret;-- ptls_buffer_init(&ech.ch_inner, "", 0);-- if ((ch = malloc(sizeof(*ch))) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }-- *ch = (struct st_ptls_client_hello_t){.unknown_extensions = {{UINT16_MAX}}};-- /* decode ClientHello */- if ((ret = decode_client_hello(tls->ctx, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties,- tls)) != 0)- goto Exit;- if ((ret = check_client_hello_constraints(tls->ctx, ch, is_second_flight ? tls->client_random : NULL, 0, message, tls)) != 0)- goto Exit;- if (!is_second_flight) {- memcpy(tls->client_random, ch->random_bytes, PTLS_HELLO_RANDOM_SIZE);- log_client_random(tls);- } else {- /* consistency check for ECH extension in response to HRR */- if (tls->ech.aead != NULL) {- if (ch->ech.payload.base == NULL) {- ret = PTLS_ALERT_MISSING_EXTENSION;- goto Exit;- }- if (!(ch->ech.config_id == tls->ech.config_id && ch->ech.cipher_suite.kdf == tls->ech.cipher->id.kdf &&- ch->ech.cipher_suite.aead == tls->ech.cipher->id.aead && ch->ech.enc.len == 0)) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- }- }-- /* ECH */- if (ch->ech.payload.base != NULL) {- if (ch->ech.type != PTLS_ECH_CLIENT_HELLO_TYPE_OUTER) {- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }- if (!is_second_flight)- tls->ech.offered = 1;- /* obtain AEAD context for opening inner CH */- if (!is_second_flight && ch->ech.payload.base != NULL && tls->ctx->ech.server.create_opener != NULL) {- if ((tls->ech.aead = tls->ctx->ech.server.create_opener->cb(- tls->ctx->ech.server.create_opener, &tls->ech.kem, &tls->ech.cipher, tls, ch->ech.config_id,- ch->ech.cipher_suite, ch->ech.enc, ptls_iovec_init(ech_info_prefix, sizeof(ech_info_prefix)))) != NULL)- tls->ech.config_id = ch->ech.config_id;- }- if (!is_second_flight) {- PTLS_PROBE(ECH_SELECTION, tls, tls->ech.aead != NULL);- PTLS_LOG_CONN(ech_selection, tls, { PTLS_LOG_ELEMENT_BOOL(is_ech, tls->ech.aead != NULL); });- }- if (tls->ech.aead != NULL) {- /* now that AEAD context is available, create AAD and decrypt inner CH */- if ((ech.encoded_ch_inner = malloc(ch->ech.payload.len - tls->ech.aead->algo->tag_size)) == NULL ||- (ech.ch_outer_aad = malloc(message.len - PTLS_HANDSHAKE_HEADER_SIZE)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- memcpy(ech.ch_outer_aad, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.len - PTLS_HANDSHAKE_HEADER_SIZE);- memset(ech.ch_outer_aad + (ch->ech.payload.base - (message.base + PTLS_HANDSHAKE_HEADER_SIZE)), 0, ch->ech.payload.len);- if (ptls_aead_decrypt(tls->ech.aead, ech.encoded_ch_inner, ch->ech.payload.base, ch->ech.payload.len, is_second_flight,- ech.ch_outer_aad, message.len - PTLS_HANDSHAKE_HEADER_SIZE) != SIZE_MAX) {- tls->ech.accepted = 1;- /* successfully decrypted EncodedCHInner, build CHInner */- if ((ret = rebuild_ch_inner(&ech.ch_inner, ech.encoded_ch_inner,- ech.encoded_ch_inner + ch->ech.payload.len - tls->ech.aead->algo->tag_size, ch,- message.base + PTLS_HANDSHAKE_HEADER_SIZE + ch->first_extension_at,- message.base + message.len)) != 0)- goto Exit;- /* treat inner ch as the message being received, re-decode it */- message = ptls_iovec_init(ech.ch_inner.base, ech.ch_inner.off);- *ch = (struct st_ptls_client_hello_t){.unknown_extensions = {{UINT16_MAX}}};- if ((ret = decode_client_hello(tls->ctx, ch, ech.ch_inner.base + PTLS_HANDSHAKE_HEADER_SIZE,- ech.ch_inner.base + ech.ch_inner.off, properties, tls)) != 0)- goto Exit;- if ((ret = check_client_hello_constraints(tls->ctx, ch, is_second_flight ? tls->ech.inner_client_random : NULL, 1,- message, tls)) != 0)- goto Exit;- if (!is_second_flight)- memcpy(tls->ech.inner_client_random, ch->random_bytes, PTLS_HELLO_RANDOM_SIZE);- } else if (is_second_flight) {- /* decryption failure of inner CH in 2nd CH is fatal */- ret = PTLS_ALERT_DECRYPT_ERROR;- goto Exit;- } else {- /* decryption failure of 1st CH indicates key mismatch; dispose of AEAD context to indicate adoption of outerCH */- ptls_aead_free(tls->ech.aead);- tls->ech.aead = NULL;- }- }- } else if (tls->ech.offered) {- assert(is_second_flight);- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- goto Exit;- }-- if (tls->ctx->require_dhe_on_psk)- ch->psk.ke_modes &= ~(1u << PTLS_PSK_KE_MODE_PSK);-- /* handle client_random, legacy_session_id, SNI, ESNI */- if (!is_second_flight) {- if (ch->legacy_session_id.len != 0)- tls->send_change_cipher_spec = 1;- ptls_iovec_t server_name = {NULL};- if (ch->server_name.base != NULL)- server_name = ch->server_name;- if ((ret = call_on_client_hello_cb(tls, server_name, message, ch->cipher_suites, ch->alpn.list, ch->alpn.count,- ch->signature_algorithms.list, ch->signature_algorithms.count,- ch->cert_compression_algos.list, ch->cert_compression_algos.count,- ch->server_certificate_types.list, ch->server_certificate_types.count, 0)) != 0)- goto Exit;- if (!certificate_type_exists(ch->server_certificate_types.list, ch->server_certificate_types.count,- tls->ctx->use_raw_public_keys ? PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY- : PTLS_CERTIFICATE_TYPE_X509)) {- ret = PTLS_ALERT_UNSUPPORTED_CERTIFICATE;- goto Exit;- }- } else {- if (ch->psk.early_data_indication) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- /* We compare SNI only when the value is saved by the on_client_hello callback. This should be OK because we are- * ignoring the value unless the callback saves the server-name. */- if (tls->server_name != NULL) {- size_t l = strlen(tls->server_name);- if (!(ch->server_name.len == l && memcmp(ch->server_name.base, tls->server_name, l) == 0)) {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- goto Exit;- }- }- }-- { /* select (or check) cipher-suite, create key_schedule */- ptls_cipher_suite_t *cs;- if ((ret = select_cipher(&cs, tls->ctx->cipher_suites, ch->cipher_suites.base,- ch->cipher_suites.base + ch->cipher_suites.len, tls->ctx->server_cipher_preference, tls->ctx->server_cipher_chacha_priority)) != 0)- goto Exit;- if (!is_second_flight) {- tls->cipher_suite = cs;- tls->key_schedule = key_schedule_new(cs, NULL, 0);- } else {- if (tls->cipher_suite != cs) {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- goto Exit;- }- }- }-- /* select key_share */- if (key_share.algorithm == NULL && ch->key_shares.base != NULL) {- const uint8_t *src = ch->key_shares.base, *const end = src + ch->key_shares.len;- ptls_decode_block(src, end, 2, {- if ((ret = select_key_share(&key_share.algorithm, &key_share.peer_key, tls->ctx->key_exchanges, &src, end, 0)) != 0)- goto Exit;- });- }-- if (!is_second_flight) {- if (ch->cookie.all.len != 0 && key_share.algorithm != NULL) {-- { /* use cookie to check the integrity of the handshake, and update the context */- uint8_t sig[PTLS_MAX_DIGEST_SIZE];- size_t sigsize = tls->ctx->cipher_suites[0]->hash->digest_size;- if ((ret = calc_cookie_signature(tls, properties, key_share.algorithm, ch->cookie.tbs, sig)) != 0)- goto Exit;- if (!(ch->cookie.signature.len == sigsize && ptls_mem_equal(ch->cookie.signature.base, sig, sigsize))) {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- goto Exit;- }- }- /* integrity check passed; update states */- key_schedule_update_ch1hash_prefix(tls->key_schedule);- ptls__key_schedule_update_hash(tls->key_schedule, ch->cookie.ch1_hash.base, ch->cookie.ch1_hash.len, 0);- key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0));- /* ... reusing sendbuf to rebuild HRR for hash calculation */- size_t hrr_start = emitter->buf->off;- EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, ch->cookie.sent_key_share ? key_share.algorithm : NULL,- {- buffer_push_extension(emitter->buf, PTLS_EXTENSION_TYPE_COOKIE, {- ptls_buffer_pushv(emitter->buf, ch->cookie.all.base, ch->cookie.all.len);- });- },- {});- emitter->buf->off = hrr_start;- is_second_flight = 1;-- } else if (key_share.algorithm == NULL || (properties != NULL && properties->server.enforce_retry)) {-- /* send HelloRetryRequest */- if (ch->negotiated_groups.base == NULL) {- ret = PTLS_ALERT_MISSING_EXTENSION;- goto Exit;- }- ptls_key_exchange_algorithm_t *negotiated_group;- if ((ret = select_negotiated_group(&negotiated_group, tls->ctx->key_exchanges, ch->negotiated_groups.base,- ch->negotiated_groups.base + ch->negotiated_groups.len)) != 0)- goto Exit;- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);- assert(tls->key_schedule->generation == 0);-- /* Either send a stateless retry (w. cookies) or a stateful one. When sending the latter, run the state machine. At the- * moment, stateless retry is disabled when ECH is used (do we need to support it?). */- int retry_uses_cookie =- properties != NULL && properties->server.retry_uses_cookie && !ptls_is_ech_handshake(tls, NULL, NULL, NULL);- if (!retry_uses_cookie) {- key_schedule_transform_post_ch1hash(tls->key_schedule);- key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0));- }- size_t ech_confirm_off = 0;- EMIT_HELLO_RETRY_REQUEST(- tls->key_schedule, key_share.algorithm != NULL ? NULL : negotiated_group,- {- ptls_buffer_t *sendbuf = emitter->buf;- if (ptls_is_ech_handshake(tls, NULL, NULL, NULL)) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO, {- if ((ret = ptls_buffer_reserve(sendbuf, PTLS_ECH_CONFIRM_LENGTH)) != 0)- goto Exit;- memset(sendbuf->base + sendbuf->off, 0, PTLS_ECH_CONFIRM_LENGTH);- ech_confirm_off = sendbuf->off;- sendbuf->off += PTLS_ECH_CONFIRM_LENGTH;- });- }- if (retry_uses_cookie) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COOKIE, {- ptls_buffer_push_block(sendbuf, 2, {- /* push to-be-signed data */- size_t tbs_start = sendbuf->off;- ptls_buffer_push_block(sendbuf, 2, {- /* first block of the cookie data is the hash(ch1) */- ptls_buffer_push_block(sendbuf, 1, {- size_t sz = tls->cipher_suite->hash->digest_size;- if ((ret = ptls_buffer_reserve(sendbuf, sz)) != 0)- goto Exit;- key_schedule_extract_ch1hash(tls->key_schedule, sendbuf->base + sendbuf->off);- sendbuf->off += sz;- });- /* second is if we have sent key_share extension */- ptls_buffer_push(sendbuf, key_share.algorithm == NULL);- /* we can add more data here */- });- size_t tbs_len = sendbuf->off - tbs_start;- /* push the signature */- ptls_buffer_push_block(sendbuf, 1, {- size_t sz = tls->ctx->cipher_suites[0]->hash->digest_size;- if ((ret = ptls_buffer_reserve(sendbuf, sz)) != 0)- goto Exit;- if ((ret = calc_cookie_signature(tls, properties, negotiated_group,- ptls_iovec_init(sendbuf->base + tbs_start, tbs_len),- sendbuf->base + sendbuf->off)) != 0)- goto Exit;- sendbuf->off += sz;- });- });- });- }- },- {- if (ech_confirm_off != 0 &&- (ret = ech_calc_confirmation(- tls->key_schedule, emitter->buf->base + ech_confirm_off, tls->ech.inner_client_random,- ECH_CONFIRMATION_HRR,- ptls_iovec_init(emitter->buf->base + sh_start_off, emitter->buf->off - sh_start_off))) != 0)- goto Exit;- });- if (retry_uses_cookie) {- if ((ret = push_change_cipher_spec(tls, emitter)) != 0)- goto Exit;- ret = PTLS_ERROR_STATELESS_RETRY;- } else {- tls->state = PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO;- if (ch->psk.early_data_indication)- tls->server.early_data_skipped_bytes = 0;- ret = PTLS_ERROR_IN_PROGRESS;- }- goto Exit;- }- }-- /* handle unknown extensions */- if ((ret = report_unknown_extensions(tls, properties, ch->unknown_extensions)) != 0)- goto Exit;-- /* try psk handshake */- if (!is_second_flight && ch->psk.hash_end != 0 &&- (ch->psk.ke_modes & ((1u << PTLS_PSK_KE_MODE_PSK) | (1u << PTLS_PSK_KE_MODE_PSK_DHE))) != 0 &&- tls->ctx->encrypt_ticket != NULL && !tls->ctx->require_client_authentication) {- if ((ret = try_psk_handshake(tls, &psk_index, &accept_early_data, ch,- ptls_iovec_init(message.base, ch->psk.hash_end - message.base))) != 0) {- goto Exit;- }- }-- /* If client authentication is enabled, we always force a full handshake.- * TODO: Check for `post_handshake_auth` extension and if that is present, do not force full handshake!- * Remove also the check `!require_client_authentication` above.- *- * adjust key_schedule, determine handshake mode- */- if (psk_index == SIZE_MAX || tls->ctx->require_client_authentication) {- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);- if (!is_second_flight) {- assert(tls->key_schedule->generation == 0);- key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0));- }- mode = HANDSHAKE_MODE_FULL;- if (properties != NULL)- properties->server.selected_psk_binder.len = 0;- } else {- ptls__key_schedule_update_hash(tls->key_schedule, ch->psk.hash_end, message.base + message.len - ch->psk.hash_end, 0);- if ((ch->psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK)) != 0) {- mode = HANDSHAKE_MODE_PSK;- } else {- assert((ch->psk.ke_modes & (1u << PTLS_PSK_KE_MODE_PSK_DHE)) != 0);- mode = HANDSHAKE_MODE_PSK_DHE;- }- tls->is_psk_handshake = 1;- if (properties != NULL) {- ptls_iovec_t *selected = &ch->psk.identities.list[psk_index].binder;- memcpy(properties->server.selected_psk_binder.base, selected->base, selected->len);- properties->server.selected_psk_binder.len = selected->len;- }- }- tls->server.can_send_session_ticket = ch->psk.ke_modes != 0;-- if (accept_early_data && tls->ctx->max_early_data_size != 0 && psk_index == 0) {- if ((tls->pending_handshake_secret = malloc(PTLS_MAX_DIGEST_SIZE)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- if ((ret = derive_exporter_secret(tls, 1)) != 0)- goto Exit;- if ((ret = setup_traffic_protection(tls, 0, "c e traffic", 1, 0)) != 0)- goto Exit;- }-- /* run key-exchange, to obtain pubkey and secret */- if (mode != HANDSHAKE_MODE_PSK) {- if (key_share.algorithm == NULL) {- ret = ch->key_shares.base != NULL ? PTLS_ALERT_HANDSHAKE_FAILURE : PTLS_ALERT_MISSING_EXTENSION;- goto Exit;- }- if ((ret = key_share.algorithm->exchange(key_share.algorithm, &pubkey, &ecdh_secret, key_share.peer_key)) != 0)- goto Exit;- tls->key_share = key_share.algorithm;- }-- { /* send ServerHello */- size_t ech_confirm_off = 0;- EMIT_SERVER_HELLO(- tls->key_schedule,- {- tls->ctx->random_bytes(emitter->buf->base + emitter->buf->off, PTLS_HELLO_RANDOM_SIZE);- /* when accepting CHInner, last 8 byte of SH.random is zero for the handshake transcript */- if (ptls_is_ech_handshake(tls, NULL, NULL, NULL)) {- ech_confirm_off = emitter->buf->off + PTLS_HELLO_RANDOM_SIZE - PTLS_ECH_CONFIRM_LENGTH;- memset(emitter->buf->base + ech_confirm_off, 0, PTLS_ECH_CONFIRM_LENGTH);- }- },- {- ptls_buffer_t *sendbuf = emitter->buf;- if (mode != HANDSHAKE_MODE_PSK) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, {- ptls_buffer_push16(sendbuf, key_share.algorithm->id);- ptls_buffer_push_block(sendbuf, 2, { ptls_buffer_pushv(sendbuf, pubkey.base, pubkey.len); });- });- }- if (mode != HANDSHAKE_MODE_FULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_PRE_SHARED_KEY,- { ptls_buffer_push16(sendbuf, (uint16_t)psk_index); });- }- },- {- if (ech_confirm_off != 0 &&- (ret = ech_calc_confirmation(- tls->key_schedule, emitter->buf->base + ech_confirm_off, tls->ech.inner_client_random,- ECH_CONFIRMATION_SERVER_HELLO,- ptls_iovec_init(emitter->buf->base + sh_start_off, emitter->buf->off - sh_start_off))) != 0)- goto Exit;- });- }-- /* processing of ECH is complete; dispose state */- clear_ech(&tls->ech, 1);-- if ((ret = push_change_cipher_spec(tls, emitter)) != 0)- goto Exit;-- /* create protection contexts for the handshake */- assert(tls->key_schedule->generation == 1);- key_schedule_extract(tls->key_schedule, ecdh_secret);- if ((ret = setup_traffic_protection(tls, 1, "s hs traffic", 2, 0)) != 0)- goto Exit;- if (tls->pending_handshake_secret != NULL) {- if ((ret = derive_secret(tls->key_schedule, tls->pending_handshake_secret, "c hs traffic")) != 0)- goto Exit;- if (tls->ctx->update_traffic_key != NULL &&- (ret = tls->ctx->update_traffic_key->cb(tls->ctx->update_traffic_key, tls, 0, 2, tls->pending_handshake_secret)) != 0)- goto Exit;- } else {- if ((ret = setup_traffic_protection(tls, 0, "c hs traffic", 2, 0)) != 0)- goto Exit;- if (ch->psk.early_data_indication)- tls->server.early_data_skipped_bytes = 0;- }-- /* send EncryptedExtensions */- ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS, {- ptls_buffer_t *sendbuf = emitter->buf;- ptls_buffer_push_block(sendbuf, 2, {- if (tls->server_name != NULL) {- /* In this event, the server SHALL include an extension of type "server_name" in the (extended) server hello.- * The "extension_data" field of this extension SHALL be empty. (RFC 6066 section 3) */- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {});- }- if (tls->ctx->use_raw_public_keys) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_CERTIFICATE_TYPE,- { ptls_buffer_push(sendbuf, PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY); });- }- if (tls->negotiated_protocol != NULL) {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ALPN, {- ptls_buffer_push_block(sendbuf, 2, {- ptls_buffer_push_block(sendbuf, 1, {- ptls_buffer_pushv(sendbuf, tls->negotiated_protocol, strlen(tls->negotiated_protocol));- });- });- });- }- if (tls->pending_handshake_secret != NULL)- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_EARLY_DATA, {});- /* send ECH retry_configs, if ECH was offered by rejected, even though we (the server) could have accepted ECH */- if (tls->ech.offered && !ptls_is_ech_handshake(tls, NULL, NULL, NULL) && tls->ctx->ech.server.create_opener != NULL &&- tls->ctx->ech.server.retry_configs.len != 0)- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO, {- ptls_buffer_pushv(sendbuf, tls->ctx->ech.server.retry_configs.base, tls->ctx->ech.server.retry_configs.len);- });- if ((ret = push_additional_extensions(properties, sendbuf)) != 0)- goto Exit;- });- });-- if (mode == HANDSHAKE_MODE_FULL) {- /* send certificate request if client authentication is activated */- if (tls->ctx->require_client_authentication) {- ptls_push_message(emitter, tls->key_schedule, PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST, {- /* certificate_request_context, this field SHALL be zero length, unless the certificate- * request is used for post-handshake authentication.- */- ptls_buffer_t *sendbuf = emitter->buf;- ptls_buffer_push(sendbuf, 0);- /* extensions */- ptls_buffer_push_block(sendbuf, 2, {- buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SIGNATURE_ALGORITHMS, {- if ((ret = push_signature_algorithms(tls->ctx->verify_certificate, sendbuf)) != 0)- goto Exit;- });- });- });-- if (ret != 0) {- goto Exit;- }- }-- /* send certificate */- if ((ret = send_certificate(tls, emitter, &ch->signature_algorithms, ptls_iovec_init(NULL, 0), ch->status_request,- ch->cert_compression_algos.list, ch->cert_compression_algos.count)) != 0)- goto Exit;- /* send certificateverify, finished, and complete the handshake */- if ((ret = server_finish_handshake(tls, emitter, 1, &ch->signature_algorithms)) != 0)- goto Exit;- } else {- /* send finished, and complete the handshake */- if ((ret = server_finish_handshake(tls, emitter, 0, NULL)) != 0)- goto Exit;- }--Exit:- free(pubkey.base);- if (ecdh_secret.base != NULL) {- ptls_clear_memory(ecdh_secret.base, ecdh_secret.len);- free(ecdh_secret.base);- }- free(ech.encoded_ch_inner);- free(ech.ch_outer_aad);- ptls_buffer_dispose(&ech.ch_inner);- free(ch);- return ret;--#undef EMIT_SERVER_HELLO-#undef EMIT_HELLO_RETRY_REQUEST-}--static int server_finish_handshake(ptls_t *tls, ptls_message_emitter_t *emitter, int send_cert_verify,- struct st_ptls_signature_algorithms_t *signature_algorithms)-{- int ret;-- if (send_cert_verify) {- if ((ret = send_certificate_verify(tls, emitter, signature_algorithms, PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING)) !=- 0) {- if (ret == PTLS_ERROR_ASYNC_OPERATION) {- tls->state = PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY;- }- goto Exit;- }- }-- if ((ret = send_finished(tls, emitter)) != 0)- goto Exit;-- assert(tls->key_schedule->generation == 2);- if ((ret = key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0))) != 0)- goto Exit;- if ((ret = setup_traffic_protection(tls, 1, "s ap traffic", 3, 0)) != 0)- goto Exit;- if ((ret = derive_secret(tls->key_schedule, tls->server.pending_traffic_secret, "c ap traffic")) != 0)- goto Exit;- if ((ret = derive_exporter_secret(tls, 0)) != 0)- goto Exit;-- if (tls->pending_handshake_secret != NULL) {- if (tls->ctx->omit_end_of_early_data) {- if ((ret = commission_handshake_secret(tls)) != 0)- goto Exit;- tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED;- } else {- tls->state = PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA;- }- } else if (tls->ctx->require_client_authentication) {- tls->state = PTLS_STATE_SERVER_EXPECT_CERTIFICATE;- } else {- tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED;- }-- /* send session ticket if necessary */- if (tls->server.can_send_session_ticket && tls->ctx->ticket_lifetime != 0) {- if ((ret = send_session_ticket(tls, emitter)) != 0)- goto Exit;- }-- if (tls->ctx->require_client_authentication) {- ret = PTLS_ERROR_IN_PROGRESS;- } else {- ret = 0;- }--Exit:- return ret;-}--static int server_handle_end_of_early_data(ptls_t *tls, ptls_iovec_t message)-{- int ret;-- if ((ret = commission_handshake_secret(tls)) != 0)- goto Exit;-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);- tls->state = PTLS_STATE_SERVER_EXPECT_FINISHED;- ret = PTLS_ERROR_IN_PROGRESS;--Exit:- return ret;-}--static int server_handle_finished(ptls_t *tls, ptls_iovec_t message)-{- int ret;-- if ((ret = verify_finished(tls, message)) != 0)- return ret;-- memcpy(tls->traffic_protection.dec.secret, tls->server.pending_traffic_secret, sizeof(tls->server.pending_traffic_secret));- ptls_clear_memory(tls->server.pending_traffic_secret, sizeof(tls->server.pending_traffic_secret));- if ((ret = setup_traffic_protection(tls, 0, NULL, 3, 0)) != 0)- return ret;-- ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);-- tls->state = PTLS_STATE_SERVER_POST_HANDSHAKE;- return 0;-}--static int update_traffic_key(ptls_t *tls, int is_enc)-{- struct st_ptls_traffic_protection_t *tp = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec;- uint8_t secret[PTLS_MAX_DIGEST_SIZE];- int ret;-- ptls_hash_algorithm_t *hash = tls->key_schedule->hashes[0].algo;- if ((ret = ptls_hkdf_expand_label(hash, secret, hash->digest_size, ptls_iovec_init(tp->secret, hash->digest_size),- "traffic upd", ptls_iovec_init(NULL, 0), NULL)) != 0)- goto Exit;- memcpy(tp->secret, secret, sizeof(secret));- ret = setup_traffic_protection(tls, is_enc, NULL, 3, 1);--Exit:- ptls_clear_memory(secret, sizeof(secret));- return ret;-}--static int handle_key_update(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message)-{- const uint8_t *src = message.base + PTLS_HANDSHAKE_HEADER_SIZE, *const end = message.base + message.len;- int ret;-- /* validate */- if (end - src != 1 || *src > 1)- return PTLS_ALERT_DECODE_ERROR;-- /* update receive key */- if ((ret = update_traffic_key(tls, 0)) != 0)- return ret;-- if (*src) {- if (tls->ctx->update_traffic_key != NULL)- return PTLS_ALERT_UNEXPECTED_MESSAGE;- tls->needs_key_update = 1;- }-- return 0;-}--static int parse_record_header(struct st_ptls_record_t *rec, const uint8_t *src)-{- rec->type = src[0];- rec->version = ntoh16(src + 1);- rec->length = ntoh16(src + 3);-- if (rec->length >- (size_t)(rec->type == PTLS_CONTENT_TYPE_APPDATA ? PTLS_MAX_ENCRYPTED_RECORD_SIZE : PTLS_MAX_PLAINTEXT_RECORD_SIZE))- return PTLS_ALERT_DECODE_ERROR;-- return 0;-}--static int parse_record(ptls_t *tls, struct st_ptls_record_t *rec, const uint8_t *src, size_t *len)-{- int ret;-- assert(*len != 0);-- /* Check if the first byte is something that we can handle, otherwise do not bother parsing / buffering the entire record as it- * is obviously broken. SSL 2.0 handshakes fall into this path as well. */- if (tls->recvbuf.rec.base == NULL) {- uint8_t type = src[0];- switch (type) {- case PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC:- case PTLS_CONTENT_TYPE_ALERT:- case PTLS_CONTENT_TYPE_HANDSHAKE:- case PTLS_CONTENT_TYPE_APPDATA:- break;- default:- return PTLS_ALERT_DECODE_ERROR;- }- }-- if (tls->recvbuf.rec.base == NULL && *len >= 5) {- /* fast path */- if ((ret = parse_record_header(rec, src)) != 0)- return ret;- if (5 + rec->length <= *len) {- rec->fragment = src + 5;- *len = rec->length + 5;- return 0;- }- }-- /* slow path */- const uint8_t *const end = src + *len;- *rec = (struct st_ptls_record_t){0};-- if (tls->recvbuf.rec.base == NULL) {- ptls_buffer_init(&tls->recvbuf.rec, "", 0);- if ((ret = ptls_buffer_reserve(&tls->recvbuf.rec, 5)) != 0)- return ret;- }-- /* fill and parse the header */- while (tls->recvbuf.rec.off < 5) {- if (src == end)- return PTLS_ERROR_IN_PROGRESS;- tls->recvbuf.rec.base[tls->recvbuf.rec.off++] = *src++;- }- if ((ret = parse_record_header(rec, tls->recvbuf.rec.base)) != 0)- return ret;-- /* fill the fragment */- size_t addlen = rec->length + 5 - tls->recvbuf.rec.off;- if (addlen != 0) {- if ((ret = ptls_buffer_reserve(&tls->recvbuf.rec, addlen)) != 0)- return ret;- if (addlen > (size_t)(end - src))- addlen = end - src;- if (addlen != 0) {- memcpy(tls->recvbuf.rec.base + tls->recvbuf.rec.off, src, addlen);- tls->recvbuf.rec.off += addlen;- src += addlen;- }- }-- /* set rec->fragment if a complete record has been parsed */- if (tls->recvbuf.rec.off == rec->length + 5) {- rec->fragment = tls->recvbuf.rec.base + 5;- ret = 0;- } else {- ret = PTLS_ERROR_IN_PROGRESS;- }-- *len -= end - src;- return ret;-}--static void update_open_count(ptls_context_t *ctx, ssize_t delta)-{- if (ctx->update_open_count != NULL)- ctx->update_open_count->cb(ctx->update_open_count, delta);-}--static ptls_t *new_instance(ptls_context_t *ctx, int is_server)-{- ptls_t *tls;-- assert(ctx->get_time != NULL && "please set ctx->get_time to `&ptls_get_time`; see #92");-- if ((tls = malloc(sizeof(*tls))) == NULL)- return NULL;-- update_open_count(ctx, 1);- *tls = (ptls_t){ctx};- tls->is_server = is_server;- tls->send_change_cipher_spec = ctx->send_change_cipher_spec;- tls->skip_tracing = ptls_default_skip_tracing;- return tls;-}--ptls_t *ptls_client_new(ptls_context_t *ctx)-{- ptls_t *tls = new_instance(ctx, 0);- tls->state = PTLS_STATE_CLIENT_HANDSHAKE_START;- tls->ctx->random_bytes(tls->client_random, sizeof(tls->client_random));- log_client_random(tls);- if (tls->send_change_cipher_spec) {- tls->client.legacy_session_id =- ptls_iovec_init(tls->client.legacy_session_id_buf, sizeof(tls->client.legacy_session_id_buf));- tls->ctx->random_bytes(tls->client.legacy_session_id.base, tls->client.legacy_session_id.len);- }-- PTLS_PROBE(NEW, tls, 0);- PTLS_LOG_CONN(new, tls, { PTLS_LOG_ELEMENT_BOOL(is_server, 0); });- return tls;-}--ptls_t *ptls_server_new(ptls_context_t *ctx)-{- ptls_t *tls = new_instance(ctx, 1);- tls->state = PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO;- tls->server.early_data_skipped_bytes = UINT32_MAX;-- PTLS_PROBE(NEW, tls, 1);- PTLS_LOG_CONN(new, tls, { PTLS_LOG_ELEMENT_BOOL(is_server, 1); });- return tls;-}--static int export_tls12_params(ptls_buffer_t *output, int is_server, int session_reused, ptls_cipher_suite_t *cipher,- const void *client_random, const char *server_name, ptls_iovec_t negotiated_protocol,- const void *enc_key, const void *enc_iv, uint64_t enc_seq, uint64_t enc_record_iv,- const void *dec_key, const void *dec_iv, uint64_t dec_seq)-{- int ret;-- ptls_buffer_push_block(output, 2, {- ptls_buffer_push(output, is_server);- ptls_buffer_push(output, session_reused);- ptls_buffer_push16(output, PTLS_PROTOCOL_VERSION_TLS12);- ptls_buffer_push16(output, cipher->id);- ptls_buffer_pushv(output, client_random, PTLS_HELLO_RANDOM_SIZE);- ptls_buffer_push_block(output, 2, {- size_t len = server_name != NULL ? strlen(server_name) : 0;- ptls_buffer_pushv(output, server_name, len);- });- ptls_buffer_push_block(output, 2, { ptls_buffer_pushv(output, negotiated_protocol.base, negotiated_protocol.len); });- ptls_buffer_push_block(output, 2, {- ptls_buffer_pushv(output, enc_key, cipher->aead->key_size);- ptls_buffer_pushv(output, enc_iv, cipher->aead->tls12.fixed_iv_size);- ptls_buffer_push64(output, enc_seq);- if (cipher->aead->tls12.record_iv_size != 0)- ptls_buffer_push64(output, enc_record_iv);- ptls_buffer_pushv(output, dec_key, cipher->aead->key_size);- ptls_buffer_pushv(output, dec_iv, cipher->aead->tls12.fixed_iv_size);- ptls_buffer_push64(output, dec_seq);- });- ptls_buffer_push_block(output, 2, {}); /* for future extensions */- });--Exit:- return ret;-}--int ptls_build_tls12_export_params(ptls_context_t *ctx, ptls_buffer_t *output, int is_server, int session_reused,- ptls_cipher_suite_t *cipher, const void *master_secret, const void *hello_randoms,- uint64_t next_send_record_iv, const char *server_name, ptls_iovec_t negotiated_protocol)-{- assert(cipher->aead->tls12.fixed_iv_size + cipher->aead->tls12.record_iv_size != 0 || !"given cipher-suite supports TLS/1.2");-- uint8_t key_block[(PTLS_MAX_SECRET_SIZE + PTLS_MAX_IV_SIZE) * 2];- size_t key_block_len = (cipher->aead->key_size + cipher->aead->tls12.fixed_iv_size) * 2;- int ret;-- assert(key_block_len <= sizeof(key_block));-- /* generate key block */- if ((ret =- ptls_tls12_phash(cipher->hash, key_block, key_block_len, ptls_iovec_init(master_secret, PTLS_TLS12_MASTER_SECRET_SIZE),- "key expansion", ptls_iovec_init(hello_randoms, PTLS_HELLO_RANDOM_SIZE * 2))) != 0)- goto Exit;-- /* determine key locations */- struct {- const void *key;- const void *iv;- } client_secret, server_secret, *enc_secret = is_server ? &server_secret : &client_secret,- *dec_secret = is_server ? &client_secret : &server_secret;- client_secret.key = key_block;- server_secret.key = key_block + cipher->aead->key_size;- client_secret.iv = key_block + cipher->aead->key_size * 2;- server_secret.iv = key_block + cipher->aead->key_size * 2 + cipher->aead->tls12.fixed_iv_size;-- /* Serialize prams. Sequence number of the first application record is 1, because Finished is the only message sent after- * ChangeCipherSpec. */- ret = export_tls12_params(output, is_server, session_reused, cipher, (uint8_t *)hello_randoms + PTLS_HELLO_RANDOM_SIZE,- server_name, negotiated_protocol, enc_secret->key, enc_secret->iv, 1, next_send_record_iv,- dec_secret->key, dec_secret->iv, 1);--Exit:- ptls_clear_memory(key_block, sizeof(key_block));- return ret;-}--int ptls_export(ptls_t *tls, ptls_buffer_t *output)-{- /* TODO add tls13 support */- if (!tls->traffic_protection.enc.tls12)- return PTLS_ERROR_LIBRARY;-- ptls_iovec_t negotiated_protocol =- ptls_iovec_init(tls->negotiated_protocol, tls->negotiated_protocol != NULL ? strlen(tls->negotiated_protocol) : 0);- return export_tls12_params(output, tls->is_server, tls->is_psk_handshake, tls->cipher_suite, tls->client_random,- tls->server_name, negotiated_protocol, tls->traffic_protection.enc.secret,- tls->traffic_protection.enc.secret + PTLS_MAX_SECRET_SIZE, tls->traffic_protection.enc.seq,- tls->traffic_protection.enc.tls12_enc_record_iv, tls->traffic_protection.dec.secret,- tls->traffic_protection.dec.secret + PTLS_MAX_SECRET_SIZE, tls->traffic_protection.dec.seq);-}--static int build_tls12_traffic_protection(ptls_t *tls, int is_enc, const uint8_t **src, const uint8_t *const end)-{- struct st_ptls_traffic_protection_t *tp = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec;-- if ((size_t)(end - *src) < tls->cipher_suite->aead->key_size + tls->cipher_suite->aead->tls12.fixed_iv_size + sizeof(uint64_t))- return PTLS_ALERT_DECODE_ERROR;-- /* set properties */- memcpy(tp->secret, *src, tls->cipher_suite->aead->key_size);- *src += tls->cipher_suite->aead->key_size;- memcpy(tp->secret + PTLS_MAX_SECRET_SIZE, *src, tls->cipher_suite->aead->tls12.fixed_iv_size);- *src += tls->cipher_suite->aead->tls12.fixed_iv_size;- if (ptls_decode64(&tp->seq, src, end) != 0)- return PTLS_ALERT_DECODE_ERROR;- if (is_enc && tls->cipher_suite->aead->tls12.record_iv_size != 0) {- if (ptls_decode64(&tp->tls12_enc_record_iv, src, end) != 0)- return PTLS_ALERT_DECODE_ERROR;- }- tp->tls12 = 1;-- /* instantiate aead */- if ((tp->aead = ptls_aead_new_direct(tls->cipher_suite->aead, is_enc, tp->secret, tp->secret + PTLS_MAX_SECRET_SIZE)) == NULL)- return PTLS_ERROR_NO_MEMORY;-- return 0;-}--int ptls_import(ptls_context_t *ctx, ptls_t **tls, ptls_iovec_t params)-{- const uint8_t *src = params.base, *const end = src + params.len;- uint16_t protocol_version, csid;- int ret;-- *tls = NULL;-- /* TODO handle flags like psk_handshake, ech_handshake as we add support for TLS/1.3 import */- ptls_decode_block(src, end, 2, {- /* instantiate, based on the is_server flag */- if (end - src < 2) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- if ((*tls = new_instance(ctx, *src++)) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- (*tls)->is_psk_handshake = *src++;- /* determine protocol version and cipher suite */- if ((ret = ptls_decode16(&protocol_version, &src, end)) != 0)- goto Exit;- if ((ret = ptls_decode16(&csid, &src, end)) != 0)- goto Exit;- (*tls)->cipher_suite = ptls_find_cipher_suite(ctx->tls12_cipher_suites, csid);- if ((*tls)->cipher_suite == NULL) {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- goto Exit;- }- /* other version-independent stuff */- if (end - src < PTLS_HELLO_RANDOM_SIZE) {- ret = PTLS_ALERT_DECODE_ERROR;- goto Exit;- }- memcpy((*tls)->client_random, src, PTLS_HELLO_RANDOM_SIZE);- src += PTLS_HELLO_RANDOM_SIZE;- ptls_decode_open_block(src, end, 2, {- if (src != end) {- if ((ret = ptls_set_server_name(*tls, (const char *)src, end - src)) != 0)- goto Exit;- src = end;- }- });- ptls_decode_open_block(src, end, 2, {- if (src != end) {- if ((ret = ptls_set_negotiated_protocol(*tls, (const char *)src, end - src)) != 0)- goto Exit;- src = end;- }- });- /* version-dependent stuff */- ptls_decode_open_block(src, end, 2, {- switch (protocol_version) {- case PTLS_PROTOCOL_VERSION_TLS12:- /* setup AEAD keys */- if ((ret = build_tls12_traffic_protection(*tls, 1, &src, end)) != 0)- goto Exit;- if ((ret = build_tls12_traffic_protection(*tls, 0, &src, end)) != 0)- goto Exit;- break;- default:- ret = PTLS_ALERT_ILLEGAL_PARAMETER;- break;- }- });- /* extensions */- ptls_decode_open_block(src, end, 2, {- src = end; /* unused */- });- });-- (*tls)->state = ptls_is_server(*tls) ? PTLS_STATE_SERVER_POST_HANDSHAKE : PTLS_STATE_CLIENT_POST_HANDSHAKE;--Exit:- if (ret != 0) {- if (*tls != NULL) {- ptls_free(*tls);- *tls = NULL;- }- }- return ret;-}--void ptls_free(ptls_t *tls)-{- PTLS_PROBE0(FREE, tls);- PTLS_LOG_CONN(free, tls, {});-- ptls_buffer_dispose(&tls->recvbuf.rec);- ptls_buffer_dispose(&tls->recvbuf.mess);- free_exporter_master_secret(tls, 1);- free_exporter_master_secret(tls, 0);- if (tls->key_schedule != NULL)- key_schedule_free(tls->key_schedule);- if (tls->traffic_protection.dec.aead != NULL)- ptls_aead_free(tls->traffic_protection.dec.aead);- if (tls->traffic_protection.enc.aead != NULL)- ptls_aead_free(tls->traffic_protection.enc.aead);- free(tls->server_name);- free(tls->negotiated_protocol);- clear_ech(&tls->ech, tls->is_server);- if (tls->is_server) {- if (tls->server.async_job != NULL)- tls->server.async_job->destroy_(tls->server.async_job);- } else {- if (tls->client.key_share_ctx != NULL)- tls->client.key_share_ctx->on_exchange(&tls->client.key_share_ctx, 1, NULL, ptls_iovec_init(NULL, 0));- if (tls->client.certificate_request.context.base != NULL)- free(tls->client.certificate_request.context.base);- }- if (tls->certificate_verify.cb != NULL)- tls->certificate_verify.cb(tls->certificate_verify.verify_ctx, 0, ptls_iovec_init(NULL, 0), ptls_iovec_init(NULL, 0));- if (tls->pending_handshake_secret != NULL) {- ptls_clear_memory(tls->pending_handshake_secret, PTLS_MAX_DIGEST_SIZE);- free(tls->pending_handshake_secret);- }- update_open_count(tls->ctx, -1);- ptls_clear_memory(tls, sizeof(*tls));- free(tls);-}--ptls_context_t *ptls_get_context(ptls_t *tls)-{- return tls->ctx;-}--void ptls_set_context(ptls_t *tls, ptls_context_t *ctx)-{- update_open_count(ctx, 1);- update_open_count(tls->ctx, -1);- tls->ctx = ctx;-}--ptls_async_job_t *ptls_get_async_job(ptls_t *tls)-{- return tls->server.async_job;-}--ptls_iovec_t ptls_get_client_random(ptls_t *tls)-{- return ptls_iovec_init(tls->client_random, PTLS_HELLO_RANDOM_SIZE);-}--ptls_cipher_suite_t *ptls_get_cipher(ptls_t *tls)-{- return tls->cipher_suite;-}--uint16_t ptls_get_protocol_version(ptls_t *tls)-{- if (tls->traffic_protection.enc.tls12)- return PTLS_PROTOCOL_VERSION_TLS12;-- return PTLS_PROTOCOL_VERSION_TLS13;-}--int ptls_get_traffic_keys(ptls_t *tls, int is_enc, uint8_t *key, uint8_t *iv, uint64_t *seq)-{- struct st_ptls_traffic_protection_t *ctx = is_enc ? &tls->traffic_protection.enc : &tls->traffic_protection.dec;- int ret;-- if ((ret = get_traffic_keys(tls->cipher_suite->aead, tls->cipher_suite->hash, key, iv, ctx->secret, ptls_iovec_init(NULL, 0),- NULL)) != 0)- return ret;- *seq = ctx->seq;- return 0;-}--const char *ptls_get_server_name(ptls_t *tls)-{- return tls->server_name;-}--int ptls_set_server_name(ptls_t *tls, const char *server_name, size_t server_name_len)-{- char *duped = NULL;-- if (server_name != NULL &&- (duped = duplicate_as_str(server_name, server_name_len != 0 ? server_name_len : strlen(server_name))) == NULL)- return PTLS_ERROR_NO_MEMORY;-- free(tls->server_name);- tls->server_name = duped;-- return 0;-}--const char *ptls_get_negotiated_protocol(ptls_t *tls)-{- return tls->negotiated_protocol;-}--int ptls_set_negotiated_protocol(ptls_t *tls, const char *protocol, size_t protocol_len)-{- char *duped = NULL;-- if (protocol != NULL && (duped = duplicate_as_str(protocol, protocol_len != 0 ? protocol_len : strlen(protocol))) == NULL)- return PTLS_ERROR_NO_MEMORY;-- free(tls->negotiated_protocol);- tls->negotiated_protocol = duped;-- return 0;-}--int ptls_handshake_is_complete(ptls_t *tls)-{- return tls->state >= PTLS_STATE_POST_HANDSHAKE_MIN;-}--int ptls_is_psk_handshake(ptls_t *tls)-{- return tls->is_psk_handshake;-}--int ptls_is_ech_handshake(ptls_t *tls, uint8_t *config_id, ptls_hpke_kem_t **kem, ptls_hpke_cipher_suite_t **cipher)-{- if (tls->ech.accepted) {- if (config_id != NULL)- *config_id = tls->ech.config_id;- if (kem != NULL)- *kem = tls->ech.kem;- if (cipher != NULL)- *cipher = tls->ech.cipher;- return 1;- }- return 0;-}--void **ptls_get_data_ptr(ptls_t *tls)-{- return &tls->data_ptr;-}--int ptls_skip_tracing(ptls_t *tls)-{- return tls->skip_tracing;-}--void ptls_set_skip_tracing(ptls_t *tls, int skip_tracing)-{- tls->skip_tracing = skip_tracing;-}--static int handle_client_handshake_message(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, int is_end_of_record,- ptls_handshake_properties_t *properties)-{- uint8_t type = message.base[0];- int ret;-- switch (tls->state) {- case PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO:- case PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO:- if (type == PTLS_HANDSHAKE_TYPE_SERVER_HELLO && is_end_of_record) {- ret = client_handle_hello(tls, emitter, message, properties);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS:- if (type == PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS) {- ret = client_handle_encrypted_extensions(tls, message, properties);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE:- if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {- ret = client_handle_certificate_request(tls, message, properties);- break;- }- /* fall through */- case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE:- switch (type) {- case PTLS_HANDSHAKE_TYPE_CERTIFICATE:- ret = client_handle_certificate(tls, message);- break;- case PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE:- ret = client_handle_compressed_certificate(tls, message);- break;- default:- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- break;- }- break;- case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY:- if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {- ret = client_handle_certificate_verify(tls, message);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_CLIENT_EXPECT_FINISHED:- if (type == PTLS_HANDSHAKE_TYPE_FINISHED && is_end_of_record) {- ret = client_handle_finished(tls, emitter, message);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_CLIENT_POST_HANDSHAKE:- switch (type) {- case PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET:- ret = client_handle_new_session_ticket(tls, message);- break;- case PTLS_HANDSHAKE_TYPE_KEY_UPDATE:- ret = handle_key_update(tls, emitter, message);- break;- default:- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- break;- }- break;- default:- assert(!"unexpected state");- ret = PTLS_ALERT_INTERNAL_ERROR;- break;- }-- PTLS_PROBE(RECEIVE_MESSAGE, tls, message.base[0], message.base + PTLS_HANDSHAKE_HEADER_SIZE,- message.len - PTLS_HANDSHAKE_HEADER_SIZE, ret);- PTLS_LOG_CONN(receive_message, tls, {- PTLS_LOG_ELEMENT_UNSIGNED(message, message.base[0]);- PTLS_LOG_ELEMENT_UNSIGNED(len, message.len - PTLS_HANDSHAKE_HEADER_SIZE);- PTLS_LOG_ELEMENT_SIGNED(result, ret);- });-- return ret;-}--static int handle_server_handshake_message(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message, int is_end_of_record,- ptls_handshake_properties_t *properties)-{- uint8_t type = message.base[0];- int ret;-- switch (tls->state) {- case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO:- case PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO:- if (type == PTLS_HANDSHAKE_TYPE_CLIENT_HELLO && is_end_of_record) {- ret = server_handle_hello(tls, emitter, message, properties);- } else {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- }- break;- case PTLS_STATE_SERVER_EXPECT_CERTIFICATE:- if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE) {- ret = server_handle_certificate(tls, message);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY:- if (type == PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {- ret = server_handle_certificate_verify(tls, message);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA:- assert(!tls->ctx->omit_end_of_early_data);- if (type == PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA) {- ret = server_handle_end_of_early_data(tls, message);- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_STATE_SERVER_EXPECT_FINISHED:- if (type == PTLS_HANDSHAKE_TYPE_FINISHED && is_end_of_record) {- ret = server_handle_finished(tls, message);- } else {- ret = PTLS_ALERT_HANDSHAKE_FAILURE;- }- break;- case PTLS_STATE_SERVER_POST_HANDSHAKE:- switch (type) {- case PTLS_HANDSHAKE_TYPE_KEY_UPDATE:- ret = handle_key_update(tls, emitter, message);- break;- default:- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- break;- }- break;- default:- assert(!"unexpected state");- ret = PTLS_ALERT_INTERNAL_ERROR;- break;- }-- PTLS_PROBE(RECEIVE_MESSAGE, tls, message.base[0], message.base + PTLS_HANDSHAKE_HEADER_SIZE,- message.len - PTLS_HANDSHAKE_HEADER_SIZE, ret);- PTLS_LOG_CONN(receive_message, tls, {- PTLS_LOG_ELEMENT_UNSIGNED(message, message.base[0]);- PTLS_LOG_ELEMENT_UNSIGNED(len, message.len - PTLS_HANDSHAKE_HEADER_SIZE);- PTLS_LOG_ELEMENT_SIGNED(result, ret);- });-- return ret;-}--static int handle_alert(ptls_t *tls, const uint8_t *src, size_t len)-{- if (len != 2)- return PTLS_ALERT_DECODE_ERROR;-- uint8_t desc = src[1];-- /* all fatal alerts and USER_CANCELLED warning tears down the connection immediately, regardless of the transmitted level */- return PTLS_ALERT_TO_PEER_ERROR(desc);-}--static int message_buffer_is_overflow(ptls_context_t *ctx, size_t size)-{- if (ctx->max_buffer_size == 0)- return 0;- if (size <= ctx->max_buffer_size)- return 0;- return 1;-}--static int handle_handshake_record(ptls_t *tls,- int (*cb)(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_iovec_t message,- int is_end_of_record, ptls_handshake_properties_t *properties),- ptls_message_emitter_t *emitter, struct st_ptls_record_t *rec,- ptls_handshake_properties_t *properties)-{- int ret;-- /* handshake */- if (rec->type != PTLS_CONTENT_TYPE_HANDSHAKE)- return PTLS_ALERT_DECODE_ERROR;-- /* flatten the unhandled messages */- const uint8_t *src, *src_end;- if (tls->recvbuf.mess.base == NULL) {- src = rec->fragment;- src_end = src + rec->length;- } else {- if (message_buffer_is_overflow(tls->ctx, tls->recvbuf.mess.off + rec->length))- return PTLS_ALERT_HANDSHAKE_FAILURE;- if ((ret = ptls_buffer_reserve(&tls->recvbuf.mess, rec->length)) != 0)- return ret;- memcpy(tls->recvbuf.mess.base + tls->recvbuf.mess.off, rec->fragment, rec->length);- tls->recvbuf.mess.off += rec->length;- src = tls->recvbuf.mess.base;- src_end = src + tls->recvbuf.mess.off;- }-- /* handle the messages */- ret = PTLS_ERROR_IN_PROGRESS;- while (src_end - src >= 4) {- size_t mess_len = 4 + ntoh24(src + 1);- if (src_end - src < (int)mess_len)- break;- ret = cb(tls, emitter, ptls_iovec_init(src, mess_len), src_end - src == mess_len, properties);- switch (ret) {- case 0:- case PTLS_ERROR_ASYNC_OPERATION:- case PTLS_ERROR_IN_PROGRESS:- break;- default:- ptls_buffer_dispose(&tls->recvbuf.mess);- return ret;- }- src += mess_len;- }-- /* keep last partial message in buffer */- if (src != src_end) {- size_t new_size = src_end - src;- if (message_buffer_is_overflow(tls->ctx, new_size))- return PTLS_ALERT_HANDSHAKE_FAILURE;- if (tls->recvbuf.mess.base == NULL) {- ptls_buffer_init(&tls->recvbuf.mess, "", 0);- if ((ret = ptls_buffer_reserve(&tls->recvbuf.mess, new_size)) != 0)- return ret;- memcpy(tls->recvbuf.mess.base, src, new_size);- } else {- memmove(tls->recvbuf.mess.base, src, new_size);- }- tls->recvbuf.mess.off = new_size;- ret = PTLS_ERROR_IN_PROGRESS;- } else {- ptls_buffer_dispose(&tls->recvbuf.mess);- }-- return ret;-}--static int handle_input(ptls_t *tls, ptls_message_emitter_t *emitter, ptls_buffer_t *decryptbuf, const void *input, size_t *inlen,- ptls_handshake_properties_t *properties)-{- struct st_ptls_record_t rec;- int ret;-- /* extract the record */- if ((ret = parse_record(tls, &rec, input, inlen)) != 0)- return ret;- assert(rec.fragment != NULL);-- /* decrypt the record */- if (rec.type == PTLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {- if (tls->state < PTLS_STATE_POST_HANDSHAKE_MIN) {- if (!(rec.length == 1 && rec.fragment[0] == 0x01))- return PTLS_ALERT_ILLEGAL_PARAMETER;- } else {- return PTLS_ALERT_HANDSHAKE_FAILURE;- }- ret = PTLS_ERROR_IN_PROGRESS;- goto NextRecord;- }- if (tls->traffic_protection.dec.aead != NULL && rec.type != PTLS_CONTENT_TYPE_ALERT) {- size_t decrypted_length;- if (rec.type != PTLS_CONTENT_TYPE_APPDATA)- return PTLS_ALERT_HANDSHAKE_FAILURE;- if ((ret = ptls_buffer_reserve(decryptbuf, 5 + rec.length)) != 0)- return ret;- if ((ret = aead_decrypt(&tls->traffic_protection.dec, decryptbuf->base + decryptbuf->off, &decrypted_length, rec.fragment,- rec.length)) != 0) {- if (tls->is_server && tls->server.early_data_skipped_bytes != UINT32_MAX)- goto ServerSkipEarlyData;- return ret;- }- rec.length = decrypted_length;- rec.fragment = decryptbuf->base + decryptbuf->off;- /* skip padding */- for (; rec.length != 0; --rec.length)- if (rec.fragment[rec.length - 1] != 0)- break;- if (rec.length == 0)- return PTLS_ALERT_UNEXPECTED_MESSAGE;- rec.type = rec.fragment[--rec.length];- } else if (rec.type == PTLS_CONTENT_TYPE_APPDATA && tls->is_server && tls->server.early_data_skipped_bytes != UINT32_MAX) {- goto ServerSkipEarlyData;- }-- if (tls->recvbuf.mess.base != NULL || rec.type == PTLS_CONTENT_TYPE_HANDSHAKE) {- /* handshake record */- ret = handle_handshake_record(tls, tls->is_server ? handle_server_handshake_message : handle_client_handshake_message,- emitter, &rec, properties);- } else {- /* handling of an alert or an application record */- switch (rec.type) {- case PTLS_CONTENT_TYPE_APPDATA:- if (tls->state >= PTLS_STATE_POST_HANDSHAKE_MIN) {- decryptbuf->off += rec.length;- ret = 0;- } else if (tls->state == PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA) {- if (tls->traffic_protection.dec.aead != NULL)- decryptbuf->off += rec.length;- ret = 0;- } else {- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- }- break;- case PTLS_CONTENT_TYPE_ALERT:- ret = handle_alert(tls, rec.fragment, rec.length);- break;- default:- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- break;- }- }--NextRecord:- ptls_buffer_dispose(&tls->recvbuf.rec);- return ret;--ServerSkipEarlyData:- tls->server.early_data_skipped_bytes += (uint32_t)rec.length;- if (tls->server.early_data_skipped_bytes > PTLS_MAX_EARLY_DATA_SKIP_SIZE)- return PTLS_ALERT_HANDSHAKE_FAILURE;- ret = PTLS_ERROR_IN_PROGRESS;- goto NextRecord;-}--static int handle_input_tls12(ptls_t *tls, ptls_buffer_t *decryptbuf, const void *input, size_t *inlen)-{- struct st_ptls_record_t rec;- int ret;-- /* extract the record, or bail out */- if ((ret = parse_record(tls, &rec, input, inlen)) != 0)- return ret;- assert(rec.fragment != NULL);-- const uint8_t *src = rec.fragment, *end = src + rec.length;- uint64_t nonce;- uint8_t aad[PTLS_TLS12_AAD_SIZE];-- /* determine the nonce */- if (tls->traffic_protection.dec.aead->algo->tls12.record_iv_size != 0) {- assert(tls->traffic_protection.dec.aead->algo->tls12.record_iv_size == 8);- if ((ret = ptls_decode64(&nonce, &src, end)) != 0)- goto Exit;- } else {- nonce = tls->traffic_protection.dec.seq;- }-- /* determine cleartext length */- size_t textlen = end - src;- if (textlen < tls->traffic_protection.dec.aead->algo->tag_size) {- ret = PTLS_ALERT_BAD_RECORD_MAC;- goto Exit;- }- textlen -= tls->traffic_protection.dec.aead->algo->tag_size;-- /* build aad */- build_tls12_aad(aad, rec.type, tls->traffic_protection.dec.seq, (uint16_t)textlen);-- /* decrypt input to decryptbuf */- if ((ret = ptls_buffer_reserve(decryptbuf, textlen)) != 0)- goto Exit;- if (ptls_aead_decrypt(tls->traffic_protection.dec.aead, decryptbuf->base + decryptbuf->off, src, end - src, nonce, aad,- sizeof(aad)) != textlen) {- ret = PTLS_ALERT_BAD_RECORD_MAC;- goto Exit;- }- ++tls->traffic_protection.dec.seq;-- /* record-type specific action */- switch (rec.type) {- case PTLS_CONTENT_TYPE_APPDATA:- /* if application data, retain the bytes being decrypted */- decryptbuf->off += textlen;- break;- case PTLS_CONTENT_TYPE_ALERT:- /* submit alert without adjusting decryptbuf, so that the decrypted data would be dropped after handling the alert */- ret = handle_alert(tls, decryptbuf->base + decryptbuf->off, textlen);- break;- default:- ret = PTLS_ALERT_UNEXPECTED_MESSAGE;- break;- }--Exit:- ptls_buffer_dispose(&tls->recvbuf.rec);- ptls_clear_memory(aad, sizeof(aad));- return 0;-}--static void init_record_message_emitter(ptls_t *tls, struct st_ptls_record_message_emitter_t *emitter, ptls_buffer_t *sendbuf)-{- *emitter = (struct st_ptls_record_message_emitter_t){- {sendbuf, &tls->traffic_protection.enc, 5, begin_record_message, commit_record_message}};-}--int ptls_handshake(ptls_t *tls, ptls_buffer_t *_sendbuf, const void *input, size_t *inlen, ptls_handshake_properties_t *properties)-{- struct st_ptls_record_message_emitter_t emitter;- int ret;-- assert(tls->state < PTLS_STATE_POST_HANDSHAKE_MIN);-- init_record_message_emitter(tls, &emitter, _sendbuf);- size_t sendbuf_orig_off = emitter.super.buf->off;-- /* special handlings */- switch (tls->state) {- case PTLS_STATE_CLIENT_HANDSHAKE_START: {- assert(input == NULL || *inlen == 0);- assert(tls->ctx->key_exchanges[0] != NULL);- return send_client_hello(tls, &emitter.super, properties, NULL);- }- case PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY:- return server_finish_handshake(tls, &emitter.super, 1, NULL);- default:- break;- }-- const uint8_t *src = input, *const src_end = src + *inlen;- ptls_buffer_t decryptbuf;-- ptls_buffer_init(&decryptbuf, "", 0);-- /* perform handhake until completion or until all the input has been swallowed */- ret = PTLS_ERROR_IN_PROGRESS;- while (ret == PTLS_ERROR_IN_PROGRESS && src != src_end) {- size_t consumed = src_end - src;- ret = handle_input(tls, &emitter.super, &decryptbuf, src, &consumed, properties);- src += consumed;- assert(decryptbuf.off == 0);- }-- ptls_buffer_dispose(&decryptbuf);-- switch (ret) {- case 0:- case PTLS_ERROR_IN_PROGRESS:- case PTLS_ERROR_STATELESS_RETRY:- case PTLS_ERROR_ASYNC_OPERATION:- break;- default:- /* Flush handshake messages that have been written partially. ECH_REQUIRED sticks out because it is a message sent- * post-handshake compared to other alerts that are generating *during* the handshake. */- if (ret != PTLS_ALERT_ECH_REQUIRED) {- ptls_clear_memory(emitter.super.buf->base + sendbuf_orig_off, emitter.super.buf->off - sendbuf_orig_off);- emitter.super.buf->off = sendbuf_orig_off;- }- /* send alert immediately */- if (PTLS_ERROR_GET_CLASS(ret) != PTLS_ERROR_CLASS_PEER_ALERT)- if (ptls_send_alert(tls, emitter.super.buf, PTLS_ALERT_LEVEL_FATAL,- PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT ? ret : PTLS_ALERT_INTERNAL_ERROR) != 0)- emitter.super.buf->off = sendbuf_orig_off;- break;- }-- *inlen -= src_end - src;- return ret;-}--int ptls_receive(ptls_t *tls, ptls_buffer_t *decryptbuf, const void *_input, size_t *inlen)-{- const uint8_t *input = (const uint8_t *)_input, *const end = input + *inlen;- size_t decryptbuf_orig_size = decryptbuf->off;- int ret = 0;-- assert(tls->state >= PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA);-- /* loop until we decrypt some application data (or an error) */- while (ret == 0 && input != end && decryptbuf_orig_size == decryptbuf->off) {- size_t consumed = end - input;- if (tls->traffic_protection.dec.tls12) {- ret = handle_input_tls12(tls, decryptbuf, input, &consumed);- } else {- ret = handle_input(tls, NULL, decryptbuf, input, &consumed, NULL);- }- input += consumed;-- switch (ret) {- case 0:- break;- case PTLS_ERROR_IN_PROGRESS:- ret = 0;- break;- case PTLS_ERROR_CLASS_PEER_ALERT + PTLS_ALERT_CLOSE_NOTIFY:- /* TODO send close alert */- break;- default:- if (PTLS_ERROR_GET_CLASS(ret) == PTLS_ERROR_CLASS_SELF_ALERT) {- /* TODO send alert */- }- break;- }- }-- *inlen -= end - input;-- return ret;-}--static int update_send_key(ptls_t *tls, ptls_buffer_t *_sendbuf, int request_update)-{- struct st_ptls_record_message_emitter_t emitter;- int ret;-- init_record_message_emitter(tls, &emitter, _sendbuf);- size_t sendbuf_orig_off = emitter.super.buf->off;-- ptls_push_message(&emitter.super, NULL, PTLS_HANDSHAKE_TYPE_KEY_UPDATE,- { ptls_buffer_push(emitter.super.buf, !!request_update); });- if ((ret = update_traffic_key(tls, 1)) != 0)- goto Exit;- ret = 0;--Exit:- if (ret != 0)- emitter.super.buf->off = sendbuf_orig_off;- return ret;-}--int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen)-{- assert(tls->traffic_protection.enc.aead != NULL);-- /* "For AES-GCM, up to 2^24.5 full-size records (about 24 million) may be encrypted on a given connection while keeping a- * safety margin of approximately 2^-57 for Authenticated Encryption (AE) security." (RFC 8446 section 5.5)- */- if (tls->traffic_protection.enc.seq >= 16777216)- tls->needs_key_update = 1;-- if (tls->needs_key_update) {- int ret;- if ((ret = update_send_key(tls, sendbuf, tls->key_update_send_request)) != 0)- return ret;- tls->needs_key_update = 0;- tls->key_update_send_request = 0;- }-- return buffer_push_encrypted_records(sendbuf, PTLS_CONTENT_TYPE_APPDATA, input, inlen, &tls->traffic_protection.enc);-}--int ptls_update_key(ptls_t *tls, int request_update)-{- assert(tls->ctx->update_traffic_key == NULL);- tls->needs_key_update = 1;- tls->key_update_send_request = request_update;- return 0;-}--size_t ptls_get_record_overhead(ptls_t *tls)-{- ptls_aead_algorithm_t *algo = tls->traffic_protection.enc.aead->algo;-- if (tls->traffic_protection.enc.tls12) {- return 5 + algo->tls12.record_iv_size + algo->tag_size;- } else {- return 6 + algo->tag_size;- }-}--int ptls_send_alert(ptls_t *tls, ptls_buffer_t *sendbuf, uint8_t level, uint8_t description)-{- size_t rec_start = sendbuf->off;- int ret = 0;-- buffer_push_record(sendbuf, PTLS_CONTENT_TYPE_ALERT, { ptls_buffer_push(sendbuf, level, description); });- /* encrypt the alert if we have the encryption keys, unless when it is the early data key */- if (tls->traffic_protection.enc.aead != NULL && !(tls->state <= PTLS_STATE_CLIENT_EXPECT_FINISHED)) {- if ((ret = buffer_encrypt_record(sendbuf, rec_start, &tls->traffic_protection.enc)) != 0)- goto Exit;- }--Exit:- return ret;-}--int ptls_export_secret(ptls_t *tls, void *output, size_t outlen, const char *label, ptls_iovec_t context_value, int is_early)-{- ptls_hash_algorithm_t *algo = tls->key_schedule->hashes[0].algo;- uint8_t *master_secret = is_early ? tls->exporter_master_secret.early : tls->exporter_master_secret.one_rtt,- derived_secret[PTLS_MAX_DIGEST_SIZE], context_value_hash[PTLS_MAX_DIGEST_SIZE];- int ret;-- if (master_secret == NULL) {- if (is_early) {- switch (tls->state) {- case PTLS_STATE_CLIENT_HANDSHAKE_START:- case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO:- ret = PTLS_ERROR_IN_PROGRESS;- break;- default:- ret = PTLS_ERROR_NOT_AVAILABLE;- break;- }- } else {- ret = PTLS_ERROR_IN_PROGRESS;- }- return ret;- }-- if ((ret = ptls_calc_hash(algo, context_value_hash, context_value.base, context_value.len)) != 0)- return ret;-- if ((ret = ptls_hkdf_expand_label(algo, derived_secret, algo->digest_size, ptls_iovec_init(master_secret, algo->digest_size),- label, ptls_iovec_init(algo->empty_digest, algo->digest_size), NULL)) != 0)- goto Exit;- ret = ptls_hkdf_expand_label(algo, output, outlen, ptls_iovec_init(derived_secret, algo->digest_size), "exporter",- ptls_iovec_init(context_value_hash, algo->digest_size), NULL);--Exit:- ptls_clear_memory(derived_secret, sizeof(derived_secret));- ptls_clear_memory(context_value_hash, sizeof(context_value_hash));- return ret;-}--struct st_picotls_hmac_context_t {- ptls_hash_context_t super;- ptls_hash_algorithm_t *algo;- ptls_hash_context_t *hash;- uint8_t key[1];-};--static void hmac_update(ptls_hash_context_t *_ctx, const void *src, size_t len)-{- struct st_picotls_hmac_context_t *ctx = (struct st_picotls_hmac_context_t *)_ctx;- ctx->hash->update(ctx->hash, src, len);-}--static void hmac_apply_key(struct st_picotls_hmac_context_t *ctx, uint8_t pad)-{- size_t i;-- for (i = 0; i != ctx->algo->block_size; ++i)- ctx->key[i] ^= pad;- ctx->hash->update(ctx->hash, ctx->key, ctx->algo->block_size);- for (i = 0; i != ctx->algo->block_size; ++i)- ctx->key[i] ^= pad;-}--static void hmac_final(ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode)-{- struct st_picotls_hmac_context_t *ctx = (struct st_picotls_hmac_context_t *)_ctx;-- assert(mode != PTLS_HASH_FINAL_MODE_SNAPSHOT || !"not supported");-- if (md != NULL) {- ctx->hash->final(ctx->hash, md, PTLS_HASH_FINAL_MODE_RESET);- hmac_apply_key(ctx, 0x5c);- ctx->hash->update(ctx->hash, md, ctx->algo->digest_size);- }- ctx->hash->final(ctx->hash, md, mode);-- switch (mode) {- case PTLS_HASH_FINAL_MODE_FREE:- ptls_clear_memory(ctx->key, ctx->algo->block_size);- free(ctx);- break;- case PTLS_HASH_FINAL_MODE_RESET:- hmac_apply_key(ctx, 0x36);- break;- default:- assert(!"FIXME");- break;- }-}--int ptls_calc_hash(ptls_hash_algorithm_t *algo, void *output, const void *src, size_t len)-{- ptls_hash_context_t *ctx;-- if ((ctx = algo->create()) == NULL)- return PTLS_ERROR_NO_MEMORY;- ctx->update(ctx, src, len);- ctx->final(ctx, output, PTLS_HASH_FINAL_MODE_FREE);- return 0;-}--ptls_hash_context_t *ptls_hmac_create(ptls_hash_algorithm_t *algo, const void *key, size_t key_size)-{- struct st_picotls_hmac_context_t *ctx;-- assert(key_size <= algo->block_size);-- if ((ctx = malloc(offsetof(struct st_picotls_hmac_context_t, key) + algo->block_size)) == NULL)- return NULL;-- *ctx = (struct st_picotls_hmac_context_t){{hmac_update, hmac_final}, algo};- if ((ctx->hash = algo->create()) == NULL) {- free(ctx);- return NULL;- }- memset(ctx->key, 0, algo->block_size);- memcpy(ctx->key, key, key_size);-- hmac_apply_key(ctx, 0x36);-- return &ctx->super;-}--int ptls_hkdf_extract(ptls_hash_algorithm_t *algo, void *output, ptls_iovec_t salt, ptls_iovec_t ikm)-{- ptls_hash_context_t *hash;-- if (salt.len == 0)- salt = ptls_iovec_init(zeroes_of_max_digest_size, algo->digest_size);-- if ((hash = ptls_hmac_create(algo, salt.base, salt.len)) == NULL)- return PTLS_ERROR_NO_MEMORY;- hash->update(hash, ikm.base, ikm.len);- hash->final(hash, output, PTLS_HASH_FINAL_MODE_FREE);- return 0;-}--int ptls_hkdf_expand(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t prk, ptls_iovec_t info)-{- ptls_hash_context_t *hmac = NULL;- size_t i;- uint8_t digest[PTLS_MAX_DIGEST_SIZE];-- for (i = 0; (i * algo->digest_size) < outlen; ++i) {- if (hmac == NULL) {- if ((hmac = ptls_hmac_create(algo, prk.base, prk.len)) == NULL)- return PTLS_ERROR_NO_MEMORY;- } else {- hmac->update(hmac, digest, algo->digest_size);- }- hmac->update(hmac, info.base, info.len);- uint8_t gen = (uint8_t)(i + 1);- hmac->update(hmac, &gen, 1);- hmac->final(hmac, digest, 1);-- size_t off_start = i * algo->digest_size, off_end = off_start + algo->digest_size;- if (off_end > outlen)- off_end = outlen;- memcpy((uint8_t *)output + off_start, digest, off_end - off_start);- }-- if (hmac != NULL)- hmac->final(hmac, NULL, PTLS_HASH_FINAL_MODE_FREE);-- ptls_clear_memory(digest, algo->digest_size);-- return 0;-}--int ptls_hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label,- ptls_iovec_t hash_value, const char *label_prefix)-{- ptls_buffer_t hkdf_label;- uint8_t hkdf_label_buf[80];- int ret;-- ptls_buffer_init(&hkdf_label, hkdf_label_buf, sizeof(hkdf_label_buf));-- ptls_buffer_push16(&hkdf_label, (uint16_t)outlen);- ptls_buffer_push_block(&hkdf_label, 1, {- if (label_prefix == NULL)- label_prefix = PTLS_HKDF_EXPAND_LABEL_PREFIX;- ptls_buffer_pushv(&hkdf_label, label_prefix, strlen(label_prefix));- ptls_buffer_pushv(&hkdf_label, label, strlen(label));- });- ptls_buffer_push_block(&hkdf_label, 1, { ptls_buffer_pushv(&hkdf_label, hash_value.base, hash_value.len); });-- ret = ptls_hkdf_expand(algo, output, outlen, secret, ptls_iovec_init(hkdf_label.base, hkdf_label.off));--Exit:- ptls_buffer_dispose(&hkdf_label);- return ret;-}--int ptls_tls12_phash(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label,- ptls_iovec_t seed)-{- ptls_hash_context_t *hmac;- uint8_t An[PTLS_MAX_DIGEST_SIZE];- size_t output_off = 0;-- if ((hmac = ptls_hmac_create(algo, secret.base, secret.len)) == NULL)- return PTLS_ERROR_NO_MEMORY;-- /* A(1) = HMAC_hash(secret, label + seed) */- if (label != NULL)- hmac->update(hmac, label, strlen(label));- hmac->update(hmac, seed.base, seed.len);- hmac->final(hmac, An, PTLS_HASH_FINAL_MODE_RESET);-- while (1) {- /* output += HMAC_hash(secret, A(i) + label + seed) */- hmac->update(hmac, An, algo->digest_size);- if (label != NULL)- hmac->update(hmac, label, strlen(label));- hmac->update(hmac, seed.base, seed.len);- if (outlen - output_off <= algo->digest_size) {- /* digest of last chunk is at first written to An then the necessary bytes are copied to output */- hmac->final(hmac, An, PTLS_HASH_FINAL_MODE_FREE);- memcpy((uint8_t *)output + output_off, An, outlen - output_off);- break;- }- hmac->final(hmac, (uint8_t *)output + output_off, PTLS_HASH_FINAL_MODE_RESET);- output_off += algo->digest_size;-- /* A(i) = HMAC_hash(secret, A(i-1)) */- hmac->update(hmac, An, algo->digest_size);- hmac->final(hmac, An, PTLS_HASH_FINAL_MODE_RESET);- }-- ptls_clear_memory(An, algo->digest_size);-- return 0;-}--ptls_cipher_context_t *ptls_cipher_new(ptls_cipher_algorithm_t *algo, int is_enc, const void *key)-{- ptls_cipher_context_t *ctx;-- if ((ctx = (ptls_cipher_context_t *)malloc(algo->context_size)) == NULL)- return NULL;- *ctx = (ptls_cipher_context_t){algo};- if (algo->setup_crypto(ctx, is_enc, key) != 0) {- free(ctx);- ctx = NULL;- }- return ctx;-}--void ptls_cipher_free(ptls_cipher_context_t *ctx)-{- ctx->do_dispose(ctx);- free(ctx);-}--ptls_aead_context_t *new_aead(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret,- ptls_iovec_t hash_value, const char *label_prefix)-{- ptls_aead_context_t *ctx = NULL;- struct {- uint8_t key[PTLS_MAX_SECRET_SIZE];- uint8_t iv[PTLS_MAX_IV_SIZE];- } key_iv;- int ret;-- if ((ret = get_traffic_keys(aead, hash, key_iv.key, key_iv.iv, secret, hash_value, label_prefix)) != 0)- goto Exit;- ctx = ptls_aead_new_direct(aead, is_enc, key_iv.key, key_iv.iv);--Exit:- ptls_clear_memory(&key_iv, sizeof(key_iv));- return ctx;-}--ptls_aead_context_t *ptls_aead_new(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret,- const char *label_prefix)-{- return new_aead(aead, hash, is_enc, secret, ptls_iovec_init(NULL, 0), label_prefix);-}--ptls_aead_context_t *ptls_aead_new_direct(ptls_aead_algorithm_t *aead, int is_enc, const void *key, const void *iv)-{- ptls_aead_context_t *ctx;-- if ((ctx = (ptls_aead_context_t *)malloc(aead->context_size)) == NULL)- return NULL;-- *ctx = (ptls_aead_context_t){aead};-- if (aead->setup_crypto(ctx, is_enc, key, iv) != 0) {- free(ctx);- return NULL;- }-- return ctx;-}--void ptls_aead_free(ptls_aead_context_t *ctx)-{- ctx->dispose_crypto(ctx);- free(ctx);-}--void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *_bytes, size_t len)-{- const uint8_t *bytes = _bytes;- uint8_t iv[PTLS_MAX_IV_SIZE];-- ptls_aead_get_iv(ctx, iv);- for (size_t i = 0; i < len; ++i)- iv[i] ^= bytes[i];- ptls_aead_set_iv(ctx, iv);-}--void ptls_aead__build_iv(ptls_aead_algorithm_t *algo, uint8_t *iv, const uint8_t *static_iv, uint64_t seq)-{- size_t iv_size = algo->iv_size, i;- const uint8_t *s = static_iv;- uint8_t *d = iv;-- /* build iv */- for (i = iv_size - 8; i != 0; --i)- *d++ = *s++;- i = 64;- do {- i -= 8;- *d++ = *s++ ^ (uint8_t)(seq >> i);- } while (i != 0);-}--static void clear_memory(void *p, size_t len)-{- if (len != 0)- memset(p, 0, len);-}--void (*volatile ptls_clear_memory)(void *p, size_t len) = clear_memory;--static int mem_equal(const void *_x, const void *_y, size_t len)-{- const volatile uint8_t *x = _x, *y = _y;- uint8_t t = 0;-- for (; len != 0; --len)- t |= *x++ ^ *y++;-- return t == 0;-}--int (*volatile ptls_mem_equal)(const void *x, const void *y, size_t len) = mem_equal;--static uint64_t get_time(ptls_get_time_t *self)-{- struct timeval tv;- gettimeofday(&tv, NULL);- return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;-}--ptls_get_time_t ptls_get_time = {get_time};-#if PICOTLS_USE_DTRACE-PTLS_THREADLOCAL unsigned ptls_default_skip_tracing = 0;-#endif--int ptls_is_server(ptls_t *tls)-{- return tls->is_server;-}--struct st_ptls_raw_message_emitter_t {- ptls_message_emitter_t super;- size_t start_off;- size_t *epoch_offsets;-};--static int begin_raw_message(ptls_message_emitter_t *_self)-{- struct st_ptls_raw_message_emitter_t *self = (void *)_self;-- self->start_off = self->super.buf->off;- return 0;-}--static int commit_raw_message(ptls_message_emitter_t *_self)-{- struct st_ptls_raw_message_emitter_t *self = (void *)_self;- size_t epoch;-- /* epoch is the key epoch, with the only exception being 2nd CH generated after 0-RTT key */- epoch = self->super.enc->epoch;- if (epoch == 1 && self->super.buf->base[self->start_off] == PTLS_HANDSHAKE_TYPE_CLIENT_HELLO)- epoch = 0;-- for (++epoch; epoch < 5; ++epoch) {- assert(self->epoch_offsets[epoch] == self->start_off);- self->epoch_offsets[epoch] = self->super.buf->off;- }-- self->start_off = SIZE_MAX;-- return 0;-}--size_t ptls_get_read_epoch(ptls_t *tls)-{- switch (tls->state) {- case PTLS_STATE_CLIENT_HANDSHAKE_START:- case PTLS_STATE_CLIENT_EXPECT_SERVER_HELLO:- case PTLS_STATE_CLIENT_EXPECT_SECOND_SERVER_HELLO:- case PTLS_STATE_SERVER_EXPECT_CLIENT_HELLO:- case PTLS_STATE_SERVER_EXPECT_SECOND_CLIENT_HELLO:- return 0; /* plaintext */- case PTLS_STATE_SERVER_EXPECT_END_OF_EARLY_DATA:- assert(!tls->ctx->omit_end_of_early_data);- return 1; /* 0-rtt */- case PTLS_STATE_CLIENT_EXPECT_ENCRYPTED_EXTENSIONS:- case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_REQUEST_OR_CERTIFICATE:- case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE:- case PTLS_STATE_CLIENT_EXPECT_CERTIFICATE_VERIFY:- case PTLS_STATE_CLIENT_EXPECT_FINISHED:- case PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY:- case PTLS_STATE_SERVER_EXPECT_CERTIFICATE:- case PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY:- case PTLS_STATE_SERVER_EXPECT_FINISHED:- return 2; /* handshake */- case PTLS_STATE_CLIENT_POST_HANDSHAKE:- case PTLS_STATE_SERVER_POST_HANDSHAKE:- return 3; /* 1-rtt */- default:- assert(!"invalid state");- return SIZE_MAX;- }-}--int ptls_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input,- size_t inlen, ptls_handshake_properties_t *properties)-{- return tls->is_server ? ptls_server_handle_message(tls, sendbuf, epoch_offsets, in_epoch, input, inlen, properties)- : ptls_client_handle_message(tls, sendbuf, epoch_offsets, in_epoch, input, inlen, properties);-}--int ptls_client_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input,- size_t inlen, ptls_handshake_properties_t *properties)-{- assert(!tls->is_server);-- struct st_ptls_raw_message_emitter_t emitter = {- {sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, SIZE_MAX, epoch_offsets};- struct st_ptls_record_t rec = {PTLS_CONTENT_TYPE_HANDSHAKE, 0, inlen, input};-- if (input == NULL)- return send_client_hello(tls, &emitter.super, properties, NULL);-- if (ptls_get_read_epoch(tls) != in_epoch)- return PTLS_ALERT_UNEXPECTED_MESSAGE;-- return handle_handshake_record(tls, handle_client_handshake_message, &emitter.super, &rec, properties);-}--int ptls_server_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input,- size_t inlen, ptls_handshake_properties_t *properties)-{- assert(tls->is_server);-- struct st_ptls_raw_message_emitter_t emitter = {- {sendbuf, &tls->traffic_protection.enc, 0, begin_raw_message, commit_raw_message}, SIZE_MAX, epoch_offsets};- struct st_ptls_record_t rec = {PTLS_CONTENT_TYPE_HANDSHAKE, 0, inlen, input};-- if (tls->state == PTLS_STATE_SERVER_GENERATING_CERTIFICATE_VERIFY) {- assert(input == NULL || inlen == 0);- return server_finish_handshake(tls, &emitter.super, 1, NULL);- }-- assert(input != NULL);-- if (ptls_get_read_epoch(tls) != in_epoch)- return PTLS_ALERT_UNEXPECTED_MESSAGE;-- return handle_handshake_record(tls, handle_server_handshake_message, &emitter.super, &rec, properties);-}--/**- * checks if given name looks like an IP address- */-int ptls_server_name_is_ipaddr(const char *name)-{-#ifdef AF_INET- struct sockaddr_in sin;- if (inet_pton(AF_INET, name, &sin) == 1)- return 1;-#endif-#ifdef AF_INET6- struct sockaddr_in6 sin6;- if (inet_pton(AF_INET6, name, &sin6) == 1)- return 1;-#endif- return 0;-}--int ptls_ech_encode_config(ptls_buffer_t *buf, uint8_t config_id, ptls_hpke_kem_t *kem, ptls_iovec_t public_key,- ptls_hpke_cipher_suite_t **ciphers, uint8_t max_name_length, const char *public_name)-{- int ret;-- ptls_buffer_push16(buf, PTLS_ECH_CONFIG_VERSION);- ptls_buffer_push_block(buf, 2, {- ptls_buffer_push(buf, config_id);- ptls_buffer_push16(buf, kem->id);- ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, public_key.base, public_key.len); });- ptls_buffer_push_block(buf, 2, {- for (size_t i = 0; ciphers[i] != NULL; ++i) {- ptls_buffer_push16(buf, ciphers[i]->id.kdf);- ptls_buffer_push16(buf, ciphers[i]->id.aead);- }- });- ptls_buffer_push(buf, max_name_length);- ptls_buffer_push_block(buf, 1, { ptls_buffer_pushv(buf, public_name, strlen(public_name)); });- ptls_buffer_push_block(buf, 2, {/* extensions */});- });--Exit:- return ret;-}--static char *byte_to_hex(char *dst, uint8_t v)-{- *dst++ = "0123456789abcdef"[v >> 4];- *dst++ = "0123456789abcdef"[v & 0xf];- return dst;-}--char *ptls_hexdump(char *dst, const void *_src, size_t len)-{- char *buf = dst;- const uint8_t *src = _src;-- for (size_t i = 0; i != len; ++i)- dst = byte_to_hex(dst, src[i]);- *dst = '\0';- return buf;-}--char *ptls_jsonescape(char *buf, const char *unsafe_str, size_t len)-{- char *dst = buf;- const uint8_t *src = (const uint8_t *)unsafe_str, *end = src + len;-- for (; src != end; ++src) {- switch (*src) {-#define MAP(ch, escaped) \- case ch: \- memcpy(dst, (escaped), sizeof(escaped) - 1); \- dst += sizeof(escaped) - 1; \- break- MAP('"', "\\\"");- MAP('\\', "\\\\");- MAP('/', "\\/");- MAP('\b', "\\b");- MAP('\f', "\\f");- MAP('\n', "\\n");- MAP('\r', "\\r");- MAP('\t', "\\t");-#undef MAP- default:- if (*src < 0x20 || *src == 0x7f) {- *dst++ = '\\';- *dst++ = 'u';- *dst++ = '0';- *dst++ = '0';- dst = byte_to_hex(dst, *src);- } else {- *dst++ = *src;- }- break;- }- }- *dst = '\0';-- return dst;-}--int ptls_log__do_pushv(ptls_buffer_t *buf, const void *p, size_t l)-{- if (ptls_buffer_reserve(buf, l) != 0)- return 0;-- memcpy(buf->base + buf->off, p, l);- buf->off += l;- return 1;-}--int ptls_log__do_push_unsafestr(ptls_buffer_t *buf, const char *s, size_t l)-{- if (ptls_buffer_reserve(buf, l * (sizeof("\\uXXXX") - 1) + 1) != 0)- return 0;-- buf->off = (uint8_t *)ptls_jsonescape((char *)(buf->base + buf->off), s, l) - buf->base;-- return 1;-}--int ptls_log__do_push_hexdump(ptls_buffer_t *buf, const void *s, size_t l)-{- if (ptls_buffer_reserve(buf, l * 2 + 1) != 0)- return 0;-- ptls_hexdump((char *)(buf->base + buf->off), s, l);- buf->off += l * 2;-- return 1;-}--int ptls_log__do_push_signed32(ptls_buffer_t *buf, int32_t v)-{- /* TODO optimize */- char s[sizeof("-2147483648")];- int len = snprintf(s, sizeof(s), "%" PRId32, v);- return ptls_log__do_pushv(buf, s, (size_t)len);-}--int ptls_log__do_push_signed64(ptls_buffer_t *buf, int64_t v)-{- /* TODO optimize */- char s[sizeof("-9223372036854775808")];- int len = snprintf(s, sizeof(s), "%" PRId64, v);- return ptls_log__do_pushv(buf, s, (size_t)len);-}--int ptls_log__do_push_unsigned32(ptls_buffer_t *buf, uint32_t v)-{- /* TODO optimize */- char s[sizeof("4294967295")];- int len = snprintf(s, sizeof(s), "%" PRIu32, v);- return ptls_log__do_pushv(buf, s, (size_t)len);-}--int ptls_log__do_push_unsigned64(ptls_buffer_t *buf, uint64_t v)-{- /* TODO optimize */- char s[sizeof("18446744073709551615")];- int len = snprintf(s, sizeof(s), "%" PRIu64, v);- return ptls_log__do_pushv(buf, s, (size_t)len);-}--#if PTLS_HAVE_LOG--volatile ptls_log_t ptls_log = {};--static struct {- int *fds;- size_t num_fds;- size_t num_lost;- pthread_mutex_t mutex;-} logctx = {.mutex = PTHREAD_MUTEX_INITIALIZER};--size_t ptls_log_num_lost(void)-{- return logctx.num_lost;-}--int ptls_log_add_fd(int fd)-{- int ret;-- pthread_mutex_lock(&logctx.mutex);-- int *newfds;- if ((newfds = realloc(logctx.fds, sizeof(logctx.fds[0]) * (logctx.num_fds + 1))) == NULL) {- ret = PTLS_ERROR_NO_MEMORY;- goto Exit;- }- logctx.fds = newfds;- logctx.fds[logctx.num_fds++] = fd;- ptls_log.is_active = 1;-- ret = 0; /* success */--Exit:- pthread_mutex_unlock(&logctx.mutex);- return ret;-}--#endif--void ptls_log__do_write(const ptls_buffer_t *buf)-{-#if PTLS_HAVE_LOG- pthread_mutex_lock(&logctx.mutex);-- for (size_t fd_index = 0; fd_index < logctx.num_fds;) {- ssize_t ret;- while ((ret = write(logctx.fds[fd_index], buf->base, buf->off)) == -1 && errno == EINTR)- ;- if (ret == buf->off) {- /* success */- ++fd_index;- } else if (ret > 0 || (ret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))) {- /* partial write or buffer full */- ++logctx.num_lost;- ++fd_index;- } else {- /* write error; close and remove that fd from array */- close(logctx.fds[fd_index]);- logctx.fds[fd_index] = logctx.fds[logctx.num_fds - 1];- --logctx.num_fds;- if (logctx.num_fds == 0)- ptls_log.is_active = 0;- }- }-- pthread_mutex_unlock(&logctx.mutex);-#endif-}
− cbits/picotls.h
@@ -1,1963 +0,0 @@-/*- * Copyright (c) 2016 DeNA Co., Ltd., Kazuho Oku- *- * Permission is hereby granted, free of charge, to any person obtaining a copy- * of this software and associated documentation files (the "Software"), to- * deal in the Software without restriction, including without limitation the- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or- * sell copies of the Software, and to permit persons to whom the Software is- * furnished to do so, subject to the following conditions:- *- * The above copyright notice and this permission notice shall be included in- * all copies or substantial portions of the Software.- *- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS- * IN THE SOFTWARE.- */-#ifndef picotls_h-#define picotls_h--#ifdef __cplusplus-extern "C" {-#endif--#ifdef _WINDOWS-#include "winsock.h"-#endif--#include <assert.h>-#include <inttypes.h>-#include <string.h>-#include <sys/types.h>--#if __GNUC__ >= 3-#define PTLS_LIKELY(x) __builtin_expect(!!(x), 1)-#define PTLS_UNLIKELY(x) __builtin_expect(!!(x), 0)-#define PTLS_BUILD_ASSERT_EXPR(cond) (sizeof(char[2 * !!(!__builtin_constant_p(cond) || (cond)) - 1]) != 0)-#define PTLS_BUILD_ASSERT(cond) ((void)PTLS_BUILD_ASSERT_EXPR(cond))-#else-#define PTLS_LIKELY(x) (x)-#define PTLS_UNLIKELY(x) (x)-#define PTLS_BUILD_ASSERT(cond) 1-#endif--/* __builtin_types_compatible_p yields incorrect results when older versions of GCC is used; see #303.- * Clang with Xcode 9.4 or prior is known to not work correctly when a pointer is const-qualified; see- * https://github.com/h2o/quicly/pull/306#issuecomment-626037269. Older versions of clang upstream works fine, but we do not need- * best coverage. This macro is for preventing misuse going into the master branch, having it work one of the compilers supported in- * our CI is enough.- */-#if ((defined(__clang__) && __clang_major__ >= 10) || __GNUC__ >= 6) && !defined(__cplusplus)-#define PTLS_ASSERT_IS_ARRAY_EXPR(a) PTLS_BUILD_ASSERT_EXPR(__builtin_types_compatible_p(__typeof__(a[0])[], __typeof__(a)))-#else-#define PTLS_ASSERT_IS_ARRAY_EXPR(a) 1-#endif--#define PTLS_ELEMENTSOF(x) (PTLS_ASSERT_IS_ARRAY_EXPR(x) * sizeof(x) / sizeof((x)[0]))--#ifdef _WINDOWS-#define PTLS_THREADLOCAL __declspec(thread)-#else-#define PTLS_THREADLOCAL __thread-#define PTLS_HAVE_LOG 1-#endif--#ifndef PTLS_FUZZ_HANDSHAKE-#define PTLS_FUZZ_HANDSHAKE 0-#endif--#define PTLS_HELLO_RANDOM_SIZE 32--#define PTLS_AES128_KEY_SIZE 16-#define PTLS_AES256_KEY_SIZE 32-#define PTLS_AES_BLOCK_SIZE 16-#define PTLS_AES_IV_SIZE 16-#define PTLS_AESGCM_IV_SIZE 12-#define PTLS_AESGCM_TAG_SIZE 16-#define PTLS_AESGCM_CONFIDENTIALITY_LIMIT 0x2000000 /* 2^25 */-#define PTLS_AESGCM_INTEGRITY_LIMIT UINT64_C(0x40000000000000) /* 2^54 */-#define PTLS_AESCCM_CONFIDENTIALITY_LIMIT 0xB504F3 /* 2^23.5 */-#define PTLS_AESCCM_INTEGRITY_LIMIT 0xB504F3 /* 2^23.5 */--#define PTLS_CHACHA20_KEY_SIZE 32-#define PTLS_CHACHA20_IV_SIZE 16 /* contrary to RFC 7539, follow OpenSSL way of using first 32 bits as ctr and latter 96 as IV */-#define PTLS_CHACHA20POLY1305_IV_SIZE 12-#define PTLS_CHACHA20POLY1305_TAG_SIZE 16-#define PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT UINT64_MAX /* at least 2^64 */-#define PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT UINT64_C(0x1000000000) /* 2^36 */--#define PTLS_BLOWFISH_KEY_SIZE 16-#define PTLS_BLOWFISH_BLOCK_SIZE 8--#define PTLS_SHA256_BLOCK_SIZE 64-#define PTLS_SHA256_DIGEST_SIZE 32--#define PTLS_SHA384_BLOCK_SIZE 128-#define PTLS_SHA384_DIGEST_SIZE 48--#define PTLS_SHA512_BLOCK_SIZE 128-#define PTLS_SHA512_DIGEST_SIZE 64--#define PTLS_MAX_SECRET_SIZE 32-#define PTLS_MAX_IV_SIZE 16-#define PTLS_MAX_DIGEST_SIZE 64--/* versions */-#define PTLS_PROTOCOL_VERSION_TLS12 0x0303-#define PTLS_PROTOCOL_VERSION_TLS13 0x0304--/* cipher-suites */-#define PTLS_CIPHER_SUITE_AES_128_GCM_SHA256 0x1301-#define PTLS_CIPHER_SUITE_NAME_AES_128_GCM_SHA256 "TLS_AES_128_GCM_SHA256"-#define PTLS_CIPHER_SUITE_AES_256_GCM_SHA384 0x1302-#define PTLS_CIPHER_SUITE_NAME_AES_256_GCM_SHA384 "TLS_AES_256_GCM_SHA384"-#define PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256 0x1303-#define PTLS_CIPHER_SUITE_NAME_CHACHA20_POLY1305_SHA256 "TLS_CHACHA20_POLY1305_SHA256"--/* TLS/1.2 cipher-suites that we support (for compatibility, OpenSSL names are used) */-#define PTLS_CIPHER_SUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xc02b-#define PTLS_CIPHER_SUITE_NAME_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 "ECDHE-ECDSA-AES128-GCM-SHA256"-#define PTLS_CIPHER_SUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xc02c-#define PTLS_CIPHER_SUITE_NAME_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 "ECDHE-ECDSA-AES256-GCM-SHA384"-#define PTLS_CIPHER_SUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xc02f-#define PTLS_CIPHER_SUITE_NAME_ECDHE_RSA_WITH_AES_128_GCM_SHA256 "ECDHE-RSA-AES128-GCM-SHA256"-#define PTLS_CIPHER_SUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xc030-#define PTLS_CIPHER_SUITE_NAME_ECDHE_RSA_WITH_AES_256_GCM_SHA384 "ECDHE-RSA-AES256-GCM-SHA384"-#define PTLS_CIPHER_SUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xcca8-#define PTLS_CIPHER_SUITE_NAME_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 "ECDHE-RSA-CHACHA20-POLY1305"-#define PTLS_CIPHER_SUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xcca9-#define PTLS_CIPHER_SUITE_NAME_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 "ECDHE-ECDSA-CHACHA20-POLY1305"--/* negotiated_groups */-#define PTLS_GROUP_SECP256R1 23-#define PTLS_GROUP_NAME_SECP256R1 "secp256r1"-#define PTLS_GROUP_SECP384R1 24-#define PTLS_GROUP_NAME_SECP384R1 "secp384r1"-#define PTLS_GROUP_SECP521R1 25-#define PTLS_GROUP_NAME_SECP521R1 "secp521r1"-#define PTLS_GROUP_X25519 29-#define PTLS_GROUP_NAME_X25519 "x25519"-#define PTLS_GROUP_X448 30-#define PTLS_GROUP_NAME_X448 "x448"--/* signature algorithms */-#define PTLS_SIGNATURE_RSA_PKCS1_SHA1 0x0201-#define PTLS_SIGNATURE_RSA_PKCS1_SHA256 0x0401-#define PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256 0x0403-#define PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384 0x0503-#define PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512 0x0603-#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256 0x0804-#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384 0x0805-#define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 0x0806-#define PTLS_SIGNATURE_ED25519 0x0807--/* HPKE */-#define PTLS_HPKE_MODE_BASE 0-#define PTLS_HPKE_MODE_PSK 1-#define PTLS_HPKE_MODE_AUTH 2-#define PTLS_HPKE_MODE_AUTH_PSK 3-#define PTLS_HPKE_KEM_P256_SHA256 16-#define PTLS_HPKE_KEM_P384_SHA384 17-#define PTLS_HPKE_KEM_X25519_SHA256 32-#define PTLS_HPKE_HKDF_SHA256 1-#define PTLS_HPKE_HKDF_SHA384 2-#define PTLS_HPKE_HKDF_SHA512 3-#define PTLS_HPKE_AEAD_AES_128_GCM 1-#define PTLS_HPKE_AEAD_AES_256_GCM 2-#define PTLS_HPKE_AEAD_CHACHA20POLY1305 3--/* error classes and macros */-#define PTLS_ERROR_CLASS_SELF_ALERT 0-#define PTLS_ERROR_CLASS_PEER_ALERT 0x100-#define PTLS_ERROR_CLASS_INTERNAL 0x200--#define PTLS_ERROR_GET_CLASS(e) ((e) & ~0xff)-#define PTLS_ALERT_TO_SELF_ERROR(e) ((e) + PTLS_ERROR_CLASS_SELF_ALERT)-#define PTLS_ALERT_TO_PEER_ERROR(e) ((e) + PTLS_ERROR_CLASS_PEER_ALERT)-#define PTLS_ERROR_TO_ALERT(e) ((e)&0xff)--/* the HKDF prefix */-#define PTLS_HKDF_EXPAND_LABEL_PREFIX "tls13 "--/* alerts */-#define PTLS_ALERT_LEVEL_WARNING 1-#define PTLS_ALERT_LEVEL_FATAL 2--#define PTLS_ALERT_CLOSE_NOTIFY 0-#define PTLS_ALERT_UNEXPECTED_MESSAGE 10-#define PTLS_ALERT_BAD_RECORD_MAC 20-#define PTLS_ALERT_HANDSHAKE_FAILURE 40-#define PTLS_ALERT_BAD_CERTIFICATE 42-#define PTLS_ALERT_UNSUPPORTED_CERTIFICATE 43-#define PTLS_ALERT_CERTIFICATE_REVOKED 44-#define PTLS_ALERT_CERTIFICATE_EXPIRED 45-#define PTLS_ALERT_CERTIFICATE_UNKNOWN 46-#define PTLS_ALERT_ILLEGAL_PARAMETER 47-#define PTLS_ALERT_UNKNOWN_CA 48-#define PTLS_ALERT_ACCESS_DENIED 49-#define PTLS_ALERT_DECODE_ERROR 50-#define PTLS_ALERT_DECRYPT_ERROR 51-#define PTLS_ALERT_PROTOCOL_VERSION 70-#define PTLS_ALERT_INTERNAL_ERROR 80-#define PTLS_ALERT_USER_CANCELED 90-#define PTLS_ALERT_MISSING_EXTENSION 109-#define PTLS_ALERT_UNSUPPORTED_EXTENSION 110-#define PTLS_ALERT_UNRECOGNIZED_NAME 112-#define PTLS_ALERT_CERTIFICATE_REQUIRED 116-#define PTLS_ALERT_NO_APPLICATION_PROTOCOL 120-#define PTLS_ALERT_ECH_REQUIRED 121--/* TLS 1.2 */-#define PTLS_TLS12_MASTER_SECRET_SIZE 48-#define PTLS_TLS12_AAD_SIZE 13-#define PTLS_TLS12_AESGCM_FIXED_IV_SIZE 4-#define PTLS_TLS12_AESGCM_RECORD_IV_SIZE 8-#define PTLS_TLS12_CHACHAPOLY_FIXED_IV_SIZE 12-#define PTLS_TLS12_CHACHAPOLY_RECORD_IV_SIZE 0--/* internal errors */-#define PTLS_ERROR_NO_MEMORY (PTLS_ERROR_CLASS_INTERNAL + 1)-#define PTLS_ERROR_IN_PROGRESS (PTLS_ERROR_CLASS_INTERNAL + 2)-#define PTLS_ERROR_LIBRARY (PTLS_ERROR_CLASS_INTERNAL + 3)-#define PTLS_ERROR_INCOMPATIBLE_KEY (PTLS_ERROR_CLASS_INTERNAL + 4)-#define PTLS_ERROR_SESSION_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 5)-#define PTLS_ERROR_STATELESS_RETRY (PTLS_ERROR_CLASS_INTERNAL + 6)-#define PTLS_ERROR_NOT_AVAILABLE (PTLS_ERROR_CLASS_INTERNAL + 7)-#define PTLS_ERROR_COMPRESSION_FAILURE (PTLS_ERROR_CLASS_INTERNAL + 8)-#define PTLS_ERROR_REJECT_EARLY_DATA (PTLS_ERROR_CLASS_INTERNAL + 9)-#define PTLS_ERROR_DELEGATE (PTLS_ERROR_CLASS_INTERNAL + 10)-#define PTLS_ERROR_ASYNC_OPERATION (PTLS_ERROR_CLASS_INTERNAL + 11)--#define PTLS_ERROR_INCORRECT_BASE64 (PTLS_ERROR_CLASS_INTERNAL + 50)-#define PTLS_ERROR_PEM_LABEL_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 51)-#define PTLS_ERROR_BER_INCORRECT_ENCODING (PTLS_ERROR_CLASS_INTERNAL + 52)-#define PTLS_ERROR_BER_MALFORMED_TYPE (PTLS_ERROR_CLASS_INTERNAL + 53)-#define PTLS_ERROR_BER_MALFORMED_LENGTH (PTLS_ERROR_CLASS_INTERNAL + 54)-#define PTLS_ERROR_BER_EXCESSIVE_LENGTH (PTLS_ERROR_CLASS_INTERNAL + 55)-#define PTLS_ERROR_BER_ELEMENT_TOO_SHORT (PTLS_ERROR_CLASS_INTERNAL + 56)-#define PTLS_ERROR_BER_UNEXPECTED_EOC (PTLS_ERROR_CLASS_INTERNAL + 57)-#define PTLS_ERROR_DER_INDEFINITE_LENGTH (PTLS_ERROR_CLASS_INTERNAL + 58)-#define PTLS_ERROR_INCORRECT_ASN1_SYNTAX (PTLS_ERROR_CLASS_INTERNAL + 59)-#define PTLS_ERROR_INCORRECT_PEM_KEY_VERSION (PTLS_ERROR_CLASS_INTERNAL + 60)-#define PTLS_ERROR_INCORRECT_PEM_ECDSA_KEY_VERSION (PTLS_ERROR_CLASS_INTERNAL + 61)-#define PTLS_ERROR_INCORRECT_PEM_ECDSA_CURVE (PTLS_ERROR_CLASS_INTERNAL + 62)-#define PTLS_ERROR_INCORRECT_PEM_ECDSA_KEYSIZE (PTLS_ERROR_CLASS_INTERNAL + 63)-#define PTLS_ERROR_INCORRECT_ASN1_ECDSA_KEY_SYNTAX (PTLS_ERROR_CLASS_INTERNAL + 64)--#define PTLS_HANDSHAKE_TYPE_CLIENT_HELLO 1-#define PTLS_HANDSHAKE_TYPE_SERVER_HELLO 2-#define PTLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET 4-#define PTLS_HANDSHAKE_TYPE_END_OF_EARLY_DATA 5-#define PTLS_HANDSHAKE_TYPE_ENCRYPTED_EXTENSIONS 8-#define PTLS_HANDSHAKE_TYPE_CERTIFICATE 11-#define PTLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST 13-#define PTLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY 15-#define PTLS_HANDSHAKE_TYPE_FINISHED 20-#define PTLS_HANDSHAKE_TYPE_KEY_UPDATE 24-#define PTLS_HANDSHAKE_TYPE_COMPRESSED_CERTIFICATE 25-#define PTLS_HANDSHAKE_TYPE_MESSAGE_HASH 254-#define PTLS_HANDSHAKE_TYPE_PSEUDO_HRR -1--#define PTLS_CERTIFICATE_TYPE_X509 0-#define PTLS_CERTIFICATE_TYPE_RAW_PUBLIC_KEY 2--#define PTLS_ZERO_DIGEST_SHA256 \- { \- 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, \- 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 \- }--#define PTLS_ZERO_DIGEST_SHA384 \- { \- 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, \- 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, \- 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b \- }--#define PTLS_ZERO_DIGEST_SHA512 \- { \- 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, \- 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, \- 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, \- 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e \- }--#define PTLS_TO__STR(n) #n-#define PTLS_TO_STR(n) PTLS_TO__STR(n)--typedef struct st_ptls_t ptls_t;-typedef struct st_ptls_context_t ptls_context_t;-typedef struct st_ptls_key_schedule_t ptls_key_schedule_t;--/**- * represents a sequence of octets- */-typedef struct st_ptls_iovec_t {- uint8_t *base;- size_t len;-} ptls_iovec_t;--/**- * used for storing output- */-typedef struct st_ptls_buffer_t {- uint8_t *base;- size_t capacity;- size_t off;- uint8_t is_allocated; /* boolean */- uint8_t align_bits; /* if particular alignment is required, set to log2(alignment); otherwize zero */-} ptls_buffer_t;--/**- * key exchange context built by ptls_key_exchange_algorithm::create.- */-typedef struct st_ptls_key_exchange_context_t {- /**- * the underlying algorithm- */- const struct st_ptls_key_exchange_algorithm_t *algo;- /**- * the public key- */- ptls_iovec_t pubkey;- /**- * If `release` is set, the callee frees resources allocated to the context and set *keyex to NULL- */- int (*on_exchange)(struct st_ptls_key_exchange_context_t **keyex, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey);-} ptls_key_exchange_context_t;--/**- * A key exchange algorithm.- */-typedef const struct st_ptls_key_exchange_algorithm_t {- /**- * ID defined by the TLS specification- */- uint16_t id;- /**- * creates a context for asynchronous key exchange. The function is called when ClientHello is generated. The on_exchange- * callback of the created context is called when the client receives ServerHello.- */- int (*create)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx);- /**- * implements synchronous key exchange. Called when receiving a ServerHello.- */- int (*exchange)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret,- ptls_iovec_t peerkey);- /**- * crypto-specific data- */- intptr_t data;- /**- * Description as defined in the IANA TLS registry- */- const char *name;-} ptls_key_exchange_algorithm_t;--/**- * context of a symmetric cipher- */-typedef struct st_ptls_cipher_context_t {- const struct st_ptls_cipher_algorithm_t *algo;- /* field above this line must not be altered by the crypto binding */- void (*do_dispose)(struct st_ptls_cipher_context_t *ctx);- void (*do_init)(struct st_ptls_cipher_context_t *ctx, const void *iv);- void (*do_transform)(struct st_ptls_cipher_context_t *ctx, void *output, const void *input, size_t len);-} ptls_cipher_context_t;--/**- * a symmetric cipher- */-typedef const struct st_ptls_cipher_algorithm_t {- const char *name;- size_t key_size;- size_t block_size;- size_t iv_size;- size_t context_size;- int (*setup_crypto)(ptls_cipher_context_t *ctx, int is_enc, const void *key);-} ptls_cipher_algorithm_t;--typedef struct st_ptls_aead_supplementary_encryption_t {- ptls_cipher_context_t *ctx;- const void *input;- uint8_t output[16];-} ptls_aead_supplementary_encryption_t;--/**- * AEAD context. AEAD implementations are allowed to stuff data at the end of the struct. The size of the memory allocated for the- * struct is governed by ptls_aead_algorithm_t::context_size.- * Ciphers for TLS over TCP MUST implement `do_encrypt`, `do_encrypt_v`, `do_decrypt`. `do_encrypt_init`, `~update`, `~final` are- * obsolete, and therefore may not be available.- */-typedef struct st_ptls_aead_context_t {- const struct st_ptls_aead_algorithm_t *algo;- /* field above this line must not be altered by the crypto binding */- void (*dispose_crypto)(struct st_ptls_aead_context_t *ctx);- void (*do_get_iv)(struct st_ptls_aead_context_t *ctx, void *iv);- void (*do_set_iv)(struct st_ptls_aead_context_t *ctx, const void *iv);- void (*do_encrypt_init)(struct st_ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen);- size_t (*do_encrypt_update)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen);- size_t (*do_encrypt_final)(struct st_ptls_aead_context_t *ctx, void *output);- void (*do_encrypt)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp);- void (*do_encrypt_v)(struct st_ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,- const void *aad, size_t aadlen);- size_t (*do_decrypt)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen);-} ptls_aead_context_t;--/**- * An AEAD cipher.- */-typedef const struct st_ptls_aead_algorithm_t {- /**- * name (following the convention of `openssl ciphers -v ALL`)- */- const char *name;- /**- * confidentiality_limit (max records / packets sent before re-key)- */- const uint64_t confidentiality_limit;- /**- * integrity_limit (max decryption failure records / packets before re-key)- */- const uint64_t integrity_limit;- /**- * the underlying key stream- */- ptls_cipher_algorithm_t *ctr_cipher;- /**- * the underlying ecb cipher (might not be available)- */- ptls_cipher_algorithm_t *ecb_cipher;- /**- * key size- */- size_t key_size;- /**- * size of the IV- */- size_t iv_size;- /**- * size of the tag- */- size_t tag_size;- /**- * TLS/1.2 Security Parameters (AEAD without support for TLS 1.2 must set both values to 0)- */- struct {- size_t fixed_iv_size;- size_t record_iv_size;- } tls12;- /**- * if encrypted bytes are going to be written using non-temporal store instructions (i.e., skip cache)- */- unsigned non_temporal : 1;- /**- * log2(alignment) being required- */- uint8_t align_bits;- /**- * size of memory allocated for ptls_aead_context_t. AEAD implementations can set this value to something greater than- * sizeof(ptls_aead_context_t) and stuff additional data at the bottom of the struct.- */- size_t context_size;- /**- * callback that sets up the crypto- */- int (*setup_crypto)(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv);-} ptls_aead_algorithm_t;--/**- *- */-typedef enum en_ptls_hash_final_mode_t {- /**- * obtains the digest and frees the context- */- PTLS_HASH_FINAL_MODE_FREE = 0,- /**- * obtains the digest and reset the context to initial state- */- PTLS_HASH_FINAL_MODE_RESET = 1,- /**- * obtains the digest while leaving the context as-is- */- PTLS_HASH_FINAL_MODE_SNAPSHOT = 2-} ptls_hash_final_mode_t;--/**- * A hash context.- */-typedef struct st_ptls_hash_context_t {- /**- * feeds additional data into the hash context- */- void (*update)(struct st_ptls_hash_context_t *ctx, const void *src, size_t len);- /**- * returns the digest and performs necessary operation specified by mode- */- void (*final)(struct st_ptls_hash_context_t *ctx, void *md, ptls_hash_final_mode_t mode);- /**- * creates a copy of the hash context- */- struct st_ptls_hash_context_t *(*clone_)(struct st_ptls_hash_context_t *src);-} ptls_hash_context_t;--/**- * A hash algorithm and its properties.- */-typedef const struct st_ptls_hash_algorithm_t {- /**- * name of the hash algorithm- */- const char *name;- /**- * block size- */- size_t block_size;- /**- * digest size- */- size_t digest_size;- /**- * constructor that creates the hash context- */- ptls_hash_context_t *(*create)(void);- /**- * digest of zero-length octets- */- uint8_t empty_digest[PTLS_MAX_DIGEST_SIZE];-} ptls_hash_algorithm_t;--typedef const struct st_ptls_cipher_suite_t {- /**- * ID as defined by the TLS Cipher Suites registry- */- uint16_t id;- /**- * underlying AEAD algorithm- */- ptls_aead_algorithm_t *aead;- /**- * underlying hash algorithm- */- ptls_hash_algorithm_t *hash;- /**- * value of the "Description" field of the TLS Cipher Suites registry- */- const char *name;-} ptls_cipher_suite_t;--struct st_ptls_traffic_protection_t;--typedef struct st_ptls_message_emitter_t {- ptls_buffer_t *buf;- struct st_ptls_traffic_protection_t *enc;- size_t record_header_length;- int (*begin_message)(struct st_ptls_message_emitter_t *self);- int (*commit_message)(struct st_ptls_message_emitter_t *self);-} ptls_message_emitter_t;--/**- * HPKE KEM- */-typedef const struct st_ptls_hpke_kem_t {- uint16_t id;- ptls_key_exchange_algorithm_t *keyex;- ptls_hash_algorithm_t *hash;-} ptls_hpke_kem_t;--typedef struct st_ptls_hpke_cipher_suite_id_t {- uint16_t kdf;- uint16_t aead;-} ptls_hpke_cipher_suite_id_t;--typedef const struct st_ptls_hpke_cipher_suite_t {- ptls_hpke_cipher_suite_id_t id;- const char *name; /* in form of "<kdf>/<aead>" using the sames specified in IANA HPKE registry */- ptls_hash_algorithm_t *hash;- ptls_aead_algorithm_t *aead;-} ptls_hpke_cipher_suite_t;--#define PTLS_CALLBACK_TYPE0(ret, name) \- typedef struct st_ptls_##name##_t { \- ret (*cb)(struct st_ptls_##name##_t * self); \- } ptls_##name##_t--#define PTLS_CALLBACK_TYPE(ret, name, ...) \- typedef struct st_ptls_##name##_t { \- ret (*cb)(struct st_ptls_##name##_t * self, __VA_ARGS__); \- } ptls_##name##_t--/**- * arguments passsed to the on_client_hello callback- */-typedef struct st_ptls_on_client_hello_parameters_t {- /**- * SNI value received from the client. The value is {NULL, 0} if the extension was absent.- */- ptls_iovec_t server_name;- /**- * Raw value of the client_hello message.- */- ptls_iovec_t raw_message;- /**- * points to the cipher-suites section of the raw_message (see above)- */- ptls_iovec_t cipher_suites;- /**- *- */- struct {- ptls_iovec_t *list;- size_t count;- } negotiated_protocols;- struct {- const uint16_t *list;- size_t count;- } signature_algorithms;- struct {- const uint16_t *list;- size_t count;- } certificate_compression_algorithms;- struct {- const uint8_t *list;- size_t count;- } server_certificate_types;- /**- * set to 1 if ClientHello is too old (or too new) to be handled by picotls- */- unsigned incompatible_version : 1;-} ptls_on_client_hello_parameters_t;--/**- * returns current time in milliseconds (ptls_get_time can be used to return the physical time)- */-PTLS_CALLBACK_TYPE0(uint64_t, get_time);-/**- * after receiving ClientHello, the core calls the optional callback to give a chance to the swap the context depending on the input- * values. The callback is required to call `ptls_set_server_name` if an SNI extension needs to be sent to the client.- */-PTLS_CALLBACK_TYPE(int, on_client_hello, ptls_t *tls, ptls_on_client_hello_parameters_t *params);-/**- * callback to generate the certificate message. `ptls_context::certificates` are set when the callback is set to NULL.- */-PTLS_CALLBACK_TYPE(int, emit_certificate, ptls_t *tls, ptls_message_emitter_t *emitter, ptls_key_schedule_t *key_sched,- ptls_iovec_t context, int push_status_request, const uint16_t *compress_algos, size_t num_compress_algos);-/**- * An object that represents an asynchronous task (e.g., RSA signature generation).- * When `ptls_handshake` returns `PTLS_ERROR_ASYNC_OPERATION`, it has an associated task in flight. The user should obtain the- * reference to the associated task by calling `ptls_get_async_job`, then either wait for the file descriptor obtained from- * the `get_fd` callback to become readable, or set a completion callback via `set_completion_callback` and wait for its- * invocation. Once notified, the user should invoke `ptls_handshake` again.- * Async jobs typically provide support for only one of the two methods.- */-typedef struct st_ptls_async_job_t {- void (*destroy_)(struct st_ptls_async_job_t *self);- /**- * optional callback returning a file descriptor that becomes readable when the job is complete- */- int (*get_fd)(struct st_ptls_async_job_t *self);- /**- * optional callback for setting a completion callback- */- void (*set_completion_callback)(struct st_ptls_async_job_t *self, void (*cb)(void *), void *cbdata);-} ptls_async_job_t;-/**- * When gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate. This callback- * supports asynchronous mode; see `ptls_openssl_sign_certificate_t` for more information.- */-PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, ptls_async_job_t **async, uint16_t *selected_algorithm,- ptls_buffer_t *output, ptls_iovec_t input, const uint16_t *algorithms, size_t num_algorithms);-/**- * after receiving Certificate, the core calls the callback to verify the certificate chain and to obtain a pointer to a- * callback that should be used for verifying CertificateVerify. If an error occurs between a successful return from this- * callback to the invocation of the verify_sign callback, verify_sign is called with both data and sign set to an empty buffer.- * The implementor of the callback should use that as the opportunity to free any temporary data allocated for the verify_sign- * callback.- * The name of the server to be verified, if any, is provided explicitly as `server_name`. When ECH is offered by the client but- * the was rejected by the server, this value can be different from that being sent via `ptls_get_server_name`.- */-typedef struct st_ptls_verify_certificate_t {- int (*cb)(struct st_ptls_verify_certificate_t *self, ptls_t *tls, const char *server_name,- int (**verify_sign)(void *verify_ctx, uint16_t algo, ptls_iovec_t data, ptls_iovec_t sign), void **verify_data,- ptls_iovec_t *certs, size_t num_certs);- /**- * list of signature algorithms being supported, terminated by UINT16_MAX- */- const uint16_t *algos;-} ptls_verify_certificate_t;-/**- * Encrypt-and-signs (or verify-and-decrypts) a ticket (server-only).- * When used for encryption (i.e., is_encrypt being set), the function should return 0 if successful, or else a non-zero value.- * When used for decryption, the function should return 0 (successful), PTLS_ERROR_REJECT_EARLY_DATA (successful, but 0-RTT is- * forbidden), or any other value to indicate failure.- */-PTLS_CALLBACK_TYPE(int, encrypt_ticket, ptls_t *tls, int is_encrypt, ptls_buffer_t *dst, ptls_iovec_t src);-/**- * saves a ticket (client-only)- */-PTLS_CALLBACK_TYPE(int, save_ticket, ptls_t *tls, ptls_iovec_t input);-/**- * event logging (incl. secret logging)- */-typedef struct st_ptls_log_event_t {- void (*cb)(struct st_ptls_log_event_t *self, ptls_t *tls, const char *type, const char *fmt, ...)- __attribute__((format(printf, 4, 5)));-} ptls_log_event_t;-/**- * reference counting- */-PTLS_CALLBACK_TYPE(void, update_open_count, ssize_t delta);-/**- * applications that have their own record layer can set this function to derive their own traffic keys from the traffic secret.- * The cipher-suite that is being associated to the connection can be obtained by calling the ptls_get_cipher function.- */-PTLS_CALLBACK_TYPE(int, update_traffic_key, ptls_t *tls, int is_enc, size_t epoch, const void *secret);-/**- * callback for every extension detected during decoding- */-PTLS_CALLBACK_TYPE(int, on_extension, ptls_t *tls, uint8_t hstype, uint16_t exttype, ptls_iovec_t extdata);-/**- *- */-typedef struct st_ptls_decompress_certificate_t {- /**- * list of supported algorithms terminated by UINT16_MAX- */- const uint16_t *supported_algorithms;- /**- * callback that decompresses the message- */- int (*cb)(struct st_ptls_decompress_certificate_t *self, ptls_t *tls, uint16_t algorithm, ptls_iovec_t output,- ptls_iovec_t input);-} ptls_decompress_certificate_t;-/**- * ECH: creates the AEAD context to be used for "Open"-ing inner CH. Given `config_id`, the callback looks up the ECH config and the- * corresponding private key, invokes `ptls_hpke_setup_base_r` with provided `cipher`, `enc`, and `info_prefix` (which will be- * "tls ech" || 00).- */-PTLS_CALLBACK_TYPE(ptls_aead_context_t *, ech_create_opener, ptls_hpke_kem_t **kem, ptls_hpke_cipher_suite_t **cipher, ptls_t *tls,- uint8_t config_id, ptls_hpke_cipher_suite_id_t cipher_id, ptls_iovec_t enc, ptls_iovec_t info_prefix);--/**- * the configuration- */-struct st_ptls_context_t {- /**- * PRNG to be used- */- void (*random_bytes)(void *buf, size_t len);- /**- *- */- ptls_get_time_t *get_time;- /**- * list of supported key-exchange algorithms terminated by NULL- */- ptls_key_exchange_algorithm_t **key_exchanges;- /**- * list of supported cipher-suites terminated by NULL- */- ptls_cipher_suite_t **cipher_suites;- /**- * list of certificates- */- struct {- ptls_iovec_t *list;- size_t count;- } certificates;- /**- * ECH- */- struct {- struct {- /**- * list of HPKE symmetric cipher-suites (set to NULL to disable ECH altogether)- */- ptls_hpke_cipher_suite_t **ciphers;- /**- * KEMs being supported- */- ptls_hpke_kem_t **kems;- } client;- struct {- /**- * callback that does ECDH key exchange and returns the AEAD context- */- ptls_ech_create_opener_t *create_opener;- /**- * ECHConfigList to be sent to the client when there is mismatch (or when the client sends a grease)- */- ptls_iovec_t retry_configs;- } server;- } ech;- /**- *- */- ptls_on_client_hello_t *on_client_hello;- /**- *- */- ptls_emit_certificate_t *emit_certificate;- /**- *- */- ptls_sign_certificate_t *sign_certificate;- /**- *- */- ptls_verify_certificate_t *verify_certificate;- /**- * lifetime of a session ticket (server-only)- */- uint32_t ticket_lifetime;- /**- * maximum permitted size of early data (server-only)- */- uint32_t max_early_data_size;- /**- * maximum size of the message buffer (default: 0 = unlimited = 3 + 2^24 bytes)- */- size_t max_buffer_size;- /**- * this field is obsolete and ignored- */- const char *hkdf_label_prefix__obsolete;- /**- * if set, psk handshakes use (ec)dhe- */- unsigned require_dhe_on_psk : 1;- /**- * if exporter master secrets should be recorded- */- unsigned use_exporter : 1;- /**- * if ChangeCipherSpec record should be sent during handshake. If the client sends CCS, the server sends one in response- * regardless of the value of this flag. See RFC 8446 Appendix D.3.- */- unsigned send_change_cipher_spec : 1;- /**- * if set, the server requests client certificates- * to authenticate the client.- */- unsigned require_client_authentication : 1;- /**- * if set, EOED will not be emitted or accepted- */- unsigned omit_end_of_early_data : 1;- /**- * This option turns on support for Raw Public Keys (RFC 7250).- *- * When running as a client, this option instructs the client to request the server to send raw public keys in place of X.509- * certificate chain. The client should set its `certificate_verify` callback to one that is capable of validating the raw- * public key that will be sent by the server.- *- * When running as a server, this option instructs the server to only handle clients requesting the use of raw public keys. If- * the client does not, the handshake is rejected. Note however that the rejection happens only after the `on_client_hello`- * callback is being called. Therefore, applications can support both X.509 and raw public keys by swapping `ptls_context_t` to- * the correct one when that callback is being called (like handling swapping the contexts based on the value of SNI).- */- unsigned use_raw_public_keys : 1;- /**- * boolean indicating if the cipher-suite should be chosen based on server's preference- */- unsigned server_cipher_preference : 1;- /**- * boolean indicating if ChaCha20-Poly1305 should be reprioritized to the top of the server cipher list if a ChaCha20-Poly1305- * cipher is at the top of the client cipher list- */- unsigned server_cipher_chacha_priority : 1;- /**- *- */- ptls_encrypt_ticket_t *encrypt_ticket;- /**- *- */- ptls_save_ticket_t *save_ticket;- /**- *- */- ptls_log_event_t *log_event;- /**- *- */- ptls_update_open_count_t *update_open_count;- /**- *- */- ptls_update_traffic_key_t *update_traffic_key;- /**- *- */- ptls_decompress_certificate_t *decompress_certificate;- /**- *- */- ptls_on_extension_t *on_extension;- /**- * (optional) list of supported tls12 cipher-suites terminated by NULL- */- ptls_cipher_suite_t **tls12_cipher_suites;-};--typedef struct st_ptls_raw_extension_t {- uint16_t type;- ptls_iovec_t data;-} ptls_raw_extension_t;--typedef enum en_ptls_early_data_acceptance_t {- PTLS_EARLY_DATA_ACCEPTANCE_UNKNOWN = 0,- PTLS_EARLY_DATA_REJECTED,- PTLS_EARLY_DATA_ACCEPTED-} ptls_early_data_acceptance_t;--/**- * optional arguments to client-driven handshake- */-#ifdef _WINDOWS-/* suppress warning C4201: nonstandard extension used: nameless struct/union */-#pragma warning(push)-#pragma warning(disable : 4201)-#endif-typedef struct st_ptls_handshake_properties_t {- union {- struct {- /**- * list of protocols offered through ALPN- */- struct {- const ptls_iovec_t *list;- size_t count;- } negotiated_protocols;- /**- * session ticket sent to the application via save_ticket callback- */- ptls_iovec_t session_ticket;- /**- * pointer to store the maximum size of early-data that can be sent immediately. If set to non-NULL, the first call to- * ptls_handshake (or ptls_handle_message) will set `*max_early_data` to the value obtained from the session ticket, or- * to zero if early-data cannot be sent. If NULL, early data will not be used.- */- size_t *max_early_data_size;- /**- * If early-data has been accepted by peer, or if the state is still unknown. The state changes anytime after handshake- * keys become available. Applications can peek the tri-state variable every time it calls `ptls_hanshake` or- * `ptls_handle_message` to determine the result at the earliest moment. This is an output parameter.- */- ptls_early_data_acceptance_t early_data_acceptance;- /**- * negotiate the key exchange method before sending key_share- */- unsigned negotiate_before_key_exchange : 1;- /**- * ECH- */- struct {- /**- * Config offered by server e.g., by HTTPS RR. If config.base is non-NULL but config.len is zero, a grease ECH will- * be sent, assuming that X25519-SHA256 KEM and SHA256-AES-128-GCM HPKE cipher is available.- */- ptls_iovec_t configs;- /**- * slot to save the config obtained from server on mismatch; user must free the returned blob by calling `free`- */- ptls_iovec_t *retry_configs;- } ech;- } client;- struct {- /**- * psk binder being selected (len is set to zero if none)- */- struct {- uint8_t base[PTLS_MAX_DIGEST_SIZE];- size_t len;- } selected_psk_binder;- /**- * parameters related to use of the Cookie extension- */- struct {- /**- * HMAC key to protect the integrity of the cookie. The key should be as long as the digest size of the first- * ciphersuite specified in ptls_context_t (i.e. the hash algorithm of the best ciphersuite that can be chosen).- */- const void *key;- /**- * additional data to be used for verifying the cookie- */- ptls_iovec_t additional_data;- } cookie;- /**- * if HRR should always be sent- */- unsigned enforce_retry : 1;- /**- * if retry should be stateless (cookie.key MUST be set when this option is used)- */- unsigned retry_uses_cookie : 1;- } server;- };- /**- * an optional list of additional extensions to send either in CH or EE, terminated by type == UINT16_MAX- */- ptls_raw_extension_t *additional_extensions;- /**- * an optional callback that returns a boolean value indicating if a particular extension should be collected- */- int (*collect_extension)(ptls_t *tls, struct st_ptls_handshake_properties_t *properties, uint16_t type);- /**- * an optional callback that reports the extensions being collected- */- int (*collected_extensions)(ptls_t *tls, struct st_ptls_handshake_properties_t *properties, ptls_raw_extension_t *extensions);-} ptls_handshake_properties_t;-#ifdef _WINDOWS-#pragma warning(pop)-#endif-#ifdef _WINDOWS-/* suppress warning C4293: >> shift count negative or too big */-#pragma warning(disable : 4293)-#endif-/**- * builds a new ptls_iovec_t instance using the supplied parameters- */-static ptls_iovec_t ptls_iovec_init(const void *p, size_t len);-/**- * initializes a buffer, setting the default destination to the small buffer provided as the argument.- */-static void ptls_buffer_init(ptls_buffer_t *buf, void *smallbuf, size_t smallbuf_size);-/**- * disposes a buffer, freeing resources allocated by the buffer itself (if any)- */-static void ptls_buffer_dispose(ptls_buffer_t *buf);-/**- * internal- */-void ptls_buffer__release_memory(ptls_buffer_t *buf);-/**- * reserves space for additional amount of memory- */-int ptls_buffer_reserve(ptls_buffer_t *buf, size_t delta);-/**- * reserves space for additional amount of memory, requiring `buf->base` to follow specified alignment- */-int ptls_buffer_reserve_aligned(ptls_buffer_t *buf, size_t delta, uint8_t align_bits);-/**- * internal- */-int ptls_buffer__do_pushv(ptls_buffer_t *buf, const void *src, size_t len);-/**- * internal- */-int ptls_buffer__adjust_quic_blocksize(ptls_buffer_t *buf, size_t body_size);-/**- * internal- */-int ptls_buffer__adjust_asn1_blocksize(ptls_buffer_t *buf, size_t body_size);-/**- * pushes an unsigned bigint- */-int ptls_buffer_push_asn1_ubigint(ptls_buffer_t *buf, const void *bignum, size_t size);-/**- * encodes a quic varint (maximum length is PTLS_ENCODE_QUICINT_CAPACITY)- */-static uint8_t *ptls_encode_quicint(uint8_t *p, uint64_t v);-#define PTLS_ENCODE_QUICINT_CAPACITY 8--#define PTLS_QUICINT_MAX 4611686018427387903 // (1 << 62) - 1-#define PTLS_QUICINT_LONGEST_STR "4611686018427387903"--#define ptls_buffer_pushv(buf, src, len) \- do { \- if ((ret = ptls_buffer__do_pushv((buf), (src), (len))) != 0) \- goto Exit; \- } while (0)--#define ptls_buffer_push(buf, ...) \- do { \- if ((ret = ptls_buffer__do_pushv((buf), (uint8_t[]){__VA_ARGS__}, sizeof((uint8_t[]){__VA_ARGS__}))) != 0) \- goto Exit; \- } while (0)--#define ptls_buffer_push16(buf, v) \- do { \- uint16_t _v = (v); \- ptls_buffer_push(buf, (uint8_t)(_v >> 8), (uint8_t)_v); \- } while (0)--#define ptls_buffer_push24(buf, v) \- do { \- uint32_t _v = (v); \- ptls_buffer_push(buf, (uint8_t)(_v >> 16), (uint8_t)(_v >> 8), (uint8_t)_v); \- } while (0)--#define ptls_buffer_push32(buf, v) \- do { \- uint32_t _v = (v); \- ptls_buffer_push(buf, (uint8_t)(_v >> 24), (uint8_t)(_v >> 16), (uint8_t)(_v >> 8), (uint8_t)_v); \- } while (0)--#define ptls_buffer_push64(buf, v) \- do { \- uint64_t _v = (v); \- ptls_buffer_push(buf, (uint8_t)(_v >> 56), (uint8_t)(_v >> 48), (uint8_t)(_v >> 40), (uint8_t)(_v >> 32), \- (uint8_t)(_v >> 24), (uint8_t)(_v >> 16), (uint8_t)(_v >> 8), (uint8_t)_v); \- } while (0)--#define ptls_buffer_push_quicint(buf, v) \- do { \- if ((ret = ptls_buffer_reserve((buf), PTLS_ENCODE_QUICINT_CAPACITY)) != 0) \- goto Exit; \- uint8_t *d = ptls_encode_quicint((buf)->base + (buf)->off, (v)); \- (buf)->off = d - (buf)->base; \- } while (0)--#define ptls_buffer_push_block(buf, _capacity, block) \- do { \- size_t capacity = (_capacity); \- ptls_buffer_pushv((buf), (uint8_t *)"\0\0\0\0\0\0\0", capacity != -1 ? capacity : 1); \- size_t body_start = (buf)->off; \- do { \- block \- } while (0); \- size_t body_size = (buf)->off - body_start; \- if (capacity != -1) { \- for (; capacity != 0; --capacity) \- (buf)->base[body_start - capacity] = (uint8_t)(body_size >> (8 * (capacity - 1))); \- } else { \- if ((ret = ptls_buffer__adjust_quic_blocksize((buf), body_size)) != 0) \- goto Exit; \- } \- } while (0)--#define ptls_buffer_push_asn1_block(buf, block) \- do { \- ptls_buffer_push((buf), 0xff); /* dummy */ \- size_t body_start = (buf)->off; \- do { \- block \- } while (0); \- size_t body_size = (buf)->off - body_start; \- if (body_size < 128) { \- (buf)->base[body_start - 1] = (uint8_t)body_size; \- } else { \- if ((ret = ptls_buffer__adjust_asn1_blocksize((buf), body_size)) != 0) \- goto Exit; \- } \- } while (0)--#define ptls_buffer_push_asn1_sequence(buf, block) \- do { \- ptls_buffer_push((buf), 0x30); \- ptls_buffer_push_asn1_block((buf), block); \- } while (0)--#define ptls_buffer_push_message_body(buf, key_sched, type, block) \- do { \- ptls_buffer_t *_buf = (buf); \- ptls_key_schedule_t *_key_sched = (key_sched); \- size_t mess_start = _buf->off; \- ptls_buffer_push(_buf, (type)); \- ptls_buffer_push_block(_buf, 3, block); \- if (_key_sched != NULL) \- ptls__key_schedule_update_hash(_key_sched, _buf->base + mess_start, _buf->off - mess_start, 0); \- } while (0)--#define ptls_push_message(emitter, key_sched, type, block) \- do { \- ptls_message_emitter_t *_emitter = (emitter); \- if ((ret = _emitter->begin_message(_emitter)) != 0) \- goto Exit; \- ptls_buffer_push_message_body(_emitter->buf, (key_sched), (type), block); \- if ((ret = _emitter->commit_message(_emitter)) != 0) \- goto Exit; \- } while (0)--int ptls_decode8(uint8_t *value, const uint8_t **src, const uint8_t *end);-int ptls_decode16(uint16_t *value, const uint8_t **src, const uint8_t *end);-int ptls_decode24(uint32_t *value, const uint8_t **src, const uint8_t *end);-int ptls_decode32(uint32_t *value, const uint8_t **src, const uint8_t *end);-int ptls_decode64(uint64_t *value, const uint8_t **src, const uint8_t *end);-uint64_t ptls_decode_quicint(const uint8_t **src, const uint8_t *end);--#define ptls_decode_open_block(src, end, capacity, block) \- do { \- size_t _capacity = (capacity); \- size_t _block_size; \- if (_capacity == -1) { \- uint64_t _block_size64; \- const uint8_t *_src = (src); \- if ((_block_size64 = ptls_decode_quicint(&_src, end)) == UINT64_MAX || \- (sizeof(size_t) < 8 && (_block_size64 >> (8 * sizeof(size_t))) != 0)) { \- ret = PTLS_ALERT_DECODE_ERROR; \- goto Exit; \- } \- (src) = _src; \- _block_size = (size_t)_block_size64; \- } else { \- if (_capacity > (size_t)(end - (src))) { \- ret = PTLS_ALERT_DECODE_ERROR; \- goto Exit; \- } \- _block_size = 0; \- do { \- _block_size = _block_size << 8 | *(src)++; \- } while (--_capacity != 0); \- } \- if (_block_size > (size_t)(end - (src))) { \- ret = PTLS_ALERT_DECODE_ERROR; \- goto Exit; \- } \- do { \- const uint8_t *const end = (src) + _block_size; \- do { \- block \- } while (0); \- if ((src) != end) { \- ret = PTLS_ALERT_DECODE_ERROR; \- goto Exit; \- } \- } while (0); \- } while (0)--#define ptls_decode_assert_block_close(src, end) \- do { \- if ((src) != end) { \- ret = PTLS_ALERT_DECODE_ERROR; \- goto Exit; \- } \- } while (0);--#define ptls_decode_block(src, end, capacity, block) \- do { \- ptls_decode_open_block((src), end, capacity, block); \- ptls_decode_assert_block_close((src), end); \- } while (0)--#define PTLS_LOG__DO_LOG(module, type, block) \- do { \- int ptlslog_skip = 0; \- char smallbuf[128]; \- ptls_buffer_t ptlslogbuf; \- ptls_buffer_init(&ptlslogbuf, smallbuf, sizeof(smallbuf)); \- PTLS_LOG__DO_PUSH_SAFESTR("{\"module\":\"" PTLS_TO_STR(module) "\",\"type\":\"" PTLS_TO_STR(type) "\""); \- do { \- block \- } while (0); \- PTLS_LOG__DO_PUSH_SAFESTR("}\n"); \- if (!ptlslog_skip) \- ptls_log__do_write(&ptlslogbuf); \- ptls_buffer_dispose(&ptlslogbuf); \- } while (0)--#define PTLS_LOG(module, type, block) \- do { \- if (!ptls_log.is_active) \- break; \- PTLS_LOG__DO_LOG((module), (type), (block)); \- } while (0)--#define PTLS_LOG_CONN(type, tls, block) \- do { \- ptls_t *_tls = (tls); \- if (!ptls_log.is_active || ptls_skip_tracing(_tls)) \- break; \- PTLS_LOG__DO_LOG(picotls, type, { \- PTLS_LOG_ELEMENT_PTR(tls, _tls); \- do { \- block \- } while (0); \- }); \- } while (0)--#define PTLS_LOG_ELEMENT_SAFESTR(name, value) \- do { \- PTLS_LOG__DO_PUSH_SAFESTR(",\"" PTLS_TO_STR(name) "\":\""); \- PTLS_LOG__DO_PUSH_SAFESTR(value); \- PTLS_LOG__DO_PUSH_SAFESTR("\""); \- } while (0)-#define PTLS_LOG_ELEMENT_UNSAFESTR(name, value, value_len) \- do { \- PTLS_LOG__DO_PUSH_SAFESTR(",\"" PTLS_TO_STR(name) "\":\""); \- PTLS_LOG__DO_PUSH_UNSAFESTR(value, value_len); \- PTLS_LOG__DO_PUSH_SAFESTR("\""); \- } while (0)-#define PTLS_LOG_ELEMENT_HEXDUMP(name, value, value_len) \- do { \- PTLS_LOG__DO_PUSH_SAFESTR(",\"" PTLS_TO_STR(name) "\":\""); \- PTLS_LOG__DO_PUSH_HEXDUMP(value, value_len); \- PTLS_LOG__DO_PUSH_SAFESTR("\""); \- } while (0)-#define PTLS_LOG_ELEMENT_PTR(name, value) PTLS_LOG_ELEMENT_UNSIGNED(name, (uint64_t)(value))-#define PTLS_LOG_ELEMENT_SIGNED(name, value) \- do { \- PTLS_LOG__DO_PUSH_SAFESTR(",\"" PTLS_TO_STR(name) "\":"); \- PTLS_LOG__DO_PUSH_SIGNED(value); \- } while (0)-#define PTLS_LOG_ELEMENT__DO_UNSIGNED(name, suffix, value) \- do { \- PTLS_LOG__DO_PUSH_SAFESTR(",\"" PTLS_TO_STR(name) suffix "\":"); \- PTLS_LOG__DO_PUSH_UNSIGNED(value); \- } while (0)-#define PTLS_LOG_ELEMENT_UNSIGNED(name, value) PTLS_LOG_ELEMENT__DO_UNSIGNED(name, "", value)-#define PTLS_LOG_ELEMENT_BOOL(name, value) \- do { \- PTLS_LOG__DO_PUSH_SAFESTR(",\"" PTLS_TO_STR(name) "\":"); \- PTLS_LOG__DO_PUSH_SAFESTR(value ? "true" : "false"); \- } while (0)--#define PTLS_LOG_APPDATA_ELEMENT_UNSAFESTR(name, value, value_len) \- do { \- size_t _len = (value_len); \- if (ptls_log.include_appdata) \- PTLS_LOG_ELEMENT_UNSAFESTR(name, value, _len); \- PTLS_LOG_ELEMENT__DO_UNSIGNED(name, "_len", _len); \- } while (0)-#define PTLS_LOG_APPDATA_ELEMENT_HEXDUMP(name, value, value_len) \- do { \- size_t _len = (value_len); \- if (ptls_log.include_appdata) \- PTLS_LOG_ELEMENT_HEXDUMP(name, value, _len); \- PTLS_LOG_ELEMENT__DO_UNSIGNED(name, "_len", _len); \- } while (0)--#define PTLS_LOG__DO_PUSH_SAFESTR(v) \- do { \- if (PTLS_UNLIKELY(!ptlslog_skip && !ptls_log__do_push_safestr(&ptlslogbuf, (v)))) \- ptlslog_skip = 1; \- } while (0)-#define PTLS_LOG__DO_PUSH_UNSAFESTR(v, l) \- do { \- if (PTLS_UNLIKELY(!ptlslog_skip && !ptls_log__do_push_unsafestr(&ptlslogbuf, (v), (l)))) \- ptlslog_skip = 1; \- } while (0)-#define PTLS_LOG__DO_PUSH_HEXDUMP(v, l) \- do { \- if (PTLS_UNLIKELY(!ptlslog_skip && !ptls_log__do_push_hexdump(&ptlslogbuf, (v), (l)))) \- ptlslog_skip = 1; \- } while (0)-#define PTLS_LOG__DO_PUSH_SIGNED(v) \- do { \- if (PTLS_UNLIKELY(!ptlslog_skip)) { \- if (sizeof(v) <= sizeof(int32_t)) { \- if (PTLS_UNLIKELY(!ptls_log__do_push_signed32(&ptlslogbuf, (v)))) \- ptlslog_skip = 1; \- } else { \- if (PTLS_UNLIKELY(!ptls_log__do_push_signed64(&ptlslogbuf, (v)))) \- ptlslog_skip = 1; \- } \- } \- } while (0)-#define PTLS_LOG__DO_PUSH_UNSIGNED(v) \- do { \- if (PTLS_UNLIKELY(!ptlslog_skip)) { \- if (sizeof(v) <= sizeof(uint32_t)) { \- if (PTLS_UNLIKELY(!ptls_log__do_push_unsigned32(&ptlslogbuf, (uint32_t)(v)))) \- ptlslog_skip = 1; \- } else { \- if (PTLS_UNLIKELY(!ptls_log__do_push_unsigned64(&ptlslogbuf, (v)))) \- ptlslog_skip = 1; \- } \- } \- } while (0)--/**- * User API is exposed only when logging is supported by the platform.- */-typedef struct st_ptls_log_t {- unsigned is_active : 1;- unsigned include_appdata : 1;-} ptls_log_t;--#if PTLS_HAVE_LOG-extern volatile ptls_log_t ptls_log;-/**- * Returns the number of log events that were unable to be emitted.- */-size_t ptls_log_num_lost(void);-/**- * Registers an fd to the logger. A registered fd is automatically closed and removed if it is invalidated.- */-int ptls_log_add_fd(int fd);-#else-static const ptls_log_t ptls_log = {0};-#endif--static int ptls_log__do_push_safestr(ptls_buffer_t *buf, const char *s);-int ptls_log__do_push_unsafestr(ptls_buffer_t *buf, const char *s, size_t l);-int ptls_log__do_push_hexdump(ptls_buffer_t *buf, const void *s, size_t l);-int ptls_log__do_pushv(ptls_buffer_t *buf, const void *p, size_t l);-int ptls_log__do_push_signed32(ptls_buffer_t *buf, int32_t v);-int ptls_log__do_push_signed64(ptls_buffer_t *buf, int64_t v);-int ptls_log__do_push_unsigned32(ptls_buffer_t *buf, uint32_t v);-int ptls_log__do_push_unsigned64(ptls_buffer_t *buf, uint64_t v);-void ptls_log__do_write(const ptls_buffer_t *buf);--/**- * create a client object to handle new TLS connection- */-ptls_t *ptls_client_new(ptls_context_t *ctx);-/**- * create a server object to handle new TLS connection- */-ptls_t *ptls_server_new(ptls_context_t *ctx);-/**- * creates an object handle new TLS connection- */-static ptls_t *ptls_new(ptls_context_t *ctx, int is_server);-/**- * creates TLS 1.2 record layer for post-handshake communication- */-int ptls_build_tls12_export_params(ptls_context_t *ctx, ptls_buffer_t *output, int is_server, int session_reused,- ptls_cipher_suite_t *cipher, const void *master_secret, const void *hello_randoms,- uint64_t next_send_record_iv, const char *server_name, ptls_iovec_t negotiated_protocol);-/**- * create a post-handshake TLS connection object using given parameters- */-int ptls_import(ptls_context_t *ctx, ptls_t **tls, ptls_iovec_t params);-/**- * releases all resources associated to the object- */-void ptls_free(ptls_t *tls);-/**- * returns address of the crypto callbacks that the connection is using- */-ptls_context_t *ptls_get_context(ptls_t *tls);-/**- * updates the context of a connection. Can be called from `on_client_hello` callback.- */-void ptls_set_context(ptls_t *tls, ptls_context_t *ctx);-/**- * get the signature context- */-ptls_async_job_t *ptls_get_async_job(ptls_t *tls);-/**- * returns the client-random- */-ptls_iovec_t ptls_get_client_random(ptls_t *tls);-/**- * returns the cipher-suite being used- */-ptls_cipher_suite_t *ptls_get_cipher(ptls_t *tls);-/**- * returns a supported cipher-suite given an id- */-ptls_cipher_suite_t *ptls_find_cipher_suite(ptls_cipher_suite_t **cipher_suites, uint16_t id);-/**- * Returns protocol version (e.g., 0x0303 for TLS 1.2, 0x0304 for TLS 1.3). The result may be unstable prior to handshake- * completion.- */-uint16_t ptls_get_protocol_version(ptls_t *tls);-/**- * Returns current state of traffic keys. The cipher-suite being used, as well as the length of the traffic keys, can be obtained- * via `ptls_get_cipher`.- * TODO: Even in case of offloading just the TX side, there should be API for handling key updates, sending Close aleart.- */-int ptls_get_traffic_keys(ptls_t *tls, int is_enc, uint8_t *key, uint8_t *iv, uint64_t *seq);-/**- * returns the server-name (NULL if SNI is not used or failed to negotiate)- */-const char *ptls_get_server_name(ptls_t *tls);-/**- * sets the server-name associated to the TLS connection. If server_name_len is zero, then strlen(server_name) is called to- * determine the length of the name.- * On the client-side, the value is used for certificate validation. The value will be also sent as an SNI extension, if it looks- * like a DNS name.- * On the server-side, it can be called from on_client_hello to indicate the acceptance of the SNI extension to the client.- */-int ptls_set_server_name(ptls_t *tls, const char *server_name, size_t server_name_len);-/**- * returns the negotiated protocol (or NULL)- */-const char *ptls_get_negotiated_protocol(ptls_t *tls);-/**- * sets the negotiated protocol. If protocol_len is zero, strlen(protocol) is called to determine the length of the protocol name.- */-int ptls_set_negotiated_protocol(ptls_t *tls, const char *protocol, size_t protocol_len);-/**- * returns if the handshake has been completed- */-int ptls_handshake_is_complete(ptls_t *tls);-/**- * returns if a PSK (or PSK-DHE) handshake was performed- */-int ptls_is_psk_handshake(ptls_t *tls);-/**- * return if a ECH handshake was performed, as well as optionally the kem and cipher-suite being used- */-int ptls_is_ech_handshake(ptls_t *tls, uint8_t *config_id, ptls_hpke_kem_t **kem, ptls_hpke_cipher_suite_t **cipher);-/**- * returns a pointer to user data pointer (client is reponsible for freeing the associated data prior to calling ptls_free)- */-void **ptls_get_data_ptr(ptls_t *tls);-/**- *- */-int ptls_skip_tracing(ptls_t *tls);-/**- *- */-void ptls_set_skip_tracing(ptls_t *tls, int skip_tracing);-/**- * proceeds with the handshake, optionally taking some input from peer. The function returns zero in case the handshake completed- * successfully. PTLS_ERROR_IN_PROGRESS is returned in case the handshake is incomplete. Otherwise, an error value is returned. The- * contents of sendbuf should be sent to the client, regardless of whether if an error is returned. inlen is an argument used for- * both input and output. As an input, the arguments takes the size of the data available as input. Upon return the value is updated- * to the number of bytes consumed by the handshake. In case the returned value is PTLS_ERROR_IN_PROGRESS there is a guarantee that- * all the input are consumed (i.e. the value of inlen does not change).- */-int ptls_handshake(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t *inlen, ptls_handshake_properties_t *args);-/**- * decrypts the first record within given buffer- */-int ptls_receive(ptls_t *tls, ptls_buffer_t *plaintextbuf, const void *input, size_t *len);-/**- * encrypts given buffer into multiple TLS records- */-int ptls_send(ptls_t *tls, ptls_buffer_t *sendbuf, const void *input, size_t inlen);-/**- * updates the send traffic key (as well as asks the peer to update)- */-int ptls_update_key(ptls_t *tls, int request_update);-/**- * Returns if the context is a server context.- */-int ptls_is_server(ptls_t *tls);-/**- * returns per-record overhead- */-size_t ptls_get_record_overhead(ptls_t *tls);-/**- * sends an alert- */-int ptls_send_alert(ptls_t *tls, ptls_buffer_t *sendbuf, uint8_t level, uint8_t description);-/**- *- */-int ptls_export_secret(ptls_t *tls, void *output, size_t outlen, const char *label, ptls_iovec_t context_value, int is_early);-/**- * build the body of a Certificate message. Can be called with tls set to NULL in order to create a precompressed message.- */-int ptls_build_certificate_message(ptls_buffer_t *buf, ptls_iovec_t request_context, ptls_iovec_t *certificates,- size_t num_certificates, ptls_iovec_t ocsp_status);-/**- *- */-int ptls_calc_hash(ptls_hash_algorithm_t *algo, void *output, const void *src, size_t len);-/**- *- */-ptls_hash_context_t *ptls_hmac_create(ptls_hash_algorithm_t *algo, const void *key, size_t key_size);-/**- *- */-int ptls_hkdf_extract(ptls_hash_algorithm_t *hash, void *output, ptls_iovec_t salt, ptls_iovec_t ikm);-/**- *- */-int ptls_hkdf_expand(ptls_hash_algorithm_t *hash, void *output, size_t outlen, ptls_iovec_t prk, ptls_iovec_t info);-/**- *- */-int ptls_hkdf_expand_label(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label,- ptls_iovec_t hash_value, const char *label_prefix);-/**- * The expansion function of TLS 1.2 defined in RFC 5426 section 5. When `label` is NULL, acts as P_<hash>, or if non-NULL, as PRF.- */-int ptls_tls12_phash(ptls_hash_algorithm_t *algo, void *output, size_t outlen, ptls_iovec_t secret, const char *label,- ptls_iovec_t seed);-/**- * instantiates a symmetric cipher- */-ptls_cipher_context_t *ptls_cipher_new(ptls_cipher_algorithm_t *algo, int is_enc, const void *key);-/**- * destroys a symmetric cipher- */-void ptls_cipher_free(ptls_cipher_context_t *ctx);-/**- * initializes the IV; this function must be called prior to calling ptls_cipher_encrypt- */-static void ptls_cipher_init(ptls_cipher_context_t *ctx, const void *iv);-/**- * Encrypts given text. The function must be used in a way that the output length would be equal to the input length. For example,- * when using a block cipher in ECB mode, `len` must be a multiple of the block size when using a block cipher. The length can be- * of any value when using a stream cipher or a block cipher in CTR mode.- */-static void ptls_cipher_encrypt(ptls_cipher_context_t *ctx, void *output, const void *input, size_t len);-/**- * instantiates an AEAD cipher given a secret, which is expanded using hkdf to a set of key and iv- * @param aead- * @param hash- * @param is_enc 1 if creating a context for encryption, 0 if creating a context for decryption- * @param secret the secret. The size must be the digest length of the hash algorithm- * @return pointer to an AEAD context if successful, otherwise NULL- */-ptls_aead_context_t *ptls_aead_new(ptls_aead_algorithm_t *aead, ptls_hash_algorithm_t *hash, int is_enc, const void *secret,- const char *label_prefix);-/**- * instantiates an AEAD cipher given key and iv- * @param aead- * @param is_enc 1 if creating a context for encryption, 0 if creating a context for decryption- * @return pointer to an AEAD context if successful, otherwise NULL- */-ptls_aead_context_t *ptls_aead_new_direct(ptls_aead_algorithm_t *aead, int is_enc, const void *key, const void *iv);-/**- * destroys an AEAD cipher context- */-void ptls_aead_free(ptls_aead_context_t *ctx);-/**- * Permutes the static IV by applying given bytes using bit-wise XOR. This API can be used for supplying nonces longer than 64-- * bits.- */-void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len);-static void ptls_aead_get_iv(ptls_aead_context_t *ctx, void *iv);-static void ptls_aead_set_iv(ptls_aead_context_t *ctx, const void *iv);-/**- * Encrypts one AEAD block, given input and output vectors.- */-static size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen);-/**- * Encrypts one AEAD block, as well as one block of ECB (for QUIC / DTLS packet number encryption). Depending on the AEAD engine- * being used, the two operations might run simultaneously.- */-static void ptls_aead_encrypt_s(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp);-/**- * Encrypts one AEAD block, given a vector of vectors.- */-static void ptls_aead_encrypt_v(ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,- const void *aad, size_t aadlen);-/**- * Obsolete; new applications should use one of: `ptls_aead_encrypt`, `ptls_aead_encrypt_s`, `ptls_aead_encrypt_v`.- */-static void ptls_aead_encrypt_init(ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen);-/**- * Obsolete; see `ptls_aead_encrypt_init`.- */-static size_t ptls_aead_encrypt_update(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen);-/**- * Obsolete; see `ptls_aead_encrypt_init`.- */-static size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output);-/**- * decrypts an AEAD record- * @return number of bytes emitted to output if successful, or SIZE_MAX if the input is invalid (e.g. broken MAC)- */-static size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen);-/**- * Return the current read epoch (i.e., that of the message being received or to be)- */-size_t ptls_get_read_epoch(ptls_t *tls);-/**- * Runs the handshake by dealing directly with handshake messages. Callers MUST delay supplying input to this function until the- * epoch of the input becomes equal to the value returned by `ptls_get_read_epoch()`.- * @param tls the TLS context- * @param sendbuf buffer to which the output will be written- * @param epoch_offsets start and end offset of the messages in each epoch. For example, when the server emits ServerHello between- * offset 0 and 38, the following handshake messages between offset 39 and 348, and a post-handshake message- * between 349 and 451, epoch_offsets will be {0,39,39,349,452} and the length of the sendbuf will be 452.- * This argument is an I/O argument. Applications can either reset sendbuf to empty and epoch_offsets and to- * all zero every time they invoke the function, or retain the values until the handshake completes so that- * data will be appended to sendbuf and epoch_offsets will be adjusted.- * @param in_epoch epoch of the input- * @param input input bytes (must be NULL when starting the handshake on the client side)- * @param inlen length of the input- * @param properties properties specific to the running handshake- * @return same as `ptls_handshake`- */-int ptls_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input,- size_t inlen, ptls_handshake_properties_t *properties);-int ptls_client_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input,- size_t inlen, ptls_handshake_properties_t *properties);-int ptls_server_handle_message(ptls_t *tls, ptls_buffer_t *sendbuf, size_t epoch_offsets[5], size_t in_epoch, const void *input,- size_t inlen, ptls_handshake_properties_t *properties);-/**- * internal- */-void ptls_aead__build_iv(ptls_aead_algorithm_t *algo, uint8_t *iv, const uint8_t *static_iv, uint64_t seq);-/**- *- */-static void ptls_aead__do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp);-/**- *- */-static void ptls_aead__do_encrypt_v(ptls_aead_context_t *ctx, void *_output, ptls_iovec_t *input, size_t incnt, uint64_t seq,- const void *aad, size_t aadlen);-/**- * internal- */-void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen, int use_outer);-/**- * clears memory- */-extern void (*volatile ptls_clear_memory)(void *p, size_t len);-/**- * constant-time memcmp- */-extern int (*volatile ptls_mem_equal)(const void *x, const void *y, size_t len);-/**- * checks if a server name is an IP address.- */-int ptls_server_name_is_ipaddr(const char *name);-/**- * encodes one ECH Config- */-int ptls_ech_encode_config(ptls_buffer_t *buf, uint8_t config_id, ptls_hpke_kem_t *kem, ptls_iovec_t public_key,- ptls_hpke_cipher_suite_t **ciphers, uint8_t max_name_length, const char *public_name);-/**- * loads a certificate chain to ptls_context_t::certificates. `certificate.list` and each element of the list is allocated by- * malloc. It is the responsibility of the user to free them when discarding the TLS context.- */-int ptls_load_certificates(ptls_context_t *ctx, char const *cert_pem_file);-/**- * SetupBaseS function of RFC 9180. Given `kem`, `algo`, `info`, and receiver's public key, returns an ephemeral public key and an- * AEAD context used for encrypting data.- */-int ptls_hpke_setup_base_s(ptls_hpke_kem_t *kem, ptls_hpke_cipher_suite_t *cipher, ptls_iovec_t *pk_s, ptls_aead_context_t **ctx,- ptls_iovec_t pk_r, ptls_iovec_t info);-/**- * SetupBaseR function of RFC 9180. Given `kem`, `algo`, `info`, receiver's private key (`keyex`), and the esnder's public key,- * returns the AEAD context to be used for decrypting data.- */-int ptls_hpke_setup_base_r(ptls_hpke_kem_t *kem, ptls_hpke_cipher_suite_t *cipher, ptls_key_exchange_context_t *keyex,- ptls_aead_context_t **ctx, ptls_iovec_t pk_s, ptls_iovec_t info);-/**- *- */-char *ptls_hexdump(char *dst, const void *src, size_t len);-/**- * Builds a JSON-safe string without double quotes. Supplied buffer MUST be at least 6x + 1 bytes larger than the input.- */-char *ptls_jsonescape(char *buf, const char *s, size_t len);-/**- * the default get_time callback- */-extern ptls_get_time_t ptls_get_time;-#if defined(PICOTLS_USE_DTRACE) && PICOTLS_USE_DTRACE-/**- *- */-extern PTLS_THREADLOCAL unsigned ptls_default_skip_tracing;-#else-#define ptls_default_skip_tracing 0-#endif--/* inline functions */--inline int ptls_log__do_push_safestr(ptls_buffer_t *buf, const char *s)-{- return ptls_log__do_pushv(buf, s, strlen(s));-}--inline ptls_t *ptls_new(ptls_context_t *ctx, int is_server)-{- return is_server ? ptls_server_new(ctx) : ptls_client_new(ctx);-}--inline ptls_iovec_t ptls_iovec_init(const void *p, size_t len)-{- /* avoid the "return (ptls_iovec_t){(uint8_t *)p, len};" construct because it requires C99- * and triggers a warning "C4204: nonstandard extension used: non-constant aggregate initializer"- * in Visual Studio */- ptls_iovec_t r;- r.base = (uint8_t *)p;- r.len = len;- return r;-}--inline void ptls_buffer_init(ptls_buffer_t *buf, void *smallbuf, size_t smallbuf_size)-{- assert(smallbuf != NULL);- buf->base = (uint8_t *)smallbuf;- buf->off = 0;- buf->capacity = smallbuf_size;- buf->is_allocated = 0;- buf->align_bits = 0;-}--inline void ptls_buffer_dispose(ptls_buffer_t *buf)-{- ptls_buffer__release_memory(buf);- *buf = (ptls_buffer_t){NULL, 0, 0, 0, 0};-}--inline uint8_t *ptls_encode_quicint(uint8_t *p, uint64_t v)-{- if (PTLS_UNLIKELY(v > 63)) {- if (PTLS_UNLIKELY(v > 16383)) {- unsigned sb;- if (PTLS_UNLIKELY(v > 1073741823)) {- assert(v <= 4611686018427387903);- *p++ = 0xc0 | (uint8_t)(v >> 56);- sb = 6 * 8;- } else {- *p++ = 0x80 | (uint8_t)(v >> 24);- sb = 2 * 8;- }- do {- *p++ = (uint8_t)(v >> sb);- } while ((sb -= 8) != 0);- } else {- *p++ = 0x40 | (uint8_t)((uint16_t)v >> 8);- }- }- *p++ = (uint8_t)v;- return p;-}--inline void ptls_cipher_init(ptls_cipher_context_t *ctx, const void *iv)-{- ctx->do_init(ctx, iv);-}--inline void ptls_cipher_encrypt(ptls_cipher_context_t *ctx, void *output, const void *input, size_t len)-{- ctx->do_transform(ctx, output, input, len);-}--inline void ptls_aead_get_iv(ptls_aead_context_t *ctx, void *iv)-{- ctx->do_get_iv(ctx, iv);-}--inline void ptls_aead_set_iv(ptls_aead_context_t *ctx, const void *iv)-{- ctx->do_set_iv(ctx, iv);-}--inline size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen)-{- ctx->do_encrypt(ctx, output, input, inlen, seq, aad, aadlen, NULL);- return inlen + ctx->algo->tag_size;-}--inline void ptls_aead_encrypt_s(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)-{- ctx->do_encrypt(ctx, output, input, inlen, seq, aad, aadlen, supp);-}--inline void ptls_aead_encrypt_v(ptls_aead_context_t *ctx, void *output, ptls_iovec_t *input, size_t incnt, uint64_t seq,- const void *aad, size_t aadlen)-{- ctx->do_encrypt_v(ctx, output, input, incnt, seq, aad, aadlen);-}--inline void ptls_aead_encrypt_init(ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen)-{- ctx->do_encrypt_init(ctx, seq, aad, aadlen);-}--inline size_t ptls_aead_encrypt_update(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen)-{- return ctx->do_encrypt_update(ctx, output, input, inlen);-}--inline size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output)-{- return ctx->do_encrypt_final(ctx, output);-}--inline void ptls_aead__do_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp)-{- ptls_iovec_t invec = ptls_iovec_init(input, inlen);- ctx->do_encrypt_v(ctx, output, &invec, 1, seq, aad, aadlen);-- if (supp != NULL) {- ptls_cipher_init(supp->ctx, supp->input);- memset(supp->output, 0, sizeof(supp->output));- ptls_cipher_encrypt(supp->ctx, supp->output, supp->output, sizeof(supp->output));- }-}--inline void ptls_aead__do_encrypt_v(ptls_aead_context_t *ctx, void *_output, ptls_iovec_t *input, size_t incnt, uint64_t seq,- const void *aad, size_t aadlen)-{- uint8_t *output = (uint8_t *)_output;-- ctx->do_encrypt_init(ctx, seq, aad, aadlen);- for (size_t i = 0; i < incnt; ++i)- output += ctx->do_encrypt_update(ctx, output, input[i].base, input[i].len);- ctx->do_encrypt_final(ctx, output);-}--inline size_t ptls_aead_decrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,- const void *aad, size_t aadlen)-{- return ctx->do_decrypt(ctx, output, input, inlen, seq, aad, aadlen);-}--#define ptls_define_hash(name, ctx_type, init_func, update_func, final_func) \- \- struct name##_context_t { \- ptls_hash_context_t super; \- ctx_type ctx; \- }; \- \- static void name##_update(ptls_hash_context_t *_ctx, const void *src, size_t len) \- { \- struct name##_context_t *ctx = (struct name##_context_t *)_ctx; \- update_func(&ctx->ctx, src, len); \- } \- \- static void name##_final(ptls_hash_context_t *_ctx, void *md, ptls_hash_final_mode_t mode) \- { \- struct name##_context_t *ctx = (struct name##_context_t *)_ctx; \- if (mode == PTLS_HASH_FINAL_MODE_SNAPSHOT) { \- ctx_type copy = ctx->ctx; \- final_func(©, md); \- ptls_clear_memory(©, sizeof(copy)); \- return; \- } \- if (md != NULL) \- final_func(&ctx->ctx, md); \- switch (mode) { \- case PTLS_HASH_FINAL_MODE_FREE: \- ptls_clear_memory(&ctx->ctx, sizeof(ctx->ctx)); \- free(ctx); \- break; \- case PTLS_HASH_FINAL_MODE_RESET: \- init_func(&ctx->ctx); \- break; \- default: \- assert(!"FIXME"); \- break; \- } \- } \- \- static ptls_hash_context_t *name##_clone(ptls_hash_context_t *_src) \- { \- struct name##_context_t *dst, *src = (struct name##_context_t *)_src; \- if ((dst = malloc(sizeof(*dst))) == NULL) \- return NULL; \- *dst = *src; \- return &dst->super; \- } \- \- static ptls_hash_context_t *name##_create(void) \- { \- struct name##_context_t *ctx; \- if ((ctx = malloc(sizeof(*ctx))) == NULL) \- return NULL; \- ctx->super = (ptls_hash_context_t){name##_update, name##_final, name##_clone}; \- init_func(&ctx->ctx); \- return &ctx->super; \- }--#ifdef __cplusplus-}-#endif--#endif
− cbits/picotls/fusion.h
@@ -1,115 +0,0 @@-/*- * Copyright (c) 2020 Fastly, Kazuho Oku- *- * Permission is hereby granted, free of charge, to any person obtaining a copy- * of this software and associated documentation files (the "Software"), to- * deal in the Software without restriction, including without limitation the- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or- * sell copies of the Software, and to permit persons to whom the Software is- * furnished to do so, subject to the following conditions:- *- * The above copyright notice and this permission notice shall be included in- * all copies or substantial portions of the Software.- *- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS- * IN THE SOFTWARE.- */-#ifndef picotls_fusion_h-#define picotls_fusion_h--#ifdef __cplusplus-extern "C" {-#endif--#include <stddef.h>-#include <emmintrin.h>-#include <immintrin.h>-#include "../picotls.h"--#define PTLS_FUSION_AES128_ROUNDS 10-#define PTLS_FUSION_AES256_ROUNDS 14--#ifndef PTLS_X86_CACHE_LINE_ALIGN_BITS-#define PTLS_X86_CACHE_LINE_ALIGN_BITS 6 /* 64-bytes */-#endif--typedef struct ptls_fusion_aesecb_context {- union {- __m128i m128[PTLS_FUSION_AES256_ROUNDS + 1];- __m256i m256[PTLS_FUSION_AES256_ROUNDS + 1];- } keys;- unsigned rounds;- uint8_t aesni256;-} __attribute__((aligned(32))) ptls_fusion_aesecb_context_t;--typedef struct ptls_fusion_aesgcm_context ptls_fusion_aesgcm_context_t;--void ptls_fusion_aesecb_init(ptls_fusion_aesecb_context_t *ctx, int is_enc, const void *key, size_t key_size, int avx256);-void ptls_fusion_aesecb_dispose(ptls_fusion_aesecb_context_t *ctx);-void ptls_fusion_aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, void *dst, const void *src);--/**- * Creates an AES-GCM context.- * @param key the AES key (128 bits)- * @param capacity maximum size of AEAD record (i.e. AAD + encrypted payload)- */-ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_new(const void *key, size_t key_size, size_t capacity);-/**- * Updates the capacity.- */-ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_set_capacity(ptls_fusion_aesgcm_context_t *ctx, size_t capacity);-/**- * Destroys an AES-GCM context.- */-void ptls_fusion_aesgcm_free(ptls_fusion_aesgcm_context_t *ctx);-/**- * Encrypts an AEAD block, and in parallel, optionally encrypts one block using AES-ECB.- * @param ctx context- * @param output output buffer- * @param input payload to be encrypted- * @param inlen size of the payload to be encrypted- * @param counter- * @param aad AAD- * @param aadlen size of AAD- * @param supp (optional) supplementary encryption context- */-void ptls_fusion_aesgcm_encrypt(ptls_fusion_aesgcm_context_t *ctx, void *output, const void *input, size_t inlen, __m128i ctr,- const void *aad, size_t aadlen, ptls_aead_supplementary_encryption_t *supp);-/**- * Decrypts an AEAD block, an in parallel, optionally encrypts one block using AES-ECB. Returns if decryption was successful.- * @param iv initialization vector of 12 bytes- * @param output output buffer- * @param input payload to be decrypted- * @param inlen size of the payload to be decrypted- * @param aad AAD- * @param aadlen size of AAD- * @param tag the AEAD tag being received from peer- */-int ptls_fusion_aesgcm_decrypt(ptls_fusion_aesgcm_context_t *ctx, void *output, const void *input, size_t inlen, __m128i ctr,- const void *aad, size_t aadlen, const void *tag);--/**- * A boolean flag indicating if vaes and vpclmulqdq (256-bit crypto instructions) should be used. This flag is set automatically- * when `ptls_fusion_is_supported_by_cpu` is called. Users can update the flag to enforce behavior. Engines that do not have support- * for these 256-bit instructions will continue using the 128-bit ones, even when this flag is set.- */-extern int ptls_fusion_can_aesni256;-extern ptls_cipher_algorithm_t ptls_fusion_aes128ctr, ptls_fusion_aes256ctr;-extern ptls_aead_algorithm_t ptls_fusion_aes128gcm, ptls_fusion_aes256gcm;-extern ptls_aead_algorithm_t ptls_non_temporal_aes128gcm, ptls_non_temporal_aes256gcm;--/**- * Returns a boolean indicating if fusion can be used.- */-int ptls_fusion_is_supported_by_cpu(void);--#ifdef __cplusplus-}-#endif--#endif
quic.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.0 name: quic-version: 0.3.5+version: 0.3.6 license: BSD3 license-file: LICENSE maintainer: kazu@iij.ad.jp@@ -13,8 +13,6 @@ build-type: Simple extra-source-files: ChangeLog.md- cbits/*.h- cbits/picotls/*.h test/servercert.pem test/serverkey.pem @@ -26,10 +24,6 @@ description: Development commands default: False -flag fusion- description: Use fusion AES-GCM engine from picotls- manual: True- library exposed-modules: Network.QUIC@@ -58,7 +52,6 @@ Network.QUIC.Connection.Types Network.QUIC.Connector Network.QUIC.Crypto- Network.QUIC.Crypto.Fusion Network.QUIC.Crypto.Keys Network.QUIC.Crypto.Nite Network.QUIC.Crypto.Types@@ -135,7 +128,7 @@ bytestring >=0.10, containers, crypto-token >=0.2.0 && <0.3,- crypton >=2.0.1 && < 2.1,+ crypton >=2.1.0 && <2.2, crypton-x509 >=1.9.0 && <1.10, crypton-x509-store >=1.9.0 && <1.10, crypton-x509-system >=1.9.0 && <1.10,@@ -162,13 +155,6 @@ if os(windows) cc-options: -D_WINDOWS - if (flag(fusion) && arch(x86_64))- cpp-options: -DUSE_FUSION- cc-options: -mavx2 -maes -mpclmul -mvaes -mvpclmulqdq- c-sources:- cbits/fusion.c- cbits/picotls.c- executable quic-server main-is: quic-server.hs hs-source-dirs: util@@ -274,10 +260,3 @@ if os(windows) ghc-options: -with-rtsopts=--io-manager=native-- if (flag(fusion) && arch(x86_64))- cpp-options: -DUSE_FUSION- cc-options: -mavx2 -maes -mpclmul -mvaes -mvpclmulqdq- c-sources:- cbits/fusion.c- cbits/picotls.c
test/Config.hs view
@@ -8,6 +8,7 @@ setServerQlog, setClientQlog, withPipe,+ withPipeStray, Scenario (..), newSessionManager, ) where@@ -17,6 +18,7 @@ import Control.Monad import Data.ByteString (ByteString) import qualified Data.ByteString as BS+import Data.Bits ((.&.)) import Data.IORef import qualified Data.List as L import qualified Data.List.NonEmpty as NE@@ -56,11 +58,12 @@ either error id <$> credentialLoadX509 "test/servercert.pem" "test/serverkey.pem" let credentials = Credentials [cred]- return- testServerConfigR- { scCredentials = credentials- , scALPN = Just chooseALPN- }+ return $+ setServerQlog+ testServerConfigR+ { scCredentials = credentials+ , scALPN = Just chooseALPN+ } testServerConfigR :: ServerConfig testServerConfigR =@@ -99,11 +102,20 @@ } } +-- | Write qlog for the connections that go through 'withPipe'.+--+-- These are the tests that lose packets on purpose, and so the ones that+-- stall. A stall costs the idle timeout and reports a test name and+-- \"ConnectionIsTimeout\", which says nothing about why; the qlog says which+-- packets went where, what the congestion window was doing and when a timer+-- fired. Two stalls found at a rate of one run in a few hundred were read+-- straight off these traces, and neither would have been diagnosable+-- without them. CI keeps the directory when a job fails. setServerQlog :: ServerConfig -> ServerConfig-setServerQlog sc = sc+setServerQlog sc = sc{scQLog = Just "qlog"} setClientQlog :: ClientConfig -> ClientConfig-setClientQlog cc = cc+setClientQlog cc = cc{ccQLog = Just "qlog"} data Scenario = Randomly Int@@ -111,7 +123,21 @@ | DropServerPacket [Int] withPipe :: Scenario -> IO () -> IO ()-withPipe scenario body = do+withPipe = withPipeWith False++-- | 'withPipe', with one short-header datagram delivered to the relay's+-- socket before the relay starts reading.+--+-- That is what the CONNECTION_CLOSE of the connection that just closed looks+-- like when it lands after this socket has taken over the port, and taking it+-- for the client ties the relay to a peer with nothing left to say. The test+-- that uses this fails within the idle timeout if the relay ever goes back to+-- latching onto the first datagram it sees.+withPipeStray :: Scenario -> IO () -> IO ()+withPipeStray = withPipeWith True++withPipeWith :: Bool -> Scenario -> IO () -> IO ()+withPipeWith stray scenario body = do addrC <- resolve "50002" let saC = addrAddress addrC addrS <- resolve "50003"@@ -124,6 +150,10 @@ setSocketOption sockS ReuseAddr 1 bind sockC saC connect sockS saS+ when stray $+ E.bracket (openSocket addrC) close $ \sock ->+ void $ sendTo sock (BS.pack [0x40, 1, 2, 3]) saC+ -- The relaying threads have to stop before the sockets close. -- Run at the end of body instead, the kills are skipped whenever -- body throws, and the threads are then left in recv on a socket@@ -135,7 +165,22 @@ startRelay sockC sockS irefC irefS = do -- from client tid0 <- forkIO $ do- (bs, saO) <- recvFrom sockC 2048+ -- Wait for the client to introduce itself, and take the first+ -- long-header packet rather than the first datagram.+ --+ -- These sockets use one fixed port, so the socket for this test+ -- binds it a fraction of a millisecond after the previous test+ -- closed its own. The client of that test signs off with a+ -- CONNECTION_CLOSE, and when that lands after the handover it is+ -- this socket that receives it. Connecting to its sender ties+ -- the relay to a peer with nothing left to say, and the kernel+ -- then drops every datagram from the client we are here to+ -- relay: it sends Initial packets until the idle timeout and+ -- hears nothing, the server never sees the connection at all.+ --+ -- A client always opens with a long header; a leftover from an+ -- established connection is a short one. That tells them apart.+ (bs, saO) <- waitForClientHello sockC connect sockC saO n0 <- atomicModifyIORef' irefC $ \x -> (x + 1, x) dropPacket0 <- shouldDrop scenario True n0@@ -155,6 +200,11 @@ when (isCC || not dropPacket) $ void $ send sockC bs return (tid0, tid1) stopRelay (tid0, tid1) = killThread tid0 >> killThread tid1+ waitForClientHello sockC = do+ (bs, saO) <- recvFrom sockC 2048+ if not (BS.null bs) && BS.head bs .&. 0x80 /= 0+ then return (bs, saO)+ else waitForClientHello sockC hints = defaultHints { addrSocketType = Network.Socket.Datagram
test/IOSpec.hs view
@@ -28,7 +28,7 @@ { onServerReady = putMVar var () } }- let cc = testClientConfigR+ let cc = setClientQlog testClientConfigR let waitS = takeMVar var :: IO () describe "send & recv" $ do it "can exchange data on random dropping" $ do@@ -93,6 +93,9 @@ -- number of bytes sent by the RESET_STREAM sender. it "sends RESET_STREAM with the bytes sent as final size" $ do withPipe (DropClientPacket []) $ testResetStreamFinalSize cc sc waitS+ describe "port handover" $ do+ it "ignores a leftover datagram from the connection that just closed" $+ withPipeStray (Randomly 20) $ testSendRecv cc sc waitS 20 describe "concurrency" $ do it "can handle multiple clients" $ do withPipe (Randomly 20) $ testMultiSendRecv cc sc waitS 500
test/TLSSpec.hs view
@@ -162,7 +162,6 @@ _ -> error "Retry version 1" {- FOURMOLU_DISABLE -}-#ifndef USE_FUSION it "describes the examples of ChaCha20-Poly1305 Short Header Packet (RFC 9001: A.5)" $ do@@ -189,7 +188,6 @@ payloadCipherText `shouldBe` Just (dec16 "65", dec16 "5e5cd55c41f69080575d7999c25a5bfb") mask `shouldBe` Mask (dec16 "aefefe7d03")-#endif {- FOURMOLU_ENSABLE -} ----------------------------------------------------------------@@ -320,7 +318,6 @@ _ -> error "Retry version 2" {- FOURMOLU_DISABLE -}-#ifndef USE_FUSION it "describes the examples of ChaCha20-Poly1305 Short Header Packet (RFC 9369: A.5)" $ do@@ -347,7 +344,6 @@ payloadCipherText `shouldBe` Just (dec16 "0a", dec16 "e7b6b932bc27d786f4bc2bb20f2162ba") mask `shouldBe` Mask (dec16 "97580e32bf")-#endif {- FOURMOLU_ENABLE -} serverCRYPTOframe :: BS.ByteString