blob: 30a615743de4bf6b5c3678b5e911d9c3a2186bfd [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stddef.h>
#include <stdint.h>
#include <array>
#include "base/strings/string_number_conversions.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
#include "components/webcrypto/jwk.h"
#include "components/webcrypto/status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/web_crypto_algorithm_params.h"
#include "third_party/blink/public/platform/web_crypto_key.h"
#include "third_party/blink/public/platform/web_crypto_key_algorithm.h"
namespace webcrypto {
namespace {
blink::WebCryptoAlgorithm CreateRsaPssAlgorithm(
unsigned int salt_length_bytes) {
return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
blink::kWebCryptoAlgorithmIdRsaPss,
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::kWebCryptoAlgorithmIdRsaPss,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageVerify, blink::kWebCryptoKeyUsageSign,
&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, message, &signature1));
ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature2));
// The signatures will be different because of the salt.
EXPECT_NE(signature1, signature2);
// However both signatures should work when verifying.
bool is_match = false;
ASSERT_EQ(Status::Success(),
Verify(params, public_key, signature1, message, &is_match));
EXPECT_TRUE(is_match);
ASSERT_EQ(Status::Success(),
Verify(params, public_key, signature2, message, &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
ASSERT_EQ(Status::Success(), Verify(params, public_key, Corrupted(signature2),
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::kWebCryptoAlgorithmIdRsaPss,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageVerify, blink::kWebCryptoKeyUsageSign,
&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, message, &signature1));
ASSERT_EQ(Status::Success(), Sign(params, private_key, message, &signature2));
// The signatures will be the same this time.
EXPECT_EQ(signature1, signature2);
// Make sure that verification works.
bool is_match = false;
ASSERT_EQ(Status::Success(),
Verify(params, public_key, signature1, message, &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
ASSERT_EQ(Status::Success(), Verify(params, public_key, Corrupted(signature2),
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::kWebCryptoAlgorithmIdRsaPss,
blink::kWebCryptoAlgorithmIdSha1),
true, blink::kWebCryptoKeyUsageVerify, blink::kWebCryptoKeyUsageSign,
&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, message, &signature));
// Make sure that verification works.
bool is_match = false;
ASSERT_EQ(Status::Success(),
Verify(params, public_key, signature, message, &is_match));
EXPECT_TRUE(is_match);
// Corrupt the signature and verification must fail.
ASSERT_EQ(Status::Success(), Verify(params, public_key, Corrupted(signature),
message, &is_match));
EXPECT_FALSE(is_match);
}
// RSA-PSS known answer tests:
const char kKey1[] =
"30819F300D06092A864886F70D010101050003818D0030818902818100A56E4A0E70101758"
"9A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056FFEDB162B4C0F283A1"
"2A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B8B6DF5D671EF6377C092"
"1CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E1386B6F64C4EF22E1E1F20D0C"
"E8CFFB2249BD9A21370203010001";
const char kKey2[] =
"30819D300D06092A864886F70D010101050003818B0030818702818100BE499B5E7F06C83F"
"A0293E31465C8EB6B58AF920BAE52A7B5B9BFEB7AA72DB1264112EB3FD431D31A2A7E50941"
"566929494A0E891ED5613918B4B51B0D1FB97783B26ACF7D0F384CFB35F4D2824F5DD38062"
"3A26BF180B63961C619DCDB20CAE406F22F6E276C80A37259490CFEB72C1A71A84F1846D33"
"0877BA3E3101EC9C7B020111";
const char kNistTestMessage[] =
"c7f5270fca725f9bd19f519a8d7cca3cc5c079024029f3bae510f9b02140fe238908e4f6c1"
"8f07a89c687c8684669b1f1db2baf9251a3c829faccb493084e16ec9e28d58868074a5d622"
"1667dd6e528d16fe2c9f3db4cfaf6c4dce8c8439af38ceaaaa9ce2ecae7bc8f4a5a55e3bf9"
"6df9cd575c4f9cb327951b8cdfe4087168";
struct RsaPssKnownAnswer {
blink::WebCryptoAlgorithmId hash;
const char* key;
const char* message;
size_t salt_length;
const char* signature;
};
constexpr auto kRsaPssKnownAnswers = std::to_array<RsaPssKnownAnswer>({
// Example 1.1 from pss-vect.txt in
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
{blink::kWebCryptoAlgorithmIdSha1, kKey1,
"cdc87da223d786df3b45e0bbbc721326d1ee2af806cc315475cc6f0d9c66e1b62371d4"
"5ce2392e1ac92844c310102f156a0d8d52c1f4c40ba3aa65095786cb769757a6563ba9"
"58fed0bcc984e8b517a3d5f515b23b8a41e74aa867693f90dfb061a6e86dfaaee64472"
"c00e5f20945729cbebe77f06ce78e08f4098fba41f9d6193c0317e8b60d4b6084acb42"
"d29e3808a3bc372d85e331170fcbf7cc72d0b71c296648b3a4d10f416295d0807aa625"
"cab2744fd9ea8fd223c42537029828bd16be02546f130fd2e33b936d2676e08aed1b73"
"318b750a0167d0",
20,
"9074308fb598e9701b2294388e52f971faac2b60a5145af185df5287b5ed2887e57ce7"
"fd44dc8634e407c8e0e4360bc226f3ec227f9d9e54638e8d31f5051215df6ebb9c2f95"
"79aa77598a38f914b5b9c1bd83c4e2f9f382a0d0aa3542ffee65984a601bc69eb28deb"
"27dca12c82c2d4c3f66cd500f1ff2b994d8a4e30cbb33c"},
// Example 1.4 from pss-vect.txt in
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1-vec.zip
{blink::kWebCryptoAlgorithmIdSha1, kKey1, "bc656747fa9eafb3f0", 20,
"4609793b23e9d09362dc21bb47da0b4f3a7622649a47d464019b9aeafe53359c178c91"
"cd58ba6bcb78be0346a7bc637f4b873d4bab38ee661f199634c547a1ad8442e03da015"
"b136e543f7ab07c0c13e4225b8de8cce25d4f6eb8400f81f7e1833b7ee6e334d370964"
"ca79fdb872b4d75223b5eeb08101591fb532d155a6de87"},
// The next three are from SigVerPSS_186-3.rsp in
// http://csrc.nist.gov/groups/STM/cavp/documents/dss/186-2rsatestvectors.zip
{blink::kWebCryptoAlgorithmIdSha256, kKey2, kNistTestMessage, 10,
"11e169f2fd40b07641b9768a2ab19965fb6c27f10fcf0323fcc6d12eb4f1c06b330dda"
"a1ea504407afa29de9ebe0374fe9d1e7d0ffbd5fc1cf3a3446e4145415d2ab24f789b3"
"464c5c43a256bbc1d692cf7f04801dac5bb401a4a03ab7d5728a860c19e1a4dc797ca5"
"42c8203cec2e601eb0c51f567f2eda022b0b9ebddeeefa"},
{blink::kWebCryptoAlgorithmIdSha384, kKey2, kNistTestMessage, 10,
"b281ad934b2775c0cba5fb10aa574d2ed85c7f99b942b78e49702480069362ed394bad"
"ed55e56cfcbe7b0b8d2217a05a60e1acd725cb09060dfac585bc2132b99b41cdbd530c"
"69d17cdbc84bc6b9830fc7dc8e1b2412cfe06dcf8c1a0cc3453f93f25ebf10cb0c9033"
"4fac573f449138616e1a194c67f44efac34cc07a526267"},
{blink::kWebCryptoAlgorithmIdSha512, kKey2, kNistTestMessage, 10,
"8ffc38f9b820ef6b080fd2ec7de5626c658d79056f3edf610a295b7b0546f73e01ffdf4d0"
"070ebf79c33fd86c2d608be9438b3d420d09535b97cd3d846ecaf8f6551cdf93197e9f8fb"
"048044473ab41a801e9f7fc983c62b324361dade9f71a65952bd35c59faaa4d6ff462f68a"
"6c4ec0b428aa47336f2178aeb276136563b7d"},
});
blink::WebCryptoKey RsaPssKeyFromBytes(blink::WebCryptoAlgorithmId hash,
const char* key) {
auto pubkey = blink::WebCryptoKey::CreateNull();
webcrypto::Status result = ImportKey(
blink::kWebCryptoKeyFormatSpki, HexStringToBytes(key),
CreateRsaHashedImportAlgorithm(blink::kWebCryptoAlgorithmIdRsaPss, hash),
true, blink::kWebCryptoKeyUsageVerify, &pubkey);
CHECK(result == Status::Success());
return pubkey;
}
bool VerifySignature(size_t salt_length,
blink::WebCryptoKey pubkey,
const std::vector<uint8_t>& signature,
const std::vector<uint8_t>& message) {
bool is_match = false;
auto result = Verify(CreateRsaPssAlgorithm(salt_length), pubkey, signature,
message, &is_match);
CHECK(result == Status::Success());
return 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) {
for (const auto& test : kRsaPssKnownAnswers) {
SCOPED_TRACE(&test - &kRsaPssKnownAnswers[0]);
blink::WebCryptoKey pubkey = RsaPssKeyFromBytes(test.hash, test.key);
std::vector<uint8_t> message = HexStringToBytes(test.message);
std::vector<uint8_t> signature = HexStringToBytes(test.signature);
EXPECT_TRUE(VerifySignature(test.salt_length, pubkey, signature, message));
EXPECT_FALSE(VerifySignature(test.salt_length, pubkey, signature,
Corrupted(message)));
EXPECT_FALSE(VerifySignature(test.salt_length, pubkey, Corrupted(signature),
message));
}
}
} // namespace
} // namespace webcrypto