Merge branch 'master' into kazuho/session-ticket-context
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 80b5401..1293876 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -19,8 +19,8 @@
             command: make -f misc/docker-ci.mk CMAKE_ARGS='-DOPENSSL_ROOT_DIR=-DOPENSSL_ROOT_DIR=/opt/openssl-1.1.0 -DWITH_FUSION=OFF' CONTAINER_NAME='h2oserver/h2o-ci:ubuntu1604'
           - name: "Linux / OpenSSL 1.1.1"
             command: make -f misc/docker-ci.mk CMAKE_ARGS='-DWITH_AEGIS=1 -DAEGIS_INCLUDE_DIR=/usr/local/include'
-          - name: "Linux / OpenSSL 3.0"
-            command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204
+          - name: "Linux / OpenSSL 3.0 + mbedtls"
+            command: make -f misc/docker-ci.mk CONTAINER_NAME=h2oserver/h2o-ci:ubuntu2204 CMAKE_ARGS='-DWITH_MBEDTLS=1'
           - name: "Linux / OpenSSL 1.1.1 + ASan & UBSan"
             command: make -f misc/docker-ci.mk CMAKE_ARGS='-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_FLAGS=-fsanitize=address,undefined -DCMAKE_CXX_FLAGS=-fsanitize=address,undefined' CHECK_ENVS='ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1'
           - name: "Linux / boringssl"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 42692a5..dc146a8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -10,18 +10,13 @@
 ENDIF ()
 
 FIND_PACKAGE(PkgConfig REQUIRED)
-INCLUDE(cmake/dtrace-utils.cmake)
 INCLUDE(cmake/boringssl-adjust.cmake)
+INCLUDE(cmake/dtrace-utils.cmake)
+INCLUDE(cmake/fusion.cmake)
+SET(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
 
 CHECK_DTRACE(${PROJECT_SOURCE_DIR}/picotls-probes.d)
-IF ((CMAKE_SIZEOF_VOID_P EQUAL 8) AND
-    (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") OR
-     (CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64") OR
-     (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64"))
-    SET(WITH_FUSION_DEFAULT "ON")
-ELSE ()
-    SET(WITH_FUSION_DEFAULT "OFF")
-ENDIF ()
+CHECK_FUSION_PREREQUISITES()
 
 OPTION(WITH_DTRACE "use USDT (userspace Dtrace probes)" ${HAVE_DTRACE})
 OPTION(WITH_FUSION "build 'fusion' AES-GCM engine" ${WITH_FUSION_DEFAULT})
@@ -32,6 +27,7 @@
     MESSAGE(STATUS "Enabling 'fusion' AES-GCM engine")
 ENDIF ()
 OPTION(WITH_AEGIS "enable AEGIS (requires libaegis)" ${WITH_AEGIS})
+OPTION(WITH_MBEDTLS "enable MBEDTLS" ${WITH_MBEDTLS})
 
 SET(CMAKE_C_FLAGS "-std=c99 -Wall -O2 -g ${CC_WARNING_FLAGS} ${CMAKE_C_FLAGS}")
 INCLUDE_DIRECTORIES(
@@ -196,9 +192,30 @@
     ENDIF ()
     SET(TEST_EXES ${TEST_EXES} test-fusion.t)
 
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPTLS_HAVE_FUSION=1")
     LIST(APPEND PTLSBENCH_LIBS picotls-fusion)
 ENDIF ()
 
+IF (WITH_MBEDTLS)
+    FIND_PACKAGE(MbedTLS)
+    IF (NOT MbedTLS_FOUND)
+        MESSAGE(FATAL_ERROR "-DWITH_MBEDTLS set but mbedtls not found")
+    ENDIF ()
+    message(STATUS "mbedtls/include: ${MBEDTLS_INCLUDE_DIRS}")
+    message(STATUS "mbedtls libraries: ${MBEDTLS_LIBRARIES}")
+    INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIRS})
+    ADD_LIBRARY(picotls-mbedtls lib/mbedtls.c)
+    ADD_EXECUTABLE(test-mbedtls.t
+        deps/picotest/picotest.c
+        ${CORE_TEST_FILES}
+        t/mbedtls.c)
+    TARGET_LINK_LIBRARIES(test-mbedtls.t
+        picotls-minicrypto picotls-mbedtls
+        ${MBEDTLS_LIBRARIES})
+    SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPTLS_HAVE_MBEDTLS=1")
+    LIST(APPEND PTLSBENCH_LIBS picotls-mbedtls ${MBEDTLS_LIBRARIES})
+ENDIF ()
+
 ADD_EXECUTABLE(ptlsbench t/ptlsbench.c)
 SET_TARGET_PROPERTIES(ptlsbench PROPERTIES COMPILE_FLAGS "-DPTLS_MEMORY_DEBUG=1")
 TARGET_LINK_LIBRARIES(ptlsbench ${PTLSBENCH_LIBS})
diff --git a/cmake/FindMbedTLS.cmake b/cmake/FindMbedTLS.cmake
new file mode 100644
index 0000000..048a1e9
--- /dev/null
+++ b/cmake/FindMbedTLS.cmake
@@ -0,0 +1,48 @@
+# Try to find MbedTLS; recognized hints are:
+#  * MBEDTLS_ROOT_DIR
+#  * MBEDTLS_LIBDIR
+# Upon return,
+#  * MBEDTLS_INCLUDE_DIRS
+#  * MBEDTLS_LIBRARIES
+# will be set.
+# Users may supply MBEDTLS_INCLUDE_DIRS or MBEDTLS_LIBRARIES directly.
+
+INCLUDE(FindPackageHandleStandardArgs)
+
+# setup default vars for the hints
+IF (NOT DEFINED MBEDTLS_ROOT_DIR)
+    SET(MBEDTLS_ROOT_DIR "/usr/local" "/usr")
+ENDIF ()
+IF (NOT DEFINED MBEDTLS_LIBDIR)
+    SET(MBEDTLS_LIBDIR)
+    FOREACH (item IN LISTS MBEDTLS_ROOT_DIR)
+        LIST(APPEND MBEDTLS_LIBDIR "${item}/lib")
+    ENDFOREACH ()
+ENDIF ()
+
+# find include directory
+IF (NOT DEFINED MBEDTLS_INCLUDE_DIRS)
+    SET(HINTS)
+    FOREACH (item IN LISTS MBEDTLS_ROOT_DIR)
+        LIST(APPEND HINTS "${item}/include")
+    ENDFOREACH ()
+    FIND_PATH(MBEDTLS_INCLUDE_DIRS
+        NAMES mbedtls/build_info.h psa/crypto.h
+        HINTS $HINTS)
+ENDIF ()
+
+# find libraries
+FIND_LIBRARY(MBEDTLS_LIBRARY mbedtls HINTS $MBEDTLS_LIBDIR)
+FIND_LIBRARY(MBEDTLS_CRYPTO mbedcrypto HINTS $MBEDTLS_LIBDIR)
+FIND_LIBRARY(MBEDTLS_X509 mbedx509 HINTS $MBEDTLS_LIBDIR)
+
+# setup
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(MbedTLS REQUIRED_VARS
+    MBEDTLS_LIBRARY
+    MBEDTLS_CRYPTO
+    MBEDTLS_X509
+    MBEDTLS_INCLUDE_DIRS)
+IF (MbedTLS_FOUND)
+    SET(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDTLS_CRYPTO} ${MBEDTLS_X509})
+    MARK_AS_ADVANCED(MBEDTLS_LIBRARIES MBEDTLS_INCLUDE_DIRS)
+ENDIF ()
diff --git a/cmake/fusion.cmake b/cmake/fusion.cmake
new file mode 100644
index 0000000..2b7ef1c
--- /dev/null
+++ b/cmake/fusion.cmake
@@ -0,0 +1,30 @@
+INCLUDE(CheckCSourceCompiles)
+INCLUDE(CMakePushCheckState)
+
+FUNCTION (CHECK_FUSION_PREREQUISITES)
+    MESSAGE(STATUS "Detecting fusion support")
+
+    CMAKE_PUSH_CHECK_STATE()
+    SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -mavx2 -maes -mpclmul -mvaes -mvpclmulqdq")
+    CHECK_C_SOURCE_COMPILES("
+    #include <emmintrin.h>
+    #include <immintrin.h>
+    int main(void) {
+        __m256i  ord0, ord1, ord2, ord3 = _mm256_setzero_si256();
+        ord0 = _mm256_aesenc_epi128(ord1, ord2);
+        ord3 = _mm256_aesenclast_epi128(ord0, ord1);
+        ord1 = _mm256_clmulepi64_epi128(ord3, ord2, 0x00);
+        _mm_insert_epi64(_mm_setr_epi32(0, 1, 2, 3), 0, 0);
+        return 0;
+    }
+    " CC_HAS_AESNI256)
+    CMAKE_POP_CHECK_STATE()
+
+    IF (CC_HAS_AESNI256)
+        MESSAGE(STATUS "Can use fusion")
+        SET(WITH_FUSION_DEFAULT "ON" PARENT_SCOPE)
+    ELSE ()
+        MESSAGE(STATUS "Cannot use fusion")
+        SET(WITH_FUSION_DEFAULT "OFF" PARENT_SCOPE)
+    ENDIF ()
+ENDFUNCTION ()
diff --git a/include/picotls.h b/include/picotls.h
index 04b885a..8af9da1 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"
 
@@ -338,11 +338,14 @@
      */
     const struct st_ptls_key_exchange_algorithm_t *algo;
     /**
-     * the public key
+     * public key of this context
      */
     ptls_iovec_t pubkey;
     /**
-     * If `release` is set, the callee frees resources allocated to the context and set *keyex to NULL
+     * This function can be used for deriving a shared secret or for destroying the context.
+     * When `secret` is non-NULL, this callback derives the shared secret using the public key of the context and the peer key being
+     * given, and sets the value in `secret`. The memory pointed to by `secret->base` must be freed by the caller by calling `free`.
+     * When `release` is set, the callee frees resources allocated to the context and set *keyex to NULL.
      */
     int (*on_exchange)(struct st_ptls_key_exchange_context_t **keyex, int release, ptls_iovec_t *secret, ptls_iovec_t peerkey);
 } ptls_key_exchange_context_t;
@@ -356,12 +359,14 @@
      */
     uint16_t id;
     /**
-     * creates a context for asynchronous key exchange. The function is called when ClientHello is generated. The on_exchange
+     * Creates a context for asynchronous key exchange. The function is called when ClientHello is generated. The on_exchange
      * callback of the created context is called when the client receives ServerHello.
      */
     int (*create)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx);
     /**
-     * implements synchronous key exchange. Called when receiving a ServerHello.
+     * Implements synchronous key exchange. Called when receiving a ServerHello.
+     * Given a public key provided by the peer (`peerkey`), this callback returns a empheral public key (`pubkey`) and a secret
+     * (`secret) `derived from the two public keys.
      */
     int (*exchange)(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret,
                     ptls_iovec_t peerkey);
@@ -398,31 +403,81 @@
     int (*setup_crypto)(ptls_cipher_context_t *ctx, int is_enc, const void *key);
 } ptls_cipher_algorithm_t;
 
+/**
+ * This object specifies symmetric cipher to be calculated alongside the AEAD encryption.
+ * QUIC stacks can use this object to apply QUIC header protection and AEAD encryption in one shot.
+ */
 typedef struct st_ptls_aead_supplementary_encryption_t {
+    /**
+     * Cipher context to be used.
+     */
     ptls_cipher_context_t *ctx;
+    /**
+     * Input to the cipher.
+     * This field may point to the output of AEAD encryption, in which case the input will be read after AEAD encryption is
+     * complete.
+     */
     const void *input;
+    /**
+     * Output.
+     */
     uint8_t output[16];
 } ptls_aead_supplementary_encryption_t;
 
 /**
- * AEAD context. AEAD implementations are allowed to stuff data at the end of the struct. The size of the memory allocated for the
- * struct is governed by ptls_aead_algorithm_t::context_size.
- * Ciphers for TLS over TCP MUST implement `do_encrypt`, `do_encrypt_v`, `do_decrypt`. `do_encrypt_init`, `~update`, `~final` are
- * obsolete, and therefore may not be available.
+ * AEAD context.
+ * AEAD implementations are allowed to stuff data at the end of the struct; see `ptls_aead_algorithm_t::setup_crypto`.
+ * 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 {
+    /**
+     * Points to the algorithm. This field is governed by picotls core; backends must not alter.
+     */
     const struct st_ptls_aead_algorithm_t *algo;
-    /* field above this line must not be altered by the crypto binding */
+    /**
+     * Mandatory callback that disposes of all the backend-specific data.
+     */
     void (*dispose_crypto)(struct st_ptls_aead_context_t *ctx);
+    /**
+     * Mandatory callback that returns the static IV. The size of IV is available as `ptls_aead_algorithm_t::iv_size`.
+     */
     void (*do_get_iv)(struct st_ptls_aead_context_t *ctx, void *iv);
+    /**
+     * Mandatory callback that sets the static IV. The size of IV is available as `ptls_aead_algorithm_t::iv_size`.
+     */
     void (*do_set_iv)(struct st_ptls_aead_context_t *ctx, const void *iv);
+    /**
+     * Deprecated.
+     */
     void (*do_encrypt_init)(struct st_ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen);
+    /**
+     * Deprecated.
+     */
     size_t (*do_encrypt_update)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen);
