Merge branch 'master' into mbed-tls-sign-certificate
diff --git a/include/picotls.h b/include/picotls.h
index 3b7a96d..218f682 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -130,8 +130,8 @@
 #define PTLS_CIPHER_SUITE_NAME_AES_256_GCM_SHA384 "TLS_AES_256_GCM_SHA384"
 #define PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256 0x1303
 #define PTLS_CIPHER_SUITE_NAME_CHACHA20_POLY1305_SHA256 "TLS_CHACHA20_POLY1305_SHA256"
-#define PTLS_CIPHER_SUITE_AEGIS256_SHA384 0x1306
-#define PTLS_CIPHER_SUITE_NAME_AEGIS256_SHA384 "TLS_AEGIS_256_SHA384"
+#define PTLS_CIPHER_SUITE_AEGIS256_SHA512 0x1306
+#define PTLS_CIPHER_SUITE_NAME_AEGIS256_SHA512 "TLS_AEGIS_256_SHA512"
 #define PTLS_CIPHER_SUITE_AEGIS128L_SHA256 0x1307
 #define PTLS_CIPHER_SUITE_NAME_AEGIS128L_SHA256 "TLS_AEGIS_128L_SHA256"
 
@@ -248,6 +248,7 @@
 #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_BLOCK_OVERFLOW (PTLS_ERROR_CLASS_INTERNAL + 12)
 
 #define PTLS_ERROR_INCORRECT_BASE64 (PTLS_ERROR_CLASS_INTERNAL + 50)
 #define PTLS_ERROR_PEM_LABEL_NOT_FOUND (PTLS_ERROR_CLASS_INTERNAL + 51)
@@ -919,8 +920,7 @@
      */
     unsigned send_change_cipher_spec : 1;
     /**
-     * if set, the server requests client certificates
-     * to authenticate the client.
+     * if set, the server requests client certificates to authenticate the client
      */
     unsigned require_client_authentication : 1;
     /**
@@ -981,6 +981,21 @@
      * (optional) list of supported tls12 cipher-suites terminated by NULL
      */
     ptls_cipher_suite_t **tls12_cipher_suites;
+    /**
+     * (optional) session ID Context to segment resumption
+     */
+    struct {
+        uint8_t bytes[PTLS_SHA256_DIGEST_SIZE];
+        unsigned is_set : 1;
+    } ticket_context;
+    /**
+     * (optional) list of CAs advertised to clients as supported in the CertificateRequest message; each item must be DNs in DER
+     * format. The values are sent to the client only when `ptls_context_t::require_client_authentication` is set to true.
+     */
+    struct {
+        const ptls_iovec_t *list;
+        size_t count;
+    } client_ca_names;
 };
 
 typedef struct st_ptls_raw_extension_t {
@@ -1203,6 +1218,10 @@
         } while (0);                                                                                                               \
         size_t body_size = (buf)->off - body_start;                                                                                \
         if (capacity != -1) {                                                                                                      \
+            if (capacity < sizeof(size_t) && body_size >= (size_t)1 << (capacity * 8)) {                                           \
+                ret = PTLS_ERROR_BLOCK_OVERFLOW;                                                                                   \
+                goto Exit;                                                                                                         \
+            }                                                                                                                      \
             for (; capacity != 0; --capacity)                                                                                      \
                 (buf)->base[body_start - capacity] = (uint8_t)(body_size >> (8 * (capacity - 1)));                                 \
         } else {                                                                                                                   \
@@ -1569,6 +1588,7 @@
 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
+ * FIXME: this function always return false when the TLS session is exported and imported
  */
 int ptls_is_ech_handshake(ptls_t *tls, uint8_t *config_id, ptls_hpke_kem_t **kem, ptls_hpke_cipher_suite_t **cipher);
 /**
diff --git a/include/picotls/minicrypto.h b/include/picotls/minicrypto.h
index ca3a31d..a6202d0 100644
--- a/include/picotls/minicrypto.h
+++ b/include/picotls/minicrypto.h
@@ -51,11 +51,11 @@
 extern ptls_aead_algorithm_t ptls_minicrypto_aegis128l;
 extern ptls_aead_algorithm_t ptls_minicrypto_aegis256;
 #endif
-extern ptls_hash_algorithm_t ptls_minicrypto_sha256, ptls_minicrypto_sha384;
+extern ptls_hash_algorithm_t ptls_minicrypto_sha256, ptls_minicrypto_sha384, ptls_minicrypto_sha512;
 extern ptls_cipher_suite_t ptls_minicrypto_aes128gcmsha256, ptls_minicrypto_aes256gcmsha384, ptls_minicrypto_chacha20poly1305sha256;
 #ifdef PTLS_HAVE_AEGIS
 extern ptls_cipher_suite_t ptls_minicrypto_aegis128lsha256;
-extern ptls_cipher_suite_t ptls_minicrypto_aegis256sha384;
+extern ptls_cipher_suite_t ptls_minicrypto_aegis256sha512;
 #endif
 extern ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[];
 extern ptls_cipher_suite_t *ptls_minicrypto_cipher_suites_all[];
diff --git a/include/picotls/openssl.h b/include/picotls/openssl.h
index 987b9b1..de777c8 100644
--- a/include/picotls/openssl.h
+++ b/include/picotls/openssl.h
@@ -98,7 +98,7 @@
 extern ptls_aead_algorithm_t ptls_openssl_aegis128l;
 extern ptls_aead_algorithm_t ptls_openssl_aegis256;
 extern ptls_cipher_suite_t ptls_openssl_aegis128lsha256;
-extern ptls_cipher_suite_t ptls_openssl_aegis256sha384;
+extern ptls_cipher_suite_t ptls_openssl_aegis256sha512;
 #endif
 
 extern ptls_cipher_suite_t ptls_openssl_tls12_ecdhe_rsa_aes128gcmsha256;
diff --git a/lib/chacha20poly1305.h b/lib/chacha20poly1305.h
index ad2d7fe..9bdc9cc 100644
--- a/lib/chacha20poly1305.h
+++ b/lib/chacha20poly1305.h
@@ -161,7 +161,7 @@
     struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
 
     memcpy(ctx->static_iv, iv, sizeof(ctx->static_iv));
- }
+}
 
 static int chacha20poly1305_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv,
                                          ptls_cipher_algorithm_t *chacha,
diff --git a/lib/cifra.c b/lib/cifra.c
index d51f2b7..6be752f 100644
--- a/lib/cifra.c
+++ b/lib/cifra.c
@@ -23,24 +23,20 @@
 #include "picotls.h"
 #include "picotls/minicrypto.h"
 
-ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[] = {// ciphers used with sha384 (must be first)
-                                                        &ptls_minicrypto_aes256gcmsha384,
+ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[] = { // ciphers used with sha512 and sha384 (must be first)
+    &ptls_minicrypto_aes256gcmsha384,
 
-                                                        // ciphers used with sha256
-                                                        &ptls_minicrypto_aes128gcmsha256,
-                                                        &ptls_minicrypto_chacha20poly1305sha256,
-                                                        NULL};
+    // ciphers used with sha256
+    &ptls_minicrypto_aes128gcmsha256, &ptls_minicrypto_chacha20poly1305sha256, NULL};
 
-ptls_cipher_suite_t *ptls_minicrypto_cipher_suites_all[] = {// ciphers used with sha384 (must be first)
+ptls_cipher_suite_t *ptls_minicrypto_cipher_suites_all[] = { // ciphers used with sha512 and sha384 (must be first)
 #ifdef PTLS_HAVE_AEGIS
-                                                           &ptls_minicrypto_aegis256sha384,
+    &ptls_minicrypto_aegis256sha512,
 #endif
-                                                           &ptls_minicrypto_aes256gcmsha384,
+    &ptls_minicrypto_aes256gcmsha384,
 
-                                                           // ciphers used with sha256
+// ciphers used with sha256
 #ifdef PTLS_HAVE_AEGIS
-                                                           &ptls_minicrypto_aegis128lsha256,
+    &ptls_minicrypto_aegis128lsha256,
 #endif
-                                                           &ptls_minicrypto_aes128gcmsha256,
-                                                           &ptls_minicrypto_chacha20poly1305sha256,
-                                                           NULL};
+    &ptls_minicrypto_aes128gcmsha256, &ptls_minicrypto_chacha20poly1305sha256, NULL};
diff --git a/lib/cifra/aes256.c b/lib/cifra/aes256.c
index d4cefa9..e074dea 100644
--- a/lib/cifra/aes256.c
+++ b/lib/cifra/aes256.c
@@ -41,6 +41,11 @@
 ptls_hash_algorithm_t ptls_minicrypto_sha384 = {"sha384", PTLS_SHA384_BLOCK_SIZE, PTLS_SHA384_DIGEST_SIZE, sha384_create,
                                                 PTLS_ZERO_DIGEST_SHA384};
 
+ptls_define_hash(sha512, cf_sha512_context, cf_sha512_init, cf_sha512_update, cf_sha512_digest_final);
+
+ptls_hash_algorithm_t ptls_minicrypto_sha512 = {"sha512", PTLS_SHA512_BLOCK_SIZE, PTLS_SHA512_DIGEST_SIZE, sha512_create,
+                                                PTLS_ZERO_DIGEST_SHA512};
+
 ptls_cipher_algorithm_t ptls_minicrypto_aes256ecb = {
     "AES256-ECB",          PTLS_AES256_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct aesecb_context_t),
     aes256ecb_setup_crypto};
diff --git a/lib/cifra/libaegis.c b/lib/cifra/libaegis.c
index 0b4af93..8956ee4 100644
--- a/lib/cifra/libaegis.c
+++ b/lib/cifra/libaegis.c
@@ -20,11 +20,9 @@
  * IN THE SOFTWARE.
  */
 
+#include "picotls/minicrypto.h"
 #include "../libaegis.h"
 
-extern ptls_hash_algorithm_t ptls_minicrypto_sha256;
-extern ptls_hash_algorithm_t ptls_minicrypto_sha384;
-
 ptls_aead_algorithm_t ptls_minicrypto_aegis128l = {"AEGIS-128L",
                                                    PTLS_AEGIS128L_CONFIDENTIALITY_LIMIT,
                                                    PTLS_AEGIS128L_INTEGRITY_LIMIT,
@@ -33,7 +31,7 @@
                                                    PTLS_AEGIS128L_KEY_SIZE,
                                                    PTLS_AEGIS128L_IV_SIZE,
                                                    PTLS_AEGIS128L_TAG_SIZE,
-                                                   { 0, 0 },
+                                                   {0, 0},
                                                    0,
                                                    0,
                                                    sizeof(struct aegis128l_context_t),
@@ -51,12 +49,12 @@
                                                   PTLS_AEGIS256_KEY_SIZE,
                                                   PTLS_AEGIS256_IV_SIZE,
                                                   PTLS_AEGIS256_TAG_SIZE,
-                                                  { 0, 0 },
+                                                  {0, 0},
                                                   0,
                                                   0,
                                                   sizeof(struct aegis256_context_t),
                                                   aegis256_setup_crypto};
-ptls_cipher_suite_t ptls_minicrypto_aegis256sha384 = {.id = PTLS_CIPHER_SUITE_AEGIS256_SHA384,
-                                                      .name = PTLS_CIPHER_SUITE_NAME_AEGIS256_SHA384,
+ptls_cipher_suite_t ptls_minicrypto_aegis256sha512 = {.id = PTLS_CIPHER_SUITE_AEGIS256_SHA512,
+                                                      .name = PTLS_CIPHER_SUITE_NAME_AEGIS256_SHA512,
                                                       .aead = &ptls_minicrypto_aegis256,
-                                                      .hash = &ptls_minicrypto_sha384};
+                                                      .hash = &ptls_minicrypto_sha512};
diff --git a/lib/libaegis.h b/lib/libaegis.h
index ec78a4b..bc82042 100644
--- a/lib/libaegis.h
+++ b/lib/libaegis.h
@@ -63,7 +63,8 @@
     struct aegis128l_context_t *ctx = (struct aegis128l_context_t *)_ctx;
     size_t written;
 
-    aegis128l_state_encrypt_update(&ctx->st, (uint8_t *)output, inlen + aegis128l_TAILBYTES_MAX, &written, (const uint8_t *)input, inlen);
+    aegis128l_state_encrypt_update(&ctx->st, (uint8_t *)output, inlen + aegis128l_TAILBYTES_MAX, &written, (const uint8_t *)input,
+                                   inlen);
 
     return written;
 }
