Compare commits
27 Commits
5246-fix-d
...
artem-quic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0813f9dd91 | ||
|
|
bf75c33368 | ||
|
|
7a0b78634c | ||
|
|
f887792c97 | ||
|
|
40bf2f894b | ||
|
|
8e80d9d8f5 | ||
|
|
681dd90ee6 | ||
|
|
417098cecb | ||
|
|
667989a2a4 | ||
|
|
cb0adfce3e | ||
|
|
71969ee5d9 | ||
|
|
cda2eb06b8 | ||
|
|
a1557cc4ca | ||
|
|
3516743b6a | ||
|
|
d6a34be81a | ||
|
|
dbc2d6076b | ||
|
|
c92b29163b | ||
|
|
71106440a1 | ||
|
|
ca3c776730 | ||
|
|
e857a5ffe7 | ||
|
|
ba7f408fee | ||
|
|
c092387582 | ||
|
|
64d08b7f1a | ||
|
|
9faa6d97a5 | ||
|
|
bcc519081f | ||
|
|
ee19edc257 | ||
|
|
dfde7bc95f |
69
configure.ac
69
configure.ac
@@ -649,6 +649,7 @@ LIBS="$OPENSSL_LIBS $LIBS"
|
||||
#
|
||||
# Check for functions added in OpenSSL or LibreSSL
|
||||
#
|
||||
AC_CHECK_FUNCS([CRYPTO_free_ex_index])
|
||||
AC_MSG_CHECKING([for Ed448 support])
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[#include <openssl/evp.h>]],
|
||||
@@ -690,6 +691,74 @@ AX_RESTORE_FLAGS([openssl])
|
||||
AC_SUBST([OPENSSL_CFLAGS])
|
||||
AC_SUBST([OPENSSL_LIBS])
|
||||
|
||||
# [pairwise: --enable-quic --with-libngtcp2=auto, --enable-quic --with-libngtcp2=yes, --disable-quic]
|
||||
AC_ARG_ENABLE([quic],
|
||||
[AS_HELP_STRING([--disable-quic], [disable QUIC, removes dependency on libngtcp2 (default is --enable-quic)])],
|
||||
[], [enable_quic=yes])
|
||||
|
||||
# [pairwise: skip]
|
||||
AC_ARG_WITH([libngtcp2],
|
||||
[AS_HELP_STRING([--with-libngtcp2],
|
||||
[build with libngtcp2 library [yes|no|auto] (default is auto)])],
|
||||
[], [with_libngtcp2="auto"])
|
||||
|
||||
AS_IF([test "$enable_quic" = "yes"],
|
||||
[AS_CASE([$with_libngtcp2],
|
||||
[no],[AC_MSG_ERROR([Use '--disable-quic' to disable QUIC support])],
|
||||
[auto|yes],[PKG_CHECK_MODULES([LIBNGTCP2], [libngtcp2 >= 1.0.0],
|
||||
[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <openssl/ssl.h>]],
|
||||
[#if (!defined SSL_OP_NO_TLSv1_3) && (!defined TLS1_3_VERSION)
|
||||
#error TLSv1.3 support is required for QUIC
|
||||
#endif
|
||||
])],
|
||||
[AC_DEFINE([HAVE_LIBNGTCP2], [1], [Build with QUIC support])],
|
||||
[AC_MSG_ERROR(m4_normalize([TLS version 1.3 support is required for QUIC but it is not provided by the OpenSSL/LibreSSL library in use. Either use an OpenSSL/LibreSSL library version with TLSv1.3 support or use --disable-quic to disable QUIC support.]))])],
|
||||
[AC_MSG_ERROR(m4_normalize([QUIC support requested, but libnghttp2 not found.
|
||||
Either install libngtcp2 or use --disable-quic.]))])],
|
||||
[AC_MSG_ERROR([Specifying libngtcp2 installation path is not supported, adjust PKG_CONFIG_PATH instead])])])
|
||||
|
||||
AM_CONDITIONAL([HAVE_LIBNGTCP2], [test -n "$LIBNGTCP2_LIBS"])
|
||||
|
||||
AS_IF([test "$enable_quic" = "yes"],
|
||||
AC_MSG_CHECKING([whether the OpenSSL library provides native TLS integration for QUIC])
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[#include <openssl/ssl.h>]],
|
||||
[[SSL_QUIC_METHOD quic_method = { 0 };]])],
|
||||
[quic_native="yes"]
|
||||
[AC_DEFINE([HAVE_NATIVE_BORINGSSL_QUIC_API], [1], [define if the OpenSSL library provides native TLS integration for QUIC])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])]))
|
||||
|
||||
AM_CONDITIONAL([HAVE_NATIVE_BORINGSSL_QUIC_API], [test "$quic_native" = "yes"])
|
||||
|
||||
AS_IF([test "$quic_native" = "yes"],
|
||||
AC_MSG_CHECKING([whether the SSL_QUIC_METHOD has both set_read_secret() and set_write_secret()])
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[#include <openssl/ssl.h>]],
|
||||
[[SSL_QUIC_METHOD quic_method = { 0 };
|
||||
void *set_read_secret = (void *)quic_method.set_read_secret;
|
||||
void *set_write_secret = (void *)quic_method.set_write_secret;]])],
|
||||
[quic_has_set_read_write_secret="yes"]
|
||||
[AC_DEFINE([HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET], [1], [define if the SSL_QUIC_METHOD has both set_read_secret() and set_write_secret()])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])]));
|
||||
|
||||
AM_CONDITIONAL([HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET], [test "$quic_has_set_read_write_secret" = "yes"])
|
||||
|
||||
AC_MSG_CHECKING([whether LibreSSL is used])
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[#include <openssl/opensslv.h>]],
|
||||
[[#ifndef LIBRESSL_VERSION_NUMBER
|
||||
#error LibreSSL is not used
|
||||
#endif
|
||||
]])],
|
||||
[have_libressl="yes"]
|
||||
[AC_DEFINE([HAVE_LIBRESSL], [1], [define if LibreSSL is used])
|
||||
AC_MSG_RESULT([yes])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
AM_CONDITIONAL([HAVE_LIBRESSL], [test "$have_libressl" = "yes"])
|
||||
|
||||
AC_CHECK_FUNCS([clock_gettime])
|
||||
|
||||
# [pairwise: --with-gssapi=yes, --with-gssapi=auto, --without-gssapi]
|
||||
|
||||
@@ -256,6 +256,44 @@ libisc_la_LIBADD += \
|
||||
$(LIBXML2_LIBS)
|
||||
endif HAVE_LIBXML2
|
||||
|
||||
if HAVE_LIBNGTCP2
|
||||
libisc_la_HEADERS += \
|
||||
include/isc/ngtcp2_crypto.h \
|
||||
include/isc/ngtcp2_utils.h \
|
||||
include/isc/quic.h
|
||||
|
||||
libisc_la_SOURCES += \
|
||||
quic/ngtcp2_crypto.c \
|
||||
quic/ngtcp2_utils.c \
|
||||
quic/quic-int.h \
|
||||
quic/quic_cid.c \
|
||||
quic/quic_crypto.c \
|
||||
quic/quic_crypto.h \
|
||||
quic/quic_session.c \
|
||||
quic/quic_session.h \
|
||||
quic/tls_quic.c
|
||||
|
||||
if HAVE_NATIVE_BORINGSSL_QUIC_API
|
||||
libisc_la_SOURCES += \
|
||||
quic/quic_interface_native.c
|
||||
endif
|
||||
|
||||
# The QUIC compatibility layer will not work on LibreSSL
|
||||
# due to purposefully broken key logging functionality
|
||||
if !HAVE_LIBRESSL
|
||||
libisc_la_SOURCES += \
|
||||
quic/quic_interface_compat.c \
|
||||
quic/tls_keylog_parser.c
|
||||
endif
|
||||
|
||||
|
||||
libisc_la_CPPFLAGS += \
|
||||
$(LIBNGTCP2_CFLAGS)
|
||||
|
||||
libisc_la_LIBADD += \
|
||||
$(LIBNGTCP2_LIBS)
|
||||
endif
|
||||
|
||||
if !HAVE_SYSTEMTAP
|
||||
DTRACE_DEPS = libisc_la-rwlock.lo libisc_la-job.lo
|
||||
DTRACE_OBJS = .libs/libisc_la-rwlock.$(OBJEXT) .libs/libisc_la-job.$(OBJEXT)
|
||||
|
||||
323
lib/isc/include/isc/ngtcp2_crypto.h
Normal file
323
lib/isc/include/isc/ngtcp2_crypto.h
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include <isc/tls.h>
|
||||
|
||||
#define ISC_NGTCP2_CRYPTO_TOKEN_RAND_DATA_LEN (16)
|
||||
/*%<
|
||||
* The length of random data added to a token used for a QUIC token
|
||||
* generated by 'ngtcp2_crypto_generate_retry_token()' or
|
||||
* 'ngtcp2_crypto_generate_regular_token()'.
|
||||
*/
|
||||
|
||||
#define ISC_NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY (0xb6)
|
||||
/*%<
|
||||
* Retry QUIC token "magic" value.
|
||||
*/
|
||||
|
||||
#define ISC_NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR (0x36)
|
||||
/*%<
|
||||
* Regular QUIC token "magic" value.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MAX(EVP_GCM_TLS_TAG_LEN, EVP_CCM_TLS_TAG_LEN, EVP_CHACHAPOLY_TLS_TAG_LEN) =
|
||||
* 16
|
||||
*/
|
||||
#define ISC__NGTCP2_CRYPTO_MAX_AEAD_TAG_LEN (16)
|
||||
|
||||
#define ISC_NGTCP2_CRYPTO_MAX_RETRY_TOKEN_LEN \
|
||||
(/* magic = */ sizeof(uint8_t) + /* cid_len = */ sizeof(uint8_t) + \
|
||||
NGTCP2_MAX_CIDLEN + sizeof(ngtcp2_tstamp) + \
|
||||
ISC__NGTCP2_CRYPTO_MAX_AEAD_TAG_LEN + \
|
||||
ISC_NGTCP2_CRYPTO_TOKEN_RAND_DATA_LEN)
|
||||
/*%<
|
||||
* Max length of a QUIC retry token.
|
||||
*/
|
||||
|
||||
#define ISC_NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_LEN \
|
||||
(/* magic = */ sizeof(uint8_t) + sizeof(ngtcp2_tstamp) + \
|
||||
ISC__NGTCP2_CRYPTO_MAX_AEAD_TAG_LEN + \
|
||||
ISC_NGTCP2_CRYPTO_TOKEN_RAND_DATA_LEN)
|
||||
/*%<
|
||||
* Max length of a QUIC regular token.
|
||||
*/
|
||||
|
||||
#define ISC_NGTCP2_CRYPTO_STATIC_SECRET_LEN (16)
|
||||
/*%<
|
||||
* QUIC static secret length. In particular, used for tokens
|
||||
* generation and verification. Intended to be used on a per-listener
|
||||
* level.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_crypto_set_crypto_callbacks(ngtcp2_callbacks *callbacks);
|
||||
/*%<
|
||||
* Set the cryptography related callbacks within the the given
|
||||
* 'callbacks' object. It never overwrites already set callbacks.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'callbacks' != NULL.
|
||||
*/
|
||||
|
||||
const ngtcp2_callbacks *
|
||||
isc_ngtcp2_crypto_get_default_crypto_callbacks(void);
|
||||
/*%<
|
||||
* Returns a global "ngtcp2_callbacks" object pointer with all
|
||||
* default ngtcp2 crypto callbacks set. Might be useful if you want
|
||||
* wrap one of the default callbacks with custom code for some
|
||||
* reason. Use with care: you unlikely want to replace a default callback
|
||||
* completely, but rather call it somewhere in your code, as the default
|
||||
* callbacks are heavily intertwined.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_crypto_bind_conn_tls(ngtcp2_conn *conn, isc_tls_t *tls);
|
||||
/*%<
|
||||
* Associate the given connection object 'conn' and a TLS object
|
||||
* 'tls' with each other.
|
||||
*
|
||||
* It is expected that the connection object was created with the
|
||||
* cryptography callbacks that are set with
|
||||
* 'isc_ngtcp2_crypto_set_crypto_callbacks()'.
|
||||
*
|
||||
* NOTE: internally it calls 'isc_tls_set_quic_method()',
|
||||
* 'ngtcp2_conn_set_tls_native_handle()',
|
||||
* 'isc_tls_quic_set_app_data()'. Thus, it occupies the associated
|
||||
* properties within these objects.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'conn' != NULL;
|
||||
*\li 'tls' != NULL.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_crypto_generate_stateless_reset_token(uint8_t *token_buf,
|
||||
const size_t token_buflen,
|
||||
const uint8_t *secret,
|
||||
const size_t secretlen,
|
||||
const ngtcp2_cid *cid);
|
||||
/*%<
|
||||
* Generate a stateless reset token via HKDF using the given secret
|
||||
* ('secret') and the connection identifier ('cid') as input.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'token_buf' != NULL;
|
||||
*\li 'token_buflen' >= NGTCP2_STATELESS_RESET_TOKENLEN;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'cid' != NULL && 'cid->data' != NULL && 'cid->datalen' > 0.
|
||||
*
|
||||
* Returns 'ISC_R_SUCCESS' on success.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc_ngtcp2_crypto_generate_retry_token(
|
||||
uint8_t *token_buf, const size_t token_buflen, const uint8_t *secret,
|
||||
const size_t secretlen, const uint32_t version,
|
||||
const ngtcp2_sockaddr *remote_addr, const ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_cid *retry_scid, const ngtcp2_cid *orig_dcid,
|
||||
const ngtcp2_tstamp ts);
|
||||
/*%<
|
||||
* Generate a "Retry" packet token in the buffer pointed to by
|
||||
* 'token' using the given secret ('secret"), QUIC version
|
||||
* ('version'), remote client address ('remote_addr'), source
|
||||
* connection ID chosen by server ('retry_scid'), original
|
||||
* destination connection ID sent by the client in the "Initial"
|
||||
* packet ('orig_dcid'). The timestamp ('ts') is supposed to be the
|
||||
* token creation (current) timestamp.
|
||||
*
|
||||
* The successfully generated token starts with a byte equal to
|
||||
* 'ISC_NGTCP2_CRYPTO_TOKEN_MAGIC_RETRY'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'token_buf' != NULL;
|
||||
*\li 'token_buflen' >= ISC_NGTCP2_CRYPTO_MAX_RETRY_TOKEN_LEN;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'remote_addr' != NULL;
|
||||
*\li 'remote_addrlen' <= sizeof(ngtcp2_sockaddr_union);
|
||||
*\li 'retry_scid' != NULL && 'retry_scid->data' != NULL &&
|
||||
* 'retry_scid->datalen' > 0;
|
||||
*\li 'orig_dcid' != NULL && 'orig_dcid->data' != NULL &&
|
||||
* 'orig_dcid->datalen' > 0.
|
||||
*
|
||||
* Returns the newly generated token size on success, or 0 on failure.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_crypto_verify_retry_token(
|
||||
ngtcp2_cid *orig_dcid, const uint8_t *token_buf,
|
||||
const size_t token_buflen, const uint8_t *secret,
|
||||
const size_t secretlen, const uint32_t version,
|
||||
const ngtcp2_sockaddr *remote_addr, const ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_cid *dcid, const ngtcp2_duration timeout,
|
||||
const ngtcp2_tstamp ts);
|
||||
/*%<
|
||||
* Verify a "Retry" token stored in the buffer pointed to by
|
||||
* 'tokenbuf' using the given secret ('secret"), QUIC version
|
||||
* ('version'), remote client address ('remote_addr'), the destination
|
||||
* Connection ID in "Initial" packet sent by the client ('dcid').
|
||||
* When verification succeeds, the extracted original destination
|
||||
* connection ID from the "Initial" packet sent by the client that
|
||||
* triggered the "Retry" packet is saved into buffer pointed to by
|
||||
* 'orig_dcid'. The timestamp ('ts') is supposed to be the
|
||||
* current timestamp.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'orig_dcid' != NULL && 'orig_dcid->data' != NULL.
|
||||
*\li 'token_buf' != NULL;
|
||||
*\li 'token_buflen' >= ISC_NGTCP2_CRYPTO_MAX_RETRY_TOKEN_LEN;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'remote_addr' != NULL;
|
||||
*\li 'remote_addrlen' <= sizeof(ngtcp2_sockaddr_union);
|
||||
*\li 'dcid' != NULL && 'dcid->data' != NULL &&
|
||||
* 'dcid->datalen' > 0.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS - on success;
|
||||
*\li #ISC_R_UNEXPECTED - on unexpected retry token magic;
|
||||
*\li #ISC_R_TIMEDOUT - on timeout;
|
||||
*\li #ISC_R_FAILURE - on generic failure.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc_ngtcp2_crypto_generate_regular_token(
|
||||
uint8_t *token_buf, const size_t token_buflen, const uint8_t *secret,
|
||||
const size_t secretlen, const ngtcp2_sockaddr *remote_addr,
|
||||
const ngtcp2_socklen remote_addrlen, const ngtcp2_tstamp ts);
|
||||
/*%<
|
||||
* Generate a token in the buffer pointed by 'token_buf' that is to
|
||||
* be sent within a "NEW_TOKEN" frame. The secret ('secret') is used
|
||||
* for generating keys and protecting data with AEAD. The client
|
||||
* address is specified with 'remote_addr'. The timestamp ('ts') is
|
||||
* supposed to be the token creation (current) timestamp.
|
||||
*
|
||||
* The successfully generated token starts with a byte equal to
|
||||
* 'ISC_NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'token_buf' != NULL;
|
||||
*\li 'token_buflen' >= ISC_NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_LEN;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'remote_addr' != NULL;
|
||||
*\li 'remote_addrlen' <= sizeof(ngtcp2_sockaddr_union).
|
||||
*
|
||||
* Returns the newly generated token size on success, or 0 on failure.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_crypto_verify_regular_token(const uint8_t *token,
|
||||
const size_t tokenlen,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const ngtcp2_sockaddr *remote_addr,
|
||||
const ngtcp2_socklen remote_addrlen,
|
||||
const ngtcp2_duration timeout,
|
||||
const ngtcp2_tstamp ts);
|
||||
/*%<
|
||||
* Verify the token in the buffer pointed by 'token_buf' that was
|
||||
* received within a "NEW_TOKEN" frame. The secret ('secret') is used
|
||||
* for verifying the data protected with AEAD. The client address is
|
||||
* specified with 'remote_addr'. The timestamp ('ts') is supposed to be the
|
||||
* current timestamp.
|
||||
*
|
||||
* The successfully verified token start with a byte that contains the
|
||||
* 'ISC_NGTCP2_CRYPTO_TOKEN_MAGIC_REGULAR' value.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'token_buf' != NULL;
|
||||
*\li 'token_buflen' >= ISC_NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_LEN;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'remote_addr' != NULL;
|
||||
*\li 'remote_addrlen' <= sizeof(ngtcp2_sockaddr_union).
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS - on success;
|
||||
*\li #ISC_R_UNEXPECTED - on unexpected regular token magic;
|
||||
*\li #ISC_R_TIMEDOUT - on timeout;
|
||||
*\li #ISC_R_FAILURE - on generic failure.
|
||||
*/
|
||||
|
||||
ssize_t
|
||||
isc_ngtcp2_crypto_write_connection_close(uint8_t *dest, const size_t destlen,
|
||||
const uint32_t version,
|
||||
const ngtcp2_cid *dcid,
|
||||
const ngtcp2_cid *scid,
|
||||
const uint64_t error_code,
|
||||
const uint8_t *reason,
|
||||
const size_t reasonlen);
|
||||
/*%<
|
||||
* Write an "Initial" packet containing "CONNECTION_CLOSE" with the
|
||||
* given 'error_code' and an optional 'reason' to the buffer pointed
|
||||
* by 'dest'. The 'dcid' must be the source connection ID in
|
||||
* "Initial" packet from the client. The 'scid' must be the
|
||||
* destination connection ID in the "Initial" packet from the client.
|
||||
*
|
||||
* The function is supposed to be used to close a connection without
|
||||
* altering the server's internal state (e.g. in the case when a retry
|
||||
* token verification fails): that is in the "hot path." It must not
|
||||
* be used on a client side.
|
||||
*
|
||||
* Internally, the function calls 'ngtcp2_pkt_write_connection_close()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'destlen' > 0;
|
||||
*\li 'dcid' != NULL && 'dcid->datalen' > 0 && 'dcid->data' != NULL;
|
||||
*\li 'scid' != NULL && 'scid->datalen' > 0 && 'scid->data' != NULL;
|
||||
*\li 'reason' != NULL;
|
||||
*\li 'reasonlen' > 0.
|
||||
*
|
||||
* Returns the newly generated token size on success, or <=0 on failure.
|
||||
*/
|
||||
|
||||
ssize_t
|
||||
isc_ngtcp2_crypto_write_retry(uint8_t *dest, const size_t destlen,
|
||||
const uint32_t version, const ngtcp2_cid *dcid,
|
||||
const ngtcp2_cid *scid,
|
||||
const ngtcp2_cid *orig_dcid,
|
||||
const uint8_t *token_buf,
|
||||
const size_t token_buflen);
|
||||
/*%<
|
||||
* Write a "Retry" packet to the buffer pointed by "dest".
|
||||
*
|
||||
* 'dcid' is the connection ID in a packet that appeared as a
|
||||
* source connection ID sent by the client.
|
||||
*
|
||||
* 'scid' is the server chosen source connection ID.
|
||||
*
|
||||
* 'orig_dcid' specifies the original destination connection ID
|
||||
* which appeared in the packet as the destination connection ID sent
|
||||
* by the client.
|
||||
*
|
||||
* 'token' specifies the pointer to the retry token data.
|
||||
*
|
||||
* Internally, the function calls 'ngtcp2_pkt_write_retry()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'destlen' > 0;
|
||||
*\li 'dcid' != NULL && 'dcid->datalen' > 0 && 'dcid->data' != NULL;
|
||||
*\li 'scid' != NULL && 'scid->datalen' > 0 && 'scid->data' != NULL;
|
||||
*\li 'token_buf' != NULL;
|
||||
*\li 'token_buflen' > 0.
|
||||
*
|
||||
* Returns the newly generated packet size on success, or <=0 on failure.
|
||||
*/
|
||||
249
lib/isc/include/isc/ngtcp2_utils.h
Normal file
249
lib/isc/include/isc/ngtcp2_utils.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
|
||||
#include <isc/region.h>
|
||||
#include <isc/sockaddr.h>
|
||||
|
||||
#define ISC_NGTCP2_PROTO_VER_RESERVED ((uint32_t)0x1a2a3a4au)
|
||||
/*%<
|
||||
* The versions in form of 0x?a?a?a?a are a reserved to test version
|
||||
* negotiation.
|
||||
*/
|
||||
|
||||
#define ISC_NGTCP2_MAX_POSSIBLE_CID_LENGTH (255)
|
||||
/*%<
|
||||
* Maximum theoretical size of a CID that is possible and supported
|
||||
* by 'ngtcp2_pkt_decode_version_cid()'. All currently used versions
|
||||
* of QUIC have smaller limit defined as 'NGTCP2_MAX_CIDLEN', but
|
||||
* future versions of QUIC might use larger CIDs.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_gen_cid(ngtcp2_cid *restrict cid, const size_t size);
|
||||
/*%<
|
||||
* Generate a new connection ID data.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'cid' != NULL;
|
||||
*\li 'size' >= NGTCP2_MIN_CIDLEN && 'size' <=
|
||||
* NGTCP2_MAX_CIDLEN.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_copy_cid(ngtcp2_cid *restrict dst, const ngtcp2_cid *restrict src);
|
||||
/*%<
|
||||
* Copy a connection ID data. 'dst->data' must point to a buffer
|
||||
* that is large enough to hold the copied identifier.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dst' != NULL;
|
||||
*\li 'src' != NULL && 'src->datalen' > 0.
|
||||
*/
|
||||
|
||||
static inline void
|
||||
isc_ngtcp2_cid_region(ngtcp2_cid *restrict cid, isc_region_t *restrict region) {
|
||||
REQUIRE(cid != NULL);
|
||||
REQUIRE(region != NULL);
|
||||
|
||||
region->base = &cid->data[0];
|
||||
region->length = cid->datalen;
|
||||
}
|
||||
/*%<
|
||||
* Initialize the given 'isc_region_t' object to point to data held
|
||||
* by the given 'ngtcp2_cid' object.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'cid' != NULL;
|
||||
*\li 'region' != NULL.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_addr_init(ngtcp2_addr *restrict ngaddr,
|
||||
const isc_sockaddr_t *restrict addr);
|
||||
/*%<
|
||||
* Initialize the given 'ngtcp2_addr' object according the data from
|
||||
* the given 'isc_sockaddr_t' object.
|
||||
*
|
||||
* NOTE: Please keep in mind that no data is copied, only pointers are
|
||||
* set and they are valid for as long as the given isc_sockaddr_t'
|
||||
* object is valid.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'ngaddr' != NULL;
|
||||
*\li 'addr' != NULL.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_path_init(ngtcp2_path *restrict path,
|
||||
const isc_sockaddr_t *restrict local,
|
||||
const isc_sockaddr_t *restrict peer);
|
||||
/*%<
|
||||
* Initialize the given 'ngtcp2_path' according the data from the
|
||||
* given 'isc_sockaddr_t' objects.
|
||||
*
|
||||
* NOTE: Please keep in mind that no data is copied, only pointers are
|
||||
* set and they are valid for as long as the given isc_sockaddr_t'
|
||||
* objects are valid.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'path' != NULL;
|
||||
*\li 'local' != NULL;
|
||||
*\li 'peer' != NULL.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_path_storage_init(ngtcp2_path_storage *restrict path_storage,
|
||||
const isc_sockaddr_t *restrict local,
|
||||
const isc_sockaddr_t *restrict peer);
|
||||
/*%<
|
||||
* Initialize the given 'ngtcp2_path_storage' according the data
|
||||
* from the given 'isc_sockaddr_t' objects. The data from the provided
|
||||
* addresses is copied inside the path storage object.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'path_storage' != NULL;
|
||||
*\li 'local' != NULL;
|
||||
*\li 'peer' != NULL.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_path_getaddrs(const ngtcp2_path *restrict path,
|
||||
isc_sockaddr_t *restrict local,
|
||||
isc_sockaddr_t *restrict peer);
|
||||
/*%<
|
||||
* Return the individual components of the given QUIC path object,
|
||||
* if pointers are specified.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'path' != NULL.
|
||||
*/
|
||||
|
||||
static inline ngtcp2_duration
|
||||
isc_ngtcp2_make_duration(const uint32_t seconds, const uint32_t millis) {
|
||||
const ngtcp2_duration duration =
|
||||
((NGTCP2_SECONDS * seconds) + (NGTCP2_MILLISECONDS * millis));
|
||||
|
||||
/*
|
||||
* UINT64_MAX is an invalid value in ngtcp2. Often used as the no-value
|
||||
* marker.
|
||||
*/
|
||||
INSIST(duration <= UINT64_MAX);
|
||||
|
||||
return duration;
|
||||
}
|
||||
/*%<
|
||||
* An utility to generate a duration/timestamp with nanosecond
|
||||
* accuracy that is suitable to use in ngtcp2.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ngtcp2_mem_init(ngtcp2_mem *restrict mem, isc_mem_t *mctx);
|
||||
/*%<
|
||||
* Initialize an 'ngtcp2_mem' object so that it can be used to route
|
||||
* memory allocation operations to the given memory context.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'mem' != NULL;
|
||||
*\li 'mctx' != NULL.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc_ngtcp2_is_version_available(const uint32_t version,
|
||||
const uint32_t *versions,
|
||||
const size_t versions_len);
|
||||
/*%<
|
||||
* Returns 'true' if the given QUIC version is available in the given
|
||||
* set of versions.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'versions' != NULL.
|
||||
*/
|
||||
|
||||
uint32_t
|
||||
isc_ngtcp2_select_version(const uint32_t client_original_chosen_version,
|
||||
const uint32_t *client_preferred_versions,
|
||||
const size_t client_preferred_versions_len,
|
||||
const uint32_t *server_preferred_versions,
|
||||
const size_t server_preferred_versions_len);
|
||||
/*%<
|
||||
*
|
||||
* Get a negotiated QUIC version following the rules described in
|
||||
* RFC8999 and, especially, RFC9368.
|
||||
*
|
||||
* NOTE: Similar to 'ngtcp2_select_version()' but a bit more strict
|
||||
* according to the RFC9368.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'client_preferred_versions' != NULL;
|
||||
*\li 'server_preferred_versions' != NULL.
|
||||
*/
|
||||
|
||||
static inline bool
|
||||
isc_ngtcp2_pkt_header_is_long(const uint8_t *pkt, const size_t pktlen) {
|
||||
REQUIRE(pkt != NULL);
|
||||
REQUIRE(pktlen >= 5);
|
||||
|
||||
if (pkt[0] & 0x80) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/*%<
|
||||
* Check if the QUIC packet uses a long form. The function is
|
||||
* expected to be used after a successful call to
|
||||
* 'ngtcp2_pkt_decode_version_cid()' which does some initial sanity
|
||||
* checks on a packet.
|
||||
*
|
||||
* See RFC8999 for more details about this and other version-agnostic
|
||||
* characteristics of QUIC.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'pkt' != NULL;
|
||||
*\li 'pktlen' >= 5.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_decode_pkt_header(const isc_region_t *pkt,
|
||||
const size_t short_pkt_dcidlen, bool *pkt_long,
|
||||
isc_region_t *pkt_scid, isc_region_t *pkt_dcid,
|
||||
uint32_t *pkt_version);
|
||||
/*%<
|
||||
* Get the basic information about the given QUIC packet header. You
|
||||
* can pass 'NULL' to the argument you are not interested in. NOTE:
|
||||
* It is a specialized thin wrapper on top of
|
||||
* ngtcp2_pkt_decode_version_cid() intended to be used as a first step
|
||||
* in processing an incoming QUIC packet/UDP-datagram.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'pkt' != NULL && 'pkt->base' != NULL && 'pkt->length' > 0.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_decode_pkt_header_data(const uint8_t *pkt_data, const size_t pkt_len,
|
||||
const size_t short_pkt_dcidlen,
|
||||
bool *pkt_long, isc_region_t *pkt_scid,
|
||||
isc_region_t *pkt_dcid,
|
||||
uint32_t *pkt_version);
|
||||
/*%<
|
||||
*
|
||||
* Mostly same as above, but accepts data directly rather than via a
|
||||
* pointer to `isc_region_t`.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'pkt_data' != NULL && 'pkt_len' > 0.
|
||||
*/
|
||||
193
lib/isc/include/isc/quic.h
Normal file
193
lib/isc/include/isc/quic.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/tls.h>
|
||||
|
||||
#define ISC_QUIC_SERVER_SCID_LEN (18)
|
||||
|
||||
/*
|
||||
* QUIC session.
|
||||
*/
|
||||
typedef struct isc_quic_session isc_quic_session_t;
|
||||
|
||||
/*
|
||||
* QUIC CIDs implementation.
|
||||
*/
|
||||
typedef struct isc_quic_cid isc_quic_cid_t;
|
||||
|
||||
typedef uint64_t (*isc_quic_get_current_ts_cb_t)(void *restrict cbarg);
|
||||
|
||||
typedef void (*isc_quic_timer_update_cb_t)(isc_quic_session_t *restrict session,
|
||||
const uint32_t timeout_ms,
|
||||
void *cbarg);
|
||||
|
||||
typedef bool (*isc_quic_gen_unique_cid_cb_t)(
|
||||
isc_quic_session_t *restrict session, const size_t cidlen,
|
||||
const bool source, void *restrict cbarg,
|
||||
isc_quic_cid_t **restrict pcid);
|
||||
|
||||
typedef bool (*isc_quic_assoc_conn_cid_cb_t)(
|
||||
isc_quic_session_t *restrict session, isc_region_t *restrict cid_data,
|
||||
const bool source, void *cbarg, isc_quic_cid_t **restrict pcid);
|
||||
|
||||
typedef void (*isc_quic_deassoc_conn_cid_cb_t)(
|
||||
isc_quic_session_t *restrict session, void *cbarg,
|
||||
isc_quic_cid_t **restrict pcid);
|
||||
|
||||
typedef bool (*isc_quic_on_handshake_cb_t)(isc_quic_session_t *session,
|
||||
void *cbarg);
|
||||
|
||||
typedef bool (*isc_quic_on_remote_stream_open_cb_t)(isc_quic_session_t *session,
|
||||
const int64_t streamd_id,
|
||||
void *cbarg);
|
||||
|
||||
typedef bool (*isc_quic_on_stream_close_cb_t)(isc_quic_session_t *session,
|
||||
const int64_t streamd_id,
|
||||
const bool app_error_set,
|
||||
const uint64_t app_error_code,
|
||||
void *cbarg,
|
||||
void *stream_user_data);
|
||||
|
||||
typedef bool (*isc_quic_on_recv_stream_data_cb_t)(
|
||||
isc_quic_session_t *session, const int64_t streamd_id, const bool fin,
|
||||
const uint64_t offset, const isc_region_t *restrict data, void *cbarg,
|
||||
void *stream_user_data);
|
||||
|
||||
typedef void (*isc_quic_on_conn_close_cb_t)(isc_quic_session_t *session,
|
||||
const uint32_t closing_timeout_ms,
|
||||
void *cbarg);
|
||||
|
||||
typedef struct isc_quic_session_interface {
|
||||
/*
|
||||
* Low-level system interaction callbacks. These provide the
|
||||
* foundation for the the session object itself
|
||||
*/
|
||||
isc_quic_get_current_ts_cb_t get_current_ts;
|
||||
isc_quic_timer_update_cb_t timer_update;
|
||||
|
||||
isc_quic_gen_unique_cid_cb_t gen_unique_cid;
|
||||
isc_quic_assoc_conn_cid_cb_t assoc_conn_cid;
|
||||
isc_quic_deassoc_conn_cid_cb_t deassoc_conn_cid;
|
||||
|
||||
/* High-level callbacks. These provide the services for the higher
|
||||
* level code. */
|
||||
isc_quic_on_handshake_cb_t on_handshake;
|
||||
isc_quic_on_remote_stream_open_cb_t on_remote_stream_open;
|
||||
isc_quic_on_stream_close_cb_t on_stream_close;
|
||||
isc_quic_on_recv_stream_data_cb_t on_recv_stream_data;
|
||||
isc_quic_on_conn_close_cb_t on_conn_close;
|
||||
} isc_quic_session_interface_t;
|
||||
|
||||
typedef void (*isc_quic_send_cb_t)(isc_quic_session_t *restrict session,
|
||||
const int64_t stream_id,
|
||||
const isc_result_t result, void *cbarg,
|
||||
void *stream_user_data);
|
||||
|
||||
void
|
||||
isc_quic_session_create(isc_mem_t *mctx, isc_tlsctx_t *tlsctx,
|
||||
const isc_quic_session_interface_t *restrict cbs,
|
||||
void *cbarg, const isc_sockaddr_t *restrict local,
|
||||
const isc_sockaddr_t *restrict peer,
|
||||
const uint32_t handshake_timeout_ms,
|
||||
const uint32_t idle_timeout_ms,
|
||||
const size_t max_uni_streams,
|
||||
const size_t max_bidi_streams,
|
||||
const uint32_t client_chosen_version,
|
||||
const uint32_t *availalbe_versions,
|
||||
const size_t available_versions_len,
|
||||
const uint8_t *secret, const size_t secret_len,
|
||||
const bool is_server, isc_quic_session_t **sessionp);
|
||||
|
||||
void
|
||||
isc_quic_session_attach(isc_quic_session_t *restrict source,
|
||||
isc_quic_session_t **targetp);
|
||||
|
||||
void
|
||||
isc_quic_session_detach(isc_quic_session_t **sessionp);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_connect(isc_quic_session_t *restrict session,
|
||||
uint8_t *restrict out_pkt_buf,
|
||||
const size_t out_pkt_buf_len,
|
||||
ssize_t *restrict ppkt_size);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_update_local_address(isc_quic_session_t *restrict session,
|
||||
const isc_sockaddr_t *restrict local);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_send_data(isc_quic_session_t *restrict session,
|
||||
const int64_t stream_id,
|
||||
const isc_region_t *restrict data, const bool fin,
|
||||
isc_quic_send_cb_t cb, void *cbarg);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_write_pkt(isc_quic_session_t *restrict session,
|
||||
uint8_t *restrict out_pkt_buf,
|
||||
const size_t out_pkt_buf_len,
|
||||
ssize_t *restrict ppkt_size);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_read_pkt(isc_quic_session_t *restrict session,
|
||||
const uint32_t version,
|
||||
const isc_region_t *restrict pkt_dcid,
|
||||
const isc_region_t *restrict pkt_scid,
|
||||
const isc_region_t *restrict pkt_data,
|
||||
uint8_t *restrict out_pkt_buf,
|
||||
const size_t out_pkt_buf_len,
|
||||
ssize_t *restrict ppkt_size);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_on_expiry_timer(isc_quic_session_t *restrict session,
|
||||
uint8_t *restrict out_pkt_buf,
|
||||
const size_t out_pkt_buf_len,
|
||||
ssize_t *restrict ppkt_size);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_open_stream(isc_quic_session_t *restrict session,
|
||||
const bool bidi, void *stream_user_data,
|
||||
int64_t *restrict pstream_id);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_set_stream_user_data(isc_quic_session_t *restrict session,
|
||||
const int64_t stream_id,
|
||||
void *stream_user_data);
|
||||
|
||||
void *
|
||||
isc_quic_session_get_stream_user_data(isc_quic_session_t *restrict session,
|
||||
const int64_t stream_id);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_shutdown_stream(isc_quic_session_t *restrict session,
|
||||
const int64_t stream_id, bool abrupt);
|
||||
|
||||
isc_result_t
|
||||
isc_quic_session_shutdown(isc_quic_session_t *restrict session,
|
||||
uint8_t *restrict out_pkt_buf,
|
||||
const size_t out_pkt_buf_len,
|
||||
ssize_t *restrict ppkt_size);
|
||||
|
||||
void
|
||||
isc_quic_cid_create(isc_mem_t *mctx, const isc_region_t *restrict cid_data,
|
||||
const bool source, isc_quic_cid_t **cidp);
|
||||
|
||||
void
|
||||
isc_quic_cid_attach(isc_quic_cid_t *restrict source, isc_quic_cid_t **targetp);
|
||||
|
||||
void
|
||||
isc_quic_cid_detach(isc_quic_cid_t **cidp);
|
||||
@@ -608,6 +608,314 @@ isc_tlsctx_set_random_session_id_context(isc_tlsctx_t *ctx);
|
||||
*\li 'ctx' - a valid non-NULL pointer;
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tls_sslkeylogfile_append(const char *line);
|
||||
/*%<
|
||||
* Appends the provided line to the dedicated SSL keys log file
|
||||
* provided via "SSLKEYLOGFILE" environmental variable (iff the variable is
|
||||
* set).
|
||||
*/
|
||||
|
||||
/* QUIC related functionality (mostly see quic/tls_quic.c) */
|
||||
#ifdef HAVE_LIBNGTCP2
|
||||
|
||||
typedef struct ssl_cipher_st isc_tls_cipher_t;
|
||||
/*%<
|
||||
* See 'SSL_CIPHER'.
|
||||
*/
|
||||
|
||||
typedef void (*isc_tls_keylog_cb_t)(const isc_tls_t *tls, const char *line);
|
||||
/*%<
|
||||
* Keylog callback. See 'SSL_CTX_set_keylog_callback()'.
|
||||
*/
|
||||
|
||||
typedef enum isc_quic_encryption_level {
|
||||
ISC_QUIC_ENCRYPTION_INITIAL = 0,
|
||||
ISC_QUIC_ENCRYPTION_EARLY_DATA,
|
||||
ISC_QUIC_ENCRYPTION_HANDSHAKE,
|
||||
ISC_QUIC_ENCRYPTION_APPLICATION
|
||||
} isc_quic_encryption_level_t;
|
||||
/*%<
|
||||
* QUIC TLS encryption level (similar to 'ssl_encryption_level_t').
|
||||
*/
|
||||
|
||||
typedef struct isc_tls_quic_method {
|
||||
bool (*set_read_secret)(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const isc_tls_cipher_t *cipher,
|
||||
const uint8_t *secret, const size_t secret_len);
|
||||
|
||||
bool (*set_write_secret)(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const isc_tls_cipher_t *cipher,
|
||||
const uint8_t *secret,
|
||||
const size_t secret_len);
|
||||
|
||||
bool (*add_handshake_data)(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t *data, const size_t len);
|
||||
|
||||
/* The flush_flight() callback is used for optimisation means and
|
||||
* cannot be implemented manually without access to the internals
|
||||
* of the crypto libraries (TLS state machine). We need to skip
|
||||
* it. */
|
||||
/*bool (*flush_flight)(isc_tls_t *tls);*/
|
||||
|
||||
bool (*send_alert)(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t alert);
|
||||
} isc_tls_quic_method_t;
|
||||
/*%<
|
||||
* As set of functions (hooks) for interaction between TLS and QUIC. Closely
|
||||
* resembles the LibreSSL/BoringSSL/QuicTLS's
|
||||
* 'SSL_QUIC_METHOD/ssl_quic_method_st' type.
|
||||
*/
|
||||
|
||||
typedef struct isc_tls_quic_interface isc_tls_quic_interface_t;
|
||||
/*%<
|
||||
* An implementation of QUIC and TLS interaction interface.
|
||||
*/
|
||||
|
||||
const char *
|
||||
isc_tls_quic_encryption_level_text(const isc_quic_encryption_level_t level);
|
||||
/*%<
|
||||
* Get textual description of a given QUIC encryption level. The
|
||||
* function is intended for log messages.
|
||||
*/
|
||||
|
||||
const isc_tls_quic_interface_t *
|
||||
isc_tls_get_default_quic_interface(void);
|
||||
/*%<
|
||||
* Returns a set of hooks to interact wit the default implementation
|
||||
* of QUIC integration API. It might be either a native QUIC
|
||||
* integration API or a compatibility layer depending on BIND
|
||||
* configuration.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tlsctx_quic_configure(isc_tlsctx_t *tlsctx,
|
||||
const isc_tls_quic_interface_t *quic_interface);
|
||||
/*%<
|
||||
* Configures the given TLS context object to be used for QUIC.
|
||||
* It is meant to be used as one of the last steps of a TLS
|
||||
* context configuration.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tlsctx' is a valid pointer to a TLS context object;
|
||||
*\li 'quic_interface' - a valid pointer to a QUIC interface object.
|
||||
*/
|
||||
|
||||
isc_tls_t *
|
||||
isc_tls_create_quic(isc_tlsctx_t *ctx);
|
||||
/*%<
|
||||
* Create a new TLS structure to hold data for a new QUIC connection.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'ctx' != NULL.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tls_quic_set_app_data(isc_tls_t *tls, void *app_data);
|
||||
/*%<
|
||||
* Sets QUIC application data (user data) for the given TLS object
|
||||
* configured for QUIC. Works similar to 'SSL_set_app_data()' which
|
||||
* cannot be used for TLS objects configured for QUIC as this
|
||||
* functionality is used internally.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
void *
|
||||
isc_tls_quic_get_app_data(isc_tls_t *tls);
|
||||
/*%<
|
||||
* Returns QUIC application data (user data) for the given TLS
|
||||
* object configured for QUIC. Works similar to 'SSL_get_app_data()'
|
||||
* which cannot be used for TLS objects configured for QUIC as this
|
||||
* functionality is used internally.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tls_quic_set_keylog_callback(isc_tls_t *tls, isc_tls_keylog_cb_t cb);
|
||||
/*%<
|
||||
* Sets QUIC keylog callback. See 'SSL_CTX_set_keylog_callback()' for more
|
||||
* details. NOTE: No-op on LibreSSL.
|
||||
*
|
||||
* Additional information:
|
||||
* https://datatracker.ietf.org/doc/draft-ietf-tls-keylogfile/
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_tls_set_quic_method(isc_tls_t *tls, const isc_tls_quic_method_t *method);
|
||||
/*%<
|
||||
* Configures the QUIC related hooks on a TLS object previously configured for
|
||||
* QUIC. The 'method' must remain valid for the lifetime of 'tls'.
|
||||
*
|
||||
* NOTE: See 'SSL_set_quic_method()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object;
|
||||
*\li 'method' is a valid pointer to a QUIC method object.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS - on success;
|
||||
*\li #ISC_R_FAILURE - on failure.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_tls_provide_quic_data(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t *data, const size_t len);
|
||||
/*%<
|
||||
* Provides data from QUIC at a particular encryption level 'level' to a TLS
|
||||
* object previously configured for QUIC.
|
||||
*
|
||||
* NOTE: See 'SSL_provide_quic_data()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object;
|
||||
*\li 'len' == 0 || 'data' != NULL;
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS - on success;
|
||||
*\li #ISC_R_FAILURE - on failure.
|
||||
*/
|
||||
|
||||
int
|
||||
isc_tls_process_quic_post_handshake(isc_tls_t *tls);
|
||||
/*%<
|
||||
* Processes any data that QUIC has provided that left after TLS handshake
|
||||
* completion in a TLS object previously configured for QUIC.
|
||||
*
|
||||
* NOTE: See 'SSL_process_quic_post_handshake()'. You can pass the
|
||||
* return value to 'SSL_get_error()' for better error processing.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*
|
||||
* Returns:
|
||||
*\li '1' - on success;
|
||||
*\li '0' - on failure.
|
||||
*/
|
||||
|
||||
int
|
||||
isc_tls_do_quic_handshake(isc_tls_t *tls);
|
||||
/*%<
|
||||
* Processes any handshake data that QUIC has provided.
|
||||
*
|
||||
* NOTE: See 'SSL_do_handshake()'. The function shares return values
|
||||
* with this function and can be considered a thin wrapper on top of
|
||||
* it. The intention behind the wrapper to hide some differences in
|
||||
* behaviour between native BoringSSL-style QUIC API and our OpenSSL
|
||||
* compatibility layer. You can pass the return value to
|
||||
* 'SSL_get_error()' for better error processing.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_tls_set_quic_transport_params(isc_tls_t *tls, const uint8_t *params,
|
||||
const size_t params_len);
|
||||
/*%<
|
||||
* Sets the 'quic_transport_parameters' extension value in either the
|
||||
*'ClientHello' or 'EncryptedExtensions' handshake message for a TLS object
|
||||
* previously configured for QUIC. The pointers need to be valid only for the
|
||||
* time of the call.
|
||||
*
|
||||
* NOTE: See 'SSL_set_quic_transport_params()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object;
|
||||
*\li 'params' is a valid pointer;
|
||||
*\li 'params_len' is more than zero.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS - on success;
|
||||
*\li #ISC_R_FAILURE - on failure.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tls_get_peer_quic_transport_params(isc_tls_t *tls,
|
||||
const uint8_t **out_params,
|
||||
size_t *out_params_len);
|
||||
/*%<
|
||||
* Returns the 'quic_transport_parameters' extension value and its length for
|
||||
* either the 'ClientHello' or 'EncryptedExtensions' handshake message sent by
|
||||
* the peer for a TLS object previously configured for QUIC. The data buffer is
|
||||
* valid for the for until the call to 'isc_tls_quic_uninit()'.
|
||||
*
|
||||
* NOTE: See 'SSL_get_peer_quic_transport_params()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object;
|
||||
*\li 'out_params' is a valid pointer to a nullified pointer;
|
||||
*\li 'out_params_len' is a valid pointer to a nullified variable.
|
||||
*
|
||||
* Returns:
|
||||
*\li #ISC_R_SUCCESS - on success;
|
||||
*\li #ISC_R_FAILURE - on failure.
|
||||
*/
|
||||
|
||||
isc_quic_encryption_level_t
|
||||
isc_tls_quic_read_level(const isc_tls_t *tls);
|
||||
/*%<
|
||||
* Returns the current read encryption level. MUST NOT be called from a
|
||||
* 'isc_tls_quic_method_t' callback (due to inconsistent behaviour in different
|
||||
* crypto libraries in this situation).
|
||||
*
|
||||
* NOTE: See 'SSL_quic_read_level()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
isc_quic_encryption_level_t
|
||||
isc_tls_quic_write_level(const isc_tls_t *tls);
|
||||
/*%<
|
||||
* Returns the current write encryption level. MUST NOT be called from a
|
||||
* 'isc_tls_quic_method_t' callback (due to inconsistent behaviour in different
|
||||
* crypto libraries in this situation).
|
||||
*
|
||||
* NOTE: See 'SSL_quic_write_level()'.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tls_quic_crypto_initialize(void);
|
||||
/*%<
|
||||
* Initiliazes the internal QUIC crypto library. It is supposed to
|
||||
* be used right after loading all required OpenSSL crypto providers.
|
||||
*
|
||||
* Internally it mostly pre-fetches the crypto algorithms according to
|
||||
* OpenSSL 3.X recommendations.
|
||||
*
|
||||
* https://docs.openssl.org/3.0/man7/crypto/#explicit-fetching
|
||||
*/
|
||||
|
||||
void
|
||||
isc_tls_quic_crypto_shutdown(void);
|
||||
/*%<
|
||||
* Uninitiliazes the internal QUIC crypto library. It is supposed to
|
||||
* be used right before unloading the used OpenSSL crypto providers.
|
||||
*/
|
||||
|
||||
void
|
||||
isc__tls_quic_initialize(void);
|
||||
|
||||
void
|
||||
isc__tls_quic_shutdown(void);
|
||||
|
||||
#endif /* HAVE_LIBNGTCP2 */
|
||||
|
||||
#define isc_tlserr2result(category, module, funcname, fallback) \
|
||||
isc__tlserr2result(category, module, funcname, fallback, __FILE__, \
|
||||
__LINE__)
|
||||
|
||||
@@ -55,6 +55,9 @@ isc__lib_initialize(void) {
|
||||
isc__mem_initialize();
|
||||
isc__log_initialize();
|
||||
isc__crypto_initialize();
|
||||
#ifdef HAVE_LIBNGTCP2
|
||||
isc__tls_quic_initialize();
|
||||
#endif /* HAVE_LIBNGTCP2 */
|
||||
isc__uv_initialize();
|
||||
isc__xml_initialize();
|
||||
isc__hash_initialize();
|
||||
@@ -71,6 +74,9 @@ isc__lib_shutdown(void) {
|
||||
isc__iterated_hash_shutdown();
|
||||
isc__xml_shutdown();
|
||||
isc__uv_shutdown();
|
||||
#ifdef HAVE_LIBNGTCP2
|
||||
isc__tls_quic_shutdown();
|
||||
#endif /* HAVE_LIBNGTCP2 */
|
||||
isc__crypto_shutdown();
|
||||
isc__log_shutdown();
|
||||
isc__mem_shutdown();
|
||||
|
||||
2481
lib/isc/quic/ngtcp2_crypto.c
Normal file
2481
lib/isc/quic/ngtcp2_crypto.c
Normal file
File diff suppressed because it is too large
Load Diff
257
lib/isc/quic/ngtcp2_utils.c
Normal file
257
lib/isc/quic/ngtcp2_utils.c
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <isc/mem.h>
|
||||
#include <isc/ngtcp2_utils.h>
|
||||
#include <isc/random.h>
|
||||
|
||||
void
|
||||
isc_ngtcp2_gen_cid(ngtcp2_cid *restrict cid, const size_t size) {
|
||||
REQUIRE(cid != NULL);
|
||||
REQUIRE(size >= NGTCP2_MIN_CIDLEN && size <= NGTCP2_MAX_CIDLEN);
|
||||
|
||||
cid->datalen = size;
|
||||
isc_random_buf(cid->data, cid->datalen);
|
||||
}
|
||||
|
||||
void
|
||||
isc_ngtcp2_copy_cid(ngtcp2_cid *restrict dst, const ngtcp2_cid *restrict src) {
|
||||
REQUIRE(dst != NULL);
|
||||
REQUIRE(src != NULL && src->datalen > 0);
|
||||
|
||||
memmove(dst->data, src->data, src->datalen);
|
||||
dst->datalen = src->datalen;
|
||||
}
|
||||
|
||||
void
|
||||
isc_ngtcp2_addr_init(ngtcp2_addr *restrict ngaddr,
|
||||
const isc_sockaddr_t *restrict addr) {
|
||||
REQUIRE(ngaddr != NULL);
|
||||
REQUIRE(addr != NULL);
|
||||
|
||||
*ngaddr = (ngtcp2_addr){ 0 };
|
||||
|
||||
ngaddr->addr = (ngtcp2_sockaddr *)&addr->type.sa;
|
||||
ngaddr->addrlen = (ngtcp2_socklen)addr->length;
|
||||
}
|
||||
|
||||
void
|
||||
isc_ngtcp2_path_init(ngtcp2_path *restrict path,
|
||||
const isc_sockaddr_t *restrict local,
|
||||
const isc_sockaddr_t *restrict peer) {
|
||||
REQUIRE(path != NULL);
|
||||
REQUIRE(local != NULL);
|
||||
REQUIRE(peer != NULL);
|
||||
|
||||
*path = (ngtcp2_path){ 0 };
|
||||
|
||||
isc_ngtcp2_addr_init(&path->local, local);
|
||||
isc_ngtcp2_addr_init(&path->remote, peer);
|
||||
}
|
||||
|
||||
void
|
||||
isc_ngtcp2_path_storage_init(ngtcp2_path_storage *restrict path_storage,
|
||||
const isc_sockaddr_t *restrict local,
|
||||
const isc_sockaddr_t *restrict peer) {
|
||||
REQUIRE(path_storage != NULL);
|
||||
REQUIRE(local != NULL);
|
||||
REQUIRE(peer != NULL);
|
||||
|
||||
*path_storage = (ngtcp2_path_storage){ 0 };
|
||||
|
||||
INSIST(local->length <= sizeof(path_storage->local_addrbuf));
|
||||
INSIST(peer->length <= sizeof(path_storage->remote_addrbuf));
|
||||
|
||||
ngtcp2_path_storage_init(
|
||||
path_storage, (ngtcp2_sockaddr *)&local->type.sa, local->length,
|
||||
(ngtcp2_sockaddr *)&peer->type.sa, peer->length, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
isc_ngtcp2_path_getaddrs(const ngtcp2_path *restrict path,
|
||||
isc_sockaddr_t *restrict local,
|
||||
isc_sockaddr_t *restrict peer) {
|
||||
REQUIRE(path != NULL);
|
||||
|
||||
if (local != NULL) {
|
||||
isc_sockaddr_fromsockaddr(local, path->local.addr);
|
||||
}
|
||||
|
||||
if (peer != NULL) {
|
||||
isc_sockaddr_fromsockaddr(peer, path->remote.addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void *
|
||||
isc__ngtcp2_malloc(size_t sz, isc_mem_t *mctx) {
|
||||
return isc_mem_allocate(mctx, sz);
|
||||
}
|
||||
|
||||
static void *
|
||||
isc__ngtcp2_calloc(size_t n, size_t sz, isc_mem_t *mctx) {
|
||||
return isc_mem_callocate(mctx, n, sz);
|
||||
}
|
||||
|
||||
static void *
|
||||
isc__ngtcp2_realloc(void *p, size_t newsz, isc_mem_t *mctx) {
|
||||
return isc_mem_reallocate(mctx, p, newsz);
|
||||
}
|
||||
|
||||
static void
|
||||
isc__ngtcp2_free(void *p, isc_mem_t *mctx) {
|
||||
if (p == NULL) { /* as standard free() behaves */
|
||||
return;
|
||||
}
|
||||
isc_mem_free(mctx, p);
|
||||
}
|
||||
|
||||
void
|
||||
isc_ngtcp2_mem_init(ngtcp2_mem *restrict mem, isc_mem_t *mctx) {
|
||||
REQUIRE(mem != NULL);
|
||||
REQUIRE(mctx != NULL);
|
||||
|
||||
*mem = (ngtcp2_mem){ .malloc = (ngtcp2_malloc)isc__ngtcp2_malloc,
|
||||
.calloc = (ngtcp2_calloc)isc__ngtcp2_calloc,
|
||||
.realloc = (ngtcp2_realloc)isc__ngtcp2_realloc,
|
||||
.free = (ngtcp2_free)isc__ngtcp2_free,
|
||||
.user_data = (void *)mctx };
|
||||
}
|
||||
|
||||
bool
|
||||
isc_ngtcp2_is_version_available(const uint32_t version,
|
||||
const uint32_t *versions,
|
||||
const size_t versions_len) {
|
||||
REQUIRE(versions != NULL);
|
||||
|
||||
if (version == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < versions_len; i++) {
|
||||
if (versions[i] == version &&
|
||||
ngtcp2_is_supported_version(version))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
isc_ngtcp2_select_version(const uint32_t client_original_chosen_version,
|
||||
const uint32_t *client_preferred_versions,
|
||||
const size_t client_preferred_versions_len,
|
||||
const uint32_t *server_preferred_versions,
|
||||
const size_t server_preferred_versions_len) {
|
||||
size_t i, k;
|
||||
|
||||
REQUIRE(client_preferred_versions != NULL);
|
||||
REQUIRE(server_preferred_versions != NULL);
|
||||
|
||||
/*
|
||||
* RFC RFC9368, Section 4. Version Downgrade Prevention:
|
||||
|
||||
* Clients MUST ignore any received Version Negotiation packets
|
||||
* that contain the Original Version.
|
||||
* ...
|
||||
* If an endpoint receives a Chosen Version equal to zero, or any
|
||||
* Available Version equal to zero, it MUST treat it as a parsing
|
||||
* failure.
|
||||
*/
|
||||
for (i = 0; i < server_preferred_versions_len; i++) {
|
||||
if (server_preferred_versions[i] ==
|
||||
client_original_chosen_version ||
|
||||
server_preferred_versions[i] == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Choose a protocol version prioritising client's preferences. */
|
||||
for (i = 0; i < client_preferred_versions_len; i++) {
|
||||
const uint32_t client_version = client_preferred_versions[i];
|
||||
for (k = 0; k < server_preferred_versions_len; k++) {
|
||||
const uint32_t server_version =
|
||||
server_preferred_versions[k];
|
||||
if (client_version == server_version &&
|
||||
ngtcp2_is_supported_version(client_version) &&
|
||||
ngtcp2_is_supported_version(server_version))
|
||||
{
|
||||
return client_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_decode_pkt_header(const isc_region_t *pkt,
|
||||
const size_t short_pkt_dcidlen, bool *pkt_long,
|
||||
isc_region_t *pkt_scid, isc_region_t *pkt_dcid,
|
||||
uint32_t *pkt_version) {
|
||||
ngtcp2_version_cid vc = { 0 };
|
||||
REQUIRE(pkt != NULL && pkt->base != NULL && pkt->length > 0);
|
||||
|
||||
int ret = ngtcp2_pkt_decode_version_cid(&vc, pkt->base, pkt->length,
|
||||
short_pkt_dcidlen);
|
||||
|
||||
/*
|
||||
* We treat version negotiation not as an error, because our code
|
||||
* is expected to handle it on its own.
|
||||
*/
|
||||
if (ret != NGTCP2_VERSION_NEGOTIATION_ERROR && ret != 0) {
|
||||
return ISC_R_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (pkt_long != NULL) {
|
||||
*pkt_long = isc_ngtcp2_pkt_header_is_long(pkt->base,
|
||||
pkt->length);
|
||||
}
|
||||
|
||||
if (pkt_scid != NULL) {
|
||||
*pkt_scid =
|
||||
(isc_region_t){ .base = (uint8_t *)vc.scid,
|
||||
.length = (unsigned int)vc.scidlen };
|
||||
}
|
||||
|
||||
if (pkt_dcid != NULL) {
|
||||
*pkt_dcid =
|
||||
(isc_region_t){ .base = (uint8_t *)vc.dcid,
|
||||
.length = (unsigned int)vc.dcidlen };
|
||||
}
|
||||
|
||||
if (pkt_version != NULL) {
|
||||
*pkt_version = vc.version;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_ngtcp2_decode_pkt_header_data(const uint8_t *pkt_data, const size_t pkt_len,
|
||||
const size_t short_pkt_dcidlen,
|
||||
bool *pkt_long, isc_region_t *pkt_scid,
|
||||
isc_region_t *pkt_dcid,
|
||||
uint32_t *pkt_version) {
|
||||
REQUIRE(pkt_data != NULL && pkt_len > 0);
|
||||
|
||||
isc_region_t pkt = { .base = (uint8_t *)pkt_data,
|
||||
.length = (unsigned int)pkt_len };
|
||||
|
||||
return isc_ngtcp2_decode_pkt_header(&pkt, short_pkt_dcidlen, pkt_long,
|
||||
pkt_scid, pkt_dcid, pkt_version);
|
||||
}
|
||||
176
lib/isc/quic/quic-int.h
Normal file
176
lib/isc/quic/quic-int.h
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <isc/tls.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
struct isc_tls_quic_interface {
|
||||
void (*tlsctx_configure)(isc_tlsctx_t *tlsctx);
|
||||
/* NOTE: the keylog callback will never be called on LibreSSL. */
|
||||
void (*tlsctx_keylog_callback)(const isc_tls_t *tls, const char *line);
|
||||
|
||||
void (*tls_init)(isc_tls_t *tls, isc_mem_t *mctx);
|
||||
void (*tls_uninit)(isc_tls_t *tls);
|
||||
|
||||
bool (*tls_calling_method_cb)(const isc_tls_t *tls);
|
||||
|
||||
/* See SSL_set_quic_method() */
|
||||
int (*tls_set_quic_method)(isc_tls_t *tls,
|
||||
const isc_tls_quic_method_t *method);
|
||||
|
||||
/* See SSL_provide_quic_data() */
|
||||
int (*tls_provide_quic_data)(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t *data, const size_t len);
|
||||
|
||||
/* See SSL_do_handshake(). Internally it is a thin wrapper on top
|
||||
* of it. It exists mostly to properly handle failed callbacks in
|
||||
* a compatibility layer.
|
||||
*/
|
||||
int (*tls_do_quic_handshake)(isc_tls_t *tls);
|
||||
|
||||
/*
|
||||
* See SSL_process_quic_post_handshake(). In the worst case can
|
||||
* be a dummy implementation (return 1) or a wrapper on top of
|
||||
* SSL_read() in a compatibility layer.
|
||||
*/
|
||||
int (*tls_process_quic_post_handshake)(isc_tls_t *tls);
|
||||
|
||||
/* See SSL_set_quic_transport_params() */
|
||||
int (*tls_set_quic_transport_params)(isc_tls_t *tls,
|
||||
const uint8_t *params,
|
||||
const size_t params_len);
|
||||
|
||||
/* See SSL_get_peer_quic_transport_params() */
|
||||
void (*tls_get_peer_quic_transport_params)(isc_tls_t *tls,
|
||||
const uint8_t **out_params,
|
||||
size_t *out_params_len);
|
||||
|
||||
/* See SSL_quic_read_level() */
|
||||
isc_quic_encryption_level_t (*tls_quic_read_level)(const isc_tls_t *tls);
|
||||
|
||||
/* See SSL_quic_read_level() */
|
||||
isc_quic_encryption_level_t (*tls_quic_write_level)(
|
||||
const isc_tls_t *tls);
|
||||
};
|
||||
/*%<
|
||||
* An interface used to implement functionality to access
|
||||
* QUIC-related TLS interfacing functionality of the used crypto
|
||||
* library (or a compatibility interface). It is based on the
|
||||
* interface used by LibreSSL/BoringSSL/QuicTLS so that it can be
|
||||
* easily implemented for these libraries.
|
||||
*/
|
||||
|
||||
void *
|
||||
isc__tls_get_quic_data(const isc_tls_t *tls);
|
||||
/*%<
|
||||
* Returns opaque pointer referring to QUIC specific data associated with the
|
||||
* 'tls' object.
|
||||
*/
|
||||
|
||||
void
|
||||
isc__tls_set_quic_data(isc_tls_t *tls, void *data);
|
||||
/*%<
|
||||
* Associates an opaque pointer referring to QUIC specific data with 'tls'
|
||||
* object.
|
||||
*/
|
||||
|
||||
void
|
||||
isc__tls_quic_init(isc_tls_t *tls);
|
||||
/*%<
|
||||
* Initializes and configures the given TLS object to be used for QUIC.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
void
|
||||
isc__tls_quic_uninit(isc_tls_t *tls);
|
||||
/*%<
|
||||
* Deinitializes the given TLS object to be used for QUIC.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__tls_is_quic(isc_tls_t *tls);
|
||||
/*%<
|
||||
* Returns 'true' if the given object is configured for QUIC.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls' is a valid pointer to a TLS object.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_NATIVE_BORINGSSL_QUIC_API
|
||||
const isc_tls_quic_interface_t *
|
||||
isc__tls_get_native_quic_interface(void);
|
||||
/*%<
|
||||
* Returns a set of hooks to interact wit the native (provided by the crypto
|
||||
* library) implementation of QUIC integration API.
|
||||
*
|
||||
* NOTE: this function is primarily exposed for testing and debugging purposes.
|
||||
* Please consider using 'isc_tls_get_default_quic_interface()' instead.
|
||||
*/
|
||||
#endif /* HAVE_NATIVE_BORINGSSL_QUIC_API */
|
||||
|
||||
#ifndef HAVE_LIBRESSL
|
||||
#include <isc/buffer.h>
|
||||
|
||||
const isc_tls_quic_interface_t *
|
||||
isc__tls_get_compat_quic_interface(void);
|
||||
/*%<
|
||||
* Returns a set of hooks to interact wit the compatibility layer-based
|
||||
* implementation of QUIC integration API.
|
||||
*
|
||||
* NOTE: this function is primarily exposed for testing and debugging purposes.
|
||||
* Please consider using 'isc_tls_get_default_quic_interface()' instead.
|
||||
*/
|
||||
|
||||
typedef enum isc__tls_keylog_label {
|
||||
ISC__TLS_KL_ILLEGAL = 0,
|
||||
/* TLSv1.3 */
|
||||
ISC__TLS_KL_CLIENT_EARLY_TRAFFIC_SECRET,
|
||||
ISC__TLS_KL_EARLY_EXPORTER_MASTER_SECRET,
|
||||
ISC__TLS_KL_CLIENT_HANDSHAKE_TRAFFIC_SECRET,
|
||||
ISC__TLS_KL_SERVER_HANDSHAKE_TRAFFIC_SECRET,
|
||||
ISC__TLS_KL_CLIENT_TRAFFIC_SECRET_0,
|
||||
ISC__TLS_KL_SERVER_TRAFFIC_SECRET_0,
|
||||
ISC__TLS_KL_EXPORTER_SECRET,
|
||||
/* TLSv1.2 */
|
||||
ISC__TLS_KL_CLIENT_RANDOM
|
||||
} isc__tls_keylog_label_t;
|
||||
/*%<
|
||||
* Definitions corresponding to TLS SSLKEYLOGFILE format labels.
|
||||
*
|
||||
* See this IETF Draft:
|
||||
* https://datatracker.ietf.org/doc/draft-ietf-tls-keylogfile/
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc__tls_parse_keylog_entry(const char *restrict line,
|
||||
isc__tls_keylog_label_t *restrict out_label,
|
||||
isc_buffer_t *restrict out_client_random,
|
||||
isc_buffer_t *restrict out_secret);
|
||||
/*%<
|
||||
* Parse SSLKEYLOGFILE entry. If you are not interested in certain parts of the
|
||||
* entry, use 'NULL' for the corresponding argument.
|
||||
*
|
||||
* NOTE: might alter the passed arguments even when fails.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'line' is a valid, non-NULL pointer.
|
||||
*/
|
||||
#endif /* HAVE_LIBRESSL */
|
||||
59
lib/isc/quic/quic_cid.c
Normal file
59
lib/isc/quic/quic_cid.c
Normal file
@@ -0,0 +1,59 @@
|
||||
#include "quic_session.h"
|
||||
|
||||
void
|
||||
isc_quic_cid_create(isc_mem_t *memctx, const isc_region_t *restrict cid_data,
|
||||
const bool source, isc_quic_cid_t **cidp) {
|
||||
isc_quic_cid_t *cid = NULL;
|
||||
|
||||
REQUIRE(cid_data != NULL && cid_data->base != NULL &&
|
||||
cid_data->length > 0);
|
||||
REQUIRE(cidp != NULL && *cidp == NULL);
|
||||
|
||||
cid = isc_mem_get(memctx, sizeof(*cid));
|
||||
|
||||
*cid = (isc_quic_cid_t){
|
||||
.source = source,
|
||||
.global_link = ISC_LINK_INITIALIZER,
|
||||
.local_link = ISC_LINK_INITIALIZER,
|
||||
};
|
||||
|
||||
isc_refcount_init(&cid->references, 1);
|
||||
|
||||
ngtcp2_cid_init(&cid->cid, cid_data->base, cid_data->length);
|
||||
|
||||
isc_mem_attach(memctx, &cid->mctx);
|
||||
|
||||
/* We need to acquire a memory barrier here */
|
||||
(void)isc_refcount_current(&cid->references);
|
||||
cid->magic = QUIC_CID_MAGIC;
|
||||
*cidp = cid;
|
||||
}
|
||||
|
||||
void
|
||||
isc_quic_cid_attach(isc_quic_cid_t *restrict source, isc_quic_cid_t **targetp) {
|
||||
REQUIRE(VALID_QUIC_CID(source));
|
||||
REQUIRE(targetp != NULL && *targetp == NULL);
|
||||
|
||||
isc_refcount_increment(&source->references);
|
||||
|
||||
*targetp = source;
|
||||
}
|
||||
|
||||
void
|
||||
isc_quic_cid_detach(isc_quic_cid_t **cidp) {
|
||||
isc_quic_cid_t *restrict cid = NULL;
|
||||
|
||||
cid = *cidp;
|
||||
*cidp = NULL;
|
||||
|
||||
REQUIRE(VALID_QUIC_CID(cid));
|
||||
|
||||
if (isc_refcount_decrement(&cid->references) > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need to acquire a memory barrier here */
|
||||
(void)isc_refcount_current(&cid->references);
|
||||
cid->magic = 0;
|
||||
isc_mem_putanddetach(&cid->mctx, cid, sizeof(*cid));
|
||||
}
|
||||
962
lib/isc/quic/quic_crypto.c
Normal file
962
lib/isc/quic/quic_crypto.c
Normal file
@@ -0,0 +1,962 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file contains QUIC-flavoured TLSv1.3-related cryptography
|
||||
* functions. The code mostly inspired by QuicTLS/LibreSSL-specific
|
||||
* code found in ngtcp2's native crypto library but with some peeks at
|
||||
* similar code found in NGINX and HAProxy.
|
||||
*
|
||||
* The functions this code provides mostly fall into the following categories:
|
||||
*
|
||||
* 1. TLS cipher ('isc_tls_cipher_t' aka SSL_CIPHER) information retrieval:
|
||||
* getting associated message digest, AEAD-scheme, etc;
|
||||
* 2. HKDF-interface implementation (HKDF(), HKDF-Extract(), HKDF-Expand(),
|
||||
* HKDF-ExpandLabel());
|
||||
* 3. AEAD-interface (context creation, authenticated encryption/decryption);
|
||||
* 4. Header Protection mask calculation;
|
||||
*
|
||||
* The code is intended to be used in both the custom ngtcp2 crypto
|
||||
* library and OpenSSL QUIC compatibility code.
|
||||
*/
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/opensslv.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x30000000L && OPENSSL_API_LEVEL >= 30000
|
||||
#define OPENSSL_3_API_OR_NEWER 1
|
||||
#include <openssl/core_names.h>
|
||||
#endif
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/crypto.h>
|
||||
|
||||
#include "quic_crypto.h"
|
||||
|
||||
/* See RFC9001, Section 6.6 (Limits on AEAD Usage ) for more details */
|
||||
|
||||
/* Maximum key usage (encryption) limits */
|
||||
#define QUIC_CRYPTO_MAX_ENCRYPTION_AES_GCM (1ULL << 23)
|
||||
#define QUIC_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305 (1ULL << 62)
|
||||
#define QUIC_CRYPTO_MAX_ENCRYPTION_AES_CCM (2965820ULL)
|
||||
|
||||
/*
|
||||
* Maximum authentication failure (decryption) limits during the
|
||||
* lifetime of a connection.
|
||||
*/
|
||||
#define QUIC_CRYPTO_MAX_DECRYPTION_FAILURES_AES_GCM (1ULL << 52)
|
||||
#define QUIC_CRYPTO_MAX_DECRYPTION_FAILURES_CHACHA20_POLY1305 (1ULL << 36)
|
||||
#define QUIC_CRYPTO_MAX_DECRYPTION_FAILURES_AES_CCM (2965820ULL)
|
||||
|
||||
static void
|
||||
quic_crypto_prefetch(void);
|
||||
|
||||
static void
|
||||
quic_crypto_prefetch_clear(void);
|
||||
|
||||
static bool quic_crypto_initialized = false;
|
||||
static bool fips_mode_used = false;
|
||||
|
||||
static const EVP_CIPHER *crypto_aead_aes_128_gcm = NULL;
|
||||
static const EVP_CIPHER *crypto_aead_aes_256_gcm = NULL;
|
||||
static const EVP_CIPHER *crypto_aead_aes_128_ccm = NULL;
|
||||
static const EVP_CIPHER *crypto_aead_chacha20_poly1305 = NULL;
|
||||
|
||||
static const EVP_CIPHER *crypto_cipher_chacha20 = NULL;
|
||||
static const EVP_CIPHER *crypto_cipher_aes_128_ctr = NULL;
|
||||
static const EVP_CIPHER *crypto_cipher_aes_256_ctr = NULL;
|
||||
|
||||
static const EVP_MD *crypto_md_sha256 = NULL;
|
||||
static const EVP_MD *crypto_md_sha384 = NULL;
|
||||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
static EVP_KDF *crypto_kdf_hkdf = NULL;
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
/*
|
||||
* Explicitly prefetch certain objects to avoid performance penalty on
|
||||
* OpenSSL >= 3.0.
|
||||
*
|
||||
* See here for more details:
|
||||
* https://www.openssl.org/docs/man3.0/man7/crypto.html#Performance
|
||||
* https://www.openssl.org/docs/man3.0/man7/crypto.html#Explicit-fetching
|
||||
* https://www.openssl.org/docs/man3.0/man7/crypto.html#Implicit-fetching
|
||||
*/
|
||||
|
||||
static void
|
||||
quic_crypto_prefetch(void) {
|
||||
crypto_aead_aes_128_gcm = EVP_CIPHER_fetch(NULL, "AES-128-GCM", NULL);
|
||||
RUNTIME_CHECK(crypto_aead_aes_128_gcm != NULL);
|
||||
|
||||
crypto_aead_aes_256_gcm = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL);
|
||||
RUNTIME_CHECK(crypto_aead_aes_256_gcm != NULL);
|
||||
|
||||
crypto_aead_aes_128_ccm = EVP_CIPHER_fetch(NULL, "AES-128-CCM", NULL);
|
||||
RUNTIME_CHECK(crypto_aead_aes_128_ccm != NULL);
|
||||
|
||||
if (!fips_mode_used) {
|
||||
crypto_aead_chacha20_poly1305 =
|
||||
EVP_CIPHER_fetch(NULL, "ChaCha20-Poly1305", NULL);
|
||||
RUNTIME_CHECK(crypto_aead_chacha20_poly1305 != NULL);
|
||||
|
||||
crypto_cipher_chacha20 = EVP_CIPHER_fetch(NULL, "ChaCha20",
|
||||
NULL);
|
||||
RUNTIME_CHECK(crypto_cipher_chacha20 != NULL);
|
||||
}
|
||||
|
||||
crypto_cipher_aes_128_ctr = EVP_CIPHER_fetch(NULL, "AES-128-CTR", NULL);
|
||||
RUNTIME_CHECK(crypto_cipher_aes_128_ctr != NULL);
|
||||
|
||||
crypto_cipher_aes_256_ctr = EVP_CIPHER_fetch(NULL, "AES-256-CTR", NULL);
|
||||
RUNTIME_CHECK(crypto_cipher_aes_256_ctr != NULL);
|
||||
|
||||
crypto_md_sha256 = EVP_MD_fetch(NULL, "sha256", NULL);
|
||||
RUNTIME_CHECK(crypto_md_sha256 != NULL);
|
||||
|
||||
crypto_md_sha384 = EVP_MD_fetch(NULL, "sha384", NULL);
|
||||
RUNTIME_CHECK(crypto_md_sha384 != NULL);
|
||||
|
||||
crypto_kdf_hkdf = EVP_KDF_fetch(NULL, "hkdf", NULL);
|
||||
RUNTIME_CHECK(crypto_kdf_hkdf != NULL);
|
||||
}
|
||||
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
|
||||
static void
|
||||
quic_crypto_prefetch(void) {
|
||||
crypto_aead_aes_128_gcm = EVP_aes_128_gcm();
|
||||
RUNTIME_CHECK(crypto_aead_aes_128_gcm != NULL);
|
||||
|
||||
crypto_aead_aes_256_gcm = EVP_aes_256_gcm();
|
||||
RUNTIME_CHECK(crypto_aead_aes_256_gcm != NULL);
|
||||
|
||||
crypto_aead_aes_128_ccm = EVP_aes_128_ccm();
|
||||
RUNTIME_CHECK(crypto_aead_aes_128_ccm != NULL);
|
||||
|
||||
if (!fips_mode_used) {
|
||||
crypto_aead_chacha20_poly1305 = EVP_chacha20_poly1305();
|
||||
RUNTIME_CHECK(crypto_aead_chacha20_poly1305 != NULL);
|
||||
|
||||
crypto_cipher_chacha20 = EVP_chacha20();
|
||||
RUNTIME_CHECK(crypto_cipher_chacha20 != NULL);
|
||||
}
|
||||
|
||||
crypto_cipher_aes_128_ctr = EVP_aes_128_ctr();
|
||||
RUNTIME_CHECK(crypto_cipher_aes_128_ctr != NULL);
|
||||
|
||||
crypto_cipher_aes_256_ctr = EVP_aes_256_ctr();
|
||||
RUNTIME_CHECK(crypto_cipher_aes_256_ctr != NULL);
|
||||
|
||||
crypto_md_sha256 = EVP_sha256();
|
||||
RUNTIME_CHECK(crypto_md_sha256 != NULL);
|
||||
|
||||
crypto_md_sha384 = EVP_sha384();
|
||||
RUNTIME_CHECK(crypto_md_sha384 != NULL);
|
||||
}
|
||||
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
static void
|
||||
quic_crypto_prefetch_clear(void) {
|
||||
RUNTIME_CHECK(crypto_aead_aes_128_gcm != NULL);
|
||||
crypto_aead_aes_128_gcm = NULL;
|
||||
|
||||
RUNTIME_CHECK(crypto_aead_aes_256_gcm != NULL);
|
||||
crypto_aead_aes_256_gcm = NULL;
|
||||
|
||||
RUNTIME_CHECK(crypto_aead_aes_128_ccm != NULL);
|
||||
crypto_aead_aes_128_ccm = NULL;
|
||||
|
||||
if (!fips_mode_used) {
|
||||
RUNTIME_CHECK(crypto_aead_chacha20_poly1305 != NULL);
|
||||
crypto_aead_chacha20_poly1305 = NULL;
|
||||
|
||||
RUNTIME_CHECK(crypto_cipher_chacha20 != NULL);
|
||||
crypto_cipher_chacha20 = NULL;
|
||||
}
|
||||
|
||||
RUNTIME_CHECK(crypto_cipher_aes_128_ctr != NULL);
|
||||
crypto_cipher_aes_128_ctr = NULL;
|
||||
|
||||
RUNTIME_CHECK(crypto_cipher_aes_256_ctr != NULL);
|
||||
crypto_cipher_aes_256_ctr = NULL;
|
||||
|
||||
RUNTIME_CHECK(crypto_md_sha256 != NULL);
|
||||
crypto_md_sha256 = NULL;
|
||||
|
||||
RUNTIME_CHECK(crypto_md_sha384 != NULL);
|
||||
crypto_md_sha384 = NULL;
|
||||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
RUNTIME_CHECK(crypto_kdf_hkdf != NULL);
|
||||
EVP_KDF_free(crypto_kdf_hkdf);
|
||||
crypto_kdf_hkdf = NULL;
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
}
|
||||
|
||||
void
|
||||
isc__quic_crypto_initialize(void) {
|
||||
if (quic_crypto_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
quic_crypto_initialized = true;
|
||||
fips_mode_used = isc_crypto_fips_mode();
|
||||
|
||||
quic_crypto_prefetch();
|
||||
}
|
||||
|
||||
void
|
||||
isc__quic_crypto_shutdown(void) {
|
||||
if (!quic_crypto_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
quic_crypto_prefetch_clear();
|
||||
|
||||
quic_crypto_initialized = false;
|
||||
fips_mode_used = false;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_tls_cipher_supported(const isc_tls_cipher_t *tls_cipher) {
|
||||
uint32_t tls_cipher_id;
|
||||
|
||||
REQUIRE(tls_cipher != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
tls_cipher_id = SSL_CIPHER_get_id(tls_cipher);
|
||||
|
||||
switch (tls_cipher_id) {
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
return true;
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
return true;
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
return !fips_mode_used;
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_tls_cipher_aead(const isc_tls_cipher_t *tls_cipher) {
|
||||
uint32_t tls_cipher_id;
|
||||
|
||||
REQUIRE(tls_cipher != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
tls_cipher_id = SSL_CIPHER_get_id(tls_cipher);
|
||||
|
||||
switch (tls_cipher_id) {
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
return crypto_aead_aes_128_gcm;
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
return crypto_aead_aes_256_gcm;
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
return crypto_aead_chacha20_poly1305;
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
return crypto_aead_aes_128_ccm;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_taglen(const EVP_CIPHER *aead) {
|
||||
REQUIRE(aead != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_keylen(const EVP_CIPHER *aead) {
|
||||
REQUIRE(aead != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
return (size_t)EVP_CIPHER_key_length(aead);
|
||||
}
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_ivlen(const EVP_CIPHER *aead) {
|
||||
REQUIRE(aead != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
return (size_t)EVP_CIPHER_iv_length(aead);
|
||||
}
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_packet_protection_ivlen(const EVP_CIPHER *aead) {
|
||||
/* By RFC9001, Section 5.1 it is at least 8 bytes long */
|
||||
return ISC_MAX(8, isc__quic_crypto_aead_ivlen(aead));
|
||||
}
|
||||
|
||||
uint64_t
|
||||
isc__quic_crypto_tls_cipher_aead_max_encryption(
|
||||
const isc_tls_cipher_t *tls_cipher) {
|
||||
uint32_t tls_cipher_id;
|
||||
|
||||
REQUIRE(tls_cipher != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
tls_cipher_id = SSL_CIPHER_get_id(tls_cipher);
|
||||
|
||||
switch (tls_cipher_id) {
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
return QUIC_CRYPTO_MAX_ENCRYPTION_AES_GCM;
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
return QUIC_CRYPTO_MAX_ENCRYPTION_CHACHA20_POLY1305;
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
return QUIC_CRYPTO_MAX_ENCRYPTION_AES_CCM;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
uint64_t
|
||||
isc__quic_crypto_tls_cipher_aead_max_decyption_failures(
|
||||
const isc_tls_cipher_t *tls_cipher) {
|
||||
uint32_t tls_cipher_id;
|
||||
|
||||
REQUIRE(tls_cipher != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
tls_cipher_id = SSL_CIPHER_get_id(tls_cipher);
|
||||
|
||||
switch (tls_cipher_id) {
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
return QUIC_CRYPTO_MAX_DECRYPTION_FAILURES_AES_GCM;
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
return QUIC_CRYPTO_MAX_DECRYPTION_FAILURES_CHACHA20_POLY1305;
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
return QUIC_CRYPTO_MAX_DECRYPTION_FAILURES_AES_CCM;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const EVP_MD *
|
||||
isc__quic_crypto_tls_cipher_md(const isc_tls_cipher_t *tls_cipher) {
|
||||
uint32_t tls_cipher_id;
|
||||
|
||||
REQUIRE(tls_cipher != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
tls_cipher_id = SSL_CIPHER_get_id(tls_cipher);
|
||||
|
||||
switch (tls_cipher_id) {
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
return crypto_md_sha256;
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
return crypto_md_sha384;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_md_hashlen(const EVP_MD *md) {
|
||||
REQUIRE(md != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
return (size_t)EVP_MD_size(md);
|
||||
}
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_tls_cipher_hp(const isc_tls_cipher_t *tls_cipher) {
|
||||
uint32_t tls_cipher_id;
|
||||
|
||||
REQUIRE(tls_cipher != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
tls_cipher_id = SSL_CIPHER_get_id(tls_cipher);
|
||||
|
||||
switch (tls_cipher_id) {
|
||||
case TLS1_3_CK_AES_128_GCM_SHA256:
|
||||
case TLS1_3_CK_AES_128_CCM_SHA256:
|
||||
return crypto_cipher_aes_128_ctr;
|
||||
case TLS1_3_CK_AES_256_GCM_SHA384:
|
||||
return crypto_cipher_aes_256_ctr;
|
||||
case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
|
||||
return crypto_cipher_chacha20;
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const EVP_MD *
|
||||
isc__quic_crypto_md_sha256(void) {
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
return crypto_md_sha256;
|
||||
}
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_aead_aes_128_gcm(void) {
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
return crypto_aead_aes_128_gcm;
|
||||
}
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_cipher_aes_128_ctr(void) {
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
return crypto_cipher_aes_128_ctr;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf_extract(uint8_t *dest, const EVP_MD *md,
|
||||
const uint8_t *secret, const size_t secretlen,
|
||||
const uint8_t *salt, const size_t saltlen) {
|
||||
REQUIRE(dest != NULL);
|
||||
REQUIRE(md != NULL);
|
||||
REQUIRE(secret != NULL);
|
||||
REQUIRE(secretlen > 0);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(crypto_kdf_hkdf);
|
||||
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(md), 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(),
|
||||
};
|
||||
bool ret = true;
|
||||
|
||||
if (EVP_KDF_derive(kctx, dest, (size_t)EVP_MD_size(md), params) <= 0) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kctx);
|
||||
|
||||
return ret;
|
||||
#else /* !( OPENSSL_3_API_OR_NEWER) */
|
||||
bool ret = true;
|
||||
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
size_t destlen = (size_t)EVP_MD_size(md);
|
||||
|
||||
if (pctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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, md) != 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)
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return ret;
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf_expand(uint8_t *dest, size_t destlen, const EVP_MD *md,
|
||||
const uint8_t *secret, const size_t secretlen,
|
||||
const uint8_t *info, const size_t infolen) {
|
||||
REQUIRE(dest != NULL);
|
||||
REQUIRE(destlen > 0);
|
||||
REQUIRE(md != NULL);
|
||||
REQUIRE(secret != NULL);
|
||||
REQUIRE(secretlen > 0);
|
||||
REQUIRE(info != NULL);
|
||||
REQUIRE(infolen > 0);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
EVP_KDF_CTX *kdf_ctx = EVP_KDF_CTX_new(crypto_kdf_hkdf);
|
||||
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(md), 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(),
|
||||
};
|
||||
bool ret = true;
|
||||
|
||||
if (EVP_KDF_derive(kdf_ctx, dest, destlen, params) <= 0) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kdf_ctx);
|
||||
|
||||
return ret;
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
bool ret = true;
|
||||
EVP_PKEY_CTX *pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
|
||||
if (pkey_ctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_derive_init(pkey_ctx) != 1 ||
|
||||
EVP_PKEY_CTX_hkdf_mode(pkey_ctx, EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) !=
|
||||
1 ||
|
||||
EVP_PKEY_CTX_set_hkdf_md(pkey_ctx, md) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_salt(pkey_ctx, (const unsigned char *)"",
|
||||
0) != 1 ||
|
||||
EVP_PKEY_CTX_set1_hkdf_key(pkey_ctx, secret, (int)secretlen) != 1 ||
|
||||
EVP_PKEY_CTX_add1_hkdf_info(pkey_ctx, info, (int)infolen) != 1 ||
|
||||
EVP_PKEY_derive(pkey_ctx, dest, &destlen) != 1)
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(pkey_ctx);
|
||||
|
||||
return ret;
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
}
|
||||
|
||||
/*
|
||||
* See RFC8446, Section 7.1:
|
||||
* https://www.rfc-editor.org/rfc/rfc8446#section-7.1
|
||||
*
|
||||
*
|
||||
* struct {
|
||||
* uint16 length = Length;
|
||||
* opaque label<7..255> = "tls13 " + Label;
|
||||
* opaque context<0..255> = Context;
|
||||
* } HkdfLabel;
|
||||
*/
|
||||
bool
|
||||
isc__quic_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
|
||||
const EVP_MD *md, const uint8_t *secret,
|
||||
const size_t secretlen, const uint8_t *label,
|
||||
const size_t labellen) {
|
||||
uint8_t label_start[] = "tls13 ";
|
||||
uint8_t hkdf_buf[UINT8_MAX];
|
||||
isc_buffer_t hkdf_label = { 0 };
|
||||
isc_region_t hkdf_data = { 0 };
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
isc_buffer_init(&hkdf_label, hkdf_buf, sizeof(hkdf_buf));
|
||||
|
||||
isc_buffer_putuint16(&hkdf_label, destlen);
|
||||
isc_buffer_putuint8(&hkdf_label,
|
||||
(uint8_t)(sizeof(label_start) - 1 + labellen));
|
||||
isc_buffer_putmem(&hkdf_label, label_start, sizeof(label_start) - 1);
|
||||
isc_buffer_putmem(&hkdf_label, label, labellen);
|
||||
/* Zero-sized "Context" */
|
||||
isc_buffer_putuint8(&hkdf_label, 0);
|
||||
|
||||
isc_buffer_usedregion(&hkdf_label, &hkdf_data);
|
||||
|
||||
return isc__quic_crypto_hkdf_expand(dest, destlen, md, secret,
|
||||
secretlen, hkdf_data.base,
|
||||
hkdf_data.length);
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf(uint8_t *dest, size_t destlen, const EVP_MD *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen, const uint8_t *info,
|
||||
size_t infolen) {
|
||||
REQUIRE(dest != NULL);
|
||||
REQUIRE(destlen > 0);
|
||||
REQUIRE(md != NULL);
|
||||
REQUIRE(secret != NULL);
|
||||
REQUIRE(secretlen > 0);
|
||||
REQUIRE(info != NULL);
|
||||
REQUIRE(infolen > 0);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
EVP_KDF_CTX *kctx = EVP_KDF_CTX_new(crypto_kdf_hkdf);
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_utf8_string(
|
||||
OSSL_KDF_PARAM_DIGEST, (char *)EVP_MD_get0_name(md), 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(),
|
||||
};
|
||||
bool ret = true;
|
||||
|
||||
if (EVP_KDF_derive(kctx, dest, destlen, params) <= 0) {
|
||||
ret = false;
|
||||
}
|
||||
|
||||
EVP_KDF_CTX_free(kctx);
|
||||
|
||||
return ret;
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
bool ret = true;
|
||||
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL);
|
||||
if (pctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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, md) != 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)
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
|
||||
EVP_PKEY_CTX_free(pctx);
|
||||
|
||||
return ret;
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_ctx_encrypt_create(EVP_CIPHER_CTX **out_aead_ctx,
|
||||
const EVP_CIPHER *aead,
|
||||
const uint8_t *key, size_t noncelen) {
|
||||
REQUIRE(out_aead_ctx != NULL && *out_aead_ctx == NULL);
|
||||
REQUIRE(aead != NULL);
|
||||
REQUIRE(key != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
int cipher_nid = EVP_CIPHER_nid(aead);
|
||||
EVP_CIPHER_CTX *aead_ctx = NULL;
|
||||
size_t taglen = 0;
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
OSSL_PARAM params[3];
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
taglen = isc__quic_crypto_aead_taglen(aead);
|
||||
|
||||
aead_ctx = EVP_CIPHER_CTX_new();
|
||||
if (aead_ctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
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_3_API_OR_NEWER */
|
||||
if (!EVP_EncryptInit_ex(aead_ctx, aead, NULL, NULL, NULL) ||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
!EVP_CIPHER_CTX_set_params(aead_ctx, params) ||
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN,
|
||||
(int)noncelen, NULL) ||
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, (int)taglen,
|
||||
NULL)) ||
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
!EVP_EncryptInit_ex(aead_ctx, NULL, NULL, key, NULL))
|
||||
{
|
||||
EVP_CIPHER_CTX_free(aead_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_aead_ctx = aead_ctx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_ctx_decrypt_create(EVP_CIPHER_CTX **out_aead_ctx,
|
||||
const EVP_CIPHER *aead,
|
||||
const uint8_t *key, size_t noncelen) {
|
||||
REQUIRE(out_aead_ctx != NULL && *out_aead_ctx == NULL);
|
||||
REQUIRE(aead != NULL);
|
||||
REQUIRE(key != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
EVP_CIPHER_CTX *actx = NULL;
|
||||
int cipher_nid = EVP_CIPHER_nid(aead);
|
||||
size_t taglen = isc__quic_crypto_aead_taglen(aead);
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
OSSL_PARAM params[3];
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
actx = EVP_CIPHER_CTX_new();
|
||||
if (actx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
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_3_API_OR_NEWER */
|
||||
|
||||
if (!EVP_DecryptInit_ex(actx, aead, NULL, NULL, NULL) ||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
!EVP_CIPHER_CTX_set_params(actx, params) ||
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
!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_3_API_OR_NEWER) */
|
||||
!EVP_DecryptInit_ex(actx, NULL, NULL, key, NULL))
|
||||
{
|
||||
EVP_CIPHER_CTX_free(actx);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_aead_ctx = actx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_encrypt(uint8_t *dest, const EVP_CIPHER *aead,
|
||||
EVP_CIPHER_CTX *aead_ctx, const uint8_t *nonce,
|
||||
const uint8_t *plaintext,
|
||||
const size_t plaintextlen, const uint8_t *aad,
|
||||
const size_t aadlen) {
|
||||
REQUIRE(dest != NULL);
|
||||
REQUIRE(aead != NULL);
|
||||
REQUIRE(aead_ctx != NULL);
|
||||
REQUIRE(nonce != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
size_t taglen = isc__quic_crypto_aead_taglen(aead);
|
||||
int cipher_nid = EVP_CIPHER_nid(aead);
|
||||
int len = 0;
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
OSSL_PARAM params[] = {
|
||||
OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
|
||||
dest + plaintextlen, taglen),
|
||||
OSSL_PARAM_construct_end(),
|
||||
};
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
if (!EVP_EncryptInit_ex(aead_ctx, NULL, NULL, NULL, nonce) ||
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_EncryptUpdate(aead_ctx, NULL, &len, NULL,
|
||||
(int)plaintextlen)) ||
|
||||
!EVP_EncryptUpdate(aead_ctx, NULL, &len, aad, (int)aadlen) ||
|
||||
!EVP_EncryptUpdate(aead_ctx, dest, &len, plaintext,
|
||||
(int)plaintextlen) ||
|
||||
!EVP_EncryptFinal_ex(aead_ctx, dest + len, &len) ||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
!EVP_CIPHER_CTX_get_params(aead_ctx, params)
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_GET_TAG, (int)taglen,
|
||||
dest + plaintextlen)
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_decrypt(uint8_t *dest, const EVP_CIPHER *aead,
|
||||
EVP_CIPHER_CTX *aead_ctx, const uint8_t *nonce,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *aad, const size_t aadlen) {
|
||||
REQUIRE(dest != NULL);
|
||||
REQUIRE(aead != NULL);
|
||||
REQUIRE(aead_ctx != NULL);
|
||||
REQUIRE(nonce != NULL);
|
||||
REQUIRE(ciphertext != NULL);
|
||||
REQUIRE(ciphertextlen > 0);
|
||||
|
||||
size_t taglen = isc__quic_crypto_aead_taglen(aead);
|
||||
int cipher_nid = EVP_CIPHER_nid(aead);
|
||||
int len = 0;
|
||||
const uint8_t *tag = NULL;
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
OSSL_PARAM params[2];
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
if (taglen > ciphertextlen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ciphertextlen -= taglen;
|
||||
tag = ciphertext + ciphertextlen;
|
||||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
params[0] = OSSL_PARAM_construct_octet_string(
|
||||
OSSL_CIPHER_PARAM_AEAD_TAG, (void *)tag, taglen);
|
||||
params[1] = OSSL_PARAM_construct_end();
|
||||
#endif /* OPENSSL_3_API_OR_NEWER */
|
||||
|
||||
if (!EVP_DecryptInit_ex(aead_ctx, NULL, NULL, NULL, nonce) ||
|
||||
#ifdef OPENSSL_3_API_OR_NEWER
|
||||
!EVP_CIPHER_CTX_set_params(aead_ctx, params) ||
|
||||
#else /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_TAG, (int)taglen,
|
||||
(uint8_t *)tag) ||
|
||||
#endif /* !(OPENSSL_3_API_OR_NEWER) */
|
||||
(cipher_nid == NID_aes_128_ccm &&
|
||||
!EVP_DecryptUpdate(aead_ctx, NULL, &len, NULL,
|
||||
(int)ciphertextlen)) ||
|
||||
!EVP_DecryptUpdate(aead_ctx, NULL, &len, aad, (int)aadlen) ||
|
||||
!EVP_DecryptUpdate(aead_ctx, dest, &len, ciphertext,
|
||||
(int)ciphertextlen) ||
|
||||
(cipher_nid != NID_aes_128_ccm &&
|
||||
!EVP_DecryptFinal_ex(aead_ctx, dest + ciphertextlen, &len)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hp_cipher_ctx_encrypt_create(EVP_CIPHER_CTX **out_hp_cipher_ctx,
|
||||
const EVP_CIPHER *hp_cipher,
|
||||
const uint8_t *key) {
|
||||
EVP_CIPHER_CTX *cipher_ctx = NULL;
|
||||
|
||||
REQUIRE(out_hp_cipher_ctx != NULL && *out_hp_cipher_ctx == NULL);
|
||||
REQUIRE(hp_cipher != NULL);
|
||||
REQUIRE(key != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
cipher_ctx = EVP_CIPHER_CTX_new();
|
||||
if (cipher_ctx == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EVP_EncryptInit_ex(cipher_ctx, hp_cipher, NULL, key, NULL)) {
|
||||
EVP_CIPHER_CTX_free(cipher_ctx);
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_hp_cipher_ctx = cipher_ctx;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* See RFC9001, sections 5.4.1-5.4.4 on QUIC header protection
|
||||
* details. OpenSSL's EVP API (aka "EnVeloPe") hides gory details of
|
||||
* different algorithms here.
|
||||
*/
|
||||
bool
|
||||
isc__quic_crypto_hp_mask(uint8_t *dest, EVP_CIPHER_CTX *hp_ctx,
|
||||
const uint8_t *sample) {
|
||||
static const uint8_t mask[ISC__QUIC_CRYPTO_HP_MASK_LEN] = { 0 };
|
||||
int len = 0;
|
||||
|
||||
REQUIRE(dest != NULL);
|
||||
REQUIRE(hp_ctx != NULL);
|
||||
REQUIRE(sample != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
if (!EVP_EncryptInit_ex(hp_ctx, NULL, NULL, NULL, sample) ||
|
||||
!EVP_EncryptUpdate(hp_ctx, dest, &len, mask, sizeof(mask)) ||
|
||||
!EVP_EncryptFinal_ex(hp_ctx, dest + sizeof(mask), &len))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
isc__quic_crypto_cipher_ctx_free(EVP_CIPHER_CTX **pcipher_ctx) {
|
||||
REQUIRE(pcipher_ctx != NULL && *pcipher_ctx != NULL);
|
||||
|
||||
RUNTIME_CHECK(quic_crypto_initialized);
|
||||
|
||||
EVP_CIPHER_CTX_free(*pcipher_ctx);
|
||||
*pcipher_ctx = NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
isc__quic_crypto_random(uint8_t *data, const size_t datalen) {
|
||||
int ret = 0;
|
||||
|
||||
REQUIRE(data != NULL);
|
||||
REQUIRE(datalen > 0);
|
||||
|
||||
ret = RAND_bytes(data, (int)datalen);
|
||||
if (ret != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
437
lib/isc/quic/quic_crypto.h
Normal file
437
lib/isc/quic/quic_crypto.h
Normal file
@@ -0,0 +1,437 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/kdf.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <isc/tls.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
/*
|
||||
* The following constants are either taken or derived from RFC9001, RFC9369
|
||||
* (QUICv2).
|
||||
*/
|
||||
#define ISC__QUIC_CRYPTO_HP_MASK_LEN (5)
|
||||
|
||||
/* Client initial key label */
|
||||
#define ISC__QUIC_CRYPTO_CLIENT_IN_LABEL "client in"
|
||||
#define ISC__QUIC_CRYPTO_CLIENT_IN_LABEL_LEN \
|
||||
(sizeof(ISC__QUIC_CRYPTO_CLIENT_IN_LABEL) - 1)
|
||||
|
||||
/* Server initial key label */
|
||||
#define ISC__QUIC_CRYPTO_SERVER_IN_LABEL "server in"
|
||||
#define ISC__QUIC_CRYPTO_SERVER_IN_LABEL_LEN \
|
||||
(sizeof(ISC__QUIC_CRYPTO_SERVER_IN_LABEL) - 1)
|
||||
|
||||
/* Salt to derive initial secret for QUIC */
|
||||
#define ISC__QUIC_CRYPTO_INITIAL_SALT_V1 \
|
||||
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad\xcc" \
|
||||
"\xbb\x7f\x0a"
|
||||
#define ISC__QUIC_CRYPTO_INITIAL_SALT_V1_LEN \
|
||||
(sizeof(ISC__QUIC_CRYPTO_INITIAL_SALT_V1) - 1)
|
||||
|
||||
/* QUIC key label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KEY_LABEL_V1 "quic key"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KEY_LABEL_V1_LEN \
|
||||
(sizeof(ISC__QUIC_CRYPTO_QUIC_KEY_LABEL_V1) - 1)
|
||||
|
||||
/* QUIC IV label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_IV_LABEL_V1 "quic iv"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_IV_LABEL_V1_LEN \
|
||||
((sizeof(ISC__QUIC_CRYPTO_QUIC_IV_LABEL_V1) - 1))
|
||||
|
||||
/* QUIC header protection label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_HP_LABEL_V1 "quic hp"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_HP_LABEL_V1_LEN \
|
||||
((sizeof(ISC__QUIC_CRYPTO_QUIC_HP_LABEL_V1) - 1))
|
||||
|
||||
/* QUIC key update label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KU_LABEL_V1 "quic ku"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KU_LABEL_V1_LEN \
|
||||
((sizeof(ISC__QUIC_CRYPTO_QUIC_KU_LABEL_V1) - 1))
|
||||
|
||||
/* Salt to derive initial secret for QUICv2 */
|
||||
#define ISC__QUIC_CRYPTO_INITIAL_SALT_V2 \
|
||||
"\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb\xf9" \
|
||||
"\xbd\x2e\xd9"
|
||||
#define ISC__QUIC_CRYPTO_INITIAL_SALT_V2_LEN \
|
||||
(sizeof(ISC__QUIC_CRYPTO_INITIAL_SALT_V2) - 1)
|
||||
|
||||
/* QUICv2 key label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KEY_LABEL_V2 "quicv2 key"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KEY_LABEL_V2_LEN \
|
||||
(sizeof(ISC__QUIC_CRYPTO_QUIC_KEY_LABEL_V2) - 1)
|
||||
|
||||
/* QUICv2 IV label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_IV_LABEL_V2 "quicv2 iv"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_IV_LABEL_V2_LEN \
|
||||
((sizeof(ISC__QUIC_CRYPTO_QUIC_IV_LABEL_V2) - 1))
|
||||
|
||||
/* QUICv2 header protection label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_HP_LABEL_V2 "quicv2 hp"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_HP_LABEL_V2_LEN \
|
||||
((sizeof(ISC__QUIC_CRYPTO_QUIC_HP_LABEL_V2) - 1))
|
||||
|
||||
/* QUICv2 key update label */
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KU_LABEL_V2 "quicv2 ku"
|
||||
#define ISC__QUIC_CRYPTO_QUIC_KU_LABEL_V2_LEN \
|
||||
((sizeof(ISC__QUIC_CRYPTO_QUIC_KU_LABEL_V2) - 1))
|
||||
|
||||
/* These are defined in RFC8446 */
|
||||
#define ISC__QUIC_CRYPTO_KEY_LABEL "key"
|
||||
#define ISC__QUIC_CRYPTO_KEY_LABEL_LEN (sizeof(ISC__QUIC_CRYPTO_KEY_LABEL) - 1)
|
||||
|
||||
#define ISC__QUIC_CRYPTO_IV_LABEL "iv"
|
||||
#define ISC__QUIC_CRYPTO_IV_LABEL_LEN ((sizeof(ISC__QUIC_CRYPTO_IV_LABEL) - 1))
|
||||
|
||||
bool
|
||||
isc__quic_crypto_tls_cipher_supported(const isc_tls_cipher_t *tls_cipher);
|
||||
/*%<
|
||||
* Check if the given TLS cipher can be used for QUIC.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls_cipher' != NULL.
|
||||
*/
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_tls_cipher_aead(const isc_tls_cipher_t *tls_cipher);
|
||||
/*%<
|
||||
* Return the AEAD-scheme associated with the given TLS cipher.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls_cipher' != NULL.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_taglen(const EVP_CIPHER *aead);
|
||||
/*%<
|
||||
* Return the tag length (overhead) for the given AEAD-scheme.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'aead' != NULL.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_keylen(const EVP_CIPHER *aead);
|
||||
/*%<
|
||||
* Return the tag length for the given AEAD-scheme.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'aead' != NULL.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_ivlen(const EVP_CIPHER *aead);
|
||||
/*%<
|
||||
* Return the IV (initialization vector) length for the given AEAD-scheme.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'aead' != NULL.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_aead_packet_protection_ivlen(const EVP_CIPHER *aead);
|
||||
/*%<
|
||||
* Return the packet protection IV (initialization vector) length for the given
|
||||
* AEAD-scheme.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'aead' != NULL.
|
||||
*/
|
||||
|
||||
uint64_t
|
||||
isc__quic_crypto_tls_cipher_aead_max_encryption(
|
||||
const isc_tls_cipher_t *tls_cipher);
|
||||
/*%<
|
||||
* Return the max encryption limit for the AEAD-scheme associated with the given
|
||||
* TLS cipher.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls_cipher' != NULL.
|
||||
*/
|
||||
|
||||
uint64_t
|
||||
isc__quic_crypto_tls_cipher_aead_max_decyption_failures(
|
||||
const isc_tls_cipher_t *tls_cipher);
|
||||
/*%<
|
||||
* Return the max decryption failures limit for the AEAD-scheme
|
||||
* associated with the given TLS cipher.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls_cipher' != NULL.
|
||||
*/
|
||||
|
||||
const EVP_MD *
|
||||
isc__quic_crypto_tls_cipher_md(const isc_tls_cipher_t *tls_cipher);
|
||||
/*%<
|
||||
* Return the message digest function associated with the given TLS cipher.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls_cipher' != NULL.
|
||||
*/
|
||||
|
||||
size_t
|
||||
isc__quic_crypto_md_hashlen(const EVP_MD *md);
|
||||
/*%<
|
||||
* Return the message digest (hash) size.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'md' != NULL.
|
||||
*/
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_tls_cipher_hp(const isc_tls_cipher_t *tls_cipher);
|
||||
/*%<
|
||||
* Return the QUIC header protection cipher associated with the given TLS
|
||||
* cipher.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'tls_cipher' != NULL.
|
||||
*/
|
||||
|
||||
const EVP_MD *
|
||||
isc__quic_crypto_md_sha256(void);
|
||||
/*%<
|
||||
* Return the SHA256 message digest function.
|
||||
*/
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_aead_aes_128_gcm(void);
|
||||
/*%<
|
||||
* Return the AES-128-GCM AEAD-scheme.
|
||||
*/
|
||||
|
||||
const EVP_CIPHER *
|
||||
isc__quic_crypto_cipher_aes_128_ctr(void);
|
||||
/*%<
|
||||
* Return the AES-128-CTR cipher.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf_extract(uint8_t *dest, const EVP_MD *md,
|
||||
const uint8_t *secret, const size_t secretlen,
|
||||
const uint8_t *salt, const size_t saltlen);
|
||||
/*%<
|
||||
* Perform "HKDF-Extract" operation.
|
||||
*
|
||||
* The caller is responsible to specify the destination buffer that
|
||||
* has enough capacity to store the output.
|
||||
*
|
||||
* See RFC5869 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'md' != NULL;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf_expand(uint8_t *dest, size_t destlen, const EVP_MD *md,
|
||||
const uint8_t *secret, const size_t secretlen,
|
||||
const uint8_t *info, const size_t infolen);
|
||||
/*%<
|
||||
* Perform "HKDF-Expand" operation.
|
||||
*
|
||||
* See RFC5869 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'destlen' > 0;
|
||||
*\li 'md' != NULL;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'info' != NULL;
|
||||
*\li 'infolen' > 0.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf_expand_label(uint8_t *dest, size_t destlen,
|
||||
const EVP_MD *md, const uint8_t *secret,
|
||||
const size_t secretlen, const uint8_t *label,
|
||||
const size_t labellen);
|
||||
/*%<
|
||||
* Perform "HKDF-Expand-Label" operation as defined for TLSv1.3.
|
||||
*
|
||||
* See RFC8446, Section 7.1 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'destlen' > 0;
|
||||
*\li 'md' != NULL;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'label' != NULL;
|
||||
*\li 'labellen' > 0.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hkdf(uint8_t *dest, size_t destlen, const EVP_MD *md,
|
||||
const uint8_t *secret, size_t secretlen,
|
||||
const uint8_t *salt, size_t saltlen, const uint8_t *info,
|
||||
size_t infolen);
|
||||
/*%<
|
||||
* Perform "HKDF" operation.
|
||||
*
|
||||
* See RFC5869 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'destlen' > 0;
|
||||
*\li 'md' != NULL;
|
||||
*\li 'secret' != NULL;
|
||||
*\li 'secretlen' > 0;
|
||||
*\li 'info' != NULL;
|
||||
*\li 'infolen' > 0.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_ctx_encrypt_create(EVP_CIPHER_CTX **out_aead_ctx,
|
||||
const EVP_CIPHER *aead,
|
||||
const uint8_t *key, size_t noncelen);
|
||||
/*%<
|
||||
* Create AEAD encryption context.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'out_aead_ctx' != NULL && '*out_aead_ctx' == NULL;
|
||||
*\li 'aead' != NULL;
|
||||
*\li 'key' != NULL.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_ctx_decrypt_create(EVP_CIPHER_CTX **out_aead_ctx,
|
||||
const EVP_CIPHER *aead,
|
||||
const uint8_t *key, size_t noncelen);
|
||||
/*%<
|
||||
* Create AEAD decryption context.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'out_aead_ctx' != NULL && '*out_aead_ctx' == NULL;
|
||||
*\li 'aead' != NULL;
|
||||
*\li 'key' != NULL.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_encrypt(uint8_t *dest, const EVP_CIPHER *aead,
|
||||
EVP_CIPHER_CTX *aead_ctx, const uint8_t *nonce,
|
||||
const uint8_t *plaintext,
|
||||
const size_t plaintextlen, const uint8_t *aad,
|
||||
const size_t aadlen);
|
||||
/*%<
|
||||
* Perform authenticated encryption operation (aka "seal" in BoringSSL
|
||||
* parlance).
|
||||
*
|
||||
* The caller is responsible to specify the destination buffer that
|
||||
* has enough capacity to store the output.
|
||||
*
|
||||
* See RFC5116 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'aead' != NULL;
|
||||
*\li 'aead_ctx' != NULL;
|
||||
*\li 'nonce' != NULL.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_aead_decrypt(uint8_t *dest, const EVP_CIPHER *aead,
|
||||
EVP_CIPHER_CTX *aead_ctx, const uint8_t *nonce,
|
||||
const uint8_t *ciphertext, size_t ciphertextlen,
|
||||
const uint8_t *aad, const size_t aadlen);
|
||||
/*%<
|
||||
* Perform authenticated decryption operation (aka "open" in BoringSSL
|
||||
* parlance).
|
||||
*
|
||||
* The caller is responsible to specify the destination buffer that
|
||||
* has enough capacity to store the output.
|
||||
*
|
||||
* See RFC5116 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'aead' != NULL;
|
||||
*\li 'aead_ctx' != NULL;
|
||||
*\li 'nonce' != NULL;
|
||||
*\li 'ciphertext' != NULL;
|
||||
*\li 'ciphertextlen' > 0.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hp_cipher_ctx_encrypt_create(EVP_CIPHER_CTX **out_hp_cipher_ctx,
|
||||
const EVP_CIPHER *hp_cipher,
|
||||
const uint8_t *key);
|
||||
/*%<
|
||||
* Create header protection encryption context.
|
||||
*
|
||||
* See RFC9001, Section 5.4 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'out_hp_cipher_ctx' != NULL && '*out_hp_cipher_ctx' == NULL;
|
||||
*\li 'hp_cipher' != NULL;
|
||||
*\li 'key' != NULL.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_hp_mask(uint8_t *dest, EVP_CIPHER_CTX *hp_ctx,
|
||||
const uint8_t *sample);
|
||||
/*%<
|
||||
* Calculate header protection mask. The output buffer 'dest' should
|
||||
* be at least 'ISC__QUIC_CRYPTO_HP_MASK_LEN' bytes long.
|
||||
*
|
||||
* See RFC9001, Section 5.4.1 for more details.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'dest' != NULL;
|
||||
*\li 'hp_ctx' != NULL;
|
||||
*\li 'sample' != NULL.
|
||||
*/
|
||||
|
||||
void
|
||||
isc__quic_crypto_cipher_ctx_free(EVP_CIPHER_CTX **pcipher_ctx);
|
||||
/*%<
|
||||
* Free a header protection or AEAD encryption context.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'pcipher_ctx' != NULL && '*pcipher_ctx' != NULL.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__quic_crypto_random(uint8_t *data, const size_t datalen);
|
||||
/*%<
|
||||
* Writes cryptographically-secure random data into the given
|
||||
* buffer.
|
||||
*
|
||||
* The function relies on a user-space CSPRNG provided by the
|
||||
* crypto-library (e.g. OpenSSL).
|
||||
*
|
||||
* Being user-space it might be preferred to 'isc_entropy_get()'
|
||||
* which, ultimately, might lead to sys. calls in relatively hot
|
||||
* paths. For what we need it (mostly - salt generation) this
|
||||
* approach is good enough.
|
||||
*
|
||||
* For things that should never cross the running system boundary it
|
||||
* is better to use 'isc_entropy_get()'.
|
||||
*
|
||||
* NOTE: See the OpenSSL's manual page for 'RAND_bytes()'. Also take
|
||||
* your time to understand why 'RAND_priv_bytes()' is preferred in
|
||||
* some cases (use 'isc_entropy_get()' in its stead).
|
||||
*/
|
||||
|
||||
void
|
||||
isc__quic_crypto_initialize(void);
|
||||
|
||||
void
|
||||
isc__quic_crypto_shutdown(void);
|
||||
1033
lib/isc/quic/quic_interface_compat.c
Normal file
1033
lib/isc/quic/quic_interface_compat.c
Normal file
File diff suppressed because it is too large
Load Diff
458
lib/isc/quic/quic_interface_native.c
Normal file
458
lib/isc/quic/quic_interface_native.c
Normal file
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Native QUIC interface for OpenSSL forks implementing
|
||||
* BoringSSL/LibreSSL/QuicTLS QUIC API.
|
||||
*
|
||||
* See here:
|
||||
* https://github.com/quictls/openssl/blob/openssl-3.1.5%2Bquic/doc/man3/SSL_CTX_set_quic_method.pod
|
||||
* https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#QUIC-integration
|
||||
*
|
||||
* LibreSSL's interface strives to be compatible with other
|
||||
* libraries. That being said, there are still differences in how
|
||||
* different libraries behave, in particular how they report current
|
||||
* read and write levels, but that should not matter much on practice.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <isc/magic.h>
|
||||
#include <isc/tls.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "quic-int.h"
|
||||
|
||||
#define QUIC_NATIVE_DATA_MAGIC ISC_MAGIC('Q', 'n', 'C', 'd')
|
||||
#define VALID_QUIC_NATIVE_DATA(t) ISC_MAGIC_VALID(t, QUIC_NATIVE_DATA_MAGIC)
|
||||
|
||||
static int
|
||||
compat_encryption_level_to_native(const isc_quic_encryption_level_t level);
|
||||
|
||||
static void
|
||||
quic_native_tlsctx_configure(isc_tlsctx_t *tlsctx);
|
||||
|
||||
static void
|
||||
quic_native_tlsctx_keylog_callback(const isc_tls_t *tls, const char *line);
|
||||
|
||||
static void
|
||||
quic_native_tls_init(isc_tls_t *tls, isc_mem_t *mctx);
|
||||
|
||||
static void
|
||||
quic_native_tls_uninit(isc_tls_t *tls);
|
||||
|
||||
static bool
|
||||
quic_native_tls_calling_method_cb(const isc_tls_t *tls);
|
||||
|
||||
static int
|
||||
quic_native_tls_set_quic_method(isc_tls_t *tls,
|
||||
const isc_tls_quic_method_t *method);
|
||||
|
||||
static int
|
||||
quic_native_tls_provide_quic_data(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t *data, const size_t len);
|
||||
|
||||
static int
|
||||
quic_native_tls_do_quic_handshake(isc_tls_t *tls);
|
||||
|
||||
static int
|
||||
quic_native_tls_process_quic_post_handshake(isc_tls_t *tls);
|
||||
|
||||
static int
|
||||
quic_native_tls_set_quic_transport_params(isc_tls_t *tls, const uint8_t *params,
|
||||
const size_t params_len);
|
||||
static void
|
||||
quic_native_tls_get_peer_quic_transport_params(isc_tls_t *tls,
|
||||
const uint8_t **out_params,
|
||||
size_t *out_params_len);
|
||||
|
||||
static isc_quic_encryption_level_t
|
||||
quic_native_read_level(const isc_tls_t *tls);
|
||||
|
||||
static isc_quic_encryption_level_t
|
||||
quic_native_write_level(const isc_tls_t *tls);
|
||||
|
||||
static isc_tls_quic_interface_t native_quic_interface =
|
||||
(isc_tls_quic_interface_t){
|
||||
.tlsctx_configure = quic_native_tlsctx_configure,
|
||||
.tlsctx_keylog_callback = quic_native_tlsctx_keylog_callback,
|
||||
|
||||
.tls_init = quic_native_tls_init,
|
||||
.tls_uninit = quic_native_tls_uninit,
|
||||
|
||||
.tls_calling_method_cb = quic_native_tls_calling_method_cb,
|
||||
.tls_set_quic_method = quic_native_tls_set_quic_method,
|
||||
|
||||
.tls_provide_quic_data = quic_native_tls_provide_quic_data,
|
||||
.tls_do_quic_handshake = quic_native_tls_do_quic_handshake,
|
||||
.tls_process_quic_post_handshake =
|
||||
quic_native_tls_process_quic_post_handshake,
|
||||
|
||||
.tls_set_quic_transport_params =
|
||||
quic_native_tls_set_quic_transport_params,
|
||||
.tls_get_peer_quic_transport_params =
|
||||
quic_native_tls_get_peer_quic_transport_params,
|
||||
|
||||
.tls_quic_read_level = quic_native_read_level,
|
||||
.tls_quic_write_level = quic_native_write_level
|
||||
};
|
||||
|
||||
static int
|
||||
quic_native_method_set_read_secret(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const isc_tls_cipher_t *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
|
||||
static int
|
||||
quic_native_method_set_write_secret(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const isc_tls_cipher_t *cipher,
|
||||
const uint8_t *secret, size_t secret_len);
|
||||
|
||||
#ifndef HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET
|
||||
static int
|
||||
quic_native_method_set_encryption_secrets(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const uint8_t *read_secret,
|
||||
const uint8_t *write_secret,
|
||||
size_t secret_len);
|
||||
#endif /* HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET */
|
||||
|
||||
static int
|
||||
quic_native_method_add_handshake_data(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const uint8_t *hs_data, size_t hs_len);
|
||||
|
||||
static int
|
||||
quic_native_method_flush_flight(isc_tls_t *tls);
|
||||
|
||||
static int
|
||||
quic_native_method_send_alert(isc_tls_t *tls, enum ssl_encryption_level_t level,
|
||||
uint8_t alert);
|
||||
|
||||
static SSL_QUIC_METHOD native_quic_method = (SSL_QUIC_METHOD){
|
||||
#ifdef HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET
|
||||
.set_read_secret = quic_native_method_set_read_secret,
|
||||
.set_write_secret = quic_native_method_set_write_secret,
|
||||
#else
|
||||
.set_encryption_secrets = quic_native_method_set_encryption_secrets,
|
||||
#endif /* HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET */
|
||||
.add_handshake_data = quic_native_method_add_handshake_data,
|
||||
.flush_flight = quic_native_method_flush_flight,
|
||||
.send_alert = quic_native_method_send_alert
|
||||
};
|
||||
|
||||
typedef struct quic_native_data {
|
||||
uint32_t magic;
|
||||
isc_mem_t *mctx;
|
||||
const isc_tls_quic_method_t *method;
|
||||
bool calling_method_cb;
|
||||
} quic_native_data_t;
|
||||
|
||||
static int
|
||||
compat_encryption_level_to_native(const isc_quic_encryption_level_t level) {
|
||||
switch (level) {
|
||||
case ISC_QUIC_ENCRYPTION_INITIAL:
|
||||
return ssl_encryption_initial;
|
||||
case ISC_QUIC_ENCRYPTION_EARLY_DATA:
|
||||
return ssl_encryption_early_data;
|
||||
case ISC_QUIC_ENCRYPTION_HANDSHAKE:
|
||||
return ssl_encryption_handshake;
|
||||
case ISC_QUIC_ENCRYPTION_APPLICATION:
|
||||
return ssl_encryption_application;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static isc_quic_encryption_level_t
|
||||
native_to_compat_encryption_level(int level) {
|
||||
switch (level) {
|
||||
case ssl_encryption_initial:
|
||||
return ISC_QUIC_ENCRYPTION_INITIAL;
|
||||
case ssl_encryption_early_data:
|
||||
return ISC_QUIC_ENCRYPTION_EARLY_DATA;
|
||||
case ssl_encryption_handshake:
|
||||
return ISC_QUIC_ENCRYPTION_HANDSHAKE;
|
||||
case ssl_encryption_application:
|
||||
return ISC_QUIC_ENCRYPTION_APPLICATION;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static void
|
||||
quic_native_tlsctx_configure(isc_tlsctx_t *tlsctx) {
|
||||
/* dummy */
|
||||
UNUSED(tlsctx);
|
||||
}
|
||||
|
||||
/* Will not be called on LibreSSL */
|
||||
static void
|
||||
quic_native_tlsctx_keylog_callback(const isc_tls_t *tls, const char *line) {
|
||||
/* dummy */
|
||||
UNUSED(tls);
|
||||
UNUSED(line);
|
||||
}
|
||||
|
||||
static void
|
||||
quic_native_tls_init(isc_tls_t *tls, isc_mem_t *mctx) {
|
||||
quic_native_data_t *data = isc_mem_cget(mctx, 1, sizeof(*data));
|
||||
|
||||
isc_mem_attach(mctx, &data->mctx);
|
||||
|
||||
data->magic = QUIC_NATIVE_DATA_MAGIC;
|
||||
|
||||
INSIST(isc__tls_get_quic_data(tls) == NULL);
|
||||
isc__tls_set_quic_data(tls, data);
|
||||
}
|
||||
|
||||
static void
|
||||
quic_native_tls_uninit(isc_tls_t *tls) {
|
||||
isc_mem_t *mctx = NULL;
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
mctx = data->mctx;
|
||||
|
||||
isc_mem_cput(mctx, data, 1, sizeof(*data));
|
||||
isc__tls_set_quic_data(tls, NULL);
|
||||
|
||||
isc_mem_detach(&mctx);
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_native_tls_calling_method_cb(const isc_tls_t *tls) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
return data->calling_method_cb;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_tls_set_quic_method(isc_tls_t *tls,
|
||||
const isc_tls_quic_method_t *method) {
|
||||
int ret = 0;
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
ret = SSL_set_quic_method(tls, &native_quic_method);
|
||||
|
||||
if (ret == 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->method = method;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_tls_provide_quic_data(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t *data, const size_t len) {
|
||||
int encryption_level = compat_encryption_level_to_native(level);
|
||||
|
||||
return SSL_provide_quic_data(tls, encryption_level, data, len);
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_tls_do_quic_handshake(isc_tls_t *tls) {
|
||||
return SSL_do_handshake(tls);
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_tls_process_quic_post_handshake(isc_tls_t *tls) {
|
||||
return SSL_process_quic_post_handshake(tls);
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_tls_set_quic_transport_params(isc_tls_t *tls, const uint8_t *params,
|
||||
const size_t params_len) {
|
||||
return SSL_set_quic_transport_params(tls, params, params_len);
|
||||
}
|
||||
|
||||
static void
|
||||
quic_native_tls_get_peer_quic_transport_params(isc_tls_t *tls,
|
||||
const uint8_t **out_params,
|
||||
size_t *out_params_len) {
|
||||
SSL_get_peer_quic_transport_params(tls, out_params, out_params_len);
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_method_set_read_secret(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const isc_tls_cipher_t *cipher,
|
||||
const uint8_t *secret, size_t secret_len) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
isc_quic_encryption_level_t compat_level =
|
||||
native_to_compat_encryption_level((int)level);
|
||||
bool ret = false;
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
data->calling_method_cb = true;
|
||||
ret = data->method->set_read_secret(tls, compat_level, cipher, secret,
|
||||
secret_len);
|
||||
data->calling_method_cb = false;
|
||||
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_method_set_write_secret(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const isc_tls_cipher_t *cipher,
|
||||
const uint8_t *secret, size_t secret_len) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
isc_quic_encryption_level_t compat_level =
|
||||
native_to_compat_encryption_level((int)level);
|
||||
bool ret = false;
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
data->calling_method_cb = true;
|
||||
ret = data->method->set_write_secret(tls, compat_level, cipher, secret,
|
||||
secret_len);
|
||||
data->calling_method_cb = false;
|
||||
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifndef HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET
|
||||
static int
|
||||
quic_native_method_set_encryption_secrets(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const uint8_t *read_secret,
|
||||
const uint8_t *write_secret,
|
||||
size_t secret_len) {
|
||||
const isc_tls_cipher_t *cipher = SSL_get_current_cipher(tls);
|
||||
int ret = 1;
|
||||
|
||||
RUNTIME_CHECK(cipher != NULL);
|
||||
|
||||
if (read_secret != NULL) {
|
||||
ret = quic_native_method_set_read_secret(
|
||||
tls, level, cipher, read_secret, secret_len);
|
||||
}
|
||||
|
||||
if (ret < 1) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (write_secret != NULL) {
|
||||
ret = quic_native_method_set_write_secret(
|
||||
tls, level, cipher, write_secret, secret_len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* HAVE_QUIC_METHOD_SET_READ_WRITE_SECRET */
|
||||
|
||||
static int
|
||||
quic_native_method_add_handshake_data(isc_tls_t *tls,
|
||||
enum ssl_encryption_level_t level,
|
||||
const uint8_t *hs_data, size_t hs_len) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
isc_quic_encryption_level_t compat_level =
|
||||
native_to_compat_encryption_level((int)level);
|
||||
bool ret = false;
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
data->calling_method_cb = true;
|
||||
ret = data->method->add_handshake_data(tls, compat_level, hs_data,
|
||||
hs_len);
|
||||
data->calling_method_cb = false;
|
||||
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_method_flush_flight(isc_tls_t *tls) {
|
||||
/* dummy */
|
||||
UNUSED(tls);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_native_method_send_alert(isc_tls_t *tls, enum ssl_encryption_level_t level,
|
||||
uint8_t alert) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
isc_quic_encryption_level_t compat_level =
|
||||
native_to_compat_encryption_level((int)level);
|
||||
bool ret = false;
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
data->calling_method_cb = true;
|
||||
ret = data->method->send_alert(tls, compat_level, alert);
|
||||
data->calling_method_cb = false;
|
||||
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static isc_quic_encryption_level_t
|
||||
quic_native_read_level(const isc_tls_t *tls) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
isc_quic_encryption_level_t compat_level;
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
compat_level =
|
||||
native_to_compat_encryption_level(SSL_quic_read_level(tls));
|
||||
|
||||
return compat_level;
|
||||
}
|
||||
|
||||
static isc_quic_encryption_level_t
|
||||
quic_native_write_level(const isc_tls_t *tls) {
|
||||
quic_native_data_t *data = isc__tls_get_quic_data(tls);
|
||||
isc_quic_encryption_level_t compat_level;
|
||||
|
||||
INSIST(VALID_QUIC_NATIVE_DATA(data));
|
||||
|
||||
compat_level =
|
||||
native_to_compat_encryption_level(SSL_quic_write_level(tls));
|
||||
|
||||
return compat_level;
|
||||
}
|
||||
|
||||
const isc_tls_quic_interface_t *
|
||||
isc__tls_get_native_quic_interface(void) {
|
||||
return &native_quic_interface;
|
||||
}
|
||||
2266
lib/isc/quic/quic_session.c
Normal file
2266
lib/isc/quic/quic_session.c
Normal file
File diff suppressed because it is too large
Load Diff
149
lib/isc/quic/quic_session.h
Normal file
149
lib/isc/quic/quic_session.h
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <isc/ht.h>
|
||||
#include <isc/ngtcp2_crypto.h>
|
||||
#include <isc/ngtcp2_utils.h>
|
||||
#include <isc/quic.h>
|
||||
#include <isc/refcount.h>
|
||||
|
||||
#define QUIC_SESSION_MAGIC ISC_MAGIC('Q', 'U', 'I', 'C')
|
||||
#define VALID_QUIC_SESSION(t) ISC_MAGIC_VALID(t, QUIC_SESSION_MAGIC)
|
||||
|
||||
#define QUIC_CID_MAGIC ISC_MAGIC('Q', 'C', 'I', 'D')
|
||||
#define VALID_QUIC_CID(t) ISC_MAGIC_VALID(t, QUIC_CID_MAGIC)
|
||||
|
||||
typedef struct isc__quic_send_req isc__quic_send_req_t;
|
||||
|
||||
/*
|
||||
* NOTE: for all send queues new requests are appended strictly to the
|
||||
* end
|
||||
*/
|
||||
|
||||
typedef struct isc__quic_stream_data {
|
||||
int64_t stream_id;
|
||||
void *stream_user_data;
|
||||
|
||||
bool close_cb_called;
|
||||
bool fin;
|
||||
|
||||
/* local send queue */
|
||||
ISC_LIST(isc__quic_send_req_t) send_queue;
|
||||
size_t send_queue_len;
|
||||
|
||||
ISC_LINK(struct isc__quic_stream_data) stream_link;
|
||||
} isc__quic_stream_data_t;
|
||||
|
||||
struct isc__quic_send_req {
|
||||
isc__quic_stream_data_t *stream;
|
||||
|
||||
isc_buffer_t data;
|
||||
bool fin;
|
||||
|
||||
isc_quic_send_cb_t cb;
|
||||
void *cbarg;
|
||||
|
||||
ISC_LINK(struct isc__quic_send_req) global_link;
|
||||
ISC_LINK(struct isc__quic_send_req) local_link;
|
||||
};
|
||||
|
||||
typedef ISC_LIST(isc__quic_stream_data_t) quic_stream_list_t;
|
||||
|
||||
struct isc_quic_session {
|
||||
unsigned int magic;
|
||||
isc_refcount_t references;
|
||||
|
||||
bool is_server;
|
||||
int state;
|
||||
isc_tlsctx_t *tlsctx;
|
||||
isc_tls_t *tls;
|
||||
isc_mem_t *mctx;
|
||||
|
||||
isc_quic_session_interface_t cbs;
|
||||
void *cbarg;
|
||||
|
||||
ngtcp2_tstamp ts;
|
||||
|
||||
uint64_t handshake_timeout;
|
||||
uint64_t idle_timeout;
|
||||
|
||||
size_t max_uni_streams;
|
||||
size_t max_bidi_streams;
|
||||
|
||||
ngtcp2_mem mem;
|
||||
|
||||
uint8_t secret_storage[ISC_NGTCP2_CRYPTO_STATIC_SECRET_LEN];
|
||||
isc_buffer_t secret;
|
||||
|
||||
uint32_t orig_client_chosen_version;
|
||||
uint32_t negotiated_version;
|
||||
|
||||
uint32_t available_versions_list_storage[8];
|
||||
isc_buffer_t available_versions;
|
||||
|
||||
isc_buffer_t token;
|
||||
uint8_t token_storage[ISC_MAX(ISC_NGTCP2_CRYPTO_MAX_RETRY_TOKEN_LEN,
|
||||
ISC_NGTCP2_CRYPTO_MAX_REGULAR_TOKEN_LEN)];
|
||||
ngtcp2_token_type token_type;
|
||||
|
||||
isc_quic_cid_t *initial_scid;
|
||||
isc_quic_cid_t *initial_dcid;
|
||||
|
||||
isc_quic_cid_t *odcid;
|
||||
isc_quic_cid_t *rcid;
|
||||
|
||||
ngtcp2_path_storage path_st;
|
||||
ngtcp2_conn *conn;
|
||||
ngtcp2_ccerr conn_err;
|
||||
|
||||
bool closing;
|
||||
isc_buffer_t *close_msg;
|
||||
|
||||
ngtcp2_tstamp last_expiry;
|
||||
size_t sent_before_expiry;
|
||||
|
||||
bool write_after_read;
|
||||
|
||||
struct quic_session_streams {
|
||||
isc_ht_t *idx;
|
||||
quic_stream_list_t list;
|
||||
isc_mempool_t *streams_pool;
|
||||
} streams;
|
||||
|
||||
/* global send queue for all streams */
|
||||
ISC_LIST(isc__quic_send_req_t) send_queue;
|
||||
size_t send_queue_len;
|
||||
isc_mempool_t *send_pool;
|
||||
|
||||
/* list of associated CIDs */
|
||||
ISC_LIST(isc_quic_cid_t) cids;
|
||||
size_t cids_len;
|
||||
|
||||
bool hs_confirmed;
|
||||
};
|
||||
|
||||
struct isc_quic_cid {
|
||||
unsigned int magic;
|
||||
isc_refcount_t references;
|
||||
|
||||
bool source; /* local */
|
||||
|
||||
ngtcp2_cid cid;
|
||||
|
||||
isc_mem_t *mctx;
|
||||
|
||||
ISC_LINK(struct isc_quic_cid) global_link;
|
||||
ISC_LINK(struct isc_quic_cid) local_link;
|
||||
};
|
||||
297
lib/isc/quic/tls_keylog_parser.c
Normal file
297
lib/isc/quic/tls_keylog_parser.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/mem.h>
|
||||
#include <isc/tls.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "quic-int.h"
|
||||
|
||||
/*
|
||||
* We have used the following IETF draft to derive the grammar below:
|
||||
*
|
||||
* https://datatracker.ietf.org/doc/draft-ietf-tls-keylogfile/
|
||||
*
|
||||
* Both the grammar and the parser should be *painfully* accurate and
|
||||
* describes what is used to be named "NSS Key Log Format".
|
||||
*
|
||||
* tls-keylog-entry = label " " client-random " " secret [ end-line ].
|
||||
* label = "CLIENT_EARLY_TRAFFIC_SECRET" |
|
||||
* "CLIENT_HANDSHAKE_TRAFFIC_SECRET" |
|
||||
* "CLIENT_RANDOM" |
|
||||
* "CLIENT_TRAFFIC_SECRET_0" |
|
||||
* "EARLY_EXPORTER_MASTER_SECRET" |
|
||||
* "EXPORTER_SECRET" |
|
||||
* "SERVER_HANDSHAKE_TRAFFIC_SECRET" |
|
||||
* "SERVER_TRAFFIC_SECRET_0".
|
||||
* client-random = hex-byte { hex-byte }. (* 64 characters, 32 bytes *)
|
||||
* secret = hex-byte { hex-byte }.
|
||||
* end-line = end-char { end-char }.
|
||||
* hex-byte = hex-char hex-char.
|
||||
* hex-char = digit | "a" | "A" | "b" | "B" | "c" | "C" |
|
||||
* "d" | "D" | "e" | "E" | "f" | "F".
|
||||
* digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".
|
||||
* end-char = "\n" | "\r".
|
||||
*/
|
||||
|
||||
#define CLIENT_RANDOM_LEN (32)
|
||||
|
||||
typedef struct keylog_parser_state {
|
||||
const char *restrict str;
|
||||
isc__tls_keylog_label_t *restrict out_label;
|
||||
isc_buffer_t *restrict out_client_random;
|
||||
isc_buffer_t *restrict out_secret;
|
||||
isc_result_t result;
|
||||
} keylog_parser_state_t;
|
||||
|
||||
static bool
|
||||
match_str(keylog_parser_state_t *restrict st, const char *s, const size_t len) {
|
||||
return strncasecmp(s, st->str, len) == 0;
|
||||
}
|
||||
|
||||
static bool
|
||||
match_str_advance(keylog_parser_state_t *restrict st, const char *s,
|
||||
const size_t len) {
|
||||
if (!match_str(st, s, len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
st->str += len;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define MATCH(ch) (st->str[0] == (ch))
|
||||
#define MATCHSTR_ADVANCE(s) (match_str_advance(st, s, sizeof(s) - 1))
|
||||
#define MATCH_XDIGIT() (isxdigit((unsigned char)(st->str[0])))
|
||||
#define ADVANCE() (st->str++)
|
||||
#define GETCH() (st->str[0])
|
||||
|
||||
static bool
|
||||
rule_tls_keylog_entry(keylog_parser_state_t *restrict st);
|
||||
|
||||
static bool
|
||||
rule_label(keylog_parser_state_t *restrict st);
|
||||
|
||||
static bool
|
||||
rule_client_random(keylog_parser_state_t *restrict st);
|
||||
|
||||
static bool
|
||||
rule_secret(keylog_parser_state_t *restrict st);
|
||||
|
||||
static bool
|
||||
rule_endline(keylog_parser_state_t *restrict st);
|
||||
|
||||
static bool
|
||||
rule_hex_byte(keylog_parser_state_t *restrict st, isc_buffer_t *buf);
|
||||
|
||||
static bool
|
||||
rule_tls_keylog_entry(keylog_parser_state_t *restrict st) {
|
||||
if (!rule_label(st)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MATCH(' ')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ADVANCE();
|
||||
|
||||
if (!rule_client_random(st)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MATCH(' ')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ADVANCE();
|
||||
|
||||
if (!rule_secret(st)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)rule_endline(st);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
rule_label(keylog_parser_state_t *restrict st) {
|
||||
isc__tls_keylog_label_t label = ISC__TLS_KL_ILLEGAL;
|
||||
|
||||
if (MATCHSTR_ADVANCE("CLIENT_")) {
|
||||
if (MATCHSTR_ADVANCE("EARLY_TRAFFIC_SECRET")) {
|
||||
label = ISC__TLS_KL_CLIENT_EARLY_TRAFFIC_SECRET;
|
||||
} else if (MATCHSTR_ADVANCE("HANDSHAKE_TRAFFIC_SECRET")) {
|
||||
label = ISC__TLS_KL_CLIENT_HANDSHAKE_TRAFFIC_SECRET;
|
||||
} else if (MATCHSTR_ADVANCE("RANDOM")) {
|
||||
label = ISC__TLS_KL_CLIENT_RANDOM;
|
||||
} else if (MATCHSTR_ADVANCE("TRAFFIC_SECRET_0")) {
|
||||
label = ISC__TLS_KL_CLIENT_TRAFFIC_SECRET_0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (MATCHSTR_ADVANCE("SERVER_")) {
|
||||
if (MATCHSTR_ADVANCE("HANDSHAKE_TRAFFIC_SECRET")) {
|
||||
label = ISC__TLS_KL_SERVER_HANDSHAKE_TRAFFIC_SECRET;
|
||||
} else if (MATCHSTR_ADVANCE("TRAFFIC_SECRET_0")) {
|
||||
label = ISC__TLS_KL_SERVER_TRAFFIC_SECRET_0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (MATCHSTR_ADVANCE("EARLY_EXPORTER_MASTER_SECRET")) {
|
||||
label = ISC__TLS_KL_EARLY_EXPORTER_MASTER_SECRET;
|
||||
} else if (MATCHSTR_ADVANCE("EXPORTER_SECRET")) {
|
||||
label = ISC__TLS_KL_EXPORTER_SECRET;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (st->out_label != NULL) {
|
||||
*st->out_label = label;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
rule_client_random(keylog_parser_state_t *restrict st) {
|
||||
for (size_t i = 0; i < CLIENT_RANDOM_LEN; i++) {
|
||||
if (!rule_hex_byte(st, st->out_client_random)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
rule_secret(keylog_parser_state_t *restrict st) {
|
||||
if (!rule_hex_byte(st, st->out_secret)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
bool ret = rule_hex_byte(st, st->out_secret);
|
||||
if (ret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (st->result != ISC_R_UNSET) {
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
rule_endline(keylog_parser_state_t *restrict st) {
|
||||
if (!(MATCH('\n') || MATCH('\r'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ADVANCE();
|
||||
|
||||
while (MATCH('\n') || MATCH('\r')) {
|
||||
ADVANCE();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
hex_subchar_val(const char ch) {
|
||||
uint8_t subval = 0;
|
||||
|
||||
if (ch >= '0' && ch <= '9') {
|
||||
subval = ch - '0';
|
||||
} else {
|
||||
subval = tolower(ch) - 'a' + 10;
|
||||
}
|
||||
|
||||
return subval;
|
||||
}
|
||||
|
||||
static inline uint8_t
|
||||
hex_chars_val(const char *restrict str) {
|
||||
uint8_t value = 0;
|
||||
|
||||
value = (hex_subchar_val(str[0]) << 4);
|
||||
value += hex_subchar_val(str[1]);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool
|
||||
rule_hex_byte(keylog_parser_state_t *restrict st, isc_buffer_t *buf) {
|
||||
char byte_str[3];
|
||||
|
||||
if (!MATCH_XDIGIT()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte_str[0] = GETCH();
|
||||
ADVANCE();
|
||||
|
||||
if (!MATCH_XDIGIT()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
byte_str[1] = GETCH();
|
||||
ADVANCE();
|
||||
|
||||
byte_str[2] = '\0';
|
||||
|
||||
if (buf != NULL) {
|
||||
const uint8_t byte = hex_chars_val(byte_str);
|
||||
const isc_result_t result = isc_buffer_reserve(buf, 1);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
st->result = result;
|
||||
return false;
|
||||
}
|
||||
isc_buffer_putuint8(buf, byte);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc__tls_parse_keylog_entry(const char *restrict line,
|
||||
isc__tls_keylog_label_t *restrict out_label,
|
||||
isc_buffer_t *restrict out_client_random,
|
||||
isc_buffer_t *restrict out_secret) {
|
||||
keylog_parser_state_t state;
|
||||
bool ret;
|
||||
|
||||
REQUIRE(line != NULL);
|
||||
|
||||
state = (keylog_parser_state_t){ .str = line,
|
||||
.out_label = out_label,
|
||||
.out_client_random = out_client_random,
|
||||
.out_secret = out_secret,
|
||||
.result = ISC_R_UNSET };
|
||||
|
||||
ret = rule_tls_keylog_entry(&state);
|
||||
if (!ret && state.result != ISC_R_UNSET) {
|
||||
return state.result;
|
||||
} else if (!ret) {
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
414
lib/isc/quic/tls_quic.c
Normal file
414
lib/isc/quic/tls_quic.c
Normal file
@@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
/*
|
||||
* QUIC-related TLS functionality
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <isc/tls.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "../openssl_shim.h"
|
||||
#include "quic-int.h"
|
||||
#include "quic_crypto.h"
|
||||
|
||||
static bool tls_quic_initialized = false;
|
||||
|
||||
static int tlsctx_quic_interface_index = 0;
|
||||
static int tls_quic_interface_index = 0;
|
||||
static int tls_quic_data_index = 0;
|
||||
static int tls_quic_app_data_index = 0;
|
||||
static int tls_quic_keylog_cb_index = 0;
|
||||
|
||||
static isc_mem_t *tls__quic_mctx = NULL;
|
||||
|
||||
void
|
||||
isc__tls_quic_initialize(void) {
|
||||
if (tls_quic_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
tlsctx_quic_interface_index = CRYPTO_get_ex_new_index(
|
||||
CRYPTO_EX_INDEX_SSL_CTX, 0, NULL, NULL, NULL, NULL);
|
||||
tls_quic_interface_index = CRYPTO_get_ex_new_index(
|
||||
CRYPTO_EX_INDEX_SSL, 0, NULL, NULL, NULL, NULL);
|
||||
tls_quic_data_index = CRYPTO_get_ex_new_index(CRYPTO_EX_INDEX_SSL, 0,
|
||||
NULL, NULL, NULL, NULL);
|
||||
tls_quic_app_data_index = CRYPTO_get_ex_new_index(
|
||||
CRYPTO_EX_INDEX_SSL, 0, NULL, NULL, NULL, NULL);
|
||||
tls_quic_keylog_cb_index = CRYPTO_get_ex_new_index(
|
||||
CRYPTO_EX_INDEX_SSL, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
isc_mem_create(&tls__quic_mctx);
|
||||
isc_mem_setname(tls__quic_mctx, "QUIC TLS");
|
||||
isc_mem_setdestroycheck(tls__quic_mctx, false);
|
||||
|
||||
tls_quic_initialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
isc__tls_quic_shutdown(void) {
|
||||
if (!tls_quic_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CRYPTO_FREE_EX_INDEX
|
||||
RUNTIME_CHECK(CRYPTO_free_ex_index(CRYPTO_EX_INDEX_SSL,
|
||||
tls_quic_keylog_cb_index) == 1);
|
||||
RUNTIME_CHECK(CRYPTO_free_ex_index(CRYPTO_EX_INDEX_SSL,
|
||||
tls_quic_app_data_index) == 1);
|
||||
RUNTIME_CHECK(CRYPTO_free_ex_index(CRYPTO_EX_INDEX_SSL,
|
||||
tls_quic_data_index) == 1);
|
||||
RUNTIME_CHECK(CRYPTO_free_ex_index(CRYPTO_EX_INDEX_SSL,
|
||||
tls_quic_interface_index) == 1);
|
||||
RUNTIME_CHECK(CRYPTO_free_ex_index(CRYPTO_EX_INDEX_SSL,
|
||||
tlsctx_quic_interface_index) == 1);
|
||||
tlsctx_quic_interface_index = 0;
|
||||
tls_quic_interface_index = 0;
|
||||
tls_quic_data_index = 0;
|
||||
tls_quic_app_data_index = 0;
|
||||
tls_quic_keylog_cb_index = 0;
|
||||
#endif /* HAVE_CRYPTO_FREE_EX_INDEX */
|
||||
|
||||
if (tls__quic_mctx != NULL) {
|
||||
isc_mem_destroy(&tls__quic_mctx);
|
||||
}
|
||||
|
||||
tls_quic_initialized = false;
|
||||
}
|
||||
|
||||
void
|
||||
isc_tls_quic_crypto_initialize(void) {
|
||||
isc__quic_crypto_initialize();
|
||||
}
|
||||
|
||||
void
|
||||
isc_tls_quic_crypto_shutdown(void) {
|
||||
isc__quic_crypto_shutdown();
|
||||
}
|
||||
|
||||
const char *
|
||||
isc_tls_quic_encryption_level_text(const isc_quic_encryption_level_t level) {
|
||||
switch (level) {
|
||||
case ISC_QUIC_ENCRYPTION_INITIAL:
|
||||
return "initial";
|
||||
case ISC_QUIC_ENCRYPTION_EARLY_DATA:
|
||||
return "early data";
|
||||
case ISC_QUIC_ENCRYPTION_HANDSHAKE:
|
||||
return "handshake";
|
||||
case ISC_QUIC_ENCRYPTION_APPLICATION:
|
||||
return "application";
|
||||
};
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const isc_tls_quic_interface_t *
|
||||
isc_tls_get_default_quic_interface(void) {
|
||||
#ifdef HAVE_NATIVE_BORINGSSL_QUIC_API
|
||||
return isc__tls_get_native_quic_interface();
|
||||
#endif /* HAVE_NATIVE_BORINGSSL_QUIC_API */
|
||||
|
||||
#ifndef HAVE_LIBRESSL
|
||||
return isc__tls_get_compat_quic_interface();
|
||||
#endif /* HAVE_LIBRESSL */
|
||||
|
||||
/* Unexpected - we need to investigate. */
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static isc_tls_quic_interface_t *
|
||||
get_tls_quic_interface(const isc_tls_t *tls) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
quicif = SSL_get_ex_data(tls, tls_quic_interface_index);
|
||||
|
||||
RUNTIME_CHECK(quicif != NULL);
|
||||
|
||||
return quicif;
|
||||
}
|
||||
|
||||
void *
|
||||
isc__tls_get_quic_data(const isc_tls_t *tls) {
|
||||
return SSL_get_ex_data(tls, tls_quic_data_index);
|
||||
}
|
||||
|
||||
void
|
||||
isc__tls_set_quic_data(isc_tls_t *tls, void *data) {
|
||||
int ret = SSL_set_ex_data(tls, tls_quic_data_index, data);
|
||||
RUNTIME_CHECK(ret == 1);
|
||||
}
|
||||
|
||||
/* Will not be called on LibreSSL */
|
||||
static void
|
||||
quic_keylog_callback(const isc_tls_t *tls, const char *line) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
isc_tls_keylog_cb_t cb = NULL;
|
||||
|
||||
INSIST(tls != NULL);
|
||||
INSIST(line != NULL && *line != '\0');
|
||||
|
||||
isc_tls_sslkeylogfile_append(line);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
if (quicif->tlsctx_keylog_callback != NULL) {
|
||||
quicif->tlsctx_keylog_callback(tls, line);
|
||||
}
|
||||
|
||||
cb = SSL_get_ex_data(tls, tls_quic_keylog_cb_index);
|
||||
if (cb != NULL) {
|
||||
cb(tls, line);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_tlsctx_quic_configure(isc_tlsctx_t *tlsctx,
|
||||
const isc_tls_quic_interface_t *quic_interface) {
|
||||
REQUIRE(tlsctx != NULL);
|
||||
REQUIRE(quic_interface != NULL);
|
||||
|
||||
/* QUIC uses TLSv1.3 and newer */
|
||||
#ifdef TLS1_3_VERSION
|
||||
SSL_CTX_set_min_proto_version(tlsctx, TLS1_3_VERSION);
|
||||
#else
|
||||
/* Everything older than TLSv1.2 is disabled by default */
|
||||
SSL_CTX_set_options(tlsctx, SSL_OP_NO_TLSv1_2);
|
||||
#endif
|
||||
|
||||
#ifdef TLS1_3_VERSION
|
||||
SSL_CTX_set_max_proto_version(tlsctx, TLS1_3_VERSION);
|
||||
#endif
|
||||
|
||||
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
|
||||
/*
|
||||
* Disable middle-box compatibility mode for QUIC, as it makes no sense
|
||||
* to use it in that case. See RFC9001, Section 8.4.
|
||||
*/
|
||||
SSL_CTX_clear_options(tlsctx, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
|
||||
#endif
|
||||
|
||||
INSIST(SSL_CTX_get_ex_data(tlsctx, tlsctx_quic_interface_index) ==
|
||||
NULL);
|
||||
RUNTIME_CHECK(SSL_CTX_set_ex_data(tlsctx, tlsctx_quic_interface_index,
|
||||
(void *)quic_interface) == 1);
|
||||
|
||||
SSL_CTX_set_keylog_callback(tlsctx, quic_keylog_callback);
|
||||
quic_interface->tlsctx_configure(tlsctx);
|
||||
}
|
||||
|
||||
void
|
||||
isc__tls_quic_init(isc_tls_t *tls) {
|
||||
int ret = 0;
|
||||
isc_tlsctx_t *tlsctx = NULL;
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
tlsctx = SSL_get_SSL_CTX(tls);
|
||||
INSIST(tlsctx != NULL);
|
||||
|
||||
quicif = SSL_CTX_get_ex_data(tlsctx, tlsctx_quic_interface_index);
|
||||
RUNTIME_CHECK(quicif != NULL);
|
||||
|
||||
ret = SSL_set_ex_data(tls, tls_quic_interface_index, quicif);
|
||||
RUNTIME_CHECK(ret == 1);
|
||||
|
||||
quicif->tls_init(tls, tls__quic_mctx);
|
||||
}
|
||||
|
||||
void
|
||||
isc__tls_quic_uninit(isc_tls_t *tls) {
|
||||
int ret = 0;
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
quicif->tls_uninit(tls);
|
||||
|
||||
ret = SSL_set_ex_data(tls, tls_quic_interface_index, NULL);
|
||||
RUNTIME_CHECK(ret == 1);
|
||||
}
|
||||
|
||||
bool
|
||||
isc__tls_is_quic(isc_tls_t *tls) {
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
return SSL_get_ex_data(tls, tls_quic_interface_index) != NULL;
|
||||
}
|
||||
|
||||
void
|
||||
isc_tls_quic_set_app_data(isc_tls_t *tls, void *app_data) {
|
||||
int ret = 0;
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
ret = SSL_set_ex_data(tls, tls_quic_app_data_index, app_data);
|
||||
RUNTIME_CHECK(ret == 1);
|
||||
}
|
||||
|
||||
void *
|
||||
isc_tls_quic_get_app_data(isc_tls_t *tls) {
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
return SSL_get_ex_data(tls, tls_quic_app_data_index);
|
||||
}
|
||||
|
||||
void
|
||||
isc_tls_quic_set_keylog_callback(isc_tls_t *tls, isc_tls_keylog_cb_t cb) {
|
||||
int ret = 0;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
ret = SSL_set_ex_data(tls, tls_quic_keylog_cb_index, cb);
|
||||
|
||||
RUNTIME_CHECK(ret == 1);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_tls_set_quic_method(isc_tls_t *tls, const isc_tls_quic_method_t *method) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
int ret = 0;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
REQUIRE(method != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
RUNTIME_CHECK(!quicif->tls_calling_method_cb(tls));
|
||||
|
||||
INSIST(method->add_handshake_data != NULL);
|
||||
INSIST(method->send_alert != NULL);
|
||||
INSIST(method->set_read_secret != NULL);
|
||||
INSIST(method->set_write_secret != NULL);
|
||||
|
||||
ret = quicif->tls_set_quic_method(tls, method);
|
||||
if (ret == 0) {
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_tls_provide_quic_data(isc_tls_t *tls,
|
||||
const isc_quic_encryption_level_t level,
|
||||
const uint8_t *data, const size_t len) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
int ret = 0;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
REQUIRE(len == 0 || data != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
RUNTIME_CHECK(!quicif->tls_calling_method_cb(tls));
|
||||
|
||||
ret = quicif->tls_provide_quic_data(tls, level, data, len);
|
||||
if (ret == 0) {
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
isc_tls_do_quic_handshake(isc_tls_t *tls) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
RUNTIME_CHECK(!quicif->tls_calling_method_cb(tls));
|
||||
|
||||
return quicif->tls_do_quic_handshake(tls);
|
||||
}
|
||||
|
||||
int
|
||||
isc_tls_process_quic_post_handshake(isc_tls_t *tls) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
RUNTIME_CHECK(!quicif->tls_calling_method_cb(tls));
|
||||
|
||||
return quicif->tls_process_quic_post_handshake(tls);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_tls_set_quic_transport_params(isc_tls_t *tls, const uint8_t *params,
|
||||
const size_t params_len) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
int ret = 0;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
REQUIRE(params != NULL);
|
||||
REQUIRE(params_len > 0);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
ret = quicif->tls_set_quic_transport_params(tls, params, params_len);
|
||||
if (ret == 0) {
|
||||
return ISC_R_FAILURE;
|
||||
}
|
||||
|
||||
return ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
void
|
||||
isc_tls_get_peer_quic_transport_params(isc_tls_t *tls,
|
||||
const uint8_t **out_params,
|
||||
size_t *out_params_len) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
REQUIRE(out_params != NULL && *out_params == NULL);
|
||||
REQUIRE(out_params_len != NULL && *out_params_len == 0);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
quicif->tls_get_peer_quic_transport_params(tls, out_params,
|
||||
out_params_len);
|
||||
}
|
||||
|
||||
isc_quic_encryption_level_t
|
||||
isc_tls_quic_read_level(const isc_tls_t *tls) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
RUNTIME_CHECK(!quicif->tls_calling_method_cb(tls));
|
||||
|
||||
return quicif->tls_quic_read_level(tls);
|
||||
}
|
||||
|
||||
isc_quic_encryption_level_t
|
||||
isc_tls_quic_write_level(const isc_tls_t *tls) {
|
||||
isc_tls_quic_interface_t *quicif = NULL;
|
||||
|
||||
REQUIRE(tls != NULL);
|
||||
|
||||
quicif = get_tls_quic_interface(tls);
|
||||
|
||||
RUNTIME_CHECK(!quicif->tls_calling_method_cb(tls));
|
||||
|
||||
return quicif->tls_quic_write_level(tls);
|
||||
}
|
||||
@@ -51,9 +51,15 @@
|
||||
|
||||
#include "openssl_shim.h"
|
||||
|
||||
#ifdef HAVE_LIBNGTCP2
|
||||
#include "quic/quic-int.h"
|
||||
#endif /* HAVE_LIBNGTCP2 */
|
||||
|
||||
#define COMMON_SSL_OPTIONS \
|
||||
(SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)
|
||||
|
||||
static bool sslkeylogfile_enabled = false;
|
||||
|
||||
void
|
||||
isc_tlsctx_free(isc_tlsctx_t **ctxp) {
|
||||
SSL_CTX *ctx = NULL;
|
||||
@@ -75,14 +81,32 @@ isc_tlsctx_attach(isc_tlsctx_t *src, isc_tlsctx_t **ptarget) {
|
||||
*ptarget = src;
|
||||
}
|
||||
|
||||
void
|
||||
isc__sslkeylog_init(void) __attribute__((__constructor__));
|
||||
|
||||
void
|
||||
isc__sslkeylog_init(void) {
|
||||
if (getenv("SSLKEYLOGFILE") != NULL) {
|
||||
sslkeylogfile_enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback invoked by the SSL library whenever a new TLS pre-master secret
|
||||
* needs to be logged.
|
||||
*/
|
||||
void
|
||||
isc_tls_sslkeylogfile_append(const char *line) {
|
||||
if (sslkeylogfile_enabled) {
|
||||
isc_log_write(ISC_LOGCATEGORY_SSLKEYLOG, ISC_LOGMODULE_CRYPTO,
|
||||
ISC_LOG_INFO, "%s", line);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sslkeylogfile_append(const SSL *ssl ISC_ATTR_UNUSED, const char *line) {
|
||||
isc_log_write(ISC_LOGCATEGORY_SSLKEYLOG, ISC_LOGMODULE_CRYPTO,
|
||||
ISC_LOG_INFO, "%s", line);
|
||||
UNUSED(ssl);
|
||||
isc_tls_sslkeylogfile_append(line);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -92,7 +116,7 @@ sslkeylogfile_append(const SSL *ssl ISC_ATTR_UNUSED, const char *line) {
|
||||
*/
|
||||
static void
|
||||
sslkeylogfile_init(isc_tlsctx_t *ctx) {
|
||||
if (getenv("SSLKEYLOGFILE") != NULL) {
|
||||
if (sslkeylogfile_enabled) {
|
||||
SSL_CTX_set_keylog_callback(ctx, sslkeylogfile_append);
|
||||
}
|
||||
}
|
||||
@@ -649,12 +673,36 @@ isc_tls_create(isc_tlsctx_t *ctx) {
|
||||
return newctx;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBNGTCP2
|
||||
isc_tls_t *
|
||||
isc_tls_create_quic(isc_tlsctx_t *ctx) {
|
||||
isc_tls_t *newtls = NULL;
|
||||
|
||||
REQUIRE(ctx != NULL);
|
||||
|
||||
newtls = isc_tls_create(ctx);
|
||||
|
||||
if (newtls != NULL) {
|
||||
isc__tls_quic_init(newtls);
|
||||
}
|
||||
|
||||
return newtls;
|
||||
}
|
||||
#endif /* HAVE_LIBNGTCP2 */
|
||||
|
||||
void
|
||||
isc_tls_free(isc_tls_t **tlsp) {
|
||||
isc_tls_t *tls = NULL;
|
||||
REQUIRE(tlsp != NULL && *tlsp != NULL);
|
||||
|
||||
tls = *tlsp;
|
||||
|
||||
#ifdef HAVE_LIBNGTCP2
|
||||
if (isc__tls_is_quic(tls)) {
|
||||
isc__tls_quic_uninit(tls);
|
||||
}
|
||||
#endif /* HAVE_LIBNGTCP2 */
|
||||
|
||||
*tlsp = NULL;
|
||||
SSL_free(tls);
|
||||
}
|
||||
|
||||
@@ -145,6 +145,51 @@ proxyudp_test_SOURCES = \
|
||||
netmgr_common.c \
|
||||
uv_wrap.h
|
||||
|
||||
if HAVE_LIBNGTCP2
|
||||
|
||||
check_PROGRAMS += \
|
||||
ngtcp2_integration_test \
|
||||
quic_session_test \
|
||||
quic_tls_test
|
||||
|
||||
ngtcp2_integration_test_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(LIBNGTCP2_CFLAGS) \
|
||||
$(OPENSSL_CFLAGS)
|
||||
|
||||
ngtcp2_integration_test_LDADD = \
|
||||
$(LDADD) \
|
||||
$(LIBNGTCP2_LIBS)
|
||||
|
||||
ngtcp2_integration_test_SOURCES = \
|
||||
ngtcp2_integration_test.c
|
||||
|
||||
quic_session_test_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(LIBNGTCP2_CFLAGS) \
|
||||
$(OPENSSL_CFLAGS)
|
||||
|
||||
quic_session_test_LDADD = \
|
||||
$(LDADD) \
|
||||
$(LIBNGTCP2_LIBS)
|
||||
|
||||
quic_session_test_SOURCES = \
|
||||
quic_session_test.c
|
||||
|
||||
quic_tls_test_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) \
|
||||
$(LIBNGTCP2_CFLAGS) \
|
||||
$(OPENSSL_CFLAGS)
|
||||
|
||||
quic_tls_test_LDADD = \
|
||||
$(LDADD) \
|
||||
$(LIBNGTCP2_LIBS)
|
||||
|
||||
quic_tls_test_SOURCES = \
|
||||
quic_tls_test.c
|
||||
|
||||
endif HAVE_LIBNGTCP2
|
||||
|
||||
random_test_LDADD = \
|
||||
$(LDADD) \
|
||||
-lm
|
||||
|
||||
2220
tests/isc/ngtcp2_integration_test.c
Normal file
2220
tests/isc/ngtcp2_integration_test.c
Normal file
File diff suppressed because it is too large
Load Diff
688
tests/isc/quic_session_test.c
Normal file
688
tests/isc/quic_session_test.c
Normal file
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <ngtcp2/ngtcp2.h>
|
||||
#include <sched.h> /* IWYU pragma: keep */
|
||||
#include <setjmp.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#define UNIT_TESTING
|
||||
#include <cmocka.h>
|
||||
|
||||
#include <isc/buffer.h>
|
||||
#include <isc/entropy.h>
|
||||
#include <isc/lib.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/ngtcp2_crypto.h>
|
||||
#include <isc/ngtcp2_utils.h>
|
||||
#include <isc/os.h>
|
||||
#include <isc/quic.h>
|
||||
#include <isc/random.h>
|
||||
#include <isc/time.h>
|
||||
#include <isc/tls.h>
|
||||
|
||||
#include "../../lib/isc/quic/quic_session.h"
|
||||
|
||||
#include <tests/isc.h>
|
||||
|
||||
/* the unit test infrastructure */
|
||||
|
||||
#define TEST_SERVER_PORT (9153)
|
||||
#define TEST_CLIENT_PORT (9154)
|
||||
|
||||
#define INITIAL_TIMEOUT (isc_ngtcp2_make_duration(15, 0))
|
||||
|
||||
static isc_tlsctx_t *default_server_tlsctx = NULL;
|
||||
static isc_tlsctx_t *default_client_tlsctx = NULL;
|
||||
|
||||
static isc_tlsctx_t *server_tlsctx = NULL;
|
||||
static isc_tlsctx_t *client_tlsctx = NULL;
|
||||
|
||||
static isc_sockaddr_t server_addr = { 0 };
|
||||
static isc_sockaddr_t client_addr = { 0 };
|
||||
static isc_sockaddr_t migrate_client_addr = { 0 };
|
||||
|
||||
static isc_quic_session_t *client_session = NULL;
|
||||
static isc_quic_session_t *server_session = NULL;
|
||||
|
||||
static const uint32_t proto_preference_list[] = { NGTCP2_PROTO_VER_V2,
|
||||
NGTCP2_PROTO_VER_V1 };
|
||||
static const size_t proto_preference_list_len =
|
||||
(sizeof(proto_preference_list) / sizeof(proto_preference_list[0]));
|
||||
|
||||
static uint8_t client_static_secret[ISC_NGTCP2_CRYPTO_STATIC_SECRET_LEN];
|
||||
static uint8_t server_static_secret[ISC_NGTCP2_CRYPTO_STATIC_SECRET_LEN];
|
||||
|
||||
static const uint8_t data_ping[] = { 'P', 'I', 'N', 'G' };
|
||||
/* static const uint8_t data_pong[] = { 'P', 'O', 'N', 'G' }; */
|
||||
|
||||
typedef struct quic_test_session_manager {
|
||||
uint64_t ts;
|
||||
bool ts_set;
|
||||
uint64_t last_timer_update;
|
||||
|
||||
bool hs_completed;
|
||||
|
||||
ISC_LIST(isc_quic_cid_t) managed_cids;
|
||||
size_t managed_cids_len;
|
||||
|
||||
size_t opened_streams;
|
||||
int64_t last_stream_id;
|
||||
|
||||
bool closed;
|
||||
uint32_t closing_timeout_ms;
|
||||
|
||||
size_t sends;
|
||||
|
||||
isc_buffer_t *input;
|
||||
isc_buffer_t *output;
|
||||
|
||||
} quic_test_session_manager_t;
|
||||
|
||||
static quic_test_session_manager_t client_sm;
|
||||
static quic_test_session_manager_t server_sm;
|
||||
|
||||
static void
|
||||
quic_sm_init(quic_test_session_manager_t *sm) {
|
||||
*sm = (quic_test_session_manager_t){
|
||||
.ts = isc_time_monotonic(),
|
||||
.managed_cids = ISC_LIST_INITIALIZER,
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
quic_sm_uninit(quic_test_session_manager_t *sm) {
|
||||
if (!ISC_LIST_EMPTY(sm->managed_cids)) {
|
||||
isc_quic_cid_t *current = NULL, *next = NULL;
|
||||
for (current = ISC_LIST_HEAD(sm->managed_cids); current != NULL;
|
||||
current = next)
|
||||
{
|
||||
next = ISC_LIST_NEXT(current, global_link);
|
||||
|
||||
isc_quic_cid_detach(¤t);
|
||||
sm->managed_cids_len--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
quic_sm_get_current_ts_cb(quic_test_session_manager_t *sm) {
|
||||
if (sm->ts_set) {
|
||||
sm->ts_set = false;
|
||||
} else {
|
||||
sm->ts += isc_ngtcp2_make_duration(0,
|
||||
4 + isc_random_uniform(12));
|
||||
}
|
||||
|
||||
return sm->ts;
|
||||
}
|
||||
|
||||
static void
|
||||
quic_sm_set_next_ts(quic_test_session_manager_t *sm, uint64_t ts) {
|
||||
sm->ts = ts;
|
||||
sm->ts_set = true;
|
||||
}
|
||||
|
||||
static void
|
||||
quic_sm_timer_update_cb(isc_quic_session_t *restrict session,
|
||||
const uint32_t timeout_ms,
|
||||
quic_test_session_manager_t *sm) {
|
||||
UNUSED(session);
|
||||
if (timeout_ms != 0) {
|
||||
printf("new_timeout: %" PRIu32 "\n", timeout_ms);
|
||||
}
|
||||
sm->last_timer_update = isc_ngtcp2_make_duration(0, timeout_ms);
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_sm_assoc_conn_cid_cb(isc_quic_session_t *restrict session,
|
||||
isc_region_t *restrict cid_data, const bool source,
|
||||
quic_test_session_manager_t *sm,
|
||||
isc_quic_cid_t **restrict pcid);
|
||||
|
||||
static bool
|
||||
quic_sm_gen_unique_cid_cb(isc_quic_session_t *restrict session,
|
||||
const size_t cidlen, const bool source,
|
||||
quic_test_session_manager_t *sm,
|
||||
isc_quic_cid_t **restrict pcid) {
|
||||
ngtcp2_cid ngcid;
|
||||
isc_region_t cid_data = { 0 };
|
||||
|
||||
isc_ngtcp2_gen_cid(&ngcid, cidlen);
|
||||
isc_ngtcp2_cid_region(&ngcid, &cid_data);
|
||||
|
||||
const bool ret = quic_sm_assoc_conn_cid_cb(session, &cid_data, source,
|
||||
sm, pcid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_sm_on_handshake_cb(isc_quic_session_t *session,
|
||||
quic_test_session_manager_t *sm) {
|
||||
UNUSED(session);
|
||||
|
||||
puts("hs");
|
||||
sm->hs_completed = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_sm_on_on_remote_stream_open_cb(isc_quic_session_t *session,
|
||||
const int64_t streamd_id,
|
||||
quic_test_session_manager_t *sm) {
|
||||
sm->last_stream_id = streamd_id;
|
||||
sm->opened_streams++;
|
||||
|
||||
isc_quic_session_set_stream_user_data(session, streamd_id, sm);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_sm_on_stream_close_cb(isc_quic_session_t *session,
|
||||
const int64_t streamd_id, const bool app_error_set,
|
||||
const uint64_t app_error_code,
|
||||
quic_test_session_manager_t *sm,
|
||||
void *stream_user_data) {
|
||||
UNUSED(app_error_set);
|
||||
UNUSED(app_error_code);
|
||||
|
||||
INSIST(isc_quic_session_get_stream_user_data(session, streamd_id) ==
|
||||
stream_user_data);
|
||||
INSIST(isc_quic_session_get_stream_user_data(session, streamd_id) ==
|
||||
sm);
|
||||
|
||||
sm->opened_streams--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_sm_on_recv_stream_data_cb(isc_quic_session_t *session,
|
||||
const int64_t streamd_id, const bool fin,
|
||||
const uint64_t offset,
|
||||
const isc_region_t *restrict data,
|
||||
quic_test_session_manager_t *sm,
|
||||
void *stream_user_data) {
|
||||
UNUSED(offset);
|
||||
UNUSED(data);
|
||||
|
||||
INSIST(isc_quic_session_get_stream_user_data(session, streamd_id) ==
|
||||
stream_user_data);
|
||||
INSIST(isc_quic_session_get_stream_user_data(session, streamd_id) ==
|
||||
sm);
|
||||
|
||||
if (fin) {
|
||||
isc_quic_session_shutdown_stream(session, streamd_id, true);
|
||||
}
|
||||
|
||||
sm->sends++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
quic_sm_on_conn_close_cb(isc_quic_session_t *session,
|
||||
const uint32_t closing_timeout_ms,
|
||||
quic_test_session_manager_t *sm) {
|
||||
UNUSED(session);
|
||||
|
||||
sm->closed = true;
|
||||
sm->closing_timeout_ms = closing_timeout_ms;
|
||||
}
|
||||
|
||||
static bool
|
||||
quic_sm_assoc_conn_cid_cb(isc_quic_session_t *restrict session,
|
||||
isc_region_t *restrict cid_data, const bool source,
|
||||
quic_test_session_manager_t *sm,
|
||||
isc_quic_cid_t **restrict pcid) {
|
||||
isc_quic_cid_t *new_cid = NULL;
|
||||
|
||||
UNUSED(session);
|
||||
|
||||
isc_quic_cid_create(mctx, cid_data, source, &new_cid);
|
||||
|
||||
ISC_LIST_APPEND(sm->managed_cids, new_cid, global_link);
|
||||
sm->managed_cids_len++;
|
||||
|
||||
isc_quic_cid_attach(new_cid, pcid);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
quic_sm_deassoc_conn_cid(isc_quic_session_t *restrict session,
|
||||
quic_test_session_manager_t *sm,
|
||||
isc_quic_cid_t **restrict pcid) {
|
||||
isc_quic_cid_t *cid = NULL;
|
||||
UNUSED(session);
|
||||
|
||||
cid = *pcid;
|
||||
|
||||
if (!ISC_LINK_LINKED(cid, global_link)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ISC_LIST_UNLINK(sm->managed_cids, cid, global_link);
|
||||
|
||||
isc_quic_cid_detach(&cid);
|
||||
|
||||
isc_quic_cid_detach(pcid);
|
||||
}
|
||||
|
||||
static void
|
||||
quic_sm_send_cb(isc_quic_session_t *restrict session, const int64_t stream_id,
|
||||
const isc_result_t result, void *cbarg,
|
||||
quic_test_session_manager_t *sm) {
|
||||
REQUIRE(session != NULL);
|
||||
REQUIRE(result == ISC_R_SUCCESS);
|
||||
REQUIRE(cbarg == sm);
|
||||
REQUIRE(sm->last_stream_id == stream_id);
|
||||
}
|
||||
|
||||
static isc_quic_session_interface_t callbacks = {
|
||||
.get_current_ts =
|
||||
(isc_quic_get_current_ts_cb_t)quic_sm_get_current_ts_cb,
|
||||
.timer_update = (isc_quic_timer_update_cb_t)quic_sm_timer_update_cb,
|
||||
|
||||
.gen_unique_cid =
|
||||
(isc_quic_gen_unique_cid_cb_t)quic_sm_gen_unique_cid_cb,
|
||||
.assoc_conn_cid =
|
||||
(isc_quic_assoc_conn_cid_cb_t)quic_sm_assoc_conn_cid_cb,
|
||||
.deassoc_conn_cid =
|
||||
(isc_quic_deassoc_conn_cid_cb_t)quic_sm_deassoc_conn_cid,
|
||||
|
||||
.on_handshake = (isc_quic_on_handshake_cb_t)quic_sm_on_handshake_cb,
|
||||
.on_remote_stream_open = (isc_quic_on_remote_stream_open_cb_t)
|
||||
quic_sm_on_on_remote_stream_open_cb,
|
||||
.on_stream_close =
|
||||
(isc_quic_on_stream_close_cb_t)quic_sm_on_stream_close_cb,
|
||||
.on_recv_stream_data = (isc_quic_on_recv_stream_data_cb_t)
|
||||
quic_sm_on_recv_stream_data_cb,
|
||||
.on_conn_close = (isc_quic_on_conn_close_cb_t)quic_sm_on_conn_close_cb
|
||||
};
|
||||
|
||||
static isc_tlsctx_t *
|
||||
create_quic_tls_context(const bool is_server,
|
||||
const isc_tls_quic_interface_t *iface) {
|
||||
isc_tlsctx_t *tlsctx = NULL;
|
||||
isc_result_t result;
|
||||
|
||||
if (is_server) {
|
||||
result = isc_tlsctx_createserver(NULL, NULL, &tlsctx);
|
||||
} else {
|
||||
result = isc_tlsctx_createclient(&tlsctx);
|
||||
}
|
||||
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
isc_tlsctx_set_random_session_id_context(tlsctx);
|
||||
isc_tlsctx_quic_configure(tlsctx, iface);
|
||||
|
||||
return tlsctx;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_session_testset_setup(void **state) {
|
||||
int ret;
|
||||
struct in6_addr in6;
|
||||
|
||||
UNUSED(state);
|
||||
|
||||
isc_tls_quic_crypto_initialize();
|
||||
|
||||
default_server_tlsctx = create_quic_tls_context(
|
||||
true, isc_tls_get_default_quic_interface());
|
||||
|
||||
if (default_server_tlsctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
default_client_tlsctx = create_quic_tls_context(
|
||||
false, isc_tls_get_default_quic_interface());
|
||||
|
||||
if (default_client_tlsctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
server_tlsctx = default_server_tlsctx;
|
||||
client_tlsctx = default_client_tlsctx;
|
||||
|
||||
isc_sockaddr_fromin6(&server_addr, &in6addr_loopback, TEST_SERVER_PORT);
|
||||
|
||||
isc_sockaddr_fromin6(&client_addr, &in6addr_loopback, TEST_CLIENT_PORT);
|
||||
|
||||
ret = inet_pton(AF_INET6, "2a03:dead:beef:34b5:7a18:f3c7:57dc:9ee1",
|
||||
&in6);
|
||||
if (ret != 1) {
|
||||
return -1;
|
||||
}
|
||||
isc_sockaddr_fromin6(&migrate_client_addr, &in6, 1);
|
||||
|
||||
isc_entropy_get((void *)server_static_secret,
|
||||
sizeof(server_static_secret));
|
||||
isc_entropy_get((void *)client_static_secret,
|
||||
sizeof(client_static_secret));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_session_testset_teardown(void **state) {
|
||||
UNUSED(state);
|
||||
|
||||
isc_tlsctx_free(&default_client_tlsctx);
|
||||
isc_tlsctx_free(&default_server_tlsctx);
|
||||
|
||||
isc_tls_quic_crypto_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_session_test_setup(void **state) {
|
||||
UNUSED(state);
|
||||
|
||||
quic_sm_init(&server_sm);
|
||||
|
||||
isc_quic_session_create(
|
||||
mctx, default_server_tlsctx, &callbacks, &server_sm,
|
||||
&server_addr, &client_addr, INITIAL_TIMEOUT, INITIAL_TIMEOUT,
|
||||
UINT16_MAX, UINT16_MAX, 0, proto_preference_list,
|
||||
proto_preference_list_len, server_static_secret,
|
||||
sizeof(server_static_secret), true, &server_session);
|
||||
|
||||
quic_sm_init(&client_sm);
|
||||
|
||||
isc_quic_session_create(
|
||||
mctx, default_client_tlsctx, &callbacks, &client_sm,
|
||||
&client_addr, &server_addr, INITIAL_TIMEOUT, INITIAL_TIMEOUT,
|
||||
UINT16_MAX, UINT16_MAX, NGTCP2_PROTO_VER_V1,
|
||||
proto_preference_list, proto_preference_list_len,
|
||||
client_static_secret, sizeof(client_static_secret), false,
|
||||
&client_session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
quic_session_test_teardown(void **state) {
|
||||
UNUSED(state);
|
||||
|
||||
if (client_session != NULL) {
|
||||
isc_quic_session_detach(&client_session);
|
||||
}
|
||||
|
||||
quic_sm_uninit(&client_sm);
|
||||
|
||||
if (server_session != NULL) {
|
||||
isc_quic_session_detach(&server_session);
|
||||
}
|
||||
|
||||
quic_sm_uninit(&server_sm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* tests */
|
||||
|
||||
ISC_RUN_TEST_IMPL(quic_session_simple_test) {
|
||||
ssize_t written = 0;
|
||||
uint8_t pkt_client[1500];
|
||||
uint8_t pkt_server[1500];
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
isc_region_t scid = { 0 }, dcid = { 0 };
|
||||
uint32_t version = 0;
|
||||
bool is_long = false;
|
||||
isc_region_t pkt_reg = { 0 }, send_data = { 0 };
|
||||
|
||||
result = isc_quic_session_connect(client_session, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 1200);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_client, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_server, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_server, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(client_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_client, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_server, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_server, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(client_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_server, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_server, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_false(is_long);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_server, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(client_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
assert_true(server_sm.hs_completed);
|
||||
assert_true(client_sm.hs_completed);
|
||||
|
||||
int64_t client_stream_id = -1;
|
||||
result = isc_quic_session_open_stream(client_session, true, &client_sm,
|
||||
&client_stream_id);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(client_stream_id != -1);
|
||||
|
||||
send_data = (isc_region_t){ .base = (uint8_t *)data_ping,
|
||||
.length = sizeof(data_ping) };
|
||||
result = isc_quic_session_send_data(
|
||||
client_session, client_stream_id, &send_data, true,
|
||||
(isc_quic_send_cb_t)quic_sm_send_cb, &client_sm);
|
||||
|
||||
written = 0;
|
||||
result = isc_quic_session_write_pkt(client_session, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
written = 0;
|
||||
result = isc_quic_session_write_pkt(client_session, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
written = 0;
|
||||
result = isc_quic_session_write_pkt(server_session, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_server, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(client_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
assert_true(client_sm.opened_streams > 0);
|
||||
assert_true(server_sm.sends > 0);
|
||||
|
||||
result = isc_quic_session_update_local_address(client_session,
|
||||
&migrate_client_addr);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
written = 0;
|
||||
result = isc_quic_session_write_pkt(client_session, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_client, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_false(is_long);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
written = 0;
|
||||
result = isc_quic_session_write_pkt(server_session, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_server, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(client_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_client, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_false(is_long);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_client, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(server_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
result = isc_quic_session_write_pkt(server_session, pkt_server,
|
||||
sizeof(pkt_server), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written > 0);
|
||||
|
||||
result = isc_ngtcp2_decode_pkt_header_data(
|
||||
pkt_server, written, ISC_QUIC_SERVER_SCID_LEN, &is_long, &scid,
|
||||
&dcid, &version);
|
||||
|
||||
assert_false(is_long);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
|
||||
pkt_reg = (isc_region_t){ .base = pkt_server, .length = written };
|
||||
written = 0;
|
||||
result = isc_quic_session_read_pkt(client_session, version, &dcid,
|
||||
&scid, &pkt_reg, pkt_client,
|
||||
sizeof(pkt_client), &written);
|
||||
assert_true(result == ISC_R_SUCCESS);
|
||||
assert_true(written == 0);
|
||||
|
||||
/* printf("%" PRId64 "\n", written); */
|
||||
}
|
||||
|
||||
ISC_TEST_LIST_START
|
||||
ISC_TEST_ENTRY_CUSTOM(quic_session_simple_test, quic_session_test_setup,
|
||||
quic_session_test_teardown)
|
||||
ISC_TEST_LIST_END
|
||||
|
||||
ISC_TEST_MAIN_CUSTOM(quic_session_testset_setup, quic_session_testset_teardown);
|
||||
1069
tests/isc/quic_tls_test.c
Normal file
1069
tests/isc/quic_tls_test.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user