diff --git a/crypto/ec/ec_ameth.c b/crypto/ec/ec_ameth.c index a274f720f3..263d084bbb 100644 --- a/crypto/ec/ec_ameth.c +++ b/crypto/ec/ec_ameth.c @@ -282,7 +282,8 @@ static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, ec_print_t ktype) } if (ktype != EC_KEY_PRINT_PARAM && EC_KEY_get0_public_key(x) != NULL) { - publen = EC_KEY_key2buf(x, EC_KEY_get_conv_form(x), &pub, NULL); + publen = EC_KEY_key2buf(x, + EC_GROUP_get_point_conversion_form(group), &pub, NULL); if (publen == 0) goto err; } @@ -500,7 +501,7 @@ static int ec_pkey_export_to(const EVP_PKEY *from, void *to_keydata, if (pub_point != NULL) { /* convert pub_point to a octet string according to the SECG standard */ - point_conversion_form_t format = EC_KEY_get_conv_form(eckey); + point_conversion_form_t format = EC_GROUP_get_point_conversion_form(ecg); if ((pub_key_buflen = EC_POINT_point2buf(ecg, pub_point, format, diff --git a/crypto/ec/ec_asn1.c b/crypto/ec/ec_asn1.c index 8c25b8ea53..66dbf4c0c1 100644 --- a/crypto/ec/ec_asn1.c +++ b/crypto/ec/ec_asn1.c @@ -1064,7 +1064,8 @@ int i2d_ECPrivateKey(const EC_KEY *a, unsigned char **out) goto err; } - publen = EC_KEY_key2buf(a, a->conv_form, &pub, NULL); + publen = EC_KEY_key2buf(a, + EC_GROUP_get_point_conversion_form(a->group), &pub, NULL); if (publen == 0 || publen > INT_MAX) { ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB); @@ -1156,6 +1157,7 @@ EC_KEY *o2i_ECPublicKey(EC_KEY **a, const unsigned char **in, long len) int i2o_ECPublicKey(const EC_KEY *a, unsigned char **out) { + point_conversion_form_t form; size_t buf_len = 0; int new_buffer = 0; @@ -1164,8 +1166,16 @@ int i2o_ECPublicKey(const EC_KEY *a, unsigned char **out) return 0; } - buf_len = EC_POINT_point2oct(a->group, a->pub_key, - a->conv_form, NULL, 0, NULL); + /* + * The encoded form follows the group's asn1_form, which the deprecated + * EC_KEY_set_conv_form() and the EC_KEY_oct2key() decode path both keep + * in sync with the key's conv_form. Reading the group avoids a stale + * key-side value when the group was updated through one of the EC_GROUP + * setters directly. + */ + form = EC_GROUP_get_point_conversion_form(a->group); + + buf_len = EC_POINT_point2oct(a->group, a->pub_key, form, NULL, 0, NULL); if (buf_len > INT_MAX) { ERR_raise(ERR_LIB_EC, ERR_R_PASSED_INVALID_ARGUMENT); @@ -1180,7 +1190,7 @@ int i2o_ECPublicKey(const EC_KEY *a, unsigned char **out) return 0; new_buffer = 1; } - if (!EC_POINT_point2oct(a->group, a->pub_key, a->conv_form, + if (!EC_POINT_point2oct(a->group, a->pub_key, form, *out, buf_len, NULL)) { ERR_raise(ERR_LIB_EC, ERR_R_EC_LIB); if (new_buffer) { diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c index 48e08e7de4..5ac22a482e 100644 --- a/crypto/ec/ec_key.c +++ b/crypto/ec/ec_key.c @@ -959,9 +959,16 @@ int EC_KEY_oct2key(EC_KEY *key, const unsigned char *buf, size_t len, * the last significant bit) contains the point conversion form. * EC_POINT_oct2point() has already performed sanity checking of * the buffer so we know it is valid. + * + * Mirror the form to the group as well, so that subsequent reads + * via EC_GROUP_get_point_conversion_form() see the loaded form + * rather than the EC_GROUP_new() default. The encoder and the + * provider OSSL_PARAM emitter both consult the group. */ - if ((key->group->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) + if ((key->group->meth->flags & EC_FLAGS_CUSTOM_CURVE) == 0) { key->conv_form = (point_conversion_form_t)(buf[0] & ~0x01); + EC_GROUP_set_point_conversion_form(key->group, key->conv_form); + } return 1; } diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index fcf64ed004..5aeb93925e 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -2408,7 +2408,12 @@ int EVP_PKEY_get_ec_point_conv_form(const EVP_PKEY *pkey) if (ec == NULL) return 0; - return EC_KEY_get_conv_form(ec); + /* + * Read the form from the group rather than the deprecated + * EC_KEY_get_conv_form(). EC_KEY_set_conv_form() and + * EC_KEY_oct2key() both keep the group's asn1_form in sync. + */ + return EC_GROUP_get_point_conversion_form(EC_KEY_get0_group(ec)); #else return 0; #endif diff --git a/providers/implementations/encode_decode/encode_key2text.c b/providers/implementations/encode_decode/encode_key2text.c index 965113426b..33bf6ab567 100644 --- a/providers/implementations/encode_decode/encode_key2text.c +++ b/providers/implementations/encode_decode/encode_key2text.c @@ -357,7 +357,8 @@ static int ec_to_text(BIO *out, const void *key, int selection) goto err; } - pub_len = EC_KEY_key2buf(ec, EC_KEY_get_conv_form(ec), &pub, NULL); + pub_len = EC_KEY_key2buf(ec, + EC_GROUP_get_point_conversion_form(group), &pub, NULL); if (pub_len == 0) goto err; } diff --git a/providers/implementations/keymgmt/ec_kmgmt.c b/providers/implementations/keymgmt/ec_kmgmt.c index d203a0c171..8af8e13aa3 100644 --- a/providers/implementations/keymgmt/ec_kmgmt.c +++ b/providers/implementations/keymgmt/ec_kmgmt.c @@ -156,7 +156,7 @@ static ossl_inline int key_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl, if (p != NULL || tmpl != NULL) { /* convert pub_point to a octet string according to the SECG standard */ - point_conversion_form_t format = EC_KEY_get_conv_form(eckey); + point_conversion_form_t format = EC_GROUP_get_point_conversion_form(ecg); if ((pub_key_len = EC_POINT_point2buf(ecg, pub_point, format, @@ -250,19 +250,10 @@ static ossl_inline int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *t { int ecdh_cofactor_mode = 0, group_check = 0; const char *name = NULL; - point_conversion_form_t format; if (ec == NULL) return 0; - format = EC_KEY_get_conv_form(ec); - name = ossl_ec_pt_format_id2name((int)format); - if (name != NULL - && !ossl_param_build_set_utf8_string(tmpl, params, - OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, - name)) - return 0; - group_check = EC_KEY_get_flags(ec) & EC_FLAG_CHECK_NAMED_GROUP_MASK; name = ossl_ec_check_group_type_id2name(group_check); if (name != NULL @@ -512,19 +503,23 @@ static int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb, && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0) return 0; - tmpl = OSSL_PARAM_BLD_new(); - if (tmpl == NULL) + if ((bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec))) == NULL) return 0; + BN_CTX_start(bnctx); - if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) { - bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec)); - if (bnctx == NULL) { - ok = 0; - goto end; - } - BN_CTX_start(bnctx); - ok = ok && ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL, ossl_ec_key_get_libctx(ec), ossl_ec_key_get0_propq(ec), bnctx, &genbuf); + if ((tmpl = OSSL_PARAM_BLD_new()) == NULL) { + ok = 0; + goto end; } + /* + * OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT is added based on the group's + * asn1_form by the call below; otherparams_to_params() no longer adds the + * key's deprecated conv_form, so this is the only source on the export + * side. + */ + ok = ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL, + ossl_ec_key_get_libctx(ec), ossl_ec_key_get0_propq(ec), + bnctx, &genbuf); if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { int include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0; @@ -748,6 +743,12 @@ static int common_get_params(void *key, OSSL_PARAM params[], int sm2) goto err; } + /* + * OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT is added base on the group's + * asn1_form by ossl_ec_group_todata() below; otherparams_to_params() no + * longer adds the key's deprecated conv_form, so this is the only source + * on the get-params side. + */ ret = ec_get_ecm_params(ecg, params) && ossl_ec_group_todata(ecg, NULL, params, libctx, propq, bnctx, &genbuf) @@ -1258,6 +1259,18 @@ static int ec_gen_assign_group(EC_KEY *ec, EC_GROUP *group) return EC_KEY_set_group(ec, group) > 0; } +/* + * Mirror the group's point-conversion form to the key's conv_form + * field so the two stay aligned after keygen. EC_KEY_new() leaves + * conv_form at the UNCOMPRESSED default; this sync ensures any caller + * that asks the key directly sees the same value the encoders do. + */ +static void ec_gen_sync_conv_form(EC_KEY *ec) +{ + EC_KEY_set_conv_form(ec, + EC_GROUP_get_point_conversion_form(EC_KEY_get0_group(ec))); +} + /* * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation */ @@ -1300,6 +1313,8 @@ static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) /* We must always assign a group, no matter what */ ret = ec_gen_assign_group(ec, gctx->gen_group); + if (ret) + ec_gen_sync_conv_form(ec); /* Whether you want it or not, you get a keypair, not just one half */ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) { @@ -1376,6 +1391,8 @@ static void *sm2_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg) /* We must always assign a group, no matter what */ ret = ec_gen_assign_group(ec, gctx->gen_group); + if (ret) + ec_gen_sync_conv_form(ec); /* Whether you want it or not, you get a keypair, not just one half */ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c index 2a15378570..84dc90cfc2 100644 --- a/ssl/t1_lib.c +++ b/ssl/t1_lib.c @@ -1911,6 +1911,8 @@ static int tls1_check_pkey_comp(SSL_CONNECTION *s, EVP_PKEY *pkey) unsigned char comp_id; size_t i; int point_conv; + const unsigned char *own_formats; + size_t own_formats_len; /* If not an EC key nothing to check */ if (!EVP_PKEY_is_a(pkey, "EC")) @@ -1944,6 +1946,21 @@ static int tls1_check_pkey_comp(SSL_CONNECTION *s, EVP_PKEY *pkey) */ if (s->ext.peer_ecpointformats == NULL) return 1; + /* + * The cert's point form must also be in our OWN advertised list -- we + * mustn't commit to a cert in a form we told the peer we don't speak. + * Without this the server's cert-selection happily picks (and the + * client happily decodes-and-then-rejects) a leaf whose form is in the + * peer's list but not ours, leaving the peer to alert illegal_parameter + * on receipt instead of catching the mismatch before it's wire-visible. + */ + tls1_get_formatlist(s, &own_formats, &own_formats_len); + for (i = 0; i < own_formats_len; i++) { + if (own_formats[i] == comp_id) + break; + } + if (i == own_formats_len) + return 0; for (i = 0; i < s->ext.peer_ecpointformats_len; i++) { if (s->ext.peer_ecpointformats[i] == comp_id) diff --git a/test/certs/mkcert.sh b/test/certs/mkcert.sh index 087dc343d7..9a417e77ae 100755 --- a/test/certs/mkcert.sh +++ b/test/certs/mkcert.sh @@ -52,7 +52,17 @@ key() { case $alg in rsa) args=("${args[@]}" -pkeyopt rsa_keygen_bits:$bits );; ec) args=("${args[@]}" -pkeyopt "ec_paramgen_curve:$bits") - args=("${args[@]}" -pkeyopt ec_param_enc:named_curve);; + args=("${args[@]}" -pkeyopt ec_param_enc:named_curve) + # OPENSSL_EC_POINT_FORMAT (uncompressed|compressed|hybrid) drives + # the per-key point conversion form recorded on the generated + # EC_KEY, surfaced in SubjectPublicKeyInfo and negotiated via the + # TLS 1.2 ec_point_formats extension. Empty/unset preserves the + # provider default (uncompressed). + if [ -n "$OPENSSL_EC_POINT_FORMAT" ]; then + args=("${args[@]}" \ + -pkeyopt "point-format:$OPENSSL_EC_POINT_FORMAT") + fi + ;; dsa) args=(-paramfile "$bits");; ed25519) ;; ed448) ;; diff --git a/test/certs/server-ec-compressed-cert.pem b/test/certs/server-ec-compressed-cert.pem new file mode 100644 index 0000000000..475ccd457b --- /dev/null +++ b/test/certs/server-ec-compressed-cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBozCCASigAwIBAgIBAjAKBggqhkjOPQQDAjAbMRkwFwYDVQQDDBBFQ0RTQSBQ +LTM4NCByb290MCAXDTI2MDQyNTA1MDgzNFoYDzIxMjYwNDI2MDUwODM0WjAZMRcw +FQYDVQQDDA5zZXJ2ZXIuZXhhbXBsZTA5MBMGByqGSM49AgEGCCqGSM49AwEHAyIA +A4Mt9T6fKt3APp8/Frw65PDi2eMYdZK98nhBW9pA1Ccho30wezAdBgNVHQ4EFgQU +6MzfJGpHsWRJ3Nuj1KOStlvabW0wHwYDVR0jBBgwFoAUJtCPHXtf3B5/QYB9Y8oc +dYHWhWkwCQYDVR0TBAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAZBgNVHREEEjAQ +gg5zZXJ2ZXIuZXhhbXBsZTAKBggqhkjOPQQDAgNpADBmAjEApXyoA/nmW3woPvjq +H/ea3zDcN7mcaUYMwaCsyvsNNlKjFv1xdSFjxRFKUTqepJ/UAjEA7hQZq6hSm85b ++2TawUUn6Yj04cYq/XTirif3VtVauLABTRJ40S57jEpkDh6sovSC +-----END CERTIFICATE----- diff --git a/test/certs/server-ec-compressed-key.pem b/test/certs/server-ec-compressed-key.pem new file mode 100644 index 0000000000..98218fcb3c --- /dev/null +++ b/test/certs/server-ec-compressed-key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MGcCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcETTBLAgEBBCAb1VgxSUhJyh43soLb +FMsebjWSp/Hma3kSyw6lT4txDaEkAyIAA4Mt9T6fKt3APp8/Frw65PDi2eMYdZK9 +8nhBW9pA1Cch +-----END PRIVATE KEY----- diff --git a/test/certs/setup.sh b/test/certs/setup.sh index 3bee78ec32..3bba1b769b 100755 --- a/test/certs/setup.sh +++ b/test/certs/setup.sh @@ -211,6 +211,16 @@ OPENSSL_KEYBITS=768 \ # EC cert with named curve signed by named curve ca ./mkcert.sh genee server.example ee-key-ec-named-named \ ee-cert-ec-named-named ca-key-ec-named ca-cert-ec-named +# EC EE cert directly issued by the P-384 EC root, with a P-256 named-curve +# leaf key whose per-key conv_form is COMPRESSED. The leading SPKI bit-string +# byte is 0x02 or 0x03, exercising the OSSL_PKEY_PARAM_EC_POINT_CONVERSION_- +# FORMAT keygen path end-to-end. Anchors TLS 1.2 ec_point_formats negotiation +# tests in 80-test_ssl_new (sslnew tests rely solely on PKIX, so the CN/SAN +# value is functionally arbitrary; we keep "server.example" for consistency). +OPENSSL_KEYALG=ec OPENSSL_KEYBITS=prime256v1 \ +OPENSSL_EC_POINT_FORMAT=compressed \ +./mkcert.sh genee server.example server-ec-compressed-key \ + server-ec-compressed-cert p384-root-key p384-root # 1024-bit leaf key OPENSSL_KEYBITS=1024 \ ./mkcert.sh genee server.example ee-key-1024 ee-cert-1024 ca-key ca-cert diff --git a/test/recipes/80-test_ssl_new.t b/test/recipes/80-test_ssl_new.t index a2d69e3c47..8516652f97 100644 --- a/test/recipes/80-test_ssl_new.t +++ b/test/recipes/80-test_ssl_new.t @@ -42,7 +42,7 @@ if (defined $ENV{SSL_TESTS}) { @conf_srcs = glob(srctop_file("test", "ssl-tests", "*.cnf.in")); # We hard-code the number of tests to double-check that the globbing above # finds all files as expected. - plan tests => 31; + plan tests => 32; } map { s/;.*// } @conf_srcs if $^O eq "VMS"; my @conf_files = map { basename($_, ".in") } @conf_srcs; @@ -100,6 +100,7 @@ my %conf_dependent_tests = ( "28-seclevel.cnf" => disabled("tls1_2") || $no_ecx, "30-extended-master-secret.cnf" => disabled("tls1_2"), "32-compressed-certificate.cnf" => disabled("comp") || disabled("tls1_3"), + "33-compressed-spki.cnf" => disabled("tls1_2") || disabled("tls1_3") || $no_ec, ); # Add your test here if it should be skipped for some compile-time @@ -135,6 +136,7 @@ my %skip = ( "26-tls13_client_auth.cnf" => disabled("tls1_3") || ($no_ec && $no_dh), "29-dtls-sctp-label-bug.cnf" => disabled("sctp") || disabled("sock"), "32-compressed-certificate.cnf" => disabled("comp") || disabled("tls1_3"), + "33-compressed-spki.cnf" => disabled("tls1_2") || disabled("tls1_3") || $no_ec, ); foreach my $conf (@conf_files) { diff --git a/test/ssl-tests/33-compressed-spki.cnf b/test/ssl-tests/33-compressed-spki.cnf new file mode 100644 index 0000000000..bc428de0e4 --- /dev/null +++ b/test/ssl-tests/33-compressed-spki.cnf @@ -0,0 +1,338 @@ +# Generated with generate_ssl_tests.pl + +num_tests = 10 + +test-0 = 0-tls12-compressed-spki-both-sides-ok +test-1 = 1-tls12-compressed-spki-client-no-option +test-2 = 2-tls12-compressed-spki-server-no-option +test-3 = 3-tls13-compressed-spki +test-4 = 4-tls12-mixed-both-sides-ok +test-5 = 5-tls12-mixed-client-no-option +test-6 = 6-tls12-mixed-server-no-option +test-7 = 7-tls13-mixed +test-8 = 8-tls12-mixed-uncompressed +test-9 = 9-tls13-mixed-uncompressed +# =========================================================== + +[0-tls12-compressed-spki-both-sides-ok] +ssl_conf = 0-tls12-compressed-spki-both-sides-ok-ssl + +[0-tls12-compressed-spki-both-sides-ok-ssl] +server = 0-tls12-compressed-spki-both-sides-ok-server +client = 0-tls12-compressed-spki-both-sides-ok-client + +[0-tls12-compressed-spki-both-sides-ok-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem + +[0-tls12-compressed-spki-both-sides-ok-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256 +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Peer + +[test-0] +ExpectedProtocol = TLSv1.2 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + +# =========================================================== + +[1-tls12-compressed-spki-client-no-option] +ssl_conf = 1-tls12-compressed-spki-client-no-option-ssl + +[1-tls12-compressed-spki-client-no-option-ssl] +server = 1-tls12-compressed-spki-client-no-option-server +client = 1-tls12-compressed-spki-client-no-option-client + +[1-tls12-compressed-spki-client-no-option-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem + +[1-tls12-compressed-spki-client-no-option-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256 +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Peer + +[test-1] +ExpectedResult = ServerFail +ExpectedServerAlert = HandshakeFailure + + +# =========================================================== + +[2-tls12-compressed-spki-server-no-option] +ssl_conf = 2-tls12-compressed-spki-server-no-option-ssl + +[2-tls12-compressed-spki-server-no-option-ssl] +server = 2-tls12-compressed-spki-server-no-option-server +client = 2-tls12-compressed-spki-server-no-option-client + +[2-tls12-compressed-spki-server-no-option-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem + +[2-tls12-compressed-spki-server-no-option-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256 +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Peer + +[test-2] +ExpectedResult = ServerFail +ExpectedServerAlert = HandshakeFailure + + +# =========================================================== + +[3-tls13-compressed-spki] +ssl_conf = 3-tls13-compressed-spki-ssl + +[3-tls13-compressed-spki-ssl] +server = 3-tls13-compressed-spki-server +client = 3-tls13-compressed-spki-client + +[3-tls13-compressed-spki-server] +Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +CipherString = DEFAULT +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem + +[3-tls13-compressed-spki-client] +CipherString = DEFAULT +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +VerifyMode = Peer + +[test-3] +ExpectedProtocol = TLSv1.3 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + +# =========================================================== + +[4-tls12-mixed-both-sides-ok] +ssl_conf = 4-tls12-mixed-both-sides-ok-ssl + +[4-tls12-mixed-both-sides-ok-ssl] +server = 4-tls12-mixed-both-sides-ok-server +client = 4-tls12-mixed-both-sides-ok-client + +[4-tls12-mixed-both-sides-ok-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +ECDSA.Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +ECDSA.PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 + +[4-tls12-mixed-both-sides-ok-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +EC.VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-4] +ExpectedProtocol = TLSv1.2 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + +# =========================================================== + +[5-tls12-mixed-client-no-option] +ssl_conf = 5-tls12-mixed-client-no-option-ssl + +[5-tls12-mixed-client-no-option-ssl] +server = 5-tls12-mixed-client-no-option-server +client = 5-tls12-mixed-client-no-option-client + +[5-tls12-mixed-client-no-option-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +ECDSA.Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +ECDSA.PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 + +[5-tls12-mixed-client-no-option-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +EC.VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-5] +ExpectedProtocol = TLSv1.2 +ExpectedResult = Success +ExpectedServerCertType = RSA + + +# =========================================================== + +[6-tls12-mixed-server-no-option] +ssl_conf = 6-tls12-mixed-server-no-option-ssl + +[6-tls12-mixed-server-no-option-ssl] +server = 6-tls12-mixed-server-no-option-server +client = 6-tls12-mixed-server-no-option-client + +[6-tls12-mixed-server-no-option-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +ECDSA.Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +ECDSA.PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 + +[6-tls12-mixed-server-no-option-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +EC.VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +Options = LegacyECPointFormats +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-6] +ExpectedProtocol = TLSv1.2 +ExpectedResult = Success +ExpectedServerCertType = RSA + + +# =========================================================== + +[7-tls13-mixed] +ssl_conf = 7-tls13-mixed-ssl + +[7-tls13-mixed-ssl] +server = 7-tls13-mixed-server +client = 7-tls13-mixed-client + +[7-tls13-mixed-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +ECDSA.Certificate = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-cert.pem +ECDSA.PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ec-compressed-key.pem +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256 + +[7-tls13-mixed-client] +CipherString = DEFAULT +EC.VerifyCAFile = ${ENV::TEST_CERTS_DIR}/p384-root.pem +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-7] +ExpectedProtocol = TLSv1.3 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + +# =========================================================== + +[8-tls12-mixed-uncompressed] +ssl_conf = 8-tls12-mixed-uncompressed-ssl + +[8-tls12-mixed-uncompressed-ssl] +server = 8-tls12-mixed-uncompressed-server +client = 8-tls12-mixed-uncompressed-client + +[8-tls12-mixed-uncompressed-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +ECDSA.Certificate = ${ENV::TEST_CERTS_DIR}/server-ecdsa-cert.pem +ECDSA.PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ecdsa-key.pem +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 + +[8-tls12-mixed-uncompressed-client] +CipherString = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256 +MaxProtocol = TLSv1.2 +MinProtocol = TLSv1.2 +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pkcs1_sha256 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-8] +ExpectedProtocol = TLSv1.2 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + +# =========================================================== + +[9-tls13-mixed-uncompressed] +ssl_conf = 9-tls13-mixed-uncompressed-ssl + +[9-tls13-mixed-uncompressed-ssl] +server = 9-tls13-mixed-uncompressed-server +client = 9-tls13-mixed-uncompressed-client + +[9-tls13-mixed-uncompressed-server] +Certificate = ${ENV::TEST_CERTS_DIR}/servercert.pem +CipherString = DEFAULT +ECDSA.Certificate = ${ENV::TEST_CERTS_DIR}/server-ecdsa-cert.pem +ECDSA.PrivateKey = ${ENV::TEST_CERTS_DIR}/server-ecdsa-key.pem +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +PrivateKey = ${ENV::TEST_CERTS_DIR}/serverkey.pem +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256 + +[9-tls13-mixed-uncompressed-client] +CipherString = DEFAULT +MaxProtocol = TLSv1.3 +MinProtocol = TLSv1.3 +SignatureAlgorithms = ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256 +VerifyCAFile = ${ENV::TEST_CERTS_DIR}/rootcert.pem +VerifyMode = Peer + +[test-9] +ExpectedProtocol = TLSv1.3 +ExpectedResult = Success +ExpectedServerCertType = P-256 + + diff --git a/test/ssl-tests/33-compressed-spki.cnf.in b/test/ssl-tests/33-compressed-spki.cnf.in new file mode 100644 index 0000000000..9b016dc67b --- /dev/null +++ b/test/ssl-tests/33-compressed-spki.cnf.in @@ -0,0 +1,345 @@ +# -*- mode: perl; -*- +# Copyright 2026 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +## SSL test configurations +## +## The server presents a TLS leaf certificate (server-ec-compressed-cert.pem) +## whose SubjectPublicKeyInfo's bit-string starts with 0x02 or 0x03 -- i.e. a +## P-256 named-curve public key encoded in COMPRESSED form, anchored to the +## P-384 EC root. +## +## TLS 1.2 ec_point_formats (RFC 4492) requires both peers to opt into legacy +## compressed support, exposed via SSL_OP_LEGACY_EC_POINT_FORMATS / the +## "LegacyECPointFormats" SSL_CONF Options token. Without it on either side +## the per-side ec_point_formats list contains only 'uncompressed', and +## tls1_check_pkey_comp() refuses to use a compressed-form leaf -- on the +## server when selecting a cert against the client's advertised list, on the +## client when verifying the server's cert against the server's advertised +## list. +## +## TLS 1.3 dropped the ec_point_formats extension entirely; a compressed-form +## leaf is just bytes the receiver decodes, so the option is irrelevant. + +package ssltests; +use OpenSSL::Test::Utils; + +# Shared bits for the mixed-cert (RSA + EC-compressed) stanzas below. The +# server loads BOTH an RSA leaf (anchored to ssltests_base.pm's default +# rootcert.pem) and the compressed-form EC leaf (anchored to p384-root.pem); +# the per-key-type prefixes ("ECDSA.", "EC.") are arbitrary distinct strings +# stripped before SSL_CONF command matching -- they exist solely to keep +# duplicate directive names from colliding. Slot dispatch is by the loaded +# cert's key type, not the prefix. do_store() appends each VerifyCAFile to +# the verify_store, so the EC.VerifyCAFile below adds p384-root.pem on top +# of the base's rootcert.pem default rather than replacing it. +my $mixed_server = { + "Certificate" => test_pem("servercert.pem"), + "PrivateKey" => test_pem("serverkey.pem"), + "ECDSA.Certificate" => test_pem("server-ec-compressed-cert.pem"), + "ECDSA.PrivateKey" => test_pem("server-ec-compressed-key.pem"), +}; + +my $mixed_client = { + "EC.VerifyCAFile" => test_pem("p384-root.pem"), +}; + +# Variant of the above where the EC leaf is the stock P-256 uncompressed-form +# cert (server-ecdsa-cert.pem). Both server-ecdsa-cert.pem and servercert.pem +# chain to ssltests_base.pm's default VerifyCAFile (rootcert.pem), so the +# client side needs no overrides. +my $uncompressed_server = { + "Certificate" => test_pem("servercert.pem"), + "PrivateKey" => test_pem("serverkey.pem"), + "ECDSA.Certificate" => test_pem("server-ecdsa-cert.pem"), + "ECDSA.PrivateKey" => test_pem("server-ecdsa-key.pem"), +}; + +# Every stanza below is EC-specific and requires both TLS 1.2 and TLS 1.3. +# In any build that lacks one of those, emit no tests at all -- there's +# nothing meaningful to exercise here. The recipe's %conf_dependent_tests +# entry skips the reference-comparison check under the same conditions. +our @tests = (); + +unless (disabled("ec") || disabled("tls1_2") || disabled("tls1_3")) { +@tests = ( + { + name => "tls12-compressed-spki-both-sides-ok", + server => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "Options" => "LegacyECPointFormats", + }, + client => { + "VerifyCAFile" => test_pem("p384-root.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256", + "Options" => "LegacyECPointFormats", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.2", + "ExpectedServerCertType" => "P-256", + }, + }, + + { + # Client did not opt in: its ec_point_formats lists 'uncompressed' + # only. tls1_check_pkey_comp() on the server compares its leaf cert's + # point form against the *peer's* advertised formats; the cert is + # compressed, the peer advertises only uncompressed, so cert selection + # finds nothing and the server aborts with handshake_failure. + name => "tls12-compressed-spki-client-no-option", + server => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "Options" => "LegacyECPointFormats", + }, + client => { + "VerifyCAFile" => test_pem("p384-root.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256", + }, + test => { + "ExpectedResult" => "ServerFail", + "ExpectedServerAlert" => "HandshakeFailure", + }, + }, + + { + # Server did not opt in: its own ec_point_formats lists 'uncompressed' + # only. tls1_check_pkey_comp() now (correctly) checks the cert's + # form against BOTH the server's own and the peer's advertised lists; + # the cert is compressed, the server's own list is uncompressed-only, + # the cert is dropped from the candidate set, no other usable cert + # exists, and the server aborts with handshake_failure -- before + # anything cert-shaped is wire-visible. + name => "tls12-compressed-spki-server-no-option", + server => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + }, + client => { + "VerifyCAFile" => test_pem("p384-root.pem"), + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256", + "Options" => "LegacyECPointFormats", + }, + test => { + "ExpectedResult" => "ServerFail", + "ExpectedServerAlert" => "HandshakeFailure", + }, + }, + + { + # TLS 1.3 omits ec_point_formats; the option is irrelevant and the + # compressed SPKI just decodes. + name => "tls13-compressed-spki", + server => { + "Certificate" => test_pem("server-ec-compressed-cert.pem"), + "PrivateKey" => test_pem("server-ec-compressed-key.pem"), + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + }, + client => { + "VerifyCAFile" => test_pem("p384-root.pem"), + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.3", + "ExpectedServerCertType" => "P-256", + }, + }, + + # ---------------------------------------------------------------------- + # Mixed-cert RSA-fallback stanzas. The server presents BOTH an RSA leaf + # (servercert.pem under root-cert.pem) and the P-256 compressed-SPKI EC + # leaf (server-ec-compressed-cert.pem under p384-root.pem). Both peers + # advertise sigalgs preferring ECDSA over RSA, so the EC leaf is used + # whenever it's eligible; when point-format negotiation rules it out (one + # side or the other didn't opt into compressed), tls1_check_pkey_comp() + # filters the EC cert and the server falls back to RSA. TLS 1.3 dropped + # ec_point_formats, so the EC leaf is always eligible there. + # ---------------------------------------------------------------------- + + { + # Both sides opted in: ECDHE-ECDSA picked, EC leaf used. + name => "tls12-mixed-both-sides-ok", + server => { + %$mixed_server, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "Options" => "LegacyECPointFormats", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + client => { + %$mixed_client, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "Options" => "LegacyECPointFormats", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.2", + "ExpectedServerCertType" => "P-256", + }, + }, + + { + # Client did not opt in. Server's selection sees the peer + # advertises only 'uncompressed' point format, so the compressed EC + # leaf is filtered; cert selection falls back to the RSA leaf and + # the handshake completes with ECDHE-RSA. + name => "tls12-mixed-client-no-option", + server => { + %$mixed_server, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "Options" => "LegacyECPointFormats", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + client => { + %$mixed_client, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.2", + "ExpectedServerCertType" => "RSA", + }, + }, + + { + # Server did not opt in. After the tls1_check_pkey_comp() fix the + # server compares its cert's point form against its OWN advertised + # list too; the compressed EC leaf is filtered and selection falls + # back to RSA. Without the fix the server would have committed to + # the EC leaf and the client would have alerted illegal_parameter + # on receipt. + name => "tls12-mixed-server-no-option", + server => { + %$mixed_server, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + client => { + %$mixed_client, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "Options" => "LegacyECPointFormats", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.2", + "ExpectedServerCertType" => "RSA", + }, + }, + + { + # TLS 1.3 omits ec_point_formats and rsa_pkcs1_* is forbidden for + # handshake signatures, so we use rsa_pss_rsae_sha256 (the cert's + # key OID is rsaEncryption -- _rsae_, not _pss_). EC is always + # eligible here; ECDSA wins by sigalg preference. + name => "tls13-mixed", + server => { + %$mixed_server, + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256", + }, + client => { + %$mixed_client, + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.3", + "ExpectedServerCertType" => "P-256", + }, + }, + + # ---------------------------------------------------------------------- + # Control: same RSA-vs-EC sigalg-preference setup, but the EC leaf is in + # the standard uncompressed SPKI form. Neither side opts into legacy + # compressed support; each side's ec_point_formats list is the default + # ('uncompressed' only). An uncompressed leaf is admitted by every + # branch of tls1_check_pkey_comp() unconditionally, so the EC cert is + # selected and the RSA cert is unused -- demonstrating that the + # point-format machinery filters only compressed leaves. + # ---------------------------------------------------------------------- + + { + name => "tls12-mixed-uncompressed", + server => { + %$uncompressed_server, + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + client => { + "MinProtocol" => "TLSv1.2", + "MaxProtocol" => "TLSv1.2", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pkcs1_sha256", + "CipherString" => "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.2", + "ExpectedServerCertType" => "P-256", + }, + }, + + { + # TLS 1.3 omits ec_point_formats entirely; this is the same setup + # as tls12-mixed-uncompressed but at TLS 1.3, kept for parity with + # the rest of the matrix. + name => "tls13-mixed-uncompressed", + server => { + %$uncompressed_server, + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256", + }, + client => { + "MinProtocol" => "TLSv1.3", + "MaxProtocol" => "TLSv1.3", + "SignatureAlgorithms" => "ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256", + }, + test => { + "ExpectedResult" => "Success", + "ExpectedProtocol" => "TLSv1.3", + "ExpectedServerCertType" => "P-256", + }, + }, +); +}