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;