summaryrefslogtreecommitdiffstats
path: root/crypto/picotls
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/picotls')
-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
5 files changed, 839 insertions, 0 deletions
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);
+ }
+}