+    /**
+     * Deprecated.
+     */
     size_t (*do_encrypt_final)(struct st_ptls_aead_context_t *ctx, void *output);
+    /**
+     * Mandatory callback that does "one-shot" encryption of an AEAD block.
+     * When `supp` is set to non-NULL, the callback must also encrypt the supplementary block.
+     * Backends may set this field to `ptls_aead__do_encrypt` that calls `do_encrypt_v` and `ptls_cipher_*` functions for handling
+     * the supplimentary block.
+     */
     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);
+    /**
+     * Variant of `do_encrypt` that gathers input from multiple blocks. Support for this callback is also mandatory.
+     * Legacy backends may set this field to `ptls_aead__do_encrypt_v` that calls `do_encrypt_init`, `do_encrypt_update`,
+     * `do_encrypt_final`.
+     */
     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);
+    /**
+     * Mandatory callback for decrypting an AEAD block.
+     * If successful, returns the amount of cleartext bytes being written to output. Otherwise, returns SIZE_MAX.
+     */
     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;
@@ -479,12 +534,16 @@
      */
     uint8_t align_bits;
     /**
-     * size of memory allocated for ptls_aead_context_t. AEAD implementations can set this value to something greater than
-     * sizeof(ptls_aead_context_t) and stuff additional data at the bottom of the struct.
+     * size of memory allocated for `ptls_aead_context_t`
      */
     size_t context_size;
     /**
-     * callback that sets up the crypto
+     * Backend callback called to setup `ptls_aead_context_t`.
+     * Backends are allowed to stuff arbitrary data at the end of `ptls_aead_context_t`; actual size of the memory chunk being
+     * allocated is that specified by `ptls_aead_algorithm_t::context_size`. When the `setup_crypto` callback is called, all the
+     * fields outside of `ptls_aead_context_t` will be in undefined state; it is the responsibility of the callback to initialize
+     * them, as well as the callbacks of `ptls_aead_context_t` that the backend supports.
+     * A non-zero return value indicates failure, in which case the error will propagate as `ptls_aead_new` returning NULL.
      */
     int (*setup_crypto)(ptls_aead_context_t *ctx, int is_enc, const void *key, const void *iv);
 } ptls_aead_algorithm_t;
@@ -1770,6 +1829,10 @@
  * the default get_time callback
  */
 extern ptls_get_time_t ptls_get_time;
