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