@@ -73,7 +74,8 @@
     struct aegis128l_context_t *ctx = (struct aegis128l_context_t *)_ctx;
     size_t written;
 
-    aegis128l_state_encrypt_final(&ctx->st, (uint8_t *)output, aegis128l_TAILBYTES_MAX + PTLS_AEGIS128L_TAG_SIZE, &written, PTLS_AEGIS128L_TAG_SIZE);
+    aegis128l_state_encrypt_final(&ctx->st, (uint8_t *)output, aegis128l_TAILBYTES_MAX + PTLS_AEGIS128L_TAG_SIZE, &written,
+                                  PTLS_AEGIS128L_TAG_SIZE);
 
     return written;
 }
@@ -177,7 +179,8 @@
     struct aegis256_context_t *ctx = (struct aegis256_context_t *)_ctx;
     size_t written;
 
-    aegis256_state_encrypt_update(&ctx->st, (uint8_t *)output, inlen + aegis256_TAILBYTES_MAX, &written, (const uint8_t *)input, inlen);
+    aegis256_state_encrypt_update(&ctx->st, (uint8_t *)output, inlen + aegis256_TAILBYTES_MAX, &written, (const uint8_t *)input,
+                                  inlen);
 
     return written;
 }
@@ -187,7 +190,8 @@
     struct aegis256_context_t *ctx = (struct aegis256_context_t *)_ctx;
     size_t written;
 
-    aegis256_state_encrypt_final(&ctx->st, (uint8_t *)output, aegis256_TAILBYTES_MAX + PTLS_AEGIS256_TAG_SIZE, &written, PTLS_AEGIS256_TAG_SIZE);
+    aegis256_state_encrypt_final(&ctx->st, (uint8_t *)output, aegis256_TAILBYTES_MAX + PTLS_AEGIS256_TAG_SIZE, &written,
+                                 PTLS_AEGIS256_TAG_SIZE);
 
     return written;
 }
diff --git a/lib/mbedtls_sign.c b/lib/mbedtls_sign.c
index 750dad5..c774cb2 100644
--- a/lib/mbedtls_sign.c
+++ b/lib/mbedtls_sign.c
@@ -20,6 +20,7 @@
 * IN THE SOFTWARE.
 */
 
