OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/child/webcrypto/shared_crypto.h" | 5 #include "content/child/webcrypto/shared_crypto.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 522 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
533 const blink::WebArrayBuffer& json) { | 533 const blink::WebArrayBuffer& json) { |
534 base::StringPiece json_string(reinterpret_cast<const char*>(json.data()), | 534 base::StringPiece json_string(reinterpret_cast<const char*>(json.data()), |
535 json.byteLength()); | 535 json.byteLength()); |
536 base::Value* value = base::JSONReader::Read(json_string); | 536 base::Value* value = base::JSONReader::Read(json_string); |
537 EXPECT_TRUE(value); | 537 EXPECT_TRUE(value); |
538 base::DictionaryValue* dict_value = NULL; | 538 base::DictionaryValue* dict_value = NULL; |
539 value->GetAsDictionary(&dict_value); | 539 value->GetAsDictionary(&dict_value); |
540 return scoped_ptr<base::DictionaryValue>(dict_value); | 540 return scoped_ptr<base::DictionaryValue>(dict_value); |
541 } | 541 } |
542 | 542 |
543 // Verifies that the JSON in the input ArrayBuffer contains the provided | 543 // Verifies the input dictionary contains the expected values. Exact matches are |
544 // expected values. Exact matches are required on the fields examined. | 544 // required on the fields examined. |
545 ::testing::AssertionResult VerifySymmetricJwk( | 545 ::testing::AssertionResult VerifyJwk( |
546 const blink::WebArrayBuffer& json, | 546 const scoped_ptr<base::DictionaryValue>& dict, |
| 547 const std::string& kty_expected, |
547 const std::string& alg_expected, | 548 const std::string& alg_expected, |
548 const std::string& k_expected_hex, | |
549 blink::WebCryptoKeyUsageMask use_mask_expected) { | 549 blink::WebCryptoKeyUsageMask use_mask_expected) { |
550 | 550 |
551 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); | |
552 if (!dict.get() || dict->empty()) | |
553 return ::testing::AssertionFailure() << "JSON parsing failed"; | |
554 | |
555 // ---- kty | 551 // ---- kty |
556 std::string value_string; | 552 std::string value_string; |
557 if (!dict->GetString("kty", &value_string)) | 553 if (!dict->GetString("kty", &value_string)) |
558 return ::testing::AssertionFailure() << "Missing 'kty'"; | 554 return ::testing::AssertionFailure() << "Missing 'kty'"; |
559 if (value_string != "oct") | 555 if (value_string != kty_expected) |
560 return ::testing::AssertionFailure() | 556 return ::testing::AssertionFailure() << "Expected 'kty' to be " |
561 << "Expected 'kty' to be 'oct' but found " << value_string; | 557 << kty_expected << "but found " |
| 558 << value_string; |
562 | 559 |
563 // ---- alg | 560 // ---- alg |
564 if (!dict->GetString("alg", &value_string)) | 561 if (!dict->GetString("alg", &value_string)) |
565 return ::testing::AssertionFailure() << "Missing 'alg'"; | 562 return ::testing::AssertionFailure() << "Missing 'alg'"; |
566 if (value_string != alg_expected) | 563 if (value_string != alg_expected) |
567 return ::testing::AssertionFailure() << "Expected 'alg' to be " | 564 return ::testing::AssertionFailure() << "Expected 'alg' to be " |
568 << alg_expected << " but found " | 565 << alg_expected << " but found " |
569 << value_string; | 566 << value_string; |
570 | 567 |
571 // ---- k | |
572 if (!dict->GetString("k", &value_string)) | |
573 return ::testing::AssertionFailure() << "Missing 'k'"; | |
574 std::string k_value; | |
575 if (!webcrypto::Base64DecodeUrlSafe(value_string, &k_value)) | |
576 return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed"; | |
577 if (!LowerCaseEqualsASCII(base::HexEncode(k_value.data(), k_value.size()), | |
578 k_expected_hex.c_str())) { | |
579 return ::testing::AssertionFailure() << "Expected 'k' to be " | |
580 << k_expected_hex | |
581 << " but found something different"; | |
582 } | |
583 // ---- ext | 568 // ---- ext |
584 // always expect ext == true in this case | 569 // always expect ext == true in this case |
585 bool ext_value; | 570 bool ext_value; |
586 if (!dict->GetBoolean("ext", &ext_value)) | 571 if (!dict->GetBoolean("ext", &ext_value)) |
587 return ::testing::AssertionFailure() << "Missing 'ext'"; | 572 return ::testing::AssertionFailure() << "Missing 'ext'"; |
588 if (!ext_value) | 573 if (!ext_value) |
589 return ::testing::AssertionFailure() | 574 return ::testing::AssertionFailure() |
590 << "Expected 'ext' to be true but found false"; | 575 << "Expected 'ext' to be true but found false"; |
591 | 576 |
592 // ---- key_ops | 577 // ---- key_ops |
593 base::ListValue* key_ops; | 578 base::ListValue* key_ops; |
594 if (!dict->GetList("key_ops", &key_ops)) | 579 if (!dict->GetList("key_ops", &key_ops)) |
595 return ::testing::AssertionFailure() << "Missing 'key_ops'"; | 580 return ::testing::AssertionFailure() << "Missing 'key_ops'"; |
596 blink::WebCryptoKeyUsageMask key_ops_mask = 0; | 581 blink::WebCryptoKeyUsageMask key_ops_mask = 0; |
597 Status status = GetWebCryptoUsagesFromJwkKeyOps(key_ops, &key_ops_mask); | 582 Status status = GetWebCryptoUsagesFromJwkKeyOps(key_ops, &key_ops_mask); |
598 if (status.IsError()) | 583 if (status.IsError()) |
599 return ::testing::AssertionFailure() << "Failure extracting 'key_ops'"; | 584 return ::testing::AssertionFailure() << "Failure extracting 'key_ops'"; |
600 if (key_ops_mask != use_mask_expected) | 585 if (key_ops_mask != use_mask_expected) |
601 return ::testing::AssertionFailure() | 586 return ::testing::AssertionFailure() |
602 << "Expected 'key_ops' mask to be " << use_mask_expected | 587 << "Expected 'key_ops' mask to be " << use_mask_expected |
603 << " but found " << key_ops_mask << " (" << value_string << ")"; | 588 << " but found " << key_ops_mask << " (" << value_string << ")"; |
604 | 589 |
605 return ::testing::AssertionSuccess(); | 590 return ::testing::AssertionSuccess(); |
606 } | 591 } |
607 | 592 |
| 593 // Verifies that the JSON in the input ArrayBuffer contains the provided |
| 594 // expected values. Exact matches are required on the fields examined. |
| 595 ::testing::AssertionResult VerifySecretJwk( |
| 596 const blink::WebArrayBuffer& json, |
| 597 const std::string& alg_expected, |
| 598 const std::string& k_expected_hex, |
| 599 blink::WebCryptoKeyUsageMask use_mask_expected) { |
| 600 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); |
| 601 if (!dict.get() || dict->empty()) |
| 602 return ::testing::AssertionFailure() << "JSON parsing failed"; |
| 603 |
| 604 // ---- k |
| 605 std::string value_string; |
| 606 if (!dict->GetString("k", &value_string)) |
| 607 return ::testing::AssertionFailure() << "Missing 'k'"; |
| 608 std::string k_value; |
| 609 if (!webcrypto::Base64DecodeUrlSafe(value_string, &k_value)) |
| 610 return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(k) failed"; |
| 611 if (!LowerCaseEqualsASCII(base::HexEncode(k_value.data(), k_value.size()), |
| 612 k_expected_hex.c_str())) { |
| 613 return ::testing::AssertionFailure() << "Expected 'k' to be " |
| 614 << k_expected_hex |
| 615 << " but found something different"; |
| 616 } |
| 617 |
| 618 return VerifyJwk(dict, "oct", alg_expected, use_mask_expected); |
| 619 } |
| 620 |
| 621 // Verifies that the JSON in the input ArrayBuffer contains the provided |
| 622 // expected values. Exact matches are required on the fields examined. |
| 623 ::testing::AssertionResult VerifyPublicJwk( |
| 624 const blink::WebArrayBuffer& json, |
| 625 const std::string& alg_expected, |
| 626 const std::string& n_expected_hex, |
| 627 const std::string& e_expected_hex, |
| 628 blink::WebCryptoKeyUsageMask use_mask_expected) { |
| 629 scoped_ptr<base::DictionaryValue> dict = GetJwkDictionary(json); |
| 630 if (!dict.get() || dict->empty()) |
| 631 return ::testing::AssertionFailure() << "JSON parsing failed"; |
| 632 |
| 633 // ---- n |
| 634 std::string value_string; |
| 635 if (!dict->GetString("n", &value_string)) |
| 636 return ::testing::AssertionFailure() << "Missing 'n'"; |
| 637 std::string n_value; |
| 638 if (!webcrypto::Base64DecodeUrlSafe(value_string, &n_value)) |
| 639 return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(n) failed"; |
| 640 if (base::HexEncode(n_value.data(), n_value.size()) != n_expected_hex) { |
| 641 return ::testing::AssertionFailure() << "'n' does not match the expected " |
| 642 "value"; |
| 643 } |
| 644 // TODO(padolph): LowerCaseEqualsASCII() does not work for above! |
| 645 |
| 646 // ---- e |
| 647 if (!dict->GetString("e", &value_string)) |
| 648 return ::testing::AssertionFailure() << "Missing 'e'"; |
| 649 std::string e_value; |
| 650 if (!webcrypto::Base64DecodeUrlSafe(value_string, &e_value)) |
| 651 return ::testing::AssertionFailure() << "Base64DecodeUrlSafe(e) failed"; |
| 652 if (!LowerCaseEqualsASCII(base::HexEncode(e_value.data(), e_value.size()), |
| 653 e_expected_hex.c_str())) { |
| 654 return ::testing::AssertionFailure() << "Expected 'e' to be " |
| 655 << e_expected_hex |
| 656 << " but found something different"; |
| 657 } |
| 658 |
| 659 return VerifyJwk(dict, "RSA", alg_expected, use_mask_expected); |
| 660 } |
| 661 |
608 } // namespace | 662 } // namespace |
609 | 663 |
610 TEST_F(SharedCryptoTest, CheckAesGcm) { | 664 TEST_F(SharedCryptoTest, CheckAesGcm) { |
611 if (!SupportsAesGcm()) { | 665 if (!SupportsAesGcm()) { |
612 LOG(WARNING) << "AES GCM not supported on this platform, so some tests " | 666 LOG(WARNING) << "AES GCM not supported on this platform, so some tests " |
613 "will be skipped. Consider upgrading local NSS libraries"; | 667 "will be skipped. Consider upgrading local NSS libraries"; |
614 return; | 668 return; |
615 } | 669 } |
616 } | 670 } |
617 | 671 |
(...skipping 645 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1263 RestoreJwkOctDictionary(&dict); | 1317 RestoreJwkOctDictionary(&dict); |
1264 | 1318 |
1265 // Fail on k actual length (192 bits) inconsistent with the embedded JWK alg | 1319 // Fail on k actual length (192 bits) inconsistent with the embedded JWK alg |
1266 // value (128) for an AES key. | 1320 // value (128) for an AES key. |
1267 dict.SetString("k", "dGhpcyAgaXMgIDI0ICBieXRlcyBsb25n"); | 1321 dict.SetString("k", "dGhpcyAgaXMgIDI0ICBieXRlcyBsb25n"); |
1268 EXPECT_STATUS(Status::ErrorJwkIncorrectKeyLength(), | 1322 EXPECT_STATUS(Status::ErrorJwkIncorrectKeyLength(), |
1269 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); | 1323 ImportKeyJwkFromDict(dict, algorithm, false, usage_mask, &key)); |
1270 RestoreJwkOctDictionary(&dict); | 1324 RestoreJwkOctDictionary(&dict); |
1271 } | 1325 } |
1272 | 1326 |
| 1327 TEST_F(SharedCryptoTest, MAYBE(ImportExportJwkRsaPublicKey)) { |
| 1328 // This test uses kPublicKeySpkiDerHex as the RSA key. The data below |
| 1329 // represents the modulus and public exponent extracted from this SPKI blob. |
| 1330 // These values appear explicitly in the JWK rendering of the key. |
| 1331 const std::string n_hex = |
| 1332 "A56E4A0E701017589A5187DC7EA841D156F2EC0E36AD52A44DFEB1E61F7AD991D8C51056" |
| 1333 "FFEDB162B4C0F283A12A88A394DFF526AB7291CBB307CEABFCE0B1DFD5CD9508096D5B2B" |
| 1334 "8B6DF5D671EF6377C0921CB23C270A70E2598E6FF89D19F105ACC2D3F0CB35F29280E138" |
| 1335 "6B6F64C4EF22E1E1F20D0CE8CFFB2249BD9A2137"; |
| 1336 const std::string e_hex = "010001"; |
| 1337 |
| 1338 struct TestCase { |
| 1339 const blink::WebCryptoAlgorithm algorithm; |
| 1340 const blink::WebCryptoKeyUsageMask usage; |
| 1341 const char* const jwk_alg; |
| 1342 }; |
| 1343 const TestCase kTests[] = { |
| 1344 // RSAES-PKCS1-v1_5 |
| 1345 {CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5), |
| 1346 blink::WebCryptoKeyUsageEncrypt, "RSA1_5"}, |
| 1347 // RSASSA-PKCS1-v1_5 SHA-1 |
| 1348 {CreateRsaHashedImportAlgorithm( |
| 1349 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| 1350 blink::WebCryptoAlgorithmIdSha1), |
| 1351 blink::WebCryptoKeyUsageSign, "RS1"}, |
| 1352 // RSASSA-PKCS1-v1_5 SHA-256 |
| 1353 {CreateRsaHashedImportAlgorithm( |
| 1354 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| 1355 blink::WebCryptoAlgorithmIdSha256), |
| 1356 blink::WebCryptoKeyUsageSign, "RS256"}, |
| 1357 // RSASSA-PKCS1-v1_5 SHA-384 |
| 1358 {CreateRsaHashedImportAlgorithm( |
| 1359 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| 1360 blink::WebCryptoAlgorithmIdSha384), |
| 1361 blink::WebCryptoKeyUsageSign, "RS384"}, |
| 1362 // RSASSA-PKCS1-v1_5 SHA-512 |
| 1363 {CreateRsaHashedImportAlgorithm( |
| 1364 blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, |
| 1365 blink::WebCryptoAlgorithmIdSha512), |
| 1366 blink::WebCryptoKeyUsageSign, "RS512"}}; |
| 1367 |
| 1368 for (size_t test_index = 0; test_index < ARRAYSIZE_UNSAFE(kTests); |
| 1369 ++test_index) { |
| 1370 SCOPED_TRACE(test_index); |
| 1371 const TestCase& test = kTests[test_index]; |
| 1372 |
| 1373 // Import the spki to create a public key |
| 1374 blink::WebCryptoKey public_key = blink::WebCryptoKey::createNull(); |
| 1375 ASSERT_STATUS_SUCCESS( |
| 1376 ImportKey(blink::WebCryptoKeyFormatSpki, |
| 1377 CryptoData(HexStringToBytes(kPublicKeySpkiDerHex)), |
| 1378 test.algorithm, |
| 1379 true, |
| 1380 test.usage, |
| 1381 &public_key)); |
| 1382 |
| 1383 // Export the public key as JWK and verify its contents |
| 1384 blink::WebArrayBuffer jwk; |
| 1385 ASSERT_STATUS_SUCCESS( |
| 1386 ExportKey(blink::WebCryptoKeyFormatJwk, public_key, &jwk)); |
| 1387 EXPECT_TRUE(VerifyPublicJwk(jwk, test.jwk_alg, n_hex, e_hex, test.usage)); |
| 1388 |
| 1389 // Import the JWK back in to create a new key |
| 1390 blink::WebCryptoKey public_key2 = blink::WebCryptoKey::createNull(); |
| 1391 EXPECT_STATUS_SUCCESS(ImportKeyJwk( |
| 1392 CryptoData(jwk), test.algorithm, true, test.usage, &public_key2)); |
| 1393 EXPECT_TRUE(public_key2.handle()); |
| 1394 EXPECT_EQ(blink::WebCryptoKeyTypePublic, public_key2.type()); |
| 1395 EXPECT_EQ(true, public_key2.extractable()); |
| 1396 EXPECT_EQ(test.algorithm.id(), public_key2.algorithm().id()); |
| 1397 |
| 1398 // Export the new key as spki and compare to the original. |
| 1399 blink::WebArrayBuffer spki; |
| 1400 ASSERT_STATUS_SUCCESS( |
| 1401 ExportKey(blink::WebCryptoKeyFormatSpki, public_key2, &spki)); |
| 1402 ExpectCryptoDataMatchesHex(kPublicKeySpkiDerHex, CryptoData(spki)); |
| 1403 } |
| 1404 } |
| 1405 |
1273 TEST_F(SharedCryptoTest, MAYBE(ImportJwkRsaFailures)) { | 1406 TEST_F(SharedCryptoTest, MAYBE(ImportJwkRsaFailures)) { |
1274 base::DictionaryValue dict; | 1407 base::DictionaryValue dict; |
1275 RestoreJwkRsaDictionary(&dict); | 1408 RestoreJwkRsaDictionary(&dict); |
1276 blink::WebCryptoAlgorithm algorithm = | 1409 blink::WebCryptoAlgorithm algorithm = |
1277 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); | 1410 CreateAlgorithm(blink::WebCryptoAlgorithmIdRsaEsPkcs1v1_5); |
1278 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; | 1411 blink::WebCryptoKeyUsageMask usage_mask = blink::WebCryptoKeyUsageEncrypt; |
1279 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); | 1412 blink::WebCryptoKey key = blink::WebCryptoKey::createNull(); |
1280 | 1413 |
1281 // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) | 1414 // An RSA public key JWK _must_ have an "n" (modulus) and an "e" (exponent) |
1282 // entry, while an RSA private key must have those plus at least a "d" | 1415 // entry, while an RSA private key must have those plus at least a "d" |
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1581 !SupportsAesGcm()) { | 1714 !SupportsAesGcm()) { |
1582 continue; | 1715 continue; |
1583 } | 1716 } |
1584 | 1717 |
1585 // Import a raw key. | 1718 // Import a raw key. |
1586 key = ImportSecretKeyFromRaw( | 1719 key = ImportSecretKeyFromRaw( |
1587 HexStringToBytes(test.key_hex), test.algorithm, test.usage); | 1720 HexStringToBytes(test.key_hex), test.algorithm, test.usage); |
1588 | 1721 |
1589 // Export the key in JWK format and validate. | 1722 // Export the key in JWK format and validate. |
1590 ASSERT_STATUS_SUCCESS(ExportKeyJwk(key, &json)); | 1723 ASSERT_STATUS_SUCCESS(ExportKeyJwk(key, &json)); |
1591 EXPECT_TRUE( | 1724 EXPECT_TRUE(VerifySecretJwk(json, test.jwk_alg, test.key_hex, test.usage)); |
1592 VerifySymmetricJwk(json, test.jwk_alg, test.key_hex, test.usage)); | |
1593 | 1725 |
1594 // Import the JWK-formatted key. | 1726 // Import the JWK-formatted key. |
1595 ASSERT_STATUS_SUCCESS( | 1727 ASSERT_STATUS_SUCCESS( |
1596 ImportKeyJwk(CryptoData(json), test.algorithm, true, test.usage, &key)); | 1728 ImportKeyJwk(CryptoData(json), test.algorithm, true, test.usage, &key)); |
1597 EXPECT_TRUE(key.handle()); | 1729 EXPECT_TRUE(key.handle()); |
1598 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); | 1730 EXPECT_EQ(blink::WebCryptoKeyTypeSecret, key.type()); |
1599 EXPECT_EQ(test.algorithm.id(), key.algorithm().id()); | 1731 EXPECT_EQ(test.algorithm.id(), key.algorithm().id()); |
1600 EXPECT_EQ(true, key.extractable()); | 1732 EXPECT_EQ(true, key.extractable()); |
1601 EXPECT_EQ(test.usage, key.usages()); | 1733 EXPECT_EQ(test.usage, key.usages()); |
1602 | 1734 |
(...skipping 1444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3047 algorithm, | 3179 algorithm, |
3048 CreateAesCbcAlgorithm(std::vector<uint8>(0, 16)), | 3180 CreateAesCbcAlgorithm(std::vector<uint8>(0, 16)), |
3049 true, | 3181 true, |
3050 blink::WebCryptoKeyUsageEncrypt, | 3182 blink::WebCryptoKeyUsageEncrypt, |
3051 &unwrapped_key)); | 3183 &unwrapped_key)); |
3052 } | 3184 } |
3053 | 3185 |
3054 } // namespace webcrypto | 3186 } // namespace webcrypto |
3055 | 3187 |
3056 } // namespace content | 3188 } // namespace content |
OLD | NEW |