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/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));