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