+
 #ifdef _WINDOWS
 #include "wincompat.h"
 #endif
@@ -36,6 +37,8 @@
 #include <psa/crypto.h>
 #include <psa/crypto_struct.h>
 #include <psa/crypto_values.h>
+#include "ptls_mbedtls.h"
+
 
 typedef struct st_ptls_mbedtls_signature_scheme_t {
     uint16_t scheme_id;
@@ -58,6 +61,7 @@
     {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, PSA_ALG_SHA_384},
     {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, PSA_ALG_SHA_512},
     {UINT16_MAX, PSA_ALG_NONE}};
+
 static const ptls_mbedtls_signature_scheme_t secp256r1_signature_schemes[] = {
     {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, PSA_ALG_SHA_256}, {UINT16_MAX, PSA_ALG_NONE}};
 static const ptls_mbedtls_signature_scheme_t secp384r1_signature_schemes[] = {
@@ -178,6 +182,7 @@
             ret = -1;
         }
         else {
+
             *key_index = x;
             *key_length = l_key;
         }
@@ -325,6 +330,7 @@
         return MBEDTLS_ERR_PK_PASSWORD_REQUIRED;
     }
     else if (ret != MBEDTLS_ERR_PEM_NO_HEADER_FOOTER_PRESENT) {
+
         return ret;
     }
 #endif /* MBEDTLS_RSA_C */
