blob: 1627992376f0501d31695c4ceb8df9296502327b [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 "base/test/gmock_expected_support.h"
#include "base/types/expected.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.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_algorithm.h"
namespace webcrypto {
namespace {
// Creates an AES-GCM algorithm.
blink::WebCryptoAlgorithm CreateAesGcmAlgorithm(
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& additional_data,
unsigned int tag_length_bits) {
return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
blink::kWebCryptoAlgorithmIdAesGcm,
new blink::WebCryptoAesGcmParams(iv, true, additional_data, true,
tag_length_bits));
}
blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm(
uint16_t key_length_bits) {
return CreateAesKeyGenAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm,
key_length_bits);
}
base::expected<std::vector<uint8_t>, Status> AesGcmEncrypt(
const blink::WebCryptoKey& key,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& additional_data,
size_t tag_length_bits,
const std::vector<uint8_t>& plain_text) {
blink::WebCryptoAlgorithm algorithm =
CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
std::vector<uint8_t> output;
Status status = Encrypt(algorithm, key, plain_text, &output);
if (status.IsError()) {
return base::unexpected(status);
}
return output;
}
base::expected<std::vector<uint8_t>, Status> AesGcmDecrypt(
const blink::WebCryptoKey& key,
const std::vector<uint8_t>& iv,
const std::vector<uint8_t>& additional_data,
unsigned int tag_length_bits,
const std::vector<uint8_t>& ciphertext) {
blink::WebCryptoAlgorithm algorithm =
CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
std::vector<uint8_t> output;
Status status = Decrypt(algorithm, key, ciphertext, &output);
if (status.IsError()) {
return base::unexpected(status);
}
return output;
}
class WebCryptoAesGcmTest : public WebCryptoTestBase {};
TEST_F(WebCryptoAesGcmTest, GenerateKeyBadLength) {
auto generate_key = [](size_t len) {
blink::WebCryptoKey key;
return GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(len), true,
blink::kWebCryptoKeyUsageDecrypt, &key);
};
EXPECT_EQ(generate_key(0), Status::ErrorGenerateAesKeyLength());
EXPECT_EQ(generate_key(127), Status::ErrorGenerateAesKeyLength());
EXPECT_EQ(generate_key(257), Status::ErrorGenerateAesKeyLength());
}
TEST_F(WebCryptoAesGcmTest, GenerateKeyEmptyUsage) {
blink::WebCryptoKey key;
EXPECT_EQ(Status::ErrorCreateKeyEmptyUsages(),
GenerateSecretKey(CreateAesGcmKeyGenAlgorithm(256), true, 0, &key));
}
TEST_F(WebCryptoAesGcmTest, ImportExportJwk) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm);
// AES-GCM 128
ImportExportJwkSymmetricKey(
128, algorithm,
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
"A128GCM");
// AES-GCM 256
ImportExportJwkSymmetricKey(256, algorithm, blink::kWebCryptoKeyUsageDecrypt,
"A256GCM");
}
struct AesGcmKnownAnswer {
const char* key;
const char* iv;
const char* plaintext;
const char* additional;
const char* ciphertext;
size_t tagbits;
};
// NIST GCM test vectors:
// http://csrc.nist.gov/groups/STM/cavp/documents/mac/gcmtestvectors.zip
const AesGcmKnownAnswer kAesGcmKnownAnswers[] = {
{"cf063a34d4a9a76c2c86787d3f96db71", "113b9785971864c83b01c787", "", "",
"72ac8493e3a5228b5d130a69d2510e42", 128},
{"6dfa1a07c14f978020ace450ad663d18", "34edfa462a14c6969a680ec1", "",
"2a35c7f5f8578e919a581c60500c04f6", "751f3098d59cf4ea1d2fb0853bde1c", 120},
{"ed6cd876ceba555706674445c229c12d", "92ecbf74b765bc486383ca2e",
"bfaaaea3880d72d4378561e2597a9b35", "95bd10d77dbe0e87fb34217f1a2e5efe",
"bdd2ed6c66fa087dce617d7fd1ff6d93ba82e49c55a22ed02ca67da4ec6f", 112},
{"e03548984a7ec8eaf0870637df0ac6bc17f7159315d0ae26a764fd224e483810",
"f4feb26b846be4cd224dbc5133a5ae13814ebe19d3032acdd3a006463fdb71e83a9d5d966"
"79f26cc1719dd6b4feb3bab5b4b7993d0c0681f36d105ad3002fb66b201538e2b7479838a"
"b83402b0d816cd6e0fe5857e6f4adf92de8ee72b122ba1ac81795024943b7d0151bbf84ce"
"87c8911f512c397d14112296da7ecdd0da52a",
"69fd0c9da10b56ec6786333f8d76d4b74f8a434195f2f241f088b2520fb5fa29455df9893"
"164fb1638abe6617915d9497a8fe2",
"aab26eb3e7acd09a034a9e2651636ab3868e51281590ecc948355e457da42b7ad1391c7be"
"0d9e82895e506173a81857c3226829fbd6dfb3f9657a71a2934445d7c05fa9401cddd5109"
"016ba32c3856afaadc48de80b8a01b57cb",
"fda718aa1ec163487e21afc34f5a3a34795a9ee71dd3e7ee9a18fdb24181dc982b29c6ec7"
"23294a130ca2234952bb0ef68c0f34795fbe0",
32},
};
// TODO(eroman):
// * Test decryption when the tag length exceeds input size
// * Test decryption with empty input
// * Test decryption with tag length of 0.
TEST_F(WebCryptoAesGcmTest, KnownAnswers) {
// Note that WebCrypto appends the authentication tag to the ciphertext.
for (const auto& test : kAesGcmKnownAnswers) {
SCOPED_TRACE(&test - &kAesGcmKnownAnswers[0]);
auto key_bytes = HexStringToBytes(test.key);
auto iv_bytes = HexStringToBytes(test.iv);
auto plaintext_bytes = HexStringToBytes(test.plaintext);
auto additional_bytes = HexStringToBytes(test.additional);
auto ciphertext_bytes = HexStringToBytes(test.ciphertext);
blink::WebCryptoKey key = ImportSecretKeyFromRaw(
key_bytes, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesGcm),
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt);
{
std::vector<uint8_t> exported_key;
EXPECT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatRaw, key, &exported_key));
EXPECT_BYTES_EQ(key_bytes, exported_key);
}
ASSERT_OK_AND_ASSIGN(auto encrypt_result,
AesGcmEncrypt(key, iv_bytes, additional_bytes,
test.tagbits, plaintext_bytes));
EXPECT_BYTES_EQ(encrypt_result, ciphertext_bytes);
ASSERT_OK_AND_ASSIGN(auto decrypt_result,
AesGcmDecrypt(key, iv_bytes, additional_bytes,
test.tagbits, ciphertext_bytes));
EXPECT_BYTES_EQ(decrypt_result, plaintext_bytes);
// Decryption should fail if any of the inputs are tampered with.
EXPECT_THAT(AesGcmDecrypt(key, Corrupted(iv_bytes), additional_bytes,
test.tagbits, ciphertext_bytes),
base::test::ErrorIs(Status::OperationError()));
EXPECT_THAT(AesGcmDecrypt(key, iv_bytes, Corrupted(additional_bytes),
test.tagbits, ciphertext_bytes),
base::test::ErrorIs(Status::OperationError()));
EXPECT_THAT(AesGcmDecrypt(key, iv_bytes, additional_bytes, test.tagbits,
Corrupted(ciphertext_bytes)),
base::test::ErrorIs(Status::OperationError()));
// Try different incorrect tag lengths
for (unsigned int length : {0, 8, 96, 120, 128, 160, 255}) {
if (test.tagbits == length)
continue;
EXPECT_FALSE(AesGcmDecrypt(key, iv_bytes, additional_bytes, length,
ciphertext_bytes)
.has_value());
}
}
}
} // namespace
} // namespace webcrypto