Add ECDSA and RSA verify Wycheproof drivers.

Along the way, add some utility functions for getting common things
(curves, hashes, etc.) in the names Wycheproof uses.

Change-Id: I09c11ea2970cf2c8a11a8c2a861d85396efda125
Reviewed-on: https://boringssl-review.googlesource.com/27786
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc
index 3bcdbd6..d73c2c4 100644
--- a/crypto/evp/evp_test.cc
+++ b/crypto/evp/evp_test.cc
@@ -79,6 +79,7 @@
 
 #include "../test/file_test.h"
 #include "../test/test_util.h"
+#include "../test/wycheproof_util.h"
 
 
 // evp_test dispatches between multiple test types. PrivateKey tests take a key
@@ -413,3 +414,54 @@
     }
   });
 }
+
+static void RunWycheproofTest(const char *path) {
+  SCOPED_TRACE(path);
+  FileTestGTest(path, [](FileTest *t) {
+    t->IgnoreInstruction("key.type");
+    // Extra ECDSA fields.
+    t->IgnoreInstruction("key.curve");
+    t->IgnoreInstruction("key.keySize");
+    t->IgnoreInstruction("key.wx");
+    t->IgnoreInstruction("key.wy");
+    // Extra RSA fields.
+    t->IgnoreInstruction("e");
+    t->IgnoreInstruction("keyAsn");
+    t->IgnoreInstruction("keysize");
+    t->IgnoreInstruction("n");
+    t->IgnoreAttribute("padding");
+
+    std::vector<uint8_t> der;
+    ASSERT_TRUE(t->GetInstructionBytes(&der, "keyDer"));
+    CBS cbs;
+    CBS_init(&cbs, der.data(), der.size());
+    bssl::UniquePtr<EVP_PKEY> key(EVP_parse_public_key(&cbs));
+    ASSERT_TRUE(key);
+
+    const EVP_MD *md = GetWycheproofDigest(t, "sha", true);
+    ASSERT_TRUE(md);
+    std::vector<uint8_t> msg;
+    ASSERT_TRUE(t->GetBytes(&msg, "msg"));
+    std::vector<uint8_t> sig;
+    ASSERT_TRUE(t->GetBytes(&sig, "sig"));
+
+    bssl::ScopedEVP_MD_CTX ctx;
+    ASSERT_TRUE(
+        EVP_DigestVerifyInit(ctx.get(), nullptr, md, nullptr, key.get()));
+    WycheproofResult result;
+    ASSERT_TRUE(GetWycheproofResult(t, &result));
+    EXPECT_EQ(result == WycheproofResult::kValid ? 1 : 0,
+              EVP_DigestVerify(ctx.get(), sig.data(), sig.size(), msg.data(),
+                               msg.size()));
+  });
+}
+
+TEST(EVPTest, Wycheproof) {
+  RunWycheproofTest("third_party/wycheproof/ecdsa_secp224r1_sha224_test.txt");
+  RunWycheproofTest("third_party/wycheproof/ecdsa_secp224r1_sha256_test.txt");
+  RunWycheproofTest("third_party/wycheproof/ecdsa_secp256r1_sha256_test.txt");
+  RunWycheproofTest("third_party/wycheproof/ecdsa_secp384r1_sha384_test.txt");
+  RunWycheproofTest("third_party/wycheproof/ecdsa_secp384r1_sha512_test.txt");
+  RunWycheproofTest("third_party/wycheproof/ecdsa_secp521r1_sha512_test.txt");
+  RunWycheproofTest("third_party/wycheproof/rsa_signature_test.txt");
+}
diff --git a/crypto/test/CMakeLists.txt b/crypto/test/CMakeLists.txt
index 46a40f8..90707dd 100644
--- a/crypto/test/CMakeLists.txt
+++ b/crypto/test/CMakeLists.txt
@@ -6,6 +6,7 @@
   file_test.cc
   malloc.cc
   test_util.cc
+  wycheproof_util.cc
 )
 
 add_library(
diff --git a/crypto/test/file_test.cc b/crypto/test/file_test.cc
index cbb9f7f..2ad49d3 100644
--- a/crypto/test/file_test.cc
+++ b/crypto/test/file_test.cc
@@ -286,52 +286,19 @@
   return true;
 }
 
