1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 ///////////////////////////////////////////////////////////////////////////////
16
17 #include "walkthrough/write_keyset.h"
18
19 #include <memory>
20 #include <ostream>
21 #include <sstream>
22 #include <string>
23 #include <utility>
24
25 #include "gmock/gmock.h"
26 #include "absl/memory/memory.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/string_view.h"
29 #include "tink/aead.h"
30 #include "tink/aead/aead_config.h"
31 #include "walkthrough/load_cleartext_keyset.h"
32 #include "walkthrough/load_encrypted_keyset.h"
33 #include "walkthrough/test_util.h"
34 #include "tink/kms_clients.h"
35 #include "tink/util/status.h"
36 #include "tink/util/statusor.h"
37 #include "tink/util/test_matchers.h"
38
39 namespace tink_walkthrough {
40 namespace {
41
42 constexpr absl::string_view kSerializedMasterKeyKeyset = R"json({
43 "key": [
44 {
45 "keyData": {
46 "keyMaterialType": "SYMMETRIC",
47 "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
48 "value": "GiBWyUfGgYk3RTRhj/LIUzSudIWlyjCftCOypTr0jCNSLg=="
49 },
50 "keyId": 294406504,
51 "outputPrefixType": "TINK",
52 "status": "ENABLED"
53 }
54 ],
55 "primaryKeyId": 294406504
56 })json";
57
58 constexpr absl::string_view kSerializedKeysetToEncrypt = R"json({
59 "key": [
60 {
61 "keyData": {
62 "keyMaterialType": "SYMMETRIC",
63 "typeUrl": "type.googleapis.com/google.crypto.tink.AesGcmKey",
64 "value": "GhD+9l0RANZjzZEZ8PDp7LRW"
65 },
66 "keyId": 1931667682,
67 "outputPrefixType": "TINK",
68 "status": "ENABLED"
69 }
70 ],
71 "primaryKeyId": 1931667682
72 })json";
73
74 using ::crypto::tink::Aead;
75 using ::crypto::tink::KeysetHandle;
76 using ::crypto::tink::test::IsOk;
77 using ::crypto::tink::test::IsOkAndHolds;
78 using ::crypto::tink::test::StatusIs;
79 using ::crypto::tink::util::Status;
80 using ::crypto::tink::util::StatusOr;
81 using ::testing::Not;
82
InitFakeKms()83 Status InitFakeKms() {
84 static Status* status = new Status([]() {
85 Status status = crypto::tink::AeadConfig::Register();
86 if (!status.ok()) {
87 return status;
88 }
89 return crypto::tink::KmsClients::Add(
90 absl::make_unique<FakeKmsClient>(kSerializedMasterKeyKeyset));
91 }());
92 return *status;
93 }
94
95 class WriteKeysetTest : public testing::Test {
96 protected:
SetUp()97 void SetUp() override {
98 ASSERT_THAT(InitFakeKms(), IsOk());
99 StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle_to_encrypt =
100 LoadKeyset(kSerializedKeysetToEncrypt);
101 ASSERT_THAT(keyset_handle_to_encrypt, IsOk());
102 keyset_handle_to_encrypt_ = std::move(*keyset_handle_to_encrypt);
103 }
104
105 std::unique_ptr<KeysetHandle> keyset_handle_to_encrypt_;
106 };
107
TEST_F(WriteKeysetTest,WriteEncryptedKeysetFailsWithNullOutputStream)108 TEST_F(WriteKeysetTest, WriteEncryptedKeysetFailsWithNullOutputStream) {
109 EXPECT_THAT(WriteEncryptedKeyset(*keyset_handle_to_encrypt_, nullptr,
110 /*master_kms_key_uri=*/"fake://some_key"),
111 StatusIs(absl::StatusCode::kInvalidArgument));
112 }
113
TEST_F(WriteKeysetTest,WriteEncryptedKeysetFailsWhenStreamFails)114 TEST_F(WriteKeysetTest, WriteEncryptedKeysetFailsWhenStreamFails) {
115 auto output_stream = absl::make_unique<std::ostream>(nullptr);
116 EXPECT_THAT(
117 WriteEncryptedKeyset(*keyset_handle_to_encrypt_, std::move(output_stream),
118 /*master_kms_key_uri=*/"fake://some_key"),
119 Not(IsOk()));
120 }
121
TEST_F(WriteKeysetTest,WriteEncryptedKeysetFailsNoKmsAvailable)122 TEST_F(WriteKeysetTest, WriteEncryptedKeysetFailsNoKmsAvailable) {
123 std::stringbuf buffer;
124 auto output_stream = absl::make_unique<std::ostream>(&buffer);
125 EXPECT_THAT(WriteEncryptedKeyset(
126 *keyset_handle_to_encrypt_, std::move(output_stream),
127 /*master_kms_key_uri=*/"does_not_exist://does_not_exist"),
128 StatusIs(absl::StatusCode::kNotFound));
129 }
130
TEST_F(WriteKeysetTest,WriteEncryptedKeysetWithValidInputs)131 TEST_F(WriteKeysetTest, WriteEncryptedKeysetWithValidInputs) {
132 std::stringbuf buffer;
133 auto output_stream = absl::make_unique<std::ostream>(&buffer);
134 constexpr absl::string_view master_kms_key_uri = "fake://some_key";
135 ASSERT_THAT(
136 WriteEncryptedKeyset(*keyset_handle_to_encrypt_, std::move(output_stream),
137 master_kms_key_uri),
138 IsOk());
139 StatusOr<std::unique_ptr<Aead>> expected_aead =
140 keyset_handle_to_encrypt_->GetPrimitive<Aead>();
141 ASSERT_THAT(expected_aead, IsOk());
142 constexpr absl::string_view associated_data = "Some associated data";
143 constexpr absl::string_view plaintext = "Some plaintext";
144
145 StatusOr<std::string> ciphertext =
146 (*expected_aead)->Encrypt(plaintext, associated_data);
147 ASSERT_THAT(ciphertext, IsOk());
148
149 // Make sure the encrypted keyset was written correctly by loading it and
150 // trying to decrypt ciphertext.
151 StatusOr<std::unique_ptr<KeysetHandle>> loaded_keyset =
152 LoadKeyset(buffer.str(), master_kms_key_uri);
153 ASSERT_THAT(loaded_keyset, IsOk());
154 StatusOr<std::unique_ptr<Aead>> loaded_keyset_aead =
155 (*loaded_keyset)->GetPrimitive<Aead>();
156 ASSERT_THAT(loaded_keyset_aead, IsOk());
157 EXPECT_THAT((*loaded_keyset_aead)->Decrypt(*ciphertext, associated_data),
158 IsOkAndHolds(plaintext));
159 }
160
161 } // namespace
162 } // namespace tink_walkthrough
163