xref: /aosp_15_r20/external/tink/cc/subtle/aes_eax_boringssl.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/aes_eax_boringssl.h"
18 
19 #include <algorithm>
20 #include <cstring>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/algorithm/container.h"
27 #include "absl/base/config.h"
28 #include "absl/memory/memory.h"
29 #include "absl/status/status.h"
30 #include "absl/types/span.h"
31 #include "openssl/err.h"
32 #include "openssl/evp.h"
33 #include "tink/aead.h"
34 #include "tink/internal/aes_util.h"
35 #include "tink/internal/util.h"
36 #include "tink/subtle/random.h"
37 #include "tink/subtle/subtle_util.h"
38 #include "tink/util/errors.h"
39 #include "tink/util/status.h"
40 #include "tink/util/statusor.h"
41 
42 namespace crypto {
43 namespace tink {
44 namespace subtle {
45 
46 namespace {
47 
48 // Loads and stores 8 bytes. The endianness of the two routines
49 // does not matter, as long as the two routines use the same order.
Load64(const uint8_t src[8])50 uint64_t Load64(const uint8_t src[8]) {
51   uint64_t res;
52   std::memcpy(&res, src, 8);
53   return res;
54 }
55 
Store64(uint64_t val,uint8_t dst[8])56 void Store64(uint64_t val, uint8_t dst[8]) { std::memcpy(dst, &val, 8); }
57 
ByteSwap(uint64_t val)58 uint64_t ByteSwap(uint64_t val) {
59   val = ((val & 0xffffffff00000000) >> 32) | ((val & 0x00000000ffffffff) << 32);
60   val = ((val & 0xffff0000ffff0000) >> 16) | ((val & 0x0000ffff0000ffff) << 16);
61   val = ((val & 0xff00ff00ff00ff00) >> 8) | ((val & 0x00ff00ff00ff00ff) << 8);
62   return val;
63 }
64 
BigEndianLoad64(const uint8_t src[8])65 uint64_t BigEndianLoad64(const uint8_t src[8]) {
66 #if defined(ABSL_IS_LITTLE_ENDIAN)
67   return ByteSwap(Load64(src));
68 #elif defined(ABSL_IS_BIG_ENDIAN)
69   return Load64(src);
70 #else
71 #error Unknown endianness
72 #endif
73 }
74 
BigEndianStore64(uint64_t val,uint8_t dst[8])75 void BigEndianStore64(uint64_t val, uint8_t dst[8]) {
76 #if defined(ABSL_IS_LITTLE_ENDIAN)
77   return Store64(ByteSwap(val), dst);
78 #elif defined(ABSL_IS_BIG_ENDIAN)
79   return Store64(val, dst);
80 #else
81 #error Unknown endianness
82 #endif
83 }
84 
InitAesKey(const util::SecretData & key)85 crypto::tink::util::StatusOr<util::SecretUniquePtr<AES_KEY>> InitAesKey(
86     const util::SecretData& key) {
87   auto aeskey = util::MakeSecretUniquePtr<AES_KEY>();
88   int status = AES_set_encrypt_key(key.data(), key.size() * 8, aeskey.get());
89   // status != 0 happens if key_value is invalid.
90   if (status != 0) {
91     return util::Status(absl::StatusCode::kInvalidArgument,
92                         "Invalid key value");
93   }
94   return std::move(aeskey);
95 }
96 
97 }  // namespace
98 
XorBlock(const uint8_t x[kBlockSize],Block * y)99 void AesEaxBoringSsl::XorBlock(const uint8_t x[kBlockSize], Block* y) {
100   uint64_t r0 = Load64(x) ^ Load64(y->data());
101   uint64_t r1 = Load64(x + 8) ^ Load64(y->data() + 8);
102   Store64(r0, y->data());
103   Store64(r1, y->data() + 8);
104 }
105 
MultiplyByX(const uint8_t in[kBlockSize],uint8_t out[kBlockSize])106 void AesEaxBoringSsl::MultiplyByX(const uint8_t in[kBlockSize],
107                                   uint8_t out[kBlockSize]) {
108   uint64_t in_high = BigEndianLoad64(in);
109   uint64_t in_low = BigEndianLoad64(in + 8);
110   uint64_t out_high = (in_high << 1) ^ (in_low >> 63);
111   // If the most significant bit is set then the result has to
112   // be reduced by x^128 + x^7 + x^2 + x + 1.
113   // The representation of x^7 + x^2 + x + 1 is 0x87.
114   uint64_t out_low = (in_low << 1) ^ (0x87 & -(in_high >> 63));
115   BigEndianStore64(out_high, out);
116   BigEndianStore64(out_low, out + 8);
117 }
118 
EqualBlocks(const uint8_t x[kBlockSize],const uint8_t y[kBlockSize])119 bool AesEaxBoringSsl::EqualBlocks(const uint8_t x[kBlockSize],
120                                   const uint8_t y[kBlockSize]) {
121   uint64_t res = Load64(x) ^ Load64(y);
122   res |= Load64(x + 8) ^ Load64(y + 8);
123   return res == 0;
124 }
125 
IsValidKeySize(size_t key_size_in_bytes)126 bool AesEaxBoringSsl::IsValidKeySize(size_t key_size_in_bytes) {
127   return key_size_in_bytes == 16 || key_size_in_bytes == 32;
128 }
129 
IsValidNonceSize(size_t nonce_size_in_bytes)130 bool AesEaxBoringSsl::IsValidNonceSize(size_t nonce_size_in_bytes) {
131   return nonce_size_in_bytes == 12 || nonce_size_in_bytes == 16;
132 }
133 
ComputeB() const134 util::SecretData AesEaxBoringSsl::ComputeB() const {
135   util::SecretData block(kBlockSize, 0);
136   EncryptBlock(&block);
137   MultiplyByX(block.data(), block.data());
138   return block;
139 }
140 
ComputeP() const141 util::SecretData AesEaxBoringSsl::ComputeP() const {
142   util::SecretData rv(kBlockSize, 0);
143   MultiplyByX(B_.data(), rv.data());
144   return rv;
145 }
146 
New(const util::SecretData & key,size_t nonce_size_in_bytes)147 crypto::tink::util::StatusOr<std::unique_ptr<Aead>> AesEaxBoringSsl::New(
148     const util::SecretData& key, size_t nonce_size_in_bytes) {
149   auto status = internal::CheckFipsCompatibility<AesEaxBoringSsl>();
150   if (!status.ok()) return status;
151 
152   if (!IsValidKeySize(key.size())) {
153     return util::Status(absl::StatusCode::kInvalidArgument, "Invalid key size");
154   }
155   if (!IsValidNonceSize(nonce_size_in_bytes)) {
156     return util::Status(absl::StatusCode::kInvalidArgument,
157                         "Invalid nonce size");
158   }
159   auto aeskey_or = InitAesKey(key);
160   if (!aeskey_or.ok()) {
161     return aeskey_or.status();
162   }
163   return {absl::WrapUnique(
164       new AesEaxBoringSsl(std::move(aeskey_or).value(), nonce_size_in_bytes))};
165 }
166 
Pad(absl::Span<const uint8_t> data) const167 AesEaxBoringSsl::Block AesEaxBoringSsl::Pad(
168     absl::Span<const uint8_t> data) const {
169   // TODO(bleichen): What are we using in tink to encode assertions?
170   // The caller must ensure that data is no longer than a block.
171   // CHECK(0 <= len && len <= kBlockSize) << "Invalid data size";
172   Block padded_block;
173   padded_block.fill(0);
174   absl::c_copy(data, padded_block.begin());
175   if (data.size() == kBlockSize) {
176     XorBlock(B_.data(), &padded_block);
177   } else {
178     padded_block[data.size()] = 0x80;
179     XorBlock(P_.data(), &padded_block);
180   }
181   return padded_block;
182 }
183 
EncryptBlock(util::SecretData * block) const184 void AesEaxBoringSsl::EncryptBlock(util::SecretData* block) const {
185   AES_encrypt(block->data(), block->data(), aeskey_.get());
186 }
187 
EncryptBlock(Block * block) const188 void AesEaxBoringSsl::EncryptBlock(Block* block) const {
189   AES_encrypt(block->data(), block->data(), aeskey_.get());
190 }
191 
Omac(absl::string_view blob,int tag) const192 AesEaxBoringSsl::Block AesEaxBoringSsl::Omac(absl::string_view blob,
193                                              int tag) const {
194   return Omac(absl::MakeSpan(reinterpret_cast<const uint8_t*>(blob.data()),
195                              blob.size()),
196               tag);
197 }
198 
Omac(absl::Span<const uint8_t> data,int tag) const199 AesEaxBoringSsl::Block AesEaxBoringSsl::Omac(absl::Span<const uint8_t> data,
200                                              int tag) const {
201   Block mac;
202   mac.fill(0);
203   mac[15] = tag;
204   if (data.empty()) {
205     XorBlock(B_.data(), &mac);
206     EncryptBlock(&mac);
207     return mac;
208   }
209   EncryptBlock(&mac);
210   int idx = 0;
211   while (data.size() - idx > kBlockSize) {
212     XorBlock(&data[idx], &mac);
213     EncryptBlock(&mac);
214     idx += kBlockSize;
215   }
216   const Block padded_block = Pad(absl::MakeSpan(data).subspan(idx));
217   XorBlock(padded_block.data(), &mac);
218   EncryptBlock(&mac);
219   return mac;
220 }
221 
CtrCrypt(const Block & N,absl::string_view in,absl::Span<char> out) const222 util::Status AesEaxBoringSsl::CtrCrypt(const Block& N, absl::string_view in,
223                                absl::Span<char> out) const {
224   // Make a copy of N, since BoringSsl changes ctr.
225   uint8_t ctr[kBlockSize];
226   std::copy_n(N.begin(), kBlockSize, ctr);
227   return internal::AesCtr128Crypt(in, ctr, aeskey_.get(), out);
228 }
229 
Encrypt(absl::string_view plaintext,absl::string_view associated_data) const230 crypto::tink::util::StatusOr<std::string> AesEaxBoringSsl::Encrypt(
231     absl::string_view plaintext, absl::string_view associated_data) const {
232   // BoringSSL expects a non-null pointer for plaintext and associated_data,
233   // regardless of whether the size is 0.
234   plaintext = internal::EnsureStringNonNull(plaintext);
235   associated_data = internal::EnsureStringNonNull(associated_data);
236 
237   size_t ciphertext_size = plaintext.size() + nonce_size_ + kTagSize;
238   std::string ciphertext;
239   ResizeStringUninitialized(&ciphertext, ciphertext_size);
240   const std::string nonce = Random::GetRandomBytes(nonce_size_);
241   const Block N = Omac(nonce, 0);
242   const Block H = Omac(associated_data, 1);
243   uint8_t* ct_start = reinterpret_cast<uint8_t*>(&ciphertext[nonce_size_]);
244   util::Status res =
245       CtrCrypt(N, plaintext, absl::MakeSpan(ciphertext).subspan(nonce_size_));
246   if (!res.ok()) {
247     return res;
248   }
249   Block mac = Omac(absl::MakeSpan(ct_start, plaintext.size()), 2);
250   XorBlock(N.data(), &mac);
251   XorBlock(H.data(), &mac);
252   absl::c_copy(nonce, ciphertext.begin());
253   std::copy_n(mac.begin(), kTagSize, &ciphertext[ciphertext_size - kTagSize]);
254   return ciphertext;
255 }
256 
Decrypt(absl::string_view ciphertext,absl::string_view associated_data) const257 crypto::tink::util::StatusOr<std::string> AesEaxBoringSsl::Decrypt(
258     absl::string_view ciphertext, absl::string_view associated_data) const {
259   // BoringSSL expects a non-null pointer for associated_data,
260   // regardless of whether the size is 0.
261   associated_data = internal::EnsureStringNonNull(associated_data);
262 
263   size_t ct_size = ciphertext.size();
264   if (ct_size < nonce_size_ + kTagSize) {
265     return util::Status(absl::StatusCode::kInvalidArgument,
266                         "Ciphertext too short");
267   }
268   size_t out_size = ct_size - kTagSize - nonce_size_;
269   absl::string_view nonce = ciphertext.substr(0, nonce_size_);
270   absl::string_view encrypted = ciphertext.substr(nonce_size_, out_size);
271   absl::string_view tag = ciphertext.substr(ct_size - kTagSize, kTagSize);
272   const Block N = Omac(nonce, 0);
273   const Block H = Omac(associated_data, 1);
274   Block mac = Omac(encrypted, 2);
275   XorBlock(N.data(), &mac);
276   XorBlock(H.data(), &mac);
277   const uint8_t* sig = reinterpret_cast<const uint8_t*>(tag.data());
278   if (!EqualBlocks(mac.data(), sig)) {
279     return util::Status(absl::StatusCode::kInvalidArgument, "Tag mismatch");
280   }
281   std::string plaintext;
282   ResizeStringUninitialized(&plaintext, out_size);
283   util::Status res = CtrCrypt(N, encrypted, absl::MakeSpan(plaintext));
284   if (!res.ok()) {
285     return res;
286   }
287   return plaintext;
288 }
289 
290 }  // namespace subtle
291 }  // namespace tink
292 }  // namespace crypto
293