xref: /aosp_15_r20/external/tink/cc/aead/internal/ssl_aead.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2021 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 #include "tink/aead/internal/ssl_aead.h"
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <limits>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 
25 #include "absl/cleanup/cleanup.h"
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 "absl/types/span.h"
31 #include "openssl/crypto.h"
32 #include "openssl/evp.h"
33 #include "tink/aead/internal/aead_util.h"
34 #include "tink/internal/err_util.h"
35 #include "tink/internal/ssl_unique_ptr.h"
36 #include "tink/internal/util.h"
37 #include "tink/util/secret_data.h"
38 #include "tink/util/status.h"
39 #include "tink/util/statusor.h"
40 
41 namespace crypto {
42 namespace tink {
43 namespace internal {
44 
45 ABSL_CONST_INIT const int kXchacha20Poly1305TagSizeInBytes = 16;
46 ABSL_CONST_INIT const int kAesGcmTagSizeInBytes = 16;
47 ABSL_CONST_INIT const int kAesGcmSivTagSizeInBytes = 16;
48 
49 namespace {
50 
51 // Encrypts/Decrypts `data` and writes the result into `out`. The direction
52 // (encrypt/decrypt) is given by `context`. `out` is assumed to be large enough
53 // to hold the encrypted/decrypted content.
UpdateCipher(EVP_CIPHER_CTX * context,absl::string_view data,absl::Span<char> out)54 util::StatusOr<int64_t> UpdateCipher(EVP_CIPHER_CTX *context,
55                                      absl::string_view data,
56                                      absl::Span<char> out) {
57   // We encrypt/decrypt in chunks of at most MAX int.
58   const int64_t kMaxChunkSize = std::numeric_limits<int>::max();
59   // Keep track of the bytes written to out.
60   int64_t total_written_bytes = 0;
61   // In practical cases data.size() is assumed to fit into a int64_t.
62   int64_t left_to_update = data.size();
63   while (left_to_update > 0) {
64     const int chunk_size = std::min(kMaxChunkSize, left_to_update);
65     auto *buffer_ptr =
66         reinterpret_cast<uint8_t *>(out.data() + total_written_bytes);
67     absl::string_view data_chunk = data.substr(total_written_bytes, chunk_size);
68     int written_bytes = 0;
69     if (EVP_CipherUpdate(context, buffer_ptr, &written_bytes,
70                          reinterpret_cast<const uint8_t *>(data_chunk.data()),
71                          data_chunk.size()) <= 0) {
72       const bool is_encrypting = EVP_CIPHER_CTX_encrypting(context) == 1;
73       return util::Status(
74           absl::StatusCode::kInternal,
75           absl::StrCat(is_encrypting ? "Encryption" : "Decryption", " failed"));
76     }
77     left_to_update -= written_bytes;
78     total_written_bytes += written_bytes;
79   }
80   return total_written_bytes;
81 }
82 
83 class OpenSslOneShotAeadImpl : public SslOneShotAead {
84  public:
OpenSslOneShotAeadImpl(const util::SecretData & key,const EVP_CIPHER * cipher,size_t tag_size)85   explicit OpenSslOneShotAeadImpl(const util::SecretData &key,
86                                   const EVP_CIPHER *cipher, size_t tag_size)
87       : key_(key), cipher_(cipher), tag_size_(tag_size) {}
88 
Encrypt(absl::string_view plaintext,absl::string_view associated_data,absl::string_view iv,absl::Span<char> out) const89   util::StatusOr<int64_t> Encrypt(absl::string_view plaintext,
90                                   absl::string_view associated_data,
91                                   absl::string_view iv,
92                                   absl::Span<char> out) const override {
93     absl::string_view plaintext_data = internal::EnsureStringNonNull(plaintext);
94     absl::string_view ad = internal::EnsureStringNonNull(associated_data);
95 
96     const int64_t min_out_buff_size = CiphertextSize(plaintext.size());
97     if (out.size() < min_out_buff_size) {
98       return util::Status(
99           absl::StatusCode::kInvalidArgument,
100           absl::StrCat("Encryption buffer too small; expected at least ",
101                        min_out_buff_size, " bytes, got ", out.size()));
102     }
103 
104     if (BuffersOverlap(plaintext, absl::string_view(out.data(), out.size()))) {
105       return util::Status(absl::StatusCode::kInvalidArgument,
106                           "Plaintext and output buffer must not overlap");
107     }
108 
109     if (associated_data.size() > std::numeric_limits<int>::max()) {
110       return util::Status(
111           absl::StatusCode::kInvalidArgument,
112           absl::StrCat("Associated data too large; expected at most ",
113                        std::numeric_limits<int>::max(), " got ",
114                        associated_data.size()));
115     }
116 
117     util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context =
118         GetContext(iv, /*encryption=*/true);
119     if (!context.ok()) {
120       return context.status();
121     }
122 
123     // Set the associated data.
124     int len = 0;
125     if (EVP_EncryptUpdate(context->get(), /*out=*/nullptr, &len,
126                           reinterpret_cast<const uint8_t *>(ad.data()),
127                           ad.size()) <= 0) {
128       return util::Status(absl::StatusCode::kInternal,
129                           "Failed to set associated data");
130     }
131 
132     util::StatusOr<int64_t> raw_ciphertext_bytes =
133         UpdateCipher(context->get(), plaintext_data, out);
134     if (!raw_ciphertext_bytes.ok()) {
135       return raw_ciphertext_bytes.status();
136     }
137 
138     if (EVP_EncryptFinal_ex(context->get(), /*out=*/nullptr, &len) <= 0) {
139       return util::Status(absl::StatusCode::kInternal, "Finalization failed");
140     }
141 
142     // Write the tag after the ciphertext.
143     absl::Span<char> tag = out.subspan(*raw_ciphertext_bytes, tag_size_);
144     if (EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_AEAD_GET_TAG, tag_size_,
145                             reinterpret_cast<uint8_t *>(tag.data())) <= 0) {
146       return util::Status(absl::StatusCode::kInternal, "Failed to get the tag");
147     }
148     return *raw_ciphertext_bytes + tag_size_;
149   }
150 
Decrypt(absl::string_view ciphertext,absl::string_view associated_data,absl::string_view iv,absl::Span<char> out) const151   util::StatusOr<int64_t> Decrypt(absl::string_view ciphertext,
152                                   absl::string_view associated_data,
153                                   absl::string_view iv,
154                                   absl::Span<char> out) const override {
155     absl::string_view ad = internal::EnsureStringNonNull(associated_data);
156 
157     if (ciphertext.size() < tag_size_) {
158       return util::Status(
159           absl::StatusCode::kInvalidArgument,
160           absl::StrCat("Ciphertext buffer too small; expected at least ",
161                        tag_size_, " got ", ciphertext.size()));
162     }
163 
164     const int64_t min_out_buff_size = PlaintextSize(ciphertext.size());
165     if (out.size() < min_out_buff_size) {
166       return util::Status(
167           absl::StatusCode::kInvalidArgument,
168           absl::StrCat("Output buffer too small; expected at least ",
169                        min_out_buff_size, " got ", out.size()));
170     }
171 
172     if (BuffersOverlap(ciphertext, absl::string_view(out.data(), out.size()))) {
173       return util::Status(absl::StatusCode::kInvalidArgument,
174                           "Ciphertext and output buffer must not overlap");
175     }
176 
177     if (associated_data.size() > std::numeric_limits<int>::max()) {
178       return util::Status(
179           absl::StatusCode::kInvalidArgument,
180           absl::StrCat("Associated data too large; expected at most ",
181                        std::numeric_limits<int>::max(), " got ",
182                        associated_data.size()));
183     }
184 
185     util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> context =
186         GetContext(iv, /*encryption=*/false);
187     if (!context.ok()) {
188       return context.status();
189     }
190 
191     int len = 0;
192     // Add the associated data.
193     if (EVP_DecryptUpdate(context->get(), /*out=*/nullptr, &len,
194                           reinterpret_cast<const uint8_t *>(ad.data()),
195                           ad.size()) <= 0) {
196       return util::Status(absl::StatusCode::kInternal,
197                           "Failed to set associated_data");
198     }
199 
200     const int64_t raw_ciphertext_size = ciphertext.size() - tag_size_;
201     // "Unpack" the ciphertext.
202     absl::string_view raw_ciphertext =
203         ciphertext.substr(0, raw_ciphertext_size);
204     // This copy is needed since EVP_CIPHER_CTX_ctrl requires a non-const
205     // pointer even if the EVP_CTRL_AEAD_SET_TAG operation doesn't modify the
206     // content of the buffer.
207     auto tag = std::string(ciphertext.substr(raw_ciphertext_size, tag_size_));
208 
209     // Set the tag.
210     if (EVP_CIPHER_CTX_ctrl(context->get(), EVP_CTRL_AEAD_SET_TAG, tag_size_,
211                             reinterpret_cast<uint8_t *>(&tag[0])) <= 0) {
212       return util::Status(absl::StatusCode::kInternal,
213                           "Could not set authentication tag");
214     }
215 
216     // If out.empty() accessing the 0th element would result in an out of
217     // bound violation. This makes sure we pass a pointer to at least one byte
218     // when calling into OpenSSL.
219     char buffer_if_size_is_zero = '\0';
220     auto out_buffer = absl::Span<char>(&buffer_if_size_is_zero, /*length=*/1);
221     if (!out.empty()) {
222       out_buffer = out.subspan(0, min_out_buff_size - tag_size_);
223     }
224 
225     // Zero the plaintext buffer in case decryption fails before returning an
226     // error.
227     auto output_eraser =
228         absl::MakeCleanup([out] { OPENSSL_cleanse(out.data(), out.size()); });
229 
230     util::StatusOr<int64_t> written_bytes =
231         UpdateCipher(context->get(), raw_ciphertext, out_buffer);
232     if (!written_bytes.ok()) {
233       return written_bytes.status();
234     }
235 
236     if (!EVP_DecryptFinal_ex(context->get(), /*out=*/nullptr, &len)) {
237       return util::Status(absl::StatusCode::kInternal, "Authentication failed");
238     }
239 
240     // Decryption executed correctly, cancel cleanup on the output buffer.
241     std::move(output_eraser).Cancel();
242     return *written_bytes;
243   }
244 
CiphertextSize(int64_t plaintext_length) const245   int64_t CiphertextSize(int64_t plaintext_length) const override {
246     return plaintext_length + tag_size_;
247   }
248 
PlaintextSize(int64_t ciphertext_length) const249   int64_t PlaintextSize(int64_t ciphertext_length) const override {
250     if (ciphertext_length < tag_size_) {
251       return 0;
252     }
253     return ciphertext_length - tag_size_;
254   }
255 
256  private:
257   // Returns a new EVP_CIPHER_CTX for encryption (`ecryption` == true) or
258   // decryption (`encryption` == false).
GetContext(absl::string_view iv,bool encryption) const259   util::StatusOr<internal::SslUniquePtr<EVP_CIPHER_CTX>> GetContext(
260       absl::string_view iv, bool encryption) const {
261     internal::SslUniquePtr<EVP_CIPHER_CTX> context(EVP_CIPHER_CTX_new());
262     if (context == nullptr) {
263       return util::Status(absl::StatusCode::kInternal,
264                           "EVP_CIPHER_CTX_new failed");
265     }
266     const int encryption_flag = encryption ? 1 : 0;
267     if (EVP_CipherInit_ex(context.get(), cipher_, /*impl=*/nullptr,
268                           /*key=*/nullptr, /*iv=*/nullptr,
269                           encryption_flag) <= 0) {
270       return util::Status(
271           absl::StatusCode::kInternal,
272           absl::StrCat("Failed initializializing context for ",
273                        encryption ? "encryption" : "decryption"));
274     }
275     // Set the size for IV first, then set the IV bytes.
276     if (EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_AEAD_SET_IVLEN, iv.size(),
277                             /*ptr=*/nullptr) <= 0) {
278       return util::Status(
279           absl::StatusCode::kInternal,
280           absl::StrCat("Failed stting size of the IV to ", iv.size()));
281     }
282     if (EVP_CipherInit_ex(context.get(), /*cipher=*/nullptr, /*impl=*/nullptr,
283                           reinterpret_cast<const uint8_t *>(key_.data()),
284                           reinterpret_cast<const uint8_t *>(iv.data()),
285                           encryption_flag) <= 0) {
286       return util::Status(
287           absl::StatusCode::kInternal,
288           absl::StrCat("Failed to set key of size ", key_.size(),
289                        "and IV of size ", iv.size()));
290     }
291 
292     return std::move(context);
293   }
294 
295   const util::SecretData key_;
296   const EVP_CIPHER *cipher_;
297   const size_t tag_size_;
298 };
299 
300 #ifdef OPENSSL_IS_BORINGSSL
301 
302 // Implementation of the one-shot AEAD cypter. This is purposely internal to
303 // an anonymous namespace to disallow direct use of this class other than
304 // through the Create* functions below.
305 class BoringSslOneShotAeadImpl : public SslOneShotAead {
306  public:
BoringSslOneShotAeadImpl(internal::SslUniquePtr<EVP_AEAD_CTX> context,size_t tag_size)307   explicit BoringSslOneShotAeadImpl(
308       internal::SslUniquePtr<EVP_AEAD_CTX> context, size_t tag_size)
309       : context_(std::move(context)), tag_size_(tag_size) {}
310 
Encrypt(absl::string_view plaintext,absl::string_view associated_data,absl::string_view iv,absl::Span<char> out) const311   util::StatusOr<int64_t> Encrypt(absl::string_view plaintext,
312                                   absl::string_view associated_data,
313                                   absl::string_view iv,
314                                   absl::Span<char> out) const override {
315     // BoringSSL expects a non-null pointer for associated_data,
316     // regardless of whether the size is 0.
317     plaintext = internal::EnsureStringNonNull(plaintext);
318     associated_data = internal::EnsureStringNonNull(associated_data);
319     iv = internal::EnsureStringNonNull(iv);
320 
321     if (BuffersOverlap(plaintext, absl::string_view(out.data(), out.size()))) {
322       return util::Status(absl::StatusCode::kInvalidArgument,
323                           "Plaintext and output buffer must not overlap");
324     }
325 
326     const int64_t min_out_buff_size = CiphertextSize(plaintext.size());
327     if (out.size() < min_out_buff_size) {
328       return util::Status(
329           absl::StatusCode::kInvalidArgument,
330           absl::StrCat("Output buffer too small; expected at least ",
331                        min_out_buff_size, " got ", out.size()));
332     }
333     size_t out_len = 0;
334     if (!EVP_AEAD_CTX_seal(
335             context_.get(), reinterpret_cast<uint8_t *>(&out[0]), &out_len,
336             out.size(), reinterpret_cast<const uint8_t *>(iv.data()), iv.size(),
337             reinterpret_cast<const uint8_t *>(plaintext.data()),
338             plaintext.size(),
339             /*ad=*/reinterpret_cast<const uint8_t *>(associated_data.data()),
340             /*ad_len=*/associated_data.size())) {
341       return util::Status(
342           absl::StatusCode::kInternal,
343           absl::StrCat("Encryption failed: ", internal::GetSslErrors()));
344     }
345 
346     return out_len;
347   }
348 
Decrypt(absl::string_view ciphertext,absl::string_view associated_data,absl::string_view iv,absl::Span<char> out) const349   util::StatusOr<int64_t> Decrypt(absl::string_view ciphertext,
350                                   absl::string_view associated_data,
351                                   absl::string_view iv,
352                                   absl::Span<char> out) const override {
353     ciphertext = internal::EnsureStringNonNull(ciphertext);
354     associated_data = internal::EnsureStringNonNull(associated_data);
355     iv = internal::EnsureStringNonNull(iv);
356 
357     if (BuffersOverlap(ciphertext, absl::string_view(out.data(), out.size()))) {
358       return util::Status(absl::StatusCode::kInvalidArgument,
359                           "Ciphertext and output buffer must not overlap");
360     }
361 
362     if (ciphertext.size() < tag_size_) {
363       return util::Status(
364           absl::StatusCode::kInvalidArgument,
365           absl::StrCat("Ciphertext buffer too small; expected at least ",
366                        tag_size_, " got ", ciphertext.size()));
367     }
368 
369     const int64_t min_out_buff_size = PlaintextSize(ciphertext.size());
370     if (out.size() < min_out_buff_size) {
371       return util::Status(
372           absl::StatusCode::kInvalidArgument,
373           absl::StrCat("Output buffer too small; expected at least ",
374                        min_out_buff_size, " got ", out.size()));
375     }
376 
377     // If out.empty() accessing the 0th element would result in an out of
378     // bound violation. This makes sure we pass a pointer to at least one byte
379     // when calling into OpenSSL.
380     uint8_t buffer_if_size_is_zero;
381     uint8_t *buffer_ptr = &buffer_if_size_is_zero;
382     if (!out.empty()) {
383       buffer_ptr = reinterpret_cast<uint8_t *>(&out[0]);
384     }
385 
386     size_t out_len = 0;
387     if (!EVP_AEAD_CTX_open(
388             context_.get(), buffer_ptr, &out_len, out.size(),
389             reinterpret_cast<const uint8_t *>(iv.data()), iv.size(),
390             reinterpret_cast<const uint8_t *>(ciphertext.data()),
391             ciphertext.size(),
392             /*ad=*/reinterpret_cast<const uint8_t *>(associated_data.data()),
393             /*ad_len=*/associated_data.size())) {
394       return util::Status(
395           absl::StatusCode::kInternal,
396           absl::StrCat("Authentication failed: ", internal::GetSslErrors()));
397     }
398 
399     return out_len;
400   }
401 
CiphertextSize(int64_t plaintext_length) const402   int64_t CiphertextSize(int64_t plaintext_length) const override {
403     return plaintext_length + tag_size_;
404   }
405 
PlaintextSize(int64_t ciphertext_length) const406   int64_t PlaintextSize(int64_t ciphertext_length) const override {
407     if (ciphertext_length < tag_size_) {
408       return 0;
409     }
410     return ciphertext_length - tag_size_;
411   }
412 
413  private:
414   const internal::SslUniquePtr<EVP_AEAD_CTX> context_;
415   const size_t tag_size_;
416 };
417 
418 #endif
419 
420 }  // namespace
421 
CreateAesGcmOneShotCrypter(const util::SecretData & key)422 util::StatusOr<std::unique_ptr<SslOneShotAead>> CreateAesGcmOneShotCrypter(
423     const util::SecretData &key) {
424 #ifdef OPENSSL_IS_BORINGSSL
425   util::StatusOr<const EVP_AEAD *> aead_cipher =
426       GetAesGcmAeadForKeySize(key.size());
427   if (!aead_cipher.ok()) {
428     return aead_cipher.status();
429   }
430 
431   internal::SslUniquePtr<EVP_AEAD_CTX> context(EVP_AEAD_CTX_new(
432       *aead_cipher, key.data(), key.size(), kAesGcmTagSizeInBytes));
433   if (context == nullptr) {
434     return util::Status(
435         absl::StatusCode::kInternal,
436         absl::StrCat("EVP_AEAD_CTX_new failed: ", internal::GetSslErrors()));
437   }
438   return {absl::make_unique<BoringSslOneShotAeadImpl>(std::move(context),
439                                                       kAesGcmTagSizeInBytes)};
440 #else
441   util::StatusOr<const EVP_CIPHER *> aead_cipher =
442       GetAesGcmCipherForKeySize(key.size());
443   if (!aead_cipher.ok()) {
444     return aead_cipher.status();
445   }
446 
447   return absl::make_unique<OpenSslOneShotAeadImpl>(key, *aead_cipher,
448                                                    kAesGcmTagSizeInBytes);
449 #endif
450 }
451 
CreateAesGcmSivOneShotCrypter(const util::SecretData & key)452 util::StatusOr<std::unique_ptr<SslOneShotAead>> CreateAesGcmSivOneShotCrypter(
453     const util::SecretData &key) {
454 #ifdef OPENSSL_IS_BORINGSSL
455   util::StatusOr<const EVP_AEAD *> aead_cipher =
456       GetAesGcmSivAeadCipherForKeySize(key.size());
457   if (!aead_cipher.ok()) {
458     return aead_cipher.status();
459   }
460   internal::SslUniquePtr<EVP_AEAD_CTX> context(EVP_AEAD_CTX_new(
461       *aead_cipher, key.data(), key.size(), kAesGcmTagSizeInBytes));
462   if (context == nullptr) {
463     return util::Status(absl::StatusCode::kInternal,
464                         absl::StrCat("EVP_AEAD_CTX_new initialization Failed: ",
465                                      internal::GetSslErrors()));
466   }
467   return {absl::make_unique<BoringSslOneShotAeadImpl>(
468       std::move(context), kAesGcmSivTagSizeInBytes)};
469 #else
470   return util::Status(absl::StatusCode::kUnimplemented,
471                       "AES-GCM-SIV is unimplemented for OpenSSL");
472 #endif
473 }
474 
475 util::StatusOr<std::unique_ptr<SslOneShotAead>>
CreateXchacha20Poly1305OneShotCrypter(const util::SecretData & key)476 CreateXchacha20Poly1305OneShotCrypter(const util::SecretData &key) {
477 #ifdef OPENSSL_IS_BORINGSSL
478   if (key.size() != 32) {
479     return util::Status(
480         absl::StatusCode::kInvalidArgument,
481         absl::StrCat("Invalid key size; valid values are {32} bytes, got ",
482                      key.size()));
483   }
484 
485   internal::SslUniquePtr<EVP_AEAD_CTX> context(
486       EVP_AEAD_CTX_new(EVP_aead_xchacha20_poly1305(), key.data(), key.size(),
487                        kAesGcmTagSizeInBytes));
488   if (context == nullptr) {
489     return util::Status(absl::StatusCode::kInternal,
490                         absl::StrCat("EVP_AEAD_CTX_new initialization Failed: ",
491                                      internal::GetSslErrors()));
492   }
493   return {absl::make_unique<BoringSslOneShotAeadImpl>(
494       std::move(context), kXchacha20Poly1305TagSizeInBytes)};
495 #else
496   return util::Status(absl::StatusCode::kUnimplemented,
497                       "Xchacha20-Poly1305 is unimplemented for OpenSSL");
498 #endif
499 }
500 
501 }  // namespace internal
502 }  // namespace tink
503 }  // namespace crypto
504