Add SSL_set_signing_algorithm_prefs.

This gives us a sigalg-based API for configuring signing algorithms.

Change-Id: Ib746a56ebd1061eadd2620cdb140d5171b59bc02
Reviewed-on: https://boringssl-review.googlesource.com/8784
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index d0bc103..7b4b349 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -869,13 +869,31 @@
                                              const uint8_t *response,
                                              size_t response_len);
 
-/* SSL_set_private_key_digest_prefs copies |num_digests| NIDs from |digest_nids|
- * into |ssl|. These digests will be used, in decreasing order of preference,
- * when signing with |ssl|'s private key. It returns one on success and zero on
- * error. */
-OPENSSL_EXPORT int SSL_set_private_key_digest_prefs(SSL *ssl,
-                                                    const int *digest_nids,
-                                                    size_t num_digests);
+/* SSL_SIGN_* are signature algorithm values as defined in TLS 1.3. */
+#define SSL_SIGN_RSA_PKCS1_SHA1 0x0201
+#define SSL_SIGN_RSA_PKCS1_SHA256 0x0401
+#define SSL_SIGN_RSA_PKCS1_SHA384 0x0501
+#define SSL_SIGN_RSA_PKCS1_SHA512 0x0601
+#define SSL_SIGN_ECDSA_SHA1 0x0203
+#define SSL_SIGN_ECDSA_SECP256R1_SHA256 0x0403
+#define SSL_SIGN_ECDSA_SECP384R1_SHA384 0x0503
+#define SSL_SIGN_ECDSA_SECP521R1_SHA512 0x0603
+#define SSL_SIGN_RSA_PSS_SHA256 0x0700
+#define SSL_SIGN_RSA_PSS_SHA384 0x0701
+#define SSL_SIGN_RSA_PSS_SHA512 0x0702
+
+/* SSL_SIGN_RSA_PKCS1_MD5_SHA1 is an internal signature algorithm used to
+ * specify raw RSASSA-PKCS1-v1_5 with an MD5/SHA-1 concatenation, as used in TLS
+ * before TLS 1.2. */
+#define SSL_SIGN_RSA_PKCS1_MD5_SHA1 0xff01
+
+/* SSL_set_signing_algorithm_prefs configures |ssl| to use |prefs| as the
+ * preference list when signing with |ssl|'s private key. It returns one on
+ * success and zero on error. |prefs| should not include the internal-only value
+ * |SSL_SIGN_RSA_PKCS1_MD5_SHA1|. */
+OPENSSL_EXPORT int SSL_set_signing_algorithm_prefs(SSL *ssl,
+                                                   const uint16_t *prefs,
+                                                   size_t prefs_len);
 
 
 /* Certificate and private key convenience functions. */
@@ -3482,6 +3500,18 @@
 OPENSSL_EXPORT uint32_t SSL_SESSION_get_key_exchange_info(
     const SSL_SESSION *session);
 
+/* SSL_set_private_key_digest_prefs copies |num_digests| NIDs from |digest_nids|
+ * into |ssl|. These digests will be used, in decreasing order of preference,
+ * when signing with |ssl|'s private key. It returns one on success and zero on
+ * error.
+ *
+ * Use |SSL_set_signing_algorithm_prefs| instead.
+ *
+ * TODO(davidben): Remove this API when callers have been updated. */
+OPENSSL_EXPORT int SSL_set_private_key_digest_prefs(SSL *ssl,
+                                                    const int *digest_nids,
+                                                    size_t num_digests);
+
 
 /* Private structures.
  *
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 84ff12f..ae1ab7c 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -245,23 +245,6 @@
 #define TLSEXT_hash_sha384 5
 #define TLSEXT_hash_sha512 6
 
-#define SSL_SIGN_RSA_PKCS1_SHA1         0x0201
-#define SSL_SIGN_RSA_PKCS1_SHA256       0x0401
-#define SSL_SIGN_RSA_PKCS1_SHA384       0x0501
-#define SSL_SIGN_RSA_PKCS1_SHA512       0x0601
-#define SSL_SIGN_ECDSA_SHA1             0x0203
-#define SSL_SIGN_ECDSA_SECP256R1_SHA256 0x0403
-#define SSL_SIGN_ECDSA_SECP384R1_SHA384 0x0503
-#define SSL_SIGN_ECDSA_SECP521R1_SHA512 0x0603
-#define SSL_SIGN_RSA_PSS_SHA256         0x0700
-#define SSL_SIGN_RSA_PSS_SHA384         0x0701
-#define SSL_SIGN_RSA_PSS_SHA512         0x0702
-
-/* Reserved SignatureScheme value to indicate RSA with MD5-SHA1. This will never
- * be negotiated in TLS 1.2 and up, but is used to unify signing interfaces in
- * older TLS versions. */
-#define SSL_SIGN_RSA_PKCS1_MD5_SHA1           0xff01
-
 #define TLSEXT_MAXLEN_host_name 255
 
 /* PSK ciphersuites from 4279 */