@@ -401,6 +407,7 @@
     const uint16_t *algorithms, size_t num_algorithms)
 {
     const ptls_mbedtls_signature_scheme_t* scheme;
+
     /* select the algorithm, driven by server-isde preference of `available` */
     for (scheme = available; scheme->scheme_id != UINT16_MAX; ++scheme) {
         for (size_t i = 0; i != num_algorithms; ++i) {
@@ -498,6 +505,7 @@
         /* First prepare the hash */
         unsigned char hash_buffer[PTLS_MAX_DIGEST_SIZE];
         unsigned char* hash_value = NULL;
+
         size_t hash_length = 0;
 
         if (scheme->hash_algo == PSA_ALG_NONE) {
@@ -530,7 +538,6 @@
             }
             if ((ret = ptls_buffer_reserve(outbuf, nb_bytes)) == 0) {
                 size_t signature_length = 0;
-
                 if (psa_sign_hash(self->key_id, sign_algo, hash_value, hash_length,
                     outbuf->base + outbuf->off, nb_bytes, &signature_length) != 0) {
                     ret = PTLS_ERROR_INCOMPATIBLE_KEY;
@@ -662,7 +669,6 @@
     return ret;
 }
 
-
 int ptls_mbedtls_load_private_key(ptls_context_t* ctx, char const* pem_fname)
 {
     int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
diff --git a/lib/openssl.c b/lib/openssl.c
index a6abafe..8ca5a6c 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -101,22 +101,21 @@
 #endif
 
 static const ptls_openssl_signature_scheme_t rsa_signature_schemes[] = {{PTLS_SIGNATURE_RSA_PSS_RSAE_SHA256, EVP_sha256},
-                                                                                  {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384},
-                                                                                  {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512},
-                                                                                  {UINT16_MAX, NULL}};
-static const ptls_openssl_signature_scheme_t secp256r1_signature_schemes[] = {
-    {PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256}, {UINT16_MAX, NULL}};
+                                                                        {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA384, EVP_sha384},
+                                                                        {PTLS_SIGNATURE_RSA_PSS_RSAE_SHA512, EVP_sha512},
+                                                                        {UINT16_MAX, NULL}};
+static const ptls_openssl_signature_scheme_t secp256r1_signature_schemes[] = {{PTLS_SIGNATURE_ECDSA_SECP256R1_SHA256, EVP_sha256},
+                                                                              {UINT16_MAX, NULL}};
 #if PTLS_OPENSSL_HAVE_SECP384R1
-static const ptls_openssl_signature_scheme_t secp384r1_signature_schemes[] = {
-    {PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384}, {UINT16_MAX, NULL}};
+static const ptls_openssl_signature_scheme_t secp384r1_signature_schemes[] = {{PTLS_SIGNATURE_ECDSA_SECP384R1_SHA384, EVP_sha384},
+                                                                              {UINT16_MAX, NULL}};
 #endif
 #if PTLS_OPENSSL_HAVE_SECP521R1
-static const ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {
-    {PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512}, {UINT16_MAX, NULL}};
+static const ptls_openssl_signature_scheme_t secp521r1_signature_schemes[] = {{PTLS_SIGNATURE_ECDSA_SECP521R1_SHA512, EVP_sha512},
+                                                                              {UINT16_MAX, NULL}};
 #endif
 #if PTLS_OPENSSL_HAVE_ED25519
-static const ptls_openssl_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, NULL},
-                                                                                      {UINT16_MAX, NULL}};
+static const ptls_openssl_signature_scheme_t ed25519_signature_schemes[] = {{PTLS_SIGNATURE_ED25519, NULL}, {UINT16_MAX, NULL}};
 #endif
 
 /**
@@ -854,8 +853,8 @@
 
 #endif
 
-static int do_sign(EVP_PKEY *key, const ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf,
-                   ptls_iovec_t input, ptls_async_job_t **async)
+static int do_sign(EVP_PKEY *key, const ptls_openssl_signature_scheme_t *scheme, ptls_buffer_t *outbuf, ptls_iovec_t input,
+                   ptls_async_job_t **async)
 {
     EVP_MD_CTX *ctx = NULL;
     const EVP_MD *md = scheme->scheme_md != NULL ? scheme->scheme_md() : NULL;
@@ -2183,7 +2182,6 @@
     .hash = &ptls_openssl_sha256};
 #endif
 
-
 #if PTLS_HAVE_AEGIS
 ptls_aead_algorithm_t ptls_openssl_aegis128l = {
     .name = "AEGIS-128L",
@@ -2194,7 +2192,7 @@
     .key_size = PTLS_AEGIS128L_KEY_SIZE,
     .iv_size = PTLS_AEGIS128L_IV_SIZE,
     .tag_size = PTLS_AEGIS128L_TAG_SIZE,
-    .tls12 = { .fixed_iv_size = 0, .record_iv_size = 0 },
+    .tls12 = {.fixed_iv_size = 0, .record_iv_size = 0},
     .non_temporal = 0,
     .align_bits = 0,
     .context_size = sizeof(struct aegis128l_context_t),
@@ -2214,45 +2212,43 @@
     .key_size = PTLS_AEGIS256_KEY_SIZE,
     .iv_size = PTLS_AEGIS256_IV_SIZE,
     .tag_size = PTLS_AEGIS256_TAG_SIZE,
-    .tls12 = { .fixed_iv_size = 0, .record_iv_size = 0 },
+    .tls12 = {.fixed_iv_size = 0, .record_iv_size = 0},
     .non_temporal = 0,
     .align_bits = 0,
     .context_size = sizeof(struct aegis256_context_t),
     .setup_crypto = aegis256_setup_crypto,
 };
-ptls_cipher_suite_t ptls_openssl_aegis256sha384 = {.id = PTLS_CIPHER_SUITE_AEGIS256_SHA384,
-                                                    .name = PTLS_CIPHER_SUITE_NAME_AEGIS256_SHA384,
-                                                    .aead = &ptls_openssl_aegis256,
-                                                    .hash = &ptls_openssl_sha384};
+ptls_cipher_suite_t ptls_openssl_aegis256sha512 = {.id = PTLS_CIPHER_SUITE_AEGIS256_SHA512,
+                                                   .name = PTLS_CIPHER_SUITE_NAME_AEGIS256_SHA512,
+                                                   .aead = &ptls_openssl_aegis256,
+                                                   .hash = &ptls_openssl_sha512};
 #endif
 
+ptls_cipher_suite_t *ptls_openssl_cipher_suites[] = { // ciphers used with sha384 (must be first)
+    &ptls_openssl_aes256gcmsha384,
 
-
-ptls_cipher_suite_t *ptls_openssl_cipher_suites[] = {// ciphers used with sha384 (must be first)
-                                                     &ptls_openssl_aes256gcmsha384,
-
-                                                     // ciphers used with sha256
-                                                     &ptls_openssl_aes128gcmsha256,
+    // ciphers used with sha256
+    &ptls_openssl_aes128gcmsha256,
 #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
-                                                     &ptls_openssl_chacha20poly1305sha256,
+    &ptls_openssl_chacha20poly1305sha256,
 #endif
-                                                     NULL};
+    NULL};
 
-ptls_cipher_suite_t *ptls_openssl_cipher_suites_all[] = {// ciphers used with sha384 (must be first)
+ptls_cipher_suite_t *ptls_openssl_cipher_suites_all[] = { // ciphers used with sha384 (must be first)
 #if PTLS_HAVE_AEGIS
-                                                        &ptls_openssl_aegis256sha384,
+    &ptls_openssl_aegis256sha512,
 #endif
-                                                        &ptls_openssl_aes256gcmsha384,
+    &ptls_openssl_aes256gcmsha384,
 
-                                                        // ciphers used with sha256
+// ciphers used with sha256
 #if PTLS_HAVE_AEGIS
-                                                        &ptls_openssl_aegis128lsha256,
+    &ptls_openssl_aegis128lsha256,
 #endif
-                                                        &ptls_openssl_aes128gcmsha256,
+    &ptls_openssl_aes128gcmsha256,
 #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
-                                                        &ptls_openssl_chacha20poly1305sha256,
+    &ptls_openssl_chacha20poly1305sha256,
 #endif
-                                                        NULL};
+    NULL};
 
 ptls_cipher_suite_t *ptls_openssl_tls12_cipher_suites[] = {&ptls_openssl_tls12_ecdhe_rsa_aes128gcmsha256,
                                                            &ptls_openssl_tls12_ecdhe_ecdsa_aes128gcmsha256,
diff --git a/lib/picotls.c b/lib/picotls.c
index b00b429..2feba7e 100644
--- a/lib/picotls.c
+++ b/lib/picotls.c
@@ -39,10 +39,6 @@
 #include "picotls-probes.h"
 #endif
 
-#ifdef PTLS_HAVE_AEGIS
-#include <aegis.h>
-#endif
-
 #define PTLS_MAX_PLAINTEXT_RECORD_SIZE 16384
 #define PTLS_MAX_ENCRYPTED_RECORD_SIZE (16384 + 256)
 
@@ -71,6 +67,7 @@
 #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_CERTIFICATE_AUTHORITIES 47
 #define PTLS_EXTENSION_TYPE_KEY_SHARE 51
 #define PTLS_EXTENSION_TYPE_ECH_OUTER_EXTENSIONS 0xfd00
 #define PTLS_EXTENSION_TYPE_ENCRYPTED_CLIENT_HELLO 0xfe0d
@@ -1618,7 +1615,7 @@
     return ret;
 }
 
-static int setup_traffic_protection(ptls_t *tls, int is_enc, const char *secret_label, size_t epoch, int skip_notify)
+static int setup_traffic_protection(ptls_t *tls, int is_enc, const char *secret_label, size_t epoch, uint64_t seq, int skip_notify)
 {
     static const char *log_labels[2][4] = {
         {NULL, "CLIENT_EARLY_TRAFFIC_SECRET", "CLIENT_HANDSHAKE_TRAFFIC_SECRET", "CLIENT_TRAFFIC_SECRET_0"},
@@ -1648,7 +1645,7 @@
     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;
+    ctx->seq = seq;
 
     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]);
@@ -1667,7 +1664,7 @@
     free(tls->pending_handshake_secret);
     tls->pending_handshake_secret = NULL;
 
-    return setup_traffic_protection(tls, is_enc, NULL, 2, 1);
+    return setup_traffic_protection(tls, is_enc, NULL, 2, 0, 1);
 }
 
 static void log_client_random(ptls_t *tls)
@@ -1706,10 +1703,13 @@
         ptls_buffer_push16(buf, csid);
         /* ticket_age_add */
         ptls_buffer_push32(buf, ticket_age_add);
-        /* server-name */
+        /* session ID context */
         ptls_buffer_push_block(buf, 2, {
-            if (server_name != NULL)
+            if (ctx->ticket_context.is_set) {
+                ptls_buffer_pushv(buf, ctx->ticket_context.bytes, sizeof(ctx->ticket_context.bytes));
+            } else if (server_name != NULL) {
                 ptls_buffer_pushv(buf, server_name, strlen(server_name));
+            }
         });
         /* alpn */
         ptls_buffer_push_block(buf, 1, {
@@ -1722,7 +1722,7 @@
     return ret;
 }
 
-int decode_session_identifier(uint64_t *issued_at, ptls_iovec_t *psk, uint32_t *ticket_age_add, ptls_iovec_t *server_name,
+int decode_session_identifier(uint64_t *issued_at, ptls_iovec_t *psk, uint32_t *ticket_age_add, ptls_iovec_t *ticket_ctx,
                               uint16_t *key_exchange_id, uint16_t *csid, ptls_iovec_t *negotiated_protocol, const uint8_t *src,
                               const uint8_t *const end)
 {
@@ -1748,7 +1748,7 @@
         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);
+            *ticket_ctx = ptls_iovec_init(src, end - src);
             src = end;
         });
         ptls_decode_open_block(src, end, 1, {
@@ -2479,7 +2479,7 @@
 
     if (tls->client.using_early_data) {
         assert(!is_second_flight);
-        if ((ret = setup_traffic_protection(tls, 1, "c e traffic", 1, 0)) != 0)
+        if ((ret = setup_traffic_protection(tls, 1, "c e traffic", 1, 0, 0)) != 0)
             goto Exit;
         if ((ret = push_change_cipher_spec(tls, emitter)) != 0)
             goto Exit;
@@ -2795,7 +2795,7 @@
 
     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)
+    if ((ret = setup_traffic_protection(tls, 0, "s hs traffic", 2, 0, 0)) != 0)
         goto Exit;
     if (tls->client.using_early_data) {
         if ((tls->pending_handshake_secret = malloc(PTLS_MAX_DIGEST_SIZE)) == NULL) {
@@ -2808,7 +2808,7 @@
             (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)
+        if ((ret = setup_traffic_protection(tls, 1, "c hs traffic", 2, 0, 0)) != 0)
             goto Exit;
     }
 
@@ -3373,7 +3373,7 @@
     /* 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)
+    if ((ret = setup_traffic_protection(tls, 0, "s ap traffic", 3, 0, 0)) != 0)
         goto Exit;
     if ((ret = derive_secret(tls->key_schedule, send_secret, "c ap traffic")) != 0)
         goto Exit;
@@ -3407,7 +3407,7 @@
     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)
+    if ((ret = setup_traffic_protection(tls, 1, NULL, 3, 0, 0)) != 0)
         goto Exit;
 
     tls->state = PTLS_STATE_CLIENT_POST_HANDSHAKE;
@@ -4009,7 +4009,7 @@
                              ptls_iovec_t ch_trunc)
 {
     ptls_buffer_t decbuf;
-    ptls_iovec_t ticket_psk, ticket_server_name, ticket_negotiated_protocol;
+    ptls_iovec_t ticket_psk, ticket_ctx, 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;
@@ -4032,7 +4032,7 @@
         default: /* decryption failure */
             continue;
         }
-        if (decode_session_identifier(&issue_at, &ticket_psk, &age_add, &ticket_server_name, &ticket_key_exchange_id, &ticket_csid,
+        if (decode_session_identifier(&issue_at, &ticket_psk, &age_add, &ticket_ctx, &ticket_key_exchange_id, &ticket_csid,
                                       &ticket_negotiated_protocol, decbuf.base, decbuf.base + decbuf.off) != 0)
             continue;
         /* check age */
@@ -4049,15 +4049,22 @@
             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))
+        /* check ticket context */
+        if (tls->ctx->ticket_context.is_set) {
+            if (!(ticket_ctx.len == sizeof(tls->ctx->ticket_context.bytes) &&
+                  memcmp(ticket_ctx.base, tls->ctx->ticket_context.bytes, ticket_ctx.len) == 0))
                 continue;
         } else {
-            if (tls->server_name != NULL)
-                continue;
+            /* check server-name */
+            if (ticket_ctx.len != 0) {
+                if (tls->server_name == NULL)
+                    continue;
+                if (!vec_is_string(ticket_ctx, tls->server_name))
+                    continue;
+            } else {
+                if (tls->server_name != NULL)
+                    continue;
+            }
         }
         { /* check key-exchange */
             ptls_key_exchange_algorithm_t **a;
@@ -4364,8 +4371,9 @@
 
     { /* 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)
+        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;
@@ -4564,7 +4572,7 @@
         }
         if ((ret = derive_exporter_secret(tls, 1)) != 0)
             goto Exit;
-        if ((ret = setup_traffic_protection(tls, 0, "c e traffic", 1, 0)) != 0)
+        if ((ret = setup_traffic_protection(tls, 0, "c e traffic", 1, 0, 0)) != 0)
             goto Exit;
     }
 
@@ -4623,7 +4631,7 @@
     /* 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)
+    if ((ret = setup_traffic_protection(tls, 1, "s hs traffic", 2, 0, 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)
@@ -4632,7 +4640,7 @@
             (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)
+        if ((ret = setup_traffic_protection(tls, 0, "c hs traffic", 2, 0, 0)) != 0)
             goto Exit;
         if (ch->psk.early_data_indication)
             tls->server.early_data_skipped_bytes = 0;
@@ -4677,10 +4685,9 @@
         /* 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;
+                /* certificate_request_context: this field SHALL be zero length, unless the certificate request is used for post-
+                 * handshake authentication. */
                 ptls_buffer_push(sendbuf, 0);
                 /* extensions */
                 ptls_buffer_push_block(sendbuf, 2, {
@@ -4688,6 +4695,19 @@
                         if ((ret = push_signature_algorithms(tls->ctx->verify_certificate, sendbuf)) != 0)
                             goto Exit;
                     });
+                    /* certificate authorities entension */
+                    if (tls->ctx->client_ca_names.count > 0) {
+                        buffer_push_extension(sendbuf, PTLS_EXTENSION_TYPE_CERTIFICATE_AUTHORITIES, {
+                            ptls_buffer_push_block(sendbuf, 2, {
+                                for (size_t i = 0; i != tls->ctx->client_ca_names.count; ++i) {
+                                    ptls_buffer_push_block(sendbuf, 2, {
+                                        ptls_iovec_t name = tls->ctx->client_ca_names.list[i];
+                                        ptls_buffer_pushv(sendbuf, name.base, name.len);
+                                    });
+                                }
+                            });
+                        });
+                    }
                 });
             });
 
@@ -4746,7 +4766,7 @@
     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)
+    if ((ret = setup_traffic_protection(tls, 1, "s ap traffic", 3, 0, 0)) != 0)
         goto Exit;
     if ((ret = derive_secret(tls->key_schedule, tls->server.pending_traffic_secret, "c ap traffic")) != 0)
         goto Exit;
@@ -4807,7 +4827,7 @@
 
     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)
