Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(101)

Side by Side Diff: content/child/webcrypto/shared_crypto_unittest.cc

Issue 205913002: [webcrypto] Add JWK RSA public key export for NSS. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « content/child/webcrypto/platform_crypto_openssl.cc ('k') | content/child/webcrypto/webcrypto_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698