This repository has been archived by the owner on Apr 17, 2024. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The implementation uses old OpenSSL interfaces for AES. I haven't found a good way to use newer ones. Possibly, it is easier to just rewrite the code with intrinsics. PiperOrigin-RevId: 212435613 GitOrigin-RevId: 6176b392469957303d3ebb34af794d422f5f34df
- Loading branch information
Showing
4 changed files
with
649 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2017 Google Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
#ifndef TINK_DAEAD_H_ | ||
#define TINK_DAEAD_H_ | ||
|
||
#include "absl/strings/string_view.h" | ||
#include "tink/util/statusor.h" | ||
|
||
namespace crypto { | ||
namespace tink { | ||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// The interface for deterministic authenticated encryption with associated | ||
// data. | ||
// TODO(bleichen): Copy the interface from Java. | ||
// Check the properties: | ||
// - authenticated | ||
// - secure in multi-user setting | ||
// - thread safe/copy safe | ||
// References: | ||
// https://eprint.iacr.org/2016/1124.pdf | ||
class Daead { | ||
public: | ||
// Encrypts 'plaintext' with 'associated_data' as associated data | ||
// deterministically, and returns the resulting ciphertext. | ||
// The ciphertext allows for checking authenticity and integrity | ||
// of the associated data, but does not guarantee its secrecy. | ||
virtual crypto::tink::util::StatusOr<std::string> EncryptDeterministically( | ||
absl::string_view plaintext, | ||
absl::string_view associated_data) const = 0; | ||
|
||
// Decrypts 'ciphertext' with 'associated_data' as associated data, | ||
// and returns the resulting plaintext. | ||
// The decryption verifies the authenticity and integrity | ||
// of the associated data, but there are no guarantees wrt. secrecy | ||
// of that data. | ||
virtual crypto::tink::util::StatusOr<std::string> DecryptDeterministically( | ||
absl::string_view ciphertext, | ||
absl::string_view associated_data) const = 0; | ||
|
||
virtual ~Daead() {} | ||
}; | ||
|
||
} // namespace tink | ||
} // namespace crypto | ||
|
||
#endif // TINK_DAEAD_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
// Copyright 2017 Google Inc. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
/////////////////////////////////////////////////////////////////////////////// | ||
|
||
#include "tink/subtle/aes_siv_boringssl.h" | ||
|
||
#include <string> | ||
#include <vector> | ||
|
||
#include "tink/daead.h" | ||
#include "tink/util/errors.h" | ||
#include "tink/util/status.h" | ||
#include "tink/util/statusor.h" | ||
#include "openssl/err.h" | ||
#include "openssl/aes.h" | ||
|
||
namespace crypto { | ||
namespace tink { | ||
namespace subtle { | ||
|
||
static void XorBlock( | ||
const uint8_t x[16], | ||
const uint8_t y[16], | ||
uint8_t res[16]) { | ||
for (int i = 0; i < 16; i++) { | ||
res[i] = x[i] ^ y[i]; | ||
} | ||
} | ||
|
||
// static | ||
crypto::tink::util::StatusOr<std::unique_ptr<Daead>> AesSivBoringSsl::New( | ||
absl::string_view key_value) { | ||
std::unique_ptr<AesSivBoringSsl> aes_siv(new AesSivBoringSsl()); | ||
if (aes_siv->SetKey(key_value)) { | ||
return std::unique_ptr<Daead>(aes_siv.release()); | ||
} else { | ||
return util::Status(util::error::INTERNAL, "invalid key size"); | ||
} | ||
} | ||
|
||
bool AesSivBoringSsl::SetKey(absl::string_view key) { | ||
size_t key_size = key.size(); | ||
if (!IsValidKeySizeInBytes(key_size)) { | ||
return false; | ||
} | ||
if (0 != AES_set_encrypt_key( | ||
reinterpret_cast<const uint8_t*>(key.data()), | ||
4 * key_size, &k1_)) { | ||
return false; | ||
} | ||
if (0 != AES_set_encrypt_key( | ||
reinterpret_cast<const uint8_t*>(key.data() + key_size / 2), | ||
4 * key_size, &k2_)) { | ||
return false; | ||
} | ||
uint8_t block[BLOCK_SIZE]; | ||
memset(block, 0, BLOCK_SIZE); | ||
EncryptBlock(block, block); | ||
MultiplyByX(block); | ||
memcpy(cmac_k1_, block, BLOCK_SIZE); | ||
MultiplyByX(block); | ||
memcpy(cmac_k2_, block, BLOCK_SIZE); | ||
return true; | ||
} | ||
|
||
void AesSivBoringSsl::CtrCrypt(const uint8_t siv[BLOCK_SIZE], | ||
const uint8_t *in, uint8_t *out, | ||
size_t size) const { | ||
uint8_t iv[BLOCK_SIZE]; | ||
memcpy(iv, siv, BLOCK_SIZE); | ||
iv[8] &= 0x7f; | ||
iv[12] &= 0x7f; | ||
unsigned int num = 0; | ||
uint8_t ecount_buf[BLOCK_SIZE]; | ||
memset(ecount_buf, 0, BLOCK_SIZE); | ||
AES_ctr128_encrypt(in, out, size, &k2_, iv, ecount_buf, &num); | ||
} | ||
|
||
void AesSivBoringSsl::EncryptBlock(const uint8_t in[BLOCK_SIZE], | ||
uint8_t out[BLOCK_SIZE]) const { | ||
AES_encrypt(in, out, &k1_); | ||
} | ||
|
||
// static | ||
void AesSivBoringSsl::MultiplyByX(uint8_t block[BLOCK_SIZE]) { | ||
uint8_t carry = block[0] >> 7; | ||
for (size_t i = 0; i < BLOCK_SIZE - 1; i++) { | ||
block[i] = (block[i] << 1) | (block[i+1] >> 7); | ||
} | ||
block[BLOCK_SIZE - 1 ] = (block[BLOCK_SIZE - 1] << 1) ^ (carry ? 0x87 : 0); | ||
} | ||
|
||
void AesSivBoringSsl::Cmac(const uint8_t* data, size_t size, | ||
uint8_t mac[BLOCK_SIZE]) const { | ||
size_t blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; | ||
if (blocks == 0) { | ||
blocks = 1; | ||
} | ||
size_t last_block_size = size - BLOCK_SIZE * (blocks - 1); | ||
uint8_t block[BLOCK_SIZE]; | ||
memset(block, 0, BLOCK_SIZE); | ||
size_t idx = 0; | ||
for (size_t i = 0; i < blocks - 1; i++) { | ||
XorBlock(block, &data[idx], block); | ||
EncryptBlock(block, block); | ||
idx += BLOCK_SIZE; | ||
} | ||
for (size_t j = 0; j < last_block_size; j++) { | ||
block[j] ^= data[idx + j]; | ||
} | ||
if (last_block_size == BLOCK_SIZE) { | ||
XorBlock(block, cmac_k1_, block); | ||
} else { | ||
block[last_block_size] ^= 0x80; | ||
XorBlock(block, cmac_k2_, block); | ||
} | ||
EncryptBlock(block, mac); | ||
} | ||
|
||
// Computes Cmac(XorEnd(data, last)) | ||
void AesSivBoringSsl::CmacLong( | ||
const uint8_t* data, size_t size, const uint8_t last[BLOCK_SIZE], | ||
uint8_t mac[BLOCK_SIZE]) const { | ||
uint8_t block[BLOCK_SIZE]; | ||
memcpy(block, data, BLOCK_SIZE); | ||
size_t idx = BLOCK_SIZE; | ||
while (BLOCK_SIZE <= size - idx) { | ||
EncryptBlock(block, block); | ||
XorBlock(block, &data[idx], block); | ||
idx += BLOCK_SIZE; | ||
} | ||
size_t remaining = size - idx; | ||
for (int j = 0; j < BLOCK_SIZE - remaining; ++j) { | ||
block[remaining + j] ^= last[j]; | ||
} | ||
if (remaining == 0) { | ||
XorBlock(block, cmac_k1_, block); | ||
} else { | ||
EncryptBlock(block, block); | ||
for (int j = 0; j < remaining; ++j) { | ||
block[j] ^= last[BLOCK_SIZE - remaining + j]; | ||
block[j] ^= data[idx + j]; | ||
} | ||
block[remaining] ^= 0x80; | ||
XorBlock(block, cmac_k2_, block); | ||
} | ||
EncryptBlock(block, mac); | ||
} | ||
|
||
void AesSivBoringSsl::S2v(const uint8_t* aad, size_t aad_size, | ||
const uint8_t* msg, size_t msg_size, | ||
uint8_t siv[BLOCK_SIZE]) const { | ||
// This stuff could be precomputed. | ||
uint8_t block[BLOCK_SIZE]; | ||
memset(block, 0, BLOCK_SIZE); | ||
Cmac(block, BLOCK_SIZE, block); | ||
MultiplyByX(block); | ||
|
||
uint8_t aad_mac[BLOCK_SIZE]; | ||
Cmac(aad, aad_size, aad_mac); | ||
XorBlock(block, aad_mac, block); | ||
|
||
if (msg_size >= BLOCK_SIZE) { | ||
CmacLong(msg, msg_size, block, siv); | ||
} else { | ||
MultiplyByX(block); | ||
for (size_t i = 0; i < msg_size; i++) { | ||
block[i] ^= msg[i]; | ||
} | ||
block[msg_size] ^= 0x80; | ||
Cmac(block, BLOCK_SIZE, siv); | ||
} | ||
} | ||
|
||
util::StatusOr<std::string> AesSivBoringSsl::EncryptDeterministically( | ||
absl::string_view plaintext, | ||
absl::string_view additional_data) const { | ||
uint8_t siv[BLOCK_SIZE]; | ||
S2v(reinterpret_cast<const uint8_t*>(additional_data.data()), | ||
additional_data.size(), | ||
reinterpret_cast<const uint8_t*>(plaintext.data()), | ||
plaintext.size(), | ||
siv); | ||
size_t ciphertext_size = plaintext.size() + BLOCK_SIZE; | ||
std::vector<uint8_t> ct(ciphertext_size); | ||
memcpy(ct.data(), siv, BLOCK_SIZE); | ||
CtrCrypt(siv, reinterpret_cast<const uint8_t*>(plaintext.data()), | ||
ct.data() + BLOCK_SIZE, plaintext.size()); | ||
return std::string(reinterpret_cast<const char*>(ct.data()), ciphertext_size); | ||
} | ||
|
||
util::StatusOr<std::string> AesSivBoringSsl::DecryptDeterministically( | ||
absl::string_view ciphertext, | ||
absl::string_view additional_data) const { | ||
if (ciphertext.size() < BLOCK_SIZE) { | ||
return util::Status(util::error::INVALID_ARGUMENT, "ciphertext too short"); | ||
} | ||
size_t plaintext_size = ciphertext.size() - BLOCK_SIZE; | ||
std::vector<uint8_t> pt(plaintext_size); | ||
const uint8_t *siv = reinterpret_cast<const uint8_t*>(&ciphertext[0]); | ||
const uint8_t *ct = reinterpret_cast<const uint8_t*>(&ciphertext[BLOCK_SIZE]); | ||
CtrCrypt(siv, ct, pt.data(), plaintext_size); | ||
|
||
uint8_t s2v[BLOCK_SIZE]; | ||
S2v(reinterpret_cast<const uint8_t*>(additional_data.data()), | ||
additional_data.size(), pt.data(), plaintext_size, s2v); | ||
// Compare the siv from the ciphertext with the recomputed siv | ||
uint8_t diff = 0; | ||
for (int i = 0; i < BLOCK_SIZE; ++i) { | ||
diff |= siv[i] ^ s2v[i]; | ||
} | ||
if (diff != 0) { | ||
return util::Status(util::error::INVALID_ARGUMENT, "invalid ciphertext"); | ||
} | ||
return std::string(reinterpret_cast<const char*>(pt.data()), plaintext_size); | ||
} | ||
|
||
} // namespace subtle | ||
} // namespace tink | ||
} // namespace crypto | ||
|
Oops, something went wrong.