Support OpenSSL APIs SSL[_CTX]_set1_sigalgs[_list]. These functions can be used to configure the signature algorithms. One of them is a string mini-languaging parsing function, which we generally dislike because it defeats static analysis. However, some dependent projects (in this case TensorFlow) need it and we also dislike making people patch. Change-Id: I13f990c896a7f7332d78b1c351357d418ade8d11 Reviewed-on: https://boringssl-review.googlesource.com/30304 Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/crypto/err/ssl.errordata b/crypto/err/ssl.errordata index e788f0b..46ff44f 100644 --- a/crypto/err/ssl.errordata +++ b/crypto/err/ssl.errordata
@@ -54,6 +54,7 @@ SSL,143,DTLS_MESSAGE_TOO_BIG SSL,257,DUPLICATE_EXTENSION SSL,264,DUPLICATE_KEY_SHARE +SSL,296,DUPLICATE_SIGNATURE_ALGORITHM SSL,283,EARLY_DATA_NOT_IN_USE SSL,144,ECC_CERT_NOT_FOR_SIGNING SSL,282,EMPTY_HELLO_RETRY_REQUEST @@ -77,6 +78,7 @@ SSL,159,INVALID_MESSAGE SSL,251,INVALID_OUTER_RECORD_TYPE SSL,269,INVALID_SCT_LIST +SSL,295,INVALID_SIGNATURE_ALGORITHM SSL,160,INVALID_SSL_SESSION SSL,161,INVALID_TICKET_KEYS_LENGTH SSL,162,LENGTH_MISMATCH
diff --git a/fuzz/ssl_ctx_api.cc b/fuzz/ssl_ctx_api.cc index ac6202d7..c050770 100644 --- a/fuzz/ssl_ctx_api.cc +++ b/fuzz/ssl_ctx_api.cc
@@ -475,6 +475,12 @@ } SSL_CTX_set_grease_enabled(ctx, b); }, + [](SSL_CTX *ctx, CBS *cbs) { + SSL_CTX_set1_sigalgs(ctx, (const int *)CBS_data(cbs), CBS_len(cbs) / 2); + }, + [](SSL_CTX *ctx, CBS *cbs) { + SSL_CTX_set1_sigalgs_list(ctx, (const char *) CBS_data(cbs)); + }, }; bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
diff --git a/include/openssl/evp.h b/include/openssl/evp.h index c187ac1..7994b84 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h
@@ -169,6 +169,7 @@ #define EVP_PKEY_NONE NID_undef #define EVP_PKEY_RSA NID_rsaEncryption +#define EVP_PKEY_RSA_PSS NID_rsassaPss #define EVP_PKEY_DSA NID_dsa #define EVP_PKEY_EC NID_X9_62_id_ecPublicKey #define EVP_PKEY_ED25519 NID_ED25519
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index cf424a7..a24af46 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -3920,6 +3920,54 @@ DH *(*cb)(SSL *ssl, int is_export, int keylength)); +// SSL_CTX_set1_sigalgs takes |num_values| ints and interprets them as pairs +// where the first is the nid of a hash function and the second is an +// |EVP_PKEY_*| value. It configures the signature algorithm preferences for +// |ctx| based on them and returns one on success or zero on error. +// +// This API is compatible with OpenSSL. However, BoringSSL-specific code should +// prefer |SSL_CTX_set_signing_algorithm_prefs| because it's clearer and it's +// more convenient to codesearch for specific algorithm values. +OPENSSL_EXPORT int SSL_CTX_set1_sigalgs(SSL_CTX *ctx, const int *values, + size_t num_values); + +// SSL_set1_sigalgs takes |num_values| ints and interprets them as pairs where +// the first is the nid of a hash function and the second is an |EVP_PKEY_*| +// value. It configures the signature algorithm preferences for |ssl| based on +// them and returns one on success or zero on error. +// +// This API is compatible with OpenSSL. However, BoringSSL-specific code should +// prefer |SSL_CTX_set_signing_algorithm_prefs| because it's clearer and it's +// more convenient to codesearch for specific algorithm values. +OPENSSL_EXPORT int SSL_set1_sigalgs(SSL *ssl, const int *values, + size_t num_values); + +// SSL_CTX_set1_sigalgs_list takes a textual specification of a set of signature +// algorithms and configures them on |ctx|. It returns one on success and zero +// on error. See +// https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set1_sigalgs_list.html for +// a description of the text format. Also note that TLS 1.3 names (e.g. +// "rsa_pkcs1_md5_sha1") can also be used (as in OpenSSL, although OpenSSL +// doesn't document that). +// +// This API is compatible with OpenSSL. However, BoringSSL-specific code should +// prefer |SSL_CTX_set_signing_algorithm_prefs| because it's clearer and it's +// more convenient to codesearch for specific algorithm values. +OPENSSL_EXPORT int SSL_CTX_set1_sigalgs_list(SSL_CTX *ctx, const char *str); + +// SSL_set1_sigalgs_list takes a textual specification of a set of signature +// algorithms and configures them on |ssl|. It returns one on success and zero +// on error. See +// https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set1_sigalgs_list.html for +// a description of the text format. Also note that TLS 1.3 names (e.g. +// "rsa_pkcs1_md5_sha1") can also be used (as in OpenSSL, although OpenSSL +// doesn't document that). +// +// This API is compatible with OpenSSL. However, BoringSSL-specific code should +// prefer |SSL_CTX_set_signing_algorithm_prefs| because it's clearer and it's +// more convenient to codesearch for specific algorithm values. +OPENSSL_EXPORT int SSL_set1_sigalgs_list(SSL *ssl, const char *str); + #define SSL_set_app_data(s, arg) (SSL_set_ex_data(s, 0, (char *)(arg))) #define SSL_get_app_data(s) (SSL_get_ex_data(s, 0)) @@ -4730,6 +4778,8 @@ #define SSL_R_CERT_DECOMPRESSION_FAILED 292 #define SSL_R_UNCOMPRESSED_CERT_TOO_LARGE 293 #define SSL_R_UNKNOWN_CERT_COMPRESSION_ALG 294 +#define SSL_R_INVALID_SIGNATURE_ALGORITHM 295 +#define SSL_R_DUPLICATE_SIGNATURE_ALGORITHM 296 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/ssl/internal.h b/ssl/internal.h index 56cbd3d..cc3e075 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -2416,6 +2416,10 @@ // Contains the QUIC transport params that this endpoint will send. Array<uint8_t> quic_transport_params; + // verify_sigalgs, if not empty, is the set of signature algorithms + // accepted from the peer in decreasing order of preference. + Array<uint16_t> verify_sigalgs; + // srtp_profiles is the list of configured SRTP protection profiles for // DTLS-SRTP. UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index 9084610..36c26cd 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc
@@ -671,7 +671,8 @@ if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) || !ssl->config->alpn_client_proto_list.CopyFrom( - ctx->alpn_client_proto_list)) { + ctx->alpn_client_proto_list) || + !ssl->config->verify_sigalgs.CopyFrom(ctx->verify_sigalgs)) { return nullptr; }
diff --git a/ssl/ssl_privkey.cc b/ssl/ssl_privkey.cc index f5c387b..fecac39 100644 --- a/ssl/ssl_privkey.cc +++ b/ssl/ssl_privkey.cc
@@ -409,38 +409,49 @@ ctx->cert->key_method = key_method; } +static constexpr size_t kMaxSignatureAlgorithmNameLen = 23; + +// This was "constexpr" rather than "const", but that triggered a bug in MSVC +// where it didn't pad the strings to the correct length. +static const struct { + uint16_t signature_algorithm; + const char name[kMaxSignatureAlgorithmNameLen]; +} kSignatureAlgorithmNames[] = { + {SSL_SIGN_RSA_PKCS1_MD5_SHA1, "rsa_pkcs1_md5_sha1"}, + {SSL_SIGN_RSA_PKCS1_SHA1, "rsa_pkcs1_sha1"}, + {SSL_SIGN_RSA_PKCS1_SHA256, "rsa_pkcs1_sha256"}, + {SSL_SIGN_RSA_PKCS1_SHA384, "rsa_pkcs1_sha384"}, + {SSL_SIGN_RSA_PKCS1_SHA512, "rsa_pkcs1_sha512"}, + {SSL_SIGN_ECDSA_SHA1, "ecdsa_sha1"}, + {SSL_SIGN_ECDSA_SECP256R1_SHA256, "ecdsa_secp256r1_sha256"}, + {SSL_SIGN_ECDSA_SECP384R1_SHA384, "ecdsa_secp384r1_sha384"}, + {SSL_SIGN_ECDSA_SECP521R1_SHA512, "ecdsa_secp521r1_sha512"}, + {SSL_SIGN_RSA_PSS_RSAE_SHA256, "rsa_pss_rsae_sha256"}, + {SSL_SIGN_RSA_PSS_RSAE_SHA384, "rsa_pss_rsae_sha384"}, + {SSL_SIGN_RSA_PSS_RSAE_SHA512, "rsa_pss_rsae_sha512"}, + {SSL_SIGN_ED25519, "ed25519"}, +}; + const char *SSL_get_signature_algorithm_name(uint16_t sigalg, int include_curve) { - switch (sigalg) { - case SSL_SIGN_RSA_PKCS1_MD5_SHA1: - return "rsa_pkcs1_md5_sha1"; - case SSL_SIGN_RSA_PKCS1_SHA1: - return "rsa_pkcs1_sha1"; - case SSL_SIGN_RSA_PKCS1_SHA256: - return "rsa_pkcs1_sha256"; - case SSL_SIGN_RSA_PKCS1_SHA384: - return "rsa_pkcs1_sha384"; - case SSL_SIGN_RSA_PKCS1_SHA512: - return "rsa_pkcs1_sha512"; - case SSL_SIGN_ECDSA_SHA1: - return "ecdsa_sha1"; - case SSL_SIGN_ECDSA_SECP256R1_SHA256: - return include_curve ? "ecdsa_secp256r1_sha256" : "ecdsa_sha256"; - case SSL_SIGN_ECDSA_SECP384R1_SHA384: - return include_curve ? "ecdsa_secp384r1_sha384" : "ecdsa_sha384"; - case SSL_SIGN_ECDSA_SECP521R1_SHA512: - return include_curve ? "ecdsa_secp521r1_sha512" : "ecdsa_sha512"; - case SSL_SIGN_RSA_PSS_RSAE_SHA256: - return "rsa_pss_rsae_sha256"; - case SSL_SIGN_RSA_PSS_RSAE_SHA384: - return "rsa_pss_rsae_sha384"; - case SSL_SIGN_RSA_PSS_RSAE_SHA512: - return "rsa_pss_rsae_sha512"; - case SSL_SIGN_ED25519: - return "ed25519"; - default: - return NULL; + if (!include_curve) { + switch (sigalg) { + case SSL_SIGN_ECDSA_SECP256R1_SHA256: + return "ecdsa_sha256"; + case SSL_SIGN_ECDSA_SECP384R1_SHA384: + return "ecdsa_sha384"; + case SSL_SIGN_ECDSA_SECP521R1_SHA512: + return "ecdsa_sha512"; + } } + + for (const auto &candidate : kSignatureAlgorithmNames) { + if (candidate.signature_algorithm == sigalg) { + return candidate.name; + } + } + + return NULL; } int SSL_get_signature_algorithm_key_type(uint16_t sigalg) { @@ -474,6 +485,315 @@ return ssl->config->cert->sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs)); } +static constexpr struct { + int pkey_type; + int hash_nid; + uint16_t signature_algorithm; +} kSignatureAlgorithmsMapping[] = { + {EVP_PKEY_RSA, NID_sha1, SSL_SIGN_RSA_PKCS1_SHA1}, + {EVP_PKEY_RSA, NID_sha256, SSL_SIGN_RSA_PKCS1_SHA256}, + {EVP_PKEY_RSA, NID_sha384, SSL_SIGN_RSA_PKCS1_SHA384}, + {EVP_PKEY_RSA, NID_sha512, SSL_SIGN_RSA_PKCS1_SHA512}, + {EVP_PKEY_RSA_PSS, NID_sha256, SSL_SIGN_RSA_PSS_RSAE_SHA256}, + {EVP_PKEY_RSA_PSS, NID_sha384, SSL_SIGN_RSA_PSS_RSAE_SHA384}, + {EVP_PKEY_RSA_PSS, NID_sha512, SSL_SIGN_RSA_PSS_RSAE_SHA512}, + {EVP_PKEY_EC, NID_sha1, SSL_SIGN_ECDSA_SHA1}, + {EVP_PKEY_EC, NID_sha256, SSL_SIGN_ECDSA_SECP256R1_SHA256}, + {EVP_PKEY_EC, NID_sha384, SSL_SIGN_ECDSA_SECP384R1_SHA384}, + {EVP_PKEY_EC, NID_sha512, SSL_SIGN_ECDSA_SECP521R1_SHA512}, + {EVP_PKEY_ED25519, NID_undef, SSL_SIGN_ED25519}, +}; + +static bool parse_sigalg_pairs(Array<uint16_t> *out, const int *values, + size_t num_values) { + if ((num_values & 1) == 1) { + return false; + } + + const size_t num_pairs = num_values / 2; + if (!out->Init(num_pairs)) { + return false; + } + + for (size_t i = 0; i < num_values; i += 2) { + const int hash_nid = values[i]; + const int pkey_type = values[i+1]; + + bool found = false; + for (const auto &candidate : kSignatureAlgorithmsMapping) { + if (candidate.pkey_type == pkey_type && candidate.hash_nid == hash_nid) { + (*out)[i / 2] = candidate.signature_algorithm; + found = true; + break; + } + } + + if (!found) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("unknown hash:%d pkey:%d", hash_nid, pkey_type); + return false; + } + } + + return true; +} + +static int compare_uint16_t(const void *p1, const void *p2) { + uint16_t u1 = *((const uint16_t *)p1); + uint16_t u2 = *((const uint16_t *)p2); + if (u1 < u2) { + return -1; + } else if (u1 > u2) { + return 1; + } else { + return 0; + } +} + +static bool sigalgs_unique(Span<const uint16_t> in_sigalgs) { + Array<uint16_t> sigalgs; + if (!sigalgs.CopyFrom(in_sigalgs)) { + return false; + } + + qsort(sigalgs.data(), sigalgs.size(), sizeof(uint16_t), compare_uint16_t); + + for (size_t i = 1; i < sigalgs.size(); i++) { + if (sigalgs[i - 1] == sigalgs[i]) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_SIGNATURE_ALGORITHM); + return false; + } + } + + return true; +} + +int SSL_CTX_set1_sigalgs(SSL_CTX *ctx, const int *values, size_t num_values) { + Array<uint16_t> sigalgs; + if (!parse_sigalg_pairs(&sigalgs, values, num_values) || + !sigalgs_unique(sigalgs)) { + return 0; + } + + if (!SSL_CTX_set_signing_algorithm_prefs(ctx, sigalgs.data(), + sigalgs.size()) || + !ctx->verify_sigalgs.CopyFrom(sigalgs)) { + return 0; + } + + return 1; +} + +int SSL_set1_sigalgs(SSL *ssl, const int *values, size_t num_values) { + if (!ssl->config) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + Array<uint16_t> sigalgs; + if (!parse_sigalg_pairs(&sigalgs, values, num_values) || + !sigalgs_unique(sigalgs)) { + return 0; + } + + if (!SSL_set_signing_algorithm_prefs(ssl, sigalgs.data(), sigalgs.size()) || + !ssl->config->verify_sigalgs.CopyFrom(sigalgs)) { + return 0; + } + + return 1; +} + +static bool parse_sigalgs_list(Array<uint16_t> *out, const char *str) { + // str looks like "RSA+SHA1:ECDSA+SHA256:ecdsa_secp256r1_sha256". + + // Count colons to give the number of output elements from any successful + // parse. + size_t num_elements = 1; + size_t len = 0; + for (const char *p = str; *p; p++) { + len++; + if (*p == ':') { + num_elements++; + } + } + + if (!out->Init(num_elements)) { + return false; + } + size_t out_i = 0; + + enum { + pkey_or_name, + hash_name, + } state = pkey_or_name; + + char buf[kMaxSignatureAlgorithmNameLen]; + // buf_used is always < sizeof(buf). I.e. it's always safe to write + // buf[buf_used] = 0. + size_t buf_used = 0; + + int pkey_type = 0, hash_nid = 0; + + // Note that the loop runs to len+1, i.e. it'll process the terminating NUL. + for (size_t offset = 0; offset < len+1; offset++) { + const char c = str[offset]; + + switch (c) { + case '+': + if (state == hash_name) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("+ found in hash name at offset %zu", offset); + return false; + } + if (buf_used == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("empty public key type at offset %zu", offset); + return false; + } + buf[buf_used] = 0; + + if (strcmp(buf, "RSA") == 0) { + pkey_type = EVP_PKEY_RSA; + } else if (strcmp(buf, "RSA-PSS") == 0 || + strcmp(buf, "PSS") == 0) { + pkey_type = EVP_PKEY_RSA_PSS; + } else if (strcmp(buf, "ECDSA") == 0) { + pkey_type = EVP_PKEY_EC; + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("unknown public key type '%s'", buf); + return false; + } + + state = hash_name; + buf_used = 0; + break; + + case ':': + OPENSSL_FALLTHROUGH; + case 0: + if (buf_used == 0) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("empty element at offset %zu", offset); + return false; + } + + buf[buf_used] = 0; + + if (state == pkey_or_name) { + // No '+' was seen thus this is a TLS 1.3-style name. + bool found = false; + for (const auto &candidate : kSignatureAlgorithmNames) { + if (strcmp(candidate.name, buf) == 0) { + assert(out_i < num_elements); + (*out)[out_i++] = candidate.signature_algorithm; + found = true; + break; + } + } + + if (!found) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("unknown signature algorithm '%s'", buf); + return false; + } + } else { + if (strcmp(buf, "SHA1") == 0) { + hash_nid = NID_sha1; + } else if (strcmp(buf, "SHA256") == 0) { + hash_nid = NID_sha256; + } else if (strcmp(buf, "SHA384") == 0) { + hash_nid = NID_sha384; + } else if (strcmp(buf, "SHA512") == 0) { + hash_nid = NID_sha512; + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("unknown hash function '%s'", buf); + return false; + } + + bool found = false; + for (const auto &candidate : kSignatureAlgorithmsMapping) { + if (candidate.pkey_type == pkey_type && + candidate.hash_nid == hash_nid) { + assert(out_i < num_elements); + (*out)[out_i++] = candidate.signature_algorithm; + found = true; + break; + } + } + + if (!found) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("unknown pkey:%d hash:%s", pkey_type, buf); + return false; + } + } + + state = pkey_or_name; + buf_used = 0; + break; + + default: + if (buf_used == sizeof(buf) - 1) { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("substring too long at offset %zu", offset); + return false; + } + + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || c == '-' || c == '_') { + buf[buf_used++] = c; + } else { + OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM); + ERR_add_error_dataf("invalid character 0x%02x at offest %zu", c, + offset); + return false; + } + } + } + + assert(out_i == out->size()); + return true; +} + +int SSL_CTX_set1_sigalgs_list(SSL_CTX *ctx, const char *str) { + Array<uint16_t> sigalgs; + if (!parse_sigalgs_list(&sigalgs, str) || + !sigalgs_unique(sigalgs)) { + return 0; + } + + if (!SSL_CTX_set_signing_algorithm_prefs(ctx, sigalgs.data(), + sigalgs.size()) || + !ctx->verify_sigalgs.CopyFrom(sigalgs)) { + return 0; + } + + return 1; +} + +int SSL_set1_sigalgs_list(SSL *ssl, const char *str) { + if (!ssl->config) { + OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); + return 0; + } + + Array<uint16_t> sigalgs; + if (!parse_sigalgs_list(&sigalgs, str) || + !sigalgs_unique(sigalgs)) { + return 0; + } + + if (!SSL_set_signing_algorithm_prefs(ssl, sigalgs.data(), sigalgs.size()) || + !ssl->config->verify_sigalgs.CopyFrom(sigalgs)) { + return 0; + } + + return 1; +} + int SSL_CTX_set_verify_algorithm_prefs(SSL_CTX *ctx, const uint16_t *prefs, size_t num_prefs) { return ctx->verify_sigalgs.CopyFrom(MakeConstSpan(prefs, num_prefs));
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 695982b..835a998 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -4118,6 +4118,144 @@ EXPECT_EQ(43, byte); } +static std::string SigAlgsToString(Span<const uint16_t> sigalgs) { + std::string ret = "{"; + + for (uint16_t v : sigalgs) { + if (ret.size() > 1) { + ret += ", "; + } + + char buf[8]; + snprintf(buf, sizeof(buf) - 1, "0x%02x", v); + buf[sizeof(buf)-1] = 0; + ret += std::string(buf); + } + + ret += "}"; + return ret; +} + +void ExpectSigAlgsEqual(Span<const uint16_t> expected, + Span<const uint16_t> actual) { + bool matches = false; + if (expected.size() == actual.size()) { + matches = true; + + for (size_t i = 0; i < expected.size(); i++) { + if (expected[i] != actual[i]) { + matches = false; + break; + } + } + } + + if (!matches) { + ADD_FAILURE() << "expected: " << SigAlgsToString(expected) + << " got: " << SigAlgsToString(actual); + } +} + +TEST(SSLTest, SigAlgs) { + static const struct { + std::vector<int> input; + bool ok; + std::vector<uint16_t> expected; + } kTests[] = { + {{}, true, {}}, + {{1}, false, {}}, + {{1, 2, 3}, false, {}}, + {{NID_sha256, EVP_PKEY_ED25519}, false, {}}, + {{NID_sha256, EVP_PKEY_RSA, NID_sha256, EVP_PKEY_RSA}, false, {}}, + + {{NID_sha256, EVP_PKEY_RSA}, true, {SSL_SIGN_RSA_PKCS1_SHA256}}, + {{NID_sha512, EVP_PKEY_RSA}, true, {SSL_SIGN_RSA_PKCS1_SHA512}}, + {{NID_sha256, EVP_PKEY_RSA_PSS}, true, {SSL_SIGN_RSA_PSS_RSAE_SHA256}}, + {{NID_undef, EVP_PKEY_ED25519}, true, {SSL_SIGN_ED25519}}, + {{NID_undef, EVP_PKEY_ED25519, NID_sha384, EVP_PKEY_EC}, + true, + {SSL_SIGN_ED25519, SSL_SIGN_ECDSA_SECP384R1_SHA384}}, + }; + + UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + + unsigned n = 1; + for (const auto &test : kTests) { + SCOPED_TRACE(n++); + + const bool ok = + SSL_CTX_set1_sigalgs(ctx.get(), test.input.data(), test.input.size()); + EXPECT_EQ(ok, test.ok); + + if (!ok) { + ERR_clear_error(); + } + + if (!test.ok) { + continue; + } + + ExpectSigAlgsEqual(test.expected, ctx->cert->sigalgs); + } +} + +TEST(SSLTest, SigAlgsList) { + static const struct { + const char *input; + bool ok; + std::vector<uint16_t> expected; + } kTests[] = { + {"", false, {}}, + {":", false, {}}, + {"+", false, {}}, + {"RSA", false, {}}, + {"RSA+", false, {}}, + {"RSA+SHA256:", false, {}}, + {":RSA+SHA256:", false, {}}, + {":RSA+SHA256+:", false, {}}, + {"!", false, {}}, + {"\x01", false, {}}, + {"RSA+SHA256:RSA+SHA384:RSA+SHA256", false, {}}, + {"RSA-PSS+SHA256:rsa_pss_rsae_sha256", false, {}}, + + {"RSA+SHA256", true, {SSL_SIGN_RSA_PKCS1_SHA256}}, + {"RSA+SHA256:ed25519", + true, + {SSL_SIGN_RSA_PKCS1_SHA256, SSL_SIGN_ED25519}}, + {"ECDSA+SHA256:RSA+SHA512", + true, + {SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PKCS1_SHA512}}, + {"ecdsa_secp256r1_sha256:rsa_pss_rsae_sha256", + true, + {SSL_SIGN_ECDSA_SECP256R1_SHA256, SSL_SIGN_RSA_PSS_RSAE_SHA256}}, + {"RSA-PSS+SHA256", true, {SSL_SIGN_RSA_PSS_RSAE_SHA256}}, + {"PSS+SHA256", true, {SSL_SIGN_RSA_PSS_RSAE_SHA256}}, + }; + + UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method())); + + unsigned n = 1; + for (const auto &test : kTests) { + SCOPED_TRACE(n++); + + const bool ok = SSL_CTX_set1_sigalgs_list(ctx.get(), test.input); + EXPECT_EQ(ok, test.ok); + + if (!ok) { + if (test.ok) { + ERR_print_errors_fp(stderr); + } + ERR_clear_error(); + } + + if (!test.ok) { + continue; + } + + ExpectSigAlgsEqual(test.expected, ctx->cert->sigalgs); + } +} + TEST_P(SSLVersionTest, VerifyBeforeCertRequest) { // Configure the server to request client certificates. SSL_CTX_set_custom_verify(
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc index 430b13d..dde767e 100644 --- a/ssl/t1_lib.cc +++ b/ssl/t1_lib.cc
@@ -492,8 +492,8 @@ static SSLSignatureAlgorithmList tls12_get_verify_sigalgs(const SSL *ssl, bool for_certs) { SSLSignatureAlgorithmList ret; - if (!ssl->ctx->verify_sigalgs.empty()) { - ret.list = ssl->ctx->verify_sigalgs; + if (!ssl->config->verify_sigalgs.empty()) { + ret.list = ssl->config->verify_sigalgs; } else { ret.list = kVerifySignatureAlgorithms; ret.skip_ed25519 = !ssl->ctx->ed25519_enabled;