+/**
+ * default hash clone function that calls memcpy
+ */
+static void ptls_hash_clone_memcpy(void *dst, const void *src, size_t size);
 #if defined(PICOTLS_USE_DTRACE) && PICOTLS_USE_DTRACE
 /**
  *
@@ -1926,7 +1989,14 @@
     return ctx->do_decrypt(ctx, output, input, inlen, seq, aad, aadlen);
 }
 
+inline void ptls_hash_clone_memcpy(void *dst, const void *src, size_t size)
+{
+    memcpy(dst, src, size);
+}
+
 #define ptls_define_hash(name, ctx_type, init_func, update_func, final_func)                                                       \
+    ptls_define_hash6(name, ctx_type, init_func, update_func, final_func, ptls_hash_clone_memcpy)
+#define ptls_define_hash6(name, ctx_type, init_func, update_func, final_func, clone_func)                                          \
                                                                                                                                    \
     struct name##_context_t {                                                                                                      \
         ptls_hash_context_t super;                                                                                                 \
@@ -1969,7 +2039,8 @@
         struct name##_context_t *dst, *src = (struct name##_context_t *)_src;                                                      \
         if ((dst = malloc(sizeof(*dst))) == NULL)                                                                                  \
             return NULL;                                                                                                           \
-        *dst = *src;                                                                                                               \
+        dst->super = src->super;                                                                                                   \
+        clone_func(&dst->ctx, &src->ctx, sizeof(dst->ctx));                                                                        \
         return &dst->super;                                                                                                        \
     }                                                                                                                              \
                                                                                                                                    \
diff --git a/include/picotls/mbedtls.h b/include/picotls/mbedtls.h
new file mode 100644
index 0000000..b649246
--- /dev/null
+++ b/include/picotls/mbedtls.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2023, Christian Huitema
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef picotls_mbedtls_h
+#define picotls_mbedtls_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <psa/crypto.h>
+#include "picotls.h"
+
+/* before using any of these objects, psa_crypto_init() must be called */
+
+extern ptls_hash_algorithm_t ptls_mbedtls_sha256;
+extern ptls_hash_algorithm_t ptls_mbedtls_sha512;
+#if defined(MBEDTLS_SHA384_C)
+extern ptls_hash_algorithm_t ptls_mbedtls_sha384;
+#endif
+
+extern ptls_cipher_algorithm_t ptls_mbedtls_aes128ecb;
+extern ptls_cipher_algorithm_t ptls_mbedtls_aes256ecb;
+extern ptls_cipher_algorithm_t ptls_mbedtls_aes128ctr;
+extern ptls_cipher_algorithm_t ptls_mbedtls_aes256ctr;
+extern ptls_cipher_algorithm_t ptls_mbedtls_chacha20;
+
+extern ptls_aead_algorithm_t ptls_mbedtls_aes128gcm;
+extern ptls_aead_algorithm_t ptls_mbedtls_aes256gcm;
+extern ptls_aead_algorithm_t ptls_mbedtls_chacha20poly1305;
+
+extern ptls_cipher_suite_t ptls_mbedtls_aes128gcmsha256;
+#if defined(MBEDTLS_SHA384_C)
+extern ptls_cipher_suite_t ptls_mbedtls_aes256gcmsha384;
+#endif
+extern ptls_cipher_suite_t ptls_mbedtls_chacha20poly1305sha256;
+extern ptls_cipher_suite_t *ptls_mbedtls_cipher_suites[];
+
+extern ptls_key_exchange_algorithm_t ptls_mbedtls_secp256r1;
+extern ptls_key_exchange_algorithm_t ptls_mbedtls_x25519;
+extern ptls_key_exchange_algorithm_t *ptls_mbedtls_key_exchanges[];
+
+void ptls_mbedtls_random_bytes(void *buf, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* picotls_mbedtls_h */
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/cifra.c b/lib/cifra.c
index d51f2b7..5fe9feb 100644
--- a/lib/cifra.c
+++ b/lib/cifra.c
@@ -23,7 +23,7 @@
 #include "picotls.h"
 #include "picotls/minicrypto.h"
 
-ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[] = {// ciphers used with sha384 (must be first)
+ptls_cipher_suite_t *ptls_minicrypto_cipher_suites[] = {// ciphers used with sha512 and sha384 (must be first)
                                                         &ptls_minicrypto_aes256gcmsha384,
 
                                                         // ciphers used with sha256
@@ -31,9 +31,9 @@
                                                         &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,
 
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..76fc9e4 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,
@@ -56,7 +54,7 @@
                                                   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/cifra/x25519.c b/lib/cifra/x25519.c
index 58f464f..3697d4d 100644
--- a/lib/cifra/x25519.c
+++ b/lib/cifra/x25519.c
@@ -45,6 +45,13 @@
         return PTLS_ERROR_NO_MEMORY;
 
     cf_curve25519_mul(secret->base, clientpriv != NULL ? clientpriv : serverpriv, clientpriv != NULL ? serverpub : clientpub);
+
+    static const uint8_t zeros[X25519_KEY_SIZE] = {0};
+    if (ptls_mem_equal(secret->base, zeros, sizeof(zeros))) {
+        free(secret->base);
+        return PTLS_ERROR_INCOMPATIBLE_KEY;
+    }
+
     secret->len = X25519_KEY_SIZE;
     return 0;
 }
@@ -111,8 +118,10 @@
 
 Exit:
     ptls_clear_memory(priv, sizeof(priv));
-    if (pub != NULL && ret != 0)
+    if (pub != NULL && ret != 0) {
         ptls_clear_memory(pub, X25519_KEY_SIZE);
+        free(pub);
+    }
     return ret;
 }
 
diff --git a/lib/mbedtls.c b/lib/mbedtls.c
new file mode 100644
index 0000000..86fa7b0
--- /dev/null
+++ b/lib/mbedtls.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2023, Christian Huitema
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifdef _WINDOWS
+#include "wincompat.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <psa/crypto.h>
+#include <mbedtls/chacha20.h>
+#include <mbedtls/ecdh.h>
+#include "picotls.h"
+
+#define PSA_FUNC_FAILED(fn, ret)                                                                                                   \
+    do {                                                                                                                           \
+        fprintf(stderr, "in %s at line %d, " PTLS_TO_STR(fn) " failed (%d)\n", __FUNCTION__, __LINE__, (int)ret);                  \
+        abort();                                                                                                                   \
+    } while (0)
+
+#define CALL_WITH_CHECK(fn, ...)                                                                                                   \
+    do {                                                                                                                           \
+        psa_status_t ret;                                                                                                          \
+        if ((ret = fn(__VA_ARGS__)) != PSA_SUCCESS)                                                                                \
+            PSA_FUNC_FAILED(fn, ret);                                                                                              \
+    } while (0)
+
+void ptls_mbedtls_random_bytes(void *buf, size_t len)
+{
+    CALL_WITH_CHECK(psa_generate_random, buf, len);
+}
+
+#define DEFINE_HASH(name, name_upcase, psa_alg)                                                                                    \
+    static void name##_do_init(psa_hash_operation_t *op)                                                                           \
+    {                                                                                                                              \
+        *op = psa_hash_operation_init();                                                                                           \
+        CALL_WITH_CHECK(psa_hash_setup, op, psa_alg);                                                                              \
+    }                                                                                                                              \
+    static void name##_do_update(psa_hash_operation_t *op, const void *src, size_t len)                                            \
+    {                                                                                                                              \
+        CALL_WITH_CHECK(psa_hash_update, op, src, len);                                                                            \
+    }                                                                                                                              \
+    static void name##_do_final(psa_hash_operation_t *op, void *md)                                                                \
+    {                                                                                                                              \
+        size_t unused;                                                                                                             \
+        CALL_WITH_CHECK(psa_hash_finish, op, md, PTLS_##name_upcase##_DIGEST_SIZE, &unused);                                       \
+    }                                                                                                                              \
+    static void name##_do_clone(psa_hash_operation_t *dst, psa_hash_operation_t *src, size_t unused)                               \
+    {                                                                                                                              \
+        CALL_WITH_CHECK(psa_hash_clone, src, dst);                                                                                 \
+    }                                                                                                                              \
+    ptls_define_hash6(name, psa_hash_operation_t, name##_do_init, name##_do_update, name##_do_final, name##_do_clone);             \
+    ptls_hash_algorithm_t ptls_mbedtls_##name = {PTLS_TO_STR(name), PTLS_##name_upcase##_BLOCK_SIZE,                               \
+                                                 PTLS_##name_upcase##_DIGEST_SIZE, name##_create, PTLS_ZERO_DIGEST_##name_upcase};
+DEFINE_HASH(sha256, SHA256, PSA_ALG_SHA_256);
+DEFINE_HASH(sha512, SHA512, PSA_ALG_SHA_512);
+#if defined(MBEDTLS_SHA384_C)
+DEFINE_HASH(sha384, SHA384, PSA_ALG_SHA_384);
+#endif
+
+/**
+ * Generic implementation of a cipher using the PSA API
+ */
+struct st_ptls_mbedtls_cipher_context_t {
+    ptls_cipher_context_t super;
+    psa_algorithm_t alg;
+    unsigned is_enc : 1;
+    unsigned is_op_in_progress : 1;
+    mbedtls_svc_key_id_t key;
+    psa_cipher_operation_t op;
+};
+
+static void cipher_init(ptls_cipher_context_t *_ctx, const void *iv)
+{
+    struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
+
+    if (ctx->is_op_in_progress) {
+        psa_cipher_abort(&ctx->op);
+        ctx->is_op_in_progress = 0;
+    }
+
+    ctx->op = psa_cipher_operation_init();
+    if (ctx->is_enc) {
+        CALL_WITH_CHECK(psa_cipher_encrypt_setup, &ctx->op, ctx->key, ctx->alg);
+    } else {
+        CALL_WITH_CHECK(psa_cipher_decrypt_setup, &ctx->op, ctx->key, ctx->alg);
+    }
+    ctx->is_op_in_progress = 1;
+    if (ctx->super.algo->iv_size > 0)
+        CALL_WITH_CHECK(psa_cipher_set_iv, &ctx->op, iv, ctx->super.algo->iv_size);
+}
+
+static void cipher_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
+{
+    struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
+    size_t unused = 0;
+
+    CALL_WITH_CHECK(psa_cipher_update, &ctx->op, input, len, output, len, &unused);
+}
+
+static void cipher_dispose(ptls_cipher_context_t *_ctx)
+{
+    struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
+
+    if (ctx->is_op_in_progress)
+        psa_cipher_abort(&ctx->op);
+    psa_destroy_key(ctx->key);
+}
+
+static int cipher_setup(ptls_cipher_context_t *_ctx, int is_enc, const void *key_bytes, psa_algorithm_t alg,
+                        psa_key_type_t key_type)
+{
+    struct st_ptls_mbedtls_cipher_context_t *ctx = (struct st_ptls_mbedtls_cipher_context_t *)_ctx;
+
+    { /* import key or fail immediately */
+        psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+        psa_set_key_usage_flags(&attributes, is_enc ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_DECRYPT);
+        psa_set_key_algorithm(&attributes, alg);
+        psa_set_key_type(&attributes, key_type);
+        psa_set_key_bits(&attributes, ctx->super.algo->key_size * 8);
+        if (psa_import_key(&attributes, key_bytes, ctx->super.algo->key_size, &ctx->key) != PSA_SUCCESS)
+            return PTLS_ERROR_LIBRARY;
+    }
+
+    /* init the rest that are guaranteed to succeed */
+    ctx->super.do_dispose = cipher_dispose;
+    ctx->super.do_init = cipher_init;
+    ctx->super.do_transform = cipher_transform;
+    ctx->alg = alg;
+    ctx->is_enc = is_enc;
+    ctx->is_op_in_progress = 0;
+    ctx->op = psa_cipher_operation_init();
+
+    return 0;
+}
+
+static int ecb_setup(ptls_cipher_context_t *ctx, int is_enc, const void *key_bytes, psa_key_type_t key_type)
+{
+    int ret;
+
+    if ((ret = cipher_setup(ctx, is_enc, key_bytes, PSA_ALG_ECB_NO_PADDING, key_type)) != 0)
+        return ret;
+    /* ECB mode does not necessary call `ptls_cipher_init` */
+    cipher_init(ctx, NULL);
+
+    return 0;
+}
+
+static int setup_aes128ecb(ptls_cipher_context_t *ctx, int is_enc, const void *key_bytes)
+{
+    return ecb_setup(ctx, is_enc, key_bytes, PSA_KEY_TYPE_AES);
+}
+
+ptls_cipher_algorithm_t ptls_mbedtls_aes128ecb = {
+    "AES128-ECB",   PTLS_AES128_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct st_ptls_mbedtls_cipher_context_t),
+    setup_aes128ecb};
+
+static int setup_aes256ecb(ptls_cipher_context_t *ctx, int is_enc, const void *key_bytes)
+{
+    return ecb_setup(ctx, is_enc, key_bytes, PSA_KEY_TYPE_AES);
+}
+
+ptls_cipher_algorithm_t ptls_mbedtls_aes256ecb = {
+    "AES256-ECB",   PTLS_AES256_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 0 /* iv size */, sizeof(struct st_ptls_mbedtls_cipher_context_t),
+    setup_aes256ecb};
+
+static int setup_aes128ctr(ptls_cipher_context_t *ctx, int is_enc, const void *key_bytes)
+{
+    return cipher_setup(ctx, is_enc, key_bytes, PSA_ALG_CTR, PSA_KEY_TYPE_AES);
+}
+
+ptls_cipher_algorithm_t ptls_mbedtls_aes128ctr = {
+    "AES128-CTR",   PTLS_AES128_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 16 /* iv size */, sizeof(struct st_ptls_mbedtls_cipher_context_t),
+    setup_aes128ctr};
+
+static int setup_aes256ctr(ptls_cipher_context_t *ctx, int is_enc, const void *key_bytes)
+{
+    return cipher_setup(ctx, is_enc, key_bytes, PSA_ALG_CTR, PSA_KEY_TYPE_AES);
+}
+
+ptls_cipher_algorithm_t ptls_mbedtls_aes256ctr = {
+    "AES128-CTR",   PTLS_AES256_KEY_SIZE, PTLS_AES_BLOCK_SIZE, 16 /* iv size */, sizeof(struct st_ptls_mbedtls_cipher_context_t),
+    setup_aes256ctr};
+
+#if 0
+/* CHACHA20 backend using PSA API is disabled for now, as there seems to be an issue when setting the 16 bytes long IV that we
+ * need. */
+static int setup_chacha20(ptls_cipher_context_t *ctx, int is_enc, const void *key_bytes)
+{
+    return cipher_setup(ctx, is_enc, key_bytes, PSA_ALG_CTR, PSA_KEY_TYPE_CHACHA20);
+}
+
+ptls_cipher_algorithm_t ptls_mbedtls_chacha20 = {
+    "CHACHA20", PTLS_CHACHA20_KEY_SIZE, 1 /* block size */, PTLS_CHACHA20_IV_SIZE, sizeof(struct st_ptls_mbedtls_cipher_context_t),
+    setup_chacha20};
+#else
+/* Implementation of ChaCha20 using the low level ChaCha20 API.
+ * TODO: remove this and the reference to chacha20.h as soon as the IV bug in the generic implementation is fixed. */
+struct st_ptls_mbedtls_chacha20_context_t {
+    ptls_cipher_context_t super;
+    mbedtls_chacha20_context mctx;
+};
+
+static void chacha20_init(ptls_cipher_context_t *_ctx, const void *v_iv)
+{
+    struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
+    const uint8_t *iv = (const uint8_t *)v_iv;
+    uint32_t ctr = iv[0] | ((uint32_t)iv[1] << 8) | ((uint32_t)iv[2] << 16) | ((uint32_t)iv[3] << 24);
+
+    int ret = mbedtls_chacha20_starts(&ctx->mctx, (const uint8_t *)(iv + 4), ctr);
+    if (ret != 0)
+        PSA_FUNC_FAILED(mbedtls_chacha20_starts, ret);
+}
+
+static void chacha20_transform(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
+{
+    struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
+
+    int ret = mbedtls_chacha20_update(&ctx->mctx, len, (const uint8_t *)input, (uint8_t *)output);
+    if (ret != 0)
+        PSA_FUNC_FAILED(mbedtls_chacha20_update, ret);
+}
+
+static void chacha20_dispose(ptls_cipher_context_t *_ctx)
+{
+    struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
+
+    mbedtls_chacha20_free(&ctx->mctx);
+}
+
+static int setup_chacha20(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
+{
+    struct st_ptls_mbedtls_chacha20_context_t *ctx = (struct st_ptls_mbedtls_chacha20_context_t *)_ctx;
+
+    mbedtls_chacha20_init(&ctx->mctx);
+    if (mbedtls_chacha20_setkey(&ctx->mctx, key) != 0)
+        return PTLS_ERROR_LIBRARY;
+
+    ctx->super.do_dispose = chacha20_dispose;
+    ctx->super.do_init = chacha20_init;
+    ctx->super.do_transform = chacha20_transform;
+
+    return 0;
+}
+
+ptls_cipher_algorithm_t ptls_mbedtls_chacha20 = {"CHACHA20",
+                                                 PTLS_CHACHA20_KEY_SIZE,
+                                                 1 /* block size */,
+                                                 PTLS_CHACHA20_IV_SIZE,
+                                                 sizeof(struct st_ptls_mbedtls_chacha20_context_t),
+                                                 setup_chacha20};
+#endif
+
+struct ptls_mbedtls_aead_context_t {
+    struct st_ptls_aead_context_t super;
+    uint8_t static_iv[PTLS_MAX_IV_SIZE];
+    psa_algorithm_t alg;
+    psa_key_id_t key;
+};
+
+static void aead_dispose_crypto(struct st_ptls_aead_context_t *_ctx)
+{
+    struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
+
+    psa_destroy_key(ctx->key);
+}
+
+static void aead_get_iv(ptls_aead_context_t *_ctx, void *iv)
+{
+    struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
+
+    memcpy(iv, ctx->static_iv, ctx->super.algo->iv_size);
+}
+
+static void aead_set_iv(ptls_aead_context_t *_ctx, const void *iv)
+{
+    struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
+
+    memcpy(ctx->static_iv, iv, ctx->super.algo->iv_size);
+}
+
+static void aead_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)
+{
+    struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
+    psa_aead_operation_t op = psa_aead_operation_init();
+    uint8_t *dst = output, iv[PTLS_MAX_IV_SIZE], tag[PSA_AEAD_TAG_MAX_SIZE];
+    size_t outlen, taglen;
+
+    /* setup op */
+    CALL_WITH_CHECK(psa_aead_encrypt_setup, &op, ctx->key, ctx->alg);
+    ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq);
+    CALL_WITH_CHECK(psa_aead_set_nonce, &op, iv, ctx->super.algo->iv_size);
+    CALL_WITH_CHECK(psa_aead_update_ad, &op, aad, aadlen);
+
+    /* encrypt */
+    for (size_t i = 0; i < incnt; i++) {
+        CALL_WITH_CHECK(psa_aead_update, &op, input[i].base, input[i].len, dst, SIZE_MAX, &outlen);
+        dst += outlen;
+    }
+    CALL_WITH_CHECK(psa_aead_finish, &op, dst, SIZE_MAX, &outlen, tag, sizeof(tag), &taglen);
+    dst += outlen;
+    memcpy(dst, tag, taglen);
+
+    /* destroy op */
+    psa_aead_abort(&op);
+}
+
+static size_t aead_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)
+{
+    struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
+    uint8_t iv[PTLS_MAX_IV_SIZE];
+    size_t outlen;
+
+    ptls_aead__build_iv(ctx->super.algo, iv, ctx->static_iv, seq);
+
+    psa_status_t ret =
+        psa_aead_decrypt(ctx->key, ctx->alg, iv, ctx->super.algo->iv_size, aad, aadlen, input, inlen, output, inlen, &outlen);
+    switch (ret) {
+    case PSA_SUCCESS:
+        break;
+    case PSA_ERROR_INVALID_SIGNATURE:
+        outlen = SIZE_MAX;
+        break;
+    default:
+        PSA_FUNC_FAILED(psa_aead_decrypt, ret);
+        break;
+    }
+
+    return outlen;
+}
+
+static int aead_setup(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv, psa_algorithm_t psa_alg,
+                      size_t key_bits, psa_key_type_t key_type)
+{
+    struct ptls_mbedtls_aead_context_t *ctx = (struct ptls_mbedtls_aead_context_t *)_ctx;
+
+    { /* setup key */
+        psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+        psa_set_key_usage_flags(&attributes, is_enc ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_DECRYPT);
+        psa_set_key_algorithm(&attributes, psa_alg);
+        psa_set_key_type(&attributes, key_type);
+        psa_set_key_bits(&attributes, key_bits);
+        if (psa_import_key(&attributes, key_bytes, key_bits / 8, &ctx->key) != PSA_SUCCESS)
+            return PTLS_ERROR_LIBRARY;
+    }
+
+    /* setup the rest */
+    ctx->super.dispose_crypto = aead_dispose_crypto;
+    ctx->super.do_get_iv = aead_get_iv;
+    ctx->super.do_set_iv = aead_set_iv;
+    if (is_enc) {
+        ctx->super.do_encrypt = ptls_aead__do_encrypt;
+        ctx->super.do_encrypt_v = aead_encrypt_v;
+    } else {
+        ctx->super.do_decrypt = aead_decrypt;
+    }
+    memcpy(ctx->static_iv, iv, ctx->super.algo->iv_size);
+    ctx->alg = psa_alg;
+
+    return 0;
+}
+
+static int aead_setup_aes128gcm(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv)
+{
+    return aead_setup(_ctx, is_enc, key_bytes, iv, PSA_ALG_GCM, 128, PSA_KEY_TYPE_AES);
+}
+
+ptls_aead_algorithm_t ptls_mbedtls_aes128gcm = {"AES128-GCM",
+                                                PTLS_AESGCM_CONFIDENTIALITY_LIMIT,
+                                                PTLS_AESGCM_INTEGRITY_LIMIT,
+                                                &ptls_mbedtls_aes128ctr,
+                                                &ptls_mbedtls_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},
+                                                0,
+                                                0,
+                                                sizeof(struct ptls_mbedtls_aead_context_t),
+                                                aead_setup_aes128gcm};
+
+ptls_cipher_suite_t ptls_mbedtls_aes128gcmsha256 = {.id = PTLS_CIPHER_SUITE_AES_128_GCM_SHA256,
+                                                    .name = PTLS_CIPHER_SUITE_NAME_AES_128_GCM_SHA256,
+                                                    .aead = &ptls_mbedtls_aes128gcm,
+                                                    .hash = &ptls_mbedtls_sha256};
+
+static int aead_setup_aes256gcm(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv)
+{
+    return aead_setup(_ctx, is_enc, key_bytes, iv, PSA_ALG_GCM, 256, PSA_KEY_TYPE_AES);
+}
+
+ptls_aead_algorithm_t ptls_mbedtls_aes256gcm = {"AES256-GCM",
+                                                PTLS_AESGCM_CONFIDENTIALITY_LIMIT,
+                                                PTLS_AESGCM_INTEGRITY_LIMIT,
+                                                &ptls_mbedtls_aes256ctr,
+                                                &ptls_mbedtls_aes256ecb,
+                                                PTLS_AES256_KEY_SIZE,
+                                                PTLS_AESGCM_IV_SIZE,
+                                                PTLS_AESGCM_TAG_SIZE,
+                                                {PTLS_TLS12_AESGCM_FIXED_IV_SIZE, PTLS_TLS12_AESGCM_RECORD_IV_SIZE},
+                                                0,
+                                                0,
+                                                sizeof(struct ptls_mbedtls_aead_context_t),
+                                                aead_setup_aes256gcm};
+
+#if defined(MBEDTLS_SHA384_C)
+ptls_cipher_suite_t ptls_mbedtls_aes256gcmsha384 = {.id = PTLS_CIPHER_SUITE_AES_256_GCM_SHA384,
+                                                    .name = PTLS_CIPHER_SUITE_NAME_AES_256_GCM_SHA384,
+                                                    .aead = &ptls_mbedtls_aes256gcm,
+                                                    .hash = &ptls_mbedtls_sha384};
+#endif
+
+static int aead_setup_chacha20poly1305(ptls_aead_context_t *_ctx, int is_enc, const void *key_bytes, const void *iv)
+{
+    return aead_setup(_ctx, is_enc, key_bytes, iv, PSA_ALG_CHACHA20_POLY1305, 256, PSA_KEY_TYPE_CHACHA20);
+}
+
+ptls_aead_algorithm_t ptls_mbedtls_chacha20poly1305 = {"CHACHA20-POLY1305",
+                                                       PTLS_CHACHA20POLY1305_CONFIDENTIALITY_LIMIT,
+                                                       PTLS_CHACHA20POLY1305_INTEGRITY_LIMIT,
+                                                       &ptls_mbedtls_chacha20,
+                                                       NULL,
+                                                       PTLS_CHACHA20_KEY_SIZE,
+                                                       PTLS_CHACHA20POLY1305_IV_SIZE,
+                                                       PTLS_CHACHA20POLY1305_TAG_SIZE,
+                                                       {PTLS_TLS12_CHACHAPOLY_FIXED_IV_SIZE, PTLS_TLS12_CHACHAPOLY_RECORD_IV_SIZE},
+                                                       0,
+                                                       0,
+                                                       sizeof(struct ptls_mbedtls_aead_context_t),
+                                                       aead_setup_chacha20poly1305};
+
+ptls_cipher_suite_t ptls_mbedtls_chacha20poly1305sha256 = {.id = PTLS_CIPHER_SUITE_CHACHA20_POLY1305_SHA256,
+                                                           .name = PTLS_CIPHER_SUITE_NAME_CHACHA20_POLY1305_SHA256,
+                                                           .aead = &ptls_mbedtls_chacha20poly1305,
+                                                           .hash = &ptls_mbedtls_sha256};
+
+ptls_cipher_suite_t *ptls_mbedtls_cipher_suites[] = {
+#if defined(MBEDTLS_SHA384_C)
+    &ptls_mbedtls_aes256gcmsha384,
+#endif
+    &ptls_mbedtls_aes128gcmsha256, &ptls_mbedtls_chacha20poly1305sha256, NULL};
+
+#define PTLS_MBEDTLS_ECDH_PUBKEY_MAX 129
+
+static const struct ptls_mbedtls_key_exchange_params_t {
+    psa_algorithm_t alg;
+    psa_ecc_family_t curve;
+    size_t curve_bits;
+    size_t secret_size;
+} secp256r1_params = {PSA_ALG_ECDH, PSA_ECC_FAMILY_SECP_R1, 256, 32},
+  x25519_params = {PSA_ALG_ECDH, PSA_ECC_FAMILY_MONTGOMERY, 255, 32};
+
+struct ptls_mbedtls_key_exchange_context_t {
+    ptls_key_exchange_context_t super;
+    const struct ptls_mbedtls_key_exchange_params_t *params;
+    psa_key_id_t private_key;
+    uint8_t pubkeybuf[PTLS_MBEDTLS_ECDH_PUBKEY_MAX];
+};
+
+/**
+ * Generates a private key. For now, we only support ECC.
+ */
+static int generate_private_key(psa_key_id_t *private_key, const struct ptls_mbedtls_key_exchange_params_t *params)
+{
+    psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
+    int ret = 0;
+
+    psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE);
+    psa_set_key_algorithm(&attributes, params->alg);
+    psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(params->curve));
+    psa_set_key_bits(&attributes, params->curve_bits);
+    if (psa_generate_key(&attributes, private_key) != 0) {
+        ret = -1;
+    }
+    return ret;
+}
+
+static int key_exchange_on_exchange(struct st_ptls_key_exchange_context_t **_keyex, int release, ptls_iovec_t *secret,
+                                    ptls_iovec_t peerkey)
+{
+    struct ptls_mbedtls_key_exchange_context_t *keyex = (struct ptls_mbedtls_key_exchange_context_t *)*_keyex;
+    int ret = 0;
+
+    if (secret == NULL)
+        goto Exit;
+
+    /* derive shared secret */
+    if ((secret->base = malloc(keyex->params->secret_size)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    if (psa_raw_key_agreement(keyex->params->alg, keyex->private_key, peerkey.base, peerkey.len, secret->base,
+                              keyex->params->secret_size, &secret->len) != 0) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+    assert(keyex->params->secret_size == secret->len);
+    ret = 0;
+
+Exit:
+    if (ret != 0 && secret != NULL) {
+        free(secret->base);
+        *secret = ptls_iovec_init(NULL, 0);
+    }
+    if (release) {
+        psa_destroy_key(keyex->private_key);
+        free(keyex);
+        *_keyex = NULL;
+    }
+    return ret;
+}
+
+static int key_exchange_create(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx,
+                               const struct ptls_mbedtls_key_exchange_params_t *params)
+{
+    struct ptls_mbedtls_key_exchange_context_t *keyex;
+
+    *ctx = NULL;
+
+    /* setup context */
+    if ((keyex = malloc(sizeof(*keyex))) == NULL)
+        return PTLS_ERROR_NO_MEMORY;
+    *keyex = (struct ptls_mbedtls_key_exchange_context_t){
+        .super.algo = algo,
+        .super.pubkey.base = keyex->pubkeybuf,
+        .super.on_exchange = key_exchange_on_exchange,
+        .params = params,
+    };
+
+    /* generate private key */
+    if (generate_private_key(&keyex->private_key, keyex->params) != 0) {
+        free(keyex);
+        return PTLS_ERROR_LIBRARY;
+    }
+    { /* export public key */
+        psa_status_t ret =
+            psa_export_public_key(keyex->private_key, keyex->pubkeybuf, sizeof(keyex->pubkeybuf), &keyex->super.pubkey.len);
+        if (ret != 0)
+            PSA_FUNC_FAILED(psa_export_public_key, ret);
+    }
+
+    *ctx = &keyex->super;
+    return 0;
+}
+
+static int key_exchange_exchange(ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret,
+                                 ptls_iovec_t peerkey, const struct ptls_mbedtls_key_exchange_params_t *params)
+{
+    psa_key_id_t private_key;
+    int ret;
+
+    *pubkey = ptls_iovec_init(NULL, 0);
+    *secret = ptls_iovec_init(NULL, 0);
+
+    /* generate private key (and return immediately upon failure) */
+    if (generate_private_key(&private_key, params) != 0)
+        return PTLS_ERROR_LIBRARY;
+
+    /* allocate buffers */
+    if ((secret->base = malloc(params->secret_size)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+    if ((pubkey->base = malloc(PTLS_MBEDTLS_ECDH_PUBKEY_MAX)) == NULL) {
+        ret = PTLS_ERROR_NO_MEMORY;
+        goto Exit;
+    }
+
+    /* export public key and call key agrement function */
+    if (psa_export_public_key(private_key, pubkey->base, PTLS_MBEDTLS_ECDH_PUBKEY_MAX, &pubkey->len) != 0 ||
+        psa_raw_key_agreement(params->alg, private_key, peerkey.base, peerkey.len, secret->base, params->secret_size,
+                              &secret->len) != 0) {
+        ret = PTLS_ERROR_LIBRARY;
+        goto Exit;
+    }
+
+    ret = 0;
+
+Exit:
+    if (ret != 0) {
+        free(pubkey->base);
+        *pubkey = ptls_iovec_init(NULL, 0);
+        free(secret->base);
+        *secret = ptls_iovec_init(NULL, 0);
+    }
+    psa_destroy_key(private_key);
+
+    return ret;
+}
+
+static int secp256r1_create(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx)
+{
+    return key_exchange_create(algo, ctx, &secp256r1_params);
+}
+
+static int secp256r1_exchange(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret,
+                              ptls_iovec_t peerkey)
+{
+    return key_exchange_exchange(algo, pubkey, secret, peerkey, &secp256r1_params);
+}
+
+ptls_key_exchange_algorithm_t ptls_mbedtls_secp256r1 = {
+    .id = PTLS_GROUP_SECP256R1, .name = PTLS_GROUP_NAME_SECP256R1, .create = secp256r1_create, .exchange = secp256r1_exchange};
+
+static int x25519_create(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **ctx)
+{
+    return key_exchange_create(algo, ctx, &x25519_params);
+}
+
+static int x25519_exchange(const struct st_ptls_key_exchange_algorithm_t *algo, ptls_iovec_t *pubkey, ptls_iovec_t *secret,
+                           ptls_iovec_t peerkey)
+{
+    return key_exchange_exchange(algo, pubkey, secret, peerkey, &x25519_params);
+}
+
+ptls_key_exchange_algorithm_t ptls_mbedtls_x25519 = {
+    .id = PTLS_GROUP_X25519, .name = PTLS_GROUP_NAME_X25519, .create = x25519_create, .exchange = x25519_exchange};
+
+ptls_key_exchange_algorithm_t *ptls_mbedtls_key_exchanges[] = {&ptls_mbedtls_secp256r1, NULL};
diff --git a/lib/openssl.c b/lib/openssl.c
index b035339..70a3e7e 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -525,22 +525,35 @@
     }
 
 #ifdef OPENSSL_IS_BORINGSSL
+#define X25519_KEY_SIZE 32
     if (ctx->super.algo->id == PTLS_GROUP_X25519) {
-        secret->len = peerkey.len;
-        if ((secret->base = malloc(secret->len)) == NULL) {
+        /* allocate memory to return secret */
+        if ((secret->base = malloc(X25519_KEY_SIZE)) == NULL) {
             ret = PTLS_ERROR_NO_MEMORY;
             goto Exit;
         }
-        uint8_t sk_raw[32];
+        secret->len = X25519_KEY_SIZE;
+        /* fetch raw key and derive the secret */
+        uint8_t sk_raw[X25519_KEY_SIZE];
         size_t sk_raw_len = sizeof(sk_raw);
         if (EVP_PKEY_get_raw_private_key(ctx->privkey, sk_raw, &sk_raw_len) != 1) {
             ret = PTLS_ERROR_LIBRARY;
             goto Exit;
         }
+        assert(sk_raw_len == sizeof(sk_raw));
         X25519(secret->base, sk_raw, peerkey.base);
+        ptls_clear_memory(sk_raw, sizeof(sk_raw));
+        /* check bad key */
+        static const uint8_t zeros[X25519_KEY_SIZE] = {0};
+        if (ptls_mem_equal(secret->base, zeros, X25519_KEY_SIZE)) {
+            ret = PTLS_ERROR_INCOMPATIBLE_KEY;
+            goto Exit;
+        }
+        /* success */
         ret = 0;
         goto Exit;
     }
+#undef X25519_KEY_SIZE
 #endif
 
     if ((evppeer = EVP_PKEY_new()) == NULL) {
@@ -595,6 +608,9 @@
     return ret;
 }
 
+/**
+ * Upon success, ownership of `pkey` is transferred to the object being created. Otherwise, the refcount remains unchanged.
+ */
 static int evp_keyex_init(ptls_key_exchange_algorithm_t *algo, ptls_key_exchange_context_t **_ctx, EVP_PKEY *pkey)
 {
     struct st_evp_keyex_context_t *ctx = NULL;
@@ -617,8 +633,10 @@
     *_ctx = &ctx->super;
     ret = 0;
 Exit:
-    if (ret != 0 && ctx != NULL)
+    if (ret != 0 && ctx != NULL) {
+        ctx->privkey = NULL; /* do not decrement refcount of pkey in case of error */
         evp_keyex_free(ctx);
+    }
     return ret;
 }
 
@@ -2202,10 +2220,10 @@
     .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,
+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_sha384};
+                                                    .hash = &ptls_openssl_sha512};
 #endif
 
 
@@ -2222,7 +2240,7 @@
 
 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,
 
diff --git a/lib/picotls.c b/lib/picotls.c
index e2a3e04..9ba15c7 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)
 
@@ -2376,7 +2372,10 @@
 
     /* 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 ((tls->key_schedule = key_schedule_new(tls->cipher_suite, tls->ctx->cipher_suites, tls->ech.aead != NULL)) == NULL) {
+            ret = PTLS_ERROR_NO_MEMORY;
+            goto Exit;
+        }
         if ((ret = key_schedule_extract(tls->key_schedule, resumption_secret)) != 0)
             goto Exit;
     }
@@ -3573,10 +3572,10 @@
         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) {
+    /* In TLS versions 1.2 and earlier CH might not have an extensions block (or they might, see what OpenSSL 1.0.0 sends); so bail
+     * out if that is the case 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 (src == end) {
         ret = 0;
         goto Exit;
     }
@@ -4376,7 +4375,10 @@
             goto Exit;
         if (!is_second_flight) {
             tls->cipher_suite = cs;
-            tls->key_schedule = key_schedule_new(cs, NULL, 0);
+            if ((tls->key_schedule = key_schedule_new(cs, NULL, 0)) == NULL) {
+                ret = PTLS_ERROR_NO_MEMORY;
+                goto Exit;
+            }
         } else {
             if (tls->cipher_suite != cs) {
                 ret = PTLS_ALERT_HANDSHAKE_FAILURE;
diff --git a/picotls.xcodeproj/project.pbxproj b/picotls.xcodeproj/project.pbxproj
index 6882e19..338535e 100644
--- a/picotls.xcodeproj/project.pbxproj
+++ b/picotls.xcodeproj/project.pbxproj
@@ -205,6 +205,14 @@
 		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>"; };
+		0883D32B2AF601B900B711CC /* mbedtls.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mbedtls.c; sourceTree = "<group>"; };
+		0883D32C2AF601CB00B711CC /* mbedtls.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mbedtls.h; sourceTree = "<group>"; };
+		0883D32D2AF6020300B711CC /* ptlsbench.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ptlsbench.c; sourceTree = "<group>"; };
 		08A835E12995E04100D872CE /* chacha20poly1305.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chacha20poly1305.h; sourceTree = "<group>"; };
 		08A835EB2996971300D872CE /* boringssl-adjust.cmake */ = {isa = PBXFileReference; lastKnownFileType = text; path = "boringssl-adjust.cmake"; sourceTree = "<group>"; };
 		08B3298229419DFC009D6766 /* ech-live.t */ = {isa = PBXFileReference; lastKnownFileType = text; path = "ech-live.t"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.perl; };
@@ -402,7 +410,6 @@
 				106530BD1D998624005B2C60 /* lib */,
 				E95EBCC2227E82BA0022C32D /* misc */,
 				E95EBCC0227B71170022C32D /* picotls-probes.d */,
-				E992F79920E99A080008154D /* src */,
 				106530C41D9B1A0E005B2C60 /* t */,
 				106530B31D9985E0005B2C60 /* Products */,
 				E973651D246E37300039AA49 /* Frameworks */,
@@ -443,6 +450,8 @@
 				E97577022212405D00D1EF74 /* ffx.c */,
 				E9B43DBF24619D1700824E51 /* fusion.c */,
 				08F0FDF52910F67A00EE657D /* hpke.c */,
+				085BDAE62B1D6238002851EA /* libaegis.h */,
+				0883D32B2AF601B900B711CC /* mbedtls.c */,
 				E949EF272073629300511ECA /* minicrypto-pem.c */,
 				106530C21D9B004B005B2C60 /* openssl.c */,
 				E99B75DF1F5CDDB500CF503E /* pembase64.c */,
@@ -461,9 +470,11 @@
 				08B3298229419DFC009D6766 /* ech-live.t */,
 				E9B43DE224619D7E00824E51 /* fusion.c */,
 				081F00C92918823200534A86 /* hpke.c */,
+				0883D32A2AF601A700B711CC /* mbedtls.c */,
 				1059003D1DC8D4E300FB4085 /* minicrypto.c */,
 				106530C51D9B1A98005B2C60 /* openssl.c */,
 				106530E91D9B7C13005B2C60 /* picotls.c */,
+				0883D32D2AF6020300B711CC /* ptlsbench.c */,
 				106530E61D9B7AF6005B2C60 /* test.h */,
 				E9E3849C1F0748DD00D50990 /* util.h */,
 			);
