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 "tink/mac/internal/chunked_mac_wrapper.h"
18
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "tink/chunked_mac.h"
27 #include "tink/crypto_format.h"
28 #include "tink/internal/util.h"
29 #include "tink/primitive_set.h"
30 #include "tink/util/status.h"
31 #include "tink/util/statusor.h"
32 #include "proto/tink.pb.h"
33
34 namespace crypto {
35 namespace tink {
36 namespace internal {
37 namespace {
38
39 using ::google::crypto::tink::OutputPrefixType;
40
41 class ChunkedMacComputationSetWrapper : public ChunkedMacComputation {
42 public:
ChunkedMacComputationSetWrapper(std::unique_ptr<ChunkedMacComputation> computation,absl::string_view tag_prefix,OutputPrefixType output_prefix_type)43 explicit ChunkedMacComputationSetWrapper(
44 std::unique_ptr<ChunkedMacComputation> computation,
45 absl::string_view tag_prefix, OutputPrefixType output_prefix_type)
46 : computation_(std::move(computation)),
47 tag_prefix_(tag_prefix),
48 output_prefix_type_(output_prefix_type) {}
49
50 util::Status Update(absl::string_view data) override;
51
52 util::StatusOr<std::string> ComputeMac() override;
53
54 private:
55 const std::unique_ptr<ChunkedMacComputation> computation_;
56 const std::string tag_prefix_;
57 const OutputPrefixType output_prefix_type_;
58 };
59
Update(absl::string_view data)60 util::Status ChunkedMacComputationSetWrapper::Update(absl::string_view data) {
61 return computation_->Update(data);
62 }
63
ComputeMac()64 util::StatusOr<std::string> ChunkedMacComputationSetWrapper::ComputeMac() {
65 if (output_prefix_type_ == OutputPrefixType::LEGACY) {
66 util::Status append_status = computation_->Update(std::string("\x00", 1));
67 if (!append_status.ok()) return append_status;
68 }
69 util::StatusOr<std::string> raw_tag = computation_->ComputeMac();
70 if (!raw_tag.ok()) return raw_tag.status();
71 return absl::StrCat(tag_prefix_, *raw_tag);
72 }
73
74 class ChunkedMacVerificationWithPrefixType : public ChunkedMacVerification {
75 public:
ChunkedMacVerificationWithPrefixType(std::unique_ptr<ChunkedMacVerification> verification,OutputPrefixType output_prefix_type)76 explicit ChunkedMacVerificationWithPrefixType(
77 std::unique_ptr<ChunkedMacVerification> verification,
78 OutputPrefixType output_prefix_type)
79 : verification_(std::move(verification)),
80 output_prefix_type_(output_prefix_type) {}
81
82 util::Status Update(absl::string_view data) override;
83
84 util::Status VerifyMac() override;
85
86 private:
87 const std::unique_ptr<ChunkedMacVerification> verification_;
88 const OutputPrefixType output_prefix_type_;
89 };
90
Update(absl::string_view data)91 util::Status ChunkedMacVerificationWithPrefixType::Update(
92 absl::string_view data) {
93 return verification_->Update(data);
94 }
95
VerifyMac()96 util::Status ChunkedMacVerificationWithPrefixType::VerifyMac() {
97 if (output_prefix_type_ == OutputPrefixType::LEGACY) {
98 util::Status append_status = verification_->Update(std::string("\x00", 1));
99 if (!append_status.ok()) return append_status;
100 }
101 return verification_->VerifyMac();
102 }
103
104 class ChunkedMacVerificationSetWrapper : public ChunkedMacVerification {
105 public:
ChunkedMacVerificationSetWrapper(std::unique_ptr<std::vector<std::unique_ptr<ChunkedMacVerificationWithPrefixType>>> verifications)106 explicit ChunkedMacVerificationSetWrapper(
107 std::unique_ptr<
108 std::vector<std::unique_ptr<ChunkedMacVerificationWithPrefixType>>>
109 verifications)
110 : verifications_(std::move(verifications)) {}
111
112 util::Status Update(absl::string_view data) override;
113
114 util::Status VerifyMac() override;
115
116 private:
117 const std::unique_ptr<
118 std::vector<std::unique_ptr<ChunkedMacVerificationWithPrefixType>>>
119 verifications_;
120 };
121
Update(absl::string_view data)122 util::Status ChunkedMacVerificationSetWrapper::Update(absl::string_view data) {
123 util::Status status =
124 util::Status(absl::StatusCode::kUnknown, "Update failed.");
125 for (auto& verification : *verifications_) {
126 util::Status individual_update_status = verification->Update(data);
127 if (individual_update_status.ok()) {
128 // At least one update succeeded.
129 status = util::OkStatus();
130 }
131 }
132 return status;
133 }
134
VerifyMac()135 util::Status ChunkedMacVerificationSetWrapper::VerifyMac() {
136 for (auto& verification : *verifications_) {
137 absl::Status status = verification->VerifyMac();
138 if (status.ok()) {
139 // One of the verifications succeeded.
140 return status;
141 }
142 }
143 return util::Status(absl::StatusCode::kUnknown, "Verification failed.");
144 }
145
146 class ChunkedMacSetWrapper : public ChunkedMac {
147 public:
ChunkedMacSetWrapper(std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set)148 explicit ChunkedMacSetWrapper(
149 std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set)
150 : mac_set_(std::move(mac_set)) {}
151
152 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> CreateComputation()
153 const override;
154
155 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> CreateVerification(
156 absl::string_view tag) const override;
157
158 ~ChunkedMacSetWrapper() override = default;
159
160 private:
161 std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set_;
162 };
163
Validate(PrimitiveSet<ChunkedMac> * mac_set)164 util::Status Validate(PrimitiveSet<ChunkedMac>* mac_set) {
165 if (mac_set == nullptr) {
166 return util::Status(absl::StatusCode::kInternal,
167 "mac_set must be non-NULL");
168 }
169 if (mac_set->get_primary() == nullptr) {
170 return util::Status(absl::StatusCode::kInvalidArgument,
171 "mac_set has no primary");
172 }
173 return util::OkStatus();
174 }
175
176 util::StatusOr<std::unique_ptr<ChunkedMacComputation>>
CreateComputation() const177 ChunkedMacSetWrapper::CreateComputation() const {
178 const PrimitiveSet<ChunkedMac>::Entry<ChunkedMac>* primary =
179 mac_set_->get_primary();
180 util::StatusOr<std::unique_ptr<ChunkedMacComputation>> computation =
181 primary->get_primitive().CreateComputation();
182 if (!computation.ok()) return computation.status();
183 return {absl::make_unique<ChunkedMacComputationSetWrapper>(
184 *std::move(computation), primary->get_identifier(),
185 primary->get_output_prefix_type())};
186 }
187
188 util::StatusOr<std::unique_ptr<ChunkedMacVerification>>
CreateVerification(absl::string_view tag) const189 ChunkedMacSetWrapper::CreateVerification(absl::string_view tag) const {
190 tag = internal::EnsureStringNonNull(tag);
191
192 auto verifications = absl::make_unique<
193 std::vector<std::unique_ptr<ChunkedMacVerificationWithPrefixType>>>();
194
195 // Create verifications for all non-RAW keys with matching identifiers by
196 // removing prefix.
197 if (tag.length() > CryptoFormat::kNonRawPrefixSize) {
198 absl::string_view key_id = tag.substr(0, CryptoFormat::kNonRawPrefixSize);
199 auto primitives_result = mac_set_->get_primitives(key_id);
200 if (primitives_result.ok()) {
201 absl::string_view raw_tag = tag.substr(CryptoFormat::kNonRawPrefixSize);
202 for (auto& mac_entry : *(primitives_result.value())) {
203 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
204 mac_entry->get_primitive().CreateVerification(raw_tag);
205 if (verification.ok()) {
206 auto verification_with_prefix =
207 absl::make_unique<ChunkedMacVerificationWithPrefixType>(
208 *std::move(verification),
209 mac_entry->get_output_prefix_type());
210 verifications->push_back(std::move(verification_with_prefix));
211 }
212 }
213 }
214 }
215
216 // Create verifications for all RAW keys by including prefix.
217 auto raw_primitives_result = mac_set_->get_raw_primitives();
218 if (raw_primitives_result.ok()) {
219 for (auto& mac_entry : *(raw_primitives_result.value())) {
220 util::StatusOr<std::unique_ptr<ChunkedMacVerification>> verification =
221 mac_entry->get_primitive().CreateVerification(tag);
222 if (verification.ok()) {
223 auto verification_with_prefix =
224 absl::make_unique<ChunkedMacVerificationWithPrefixType>(
225 *std::move(verification), mac_entry->get_output_prefix_type());
226 verifications->push_back(std::move(verification_with_prefix));
227 }
228 }
229 }
230
231 return {absl::make_unique<ChunkedMacVerificationSetWrapper>(
232 std::move(verifications))};
233 }
234
235 } // namespace
236
Wrap(std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set) const237 util::StatusOr<std::unique_ptr<ChunkedMac>> ChunkedMacWrapper::Wrap(
238 std::unique_ptr<PrimitiveSet<ChunkedMac>> mac_set) const {
239 util::Status status = Validate(mac_set.get());
240 if (!status.ok()) return status;
241 return {absl::make_unique<ChunkedMacSetWrapper>(std::move(mac_set))};
242 }
243
244 } // namespace internal
245 } // namespace tink
246 } // namespace crypto
247