blob: 88204984a125f0b0bbb45ec627b5b70f37a08d43 [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 <limits.h>
#include <stddef.h>
#include <stdint.h>
#include <utility>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/values.h"
#include "components/webcrypto/algorithm_dispatch.h"
#include "components/webcrypto/algorithms/test_helpers.h"
#include "components/webcrypto/crypto_data.h"
#include "components/webcrypto/status.h"
#include "third_party/WebKit/public/platform/WebCryptoAlgorithmParams.h"
#include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h"
namespace webcrypto {
namespace {
// Creates an AES-CBC algorithm.
blink::WebCryptoAlgorithm CreateAesCbcAlgorithm(
const std::vector<uint8_t>& iv) {
return blink::WebCryptoAlgorithm::AdoptParamsAndCreate(
blink::kWebCryptoAlgorithmIdAesCbc, new blink::WebCryptoAesCbcParams(iv));
}
blink::WebCryptoAlgorithm CreateAesCbcKeyGenAlgorithm(
unsigned short key_length_bits) {
return CreateAesKeyGenAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc,
key_length_bits);
}
blink::WebCryptoKey GetTestAesCbcKey() {
const std::string key_hex = "2b7e151628aed2a6abf7158809cf4f3c";
blink::WebCryptoKey key = ImportSecretKeyFromRaw(
HexStringToBytes(key_hex),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt);
// Verify exported raw key is identical to the imported data
std::vector<uint8_t> raw_key;
EXPECT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatRaw, key, &raw_key));
EXPECT_BYTES_EQ_HEX(key_hex, raw_key);
return key;
}
class WebCryptoAesCbcTest : public WebCryptoTestBase {};
TEST_F(WebCryptoAesCbcTest, InputTooLarge) {
std::vector<uint8_t> output;
std::vector<uint8_t> iv(16);
// Give an input that is too large. It would cause integer overflow when
// narrowing the ciphertext size to an int, since OpenSSL operates on signed
// int lengths NOT unsigned.
//
// 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_F(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::kWebCryptoKeyFormatSpki, GetTestAesCbcKey(), &output));
EXPECT_EQ(
Status::ErrorUnsupportedExportKeyFormat(),
ExportKey(blink::kWebCryptoKeyFormatPkcs8, GetTestAesCbcKey(), &output));
}
// Tests importing of keys (in a variety of formats), errors during import,
// encryption, and decryption, using known answers.
TEST_F(WebCryptoAesCbcTest, KnownAnswerEncryptDecrypt) {
std::unique_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));
blink::WebCryptoKeyFormat key_format = GetKeyFormatFromJsonTestCase(test);
std::vector<uint8_t> key_data =
GetKeyDataFromJsonTestCase(test, key_format);
std::string import_error = "Success";
test->GetString("import_error", &import_error);
// Import the key.
blink::WebCryptoKey key;
Status status = ImportKey(
key_format, CryptoData(key_data),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
&key);
ASSERT_EQ(import_error, StatusToString(status));
if (status.IsError())
continue;
// Test encryption.
if (test->HasKey("plain_text")) {
std::vector<uint8_t> test_plain_text =
GetBytesFromHexString(test, "plain_text");
std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv");
std::string encrypt_error = "Success";
test->GetString("encrypt_error", &encrypt_error);
std::vector<uint8_t> output;
status = Encrypt(CreateAesCbcAlgorithm(test_iv), key,
CryptoData(test_plain_text), &output);
ASSERT_EQ(encrypt_error, StatusToString(status));
if (status.IsError())
continue;
std::vector<uint8_t> test_cipher_text =
GetBytesFromHexString(test, "cipher_text");
EXPECT_BYTES_EQ(test_cipher_text, output);
}
// Test decryption.
if (test->HasKey("cipher_text")) {
std::vector<uint8_t> test_cipher_text =
GetBytesFromHexString(test, "cipher_text");
std::vector<uint8_t> test_iv = GetBytesFromHexString(test, "iv");
std::string decrypt_error = "Success";
test->GetString("decrypt_error", &decrypt_error);
std::vector<uint8_t> output;
status = Decrypt(CreateAesCbcAlgorithm(test_iv), key,
CryptoData(test_cipher_text), &output);
ASSERT_EQ(decrypt_error, StatusToString(status));
if (status.IsError())
continue;
std::vector<uint8_t> test_plain_text =
GetBytesFromHexString(test, "plain_text");
EXPECT_BYTES_EQ(test_plain_text, output);
}
}
}
// TODO(eroman): Do this same test for AES-GCM, AES-KW, AES-CTR ?
TEST_F(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,
blink::kWebCryptoKeyUsageEncrypt, &key));
EXPECT_TRUE(key.Handle());
EXPECT_EQ(blink::kWebCryptoKeyTypeSecret, key.GetType());
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatRaw, 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_F(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::ErrorGenerateAesKeyLength(),
GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(kKeyLen[i]), true,
blink::kWebCryptoKeyUsageEncrypt, &key));
}
}
TEST_F(WebCryptoAesCbcTest, ImportKeyEmptyUsage) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
ImportKey(blink::kWebCryptoKeyFormatRaw,
CryptoData(std::vector<uint8_t>(16)),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
0, &key));
}
// If key_ops is specified but empty, no key usages are allowed for the key.
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkEmptyKeyOps) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetBoolean("ext", false);
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
dict.Set("key_ops", base::MakeUnique<base::ListValue>());
// The JWK does not contain encrypt usages.
EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
// The JWK does not contain sign usage (nor is it applicable).
EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageSign, &key));
}
// If key_ops is missing, then any key usages can be specified.
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkNoKeyOps) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
EXPECT_EQ(blink::kWebCryptoKeyUsageEncrypt, key.Usages());
// The JWK does not contain sign usage (nor is it applicable).
EXPECT_EQ(Status::ErrorCreateKeyBadUsages(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageVerify, &key));
}
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsEncryptDecrypt) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
base::ListValue* key_ops =
dict.SetList("key_ops", base::MakeUnique<base::ListValue>());
key_ops->AppendString("encrypt");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
EXPECT_EQ(blink::kWebCryptoKeyUsageEncrypt, key.Usages());
key_ops->AppendString("decrypt");
EXPECT_EQ(Status::Success(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageDecrypt, &key));
EXPECT_EQ(blink::kWebCryptoKeyUsageDecrypt, key.Usages());
EXPECT_EQ(
Status::Success(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageEncrypt,
&key));
EXPECT_EQ(blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
key.Usages());
}
// Test failure if input usage is NOT a strict subset of the JWK usage.
TEST_F(WebCryptoAesCbcTest, ImportKeyJwkKeyOpsNotSuperset) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
auto key_ops = base::MakeUnique<base::ListValue>();
key_ops->AppendString("encrypt");
dict.Set("key_ops", std::move(key_ops));
EXPECT_EQ(
Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
&key));
}
TEST_F(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::kWebCryptoAlgorithmIdAesCbc), false,
blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageEncrypt |
blink::kWebCryptoKeyUsageWrapKey |
blink::kWebCryptoKeyUsageUnwrapKey,
&key));
EXPECT_EQ(
blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageEncrypt |
blink::kWebCryptoKeyUsageWrapKey | blink::kWebCryptoKeyUsageUnwrapKey,
key.Usages());
}
TEST_F(WebCryptoAesCbcTest, ImportJwkInvalidJson) {
blink::WebCryptoKey key;
// Fail on empty JSON.
EXPECT_EQ(
Status::ErrorJwkNotDictionary(),
ImportKey(blink::kWebCryptoKeyFormatJwk, CryptoData(MakeJsonVector("")),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), false,
blink::kWebCryptoKeyUsageEncrypt, &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::kWebCryptoKeyFormatJwk, CryptoData(bad_json_vec),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
}
// Fail on inconsistent key_ops - asking for "encrypt" however JWK contains
// only "foo".
TEST_F(WebCryptoAesCbcTest, ImportJwkKeyOpsLacksUsages) {
blink::WebCryptoKey key;
base::DictionaryValue dict;
dict.SetString("kty", "oct");
dict.SetString("k", "GADWrMRHwQfoNaXU5fZvTg");
auto key_ops = base::MakeUnique<base::ListValue>();
key_ops->AppendString("foo");
dict.Set("key_ops", std::move(key_ops));
EXPECT_EQ(Status::ErrorJwkKeyopsInconsistent(),
ImportKeyJwkFromDict(
dict, CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc),
false, blink::kWebCryptoKeyUsageEncrypt, &key));
}
TEST_F(WebCryptoAesCbcTest, ImportExportJwk) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);
// AES-CBC 128
ImportExportJwkSymmetricKey(
128, algorithm,
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt,
"A128CBC");
// AES-CBC 256
ImportExportJwkSymmetricKey(256, algorithm, blink::kWebCryptoKeyUsageDecrypt,
"A256CBC");
// Large usage value
ImportExportJwkSymmetricKey(
256, algorithm,
blink::kWebCryptoKeyUsageEncrypt | blink::kWebCryptoKeyUsageDecrypt |
blink::kWebCryptoKeyUsageWrapKey | blink::kWebCryptoKeyUsageUnwrapKey,
"A256CBC");
}
// 192-bit AES is intentionally unsupported (http://crbug.com/533699).
TEST_F(WebCryptoAesCbcTest, GenerateAesCbc192) {
blink::WebCryptoKey key;
Status status = GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(192), true,
blink::kWebCryptoKeyUsageEncrypt, &key);
ASSERT_EQ(Status::ErrorAes192BitUnsupported(), status);
}
// 192-bit AES is intentionally unsupported (http://crbug.com/533699).
TEST_F(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::kWebCryptoAlgorithmIdAesKw),
blink::kWebCryptoKeyUsageUnwrapKey);
blink::WebCryptoKey unwrapped_key;
ASSERT_EQ(Status::ErrorAes192BitUnsupported(),
UnwrapKey(blink::kWebCryptoKeyFormatRaw, CryptoData(wrapped_key),
wrapping_key,
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesKw),
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc), true,
blink::kWebCryptoKeyUsageEncrypt, &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_F(WebCryptoAesCbcTest, ImportKeyBadUsage_Raw) {
const blink::WebCryptoAlgorithm algorithm =
CreateAlgorithm(blink::kWebCryptoAlgorithmIdAesCbc);
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::kWebCryptoKeyUsageSign,
blink::kWebCryptoKeyUsageSign | blink::kWebCryptoKeyUsageDecrypt,
blink::kWebCryptoKeyUsageDeriveBits,
blink::kWebCryptoKeyUsageUnwrapKey | blink::kWebCryptoKeyUsageVerify,
};
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::kWebCryptoKeyFormatRaw, 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_F(WebCryptoAesCbcTest, GenerateKeyBadUsages) {
blink::WebCryptoKeyUsageMask bad_usages[] = {
blink::kWebCryptoKeyUsageSign, blink::kWebCryptoKeyUsageVerify,
blink::kWebCryptoKeyUsageDecrypt | blink::kWebCryptoKeyUsageVerify,
};
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 with no usages.
TEST_F(WebCryptoAesCbcTest, GenerateKeyEmptyUsages) {
blink::WebCryptoKey key;
ASSERT_EQ(Status::ErrorCreateKeyEmptyUsages(),
GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true, 0, &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_F(WebCryptoAesCbcTest, WrapUnwrapRoundtripSpkiPkcs8) {
// Generate the wrapping key.
blink::WebCryptoKey wrapping_key;
ASSERT_EQ(Status::Success(),
GenerateSecretKey(CreateAesCbcKeyGenAlgorithm(128), true,
blink::kWebCryptoKeyUsageWrapKey |
blink::kWebCryptoKeyUsageUnwrapKey,
&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::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256,
modulus_length, public_exponent),
true, blink::kWebCryptoKeyUsageSign, &public_key,
&private_key));
// Export key pair as SPKI + PKCS8
std::vector<uint8_t> public_key_spki;
ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatSpki,
public_key, &public_key_spki));
std::vector<uint8_t> private_key_pkcs8;
ASSERT_EQ(Status::Success(), ExportKey(blink::kWebCryptoKeyFormatPkcs8,
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::kWebCryptoKeyFormatSpki, public_key, wrapping_key,
wrap_algorithm, &wrapped_public_key));
std::vector<uint8_t> wrapped_private_key;
ASSERT_EQ(Status::Success(),
WrapKey(blink::kWebCryptoKeyFormatPkcs8, private_key, wrapping_key,
wrap_algorithm, &wrapped_private_key));
// Unwrap the key pair.
blink::WebCryptoAlgorithm rsa_import_algorithm =
CreateRsaHashedImportAlgorithm(
blink::kWebCryptoAlgorithmIdRsaSsaPkcs1v1_5,
blink::kWebCryptoAlgorithmIdSha256);
blink::WebCryptoKey unwrapped_public_key;
ASSERT_EQ(
Status::Success(),
UnwrapKey(blink::kWebCryptoKeyFormatSpki, CryptoData(wrapped_public_key),
wrapping_key, wrap_algorithm, rsa_import_algorithm, true,
blink::kWebCryptoKeyUsageVerify, &unwrapped_public_key));
blink::WebCryptoKey unwrapped_private_key;
ASSERT_EQ(Status::Success(),
UnwrapKey(blink::kWebCryptoKeyFormatPkcs8,
CryptoData(wrapped_private_key), wrapping_key,
wrap_algorithm, rsa_import_algorithm, true,
blink::kWebCryptoKeyUsageSign, &unwrapped_private_key));
// Export unwrapped key pair as SPKI + PKCS8
std::vector<uint8_t> unwrapped_public_key_spki;
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatSpki, unwrapped_public_key,
&unwrapped_public_key_spki));
std::vector<uint8_t> unwrapped_private_key_pkcs8;
ASSERT_EQ(Status::Success(),
ExportKey(blink::kWebCryptoKeyFormatPkcs8, 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