@@ -496,6 +507,7 @@
 				E9E4B1292180514000514B47 /* certificate_compression.h */,
 				E97577002212405300D1EF74 /* ffx.h */,
 				E9B43DE62461A06800824E51 /* fusion.h */,
+				0883D32C2AF601CB00B711CC /* mbedtls.h */,
 				1059004F1DC8D64E00FB4085 /* minicrypto.h */,
 				106530ED1D9CEFF7005B2C60 /* openssl.h */,
 				081F00CD291A358800534A86 /* pembase64.h */,
@@ -527,6 +539,8 @@
 			children = (
 				08A835EB2996971300D872CE /* boringssl-adjust.cmake */,
 				E95EBCCA227EA0180022C32D /* dtrace-utils.cmake */,
+				087F1A732B034AEB00E81AC1 /* FindMbedTLS.cmake */,
+				0883D3272AEF8F2500B711CC /* fusion.cmake */,
 			);
 			path = cmake;
 			sourceTree = "<group>";
@@ -538,13 +552,6 @@
 			name = Frameworks;
 			sourceTree = "<group>";
 		};
-		E992F79920E99A080008154D /* src */ = {
-			isa = PBXGroup;
-			children = (
-			);
-			path = src;
-			sourceTree = "<group>";
-		};
 		E9F20BDF22E34B210018D260 /* cifra */ = {
 			isa = PBXGroup;
 			children = (
@@ -552,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/mbedtls.c b/t/mbedtls.c
new file mode 100644
index 0000000..d3651de
--- /dev/null
+++ b/t/mbedtls.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2023, Christian Huitema
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifdef _WINDOWS
+#include "wincompat.h"
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <picotls.h>
+#include "picotls/mbedtls.h"
+#include "picotls/minicrypto.h"
+#include "../deps/picotest/picotest.h"
+#include "test.h"
+
+static int random_trial()
+{
+    /* The random test is just trying to check that we call the API properly.
+     * This is done by getting a vector of 1021 bytes, computing the sum of
+     * all values, and comparing to theoretical min and max,
+     * computed as average +- 8*standard deviation for sum of 1021 terms.
+     * 8 random deviations results in an extremely low probability of random
+     * failure.
+     * Note that this does not actually test the random generator.
+     */
+
+    uint8_t buf[1021];
+    uint64_t sum = 0;
+    const uint64_t max_sum_1021 = 149505;
+    const uint64_t min_sum_1021 = 110849;
+    int ret = 0;
+
+    ptls_mbedtls_random_bytes(buf, sizeof(buf));
+    for (size_t i = 0; i < sizeof(buf); i++) {
+        sum += buf[i];
+    }
+    if (sum > max_sum_1021 || sum < min_sum_1021) {
+        ret = -1;
+    }
+
+    return ret;
+}
+
+static void test_random(void)
+{
+    if (random_trial() != 0) {
+        ok(!"fail");
+        return;
+    }
+    ok(!!"success");
+}
+
+static void test_secp256r1(void)
+{
+    test_key_exchange(&ptls_mbedtls_secp256r1, &ptls_minicrypto_secp256r1);
+    test_key_exchange(&ptls_minicrypto_secp256r1, &ptls_mbedtls_secp256r1);
+}
+
+static void test_x25519(void)
+{
+    test_key_exchange(&ptls_mbedtls_x25519, &ptls_minicrypto_x25519);
+    test_key_exchange(&ptls_minicrypto_x25519, &ptls_mbedtls_x25519);
+}
+
+static void test_key_exchanges(void)
+{
+    subtest("secp256r1", test_secp256r1);
+    subtest("x25519", test_x25519);
+}
+
+DEFINE_FFX_AES128_ALGORITHMS(mbedtls);
+DEFINE_FFX_CHACHA20_ALGORITHMS(mbedtls);
+
+int main(int argc, char **argv)
+{
+    /* Initialize the PSA crypto library. */
+    if (psa_crypto_init() != PSA_SUCCESS) {
+        note("psa_crypto_init fails.");
+        return done_testing();
+    }
+
+    /* Test of the port of the mbedtls random generator */
+    subtest("random", test_random);
+    subtest("key_exchanges", test_key_exchanges);
+
+    ADD_FFX_AES128_ALGORITHMS(mbedtls);
+    ADD_FFX_CHACHA20_ALGORITHMS(mbedtls);
+
+    /* minicrypto contexts used as peer for valiation */
+    ptls_iovec_t secp256r1_certificate = ptls_iovec_init(SECP256R1_CERTIFICATE, sizeof(SECP256R1_CERTIFICATE) - 1);
+    ptls_minicrypto_secp256r1sha256_sign_certificate_t minicrypto_sign_certificate;
+    ptls_minicrypto_init_secp256r1sha256_sign_certificate(
+        &minicrypto_sign_certificate, ptls_iovec_init(SECP256R1_PRIVATE_KEY, sizeof(SECP256R1_PRIVATE_KEY) - 1));
+    ptls_context_t minicrypto_ctx = {ptls_minicrypto_random_bytes,
+                                     &ptls_get_time,
+                                     ptls_minicrypto_key_exchanges,
+                                     ptls_minicrypto_cipher_suites,
+                                     {&secp256r1_certificate, 1},
+                                     {{NULL}},
+                                     NULL,
+                                     NULL,
+                                     &minicrypto_sign_certificate.super};
+
+    /* context using mbedtls as backend; minicrypto is used for signing certificate as the mbedtls backend does not (yet) have the
+     * capability */
+    ptls_minicrypto_secp256r1sha256_sign_certificate_t mbedtls_sign_certificate;
+    ptls_minicrypto_init_secp256r1sha256_sign_certificate(
+        &mbedtls_sign_certificate, ptls_iovec_init(SECP256R1_PRIVATE_KEY, sizeof(SECP256R1_PRIVATE_KEY) - 1));
+    ptls_context_t mbedtls_ctx = {ptls_mbedtls_random_bytes,
+                                  &ptls_get_time,
+                                  ptls_mbedtls_key_exchanges,
+                                  ptls_mbedtls_cipher_suites,
+                                  {&secp256r1_certificate, 1},
+                                  {{NULL}},
+                                  NULL,
+                                  NULL,
+                                  &mbedtls_sign_certificate.super};
+
+    ctx = &mbedtls_ctx;
+    ctx_peer = &mbedtls_ctx;
+    subtest("selt-test", test_picotls);
+
+    ctx = &mbedtls_ctx;
+    ctx_peer = &minicrypto_ctx;
+    subtest("vs. minicrypto", test_picotls);
+
+    ctx = &minicrypto_ctx;
+    ctx_peer = &mbedtls_ctx;
+    subtest("minicrypto vs.", test_picotls);
+
+    /* Deinitialize the PSA crypto library. */
+    mbedtls_psa_crypto_free();
+
+    return done_testing();
+}
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 ecca51d..c82cc41 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -225,12 +225,8 @@
     /* encrypt */
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
-    ptls_aead_encrypt_init(c, 0, NULL, 0);
-    enc1len = ptls_aead_encrypt_update(c, enc1, src1, strlen(src1));
-    enc1len += ptls_aead_encrypt_final(c, enc1 + enc1len);
-    ptls_aead_encrypt_init(c, 1, NULL, 0);
-    enc2len = ptls_aead_encrypt_update(c, enc2, src2, strlen(src2));
-    enc2len += ptls_aead_encrypt_final(c, enc2 + enc2len);
+    enc1len = ptls_aead_encrypt(c, enc1, src1, strlen(src1), 0, NULL, 0);
+    enc2len = ptls_aead_encrypt(c, enc2, src2, strlen(src2), 1, NULL, 0);
     ptls_aead_free(c);
 
     c = ptls_aead_new(cs2->aead, cs2->hash, 0, traffic_secret, NULL);
@@ -275,6 +271,10 @@
     /* encrypt */
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
+    if (c->do_encrypt_init == NULL) {
+        note("new ciphers may omit support for init-update-final");
+        return;
+    }
     ptls_aead_encrypt_init(c, 0, NULL, 0);
     enclen = 0;
     for (size_t i = 0; text[i] != NULL; ++i)
@@ -311,9 +311,7 @@
     /* encrypt */
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
-    ptls_aead_encrypt_init(c, 123, aad, strlen(aad));
-    enclen = ptls_aead_encrypt_update(c, enc, src, strlen(src));
-    enclen += ptls_aead_encrypt_final(c, enc + enclen);
+    enclen = ptls_aead_encrypt(c, enc, src, strlen(src), 123, aad, strlen(aad));
     ptls_aead_free(c);
 
     /* decrypt */
@@ -340,9 +338,7 @@
     c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
     assert(c != NULL);
     ptls_aead_xor_iv(c, seq32, sizeof(seq32));
-    ptls_aead_encrypt_init(c, 123, aad, strlen(aad));
-    enclen = ptls_aead_encrypt_update(c, enc, src, strlen(src));
-    enclen += ptls_aead_encrypt_final(c, enc + enclen);
+    enclen = ptls_aead_encrypt(c, enc, src, strlen(src), 123, aad, strlen(aad));
     ptls_aead_free(c);
 
     /* decrypt */
@@ -510,8 +506,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);
@@ -2026,6 +2022,13 @@
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
         0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x32, 0x00, 0x0a, 0x00, 0x16,
         0x00, 0x13, 0x00, 0x09, 0x00, 0x15, 0x00, 0x12, 0x00, 0x03, 0x00, 0x08, 0x00, 0x14, 0x00, 0x11, 0x00, 0xff, 0x01, 0x00};
