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