| Index: content/child/webcrypto/shared_crypto_unittest.cc
|
| diff --git a/content/child/webcrypto/shared_crypto_unittest.cc b/content/child/webcrypto/shared_crypto_unittest.cc
|
| index 48db1a6da8d72e5a49c27bc39cc298fb41fc6402..a71b52fc187973fbabcc1f9839237b49335ce506 100644
|
| --- a/content/child/webcrypto/shared_crypto_unittest.cc
|
| +++ b/content/child/webcrypto/shared_crypto_unittest.cc
|
| @@ -16,7 +16,7 @@
|
| #include "base/memory/ref_counted.h"
|
| #include "base/path_service.h"
|
| #include "base/strings/string_number_conversions.h"
|
| -#include "base/values.h"
|
| +#include "base/strings/string_util.h"
|
| #include "content/child/webcrypto/crypto_data.h"
|
| #include "content/child/webcrypto/webcrypto_util.h"
|
| #include "content/public/common/content_paths.h"
|
| @@ -51,6 +51,25 @@ namespace webcrypto {
|
|
|
| namespace {
|
|
|
| +// TODO(eroman): For Linux builds using system NSS, AES-GCM support is a
|
| +// runtime dependency. Test it by trying to import a key.
|
| +// TODO(padolph): Consider caching the result of the import key test.
|
| +bool SupportsAesGcm() {
|
| + std::vector<uint8> key_raw(16, 0);
|
| +
|
| + blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
| + Status status = ImportKey(blink::WebCryptoKeyFormatRaw,
|
| + CryptoData(key_raw),
|
| + CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm),
|
| + true,
|
| + blink::WebCryptoKeyUsageEncrypt,
|
| + &key);
|
| +
|
| + if (status.IsError())
|
| + EXPECT_STATUS(Status::ErrorUnsupported(), status);
|
| + return status.IsSuccess();
|
| +}
|
| +
|
| blink::WebCryptoAlgorithm CreateRsaKeyGenAlgorithm(
|
| blink::WebCryptoAlgorithmId algorithm_id,
|
| unsigned int modulus_length,
|
| @@ -93,6 +112,7 @@ blink::WebCryptoAlgorithm CreateAesGcmAlgorithm(
|
| const std::vector<uint8>& iv,
|
| const std::vector<uint8>& additional_data,
|
| unsigned int tag_length_bits) {
|
| + EXPECT_TRUE(SupportsAesGcm());
|
| return blink::WebCryptoAlgorithm::adoptParamsAndCreate(
|
| blink::WebCryptoAlgorithmIdAesGcm,
|
| new blink::WebCryptoAesGcmParams(Uint8VectorStart(iv),
|
| @@ -279,7 +299,7 @@ void RestoreJwkOctDictionary(base::DictionaryValue* dict) {
|
| dict->SetString("kty", "oct");
|
| dict->SetString("alg", "A128CBC");
|
| dict->SetString("use", "enc");
|
| - dict->SetBoolean("extractable", false);
|
| + dict->SetBoolean("ext", false);
|
| dict->SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
|
| }
|
|
|
| @@ -290,7 +310,7 @@ void RestoreJwkRsaDictionary(base::DictionaryValue* dict) {
|
| dict->SetString("kty", "RSA");
|
| dict->SetString("alg", "RSA1_5");
|
| dict->SetString("use", "enc");
|
| - dict->SetBoolean("extractable", false);
|
| + dict->SetBoolean("ext", false);
|
| dict->SetString(
|
| "n",
|
| "qLOyhK-OtQs4cDSoYPFGxJGfMYdjzWxVmMiuSBGh4KvEx-CwgtaTpef87Wdc9GaFEncsDLxk"
|
| @@ -343,6 +363,7 @@ blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm(
|
|
|
| blink::WebCryptoAlgorithm CreateAesGcmKeyGenAlgorithm(
|
| unsigned short key_length_bits) {
|
| + EXPECT_TRUE(SupportsAesGcm());
|
| return CreateAesKeyGenAlgorithm(blink::WebCryptoAlgorithmIdAesGcm,
|
| key_length_bits);
|
| }
|
| @@ -450,24 +471,6 @@ void ImportRsaKeyPair(const std::vector<uint8>& spki_der,
|
| EXPECT_EQ(usage_mask, private_key->usages());
|
| }
|
|
|
| -// TODO(eroman): For Linux builds using system NSS, AES-GCM support is a
|
| -// runtime dependency. Test it by trying to import a key.
|
| -bool SupportsAesGcm() {
|
| - std::vector<uint8> key_raw(16, 0);
|
| -
|
| - blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
| - Status status = ImportKey(blink::WebCryptoKeyFormatRaw,
|
| - CryptoData(key_raw),
|
| - CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm),
|
| - true,
|
| - blink::WebCryptoKeyUsageEncrypt,
|
| - &key);
|
| -
|
| - if (status.IsError())
|
| - EXPECT_EQ(Status::ErrorUnsupported().ToString(), status.ToString());
|
| - return status.IsSuccess();
|
| -}
|
| -
|
| Status AesGcmEncrypt(const blink::WebCryptoKey& key,
|
| const std::vector<uint8>& iv,
|
| const std::vector<uint8>& additional_data,
|
| @@ -475,6 +478,7 @@ Status AesGcmEncrypt(const blink::WebCryptoKey& key,
|
| const std::vector<uint8>& plain_text,
|
| std::vector<uint8>* cipher_text,
|
| std::vector<uint8>* authentication_tag) {
|
| + EXPECT_TRUE(SupportsAesGcm());
|
| blink::WebCryptoAlgorithm algorithm =
|
| CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
|
|
|
| @@ -506,6 +510,7 @@ Status AesGcmDecrypt(const blink::WebCryptoKey& key,
|
| const std::vector<uint8>& cipher_text,
|
| const std::vector<uint8>& authentication_tag,
|
| blink::WebArrayBuffer* plain_text) {
|
| + EXPECT_TRUE(SupportsAesGcm());
|
| blink::WebCryptoAlgorithm algorithm =
|
| CreateAesGcmAlgorithm(iv, additional_data, tag_length_bits);
|
|
|
| @@ -533,8 +538,93 @@ Status ImportKeyJwkFromDict(const base::DictionaryValue& dict,
|
| key);
|
| }
|
|
|
| +// Parses an ArrayBuffer of JSON into a dictionary.
|
| +scoped_ptr<base::DictionaryValue> GetJwkDictionary(
|
| + const blink::WebArrayBuffer& json) {
|
| + base::StringPiece json_string(reinterpret_cast<const char*>(json.data()),
|
| + json.byteLength());
|
| + base::Value* value = base::JSONReader::Read(json_string);
|
| + EXPECT_TRUE(value);
|
| + base::DictionaryValue* dict_value = NULL;
|
| + value->GetAsDictionary(&dict_value);
|
| + return scoped_ptr<base::DictionaryValue>(dict_value);
|
| +}
|
| +
|
| +// Verifies that the JSON in the input ArrayBuffer contains the provided
|
| +// expected values. Exact matches are required on the fields examined.
|
| +::testing::AssertionResult VerifySymmetricJwk(
|
| + const blink::WebArrayBuffer& json,
|
| + const std::string& alg_expected,
|
| + const std::string& k_expected_hex,
|
| + blink::WebCryptoKeyUsageMask use_mask_expected) {
|
| +
|
| + scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json);
|
| + if (!dict.get() || dict->empty())
|
| + return ::testing::AssertionFailure() << "JSON parsing failed";
|
| +
|
| + // ---- kty
|
| + std::string value_string;
|
| + if (!dict->GetString("kty", &value_string))
|
| + return ::testing::AssertionFailure() << "Missing 'kty'";
|
| + if (value_string != "oct")
|
| + return ::testing::AssertionFailure()
|
| + << "Expected 'kty' to be 'oct' but found " << value_string;
|
| +
|
| + // ---- alg
|
| + if (!dict->GetString("alg", &value_string))
|
| + return ::testing::AssertionFailure() << "Missing 'alg'";
|
| + if (value_string != alg_expected)
|
| + return ::testing::AssertionFailure() << "Expected 'alg' to be "
|
| + << alg_expected << " but found "
|
| + << value_string;
|
| +
|
| + // ---- k
|
| + if (!dict->GetString("k", &value_string))
|
| + return ::testing::AssertionFailure() << "Missing 'k'";
|
| + std::string k_value;
|
| + if (!webcrypto::Base64DecodeUrlSafe(value_string, &k_value))
|
| + return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed";
|
| + if (!LowerCaseEqualsASCII(base::HexEncode(k_value.data(), k_value.size()),
|
| + k_expected_hex.c_str())) {
|
| + return ::testing::AssertionFailure() << "Expected 'k' to be "
|
| + << k_expected_hex
|
| + << " but found something different";
|
| + }
|
| + // ---- ext
|
| + // always expect ext == true in this case
|
| + bool ext_value;
|
| + if (!dict->GetBoolean("ext", &ext_value))
|
| + return ::testing::AssertionFailure() << "Missing 'ext'";
|
| + if (!ext_value)
|
| + return ::testing::AssertionFailure()
|
| + << "Expected 'ext' to be true but found false";
|
| +
|
| + // ---- key_ops
|
| + base::ListValue* key_ops;
|
| + if (!dict->GetList("key_ops", &key_ops))
|
| + return ::testing::AssertionFailure() << "Missing 'key_ops'";
|
| + blink::WebCryptoKeyUsageMask key_ops_mask = 0;
|
| + Status status = GetWebCryptoUsagesFromJwkKeyOps(key_ops, &key_ops_mask);
|
| + if (status.IsError())
|
| + return ::testing::AssertionFailure() << "Failure extracting 'key_ops'";
|
| + if (key_ops_mask != use_mask_expected)
|
| + return ::testing::AssertionFailure()
|
| + << "Expected 'key_ops' mask to be " << use_mask_expected
|
| + << " but found " << key_ops_mask << " (" << value_string << ")";
|
| +
|
| + return ::testing::AssertionSuccess();
|
| +}
|
| +
|
| } // namespace
|
|
|
| +TEST_F(SharedCryptoTest, CheckAesGcm) {
|
| + if (!SupportsAesGcm()) {
|
| + LOG(WARNING) << "AES GCM not supported on this platform, so some tests "
|
| + "will be skipped. Consider upgrading local NSS libraries";
|
| + return;
|
| + }
|
| +}
|
| +
|
| TEST_F(SharedCryptoTest, StatusToString) {
|
| EXPECT_EQ("Success", Status::Success().ToString());
|
| EXPECT_EQ("", Status::Error().ToString());
|
| @@ -572,7 +662,7 @@ TEST_F(SharedCryptoTest, DigestSampleSets) {
|
| TEST_F(SharedCryptoTest, HMACSampleSets) {
|
| scoped_ptr<base::ListValue> tests;
|
| ASSERT_TRUE(ReadJsonTestFileToList("hmac.json", &tests));
|
| -
|
| + // TODO(padolph): Missing known answer tests for HMAC SHA384, and SHA512.
|
| for (size_t test_index = 0; test_index < tests->GetSize(); ++test_index) {
|
| SCOPED_TRACE(test_index);
|
| base::DictionaryValue* test;
|
| @@ -814,8 +904,9 @@ TEST_F(SharedCryptoTest, MAYBE(GenerateKeyAes)) {
|
| const unsigned short kKeyLength[] = {128, 192, 256};
|
| for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kKeyLength); ++i) {
|
| algorithm.push_back(CreateAesCbcKeyGenAlgorithm(kKeyLength[i]));
|
| - algorithm.push_back(CreateAesGcmKeyGenAlgorithm(kKeyLength[i]));
|
| algorithm.push_back(CreateAesKwKeyGenAlgorithm(kKeyLength[i]));
|
| + if (SupportsAesGcm())
|
| + algorithm.push_back(CreateAesGcmKeyGenAlgorithm(kKeyLength[i]));
|
| }
|
| blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
| std::vector<blink::WebArrayBuffer> keys;
|
| @@ -850,10 +941,13 @@ TEST_F(SharedCryptoTest, MAYBE(GenerateKeyAesBadLength)) {
|
| CreateAesCbcKeyGenAlgorithm(kKeyLen[i]), true, 0, &key));
|
| EXPECT_STATUS(Status::ErrorGenerateKeyLength(),
|
| GenerateSecretKey(
|
| - CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true, 0, &key));
|
| - EXPECT_STATUS(Status::ErrorGenerateKeyLength(),
|
| - GenerateSecretKey(
|
| CreateAesKwKeyGenAlgorithm(kKeyLen[i]), true, 0, &key));
|
| + if (SupportsAesGcm()) {
|
| + EXPECT_STATUS(
|
| + Status::ErrorGenerateKeyLength(),
|
| + GenerateSecretKey(
|
| + CreateAesGcmKeyGenAlgorithm(kKeyLen[i]), true, 0, &key));
|
| + }
|
| }
|
| }
|
|
|
| @@ -918,8 +1012,139 @@ TEST_F(SharedCryptoTest, MAYBE(ImportSecretKeyNoAlgorithm)) {
|
| &key));
|
| }
|
|
|
| -TEST_F(SharedCryptoTest, ImportJwkFailures) {
|
| +TEST_F(SharedCryptoTest, ImportJwkKeyUsage) {
|
| + blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
| + base::DictionaryValue dict;
|
| + dict.SetString("kty", "oct");
|
| + dict.SetBoolean("ext", false);
|
| + dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg==");
|
| + const blink::WebCryptoAlgorithm aes_cbc_algorithm =
|
| + webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
|
| + const blink::WebCryptoAlgorithm hmac_algorithm =
|
| + webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256);
|
| + const blink::WebCryptoAlgorithm aes_kw_algorithm =
|
| + webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw);
|
| +
|
| + // Test null usage.
|
| + base::ListValue* key_ops = new base::ListValue;
|
| + // Note: the following call makes dict assume ownership of key_ops.
|
| + dict.Set("key_ops", key_ops);
|
| + EXPECT_STATUS_SUCCESS(
|
| + ImportKeyJwkFromDict(dict, aes_cbc_algorithm, false, 0, &key));
|
| + EXPECT_EQ(0, key.usages());
|
| +
|
| + // Test each key_ops value translates to the correct Web Crypto value.
|
| + struct TestCase {
|
| + const char* jwk_key_op;
|
| + const char* jwk_alg;
|
| + const blink::WebCryptoAlgorithm algorithm;
|
| + const blink::WebCryptoKeyUsage usage;
|
| + };
|
| + // TODO(padolph): Add 'deriveBits' key_ops value once it is supported.
|
| + const TestCase test_case[] = {
|
| + {"encrypt", "A128CBC", aes_cbc_algorithm,
|
| + blink::WebCryptoKeyUsageEncrypt},
|
| + {"decrypt", "A128CBC", aes_cbc_algorithm,
|
| + blink::WebCryptoKeyUsageDecrypt},
|
| + {"sign", "HS256", hmac_algorithm, blink::WebCryptoKeyUsageSign},
|
| + {"verify", "HS256", hmac_algorithm, blink::WebCryptoKeyUsageVerify},
|
| + {"wrapKey", "A128KW", aes_kw_algorithm, blink::WebCryptoKeyUsageWrapKey},
|
| + {"unwrapKey", "A128KW", aes_kw_algorithm,
|
| + blink::WebCryptoKeyUsageUnwrapKey},
|
| + {"deriveKey", "HS256", hmac_algorithm,
|
| + blink::WebCryptoKeyUsageDeriveKey}};
|
| + for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(test_case);
|
| + ++test_index) {
|
| + SCOPED_TRACE(test_index);
|
| + dict.SetString("alg", test_case[test_index].jwk_alg);
|
| + key_ops->Clear();
|
| + key_ops->AppendString(test_case[test_index].jwk_key_op);
|
| + EXPECT_STATUS_SUCCESS(ImportKeyJwkFromDict(dict,
|
| + test_case[test_index].algorithm,
|
| + false,
|
| + test_case[test_index].usage,
|
| + &key));
|
| + EXPECT_EQ(test_case[test_index].usage, key.usages());
|
| + }
|
| +
|
| + // Test discrete multiple usages.
|
| + dict.SetString("alg", "A128CBC");
|
| + key_ops->Clear();
|
| + key_ops->AppendString("encrypt");
|
| + key_ops->AppendString("decrypt");
|
| + EXPECT_STATUS_SUCCESS(ImportKeyJwkFromDict(
|
| + dict,
|
| + aes_cbc_algorithm,
|
| + false,
|
| + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt,
|
| + &key));
|
| + EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt,
|
| + key.usages());
|
| +
|
| + // Test constrained key usage (input usage is a subset of JWK usage).
|
| + key_ops->Clear();
|
| + key_ops->AppendString("encrypt");
|
| + key_ops->AppendString("decrypt");
|
| + EXPECT_STATUS_SUCCESS(ImportKeyJwkFromDict(
|
| + dict, aes_cbc_algorithm, false, blink::WebCryptoKeyUsageDecrypt, &key));
|
| + EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt, key.usages());
|
| +
|
| + // Test failure if input usage is NOT a strict subset of the JWK usage.
|
| + key_ops->Clear();
|
| + key_ops->AppendString("encrypt");
|
| + EXPECT_STATUS(Status::ErrorJwkKeyopsInconsistent(),
|
| + ImportKeyJwkFromDict(dict,
|
| + aes_cbc_algorithm,
|
| + false,
|
| + blink::WebCryptoKeyUsageEncrypt |
|
| + blink::WebCryptoKeyUsageDecrypt,
|
| + &key));
|
| +
|
| + // Test 'use' inconsistent with 'key_ops'.
|
| + dict.SetString("alg", "HS256");
|
| + dict.SetString("use", "sig");
|
| + key_ops->AppendString("sign");
|
| + key_ops->AppendString("verify");
|
| + key_ops->AppendString("encrypt");
|
| + EXPECT_STATUS(Status::ErrorJwkUseAndKeyopsInconsistent(),
|
| + ImportKeyJwkFromDict(dict,
|
| + hmac_algorithm,
|
| + false,
|
| + blink::WebCryptoKeyUsageSign |
|
| + blink::WebCryptoKeyUsageVerify,
|
| + &key));
|
| +
|
| + // Test JWK composite 'sig' use
|
| + dict.Remove("key_ops", NULL);
|
| + dict.SetString("use", "sig");
|
| + EXPECT_STATUS_SUCCESS(ImportKeyJwkFromDict(
|
| + dict,
|
| + hmac_algorithm,
|
| + false,
|
| + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify,
|
| + &key));
|
| + EXPECT_EQ(blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify,
|
| + key.usages());
|
| +
|
| + // Test JWK composite use 'enc' usage
|
| + dict.SetString("alg", "A128CBC");
|
| + dict.SetString("use", "enc");
|
| + EXPECT_STATUS_SUCCESS(ImportKeyJwkFromDict(
|
| + dict,
|
| + aes_cbc_algorithm,
|
| + false,
|
| + blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt |
|
| + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey |
|
| + blink::WebCryptoKeyUsageDeriveKey,
|
| + &key));
|
| + EXPECT_EQ(blink::WebCryptoKeyUsageDecrypt | blink::WebCryptoKeyUsageEncrypt |
|
| + blink::WebCryptoKeyUsageWrapKey |
|
| + blink::WebCryptoKeyUsageUnwrapKey |
|
| + blink::WebCryptoKeyUsageDeriveKey,
|
| + key.usages());
|
| +}
|
|
|
| +TEST_F(SharedCryptoTest, ImportJwkFailures) {
|
| blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
| blink::WebCryptoAlgorithm algorithm =
|
| CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
|
| @@ -987,7 +1212,7 @@ TEST_F(SharedCryptoTest, ImportJwkFailures) {
|
|
|
| // Fail on invalid use.
|
| dict.SetString("use", "foo");
|
| - EXPECT_STATUS(Status::ErrorJwkUnrecognizedUsage(),
|
| + EXPECT_STATUS(Status::ErrorJwkUnrecognizedUse(),
|
| ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key));
|
| RestoreJwkOctDictionary(&dict);
|
|
|
| @@ -998,14 +1223,28 @@ TEST_F(SharedCryptoTest, ImportJwkFailures) {
|
| RestoreJwkOctDictionary(&dict);
|
|
|
| // Fail on invalid extractable (wrong type).
|
| - dict.SetInteger("extractable", 0);
|
| - EXPECT_STATUS(Status::ErrorJwkPropertyWrongType("extractable", "boolean"),
|
| + dict.SetInteger("ext", 0);
|
| + EXPECT_STATUS(Status::ErrorJwkPropertyWrongType("ext", "boolean"),
|
| + ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key));
|
| + RestoreJwkOctDictionary(&dict);
|
| +
|
| + // Fail on invalid key_ops (wrong type).
|
| + dict.SetBoolean("key_ops", true);
|
| + EXPECT_STATUS(Status::ErrorJwkPropertyWrongType("key_ops", "list"),
|
| + ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key));
|
| + RestoreJwkOctDictionary(&dict);
|
| +
|
| + // Fail on invalid key_ops (wrong element value).
|
| + 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_STATUS(Status::ErrorJwkUnrecognizedKeyop(),
|
| ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key));
|
| RestoreJwkOctDictionary(&dict);
|
| }
|
|
|
| TEST_F(SharedCryptoTest, ImportJwkOctFailures) {
|
| -
|
| base::DictionaryValue dict;
|
| RestoreJwkOctDictionary(&dict);
|
| blink::WebCryptoAlgorithm algorithm =
|
| @@ -1057,7 +1296,6 @@ TEST_F(SharedCryptoTest, ImportJwkOctFailures) {
|
| }
|
|
|
| TEST_F(SharedCryptoTest, MAYBE(ImportJwkRsaFailures)) {
|
| -
|
| base::DictionaryValue dict;
|
| RestoreJwkRsaDictionary(&dict);
|
| blink::WebCryptoAlgorithm algorithm =
|
| @@ -1084,7 +1322,6 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkRsaFailures)) {
|
| // Fail if either "n" or "e" is not present or malformed.
|
| const std::string kKtyParmName[] = {"n", "e"};
|
| for (size_t idx = 0; idx < ARRAYSIZE_UNSAFE(kKtyParmName); ++idx) {
|
| -
|
| // Fail on missing parameter.
|
| dict.Remove(kKtyParmName[idx], NULL);
|
| EXPECT_STATUS_ERROR(
|
| @@ -1144,7 +1381,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
|
| dict.SetString("kty", "oct");
|
| dict.SetString("alg", "HS256");
|
| dict.SetString("use", "sig");
|
| - dict.SetBoolean("extractable", false);
|
| + dict.SetBoolean("ext", false);
|
| dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
|
| json_vec = MakeJsonVector(dict);
|
| EXPECT_STATUS_SUCCESS(ImportKeyJwk(
|
| @@ -1156,19 +1393,19 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
|
| // 2. input=T, JWK=T ==> pass, result extractable is T
|
| // 3. input=F, JWK=T ==> pass, result extractable is F
|
| EXPECT_STATUS(
|
| - Status::ErrorJwkExtractableInconsistent(),
|
| + Status::ErrorJwkExtInconsistent(),
|
| ImportKeyJwk(CryptoData(json_vec), algorithm, true, usage_mask, &key));
|
| EXPECT_STATUS_SUCCESS(
|
| ImportKeyJwk(CryptoData(json_vec), algorithm, false, usage_mask, &key));
|
| EXPECT_FALSE(key.extractable());
|
| - dict.SetBoolean("extractable", true);
|
| + dict.SetBoolean("ext", true);
|
| EXPECT_STATUS_SUCCESS(
|
| ImportKeyJwkFromDict(dict, algorithm, true, usage_mask, &key));
|
| EXPECT_TRUE(key.extractable());
|
| EXPECT_STATUS_SUCCESS(
|
| ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key));
|
| EXPECT_FALSE(key.extractable());
|
| - dict.SetBoolean("extractable", true); // restore previous value
|
| + dict.SetBoolean("ext", true); // restore previous value
|
|
|
| // Fail: Input algorithm (AES-CBC) is inconsistent with JWK value
|
| // (HMAC SHA256).
|
| @@ -1210,7 +1447,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
|
|
|
| // Fail: Input usage_mask (encrypt) is not a subset of the JWK value
|
| // (sign|verify)
|
| - EXPECT_STATUS(Status::ErrorJwkUsageInconsistent(),
|
| + EXPECT_STATUS(Status::ErrorJwkUseInconsistent(),
|
| ImportKeyJwk(CryptoData(json_vec),
|
| algorithm,
|
| extractable,
|
| @@ -1222,7 +1459,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
|
| usage_mask = blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageSign |
|
| blink::WebCryptoKeyUsageVerify;
|
| EXPECT_STATUS(
|
| - Status::ErrorJwkUsageInconsistent(),
|
| + Status::ErrorJwkUseInconsistent(),
|
| ImportKeyJwk(
|
| CryptoData(json_vec), algorithm, extractable, usage_mask, &key));
|
|
|
| @@ -1230,10 +1467,11 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkInputConsistency)) {
|
| // only certain alg values are permitted. For example, when kty = "RSA" alg
|
| // must be of the RSA family, or when kty = "oct" alg must be symmetric
|
| // algorithm.
|
| +
|
| + // TODO(padolph): key_ops consistency tests
|
| }
|
|
|
| TEST_F(SharedCryptoTest, MAYBE(ImportJwkHappy)) {
|
| -
|
| // This test verifies the happy path of JWK import, including the application
|
| // of the imported key material.
|
|
|
| @@ -1250,7 +1488,7 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkHappy)) {
|
| dict.SetString("kty", "oct");
|
| dict.SetString("alg", "HS256");
|
| dict.SetString("use", "sig");
|
| - dict.SetBoolean("extractable", false);
|
| + dict.SetBoolean("ext", false);
|
| dict.SetString("k", "l3nZEgZCeX8XRwJdWyK3rGB8qwjhdY8vOkbIvh4lxTuMao9Y_--hdg");
|
|
|
| ASSERT_STATUS_SUCCESS(
|
| @@ -1280,6 +1518,128 @@ TEST_F(SharedCryptoTest, MAYBE(ImportJwkHappy)) {
|
| // TODO(padolph): Import an RSA public key JWK and use it
|
| }
|
|
|
| +TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkSymmetricKey)) {
|
| + // Raw keys are generated by openssl:
|
| + // % openssl rand -hex <key length bytes>
|
| + const char* const key_hex_128 = "3f1e7cd4f6f8543f6b1e16002e688623";
|
| + const char* const key_hex_192 =
|
| + "ed91f916dc034eba68a0f9e7f34ddd48b98bd2848109e243";
|
| + const char* const key_hex_256 =
|
| + "bd08286b81a74783fd1ccf46b7e05af84ee25ae021210074159e0c4d9d907692";
|
| + const char* const key_hex_384 =
|
| + "a22c5441c8b185602283d64c7221de1d0951e706bfc09539435ec0e0ed614e1d406623f2"
|
| + "b31d31819fec30993380dd82";
|
| + const char* const key_hex_512 =
|
| + "5834f639000d4cf82de124fbfd26fb88d463e99f839a76ba41ac88967c80a3f61e1239a4"
|
| + "52e573dba0750e988152988576efd75b8d0229b7aca2ada2afd392ee";
|
| + const blink::WebCryptoAlgorithm aes_cbc_alg =
|
| + webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesCbc);
|
| + const blink::WebCryptoAlgorithm aes_gcm_alg =
|
| + webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesGcm);
|
| + const blink::WebCryptoAlgorithm aes_kw_alg =
|
| + webcrypto::CreateAlgorithm(blink::WebCryptoAlgorithmIdAesKw);
|
| + const blink::WebCryptoAlgorithm hmac_sha_1_alg =
|
| + webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha1);
|
| + const blink::WebCryptoAlgorithm hmac_sha_256_alg =
|
| + webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha256);
|
| + const blink::WebCryptoAlgorithm hmac_sha_384_alg =
|
| + webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha384);
|
| + const blink::WebCryptoAlgorithm hmac_sha_512_alg =
|
| + webcrypto::CreateHmacImportAlgorithm(blink::WebCryptoAlgorithmIdSha512);
|
| +
|
| + struct TestCase {
|
| + const char* const key_hex;
|
| + const blink::WebCryptoAlgorithm algorithm;
|
| + const blink::WebCryptoKeyUsageMask usage;
|
| + const char* const jwk_alg;
|
| + };
|
| +
|
| + // TODO(padolph): Test AES-CTR JWK export, once AES-CTR import works.
|
| + const TestCase kTests[] = {
|
| + // AES-CBC 128
|
| + {key_hex_128, aes_cbc_alg,
|
| + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
|
| + "A128CBC"},
|
| + // AES-CBC 192
|
| + {key_hex_192, aes_cbc_alg, blink::WebCryptoKeyUsageEncrypt, "A192CBC"},
|
| + // AES-CBC 256
|
| + {key_hex_256, aes_cbc_alg, blink::WebCryptoKeyUsageDecrypt, "A256CBC"},
|
| + // AES-GCM 128
|
| + {key_hex_128, aes_gcm_alg,
|
| + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt,
|
| + "A128GCM"},
|
| + // AES-CGM 192
|
| + {key_hex_192, aes_gcm_alg, blink::WebCryptoKeyUsageEncrypt, "A192GCM"},
|
| + // AES-GCM 256
|
| + {key_hex_256, aes_gcm_alg, blink::WebCryptoKeyUsageDecrypt, "A256GCM"},
|
| + // AES-KW 128
|
| + {key_hex_128, aes_kw_alg,
|
| + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
|
| + "A128KW"},
|
| + // AES-KW 192
|
| + {key_hex_192, aes_kw_alg,
|
| + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
|
| + "A192KW"},
|
| + // AES-KW 256
|
| + {key_hex_256, aes_kw_alg,
|
| + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
|
| + "A256KW"},
|
| + // HMAC SHA-1
|
| + {key_hex_256, hmac_sha_1_alg,
|
| + blink::WebCryptoKeyUsageSign | blink::WebCryptoKeyUsageVerify, "HS1"},
|
| + // HMAC SHA-384
|
| + {key_hex_384, hmac_sha_384_alg, blink::WebCryptoKeyUsageSign, "HS384"},
|
| + // HMAC SHA-512
|
| + {key_hex_512, hmac_sha_512_alg, blink::WebCryptoKeyUsageVerify, "HS512"},
|
| + // Large usage value
|
| + {key_hex_256, aes_cbc_alg,
|
| + blink::WebCryptoKeyUsageEncrypt | blink::WebCryptoKeyUsageDecrypt |
|
| + blink::WebCryptoKeyUsageWrapKey | blink::WebCryptoKeyUsageUnwrapKey,
|
| + "A256CBC"},
|
| + // Zero usage value
|
| + {key_hex_512, hmac_sha_512_alg, 0, "HS512"}, };
|
| +
|
| + // Round-trip import/export each key.
|
| +
|
| + blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
| + blink::WebArrayBuffer json;
|
| + for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests);
|
| + ++test_index) {
|
| + SCOPED_TRACE(test_index);
|
| + const TestCase& test = kTests[test_index];
|
| +
|
| + // Skip AES-GCM tests where not supported.
|
| + if (test.algorithm.id() == blink::WebCryptoAlgorithmIdAesGcm &&
|
| + !SupportsAesGcm()) {
|
| + continue;
|
| + }
|
| +
|
| + // Import a raw key.
|
| + key = ImportSecretKeyFromRaw(
|
| + HexStringToBytes(test.key_hex), test.algorithm, test.usage);
|
| +
|
| + // Export the key in JWK format and validate.
|
| + ASSERT_STATUS_SUCCESS(ExportKeyJwk(key, &json));
|
| + EXPECT_TRUE(
|
| + VerifySymmetricJwk(json, test.jwk_alg, test.key_hex, test.usage));
|
| +
|
| + // Import the JWK-formatted key.
|
| + ASSERT_STATUS_SUCCESS(
|
| + ImportKeyJwk(CryptoData(json), test.algorithm, true, test.usage, &key));
|
| + EXPECT_TRUE(key.handle());
|
| + EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type());
|
| + EXPECT_EQ(test.algorithm.id(), key.algorithm().id());
|
| + EXPECT_EQ(true, key.extractable());
|
| + EXPECT_EQ(test.usage, key.usages());
|
| +
|
| + // Export the key in raw format and compare to the original.
|
| + blink::WebArrayBuffer key_raw_out;
|
| + ASSERT_STATUS_SUCCESS(
|
| + ExportKey(blink::WebCryptoKeyFormatRaw, key, &key_raw_out));
|
| + ExpectArrayBufferMatchesHex(test.key_hex, key_raw_out);
|
| + }
|
| +}
|
| +
|
| TEST_F(SharedCryptoTest, MAYBE(ImportExportSpki)) {
|
| // Passing case: Import a valid RSA key in SPKI format.
|
| blink::WebCryptoKey key = blink::WebCryptoKey::createNull();
|
|
|