diff --git a/ssl/ssl_rsa.c b/ssl/ssl_rsa.c
index 3e0894e..fec5d46 100644
--- a/ssl/ssl_rsa.c
+++ b/ssl/ssl_rsa.c
@@ -335,6 +335,19 @@
   ctx->cert->key_method = key_method;
 }
 
+int SSL_set_signing_algorithm_prefs(SSL *ssl, const uint16_t *prefs,
+                                    size_t prefs_len) {
+  ssl->cert->sigalgs_len = 0;
+  ssl->cert->sigalgs = BUF_memdup(prefs, prefs_len * sizeof(prefs[0]));
+  if (ssl->cert->sigalgs == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  ssl->cert->sigalgs_len = prefs_len;
+
+  return 1;
+}
+
 OPENSSL_COMPILE_ASSERT(sizeof(int) >= 2 * sizeof(uint16_t),
                        digest_list_conversion_cannot_overflow);
 
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 02af8f2..3cfbeb3 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -315,6 +315,14 @@
     }
   }
 
+  if (!config->signing_prefs.empty()) {
+    std::vector<uint16_t> u16s(config->signing_prefs.begin(),
+                               config->signing_prefs.end());
+    if (!SSL_set_signing_algorithm_prefs(ssl, u16s.data(), u16s.size())) {
+      return false;
+    }
+  }
+
   if (!config->key_file.empty()) {
     *out_pkey = LoadPrivateKey(config->key_file.c_str());
     if (!*out_pkey) {
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 5bbf57d..5a3256c 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -259,8 +259,6 @@
 	messageLen int
 	// messageCount is the number of test messages that will be sent.
 	messageCount int
-	// digestPrefs is the list of digest preferences from the client.
-	digestPrefs string
 	// certFile is the path to the certificate to use for the server.
 	certFile string
 	// keyFile is the path to the private key to use for the server.
@@ -744,11 +742,6 @@
 		}
 	}
 
-	if test.digestPrefs != "" {
-		flags = append(flags, "-digest-prefs")
-		flags = append(flags, test.digestPrefs)
-	}
-
 	if test.protocol == dtls {
 		flags = append(flags, "-dtls")
 	}
@@ -4736,6 +4729,13 @@
 		TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
 	}
 
+	var allAlgorithms []signatureAlgorithm
+	for _, alg := range testSignatureAlgorithms {
+		if alg.id != 0 {
+			allAlgorithms = append(allAlgorithms, alg.id)
+		}
+	}
+
 	// Make sure each signature algorithm works. Include some fake values in
 	// the list and ensure they're ignored.
 	for _, alg := range testSignatureAlgorithms {
@@ -4899,6 +4899,41 @@
 					expectedError: ":BAD_SIGNATURE:",
 				})
 			}
+
+			if ver.version >= VersionTLS12 && !shouldFail {
+				testCases = append(testCases, testCase{
+					name: "ClientAuth-Sign-Negotiate" + suffix,
+					config: Config{
+						MaxVersion:                ver.version,
+						ClientAuth:                RequireAnyClientCert,
+						VerifySignatureAlgorithms: allAlgorithms,
+					},
+					flags: []string{
+						"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
+						"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
+						"-enable-all-curves",
+						"-signing-prefs", strconv.Itoa(int(alg.id)),
+					},
+					expectedPeerSignatureAlgorithm: alg.id,
+				})
+
+				testCases = append(testCases, testCase{
+					testType: serverTest,
+					name:     "ServerAuth-Sign-Negotiate" + suffix,
+					config: Config{
+						MaxVersion:                ver.version,
+						CipherSuites:              signingCiphers,
+						VerifySignatureAlgorithms: allAlgorithms,
+					},
+					flags: []string{
+						"-cert-file", path.Join(*resourceDir, getShimCertificate(alg.cert)),
+						"-key-file", path.Join(*resourceDir, getShimKey(alg.cert)),
+						"-enable-all-curves",
+						"-signing-prefs", strconv.Itoa(int(alg.id)),
+					},
+					expectedPeerSignatureAlgorithm: alg.id,
+				})
+			}
 		}
 	}
 
