blob: b7f897c6db5a3aa9794e2e094935b309de971060 [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/stl_util.h"
#include "content/child/webcrypto/algorithm_dispatch.h"
#include "content/child/webcrypto/crypto_data.h"
#include "content/child/webcrypto/status.h"
#include "content/child/webcrypto/test/test_helpers.h"
#include "content/child/webcrypto/webcrypto_util.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
namespace content {
namespace webcrypto {
namespace {
// Creates an AES-CBC algorithm.
blink::WebCryptoAlgorithm CreateAesCbcAlgorithm(
const std::vector<uint8_t>& iv) {
return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
blink::WebCryptoAlgorithmIdAesCbc,
new blink::WebCryptoAesCbcParams(vector_as_array(&iv), iv.size()));
}
blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm(
unsigned short key_length_bits) {
return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesCbc,
key_length_bits);
}
blink::WebCryptoKey GetTestAesCbcKey() {
const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c";
blink::WebCryptoKey key = ImportSecretKeyFromRaw(
HexStringToBytes(key_hex),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt);
// Verify exported raw key is identical to the imported data
std::vector<uint8_t> raw_key;
EXPECT_EQ(Status::Success(),
ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key));
EXPECT_BYTES_EQ_HEX(key_hex, raw_key);
return key;
}
TEST(WebCryptoAesCbcTest, IvTooSmall) {
std::vector<uint8_t> output;
// Use an invalid |iv| (fewer than 16 bytes)
std::vector<uint8_t> input(32);
std::vector<uint8_t> iv;
EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(),
Encrypt(CreateAesCbcAlgorithm(iv),
GetTestAesCbcKey(),
CryptoData(input),
&output));
EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(),
Decrypt(CreateAesCbcAlgorithm(iv),
GetTestAesCbcKey(),
CryptoData(input),
&output));
}
TEST(WebCryptoAesCbcTest, IvTooLarge) {
std::vector<uint8_t> output;
// Use an invalid |iv| (more than 16 bytes)
std::vector<uint8_t> input(32);
std::vector<uint8_t> iv(17);
EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(),
Encrypt(CreateAesCbcAlgorithm(iv),
GetTestAesCbcKey(),
CryptoData(input),
&output));
EXPECT_EQ(Status::ErrorIncorrectSizeAesCbcIv(),
Decrypt(CreateAesCbcAlgorithm(iv),
GetTestAesCbcKey(),
CryptoData(input),
&output));
}
TEST(WebCryptoAesCbcTest, InputTooLarge) {
std::vector<uint8_t> output;
// Give an input that is too large (would cause integer overflow when
// narrowing to an int). Note that both OpenSSL and NSS operate on signed int
// lengths.
std::vector<uint8_t> iv(16);
// Pretend the input is large. Don't pass data pointer as NULL in case that
// is special cased; the implementation shouldn't actually dereference the
// data.
CryptoData input(&iv[0], INT_MAX - 3);
EXPECT_EQ(
Status::ErrorDataTooLarge(),
Encrypt(CreateAesCbcAlgorithm(iv), GetTestAesCbcKey(), input, &output));
EXPECT_EQ(
Status::ErrorDataTooLarge(),
Decrypt(CreateAesCbcAlgorithm(iv), GetTestAesCbcKey(), input, &output));
}
TEST(WebCryptoAesCbcTest, KeyTooSmall) {
std::vector<uint8_t> output;
// Fail importing the key (too few bytes specified)
std::vector<uint8_t> key_raw(1);
std::vector<uint8_t> iv(16);
blink::WebCryptoKey key;
EXPECT_EQ(Status::ErrorImportAesKeyLength(),
ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(key_raw),
CreateAesCbcAlgorithm(iv),
true,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
TEST(WebCryptoAesCbcTest, ExportKeyUnsupportedFormat) {
std::vector<uint8_t> output;
// Fail exporting the key in SPKI and PKCS#8 formats (not allowed for secret
// keys).
EXPECT_EQ(
Status::ErrorUnsupportedExportKeyFormat(),
ExportKey(blink::WebCryptoKeyFormatSpki, GetTestAesCbcKey(), &output));
EXPECT_EQ(
Status::ErrorUnsupportedExportKeyFormat(),
ExportKey(blink::WebCryptoKeyFormatPkcs8, GetTestAesCbcKey(), &output));
}
TEST(WebCryptoAesCbcTest, ImportKeyUnsupportedFormat) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorUnsupportedImportKeyFormat(),
ImportKey(blink::WebCryptoKeyFormatSpki,
CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
true,
blink::WebCryptoKeyUsageEncrypt,
&key));
ASSERT_EQ(Status::ErrorUnsupportedImportKeyFormat(),
ImportKey(blink::WebCryptoKeyFormatPkcs8,
CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
true,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
TEST(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
scoped_ptr<base::ListValue> tests;
ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests));
for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
SCOPED_TRACE(test_index);
base::DictionaryValue* test;
ASSERT_TRUE(tests->GetDictionary(test_index, &test));
std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key");
std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv");
std::vector<uint8_t> test_plain_text =
GetBytesFromHexString(test, "plain_text");
std::vector<uint8_t> test_cipher_text =
GetBytesFromHexString(test, "cipher_text");
blink::WebCryptoKey key = ImportSecretKeyFromRaw(
test_key,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt);
EXPECT_EQ(test_key.size() * 8, key.algorithm().aesParams()->lengthBits());
// Verify exported raw key is identical to the imported data
std::vector<uint8_t> raw_key;
EXPECT_EQ(Status::Success(),
ExportKey(blink::WebCryptoKeyFormatRaw, key, &raw_key));
EXPECT_BYTES_EQ(test_key, raw_key);
std::vector<uint8_t> output;
// Test encryption.
EXPECT_EQ(Status::Success(),
Encrypt(CreateAesCbcAlgorithm(test_iv),
key,
CryptoData(test_plain_text),
&output));
EXPECT_BYTES_EQ(test_cipher_text, output);
// Test decryption.
EXPECT_EQ(Status::Success(),
Decrypt(CreateAesCbcAlgorithm(test_iv),
key,
CryptoData(test_cipher_text),
&output));
EXPECT_BYTES_EQ(test_plain_text, output);
}
}
TEST(WebCryptoAesCbcTest, DecryptTruncatedCipherText) {
scoped_ptr<base::ListValue> tests;
ASSERT_TRUE(ReadJsonTestFileToList("aes_cbc.json", &tests));
for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
SCOPED_TRACE(test_index);
base::DictionaryValue* test;
ASSERT_TRUE(tests->GetDictionary(test_index, &test));
std::vector<uint8_t> test_key = GetBytesFromHexString(test, "key");
std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv");
std::vector<uint8_t> test_cipher_text =
GetBytesFromHexString(test, "cipher_text");
blink::WebCryptoKey key = ImportSecretKeyFromRaw(
test_key,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt);
std::vector<uint8_t> output;
const unsigned int kAesCbcBlockSize = 16;
// Decrypt with a padding error by stripping the last block. This also ends
// up testing decryption over empty cipher text.
if (test_cipher_text.size() >= kAesCbcBlockSize) {
EXPECT_EQ(Status::OperationError(),
Decrypt(CreateAesCbcAlgorithm(test_iv),
key,
CryptoData(&test_cipher_text[0],
test_cipher_text.size() - kAesCbcBlockSize),
&output));
}
// Decrypt cipher text which is not a multiple of block size by stripping
// a few bytes off the cipher text.
if (test_cipher_text.size() > 3) {
EXPECT_EQ(
Status::OperationError(),
Decrypt(CreateAesCbcAlgorithm(test_iv),
key,
CryptoData(&test_cipher_text[0], test_cipher_text.size() - 3),
&output));
}
}
}
// TODO(eroman): Do this same test for AES-GCM, AES-KW, AES-CTR ?
TEST(WebCryptoAesCbcTest, GenerateKeyIsRandom) {
// Check key generation for each allowed key length.
std::vector<blink::WebCryptoAlgorithm> algorithm;
const unsigned short kKeyLength[] = {128, 256};
for (size_t key_length_i = 0; key_length_i < arraysize(kKeyLength);
++key_length_i) {
blink::WebCryptoKey key;
std::vector<std::vector<uint8_t> > keys;
std::vector<uint8_t> key_bytes;
// Generate a small sample of keys.
for (int j = 0; j < 16; ++j) {
ASSERT_EQ(Status::Success(),
GenerateSecretKey(
CreateAesCbcKeyGenAlgorithm(kKeyLength[key_length_i]),
true,
0,
&key));
EXPECT_TRUE(key.handle());
EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type());
ASSERT_EQ(Status::Success(),
ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_bytes));
EXPECT_EQ(key_bytes.size() * 8,
key.algorithm().aesParams()->lengthBits());
keys.push_back(key_bytes);
}
// Ensure all entries in the key sample set are unique. This is a simplistic
// estimate of whether the generated keys appear random.
EXPECT_FALSE(CopiesExist(keys));
}
}
TEST(WebCryptoAesCbcTest, GenerateKeyBadLength) {
const unsigned short kKeyLen[] = {0, 127, 257};
blink::WebCryptoKey key;
for (size_t i = 0; i < arraysize(kKeyLen); ++i) {
SCOPED_TRACE(i);
EXPECT_EQ(Status::ErrorGenerateKeyLength(),
GenerateSecretKey(
CreateAesCbcKeyGenAlgorithm(kKeyLen[i]), true, 0, &key));
}
}
// If key_ops is specified but empty, no key usages are allowed for the key.
TEST(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetBoolean("ext", false);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
dict.Set("key_ops", new base::ListValue); // Takes ownership.
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
0,
&key));
EXPECT_EQ(0, key.usages());
// The JWK does not contain encrypt usages.
EXPECT_EQ(
Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
// The JWK does not contain sign usage (nor is it applicable).
EXPECT_EQ(
Status::ErrorCreateKeyBadUsages(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageSign,
&key));
}
// If key_ops is missing, then any key usages can be specified.
TEST(WebCryptoAesCbcTest, ImportKeyJwkNoKeyOps) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages());
// The JWK does not contain sign usage (nor is it applicable).
EXPECT_EQ(
Status::ErrorCreateKeyBadUsages(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageVerify,
&key));
}
TEST(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
base::ListValue* key_ops = new base::ListValue;
dict.Set("key_ops", key_ops); // Takes ownership.
key_ops->AppendString("encrypt");
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt, key.usages());
key_ops->AppendString("decrypt");
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageDecrypt,
&key));
EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt, key.usages());
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt,
&key));
EXPECT_EQ(blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
key.usages());
}
// Test failure if input usage is NOT a strict subset of the JWK usage.
TEST(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
base::ListValue* key_ops = new base::ListValue;
dict.Set("key_ops", key_ops); // Takes ownership.
key_ops->AppendString("encrypt");
EXPECT_EQ(
Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(
dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
&key));
}
TEST(WebCryptoAesCbcTest, ImportKeyJwkUseEnc) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
// Test JWK composite use 'enc' usage
dict.SetString("alg", "A128CBC");
dict.SetString("use", "enc");
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageDecrypt |
blink::WebCryptoKeyUsageEncrypt |
blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey,
&key));
EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt |
blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey,
key.usages());
}
TEST(WebCryptoAesCbcTest, ImportJwkInvalidJson) {
blink::WebCryptoKey key;
// Fail on empty JSON.
EXPECT_EQ(Status::ErrorImportEmptyKeyData(),
ImportKey(blink::WebCryptoKeyFormatJwk,
CryptoData(MakeJsonVector("")),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
// Fail on invalid JSON.
const std::vector<uint8_t> bad_json_vec = MakeJsonVector(
"{"
"\"kty\" : \"oct\","
"\"alg\" : \"HS256\","
"\"use\" : ");
EXPECT_EQ(Status::ErrorJwkNotDictionary(),
ImportKey(blink::WebCryptoKeyFormatJwk,
CryptoData(bad_json_vec),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on JWK alg present but incorrect (expecting A128CBC).
TEST(WebCryptoAesCbcTest, ImportJwkIncorrectAlg) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A127CBC"); // Not valid.
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkAlgorithmInconsistent(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on invalid kty.
TEST(WebCryptoAesCbcTest, ImportJwkInvalidKty) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "foo");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkUnexpectedKty("oct"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on missing kty.
TEST(WebCryptoAesCbcTest, ImportJwkMissingKty) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkPropertyMissing("kty"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on kty wrong type.
TEST(WebCryptoAesCbcTest, ImportJwkKtyWrongType) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetDouble("kty", 0.1);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkPropertyWrongType("kty", "string"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on invalid use.
TEST(WebCryptoAesCbcTest, ImportJwkUnrecognizedUse) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("use", "foo");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkUnrecognizedUse(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on invalid use (wrong type).
TEST(WebCryptoAesCbcTest, ImportJwkUseWrongType) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetBoolean("use", true);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkPropertyWrongType("use", "string"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on invalid extractable (wrong type).
TEST(WebCryptoAesCbcTest, ImportJwkExtWrongType) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetInteger("ext", 0);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
EXPECT_EQ(
Status::ErrorJwkPropertyWrongType("ext", "boolean"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on invalid key_ops (wrong type).
TEST(WebCryptoAesCbcTest, ImportJwkKeyOpsWrongType) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
dict.SetBoolean("key_ops", true);
EXPECT_EQ(
Status::ErrorJwkPropertyWrongType("key_ops", "list"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on inconsistent key_ops - asking for "encrypt" however JWK contains
// only "foo".
TEST(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
base::ListValue* key_ops = new base::ListValue;
// Note: the following call makes dict assume ownership of key_ops.
dict.Set("key_ops", key_ops);
key_ops->AppendString("foo");
EXPECT_EQ(
Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Import a JWK with unrecognized values for "key_ops".
TEST(WebCryptoAesCbcTest, ImportJwkUnrecognizedKeyOps) {
blink::WebCryptoKey key;
blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageEncrypt;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A128CBC");
dict.SetString("use", "enc");
dict.SetBoolean("ext", false);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
base::ListValue* key_ops = new base::ListValue;
dict.Set("key_ops", key_ops);
key_ops->AppendString("foo");
key_ops->AppendString("bar");
key_ops->AppendString("baz");
key_ops->AppendString("encrypt");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(dict, algorithm, false, usages, &key));
}
// Import a JWK with a value in key_ops array that is not a string.
TEST(WebCryptoAesCbcTest, ImportJwkNonStringKeyOp) {
blink::WebCryptoKey key;
blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
blink::WebCryptoKeyUsageMask usages = blink::WebCryptoKeyUsageEncrypt;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A128CBC");
dict.SetString("use", "enc");
dict.SetBoolean("ext", false);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
base::ListValue* key_ops = new base::ListValue;
dict.Set("key_ops", key_ops);
key_ops->AppendString("encrypt");
key_ops->AppendInteger(3);
EXPECT_EQ(Status::ErrorJwkPropertyWrongType("key_ops[1]", "string"),
ImportKeyJwkFromDict(dict, algorithm, false, usages, &key));
}
// Fail on missing k.
TEST(WebCryptoAesCbcTest, ImportJwkMissingK) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
EXPECT_EQ(
Status::ErrorJwkPropertyMissing("k"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on bad b64 encoding for k.
TEST(WebCryptoAesCbcTest, ImportJwkBadB64ForK) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "Qk3f0DsytU8lfza2au #$% Htaw2xpop9GYyTuH0p5GghxTI=");
EXPECT_EQ(
Status::ErrorJwkBase64Decode("k"),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on empty k.
TEST(WebCryptoAesCbcTest, ImportJwkEmptyK) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "");
EXPECT_EQ(
Status::ErrorImportAesKeyLength(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on empty k (with alg specified).
TEST(WebCryptoAesCbcTest, ImportJwkEmptyK2) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A128CBC");
dict.SetString("k", "");
EXPECT_EQ(
Status::ErrorJwkIncorrectKeyLength(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on k actual length (120 bits) inconsistent with the embedded JWK alg
// value (128) for an AES key.
TEST(WebCryptoAesCbcTest, ImportJwkInconsistentKLength) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A128CBC");
dict.SetString("k", "AVj42h0Y5aqGtE3yluKL");
EXPECT_EQ(
Status::ErrorJwkIncorrectKeyLength(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// Fail on k actual length (192 bits) inconsistent with the embedded JWK alg
// value (128) for an AES key.
TEST(WebCryptoAesCbcTest, ImportJwkInconsistentKLength2) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A128CBC");
dict.SetString("k", "dGhpcyAgaXMgIDI0ICBieXRlcyBsb25n");
EXPECT_EQ(
Status::ErrorJwkIncorrectKeyLength(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
TEST(WebCryptoAesCbcTest, ImportExportJwk) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
// AES-CBC 128
ImportExportJwkSymmetricKey(
128,
algorithm,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
"A128CBC");
// AES-CBC 256
ImportExportJwkSymmetricKey(
256, algorithm, blink::WebCryptoKeyUsageDecrypt, "A256CBC");
// Large usage value
ImportExportJwkSymmetricKey(
256,
algorithm,
blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
"A256CBC");
}
// AES 192-bit is not allowed: http://crbug.com/381829
TEST(WebCryptoAesCbcTest, ImportAesCbc192Raw) {
std::vector<uint8_t> key_raw(24, 0);
blink::WebCryptoKey key;
Status status = ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(key_raw),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
true,
blink::WebCryptoKeyUsageEncrypt,
&key);
ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status);
}
// AES 192-bit is not allowed: http://crbug.com/381829
TEST(WebCryptoAesCbcTest, ImportAesCbc192Jwk) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("alg", "A192CBC");
dict.SetString("k", "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh");
EXPECT_EQ(
Status::ErrorAes192BitUnsupported(),
ImportKeyJwkFromDict(dict,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
false,
blink::WebCryptoKeyUsageEncrypt,
&key));
}
// AES 192-bit is not allowed: http://crbug.com/381829
TEST(WebCryptoAesCbcTest, GenerateAesCbc192) {
blink::WebCryptoKey key;
Status status = GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(192),
true,
blink::WebCryptoKeyUsageEncrypt,
&key);
ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status);
}
// AES 192-bit is not allowed: http://crbug.com/381829
TEST(WebCryptoAesCbcTest, UnwrapAesCbc192) {
std::vector<uint8_t> wrapping_key_data(16, 0);
std::vector<uint8_t> wrapped_key = HexStringToBytes(
"1A07ACAB6C906E50883173C29441DB1DE91D34F45C435B5F99C822867FB3956F");
blink::WebCryptoKey wrapping_key =
ImportSecretKeyFromRaw(wrapping_key_data,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw),
blink::WebCryptoKeyUsageUnwrapKey);
blink::WebCryptoKey unwrapped_key;
ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
UnwrapKey(blink::WebCryptoKeyFormatRaw,
CryptoData(wrapped_key),
wrapping_key,
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw),
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc),
true,
blink::WebCryptoKeyUsageEncrypt,
&unwrapped_key));
}
// Try importing an AES-CBC key with unsupported key usages using raw
// format. AES-CBC keys support the following usages:
// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'
TEST(WebCryptoAesCbcTest, ImportKeyBadUsage_Raw) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageSign,
blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageDecrypt,
blink::WebCryptoKeyUsageDeriveBits,
blink::WebCryptoKeyUsageUnwrapKey | blink::WebCryptoKeyUsageVerify,
};
std::vector<uint8_t> key_bytes(16);
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKey(blink::WebCryptoKeyFormatRaw,
CryptoData(key_bytes),
algorithm,
true,
bad_usages[i],
&key));
}
}
// Generate an AES-CBC key with invalid usages. AES-CBC supports:
// 'encrypt', 'decrypt', 'wrapKey', 'unwrapKey'
TEST(WebCryptoAesCbcTest, GenerateKeyBadUsages) {
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::WebCryptoKeyUsageSign, blink::WebCryptoKeyUsageVerify,
blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageVerify,
};
for (size_t i = 0; i < arraysize(bad_usages); ++i) {
SCOPED_TRACE(i);
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyBadUsages(),
GenerateSecretKey(
CreateAesCbcKeyGenAlgorithm(128), true, bad_usages[i], &key));
}
}
// Generate an AES-CBC key and an RSA key pair. Use the AES-CBC key to wrap the
// key pair (using SPKI format for public key, PKCS8 format for private key).
// Then unwrap the wrapped key pair and verify that the key data is the same.
TEST(WebCryptoAesCbcTest, WrapUnwrapRoundtripSpkiPkcs8) {
if (!SupportsRsaPrivateKeyImport())
return;
// Generate the wrapping key.
blink::WebCryptoKey wrapping_key;
ASSERT_EQ(Status::Success(),
GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128),
true,
blink::WebCryptoKeyUsageWrapKey |
blink::WebCryptoKeyUsageUnwrapKey,
&wrapping_key));
// Generate an RSA key pair to be wrapped.
const unsigned int modulus_length = 256;
const std::vector<uint8_t> public_exponent = HexStringToBytes("010001");
blink::WebCryptoKey public_key;
blink::WebCryptoKey private_key;
ASSERT_EQ(Status::Success(),
GenerateKeyPair(CreateRsaHashedKeyGenAlgorithm(
blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256,
modulus_length,
public_exponent),
true,
0,
&public_key,
&private_key));
// Export key pair as SPKI + PKCS8
std::vector<uint8_t> public_key_spki;
ASSERT_EQ(
Status::Success(),
ExportKey(blink::WebCryptoKeyFormatSpki, public_key, &public_key_spki));
std::vector<uint8_t> private_key_pkcs8;
ASSERT_EQ(
Status::Success(),
ExportKey(
blink::WebCryptoKeyFormatPkcs8, private_key, &private_key_pkcs8));
// Wrap the key pair.
blink::WebCryptoAlgorithm wrap_algorithm =
CreateAesCbcAlgorithm(std::vector<uint8_t>(16, 0));
std::vector<uint8_t> wrapped_public_key;
ASSERT_EQ(Status::Success(),
WrapKey(blink::WebCryptoKeyFormatSpki,
public_key,
wrapping_key,
wrap_algorithm,
&wrapped_public_key));
std::vector<uint8_t> wrapped_private_key;
ASSERT_EQ(Status::Success(),
WrapKey(blink::WebCryptoKeyFormatPkcs8,
private_key,
wrapping_key,
wrap_algorithm,
&wrapped_private_key));
// Unwrap the key pair.
blink::WebCryptoAlgorithm rsa_import_algorithm =
CreateRsaHashedImportAlgorithm(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::WebCryptoAlgorithmIdSha256);
blink::WebCryptoKey unwrapped_public_key;
ASSERT_EQ(Status::Success(),
UnwrapKey(blink::WebCryptoKeyFormatSpki,
CryptoData(wrapped_public_key),
wrapping_key,
wrap_algorithm,
rsa_import_algorithm,
true,
0,
&unwrapped_public_key));
blink::WebCryptoKey unwrapped_private_key;
ASSERT_EQ(Status::Success(),
UnwrapKey(blink::WebCryptoKeyFormatPkcs8,
CryptoData(wrapped_private_key),
wrapping_key,
wrap_algorithm,
rsa_import_algorithm,
true,
0,
&unwrapped_private_key));
// Export unwrapped key pair as SPKI + PKCS8
std::vector<uint8_t> unwrapped_public_key_spki;
ASSERT_EQ(Status::Success(),
ExportKey(blink::WebCryptoKeyFormatSpki,
unwrapped_public_key,
&unwrapped_public_key_spki));
std::vector<uint8_t> unwrapped_private_key_pkcs8;
ASSERT_EQ(Status::Success(),
ExportKey(blink::WebCryptoKeyFormatPkcs8,
unwrapped_private_key,
&unwrapped_private_key_pkcs8));
EXPECT_EQ(public_key_spki, unwrapped_public_key_spki);
EXPECT_EQ(private_key_pkcs8, unwrapped_private_key_pkcs8);
EXPECT_NE(public_key_spki, wrapped_public_key);
EXPECT_NE(private_key_pkcs8, wrapped_private_key);
}
} // namespace
} // namespace webcrypto
} // namespace content