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 <memory>
18 #include <string>
19 #include <tuple>
20
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "tink/chunked_mac.h"
24 #include "tink/keyset_handle.h"
25 #include "tink/mac.h"
26 #include "tink/mac/mac_config.h"
27 #include "tink/mac/mac_key_templates.h"
28 #include "tink/util/status.h"
29 #include "tink/util/statusor.h"
30 #include "tink/util/test_matchers.h"
31 #include "proto/tink.pb.h"
32
33 namespace crypto {
34 namespace tink {
35 namespace internal {
36 namespace {
37
38 using ::crypto::tink::MacKeyTemplates;
39 using ::crypto::tink::test::IsOk;
40 using ::crypto::tink::test::StatusIs;
41 using ::crypto::tink::util::StatusOr;
42 using ::google::crypto::tink::KeyTemplate;
43 using ::google::crypto::tink::OutputPrefixType;
44 using ::testing::Eq;
45 using ::testing::Combine;
46 using ::testing::Values;
47
48 class ChunkedMacCompatibilityTest
49 : public testing::TestWithParam<std::tuple<KeyTemplate, OutputPrefixType>> {
50 };
51
52 INSTANTIATE_TEST_SUITE_P(
53 ChunkedMacCompatibilityTestSuite, ChunkedMacCompatibilityTest,
54 Combine(Values(MacKeyTemplates::AesCmac(), MacKeyTemplates::HmacSha256()),
55 Values(OutputPrefixType::LEGACY, OutputPrefixType::RAW,
56 OutputPrefixType::CRUNCHY, OutputPrefixType::TINK)));
57
TEST_P(ChunkedMacCompatibilityTest,ComputeAndVerify)58 TEST_P(ChunkedMacCompatibilityTest, ComputeAndVerify) {
59 KeyTemplate key_template;
60 OutputPrefixType output_prefix_type;
61 std::tie(key_template, output_prefix_type) = GetParam();
62 key_template.set_output_prefix_type(output_prefix_type);
63
64 ASSERT_THAT(MacConfig::Register(), IsOk());
65
66 util::StatusOr<std::unique_ptr<KeysetHandle>> key =
67 KeysetHandle::GenerateNew(key_template);
68 ASSERT_THAT(key, IsOk());
69
70 util::StatusOr<std::unique_ptr<Mac>> mac = (*key)->GetPrimitive<Mac>();
71 ASSERT_THAT(mac, IsOk());
72
73 util::StatusOr<std::unique_ptr<ChunkedMac>> chunked_mac =
74 (*key)->GetPrimitive<ChunkedMac>();
75 ASSERT_THAT(chunked_mac, IsOk());
76
77 // Compute tag with chunked MAC.
78 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
79 (*chunked_mac)->CreateComputation();
80 ASSERT_THAT(computation, IsOk());
81 ASSERT_THAT((*computation)->Update("abc"), IsOk());
82 ASSERT_THAT((*computation)->Update("xyz"), IsOk());
83 util::StatusOr<std::string> chunked_tag = (*computation)->ComputeMac();
84 ASSERT_THAT(chunked_tag, IsOk());
85
86 // Verify tag with regular MAC.
87 ASSERT_THAT((*mac)->VerifyMac(*chunked_tag, "abcxyz"), IsOk());
88
89 // Compute tag with regular MAC.
90 util::StatusOr<std::string> tag = (*mac)->ComputeMac("abcxyz");
91 ASSERT_THAT(tag, IsOk());
92 ASSERT_THAT(*tag, Eq(*chunked_tag));
93
94 // Verify tag with chunked MAC.
95 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
96 (*chunked_mac)->CreateVerification(*tag);
97 ASSERT_THAT(verification, IsOk());
98 ASSERT_THAT((*verification)->Update("abc"), IsOk());
99 ASSERT_THAT((*verification)->Update("xyz"), IsOk());
100 EXPECT_THAT((*verification)->VerifyMac(), IsOk());
101 }
102
TEST(ChunkedMacSlicingTest,DifferentChunkSizes)103 TEST(ChunkedMacSlicingTest, DifferentChunkSizes) {
104 ASSERT_THAT(MacConfig::Register(), IsOk());
105
106 util::StatusOr<std::unique_ptr<KeysetHandle>> key =
107 KeysetHandle::GenerateNew(MacKeyTemplates::HmacSha256());
108 ASSERT_THAT(key, IsOk());
109
110 util::StatusOr<std::unique_ptr<ChunkedMac>> chunked_mac =
111 (*key)->GetPrimitive<ChunkedMac>();
112 ASSERT_THAT(chunked_mac, IsOk());
113
114 // Update three input chunks.
115 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
116 (*chunked_mac)->CreateComputation();
117 ASSERT_THAT(computation, IsOk());
118 ASSERT_THAT((*computation)->Update("ab"), IsOk());
119 ASSERT_THAT((*computation)->Update("cx"), IsOk());
120 ASSERT_THAT((*computation)->Update("yz"), IsOk());
121 util::StatusOr<std::string> tag = (*computation)->ComputeMac();
122 ASSERT_THAT(tag, IsOk());
123
124 // Update two input chunks.
125 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
126 (*chunked_mac)->CreateVerification(*tag);
127 ASSERT_THAT(verification, IsOk());
128 ASSERT_THAT((*verification)->Update("abc"), IsOk());
129 ASSERT_THAT((*verification)->Update("xyz"), IsOk());
130 EXPECT_THAT((*verification)->VerifyMac(), IsOk());
131 }
132
TEST(ChunkedMacTest,VerifyPrefixFails)133 TEST(ChunkedMacTest, VerifyPrefixFails) {
134 ASSERT_THAT(MacConfig::Register(), IsOk());
135
136 util::StatusOr<std::unique_ptr<KeysetHandle>> key =
137 KeysetHandle::GenerateNew(MacKeyTemplates::HmacSha256());
138 ASSERT_THAT(key, IsOk());
139
140 util::StatusOr<std::unique_ptr<ChunkedMac>> chunked_mac =
141 (*key)->GetPrimitive<ChunkedMac>();
142 ASSERT_THAT(chunked_mac, IsOk());
143
144 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
145 (*chunked_mac)->CreateComputation();
146 ASSERT_THAT(computation, IsOk());
147 ASSERT_THAT((*computation)->Update("abcxyz"), IsOk());
148 util::StatusOr<std::string> tag = (*computation)->ComputeMac();
149 ASSERT_THAT(tag, IsOk());
150
151 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
152 (*chunked_mac)->CreateVerification(*tag);
153 ASSERT_THAT(verification, IsOk());
154 ASSERT_THAT((*verification)->Update("abc"), IsOk());
155 EXPECT_THAT((*verification)->VerifyMac(),
156 StatusIs(absl::StatusCode::kUnknown));
157 }
158
TEST(ChunkedMacTest,UpdateWrongOrderFails)159 TEST(ChunkedMacTest, UpdateWrongOrderFails) {
160 ASSERT_THAT(MacConfig::Register(), IsOk());
161
162 util::StatusOr<std::unique_ptr<KeysetHandle>> key =
163 KeysetHandle::GenerateNew(MacKeyTemplates::HmacSha256());
164 ASSERT_THAT(key, IsOk());
165
166 util::StatusOr<std::unique_ptr<ChunkedMac>> chunked_mac =
167 (*key)->GetPrimitive<ChunkedMac>();
168 ASSERT_THAT(chunked_mac, IsOk());
169
170 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
171 (*chunked_mac)->CreateComputation();
172 ASSERT_THAT(computation, IsOk());
173 ASSERT_THAT((*computation)->Update("abc"), IsOk());
174 ASSERT_THAT((*computation)->Update("xyz"), IsOk());
175 util::StatusOr<std::string> tag = (*computation)->ComputeMac();
176 ASSERT_THAT(tag, IsOk());
177
178 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
179 (*chunked_mac)->CreateVerification(*tag);
180 ASSERT_THAT(verification, IsOk());
181 ASSERT_THAT((*verification)->Update("xyz"), IsOk());
182 ASSERT_THAT((*verification)->Update("abc"), IsOk());
183 EXPECT_THAT((*verification)->VerifyMac(),
184 StatusIs(absl::StatusCode::kUnknown));
185 }
186
TEST(ChunkedMacTest,OperationsFailAfterComputeVerifyMac)187 TEST(ChunkedMacTest, OperationsFailAfterComputeVerifyMac) {
188 ASSERT_THAT(MacConfig::Register(), IsOk());
189
190 util::StatusOr<std::unique_ptr<KeysetHandle>> key =
191 KeysetHandle::GenerateNew(MacKeyTemplates::HmacSha256());
192 ASSERT_THAT(key, IsOk());
193
194 util::StatusOr<std::unique_ptr<ChunkedMac>> chunked_mac =
195 (*key)->GetPrimitive<ChunkedMac>();
196 ASSERT_THAT(chunked_mac, IsOk());
197
198 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
199 (*chunked_mac)->CreateComputation();
200 ASSERT_THAT(computation, IsOk());
201 ASSERT_THAT((*computation)->Update("abc"), IsOk());
202 ASSERT_THAT((*computation)->Update("xyz"), IsOk());
203 util::StatusOr<std::string> tag = (*computation)->ComputeMac();
204 ASSERT_THAT(tag, IsOk());
205
206 // ChunkedMacComputation has already been finalized.
207 EXPECT_THAT((*computation)->Update("toolate"),
208 StatusIs(absl::StatusCode::kFailedPrecondition));
209 EXPECT_THAT((*computation)->ComputeMac().status(),
210 StatusIs(absl::StatusCode::kFailedPrecondition));
211
212 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
213 (*chunked_mac)->CreateVerification(*tag);
214 ASSERT_THAT(verification, IsOk());
215 ASSERT_THAT((*verification)->Update("abc"), IsOk());
216 ASSERT_THAT((*verification)->Update("xyz"), IsOk());
217 EXPECT_THAT((*verification)->VerifyMac(), IsOk());
218
219 // ChunkedMacVerification has already been finalized.
220 EXPECT_THAT((*verification)->Update("toolate"),
221 StatusIs(absl::StatusCode::kUnknown));
222 EXPECT_THAT((*verification)->VerifyMac(),
223 StatusIs(absl::StatusCode::kUnknown));
224 }
225
226 } // namespace
227 } // namespace internal
228 } // namespace tink
229 } // namespace crypto
230