@@ -5095,6 +5130,24 @@
 	//
 	// TODO(davidben): Add TLS 1.3 versions of these.
 	testCases = append(testCases, testCase{
+		name: "NoCommonAlgorithms-Digests",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			ClientAuth: RequireAnyClientCert,
+			VerifySignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA512,
+				signatureRSAPKCS1WithSHA1,
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-digest-prefs", "SHA256",
+		},
+		shouldFail:    true,
+		expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
+	})
+	testCases = append(testCases, testCase{
 		name: "NoCommonAlgorithms",
 		config: Config{
 			MaxVersion: VersionTLS12,
@@ -5107,8 +5160,26 @@
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
 		},
-		digestPrefs:   "SHA256",
+		shouldFail:    true,
+		expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
+	})
+	testCases = append(testCases, testCase{
+		name: "NoCommonAlgorithms-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			ClientAuth: RequireAnyClientCert,
+			VerifySignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPSSWithSHA512,
+				signatureRSAPSSWithSHA384,
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-signing-prefs", strconv.Itoa(int(signatureRSAPSSWithSHA256)),
+		},
 		shouldFail:    true,
 		expectedError: ":NO_COMMON_SIGNATURE_ALGORITHMS:",
 	})
@@ -5125,8 +5196,8 @@
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-digest-prefs", "SHA256,SHA1",
 		},
-		digestPrefs:                    "SHA256,SHA1",
 		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
 	})
 	testCases = append(testCases, testCase{
@@ -5141,8 +5212,8 @@
 		flags: []string{
 			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
 			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-digest-prefs", "SHA512,SHA256,SHA1",
 		},
-		digestPrefs:                    "SHA512,SHA256,SHA1",
 		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA1,
 	})
 	testCases = append(testCases, testCase{
@@ -5164,6 +5235,28 @@
 		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
 	})
 
+	// Test that the signing preference list may include extra algorithms
+	// without negotiation problems.
+	testCases = append(testCases, testCase{
+		testType: serverTest,
+		name:     "FilterExtraAlgorithms",
+		config: Config{
+			MaxVersion: VersionTLS12,
+			VerifySignatureAlgorithms: []signatureAlgorithm{
+				signatureRSAPKCS1WithSHA256,
+			},
+		},
+		flags: []string{
+			"-cert-file", path.Join(*resourceDir, rsaCertificateFile),
+			"-key-file", path.Join(*resourceDir, rsaKeyFile),
+			"-signing-prefs", strconv.Itoa(int(fakeSigAlg1)),
+			"-signing-prefs", strconv.Itoa(int(signatureECDSAWithP256AndSHA256)),
+			"-signing-prefs", strconv.Itoa(int(signatureRSAPKCS1WithSHA256)),
+			"-signing-prefs", strconv.Itoa(int(fakeSigAlg2)),
+		},
+		expectedPeerSignatureAlgorithm: signatureRSAPKCS1WithSHA256,
+	})
+
 	// In TLS 1.2 and below, ECDSA uses the curve list rather than the
 	// signature algorithms.
 	testCases = append(testCases, testCase{
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 1c3302e..24a4646 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -153,6 +153,10 @@
   { "-initial-timeout-duration-ms", &TestConfig::initial_timeout_duration_ms },
 };
 
+const Flag<std::vector<int>> kIntVectorFlags[] = {
+  { "-signing-prefs", &TestConfig::signing_prefs },
+};
+
 }  // namespace
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
@@ -208,6 +212,20 @@
       continue;
     }
 
+    std::vector<int> *int_vector_field =
+        FindField(out_config, kIntVectorFlags, argv[i]);
+    if (int_vector_field) {
+      i++;
+      if (i >= argc) {
+        fprintf(stderr, "Missing parameter\n");
+        return false;
+      }
+
+      // Each instance of the flag adds to the list.
+      int_vector_field->push_back(atoi(argv[i]));
+      continue;
+    }
+
     fprintf(stderr, "Unknown argument: %s\n", argv[i]);
     return false;
   }
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index b3c858d..4ee717e 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -16,6 +16,7 @@
 #define HEADER_TEST_CONFIG
 
 #include <string>
+#include <vector>
 
 
 struct TestConfig {
@@ -25,6 +26,7 @@
   bool resume = false;
   bool fallback_scsv = false;
   std::string digest_prefs;
+  std::vector<int> signing_prefs;
   std::string key_file;
   std::string cert_file;
   std::string expected_server_name;