xref: /aosp_15_r20/external/tink/cc/subtle/encrypt_then_authenticate.cc (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1 // Copyright 2017 Google Inc.
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/encrypt_then_authenticate.h"
18 
19 #include <cstdint>
20 #include <memory>
21 #include <string>
22 #include <utility>
23 #include <vector>
24 
25 #include "absl/status/status.h"
26 #include "absl/strings/str_cat.h"
27 #include "absl/strings/string_view.h"
28 #include "tink/aead.h"
29 #include "tink/internal/util.h"
30 #include "tink/mac.h"
31 #include "tink/subtle/ind_cpa_cipher.h"
32 #include "tink/util/errors.h"
33 #include "tink/util/status.h"
34 #include "tink/util/statusor.h"
35 
36 namespace crypto {
37 namespace tink {
38 namespace subtle {
39 
longToBigEndianStr(uint64_t value)40 static std::string longToBigEndianStr(uint64_t value) {
41   uint8_t bytes[8];
42   for (int i = sizeof(bytes) - 1; i >= 0; i--) {
43     bytes[i] = value & 0xff;
44     value >>= 8;
45   }
46   return std::string(reinterpret_cast<const char*>(&bytes[0]), sizeof(bytes));
47 }
48 
New(std::unique_ptr<IndCpaCipher> ind_cpa_cipher,std::unique_ptr<Mac> mac,uint8_t tag_size)49 util::StatusOr<std::unique_ptr<Aead>> EncryptThenAuthenticate::New(
50     std::unique_ptr<IndCpaCipher> ind_cpa_cipher, std::unique_ptr<Mac> mac,
51     uint8_t tag_size) {
52   if (tag_size < kMinTagSizeInBytes) {
53     return util::Status(absl::StatusCode::kInvalidArgument,
54                         "tag size too small");
55   }
56   std::unique_ptr<Aead> aead(new EncryptThenAuthenticate(
57       std::move(ind_cpa_cipher), std::move(mac), tag_size));
58   return std::move(aead);
59 }
60 
Encrypt(absl::string_view plaintext,absl::string_view associated_data) const61 util::StatusOr<std::string> EncryptThenAuthenticate::Encrypt(
62     absl::string_view plaintext, absl::string_view associated_data) const {
63   // BoringSSL expects a non-null pointer for plaintext and associated_data,
64   // regardless of whether the size is 0.
65   plaintext = internal::EnsureStringNonNull(plaintext);
66   associated_data = internal::EnsureStringNonNull(associated_data);
67 
68   uint64_t associated_data_size_in_bytes = associated_data.size();
69   uint64_t associated_data_size_in_bits = associated_data_size_in_bytes * 8;
70   if (associated_data_size_in_bits / 8 !=
71       associated_data_size_in_bytes /* overflow occured! */) {
72     return util::Status(absl::StatusCode::kInvalidArgument,
73                         "associated data too long");
74   }
75 
76   auto ciphertext = ind_cpa_cipher_->Encrypt(plaintext);
77   if (!ciphertext.ok()) {
78     return ciphertext.status();
79   }
80   auto tag = mac_->ComputeMac(
81       absl::StrCat(associated_data, *ciphertext,
82                    longToBigEndianStr(associated_data_size_in_bits)));
83   if (!tag.ok()) {
84     return tag.status();
85   }
86   if (tag->size() != tag_size_) {
87     return util::Status(absl::StatusCode::kInternal, "invalid tag size");
88   }
89   return ciphertext->append(tag.value());
90 }
91 
Decrypt(absl::string_view ciphertext,absl::string_view associated_data) const92 util::StatusOr<std::string> EncryptThenAuthenticate::Decrypt(
93     absl::string_view ciphertext, absl::string_view associated_data) const {
94   // BoringSSL expects a non-null pointer for associated_data,
95   // regardless of whether the size is 0.
96   associated_data = internal::EnsureStringNonNull(associated_data);
97 
98   if (ciphertext.size() < tag_size_) {
99     return util::Status(absl::StatusCode::kInvalidArgument,
100                         "ciphertext too short");
101   }
102 
103   uint64_t associated_data_size_in_bytes = associated_data.size();
104   uint64_t associated_data_size_in_bits = associated_data_size_in_bytes * 8;
105   if (associated_data_size_in_bits / 8 !=
106       associated_data_size_in_bytes /* overflow occured! */) {
107     return util::Status(absl::StatusCode::kInvalidArgument,
108                         "additional data too long");
109   }
110 
111   auto payload = ciphertext.substr(0, ciphertext.size() - tag_size_);
112   auto tag = ciphertext.substr(ciphertext.size() - tag_size_, tag_size_);
113 
114   auto verified = mac_->VerifyMac(
115       tag, absl::StrCat(associated_data, payload,
116                         longToBigEndianStr(associated_data_size_in_bits)));
117   if (!verified.ok()) {
118     return verified;
119   }
120 
121   auto pt = ind_cpa_cipher_->Decrypt(payload);
122   if (!pt.ok()) {
123     return pt.status();
124   }
125 
126   return pt.value();
127 }
128 
129 }  // namespace subtle
130 }  // namespace tink
131 }  // namespace crypto
132