/
niceSOFT
/
openssl
Обзор
Документация
Войти
/
niceSOFT
/
openssl
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/ech_test.c
2 091 строка
81 KB
Andrew Dinh
Use %zu for printing size_t values
15 июн 2026, 22:24
15 июн 2026, 22:24
145aea4
Код
Авторство
О чём код?
/* * Copyright 2024-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 <openssl/ssl.h> #include <openssl/hpke.h> #include "testutil.h" #include "helpers/ssltestlib.h" #include "internal/packet.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; static int ch_test_cb_ok = 0; /* ECH callback */ static unsigned int ech_test_cb(SSL *s, const char *str) { if (verbose) TEST_info("ech_test_cb called: str=\n%s", str); return 1; } /* ClientHello callback */ static int ch_test_cb(SSL *ssl, int *al, void *arg) { char *servername = NULL; const unsigned char *pos; size_t remaining; unsigned int servname_type; PACKET pkt, sni, hostname; if (verbose) { TEST_info("ch_test_cb called"); if (SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_ech, &pos, &remaining)) { TEST_info("there is an ECH extension"); } else { TEST_info("there is NO ECH extension"); } } if (!SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &pos, &remaining) || remaining <= 2) goto give_up; if (!PACKET_buf_init(&pkt, pos, remaining) || !PACKET_as_length_prefixed_2(&pkt, &sni) || !PACKET_get_1(&sni, &servname_type) || servname_type != TLSEXT_NAMETYPE_host_name || !PACKET_as_length_prefixed_2(&sni, &hostname) || (PACKET_remaining(&hostname) > TLSEXT_MAXLEN_host_name) || PACKET_contains_zero_byte(&hostname) || !PACKET_strndup(&hostname, &servername)) goto give_up; if (verbose) TEST_info("servername: %s", servername); OPENSSL_free(servername); /* signal to caller all is good */ ch_test_cb_ok = 1; return 1; give_up: return 0; } /* * 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"; static const char ec_kp1[] = "AD7+DQA6bAAgACCY7B0f/3KvHIFdoqFaObdU8YYU+MdBf4vzbLhAAL2QCwAEAAEAAQALZXhhbXBsZS5jb20AAA=="; static size_t ec_kp1len = sizeof(ec_kp1) - 1; /* * x25519 ech key pair with public key front.server.example, used for * in_out test */ static const char pem_kp2[] = "-----BEGIN PRIVATE KEY-----\n" "MC4CAQAwBQYDK2VuBCIEIEjRn9R/gwDu11v6bLKaf0AGoe5Etl2g6nU1GdQLTHNe\n" "-----END PRIVATE KEY-----\n" "-----BEGIN ECHCONFIG-----\n" "AEf+DQBDvQAgACCr9pErR7E/gNeoni+0YpDZaMd7XN+hFnCN+H0Xnm1EHQAEAAEAAQAUZnJvbnQuc2VydmVyLmV4YW1wbGUAAA==\n" "-----END ECHCONFIG-----\n"; static const char ec_kp2[] = "AEf+DQBDvQAgACCr9pErR7E/gNeoni+0YpDZaMd7XN+hFnCN+H0Xnm1EHQAEAAEAAQAUZnJvbnQuc2VydmVyLmV4YW1wbGUAAA=="; static size_t ec_kp2len = sizeof(ec_kp2) - 1; /* another, used in grease/retry-config tests, pn: f1.server.example */ static const char pem_kp3[] = "-----BEGIN PRIVATE KEY-----\n" "MC4CAQAwBQYDK2VuBCIEIDDbFFGbdUUQgKJzx6zaqn0rE8Bi9DJQpkfbAL6HHwtu\n" "-----END PRIVATE KEY-----\n" "-----BEGIN ECHCONFIG-----\n" "AET+DQBAYQAgACAUpnXhRlufbhe61F02+NR7xVA3200ujwOp2JLivunoQAAEAAEA\n" "AQARZjEuc2VydmVyLmV4YW1wbGUAAA==\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[] = { { "echdir/ech-eg.pem", 1 }, { "echdir/ech-mid.pem", 1 }, { "echdir/ech-big.pem", 1 }, { "echdir/ech-giant.pem", 0 }, { "echdir/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", #if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) "chacha20-poly1305", #endif }; 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 secs = 0, add_time = 0, flush_time = 0; char *pn = NULL, *ec = NULL; if ((in = BIO_new(BIO_s_mem())) == NULL || BIO_write(in, tv->tv, (int)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; } add_time = time(0); 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; flush_time = time(0); /* * Occasionally, flush_time will be 1 more than add_time. We'll * check for that as that should catch a few more code paths * in the flush_keys API. * When flush_time is 1 more, we may or may not have flushed * the one and only key (depending on which "side" of the second * it was generated, so we may be left with 0 or 1 keys. */ if (!TEST_true(OSSL_ECHSTORE_flush_keys(es, flush_time - add_time)) || !TEST_int_eq(OSSL_ECHSTORE_num_keys(es, &keysaftr), 1) || ((flush_time <= add_time) && !TEST_int_eq(keysaftr, 0)) || ((flush_time > add_time) && !TEST_int_eq(keysaftr, 1) && !TEST_int_eq(keysaftr, 0))) { TEST_info("Flush time: %lld, add_time: %lld", (long long)flush_time, (long long)add_time); 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; BIO_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 = NULL, *inner = "inner.example.com"; char *router = NULL, *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_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, ech_test_cb); SSL_ech_set_callback(s, ech_test_cb); /* all good */ rv = 1; end: BIO_free_all(in); OSSL_ECHSTORE_free(es1); OSSL_ECHSTORE_free(es); OPENSSL_free(router); OPENSSL_free(rinner); 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; } /* * Check whether various public_name values are good or bad according to * our RFC 9849 checker, which imposes some oddball restrictions on those. * Read section 6.1.7 of RFC 9849 for details. */ static int ech_bad_public_names(void) { int rv = 0, i; OSSL_ECHSTORE *es = NULL; OSSL_HPKE_SUITE hpke_suite = OSSL_HPKE_SUITE_DEFAULT; const char *bad_names[] = { ".dot.", /* leading dot */ "dot.", /* trailing dot */ ".dot", /* check both, why not */ /* a label > 62 chars (70 in this case) */ "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij.org", /* last label numeric */ "last.one.is.numeric.456", "456", /* last label ascii-hex */ "last.ah.0x123", "0x123" }; const char *good_names[] = { "example.com", "0x123.stuff", "0x", "a-b.c", "example.com.c", "a.b.0x1234567890abcdefX", "a.b.1234567890Y" }; if (!TEST_ptr(es = OSSL_ECHSTORE_new(libctx, propq))) goto end; for (i = 0; i != OSSL_NELEM(bad_names); i++) { if (verbose) TEST_info("checking bad name |%s|", bad_names[i]); if (!TEST_false(OSSL_ECHSTORE_new_config(es, 0xfe0d, 0, bad_names[i], hpke_suite))) { if (verbose) TEST_info("bad name |%s| erroneously accepted", bad_names[i]); goto end; } } for (i = 0; i != OSSL_NELEM(good_names); i++) { if (verbose) TEST_info("checking good name |%s|", good_names[i]); if (!TEST_true(OSSL_ECHSTORE_new_config(es, 0xfe0d, 0, good_names[i], hpke_suite))) { if (verbose) TEST_info("good name |%s| erroneously rejected", good_names[i]); goto end; } } rv = 1; end: OSSL_ECHSTORE_free(es); 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 #define OSSL_ECH_TEST_ENOE 4 /* early + no-ech */ #define OSSL_ECH_TEST_CBS 5 /* test callbacks */ #define OSSL_ECH_TEST_V12 6 /* test TLSv1.2 */ #define OSSL_ECH_TEST_NO_INNER 7 /* test no inner SNI */ /* note: early-data is prohibited after HRR so no tests for that */ /* * @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. */ static int test_ech_roundtrip_helper(int idx, int combo) { int res = 0, kemind, kdfind, aeadind, kemsz, kdfsz, aeadsz; int clientstatus, serverstatus, server = 1, client = 0; unsigned int context; 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", suitestr[100]; SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; char *cinner = NULL, *couter = NULL, *sinner = NULL, *souter = NULL; SSL_SESSION *sess = NULL; size_t written = 0, readbytes = 0; unsigned char ed[21], buf[1024]; /* 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)); BIO_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); /* * Set a max name length just to exercise more code. * We may as well use the index, just to make it vary:-) */ if (combo == OSSL_ECH_TEST_NO_INNER) max_name_length = (idx % 256); 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_V12) { /* force client to TLSv1.2 and later fail as expected */ if (!TEST_true(SSL_CTX_set_max_proto_version(cctx, TLS1_2_VERSION))) goto end; if (!TEST_true(SSL_CTX_set_min_proto_version(cctx, TLS1_2_VERSION))) goto end; } if (combo == OSSL_ECH_TEST_EARLY || combo == OSSL_ECH_TEST_ENOE) { 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)) || !TEST_true(SSL_CTX_set_recv_max_early_data(sctx, SSL3_RT_MAX_PLAIN_LENGTH))) goto end; } if (combo == OSSL_ECH_TEST_CUSTOM) { context = SSL_EXT_CLIENT_HELLO; /* add custom CH ext to client/server */ 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 (combo == OSSL_ECH_TEST_CBS) { SSL_CTX_ech_set_callback(sctx, ech_test_cb); SSL_CTX_set_client_hello_cb(sctx, ch_test_cb, NULL); } if (combo != OSSL_ECH_TEST_ENOE && !TEST_true(SSL_CTX_set1_echstore(cctx, es))) goto end; /* set callback for client, just to exercise code */ SSL_CTX_ech_set_callback(cctx, ech_test_cb); if (!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 (combo != OSSL_ECH_TEST_NO_INNER && !TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))) goto end; if (combo == OSSL_ECH_TEST_V12) { if (!TEST_false(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) goto end; res = 1; goto end; } else { if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) 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); serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter); if (verbose) TEST_info("server status %d, %s, %s", serverstatus, sinner, souter); if (combo != OSSL_ECH_TEST_ENOE && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS)) goto end; if (combo == OSSL_ECH_TEST_ENOE && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_NOT_TRIED)) goto end; if (combo != OSSL_ECH_TEST_ENOE && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) goto end; if (combo == OSSL_ECH_TEST_ENOE && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_NOT_CONFIGURED)) goto end; if (combo == OSSL_ECH_TEST_CBS && !TEST_int_eq(ch_test_cb_ok, 1)) goto end; /* all good */ if (combo == OSSL_ECH_TEST_BASIC || combo == OSSL_ECH_TEST_HRR || combo == OSSL_ECH_TEST_CUSTOM || combo == OSSL_ECH_TEST_CBS || combo == OSSL_ECH_TEST_NO_INNER) { res = 1; goto end; } /* continue for EARLY test */ if (combo != OSSL_ECH_TEST_EARLY && combo != OSSL_ECH_TEST_ENOE) 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; /* 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); serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter); if (verbose) TEST_info("server status %d, %s, %s", serverstatus, sinner, souter); if (combo != OSSL_ECH_TEST_ENOE && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS)) goto end; if (combo == OSSL_ECH_TEST_ENOE && !TEST_int_eq(serverstatus, SSL_ECH_STATUS_NOT_TRIED)) goto end; if (combo != OSSL_ECH_TEST_ENOE && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) goto end; if (combo == OSSL_ECH_TEST_ENOE && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_NOT_CONFIGURED)) 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); ch_test_cb_ok = 0; 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 no inner SNI for the given suite */ static int test_ech_no_inner(int idx) { if (verbose) TEST_info("Doing: test_ech_no_inner"); return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_NO_INNER); } /* 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); } /* Test a roundtrip with No ECH, and early data */ static int ech_enoe_test(int idx) { if (verbose) TEST_info("Doing: ech_no ech + early test "); return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_ENOE); } /* Test a roundtrip with ECH, and callbacks */ static int ech_cb_test(int idx) { if (verbose) TEST_info("Doing: ech + callbacks test "); return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_CBS); } /* Test a roundtrip (fails) with ECH but a TLSv1.2 SSL_CTX */ static int ech_v12_test(int idx) { if (verbose) TEST_info("Doing: ech TLSv1.2 test "); return test_ech_roundtrip_helper(idx, OSSL_ECH_TEST_V12); } /* * Test roundtrip with SNI/ALPN variations. * Inner and outer names can be supplied to SSL_CTX or SSL * connection via ECH APIs, and inner can be supplied via * the existing non-ECH API. We can specify that no outer * SNI at all be sent if we want. If an outer SNI value is * supplied via the ECH API then that over-rides the * public_name field from the ECHConfig, which in this * cases will be example.com. We have the option of setting * both inner, outer and no_outer setting via eiher: * * int SSL_ech_set1_server_names(SSL *s, const char *inner_name, * const char *outer_name, int no_outer); * int SSL_ech_set1_outer_server_name(SSL *s, const char *outer_name, * int no_outer); * * So there's a bunch of cases to test, as usual we pick * between 'em using the idx parameter. * * idx : case * 0 : set no names via ECH APIs; * set inner to inner.example.com non-ECH API * expect public_name as outer * 1 : as for 0, but additionally: * set NULL and "no_outer" via set_outer API * 2 : as for 1, but additionally: * set non-NULL outer and "no_outer" via set_outer API * 3 : override outer via ECH API * 4 : like 1, but using set_server_names API * 5 : like 2, but using set_server_names API * 6 : like 3, but using set_server_names API * 7 : like 4, but overriding previous call to non-ECH SNI * 8 : like 5, but overriding previous call to non-ECH SNI * 9 : like 6, but overriding previous call to non-ECH SNI * 10 : like 7, but reversing call order * 11 : like 8, but reversing call order * 12 : like 9, but reversing call order * 13 : like 1, but with a NULL outer input to API * that's a bit pointless as it's more or less a NO-OP * but worth checking */ static int ech_in_out_test(int idx) { int res = 0, cres = 0, sres = 0, clientstatus, serverstatus; SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; char *non_ech_sni = "trad.server.example"; /* SNI set via non-ECH API */ char *supplied_inner = "inner.server.example"; /* inner set via ECH API */ char *supplied_outer = "outer.server.example"; /* outer set via ECH API */ char *public_name = "front.server.example"; /* we know that's inside pem_kp2 */ char *cinner = NULL, *couter = NULL, *sinner = NULL, *souter = NULL; unsigned char alpn_inner[] = { /* "inner, secret, http/1.1" */ 0x05, 0x69, 0x6e, 0x6e, 0x65, 0x72, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 }; size_t alpn_inner_len = sizeof(alpn_inner); unsigned char alpn_outer[] = { /* "outer, public, h2" */ 0x05, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x02, 0x68, 0x32 }; size_t alpn_outer_len = sizeof(alpn_outer); char *expected_inner = NULL, *expected_outer = NULL; BIO *in = NULL; OSSL_ECHSTORE *es = NULL; /* make an OSSL_ECHSTORE for pem_kp2 */ if ((in = BIO_new(BIO_s_mem())) == NULL || BIO_write(in, pem_kp2, (int)strlen(pem_kp2)) <= 0 || !TEST_ptr(es = OSSL_ECHSTORE_new(libctx, propq)) || !TEST_true(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_FOR_RETRY)) || !TEST_true(create_ssl_ctx_pair(libctx, TLS_server_method(), TLS_client_method(), TLS1_3_VERSION, TLS1_3_VERSION, &sctx, &cctx, cert, privkey)) || !TEST_true(SSL_CTX_set1_echstore(sctx, es)) || !TEST_false(SSL_CTX_set_alpn_protos(cctx, alpn_inner, (unsigned int)alpn_inner_len)) || !TEST_true(SSL_CTX_ech_set1_outer_alpn_protos(cctx, alpn_outer, alpn_outer_len)) || !TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL)) || !TEST_true(SSL_set1_ech_config_list(clientssl, (unsigned char *)ec_kp2, ec_kp2len))) goto end; if (idx == 0) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni))) goto end; expected_inner = non_ech_sni; expected_outer = public_name; } if (idx == 1) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni)) || !TEST_true(SSL_ech_set1_outer_server_name(clientssl, NULL, 1))) goto end; expected_inner = non_ech_sni; expected_outer = NULL; } if (idx == 2) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni)) || !TEST_true(SSL_ech_set1_outer_server_name(clientssl, "blah", 1))) goto end; expected_inner = non_ech_sni; expected_outer = NULL; } if (idx == 3) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni)) || !TEST_true(SSL_ech_set1_outer_server_name(clientssl, supplied_outer, 0)) || !TEST_true(SSL_ech_set1_outer_alpn_protos(clientssl, alpn_outer, alpn_outer_len))) goto end; expected_inner = non_ech_sni; expected_outer = supplied_outer; } if (idx == 4) { if (!TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, NULL, 0))) goto end; expected_inner = supplied_inner; expected_outer = public_name; } if (idx == 5) { if (!TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, "blah", 1))) goto end; expected_inner = supplied_inner; expected_outer = NULL; } if (idx == 6) { if (!TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, supplied_outer, 0))) goto end; expected_inner = supplied_inner; expected_outer = supplied_outer; } if (idx == 7) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "blah")) || !TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, NULL, 0))) goto end; expected_inner = supplied_inner; expected_outer = public_name; } if (idx == 8) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "blah")) || !TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, "blah", 1))) goto end; expected_inner = supplied_inner; expected_outer = NULL; } if (idx == 9) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "blah")) || !TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, supplied_outer, 0))) goto end; expected_inner = supplied_inner; expected_outer = supplied_outer; } if (idx == 10) { if (!TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, NULL, 0)) || !TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni))) goto end; expected_inner = non_ech_sni; expected_outer = public_name; } if (idx == 11) { if (!TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, "blah", 1)) || !TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni))) goto end; expected_inner = non_ech_sni; expected_outer = NULL; } if (idx == 12) { if (!TEST_true(SSL_ech_set1_server_names(clientssl, supplied_inner, supplied_outer, 0)) || !TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni))) goto end; expected_inner = non_ech_sni; expected_outer = supplied_outer; } if (idx == 13) { if (!TEST_true(SSL_set_tlsext_host_name(clientssl, non_ech_sni)) || !TEST_true(SSL_ech_set1_outer_server_name(clientssl, NULL, 0))) goto end; expected_inner = non_ech_sni; expected_outer = public_name; } if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) goto end; serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter); if (!TEST_int_eq(serverstatus, SSL_ECH_STATUS_SUCCESS)) goto end; SSL_set_verify_result(clientssl, X509_V_OK); /* override cert check */ clientstatus = SSL_ech_get1_status(clientssl, &cinner, &couter); if (!TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) goto end; cres = sres = 0; /* check result vs. expected */ if ((expected_inner == NULL && cinner == NULL) || (expected_inner != NULL && cinner != NULL && strlen(expected_inner) == strlen(cinner) && strcmp(expected_inner, cinner) == 0)) cres = 1; if (!TEST_int_eq(cres, 1)) goto end; if ((expected_inner == NULL && sinner == NULL) || (expected_inner != NULL && sinner != NULL && strlen(expected_inner) == strlen(sinner) && strcmp(expected_inner, sinner) == 0)) sres = 1; if (!TEST_int_eq(sres, 1)) goto end; cres = sres = 0; if ((expected_outer == NULL && couter == NULL) || (expected_outer != NULL && couter != NULL && strlen(expected_outer) == strlen(couter) && strcmp(expected_outer, couter) == 0)) cres = 1; if (!TEST_int_eq(cres, 1)) goto end; if ((expected_outer == NULL && souter == NULL) || (expected_outer != NULL && souter != NULL && strlen(expected_outer) == strlen(souter) && strcmp(expected_outer, souter) == 0)) sres = 1; if (!TEST_int_eq(sres, 1)) goto end; res = 1; /* all good */ end: OSSL_ECHSTORE_free(es); BIO_free_all(in); OPENSSL_free(sinner); OPENSSL_free(souter); OPENSSL_free(cinner); OPENSSL_free(couter); SSL_free(clientssl); SSL_free(serverssl); SSL_CTX_free(cctx); SSL_CTX_free(sctx); return res; } /* Test roundtrip with GREASE'd ECH, then again with retry-config */ static int ech_grease_test(int idx) { int res = 0, clientstatus, serverstatus; SSL_CTX *cctx = NULL, *sctx = NULL; SSL *clientssl = NULL, *serverssl = NULL; char *cinner = NULL, *couter = NULL, *sinner = NULL, *souter = NULL; unsigned char *retryconfig = NULL; size_t retryconfiglen = 0; X509_STORE *ch = NULL; OSSL_ECHSTORE *es = NULL; BIO *in; /* make OSSL_ECHSTORE vars for pem_kp2 */ if ((in = BIO_new(BIO_s_mem())) == NULL || BIO_write(in, pem_kp2, (int)strlen(pem_kp2)) <= 0 || !TEST_ptr(es = OSSL_ECHSTORE_new(libctx, propq)) || !TEST_true(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_FOR_RETRY))) goto end; if (idx == 2) { /* * In our third test iteration set various other ECH configs, to make * for a bigger retry-config. (It's ok that we set the same key pair * a few times here.) */ BIO_free_all(in); in = NULL; if ((in = BIO_new(BIO_s_mem())) == NULL || BIO_write(in, pem_kp3, (int)strlen(pem_kp3)) <= 0 || !TEST_true(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY))) goto end; BIO_free_all(in); in = NULL; if ((in = BIO_new(BIO_s_mem())) == NULL || BIO_write(in, pem_kp3, (int)strlen(pem_kp3)) <= 0 || !TEST_true(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_FOR_RETRY))) goto end; BIO_free_all(in); in = NULL; if ((in = BIO_new(BIO_s_mem())) == NULL || BIO_write(in, pem_kp3, (int)strlen(pem_kp3)) <= 0 || !TEST_true(OSSL_ECHSTORE_read_pem(es, in, OSSL_ECH_NO_RETRY))) goto end; } BIO_free_all(in); in = NULL; if (!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 (!TEST_true(SSL_CTX_set1_echstore(sctx, es))) goto end; /* set the client GREASE flag via SSL_CTX 1st time, and via SSL* 2nd */ if (idx == 0 && !TEST_true(SSL_CTX_set_options(cctx, SSL_OP_ECH_GREASE))) goto end; if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL))) goto end; if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "back.server.example"))) goto end; /* set the GREASE flag via SSL_CTX 1st time, and via SSL* 2nd & 3rd */ if (idx >= 1 && !TEST_true(SSL_set_options(clientssl, SSL_OP_ECH_GREASE))) goto end; /* 3rd time, fail to set a bad grease suite, then set a good one */ if (idx == 2 && !TEST_false(SSL_ech_set1_grease_suite(clientssl, "notanhpkesuite"))) goto end; if (idx == 2 && !TEST_true(SSL_ech_set1_grease_suite(clientssl, "x25519,hkdf-sha384,aes-256-gcm"))) goto end; /* for 4th test, set a real but wrong ECHConfig which'll override GREASE setting */ if (idx == 3) { if (!TEST_true(SSL_set1_ech_config_list(clientssl, (unsigned char *)ec_kp1, ec_kp1len))) goto end; /* real but wrong => failure, due to ECH */ if (!TEST_false(create_ssl_connection(serverssl, clientssl, SSL_R_ECH_REQUIRED))) goto end; } else { /* asked for GREASE => should work */ if (!TEST_true(create_ssl_connection(serverssl, clientssl, SSL_ERROR_NONE))) goto end; } serverstatus = SSL_ech_get1_status(serverssl, &sinner, &souter); if (verbose) TEST_info("ech_grease_test: server status %d, %s, %s", serverstatus, sinner, souter); if (!TEST_int_eq(serverstatus, SSL_ECH_STATUS_GREASE)) 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("ech_grease_test: client status %d, %s, %s", clientstatus, cinner, couter); if (idx != 3 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_GREASE_ECH)) goto end; if (idx == 3 && !TEST_int_eq(clientstatus, SSL_ECH_STATUS_FAILED_ECH)) goto end; if (!TEST_true(SSL_ech_get1_retry_config(clientssl, &retryconfig, &retryconfiglen))) goto end; if (!TEST_ptr(retryconfig)) goto end; if (!TEST_int_ne((int)retryconfiglen, 0)) goto end; if (verbose) TEST_info("ech_grease_test: retryconfglen: %zu\n", retryconfiglen); /* we kow the sizes to expect as the configs are hard-coded above */ if (idx == 2 && !TEST_size_t_eq(retryconfiglen, 141)) goto end; if (idx < 2 && !TEST_size_t_eq(retryconfiglen, 73)) goto end; /* cleanup */ 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))) goto end; /* setting an ECHConfig should over-ride GREASE flag */ if (!TEST_true(SSL_set1_ech_config_list(clientssl, retryconfig, retryconfiglen))) goto end; if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))) goto end; if (!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 (!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 (!TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) goto end; /* 3rd connection - this time grease+HRR which had a late fail */ 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; if (!TEST_true(create_ssl_objects(sctx, cctx, &serverssl, &clientssl, NULL, NULL))) goto end; /* force ECH+HRR */ if (!TEST_true(SSL_set_options(clientssl, SSL_OP_ECH_GREASE))) goto end; /* setting an ECHConfig should over-ride GREASE flag */ if (!TEST_true(SSL_set1_ech_config_list(clientssl, retryconfig, retryconfiglen))) goto end; if (!TEST_true(SSL_set1_groups_list(serverssl, "P-384"))) goto end; if (!TEST_true(SSL_set_tlsext_host_name(clientssl, "server.example"))) goto end; if (!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 (!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 (!TEST_int_eq(clientstatus, SSL_ECH_STATUS_SUCCESS)) goto end; /* all good */ res = 1; end: OPENSSL_free(sinner); OPENSSL_free(souter); OPENSSL_free(cinner); OPENSSL_free(couter); OPENSSL_free(retryconfig); SSL_free(clientssl); SSL_free(serverssl); X509_STORE_free(ch); SSL_CTX_free(cctx); SSL_CTX_free(sctx); OSSL_ECHSTORE_free(es); BIO_free_all(in); return res; } #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); ADD_TEST(ech_bad_public_names); 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); ADD_ALL_TESTS(ech_enoe_test, suite_combos); ADD_ALL_TESTS(ech_cb_test, suite_combos); ADD_ALL_TESTS(ech_v12_test, suite_combos); ADD_ALL_TESTS(ech_in_out_test, 14); ADD_ALL_TESTS(ech_grease_test, 4); ADD_ALL_TESTS(test_ech_no_inner, suite_combos); 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 }