+    if ((ret = setup_traffic_protection(tls, 0, NULL, 3, 0, 0)) != 0)
         return ret;
 
     ptls__key_schedule_update_hash(tls->key_schedule, message.base, message.len, 0);
@@ -4827,7 +4847,7 @@
                                       "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);
+    ret = setup_traffic_protection(tls, is_enc, NULL, 3, 0, 1);
 
 Exit:
     ptls_clear_memory(secret, sizeof(secret));
@@ -4997,6 +5017,28 @@
     return tls;
 }
 
+#define export_tls_params(output, is_server, session_reused, protocol_version, cipher, client_random, server_name,                 \
+                          negotiated_protocol, ver_block)                                                                          \
+    do {                                                                                                                           \
+        const char *_server_name = (server_name);                                                                                  \
+        ptls_iovec_t _negotiated_protocol = (negotiated_protocol);                                                                 \
+        ptls_buffer_push_block((output), 2, {                                                                                      \
+            ptls_buffer_push((output), (is_server));                                                                               \
+            ptls_buffer_push((output), (session_reused));                                                                          \
+            ptls_buffer_push16((output), (protocol_version));                                                                      \
+            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, {ver_block}); /* version-specific block */                                         \
+            ptls_buffer_push_block((output), 2, {});          /* for future extensions */                                          \
+        });                                                                                                                        \
+    } while (0)
+
 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,
@@ -5004,29 +5046,18 @@
 {
     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 */
-    });
+    export_tls_params(output, is_server, session_reused, PTLS_PROTOCOL_VERSION_TLS12, cipher, client_random, server_name,
+                      negotiated_protocol, {
+                          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);
+                      });
+    ret = 0;
 
 Exit:
     return ret;
@@ -5074,20 +5105,39 @@
 
 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);
+    int ret;
+
+    if (tls->state != PTLS_STATE_SERVER_POST_HANDSHAKE) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+
+    if (ptls_get_protocol_version(tls) == PTLS_PROTOCOL_VERSION_TLS13) {
+        export_tls_params(output, tls->is_server, tls->is_psk_handshake, PTLS_PROTOCOL_VERSION_TLS13, tls->cipher_suite,
+                          tls->client_random, tls->server_name, negotiated_protocol, {
+                              ptls_buffer_pushv(output, tls->traffic_protection.enc.secret, tls->cipher_suite->hash->digest_size);
+                              ptls_buffer_push64(output, tls->traffic_protection.enc.seq);
+                              ptls_buffer_pushv(output, tls->traffic_protection.dec.secret, tls->cipher_suite->hash->digest_size);
+                              ptls_buffer_push64(output, tls->traffic_protection.dec.seq);
+                          });
+        ret = 0;
+    } else {
+        if ((ret = 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)) != 0)
+            goto Exit;
+    }
+
+Exit:
+    return ret;
 }
 
-static int build_tls12_traffic_protection(ptls_t *tls, int is_enc, const uint8_t **src, const uint8_t *const end)
+static int import_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;
 
@@ -5114,6 +5164,22 @@
     return 0;
 }
 
