xref: /aosp_15_r20/external/tink/cc/subtle/aes_ctr_hmac_streaming.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
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