xref: /aosp_15_r20/external/tink/cc/subtle/aes_siv_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_siv_boringssl.h"
18 
19 #include <algorithm>
20 #include <cstdint>
21 #include <iterator>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 
26 #include "absl/memory/memory.h"
27 #include "absl/status/status.h"
28 #include "absl/strings/string_view.h"
29 #include "absl/types/span.h"
30 #include "openssl/aes.h"
31 #include "openssl/crypto.h"
32 #include "tink/aead/internal/aead_util.h"
33 #include "tink/deterministic_aead.h"
34 #include "tink/internal/aes_util.h"
35 #include "tink/subtle/subtle_util.h"
36 #include "tink/util/errors.h"
37 #include "tink/util/status.h"
38 #include "tink/util/statusor.h"
39 
40 namespace crypto {
41 namespace tink {
42 namespace subtle {
43 namespace {
44 
InitializeAesKey(absl::Span<const uint8_t> key)45 crypto::tink::util::StatusOr<util::SecretUniquePtr<AES_KEY>> InitializeAesKey(
46     absl::Span<const uint8_t> key) {
47   util::SecretUniquePtr<AES_KEY> aes_key = util::MakeSecretUniquePtr<AES_KEY>();
48   if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
49                           8 * key.size(), aes_key.get()) != 0) {
50     return util::Status(absl::StatusCode::kInternal,
51                         "could not initialize aes key");
52   }
53   return std::move(aes_key);
54 }
55 
56 }  // namespace
57 
58 // static
59 crypto::tink::util::StatusOr<std::unique_ptr<DeterministicAead>>
New(const util::SecretData & key)60 AesSivBoringSsl::New(const util::SecretData& key) {
61   auto status = internal::CheckFipsCompatibility<AesSivBoringSsl>();
62   if (!status.ok()) return status;
63 
64   if (!IsValidKeySizeInBytes(key.size())) {
65     return util::Status(absl::StatusCode::kInvalidArgument, "invalid key size");
66   }
67   auto k1_or = InitializeAesKey(absl::MakeSpan(key).subspan(0, key.size() / 2));
68   if (!k1_or.ok()) {
69     return k1_or.status();
70   }
71   util::SecretUniquePtr<AES_KEY> k1 = std::move(k1_or).value();
72   auto k2_or = InitializeAesKey(absl::MakeSpan(key).subspan(key.size() / 2));
73   if (!k2_or.ok()) {
74     return k2_or.status();
75   }
76 
77   util::SecretUniquePtr<AES_KEY> k2 = std::move(k2_or).value();
78   return {absl::WrapUnique(new AesSivBoringSsl(std::move(k1), std::move(k2)))};
79 }
80 
ComputeCmacK1() const81 util::SecretData AesSivBoringSsl::ComputeCmacK1() const {
82   util::SecretData cmac_k1(kBlockSize, 0);
83   EncryptBlock(cmac_k1.data(), cmac_k1.data());
84   MultiplyByX(cmac_k1.data());
85   return cmac_k1;
86 }
87 
ComputeCmacK2() const88 util::SecretData AesSivBoringSsl::ComputeCmacK2() const {
89   util::SecretData cmac_k2(cmac_k1_);
90   MultiplyByX(cmac_k2.data());
91   return cmac_k2;
92 }
93 
EncryptBlock(const uint8_t in[kBlockSize],uint8_t out[kBlockSize]) const94 void AesSivBoringSsl::EncryptBlock(const uint8_t in[kBlockSize],
95                                    uint8_t out[kBlockSize]) const {
96   AES_encrypt(in, out, k1_.get());
97 }
98 
99 // static
MultiplyByX(uint8_t block[kBlockSize])100 void AesSivBoringSsl::MultiplyByX(uint8_t block[kBlockSize]) {
101   // Carry over 0x87 if msb is 1 0x00 if msb is 0.
102   uint8_t carry = 0x87 & -(block[0] >> 7);
103   for (size_t i = 0; i < kBlockSize - 1; ++i) {
104     block[i] = (block[i] << 1) | (block[i + 1] >> 7);
105   }
106   block[kBlockSize - 1] =
107       (block[kBlockSize - 1] << 1) ^ carry;
108 }
109 
110 // static
XorBlock(const uint8_t x[kBlockSize],const uint8_t y[kBlockSize],uint8_t res[kBlockSize])111 void AesSivBoringSsl::XorBlock(const uint8_t x[kBlockSize],
112                                const uint8_t y[kBlockSize],
113                                uint8_t res[kBlockSize]) {
114   for (int i = 0; i < kBlockSize; ++i) {
115     res[i] = x[i] ^ y[i];
116   }
117 }
118 
Cmac(absl::Span<const uint8_t> data,uint8_t mac[kBlockSize]) const119 void AesSivBoringSsl::Cmac(absl::Span<const uint8_t> data,
120                            uint8_t mac[kBlockSize]) const {
121   const size_t blocks =
122       std::max(size_t{1}, (data.size() + kBlockSize - 1) / kBlockSize);
123   const size_t last_block_idx = kBlockSize * (blocks - 1);
124   const size_t last_block_size = data.size() - last_block_idx;
125   uint8_t block[kBlockSize];
126   std::fill(std::begin(block), std::end(block), 0);
127   for (size_t idx = 0; idx < last_block_idx; idx += kBlockSize) {
128     XorBlock(block, &data[idx], block);
129     EncryptBlock(block, block);
130   }
131   for (size_t j = 0; j < last_block_size; j++) {
132     block[j] ^= data[last_block_idx + j];
133   }
134   if (last_block_size == kBlockSize) {
135     XorBlock(block, cmac_k1_.data(), block);
136   } else {
137     block[last_block_size] ^= 0x80;
138     XorBlock(block, cmac_k2_.data(), block);
139   }
140   EncryptBlock(block, mac);
141 }
142 
143 // Computes Cmac(XorEnd(data, last))
CmacLong(absl::Span<const uint8_t> data,const uint8_t last[kBlockSize],uint8_t mac[kBlockSize]) const144 void AesSivBoringSsl::CmacLong(absl::Span<const uint8_t> data,
145                                const uint8_t last[kBlockSize],
146                                uint8_t mac[kBlockSize]) const {
147   uint8_t block[kBlockSize];
148   std::copy_n(data.begin(), kBlockSize, block);
149   size_t idx = kBlockSize;
150   while (kBlockSize <= data.size() - idx) {
151     EncryptBlock(block, block);
152     XorBlock(block, &data[idx], block);
153     idx += kBlockSize;
154   }
155   size_t remaining = data.size() - idx;
156   for (int j = 0; j < kBlockSize - remaining; ++j) {
157     block[remaining + j] ^= last[j];
158   }
159   if (remaining == 0) {
160     XorBlock(block, cmac_k1_.data(), block);
161   } else {
162     EncryptBlock(block, block);
163     for (int j = 0; j < remaining; ++j) {
164       block[j] ^= last[kBlockSize - remaining + j];
165       block[j] ^= data[idx + j];
166     }
167     block[remaining] ^= 0x80;
168     XorBlock(block, cmac_k2_.data(), block);
169   }
170   EncryptBlock(block, mac);
171 }
172 
S2v(absl::Span<const uint8_t> aad,absl::Span<const uint8_t> msg,uint8_t siv[kBlockSize]) const173 void AesSivBoringSsl::S2v(absl::Span<const uint8_t> aad,
174                           absl::Span<const uint8_t> msg,
175                           uint8_t siv[kBlockSize]) const {
176   // This stuff could be precomputed.
177   uint8_t block[kBlockSize];
178   std::fill(std::begin(block), std::end(block), 0);
179   Cmac(block, block);
180   MultiplyByX(block);
181 
182   uint8_t aad_mac[kBlockSize];
183   Cmac(aad, aad_mac);
184   XorBlock(block, aad_mac, block);
185 
186   if (msg.size() >= kBlockSize) {
187     CmacLong(msg, block, siv);
188   } else {
189     MultiplyByX(block);
190     for (size_t i = 0; i < msg.size(); ++i) {
191       block[i] ^= msg[i];
192     }
193     block[msg.size()] ^= 0x80;
194     Cmac(block, siv);
195   }
196 }
197 
AesCtrCrypt(absl::string_view in,const uint8_t siv[kBlockSize],const AES_KEY * key,absl::Span<char> out) const198 util::Status AesSivBoringSsl::AesCtrCrypt(absl::string_view in,
199                                           const uint8_t siv[kBlockSize],
200                                           const AES_KEY* key,
201                                           absl::Span<char> out) const {
202   uint8_t iv[kBlockSize];
203   std::copy_n(siv, kBlockSize, iv);
204   iv[8] &= 0x7f;
205   iv[12] &= 0x7f;
206   return internal::AesCtr128Crypt(in, iv, key, out);
207 }
208 
EncryptDeterministically(absl::string_view plaintext,absl::string_view associated_data) const209 util::StatusOr<std::string> AesSivBoringSsl::EncryptDeterministically(
210     absl::string_view plaintext, absl::string_view associated_data) const {
211   uint8_t siv[kBlockSize];
212   S2v(absl::MakeSpan(reinterpret_cast<const uint8_t*>(associated_data.data()),
213                      associated_data.size()),
214       absl::MakeSpan(reinterpret_cast<const uint8_t*>(plaintext.data()),
215                      plaintext.size()),
216       siv);
217   size_t ciphertext_size = plaintext.size() + kBlockSize;
218 
219   std::string ciphertext;
220   ResizeStringUninitialized(&ciphertext, ciphertext_size);
221   std::copy(std::begin(siv), std::end(siv), ciphertext.begin());
222   util::Status res =
223       AesCtrCrypt(plaintext, siv, k2_.get(),
224                   absl::MakeSpan(ciphertext).subspan(kBlockSize));
225   if (!res.ok()) {
226     return res;
227   }
228   return ciphertext;
229 }
230 
DecryptDeterministically(absl::string_view ciphertext,absl::string_view associated_data) const231 util::StatusOr<std::string> AesSivBoringSsl::DecryptDeterministically(
232     absl::string_view ciphertext, absl::string_view associated_data) const {
233   if (ciphertext.size() < kBlockSize) {
234     return util::Status(absl::StatusCode::kInvalidArgument,
235                         "ciphertext too short");
236   }
237   size_t plaintext_size = ciphertext.size() - kBlockSize;
238   std::string plaintext;
239   ResizeStringUninitialized(&plaintext, plaintext_size);
240   const uint8_t* siv = reinterpret_cast<const uint8_t*>(&ciphertext[0]);
241   util::Status res = AesCtrCrypt(ciphertext.substr(kBlockSize), siv, k2_.get(),
242                                  absl::MakeSpan(plaintext));
243   if (!res.ok()) {
244     return res;
245   }
246 
247   uint8_t s2v[kBlockSize];
248   S2v(absl::MakeSpan(reinterpret_cast<const uint8_t*>(associated_data.data()),
249                      associated_data.size()),
250       absl::MakeSpan(reinterpret_cast<const uint8_t*>(plaintext.data()),
251                      plaintext_size),
252       s2v);
253   if (CRYPTO_memcmp(siv, s2v, kBlockSize) != 0) {
254     return util::Status(absl::StatusCode::kInvalidArgument,
255                         "invalid ciphertext");
256   }
257   return plaintext;
258 }
259 
260 }  // namespace subtle
261 }  // namespace tink
262 }  // namespace crypto
263