blob: 00ef290503e60d273d837b64374ab2c531e6507f [file] [log] [blame]
// Copyright 2014 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 "base/logging.h"
#include "base/stl_util.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "components/webcrypto/webcrypto_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKey.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
namespace webcrypto {
namespace {
blink::WebCryptoAlgorithm CreateRsaPssAlgorithm(
unsigned int salt_length_bytes) {
return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
blink::WebCryptoAlgorithmIdRsaPss,
new blink::WebCryptoRsaPssParams(salt_length_bytes));
}
class WebCryptoRsaPssTest : public WebCryptoTestBase {};
// Test that no two RSA-PSS signatures are identical, when using a non-zero
// lengthed salt.
TEST_F(WebCryptoRsaPssTest, SignIsRandom) {
// Import public/private key pair.
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ImportRsaKeyPair(
HexStringToBytes(kPublicKeySpkiDerHex),
HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaPss,
blink::WebCryptoAlgorithmIdSha1),
true, blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign,
&public_key, &private_key);
// Use a 20-byte length salt.
blink::WebCryptoAlgorithm params = CreateRsaPssAlgorithm(20);
// Some random message to sign.
std::vector<uint8_t> message = HexStringToBytes(kPublicKeySpkiDerHex);
// Sign twice.
std::vector<uint8_t> signature1;
std::vector<uint8_t> signature2;
ASSERT_EQ(Status::Success(),
Sign(params, private_key, CryptoData(message), &signature1));
ASSERT_EQ(Status::Success(),
Sign(params, private_key, CryptoData(message), &signature2));
// The signatures will be different because of the salt.
EXPECT_NE(CryptoData(signature1), CryptoData(signature2));
// However both signatures should work when verifying.
bool is_match = false;
ASSERT_EQ(Status::Success(),
Verify(params, public_key, CryptoData(signature1),
CryptoData(message), &is_match));
EXPECT_TRUE(is_match);
ASSERT_EQ(Status::Success(),
Verify(params, public_key, CryptoData(signature2),
CryptoData(message), &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
ASSERT_EQ(Status::Success(),
Verify(params, public_key, CryptoData(Corrupted(signature2)),
CryptoData(message), &is_match));
EXPECT_FALSE(is_match);
}
// Try signing and verifying when the salt length is 0. The signature in this
// case is not random.
TEST_F(WebCryptoRsaPssTest, SignVerifyNoSalt) {
// Import public/private key pair.
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ImportRsaKeyPair(
HexStringToBytes(kPublicKeySpkiDerHex),
HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaPss,
blink::WebCryptoAlgorithmIdSha1),
true, blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign,
&public_key, &private_key);
// Zero-length salt.
blink::WebCryptoAlgorithm params = CreateRsaPssAlgorithm(0);
// Some random message to sign.
std::vector<uint8_t> message = HexStringToBytes(kPublicKeySpkiDerHex);
// Sign twice.
std::vector<uint8_t> signature1;
std::vector<uint8_t> signature2;
ASSERT_EQ(Status::Success(),
Sign(params, private_key, CryptoData(message), &signature1));
ASSERT_EQ(Status::Success(),
Sign(params, private_key, CryptoData(message), &signature2));
// The signatures will be the same this time.
EXPECT_EQ(CryptoData(signature1), CryptoData(signature2));
// Make sure that verification works.
bool is_match = false;
ASSERT_EQ(Status::Success(),
Verify(params, public_key, CryptoData(signature1),
CryptoData(message), &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
ASSERT_EQ(Status::Success(),
Verify(params, public_key, CryptoData(Corrupted(signature2)),
CryptoData(message), &is_match));
EXPECT_FALSE(is_match);
}
TEST_F(WebCryptoRsaPssTest, SignEmptyMessage) {
// Import public/private key pair.
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
blink::WebCryptoKey private_key = blink::WebCryptoKey::createNull();
ImportRsaKeyPair(
HexStringToBytes(kPublicKeySpkiDerHex),
HexStringToBytes(kPrivateKeyPkcs8DerHex),
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaPss,
blink::WebCryptoAlgorithmIdSha1),
true, blink::WebCryptoKeyUsageVerify, blink::WebCryptoKeyUsageSign,
&public_key, &private_key);
blink::WebCryptoAlgorithm params = CreateRsaPssAlgorithm(20);
std::vector<uint8_t> message; // Empty message.
std::vector<uint8_t> signature;
ASSERT_EQ(Status::Success(),
Sign(params, private_key, CryptoData(message), &signature));
// Make sure that verification works.
bool is_match = false;
ASSERT_EQ(Status::Success(), Verify(params, public_key, CryptoData(signature),
CryptoData(message), &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
ASSERT_EQ(Status::Success(),
Verify(params, public_key, CryptoData(Corrupted(signature)),
CryptoData(message), &is_match));
EXPECT_FALSE(is_match);
}
// Iterate through known answers and test verification.
// * Verify over original message should succeed
// * Verify over corrupted message should fail
// * Verification with corrupted signature should fail
TEST_F(WebCryptoRsaPssTest, VerifyKnownAnswer) {
scoped_ptr<base::DictionaryValue> test_data;
ASSERT_TRUE(ReadJsonTestFileToDictionary("rsa_pss.json", &test_data));
const base::DictionaryValue* keys_dict = NULL;
ASSERT_TRUE(test_data->GetDictionary("keys", &keys_dict));
const base::ListValue* tests = NULL;
ASSERT_TRUE(test_data->GetList("tests", &tests));
for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
SCOPED_TRACE(test_index);
const base::DictionaryValue* test;
ASSERT_TRUE(tests->GetDictionary(test_index, &test));
blink::WebCryptoAlgorithm hash = GetDigestAlgorithm(test, "hash");
std::string key_name;
ASSERT_TRUE(test->GetString("key", &key_name));
// Import the public key.
blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull();
std::vector<uint8_t> spki_bytes =
GetBytesFromHexString(keys_dict, key_name);
ASSERT_EQ(Status::Success(),
ImportKey(blink::WebCryptoKeyFormatSpki, CryptoData(spki_bytes),
CreateRsaHashedImportAlgorithm(
blink::WebCryptoAlgorithmIdRsaPss, hash.id()),
true, blink::WebCryptoKeyUsageVerify, &public_key));
int saltLength;
ASSERT_TRUE(test->GetInteger("saltLength", &saltLength));
std::vector<uint8_t> message = GetBytesFromHexString(test, "message");
std::vector<uint8_t> signature = GetBytesFromHexString(test, "signature");
// Test that verification returns true when it should.
bool is_match = false;
ASSERT_EQ(Status::Success(),
Verify(CreateRsaPssAlgorithm(saltLength), public_key,
CryptoData(signature), CryptoData(message), &is_match));
EXPECT_TRUE(is_match);
// Corrupt the message and make sure that verification fails.
ASSERT_EQ(Status::Success(),
Verify(CreateRsaPssAlgorithm(saltLength), public_key,
CryptoData(signature), CryptoData(Corrupted(message)),
&is_match));
EXPECT_FALSE(is_match);
// Corrupt the signature and make sure that verification fails.
ASSERT_EQ(Status::Success(),
Verify(CreateRsaPssAlgorithm(saltLength), public_key,
CryptoData(Corrupted(signature)), CryptoData(message),
&is_match));
EXPECT_FALSE(is_match);
}
}
} // namespace
} // namespace webcrypto