diff options
Diffstat (limited to '')
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; +} |