| Index: net/cert/internal/verify_signed_data_unittest.cc
|
| diff --git a/net/cert/internal/verify_signed_data_unittest.cc b/net/cert/internal/verify_signed_data_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..5162c2d67bd4b5a1560427492ccfda181148881c
|
| --- /dev/null
|
| +++ b/net/cert/internal/verify_signed_data_unittest.cc
|
| @@ -0,0 +1,281 @@
|
| +// Copyright 2015 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "net/cert/internal/verify_signed_data.h"
|
| +
|
| +#include <set>
|
| +
|
| +#include "base/base_paths.h"
|
| +#include "base/files/file_util.h"
|
| +#include "base/path_service.h"
|
| +#include "net/cert/internal/signature_algorithm.h"
|
| +#include "net/cert/pem_tokenizer.h"
|
| +#include "net/der/input.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +// Creates a der::Input from an std::string. The lifetimes are a bit subtle
|
| +// when using this function:
|
| +//
|
| +// The returned der::Input() is only valid so long as the input string is alive
|
| +// and is not mutated.
|
| +//
|
| +// Note that the input parameter has been made a pointer to prevent callers
|
| +// from accidentally passing an r-value.
|
| +der::Input InputFromString(const std::string* s) {
|
| + return der::Input(reinterpret_cast<const uint8_t*>(s->data()), s->size());
|
| +}
|
| +
|
| +// Reads a signature verification test file.
|
| +//
|
| +// The test file is a series of PEM blocks (PEM is just base64 data) with
|
| +// headings of:
|
| +//
|
| +// "PUBLIC KEY" - DER encoding of the SubjectPublicKeyInfo
|
| +// "ALGORITHM" - DER encoding of the AlgorithmIdentifier for the signature
|
| +// algorithm (signatureAlgorithm in X.509)
|
| +// "DATA" - The data that was signed (tbsCertificate in X.509)
|
| +// "SIGNATURE" - The result of signing DATA.
|
| +::testing::AssertionResult ParseTestDataFile(const std::string& file_data,
|
| + std::string* public_key,
|
| + std::string* algorithm,
|
| + std::string* signed_data,
|
| + std::string* signature_value) {
|
| + const char kPublicKeyBlock[] = "PUBLIC KEY";
|
| + const char kAlgorithmBlock[] = "ALGORITHM";
|
| + const char kSignedDataBlock[] = "DATA";
|
| + const char kSignatureBlock[] = "SIGNATURE";
|
| +
|
| + std::vector<std::string> pem_headers;
|
| + pem_headers.push_back(kPublicKeyBlock);
|
| + pem_headers.push_back(kAlgorithmBlock);
|
| + pem_headers.push_back(kSignedDataBlock);
|
| + pem_headers.push_back(kSignatureBlock);
|
| +
|
| + // Keep track of which blocks have been encountered (by elimination).
|
| + std::set<std::string> remaining_blocks(pem_headers.begin(),
|
| + pem_headers.end());
|
| +
|
| + PEMTokenizer pem_tokenizer(file_data, pem_headers);
|
| + while (pem_tokenizer.GetNext()) {
|
| + const std::string& block_type = pem_tokenizer.block_type();
|
| + if (block_type == kPublicKeyBlock) {
|
| + public_key->assign(pem_tokenizer.data());
|
| + } else if (block_type == kAlgorithmBlock) {
|
| + algorithm->assign(pem_tokenizer.data());
|
| + } else if (block_type == kSignedDataBlock) {
|
| + signed_data->assign(pem_tokenizer.data());
|
| + } else if (block_type == kSignatureBlock) {
|
| + signature_value->assign(pem_tokenizer.data());
|
| + }
|
| +
|
| + if (remaining_blocks.erase(block_type) != 1u) {
|
| + return ::testing::AssertionFailure()
|
| + << "PEM block defined multiple times: " << block_type;
|
| + }
|
| + }
|
| +
|
| + if (!remaining_blocks.empty()) {
|
| + // Print one of the missing PEM blocks.
|
| + return ::testing::AssertionFailure() << "PEM block missing: "
|
| + << *remaining_blocks.begin();
|
| + }
|
| +
|
| + return ::testing::AssertionSuccess();
|
| +}
|
| +
|
| +// Returns a path to the file |file_name| within the unittest data directory.
|
| +base::FilePath GetTestFilePath(const char* file_name) {
|
| + base::FilePath src_root;
|
| + PathService::Get(base::DIR_SOURCE_ROOT, &src_root);
|
| + return src_root.Append(
|
| + FILE_PATH_LITERAL("net/data/verify_signed_data_unittest"))
|
| + .AppendASCII(file_name);
|
| +}
|
| +
|
| +enum VerifyResult {
|
| + SUCCESS,
|
| + FAILURE,
|
| +};
|
| +
|
| +// Reads test data from |file_name| and runs VerifySignedData() over its inputs.
|
| +//
|
| +// If expected_result was SUCCESS then the test will only succeed if
|
| +// VerifySignedData() returns true.
|
| +//
|
| +// If expected_result was FAILURE then the test will only succeed if
|
| +// VerifySignedData() returns false.
|
| +void RunTestCase(VerifyResult expected_result, const char* file_name) {
|
| +#if !defined(USE_OPENSSL)
|
| + LOG(INFO) << "Skipping test, only implemented for BoringSSL";
|
| + return;
|
| +#endif
|
| +
|
| + base::FilePath test_file_path = GetTestFilePath(file_name);
|
| +
|
| + std::string file_data;
|
| + ASSERT_TRUE(base::ReadFileToString(test_file_path, &file_data))
|
| + << "Couldn't read file: " << test_file_path.value();
|
| +
|
| + std::string public_key;
|
| + std::string algorithm;
|
| + std::string signed_data;
|
| + std::string signature_value;
|
| +
|
| + ASSERT_TRUE(ParseTestDataFile(file_data, &public_key, &algorithm,
|
| + &signed_data, &signature_value));
|
| +
|
| + scoped_ptr<SignatureAlgorithm> signature_algorithm =
|
| + SignatureAlgorithm::CreateFromDer(InputFromString(&algorithm));
|
| + ASSERT_TRUE(signature_algorithm);
|
| +
|
| + bool expected_result_bool = expected_result == SUCCESS;
|
| +
|
| + EXPECT_EQ(
|
| + expected_result_bool,
|
| + VerifySignedData(*signature_algorithm, InputFromString(&signed_data),
|
| + InputFromString(&signature_value),
|
| + InputFromString(&public_key)));
|
| +}
|
| +
|
| +// Read the descriptions in the test files themselves for details on what is
|
| +// being tested.
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha1) {
|
| + RunTestCase(SUCCESS, "rsa-pkcs1-sha1.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha256) {
|
| + RunTestCase(SUCCESS, "rsa-pkcs1-sha256.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha256_KeyEncodedBer) {
|
| + // TODO(eroman): This should fail! (SPKI should be DER-encoded).
|
| + RunTestCase(SUCCESS, "rsa-pkcs1-sha256-key-encoded-ber.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_Secp384r1_Sha256) {
|
| + RunTestCase(SUCCESS, "ecdsa-secp384r1-sha256.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_prime256v1_Sha512) {
|
| + RunTestCase(SUCCESS, "ecdsa-prime256v1-sha512.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha1) {
|
| + RunTestCase(SUCCESS, "rsa-pss-sha1-salt20.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha256_Mgf1_Sha512_Salt33) {
|
| + RunTestCase(SUCCESS, "rsa-pss-sha256-mgf1-sha512-salt33.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha256) {
|
| + RunTestCase(SUCCESS, "rsa-pss-sha256-salt10.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha1_WrongSalt) {
|
| + RunTestCase(FAILURE, "rsa-pss-sha1-wrong-salt.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_Secp384r1_Sha256_CorruptedData) {
|
| + RunTestCase(FAILURE, "ecdsa-secp384r1-sha256-corrupted-data.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha1_WrongAlgorithm) {
|
| + RunTestCase(FAILURE, "rsa-pkcs1-sha1-wrong-algorithm.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_prime256v1_Sha512_WrongSignatureFormat) {
|
| + RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-wrong-signature-format.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, EcdsaUsingRsaKey) {
|
| + RunTestCase(FAILURE, "ecdsa-using-rsa-key.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaUsingEcKey) {
|
| + RunTestCase(FAILURE, "rsa-using-ec-key.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha1_BadKeyDerNull) {
|
| + RunTestCase(FAILURE, "rsa-pkcs1-sha1-bad-key-der-null.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha1_BadKeyDerLength) {
|
| + RunTestCase(FAILURE, "rsa-pkcs1-sha1-bad-key-der-length.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha256_UsingEcdsaAlgorithm) {
|
| + RunTestCase(FAILURE, "rsa-pkcs1-sha256-using-ecdsa-algorithm.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_Prim256v1_Sha512_UsingRsaAlgorithm) {
|
| + RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-using-rsa-algorithm.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_Prim256v1_Sha512_UsingEcdhKey) {
|
| + RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-using-ecdh-key.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_Prim256v1_Sha512_UsingEcmqvKey) {
|
| + RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-using-ecmqv-key.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha1_KeyParamsAbsent) {
|
| + // TODO(eroman): This should fail! (key algoritm parsing is too permissive)
|
| + RunTestCase(SUCCESS, "rsa-pkcs1-sha1-key-params-absent.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha1_Salt20_UsingPssKeyNoParams) {
|
| + // TODO(eroman): This should pass! (rsaPss not currently supported in key
|
| + // algorithm).
|
| + RunTestCase(FAILURE, "rsa-pss-sha1-salt20-using-pss-key-no-params.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha1_UsingPssKeyNoParams) {
|
| + RunTestCase(FAILURE, "rsa-pkcs1-sha1-using-pss-key-no-params.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha256_Salt10_UsingPssKeyWithParams) {
|
| + // TODO(eroman): This should pass! (rsaPss not currently supported in key
|
| + // algorithm).
|
| + RunTestCase(FAILURE, "rsa-pss-sha256-salt10-using-pss-key-with-params.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha256_Salt10_UsingPssKeyWithWrongParams) {
|
| + RunTestCase(FAILURE,
|
| + "rsa-pss-sha256-salt10-using-pss-key-with-wrong-params.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPss_Sha256_Salt12_UsingPssKeyWithNullParams) {
|
| + RunTestCase(FAILURE,
|
| + "rsa-pss-sha1-salt20-using-pss-key-with-null-params.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_prime256v1_Sha512_SpkiParamsNull) {
|
| + RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-spki-params-null.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha256_UsingIdEaRsa) {
|
| + // TODO(eroman): This should fail! (shouldn't recognize this weird OID).
|
| + RunTestCase(SUCCESS, "rsa-pkcs1-sha256-using-id-ea-rsa.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, RsaPkcs1_Sha256_SpkiNonNullParams) {
|
| + // TODO(eroman): This should fail! (shouldn't recognize bogus params in rsa
|
| + // SPKI).
|
| + RunTestCase(SUCCESS, "rsa-pkcs1-sha256-spki-non-null-params.pem");
|
| +}
|
| +
|
| +TEST(VerifySignedDataTest, Ecdsa_prime256v1_Sha512_SignatureNotBitString) {
|
| + RunTestCase(FAILURE, "ecdsa-prime256v1-sha512-signature-not-bitstring.pem");
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +} // namespace net
|
|
|