blob: 8c2a580d4ffc83d45ce3c4d38ab2764983d4e37e [file] [edit]
/*
* Copyright 2026 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 "../ssl/record/methods/recmethod_local.h"
#include "../ssl/ssl_local.h"
#include "internal/nelem.h"
#include "internal/ssl_unwrap.h"
#include "helpers/ssltestlib.h"
#include "testutil.h"
#include <openssl/evp.h>
#include <openssl/ssl.h>
static char *cert = NULL;
static char *privkey = NULL;
static const char *cipher_names[] = {
"aes-128-ecb",
"aes-256-ecb",
#if !defined(OPENSSL_NO_CHACHA)
"chacha20",
#endif
};
static int test_dtls_crypt_sequence_number(int idx)
{
/*
* Test all possiblie Encryption Algorithms for dtls_crypt_sequence_number function
* aes-128-ecb, "aes-256-ecb" and "chacha20"
*/
EVP_CIPHER_CTX *ctx = NULL;
EVP_CIPHER *cipher = NULL;
unsigned char key[32] = { 0 };
unsigned char iv[16] = { 0 };
unsigned char initial_seq[2] = { 0, 0 };
unsigned char zero_seq[2] = { 0, 0 };
unsigned char rec_data[16] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
cipher = EVP_CIPHER_fetch(NULL, cipher_names[idx], NULL);
if (!TEST_ptr(cipher))
goto err;
ctx = EVP_CIPHER_CTX_new();
if (!TEST_ptr(ctx))
goto err;
if (!TEST_true(EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, 1)))
goto err;
if (!TEST_int_eq(dtls_crypt_sequence_number(ctx, initial_seq, sizeof(initial_seq), rec_data), 1))
goto err;
/* Verify Sequence Number is no longer zero */
if (!TEST_mem_ne(initial_seq, sizeof(initial_seq), zero_seq, sizeof(zero_seq)))
goto err;
if (!TEST_int_eq(dtls_crypt_sequence_number(ctx, initial_seq, sizeof(initial_seq), rec_data), 1))
goto err;
/* Verify Sequence Number is back to zero */
if (!TEST_mem_eq(initial_seq, sizeof(initial_seq), zero_seq, sizeof(zero_seq)))
goto err;
EVP_CIPHER_CTX_free(ctx);
EVP_CIPHER_free(cipher);
return 1;
err:
if (ctx != NULL)
EVP_CIPHER_CTX_free(ctx);
if (cipher != NULL)
EVP_CIPHER_free(cipher);
return 0;
}
/* rfc9147 section 4.2.2 sequence number reconstruction vectors. */
typedef struct seq_num_test_st {
/* Zero also represents the initial empty replay window. */
uint64_t max_seq_num;
uint64_t truncated;
size_t seqlen;
uint64_t seq_num;
} SEQ_NUM_TEST;
static const SEQ_NUM_TEST seq_num_tests[] = {
/* Empty window and first-period lower-bound cases. */
{ 0, 0, 1, 0 },
{ 0, 1, 1, 1 },
{ 0, 0x7f, 1, 0x7f },
{ 0, 0x80, 1, 0x80 },
{ 0, 0x81, 1, 0x81 },
{ 0, 0xff, 1, 0xff },
{ 0, 0, 2, 0 },
{ 0, 1, 2, 1 },
{ 0, 0x7fff, 2, 0x7fff },
{ 0, 0x8000, 2, 0x8000 },
{ 0, 0x8001, 2, 0x8001 },
{ 0, 0x8002, 2, 0x8002 },
{ 0, 40000, 2, 40000 },
{ 0, 0xffff, 2, 0xffff },
/* Ordinary forward progression */
{ 5, 6, 2, 6 },
{ 5, 6, 1, 6 },
{ 200, 201, 2, 201 },
{ 0x1234, 0x1235, 2, 0x1235 },
/* 8- and 16-bit wraps */
{ 0xfe, 0xff, 1, 0xff },
{ 0xff, 0x00, 1, 0x100 },
{ 0x100, 0x01, 1, 0x101 },
{ 0x1fe, 0xff, 1, 0x1ff },
{ 0x1ff, 0x00, 1, 0x200 },
{ 0xfffe, 0xffff, 2, 0xffff },
{ 0xffff, 0x0000, 2, 0x10000 },
{ 0x10000, 0x0001, 2, 0x10001 },
{ 0x1fffe, 0xffff, 2, 0x1ffff },
{ 0x1ffff, 0x0000, 2, 0x20000 },
{ 0x20000, 0x0001, 2, 0x20001 },
/* Reordered records */
{ 0x100, 0xff, 2, 0xff },
{ 0x100, 0xfe, 2, 0xfe },
{ 0x10010, 0x000f, 2, 0x1000f },
{ 300, 40, 1, 296 },
/* Half-period ties select the forward candidate in either phase. */
{ 199, 72, 1, 328 }, /* candidate 72, 128 behind: pick 328 */
{ 299, 172, 1, 428 }, /* candidate 428, 128 ahead: keep it */
{ 0x180ff, 0x0100, 2, 0x20100 }, /* candidate 0x10100: pick 0x20100 */
{ 0x100ff, 0x8100, 2, 0x18100 }, /* candidate 0x18100: keep it */
/* Top-of-uint64_t fallback and overflow boundaries */
{ UINT64_MAX, 0, 2, UINT64_MAX & ~UINT64_C(0xffff) },
{ UINT64_MAX, 0xffff, 2, UINT64_MAX },
{ UINT64_MAX, 0xffc0, 2, (UINT64_MAX & ~UINT64_C(0xffff)) | 0xffc0 },
{ UINT64_MAX, 5, 1, (UINT64_MAX & ~UINT64_C(0xff)) | 5 },
{ UINT64_MAX - 1, 0, 2, UINT64_MAX & ~UINT64_C(0xffff) },
{ UINT64_MAX - 0x10000, 0, 2, UINT64_MAX - 0xffff },
{ UINT64_MAX - 0x10000, 1, 2, UINT64_MAX - 0xffff + 1 },
{ UINT64_MAX - 0x10000, 0x8000, 2,
(UINT64_MAX - 0xffff) | 0x8000 },
};
static int test_seq_num_reconstruction(int idx)
{
const SEQ_NUM_TEST *t = &seq_num_tests[idx];
uint64_t seq_num = 0;
seq_num = dtls13_reconstruct_seq_num(t->max_seq_num, t->truncated,
t->seqlen);
if (!TEST_uint64_t_eq(seq_num, t->seq_num))
return 0;
/* The reconstructed value must retain the wire bits used in the AAD. */
return TEST_uint64_t_eq(seq_num & DTLS13_UNI_HDR_SEQ_MASK(t->seqlen),
t->truncated);
}
#ifndef OPENSSL_NO_DTLS1_3
/*
* Test that dtls1_increment_epoch() enforces the RFC 9147 Section 8 limit
* on the write (sending) epoch for DTLS 1.3: "sending implementations MUST
* NOT allow the epoch to exceed 2^48-1". This is stricter than the 2^64-1
* wrap-around ceiling in Section 6.1, and applies only to the write side
* and only for DTLS 1.3 -- DTLS 1.2 keeps its existing UINT16_MAX limit.
*
* There's no way to actually drive 2^48 real KeyUpdates in a test, so this
* drives one real DTLS 1.3 handshake to get a genuinely-typed connection
* (SSL_CONNECTION_IS_DTLS13() depends on the negotiated method, not
* anything that can be poked directly), then writes w_conn_epoch directly
* to one below the limit before calling the real increment function at and
* past the boundary.
*/
static int test_dtls13_increment_epoch_max(void)
{
SSL_CTX *sctx = NULL, *cctx = NULL;
SSL *serverssl = NULL, *clientssl = NULL;
SSL_CONNECTION *sc = NULL;
int testresult = 0;
if (!TEST_true(create_ssl_ctx_pair(NULL, DTLS_server_method(),
DTLS_client_method(),
DTLS1_3_VERSION, DTLS1_3_VERSION,
&sctx, &cctx, cert, privkey)))
goto end;
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl,
NULL, NULL)))
goto end;
if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE)))
goto end;
if (!TEST_int_eq(SSL_version(serverssl), DTLS1_3_VERSION))
goto end;
if (!TEST_ptr(sc = SSL_CONNECTION_FROM_SSL(serverssl)))
goto end;
if (!TEST_true(SSL_CONNECTION_IS_DTLS13(sc)))
goto end;
/* One below the Section 8 limit: incrementing must still succeed. */
sc->rlayer.d->w_conn_epoch = DTLS1_3_MAX_EPOCH - 1;
if (!TEST_true(dtls1_increment_epoch(sc, SSL3_CC_WRITE)))
goto end;
if (!TEST_uint64_t_eq(sc->rlayer.d->w_conn_epoch, DTLS1_3_MAX_EPOCH))
goto end;
/* Already at the limit: incrementing further must be rejected. */
if (!TEST_false(dtls1_increment_epoch(sc, SSL3_CC_WRITE)))
goto end;
if (!TEST_uint64_t_eq(sc->rlayer.d->w_conn_epoch, DTLS1_3_MAX_EPOCH))
goto end;
testresult = 1;
end:
SSL_free(serverssl);
SSL_free(clientssl);
SSL_CTX_free(sctx);
SSL_CTX_free(cctx);
return testresult;
}
#endif /* OPENSSL_NO_DTLS1_3 */
int setup_tests(void)
{
if (!TEST_ptr(cert = test_get_argument(0))
|| !TEST_ptr(privkey = test_get_argument(1)))
return 0;
ADD_ALL_TESTS(test_dtls_crypt_sequence_number, OSSL_NELEM(cipher_names));
ADD_ALL_TESTS(test_seq_num_reconstruction, OSSL_NELEM(seq_num_tests));
#ifndef OPENSSL_NO_DTLS1_3
ADD_TEST(test_dtls13_increment_epoch_max);
#endif
return 1;
}