| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/trusted_vault/securebox.h" |
| |
| #include <cstdint> |
| #include <map> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <vector> |
| |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace trusted_vault { |
| |
| namespace { |
| |
| using testing::Eq; |
| using testing::IsEmpty; |
| using testing::Ne; |
| using testing::NotNull; |
| using testing::SizeIs; |
| |
| std::vector<uint8_t> StringToBytes(base::StringPiece str) { |
| const uint8_t* raw_data = reinterpret_cast<const uint8_t*>(str.data()); |
| return std::vector<uint8_t>(raw_data, raw_data + str.length()); |
| } |
| |
| class SecureBoxTest : public testing::Test { |
| public: |
| const size_t kPublicKeyLengthInBytes = 65; |
| const size_t kPrivateKeyLengthInBytes = 32; |
| const std::vector<uint8_t> kTestSharedSecret = |
| StringToBytes("TEST_SHARED_SECRET"); |
| const std::vector<uint8_t> kTestHeader = StringToBytes("TEST_HEADER"); |
| const std::vector<uint8_t> kTestPayload = StringToBytes("TEST_PAYLOAD"); |
| }; |
| |
| TEST_F(SecureBoxTest, ShouldExportAndImportPublicKey) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> exported_public_key = |
| key_pair->public_key().ExportToBytes(); |
| EXPECT_THAT(exported_public_key, SizeIs(kPublicKeyLengthInBytes)); |
| |
| std::unique_ptr<SecureBoxPublicKey> imported_public_key = |
| SecureBoxPublicKey::CreateByImport(exported_public_key); |
| EXPECT_THAT(imported_public_key, NotNull()); |
| EXPECT_THAT(imported_public_key->ExportToBytes(), Eq(exported_public_key)); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldExportAndImportPrivateKey) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> exported_private_key = |
| key_pair->private_key().ExportToBytes(); |
| EXPECT_THAT(exported_private_key, SizeIs(kPrivateKeyLengthInBytes)); |
| |
| std::unique_ptr<SecureBoxPrivateKey> imported_private_key = |
| SecureBoxPrivateKey::CreateByImport(exported_private_key); |
| ASSERT_THAT(imported_private_key, NotNull()); |
| EXPECT_THAT(imported_private_key->ExportToBytes(), Eq(exported_private_key)); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldExportPrivateKeyAndImportKeyPair) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> exported_private_key = |
| key_pair->private_key().ExportToBytes(); |
| std::unique_ptr<SecureBoxKeyPair> imported_key_pair = |
| SecureBoxKeyPair::CreateByPrivateKeyImport(exported_private_key); |
| ASSERT_THAT(imported_key_pair, NotNull()); |
| EXPECT_THAT(imported_key_pair->private_key().ExportToBytes(), |
| Eq(exported_private_key)); |
| EXPECT_THAT(imported_key_pair->public_key().ExportToBytes(), |
| Eq(key_pair->public_key().ExportToBytes())); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldGenerateDifferentKeys) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair_a = |
| SecureBoxKeyPair::GenerateRandom(); |
| std::unique_ptr<SecureBoxKeyPair> key_pair_b = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair_a, NotNull()); |
| ASSERT_THAT(key_pair_b, NotNull()); |
| |
| EXPECT_THAT(key_pair_a->public_key().ExportToBytes(), |
| Ne(key_pair_b->public_key().ExportToBytes())); |
| EXPECT_THAT(key_pair_a->private_key().ExportToBytes(), |
| Ne(key_pair_b->private_key().ExportToBytes())); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldEncryptThenDecrypt) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> encrypted = key_pair->public_key().Encrypt( |
| kTestSharedSecret, kTestHeader, kTestPayload); |
| |
| std::optional<std::vector<uint8_t>> decrypted = |
| key_pair->private_key().Decrypt(kTestSharedSecret, kTestHeader, |
| encrypted); |
| ASSERT_THAT(decrypted, Ne(std::nullopt)); |
| EXPECT_THAT(*decrypted, Eq(kTestPayload)); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldEncryptThenDecryptWithEmptySharedSecret) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> encrypted = key_pair->public_key().Encrypt( |
| /*shared_secret=*/base::span<uint8_t>(), kTestHeader, kTestPayload); |
| |
| std::optional<std::vector<uint8_t>> decrypted = |
| key_pair->private_key().Decrypt(/*shared_secret=*/base::span<uint8_t>(), |
| kTestHeader, encrypted); |
| ASSERT_THAT(decrypted, Ne(std::nullopt)); |
| EXPECT_THAT(*decrypted, Eq(kTestPayload)); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldEncryptThenDecryptWithEmptyHeader) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> encrypted = key_pair->public_key().Encrypt( |
| kTestSharedSecret, /*header=*/base::span<uint8_t>(), kTestPayload); |
| |
| std::optional<std::vector<uint8_t>> decrypted = |
| key_pair->private_key().Decrypt( |
| kTestSharedSecret, /*header=*/base::span<uint8_t>(), encrypted); |
| ASSERT_THAT(decrypted, Ne(std::nullopt)); |
| EXPECT_THAT(*decrypted, Eq(kTestPayload)); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldEncryptThenDecryptWithEmptyPayload) { |
| std::unique_ptr<SecureBoxKeyPair> key_pair = |
| SecureBoxKeyPair::GenerateRandom(); |
| ASSERT_THAT(key_pair, NotNull()); |
| |
| std::vector<uint8_t> encrypted = key_pair->public_key().Encrypt( |
| kTestSharedSecret, kTestHeader, /*payload=*/base::span<uint8_t>()); |
| |
| std::optional<std::vector<uint8_t>> decrypted = |
| key_pair->private_key().Decrypt(kTestSharedSecret, kTestHeader, |
| encrypted); |
| ASSERT_THAT(decrypted, Ne(std::nullopt)); |
| EXPECT_THAT(*decrypted, IsEmpty()); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldDecryptTestVectors) { |
| struct TestVector { |
| std::string private_key; |
| std::string shared_secret; |
| std::string header; |
| std::string payload; |
| std::string encrypted_payload; |
| }; |
| |
| const std::vector<TestVector> kTestVectors = { |
| {/*private_key=*/ |
| "49e052293c29b5a50b0013eec9d030ac2ad70a42fe093be084264647cb04e16f", |
| /*shared_secret=*/"efbaea750bb3e8190e8bb0b6df9f1382", |
| /*header=*/"6430055109c9c1853ab0d11e", |
| /*payload=*/ |
| "9f2648ea719ee452bd4f4e9e4b19df75de6c028c8bd3a385", |
| /*encrypted_payload=*/ |
| "020004c4c803b44a189adac641bb04ed0073d352de5e2cfdba935a88e33c5f39f26d4d" |
| "8c7e87e4dd9322491ac401f92d3336560d181629017bd58e4884ea25e44423ec75a889" |
| "b43e4ea48f46864fc863430459dc241d7acef4042255eed8c4fbf9a71cde4ef4d650d5" |
| "72f8a22f67a73751f4a1b4dad0fc"}, |
| {/*private_key=*/ |
| "2818d19cc43b873d94e50d9e4cd07dac8814c0597c6b11866350f1e17aeb87c3", |
| /*shared_secret=*/"6689c9b65a6723c419b818c03340b3ce", |
| /*header=*/"278a6d38639793e2dcdae738", |
| /*payload=*/ |
| "732fd9df1c334a0404db3ca15cc8bb7dd03fa0a6b42f329f", |
| /*encrypted_payload=*/ |
| "020004fe4928f722ec664567779d83b1254786931a2e119019f33b4e0413c0e08b0845" |
| "eb892ce5a9bf54154081fae12808ce40824743df8c70aeb681d5f132d75aecf00eb89a" |
| "4827bf50fb7e11ca57f5f7d9364dbf5d552a513ac705e2159d3bb801e414c16f14a837" |
| "400e31cc85181c2dccd14f558af1"}, |
| {/*private_key=*/ |
| "90149c92a432b9a62d4ebee16e2d358aa7253c3160126f01bb16e3d70523643a", |
| /*shared_secret=*/"6f7171c87b33c444f9f0b0c0956b4977", |
| /*header=*/"6094dd78335861acaf599a29", |
| /*payload=*/ |
| "011360559ef4615e42bfb67de144acf4a10c750a92af6cef", |
| /*encrypted_payload=*/ |
| "02000413765df10f60bc52a5be14e1d7a3c08ab907704574d30993db6d960344e366d3" |
| "42f0e06416ac0baa15a8c7e6adaffece55c4df14cc0f6d8769e3a6dc64a85df4ed5959" |
| "f76b51b123bc8f2572bec12c46138b5362b967850cc6b297fffc20fa639adfc2aebc37" |
| "f96aea37d9f46c42970d44ebe245"}, |
| {/*private_key=*/ |
| "332f062284ca639d4d89047b4518b57e081ad211ce60d2855c162e55adf702b4", |
| /*shared_secret=*/"3f84552779a8e37a8cfed47cde41a14f", |
| /*header=*/"8b21ec8a81b2e79221af61e3", |
| /*payload=*/ |
| "92fd1f0f297b0e60cf1e61d59c7b820f90c027ef74f57a91", |
| /*encrypted_payload=*/ |
| "020004c8d7a52c441f298e9366ccb10a0197db798f56ef1b94c026783b95bd209f48ea" |
| "07f5f783ea7c9617ddd6c1651b7f983f0404fb6a0d59f57035416e7d079479b7197662" |
| "21930955107978660153165b30aea9e6bf9cae23e9fa9156c27e44da6bc254d636fd0b" |
| "3b5e1ec279c7d9d2ed5e6644d638"}}; |
| |
| for (const TestVector& test_vector : kTestVectors) { |
| SCOPED_TRACE("Failure with private key: " + test_vector.private_key); |
| |
| std::vector<uint8_t> private_key_bytes; |
| ASSERT_TRUE( |
| base::HexStringToBytes(test_vector.private_key, &private_key_bytes)); |
| std::unique_ptr<SecureBoxPrivateKey> private_key = |
| SecureBoxPrivateKey::CreateByImport(private_key_bytes); |
| ASSERT_THAT(private_key, NotNull()); |
| |
| std::vector<uint8_t> shared_secret; |
| ASSERT_TRUE( |
| base::HexStringToBytes(test_vector.shared_secret, &shared_secret)); |
| |
| std::vector<uint8_t> header; |
| ASSERT_TRUE(base::HexStringToBytes(test_vector.header, &header)); |
| |
| std::vector<uint8_t> encrypted_payload; |
| ASSERT_TRUE(base::HexStringToBytes(test_vector.encrypted_payload, |
| &encrypted_payload)); |
| |
| std::optional<std::vector<uint8_t>> decrypted_payload = |
| private_key->Decrypt(shared_secret, header, encrypted_payload); |
| ASSERT_THAT(decrypted_payload, Ne(std::nullopt)); |
| |
| std::vector<uint8_t> expected_payload; |
| ASSERT_TRUE(base::HexStringToBytes(test_vector.payload, &expected_payload)); |
| EXPECT_THAT(*decrypted_payload, Eq(expected_payload)); |
| } |
| } |
| |
| TEST_F(SecureBoxTest, ShouldEncryptThenDecryptInSymmetricMode) { |
| std::vector<uint8_t> encrypted = |
| SecureBoxSymmetricEncrypt(kTestSharedSecret, kTestHeader, kTestPayload); |
| |
| std::optional<std::vector<uint8_t>> decrypted = |
| SecureBoxSymmetricDecrypt(kTestSharedSecret, kTestHeader, encrypted); |
| |
| ASSERT_THAT(decrypted, Ne(std::nullopt)); |
| EXPECT_THAT(*decrypted, Eq(kTestPayload)); |
| } |
| |
| TEST_F(SecureBoxTest, ShouldDecryptTestVectorsInSymmetricMode) { |
| struct TestVector { |
| std::string shared_secret; |
| std::string header; |
| std::string payload; |
| std::string encrypted_payload; |
| }; |
| |
| const std::vector<TestVector> kTestVectors = { |
| {/*shared_secret=*/"b9bea7bdda7050128a93132a62df5eb9", |
| /*header=*/"e343638bb4efdf81a7fe8dd4", |
| /*payload=*/"a93b641a36b9cc09a60be2d6fc8b120d76d4e000c191004d", |
| /*encrypted_payload=*/ |
| "0200f673db733138c4f2a688e71cdae2ad48319e11471139e5f0fccad6bfb2b67547cc0" |
| "4cdcb95713d234275f3523bdaca1f8248f91c"}, |
| {/*shared_secret=*/"225ef04a79a0883f5f8c069a62484ea6", |
| /*header=*/"bead3e2a4a6420715a46a0d3", |
| /*payload=*/"a92998b81b6f6bafb5f477612365cdbc2cc2887901dc8738", |
| /*encrypted_payload=*/ |
| "02004e25b46801af126c8dc4d022a2274f4f99286f7bb11b20beb591248794b7547b48b" |
| "60653afe4c2f0783f4d8d4e2d50a17c9a761f"}, |
| {/*shared_secret=*/"71e6adfc21920c3202a108db88e82ea9", |
| /*header=*/"64a990cc5776e78224c7386a", |
| /*payload=*/"8bff0dd38037924864752daf872aeb76c46801e4ce8066ad", |
| /*encrypted_payload=*/ |
| "0200074a4bb7fa9fa5fd3913b34c208351fba7f56d16fa32f23bfd3d93e9304a5ea50e5" |
| "f610bf5efe5d572b993859b6e6e0a4baeb134"}, |
| {/*shared_secret=*/"777b4d6df6f20045dd5658f777029a72", |
| /*header=*/"993c498d5b4da8bc5b525f04", |
| /*payload=*/"7614145015b19eb161dba85a32eddab6156fc9d49a3ee73c", |
| /*encrypted_payload=*/ |
| "0200f27b526bb6676548ddb05d3903703eb134346414443f610d8d2c0e46109129cc9f7" |
| "2daa600c7686f1b1991790e905ad79f58a6c3"}}; |
| |
| for (const TestVector& test_vector : kTestVectors) { |
| SCOPED_TRACE("Failure with shared secret: " + test_vector.shared_secret); |
| |
| std::vector<uint8_t> shared_secret; |
| ASSERT_TRUE( |
| base::HexStringToBytes(test_vector.shared_secret, &shared_secret)); |
| |
| std::vector<uint8_t> header; |
| ASSERT_TRUE(base::HexStringToBytes(test_vector.header, &header)); |
| |
| std::vector<uint8_t> encrypted_payload; |
| ASSERT_TRUE(base::HexStringToBytes(test_vector.encrypted_payload, |
| &encrypted_payload)); |
| |
| std::optional<std::vector<uint8_t>> decrypted_payload = |
| SecureBoxSymmetricDecrypt(shared_secret, header, encrypted_payload); |
| ASSERT_THAT(decrypted_payload, Ne(std::nullopt)); |
| |
| std::vector<uint8_t> expected_payload; |
| ASSERT_TRUE(base::HexStringToBytes(test_vector.payload, &expected_payload)); |
| EXPECT_THAT(*decrypted_payload, Eq(expected_payload)); |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace trusted_vault |