1 // Copyright 2019 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/subtle/aes_ctr_hmac_streaming.h"
18
19 #include <algorithm>
20 #include <limits>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25
26 #include "absl/memory/memory.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/string_view.h"
30 #include "openssl/err.h"
31 #include "openssl/evp.h"
32 #include "tink/internal/aes_util.h"
33 #include "tink/internal/ssl_unique_ptr.h"
34 #include "tink/subtle/common_enums.h"
35 #include "tink/subtle/hkdf.h"
36 #include "tink/subtle/hmac_boringssl.h"
37 #include "tink/subtle/random.h"
38 #include "tink/subtle/stream_segment_decrypter.h"
39 #include "tink/subtle/stream_segment_encrypter.h"
40 #include "tink/subtle/subtle_util.h"
41 #include "tink/util/errors.h"
42 #include "tink/util/secret_data.h"
43 #include "tink/util/status.h"
44 #include "tink/util/statusor.h"
45
46 namespace crypto {
47 namespace tink {
48 namespace subtle {
49
NonceForSegment(absl::string_view nonce_prefix,int64_t segment_number,bool is_last_segment)50 static std::string NonceForSegment(absl::string_view nonce_prefix,
51 int64_t segment_number,
52 bool is_last_segment) {
53 return absl::StrCat(
54 nonce_prefix, BigEndian32(segment_number),
55 is_last_segment ? std::string(1, '\x01') : std::string(1, '\x00'),
56 std::string(4, '\x00'));
57 }
58
DeriveKeys(const util::SecretData & ikm,HashType hkdf_algo,absl::string_view salt,absl::string_view associated_data,int key_size,util::SecretData * key_value,util::SecretData * hmac_key_value)59 static util::Status DeriveKeys(const util::SecretData& ikm, HashType hkdf_algo,
60 absl::string_view salt,
61 absl::string_view associated_data, int key_size,
62 util::SecretData* key_value,
63 util::SecretData* hmac_key_value) {
64 int derived_key_material_size =
65 key_size + AesCtrHmacStreaming::kHmacKeySizeInBytes;
66 auto hkdf_result = Hkdf::ComputeHkdf(hkdf_algo, ikm, salt, associated_data,
67 derived_key_material_size);
68 if (!hkdf_result.ok()) return hkdf_result.status();
69 util::SecretData key_material = std::move(hkdf_result.value());
70 *key_value =
71 util::SecretData(key_material.begin(), key_material.begin() + key_size);
72 *hmac_key_value =
73 util::SecretData(key_material.begin() + key_size, key_material.end());
74 return util::OkStatus();
75 }
76
Validate(const AesCtrHmacStreaming::Params & params)77 static util::Status Validate(const AesCtrHmacStreaming::Params& params) {
78 if (params.ikm.size() < std::max(16, params.key_size)) {
79 return util::Status(absl::StatusCode::kInvalidArgument,
80 "input key material too small");
81 }
82 if (!(params.hkdf_algo == SHA1 || params.hkdf_algo == SHA256 ||
83 params.hkdf_algo == SHA512)) {
84 return util::Status(absl::StatusCode::kInvalidArgument,
85 "unsupported hkdf_algo");
86 }
87 if (params.key_size != 16 && params.key_size != 32) {
88 return util::Status(absl::StatusCode::kInvalidArgument,
89 "key_size must be 16 or 32");
90 }
91 int header_size =
92 1 + params.key_size + AesCtrHmacStreaming::kNoncePrefixSizeInBytes;
93 if (params.ciphertext_segment_size <=
94 params.ciphertext_offset + header_size + params.tag_size) {
95 return util::Status(absl::StatusCode::kInvalidArgument,
96 "ciphertext_segment_size too small");
97 }
98 if (params.ciphertext_offset < 0) {
99 return util::Status(absl::StatusCode::kInvalidArgument,
100 "ciphertext_offset must be non-negative");
101 }
102 if (params.tag_size < 10) {
103 return util::Status(absl::StatusCode::kInvalidArgument,
104 "tag_size too small");
105 }
106 if (!(params.tag_algo == SHA1 || params.tag_algo == SHA256 ||
107 params.tag_algo == SHA512)) {
108 return util::Status(absl::StatusCode::kInvalidArgument,
109 "unsupported tag_algo");
110 }
111 if ((params.tag_algo == SHA1 && params.tag_size > 20) ||
112 (params.tag_algo == SHA256 && params.tag_size > 32) ||
113 (params.tag_algo == SHA512 && params.tag_size > 64)) {
114 return util::Status(absl::StatusCode::kInvalidArgument, "tag_size too big");
115 }
116
117 return util::OkStatus();
118 }
119
120 // AesCtrHmacStreaming
121 // static
New(Params params)122 util::StatusOr<std::unique_ptr<AesCtrHmacStreaming>> AesCtrHmacStreaming::New(
123 Params params) {
124 auto status = internal::CheckFipsCompatibility<AesCtrHmacStreaming>();
125 if (!status.ok()) return status;
126
127 status = Validate(params);
128 if (!status.ok()) return status;
129 return {absl::WrapUnique(new AesCtrHmacStreaming(std::move(params)))};
130 }
131
132 // static
133 util::StatusOr<std::unique_ptr<StreamSegmentEncrypter>>
NewSegmentEncrypter(absl::string_view associated_data) const134 AesCtrHmacStreaming::NewSegmentEncrypter(
135 absl::string_view associated_data) const {
136 return AesCtrHmacStreamSegmentEncrypter::New(params_, associated_data);
137 }
138
139 // static
140 util::StatusOr<std::unique_ptr<StreamSegmentDecrypter>>
NewSegmentDecrypter(absl::string_view associated_data) const141 AesCtrHmacStreaming::NewSegmentDecrypter(
142 absl::string_view associated_data) const {
143 return AesCtrHmacStreamSegmentDecrypter::New(params_, associated_data);
144 }
145
146 // AesCtrHmacStreamSegmentEncrypter
MakeHeader(absl::string_view salt,absl::string_view nonce_prefix)147 static std::string MakeHeader(absl::string_view salt,
148 absl::string_view nonce_prefix) {
149 uint8_t header_size =
150 static_cast<uint8_t>(1 + salt.size() + nonce_prefix.size());
151 return absl::StrCat(std::string(1, header_size), salt, nonce_prefix);
152 }
153
154 // static
155 util::StatusOr<std::unique_ptr<StreamSegmentEncrypter>>
New(const AesCtrHmacStreaming::Params & params,absl::string_view associated_data)156 AesCtrHmacStreamSegmentEncrypter::New(const AesCtrHmacStreaming::Params& params,
157 absl::string_view associated_data) {
158 auto status = Validate(params);
159 if (!status.ok()) return status;
160
161 std::string salt = Random::GetRandomBytes(params.key_size);
162 std::string nonce_prefix =
163 Random::GetRandomBytes(AesCtrHmacStreaming::kNoncePrefixSizeInBytes);
164 std::string header = MakeHeader(salt, nonce_prefix);
165
166 util::SecretData key_value;
167 util::SecretData hmac_key_value;
168 status = DeriveKeys(params.ikm, params.hkdf_algo, salt, associated_data,
169 params.key_size, &key_value, &hmac_key_value);
170 if (!status.ok()) return status;
171
172 util::StatusOr<const EVP_CIPHER*> cipher =
173 internal::GetAesCtrCipherForKeySize(params.key_size);
174 if (!cipher.ok()) {
175 return cipher.status();
176 }
177
178 auto hmac_result = HmacBoringSsl::New(params.tag_algo, params.tag_size,
179 std::move(hmac_key_value));
180 if (!hmac_result.ok()) return hmac_result.status();
181 auto mac = std::move(hmac_result.value());
182
183 return {absl::WrapUnique(new AesCtrHmacStreamSegmentEncrypter(
184 std::move(key_value), header, nonce_prefix,
185 params.ciphertext_segment_size, params.ciphertext_offset, params.tag_size,
186 *cipher, std::move(mac)))};
187 }
188
EncryptSegment(const std::vector<uint8_t> & plaintext,bool is_last_segment,std::vector<uint8_t> * ciphertext_buffer)189 util::Status AesCtrHmacStreamSegmentEncrypter::EncryptSegment(
190 const std::vector<uint8_t>& plaintext, bool is_last_segment,
191 std::vector<uint8_t>* ciphertext_buffer) {
192 if (plaintext.size() > get_plaintext_segment_size()) {
193 return util::Status(absl::StatusCode::kInvalidArgument,
194 "plaintext too long");
195 }
196 if (ciphertext_buffer == nullptr) {
197 return util::Status(absl::StatusCode::kInvalidArgument,
198 "ciphertext_buffer must be non-null");
199 }
200 if (get_segment_number() > std::numeric_limits<uint32_t>::max() ||
201 (get_segment_number() == std::numeric_limits<uint32_t>::max() &&
202 !is_last_segment)) {
203 return util::Status(absl::StatusCode::kInvalidArgument,
204 "too many segments");
205 }
206
207 int ct_size = plaintext.size() + tag_size_;
208 ciphertext_buffer->resize(ct_size);
209
210 std::string nonce =
211 NonceForSegment(nonce_prefix_, segment_number_, is_last_segment);
212
213 // Encrypt.
214 internal::SslUniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
215 if (ctx == nullptr) {
216 return util::Status(absl::StatusCode::kInternal,
217 "could not initialize EVP_CIPHER_CTX");
218 }
219 if (EVP_EncryptInit_ex(ctx.get(), cipher_, nullptr /* engine */,
220 reinterpret_cast<const uint8_t*>(key_value_.data()),
221 reinterpret_cast<const uint8_t*>(nonce.data())) != 1) {
222 return util::Status(absl::StatusCode::kInternal,
223 "could not initialize ctx");
224 }
225
226 int out_len;
227 if (EVP_EncryptUpdate(ctx.get(), ciphertext_buffer->data(), &out_len,
228 plaintext.data(), plaintext.size()) != 1) {
229 return util::Status(absl::StatusCode::kInternal, "encryption failed");
230 }
231 if (out_len != plaintext.size()) {
232 return util::Status(absl::StatusCode::kInternal,
233 "incorrect ciphertext size");
234 }
235
236 // Add MAC tag.
237 absl::string_view ciphertext_string(
238 reinterpret_cast<const char*>(ciphertext_buffer->data()),
239 plaintext.size());
240 auto tag_result = mac_->ComputeMac(absl::StrCat(nonce, ciphertext_string));
241 if (!tag_result.ok()) return tag_result.status();
242 std::string tag = tag_result.value();
243 memcpy(ciphertext_buffer->data() + plaintext.size(),
244 reinterpret_cast<const uint8_t*>(tag.data()), tag_size_);
245
246 IncSegmentNumber();
247 return util::OkStatus();
248 }
249
250 // AesCtrHmacStreamSegmentDecrypter
251 // static
252 util::StatusOr<std::unique_ptr<StreamSegmentDecrypter>>
New(const AesCtrHmacStreaming::Params & params,absl::string_view associated_data)253 AesCtrHmacStreamSegmentDecrypter::New(const AesCtrHmacStreaming::Params& params,
254 absl::string_view associated_data) {
255 auto status = Validate(params);
256 if (!status.ok()) return status;
257
258 return {absl::WrapUnique(new AesCtrHmacStreamSegmentDecrypter(
259 params.ikm, params.hkdf_algo, params.key_size, associated_data,
260 params.ciphertext_segment_size, params.ciphertext_offset, params.tag_algo,
261 params.tag_size))};
262 }
263
Init(const std::vector<uint8_t> & header)264 util::Status AesCtrHmacStreamSegmentDecrypter::Init(
265 const std::vector<uint8_t>& header) {
266 if (is_initialized_) {
267 return util::Status(absl::StatusCode::kFailedPrecondition,
268 "decrypter alreday initialized");
269 }
270 if (header.size() != get_header_size()) {
271 return util::Status(absl::StatusCode::kInvalidArgument,
272 absl::StrCat("wrong header size, expected ",
273 get_header_size(), " bytes"));
274 }
275 if (header[0] != header.size()) {
276 return util::Status(absl::StatusCode::kInvalidArgument, "corrupted header");
277 }
278
279 // Extract salt and nonce prefix.
280 std::string salt(reinterpret_cast<const char*>(header.data() + 1), key_size_);
281 nonce_prefix_ =
282 std::string(reinterpret_cast<const char*>(header.data() + 1 + key_size_),
283 AesCtrHmacStreaming::kNoncePrefixSizeInBytes);
284
285 util::SecretData hmac_key_value;
286 auto status = DeriveKeys(ikm_, hkdf_algo_, salt, associated_data_, key_size_,
287 &key_value_, &hmac_key_value);
288 if (!status.ok()) return status;
289
290 util::StatusOr<const EVP_CIPHER*> cipher =
291 internal::GetAesCtrCipherForKeySize(key_size_);
292 if (!cipher.ok()) {
293 return cipher.status();
294 }
295
296 cipher_ = *cipher;
297
298 auto hmac_result =
299 HmacBoringSsl::New(tag_algo_, tag_size_, std::move(hmac_key_value));
300 if (!hmac_result.ok()) return hmac_result.status();
301 mac_ = std::move(hmac_result.value());
302
303 is_initialized_ = true;
304 return util::OkStatus();
305 }
306
DecryptSegment(const std::vector<uint8_t> & ciphertext,int64_t segment_number,bool is_last_segment,std::vector<uint8_t> * plaintext_buffer)307 util::Status AesCtrHmacStreamSegmentDecrypter::DecryptSegment(
308 const std::vector<uint8_t>& ciphertext, int64_t segment_number,
309 bool is_last_segment, std::vector<uint8_t>* plaintext_buffer) {
310 if (!is_initialized_) {
311 return util::Status(absl::StatusCode::kFailedPrecondition,
312 "decrypter not initialized");
313 }
314 if (ciphertext.size() > get_ciphertext_segment_size()) {
315 return util::Status(absl::StatusCode::kInvalidArgument,
316 "ciphertext too long");
317 }
318 if (ciphertext.size() < tag_size_) {
319 return util::Status(absl::StatusCode::kInvalidArgument,
320 "ciphertext too short");
321 }
322 if (plaintext_buffer == nullptr) {
323 return util::Status(absl::StatusCode::kInvalidArgument,
324 "plaintext_buffer must be non-null");
325 }
326 if (segment_number > std::numeric_limits<uint32_t>::max() ||
327 (segment_number == std::numeric_limits<uint32_t>::max() &&
328 !is_last_segment)) {
329 return util::Status(absl::StatusCode::kInvalidArgument,
330 "too many segments");
331 }
332
333 int pt_size = ciphertext.size() - tag_size_;
334 plaintext_buffer->resize(pt_size);
335
336 std::string nonce =
337 NonceForSegment(nonce_prefix_, segment_number, is_last_segment);
338
339 // Verify MAC tag.
340 absl::string_view tag(
341 reinterpret_cast<const char*>(ciphertext.data() + pt_size), tag_size_);
342 absl::string_view ciphertext_string(
343 reinterpret_cast<const char*>(ciphertext.data()), pt_size);
344 auto status = mac_->VerifyMac(tag, absl::StrCat(nonce, ciphertext_string));
345 if (!status.ok()) return status;
346
347 // Decrypt.
348 internal::SslUniquePtr<EVP_CIPHER_CTX> ctx(EVP_CIPHER_CTX_new());
349 if (ctx.get() == nullptr) {
350 return util::Status(absl::StatusCode::kInternal,
351 "could not initialize EVP_CIPHER_CTX");
352 }
353 if (EVP_DecryptInit_ex(ctx.get(), cipher_, nullptr /* engine */,
354 reinterpret_cast<const uint8_t*>(key_value_.data()),
355 reinterpret_cast<const uint8_t*>(nonce.data())) != 1) {
356 return util::Status(absl::StatusCode::kInternal,
357 "could not initialize ctx");
358 }
359
360 int out_len;
361 if (EVP_DecryptUpdate(ctx.get(), plaintext_buffer->data(), &out_len,
362 ciphertext.data(), pt_size) != 1) {
363 return util::Status(absl::StatusCode::kInternal, "decryption failed");
364 }
365 if (out_len != pt_size) {
366 return util::Status(absl::StatusCode::kInternal,
367 "incorrect plaintext size");
368 }
369
370 return util::OkStatus();
371 }
372
373 } // namespace subtle
374 } // namespace tink
375 } // namespace crypto
376