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>kris/onging/CECPQ3_patch15
@@ -79,6 +79,7 @@ OPENSSL_MSVC_PRAGMA(warning(pop)) | |||
#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 @@ TEST(EVPTest, TestVectors) { | |||
} | |||
}); | |||
} | |||
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"); | |||
} |
@@ -6,6 +6,7 @@ add_library( | |||
file_test.cc | |||
malloc.cc | |||
test_util.cc | |||
wycheproof_util.cc | |||
) | |||
add_library( | |||
@@ -286,52 +286,19 @@ bool FileTest::GetInstruction(std::string *out_value, const std::string &key) { | |||
return true; | |||
} | |||
const std::string &FileTest::CurrentTestToString() const { | |||
return current_test_; | |||
bool FileTest::GetInstructionBytes(std::vector<uint8_t> *out, | |||
const std::string &key) { | |||
std::string value; | |||
return GetInstruction(&value, key) && ConvertToBytes(out, value); | |||
} | |||
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; | |||
const std::string &FileTest::CurrentTestToString() const { | |||
return current_test_; | |||
} | |||
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 @@ void FileTest::OnInstructionUsed(const std::string &key) { | |||
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_; | |||
} | |||
@@ -185,6 +185,10 @@ class FileTest { | |||
// 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 @@ class FileTest { | |||
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. | |||
@@ -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)); | |||
} |
@@ -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 |
@@ -59,5 +59,12 @@ set( | |||
crypto/x509/some_names1.pem | |||
crypto/x509/some_names2.pem | |||
crypto/x509/some_names3.pem | |||
third_party/wycheproof/ecdsa_secp224r1_sha224_test.txt | |||
third_party/wycheproof/ecdsa_secp224r1_sha256_test.txt | |||
third_party/wycheproof/ecdsa_secp256r1_sha256_test.txt | |||
third_party/wycheproof/ecdsa_secp384r1_sha384_test.txt | |||
third_party/wycheproof/ecdsa_secp384r1_sha512_test.txt | |||
third_party/wycheproof/ecdsa_secp521r1_sha512_test.txt | |||
third_party/wycheproof/rsa_signature_test.txt | |||
third_party/wycheproof/x25519_test.txt | |||
) |
@@ -224,6 +224,13 @@ func convertWycheproof(jsonPath, txtPath string) error { | |||
func main() { | |||
jsonPaths := []string{ | |||
"ecdsa_secp224r1_sha224_test.json", | |||
"ecdsa_secp224r1_sha256_test.json", | |||
"ecdsa_secp256r1_sha256_test.json", | |||
"ecdsa_secp384r1_sha384_test.json", | |||
"ecdsa_secp384r1_sha512_test.json", | |||
"ecdsa_secp521r1_sha512_test.json", | |||
"rsa_signature_test.json", | |||
"x25519_test.json", | |||
// TODO(davidben): The following tests still need test drivers. | |||
@@ -233,14 +240,7 @@ func main() { | |||
// "chacha20_poly1305_test.json", | |||
// "dsa_test.json", | |||
// "ecdh_test.json", | |||
// "ecdsa_secp224r1_sha224_test.json", | |||
// "ecdsa_secp224r1_sha256_test.json", | |||
// "ecdsa_secp256r1_sha256_test.json", | |||
// "ecdsa_secp384r1_sha384_test.json", | |||
// "ecdsa_secp384r1_sha512_test.json", | |||
// "ecdsa_secp521r1_sha512_test.json", | |||
// "eddsa_test.json", | |||
// "rsa_signature_test.json", | |||
} | |||
for _, jsonPath := range jsonPaths { | |||
if !strings.HasSuffix(jsonPath, ".json") { | |||