+static int import_tls13_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;
+
+    /* set properties */
+    memcpy(tp->secret, *src, tls->cipher_suite->hash->digest_size);
+    *src += tls->cipher_suite->hash->digest_size;
+    if (ptls_decode64(&tp->seq, src, end) != 0)
+        return PTLS_ALERT_DECODE_ERROR;
+
+    if (setup_traffic_protection(tls, is_enc, NULL, 3, tp->seq, 0) != 0)
+        return PTLS_ERROR_INCOMPATIBLE_KEY;
+
+    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;
@@ -5139,11 +5205,6 @@
             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;
@@ -5169,15 +5230,36 @@
         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)
+                (*tls)->cipher_suite = ptls_find_cipher_suite(ctx->tls12_cipher_suites, csid);
+                if ((*tls)->cipher_suite == NULL) {
+                    ret = PTLS_ALERT_HANDSHAKE_FAILURE;
                     goto Exit;
-                if ((ret = build_tls12_traffic_protection(*tls, 0, &src, end)) != 0)
+                }
+                /* setup AEAD keys */
+                if ((ret = import_tls12_traffic_protection(*tls, 1, &src, end)) != 0)
+                    goto Exit;
+                if ((ret = import_tls12_traffic_protection(*tls, 0, &src, end)) != 0)
+                    goto Exit;
+                break;
+            case PTLS_PROTOCOL_VERSION_TLS13:
+                (*tls)->cipher_suite = ptls_find_cipher_suite(ctx->cipher_suites, csid);
+                if ((*tls)->cipher_suite == NULL) {
+                    ret = PTLS_ALERT_HANDSHAKE_FAILURE;
+                    goto Exit;
+                }
+                /* setup AEAD keys */
+                if (((*tls)->key_schedule = key_schedule_new((*tls)->cipher_suite, NULL, (*tls)->ech.aead != NULL)) == NULL) {
+                    ret = PTLS_ERROR_NO_MEMORY;
+                    goto Exit;
+                }
+                if ((ret = import_tls13_traffic_protection(*tls, 1, &src, end)) != 0)
+                    goto Exit;
+                if ((ret = import_tls13_traffic_protection(*tls, 0, &src, end)) != 0)
                     goto Exit;
                 break;
             default:
                 ret = PTLS_ALERT_ILLEGAL_PARAMETER;
-                break;
+                goto Exit;
             }
         });
         /* extensions */
@@ -6212,7 +6294,6 @@
     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;
diff --git a/picotls.xcodeproj/project.pbxproj b/picotls.xcodeproj/project.pbxproj
index 053d89e..338535e 100644
--- a/picotls.xcodeproj/project.pbxproj
+++ b/picotls.xcodeproj/project.pbxproj
@@ -205,6 +205,8 @@
 		081F00CC291A358800534A86 /* asn1.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = asn1.h; sourceTree = "<group>"; };
 		081F00CD291A358800534A86 /* pembase64.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pembase64.h; sourceTree = "<group>"; };
 		081F00CE291A358800534A86 /* ptlsbcrypt.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ptlsbcrypt.h; sourceTree = "<group>"; };
+		085BDAE52B1D618E002851EA /* libaegis.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = libaegis.c; sourceTree = "<group>"; };
+		085BDAE62B1D6238002851EA /* libaegis.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = libaegis.h; sourceTree = "<group>"; };
 		087F1A732B034AEB00E81AC1 /* FindMbedTLS.cmake */ = {isa = PBXFileReference; lastKnownFileType = text; path = FindMbedTLS.cmake; sourceTree = "<group>"; };
 		0883D3272AEF8F2500B711CC /* fusion.cmake */ = {isa = PBXFileReference; lastKnownFileType = text; path = fusion.cmake; sourceTree = "<group>"; };
 		0883D32A2AF601A700B711CC /* mbedtls.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mbedtls.c; sourceTree = "<group>"; };
@@ -448,6 +450,7 @@
 				E97577022212405D00D1EF74 /* ffx.c */,
 				E9B43DBF24619D1700824E51 /* fusion.c */,
 				08F0FDF52910F67A00EE657D /* hpke.c */,
+				085BDAE62B1D6238002851EA /* libaegis.h */,
 				0883D32B2AF601B900B711CC /* mbedtls.c */,
 				E949EF272073629300511ECA /* minicrypto-pem.c */,
 				106530C21D9B004B005B2C60 /* openssl.c */,
@@ -556,6 +559,7 @@
 				E9F20BE222E34B340018D260 /* aes128.c */,
 				E9F20BE022E34B340018D260 /* aes256.c */,
 				E9F20BE422E34B340018D260 /* chacha20.c */,
+				085BDAE52B1D618E002851EA /* libaegis.c */,
 				E9F20BF922E34C110018D260 /* random.c */,
 				E9F20BE122E34B340018D260 /* x25519.c */,
 			);
diff --git a/t/openssl.c b/t/openssl.c
index b3188ff..d487445 100644
--- a/t/openssl.c
+++ b/t/openssl.c
@@ -564,7 +564,8 @@
                                   .sign_certificate = &openssl_sign_certificate.super};
     ptls_context_t openssl_ctx_sha256only = openssl_ctx;
     while (openssl_ctx_sha256only.cipher_suites[0]->hash->digest_size != 32) {
-        assert(openssl_ctx.cipher_suites[0]->hash->digest_size == 48); /* sha384 */
+        assert(openssl_ctx.cipher_suites[0]->hash->digest_size == 64 || /* sha512 */
+               openssl_ctx.cipher_suites[0]->hash->digest_size == 48);  /* sha384 */
         ++openssl_ctx_sha256only.cipher_suites;
     }
     assert(openssl_ctx_sha256only.cipher_suites[0]->hash->digest_size == 32); /* sha256 */
diff --git a/t/picotls.c b/t/picotls.c
index 80c8543..1ce5925 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -87,7 +87,8 @@
     }
 
     {
-        ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes128gcmsha256, &ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_chacha20poly1305sha256, NULL};
+        ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes128gcmsha256, &ptls_minicrypto_aes256gcmsha384,
+                                             &ptls_minicrypto_chacha20poly1305sha256, NULL};
         static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256), C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256)};
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 0) == 0);
         ok(selected == &ptls_minicrypto_aes128gcmsha256);
@@ -96,8 +97,10 @@
     }
 
     {
-        ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_chacha20poly1305sha256, &ptls_minicrypto_aes128gcmsha256, NULL};
-        static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256), C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256), C(PTLS_CIPHER_SUITE_AES_256_GCM_SHA384)};
+        ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_chacha20poly1305sha256,
+                                             &ptls_minicrypto_aes128gcmsha256, NULL};
+        static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256), C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256),
+                                        C(PTLS_CIPHER_SUITE_AES_256_GCM_SHA384)};
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 0) == 0);
         ok(selected == &ptls_minicrypto_aes256gcmsha384);
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 1) == 0);
@@ -107,8 +110,10 @@
     }
 
     {
-        ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_chacha20poly1305sha256, &ptls_minicrypto_aes128gcmsha256, NULL};
-        static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256), C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256), C(PTLS_CIPHER_SUITE_AES_256_GCM_SHA384)};
+        ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_chacha20poly1305sha256,
+                                             &ptls_minicrypto_aes128gcmsha256, NULL};
+        static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256), C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256),
+                                        C(PTLS_CIPHER_SUITE_AES_256_GCM_SHA384)};
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 1) == 0);
         ok(selected == &ptls_minicrypto_aes256gcmsha384);
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 1) == 0);
@@ -117,7 +122,8 @@
 
     {
         ptls_cipher_suite_t *candidates[] = {&ptls_minicrypto_aes256gcmsha384, &ptls_minicrypto_aes128gcmsha256, NULL};
-        static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256), C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256), C(PTLS_CIPHER_SUITE_AES_256_GCM_SHA384)};
+        static const uint8_t input[] = {C(PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256), C(PTLS_CIPHER_SUITE_AES_128_GCM_SHA256),
+                                        C(PTLS_CIPHER_SUITE_AES_256_GCM_SHA384)};
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 0) == 0);
         ok(selected == &ptls_minicrypto_aes256gcmsha384);
         ok(select_cipher(&selected, candidates, input, input + sizeof(input), 1, 1) == 0);
