openssl/test/ech_test.c
sftcd 000b8958c9 ECH external APIs
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25663)
2025-01-09 16:53:04 +01:00

1408 lines
55 KiB
C

/*
* Copyright 2024 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 <openssl/ssl.h>
#include <openssl/hpke.h>
#include "testutil.h"
#include "helpers/ssltestlib.h"
#ifndef OPENSSL_NO_ECH
# define DEF_CERTS_DIR "test/certs"
static OSSL_LIB_CTX *libctx = NULL;
static char *propq = NULL;
static int verbose = 0;
static char *certsdir = NULL;
static char *cert = NULL;
static char *privkey = NULL;
static char *rootcert = NULL;
/* callback */
static unsigned int test_cb(SSL *s, const char *str)
{
return 1;
}
/*
* The define/vars below and the 3 callback functions are modified
* from test/sslapitest.c
*/
# define TEST_EXT_TYPE1 0xffab /* custom ext type 1: has 1 octet payload */
# define TEST_EXT_TYPE2 0xffcd /* custom ext type 2: no payload */
/* A well-encoded ECH extension value */
static const unsigned char encoded_ech_val[] = {
0x00, 0x00, 0x01, 0x00, 0x01, 0xf7, 0x00, 0x20,
0xc9, 0x2c, 0x12, 0xc9, 0xc0, 0x4d, 0x11, 0x5d,
0x09, 0xe1, 0xeb, 0x7a, 0x18, 0xb2, 0x83, 0x28,
0x35, 0x00, 0x3c, 0x8d, 0x78, 0x09, 0xfd, 0x09,
0x84, 0xca, 0x94, 0x77, 0xcf, 0x78, 0xd0, 0x04,
0x00, 0x90, 0x5e, 0xc7, 0xc0, 0x62, 0x84, 0x8d,
0x4b, 0x85, 0xd5, 0x6a, 0x9a, 0xc1, 0xc6, 0xc2,
0x28, 0xac, 0x87, 0xb9, 0x2f, 0x36, 0xa0, 0xf7,
0x5f, 0xd0, 0x23, 0x7b, 0xf4, 0xc1, 0x62, 0x1c,
0xf1, 0x91, 0xfd, 0x46, 0x35, 0x41, 0xc9, 0x06,
0xd3, 0x19, 0xd6, 0x34, 0x01, 0xc3, 0xb3, 0x66,
0x4e, 0x7a, 0x28, 0xac, 0xd4, 0xd2, 0x35, 0x2b,
0xd0, 0xc6, 0x94, 0x34, 0xc1, 0x94, 0x62, 0x77,
0x1b, 0x5a, 0x02, 0x3c, 0xdd, 0xa2, 0x4d, 0x33,
0xa5, 0xd0, 0x59, 0x12, 0xf5, 0x17, 0x03, 0xe5,
0xab, 0xbd, 0x83, 0x52, 0x40, 0x6c, 0x99, 0xac,
0x25, 0x07, 0x63, 0x8c, 0x16, 0x5d, 0x93, 0x34,
0x56, 0x34, 0x60, 0x86, 0x25, 0xa7, 0x0d, 0xac,
0xb8, 0x5e, 0x87, 0xc6, 0xf7, 0x23, 0xaf, 0xf8,
0x3e, 0x2a, 0x46, 0x75, 0xa9, 0x5f, 0xaf, 0xd2,
0x91, 0xe6, 0x44, 0xcb, 0xe7, 0xe0, 0x85, 0x36,
0x9d, 0xd2, 0xaf, 0xae, 0xb3, 0x0f, 0x70, 0x6a,
0xaf, 0x42, 0xc0, 0xb3, 0xe4, 0x65, 0x53, 0x01,
0x75, 0xbf
};
static int new_add_cb(SSL *s, unsigned int ext_type, unsigned int context,
const unsigned char **out, size_t *outlen, X509 *x,
size_t chainidx, int *al, void *add_arg)
{
int *server = (int *)add_arg;
unsigned char *data;
if (*server != SSL_is_server(s))
return -1;
if (ext_type == TEST_EXT_TYPE1) {
if ((data = OPENSSL_malloc(sizeof(*data))) == NULL)
return -1;
*data = 1;
*out = data;
*outlen = sizeof(*data);
} else if (ext_type == OSSL_ECH_CURRENT_VERSION) {
/* inject a sample ECH extension value into the CH */
if ((data = OPENSSL_memdup(encoded_ech_val,
sizeof(encoded_ech_val))) == NULL)
return -1;
*out = data;
*outlen = sizeof(encoded_ech_val);
} else {
/* inject a TEST_EXT_TYPE2, with a zero-length payload */
*out = NULL;
*outlen = 0;
}
return 1;
}
static void new_free_cb(SSL *s, unsigned int ext_type, unsigned int context,
const unsigned char *out, void *add_arg)
{
OPENSSL_free((unsigned char *)out);
}
static int new_parse_cb(SSL *s, unsigned int ext_type, unsigned int context,
const unsigned char *in, size_t inlen, X509 *x,
size_t chainidx, int *al, void *parse_arg)
{
int *server = (int *)parse_arg;
if (*server != SSL_is_server(s)
|| inlen != sizeof(char) || *in != 1)
return -1;
return 1;
}
/* general test vector values */
/* standard x25519 ech key pair with public key example.com */
static const char pem_kp1[] =
"-----BEGIN PRIVATE KEY-----\n"
"MC4CAQAwBQYDK2VuBCIEILDIeo9Eqc4K9/uQ0PNAyMaP60qrxiSHT2tNZL3ksIZS\n"
"-----END PRIVATE KEY-----\n"
"-----BEGIN ECHCONFIG-----\n"
"AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEA\n"
"AQALZXhhbXBsZS5jb20AAA==\n"
"-----END ECHCONFIG-----\n";
/* standard x25519 ECHConfigList with public key example.com */
static const char pem_pk1[] =
"-----BEGIN ECHCONFIG-----\n"
"AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEA\n"
"AQALZXhhbXBsZS5jb20AAA==\n"
"-----END ECHCONFIG-----\n";
/* an ECDSA private with an x25519 ech public key example.com */
static const char pem_mismatch_priv[] =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEIGKONznbHOMEKT4AKMufc37O9lUEBHO+Nb6ztkXhGXLcoAoGCCqGSM49\n"
"AwEHoUQDQgAEYDznfezvj5ufhQsZOQvSdiNpYKCd8tRI1aI3gc4y7gmdDUKpwzHa\n"
"VS4Qq0xyeG6fDMJv668UCotQANFsifGirQ==\n"
"-----END EC PRIVATE KEY-----\n"
"-----BEGIN ECHCONFIG-----\n"
"AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEA\n"
"AQALZXhhbXBsZS5jb20AAA==\n"
"-----END ECHCONFIG-----\n";
/*
* This ECHConfigList has 4 entries with different versions,
* from drafts: 13,10,13,9 - since our runtime no longer supports
* version 9 or 10, we should see 2 configs loaded.
*/
static const char pem_4_to_2[] =
"-----BEGIN ECHCONFIG-----\n"
"APv+DQA6xQAgACBm54KSIPXu+pQq2oY183wt3ybx7CKbBYX0ogPq5u6FegAEAAEA\n"
"AQALZXhhbXBsZS5jb20AAP4KADzSACAAIIP+0Qt0WGBF3H5fz8HuhVRTCEMuHS4K\n"
"hu6ibR/6qER4AAQAAQABAAAAC2V4YW1wbGUuY29tAAD+DQA6QwAgACB3xsNUtSgi\n"
"piYpUkW6OSrrg03I4zIENMFa0JR2+Mm1WwAEAAEAAQALZXhhbXBsZS5jb20AAP4J\n"
"ADsAC2V4YW1wbGUuY29tACCjJCv5w/yaHjbOc6nVuM/GksIGLgDR+222vww9dEk8\n"
"FwAgAAQAAQABAAAAAA==\n"
"-----END ECHCONFIG-----\n";
/* mis-spelled PEM string */
static const char pem_typo[] =
"-----BEGIN PRIVATE KEY-----\n"
"MC4CAQAwBQYDK2VuBCIEILDIeo9Eqc4K9/uQ0PNAyMaP60qrxiSHT2tNZL3ksIZS\n"
"-----END PRIVATE KEY-----\n"
"-----BEGIN ExHCOxFIG-----\n"
"AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEA\n"
"AQALZXhhbXBsZS5jb20AAA==\n"
"-----END ExHCOxFIG-----\n";
/* single-line base64(ECHConfigList) form of pem_pk1 */
static const char b64_pk1[] =
"AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEA"
"AQALZXhhbXBsZS5jb20AAA==";
/* single-line base64(ECHConfigList) form of pem_6_to3 */
static const char b64_6_to_3[] =
"AXn+DQA6xQAgACBm54KSIPXu+pQq2oY183wt3ybx7CKbBYX0ogPq5u6FegAEAAE"
"AAQALZXhhbXBsZS5jb20AAP4KADzSACAAIIP+0Qt0WGBF3H5fz8HuhVRTCEMuHS"
"4Khu6ibR/6qER4AAQAAQABAAAAC2V4YW1wbGUuY29tAAD+CQA7AAtleGFtcGxlL"
"mNvbQAgoyQr+cP8mh42znOp1bjPxpLCBi4A0ftttr8MPXRJPBcAIAAEAAEAAQAA"
"AAD+DQA6QwAgACB3xsNUtSgipiYpUkW6OSrrg03I4zIENMFa0JR2+Mm1WwAEAAE"
"AAQALZXhhbXBsZS5jb20AAP4KADwDACAAIH0BoAdiJCX88gv8nYpGVX5BpGBa9y"
"T0Pac3Kwx6i8URAAQAAQABAAAAC2V4YW1wbGUuY29tAAD+DQA6QwAgACDcZIAx7"
"OcOiQuk90VV7/DO4lFQr5I3Zw9tVbK8MGw1dgAEAAEAAQALZXhhbXBsZS5jb20A"
"AA==";
/* same as above but binary encoded */
static const unsigned char bin_6_to_3[] = {
0x01, 0x79, 0xfe, 0x0d, 0x00, 0x3a, 0xc5, 0x00,
0x20, 0x00, 0x20, 0x66, 0xe7, 0x82, 0x92, 0x20,
0xf5, 0xee, 0xfa, 0x94, 0x2a, 0xda, 0x86, 0x35,
0xf3, 0x7c, 0x2d, 0xdf, 0x26, 0xf1, 0xec, 0x22,
0x9b, 0x05, 0x85, 0xf4, 0xa2, 0x03, 0xea, 0xe6,
0xee, 0x85, 0x7a, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
0xfe, 0x0a, 0x00, 0x3c, 0xd2, 0x00, 0x20, 0x00,
0x20, 0x83, 0xfe, 0xd1, 0x0b, 0x74, 0x58, 0x60,
0x45, 0xdc, 0x7e, 0x5f, 0xcf, 0xc1, 0xee, 0x85,
0x54, 0x53, 0x08, 0x43, 0x2e, 0x1d, 0x2e, 0x0a,
0x86, 0xee, 0xa2, 0x6d, 0x1f, 0xfa, 0xa8, 0x44,
0x78, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
0xfe, 0x09, 0x00, 0x3b, 0x00, 0x0b, 0x65, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
0x6d, 0x00, 0x20, 0xa3, 0x24, 0x2b, 0xf9, 0xc3,
0xfc, 0x9a, 0x1e, 0x36, 0xce, 0x73, 0xa9, 0xd5,
0xb8, 0xcf, 0xc6, 0x92, 0xc2, 0x06, 0x2e, 0x00,
0xd1, 0xfb, 0x6d, 0xb6, 0xbf, 0x0c, 0x3d, 0x74,
0x49, 0x3c, 0x17, 0x00, 0x20, 0x00, 0x04, 0x00,
0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfe,
0x0d, 0x00, 0x3a, 0x43, 0x00, 0x20, 0x00, 0x20,
0x77, 0xc6, 0xc3, 0x54, 0xb5, 0x28, 0x22, 0xa6,
0x26, 0x29, 0x52, 0x45, 0xba, 0x39, 0x2a, 0xeb,
0x83, 0x4d, 0xc8, 0xe3, 0x32, 0x04, 0x34, 0xc1,
0x5a, 0xd0, 0x94, 0x76, 0xf8, 0xc9, 0xb5, 0x5b,
0x00, 0x04, 0x00, 0x01, 0x00, 0x01, 0x00, 0x0b,
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0xfe, 0x0a, 0x00,
0x3c, 0x03, 0x00, 0x20, 0x00, 0x20, 0x7d, 0x01,
0xa0, 0x07, 0x62, 0x24, 0x25, 0xfc, 0xf2, 0x0b,
0xfc, 0x9d, 0x8a, 0x46, 0x55, 0x7e, 0x41, 0xa4,
0x60, 0x5a, 0xf7, 0x24, 0xf4, 0x3d, 0xa7, 0x37,
0x2b, 0x0c, 0x7a, 0x8b, 0xc5, 0x11, 0x00, 0x04,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b,
0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
0x63, 0x6f, 0x6d, 0x00, 0x00, 0xfe, 0x0d, 0x00,
0x3a, 0x43, 0x00, 0x20, 0x00, 0x20, 0xdc, 0x64,
0x80, 0x31, 0xec, 0xe7, 0x0e, 0x89, 0x0b, 0xa4,
0xf7, 0x45, 0x55, 0xef, 0xf0, 0xce, 0xe2, 0x51,
0x50, 0xaf, 0x92, 0x37, 0x67, 0x0f, 0x6d, 0x55,
0xb2, 0xbc, 0x30, 0x6c, 0x35, 0x76, 0x00, 0x04,
0x00, 0x01, 0x00, 0x01, 0x00, 0x0b, 0x65, 0x78,
0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
0x6d, 0x00, 0x00
};
/* base64(ECHConfigList) with corrupt ciphersuite length and public_name */
static const char b64_bad_cs[] =
"AD7+DQA6uAAgACAogff+HZbirYdQCfXI01GBPP8AEKYyK/D/0DoeXD84fgAQAAE"
"AAQgLZXhhbUNwbGUuYwYAAAAAQwA=";
/* An ECHConfigList with one ECHConfig but of the wrong version */
static const unsigned char bin_bad_ver[] = {
0x00, 0x3e, 0xfe, 0xff, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/*
* An ECHConflgList with 2 ECHConfig values that are both
* of the wrong version. The versions here are 0xfe03 (we
* currently support only 0xfe0d)
*/
static const unsigned char bin_bad_ver2[] = {
0x00, 0x80, 0xfe, 0x03, 0x00, 0x3c, 0x00, 0x00,
0x20, 0x00, 0x20, 0x71, 0xa5, 0xe0, 0xb4, 0x6d,
0xdf, 0xa4, 0xda, 0xed, 0x69, 0xa5, 0xc7, 0x8b,
0x9d, 0xa5, 0x13, 0x0c, 0x36, 0x83, 0x7a, 0x03,
0x72, 0x1d, 0xf6, 0x1e, 0xc5, 0x83, 0x1a, 0x11,
0x73, 0xce, 0x2d, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x31,
0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x00, 0x00, 0xfe, 0x03, 0x00, 0x3c, 0x00, 0x00,
0x20, 0x00, 0x20, 0x69, 0x88, 0xfd, 0x8f, 0xc9,
0x0b, 0xb7, 0x2d, 0x96, 0x6d, 0xe0, 0x22, 0xf0,
0xc8, 0x1b, 0x62, 0x2b, 0x1c, 0x94, 0x96, 0xad,
0xef, 0x55, 0xdb, 0x9f, 0xeb, 0x0d, 0xa1, 0x4b,
0x0c, 0xd7, 0x36, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0d, 0x70, 0x61, 0x72, 0x74, 0x32,
0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x00, 0x00
};
/*
* An ECHConfigList with one ECHConfig with an all-zero public value.
* That should be ok, for 25519, but hey, just in case:-)
*/
static const unsigned char bin_zero[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/*
* The next set of samples are syntactically invalid
* Proper fuzzing is still needed but no harm having
* these too. Generally these are bad version of
* our nominal encoding with some octet(s) replaced
* by 0xFF values. Other hex letters are lowercase
* so you can find the altered octet(s).
*/
/* wrong overall length (replacing 0x3e with 0xFF) */
static const unsigned char bin_bad_olen[] = {
0x00, 0xFF, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0xFF, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* wrong ECHConfig inner length (replacing 0x3a with 0xFF) */
static const unsigned char bin_bad_ilen[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0xFF, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* wrong length for public key (replaced 0x20 with 0xFF) */
static const unsigned char bin_bad_pklen[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0xFF, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* wrong length for ciphersuites (replaced 0x04 with 0xFF) */
static const unsigned char bin_bad_cslen[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0xFF, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* wrong length for public name (replaced 0x0b with 0xFF) */
static const unsigned char bin_bad_pnlen[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0xFF, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* non-zero extension length (0xFF at end) but no extension value */
static const unsigned char bin_bad_extlen[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0xFF
};
/*
* The next set have bad kem, kdf or aead values - this time with
* 0xAA as the replacement value
*/
/* wrong KEM ID (replaced 0x20 with 0xAA) */
static const unsigned char bin_bad_kemid[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0xAA, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* wrong KDF ID (replaced 0x01 with 0xAA) */
static const unsigned char bin_bad_kdfid[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0xAA, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* wrong AEAD ID (replaced 0x01 with 0xAA) */
static const unsigned char bin_bad_aeadid[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0xAA, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* ECHConfig supports two symmetric suites */
static const unsigned char bin_multi_suite[] = {
0x00, 0x42, 0xfe, 0x0d, 0x00, 0x3e, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x08, 0x00, 0x01, 0x00,
0x01,
0x00, 0x02, 0x00, 0x02,
0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/*
* sorta wrong AEAD ID; replaced 0x0001 with 0xFFFF
* which is the export only pseudo-aead-id - that
* should not work in our test, same as the others,
* but worth a specific test, as it'll fail in a
* different manner
*/
static const unsigned char bin_bad_aeadid_ff[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0xFF,
0xFF, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/*
* An ECHConfigList with a bad ECHConfig
* (aead is 0xFFFF), followed by a good
* one.
*/
static const unsigned char bin_bad_then_good[] = {
0x00, 0x7c, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0xFF,
0xFF, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00, 0x20, 0x00,
0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2, 0xc5, 0xfe,
0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c, 0xa4, 0x33,
0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e, 0x5a, 0x42,
0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73, 0x60, 0x16,
0x3c, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01, 0x00,
0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* couple of harmless extensions */
static const unsigned char bin_ok_exts[] = {
0x00, 0x47, 0xfe, 0x0d, 0x00, 0x43, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x09,
0x0a, 0x0b, 0x00, 0x00, 0x0c, 0x0d, 0x00, 0x01,
0x02
};
/* one "mandatory" extension (high bit of type set) */
static const unsigned char bin_mand_ext[] = {
0x00, 0x47, 0xfe, 0x0d, 0x00, 0x43, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x09,
0x0a, 0x0b, 0x00, 0x00, 0xFc, 0x0d, 0x00, 0x01,
0x02
};
/* extension with bad length (0xFFFF) */
static const unsigned char bin_bad_inner_extlen[] = {
0x00, 0x47, 0xfe, 0x0d, 0x00, 0x43, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x09,
0x0a, 0x0b, 0x00, 0x00, 0x0c, 0x0d, 0x00, 0xFF,
0x02
};
/* good, other than a NUL inside the public_name */
static const unsigned char bin_nul_in_pn[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* good, other than a dot at the end of the public_name */
static const unsigned char bin_pn_dot_at_end[] = {
0x00, 0x3e, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x2e, 0x00, 0x00
};
/*
* An ECHConfigList with a good ECHConfig followed by a bad
* one with the 1st internal length (0xFFFF) too big
*/
static const unsigned char bin_good_then_bad[] = {
0x00, 0x7c, 0xfe, 0x0d, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
0xfe, 0x0d, 0xFF, 0xFF, 0xbb, 0x00, 0x20, 0x00,
0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2, 0xc5, 0xfe,
0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c, 0xa4, 0x33,
0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e, 0x5a, 0x42,
0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73, 0x60, 0x16,
0x3c, 0x00, 0x04, 0x00, 0x01, 0x00, 0x01, 0x00,
0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00
};
/* generally very short:-) */
static const unsigned char bin_short[] = {
0x00, 0x05, 0xfe, 0x0d, 0x00, 0x01, 0x01
};
/* kind of an empty value */
static const unsigned char bin_empty[] = {
0x00, 0x00
};
/*
* An ECHConfigList with an unsupported ECHConfig and
* that's too short.
*/
static const unsigned char bin_ver_short[] = {
0x00, 0x3e, 0xfe, 0xFF, 0x00, 0x3a, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/*
* too-long extension - OSSL_ECH_MAX_ECHCONFIGEXT_LEN is
* 512, this is 513 (0x0201), end of the 8-th line
* */
static const unsigned char bin_long_ext[] = {
0x02, 0x43, 0xfe, 0x0d, 0x02, 0x3f, 0xbb, 0x00,
0x20, 0x00, 0x20, 0x62, 0xc7, 0x60, 0x7b, 0xf2,
0xc5, 0xfe, 0x11, 0x08, 0x44, 0x6f, 0x13, 0x2c,
0xa4, 0x33, 0x9c, 0xf1, 0x9d, 0xf1, 0x55, 0x2e,
0x5a, 0x42, 0x96, 0x0f, 0xd0, 0x2c, 0x69, 0x73,
0x60, 0x16, 0x3c, 0x00, 0x04, 0x00, 0x01, 0x00,
0x01, 0x00, 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70,
0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x02, 0x05,
0xFF, 0xFF, 0x02, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00
};
/* struct for ingest test vector and results */
typedef struct INGEST_TV_T {
char *name; /* name for verbose output */
const unsigned char *tv; /* test vector */
size_t len; /* len(tv) - sizeof(tv) if binary, subtract 1 for strings */
int pemenc; /* whether PEM encoded (1) or not (0) */
int read; /* result expected from read function on tv */
int keysb4; /* the number of private keys expected before downselect */
int entsb4; /* the number of public keys b4 */
int index; /* the index to use for downselect */
int expected; /* the result expected from a downselect */
int keysaftr; /* the number of keys expected after downselect */
int entsaftr; /* the number of public keys after */
} ingest_tv_t;
static ingest_tv_t ingest_tvs[] = {
/* PEM test vectors */
{ "PEM basic/last", (unsigned char *)pem_kp1, sizeof(pem_kp1) - 1,
1, 1, 1, 1, OSSL_ECHSTORE_LAST, 1, 1, 1 },
{ "PEM basic/0", (unsigned char *)pem_pk1, sizeof(pem_pk1) - 1,
1, 1, 0, 1, 0, 1, 0, 1 },
{ "PEM basic/2nd", (unsigned char *)pem_pk1, sizeof(pem_pk1) - 1,
1, 1, 0, 1, 2, 0, 0, 1 },
{ "ECDSA priv + 25519 pub", (unsigned char *)pem_mismatch_priv,
sizeof(pem_mismatch_priv) - 1,
1, 0, 0, 0, 0, 0, 0, 0 },
{ "PEM string typo", (unsigned char *)pem_typo, sizeof(pem_typo) - 1,
1, 0, 0, 0, 0, 0, 0, 0 },
/* downselect from the 2, at each position */
{ "PEM 4->2/0", (unsigned char *)pem_4_to_2, sizeof(pem_4_to_2) - 1,
1, 1, 0, 2, 0, 1, 0, 1 },
{ "PEM 4->2/1", (unsigned char *)pem_4_to_2, sizeof(pem_4_to_2) - 1,
1, 1, 0, 2, 1, 1, 0, 1 },
/* in the next one below, downselect fails, so we still have 2 entries */
{ "PEM 4->2/2", (unsigned char *)pem_4_to_2, sizeof(pem_4_to_2) - 1,
1, 1, 0, 2, 3, 0, 0, 2 },
/* b64 test vectors */
{ "B64 basic/last", (unsigned char *)b64_pk1, sizeof(b64_pk1) - 1,
0, 1, 0, 1, OSSL_ECHSTORE_LAST, 1, 0, 1 },
{ "B64 6->3/2", (unsigned char *)b64_6_to_3, sizeof(b64_6_to_3) - 1,
0, 1, 0, 3, 2, 1, 0, 1 },
{ "B64 bad suitelen", (unsigned char *)b64_bad_cs, sizeof(b64_bad_cs) - 1,
0, 0, 0, 0, 0, 0, 0, 0 },
/* binary test vectors */
{ "bin 6->3/2", (unsigned char *)bin_6_to_3, sizeof(bin_6_to_3),
0, 1, 0, 3, 2, 1, 0, 1 },
{ "bin 2 symm suites", (unsigned char *)bin_multi_suite,
sizeof(bin_multi_suite),
0, 1, 0, 1, OSSL_ECHSTORE_LAST, 1, 0, 1 },
{ "bin all-zero pub", (unsigned char *)bin_zero, sizeof(bin_zero),
0, 1, 0, 1, OSSL_ECHSTORE_LAST, 1, 0, 1 },
{ "bin ok exts", (unsigned char *)bin_ok_exts, sizeof(bin_ok_exts),
0, 1, 0, 1, OSSL_ECHSTORE_LAST, 1, 0, 1 },
{ "bin bad ver", (unsigned char *)bin_bad_ver, sizeof(bin_bad_ver),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin 2 bad ver", (unsigned char *)bin_bad_ver2, sizeof(bin_bad_ver2),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad len", (unsigned char *)bin_bad_olen, sizeof(bin_bad_olen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad inner len", (unsigned char *)bin_bad_ilen, sizeof(bin_bad_ilen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad pk len", (unsigned char *)bin_bad_pklen, sizeof(bin_bad_pklen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad suitelen", (unsigned char *)bin_bad_cslen, sizeof(bin_bad_cslen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad pn len", (unsigned char *)bin_bad_pnlen, sizeof(bin_bad_pnlen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad extlen", (unsigned char *)bin_bad_extlen, sizeof(bin_bad_extlen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad kemid", (unsigned char *)bin_bad_kemid, sizeof(bin_bad_kemid),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad kdfid", (unsigned char *)bin_bad_kdfid, sizeof(bin_bad_kdfid),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad aeadid", (unsigned char *)bin_bad_aeadid, sizeof(bin_bad_aeadid),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin exp aeadid", (unsigned char *)bin_bad_aeadid_ff,
sizeof(bin_bad_aeadid_ff),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad,good", (unsigned char *)bin_bad_then_good,
sizeof(bin_bad_then_good),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin mand ext", (unsigned char *)bin_mand_ext, sizeof(bin_mand_ext),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin bad inner extlen", (unsigned char *)bin_bad_inner_extlen,
sizeof(bin_bad_inner_extlen),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin NUL in PN", (unsigned char *)bin_nul_in_pn, sizeof(bin_nul_in_pn),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin PN ends in dot", (unsigned char *)bin_pn_dot_at_end,
sizeof(bin_pn_dot_at_end),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin short", (unsigned char *)bin_short, sizeof(bin_short),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin empty", (unsigned char *)bin_empty, sizeof(bin_empty),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin ver short", (unsigned char *)bin_ver_short, sizeof(bin_ver_short),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin long ext", (unsigned char *)bin_long_ext, sizeof(bin_long_ext),
0, 0, 0, 0, 0, 0, 0, 0 },
{ "bin good then bad", (unsigned char *)bin_good_then_bad,
sizeof(bin_good_then_bad),
0, 0, 0, 0, 0, 0, 0, 0 },
};
/* similar, but slightly simpler setup for file reading tests */
typedef struct FNT_T {
char *fname; /* relative file name */
int read; /* expected result from a pem_read of that */
} fnt_t;
static fnt_t fnames[] = {
{ "ech-eg.pem", 1 },
{ "ech-mid.pem", 1 },
{ "ech-big.pem", 1 },
{ "ech-giant.pem", 0 },
{ "ech-rsa.pem", 0 },
};
/* string from which we construct varieties of HPKE suite */
static const char *kem_str_list[] = {
"P-256", "P-384", "P-521", "x25519", "x448",
};
static const char *kdf_str_list[] = {
"hkdf-sha256", "hkdf-sha384", "hkdf-sha512",
};
static const char *aead_str_list[] = {
"aes-128-gcm", "aes-256-gcm", "chacha20-poly1305",
};
typedef enum OPTION_choice {
OPT_ERR = -1,
OPT_EOF = 0,
OPT_VERBOSE,
OPT_TEST_ENUM
} OPTION_CHOICE;
const OPTIONS *test_get_options(void)
{
static const OPTIONS test_options[] = {
OPT_TEST_OPTIONS_DEFAULT_USAGE,
{ "v", OPT_VERBOSE, '-', "Enable verbose mode" },
{ OPT_HELP_STR, 1, '-', "Run ECH tests\n" },
{ NULL }
};
return test_options;
}
/*
* For the relevant test vector in our array above:
* - try decode
* - if not expected to decode, we're done
* - check we got the right number of keys/ECHConfig values
* - do some calls with getting info, downselecting etc. and
* check results as expected
* - do a write_pem call on the results
* - flush keys 'till now and check they're all gone
*/
static int ech_ingest_test(int run)
{
OSSL_ECHSTORE *es = NULL;
BIO *in = NULL, *out = NULL;
int i, rv = 0, keysb4, keysaftr, actual_ents = 0, has_priv, for_retry;
ingest_tv_t *tv = &ingest_tvs[run];
time_t now = 0, secs = 0;
char *pn = NULL, *ec = NULL;
if ((in = BIO_new(BIO_s_mem())) == NULL
|| BIO_write(in, tv->tv, tv->len) <= 0
|| (out = BIO_new(BIO_s_mem())) == NULL
|| (es = OSSL_ECHSTORE_new(NULL, NULL)) == NULL)
goto end;
if (verbose)
TEST_info("Iteration: %d %s", run + 1, tv->name);
/* just in case of bad edits to table */
if (tv->pemenc != 1 && tv->pemenc != 0) {
TEST_info("Bad test vector entry");
goto end;
}
if (tv->pemenc == 1
&& !TEST_int_eq(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY),
tv->read))
goto end;
if (tv->pemenc != 1
&& !TEST_int_eq(OSSL_ECHSTORE_read_echconfiglist(es, in), tv->read))
goto end;
/* if we provided a deliberately bad tv then we're done */
if (tv->read != 1) {
rv = 1;
goto end;
}
if (!TEST_true(OSSL_ECHSTORE_num_keys(es, &keysb4))
|| !TEST_true(OSSL_ECHSTORE_num_entries(es, &actual_ents))
|| !TEST_int_eq(keysb4, tv->keysb4)
|| !TEST_int_eq(actual_ents, tv->entsb4)
|| !TEST_int_eq(OSSL_ECHSTORE_get1_info(es, -1, &secs, &pn, &ec,
&has_priv, &for_retry), 0))
goto end;
OPENSSL_free(pn);
pn = NULL;
OPENSSL_free(ec);
ec = NULL;
for (i = 0; i != actual_ents; i++) {
if (!TEST_true(OSSL_ECHSTORE_get1_info(es, i, &secs, &pn, &ec,
&has_priv, &for_retry)))
goto end;
OPENSSL_free(pn);
pn = NULL;
OPENSSL_free(ec);
ec = NULL;
}
/* ensure silly index fails ok */
if (!TEST_false(OSSL_ECHSTORE_downselect(es, -20))
|| !TEST_int_eq(OSSL_ECHSTORE_downselect(es, tv->index), tv->expected)
|| !TEST_true(OSSL_ECHSTORE_num_keys(es, &keysaftr))
|| !TEST_int_eq(keysaftr, tv->keysaftr)
|| !TEST_true(OSSL_ECHSTORE_num_entries(es, &actual_ents))
|| !TEST_int_eq(actual_ents, tv->entsaftr)
|| !TEST_true(OSSL_ECHSTORE_write_pem(es, OSSL_ECHSTORE_LAST, out))
|| !TEST_true(OSSL_ECHSTORE_write_pem(es, OSSL_ECHSTORE_ALL, out))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 100, out)))
goto end;
now = time(0);
if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, now))
|| !TEST_true(OSSL_ECHSTORE_num_keys(es, &keysaftr))
|| !TEST_false(keysaftr))
goto end;
rv = 1;
end:
OPENSSL_free(pn);
OPENSSL_free(ec);
OSSL_ECHSTORE_free(es);
BIO_free_all(in);
BIO_free_all(out);
return rv;
}
/* make a bunch of calls with bad, mostly NULL, arguments */
static int ech_store_null_calls(void)
{
int rv = 0, count = 0, has_priv, for_retry;
OSSL_ECHSTORE *es = OSSL_ECHSTORE_new(NULL, NULL);
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
BIO *inout = BIO_new(BIO_s_mem());
EVP_PKEY *priv = EVP_PKEY_new();
time_t secs;
char *pn = NULL, *ec = NULL;
OSSL_ECHSTORE_free(NULL);
if (!TEST_false(OSSL_ECHSTORE_new_config(NULL, OSSL_ECH_CURRENT_VERSION,
0, "example.com", hpke_suite))
|| !TEST_false(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
0, NULL, hpke_suite))
|| !TEST_false(OSSL_ECHSTORE_new_config(es, 0xffff, 0,
"example.com", hpke_suite)))
goto end;
hpke_suite.kdf_id = 0xAAAA; /* a bad value */
if (!TEST_false(OSSL_ECHSTORE_new_config(es, OSSL_ECH_CURRENT_VERSION,
0, "example.com", hpke_suite))
|| !TEST_false(OSSL_ECHSTORE_write_pem(NULL, 0, inout))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 0, NULL))
|| !TEST_false(OSSL_ECHSTORE_write_pem(es, 100, inout))
|| !TEST_false(OSSL_ECHSTORE_read_echconfiglist(NULL, inout))
|| !TEST_false(OSSL_ECHSTORE_read_echconfiglist(es, NULL))
|| !TEST_false(OSSL_ECHSTORE_get1_info(NULL, 0, &secs, &pn, &ec,
&has_priv, &for_retry))
|| !TEST_false(OSSL_ECHSTORE_downselect(NULL, 0))
|| !TEST_false(OSSL_ECHSTORE_downselect(es, 100))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(NULL, priv,
inout, 0))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, NULL, inout, 0))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv, NULL, 0))
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv,
inout, 100))
/* this one fails 'cause priv has no real value, even if non NULL */
|| !TEST_false(OSSL_ECHSTORE_set1_key_and_read_pem(es, priv, inout,
OSSL_ECH_NO_RETRY))
|| !TEST_false(OSSL_ECHSTORE_read_pem(NULL, inout, OSSL_ECH_NO_RETRY))
|| !TEST_false(OSSL_ECHSTORE_read_pem(es, NULL, OSSL_ECH_NO_RETRY))
|| !TEST_false(OSSL_ECHSTORE_read_pem(es, inout, 100))
|| !TEST_false(OSSL_ECHSTORE_num_keys(NULL, &count))
|| !TEST_false(OSSL_ECHSTORE_num_keys(es, NULL))
|| !TEST_false(OSSL_ECHSTORE_flush_keys(NULL, 0))
|| !TEST_false(OSSL_ECHSTORE_flush_keys(es, -1))
|| !TEST_false(OSSL_ECHSTORE_num_entries(es, NULL)))
goto end;
rv = 1;
end:
OSSL_ECHSTORE_free(es);
BIO_free_all(inout);
EVP_PKEY_free(priv);
return rv;
}
/* read some files, some that work, some that fail */
static int ech_test_file_read(int run)
{
int rv = 0;
OSSL_ECHSTORE *es = NULL;
BIO *in = NULL;
fnt_t *ft = &fnames[run];
char *fullname = NULL;
size_t fnlen = 0;
es = OSSL_ECHSTORE_new(NULL, NULL);
if (es == NULL)
goto end;
fnlen = strlen(certsdir) + 1 + strlen(ft->fname) + 1;
fullname = OPENSSL_malloc(fnlen);
if (fullname == NULL)
goto end;
snprintf(fullname, fnlen, "%s/%s", certsdir, ft->fname);
if (verbose)
TEST_info("testing read of %s", fullname);
in = BIO_new_file(fullname, "r");
if (in == NULL) {
TEST_info("BIO_new_file failed for %s", ft->fname);
goto end;
}
if (!TEST_int_eq(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY),
ft->read))
goto end;
rv = 1;
end:
OPENSSL_free(fullname);
OSSL_ECHSTORE_free(es);
BIO_free_all(in);
return rv;
}
/* calls with bad, NULL, and simple, arguments, for generic code coverage */
static int ech_api_basic_calls(void)
{
int rv = 0;
SSL_CTX *ctx = NULL;
SSL *s = NULL;
OSSL_ECHSTORE *es = NULL, *es1 = NULL;
char *rinner, *inner = "inner.example.com";
char *router, *outer = "example.com";
unsigned char alpns[] = { 'h', '2' };
size_t alpns_len = sizeof(alpns);
char *gsuite = "X25519,hkdf-sha256,aes-256-gcm";
uint16_t gtype = 0xfe09;
unsigned char *rc = NULL;
size_t rclen = 0;
BIO *in = NULL;
/* NULL args */
if (!TEST_false(SSL_CTX_set1_echstore(NULL, NULL))
|| !TEST_false(SSL_set1_echstore(NULL, NULL))
|| !TEST_ptr_eq(SSL_CTX_get1_echstore(NULL), NULL)
|| !TEST_ptr_eq(SSL_get1_echstore(NULL), NULL)
|| !TEST_false(SSL_ech_set1_server_names(NULL, NULL, NULL, -1))
|| !TEST_false(SSL_ech_set1_outer_server_name(NULL, NULL, -1))
|| !TEST_false(SSL_CTX_ech_set1_outer_alpn_protos(NULL, NULL, -1))
|| !TEST_false(SSL_ech_set1_outer_alpn_protos(NULL, NULL, -1))
|| !TEST_false(SSL_ech_set1_grease_suite(NULL, NULL))
|| !TEST_false(SSL_ech_set_grease_type(NULL, 0)))
goto end;
SSL_CTX_ech_set_callback(NULL, NULL);
SSL_ech_set_callback(NULL, NULL);
if (!TEST_false(SSL_ech_get1_retry_config(NULL, NULL, NULL))
|| !TEST_false(SSL_CTX_ech_raw_decrypt(NULL, NULL, NULL, NULL,
NULL, 0, NULL, NULL,
NULL, NULL))
|| !TEST_int_eq(SSL_ech_get1_status(NULL, &rinner, &router),
SSL_ECH_STATUS_FAILED))
goto end;
/* add an ECHConfigList with extensions to exercise init code */
if (!TEST_ptr(es = OSSL_ECHSTORE_new(NULL, NULL))
|| !TEST_ptr(in = BIO_new(BIO_s_mem()))
|| !TEST_int_gt(BIO_write(in, bin_ok_exts, sizeof(bin_ok_exts)), 0)
|| !TEST_true(OSSL_ECHSTORE_read_echconfiglist(es, in))
|| !TEST_ptr(ctx = SSL_CTX_new_ex(NULL, NULL, TLS_server_method())))
goto end;
/* check status of SSL connection before OSSL_ECHSTORE set */
if (!TEST_ptr(s = SSL_new(ctx))
|| !TEST_int_eq(SSL_ech_get1_status(s, NULL, NULL),
SSL_ECH_STATUS_FAILED)
|| !TEST_int_eq(SSL_ech_get1_status(s, &rinner, &router),
SSL_ECH_STATUS_NOT_CONFIGURED))
goto end;
SSL_set_options(s, SSL_OP_ECH_GREASE);
if (!TEST_int_eq(SSL_ech_get1_status(s, &rinner, &router),
SSL_ECH_STATUS_GREASE))
goto end;
SSL_free(s);
s = NULL; /* for some other tests */
if (!TEST_true(SSL_CTX_set1_echstore(ctx, es)))
goto end;
if (!TEST_ptr((es1 = SSL_CTX_get1_echstore(ctx))))
goto end;
OSSL_ECHSTORE_free(es1);
es1 = NULL;
if (!TEST_false(SSL_set1_echstore(s, es)))
goto end;
/* do this one before SSL_new to exercise a bit of init code */
if (!TEST_true(SSL_CTX_ech_set1_outer_alpn_protos(ctx, alpns, alpns_len)))
goto end;
s = SSL_new(ctx);
if (!TEST_true(SSL_set1_echstore(s, es)))
goto end;
if (!TEST_ptr(es1 = SSL_get1_echstore(s)))
goto end;
OSSL_ECHSTORE_free(es1);
es1 = NULL;
if (!TEST_true(SSL_ech_set1_server_names(s, inner, outer, 0))
|| !TEST_true(SSL_ech_set1_outer_server_name(s, outer, 0))
|| !TEST_true(SSL_ech_set1_outer_alpn_protos(s, alpns, alpns_len))
|| !TEST_true(SSL_ech_set1_grease_suite(s, gsuite))
|| !TEST_true(SSL_ech_set_grease_type(s, gtype))
|| !TEST_true(SSL_ech_get1_retry_config(s, &rc, &rclen))
|| !TEST_false(rclen)
|| !TEST_ptr_eq(rc, NULL))
goto end;
SSL_CTX_ech_set_callback(ctx, test_cb);
SSL_ech_set_callback(s, test_cb);
/* all good */
rv = 1;
end:
BIO_free_all(in);
OSSL_ECHSTORE_free(es1);
OSSL_ECHSTORE_free(es);
SSL_CTX_free(ctx);
SSL_free(s);
return rv;
}
/*
* Test boringssl compatibility API. We don't need exhaustive
* tests here as this is a simple enough wrapper on things
* tested elsewhere.
*/
static int ech_boring_compat(void)
{
int rv = 0;
SSL_CTX *ctx = NULL;
SSL *s = NULL;
if (!TEST_false(SSL_set1_ech_config_list(NULL, NULL, 0))
|| !TEST_ptr(ctx = SSL_CTX_new_ex(NULL, NULL, TLS_server_method()))
|| !TEST_ptr(s = SSL_new(ctx))
|| !TEST_true(SSL_set1_ech_config_list(s, NULL, 0))
|| !TEST_true(SSL_set1_ech_config_list(s, (uint8_t *)b64_pk1,
sizeof(b64_pk1) - 1))
|| !TEST_true(SSL_set1_ech_config_list(s, (uint8_t *)bin_6_to_3,
sizeof(bin_6_to_3)))
/* test a fail */
|| !TEST_false(SSL_set1_ech_config_list(s, (uint8_t *)b64_pk1,
sizeof(b64_pk1) - 2)))
goto end;
rv = 1;
end:
SSL_CTX_free(ctx);
SSL_free(s);
return rv;
}
/* values that can be used in helper below */
# define OSSL_ECH_TEST_BASIC 0
# define OSSL_ECH_TEST_HRR 1
# define OSSL_ECH_TEST_EARLY 2
# define OSSL_ECH_TEST_CUSTOM 3
/*
* @brief ECH roundtrip test helper
* @param idx specifies which ciphersuite
* @araam combo specifies which particular test we want to roundtrip
* @return 1 for good, 0 for bad
*
* The idx input here is from 0..44 and is broken down into a
* kem, kdf and aead. If you run in verbose more ("-v") then
* there'll be a "Doing: ..." trace line that says which suite
* is being tested in string form.
*
* The combo input is one of the #define'd OSSL_ECH_TEST_*
* values above.
*
* TODO(ECH): we're not yet really attempting ECH, but we currently
* set the inputs as if we were doing ECH, yet don't expect to see
* real ECH status outcomes, so while we do make calls to get that
* status outcome, we don't compare vs. real expected results.
* That's done via the "if (0 &&" clauses below which will be
* removed once ECH is really being attempted.
*/
static int test_ech_roundtrip_helper(int idx, int combo)
{
int res = 0, kemind, kdfind, aeadind, kemsz, kdfsz, aeadsz;
char suitestr[100];
OSSL_ECHSTORE *es = NULL;
OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT;
uint16_t ech_version = OSSL_ECH_CURRENT_VERSION;
uint8_t max_name_length = 0;
char *public_name = "example.com";
SSL_CTX *cctx = NULL, *sctx = NULL;
SSL *clientssl = NULL, *serverssl = NULL;
int clientstatus, serverstatus;
char *cinner = NULL, *couter = NULL, *sinner = NULL, *souter = NULL;
SSL_SESSION *sess = NULL;
unsigned char ed[21];
size_t written = 0, readbytes = 0;
unsigned char buf[1024];
unsigned int context;
int server = 1, client = 0;
/* split idx into kemind, kdfind, aeadind */
kemsz = OSSL_NELEM(kem_str_list);
kdfsz = OSSL_NELEM(kdf_str_list);
aeadsz = OSSL_NELEM(aead_str_list);
kemind = (idx / (kdfsz * aeadsz)) % kemsz;
kdfind = (idx / aeadsz) % kdfsz;
aeadind = idx % aeadsz;
/* initialise early data stuff, just in case */
memset(ed, 'A', sizeof(ed));
snprintf(suitestr, 100, "%s,%s,%s", kem_str_list[kemind],
kdf_str_list[kdfind], aead_str_list[aeadind]);
if (verbose)
TEST_info("Doing: iter: %d, suite: %s", idx, suitestr);
if (!TEST_true(OSSL_HPKE_str2suite(suitestr, &hpke_suite))
|| !TEST_ptr(es = OSSL_ECHSTORE_new(libctx, propq))
|| !TEST_true(OSSL_ECHSTORE_new_config(es, ech_version, max_name_length,
public_name, hpke_suite))
|| !TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(),
TLS_client_method(),
TLS1_3_VERSION, TLS1_3_VERSION,
&sctx, &cctx, cert, privkey)))
goto end;
if (combo == OSSL_ECH_TEST_EARLY) {
/* just to keep the format checker happy :-) */
int lrv = 0;
if (!TEST_true(SSL_CTX_set_options(sctx, SSL_OP_NO_ANTI_REPLAY))
|| !TEST_true(SSL_CTX_set_max_early_data(sctx,
SSL3_RT_MAX_PLAIN_LENGTH)))
goto end;
lrv = SSL_CTX_set_recv_max_early_data(sctx, SSL3_RT_MAX_PLAIN_LENGTH);
if (!TEST_true(lrv))
goto end;
}
if (combo == OSSL_ECH_TEST_CUSTOM) {
/* add custom CH ext to client and server */
context = SSL_EXT_CLIENT_HELLO;
if (!TEST_true(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE1, context,
new_add_cb, new_free_cb,
&client, new_parse_cb, &client))
|| !TEST_true(SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE1, context,
new_add_cb, new_free_cb,
&server, new_parse_cb, &server))
|| !TEST_true(SSL_CTX_add_custom_ext(cctx, TEST_EXT_TYPE2, context,
new_add_cb, NULL,
&client, NULL, &client))
|| !TEST_true(SSL_CTX_add_custom_ext(sctx, TEST_EXT_TYPE2, context,
new_add_cb, NULL,
&server, NULL, &server)))
goto end;
}
if (!TEST_true(SSL_CTX_set1_echstore(cctx, es))
|| !TEST_true(SSL_CTX_set1_echstore(sctx, es))
|| !TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL)))
goto end;
if (combo == OSSL_ECH_TEST_HRR
&& !TEST_true(SSL_set1_groups_list(serverssl, "P-384")))
goto end;
if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))
|| !TEST_true(create_ssl_connection(serverssl, clientssl,
SSL_ERROR_NONE)))
goto end;
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
if (verbose)
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* override cert verification */
SSL_set_verify_result(clientssl, X509_V_OK);
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
if (verbose)
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* all good */
if (combo == OSSL_ECH_TEST_BASIC
|| combo == OSSL_ECH_TEST_HRR
|| combo == OSSL_ECH_TEST_CUSTOM) {
res = 1;
goto end;
}
/* continue for EARLY test */
if (combo != OSSL_ECH_TEST_EARLY)
goto end;
/* shutdown for start over */
sess = SSL_get1_session(clientssl);
OPENSSL_free(sinner);
OPENSSL_free(souter);
OPENSSL_free(cinner);
OPENSSL_free(couter);
sinner = souter = cinner = couter = NULL;
SSL_shutdown(clientssl);
SSL_shutdown(serverssl);
SSL_free(serverssl);
SSL_free(clientssl);
serverssl = clientssl = NULL;
/* second connection */
if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl,
&clientssl, NULL, NULL))
|| !TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))
|| !TEST_true(SSL_set_session(clientssl, sess))
|| !TEST_true(SSL_write_early_data(clientssl, ed, sizeof(ed), &written))
|| !TEST_size_t_eq(written, sizeof(ed))
|| !TEST_int_eq(SSL_read_early_data(serverssl, buf,
sizeof(buf), &readbytes),
SSL_READ_EARLY_DATA_SUCCESS)
|| !TEST_size_t_eq(written, readbytes))
goto end;
/*
* Server should be able to write data, and client should be able to
* read it.
*/
if (!TEST_true(SSL_write_early_data(serverssl, ed, sizeof(ed), &written))
|| !TEST_size_t_eq(written, sizeof(ed))
|| !TEST_true(SSL_read_ex(clientssl, buf, sizeof(buf), &readbytes))
|| !TEST_mem_eq(buf, readbytes, ed, sizeof(ed)))
goto end;
serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter);
if (verbose)
TEST_info("server status %d, %s, %s", serverstatus, sinner, souter);
if (0 && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* override cert verification */
SSL_set_verify_result(clientssl, X509_V_OK);
clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter);
if (verbose)
TEST_info("client status %d, %s, %s", clientstatus, cinner, couter);
if (0 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS))
goto end;
/* all good */
res = 1;
end:
OSSL_ECHSTORE_free(es);
OPENSSL_free(sinner);
OPENSSL_free(souter);
OPENSSL_free(cinner);
OPENSSL_free(couter);
SSL_SESSION_free(sess);
SSL_free(clientssl);
SSL_free(serverssl);
SSL_CTX_free(cctx);
SSL_CTX_free(sctx);
return res;
}
/* Test roundtrip with ECH for any suite */
static int test_ech_suites(int idx)
{
if (verbose)
TEST_info("Doing: test_ech_suites");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_BASIC);
}
/* ECH with HRR for the given suite */
static int test_ech_hrr(int idx)
{
if (verbose)
TEST_info("Doing: test_ech_hrr");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_HRR);
}
/* ECH with early data for the given suite */
static int test_ech_early(int idx)
{
if (verbose)
TEST_info("Doing: test_ech_early");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_EARLY);
}
/* Test a roundtrip with ECH, and a custom CH extension */
static int ech_custom_test(int idx)
{
if (verbose)
TEST_info("Doing: ech_custom_test");
return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_CUSTOM);
}
#endif
int setup_tests(void)
{
#ifndef OPENSSL_NO_ECH
OPTION_CHOICE o;
int suite_combos;
while ((o = opt_next()) != OPT_EOF) {
switch (o) {
case OPT_VERBOSE:
verbose = 1;
break;
case OPT_TEST_CASES:
break;
default:
return 0;
}
}
certsdir = test_get_argument(0);
if (certsdir == NULL)
certsdir = DEF_CERTS_DIR;
cert = test_mk_file_path(certsdir, "echserver.pem");
if (cert == NULL)
goto err;
privkey = test_mk_file_path(certsdir, "echserver.key");
if (privkey == NULL)
goto err;
rootcert = test_mk_file_path(certsdir, "rootcert.pem");
if (rootcert == NULL)
goto err;
ADD_ALL_TESTS(ech_ingest_test, OSSL_NELEM(ingest_tvs));
ADD_TEST(ech_store_null_calls);
ADD_ALL_TESTS(ech_test_file_read, OSSL_NELEM(fnames));
ADD_TEST(ech_api_basic_calls);
ADD_TEST(ech_boring_compat);
suite_combos = OSSL_NELEM(kem_str_list) * OSSL_NELEM(kdf_str_list)
* OSSL_NELEM(aead_str_list);
ADD_ALL_TESTS(test_ech_suites, suite_combos);
ADD_ALL_TESTS(test_ech_hrr, suite_combos);
ADD_ALL_TESTS(test_ech_early, suite_combos);
ADD_ALL_TESTS(ech_custom_test, suite_combos);
/* TODO(ECH): add more test code as other PRs done */
return 1;
err:
return 0;
#endif
return 1;
}
void cleanup_tests(void)
{
#ifndef OPENSSL_NO_ECH
OPENSSL_free(cert);
OPENSSL_free(privkey);
OPENSSL_free(rootcert);
#endif
}