+bool FileTest::GetInstructionBytes(std::vector<uint8_t> *out,
+                                   const std::string &key) {
+  std::string value;
+  return GetInstruction(&value, key) && ConvertToBytes(out, value);
+}
+
 const std::string &FileTest::CurrentTestToString() const {
   return current_test_;
 }
 
-static bool FromHexDigit(uint8_t *out, char c) {
-  if ('0' <= c && c <= '9') {
-    *out = c - '0';
-    return true;
-  }
-  if ('a' <= c && c <= 'f') {
-    *out = c - 'a' + 10;
-    return true;
-  }
-  if ('A' <= c && c <= 'F') {
-    *out = c - 'A' + 10;
-    return true;
-  }
-  return false;
-}
-
 bool FileTest::GetBytes(std::vector<uint8_t> *out, const std::string &key) {
   std::string value;
-  if (!GetAttribute(&value, key)) {
-    return false;
-  }
-
-  if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') {
-    out->assign(value.begin() + 1, value.end() - 1);
-    return true;
-  }
-
-  if (value.size() % 2 != 0) {
-    PrintLine("Error decoding value: %s", value.c_str());
-    return false;
-  }
-  out->clear();
-  out->reserve(value.size() / 2);
-  for (size_t i = 0; i < value.size(); i += 2) {
-    uint8_t hi, lo;
-    if (!FromHexDigit(&hi, value[i]) || !FromHexDigit(&lo, value[i + 1])) {
-      PrintLine("Error decoding value: %s", value.c_str());
-      return false;
-    }
-    out->push_back((hi << 4) | lo);
-  }
-  return true;
+  return GetAttribute(&value, key) && ConvertToBytes(out, value);
 }
 
 static std::string EncodeHex(const uint8_t *in, size_t in_len) {
@@ -381,6 +348,46 @@
   unused_instructions_.erase(key);
 }
 
+static bool FromHexDigit(uint8_t *out, char c) {
+  if ('0' <= c && c <= '9') {
+    *out = c - '0';
+    return true;
+  }
+  if ('a' <= c && c <= 'f') {
+    *out = c - 'a' + 10;
+    return true;
+  }
+  if ('A' <= c && c <= 'F') {
+    *out = c - 'A' + 10;
+    return true;
+  }
+  return false;
+}
+
+bool FileTest::ConvertToBytes(std::vector<uint8_t> *out,
+                              const std::string &value) {
+  if (value.size() >= 2 && value[0] == '"' && value[value.size() - 1] == '"') {
+    out->assign(value.begin() + 1, value.end() - 1);
+    return true;
+  }
+
+  if (value.size() % 2 != 0) {
+    PrintLine("Error decoding value: %s", value.c_str());
+    return false;
+  }
+  out->clear();
+  out->reserve(value.size() / 2);
+  for (size_t i = 0; i < value.size(); i += 2) {
+    uint8_t hi, lo;
+    if (!FromHexDigit(&hi, value[i]) || !FromHexDigit(&lo, value[i + 1])) {
+      PrintLine("Error decoding value: %s", value.c_str());
+      return false;
+    }
+    out->push_back((hi << 4) | lo);
+  }
+  return true;
+}
+
 bool FileTest::IsAtNewInstructionBlock() const {
   return is_at_new_instruction_block_;
 }
diff --git a/crypto/test/file_test.h b/crypto/test/file_test.h
index a164e99..52a2c3f 100644
--- a/crypto/test/file_test.h
+++ b/crypto/test/file_test.h
@@ -185,6 +185,10 @@
   // otherwise.
   bool GetInstruction(std::string *out_value, const std::string &key);
 
+  // GetInstructionBytes behaves like GetBytes, but looks up the corresponding
+  // instruction.
+  bool GetInstructionBytes(std::vector<uint8_t> *out, const std::string &key);
+
   // CurrentTestToString returns the file content parsed for the current test.
   // If the current test was preceded by an instruction block, the return test
   // case is preceded by the instruction block and a single blank line. All
@@ -203,6 +207,7 @@
   void ClearInstructions();
   void OnKeyUsed(const std::string &key);
   void OnInstructionUsed(const std::string &key);
+  bool ConvertToBytes(std::vector<uint8_t> *out, const std::string &value);
 
   std::unique_ptr<LineReader> reader_;
   // line_ is the number of lines read.