@@ -506,8 +512,8 @@
 
 static void test_aegis256(void)
 {
-    ptls_cipher_suite_t *cs = find_cipher(ctx, PTLS_CIPHER_SUITE_AEGIS256_SHA384),
-                        *cs_peer = find_cipher(ctx_peer, PTLS_CIPHER_SUITE_AEGIS256_SHA384);
+    ptls_cipher_suite_t *cs = find_cipher(ctx, PTLS_CIPHER_SUITE_AEGIS256_SHA512),
+                        *cs_peer = find_cipher(ctx_peer, PTLS_CIPHER_SUITE_AEGIS256_SHA512);
 
     if (cs != NULL && cs_peer != NULL) {
         test_ciphersuite(cs, cs_peer);
@@ -858,7 +864,50 @@
     }
 }
 
-static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int check_ch, int require_client_authentication)
+static void check_clone(ptls_t *src, ptls_t *dest)
+{
+    ok(src->cipher_suite->hash->digest_size == dest->cipher_suite->hash->digest_size);
+    size_t digest_size = dest->cipher_suite->hash->digest_size;
+    ok(memcmp(src->traffic_protection.enc.secret, dest->traffic_protection.enc.secret, digest_size) == 0);
+    ok(memcmp(src->traffic_protection.dec.secret, dest->traffic_protection.dec.secret, digest_size) == 0);
+    const unsigned enc_idx = 0;
+    const unsigned dec_idx = 1;
+    struct {
+        uint8_t key[PTLS_MAX_SECRET_SIZE];
+        uint8_t iv[PTLS_MAX_IV_SIZE];
+        uint64_t seq;
+    } src_keys[2] = {0}, dest_keys[2] = {0};
+    ok(ptls_get_traffic_keys(src, 1, src_keys[enc_idx].key, src_keys[enc_idx].iv, &src_keys[enc_idx].seq) == 0);
+    ok(ptls_get_traffic_keys(src, 0, src_keys[dec_idx].key, src_keys[dec_idx].iv, &src_keys[dec_idx].seq) == 0);
+    ok(ptls_get_traffic_keys(dest, 1, dest_keys[enc_idx].key, dest_keys[enc_idx].iv, &dest_keys[enc_idx].seq) == 0);
+    ok(ptls_get_traffic_keys(dest, 0, dest_keys[dec_idx].key, dest_keys[dec_idx].iv, &dest_keys[dec_idx].seq) == 0);
+    ok(src_keys[enc_idx].seq == dest_keys[enc_idx].seq);
+    ok(src_keys[dec_idx].seq == dest_keys[dec_idx].seq);
+    ok(memcmp(src_keys[enc_idx].key, dest_keys[enc_idx].key, PTLS_MAX_SECRET_SIZE) == 0);
+    ok(memcmp(src_keys[dec_idx].key, dest_keys[dec_idx].key, PTLS_MAX_SECRET_SIZE) == 0);
+    ok(memcmp(src_keys[enc_idx].iv, dest_keys[enc_idx].iv, PTLS_MAX_IV_SIZE) == 0);
+    ok(memcmp(src_keys[dec_idx].iv, dest_keys[dec_idx].iv, PTLS_MAX_IV_SIZE) == 0);
+}
+
+static ptls_t *clone_tls(ptls_t *src)
+{
+    ptls_t *dest = NULL;
+    ptls_buffer_t sess_data;
+
+    ptls_buffer_init(&sess_data, "", 0);
+    int r = ptls_export(src, &sess_data);
+    assert(r == 0);
+    r = ptls_import(ctx_peer, &dest, (ptls_iovec_t){.base = sess_data.base, .len = sess_data.off});
+    assert(r == 0);
+    ptls_buffer_dispose(&sess_data);
+
+    check_clone(src, dest);
+
+    return dest;
+}
+
+static void test_handshake(ptls_iovec_t ticket, int mode, int expect_ticket, int check_ch, int require_client_authentication,
+                           int transfer_session)
 {
     ptls_t *client, *server;
     ptls_handshake_properties_t client_hs_prop = {{{{NULL}, ticket}}}, server_hs_prop = {{{{NULL}}}};
@@ -1049,6 +1098,9 @@
         cbuf.off = 0;
     }
 
+    /* holds the ptls_t pointer of server prior to migration */
+    ptls_t *original_server = server;
+
     if (mode != TEST_HANDSHAKE_EARLY_DATA || require_client_authentication) {
         ret = ptls_send(client, &cbuf, req, strlen(req));
         ok(ret == 0);
@@ -1062,6 +1114,8 @@
         ok(ptls_handshake_is_complete(server));
         decbuf.off = 0;
         cbuf.off = 0;
+        if (transfer_session)
+            server = clone_tls(original_server);
 
         ret = ptls_send(server, &sbuf, resp, strlen(resp));
         ok(ret == 0);
@@ -1119,18 +1173,21 @@
         decbuf.off = 0;
     }
 
+    /* original_server is used for the server-side checks because handshake data is never migrated */
     if (can_ech(ctx_peer, 1) && can_ech(ctx, 0)) {
         ok(ptls_is_ech_handshake(client, NULL, NULL, NULL));
-        ok(ptls_is_ech_handshake(server, NULL, NULL, NULL));
+        ok(ptls_is_ech_handshake(original_server, NULL, NULL, NULL));
     } else {
         ok(!ptls_is_ech_handshake(client, NULL, NULL, NULL));
-        ok(!ptls_is_ech_handshake(server, NULL, NULL, NULL));
+        ok(!ptls_is_ech_handshake(original_server, NULL, NULL, NULL));
     }
 
     ptls_buffer_dispose(&cbuf);
     ptls_buffer_dispose(&sbuf);
     ptls_buffer_dispose(&decbuf);
     ptls_free(client);
+    if (original_server != server)
+        ptls_free(original_server);
     ptls_free(server);
 
     if (check_ch)
@@ -1196,19 +1253,19 @@
     return second_sc_orig->cb(second_sc_orig, tls, async, selected_algorithm, output, input, algorithms, num_algorithms);
 }
 
-static void test_full_handshake_impl(int require_client_authentication, int is_async)
+static void test_full_handshake_impl(int require_client_authentication, int is_async, int transfer_session)
 {
-    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == 1);
     ok(async_sc_callcnt == is_async);
     ok(client_sc_callcnt == require_client_authentication);
 
-    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 0, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == 1);
     ok(async_sc_callcnt == is_async);
     ok(client_sc_callcnt == require_client_authentication);
 
