summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crypto/CMakeLists.txt56
-rw-r--r--crypto/Makefile.am49
-rw-r--r--crypto/boringssl/.gitignore2
-rw-r--r--crypto/boringssl/CMakeLists.txt63
-rw-r--r--crypto/boringssl/Makefile.am39
-rw-r--r--crypto/boringssl/boringssl.c630
-rw-r--r--crypto/boringssl/libngtcp2_crypto_boringssl.pc.in33
-rw-r--r--crypto/gnutls/.gitignore1
-rw-r--r--crypto/gnutls/CMakeLists.txt85
-rw-r--r--crypto/gnutls/Makefile.am43
-rw-r--r--crypto/gnutls/gnutls.c644
-rw-r--r--crypto/gnutls/libngtcp2_crypto_gnutls.pc.in33
-rw-r--r--crypto/includes/CMakeLists.txt56
-rw-r--r--crypto/includes/Makefile.am45
-rw-r--r--crypto/includes/ngtcp2/ngtcp2_crypto.h893
-rw-r--r--crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h104
-rw-r--r--crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h107
-rw-r--r--crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h132
-rw-r--r--crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h246
-rw-r--r--crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h106
-rw-r--r--crypto/openssl/.gitignore1
-rw-r--r--crypto/openssl/CMakeLists.txt85
-rw-r--r--crypto/openssl/Makefile.am43
-rw-r--r--crypto/openssl/libngtcp2_crypto_openssl.pc.in33
-rw-r--r--crypto/openssl/openssl.c807
-rw-r--r--crypto/picotls/.gitignore2
-rw-r--r--crypto/picotls/CMakeLists.txt64
-rw-r--r--crypto/picotls/Makefile.am39
-rw-r--r--crypto/picotls/libngtcp2_crypto_picotls.pc.in33
-rw-r--r--crypto/picotls/picotls.c701
-rw-r--r--crypto/shared.c1418
-rw-r--r--crypto/shared.h350
-rw-r--r--crypto/wolfssl/.gitignore1
-rw-r--r--crypto/wolfssl/CMakeLists.txt83
-rw-r--r--crypto/wolfssl/Makefile.am43
-rw-r--r--crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in33
-rw-r--r--crypto/wolfssl/wolfssl.c534
37 files changed, 7637 insertions, 0 deletions
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
new file mode 100644
index 0000000..6ac8c74
--- /dev/null
+++ b/crypto/CMakeLists.txt
@@ -0,0 +1,56 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2
+
+# 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.
+
+if(HAVE_CRYPTO)
+ add_subdirectory(includes)
+endif()
+
+if(HAVE_OPENSSL)
+ add_subdirectory(openssl)
+elseif(ENABLE_OPENSSL)
+ message(WARNING "libngtcp2_crypto_openssl library is disabled due to lack of good OpenSSL")
+endif()
+
+if(HAVE_GNUTLS)
+ add_subdirectory(gnutls)
+elseif(ENABLE_GNUTLS)
+ message(WARNING "libngtcp2_crypto_gnutls library is disabled due to lack of good GnuTLS")
+endif()
+
+if(HAVE_BORINGSSL)
+ add_subdirectory(boringssl)
+elseif(ENABLE_BORINGSSL)
+ message(WARNING "libngtcp2_crypto_boringssl library is disabled due to lack of good BoringSSL")
+endif()
+
+if(HAVE_PICOTLS)
+ add_subdirectory(picotls)
+elseif(ENABLE_PICOTLS)
+ message(WARNING "libngtcp2_crypto_picotls library is disabled due to lack of good Picotls")
+endif()
+
+if(HAVE_WOLFSSL)
+ add_subdirectory(wolfssl)
+elseif(ENABLE_WOLFSSL)
+ message(WARNING "libngtcp2_crypto_wolfssl library is disabled due to lack of good wolfSSL")
+endif()
diff --git a/crypto/Makefile.am b/crypto/Makefile.am
new file mode 100644
index 0000000..67c6d3b
--- /dev/null
+++ b/crypto/Makefile.am
@@ -0,0 +1,49 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2 contributors
+
+# 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.
+SUBDIRS =
+
+if HAVE_CRYPTO
+SUBDIRS += includes
+endif
+
+if HAVE_OPENSSL
+SUBDIRS += openssl
+endif
+
+if HAVE_GNUTLS
+SUBDIRS += gnutls
+endif
+
+if HAVE_BORINGSSL
+SUBDIRS += boringssl
+endif
+
+if HAVE_PICOTLS
+SUBDIRS += picotls
+endif
+
+if HAVE_WOLFSSL
+SUBDIRS += wolfssl
+endif
+
+EXTRA_DIST = CMakeLists.txt
diff --git a/crypto/boringssl/.gitignore b/crypto/boringssl/.gitignore
new file mode 100644
index 0000000..3d57277
--- /dev/null
+++ b/crypto/boringssl/.gitignore
@@ -0,0 +1,2 @@
+/libngtcp2_crypto_boringssl.pc
+/libngtcp2_crypto_boringssl.a
diff --git a/crypto/boringssl/CMakeLists.txt b/crypto/boringssl/CMakeLists.txt
new file mode 100644
index 0000000..ebaae3d
--- /dev/null
+++ b/crypto/boringssl/CMakeLists.txt
@@ -0,0 +1,63 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2
+
+# 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.
+
+add_definitions(-DBUILDING_NGTCP2)
+
+set(ngtcp2_crypto_boringssl_SOURCES
+ boringssl.c
+ ../shared.c
+)
+
+set(ngtcp2_crypto_boringssl_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto"
+ "${BORINGSSL_INCLUDE_DIRS}"
+)
+
+foreach(name libngtcp2_crypto_boringssl.pc)
+ configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+if(ENABLE_STATIC_LIB)
+ # Public static library
+ add_library(ngtcp2_crypto_boringssl_static ${ngtcp2_crypto_boringssl_SOURCES})
+ set_target_properties(ngtcp2_crypto_boringssl_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ C_VISIBILITY_PRESET hidden
+ )
+ target_compile_definitions(ngtcp2_crypto_boringssl_static PUBLIC
+ "-DNGTCP2_STATICLIB")
+ target_include_directories(ngtcp2_crypto_boringssl_static PUBLIC
+ ${ngtcp2_crypto_boringssl_INCLUDE_DIRS})
+
+ install(TARGETS ngtcp2_crypto_boringssl_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_boringssl.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/crypto/boringssl/Makefile.am b/crypto/boringssl/Makefile.am
new file mode 100644
index 0000000..7743233
--- /dev/null
+++ b/crypto/boringssl/Makefile.am
@@ -0,0 +1,39 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2 contributors
+
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
+ -I$(top_srcdir)/lib -DBUILDING_NGTCP2 \
+ -I$(top_srcdir)/crypto/includes -I$(top_builddir)/crypto/includes \
+ -I$(top_srcdir)/crypto -I$(top_builddir)/crypto \
+ @BORINGSSL_CFLAGS@
+AM_LDFLAGS = ${LIBTOOL_LDFLAGS}
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libngtcp2_crypto_boringssl.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LIBRARIES = libngtcp2_crypto_boringssl.a
+
+libngtcp2_crypto_boringssl_a_SOURCES = boringssl.c ../shared.c ../shared.h
diff --git a/crypto/boringssl/boringssl.c b/crypto/boringssl/boringssl.c
new file mode 100644
index 0000000..8b650bd
--- /dev/null
+++ b/crypto/boringssl/boringssl.c
@@ -0,0 +1,630 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * 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 HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+#include <string.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_boringssl.h>
+
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/aes.h>
+#include <openssl/chacha.h>
+#include <openssl/rand.h>
+
+#include "shared.h"
+
+typedef enum ngtcp2_crypto_boringssl_cipher_type {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128,
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256,
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
+} ngtcp2_crypto_boringssl_cipher_type;
+
+typedef struct ngtcp2_crypto_boringssl_cipher {
+ ngtcp2_crypto_boringssl_cipher_type type;
+} ngtcp2_crypto_boringssl_cipher;
+
+static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_128 = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128,
+};
+
+static ngtcp2_crypto_boringssl_cipher crypto_cipher_aes_256 = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256,
+};
+
+static ngtcp2_crypto_boringssl_cipher crypto_cipher_chacha20 = {
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20,
+};
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm());
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)EVP_sha256();
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aead_aes_128_gcm());
+ ctx->md.native_handle = (void *)EVP_sha256();
+ ctx->hp.native_handle = (void *)&crypto_cipher_aes_128;
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = EVP_AEAD_max_overhead(aead->native_handle);
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aead_aes_128_gcm());
+}
+
+static const EVP_AEAD *crypto_ssl_get_aead(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return EVP_aead_aes_128_gcm();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return EVP_aead_aes_256_gcm();
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_aead_chacha20_poly1305();
+ default:
+ return NULL;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ default:
+ return 0;
+ }
+}
+
+static const ngtcp2_crypto_boringssl_cipher *crypto_ssl_get_hp(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ return &crypto_cipher_aes_128;
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return &crypto_cipher_aes_256;
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return &crypto_cipher_chacha20;
+ default:
+ return NULL;
+ }
+}
+
+static const EVP_MD *crypto_ssl_get_md(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_CK_AES_128_GCM_SHA256:
+ case TLS1_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_sha256();
+ case TLS1_CK_AES_256_GCM_SHA384:
+ return EVP_sha384();
+ default:
+ return NULL;
+ }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ SSL *ssl = tls_native_handle;
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_ssl_get_aead(ssl));
+ ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl);
+ ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl);
+ ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl);
+ ctx->max_decryption_failure = crypto_ssl_get_aead_max_decryption_failure(ssl);
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
+}
+
+static size_t crypto_md_hashlen(const EVP_MD *md) {
+ return (size_t)EVP_MD_size(md);
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return crypto_md_hashlen(md->native_handle);
+}
+
+static size_t crypto_aead_keylen(const EVP_AEAD *aead) {
+ return (size_t)EVP_AEAD_key_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_keylen(aead->native_handle);
+}
+
+static size_t crypto_aead_noncelen(const EVP_AEAD *aead) {
+ return (size_t)EVP_AEAD_nonce_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_noncelen(aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const EVP_AEAD *cipher = aead->native_handle;
+ size_t keylen = crypto_aead_keylen(cipher);
+ EVP_AEAD_CTX *actx;
+
+ (void)noncelen;
+
+ actx = EVP_AEAD_CTX_new(cipher, key, keylen, EVP_AEAD_DEFAULT_TAG_LENGTH);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ return ngtcp2_crypto_aead_ctx_encrypt_init(aead_ctx, aead, key, noncelen);
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ EVP_AEAD_CTX_free(aead_ctx->native_handle);
+ }
+}
+
+typedef struct ngtcp2_crypto_boringssl_cipher_ctx {
+ ngtcp2_crypto_boringssl_cipher_type type;
+ union {
+ /* aes_key is an encryption key when type is either
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128 or
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256. */
+ AES_KEY aes_key;
+ /* key contains an encryption key when type ==
+ NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20. */
+ uint8_t key[32];
+ };
+} ngtcp2_crypto_boringssl_cipher_ctx;
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ ngtcp2_crypto_boringssl_cipher *hp_cipher = cipher->native_handle;
+ ngtcp2_crypto_boringssl_cipher_ctx *ctx;
+ int rv;
+ (void)rv;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ return -1;
+ }
+
+ ctx->type = hp_cipher->type;
+ cipher_ctx->native_handle = ctx;
+
+ switch (hp_cipher->type) {
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128:
+ rv = AES_set_encrypt_key(key, 128, &ctx->aes_key);
+ assert(0 == rv);
+ return 0;
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256:
+ rv = AES_set_encrypt_key(key, 256, &ctx->aes_key);
+ assert(0 == rv);
+ return 0;
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
+ memcpy(ctx->key, key, sizeof(ctx->key));
+ return 0;
+ default:
+ assert(0);
+ abort();
+ };
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (!cipher_ctx->native_handle) {
+ return;
+ }
+
+ free(cipher_ctx->native_handle);
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ const EVP_MD *prf = md->native_handle;
+ size_t destlen = (size_t)EVP_MD_size(prf);
+
+ if (HKDF_extract(dest, &destlen, prf, secret, secretlen, salt, saltlen) !=
+ 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ const EVP_MD *prf = md->native_handle;
+
+ if (HKDF_expand(dest, destlen, prf, secret, secretlen, info, infolen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ const EVP_MD *prf = md->native_handle;
+
+ if (HKDF(dest, destlen, prf, secret, secretlen, salt, saltlen, info,
+ infolen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ const EVP_AEAD *cipher = aead->native_handle;
+ EVP_AEAD_CTX *actx = aead_ctx->native_handle;
+ size_t max_outlen = plaintextlen + EVP_AEAD_max_overhead(cipher);
+ size_t outlen;
+
+ if (EVP_AEAD_CTX_seal(actx, dest, &outlen, max_outlen, nonce, noncelen,
+ plaintext, plaintextlen, aad, aadlen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ const EVP_AEAD *cipher = aead->native_handle;
+ EVP_AEAD_CTX *actx = aead_ctx->native_handle;
+ size_t max_overhead = EVP_AEAD_max_overhead(cipher);
+ size_t max_outlen;
+ size_t outlen;
+
+ if (ciphertextlen < max_overhead) {
+ return -1;
+ }
+
+ max_outlen = ciphertextlen - max_overhead;
+
+ if (EVP_AEAD_CTX_open(actx, dest, &outlen, max_outlen, nonce, noncelen,
+ ciphertext, ciphertextlen, aad, aadlen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ ngtcp2_crypto_boringssl_cipher_ctx *ctx = hp_ctx->native_handle;
+ uint32_t counter;
+
+ (void)hp;
+
+ switch (ctx->type) {
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_128:
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_AES_256:
+ AES_ecb_encrypt(sample, dest, &ctx->aes_key, 1);
+ return 0;
+ case NGTCP2_CRYPTO_BORINGSSL_CIPHER_TYPE_CHACHA20:
+#if defined(WORDS_BIGENDIAN)
+ counter = (uint32_t)sample[0] + (uint32_t)(sample[1] << 8) +
+ (uint32_t)(sample[2] << 16) + (uint32_t)(sample[3] << 24);
+#else /* !WORDS_BIGENDIAN */
+ memcpy(&counter, sample, sizeof(counter));
+#endif /* !WORDS_BIGENDIAN */
+ CRYPTO_chacha_20(dest, PLAINTEXT, sizeof(PLAINTEXT) - 1, ctx->key,
+ sample + sizeof(counter), counter);
+ return 0;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
+ int rv;
+ int err;
+
+ if (SSL_provide_quic_data(
+ ssl, ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(crypto_level),
+ data, datalen) != 1) {
+ return -1;
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ retry:
+ rv = SSL_do_handshake(ssl);
+ if (rv <= 0) {
+ err = SSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ return -1;
+ case SSL_ERROR_EARLY_DATA_REJECTED:
+ assert(!ngtcp2_conn_is_server(conn));
+
+ SSL_reset_early_data_reject(ssl);
+
+ rv = ngtcp2_conn_early_data_rejected(conn);
+ if (rv != 0) {
+ return -1;
+ }
+
+ goto retry;
+ default:
+ return -1;
+ }
+ }
+
+ if (SSL_in_early_data(ssl)) {
+ return 0;
+ }
+
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = SSL_process_quic_post_handshake(ssl);
+ if (rv != 1) {
+ err = SSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ SSL *ssl = tls;
+ const uint8_t *tp;
+ size_t tplen;
+ int rv;
+
+ SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ if (SSL_set_quic_transport_params(tls, buf, len) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_boringssl_from_ssl_encryption_level(
+ enum ssl_encryption_level_t ssl_level) {
+ switch (ssl_level) {
+ case ssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case ssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case ssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case ssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+enum ssl_encryption_level_t ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return ssl_encryption_initial;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return ssl_encryption_handshake;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return ssl_encryption_application;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return ssl_encryption_early_data;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ if (RAND_bytes(data, datalen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_read_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ const SSL_CIPHER *cipher, const uint8_t *secret,
+ size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
+ (void)cipher;
+
+ if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int set_write_secret(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ const SSL_CIPHER *cipher, const uint8_t *secret,
+ size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
+ (void)cipher;
+
+ if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int add_handshake_data(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_boringssl_from_ssl_encryption_level(bssl_level);
+ int rv;
+
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int flush_flight(SSL *ssl) {
+ (void)ssl;
+ return 1;
+}
+
+static int send_alert(SSL *ssl, enum ssl_encryption_level_t bssl_level,
+ uint8_t alert) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)bssl_level;
+
+ ngtcp2_conn_set_tls_alert(conn, alert);
+
+ return 1;
+}
+
+static SSL_QUIC_METHOD quic_method = {
+ set_read_secret, set_write_secret, add_handshake_data,
+ flush_flight, send_alert,
+};
+
+static void crypto_boringssl_configure_context(SSL_CTX *ssl_ctx) {
+ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+}
+
+int ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx) {
+ crypto_boringssl_configure_context(ssl_ctx);
+
+ return 0;
+}
+
+int ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx) {
+ crypto_boringssl_configure_context(ssl_ctx);
+
+ return 0;
+}
diff --git a/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in b/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in
new file mode 100644
index 0000000..737970a
--- /dev/null
+++ b/crypto/boringssl/libngtcp2_crypto_boringssl.pc.in
@@ -0,0 +1,33 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2 contributors
+
+# 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.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_boringssl
+Description: ngtcp2 BoringSSL crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_boringssl
+Cflags: -I${includedir}
diff --git a/crypto/gnutls/.gitignore b/crypto/gnutls/.gitignore
new file mode 100644
index 0000000..b11bf3d
--- /dev/null
+++ b/crypto/gnutls/.gitignore
@@ -0,0 +1 @@
+/libngtcp2_crypto_gnutls.pc
diff --git a/crypto/gnutls/CMakeLists.txt b/crypto/gnutls/CMakeLists.txt
new file mode 100644
index 0000000..9b7f225
--- /dev/null
+++ b/crypto/gnutls/CMakeLists.txt
@@ -0,0 +1,85 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2
+
+# 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.
+
+add_definitions(-DBUILDING_NGTCP2)
+
+set(ngtcp2_crypto_gnutls_SOURCES
+ gnutls.c
+ ../shared.c
+)
+
+set(ngtcp2_crypto_gnutls_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto"
+ "${GNUTLS_INCLUDE_DIRS}"
+)
+
+foreach(name libngtcp2_crypto_gnutls.pc)
+ configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+# Public shared library
+if(ENABLE_SHARED_LIB)
+ add_library(ngtcp2_crypto_gnutls SHARED ${ngtcp2_crypto_gnutls_SOURCES})
+ set_target_properties(ngtcp2_crypto_gnutls PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${CRYPTO_GNUTLS_LT_VERSION}
+ SOVERSION ${CRYPTO_GNUTLS_LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ POSITION_INDEPENDENT_CODE ON
+ )
+ target_include_directories(ngtcp2_crypto_gnutls PUBLIC
+ ${ngtcp2_crypto_gnutls_INCLUDE_DIRS})
+ target_link_libraries(ngtcp2_crypto_gnutls ngtcp2 ${GNUTLS_LIBRARIES})
+
+ install(TARGETS ngtcp2_crypto_gnutls
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif()
+
+if(ENABLE_STATIC_LIB)
+ # Public static library
+ add_library(ngtcp2_crypto_gnutls_static ${ngtcp2_crypto_gnutls_SOURCES})
+ set_target_properties(ngtcp2_crypto_gnutls_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${CRYPTO_GNUTLS_LT_VERSION}
+ SOVERSION ${CRYPTO_GNUTLS_LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ )
+ target_compile_definitions(ngtcp2_crypto_gnutls_static PUBLIC
+ "-DNGTCP2_STATICLIB")
+ target_include_directories(ngtcp2_crypto_gnutls_static PUBLIC
+ ${ngtcp2_crypto_gnutls_INCLUDE_DIRS})
+
+ install(TARGETS ngtcp2_crypto_gnutls_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_gnutls.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/crypto/gnutls/Makefile.am b/crypto/gnutls/Makefile.am
new file mode 100644
index 0000000..b4ed6c6
--- /dev/null
+++ b/crypto/gnutls/Makefile.am
@@ -0,0 +1,43 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2 contributors
+
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
+ -I$(top_srcdir)/lib -DBUILDING_NGTCP2 \
+ -I$(top_srcdir)/crypto/includes -I$(top_builddir)/crypto/includes \
+ -I$(top_srcdir)/crypto -I$(top_builddir)/crypto \
+ @GNUTLS_CFLAGS@
+AM_LDFLAGS = ${LIBTOOL_LDFLAGS}
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libngtcp2_crypto_gnutls.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libngtcp2_crypto_gnutls.la
+
+libngtcp2_crypto_gnutls_la_SOURCES = gnutls.c ../shared.c ../shared.h
+libngtcp2_crypto_gnutls_la_LDFLAGS = -no-undefined \
+ -version-info $(CRYPTO_GNUTLS_LT_CURRENT):$(CRYPTO_GNUTLS_LT_REVISION):$(CRYPTO_GNUTLS_LT_AGE)
+libngtcp2_crypto_gnutls_la_LIBADD = $(top_builddir)/lib/libngtcp2.la \
+ @GNUTLS_LIBS@
diff --git a/crypto/gnutls/gnutls.c b/crypto/gnutls/gnutls.c
new file mode 100644
index 0000000..73ea0c1
--- /dev/null
+++ b/crypto/gnutls/gnutls.c
@@ -0,0 +1,644 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * 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 HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <string.h>
+
+#include "shared.h"
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)GNUTLS_CIPHER_AES_128_GCM);
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)GNUTLS_DIG_SHA256;
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)GNUTLS_CIPHER_AES_128_GCM);
+ ctx->md.native_handle = (void *)GNUTLS_DIG_SHA256;
+ ctx->hp.native_handle = (void *)GNUTLS_CIPHER_AES_128_CBC;
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = gnutls_cipher_get_tag_size(
+ (gnutls_cipher_algorithm_t)(intptr_t)aead_native_handle);
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)GNUTLS_CIPHER_AES_128_GCM);
+}
+
+static gnutls_cipher_algorithm_t
+crypto_get_hp(gnutls_cipher_algorithm_t cipher) {
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ case GNUTLS_CIPHER_AES_128_CCM:
+ return GNUTLS_CIPHER_AES_128_CBC;
+ case GNUTLS_CIPHER_AES_256_GCM:
+ case GNUTLS_CIPHER_AES_256_CCM:
+ return GNUTLS_CIPHER_AES_256_CBC;
+ case GNUTLS_CIPHER_CHACHA20_POLY1305:
+ return GNUTLS_CIPHER_CHACHA20_32;
+ default:
+ return GNUTLS_CIPHER_UNKNOWN;
+ }
+}
+
+static uint64_t
+crypto_get_aead_max_encryption(gnutls_cipher_algorithm_t cipher) {
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ case GNUTLS_CIPHER_AES_256_GCM:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ case GNUTLS_CIPHER_CHACHA20_POLY1305:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ case GNUTLS_CIPHER_AES_128_CCM:
+ case GNUTLS_CIPHER_AES_256_CCM:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t
+crypto_get_aead_max_decryption_failure(gnutls_cipher_algorithm_t cipher) {
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ case GNUTLS_CIPHER_AES_256_GCM:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ case GNUTLS_CIPHER_CHACHA20_POLY1305:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ case GNUTLS_CIPHER_AES_128_CCM:
+ case GNUTLS_CIPHER_AES_256_CCM:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ gnutls_session_t session = tls_native_handle;
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_digest_algorithm_t hash;
+ gnutls_cipher_algorithm_t hp_cipher;
+
+ cipher = gnutls_cipher_get(session);
+ if (cipher != GNUTLS_CIPHER_UNKNOWN && cipher != GNUTLS_CIPHER_NULL) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)cipher);
+ }
+
+ hash = gnutls_prf_hash_get(session);
+ if (hash != GNUTLS_DIG_UNKNOWN && hash != GNUTLS_DIG_NULL) {
+ ctx->md.native_handle = (void *)hash;
+ }
+
+ hp_cipher = crypto_get_hp(cipher);
+ if (hp_cipher != GNUTLS_CIPHER_UNKNOWN) {
+ ctx->hp.native_handle = (void *)hp_cipher;
+ }
+
+ ctx->max_encryption = crypto_get_aead_max_encryption(cipher);
+ ctx->max_decryption_failure = crypto_get_aead_max_decryption_failure(cipher);
+
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ gnutls_session_t session = tls_native_handle;
+ gnutls_cipher_algorithm_t cipher;
+ gnutls_digest_algorithm_t hash;
+ gnutls_cipher_algorithm_t hp_cipher;
+
+ cipher = gnutls_early_cipher_get(session);
+ if (cipher != GNUTLS_CIPHER_UNKNOWN && cipher != GNUTLS_CIPHER_NULL) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)cipher);
+ }
+
+ hash = gnutls_early_prf_hash_get(session);
+ if (hash != GNUTLS_DIG_UNKNOWN && hash != GNUTLS_DIG_NULL) {
+ ctx->md.native_handle = (void *)hash;
+ }
+
+ hp_cipher = crypto_get_hp(cipher);
+ if (hp_cipher != GNUTLS_CIPHER_UNKNOWN) {
+ ctx->hp.native_handle = (void *)hp_cipher;
+ }
+
+ ctx->max_encryption = crypto_get_aead_max_encryption(cipher);
+ ctx->max_decryption_failure = crypto_get_aead_max_decryption_failure(cipher);
+
+ return ctx;
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return gnutls_hash_get_len(
+ (gnutls_digest_algorithm_t)(intptr_t)md->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return gnutls_cipher_get_key_size(
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return gnutls_cipher_get_iv_size(
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd;
+ gnutls_datum_t _key;
+
+ (void)noncelen;
+
+ _key.data = (void *)key;
+ _key.size = (unsigned int)ngtcp2_crypto_aead_keylen(aead);
+
+ if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = hd;
+
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd;
+ gnutls_datum_t _key;
+
+ (void)noncelen;
+
+ _key.data = (void *)key;
+ _key.size = (unsigned int)ngtcp2_crypto_aead_keylen(aead);
+
+ if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = hd;
+
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ gnutls_aead_cipher_deinit(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ gnutls_cipher_algorithm_t _cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)cipher->native_handle;
+ gnutls_cipher_hd_t hd;
+ gnutls_datum_t _key;
+
+ _key.data = (void *)key;
+ _key.size = (unsigned int)gnutls_cipher_get_key_size(_cipher);
+
+ if (gnutls_cipher_init(&hd, _cipher, &_key, NULL) != 0) {
+ return -1;
+ }
+
+ cipher_ctx->native_handle = hd;
+
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ gnutls_cipher_deinit(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ gnutls_mac_algorithm_t prf =
+ (gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
+ gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
+ gnutls_datum_t _salt = {(void *)salt, (unsigned int)saltlen};
+
+ if (gnutls_hkdf_extract(prf, &_secret, &_salt, dest) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ gnutls_mac_algorithm_t prf =
+ (gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
+ gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
+ gnutls_datum_t _info = {(void *)info, (unsigned int)infolen};
+
+ if (gnutls_hkdf_expand(prf, &_secret, &_info, dest, destlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ gnutls_mac_algorithm_t prf =
+ (gnutls_mac_algorithm_t)(intptr_t)md->native_handle;
+ size_t keylen = ngtcp2_crypto_md_hashlen(md);
+ uint8_t key[64];
+ gnutls_datum_t _secret = {(void *)secret, (unsigned int)secretlen};
+ gnutls_datum_t _key = {(void *)key, (unsigned int)keylen};
+ gnutls_datum_t _salt = {(void *)salt, (unsigned int)saltlen};
+ gnutls_datum_t _info = {(void *)info, (unsigned int)infolen};
+
+ assert(keylen <= sizeof(key));
+
+ if (gnutls_hkdf_extract(prf, &_secret, &_salt, key) != 0) {
+ return -1;
+ }
+
+ if (gnutls_hkdf_expand(prf, &_key, &_info, dest, destlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd = aead_ctx->native_handle;
+ size_t taglen = gnutls_cipher_get_tag_size(cipher);
+ size_t ciphertextlen = plaintextlen + taglen;
+
+ if (gnutls_aead_cipher_encrypt(hd, nonce, noncelen, aad, aadlen, taglen,
+ plaintext, plaintextlen, dest,
+ &ciphertextlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)aead->native_handle;
+ gnutls_aead_cipher_hd_t hd = aead_ctx->native_handle;
+ size_t taglen = gnutls_cipher_get_tag_size(cipher);
+ size_t plaintextlen;
+
+ if (taglen > ciphertextlen) {
+ return -1;
+ }
+
+ plaintextlen = ciphertextlen - taglen;
+
+ if (gnutls_aead_cipher_decrypt(hd, nonce, noncelen, aad, aadlen, taglen,
+ ciphertext, ciphertextlen, dest,
+ &plaintextlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ gnutls_cipher_algorithm_t cipher =
+ (gnutls_cipher_algorithm_t)(intptr_t)hp->native_handle;
+ gnutls_cipher_hd_t hd = hp_ctx->native_handle;
+
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_CBC:
+ case GNUTLS_CIPHER_AES_256_CBC: {
+ uint8_t iv[16];
+ uint8_t buf[16];
+
+ /* Emulate one block AES-ECB by invalidating the effect of IV */
+ memset(iv, 0, sizeof(iv));
+
+ gnutls_cipher_set_iv(hd, iv, sizeof(iv));
+
+ if (gnutls_cipher_encrypt2(hd, sample, 16, buf, sizeof(buf)) != 0) {
+ return -1;
+ }
+
+ memcpy(dest, buf, 5);
+ } break;
+
+ case GNUTLS_CIPHER_CHACHA20_32: {
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ uint8_t buf[5 + 16];
+ size_t buflen = sizeof(buf);
+
+ gnutls_cipher_set_iv(hd, (void *)sample, 16);
+
+ if (gnutls_cipher_encrypt2(hd, PLAINTEXT, sizeof(PLAINTEXT) - 1, buf,
+ buflen) != 0) {
+ return -1;
+ }
+
+ memcpy(dest, buf, 5);
+ } break;
+ default:
+ assert(0);
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(
+ gnutls_record_encryption_level_t gtls_level) {
+ switch (gtls_level) {
+ case GNUTLS_ENCRYPTION_LEVEL_INITIAL:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case GNUTLS_ENCRYPTION_LEVEL_APPLICATION:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ case GNUTLS_ENCRYPTION_LEVEL_EARLY:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+gnutls_record_encryption_level_t
+ngtcp2_crypto_gnutls_from_ngtcp2_level(ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return GNUTLS_ENCRYPTION_LEVEL_INITIAL;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return GNUTLS_ENCRYPTION_LEVEL_APPLICATION;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return GNUTLS_ENCRYPTION_LEVEL_EARLY;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ gnutls_session_t session = ngtcp2_conn_get_tls_native_handle(conn);
+ int rv;
+
+ if (datalen > 0) {
+ rv = gnutls_handshake_write(
+ session, ngtcp2_crypto_gnutls_from_ngtcp2_level(crypto_level), data,
+ datalen);
+ if (rv != 0) {
+ if (!gnutls_error_is_fatal(rv)) {
+ return 0;
+ }
+ gnutls_alert_send_appropriate(session, rv);
+ return -1;
+ }
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ rv = gnutls_handshake(session);
+ if (rv < 0) {
+ if (!gnutls_error_is_fatal(rv)) {
+ return 0;
+ }
+ gnutls_alert_send_appropriate(session, rv);
+ return -1;
+ }
+
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ (void)conn;
+ (void)tls;
+ /* Nothing to do; GnuTLS applications are supposed to register the
+ quic_transport_parameters extension with
+ gnutls_session_ext_register. */
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ (void)tls;
+ (void)buf;
+ (void)len;
+ /* Nothing to do; GnuTLS applications are supposed to register the
+ quic_transport_parameters extension with
+ gnutls_session_ext_register. */
+ return 0;
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (gnutls_rnd(GNUTLS_RND_RANDOM, data, NGTCP2_PATH_CHALLENGE_DATALEN) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ if (gnutls_rnd(GNUTLS_RND_RANDOM, data, datalen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int secret_func(gnutls_session_t session,
+ gnutls_record_encryption_level_t gtls_level,
+ const void *rx_secret, const void *tx_secret,
+ size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
+
+ if (rx_secret &&
+ ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ rx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (tx_secret &&
+ ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ tx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_func(gnutls_session_t session,
+ gnutls_record_encryption_level_t gtls_level,
+ gnutls_handshake_description_t htype, const void *data,
+ size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
+ int rv;
+
+ if (htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC) {
+ return 0;
+ }
+
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int alert_read_func(gnutls_session_t session,
+ gnutls_record_encryption_level_t gtls_level,
+ gnutls_alert_level_t alert_level,
+ gnutls_alert_description_t alert_desc) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)gtls_level;
+ (void)alert_level;
+
+ ngtcp2_conn_set_tls_alert(conn, (uint8_t)alert_desc);
+
+ return 0;
+}
+
+static int tp_recv_func(gnutls_session_t session, const uint8_t *data,
+ size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ int rv;
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int tp_send_func(gnutls_session_t session, gnutls_buffer_t extdata) {
+ ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ uint8_t buf[256];
+ ngtcp2_ssize nwrite;
+ int rv;
+
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf));
+ if (nwrite < 0) {
+ return -1;
+ }
+
+ rv = gnutls_buffer_append_data(extdata, buf, (size_t)nwrite);
+ if (rv != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int crypto_gnutls_configure_session(gnutls_session_t session) {
+ int rv;
+
+ gnutls_handshake_set_secret_function(session, secret_func);
+ gnutls_handshake_set_read_function(session, read_func);
+ gnutls_alert_set_read_function(session, alert_read_func);
+
+ rv = gnutls_session_ext_register(
+ session, "QUIC Transport Parameters",
+ NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1, GNUTLS_EXT_TLS, tp_recv_func,
+ tp_send_func, NULL, NULL, NULL,
+ GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO | GNUTLS_EXT_FLAG_EE);
+ if (rv != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_gnutls_configure_server_session(gnutls_session_t session) {
+ return crypto_gnutls_configure_session(session);
+}
+
+int ngtcp2_crypto_gnutls_configure_client_session(gnutls_session_t session) {
+ return crypto_gnutls_configure_session(session);
+}
diff --git a/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in b/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
new file mode 100644
index 0000000..890e89d
--- /dev/null
+++ b/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
@@ -0,0 +1,33 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2 contributors
+
+# 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.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_gnutls
+Description: ngtcp2 GnuTLS crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_gnutls
+Cflags: -I${includedir}
diff --git a/crypto/includes/CMakeLists.txt b/crypto/includes/CMakeLists.txt
new file mode 100644
index 0000000..10f9122
--- /dev/null
+++ b/crypto/includes/CMakeLists.txt
@@ -0,0 +1,56 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2
+
+# 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.
+
+install(FILES
+ ngtcp2/ngtcp2_crypto.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
+
+if(HAVE_OPENSSL)
+ install(FILES
+ ngtcp2/ngtcp2_crypto_openssl.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
+endif()
+
+if(HAVE_GNUTLS)
+ install(FILES
+ ngtcp2/ngtcp2_crypto_gnutls.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
+endif()
+
+if(HAVE_BORINGSSL)
+ install(FILES
+ ngtcp2/ngtcp2_crypto_boringssl.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
+endif()
+
+if(HAVE_PICOTLS)
+ install(FILES
+ ngtcp2/ngtcp2_crypto_picotls.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
+endif()
+
+if(HAVE_WOLFSSL)
+ install(FILES
+ ngtcp2/ngtcp2_crypto_wolfssl.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/ngtcp2")
+endif()
diff --git a/crypto/includes/Makefile.am b/crypto/includes/Makefile.am
new file mode 100644
index 0000000..a688a20
--- /dev/null
+++ b/crypto/includes/Makefile.am
@@ -0,0 +1,45 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2 contributors
+
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+nobase_include_HEADERS = ngtcp2/ngtcp2_crypto.h
+
+if HAVE_OPENSSL
+nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_openssl.h
+endif
+
+if HAVE_GNUTLS
+nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_gnutls.h
+endif
+
+if HAVE_BORINGSSL
+nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_boringssl.h
+endif
+
+if HAVE_PICOTLS
+nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_picotls.h
+endif
+
+if HAVE_WOLFSSL
+nobase_include_HEADERS += ngtcp2/ngtcp2_crypto_wolfssl.h
+endif
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto.h b/crypto/includes/ngtcp2/ngtcp2_crypto.h
new file mode 100644
index 0000000..4736b51
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto.h
@@ -0,0 +1,893 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * 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 NGTCP2_CRYPTO_H
+#define NGTCP2_CRYPTO_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef WIN32
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <ws2tcpip.h>
+#endif /* WIN32 */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_SECRETLEN` is the length of secret
+ * for Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_KEYLEN` is the length of key for
+ * Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_KEYLEN 16
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_INITIAL_IVLEN` is the length of IV for
+ * Initial packets.
+ */
+#define NGTCP2_CRYPTO_INITIAL_IVLEN 12
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_tls` initializes |ctx| by extracting negotiated
+ * ciphers and message digests from native TLS session
+ * |tls_native_handle|. This is used for encrypting/decrypting
+ * Handshake and Short header packets.
+ *
+ * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
+ * a pointer to SSL object.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_tls_early` initializes |ctx| by extracting early
+ * ciphers and message digests from native TLS session
+ * |tls_native_handle|. This is used for encrypting/decrypting 0RTT
+ * packets.
+ *
+ * If libngtcp2_crypto_openssl is linked, |tls_native_handle| must be
+ * a pointer to SSL object.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_ctx *
+ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx, void *tls_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_md_init` initializes |md| with the provided
+ * |md_native_handle| which is an underlying message digest object.
+ *
+ * If libngtcp2_crypto_openssl is linked, |md_native_handle| must be a
+ * pointer to EVP_MD.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |md_native_handle| must be
+ * gnutls_mac_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |md_native_handle| must be
+ * a pointer to EVP_MD.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
+ void *md_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_md_hashlen` returns the length of |md| output.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_keylen` returns the length of key for |aead|.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_noncelen` returns the length of nonce for
+ * |aead|.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_extract` performs HKDF extract operation. The
+ * result is the length of |md| and is stored to the buffer pointed by
+ * |dest|. The caller is responsible to specify the buffer that can
+ * store the output.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_expand` performs HKDF expand operation. The
+ * result is |destlen| bytes long and is stored to the buffer pointed
+ * by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen,
+ const uint8_t *info,
+ size_t infolen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf` performs HKDF operation. The result is
+ * |destlen| bytes long and is stored to the buffer pointed by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hkdf_expand_label` performs HKDF expand label. The
+ * result is |destlen| bytes long and is stored to the buffer pointed
+ * by |dest|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen,
+ const uint8_t *label,
+ size_t labellen);
+
+/**
+ * @enum
+ *
+ * :type:`ngtcp2_crypto_side` indicates which side the application
+ * implements; client or server.
+ */
+typedef enum ngtcp2_crypto_side {
+ /**
+ * :enum:`NGTCP2_CRYPTO_SIDE_CLIENT` indicates that the application
+ * is client.
+ */
+ NGTCP2_CRYPTO_SIDE_CLIENT,
+ /**
+ * :enum:`NGTCP2_CRYPTO_SIDE_SERVER` indicates that the application
+ * is server.
+ */
+ NGTCP2_CRYPTO_SIDE_SERVER
+} ngtcp2_crypto_side;
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_packet_protection_ivlen` returns the length of IV
+ * used to encrypt QUIC packet.
+ */
+NGTCP2_EXTERN size_t
+ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_encrypt` encrypts |plaintext| of length
+ * |plaintextlen| and writes the ciphertext into the buffer pointed by
+ * |dest|. The length of ciphertext is plaintextlen +
+ * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
+ * bytes long. |dest| must have enough capacity to store the
+ * ciphertext. It is allowed to specify the same value to |dest| and
+ * |plaintext|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_encrypt(uint8_t *dest,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext,
+ size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_encrypt_cb` is a wrapper function around
+ * `ngtcp2_crypto_encrypt`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.encrypt` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_decrypt` decrypts |ciphertext| of length
+ * |ciphertextlen| and writes the plaintext into the buffer pointed by
+ * |dest|. The length of plaintext is ciphertextlen -
+ * :member:`aead->max_overhead <ngtcp2_crypto_aead.max_overhead>`
+ * bytes long. |dest| must have enough capacity to store the
+ * plaintext. It is allowed to specify the same value to |dest| and
+ * |ciphertext|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_decrypt(uint8_t *dest,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext,
+ size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_decrypt_cb` is a wrapper function around
+ * `ngtcp2_crypto_decrypt`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.decrypt` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_TLS_DECRYPT`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hp_mask` generates mask which is used in packet
+ * header encryption. The mask is written to the buffer pointed by
+ * |dest|. The sample is passed as |sample| which is
+ * :macro:`NGTCP2_HP_SAMPLELEN` bytes long. The length of mask must
+ * be at least :macro:`NGTCP2_HP_MASKLEN`. The library only uses the
+ * first :macro:`NGTCP2_HP_MASKLEN` bytes of the produced mask. The
+ * buffer pointed by |dest| must have at least
+ * :macro:`NGTCP2_HP_SAMPLELEN` bytes available.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_hp_mask(uint8_t *dest,
+ const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_hp_mask_cb` is a wrapper function around
+ * `ngtcp2_crypto_hp_mask`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.hp_mask` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_rx_key` derives the rx keys from
+ * |secret| and installs new keys to |conn|.
+ *
+ * If |key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |key|. If |iv| is
+ * not NULL, the derived packet protection IV for decryption is
+ * written to the buffer pointed by |iv|. If |hp| is not NULL, the
+ * derived header protection key for decryption is written to the
+ * buffer pointed by |hp|.
+ *
+ * |secretlen| specifies the length of |secret|.
+ *
+ * The length of packet protection key and header protection key is
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
+ * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
+ *
+ * In the first call of this function, it calls
+ * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
+ * negotiated AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
+ * :type:`ngtcp2_crypto_ctx`.
+ *
+ * If |conn| is initialized as client, and |level| is
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
+ * function retrieves a remote QUIC transport parameters extension
+ * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
+ * sets it to |conn| by calling
+ * `ngtcp2_conn_decode_remote_transport_params`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_rx_key(
+ ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp,
+ ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_tx_key` derives the tx keys from
+ * |secret| and installs new keys to |conn|.
+ *
+ * If |key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |key|. If |iv| is
+ * not NULL, the derived packet protection IV for encryption is
+ * written to the buffer pointed by |iv|. If |hp| is not NULL, the
+ * derived header protection key for encryption is written to the
+ * buffer pointed by |hp|.
+ *
+ * |secretlen| specifies the length of |secret|.
+ *
+ * The length of packet protection key and header protection key is
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls` (or `ngtcp2_crypto_ctx_tls_early` if
+ * |level| == :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`).
+ *
+ * In the first call of this function, it calls
+ * `ngtcp2_conn_set_crypto_ctx` (or `ngtcp2_conn_set_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to set
+ * negotiated AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_crypto_ctx` (or `ngtcp2_conn_get_early_crypto_ctx`
+ * if |level| ==
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_EARLY`) to get
+ * :type:`ngtcp2_crypto_ctx`.
+ *
+ * If |conn| is initialized as server, and |level| is
+ * :enum:`ngtcp2_crypto_level.NGTCP2_CRYPTO_LEVEL_APPLICATION`, this
+ * function retrieves a remote QUIC transport parameters extension
+ * from an object obtained by `ngtcp2_conn_get_tls_native_handle` and
+ * sets it to |conn| by calling
+ * `ngtcp2_conn_decode_remote_transport_params`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_derive_and_install_tx_key(
+ ngtcp2_conn *conn, uint8_t *key, uint8_t *iv, uint8_t *hp,
+ ngtcp2_crypto_level level, const uint8_t *secret, size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_key` updates traffic keying materials.
+ *
+ * The new traffic secret for decryption is written to the buffer
+ * pointed by |rx_secret|. The length of secret is |secretlen| bytes,
+ * and |rx_secret| must point to the buffer which has enough capacity.
+ *
+ * The new traffic secret for encryption is written to the buffer
+ * pointed by |tx_secret|. The length of secret is |secretlen| bytes,
+ * and |tx_secret| must point to the buffer which has enough capacity.
+ *
+ * The derived packet protection key for decryption is written to the
+ * buffer pointed by |rx_key|. The derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|.
+ * |rx_aead_ctx| must be constructed with |rx_key|.
+ *
+ * The derived packet protection key for encryption is written to the
+ * buffer pointed by |tx_key|. The derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|.
+ * |tx_aead_ctx| must be constructed with |rx_key|.
+ *
+ * |current_rx_secret| and |current_tx_secret| are the current traffic
+ * secrets for decryption and encryption. |secretlen| specifies the
+ * length of |rx_secret| and |tx_secret|.
+ *
+ * The length of packet protection key and header protection key is
+ * `ngtcp2_crypto_aead_keylen(ctx->aead) <ngtcp2_crypto_aead_keylen>`,
+ * and the length of packet protection IV is
+ * `ngtcp2_crypto_packet_protection_ivlen(ctx->aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` where ctx is obtained by
+ * `ngtcp2_crypto_ctx_tls`.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_update_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_key_cb` is a wrapper function around
+ * `ngtcp2_crypto_update_key`. It can be directly passed to
+ * :member:`ngtcp2_callbacks.update_key` field.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_update_key_cb(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_client_initial_cb` installs initial secrets and
+ * encryption keys and sets QUIC transport parameters.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.client_initial` field. It is only used
+ * by client.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_retry_cb` re-installs initial secrets in
+ * response to incoming Retry packet.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.recv_retry` field. It is only used
+ * by client.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn,
+ const ngtcp2_pkt_hd *hd,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_client_initial_cb` installs initial secrets in
+ * response to an incoming Initial packet from client, and sets QUIC
+ * transport parameters.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.recv_client_initial` field. It is
+ * only used by server.
+ *
+ * This function returns 0 if it succeeds, or
+ * :macro:`NGTCP2_ERR_CALLBACK_FAILURE`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_read_write_crypto_data` reads CRYPTO data |data| of
+ * length |datalen| in encryption level |crypto_level| and may feed
+ * outgoing CRYPTO data to |conn|. This function can drive handshake.
+ * This function can be also used after handshake completes. It is
+ * allowed to call this function with |datalen| == 0. In this case,
+ * no additional read operation is done.
+ *
+ * This function returns 0 if it succeeds, or a negative error code.
+ * The generic error code is -1 if a specific error code is not
+ * suitable. The error codes less than -10000 are specific to
+ * underlying TLS implementation. For OpenSSL, the error codes are
+ * defined in *ngtcp2_crypto_openssl.h*.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_recv_crypto_data_cb` is a wrapper function around
+ * `ngtcp2_crypto_read_write_crypto_data`. It can be directly passed
+ * to :member:`ngtcp2_callbacks.recv_crypto_data` field.
+ *
+ * If this function is used, the TLS implementation specific error
+ * codes described in `ngtcp2_crypto_read_write_crypto_data` are
+ * treated as if it returns -1. Do not use this function if an
+ * application wishes to use the TLS implementation specific error
+ * codes.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_recv_crypto_data_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, uint64_t offset,
+ const uint8_t *data, size_t datalen, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_stateless_reset_token` generates a
+ * stateless reset token using HKDF extraction using the given |cid|
+ * and static key |secret| as input. The token will be written to
+ * the buffer pointed by |token| and it must have a capacity of at
+ * least :macro:`NGTCP2_STATELESS_RESET_TOKENLEN` bytes.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_generate_stateless_reset_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_cid *cid);
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_RAND_DATALEN` is the length of random
+ * data added to a token generated by
+ * `ngtcp2_crypto_generate_retry_token` or
+ * `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_RAND_DATALEN 32
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY` is the magic byte for
+ * Retry token generated by `ngtcp2_crypto_generate_retry_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY 0xb6
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR` is the magic byte for a
+ * token generated by `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR 0x36
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` is the maximum length of
+ * a token generated by `ngtcp2_crypto_generate_retry_token`.
+ */
+#define NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN \
+ (/* magic = */ 1 + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN + \
+ sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` is the maximum length
+ * of a token generated by `ngtcp2_crypto_generate_regular_token`.
+ */
+#define NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN \
+ (/* magic = */ 1 + sizeof(ngtcp2_tstamp) + /* aead tag = */ 16 + \
+ NGTCP2_CRYPTO_TOKEN_RAND_DATALEN)
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_retry_token` generates a token in the
+ * buffer pointed by |token| that is sent with Retry packet. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN` bytes long. The
+ * successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY`. |secret| of length
+ * |secretlen| is an initial keying material to generate keys to
+ * encrypt the token. |version| is QUIC version. |remote_addr| of
+ * length |remote_addrlen| is an address of client. |retry_scid| is a
+ * Source Connection ID chosen by server and set in Retry packet.
+ * |odcid| is a Destination Connection ID in Initial packet sent by
+ * client. |ts| is the timestamp when the token is generated.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_retry_token` verifies Retry token stored in
+ * the buffer pointed by |token| of length |tokenlen|. |secret| of
+ * length |secretlen| is an initial keying material to generate keys
+ * to decrypt the token. |version| is QUIC version of the Initial
+ * packet that contains this token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |dcid| is a Destination
+ * Connection ID in Initial packet sent by client. |timeout| is the
+ * period during which the token is valid. |ts| is the current
+ * timestamp. When validation succeeds, the extracted Destination
+ * Connection ID (which is the Destination Connection ID in Initial
+ * packet sent by client that triggered Retry packet) is stored to the
+ * buffer pointed by |odcid|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_verify_retry_token(
+ ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_generate_regular_token` generates a token in the
+ * buffer pointed by |token| that is sent with NEW_TOKEN frame. The
+ * buffer pointed by |token| must have at least
+ * :macro:`NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN` bytes long. The
+ * successfully generated token starts with
+ * :macro:`NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR`. |secret| of length
+ * |secretlen| is an initial keying material to generate keys to
+ * encrypt the token. |remote_addr| of length |remote_addrlen| is an
+ * address of client. |ts| is the timestamp when the token is
+ * generated.
+ *
+ * This function returns the length of generated token if it succeeds,
+ * or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_verify_regular_token` verifies a regular token
+ * stored in the buffer pointed by |token| of length |tokenlen|.
+ * |secret| of length |secretlen| is an initial keying material to
+ * generate keys to decrypt the token. |remote_addr| of length
+ * |remote_addrlen| is an address of client. |timeout| is the period
+ * during which the token is valid. |ts| is the current timestamp.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_verify_regular_token(
+ const uint8_t *token, size_t tokenlen, const uint8_t *secret,
+ size_t secretlen, const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen, ngtcp2_duration timeout, ngtcp2_tstamp ts);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_write_connection_close` writes Initial packet
+ * containing CONNECTION_CLOSE with the given |error_code| and the
+ * optional |reason| of length |reasonlen| to the buffer pointed by
+ * |dest| of length |destlen|. This function is designed for server
+ * to close connection without committing the state when validating
+ * Retry token fails. This function must not be used by client. The
+ * |dcid| must be the Source Connection ID in Initial packet from
+ * client. The |scid| must be the Destination Connection ID in
+ * Initial packet from client. |scid| is used to derive initial
+ * keying materials.
+ *
+ * This function wraps around `ngtcp2_pkt_write_connection_close` for
+ * easier use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_write_retry` writes Retry packet to the buffer
+ * pointed by |dest| of length |destlen|. |odcid| specifies Original
+ * Destination Connection ID. |token| specifies Retry Token, and
+ * |tokenlen| specifies its length.
+ *
+ * This function wraps around `ngtcp2_pkt_write_retry` for easier use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN ngtcp2_ssize ngtcp2_crypto_write_retry(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, const ngtcp2_cid *odcid, const uint8_t *token,
+ size_t tokenlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_encrypt_init` initializes |aead_ctx| with
+ * new AEAD cipher context object for encryption which is constructed
+ * to use |key| as encryption key. |aead| specifies AEAD cipher to
+ * use. |noncelen| is the length of nonce.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_decrypt_init` initializes |aead_ctx| with
+ * new AEAD cipher context object for decryption which is constructed
+ * to use |key| as encryption key. |aead| specifies AEAD cipher to
+ * use. |noncelen| is the length of nonce.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_ctx_free` frees up resources used by
+ * |aead_ctx|. This function does not free the memory pointed by
+ * |aead_ctx| itself.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_delete_crypto_aead_ctx_cb` deletes the given |aead_ctx|.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.delete_crypto_aead_ctx` field.
+ */
+NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_aead_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_aead_ctx *aead_ctx, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_delete_crypto_cipher_ctx_cb` deletes the given
+ * |cipher_ctx|.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.delete_crypto_cipher_ctx` field.
+ */
+NGTCP2_EXTERN void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_get_path_challenge_data_cb` writes unpredictable
+ * sequence of :macro:`NGTCP2_PATH_CHALLENGE_DATALEN` bytes to |data|
+ * which is sent with PATH_CHALLENGE frame.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.get_path_challenge_data` field.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn,
+ uint8_t *data,
+ void *user_data);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_version_negotiation_cb` installs Initial keys for
+ * |version| which is negotiated or being negotiated. |client_dcid|
+ * is the destination connection ID in first Initial packet of client.
+ *
+ * This function can be directly passed to
+ * :member:`ngtcp2_callbacks.version_negotiation` field.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data);
+
+typedef struct ngtcp2_crypto_conn_ref ngtcp2_crypto_conn_ref;
+
+/**
+ * @functypedef
+ *
+ * :type:`ngtcp2_crypto_get_conn` is a callback function to get a
+ * pointer to :type:`ngtcp2_conn` from |conn_ref|. The implementation
+ * must return non-NULL :type:`ngtcp2_conn` object.
+ */
+typedef ngtcp2_conn *(*ngtcp2_crypto_get_conn)(
+ ngtcp2_crypto_conn_ref *conn_ref);
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_conn_ref` is a structure to get a pointer to
+ * :type:`ngtcp2_conn`. It is meant to be set to TLS native handle as
+ * an application specific data (e.g. SSL_set_app_data in OpenSSL).
+ */
+typedef struct ngtcp2_crypto_conn_ref {
+ /**
+ * :member:`get_conn` is a callback function to get a pointer to
+ * :type:`ngtcp2_conn` object.
+ */
+ ngtcp2_crypto_get_conn get_conn;
+ /**
+ * :member:`user_data` is a pointer to arbitrary user data.
+ */
+ void *user_data;
+} ngtcp2_crypto_conn_ref;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_H */
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h b/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
new file mode 100644
index 0000000..6497c09
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto_boringssl.h
@@ -0,0 +1,104 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * 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 NGTCP2_CRYPTO_BORINGSSL_H
+#define NGTCP2_CRYPTO_BORINGSSL_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <openssl/ssl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_from_ssl_encryption_level` translates
+ * |ssl_level| to :type:`ngtcp2_crypto_level`. This function is only
+ * available for BoringSSL backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_boringssl_from_ssl_encryption_level(
+ enum ssl_encryption_level_t ssl_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to ssl_encryption_level_t. This function is only
+ * available for BoringSSL backend.
+ */
+NGTCP2_EXTERN enum ssl_encryption_level_t
+ngtcp2_crypto_boringssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_configure_server_context` configures
+ * |ssl_ctx| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_boringssl_configure_server_context(SSL_CTX *ssl_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_boringssl_configure_client_context` configures
+ * |ssl_ctx| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_boringssl_configure_client_context(SSL_CTX *ssl_ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_BORINGSSL_H */
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h b/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h
new file mode 100644
index 0000000..af5503f
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h
@@ -0,0 +1,107 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * 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 NGTCP2_CRYPTO_GNUTLS_H
+#define NGTCP2_CRYPTO_GNUTLS_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <gnutls/gnutls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level`
+ * translates |gtls_level| to :type:`ngtcp2_crypto_level`. This
+ * function is only available for GnuTLS backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(
+ gnutls_record_encryption_level_t gtls_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to gnutls_record_encryption_level_t. This function
+ * is only available for GnuTLS backend.
+ */
+NGTCP2_EXTERN gnutls_record_encryption_level_t
+ngtcp2_crypto_gnutls_from_ngtcp2_level(ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_configure_server_session` configures
+ * |session| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set gnutls_handshake_set_secret_function.
+ * - Set gnutls_handshake_set_read_function.
+ * - Set gnutls_alert_set_read_function.
+ * - Register a TLS extension handler for QUIC Transport Parameters.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * gnutls_session_t object by calling gnutls_session_set_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_gnutls_configure_server_session(gnutls_session_t session);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_gnutls_configure_client_session` configures
+ * |session| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set gnutls_handshake_set_secret_function.
+ * - Set gnutls_handshake_set_read_function.
+ * - Set gnutls_alert_set_read_function.
+ * - Register a TLS extension handler for QUIC Transport Parameters.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * gnutls_session_t object by calling gnutls_session_set_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_gnutls_configure_client_session(gnutls_session_t session);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_GNUTLS_H */
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h b/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
new file mode 100644
index 0000000..844081b
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto_openssl.h
@@ -0,0 +1,132 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * 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 NGTCP2_CRYPTO_OPENSSL_H
+#define NGTCP2_CRYPTO_OPENSSL_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <openssl/ssl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @macrosection
+ *
+ * OpenSSL specific error codes
+ */
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP` is the
+ * error code which indicates that TLS handshake routine is
+ * interrupted by X509 certificate lookup. See
+ * :macro:`SSL_ERROR_WANT_X509_LOOKUP` error description from
+ * `SSL_do_handshake`.
+ */
+#define NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP -10001
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB` is the
+ * error code which indicates that TLS handshake routine is
+ * interrupted by client hello callback. See
+ * :macro:`SSL_ERROR_WANT_CLIENT_HELLO_CB` error description from
+ * `SSL_do_handshake`.
+ */
+#define NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB -10002
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_from_ossl_encryption_level` translates
+ * |ossl_level| to :type:`ngtcp2_crypto_level`. This function is only
+ * available for OpenSSL backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_openssl_from_ossl_encryption_level(
+ OSSL_ENCRYPTION_LEVEL ossl_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to OSSL_ENCRYPTION_LEVEL. This function is only
+ * available for OpenSSL backend.
+ */
+NGTCP2_EXTERN OSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_configure_server_context` configures
+ * |ssl_ctx| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_openssl_configure_client_context` configures
+ * |ssl_ctx| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set SSL_QUIC_METHOD by calling SSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling SSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_OPENSSL_H */
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h b/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h
new file mode 100644
index 0000000..d4b551c
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto_picotls.h
@@ -0,0 +1,246 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * 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 NGTCP2_CRYPTO_PICOTLS_H
+#define NGTCP2_CRYPTO_PICOTLS_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <picotls.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @struct
+ *
+ * :type:`ngtcp2_crypto_picotls_ctx` contains per-connection state
+ * of Picotls objects and must be an object to bet set to
+ * `ngtcp2_conn_set_tls_native_handle`.
+ */
+typedef struct ngtcp2_crypto_picotls_ctx {
+ /**
+ * :member:`ptls` is a pointer to ptls_t object.
+ */
+ ptls_t *ptls;
+ /**
+ * :member:`handshake_properties` is a set of configurations used
+ * during this particular TLS handshake.
+ */
+ ptls_handshake_properties_t handshake_properties;
+} ngtcp2_crypto_picotls_ctx;
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_ctx_init` initializes the object pointed by
+ * |cptls|. |cptls| must not be NULL.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_from_epoch` translates |epoch| to
+ * :type:`ngtcp2_crypto_level`. This function is only available for
+ * Picotls backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_picotls_from_epoch(size_t epoch);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to epoch. This function is only available for
+ * Picotls backend.
+ */
+NGTCP2_EXTERN size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_server_context` configures |ctx|
+ * for server side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set max_early_data_size to UINT32_MAX.
+ * - Set omit_end_of_early_data to 1.
+ * - Set update_traffic_key callback.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_client_context` configures |ctx|
+ * for client side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set omit_end_of_early_data to 1.
+ * - Set update_traffic_key callback.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_server_session` configures |cptls|
+ * for server side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set handshake_properties.collect_extension to
+ * `ngtcp2_crypto_picotls_collect_extension`.
+ * - Set handshake_properties.collected_extensions to
+ * `ngtcp2_crypto_picotls_collected_extensions`.
+ *
+ * The callbacks set by this function only handle QUIC Transport
+ * Parameters TLS extension. If an application needs to handle the
+ * other TLS extensions, set its own callbacks and call
+ * `ngtcp2_crypto_picotls_collect_extension` and
+ * `ngtcp2_crypto_picotls_collected_extensions` form them.
+ *
+ * During the QUIC handshake, the first element of
+ * handshake_properties.additional_extensions is assigned to send QUIC
+ * Transport Parameter TLS extension. Therefore, an application must
+ * allocate at least 2 elements for
+ * handshake_properties.additional_extensions.
+ *
+ * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the
+ * resources.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_picotls_configure_server_session(
+ ngtcp2_crypto_picotls_ctx *cptls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_configure_client_session` configures |cptls|
+ * for client side QUIC connection. It performs the following
+ * modifications:
+ *
+ * - Set handshake_properties.max_early_data_size to a pointer to
+ * uint32_t, which is allocated dynamically by this function.
+ * - Set handshake_properties.collect_extension to
+ * `ngtcp2_crypto_picotls_collect_extension`.
+ * - Set handshake_properties.collected_extensions to
+ * `ngtcp2_crypto_picotls_collected_extensions`.
+ * - Set handshake_properties.additional_extensions[0].data to the
+ * dynamically allocated buffer which contains QUIC Transport
+ * Parameters TLS extension. An application must allocate at least
+ * 2 elements for handshake_properties.additional_extensions.
+ *
+ * The callbacks set by this function only handle QUIC Transport
+ * Parameters TLS extension. If an application needs to handle the
+ * other TLS extensions, set its own callbacks and call
+ * `ngtcp2_crypto_picotls_collect_extension` and
+ * `ngtcp2_crypto_picotls_collected_extensions` form them.
+ *
+ * Call `ngtcp2_crypto_picotls_deconfigure_session` to free up the
+ * resources.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * ptls_t object by assigning the pointer using ptls_get_data_ptr, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_picotls_configure_client_session(ngtcp2_crypto_picotls_ctx *cptls,
+ ngtcp2_conn *conn);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_deconfigure_session` frees the resources
+ * allocated for |cptls| during QUIC connection. It frees the
+ * following data using :manpage:`free(3)`.
+ *
+ * - handshake_properties.max_early_data_size
+ * - handshake_properties.additional_extensions[0].data.base
+ *
+ * If |cptls| is NULL, this function does nothing.
+ */
+NGTCP2_EXTERN void
+ngtcp2_crypto_picotls_deconfigure_session(ngtcp2_crypto_picotls_ctx *cptls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_collect_extension` is a callback function
+ * which only returns nonzero if |type| ==
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_picotls_collect_extension(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ uint16_t type);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_picotls_collected_extensions` is a callback function
+ * which only handles the extension of type
+ * :macro:`NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1`. The other
+ * extensions are ignored.
+ */
+NGTCP2_EXTERN int ngtcp2_crypto_picotls_collected_extensions(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ ptls_raw_extension_t *extensions);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_PICOTLS_H */
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h b/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h
new file mode 100644
index 0000000..3b10802
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto_wolfssl.h
@@ -0,0 +1,106 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * 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 NGTCP2_CRYPTO_WOLFSSL_H
+#define NGTCP2_CRYPTO_WOLFSSL_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level` translates
+ * |wolfssl_level| to :type:`ngtcp2_crypto_level`. This function is only
+ * available for wolfSSL backend.
+ */
+NGTCP2_EXTERN ngtcp2_crypto_level
+ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level` translates
+ * |crypto_level| to WOLFSSL_ENCRYPTION_LEVEL. This function is only
+ * available for wolfSSL backend.
+ */
+NGTCP2_EXTERN WOLFSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_configure_server_context` configures
+ * |ssl_ctx| for server side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * WOLFSSL object by calling wolfSSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_wolfssl_configure_client_context` configures
+ * |ssl_ctx| for client side QUIC connection. It performs the
+ * following modifications:
+ *
+ * - Set minimum and maximum TLS version to TLSv1.3.
+ * - Set WOLFSSL_QUIC_METHOD by calling wolfSSL_CTX_set_quic_method.
+ *
+ * Application must set a pointer to :type:`ngtcp2_crypto_conn_ref` to
+ * SSL object by calling wolfSSL_set_app_data, and
+ * :type:`ngtcp2_crypto_conn_ref` object must have
+ * :member:`ngtcp2_crypto_conn_ref.get_conn` field assigned to get
+ * :type:`ngtcp2_conn`.
+ *
+ * It returns 0 if it succeeds, or -1.
+ */
+NGTCP2_EXTERN int
+ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NGTCP2_CRYPTO_WOLFSSL_H */
diff --git a/crypto/openssl/.gitignore b/crypto/openssl/.gitignore
new file mode 100644
index 0000000..296fddc
--- /dev/null
+++ b/crypto/openssl/.gitignore
@@ -0,0 +1 @@
+/libngtcp2_crypto_openssl.pc
diff --git a/crypto/openssl/CMakeLists.txt b/crypto/openssl/CMakeLists.txt
new file mode 100644
index 0000000..d9a126c
--- /dev/null
+++ b/crypto/openssl/CMakeLists.txt
@@ -0,0 +1,85 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2
+
+# 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.
+
+add_definitions(-DBUILDING_NGTCP2)
+
+set(ngtcp2_crypto_openssl_SOURCES
+ openssl.c
+ ../shared.c
+)
+
+set(ngtcp2_crypto_openssl_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto"
+ "${OPENSSL_INCLUDE_DIRS}"
+)
+
+foreach(name libngtcp2_crypto_openssl.pc)
+ configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+# Public shared library
+if(ENABLE_SHARED_LIB)
+ add_library(ngtcp2_crypto_openssl SHARED ${ngtcp2_crypto_openssl_SOURCES})
+ set_target_properties(ngtcp2_crypto_openssl PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${CRYPTO_OPENSSL_LT_VERSION}
+ SOVERSION ${CRYPTO_OPENSSL_LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ POSITION_INDEPENDENT_CODE ON
+ )
+ target_include_directories(ngtcp2_crypto_openssl PUBLIC
+ ${ngtcp2_crypto_openssl_INCLUDE_DIRS})
+ target_link_libraries(ngtcp2_crypto_openssl ngtcp2 ${OPENSSL_LIBRARIES})
+
+ install(TARGETS ngtcp2_crypto_openssl
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif()
+
+if(ENABLE_STATIC_LIB)
+ # Public static library
+ add_library(ngtcp2_crypto_openssl_static ${ngtcp2_crypto_openssl_SOURCES})
+ set_target_properties(ngtcp2_crypto_openssl_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${CRYPTO_OPENSSL_LT_VERSION}
+ SOVERSION ${CRYPTO_OPENSSL_LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ )
+ target_compile_definitions(ngtcp2_crypto_openssl_static PUBLIC
+ "-DNGTCP2_STATICLIB")
+ target_include_directories(ngtcp2_crypto_openssl_static PUBLIC
+ ${ngtcp2_crypto_openssl_INCLUDE_DIRS})
+
+ install(TARGETS ngtcp2_crypto_openssl_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_openssl.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/crypto/openssl/Makefile.am b/crypto/openssl/Makefile.am
new file mode 100644
index 0000000..6880b8b
--- /dev/null
+++ b/crypto/openssl/Makefile.am
@@ -0,0 +1,43 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2 contributors
+
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
+ -I$(top_srcdir)/lib -DBUILDING_NGTCP2 \
+ -I$(top_srcdir)/crypto/includes -I$(top_builddir)/crypto/includes \
+ -I$(top_srcdir)/crypto -I$(top_builddir)/crypto \
+ @OPENSSL_CFLAGS@
+AM_LDFLAGS = ${LIBTOOL_LDFLAGS}
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libngtcp2_crypto_openssl.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libngtcp2_crypto_openssl.la
+
+libngtcp2_crypto_openssl_la_SOURCES = openssl.c ../shared.c ../shared.h
+libngtcp2_crypto_openssl_la_LDFLAGS = -no-undefined \
+ -version-info $(CRYPTO_OPENSSL_LT_CURRENT):$(CRYPTO_OPENSSL_LT_REVISION):$(CRYPTO_OPENSSL_LT_AGE)
+libngtcp2_crypto_openssl_la_LIBADD = $(top_builddir)/lib/libngtcp2.la \
+ @OPENSSL_LIBS@
diff --git a/crypto/openssl/libngtcp2_crypto_openssl.pc.in b/crypto/openssl/libngtcp2_crypto_openssl.pc.in
new file mode 100644
index 0000000..f3226a9
--- /dev/null
+++ b/crypto/openssl/libngtcp2_crypto_openssl.pc.in
@@ -0,0 +1,33 @@
+# ngtcp2
+
+# Copyright (c) 2019 ngtcp2 contributors
+
+# 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.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_openssl
+Description: ngtcp2 OpenSSL crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_openssl
+Cflags: -I${includedir}
diff --git a/crypto/openssl/openssl.c b/crypto/openssl/openssl.c
new file mode 100644
index 0000000..466d9e1
--- /dev/null
+++ b/crypto/openssl/openssl.c
@@ -0,0 +1,807 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * 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 HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/rand.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+# include <openssl/core_names.h>
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+#include "shared.h"
+
+static size_t crypto_aead_max_overhead(const EVP_CIPHER *aead) {
+ switch (EVP_CIPHER_nid(aead)) {
+ case NID_aes_128_gcm:
+ case NID_aes_256_gcm:
+ return EVP_GCM_TLS_TAG_LEN;
+ case NID_chacha20_poly1305:
+ return EVP_CHACHAPOLY_TLS_TAG_LEN;
+ case NID_aes_128_ccm:
+ return EVP_CCM_TLS_TAG_LEN;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm());
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)EVP_sha256();
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)EVP_aes_128_gcm());
+ ctx->md.native_handle = (void *)EVP_sha256();
+ ctx->hp.native_handle = (void *)EVP_aes_128_ctr();
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = crypto_aead_max_overhead(aead_native_handle);
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)EVP_aes_128_gcm());
+}
+
+static const EVP_CIPHER *crypto_ssl_get_aead(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ return EVP_aes_128_gcm();
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return EVP_aes_256_gcm();
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_chacha20_poly1305();
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return EVP_aes_128_ccm();
+ default:
+ return NULL;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_encryption(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+static uint64_t crypto_ssl_get_aead_max_decryption_failure(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
+ default:
+ return 0;
+ }
+}
+
+static const EVP_CIPHER *crypto_ssl_get_hp(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return EVP_aes_128_ctr();
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return EVP_aes_256_ctr();
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ return EVP_chacha20();
+ default:
+ return NULL;
+ }
+}
+
+static const EVP_MD *crypto_ssl_get_md(SSL *ssl) {
+ switch (SSL_CIPHER_get_id(SSL_get_current_cipher(ssl))) {
+ case TLS1_3_CK_AES_128_GCM_SHA256:
+ case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+ case TLS1_3_CK_AES_128_CCM_SHA256:
+ return EVP_sha256();
+ case TLS1_3_CK_AES_256_GCM_SHA384:
+ return EVP_sha384();
+ default:
+ return NULL;
+ }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ SSL *ssl = tls_native_handle;
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)crypto_ssl_get_aead(ssl));
+ ctx->md.native_handle = (void *)crypto_ssl_get_md(ssl);
+ ctx->hp.native_handle = (void *)crypto_ssl_get_hp(ssl);
+ ctx->max_encryption = crypto_ssl_get_aead_max_encryption(ssl);
+ ctx->max_decryption_failure = crypto_ssl_get_aead_max_decryption_failure(ssl);
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
+}
+
+static size_t crypto_md_hashlen(const EVP_MD *md) {
+ return (size_t)EVP_MD_size(md);
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return crypto_md_hashlen(md->native_handle);
+}
+
+static size_t crypto_aead_keylen(const EVP_CIPHER *aead) {
+ return (size_t)EVP_CIPHER_key_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_keylen(aead->native_handle);
+}
+
+static size_t crypto_aead_noncelen(const EVP_CIPHER *aead) {
+ return (size_t)EVP_CIPHER_iv_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_noncelen(aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const EVP_CIPHER *cipher = aead->native_handle;
+ int cipher_nid = EVP_CIPHER_nid(cipher);
+ EVP_CIPHER_CTX *actx;
+ size_t taglen = crypto_aead_max_overhead(cipher);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[3];
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ actx = EVP_CIPHER_CTX_new();
+ if (actx == NULL) {
+ return -1;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen);
+
+ if (cipher_nid == NID_aes_128_ccm) {
+ params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ NULL, taglen);
+ params[2] = OSSL_PARAM_construct_end();
+ } else {
+ params[1] = OSSL_PARAM_construct_end();
+ }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ if (!EVP_EncryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_set_params(actx, params) ||
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen,
+ NULL) ||
+ (cipher_nid == NID_aes_128_ccm &&
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) ||
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ !EVP_EncryptInit_ex(actx, NULL, NULL, key, NULL)) {
+ EVP_CIPHER_CTX_free(actx);
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const EVP_CIPHER *cipher = aead->native_handle;
+ int cipher_nid = EVP_CIPHER_nid(cipher);
+ EVP_CIPHER_CTX *actx;
+ size_t taglen = crypto_aead_max_overhead(cipher);
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[3];
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ actx = EVP_CIPHER_CTX_new();
+ if (actx == NULL) {
+ return -1;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_IVLEN, &noncelen);
+
+ if (cipher_nid == NID_aes_128_ccm) {
+ params[1] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ NULL, taglen);
+ params[2] = OSSL_PARAM_construct_end();
+ } else {
+ params[1] = OSSL_PARAM_construct_end();
+ }
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ if (!EVP_DecryptInit_ex(actx, cipher, NULL, NULL, NULL) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_set_params(actx, params) ||
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_IVLEN, (int)noncelen,
+ NULL) ||
+ (cipher_nid == NID_aes_128_ccm &&
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen, NULL)) ||
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ !EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL)) {
+ EVP_CIPHER_CTX_free(actx);
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ EVP_CIPHER_CTX_free(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ EVP_CIPHER_CTX *actx;
+
+ actx = EVP_CIPHER_CTX_new();
+ if (actx == NULL) {
+ return -1;
+ }
+
+ if (!EVP_EncryptInit_ex(actx, cipher->native_handle, NULL, key, NULL)) {
+ EVP_CIPHER_CTX_free(actx);
+ return -1;
+ }
+
+ cipher_ctx->native_handle = actx;
+
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ EVP_CIPHER_CTX_free(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const EVP_MD *prf = md->native_handle;
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+ int mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(prf), 0),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
+ secretlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt,
+ saltlen),
+ OSSL_PARAM_construct_end(),
+ };
+ int rv = 0;
+
+ EVP_KDF_free(kdf);
+
+ if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(prf), params) <= 0) {
+ rv = -1;
+ }
+
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ const EVP_MD *prf = md->native_handle;
+ int rv = 0;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ size_t destlen = (size_t)EVP_MD_size(prf);
+
+ if (pctx == NULL) {
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_ONLY) != 1 ||
+ EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
+ EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
+ rv = -1;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+
+ return rv;
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const EVP_MD *prf = md->native_handle;
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+ int mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode),
+ OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(prf), 0),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
+ secretlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info,
+ infolen),
+ OSSL_PARAM_construct_end(),
+ };
+ int rv = 0;
+
+ EVP_KDF_free(kdf);
+
+ if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
+ rv = -1;
+ }
+
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ const EVP_MD *prf = md->native_handle;
+ int rv = 0;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) != 1 ||
+ EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_salt(pctx, (const unsigned char *)"", 0) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
+ EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 ||
+ EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
+ rv = -1;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+
+ return rv;
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const EVP_MD *prf = md->native_handle;
+ EVP_KDF *kdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
+ EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(kdf);
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)EVP_MD_get0_name(prf), 0),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY, (void *)secret,
+ secretlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, (void *)salt,
+ saltlen),
+ OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO, (void *)info,
+ infolen),
+ OSSL_PARAM_construct_end(),
+ };
+ int rv = 0;
+
+ EVP_KDF_free(kdf);
+
+ if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
+ rv = -1;
+ }
+
+ EVP_KDF_CTX_free(kctx);
+
+ return rv;
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ const EVP_MD *prf = md->native_handle;
+ int rv = 0;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
+ if (pctx == NULL) {
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_CTX_hkdf_mode(pctx, EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) !=
+ 1 ||
+ EVP_PKEY_CTX_set_hkdf_md(pctx, prf) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_salt(pctx, salt, (int)saltlen) != 1 ||
+ EVP_PKEY_CTX_set1_hkdf_key(pctx, secret, (int)secretlen) != 1 ||
+ EVP_PKEY_CTX_add1_hkdf_info(pctx, info, (int)infolen) != 1 ||
+ EVP_PKEY_derive(pctx, dest, &destlen) != 1) {
+ rv = -1;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+
+ return rv;
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ const EVP_CIPHER *cipher = aead->native_handle;
+ size_t taglen = crypto_aead_max_overhead(cipher);
+ int cipher_nid = EVP_CIPHER_nid(cipher);
+ EVP_CIPHER_CTX *actx = aead_ctx->native_handle;
+ int len;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[] = {
+ OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ dest + plaintextlen, taglen),
+ OSSL_PARAM_construct_end(),
+ };
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ (void)noncelen;
+
+ if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
+ (cipher_nid == NID_aes_128_ccm &&
+ !EVP_EncryptUpdate(actx, NULL, &len, NULL, (int)plaintextlen)) ||
+ !EVP_EncryptUpdate(actx, NULL, &len, aad, (int)aadlen) ||
+ !EVP_EncryptUpdate(actx, dest, &len, plaintext, (int)plaintextlen) ||
+ !EVP_EncryptFinal_ex(actx, dest + len, &len) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_get_params(actx, params)
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_GET_TAG, (int)taglen,
+ dest + plaintextlen)
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ ) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ const EVP_CIPHER *cipher = aead->native_handle;
+ size_t taglen = crypto_aead_max_overhead(cipher);
+ int cipher_nid = EVP_CIPHER_nid(cipher);
+ EVP_CIPHER_CTX *actx = aead_ctx->native_handle;
+ int len;
+ const uint8_t *tag;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ OSSL_PARAM params[2];
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ (void)noncelen;
+
+ if (taglen > ciphertextlen) {
+ return -1;
+ }
+
+ ciphertextlen -= taglen;
+ tag = ciphertext + ciphertextlen;
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ (void *)tag, taglen);
+ params[1] = OSSL_PARAM_construct_end();
+#endif /* OPENSSL_VERSION_NUMBER >= 0x30000000L */
+
+ if (!EVP_DecryptInit_ex(actx, NULL, NULL, NULL, nonce) ||
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ !EVP_CIPHER_CTX_set_params(actx, params) ||
+#else /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ !EVP_CIPHER_CTX_ctrl(actx, EVP_CTRL_AEAD_SET_TAG, (int)taglen,
+ (uint8_t *)tag) ||
+#endif /* !(OPENSSL_VERSION_NUMBER >= 0x30000000L) */
+ (cipher_nid == NID_aes_128_ccm &&
+ !EVP_DecryptUpdate(actx, NULL, &len, NULL, (int)ciphertextlen)) ||
+ !EVP_DecryptUpdate(actx, NULL, &len, aad, (int)aadlen) ||
+ !EVP_DecryptUpdate(actx, dest, &len, ciphertext, (int)ciphertextlen) ||
+ (cipher_nid != NID_aes_128_ccm &&
+ !EVP_DecryptFinal_ex(actx, dest + ciphertextlen, &len))) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
+ int len;
+
+ (void)hp;
+
+ if (!EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) ||
+ !EVP_EncryptUpdate(actx, dest, &len, PLAINTEXT, sizeof(PLAINTEXT) - 1) ||
+ !EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ SSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
+ int rv;
+ int err;
+
+ if (SSL_provide_quic_data(
+ ssl, ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(crypto_level),
+ data, datalen) != 1) {
+ return -1;
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ rv = SSL_do_handshake(ssl);
+ if (rv <= 0) {
+ err = SSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_WANT_CLIENT_HELLO_CB:
+ return NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_CLIENT_HELLO_CB;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return NGTCP2_CRYPTO_OPENSSL_ERR_TLS_WANT_X509_LOOKUP;
+ case SSL_ERROR_SSL:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = SSL_process_quic_post_handshake(ssl);
+ if (rv != 1) {
+ err = SSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ SSL *ssl = tls;
+ const uint8_t *tp;
+ size_t tplen;
+ int rv;
+
+ SSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ if (SSL_set_quic_transport_params(tls, buf, len) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_openssl_from_ossl_encryption_level(
+ OSSL_ENCRYPTION_LEVEL ossl_level) {
+ switch (ossl_level) {
+ case ssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case ssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case ssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case ssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+OSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_openssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return ssl_encryption_initial;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return ssl_encryption_handshake;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return ssl_encryption_application;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return ssl_encryption_early_data;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ if (RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ if (RAND_bytes(data, (int)datalen) != 1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int set_encryption_secrets(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
+ const uint8_t *rx_secret,
+ const uint8_t *tx_secret, size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
+
+ if (rx_secret &&
+ ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ rx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ if (tx_secret &&
+ ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ tx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
+ int rv;
+
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int flush_flight(SSL *ssl) {
+ (void)ssl;
+ return 1;
+}
+
+static int send_alert(SSL *ssl, enum ssl_encryption_level_t level,
+ uint8_t alert) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)level;
+
+ ngtcp2_conn_set_tls_alert(conn, alert);
+
+ return 1;
+}
+
+static SSL_QUIC_METHOD quic_method = {
+ set_encryption_secrets,
+ add_handshake_data,
+ flush_flight,
+ send_alert,
+};
+
+static void crypto_openssl_configure_context(SSL_CTX *ssl_ctx) {
+ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+}
+
+int ngtcp2_crypto_openssl_configure_server_context(SSL_CTX *ssl_ctx) {
+ crypto_openssl_configure_context(ssl_ctx);
+
+ return 0;
+}
+
+int ngtcp2_crypto_openssl_configure_client_context(SSL_CTX *ssl_ctx) {
+ crypto_openssl_configure_context(ssl_ctx);
+
+ return 0;
+}
diff --git a/crypto/picotls/.gitignore b/crypto/picotls/.gitignore
new file mode 100644
index 0000000..3df25b1
--- /dev/null
+++ b/crypto/picotls/.gitignore
@@ -0,0 +1,2 @@
+/libngtcp2_crypto_picotls.pc
+/libngtcp2_crypto_picotls.a
diff --git a/crypto/picotls/CMakeLists.txt b/crypto/picotls/CMakeLists.txt
new file mode 100644
index 0000000..4bfdede
--- /dev/null
+++ b/crypto/picotls/CMakeLists.txt
@@ -0,0 +1,64 @@
+# ngtcp2
+
+# Copyright (c) 2022 ngtcp2
+
+# 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.
+
+add_definitions(-DBUILDING_NGTCP2)
+
+set(ngtcp2_crypto_picotls_SOURCES
+ picotls.c
+ ../shared.c
+)
+
+set(ngtcp2_crypto_picotls_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto"
+ "${PICOTLS_INCLUDE_DIRS}"
+ "${VANILLA_OPENSSL_INCLUDE_DIRS}"
+)
+
+foreach(name libngtcp2_crypto_picotls.pc)
+ configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+if(ENABLE_STATIC_LIB)
+ # Public static library
+ add_library(ngtcp2_crypto_picotls_static ${ngtcp2_crypto_picotls_SOURCES})
+ set_target_properties(ngtcp2_crypto_picotls_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ C_VISIBILITY_PRESET hidden
+ )
+ target_compile_definitions(ngtcp2_crypto_picotls_static PUBLIC
+ "-DNGTCP2_STATICLIB")
+ target_include_directories(ngtcp2_crypto_picotls_static PUBLIC
+ ${ngtcp2_crypto_picotls_INCLUDE_DIRS})
+
+ install(TARGETS ngtcp2_crypto_picotls_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_picotls.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/crypto/picotls/Makefile.am b/crypto/picotls/Makefile.am
new file mode 100644
index 0000000..b2ed766
--- /dev/null
+++ b/crypto/picotls/Makefile.am
@@ -0,0 +1,39 @@
+# ngtcp2
+
+# Copyright (c) 2022 ngtcp2 contributors
+
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
+ -I$(top_srcdir)/lib -DBUILDING_NGTCP2 \
+ -I$(top_srcdir)/crypto/includes -I$(top_builddir)/crypto/includes \
+ -I$(top_srcdir)/crypto -I$(top_builddir)/crypto \
+ @PICOTLS_CFLAGS@ @VANILLA_OPENSSL_CFLAGS@
+AM_LDFLAGS = ${LIBTOOL_LDFLAGS}
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libngtcp2_crypto_picotls.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LIBRARIES = libngtcp2_crypto_picotls.a
+
+libngtcp2_crypto_picotls_a_SOURCES = picotls.c ../shared.c ../shared.h
diff --git a/crypto/picotls/libngtcp2_crypto_picotls.pc.in b/crypto/picotls/libngtcp2_crypto_picotls.pc.in
new file mode 100644
index 0000000..1cb24f1
--- /dev/null
+++ b/crypto/picotls/libngtcp2_crypto_picotls.pc.in
@@ -0,0 +1,33 @@
+# ngtcp2
+
+# Copyright (c) 2022 ngtcp2 contributors
+
+# 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.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_picotls
+Description: ngtcp2 Picotls crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_picotls
+Cflags: -I${includedir}
diff --git a/crypto/picotls/picotls.c b/crypto/picotls/picotls.c
new file mode 100644
index 0000000..b1bbb6c
--- /dev/null
+++ b/crypto/picotls/picotls.c
@@ -0,0 +1,701 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * 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 HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+#include <string.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_picotls.h>
+
+#include <picotls.h>
+#include <picotls/openssl.h>
+
+#include "shared.h"
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm);
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)&ptls_openssl_sha256;
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)&ptls_openssl_aes128gcm);
+ ctx->md.native_handle = (void *)&ptls_openssl_sha256;
+ ctx->hp.native_handle = (void *)&ptls_openssl_aes128ctr;
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ ptls_aead_algorithm_t *alg = aead_native_handle;
+
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = alg->tag_size;
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)&ptls_openssl_aes128gcm);
+}
+
+static const ptls_aead_algorithm_t *crypto_ptls_get_aead(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ return cs->aead;
+}
+
+static uint64_t crypto_ptls_get_aead_max_encryption(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ if (cs->aead == &ptls_openssl_aes128gcm ||
+ cs->aead == &ptls_openssl_aes256gcm) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ }
+
+ if (cs->aead == &ptls_openssl_chacha20poly1305) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ }
+
+ return 0;
+}
+
+static uint64_t crypto_ptls_get_aead_max_decryption_failure(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ if (cs->aead == &ptls_openssl_aes128gcm ||
+ cs->aead == &ptls_openssl_aes256gcm) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ }
+
+ if (cs->aead == &ptls_openssl_chacha20poly1305) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ }
+
+ return 0;
+}
+
+static const ptls_cipher_algorithm_t *crypto_ptls_get_hp(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ if (cs->aead == &ptls_openssl_aes128gcm) {
+ return &ptls_openssl_aes128ctr;
+ }
+
+ if (cs->aead == &ptls_openssl_aes256gcm) {
+ return &ptls_openssl_aes256ctr;
+ }
+
+ if (cs->aead == &ptls_openssl_chacha20poly1305) {
+ return &ptls_openssl_chacha20;
+ }
+
+ return NULL;
+}
+
+static const ptls_hash_algorithm_t *crypto_ptls_get_md(ptls_t *ptls) {
+ ptls_cipher_suite_t *cs = ptls_get_cipher(ptls);
+
+ return cs->hash;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ ngtcp2_crypto_picotls_ctx *cptls = tls_native_handle;
+ ngtcp2_crypto_aead_init(&ctx->aead,
+ (void *)crypto_ptls_get_aead(cptls->ptls));
+ ctx->md.native_handle = (void *)crypto_ptls_get_md(cptls->ptls);
+ ctx->hp.native_handle = (void *)crypto_ptls_get_hp(cptls->ptls);
+ ctx->max_encryption = crypto_ptls_get_aead_max_encryption(cptls->ptls);
+ ctx->max_decryption_failure =
+ crypto_ptls_get_aead_max_decryption_failure(cptls->ptls);
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
+}
+
+static size_t crypto_md_hashlen(const ptls_hash_algorithm_t *md) {
+ return md->digest_size;
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return crypto_md_hashlen(md->native_handle);
+}
+
+static size_t crypto_aead_keylen(const ptls_aead_algorithm_t *aead) {
+ return aead->key_size;
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_keylen(aead->native_handle);
+}
+
+static size_t crypto_aead_noncelen(const ptls_aead_algorithm_t *aead) {
+ return aead->iv_size;
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_noncelen(aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const ptls_aead_algorithm_t *cipher = aead->native_handle;
+ size_t keylen = crypto_aead_keylen(cipher);
+ ptls_aead_context_t *actx;
+ static const uint8_t iv[PTLS_MAX_IV_SIZE] = {0};
+
+ (void)noncelen;
+ (void)keylen;
+
+ actx = ptls_aead_new_direct(cipher, /* is_enc = */ 1, key, iv);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const ptls_aead_algorithm_t *cipher = aead->native_handle;
+ size_t keylen = crypto_aead_keylen(cipher);
+ ptls_aead_context_t *actx;
+ const uint8_t iv[PTLS_MAX_IV_SIZE] = {0};
+
+ (void)noncelen;
+ (void)keylen;
+
+ actx = ptls_aead_new_direct(cipher, /* is_enc = */ 0, key, iv);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ ptls_aead_free(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ ptls_cipher_context_t *actx;
+
+ actx = ptls_cipher_new(cipher->native_handle, /* is_enc = */ 1, key);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ cipher_ctx->native_handle = actx;
+
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ ptls_cipher_free(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ ptls_iovec_t saltv, ikm;
+
+ saltv = ptls_iovec_init(salt, saltlen);
+ ikm = ptls_iovec_init(secret, secretlen);
+
+ if (ptls_hkdf_extract(md->native_handle, dest, saltv, ikm) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ ptls_iovec_t prk, infov;
+
+ prk = ptls_iovec_init(secret, secretlen);
+ infov = ptls_iovec_init(info, infolen);
+
+ if (ptls_hkdf_expand(md->native_handle, dest, destlen, prk, infov) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ ptls_iovec_t saltv, ikm, prk, infov;
+ uint8_t prkbuf[PTLS_MAX_DIGEST_SIZE];
+ ptls_hash_algorithm_t *algo = md->native_handle;
+
+ saltv = ptls_iovec_init(salt, saltlen);
+ ikm = ptls_iovec_init(secret, secretlen);
+
+ if (ptls_hkdf_extract(algo, prkbuf, saltv, ikm) != 0) {
+ return -1;
+ }
+
+ prk = ptls_iovec_init(prkbuf, algo->digest_size);
+ infov = ptls_iovec_init(info, infolen);
+
+ if (ptls_hkdf_expand(algo, dest, destlen, prk, infov) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ ptls_aead_context_t *actx = aead_ctx->native_handle;
+
+ (void)aead;
+
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ ptls_aead_encrypt(actx, dest, plaintext, plaintextlen, 0, aad, aadlen);
+
+ /* zero-out static iv once again */
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ ptls_aead_context_t *actx = aead_ctx->native_handle;
+
+ (void)aead;
+
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ if (ptls_aead_decrypt(actx, dest, ciphertext, ciphertextlen, 0, aad,
+ aadlen) == SIZE_MAX) {
+ return -1;
+ }
+
+ /* zero-out static iv once again */
+ ptls_aead_xor_iv(actx, nonce, noncelen);
+
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ ptls_cipher_context_t *actx = hp_ctx->native_handle;
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+
+ (void)hp;
+
+ ptls_cipher_init(actx, sample);
+ ptls_cipher_encrypt(actx, dest, PLAINTEXT, sizeof(PLAINTEXT) - 1);
+
+ return 0;
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_picotls_ctx *cptls = ngtcp2_conn_get_tls_native_handle(conn);
+ ptls_buffer_t sendbuf;
+ size_t epoch_offsets[5] = {0};
+ size_t epoch = ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(crypto_level);
+ size_t epoch_datalen;
+ size_t i;
+ int rv;
+
+ ptls_buffer_init(&sendbuf, (void *)"", 0);
+
+ assert(epoch == ptls_get_read_epoch(cptls->ptls));
+
+ rv = ptls_handle_message(cptls->ptls, &sendbuf, epoch_offsets, epoch, data,
+ datalen, &cptls->handshake_properties);
+ if (rv != 0 && rv != PTLS_ERROR_IN_PROGRESS) {
+ if (PTLS_ERROR_GET_CLASS(rv) == PTLS_ERROR_CLASS_SELF_ALERT) {
+ ngtcp2_conn_set_tls_alert(conn, (uint8_t)PTLS_ERROR_TO_ALERT(rv));
+ }
+
+ rv = -1;
+ goto fin;
+ }
+
+ if (!ngtcp2_conn_is_server(conn) &&
+ cptls->handshake_properties.client.early_data_acceptance ==
+ PTLS_EARLY_DATA_REJECTED) {
+ rv = ngtcp2_conn_early_data_rejected(conn);
+ if (rv != 0) {
+ rv = -1;
+ goto fin;
+ }
+ }
+
+ for (i = 0; i < 4; ++i) {
+ epoch_datalen = epoch_offsets[i + 1] - epoch_offsets[i];
+ if (epoch_datalen == 0) {
+ continue;
+ }
+
+ assert(i != 1);
+
+ if (ngtcp2_conn_submit_crypto_data(
+ conn, ngtcp2_crypto_picotls_from_epoch(i),
+ sendbuf.base + epoch_offsets[i], epoch_datalen) != 0) {
+ rv = -1;
+ goto fin;
+ }
+ }
+
+ if (rv == 0) {
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = 0;
+
+fin:
+ ptls_buffer_dispose(&sendbuf);
+
+ return rv;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ (void)conn;
+ (void)tls;
+
+ /* The remote transport parameters will be set via picotls
+ collected_extensions callback */
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ (void)tls;
+ (void)buf;
+ (void)len;
+
+ /* The local transport parameters will be set in an external
+ call. */
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_picotls_from_epoch(size_t epoch) {
+ switch (epoch) {
+ case 0:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case 1:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case 2:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case 3:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+size_t ngtcp2_crypto_picotls_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return 0;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return 1;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return 2;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return 3;
+ default:
+ assert(0);
+ abort();
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ptls_openssl_random_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN);
+
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ ptls_openssl_random_bytes(data, datalen);
+
+ return 0;
+}
+
+void ngtcp2_crypto_picotls_ctx_init(ngtcp2_crypto_picotls_ctx *cptls) {
+ cptls->ptls = NULL;
+ memset(&cptls->handshake_properties, 0, sizeof(cptls->handshake_properties));
+}
+
+static int set_additional_extensions(ptls_handshake_properties_t *hsprops,
+ ngtcp2_conn *conn) {
+ const size_t buflen = 256;
+ uint8_t *buf;
+ ngtcp2_ssize nwrite;
+ ptls_raw_extension_t *exts = hsprops->additional_extensions;
+
+ assert(exts);
+
+ buf = malloc(buflen);
+ if (buf == NULL) {
+ return -1;
+ }
+
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, buflen);
+ if (nwrite < 0) {
+ goto fail;
+ }
+
+ exts[0].type = NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1;
+ exts[0].data.base = buf;
+ exts[0].data.len = (size_t)nwrite;
+
+ return 0;
+
+fail:
+ free(buf);
+
+ return -1;
+}
+
+int ngtcp2_crypto_picotls_collect_extension(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ uint16_t type) {
+ (void)ptls;
+ (void)properties;
+
+ return type == NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1;
+}
+
+int ngtcp2_crypto_picotls_collected_extensions(
+ ptls_t *ptls, struct st_ptls_handshake_properties_t *properties,
+ ptls_raw_extension_t *extensions) {
+ ngtcp2_crypto_conn_ref *conn_ref;
+ ngtcp2_conn *conn;
+ int rv;
+
+ (void)properties;
+
+ for (; extensions->type != UINT16_MAX; ++extensions) {
+ if (extensions->type != NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1) {
+ continue;
+ }
+
+ conn_ref = *ptls_get_data_ptr(ptls);
+ conn = conn_ref->get_conn(conn_ref);
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, extensions->data.base,
+ extensions->data.len);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static int update_traffic_key_server_cb(ptls_update_traffic_key_t *self,
+ ptls_t *ptls, int is_enc, size_t epoch,
+ const void *secret) {
+ ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch);
+ ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls);
+ size_t secretlen = cipher->hash->digest_size;
+ ngtcp2_crypto_picotls_ctx *cptls;
+
+ (void)self;
+
+ if (is_enc) {
+ if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (level == NGTCP2_CRYPTO_LEVEL_HANDSHAKE) {
+ /* libngtcp2 allows an application to change QUIC transport
+ * parameters before installing Handshake tx key. We need to
+ * wait for the key to get the correct local transport
+ * parameters from ngtcp2_conn.
+ */
+ cptls = ngtcp2_conn_get_tls_native_handle(conn);
+
+ if (set_additional_extensions(&cptls->handshake_properties, conn) != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+
+ if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ptls_update_traffic_key_t update_traffic_key_server = {
+ update_traffic_key_server_cb,
+};
+
+static int update_traffic_key_cb(ptls_update_traffic_key_t *self, ptls_t *ptls,
+ int is_enc, size_t epoch, const void *secret) {
+ ngtcp2_crypto_conn_ref *conn_ref = *ptls_get_data_ptr(ptls);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level = ngtcp2_crypto_picotls_from_epoch(epoch);
+ ptls_cipher_suite_t *cipher = ptls_get_cipher(ptls);
+ size_t secretlen = cipher->hash->digest_size;
+
+ (void)self;
+
+ if (is_enc) {
+ if (ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ if (ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ secret, secretlen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static ptls_update_traffic_key_t update_traffic_key = {update_traffic_key_cb};
+
+int ngtcp2_crypto_picotls_configure_server_context(ptls_context_t *ctx) {
+ ctx->max_early_data_size = UINT32_MAX;
+ ctx->omit_end_of_early_data = 1;
+ ctx->update_traffic_key = &update_traffic_key_server;
+
+ return 0;
+}
+
+int ngtcp2_crypto_picotls_configure_client_context(ptls_context_t *ctx) {
+ ctx->omit_end_of_early_data = 1;
+ ctx->update_traffic_key = &update_traffic_key;
+
+ return 0;
+}
+
+int ngtcp2_crypto_picotls_configure_server_session(
+ ngtcp2_crypto_picotls_ctx *cptls) {
+ ptls_handshake_properties_t *hsprops = &cptls->handshake_properties;
+
+ hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension;
+ hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions;
+
+ return 0;
+}
+
+int ngtcp2_crypto_picotls_configure_client_session(
+ ngtcp2_crypto_picotls_ctx *cptls, ngtcp2_conn *conn) {
+ ptls_handshake_properties_t *hsprops = &cptls->handshake_properties;
+
+ hsprops->client.max_early_data_size = calloc(1, sizeof(uint32_t));
+ if (hsprops->client.max_early_data_size == NULL) {
+ return -1;
+ }
+
+ if (set_additional_extensions(hsprops, conn) != 0) {
+ free(hsprops->client.max_early_data_size);
+ hsprops->client.max_early_data_size = NULL;
+ return -1;
+ }
+
+ hsprops->collect_extension = ngtcp2_crypto_picotls_collect_extension;
+ hsprops->collected_extensions = ngtcp2_crypto_picotls_collected_extensions;
+
+ return 0;
+}
+
+void ngtcp2_crypto_picotls_deconfigure_session(
+ ngtcp2_crypto_picotls_ctx *cptls) {
+ ptls_handshake_properties_t *hsprops;
+ ptls_raw_extension_t *exts;
+
+ if (cptls == NULL) {
+ return;
+ }
+
+ hsprops = &cptls->handshake_properties;
+
+ free(hsprops->client.max_early_data_size);
+
+ exts = hsprops->additional_extensions;
+ if (exts) {
+ free(hsprops->additional_extensions[0].data.base);
+ }
+}
diff --git a/crypto/shared.c b/crypto/shared.c
new file mode 100644
index 0000000..78252b8
--- /dev/null
+++ b/crypto/shared.c
@@ -0,0 +1,1418 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "shared.h"
+
+#ifdef WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <netinet/in.h>
+#endif
+
+#include <string.h>
+#include <assert.h>
+
+#include "ngtcp2_macro.h"
+#include "ngtcp2_net.h"
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_init(ngtcp2_crypto_md *md,
+ void *md_native_handle) {
+ md->native_handle = md_native_handle;
+ return md;
+}
+
+int ngtcp2_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *label, size_t labellen) {
+ static const uint8_t LABEL[] = "tls13 ";
+ uint8_t info[256];
+ uint8_t *p = info;
+
+ *p++ = (uint8_t)(destlen / 256);
+ *p++ = (uint8_t)(destlen % 256);
+ *p++ = (uint8_t)(sizeof(LABEL) - 1 + labellen);
+ memcpy(p, LABEL, sizeof(LABEL) - 1);
+ p += sizeof(LABEL) - 1;
+ memcpy(p, label, labellen);
+ p += labellen;
+ *p++ = 0;
+
+ return ngtcp2_crypto_hkdf_expand(dest, destlen, md, secret, secretlen, info,
+ (size_t)(p - info));
+}
+
+#define NGTCP2_CRYPTO_INITIAL_SECRETLEN 32
+
+int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
+ uint8_t *tx_secret,
+ uint8_t *initial_secret,
+ const ngtcp2_cid *client_dcid,
+ ngtcp2_crypto_side side) {
+ static const uint8_t CLABEL[] = "client in";
+ static const uint8_t SLABEL[] = "server in";
+ uint8_t initial_secret_buf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t *client_secret;
+ uint8_t *server_secret;
+ ngtcp2_crypto_ctx ctx;
+ const uint8_t *salt;
+ size_t saltlen;
+
+ if (!initial_secret) {
+ initial_secret = initial_secret_buf;
+ }
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V1;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_V2_DRAFT;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_V2_DRAFT) - 1;
+ break;
+ default:
+ salt = (const uint8_t *)NGTCP2_INITIAL_SALT_DRAFT;
+ saltlen = sizeof(NGTCP2_INITIAL_SALT_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_hkdf_extract(initial_secret, &ctx.md, client_dcid->data,
+ client_dcid->datalen, salt, saltlen) != 0) {
+ return -1;
+ }
+
+ if (side == NGTCP2_CRYPTO_SIDE_SERVER) {
+ client_secret = rx_secret;
+ server_secret = tx_secret;
+ } else {
+ client_secret = tx_secret;
+ server_secret = rx_secret;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(
+ client_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md,
+ initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, CLABEL,
+ sizeof(CLABEL) - 1) != 0 ||
+ ngtcp2_crypto_hkdf_expand_label(
+ server_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, &ctx.md,
+ initial_secret, NGTCP2_CRYPTO_INITIAL_SECRETLEN, SLABEL,
+ sizeof(SLABEL) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+size_t ngtcp2_crypto_packet_protection_ivlen(const ngtcp2_crypto_aead *aead) {
+ size_t noncelen = ngtcp2_crypto_aead_noncelen(aead);
+ return ngtcp2_max(8, noncelen);
+}
+
+int ngtcp2_crypto_derive_packet_protection_key(
+ uint8_t *key, uint8_t *iv, uint8_t *hp_key, uint32_t version,
+ const ngtcp2_crypto_aead *aead, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen) {
+ static const uint8_t KEY_LABEL_V1[] = "quic key";
+ static const uint8_t IV_LABEL_V1[] = "quic iv";
+ static const uint8_t HP_KEY_LABEL_V1[] = "quic hp";
+ static const uint8_t KEY_LABEL_V2_DRAFT[] = "quicv2 key";
+ static const uint8_t IV_LABEL_V2_DRAFT[] = "quicv2 iv";
+ static const uint8_t HP_KEY_LABEL_V2_DRAFT[] = "quicv2 hp";
+ size_t keylen = ngtcp2_crypto_aead_keylen(aead);
+ size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+ const uint8_t *key_label;
+ size_t key_labellen;
+ const uint8_t *iv_label;
+ size_t iv_labellen;
+ const uint8_t *hp_key_label;
+ size_t hp_key_labellen;
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ key_label = KEY_LABEL_V2_DRAFT;
+ key_labellen = sizeof(KEY_LABEL_V2_DRAFT) - 1;
+ iv_label = IV_LABEL_V2_DRAFT;
+ iv_labellen = sizeof(IV_LABEL_V2_DRAFT) - 1;
+ hp_key_label = HP_KEY_LABEL_V2_DRAFT;
+ hp_key_labellen = sizeof(HP_KEY_LABEL_V2_DRAFT) - 1;
+ break;
+ default:
+ key_label = KEY_LABEL_V1;
+ key_labellen = sizeof(KEY_LABEL_V1) - 1;
+ iv_label = IV_LABEL_V1;
+ iv_labellen = sizeof(IV_LABEL_V1) - 1;
+ hp_key_label = HP_KEY_LABEL_V1;
+ hp_key_labellen = sizeof(HP_KEY_LABEL_V1) - 1;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(key, keylen, md, secret, secretlen,
+ key_label, key_labellen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_hkdf_expand_label(iv, ivlen, md, secret, secretlen,
+ iv_label, iv_labellen) != 0) {
+ return -1;
+ }
+
+ if (hp_key != NULL &&
+ ngtcp2_crypto_hkdf_expand_label(hp_key, keylen, md, secret, secretlen,
+ hp_key_label, hp_key_labellen) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_update_traffic_secret(uint8_t *dest,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen) {
+ static const uint8_t LABEL[] = "quic ku";
+
+ if (ngtcp2_crypto_hkdf_expand_label(dest, secretlen, md, secret, secretlen,
+ LABEL, sizeof(LABEL) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_derive_and_install_rx_key(ngtcp2_conn *conn, uint8_t *key,
+ uint8_t *iv, uint8_t *hp_key,
+ ngtcp2_crypto_level level,
+ const uint8_t *secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx;
+ const ngtcp2_crypto_aead *aead;
+ const ngtcp2_crypto_md *md;
+ const ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ uint8_t keybuf[64], ivbuf[64], hp_keybuf[64];
+ size_t ivlen;
+ int rv;
+ ngtcp2_crypto_ctx cctx;
+ uint32_t version;
+
+ if (level == NGTCP2_CRYPTO_LEVEL_EARLY && !ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ if (!key) {
+ key = keybuf;
+ }
+ if (!iv) {
+ iv = ivbuf;
+ }
+ if (!hp_key) {
+ hp_key = hp_keybuf;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ ngtcp2_crypto_ctx_tls_early(&cctx, tls);
+ ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
+ version = ngtcp2_conn_get_client_chosen_version(conn);
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ if (ngtcp2_conn_is_server(conn) &&
+ !ngtcp2_conn_get_negotiated_version(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ return -1;
+ }
+ }
+ /* fall through */
+ default:
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ version = ngtcp2_conn_get_negotiated_version(conn);
+
+ if (!ctx->aead.native_handle) {
+ ngtcp2_crypto_ctx_tls(&cctx, tls);
+ ngtcp2_conn_set_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ }
+ }
+
+ aead = &ctx->aead;
+ md = &ctx->md;
+ hp = &ctx->hp;
+ ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+
+ if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
+ md, secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, aead, key, ivlen) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) {
+ goto fail;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, iv, ivlen,
+ &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ if (!ngtcp2_conn_is_server(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ goto fail;
+ }
+ }
+
+ rv = ngtcp2_conn_install_rx_key(conn, secret, secretlen, &aead_ctx, iv,
+ ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return -1;
+}
+
+/*
+ * crypto_set_local_transport_params gets local QUIC transport
+ * parameters from |conn| and sets it to |tls|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+static int crypto_set_local_transport_params(ngtcp2_conn *conn, void *tls) {
+ ngtcp2_ssize nwrite;
+ uint8_t buf[256];
+
+ nwrite = ngtcp2_conn_encode_local_transport_params(conn, buf, sizeof(buf));
+ if (nwrite < 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_set_local_transport_params(tls, buf, (size_t)nwrite) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_derive_and_install_tx_key(ngtcp2_conn *conn, uint8_t *key,
+ uint8_t *iv, uint8_t *hp_key,
+ ngtcp2_crypto_level level,
+ const uint8_t *secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx;
+ const ngtcp2_crypto_aead *aead;
+ const ngtcp2_crypto_md *md;
+ const ngtcp2_crypto_cipher *hp;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ uint8_t keybuf[64], ivbuf[64], hp_keybuf[64];
+ size_t ivlen;
+ int rv;
+ ngtcp2_crypto_ctx cctx;
+ uint32_t version;
+
+ if (level == NGTCP2_CRYPTO_LEVEL_EARLY && ngtcp2_conn_is_server(conn)) {
+ return 0;
+ }
+
+ if (!key) {
+ key = keybuf;
+ }
+ if (!iv) {
+ iv = ivbuf;
+ }
+ if (!hp_key) {
+ hp_key = hp_keybuf;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ ngtcp2_crypto_ctx_tls_early(&cctx, tls);
+ ngtcp2_conn_set_early_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_early_crypto_ctx(conn);
+ version = ngtcp2_conn_get_client_chosen_version(conn);
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ if (ngtcp2_conn_is_server(conn) &&
+ !ngtcp2_conn_get_negotiated_version(conn)) {
+ rv = ngtcp2_crypto_set_remote_transport_params(conn, tls);
+ if (rv != 0) {
+ return -1;
+ }
+ }
+ /* fall through */
+ default:
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ version = ngtcp2_conn_get_negotiated_version(conn);
+
+ if (!ctx->aead.native_handle) {
+ ngtcp2_crypto_ctx_tls(&cctx, tls);
+ ngtcp2_conn_set_crypto_ctx(conn, &cctx);
+ ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ }
+ }
+
+ aead = &ctx->aead;
+ md = &ctx->md;
+ hp = &ctx->hp;
+ ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+
+ if (ngtcp2_crypto_derive_packet_protection_key(key, iv, hp_key, version, aead,
+ md, secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, aead, key, ivlen) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, hp, hp_key) != 0) {
+ goto fail;
+ }
+
+ switch (level) {
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ rv = ngtcp2_conn_install_early_key(conn, &aead_ctx, iv, ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+ break;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, iv, ivlen,
+ &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_conn_is_server(conn) &&
+ crypto_set_local_transport_params(conn, tls) != 0) {
+ goto fail;
+ }
+
+ break;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ rv = ngtcp2_conn_install_tx_key(conn, secret, secretlen, &aead_ctx, iv,
+ ivlen, &hp_ctx);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ break;
+ default:
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return -1;
+}
+
+int ngtcp2_crypto_derive_and_install_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
+ uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
+ uint32_t version, const ngtcp2_cid *client_dcid) {
+ uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ ngtcp2_crypto_ctx ctx;
+ ngtcp2_crypto_aead retry_aead;
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx retry_aead_ctx = {0};
+ int rv;
+ int server = ngtcp2_conn_is_server(conn);
+ const uint8_t *retry_key;
+ size_t retry_noncelen;
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ if (!rx_secret) {
+ rx_secret = rx_secretbuf;
+ }
+ if (!tx_secret) {
+ tx_secret = tx_secretbuf;
+ }
+ if (!initial_secret) {
+ initial_secret = initial_secretbuf;
+ }
+
+ if (!rx_key) {
+ rx_key = rx_keybuf;
+ }
+ if (!rx_iv) {
+ rx_iv = rx_ivbuf;
+ }
+ if (!rx_hp_key) {
+ rx_hp_key = rx_hp_keybuf;
+ }
+ if (!tx_key) {
+ tx_key = tx_keybuf;
+ }
+ if (!tx_iv) {
+ tx_iv = tx_ivbuf;
+ }
+ if (!tx_hp_key) {
+ tx_hp_key = tx_hp_keybuf;
+ }
+
+ ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx);
+
+ if (ngtcp2_crypto_derive_initial_secrets(
+ version, rx_secret, tx_secret, initial_secret, client_dcid,
+ server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, rx_hp_key, version, &ctx.aead, &ctx.md, rx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx.aead, rx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx.hp, rx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx.aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx.hp, tx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (!server && !ngtcp2_conn_after_retry(conn)) {
+ ngtcp2_crypto_aead_retry(&retry_aead);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+ break;
+ default:
+ retry_key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
+ retry_noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&retry_aead_ctx, &retry_aead,
+ retry_key, retry_noncelen) != 0) {
+ goto fail;
+ }
+ }
+
+ rv = ngtcp2_conn_install_initial_key(conn, &rx_aead_ctx, rx_iv, &rx_hp_ctx,
+ &tx_aead_ctx, tx_iv, &tx_hp_ctx,
+ NGTCP2_CRYPTO_INITIAL_IVLEN);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ if (retry_aead_ctx.native_handle) {
+ ngtcp2_conn_set_retry_aead(conn, &retry_aead, &retry_aead_ctx);
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_aead_ctx_free(&retry_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
+
+ return -1;
+}
+
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv,
+ uint8_t *rx_hp_key, uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp_key,
+ uint32_t version, const ngtcp2_cid *client_dcid) {
+ uint8_t rx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secretbuf[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t rx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t rx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t rx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_ivbuf[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_keybuf[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_initial_crypto_ctx(conn);
+ ngtcp2_crypto_aead_ctx rx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx rx_hp_ctx = {0};
+ ngtcp2_crypto_aead_ctx tx_aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx tx_hp_ctx = {0};
+ int rv;
+ int server = ngtcp2_conn_is_server(conn);
+
+ if (!rx_secret) {
+ rx_secret = rx_secretbuf;
+ }
+ if (!tx_secret) {
+ tx_secret = tx_secretbuf;
+ }
+ if (!initial_secret) {
+ initial_secret = initial_secretbuf;
+ }
+
+ if (!rx_key) {
+ rx_key = rx_keybuf;
+ }
+ if (!rx_iv) {
+ rx_iv = rx_ivbuf;
+ }
+ if (!rx_hp_key) {
+ rx_hp_key = rx_hp_keybuf;
+ }
+ if (!tx_key) {
+ tx_key = tx_keybuf;
+ }
+ if (!tx_iv) {
+ tx_iv = tx_ivbuf;
+ }
+ if (!tx_hp_key) {
+ tx_hp_key = tx_hp_keybuf;
+ }
+
+ if (ngtcp2_crypto_derive_initial_secrets(
+ version, rx_secret, tx_secret, initial_secret, client_dcid,
+ server ? NGTCP2_CRYPTO_SIDE_SERVER : NGTCP2_CRYPTO_SIDE_CLIENT) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, rx_hp_key, version, &ctx->aead, &ctx->md, rx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx->aead, &ctx->md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&rx_aead_ctx, &ctx->aead, rx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&rx_hp_ctx, &ctx->hp, rx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&tx_aead_ctx, &ctx->aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ goto fail;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&tx_hp_ctx, &ctx->hp, tx_hp_key) !=
+ 0) {
+ goto fail;
+ }
+
+ rv = ngtcp2_conn_install_vneg_initial_key(
+ conn, version, &rx_aead_ctx, rx_iv, &rx_hp_ctx, &tx_aead_ctx, tx_iv,
+ &tx_hp_ctx, NGTCP2_CRYPTO_INITIAL_IVLEN);
+ if (rv != 0) {
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ ngtcp2_crypto_cipher_ctx_free(&tx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&tx_aead_ctx);
+ ngtcp2_crypto_cipher_ctx_free(&rx_hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&rx_aead_ctx);
+
+ return -1;
+}
+
+int ngtcp2_crypto_update_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_key, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_key, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen) {
+ const ngtcp2_crypto_ctx *ctx = ngtcp2_conn_get_crypto_ctx(conn);
+ const ngtcp2_crypto_aead *aead = &ctx->aead;
+ const ngtcp2_crypto_md *md = &ctx->md;
+ size_t ivlen = ngtcp2_crypto_packet_protection_ivlen(aead);
+ uint32_t version = ngtcp2_conn_get_negotiated_version(conn);
+
+ if (ngtcp2_crypto_update_traffic_secret(rx_secret, md, current_rx_secret,
+ secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ rx_key, rx_iv, NULL, version, aead, md, rx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_update_traffic_secret(tx_secret, md, current_tx_secret,
+ secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, NULL, version, aead, md, tx_secret, secretlen) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(rx_aead_ctx, aead, rx_key, ivlen) !=
+ 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(tx_aead_ctx, aead, tx_key, ivlen) !=
+ 0) {
+ ngtcp2_crypto_aead_ctx_free(rx_aead_ctx);
+ rx_aead_ctx->native_handle = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ if (ngtcp2_crypto_encrypt(dest, aead, aead_ctx, plaintext, plaintextlen,
+ nonce, noncelen, aad, aadlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt_cb(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ if (ngtcp2_crypto_decrypt(dest, aead, aead_ctx, ciphertext, ciphertextlen,
+ nonce, noncelen, aad, aadlen) != 0) {
+ return NGTCP2_ERR_DECRYPT;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask_cb(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ if (ngtcp2_crypto_hp_mask(dest, hp, hp_ctx, sample) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_update_key_cb(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv,
+ ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv,
+ const uint8_t *current_rx_secret, const uint8_t *current_tx_secret,
+ size_t secretlen, void *user_data) {
+ uint8_t rx_key[64];
+ uint8_t tx_key[64];
+ (void)conn;
+ (void)user_data;
+
+ if (ngtcp2_crypto_update_key(conn, rx_secret, tx_secret, rx_aead_ctx, rx_key,
+ rx_iv, tx_aead_ctx, tx_key, tx_iv,
+ current_rx_secret, current_tx_secret,
+ secretlen) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token,
+ const uint8_t *secret,
+ size_t secretlen,
+ const ngtcp2_cid *cid) {
+ static const uint8_t info[] = "stateless_reset";
+ ngtcp2_crypto_md md;
+
+ if (ngtcp2_crypto_hkdf(token, NGTCP2_STATELESS_RESET_TOKENLEN,
+ ngtcp2_crypto_md_sha256(&md), secret, secretlen,
+ cid->data, cid->datalen, info,
+ sizeof(info) - 1) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int crypto_derive_token_key(uint8_t *key, size_t keylen, uint8_t *iv,
+ size_t ivlen, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen,
+ const uint8_t *info_prefix,
+ size_t info_prefixlen) {
+ static const uint8_t key_info_suffix[] = " key";
+ static const uint8_t iv_info_suffix[] = " iv";
+ uint8_t intsecret[32];
+ uint8_t info[32];
+ uint8_t *p;
+
+ assert(ngtcp2_crypto_md_hashlen(md) == sizeof(intsecret));
+ assert(info_prefixlen + sizeof(key_info_suffix) - 1 <= sizeof(info));
+ assert(info_prefixlen + sizeof(iv_info_suffix) - 1 <= sizeof(info));
+
+ if (ngtcp2_crypto_hkdf_extract(intsecret, md, secret, secretlen, salt,
+ saltlen) != 0) {
+ return -1;
+ }
+
+ memcpy(info, info_prefix, info_prefixlen);
+ p = info + info_prefixlen;
+
+ memcpy(p, key_info_suffix, sizeof(key_info_suffix) - 1);
+ p += sizeof(key_info_suffix) - 1;
+
+ if (ngtcp2_crypto_hkdf_expand(key, keylen, md, intsecret, sizeof(intsecret),
+ info, (size_t)(p - info)) != 0) {
+ return -1;
+ }
+
+ p = info + info_prefixlen;
+
+ memcpy(p, iv_info_suffix, sizeof(iv_info_suffix) - 1);
+ p += sizeof(iv_info_suffix) - 1;
+
+ if (ngtcp2_crypto_hkdf_expand(iv, ivlen, md, intsecret, sizeof(intsecret),
+ info, (size_t)(p - info)) != 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static size_t crypto_generate_retry_token_aad(uint8_t *dest, uint32_t version,
+ const ngtcp2_sockaddr *sa,
+ ngtcp2_socklen salen,
+ const ngtcp2_cid *retry_scid) {
+ uint8_t *p = dest;
+
+ version = ngtcp2_htonl(version);
+ memcpy(p, &version, sizeof(version));
+ memcpy(p, sa, (size_t)salen);
+ p += salen;
+ memcpy(p, retry_scid->data, retry_scid->datalen);
+ p += retry_scid->datalen;
+
+ return (size_t)(p - dest);
+}
+
+static const uint8_t retry_token_info_prefix[] = "retry_token";
+
+ngtcp2_ssize ngtcp2_crypto_generate_retry_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *retry_scid, const ngtcp2_cid *odcid, ngtcp2_tstamp ts) {
+ uint8_t plaintext[NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN];
+ uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ size_t plaintextlen;
+ uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+ NGTCP2_MAX_CIDLEN];
+ size_t aadlen;
+ uint8_t *p = plaintext;
+ ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
+ int rv;
+
+ memset(plaintext, 0, sizeof(plaintext));
+
+ *p++ = (uint8_t)odcid->datalen;
+ memcpy(p, odcid->data, odcid->datalen);
+ p += NGTCP2_MAX_CIDLEN;
+ memcpy(p, &ts_be, sizeof(ts_be));
+ p += sizeof(ts_be);
+
+ plaintextlen = (size_t)(p - plaintext);
+
+ if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
+ return -1;
+ }
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ assert(sizeof(key) >= keylen);
+ assert(sizeof(iv) >= ivlen);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, sizeof(rand_data),
+ retry_token_info_prefix,
+ sizeof(retry_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+ remote_addrlen, retry_scid);
+
+ p = token;
+ *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY;
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
+ ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ p += plaintextlen + aead.max_overhead;
+ memcpy(p, rand_data, sizeof(rand_data));
+ p += sizeof(rand_data);
+
+ return p - token;
+}
+
+int ngtcp2_crypto_verify_retry_token(
+ ngtcp2_cid *odcid, const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen, uint32_t version,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ const ngtcp2_cid *dcid, ngtcp2_duration timeout, ngtcp2_tstamp ts) {
+ uint8_t
+ plaintext[/* cid len = */ 1 + NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp)];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ uint8_t aad[sizeof(version) + sizeof(ngtcp2_sockaddr_storage) +
+ NGTCP2_MAX_CIDLEN];
+ size_t aadlen;
+ const uint8_t *rand_data;
+ const uint8_t *ciphertext;
+ size_t ciphertextlen;
+ size_t cil;
+ int rv;
+ ngtcp2_tstamp gen_ts;
+
+ if (tokenlen != NGTCP2_CRYPTO_MAX_RETRY_TOKENLEN ||
+ token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY) {
+ return -1;
+ }
+
+ rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+ ciphertext = token + 1;
+ ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
+ retry_token_info_prefix,
+ sizeof(retry_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_retry_token_aad(aad, version, remote_addr,
+ remote_addrlen, dcid);
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
+ ciphertextlen, iv, ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ cil = plaintext[0];
+
+ assert(cil == 0 || (cil >= NGTCP2_MIN_CIDLEN && cil <= NGTCP2_MAX_CIDLEN));
+
+ memcpy(&gen_ts, plaintext + /* cid len = */ 1 + NGTCP2_MAX_CIDLEN,
+ sizeof(gen_ts));
+
+ gen_ts = ngtcp2_ntohl64(gen_ts);
+ if (gen_ts + timeout <= ts) {
+ return -1;
+ }
+
+ ngtcp2_cid_init(odcid, plaintext + /* cid len = */ 1, cil);
+
+ return 0;
+}
+
+static size_t crypto_generate_regular_token_aad(uint8_t *dest,
+ const ngtcp2_sockaddr *sa) {
+ const uint8_t *addr;
+ size_t addrlen;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ addr = (const uint8_t *)&((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr;
+ addrlen = sizeof(((const ngtcp2_sockaddr_in *)(void *)sa)->sin_addr);
+ break;
+ case AF_INET6:
+ addr =
+ (const uint8_t *)&((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr;
+ addrlen = sizeof(((const ngtcp2_sockaddr_in6 *)(void *)sa)->sin6_addr);
+ break;
+ default:
+ assert(0);
+ abort();
+ }
+
+ memcpy(dest, addr, addrlen);
+
+ return addrlen;
+}
+
+static const uint8_t regular_token_info_prefix[] = "regular_token";
+
+ngtcp2_ssize ngtcp2_crypto_generate_regular_token(
+ uint8_t *token, const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr, ngtcp2_socklen remote_addrlen,
+ ngtcp2_tstamp ts) {
+ uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ uint8_t rand_data[NGTCP2_CRYPTO_TOKEN_RAND_DATALEN];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ size_t plaintextlen;
+ uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
+ size_t aadlen;
+ uint8_t *p = plaintext;
+ ngtcp2_tstamp ts_be = ngtcp2_htonl64(ts);
+ int rv;
+ (void)remote_addrlen;
+
+ memcpy(p, &ts_be, sizeof(ts_be));
+ p += sizeof(ts_be);
+
+ plaintextlen = (size_t)(p - plaintext);
+
+ if (ngtcp2_crypto_random(rand_data, sizeof(rand_data)) != 0) {
+ return -1;
+ }
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ assert(sizeof(key) >= keylen);
+ assert(sizeof(iv) >= ivlen);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, sizeof(rand_data),
+ regular_token_info_prefix,
+ sizeof(regular_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
+
+ p = token;
+ *p++ = NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR;
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_encrypt(p, &aead, &aead_ctx, plaintext, plaintextlen, iv,
+ ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ p += plaintextlen + aead.max_overhead;
+ memcpy(p, rand_data, sizeof(rand_data));
+ p += sizeof(rand_data);
+
+ return p - token;
+}
+
+int ngtcp2_crypto_verify_regular_token(const uint8_t *token, size_t tokenlen,
+ const uint8_t *secret, size_t secretlen,
+ const ngtcp2_sockaddr *remote_addr,
+ ngtcp2_socklen remote_addrlen,
+ ngtcp2_duration timeout,
+ ngtcp2_tstamp ts) {
+ uint8_t plaintext[sizeof(ngtcp2_tstamp)];
+ uint8_t key[32];
+ uint8_t iv[32];
+ size_t keylen;
+ size_t ivlen;
+ ngtcp2_crypto_aead_ctx aead_ctx;
+ ngtcp2_crypto_aead aead;
+ ngtcp2_crypto_md md;
+ uint8_t aad[sizeof(ngtcp2_sockaddr_in6)];
+ size_t aadlen;
+ const uint8_t *rand_data;
+ const uint8_t *ciphertext;
+ size_t ciphertextlen;
+ int rv;
+ ngtcp2_tstamp gen_ts;
+ (void)remote_addrlen;
+
+ if (tokenlen != NGTCP2_CRYPTO_MAX_REGULAR_TOKENLEN ||
+ token[0] != NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR) {
+ return -1;
+ }
+
+ rand_data = token + tokenlen - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+ ciphertext = token + 1;
+ ciphertextlen = tokenlen - 1 - NGTCP2_CRYPTO_TOKEN_RAND_DATALEN;
+
+ ngtcp2_crypto_aead_aes_128_gcm(&aead);
+ ngtcp2_crypto_md_sha256(&md);
+
+ keylen = ngtcp2_crypto_aead_keylen(&aead);
+ ivlen = ngtcp2_crypto_aead_noncelen(&aead);
+
+ if (crypto_derive_token_key(key, keylen, iv, ivlen, &md, secret, secretlen,
+ rand_data, NGTCP2_CRYPTO_TOKEN_RAND_DATALEN,
+ regular_token_info_prefix,
+ sizeof(regular_token_info_prefix) - 1) != 0) {
+ return -1;
+ }
+
+ aadlen = crypto_generate_regular_token_aad(aad, remote_addr);
+
+ if (ngtcp2_crypto_aead_ctx_decrypt_init(&aead_ctx, &aead, key, ivlen) != 0) {
+ return -1;
+ }
+
+ rv = ngtcp2_crypto_decrypt(plaintext, &aead, &aead_ctx, ciphertext,
+ ciphertextlen, iv, ivlen, aad, aadlen);
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ if (rv != 0) {
+ return -1;
+ }
+
+ memcpy(&gen_ts, plaintext, sizeof(gen_ts));
+
+ gen_ts = ngtcp2_ntohl64(gen_ts);
+ if (gen_ts + timeout <= ts) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_ssize ngtcp2_crypto_write_connection_close(
+ uint8_t *dest, size_t destlen, uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid, uint64_t error_code, const uint8_t *reason,
+ size_t reasonlen) {
+ uint8_t rx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t initial_secret[NGTCP2_CRYPTO_INITIAL_SECRETLEN];
+ uint8_t tx_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ uint8_t tx_iv[NGTCP2_CRYPTO_INITIAL_IVLEN];
+ uint8_t tx_hp_key[NGTCP2_CRYPTO_INITIAL_KEYLEN];
+ ngtcp2_crypto_ctx ctx;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ ngtcp2_crypto_cipher_ctx hp_ctx = {0};
+
+ ngtcp2_crypto_ctx_initial(&ctx);
+
+ if (ngtcp2_crypto_derive_initial_secrets(version, rx_secret, tx_secret,
+ initial_secret, scid,
+ NGTCP2_CRYPTO_SIDE_SERVER) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_derive_packet_protection_key(
+ tx_key, tx_iv, tx_hp_key, version, &ctx.aead, &ctx.md, tx_secret,
+ NGTCP2_CRYPTO_INITIAL_SECRETLEN) != 0) {
+ return -1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &ctx.aead, tx_key,
+ NGTCP2_CRYPTO_INITIAL_IVLEN) != 0) {
+ spktlen = -1;
+ goto end;
+ }
+
+ if (ngtcp2_crypto_cipher_ctx_encrypt_init(&hp_ctx, &ctx.hp, tx_hp_key) != 0) {
+ spktlen = -1;
+ goto end;
+ }
+
+ spktlen = ngtcp2_pkt_write_connection_close(
+ dest, destlen, version, dcid, scid, error_code, reason, reasonlen,
+ ngtcp2_crypto_encrypt_cb, &ctx.aead, &aead_ctx, tx_iv,
+ ngtcp2_crypto_hp_mask_cb, &ctx.hp, &hp_ctx);
+ if (spktlen < 0) {
+ spktlen = -1;
+ }
+
+end:
+ ngtcp2_crypto_cipher_ctx_free(&hp_ctx);
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return spktlen;
+}
+
+ngtcp2_ssize ngtcp2_crypto_write_retry(uint8_t *dest, size_t destlen,
+ uint32_t version, const ngtcp2_cid *dcid,
+ const ngtcp2_cid *scid,
+ const ngtcp2_cid *odcid,
+ const uint8_t *token, size_t tokenlen) {
+ ngtcp2_crypto_aead aead;
+ ngtcp2_ssize spktlen;
+ ngtcp2_crypto_aead_ctx aead_ctx = {0};
+ const uint8_t *key;
+ size_t noncelen;
+
+ ngtcp2_crypto_aead_retry(&aead);
+
+ switch (version) {
+ case NGTCP2_PROTO_VER_V1:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_V1;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V1) - 1;
+ break;
+ case NGTCP2_PROTO_VER_V2_DRAFT:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_V2_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_V2_DRAFT) - 1;
+ break;
+ default:
+ key = (const uint8_t *)NGTCP2_RETRY_KEY_DRAFT;
+ noncelen = sizeof(NGTCP2_RETRY_NONCE_DRAFT) - 1;
+ }
+
+ if (ngtcp2_crypto_aead_ctx_encrypt_init(&aead_ctx, &aead, key, noncelen) !=
+ 0) {
+ return -1;
+ }
+
+ spktlen = ngtcp2_pkt_write_retry(dest, destlen, version, dcid, scid, odcid,
+ token, tokenlen, ngtcp2_crypto_encrypt_cb,
+ &aead, &aead_ctx);
+ if (spktlen < 0) {
+ spktlen = -1;
+ }
+
+ ngtcp2_crypto_aead_ctx_free(&aead_ctx);
+
+ return spktlen;
+}
+
+int ngtcp2_crypto_client_initial_cb(ngtcp2_conn *conn, void *user_data) {
+ const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(conn);
+ void *tls = ngtcp2_conn_get_tls_native_handle(conn);
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (crypto_set_local_transport_params(conn, tls) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ if (ngtcp2_crypto_read_write_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL,
+ NULL, 0) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_recv_retry_cb(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), &hd->scid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_recv_client_initial_cb(ngtcp2_conn *conn,
+ const ngtcp2_cid *dcid,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ ngtcp2_conn_get_client_chosen_version(conn), dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_version_negotiation_cb(ngtcp2_conn *conn, uint32_t version,
+ const ngtcp2_cid *client_dcid,
+ void *user_data) {
+ (void)user_data;
+
+ if (ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ conn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, version,
+ client_dcid) != 0) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+void ngtcp2_crypto_delete_crypto_aead_ctx_cb(ngtcp2_conn *conn,
+ ngtcp2_crypto_aead_ctx *aead_ctx,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ngtcp2_crypto_aead_ctx_free(aead_ctx);
+}
+
+void ngtcp2_crypto_delete_crypto_cipher_ctx_cb(
+ ngtcp2_conn *conn, ngtcp2_crypto_cipher_ctx *cipher_ctx, void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ ngtcp2_crypto_cipher_ctx_free(cipher_ctx);
+}
+
+int ngtcp2_crypto_recv_crypto_data_cb(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ uint64_t offset, const uint8_t *data,
+ size_t datalen, void *user_data) {
+ int rv;
+ (void)offset;
+ (void)user_data;
+
+ if (ngtcp2_crypto_read_write_crypto_data(conn, crypto_level, data, datalen) !=
+ 0) {
+ rv = ngtcp2_conn_get_tls_error(conn);
+ if (rv) {
+ return rv;
+ }
+ return NGTCP2_ERR_CRYPTO;
+ }
+
+ return 0;
+}
diff --git a/crypto/shared.h b/crypto/shared.h
new file mode 100644
index 0000000..02b9489
--- /dev/null
+++ b/crypto/shared.h
@@ -0,0 +1,350 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2019 ngtcp2 contributors
+ *
+ * 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 NGTCP2_SHARED_H
+#define NGTCP2_SHARED_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <ngtcp2/ngtcp2_crypto.h>
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_DRAFT` is a salt value which is used to
+ * derive initial secret. It is used for QUIC draft versions.
+ */
+#define NGTCP2_INITIAL_SALT_DRAFT \
+ "\xaf\xbf\xec\x28\x99\x93\xd2\x4c\x9e\x97\x86\xf1\x9c\x61\x11\xe0\x43\x90" \
+ "\xa8\x99"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to
+ * derive initial secret. It is used for QUIC v1.
+ */
+#define NGTCP2_INITIAL_SALT_V1 \
+ "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc\xbb" \
+ "\x7f\x0a"
+
+/**
+ * @macro
+ *
+ * :macro:`NGTCP2_INITIAL_SALT_V2_DRAFT` is a salt value which is used to
+ * derive initial secret. It is used for QUIC v2 draft.
+ */
+#define NGTCP2_INITIAL_SALT_V2_DRAFT \
+ "\xa7\x07\xc2\x03\xa5\x9b\x47\x18\x4a\x1d\x62\xca\x57\x04\x06\xea\x7a\xe3" \
+ "\xe5\xd3"
+
+/* Maximum key usage (encryption) limits */
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
+#define NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM (2965820ULL)
+
+/* Maximum authentication failure (decryption) limits during the
+ lifetime of a connection. */
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM (1ULL << 52)
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305 (1ULL << 36)
+#define NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM (2965820ULL)
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_ctx_initial` initializes |ctx| for Initial packet
+ * encryption and decryption.
+ */
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_init` initializes |aead| with the provided
+ * |aead_native_handle| which is an underlying AEAD object.
+ *
+ * If libngtcp2_crypto_openssl is linked, |aead_native_handle| must be
+ * a pointer to EVP_CIPHER.
+ *
+ * If libngtcp2_crypto_gnutls is linked, |aead_native_handle| must be
+ * gnutls_cipher_algorithm_t casted to ``void *``.
+ *
+ * If libngtcp2_crypto_boringssl is linked, |aead_native_handle| must
+ * be a pointer to EVP_AEAD.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_aead_retry` initializes |aead| with the AEAD cipher
+ * AEAD_AES_128_GCM for Retry packet integrity protection.
+ */
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_initial_secrets` derives initial secrets.
+ * |rx_secret| and |tx_secret| must point to the buffer of at least 32
+ * bytes capacity. rx for read and tx for write. This function
+ * writes rx and tx secrets into |rx_secret| and |tx_secret|
+ * respectively. The length of secret is 32 bytes long.
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client. If |initial_secret| is not NULL, the initial
+ * secret is written to it. It must point to the buffer which has at
+ * least 32 bytes capacity. The initial secret is 32 bytes long.
+ * |side| specifies the side of application.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_initial_secrets(uint32_t version, uint8_t *rx_secret,
+ uint8_t *tx_secret,
+ uint8_t *initial_secret,
+ const ngtcp2_cid *client_dcid,
+ ngtcp2_crypto_side side);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_packet_protection_key` derives packet
+ * protection key. This function writes packet protection key into
+ * the buffer pointed by |key|. The length of derived key is
+ * `ngtcp2_crypto_aead_keylen(aead) <ngtcp2_crypto_aead_keylen>`
+ * bytes. |key| must have enough capacity to store the key. This
+ * function writes packet protection IV into |iv|. The length of
+ * derived IV is `ngtcp2_crypto_packet_protection_ivlen(aead)
+ * <ngtcp2_crypto_packet_protection_ivlen>` bytes. |iv| must have
+ * enough capacity to store the IV.
+ *
+ * If |hp| is not NULL, this function also derives packet header
+ * protection key and writes the key into the buffer pointed by |hp|.
+ * The length of derived key is `ngtcp2_crypto_aead_keylen(aead)
+ * <ngtcp2_crypto_aead_keylen>` bytes. |hp|, if not NULL, must have
+ * enough capacity to store the key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_packet_protection_key(uint8_t *key, uint8_t *iv,
+ uint8_t *hp, uint32_t version,
+ const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_update_traffic_secret` derives the next generation
+ * of the traffic secret. |secret| specifies the current secret and
+ * its length is given in |secretlen|. The length of new key is the
+ * same as the current key. This function writes new key into the
+ * buffer pointed by |dest|. |dest| must have the enough capacity to
+ * store the new key.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_update_traffic_secret(uint8_t *dest,
+ const ngtcp2_crypto_md *md,
+ const uint8_t *secret,
+ size_t secretlen);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_set_local_transport_params` sets QUIC transport
+ * parameter, which is encoded in wire format and stored in the buffer
+ * pointed by |buf| of length |len|, to the native handle |tls|.
+ *
+ * |tls| points to a implementation dependent TLS session object. If
+ * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL
+ * object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_set_remote_transport_params` retrieves a remote QUIC
+ * transport parameters from |tls| and sets it to |conn| using
+ * `ngtcp2_conn_set_remote_transport_params`.
+ *
+ * |tls| points to a implementation dependent TLS session object. If
+ * libngtcp2_crypto_openssl is linked, |tls| must be a pointer to SSL
+ * object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_initial_key` derives initial
+ * keying materials and installs keys to |conn|.
+ *
+ * If |rx_secret| is not NULL, the secret for decryption is written to
+ * the buffer pointed by |rx_secret|. The length of secret is 32
+ * bytes, and |rx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |tx_secret| is not NULL, the secret for encryption is written to
+ * the buffer pointed by |tx_secret|. The length of secret is 32
+ * bytes, and |tx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |initial_secret| is not NULL, the initial secret is written to
+ * the buffer pointed by |initial_secret|. The length of secret is 32
+ * bytes, and |initial_secret| must point to the buffer which has
+ * enough capacity.
+ *
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client.
+ *
+ * If |rx_key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |rx_key|. If
+ * |rx_iv| is not NULL, the derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp|
+ * is not NULL, the derived header protection key for decryption is
+ * written to the buffer pointed by |rx_hp|.
+ *
+ * If |tx_key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |tx_key|. If
+ * |tx_iv| is not NULL, the derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp|
+ * is not NULL, the derived header protection key for encryption is
+ * written to the buffer pointed by |tx_hp|.
+ *
+ * The length of packet protection key and header protection key is 16
+ * bytes long. The length of packet protection IV is 12 bytes long.
+ *
+ * This function calls `ngtcp2_conn_set_initial_crypto_ctx` to set
+ * initial AEAD and message digest algorithm. After the successful
+ * call of this function, application can use
+ * `ngtcp2_conn_get_initial_crypto_ctx` to get the object.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_and_install_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
+ uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
+ const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_derive_and_install_vneg_initial_key` derives initial
+ * keying materials and installs keys to |conn|. This function is
+ * dedicated to install keys for |version| which is negotiated, or
+ * being negotiated.
+ *
+ * If |rx_secret| is not NULL, the secret for decryption is written to
+ * the buffer pointed by |rx_secret|. The length of secret is 32
+ * bytes, and |rx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |tx_secret| is not NULL, the secret for encryption is written to
+ * the buffer pointed by |tx_secret|. The length of secret is 32
+ * bytes, and |tx_secret| must point to the buffer which has enough
+ * capacity.
+ *
+ * If |initial_secret| is not NULL, the initial secret is written to
+ * the buffer pointed by |initial_secret|. The length of secret is 32
+ * bytes, and |initial_secret| must point to the buffer which has
+ * enough capacity.
+ *
+ * |client_dcid| is the destination connection ID in first Initial
+ * packet of client.
+ *
+ * If |rx_key| is not NULL, the derived packet protection key for
+ * decryption is written to the buffer pointed by |rx_key|. If
+ * |rx_iv| is not NULL, the derived packet protection IV for
+ * decryption is written to the buffer pointed by |rx_iv|. If |rx_hp|
+ * is not NULL, the derived header protection key for decryption is
+ * written to the buffer pointed by |rx_hp|.
+ *
+ * If |tx_key| is not NULL, the derived packet protection key for
+ * encryption is written to the buffer pointed by |tx_key|. If
+ * |tx_iv| is not NULL, the derived packet protection IV for
+ * encryption is written to the buffer pointed by |tx_iv|. If |tx_hp|
+ * is not NULL, the derived header protection key for encryption is
+ * written to the buffer pointed by |tx_hp|.
+ *
+ * The length of packet protection key and header protection key is 16
+ * bytes long. The length of packet protection IV is 12 bytes long.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_derive_and_install_vneg_initial_key(
+ ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret,
+ uint8_t *initial_secret, uint8_t *rx_key, uint8_t *rx_iv, uint8_t *rx_hp,
+ uint8_t *tx_key, uint8_t *tx_iv, uint8_t *tx_hp, uint32_t version,
+ const ngtcp2_cid *client_dcid);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_cipher_ctx_encrypt_init` initializes |cipher_ctx|
+ * with new cipher context object for encryption which is constructed
+ * to use |key| as encryption key. |cipher| specifies cipher to use.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key);
+
+/**
+ * @function
+ *
+ * `ngtcp2_crypto_cipher_ctx_free` frees up resources used by
+ * |cipher_ctx|. This function does not free the memory pointed by
+ * |cipher_ctx| itself.
+ */
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx);
+
+/*
+ * `ngtcp2_crypto_md_sha256` initializes |md| with SHA256 message
+ * digest algorithm and returns |md|.
+ */
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md);
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead);
+
+/*
+ * `ngtcp2_crypto_random` writes cryptographically-secure random
+ * |datalen| bytes into the buffer pointed by |data|.
+ *
+ * This function returns 0 if it succeeds, or -1.
+ */
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen);
+
+#endif /* NGTCP2_SHARED_H */
diff --git a/crypto/wolfssl/.gitignore b/crypto/wolfssl/.gitignore
new file mode 100644
index 0000000..936b2be
--- /dev/null
+++ b/crypto/wolfssl/.gitignore
@@ -0,0 +1 @@
+/libngtcp2_crypto_wolfssl.pc
diff --git a/crypto/wolfssl/CMakeLists.txt b/crypto/wolfssl/CMakeLists.txt
new file mode 100644
index 0000000..8cea3e5
--- /dev/null
+++ b/crypto/wolfssl/CMakeLists.txt
@@ -0,0 +1,83 @@
+# ngtcp2
+
+# Copyright (c) 2022 ngtcp2
+
+# 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.
+
+add_definitions(-DBUILDING_NGTCP2)
+
+set(ngtcp2_crypto_wolfssl_SOURCES
+ wolfssl.c
+ ../shared.c
+)
+
+set(ngtcp2_crypto_wolfssl_INCLUDE_DIRS
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
+ "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto"
+ "${CMAKE_CURRENT_BINARY_DIR}/../../crypto"
+ "${WOLFSSL_INCLUDE_DIRS}"
+)
+
+foreach(name libngtcp2_crypto_wolfssl.pc)
+ configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+# Public shared library
+if(ENABLE_SHARED_LIB)
+ add_library(ngtcp2_crypto_wolfssl SHARED ${ngtcp2_crypto_wolfssl_SOURCES})
+ set_target_properties(ngtcp2_crypto_wolfssl PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ VERSION ${CRYPTO_WOLFSSL_LT_VERSION}
+ SOVERSION ${CRYPTO_WOLFSSL_LT_SOVERSION}
+ C_VISIBILITY_PRESET hidden
+ POSITION_INDEPENDENT_CODE ON
+ )
+ target_include_directories(ngtcp2_crypto_wolfssl PUBLIC
+ ${ngtcp2_crypto_wolfssl_INCLUDE_DIRS})
+ target_link_libraries(ngtcp2_crypto_wolfssl ngtcp2 ${WOLFSSL_LIBRARIES})
+
+ install(TARGETS ngtcp2_crypto_wolfssl
+ ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
+ RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif()
+
+if(ENABLE_STATIC_LIB)
+ # Public static library
+ add_library(ngtcp2_crypto_wolfssl_static ${ngtcp2_crypto_wolfssl_SOURCES})
+ set_target_properties(ngtcp2_crypto_wolfssl_static PROPERTIES
+ COMPILE_FLAGS "${WARNCFLAGS}"
+ C_VISIBILITY_PRESET hidden
+ )
+ target_compile_definitions(ngtcp2_crypto_wolfssl_static PUBLIC
+ "-DNGTCP2_STATICLIB")
+ target_include_directories(ngtcp2_crypto_wolfssl_static PUBLIC
+ ${ngtcp2_crypto_wolfssl_INCLUDE_DIRS})
+
+ install(TARGETS ngtcp2_crypto_wolfssl_static
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+endif()
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_wolfssl.pc"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/crypto/wolfssl/Makefile.am b/crypto/wolfssl/Makefile.am
new file mode 100644
index 0000000..b9d5f8c
--- /dev/null
+++ b/crypto/wolfssl/Makefile.am
@@ -0,0 +1,43 @@
+# ngtcp2
+
+# Copyright (c) 2022 ngtcp2 contributors
+
+# 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.
+EXTRA_DIST = CMakeLists.txt
+
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
+ -I$(top_srcdir)/lib -DBUILDING_NGTCP2 \
+ -I$(top_srcdir)/crypto/includes -I$(top_builddir)/crypto/includes \
+ -I$(top_srcdir)/crypto -I$(top_builddir)/crypto \
+ @WOLFSSL_CFLAGS@
+AM_LDFLAGS = ${LIBTOOL_LDFLAGS}
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libngtcp2_crypto_wolfssl.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libngtcp2_crypto_wolfssl.la
+
+libngtcp2_crypto_wolfssl_la_SOURCES = wolfssl.c ../shared.c ../shared.h
+libngtcp2_crypto_wolfssl_la_LDFLAGS = -no-undefined \
+ -version-info $(CRYPTO_WOLFSSL_LT_CURRENT):$(CRYPTO_WOLFSSL_LT_REVISION):$(CRYPTO_WOLFSSL_LT_AGE)
+libngtcp2_crypto_wolfssl_la_LIBADD = $(top_builddir)/lib/libngtcp2.la \
+ @WOLFSSL_LIBS@
diff --git a/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in b/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in
new file mode 100644
index 0000000..720c784
--- /dev/null
+++ b/crypto/wolfssl/libngtcp2_crypto_wolfssl.pc.in
@@ -0,0 +1,33 @@
+# ngtcp2
+
+# Copyright (c) 2022 ngtcp2 contributors
+
+# 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.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_wolfssl
+Description: ngtcp2 wolfSSL crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_wolfssl
+Cflags: -I${includedir}
diff --git a/crypto/wolfssl/wolfssl.c b/crypto/wolfssl/wolfssl.c
new file mode 100644
index 0000000..4c341de
--- /dev/null
+++ b/crypto/wolfssl/wolfssl.c
@@ -0,0 +1,534 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2022 ngtcp2 contributors
+ *
+ * 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 HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
+
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+
+#include "shared.h"
+
+#define PRINTF_DEBUG 0
+#if PRINTF_DEBUG
+# define DEBUG_MSG(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define DEBUG_MSG(...) (void)0
+#endif
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_aes_128_gcm(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm());
+}
+
+ngtcp2_crypto_md *ngtcp2_crypto_md_sha256(ngtcp2_crypto_md *md) {
+ md->native_handle = (void *)wolfSSL_EVP_sha256();
+ return md;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_EVP_aes_128_gcm());
+ ctx->md.native_handle = (void *)wolfSSL_EVP_sha256();
+ ctx->hp.native_handle = (void *)wolfSSL_EVP_aes_128_ctr();
+ ctx->max_encryption = 0;
+ ctx->max_decryption_failure = 0;
+ return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_init(ngtcp2_crypto_aead *aead,
+ void *aead_native_handle) {
+ aead->native_handle = aead_native_handle;
+ aead->max_overhead = wolfSSL_quic_get_aead_tag_len(
+ (const WOLFSSL_EVP_CIPHER *)(aead_native_handle));
+ return aead;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+ return ngtcp2_crypto_aead_init(aead, (void *)wolfSSL_EVP_aes_128_gcm());
+}
+
+static uint64_t crypto_wolfssl_get_aead_max_encryption(WOLFSSL *ssl) {
+ const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl);
+
+ if (wolfSSL_quic_aead_is_gcm(aead)) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_GCM;
+ }
+ if (wolfSSL_quic_aead_is_chacha20(aead)) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
+ }
+ if (wolfSSL_quic_aead_is_ccm(aead)) {
+ return NGTCP2_CRYPTO_MAX_ENCRYPTION_AES_CCM;
+ }
+ return 0;
+}
+
+static uint64_t crypto_wolfssl_get_aead_max_decryption_failure(WOLFSSL *ssl) {
+ const WOLFSSL_EVP_CIPHER *aead = wolfSSL_quic_get_aead(ssl);
+
+ if (wolfSSL_quic_aead_is_gcm(aead)) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_GCM;
+ }
+ if (wolfSSL_quic_aead_is_chacha20(aead)) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_CHACHA20_POLY1305;
+ }
+ if (wolfSSL_quic_aead_is_ccm(aead)) {
+ return NGTCP2_CRYPTO_MAX_DECRYPTION_FAILURE_AES_CCM;
+ }
+ return 0;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ WOLFSSL *ssl = tls_native_handle;
+
+ ngtcp2_crypto_aead_init(&ctx->aead, (void *)wolfSSL_quic_get_aead(ssl));
+ ctx->md.native_handle = (void *)wolfSSL_quic_get_md(ssl);
+ ctx->hp.native_handle = (void *)wolfSSL_quic_get_hp(ssl);
+ ctx->max_encryption = crypto_wolfssl_get_aead_max_encryption(ssl);
+ ctx->max_decryption_failure =
+ crypto_wolfssl_get_aead_max_decryption_failure(ssl);
+ return ctx;
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls_early(ngtcp2_crypto_ctx *ctx,
+ void *tls_native_handle) {
+ return ngtcp2_crypto_ctx_tls(ctx, tls_native_handle);
+}
+
+static size_t crypto_md_hashlen(const WOLFSSL_EVP_MD *md) {
+ return (size_t)wolfSSL_EVP_MD_size(md);
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+ return crypto_md_hashlen(md->native_handle);
+}
+
+static size_t crypto_aead_keylen(const WOLFSSL_EVP_CIPHER *aead) {
+ return (size_t)wolfSSL_EVP_Cipher_key_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_keylen(aead->native_handle);
+}
+
+static size_t crypto_aead_noncelen(const WOLFSSL_EVP_CIPHER *aead) {
+ return (size_t)wolfSSL_EVP_CIPHER_iv_length(aead);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+ return crypto_aead_noncelen(aead->native_handle);
+}
+
+int ngtcp2_crypto_aead_ctx_encrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle;
+ WOLFSSL_EVP_CIPHER_CTX *actx;
+ static const uint8_t iv[AES_BLOCK_SIZE] = {0};
+
+ (void)noncelen;
+ actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 1);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+ return 0;
+}
+
+int ngtcp2_crypto_aead_ctx_decrypt_init(ngtcp2_crypto_aead_ctx *aead_ctx,
+ const ngtcp2_crypto_aead *aead,
+ const uint8_t *key, size_t noncelen) {
+ const WOLFSSL_EVP_CIPHER *cipher = aead->native_handle;
+ WOLFSSL_EVP_CIPHER_CTX *actx;
+ static const uint8_t iv[AES_BLOCK_SIZE] = {0};
+
+ (void)noncelen;
+ actx = wolfSSL_quic_crypt_new(cipher, key, iv, /* encrypt */ 0);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ aead_ctx->native_handle = actx;
+ return 0;
+}
+
+void ngtcp2_crypto_aead_ctx_free(ngtcp2_crypto_aead_ctx *aead_ctx) {
+ if (aead_ctx->native_handle) {
+ wolfSSL_EVP_CIPHER_CTX_free(aead_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_cipher_ctx_encrypt_init(ngtcp2_crypto_cipher_ctx *cipher_ctx,
+ const ngtcp2_crypto_cipher *cipher,
+ const uint8_t *key) {
+ WOLFSSL_EVP_CIPHER_CTX *actx;
+
+ actx =
+ wolfSSL_quic_crypt_new(cipher->native_handle, key, NULL, /* encrypt */ 1);
+ if (actx == NULL) {
+ return -1;
+ }
+
+ cipher_ctx->native_handle = actx;
+ return 0;
+}
+
+void ngtcp2_crypto_cipher_ctx_free(ngtcp2_crypto_cipher_ctx *cipher_ctx) {
+ if (cipher_ctx->native_handle) {
+ wolfSSL_EVP_CIPHER_CTX_free(cipher_ctx->native_handle);
+ }
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+ const uint8_t *secret, size_t secretlen,
+ const uint8_t *salt, size_t saltlen) {
+ if (wolfSSL_quic_hkdf_extract(dest, md->native_handle, secret, secretlen,
+ salt, saltlen) != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: wolfSSL_quic_hkdf_extract FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *info,
+ size_t infolen) {
+ if (wolfSSL_quic_hkdf_expand(dest, destlen, md->native_handle, secret,
+ secretlen, info, infolen) != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: wolfSSL_quic_hkdf_expand FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hkdf(uint8_t *dest, size_t destlen,
+ const ngtcp2_crypto_md *md, const uint8_t *secret,
+ size_t secretlen, const uint8_t *salt, size_t saltlen,
+ const uint8_t *info, size_t infolen) {
+ if (wolfSSL_quic_hkdf(dest, destlen, md->native_handle, secret, secretlen,
+ salt, saltlen, info, infolen) != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: wolfSSL_quic_hkdf FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *plaintext, size_t plaintextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)aead;
+ (void)noncelen;
+ if (wolfSSL_quic_aead_encrypt(dest, aead_ctx->native_handle, plaintext,
+ plaintextlen, nonce, aad,
+ aadlen) != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: encrypt FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+ const ngtcp2_crypto_aead_ctx *aead_ctx,
+ const uint8_t *ciphertext, size_t ciphertextlen,
+ const uint8_t *nonce, size_t noncelen,
+ const uint8_t *aad, size_t aadlen) {
+ (void)aead;
+ (void)noncelen;
+ if (wolfSSL_quic_aead_decrypt(dest, aead_ctx->native_handle, ciphertext,
+ ciphertextlen, nonce, aad,
+ aadlen) != WOLFSSL_SUCCESS) {
+
+ DEBUG_MSG("WOLFSSL: decrypt FAILED\n");
+ return -1;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+ const ngtcp2_crypto_cipher_ctx *hp_ctx,
+ const uint8_t *sample) {
+ static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+ WOLFSSL_EVP_CIPHER_CTX *actx = hp_ctx->native_handle;
+ int len;
+
+ (void)hp;
+
+ if (wolfSSL_EVP_EncryptInit_ex(actx, NULL, NULL, NULL, sample) !=
+ WOLFSSL_SUCCESS ||
+ wolfSSL_EVP_CipherUpdate(actx, dest, &len, PLAINTEXT,
+ sizeof(PLAINTEXT) - 1) != WOLFSSL_SUCCESS ||
+ wolfSSL_EVP_EncryptFinal_ex(actx, dest + sizeof(PLAINTEXT) - 1, &len) !=
+ WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: hp_mask FAILED\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn,
+ ngtcp2_crypto_level crypto_level,
+ const uint8_t *data, size_t datalen) {
+ WOLFSSL *ssl = ngtcp2_conn_get_tls_native_handle(conn);
+ WOLFSSL_ENCRYPTION_LEVEL level =
+ ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(crypto_level);
+ int rv;
+ int err;
+
+ DEBUG_MSG("WOLFSSL: read/write crypto data, level=%d len=%lu\n", level,
+ datalen);
+ if (datalen > 0) {
+ rv = wolfSSL_provide_quic_data(ssl, level, data, datalen);
+ if (rv != WOLFSSL_SUCCESS) {
+ DEBUG_MSG("WOLFSSL: read/write crypto data FAILED, rv=%d\n", rv);
+ return -1;
+ }
+ }
+
+ if (!ngtcp2_conn_get_handshake_completed(conn)) {
+ rv = wolfSSL_quic_do_handshake(ssl);
+ if (rv <= 0) {
+ err = wolfSSL_get_error(ssl, rv);
+ DEBUG_MSG("WOLFSSL: do_handshake, rv=%d, err=%d\n", rv, err);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ DEBUG_MSG("WOLFSSL: handshake done\n");
+ ngtcp2_conn_handshake_completed(conn);
+ }
+
+ rv = wolfSSL_process_quic_post_handshake(ssl);
+ DEBUG_MSG("WOLFSSL: process post handshake, rv=%d\n", rv);
+ if (rv != 1) {
+ err = wolfSSL_get_error(ssl, rv);
+ switch (err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ return 0;
+ case SSL_ERROR_SSL:
+ case SSL_ERROR_ZERO_RETURN:
+ return -1;
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls) {
+ WOLFSSL *ssl = tls;
+ const uint8_t *tp;
+ size_t tplen;
+ int rv;
+
+ wolfSSL_get_peer_quic_transport_params(ssl, &tp, &tplen);
+ DEBUG_MSG("WOLFSSL: get peer transport params, len=%lu\n", tplen);
+
+ rv = ngtcp2_conn_decode_remote_transport_params(conn, tp, tplen);
+ if (rv != 0) {
+ DEBUG_MSG("WOLFSSL: decode peer transport params failed, rv=%d\n", rv);
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ngtcp2_crypto_set_local_transport_params(void *tls, const uint8_t *buf,
+ size_t len) {
+ WOLFSSL *ssl = tls;
+ DEBUG_MSG("WOLFSSL: set local peer transport params, len=%lu\n", len);
+ if (wolfSSL_set_quic_transport_params(ssl, buf, len) != WOLFSSL_SUCCESS) {
+ return -1;
+ }
+
+ return 0;
+}
+
+ngtcp2_crypto_level ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level) {
+ switch (wolfssl_level) {
+ case wolfssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case wolfssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case wolfssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case wolfssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APPLICATION;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+WOLFSSL_ENCRYPTION_LEVEL
+ngtcp2_crypto_wolfssl_from_ngtcp2_crypto_level(
+ ngtcp2_crypto_level crypto_level) {
+ switch (crypto_level) {
+ case NGTCP2_CRYPTO_LEVEL_INITIAL:
+ return wolfssl_encryption_initial;
+ case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+ return wolfssl_encryption_handshake;
+ case NGTCP2_CRYPTO_LEVEL_APPLICATION:
+ return wolfssl_encryption_application;
+ case NGTCP2_CRYPTO_LEVEL_EARLY:
+ return wolfssl_encryption_early_data;
+ default:
+ assert(0);
+ abort(); /* if NDEBUG is set */
+ }
+}
+
+int ngtcp2_crypto_get_path_challenge_data_cb(ngtcp2_conn *conn, uint8_t *data,
+ void *user_data) {
+ (void)conn;
+ (void)user_data;
+
+ DEBUG_MSG("WOLFSSL: get path challenge data\n");
+ if (wolfSSL_RAND_bytes(data, NGTCP2_PATH_CHALLENGE_DATALEN) != 1) {
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+ return 0;
+}
+
+int ngtcp2_crypto_random(uint8_t *data, size_t datalen) {
+ DEBUG_MSG("WOLFSSL: get random\n");
+ if (wolfSSL_RAND_bytes(data, (int)datalen) != 1) {
+ return -1;
+ }
+ return 0;
+}
+
+static int set_encryption_secrets(WOLFSSL *ssl,
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level,
+ const uint8_t *rx_secret,
+ const uint8_t *tx_secret, size_t secretlen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level);
+
+ DEBUG_MSG("WOLFSSL: set encryption secrets, level=%d, rxlen=%lu, txlen=%lu\n",
+ wolfssl_level, rx_secret ? secretlen : 0,
+ tx_secret ? secretlen : 0);
+ if (rx_secret &&
+ ngtcp2_crypto_derive_and_install_rx_key(conn, NULL, NULL, NULL, level,
+ rx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ if (tx_secret &&
+ ngtcp2_crypto_derive_and_install_tx_key(conn, NULL, NULL, NULL, level,
+ tx_secret, secretlen) != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+static int add_handshake_data(WOLFSSL *ssl,
+ WOLFSSL_ENCRYPTION_LEVEL wolfssl_level,
+ const uint8_t *data, size_t datalen) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ ngtcp2_crypto_level level =
+ ngtcp2_crypto_wolfssl_from_wolfssl_encryption_level(wolfssl_level);
+ int rv;
+
+ DEBUG_MSG("WOLFSSL: add handshake data, level=%d len=%lu\n", wolfssl_level,
+ datalen);
+ rv = ngtcp2_conn_submit_crypto_data(conn, level, data, datalen);
+ if (rv != 0) {
+ ngtcp2_conn_set_tls_error(conn, rv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int flush_flight(WOLFSSL *ssl) {
+ (void)ssl;
+ return 1;
+}
+
+static int send_alert(WOLFSSL *ssl, enum wolfssl_encryption_level_t level,
+ uint8_t alert) {
+ ngtcp2_crypto_conn_ref *conn_ref = SSL_get_app_data(ssl);
+ ngtcp2_conn *conn = conn_ref->get_conn(conn_ref);
+ (void)level;
+
+ DEBUG_MSG("WOLFSSL: send alert, level=%d alert=%d\n", level, alert);
+ ngtcp2_conn_set_tls_alert(conn, alert);
+
+ return 1;
+}
+
+static WOLFSSL_QUIC_METHOD quic_method = {
+ set_encryption_secrets,
+ add_handshake_data,
+ flush_flight,
+ send_alert,
+};
+
+static void crypto_wolfssl_configure_context(WOLFSSL_CTX *ssl_ctx) {
+ wolfSSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ wolfSSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+ wolfSSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+}
+
+int ngtcp2_crypto_wolfssl_configure_server_context(WOLFSSL_CTX *ssl_ctx) {
+ crypto_wolfssl_configure_context(ssl_ctx);
+#if PRINTF_DEBUG
+ wolfSSL_Debugging_ON();
+#endif
+ return 0;
+}
+
+int ngtcp2_crypto_wolfssl_configure_client_context(WOLFSSL_CTX *ssl_ctx) {
+ crypto_wolfssl_configure_context(ssl_ctx);
+ wolfSSL_CTX_UseSessionTicket(ssl_ctx);
+#if PRINTF_DEBUG
+ wolfSSL_Debugging_ON();
+#endif
+ return 0;
+}