diff --git a/crypto/test/wycheproof_util.cc b/crypto/test/wycheproof_util.cc
new file mode 100644
index 0000000..3ff132f
--- /dev/null
+++ b/crypto/test/wycheproof_util.cc
@@ -0,0 +1,91 @@
+/* Copyright (c) 2018, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include "./wycheproof_util.h"
+
+#include <openssl/digest.h>
+#include <openssl/ec.h>
+#include <openssl/nid.h>
+
+#include "./file_test.h"
+
+
+bool GetWycheproofResult(FileTest *t, WycheproofResult *out) {
+  std::string result;
+  if (!t->GetAttribute(&result, "result")) {
+    return false;
+  }
+  if (result == "valid") {
+    *out = WycheproofResult::kValid;
+  } else if (result == "invalid") {
+    *out = WycheproofResult::kInvalid;
+  } else if (result == "acceptable") {
+    *out = WycheproofResult::kAcceptable;
+  } else {
+    t->PrintLine("Bad result string '%s'", result.c_str());
+    return false;
+  }
+  return true;
+}
+
+const EVP_MD *GetWycheproofDigest(FileTest *t, const char *key,
+                                  bool instruction) {
+  std::string name;
+  bool ok =
+      instruction ? t->GetInstruction(&name, key) : t->GetAttribute(&name, key);
+  if (!ok) {
+    return nullptr;
+  }
+  if (name == "SHA-1") {
+    return EVP_sha1();
+  }
+  if (name == "SHA-224") {
+    return EVP_sha224();
+  }
+  if (name == "SHA-256") {
+    return EVP_sha256();
+  }
+  if (name == "SHA-384") {
+    return EVP_sha384();
+  }
+  if (name == "SHA-512") {
+    return EVP_sha512();
+  }
+  t->PrintLine("Unknown digest '%s'", name.c_str());
+  return nullptr;
+}
+
+bssl::UniquePtr<EC_GROUP> GetWycheproofCurve(FileTest *t, const char *key,
+                                             bool instruction) {
+  std::string name;
+  bool ok =
+      instruction ? t->GetInstruction(&name, key) : t->GetAttribute(&name, key);
+  if (!ok) {
+    return nullptr;
+  }
+  int nid;
+  if (name == "secp224r1") {
+    nid = NID_secp224r1;
+  } else if (name == "secp256r1") {
+    nid = NID_X9_62_prime256v1;
+  } else if (name == "secp384r1") {
+    nid = NID_secp384r1;
+  } else if (name == "secp521r1") {
+    nid = NID_secp521r1;
+  } else {
+    t->PrintLine("Unknown curve '%s'", name.c_str());
+    return nullptr;
+  }
+  return bssl::UniquePtr<EC_GROUP>(EC_GROUP_new_by_curve_name(nid));
+}
diff --git a/crypto/test/wycheproof_util.h b/crypto/test/wycheproof_util.h
new file mode 100644
index 0000000..cf50b8e
--- /dev/null
+++ b/crypto/test/wycheproof_util.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2018, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_TEST_WYCHEPROOF_UTIL_H
+#define OPENSSL_HEADER_CRYPTO_TEST_WYCHEPROOF_UTIL_H
+
+#include <openssl/base.h>
+
+
+// This header contains convenience functions for Wycheproof tests.
+
+class FileTest;
+
+enum class WycheproofResult {
+  kValid,
+  kInvalid,
+  kAcceptable,
+};
+
+// GetWycheproofResult sets |*out| to the parsed "result" key of |t|.
+bool GetWycheproofResult(FileTest *t, WycheproofResult *out);
+
+// GetWycheproofDigest returns a digest function using the Wycheproof name, or
+// nullptr on error.
+const EVP_MD *GetWycheproofDigest(FileTest *t, const char *key,
+                                  bool instruction);
+
+// GetWycheproofCurve returns a curve using the Wycheproof name, or nullptr on
+// error.
+bssl::UniquePtr<EC_GROUP> GetWycheproofCurve(FileTest *t, const char *key,
+                                             bool instruction);
+
+
+#endif  // OPENSSL_HEADER_CRYPTO_TEST_WYCHEPROOF_UTIL_H