-    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 1, require_client_authentication);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_1RTT, 0, 1, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == 1);
     ok(async_sc_callcnt == is_async);
     ok(client_sc_callcnt == require_client_authentication);
@@ -1216,28 +1273,32 @@
 
 static void test_full_handshake(void)
 {
-    test_full_handshake_impl(0, 0);
+    test_full_handshake_impl(0, 0, 0);
+    test_full_handshake_impl(0, 0, 0);
 }
 
 static void test_full_handshake_with_client_authentication(void)
 {
-    test_full_handshake_impl(1, 0);
+    test_full_handshake_impl(1, 0, 0);
+    test_full_handshake_impl(1, 0, 1);
 }
 
 static void test_key_update(void)
 {
-    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_KEY_UPDATE, 0, 0, 0);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_KEY_UPDATE, 0, 0, 0, 0);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_KEY_UPDATE, 0, 0, 0, 1);
 }
 
 static void test_hrr_handshake(void)
 {
-    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR, 0, 0, 0);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR, 0, 0, 0, 0);
     ok(server_sc_callcnt == 1);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR, 0, 0, 0, 0);
 }
 
 static void test_hrr_stateless_handshake(void)
 {
-    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR_STATELESS, 0, 0, 0);
+    test_handshake(ptls_iovec_init(NULL, 0), TEST_HANDSHAKE_HRR_STATELESS, 0, 0, 0, 0);
     ok(server_sc_callcnt == 1);
 }
 
@@ -1263,7 +1324,7 @@
     return 0;
 }
 
-static void test_resumption_impl(int different_preferred_key_share, int require_client_authentication)
+static void test_resumption_impl(int different_preferred_key_share, int require_client_authentication, int transfer_session)
 {
     assert(ctx->key_exchanges[0]->id == ctx_peer->key_exchanges[0]->id);
     assert(ctx->key_exchanges[1] == NULL);
@@ -1289,29 +1350,30 @@
     ctx_peer->encrypt_ticket = &et;
     ctx->save_ticket = &st;
 
-    test_handshake(saved_ticket, different_preferred_key_share ? TEST_HANDSHAKE_2RTT : TEST_HANDSHAKE_1RTT, 1, 0, 0);
+    test_handshake(saved_ticket, different_preferred_key_share ? TEST_HANDSHAKE_2RTT : TEST_HANDSHAKE_1RTT, 1, 0, 0,
+                   transfer_session);
     ok(server_sc_callcnt == 1);
     ok(saved_ticket.base != NULL);
 
     /* psk using saved ticket */
-    test_handshake(saved_ticket, TEST_HANDSHAKE_1RTT, 1, 0, require_client_authentication);
+    test_handshake(saved_ticket, TEST_HANDSHAKE_1RTT, 1, 0, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */
     ok(client_sc_callcnt == require_client_authentication);
 
     /* 0-rtt psk using saved ticket */
-    test_handshake(saved_ticket, TEST_HANDSHAKE_EARLY_DATA, 1, 0, require_client_authentication);
+    test_handshake(saved_ticket, TEST_HANDSHAKE_EARLY_DATA, 1, 0, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */
     ok(client_sc_callcnt == require_client_authentication);
 
     ctx->require_dhe_on_psk = 1;
 
     /* psk-dhe using saved ticket */
-    test_handshake(saved_ticket, TEST_HANDSHAKE_1RTT, 1, 0, require_client_authentication);
+    test_handshake(saved_ticket, TEST_HANDSHAKE_1RTT, 1, 0, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */
     ok(client_sc_callcnt == require_client_authentication);
 
     /* 0-rtt psk-dhe using saved ticket */
-    test_handshake(saved_ticket, TEST_HANDSHAKE_EARLY_DATA, 1, 0, require_client_authentication);
+    test_handshake(saved_ticket, TEST_HANDSHAKE_EARLY_DATA, 1, 0, require_client_authentication, transfer_session);
     ok(server_sc_callcnt == require_client_authentication); /* client authentication turns off resumption */
     ok(client_sc_callcnt == require_client_authentication);
 
@@ -1325,19 +1387,22 @@
 
 static void test_resumption(void)
 {
-    test_resumption_impl(0, 0);
+    test_resumption_impl(0, 0, 0);
+    test_resumption_impl(0, 0, 1);
 }
 
 static void test_resumption_different_preferred_key_share(void)
 {
     if (ctx == ctx_peer)
         return;
-    test_resumption_impl(1, 0);
+    test_resumption_impl(1, 0, 0);
+    test_resumption_impl(0, 0, 1);
 }
 
 static void test_resumption_with_client_authentication(void)
 {
-    test_resumption_impl(0, 1);
+    test_resumption_impl(0, 0, 0);
+    test_resumption_impl(0, 1, 1);
 }
 
 static void test_async_sign_certificate(void)
@@ -1347,7 +1412,7 @@
     ptls_sign_certificate_t async_sc = {async_sign_certificate}, *orig_sc = ctx_peer->sign_certificate;
     ctx_peer->sign_certificate = &async_sc;
 
-    test_full_handshake_impl(0, 1);
+    test_full_handshake_impl(0, 1, 0);
 
     ctx_peer->sign_certificate = orig_sc;
 }
@@ -1901,6 +1966,60 @@
         ctx->sign_certificate = second_sc_orig;
 }
 
+static void do_test_tlsblock(size_t len_encoded, size_t max_bytes)
+{
+    ptls_buffer_t buf;
+    const uint8_t *src, *end;
+    int expect_overflow = 0, ret;
+
+    /* block that fits in */
+    ptls_buffer_init(&buf, "", 0);
+    ptls_buffer_push_block(&buf, len_encoded, {
+        for (size_t i = 0; i < max_bytes; ++i)
+            ptls_buffer_push(&buf, (uint8_t)i);
+    });
+    src = buf.base;
+    end = buf.base + buf.off;
+    ptls_decode_block(src, end, len_encoded, {
+        ok(end - src == max_bytes);
+        int bytes_eq = 1;
+        for (size_t i = 0; i < max_bytes; ++i) {
+            if (src[i] != (uint8_t)i)
+                bytes_eq = 0;
+        }
+        ok(bytes_eq);
+        src = end;
+    });
+
+    /* block that does not fit in */
+    ptls_buffer_push_block(&buf, len_encoded, {
+        for (size_t i = 0; i < max_bytes + 1; i++)
+            ptls_buffer_push(&buf, 1);
+        expect_overflow = 1;
+    });
+    ok(!"fail");
+
+Exit:
+    if (ret != 0) {
+        if (expect_overflow) {
+            ok(ret == PTLS_ERROR_BLOCK_OVERFLOW);
+        } else {
+            ok(!"fail");
+        }
+    }
+    ptls_buffer_dispose(&buf);
+}
+
+static void test_tlsblock8(void)
+{
+    do_test_tlsblock(1, 255);
+}
+
+static void test_tlsblock16(void)
+{
+    do_test_tlsblock(2, 65535);
+}
+
 static void test_quicint(void)
 {
 #define CHECK_PATTERN(output, ...)                                                                                                 \
@@ -2161,6 +2280,8 @@
     subtest("chacha20", test_chacha20);
     subtest("ffx", test_ffx);
     subtest("base64-decode", test_base64_decode);
+    subtest("tls-block8", test_tlsblock8);
+    subtest("tls-block16", test_tlsblock16);
     subtest("ech", test_ech);
     subtest("fragmented-message", test_fragmented_message);
     subtest("handshake", test_all_handshakes);