diff --git a/include/internal/quic_srt_gen.h b/include/internal/quic_srt_gen.h new file mode 100644 index 0000000000..a25e71aa81 --- /dev/null +++ b/include/internal/quic_srt_gen.h @@ -0,0 +1,57 @@ +/* +* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. +* +* Licensed under the Apache License 2.0 (the "License"). You may not use +* this file except in compliance with the License. You can obtain a copy +* in the file LICENSE in the source distribution or at +* https://www.openssl.org/source/license.html +*/ + +#ifndef OSSL_INTERNAL_QUIC_SRT_GEN_H +# define OSSL_INTERNAL_QUIC_SRT_GEN_H +# pragma once + +# include "internal/e_os.h" +# include "internal/time.h" +# include "internal/quic_types.h" +# include "internal/quic_wire.h" + +# ifndef OPENSSL_NO_QUIC + +/* + * QUIC Stateless Reset Token Generator + * ==================================== + * + * This generates 16-byte QUIC Stateless Reset Tokens given a secret symmetric + * key and a DCID. Because the output is deterministic with regards to these + * inputs, assuming the same key is used between invocations of a process, we + * are able to generate the same stateless reset token in a subsequent process, + * thereby allowing us to achieve stateless reset of a peer which still thinks + * it is connected to a past process at the same UDP address. + */ +typedef struct quic_srt_gen_st QUIC_SRT_GEN; + +/* + * Create a new stateless reset token generator using the given key as input. + * The key may be of arbitrary length. + * + * The caller is responsible for performing domain separation with regards to + * the key; i.e., the caller is responsible for ensuring the key is never used + * in any other context. + */ +QUIC_SRT_GEN *ossl_quic_srt_gen_new(OSSL_LIB_CTX *libctx, const char *propq, + const unsigned char *key, size_t key_len); + +/* Free the stateless reset token generator. No-op if srt_gen is NULL. */ +void ossl_quic_srt_gen_free(QUIC_SRT_GEN *srt_gen); + +/* + * Calculates a token using the given DCID and writes it to *token. Returns 0 on + * failure. + */ +int ossl_quic_srt_gen_calculate_token(QUIC_SRT_GEN *srt_gen, + const QUIC_CONN_ID *dcid, + QUIC_STATELESS_RESET_TOKEN *token); + +# endif +#endif diff --git a/ssl/quic/build.info b/ssl/quic/build.info index 866f581fa9..b8d871848c 100644 --- a/ssl/quic/build.info +++ b/ssl/quic/build.info @@ -14,4 +14,4 @@ SOURCE[$LIBSSL]=quic_tserver.c SOURCE[$LIBSSL]=quic_tls.c SOURCE[$LIBSSL]=quic_thread_assist.c SOURCE[$LIBSSL]=quic_trace.c -SOURCE[$LIBSSL]=quic_srtm.c +SOURCE[$LIBSSL]=quic_srtm.c quic_srt_gen.c diff --git a/ssl/quic/quic_srt_gen.c b/ssl/quic/quic_srt_gen.c new file mode 100644 index 0000000000..e43a55b29e --- /dev/null +++ b/ssl/quic/quic_srt_gen.c @@ -0,0 +1,83 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ +#include "internal/quic_srt_gen.h" +#include +#include + +struct quic_srt_gen_st { + EVP_MAC *mac; + EVP_MAC_CTX *mac_ctx; +}; + +/* + * Simple HMAC-SHA256-based stateless reset token generator. + */ + +QUIC_SRT_GEN *ossl_quic_srt_gen_new(OSSL_LIB_CTX *libctx, const char *propq, + const unsigned char *key, size_t key_len) +{ + QUIC_SRT_GEN *srt_gen; + OSSL_PARAM params[3], *p = params; + + if ((srt_gen = OPENSSL_zalloc(sizeof(*srt_gen))) == NULL) + return NULL; + + if ((srt_gen->mac = EVP_MAC_fetch(libctx, "HMAC", propq)) == NULL) + goto err; + + if ((srt_gen->mac_ctx = EVP_MAC_CTX_new(srt_gen->mac)) == NULL) + goto err; + + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, "SHA256", 7); + if (propq != NULL) + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_PROPERTIES, + (char *)propq, 0); + *p++ = OSSL_PARAM_construct_end(); + + if (!EVP_MAC_init(srt_gen->mac_ctx, key, key_len, params)) + goto err; + + return srt_gen; + +err: + ossl_quic_srt_gen_free(srt_gen); + return NULL; +} + +void ossl_quic_srt_gen_free(QUIC_SRT_GEN *srt_gen) +{ + if (srt_gen == NULL) + return; + + EVP_MAC_CTX_free(srt_gen->mac_ctx); + EVP_MAC_free(srt_gen->mac); + OPENSSL_free(srt_gen); +} + +int ossl_quic_srt_gen_calculate_token(QUIC_SRT_GEN *srt_gen, + const QUIC_CONN_ID *dcid, + QUIC_STATELESS_RESET_TOKEN *token) +{ + size_t outl = 0; + unsigned char mac[32]; + + if (!EVP_MAC_init(srt_gen->mac_ctx, NULL, 0, NULL)) + return 0; + + if (!EVP_MAC_update(srt_gen->mac_ctx, (const unsigned char *)dcid->id, + dcid->id_len)) + return 0; + + if (!EVP_MAC_final(srt_gen->mac_ctx, mac, &outl, sizeof(mac)) + || outl != sizeof(mac)) + return 0; + + memcpy(token, mac, sizeof(*token)); + return 1; +} diff --git a/test/build.info b/test/build.info index cba48e6db0..a71ee13d1f 100644 --- a/test/build.info +++ b/test/build.info @@ -75,7 +75,7 @@ IF[{- !$disabled{tests} -}] IF[{- !$disabled{quic} -}] PROGRAMS{noinst}=priority_queue_test event_queue_test quicfaultstest quicapitest \ - quic_newcid_test + quic_newcid_test quic_srt_gen_test ENDIF IF[{- !$disabled{comp} && (!$disabled{brotli} || !$disabled{zstd} || !$disabled{zlib}) -}] @@ -850,6 +850,10 @@ IF[{- !$disabled{tests} -}] SOURCE[quic_newcid_test]=quic_newcid_test.c helpers/ssltestlib.c $QUICTESTHELPERS INCLUDE[quic_newcid_test]=../include ../apps/include .. DEPEND[quic_newcid_test]=../libcrypto.a ../libssl.a libtestutil.a + + SOURCE[quic_srt_gen_test]=quic_srt_gen_test.c helpers/ssltestlib.c $QUICTESTHELPERS + INCLUDE[quic_srt_gen_test]=../include ../apps/include .. + DEPEND[quic_srt_gen_test]=../libcrypto.a ../libssl.a libtestutil.a ENDIF SOURCE[dhtest]=dhtest.c diff --git a/test/quic_srt_gen_test.c b/test/quic_srt_gen_test.c new file mode 100644 index 0000000000..6982f599c0 --- /dev/null +++ b/test/quic_srt_gen_test.c @@ -0,0 +1,83 @@ +/* + * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include +#include + +#include +#include "internal/quic_srt_gen.h" + +#include "testutil.h" +#include "testutil/output.h" + +struct test_case { + const unsigned char *key; + size_t key_len; + QUIC_CONN_ID dcid; + QUIC_STATELESS_RESET_TOKEN expected; +}; + +static const unsigned char key_1[] = { 0x01, 0x02, 0x03 }; + +static const unsigned char key_2[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +static const struct test_case tests[] = { + { + key_1, sizeof(key_1), { 2, { 0x55, 0x66 } }, + {{ 0x02,0x9e,0x8f,0x3d,0x1e,0xa9,0x06,0x23,0xb2,0x43,0xd2,0x19,0x59,0x8a,0xa1,0x66 }} + }, + { + key_2, sizeof(key_2), { 0, { 0 } }, + {{ 0x93,0x10,0x2f,0xc7,0xaf,0x9d,0x9b,0x28,0x3f,0x84,0x95,0x6b,0xa3,0xdc,0x07,0x6b }} + }, + { + key_2, sizeof(key_2), + { 20, { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } }, + {{ 0x9a,0x98,0x98,0x61,0xbe,0xfd,0xe3,0x05,0x45,0xac,0x66,0xcf,0x3b,0x58,0xfb,0xab }} + } +}; + +static int test_srt_gen(int idx) +{ + int testresult = 0; + const struct test_case *t = &tests[idx]; + QUIC_SRT_GEN *srt_gen = NULL; + QUIC_STATELESS_RESET_TOKEN token; + size_t i; + + if (!TEST_ptr(srt_gen = ossl_quic_srt_gen_new(NULL, NULL, + t->key, t->key_len))) + goto err; + + for (i = 0; i < 2; ++i) { + memset(&token, 0xff, sizeof(token)); + + if (!TEST_true(ossl_quic_srt_gen_calculate_token(srt_gen, &t->dcid, + &token))) + goto err; + + if (!TEST_mem_eq(&token, sizeof(token), + &t->expected, sizeof(t->expected))) + goto err; + } + + testresult = 1; +err: + ossl_quic_srt_gen_free(srt_gen); + return testresult; +} + +int setup_tests(void) +{ + ADD_ALL_TESTS(test_srt_gen, OSSL_NELEM(tests)); + return 1; +} diff --git a/test/recipes/75-test_quic_srt_gen.t b/test/recipes/75-test_quic_srt_gen.t new file mode 100644 index 0000000000..64c2cae34f --- /dev/null +++ b/test/recipes/75-test_quic_srt_gen.t @@ -0,0 +1,19 @@ +#! /usr/bin/env perl +# Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + +use OpenSSL::Test qw/:DEFAULT/; +use OpenSSL::Test::Utils; + +setup("test_quic_srt_gen"); + +plan skip_all => "QUIC protocol is not supported by this OpenSSL build" + if disabled('quic'); + +plan tests => 1; + +ok(run(test(["quic_srt_gen_test"])));