packages feed

quic 0.1.0 → 0.1.1

raw patch · 13 files changed

+9475/−6765 lines, 13 filesdep −doctest

Dependencies removed: doctest

Files

ChangeLog.md view
@@ -1,3 +1,12 @@+## 0.1.1++- Fix recvStream hanging+  [#54](https://github.com/kazu-yamamoto/quic/pull/54)+- Don't use the fusion crypto on Intel if the CPU does not+  provides enough features.+- Add cabal flag for fusion support+  [#53](https://github.com/kazu-yamamoto/quic/pull/53)+ ## 0.1.0  - Supporting QUICv2 and version negotiation.
Network/QUIC/Connection/Crypto.hs view
@@ -105,8 +105,9 @@            else              getVersion conn     cipher <- getCipher conn lvl+    avail <- isFusionAvailable     (coder, protector) <--        if useFusion then+        if useFusion && avail then             genFusionCoder (isClient conn) ver cipher sec           else             genNiteCoder (isClient conn) ver cipher sec@@ -117,8 +118,9 @@ initializeCoder1RTT conn sec = do     ver <- getVersion conn     cipher <- getCipher conn RTT1Level+    avail <- isFusionAvailable     (coder, protector) <--        if useFusion then+        if useFusion && avail then             genFusionCoder (isClient conn) ver cipher sec           else             genNiteCoder (isClient conn) ver cipher sec@@ -133,7 +135,8 @@     cipher <- getCipher conn RTT1Level     Coder1RTT coder secN <- readArray (coders1RTT conn) (not nextPhase)     let secN1 = updateSecret ver cipher secN-    coderN1 <- if useFusion then+    avail <- isFusionAvailable+    coderN1 <- if useFusion && avail then                    genFusionCoder1RTT (isClient conn) ver cipher secN1 coder                  else                    genNiteCoder1RTT (isClient conn) ver cipher secN1 coder
Network/QUIC/Crypto/Fusion.hs view
@@ -10,6 +10,7 @@   , fusionSetupSupplement   , fusionSetSample   , fusionGetMask+  , isFusionAvailable   ) where  #ifdef USE_FUSION@@ -97,6 +98,11 @@ 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"@@ -155,6 +161,10 @@  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@@ -186,4 +196,7 @@  fusionGetMask :: Supplement -> IO Buffer fusionGetMask = undefined++isFusionAvailable :: IO Bool+isFusionAvailable = return False #endif
Network/QUIC/Stream/Misc.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-}  module Network.QUIC.Stream.Misc (@@ -22,6 +23,7 @@ import UnliftIO.STM  import Network.QUIC.Imports+import Network.QUIC.Stream.Queue import Network.QUIC.Stream.Types  ----------------------------------------------------------------@@ -54,7 +56,9 @@     return fin  setRxStreamClosed :: Stream -> IO ()-setRxStreamClosed Stream{..} = atomicModifyIORef'' streamStateRx set+setRxStreamClosed strm@Stream{..} = do+    atomicModifyIORef'' streamStateRx set+    putRecvStreamQ strm ""   where     set (StreamState off _) = StreamState off True 
cbits/fusion.c view
@@ -17,1059 +17,2247 @@  * All other work, including modifications to the `transformH` function is  * covered by the following MIT license:  *- * 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.- */-#include <stdint.h>-#ifdef _WINDOWS-#include <intrin.h>-#endif-#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"--struct ptls_fusion_aesgcm_context {-    ptls_fusion_aesecb_context_t ecb;-    size_t capacity;-    size_t ghash_cnt;-    struct ptls_fusion_aesgcm_ghash_precompute {-        __m128i H;-        __m128i r;-    } 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 bswap8_[16] __attribute__((aligned(16))) = {15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};-#define bswap8 (*(__m128i *)bswap8_)-static const uint8_t one8_[16] __attribute__((aligned(16))) = {1};-#define one8 (*(__m128i *)one8_)--/* 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);-}--struct ptls_fusion_gfmul_state {-    __m128i hi, lo, mid;-};--static inline void gfmul_onestep(struct ptls_fusion_gfmul_state *gstate, __m128i X,-                                 struct ptls_fusion_aesgcm_ghash_precompute *precompute)-{-    X = _mm_shuffle_epi8(X, bswap8);-    __m128i t = _mm_clmulepi64_si128(precompute->H, X, 0x00);-    gstate->lo = _mm_xor_si128(gstate->lo, t);-    t = _mm_clmulepi64_si128(precompute->H, X, 0x11);-    gstate->hi = _mm_xor_si128(gstate->hi, t);-    t = _mm_shuffle_epi32(X, 78);-    t = _mm_xor_si128(t, X);-    t = _mm_clmulepi64_si128(precompute->r, t, 0x00);-    gstate->mid = _mm_xor_si128(gstate->mid, t);-}--static inline __m128i gfmul_final(struct ptls_fusion_gfmul_state *gstate, __m128i ek0)-{-    /* finish multiplication */-    gstate->mid = _mm_xor_si128(gstate->mid, gstate->hi);-    gstate->mid = _mm_xor_si128(gstate->mid, gstate->lo);-    gstate->lo = _mm_xor_si128(gstate->lo, _mm_slli_si128(gstate->mid, 8));-    gstate->hi = _mm_xor_si128(gstate->hi, _mm_srli_si128(gstate->mid, 8));--    /* fast reduction, using https://crypto.stanford.edu/RealWorldCrypto/slides/gueron.pdf */-    __m128i r = _mm_clmulepi64_si128(gstate->lo, poly, 0x10);-    gstate->lo = _mm_shuffle_epi32(gstate->lo, 78);-    gstate->lo = _mm_xor_si128(gstate->lo, r);-    r = _mm_clmulepi64_si128(gstate->lo, poly, 0x10);-    gstate->lo = _mm_shuffle_epi32(gstate->lo, 78);-    gstate->lo = _mm_xor_si128(gstate->lo, r);-    __m128i tag = _mm_xor_si128(gstate->hi, gstate->lo);-    tag = _mm_shuffle_epi8(tag, bswap8);-    tag = _mm_xor_si128(tag, ek0);--    return tag;-}--static inline __m128i aesecb_encrypt(ptls_fusion_aesecb_context_t *ctx, __m128i v)-{-    size_t i;--    v = _mm_xor_si128(v, ctx->keys[0]);-    for (i = 1; i < ctx->rounds; ++i)-        v = _mm_aesenc_si128(v, ctx->keys[i]);-    v = _mm_aesenclast_si128(v, ctx->keys[i]);--    return v;-}--static const uint8_t loadn_mask[31] = {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--static inline __m128i loadn(const void *p, size_t l)-{-    __m128i v, mask = _mm_loadu_si128((__m128i *)(loadn_mask + 16 - l));-    uintptr_t mod4k = (uintptr_t)p % 4096;--    if (PTLS_LIKELY(mod4k <= 4080) || mod4k + l > 4096) {-        v = _mm_loadu_si128(p);-    } else {-        uintptr_t shift = (uintptr_t)p & 15;-        __m128i pattern = _mm_loadu_si128((const __m128i *)(loadn_shuffle + shift));-        v = _mm_shuffle_epi8(_mm_load_si128((const __m128i *)((uintptr_t)p - shift)), pattern);-    }-    v = _mm_and_si128(v, mask);-    return v;-}--static inline void storen(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, bswap8);                                                                                     \-        ctr = _mm_add_epi64(ctr, one8);                                                                                            \-        bits1 = _mm_shuffle_epi8(ctr, bswap8);                                                                                     \-        ctr = _mm_add_epi64(ctr, one8);                                                                                            \-        bits2 = _mm_shuffle_epi8(ctr, bswap8);                                                                                     \-        ctr = _mm_add_epi64(ctr, one8);                                                                                            \-        bits3 = _mm_shuffle_epi8(ctr, bswap8);                                                                                     \-        ctr = _mm_add_epi64(ctr, one8);                                                                                            \-        bits4 = _mm_shuffle_epi8(ctr, bswap8);                                                                                     \-        if (PTLS_LIKELY(srclen > 16 * 5)) {                                                                                        \-            ctr = _mm_add_epi64(ctr, one8);                                                                                        \-            bits5 = _mm_shuffle_epi8(ctr, bswap8);                                                                                 \-        } 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;                                                        \-                state |= STATE_SUPP_IN_PROCESS;                                                                                    \-            }                                                                                                                      \-        }                                                                                                                          \-        __m128i k = ctx->ecb.keys[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->ecb.keys[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->ecb.keys[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)--    __m128i ek0, bits0, bits1, bits2, bits3, bits4, bits5 = _mm_setzero_si128();-    const __m128i *bits4keys = ctx->ecb.keys; /* is changed to supp->ctx.keys when calcurating suppout */-    struct ptls_fusion_gfmul_state gstate = {0};-    __m128i gdatabuf[6];-    __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), bswap8);--    // 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_precompute *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, bswap8);--    /* 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++] = loadn(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_onestep(&gstate, _mm_loadu_si128(gdata++), --ghash_precompute);-        }-        for (; i < ctx->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:-                storen(dst, srclen, _mm_xor_si128(loadn(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++] = loadn(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++] = loadn(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_onestep(&gstate, gdatabuf[i], --ghash_precompute);--    _mm_storeu_si128(dst, gfmul_final(&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;-            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->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)-{-    __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_state gstate = {0};-    __m128i gdatabuf[6];-    __m128i ac = _mm_shuffle_epi8(_mm_set_epi32(0, (int)aadlen * 8, 0, (int)inlen * 8), bswap8);-    struct ptls_fusion_aesgcm_ghash_precompute *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, bswap8), ctx->ecb.keys[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++] = loadn(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++] = loadn(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, bswap8), keys[0]);-        InitAllBits:-            INIT_BITS(0, ctx->ecb.keys);-            INIT_BITS(1, ctx->ecb.keys);-            INIT_BITS(2, ctx->ecb.keys);-            INIT_BITS(3, ctx->ecb.keys);-            INIT_BITS(4, ctx->ecb.keys);-            INIT_BITS(5, ctx->ecb.keys);-#undef INIT_BITS-        }--        { /* run aes and ghash */-#define AESECB6_UPDATE(i)                                                                                                          \-    do {                                                                                                                           \-        __m128i k = ctx->ecb.keys[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_onestep(&gstate, _mm_loadu_si128(gdata++), --ghash_precompute);-            }-            for (; aesi < ctx->ecb.rounds; ++aesi)-                AESECB6_UPDATE(aesi);-            __m128i k = ctx->ecb.keys[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) {-        storen(dst, src_aeslen, _mm_xor_si128(loadn(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_onestep(&gstate, ac, --ghash_precompute);-    }--    __m128i calctag = gfmul_final(&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)-{-    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->keys[i++] = _mm_loadu_si128((__m128i *)key);-    if (key_size == 32)-        ctx->keys[i++] = _mm_loadu_si128((__m128i *)key + 1);--#define EXPAND(R)                                                                                                                  \-    do {                                                                                                                           \-        ctx->keys[i] = expand_key(ctx->keys[i - key_size / 16],                                                                    \-                                  _mm_shuffle_epi32(_mm_aeskeygenassist_si128(ctx->keys[i - 1], R), _MM_SHUFFLE(3, 3, 3, 3)));     \-        if (i == ctx->rounds)                                                                                                      \-            goto Done;                                                                                                             \-        ++i;                                                                                                                       \-        if (key_size > 24) {                                                                                                       \-            ctx->keys[i] = expand_key(ctx->keys[i - key_size / 16],                                                                \-                                      _mm_shuffle_epi32(_mm_aeskeygenassist_si128(ctx->keys[i - 1], R), _MM_SHUFFLE(2, 2, 2, 2))); \-            ++i;                                                                                                                   \-        }                                                                                                                          \-    } while (0)-    EXPAND(0x1);-    EXPAND(0x2);-    EXPAND(0x4);-    EXPAND(0x8);-    EXPAND(0x10);-    EXPAND(0x20);-    EXPAND(0x40);-    EXPAND(0x80);-    EXPAND(0x1b);-    EXPAND(0x36);-#undef EXPAND-Done:-    assert(i == ctx->rounds);-}--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)-{-    if (ctx->ghash_cnt != 0)-        ctx->ghash[ctx->ghash_cnt].H = gfmul(ctx->ghash[ctx->ghash_cnt - 1].H, ctx->ghash[0].H);--    __m128i r = _mm_shuffle_epi32(ctx->ghash[ctx->ghash_cnt].H, 78);-    r = _mm_xor_si128(r, ctx->ghash[ctx->ghash_cnt].H);-    ctx->ghash[ctx->ghash_cnt].r = r;--    ++ctx->ghash_cnt;-}--ptls_fusion_aesgcm_context_t *ptls_fusion_aesgcm_new(const void *key, size_t key_size, size_t capacity)-{-    ptls_fusion_aesgcm_context_t *ctx;-    size_t ghash_cnt = aesgcm_calc_ghash_cnt(capacity);--    if ((ctx = malloc(sizeof(*ctx) + sizeof(ctx->ghash[0]) * ghash_cnt)) == NULL)-        return NULL;--    ptls_fusion_aesecb_init(&ctx->ecb, 1, key, key_size);--    ctx->capacity = capacity;--    ctx->ghash[0].H = aesecb_encrypt(&ctx->ecb, _mm_setzero_si128());-    ctx->ghash[0].H = _mm_shuffle_epi8(ctx->ghash[0].H, bswap8);-    ctx->ghash[0].H = transformH(ctx->ghash[0].H);-    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_set_capacity(ptls_fusion_aesgcm_context_t *ctx, size_t capacity)-{-    size_t ghash_cnt = aesgcm_calc_ghash_cnt(capacity);--    if (ghash_cnt <= ctx->ghash_cnt)-        return ctx;--    if ((ctx = realloc(ctx, sizeof(*ctx) + sizeof(ctx->ghash[0]) * ghash_cnt)) == NULL)-        return NULL;--    ctx->capacity = capacity;-    while (ghash_cnt < ctx->ghash_cnt)-        setup_one_ghash_entry(ctx);--    return ctx;-}--void ptls_fusion_aesgcm_free(ptls_fusion_aesgcm_context_t *ctx)-{-    ptls_clear_memory(ctx->ghash, sizeof(ctx->ghash[0]) * ctx->ghash_cnt);-    ctx->ghash_cnt = 0;-    ptls_fusion_aesecb_dispose(&ctx->ecb);-    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) {-        storen(output, len, _mm_xor_si128(_mm_loadu_si128(&ctx->bits), loadn(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);-    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);-}--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_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)-{-    struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;-    __m128i xor_mask = loadn(_bytes, len);-    xor_mask = _mm_shuffle_epi8(xor_mask, bswap8);-    ctx->static_iv = _mm_xor_si128(ctx->static_iv, xor_mask);-}--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 = loadn(iv, PTLS_AESGCM_IV_SIZE);-    ctx->static_iv = _mm_shuffle_epi8(ctx->static_iv, bswap8);-    if (key == NULL)-        return 0;--    ctx->super.dispose_crypto = aesgcm_dispose_crypto;-    ctx->super.do_xor_iv = aesgcm_xor_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_decrypt = aead_do_decrypt;--    ctx->aesgcm = ptls_fusion_aesgcm_new(key, key_size, 1500 /* assume ordinary packet size */);--    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);-}--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,-                                               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,-                                               sizeof(struct aesgcm_context),-                                               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;-            __cpuid(cpu_info, 7);-            leaf7_ebx = cpu_info[1];--            is_supported = /* AVX2 */ (leaf7_ebx & (1 << 5)) != 0;-        }-    }--    return is_supported;-}-#else-int ptls_fusion_is_supported_by_cpu(void)-{-    unsigned leaf1_ecx, leaf7_ebx;--    { /* 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) : "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;--    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;-}+ * 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);
cbits/picotls.c view
@@ -19,5568 +19,6629 @@  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS  * IN THE SOFTWARE.  */-#include <assert.h>-#include <stddef.h>-#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#ifdef _WINDOWS-#include "winsock.h"-#else-#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_ENCRYPTED_SERVER_NAME 0xffce--#define PTLS_PROTOCOL_VERSION_TLS13_FINAL 0x0304-#define PTLS_PROTOCOL_VERSION_TLS13_DRAFT26 0x7f1a-#define PTLS_PROTOCOL_VERSION_TLS13_DRAFT27 0x7f1b-#define PTLS_PROTOCOL_VERSION_TLS13_DRAFT28 0x7f1c--#define PTLS_SERVER_NAME_TYPE_HOSTNAME 0--#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_FINAL, PTLS_PROTOCOL_VERSION_TLS13_DRAFT28,-                                              PTLS_PROTOCOL_VERSION_TLS13_DRAFT27, PTLS_PROTOCOL_VERSION_TLS13_DRAFT26};--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;-};--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_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_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-     */-    uint8_t client_random[PTLS_HELLO_RANDOM_SIZE];-    /**-     * esni-     */-    ptls_esni_secret_t *esni;-    /**-     * exporter master secret (either 0rtt or 1rtt)-     */-    struct {-        uint8_t *early;-        uint8_t *one_rtt;-    } exporter_master_secret;-    /* 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 */-        } 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_CLIENT_CIPHERS 32-#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_cipher_suite_t *cipher; /* selected cipher-suite, or NULL if esni extension is not used */-        ptls_key_exchange_algorithm_t *key_share;-        ptls_iovec_t peer_key;-        const uint8_t *record_digest;-        ptls_iovec_t encrypted_sni;-    } esni;-    struct {-        ptls_iovec_t list[16];-        size_t count;-    } alpn;-    struct {-        uint16_t list[16];-        size_t count;-    } cert_compression_algos;-    struct {-        uint16_t list[MAX_CLIENT_CIPHERS];-        size_t count;-    } client_ciphers;-    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 {-        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;-    struct {-        uint8_t list[MAX_CERTIFICATE_TYPES];-        size_t count;-    } server_certificate_types;-    ptls_raw_extension_t unknown_extensions[MAX_UNKNOWN_EXTENSIONS + 1];-    unsigned status_request : 1;-};--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;-        } retry_request;-    };-};--struct st_ptls_key_schedule_t {-    unsigned generation; /* early secret (1), hanshake secret (2), master secret (3) */-    const char *hkdf_label_prefix;-    uint8_t secret[PTLS_MAX_DIGEST_SIZE];-    size_t num_hashes;-    struct {-        ptls_hash_algorithm_t *algo;-        ptls_hash_context_t *ctx;-    } 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 {-    uint8_t bits[8]; /* only ids below 64 is tracked */-};--static const uint8_t zeroes_of_max_digest_size[PTLS_MAX_DIGEST_SIZE] = {0};--static int 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);-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 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 inline int extension_bitmap_is_set(struct st_ptls_extension_bitmap_t *bitmap, uint16_t id)-{-    if (id < sizeof(bitmap->bits) * 8)-        return (bitmap->bits[id / 8] & (1 << (id % 8))) != 0;-    return 0;-}--static inline void extension_bitmap_set(struct st_ptls_extension_bitmap_t *bitmap, uint16_t id)-{-    if (id < sizeof(bitmap->bits) * 8)-        bitmap->bits[id / 8] |= 1 << (id % 8);-}--static inline void init_extension_bitmap(struct st_ptls_extension_bitmap_t *bitmap, uint8_t hstype)-{-    *bitmap = (struct st_ptls_extension_bitmap_t){{0}};--#define EXT(extid, proc)                                                                                                           \-    do {                                                                                                                           \-        int _found = 0;                                                                                                            \-        do {                                                                                                                       \-            proc                                                                                                                   \-        } while (0);                                                                                                               \-        if (!_found)                                                                                                               \-            extension_bitmap_set(bitmap, PTLS_EXTENSION_TYPE_##extid);                                                             \-    } while (0)-#define ALLOW(allowed_hstype) _found = _found || hstype == PTLS_HANDSHAKE_TYPE_##allowed_hstype--    /* Implements the table found in section 4.2 of draft-19; "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."-     */-    EXT(SERVER_NAME, {-        ALLOW(CLIENT_HELLO);-        ALLOW(ENCRYPTED_EXTENSIONS);-    });-    EXT(STATUS_REQUEST, {-        ALLOW(CLIENT_HELLO);-        ALLOW(CERTIFICATE);-        ALLOW(CERTIFICATE_REQUEST);-    });-    EXT(SUPPORTED_GROUPS, {-        ALLOW(CLIENT_HELLO);-        ALLOW(ENCRYPTED_EXTENSIONS);-    });-    EXT(SIGNATURE_ALGORITHMS, {-        ALLOW(CLIENT_HELLO);-        ALLOW(CERTIFICATE_REQUEST);-    });-    EXT(ALPN, {-        ALLOW(CLIENT_HELLO);-        ALLOW(ENCRYPTED_EXTENSIONS);-    });-    EXT(KEY_SHARE, {-        ALLOW(CLIENT_HELLO);-        ALLOW(SERVER_HELLO);-    });-    EXT(PRE_SHARED_KEY, {-        ALLOW(CLIENT_HELLO);-        ALLOW(SERVER_HELLO);-    });-    EXT(PSK_KEY_EXCHANGE_MODES, { ALLOW(CLIENT_HELLO); });-    EXT(EARLY_DATA, {-        ALLOW(CLIENT_HELLO);-        ALLOW(ENCRYPTED_EXTENSIONS);-        ALLOW(NEW_SESSION_TICKET);-    });-    EXT(COOKIE, {-        ALLOW(CLIENT_HELLO);-        ALLOW(SERVER_HELLO);-    });-    EXT(SUPPORTED_VERSIONS, {-        ALLOW(CLIENT_HELLO);-        ALLOW(SERVER_HELLO);-    });-    EXT(SERVER_CERTIFICATE_TYPE, {-        ALLOW(CLIENT_HELLO);-        ALLOW(ENCRYPTED_EXTENSIONS);-    });--#undef ALLOW-#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--void ptls_buffer__release_memory(ptls_buffer_t *buf)-{-    ptls_clear_memory(buf->base, buf->off);-    if (buf->is_allocated)-        free(buf->base);-}--int ptls_buffer_reserve(ptls_buffer_t *buf, size_t delta)-{-    if (buf->base == NULL)-        return PTLS_ERROR_NO_MEMORY;--    if (PTLS_MEMORY_DEBUG || buf->capacity < buf->off + delta) {-        uint8_t *newp;-        size_t new_capacity = buf->capacity;-        if (new_capacity < 1024)-            new_capacity = 1024;-        while (new_capacity < buf->off + delta) {-            new_capacity *= 2;-        }-        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;-    }--    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)-{-    uint8_t aad[5];-    size_t off = 0;--    build_aad(aad, inlen + 1 + ctx->aead->algo->tag_size);-    ptls_aead_encrypt_init(ctx->aead, ctx->seq++, aad, sizeof(aad));-    off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, input, inlen);-    off += ptls_aead_encrypt_update(ctx->aead, ((uint8_t *)output) + off, &content_type, 1);-    off += ptls_aead_encrypt_final(ctx->aead, ((uint8_t *)output) + off);--    return off;-}--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 */--#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;-        buffer_push_record(buf, PTLS_CONTENT_TYPE_APPDATA, {-            if ((ret = ptls_buffer_reserve(buf, chunk_size + enc->aead->algo->tag_size + 1)) != 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 */-    if (bodylen <= PTLS_MAX_PLAINTEXT_RECORD_SIZE) {-        size_t overhead = 1 + enc->aead->algo->tag_size;-        if ((ret = ptls_buffer_reserve(buf, overhead)) != 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;                                                                                  \-        init_extension_bitmap(&bitmap, (hstype));                                                                                  \-        ptls_decode_open_block((src), end, 2, {                                                                                    \-            while ((src) != end) {                                                                                                 \-                if ((ret = ptls_decode16((exttype), &(src), end)) != 0)                                                            \-                    goto Exit;                                                                                                     \-                if (extension_bitmap_is_set(&bitmap, *(exttype)) != 0) {                                                           \-                    ret = PTLS_ALERT_ILLEGAL_PARAMETER;                                                                            \-                    goto Exit;                                                                                                     \-                }                                                                                                                  \-                extension_bitmap_set(&bitmap, *(exttype));                                                                         \-                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_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));--    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));-}--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);-    free(sched);-}--static ptls_key_schedule_t *key_schedule_new(ptls_cipher_suite_t *preferred, ptls_cipher_suite_t **offered,-                                             const char *hkdf_label_prefix)-{-#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;--    if (hkdf_label_prefix == NULL)-        hkdf_label_prefix = PTLS_HKDF_EXPAND_LABEL_PREFIX;--    { /* 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, hkdf_label_prefix};-    }--    /* 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;-        ++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 = 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),-                                 sched->hkdf_label_prefix)) != 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_one(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 (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;-}--void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen)-{-    size_t i;--    PTLS_DEBUGF("%s:%zu\n", __FUNCTION__, msglen);-    for (i = 0; i != sched->num_hashes; ++i)-        sched->hashes[i].ctx->update(sched->hashes[i].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));-}--static void key_schedule_extract_ch1hash(ptls_key_schedule_t *sched, uint8_t *hash)-{-    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)-{-    uint8_t ch1hash[PTLS_MAX_DIGEST_SIZE];--    key_schedule_extract_ch1hash(sched, ch1hash);--    key_schedule_update_ch1hash_prefix(sched);-    ptls__key_schedule_update_hash(sched, ch1hash, sched->hashes[0].algo->digest_size);-}--static int derive_secret_with_hash(ptls_key_schedule_t *sched, void *secret, const char *label, const uint8_t *hash)-{-    int ret = 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), sched->hkdf_label_prefix);-    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 = 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,-                                 sched->hkdf_label_prefix)) != 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 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;--    /* 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;--    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));-    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)-{-    PTLS_PROBE(CLIENT_RANDOM, tls,-               ptls_hexdump(alloca(sizeof(tls->client_random) * 2 + 1), 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 = 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),-                                 sched->hkdf_label_prefix)) != 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);--    { /* 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 */-    ptls_buffer_init(&session_id, session_id_smallbuf, sizeof(session_id_smallbuf));-    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 ptls_hash_context_t *create_sha256_context(ptls_context_t *ctx)-{-    ptls_cipher_suite_t **cs;--    for (cs = ctx->cipher_suites; *cs != NULL; ++cs) {-        switch ((*cs)->id) {-        case PTLS_CIPHER_SUITE_AES_128_GCM_SHA256:-        case PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256:-            return (*cs)->hash->create();-        }-    }--    return NULL;-}--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)-{-    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) {-                    /* 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;-                }-            }-        }-    }-    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;-}--static int parse_esni_keys(ptls_context_t *ctx, uint16_t *esni_version, ptls_key_exchange_algorithm_t **selected_key_share,-                           ptls_cipher_suite_t **selected_cipher, ptls_iovec_t *peer_key, uint16_t *padded_length,-                           char **published_sni, ptls_iovec_t input)-{-    const uint8_t *src = input.base, *const end = input.base + input.len;-    uint16_t version;-    uint64_t not_before, not_after, now;-    int ret = 0;--    /* version */-    if ((ret = ptls_decode16(&version, &src, end)) != 0)-        goto Exit;-    if (version != PTLS_ESNI_VERSION_DRAFT03) {-        ret = PTLS_ALERT_DECODE_ERROR;-        goto Exit;-    }--    { /* verify checksum */-        ptls_hash_context_t *hctx;-        uint8_t digest[PTLS_SHA256_DIGEST_SIZE];-        if (end - src < 4) {-            ret = PTLS_ALERT_DECODE_ERROR;-            goto Exit;-        }-        if ((hctx = create_sha256_context(ctx)) == NULL) {-            ret = PTLS_ERROR_LIBRARY;-            goto Exit;-        }-        hctx->update(hctx, input.base, src - input.base);-        hctx->update(hctx, "\0\0\0\0", 4);-        hctx->update(hctx, src + 4, end - (src + 4));-        hctx->final(hctx, digest, PTLS_HASH_FINAL_MODE_FREE);-        if (memcmp(src, digest, 4) != 0) {-            ret = PTLS_ALERT_DECODE_ERROR;-            goto Exit;-        }-        src += 4;-    }-    *esni_version = version;-    /* published sni */-    ptls_decode_open_block(src, end, 2, {-        size_t len = end - src;-        *published_sni = malloc(len + 1);-        if (*published_sni == NULL) {-            ret = PTLS_ERROR_NO_MEMORY;-            goto Exit;-        }-        if (len > 0) {-            memcpy(*published_sni, src, len);-        }-        (*published_sni)[len] = 0;-        src = end;-    });-    /* key-shares */-    ptls_decode_open_block(src, end, 2, {-        if ((ret = select_key_share(selected_key_share, peer_key, ctx->key_exchanges, &src, end, 0)) != 0)-            goto Exit;-    });-    /* cipher-suite */-    ptls_decode_open_block(src, end, 2, {-        if ((ret = select_cipher(selected_cipher, ctx->cipher_suites, src, end, ctx->server_cipher_preference)) != 0)-            goto Exit;-        src = end;-    });-    /* padded-length */-    if ((ret = ptls_decode16(padded_length, &src, end)) != 0)-        goto Exit;-    if (padded_length == 0)-        goto Exit;-    /* not-before, not_after */-    if ((ret = ptls_decode64(&not_before, &src, end)) != 0 || (ret = ptls_decode64(&not_after, &src, end)) != 0)-        goto Exit;-    /* extensions */-    ptls_decode_block(src, end, 2, {-        while (src != end) {-            uint16_t id;-            if ((ret = ptls_decode16(&id, &src, end)) != 0)-                goto Exit;-            ptls_decode_open_block(src, end, 2, { src = end; });-        }-    });--    /* check validity period */-    now = ctx->get_time->cb(ctx->get_time);-    if (!(not_before * 1000 <= now && now <= not_after * 1000)) {-        ret = PTLS_ALERT_DECODE_ERROR;-        goto Exit;-    }--    ret = 0;-Exit:-    return ret;-}--static int create_esni_aead(ptls_aead_context_t **aead_ctx, int is_enc, ptls_cipher_suite_t *cipher, ptls_iovec_t ecdh_secret,-                            const uint8_t *esni_contents_hash)-{-    uint8_t aead_secret[PTLS_MAX_DIGEST_SIZE];-    int ret;--    if ((ret = ptls_hkdf_extract(cipher->hash, aead_secret, ptls_iovec_init(NULL, 0), ecdh_secret)) != 0)-        goto Exit;-    if ((*aead_ctx = new_aead(cipher->aead, cipher->hash, is_enc, aead_secret,-                              ptls_iovec_init(esni_contents_hash, cipher->hash->digest_size), "tls13 esni ")) == NULL) {-        ret = PTLS_ERROR_NO_MEMORY;-        goto Exit;-    }--    ret = 0;-Exit:-    ptls_clear_memory(aead_secret, sizeof(aead_secret));-    return ret;-}--static int build_esni_contents_hash(ptls_hash_algorithm_t *hash, uint8_t *digest, const uint8_t *record_digest, uint16_t group,-                                    ptls_iovec_t pubkey, const uint8_t *client_random)-{-    ptls_buffer_t buf;-    uint8_t smallbuf[256];-    int ret;--    /* build ESNIContents */-    ptls_buffer_init(&buf, smallbuf, sizeof(smallbuf));-    ptls_buffer_push_block(&buf, 2, { ptls_buffer_pushv(&buf, record_digest, hash->digest_size); });-    if ((ret = push_key_share_entry(&buf, group, pubkey)) != 0)-        goto Exit;-    ptls_buffer_pushv(&buf, client_random, PTLS_HELLO_RANDOM_SIZE);--    /* calculate digest */-    if ((ret = ptls_calc_hash(hash, digest, buf.base, buf.off)) != 0)-        goto Exit;--    ret = 0;-Exit:-    ptls_buffer_dispose(&buf);-    return ret;-}--static void free_esni_secret(ptls_esni_secret_t **esni, int is_server)-{-    assert(*esni != NULL);-    if ((*esni)->secret.base != NULL) {-        ptls_clear_memory((*esni)->secret.base, (*esni)->secret.len);-        free((*esni)->secret.base);-    }-    if (!is_server)-        free((*esni)->client.pubkey.base);-    ptls_clear_memory((*esni), sizeof(**esni));-    free(*esni);-    *esni = NULL;-}--static int client_setup_esni(ptls_context_t *ctx, ptls_esni_secret_t **esni, ptls_iovec_t esni_keys, char **published_sni,-                             const uint8_t *client_random)-{-    ptls_iovec_t peer_key;-    int ret;--    if ((*esni = malloc(sizeof(**esni))) == NULL)-        return PTLS_ERROR_NO_MEMORY;-    memset(*esni, 0, sizeof(**esni));--    /* parse ESNI_Keys (and return success while keeping *esni NULL) */-    if (parse_esni_keys(ctx, &(*esni)->version, &(*esni)->client.key_share, &(*esni)->client.cipher, &peer_key,-                        &(*esni)->client.padded_length, published_sni, esni_keys) != 0) {-        free(*esni);-        *esni = NULL;-        return 0;-    }--    ctx->random_bytes((*esni)->nonce, sizeof((*esni)->nonce));--    /* calc record digest */-    if ((ret = ptls_calc_hash((*esni)->client.cipher->hash, (*esni)->client.record_digest, esni_keys.base, esni_keys.len)) != 0)-        goto Exit;-    /* derive ECDH secret */-    if ((ret = (*esni)->client.key_share->exchange((*esni)->client.key_share, &(*esni)->client.pubkey, &(*esni)->secret,-                                                   peer_key)) != 0)-        goto Exit;-    /* calc H(ESNIContents) */-    if ((ret = build_esni_contents_hash((*esni)->client.cipher->hash, (*esni)->esni_contents_hash, (*esni)->client.record_digest,-                                        (*esni)->client.key_share->id, (*esni)->client.pubkey, client_random)) != 0)-        goto Exit;--    ret = 0;-Exit:-    if (ret != 0)-        free_esni_secret(esni, 0);-    return ret;-}--static int emit_esni_extension(ptls_esni_secret_t *esni, ptls_buffer_t *buf, ptls_iovec_t esni_keys, const char *server_name,-                               size_t key_share_ch_off, size_t key_share_ch_len)-{-    ptls_aead_context_t *aead = NULL;-    int ret;--    if ((ret = create_esni_aead(&aead, 1, esni->client.cipher, esni->secret, esni->esni_contents_hash)) != 0)-        goto Exit;--    /* cipher-suite id */-    ptls_buffer_push16(buf, esni->client.cipher->id);-    /* key-share */-    if ((ret = push_key_share_entry(buf, esni->client.key_share->id, esni->client.pubkey)) != 0)-        goto Exit;-    /* record-digest */-    ptls_buffer_push_block(buf, 2, { ptls_buffer_pushv(buf, esni->client.record_digest, esni->client.cipher->hash->digest_size); });-    /* encrypted sni */-    ptls_buffer_push_block(buf, 2, {-        size_t start_off = buf->off;-        /* nonce */-        ptls_buffer_pushv(buf, esni->nonce, PTLS_ESNI_NONCE_SIZE);-        /* emit server-name extension */-        if ((ret = emit_server_name_extension(buf, server_name)) != 0)-            goto Exit;-        /* pad */-        if (buf->off - start_off < (size_t)(esni->client.padded_length + PTLS_ESNI_NONCE_SIZE)) {-            size_t bytes_to_pad = esni->client.padded_length + PTLS_ESNI_NONCE_SIZE - (buf->off - start_off);-            if ((ret = ptls_buffer_reserve(buf, bytes_to_pad)) != 0)-                goto Exit;-            memset(buf->base + buf->off, 0, bytes_to_pad);-            buf->off += bytes_to_pad;-        }-        /* encrypt */-        if ((ret = ptls_buffer_reserve(buf, aead->algo->tag_size)) != 0)-            goto Exit;-        ptls_aead_encrypt(aead, buf->base + start_off, buf->base + start_off, buf->off - start_off, 0, buf->base + key_share_ch_off,-                          key_share_ch_len);-        buf->off += aead->algo->tag_size;-    });--    ret = 0;-Exit:-    if (aead != NULL)-        ptls_aead_free(aead);-    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;-    char *published_sni = NULL;-    uint32_t obfuscated_ticket_age = 0;-    size_t msghash_off;-    uint8_t binder_key[PTLS_MAX_DIGEST_SIZE];-    int ret, is_second_flight = tls->key_schedule != NULL,-             send_sni = tls->server_name != NULL && !ptls_server_name_is_ipaddr(tls->server_name);--    if (properties != NULL) {-        /* try to use ESNI */-        if (!is_second_flight && send_sni && properties->client.esni_keys.base != NULL) {-            if ((ret = client_setup_esni(tls->ctx, &tls->esni, properties->client.esni_keys, &published_sni, tls->client_random)) !=-                0) {-                goto Exit;-            }-            if (tls->ctx->update_esni_key != NULL) {-                if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret,-                                                         tls->esni->client.cipher->hash, tls->esni->esni_contents_hash)) != 0)-                    goto Exit;-            }-        }-        /* 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];--    if (!is_second_flight) {-        tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites, tls->ctx->hkdf_label_prefix__obsolete);-        if ((ret = key_schedule_extract(tls->key_schedule, resumption_secret)) != 0)-            goto Exit;-    }--    msghash_off = emitter->buf->off + emitter->record_header_length;-    ptls_push_message(emitter, NULL, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, {-        ptls_buffer_t *sendbuf = emitter->buf;-        /* legacy_version */-        ptls_buffer_push16(sendbuf, 0x0303);-        /* random_bytes */-        ptls_buffer_pushv(sendbuf, tls->client_random, sizeof(tls->client_random));-        /* lecagy_session_id */-        ptls_buffer_push_block(-            sendbuf, 1, { ptls_buffer_pushv(sendbuf, tls->client.legacy_session_id.base, tls->client.legacy_session_id.len); });-        /* cipher_suites */-        ptls_buffer_push_block(sendbuf, 2, {-            ptls_cipher_suite_t **cs = tls->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, {-            struct {-                size_t off;-                size_t len;-            } key_share_client_hello;-            buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_KEY_SHARE, {-                key_share_client_hello.off = sendbuf->off;-                ptls_buffer_push_block(sendbuf, 2, {-                    if (tls->key_share != NULL) {-                        if ((ret = tls->key_share->create(tls->key_share, &tls->client.key_share_ctx)) != 0)-                            goto Exit;-                        if ((ret = push_key_share_entry(sendbuf, tls->key_share->id, tls->client.key_share_ctx->pubkey)) != 0)-                            goto Exit;-                    }-                });-                key_share_client_hello.len = sendbuf->off - key_share_client_hello.off;-            });-            if (send_sni) {-                if (tls->esni != NULL) {-                    if (published_sni != NULL) {-                        buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {-                            if ((ret = emit_server_name_extension(sendbuf, published_sni)) != 0)-                                goto Exit;-                        });-                    }-                    buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, {-                        if ((ret = emit_esni_extension(tls->esni, sendbuf, properties->client.esni_keys, tls->server_name,-                                                       key_share_client_hello.off, key_share_client_hello.len)) != 0)-                            goto Exit;-                    });-                } else {-                    buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SERVER_NAME, {-                        if ((ret = emit_server_name_extension(sendbuf, tls->server_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 (tls->ctx->decompress_certificate != NULL) {-                buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_COMPRESS_CERTIFICATE, {-                    ptls_buffer_push_block(sendbuf, 1, {-                        const uint16_t *algo = tls->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(tls->ctx->verify_certificate, sendbuf)) != 0)-                    goto Exit;-            });-            buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_SUPPORTED_GROUPS, {-                ptls_key_exchange_algorithm_t **algo = tls->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 (tls->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 (tls->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 (!tls->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 (tls->client.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,-                                               { ptls_buffer_pushv(sendbuf, resumption_ticket.base, resumption_ticket.len); });-                        ptls_buffer_push32(sendbuf, obfuscated_ticket_age);-                    });-                    /* allocate space for PSK binder. the space is filled at the bottom of the function */-                    ptls_buffer_push_block(sendbuf, 2, {-                        ptls_buffer_push_block(sendbuf, 1, {-                            if ((ret = ptls_buffer_reserve(sendbuf, tls->key_schedule->hashes[0].algo->digest_size)) != 0)-                                goto Exit;-                            sendbuf->off += tls->key_schedule->hashes[0].algo->digest_size;-                        });-                    });-                });-            }-        });-    });--    /* 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);-        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);--    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:-    if (published_sni != NULL) {-        free(published_sni);-    }-    ptls_clear_memory(binder_key, sizeof(binder_key));-    return ret;-}--static ptls_cipher_suite_t *find_cipher_suite(ptls_context_t *ctx, uint16_t id)-{-    ptls_cipher_suite_t **cs;--    for (cs = ctx->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 = find_cipher_suite(tls->ctx, csid)) == NULL) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-    }--    /* legacy_compression_method */-    if (src == end || *src++ != 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, 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:-            if (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;-                });-            } else {-                ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                goto Exit;-            }-            break;-        case PTLS_EXTENSION_TYPE_PRE_SHARED_KEY:-            if (sh->is_retry_request) {-                ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                goto Exit;-            } else {-                if ((ret = ptls_decode16(&selected_psk_identity, &src, end)) != 0)-                    goto Exit;-            }-            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;-    }--    key_schedule_transform_post_ch1hash(tls->key_schedule);-    ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len);-    ret = send_client_hello(tls, emitter, properties, &sh->retry_request.cookie);--Exit:-    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_one(tls->key_schedule, tls->cipher_suite, 0)) != 0)-            goto Exit;-        return handle_hello_retry_request(tls, emitter, &sh, message, properties);-    }--    if ((ret = key_schedule_select_one(tls->key_schedule, tls->cipher_suite, tls->client.offered_psk && !tls->is_psk_handshake)) !=-        0)-        goto Exit;--    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;-    }--    ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len);--    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, *esni_nonce = NULL;-    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_ENCRYPTED_SERVER_NAME:-            if (*src == PTLS_ESNI_RESPONSE_TYPE_ACCEPT) {-                if (end - src != PTLS_ESNI_NONCE_SIZE + 1) {-                    ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                    goto Exit;-                }-                esni_nonce = src + 1;-            } else {-                /* TODO: provide API to parse the RETRY REQUEST response */-                ret = PTLS_ERROR_ESNI_RETRY;-                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;-        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->esni != NULL) {-        if (esni_nonce == NULL || !ptls_mem_equal(esni_nonce, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE)) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-        free_esni_secret(&tls->esni, 0);-    } else {-        if (esni_nonce != NULL) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            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);-    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_and_certificate_verify(ptls_t *tls, ptls_message_emitter_t *emitter,-                                                   struct st_ptls_signature_algorithms_t *signature_algorithms,-                                                   ptls_iovec_t context, const char *context_string, 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;-        }-    }--    /* build and send CertificateVerify */-    if (tls->ctx->sign_certificate != NULL) {-        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, &algo, sendbuf,-                                                          ptls_iovec_init(data, datalen), signature_algorithms->list,-                                                          signature_algorithms->count)) != 0) {-                    goto Exit;-                }-                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;--    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);--    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 (num_certs != 0 && tls->ctx->verify_certificate != NULL) {-        if ((ret = tls->ctx->verify_certificate->cb(tls->ctx->verify_certificate, tls, &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);--    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);-    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;-    if (!got_certs)-        return PTLS_ALERT_CERTIFICATE_REQUIRED;--    ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len);--    tls->state = PTLS_STATE_SERVER_EXPECT_CERTIFICATE_VERIFY;-    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);--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 ret;--    if ((ret = verify_finished(tls, message)) != 0)-        goto Exit;-    ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len);--    /* 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 (tls->client.certificate_request.context.base != NULL) {-        /* If this is a resumed session, the server must not send the certificate request in the handshake */-        if (tls->is_psk_handshake) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-        ret = send_certificate_and_certificate_verify(tls, emitter, &tls->client.certificate_request.signature_algorithms,-                                                      tls->client.certificate_request.context,-                                                      PTLS_CLIENT_CERTIFICATE_VERIFY_CONTEXT_STRING, 0, NULL, 0);-        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;--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, {-        if (*src == end) {-            ret = PTLS_ALERT_DECODE_ERROR;-            goto Exit;-        }-        do {-            uint8_t type = *(*src)++;-            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 client_hello_decrypt_esni(ptls_context_t *ctx, ptls_iovec_t *server_name, ptls_esni_secret_t **secret,-                                     struct st_ptls_client_hello_t *ch)-{-    ptls_esni_context_t **esni;-    ptls_key_exchange_context_t **key_share_ctx;-    uint8_t *decrypted = NULL;-    ptls_aead_context_t *aead = NULL;-    int ret;--    /* allocate secret */-    assert(*secret == NULL);-    if ((*secret = malloc(sizeof(**secret))) == NULL)-        return PTLS_ERROR_NO_MEMORY;-    memset(*secret, 0, sizeof(**secret));--    /* find the matching esni structure */-    for (esni = ctx->esni; *esni != NULL; ++esni) {-        size_t i;-        for (i = 0; (*esni)->cipher_suites[i].cipher_suite != NULL; ++i)-            if ((*esni)->cipher_suites[i].cipher_suite->id == ch->esni.cipher->id)-                break;-        if ((*esni)->cipher_suites[i].cipher_suite == NULL) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-        if (memcmp((*esni)->cipher_suites[i].record_digest, ch->esni.record_digest, ch->esni.cipher->hash->digest_size) == 0) {-            (*secret)->version = (*esni)->version;-            break;-        }-    }-    if (*esni == NULL) {-        ret = PTLS_ALERT_ILLEGAL_PARAMETER;-        goto Exit;-    }--    /* find the matching private key for ESNI decryption */-    for (key_share_ctx = (*esni)->key_exchanges; *key_share_ctx != NULL; ++key_share_ctx)-        if ((*key_share_ctx)->algo->id == ch->esni.key_share->id)-            break;-    if (*key_share_ctx == NULL) {-        ret = PTLS_ALERT_ILLEGAL_PARAMETER;-        goto Exit;-    }--    /* calculate ESNIContents */-    if ((ret = build_esni_contents_hash(ch->esni.cipher->hash, (*secret)->esni_contents_hash, ch->esni.record_digest,-                                        ch->esni.key_share->id, ch->esni.peer_key, ch->random_bytes)) != 0)-        goto Exit;-    /* derive the shared secret */-    if ((ret = (*key_share_ctx)->on_exchange(key_share_ctx, 0, &(*secret)->secret, ch->esni.peer_key)) != 0)-        goto Exit;-    /* decrypt */-    if (ch->esni.encrypted_sni.len - ch->esni.cipher->aead->tag_size != (*esni)->padded_length + PTLS_ESNI_NONCE_SIZE) {-        ret = PTLS_ALERT_ILLEGAL_PARAMETER;-        goto Exit;-    }-    if ((decrypted = malloc((*esni)->padded_length + PTLS_ESNI_NONCE_SIZE)) == NULL) {-        ret = PTLS_ERROR_NO_MEMORY;-        goto Exit;-    }-    if ((ret = create_esni_aead(&aead, 0, ch->esni.cipher, (*secret)->secret, (*secret)->esni_contents_hash)) != 0)-        goto Exit;-    if (ptls_aead_decrypt(aead, decrypted, ch->esni.encrypted_sni.base, ch->esni.encrypted_sni.len, 0, ch->key_shares.base,-                          ch->key_shares.len) != (*esni)->padded_length + PTLS_ESNI_NONCE_SIZE) {-        ret = PTLS_ALERT_DECRYPT_ERROR;-        goto Exit;-    }-    ptls_aead_free(aead);-    aead = NULL;--    { /* decode sni */-        const uint8_t *src = decrypted, *const end = src + (*esni)->padded_length;-        ptls_iovec_t found_name;-        if (end - src < PTLS_ESNI_NONCE_SIZE) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-        memcpy((*secret)->nonce, src, PTLS_ESNI_NONCE_SIZE);-        src += PTLS_ESNI_NONCE_SIZE;-        if ((ret = client_hello_decode_server_name(&found_name, &src, end)) != 0)-            goto Exit;-        for (; src != end; ++src) {-            if (*src != '\0') {-                ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                goto Exit;-            }-        }-        /* if successful, reuse memory allocated for padded_server_name for storing the found name (freed by the caller) */-        memmove(decrypted, found_name.base, found_name.len);-        *server_name = ptls_iovec_init(decrypted, found_name.len);-        decrypted = NULL;-    }--    ret = 0;-Exit:-    if (decrypted != NULL)-        free(decrypted);-    if (aead != NULL)-        ptls_aead_free(aead);-    if (ret != 0 && *secret != NULL)-        free_esni_secret(secret, 1);-    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_t *tls, struct st_ptls_client_hello_t *ch, const uint8_t *src, const uint8_t *const end,-                               ptls_handshake_properties_t *properties)-{-    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, {-        ch->cipher_suites = ptls_iovec_init(src, end - src);-        uint16_t *id = ch->client_ciphers.list;-        do {-            if ((ret = ptls_decode16(id, &src, end)) != 0)-                goto Exit;-            id++;-            ch->client_ciphers.count++;-            if (id >= ch->client_ciphers.list + MAX_CLIENT_CIPHERS) {-                src = end;-                break;-            }-        } while (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;-    });--    /* decode extensions */-    decode_extensions(src, end, PTLS_HANDSHAKE_TYPE_CLIENT_HELLO, &exttype, {-        ch->psk.is_last_extension = 0;-        if (tls->ctx->on_extension != NULL &&-            (ret = tls->ctx->on_extension->cb(tls->ctx->on_extension, tls, 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_ENCRYPTED_SERVER_NAME: {-            ptls_cipher_suite_t **cipher;-            if (ch->esni.cipher != NULL) {-                ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                goto Exit;-            }-            { /* cipher-suite */-                uint16_t csid;-                if ((ret = ptls_decode16(&csid, &src, end)) != 0)-                    goto Exit;-                for (cipher = tls->ctx->cipher_suites; *cipher != NULL; ++cipher)-                    if ((*cipher)->id == csid)-                        break;-                if (*cipher == NULL) {-                    ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                    goto Exit;-                }-            }-            /* key-share (including peer-key) */-            if ((ret = select_key_share(&ch->esni.key_share, &ch->esni.peer_key, tls->ctx->key_exchanges, &src, end, 1)) != 0)-                goto Exit;-            ptls_decode_open_block(src, end, 2, {-                size_t len = end - src;-                if (len != (*cipher)->hash->digest_size) {-                    ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                    goto Exit;-                }-                ch->esni.record_digest = src;-                src += len;-            });-            ptls_decode_block(src, end, 2, {-                size_t len = end - src;-                if (len < (*cipher)->aead->tag_size) {-                    ret = PTLS_ALERT_ILLEGAL_PARAMETER;-                    goto Exit;-                }-                ch->esni.encrypted_sni = ptls_iovec_init(src, len);-                src += len;-            });-            ch->esni.cipher = *cipher; /* set only after successful parsing */-        } 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;-                    });-                    if (src == end) {-                        ret = PTLS_ALERT_DECODE_ERROR;-                        goto Exit;-                    }-                    switch (*src++) {-                    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, {-                if (src == end) {-                    ret = PTLS_ALERT_DECODE_ERROR;-                    goto Exit;-                }-                for (; src != end; ++src) {-                    if (*src < sizeof(ch->psk.ke_modes) * 8)-                        ch->psk.ke_modes |= 1u << *src;-                }-            });-            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;-        default:-            if (should_collect_unknown_extension(tls, properties, exttype)) {-                if ((ret = collect_unknown_extension(tls, exttype, src, end, ch->unknown_extensions)) != 0)-                    goto Exit;-            }-            break;-        }-        src = end;-    });--    ret = 0;-Exit:-    return ret;-}--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);-    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)                                                                            \-    ptls_push_message(emitter, (sched), PTLS_HANDSHAKE_TYPE_SERVER_HELLO, {                                                        \-        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);                                                                                                           \-        });                                                                                                                        \-    });--#define EMIT_HELLO_RETRY_REQUEST(sched, negotiated_group, additional_extensions)                                                   \-    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);                                                                                             \-                      })-    struct st_ptls_client_hello_t *ch;-    struct {-        ptls_key_exchange_algorithm_t *algorithm;-        ptls_iovec_t peer_key;-    } key_share = {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;--    if ((ch = malloc(sizeof(*ch))) == NULL) {-        ret = PTLS_ERROR_NO_MEMORY;-        goto Exit;-    }--    *ch = (struct st_ptls_client_hello_t){0,      NULL,   {NULL},     {NULL}, 0,     {NULL},   {NULL}, {NULL}, {{0}},-                                          {NULL}, {NULL}, {{{NULL}}}, {{0}},  {{0}}, {{NULL}}, {NULL}, {{0}},  {{UINT16_MAX}}};--    /* decode ClientHello */-    if ((ret = decode_client_hello(tls, ch, message.base + PTLS_HANDSHAKE_HEADER_SIZE, message.base + message.len, properties)) !=-        0)-        goto Exit;--    /* bail out if CH cannot be handled as TLS 1.3, providing the application the raw CH and SNI, to help them fallback */-    if (!is_supported_version(ch->selected_version)) {-        if (!is_second_flight && tls->ctx->on_client_hello != NULL) {-            ptls_on_client_hello_parameters_t params = {-                .server_name = ch->server_name,-                .raw_message = message,-                .negotiated_protocols = {ch->alpn.list, ch->alpn.count},-                .incompatible_version = 1,-            };-            if ((ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, &params)) != 0)-                goto Exit;-        }-        ret = PTLS_ALERT_PROTOCOL_VERSION;-        goto Exit;-    }--    /* 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. */-        ret = PTLS_ALERT_PROTOCOL_VERSION;-        goto Exit;-    }-    if (!(ch->compression_methods.count == 1 && ch->compression_methods.ids[0] == 0)) {-        ret = PTLS_ALERT_ILLEGAL_PARAMETER;-        goto Exit;-    }-    /* esni */-    if (ch->esni.cipher != NULL) {-        if (ch->key_shares.base == NULL) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-    }-    /* pre-shared key */-    if (ch->psk.hash_end != NULL) {-        /* PSK must be the last extension */-        if (!ch->psk.is_last_extension) {-            ret = PTLS_ALERT_ILLEGAL_PARAMETER;-            goto Exit;-        }-    } else {-        if (ch->psk.early_data_indication) {-            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) {-        memcpy(tls->client_random, ch->random_bytes, sizeof(tls->client_random));-        log_client_random(tls);-        if (ch->legacy_session_id.len != 0)-            tls->send_change_cipher_spec = 1;-        ptls_iovec_t server_name = {NULL};-        int is_esni = 0;-        if (ch->esni.cipher != NULL && tls->ctx->esni != NULL) {-            if ((ret = client_hello_decrypt_esni(tls->ctx, &server_name, &tls->esni, ch)) != 0)-                goto Exit;-            if (tls->ctx->update_esni_key != NULL) {-                if ((ret = tls->ctx->update_esni_key->cb(tls->ctx->update_esni_key, tls, tls->esni->secret, ch->esni.cipher->hash,-                                                         tls->esni->esni_contents_hash)) != 0)-                    goto Exit;-            }-            is_esni = 1;-        } else if (ch->server_name.base != NULL) {-            server_name = ch->server_name;-        }-        if (tls->ctx->on_client_hello != NULL) {-            ptls_on_client_hello_parameters_t params = {server_name,-                                                        message,-                                                        {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->client_ciphers.list, ch->client_ciphers.count},-                                                        {ch->server_certificate_types.list, ch->server_certificate_types.count},-                                                        is_esni};-            ret = tls->ctx->on_client_hello->cb(tls->ctx->on_client_hello, tls, &params);-        } else {-            ret = 0;-        }--        if (is_esni)-            free(server_name.base);-        if (ret != 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;-        }-        /* 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 (!ptls_mem_equal(tls->client_random, ch->random_bytes, sizeof(tls->client_random))) {-            ret = PTLS_ALERT_HANDSHAKE_FAILURE;-            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)) != 0)-            goto Exit;-        if (!is_second_flight) {-            tls->cipher_suite = cs;-            tls->key_schedule = key_schedule_new(cs, NULL, tls->ctx->hkdf_label_prefix__obsolete);-        } 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 */-            size_t sigsize = tls->ctx->cipher_suites[0]->hash->digest_size;-            uint8_t *sig = alloca(sigsize);-            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);-            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);-            assert(tls->key_schedule->generation == 0);-            if (properties != NULL && properties->server.retry_uses_cookie) {-                /* emit HRR with cookie (note: we MUST omit KeyShare if the client has specified the correct one; see 46554f0)-                 */-                EMIT_HELLO_RETRY_REQUEST(NULL, key_share.algorithm != NULL ? NULL : negotiated_group, {-                    ptls_buffer_t *sendbuf = emitter->buf;-                    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 ((ret = push_change_cipher_spec(tls, emitter)) != 0)-                    goto Exit;-                ret = PTLS_ERROR_STATELESS_RETRY;-            } else {-                /* invoking stateful retry; roll the key schedule and emit HRR */-                key_schedule_transform_post_ch1hash(tls->key_schedule);-                key_schedule_extract(tls->key_schedule, ptls_iovec_init(NULL, 0));-                EMIT_HELLO_RETRY_REQUEST(tls->key_schedule, key_share.algorithm != NULL ? NULL : negotiated_group, {});-                if ((ret = push_change_cipher_spec(tls, emitter)) != 0)-                    goto Exit;-                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);-        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);-        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;-        }-    }--    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 */-    EMIT_SERVER_HELLO(-        tls->key_schedule, { tls->ctx->random_bytes(emitter->buf->base + emitter->buf->off, PTLS_HELLO_RANDOM_SIZE); },-        {-            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 ((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->esni != NULL) {-                /* the extension is sent even if the application does not handle server name, because otherwise the handshake-                 * would fail (FIXME ch->esni.nonce will be zero on HRR) */-                buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_ENCRYPTED_SERVER_NAME, {-                    uint8_t response_type = PTLS_ESNI_RESPONSE_TYPE_ACCEPT;-                    ptls_buffer_pushv(sendbuf, &response_type, 1);-                    ptls_buffer_pushv(sendbuf, tls->esni->nonce, PTLS_ESNI_NONCE_SIZE);-                });-                free_esni_secret(&tls->esni, 1);-            } else 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, {});-            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;-            }-        }--        ret = send_certificate_and_certificate_verify(tls, emitter, &ch->signature_algorithms, ptls_iovec_init(NULL, 0),-                                                      PTLS_SERVER_CERTIFICATE_VERIFY_CONTEXT_STRING, ch->status_request,-                                                      ch->cert_compression_algos.list, ch->cert_compression_algos.count);--        if (ret != 0) {-            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 (ch->psk.ke_modes != 0 && 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:-    free(pubkey.base);-    if (ecdh_secret.base != NULL) {-        ptls_clear_memory(ecdh_secret.base, ecdh_secret.len);-        free(ecdh_secret.base);-    }-    free(ch);-    return ret;--#undef EMIT_SERVER_HELLO-#undef EMIT_HELLO_RETRY_REQUEST-}--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);-    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);--    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 = hkdf_expand_label(hash, secret, hash->digest_size, ptls_iovec_init(tp->secret, hash->digest_size), "traffic upd",-                                 ptls_iovec_init(NULL, 0), tls->key_schedule->hkdf_label_prefix)) != 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;--    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);-    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);-    return tls;-}--void ptls_free(ptls_t *tls)-{-    PTLS_PROBE0(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->esni != NULL)-        free_esni_secret(&tls->esni, tls->is_server);-    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);-    if (tls->is_server) {-        /* nothing to do */-    } 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_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;-}--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) {-        if (server_name_len == 0)-            server_name_len = strlen(server_name);-        if ((duped = malloc(server_name_len + 1)) == NULL)-            return PTLS_ERROR_NO_MEMORY;-        memcpy(duped, server_name, server_name_len);-        duped[server_name_len] = '\0';-    }--    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) {-        if (protocol_len == 0)-            protocol_len = strlen(protocol);-        if ((duped = malloc(protocol_len + 1)) == NULL)-            return PTLS_ERROR_NO_MEMORY;-        memcpy(duped, protocol, protocol_len);-        duped[protocol_len] = '\0';-    }--    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;-}--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);--    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);--    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_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 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);-    }-    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:-        break;-    default:-        /* flush partially written response */-        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;-        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)-{-    return 6 + tls->traffic_protection.enc.aead->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 = 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), tls->key_schedule->hkdf_label_prefix)) !=-        0)-        goto Exit;-    ret = hkdf_expand_label(algo, output, outlen, ptls_iovec_init(derived_secret, algo->digest_size), "exporter",-                            ptls_iovec_init(context_value_hash, algo->digest_size), tls->key_schedule->hkdf_label_prefix);--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 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;--    assert(label_prefix != NULL);--    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, {-        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_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 handshake layer should call hkdf_expand_label directly, always setting key_schedule->hkdf_label_prefix as the-     * argument */-    if (label_prefix == NULL)-        label_prefix = PTLS_HKDF_EXPAND_LABEL_PREFIX;-    return hkdf_expand_label(algo, output, outlen, secret, label, hash_value, label_prefix);-}--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;-    uint8_t key_iv[PTLS_MAX_SECRET_SIZE + PTLS_MAX_IV_SIZE];-    int ret;--    if ((ret = get_traffic_key(hash, key_iv, aead->key_size, 0, secret, hash_value, label_prefix)) != 0)-        goto Exit;-    if ((ret = get_traffic_key(hash, key_iv + aead->key_size, aead->iv_size, 1, secret, hash_value, label_prefix)) != 0)-        goto Exit;-    ctx = ptls_aead_new_direct(aead, is_enc, key_iv, key_iv + aead->key_size);--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__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)-{-#if 0-    struct timeval tv;-    gettimeofday(&tv, NULL);-    return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;-#else-    return 0;-#endif-}--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_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};--    assert(input);--    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);-}--int ptls_esni_init_context(ptls_context_t *ctx, ptls_esni_context_t *esni, ptls_iovec_t esni_keys,-                           ptls_key_exchange_context_t **key_exchanges)-{-    const uint8_t *src = esni_keys.base, *const end = src + esni_keys.len;-    size_t num_key_exchanges, num_cipher_suites = 0;-    int ret;--    for (num_key_exchanges = 0; key_exchanges[num_key_exchanges] != NULL; ++num_key_exchanges)-        ;--    memset(esni, 0, sizeof(*esni));-    if ((esni->key_exchanges = malloc(sizeof(*esni->key_exchanges) * (num_key_exchanges + 1))) == NULL) {-        ret = PTLS_ERROR_NO_MEMORY;-        goto Exit;-    }-    memcpy(esni->key_exchanges, key_exchanges, sizeof(*esni->key_exchanges) * (num_key_exchanges + 1));--    /* ESNIKeys */-    if ((ret = ptls_decode16(&esni->version, &src, end)) != 0)-        goto Exit;-    /* Skip checksum fields */-    if (end - src < 4) {-        ret = PTLS_ALERT_DECRYPT_ERROR;-        goto Exit;-    }-    src += 4;-    /* Published SNI field */-    ptls_decode_open_block(src, end, 2, { src = end; });--    /* Process the list of KeyShareEntries, verify for each of them that the ciphersuite is supported. */-    ptls_decode_open_block(src, end, 2, {-        do {-            /* parse */-            uint16_t id;-            if ((ret = ptls_decode16(&id, &src, end)) != 0)-                goto Exit;-            ptls_decode_open_block(src, end, 2, { src = end; });-            /* check that matching key-share exists */-            ptls_key_exchange_context_t **found;-            for (found = key_exchanges; *found != NULL; ++found)-                if ((*found)->algo->id == id)-                    break;-            if (found == NULL) {-                ret = PTLS_ERROR_INCOMPATIBLE_KEY;-                goto Exit;-            }-        } while (src != end);-    });-    /* Process the list of cipher_suites. If they are supported, store in esni context  */-    ptls_decode_open_block(src, end, 2, {-        void *newp;-        do {-            uint16_t id;-            if ((ret = ptls_decode16(&id, &src, end)) != 0)-                goto Exit;-            size_t i;-            for (i = 0; ctx->cipher_suites[i] != NULL; ++i)-                if (ctx->cipher_suites[i]->id == id)-                    break;-            if (ctx->cipher_suites[i] != NULL) {-                if ((newp = realloc(esni->cipher_suites, sizeof(*esni->cipher_suites) * (num_cipher_suites + 1))) == NULL) {-                    ret = PTLS_ERROR_NO_MEMORY;-                    goto Exit;-                }-                esni->cipher_suites = newp;-                esni->cipher_suites[num_cipher_suites++].cipher_suite = ctx->cipher_suites[i];-            }-        } while (src != end);-        if ((newp = realloc(esni->cipher_suites, sizeof(*esni->cipher_suites) * (num_cipher_suites + 1))) == NULL) {-            ret = PTLS_ERROR_NO_MEMORY;-            goto Exit;-        }-        esni->cipher_suites = newp;-        esni->cipher_suites[num_cipher_suites].cipher_suite = NULL;-    });-    /* Parse the padded length, not before, not after parameters */-    if ((ret = ptls_decode16(&esni->padded_length, &src, end)) != 0)-        goto Exit;-    if ((ret = ptls_decode64(&esni->not_before, &src, end)) != 0)-        goto Exit;-    if ((ret = ptls_decode64(&esni->not_after, &src, end)) != 0)-        goto Exit;-    /* Skip the extension fields */-    ptls_decode_block(src, end, 2, {-        while (src != end) {-            uint16_t ext_type;-            if ((ret = ptls_decode16(&ext_type, &src, end)) != 0)-                goto Exit;-            ptls_decode_open_block(src, end, 2, { src = end; });-        }-    });--    { /* calculate digests for every cipher-suite */-        size_t i;-        for (i = 0; esni->cipher_suites[i].cipher_suite != NULL; ++i) {-            if ((ret = ptls_calc_hash(esni->cipher_suites[i].cipher_suite->hash, esni->cipher_suites[i].record_digest,-                                      esni_keys.base, esni_keys.len)) != 0)-                goto Exit;-        }-    }--    ret = 0;-Exit:-    if (ret != 0)-        ptls_esni_dispose_context(esni);-    return ret;-}--void ptls_esni_dispose_context(ptls_esni_context_t *esni)-{-    size_t i;--    if (esni->key_exchanges != NULL) {-        for (i = 0; esni->key_exchanges[i] != NULL; ++i)-            esni->key_exchanges[i]->on_exchange(esni->key_exchanges + i, 1, NULL, ptls_iovec_init(NULL, 0));-        free(esni->key_exchanges);-    }-    free(esni->cipher_suites);-}--/**- * Obtain the ESNI secrets negotiated during the handshake.- */-ptls_esni_secret_t *ptls_get_esni_secret(ptls_t *ctx)-{-    return ctx->esni;-}--/**- * 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;-}--char *ptls_hexdump(char *buf, const void *_src, size_t len)-{-    char *dst = buf;-    const uint8_t *src = _src;-    size_t i;--    for (i = 0; i != len; ++i) {-        *dst++ = "0123456789abcdef"[src[i] >> 4];-        *dst++ = "0123456789abcdef"[src[i] & 0xf];-    }-    *dst++ = '\0';-    return buf;+#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, &params);+}++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 view
@@ -64,6 +64,7 @@ #define PTLS_THREADLOCAL __declspec(thread) #else #define PTLS_THREADLOCAL __thread+#define PTLS_HAVE_LOG 1 #endif  #ifndef PTLS_FUZZ_HANDSHAKE@@ -84,7 +85,7 @@ #define PTLS_AESCCM_INTEGRITY_LIMIT 0xB504F3                   /* 2^23.5 */  #define PTLS_CHACHA20_KEY_SIZE 32-#define PTLS_CHACHA20_IV_SIZE 16+#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 */@@ -99,10 +100,17 @@ #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"@@ -111,9 +119,23 @@ #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 "scep256r1"+#define PTLS_GROUP_NAME_SECP256R1 "secp256r1" #define PTLS_GROUP_SECP384R1 24 #define PTLS_GROUP_NAME_SECP384R1 "secp384r1" #define PTLS_GROUP_SECP521R1 25@@ -134,11 +156,20 @@ #define PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512 0x0806 #define PTLS_SIGNATURE_ED25519 0x0807 -/* ESNI */-#define PTLS_ESNI_VERSION_DRAFT03 0xff02--#define PTLS_ESNI_RESPONSE_TYPE_ACCEPT 0-#define PTLS_ESNI_RESPONSE_TYPE_RETRY_REQUEST 1+/* 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@@ -168,16 +199,27 @@ #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)@@ -187,9 +229,9 @@ #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_ESNI_RETRY (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)@@ -219,6 +261,7 @@ #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@@ -236,6 +279,17 @@             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;@@ -255,7 +309,8 @@     uint8_t *base;     size_t capacity;     size_t off;-    int is_allocated;+    uint8_t is_allocated; /* boolean */+    uint8_t align_bits;   /* if particular alignment is required, set to log2(alignment); otherwize zero */ } ptls_buffer_t;  /**@@ -336,17 +391,22 @@ /**  * 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_xor_iv)(struct st_ptls_aead_context_t *ctx, const void *bytes, size_t len);+    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;@@ -388,6 +448,21 @@      */     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.      */@@ -439,6 +514,10 @@  */ typedef const struct st_ptls_hash_algorithm_t {     /**+     * name of the hash algorithm+     */+    const char *name;+    /**      * block size      */     size_t block_size;@@ -486,39 +565,25 @@ } ptls_message_emitter_t;  /**- * holds ESNIKeys and the private key (instantiated by ptls_esni_parse, freed using ptls_esni_dispose)- */-typedef struct st_ptls_esni_context_t {-    ptls_key_exchange_context_t **key_exchanges;-    struct {-        ptls_cipher_suite_t *cipher_suite;-        uint8_t record_digest[PTLS_MAX_DIGEST_SIZE];-    } * cipher_suites;-    uint16_t padded_length;-    uint64_t not_before;-    uint64_t not_after;-    uint16_t version;-} ptls_esni_context_t;--/**- * holds the ESNI secret, as exchanged during the handshake+ * 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; -#define PTLS_ESNI_NONCE_SIZE 16+typedef struct st_ptls_hpke_cipher_suite_id_t {+    uint16_t kdf;+    uint16_t aead;+} ptls_hpke_cipher_suite_id_t; -typedef struct st_ptls_esni_secret_t {-    ptls_iovec_t secret;-    uint8_t nonce[PTLS_ESNI_NONCE_SIZE];-    uint8_t esni_contents_hash[PTLS_MAX_DIGEST_SIZE];-    struct {-        ptls_key_exchange_algorithm_t *key_share;-        ptls_cipher_suite_t *cipher;-        ptls_iovec_t pubkey;-        uint8_t record_digest[PTLS_MAX_DIGEST_SIZE];-        uint16_t padded_length;-    } client;-    uint16_t version;-} ptls_esni_secret_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 {                                                                                            \@@ -543,6 +608,10 @@      */     ptls_iovec_t raw_message;     /**+     * points to the cipher-suites section of the raw_message (see above)+     */+    ptls_iovec_t cipher_suites;+    /**      *      */     struct {@@ -558,18 +627,10 @@         size_t count;     } certificate_compression_algorithms;     struct {-        const uint16_t *list;-        size_t count;-    } cipher_suites;-    struct {         const uint8_t *list;         size_t count;     } server_certificate_types;     /**-     * if ESNI was used-     */-    unsigned esni : 1;-    /**      * set to 1 if ClientHello is too old (or too new) to be handled by picotls      */     unsigned incompatible_version : 1;@@ -590,19 +651,41 @@ 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); /**- * when gerenating CertificateVerify, the core calls the callback to sign the handshake context using the certificate.+ * 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.  */-PTLS_CALLBACK_TYPE(int, sign_certificate, ptls_t *tls, uint16_t *selected_algorithm, ptls_buffer_t *output, ptls_iovec_t input,-                   const uint16_t *algorithms, size_t num_algorithms);+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,+    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);     /**@@ -656,10 +739,12 @@               ptls_iovec_t input); } ptls_decompress_certificate_t; /**- * provides access to the ESNI shared secret (Zx).  API is subject to change.+ * 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(int, update_esni_key, ptls_t *tls, ptls_iovec_t secret, ptls_hash_algorithm_t *hash,-                   const void *hashed_esni_contents);+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@@ -689,9 +774,30 @@         size_t count;     } certificates;     /**-     * list of ESNI data terminated by NULL+     * ECH      */-    ptls_esni_context_t **esni;+    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;     /**      *      */@@ -721,8 +827,7 @@      */     size_t max_buffer_size;     /**-     * the field is obsolete; should be set to NULL for QUIC draft-17.  Note also that even though everybody did, it was incorrect-     * to set the value to "quic " in the earlier versions of the draft.+     * this field is obsolete and ignored      */     const char *hkdf_label_prefix__obsolete;     /**@@ -765,6 +870,11 @@      */     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;@@ -791,11 +901,11 @@     /**      *      */-    ptls_update_esni_key_t *update_esni_key;+    ptls_on_extension_t *on_extension;     /**-     *+     * (optional) list of supported tls12 cipher-suites terminated by NULL      */-    ptls_on_extension_t *on_extension;+    ptls_cipher_suite_t **tls12_cipher_suites; };  typedef struct st_ptls_raw_extension_t {@@ -848,9 +958,19 @@              */             unsigned negotiate_before_key_exchange : 1;             /**-             * ESNIKeys (the value of the TXT record, after being base64-"decoded")+             * ECH              */-            ptls_iovec_t esni_keys;+            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 {             /**@@ -925,6 +1045,10 @@  */ 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);@@ -946,6 +1070,9 @@ 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)                                                               \@@ -1039,7 +1166,7 @@         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);                           \+            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)                                                                         \@@ -1052,6 +1179,7 @@             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);@@ -1112,7 +1240,166 @@         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);@@ -1121,10 +1408,20 @@  */ ptls_t *ptls_server_new(ptls_context_t *ctx); /**- * creates a object handle new TLS connection+ * 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);@@ -1137,6 +1434,10 @@  */ 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);@@ -1145,6 +1446,21 @@  */ 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);@@ -1173,6 +1489,10 @@  */ 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);@@ -1248,6 +1568,11 @@ 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);@@ -1290,26 +1615,35 @@  * Permutes the static IV by applying given bytes using bit-wise XOR. This API can be used for supplying nonces longer than 64-  * bits.  */-static void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len);+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); /**- * initializes the internal state of the encryptor+ * 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); /**- * encrypts the input and updates the GCM state- * @return number of bytes emitted to output+ * 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); /**- * emits buffered data (if any) and the GCM tag- * @return number of bytes emitted to output+ * Obsolete; see `ptls_aead_encrypt_init`.  */ static size_t ptls_aead_encrypt_final(ptls_aead_context_t *ctx, void *output); /**@@ -1319,7 +1653,7 @@ 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.+ * 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); /**@@ -1355,9 +1689,14 @@ 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);+void ptls__key_schedule_update_hash(ptls_key_schedule_t *sched, const uint8_t *msg, size_t msglen, int use_outer); /**  * clears memory  */@@ -1367,40 +1706,44 @@  */ extern int (*volatile ptls_mem_equal)(const void *x, const void *y, size_t len); /**- *- */-static ptls_iovec_t ptls_iovec_init(const void *p, 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); /**- *- */-int ptls_esni_init_context(ptls_context_t *ctx, ptls_esni_context_t *esni, ptls_iovec_t esni_keys,-                           ptls_key_exchange_context_t **key_exchanges);-/**- *+ * 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.  */-void ptls_esni_dispose_context(ptls_esni_context_t *esni);+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); /**- * Obtain the ESNI secrets negotiated during the handshake.+ * 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.  */-ptls_esni_secret_t *ptls_get_esni_secret(ptls_t *ctx);+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 PICOTLS_USE_DTRACE+#if defined(PICOTLS_USE_DTRACE) && PICOTLS_USE_DTRACE /**  *  */@@ -1411,6 +1754,11 @@  /* 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);@@ -1434,12 +1782,13 @@     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};+    *buf = (ptls_buffer_t){NULL, 0, 0, 0, 0}; }  inline uint8_t *ptls_encode_quicint(uint8_t *p, uint64_t v)@@ -1476,11 +1825,16 @@     ctx->do_transform(ctx, output, input, len); } -inline void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len)+inline void ptls_aead_get_iv(ptls_aead_context_t *ctx, void *iv) {-    ctx->do_xor_iv(ctx, bytes, len);+    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) {@@ -1494,6 +1848,12 @@     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);@@ -1512,15 +1872,25 @@ 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) {-    ctx->do_encrypt_init(ctx, seq, aad, aadlen);-    ctx->do_encrypt_update(ctx, output, input, inlen);-    ctx->do_encrypt_final(ctx, (uint8_t *)output + inlen);+    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,
cbits/picotls/fusion.h view
@@ -28,19 +28,28 @@  #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 {-    __m128i keys[PTLS_FUSION_AES256_ROUNDS + 1];+    union {+        __m128i m128[PTLS_FUSION_AES256_ROUNDS + 1];+        __m256i m256[PTLS_FUSION_AES256_ROUNDS + 1];+    } keys;     unsigned rounds;-} ptls_fusion_aesecb_context_t;+    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);+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); @@ -84,8 +93,15 @@ 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.
quic.cabal view
@@ -1,5 +1,5 @@ name:                quic-version:             0.1.0+version:             0.1.1 synopsis:            QUIC description:         Library for QUIC: A UDP-Based Multiplexed and Secure Transport license:             BSD3@@ -26,6 +26,11 @@   Description:          Development commands   Default:              False +Flag fusion+  Description:          Use fusion AES-GCM engine from picotls+  Default:              True+  Manual:               True+ ----------------------------------------------------------------  library@@ -147,9 +152,9 @@   default-extensions:  Strict StrictData   if os(windows)     cc-options:        -D_WINDOWS-  if arch(x86_64)+  if flag(fusion) && arch(x86_64)     cpp-options:       -DUSE_FUSION-    cc-options:        -mavx2 -maes -mpclmul+    cc-options:        -mavx2 -maes -mpclmul -mvaes -mvpclmulqdq     c-sources:         cbits/fusion.c cbits/picotls.c  test-suite spec@@ -186,19 +191,6 @@                       , unliftio   default-extensions:  Strict StrictData   build-tool-depends: hspec-discover:hspec-discover---test-suite doctests-  type:                 exitcode-stdio-1.0-  default-language:     Haskell2010-  hs-source-dirs:       test-  main-is:              doctests.hs--  build-depends:        base >= 4.9 && < 5-                      , doctest >= 0.10.1--  ghc-options: -Wall-  default-extensions:  Strict StrictData  executable server   if flag(devel)
test/Config.hs view
@@ -38,8 +38,7 @@ testServerConfig :: ServerConfig testServerConfig = defaultServerConfig {     -- Don't use "0.0.0.0" and "::" for Windows (UDP dispatching bug)-    -- Don't use "127.0.0.1" for macOS (recvmsg bug)-    scAddresses = [("::1",50003)]+    scAddresses = [("127.0.0.1",50003)]   }  makeTestServerConfigR :: IO ServerConfig@@ -54,13 +53,12 @@ testServerConfigR :: ServerConfig testServerConfigR = defaultServerConfig {     -- Don't use "0.0.0.0" and "::" for Windows (UDP dispatching bug)-    -- Don't use "127.0.0.1" for macOS (recvmsg bug)-    scAddresses = [("::1",50003)]+    scAddresses = [("127.0.0.1",50003)]   }  testClientConfig :: ClientConfig testClientConfig = defaultClientConfig {-    ccServerName = "::1"+    ccServerName = "127.0.0.1"   , ccPortName   = "50003"   , ccValidate   = False   , ccDebugLog   = True@@ -68,7 +66,7 @@  testClientConfigR :: ClientConfig testClientConfigR = defaultClientConfig {-    ccServerName = "::1"+    ccServerName = "127.0.0.1"   , ccPortName   = "50002"   , ccValidate   = False   , ccDebugLog   = True@@ -130,10 +128,10 @@   where     hints = defaultHints { addrSocketType = Datagram                          , addrFlags = [AI_NUMERICHOST]-                         , addrFamily = AF_INET6+                         , addrFamily = AF_INET                          }     resolve port =-        head <$> getAddrInfo (Just hints) (Just "::1") (Just port)+        head <$> getAddrInfo (Just hints) (Just "127.0.0.1") (Just port)     shouldDrop (Randomly n) _ _ = do         w <- getRandomOneByte         return ((w `mod` fromIntegral n) == 0)
test/HandshakeSpec.hs view
@@ -84,10 +84,10 @@ testHandshake :: ClientConfig -> ServerConfig -> HandshakeMode13 -> IO () testHandshake cc sc mode = do     concurrently_ server client-    threadDelay 10000+    threadDelay 50000   where     client = do-        threadDelay 10000+        threadDelay 50000         C.run cc $ \conn -> do             waitEstablished conn             handshakeMode <$> getConnectionInfo conn `shouldReturn` mode@@ -107,17 +107,17 @@ testHandshake2 :: ClientConfig -> ServerConfig -> (HandshakeMode13, HandshakeMode13) -> Bool -> IO () testHandshake2 cc1 sc (mode1, mode2) use0RTT = do     concurrently_ server client-    threadDelay 10000+    threadDelay 50000   where     runClient cc mode action = C.run cc $ \conn -> do         void $ action conn         handshakeMode <$> getConnectionInfo conn `shouldReturn` mode-        threadDelay 10000+        threadDelay 50000         getResumptionInfo conn     client = do         threadDelay 10000         res <- runClient cc1 mode1 $ query "first"-        threadDelay 10000+        threadDelay 50000         let cc2 = cc1 { ccResumption = res                       , ccUse0RTT    = use0RTT                       }@@ -134,10 +134,10 @@ testHandshake3 :: ClientConfig -> ClientConfig -> ServerConfig -> (QUICException -> Bool) -> IO () testHandshake3 cc1 cc2 sc selector = do     concurrently_ server client-    threadDelay 10000+    threadDelay 50000   where     client = do-        threadDelay 10000+        threadDelay 50000         C.run cc1 (query "first")  `shouldThrow` selector         C.run cc2 (query "second") `shouldReturn` ()     server = S.run sc $ \conn -> do
test/IOSpec.hs view
@@ -69,7 +69,76 @@             withPipe (DropClientPacket [10]) $ testSendRecv cc sc 20         it "can exchange data on client 11" $ do             withPipe (DropClientPacket [11]) $ testSendRecv cc sc 20+    describe "recvStream" $ do+        it "don't block if client stop sending first" $ do+            withPipe (Randomly 20) $ testRecvStreamClientStopFirst cc sc+        it "don't block if server stop sending first" $ do+            withPipe (Randomly 20) $ testRecvStreamServerStopFirst cc sc +consumeBytes :: Stream -> Int -> IO ()+consumeBytes _ 0 = return ()+consumeBytes strm left = do+    bs <- recvStream strm 1024+    when (BS.null bs) $ expectationFailure "no enough bytes received"+    let len = BS.length bs+    when (len > left) $ expectationFailure "extra bytes received"+    consumeBytes strm (left - len)++assertEndOfStream :: Stream -> IO ()+assertEndOfStream strm = recvStream strm 1024 `shouldReturn` ""++testRecvStreamClientStopFirst :: C.ClientConfig -> ServerConfig -> IO ()+testRecvStreamClientStopFirst cc sc = do+    mvar <- newEmptyMVar+    void $ concurrently (client mvar) (server mvar)+    threadDelay 10000+  where+    aerr = ApplicationProtocolError 0++    client mvar = do+        threadDelay 10000+        C.run cc $ \conn -> do+            strm <- stream conn+            sendStream strm (BS.replicate 10000 0)+            takeMVar mvar `shouldReturn` ()+            stopStream strm aerr+            resetStream strm aerr+            takeMVar mvar `shouldReturn` ()+    server mvar = run sc $ \conn -> do+        strm <- acceptStream conn+        consumeBytes strm 10000 `shouldReturn` ()+        -- notify client to stop stream after all bytes are received.+        putMVar mvar ()+        -- verify that client has stopped sending.+        assertEndOfStream strm+        putMVar mvar ()+        stop conn++testRecvStreamServerStopFirst :: C.ClientConfig -> ServerConfig -> IO ()+testRecvStreamServerStopFirst cc sc = do+    mvar <- newEmptyMVar+    void $ concurrently (client mvar) (server mvar)+    threadDelay 10000+  where+    aerr = ApplicationProtocolError 0++    client mvar = do+        threadDelay 10000+        C.run cc $ \conn -> do+            strm <- stream conn+            sendStream strm (BS.replicate 10000 0)+            takeMVar mvar `shouldReturn` ()+    server mvar = run sc $ \conn -> do+        strm <- acceptStream conn+        consumeBytes strm 10000 `shouldReturn` ()+        -- ask client to stop sending.+        stopStream strm aerr+        -- verify that client has stopped sending.+        assertEndOfStream strm+        resetStream strm aerr+        putMVar mvar ()+        stop conn+ testSendRecv :: C.ClientConfig -> ServerConfig -> Int -> IO () testSendRecv cc sc times = do     mvar <- newEmptyMVar@@ -86,16 +155,7 @@             takeMVar mvar `shouldReturn` ()     server mvar = run sc $ \conn -> do         strm <- acceptStream conn-        bs <- recvStream strm 1024-        let len = BS.length bs-        n <- loop strm bs len-        n `shouldBe` (10000 * times)+        consumeBytes strm (10000 * times)+        assertEndOfStream strm         putMVar mvar ()         stop conn-      where-        loop _    "" n = return n-        loop strm _  n = do-            bs <- recvStream strm 1024-            let len = BS.length bs-                n' = n + len-            loop strm bs n'
− test/doctests.hs
@@ -1,4 +0,0 @@-import Test.DocTest--main :: IO ()-main = doctest ["Network.QUIC.Client","Network.QUIC.Server"]