+    static const uint8_t tls12_no_exts[] = {
+        0x16, 0x03, 0x01, 0x00, 0x67, 0x01, 0x00, 0x00, 0x63, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xee, 0x8a, 0x29, 0xdd, 0xcf, 0x6d, 0x64, 0xfd, 0xd0, 0xcd,
+        0xa0, 0x9b, 0xc1, 0x32, 0x46, 0xbf, 0x53, 0xda, 0x29, 0x23, 0x81, 0x5f, 0x54, 0x1f, 0xbd, 0xe0, 0x8e, 0x97,
+        0x17, 0x5b, 0x03, 0x5d, 0x00, 0x1c, 0x00, 0xff, 0x00, 0x9c, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x0a, 0xc0, 0x09,
+        0xc0, 0x13, 0xc0, 0x14, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x9e, 0x00, 0x33, 0x00, 0x39, 0x00, 0x0a, 0x01, 0x00};
     /* client hello generated by openssl 1.0.0s; s_client -bugs -no_ticket -cipher DES-CBC-SHA -connect */
     static const uint8_t tls10_with_exts[] = {0x16, 0x03, 0x01, 0x00, 0x2f, 0x01, 0x00, 0x00, 0x2b, 0x03, 0x01, 0x63, 0xfc,
                                               0x5a, 0x16, 0xde, 0x7a, 0xfc, 0xc1, 0x0c, 0x54, 0x12, 0xa6, 0xd3, 0x8c, 0xcf,
@@ -2088,6 +2091,14 @@
     legacy_params = NULL;
 
     tls = ptls_new(ctx, 1);
+    len = sizeof(tls12_no_exts);
+    ret = ptls_handshake(tls, &sendbuf, tls12_no_exts, &len, NULL);
+    ptls_free(tls);
+    ok(ret == PTLS_ALERT_PROTOCOL_VERSION);
+    free(legacy_params);
+    legacy_params = NULL;
+
+    tls = ptls_new(ctx, 1);
     len = sizeof(tls10_with_exts);
     ret = ptls_handshake(tls, &sendbuf, tls10_with_exts, &len, NULL);
     ptls_free(tls);
@@ -2188,4 +2199,10 @@
     ret = ctx->on_exchange(&ctx, 1, NULL, ptls_iovec_init(NULL, 0));
     ok(ret == 0);
     ok(ctx == NULL);
+
+    /* test derivation failure. In case of X25519, the outcome is derived key becoming all-zero and rejected. In case of others, it
+     * is most likely that the provided key would be rejected. */
+    static uint8_t zeros[32] = {0};
+    ret = server->exchange(server, &server_pubkey, &server_secret, ptls_iovec_init(zeros, sizeof(zeros)));
+    ok(ret != 0);
 }
diff --git a/t/ptlsbench.c b/t/ptlsbench.c
index 1b27607..b0f525b 100644
--- a/t/ptlsbench.c
+++ b/t/ptlsbench.c
@@ -36,8 +36,10 @@
 #include "picotls/minicrypto.h"
 #include "picotls/openssl.h"
 #ifndef _WINDOWS
+#ifdef PTLS_HAVE_FUSION
 #include "picotls/fusion.h"
 #endif
+#endif
 #include <openssl/opensslv.h>
 
 #ifdef _WINDOWS
@@ -57,6 +59,10 @@
 #endif
 #endif
 
+#ifdef PTLS_HAVE_MBEDTLS
+#include "picotls/mbedtls.h"
+#endif
+
 /* Time in microseconds */
 static uint64_t bench_time()
 {
@@ -262,14 +268,24 @@
     {"ptlsbcrypt", "aes256gcm", &ptls_bcrypt_aes256gcm, &ptls_bcrypt_sha384, 1},
 #endif
 #if !defined(_WINDOWS)
+#ifdef PTLS_HAVE_FUSION
     {"fusion", "aes128gcm", &ptls_fusion_aes128gcm, &ptls_minicrypto_sha256, 1},
     {"fusion", "aes256gcm", &ptls_fusion_aes256gcm, &ptls_minicrypto_sha384, 1},
 #endif
+#endif
 #if PTLS_OPENSSL_HAVE_CHACHA20_POLY1305
     {"openssl", "chacha20poly1305", &ptls_openssl_chacha20poly1305, &ptls_minicrypto_sha256, 1},
 #endif
     {"openssl", "aes128gcm", &ptls_openssl_aes128gcm, &ptls_minicrypto_sha256, 1},
-    {"openssl", "aes256gcm", &ptls_openssl_aes256gcm, &ptls_minicrypto_sha384, 1}};
+    {"openssl", "aes256gcm", &ptls_openssl_aes256gcm, &ptls_minicrypto_sha384, 1},
+#ifdef PTLS_HAVE_MBEDTLS
+    {"mbedtls", "aes128gcm", &ptls_mbedtls_aes128gcm, &ptls_mbedtls_sha256, 1},
+#if defined(MBEDTLS_SHA384_C)
+    {"mbedtls", "aes256gcm", &ptls_mbedtls_aes256gcm, &ptls_mbedtls_sha384, 1},
+#endif
+    {"mbedtls", "chacha20poly1305", &ptls_mbedtls_chacha20poly1305, &ptls_mbedtls_sha256, 1},
+#endif
+};
 
 static size_t nb_aead_list = sizeof(aead_list) / sizeof(ptls_bench_entry_t);
 
@@ -323,6 +339,13 @@
     }
 #endif
 
+#ifdef PTLS_HAVE_MBEDTLS
+    if (psa_crypto_init() != PSA_SUCCESS) {
+        fprintf(stderr, "psa_crypto_init fails.\n");
+        exit(-1);
+    }
+#endif
+
     if (argc == 2 && strcmp(argv[1], "-f") == 0) {
         force_all_tests = 1;
     } else if (argc > 1) {
@@ -346,5 +369,10 @@
         printf("Unexpected value of test sum s = %llx\n", (unsigned long long)s);
     }
 
+#ifdef PTLS_HAVE_MBEDTLS
+    /* Deinitialize the PSA crypto library. */
+    mbedtls_psa_crypto_free();
+#endif
+
     return ret;
 }