blob: 375e9cda4bc1a765ac57b99ddaede1baae3a70ba [file] [log] [blame]
// Copyright 2021 Google LLC.
//
// 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/aead/internal/ssl_aead.h"
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <iterator>
#include <limits>
#include <memory>
#include <string>
#include <unordered_set>
#include <vector>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "tink/aead/internal/wycheproof_aead.h"
#include "tink/config/tink_fips.h"
#include "tink/internal/ssl_util.h"
#include "tink/subtle/subtle_util.h"
#include "tink/util/secret_data.h"
#include "tink/util/statusor.h"
#include "tink/util/test_matchers.h"
namespace crypto {
namespace tink {
namespace internal {
namespace {
using ::crypto::tink::test::IsOk;
using ::crypto::tink::test::StatusIs;
using ::testing::AllOf;
using ::testing::Eq;
using ::testing::Not;
using ::testing::TestParamInfo;
using ::testing::TestWithParam;
using ::testing::ValuesIn;
constexpr absl::string_view kMessage = "Some data to encrypt.";
constexpr absl::string_view kAssociatedData = "Some associated data.";
// 128 bits key.
constexpr absl::string_view k128Key = "000102030405060708090a0b0c0d0e0f";
// 256 bits key.
constexpr absl::string_view k256Key =
"000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f";
// 12 bytes IV.
constexpr absl::string_view kAesGcmIvHex = "0123456789012345678901234";
// 24 bytes IV.
constexpr absl::string_view kXchacha20Poly1305IvHex =
"012345678901234567890123456789012345678901234567";
enum CipherType {
kAesGcm,
kAesGcmSiv,
kXchacha20Poly1305,
};
struct SslOneShotAeadTestParams {
std::string test_name;
CipherType cipher;
int tag_size;
absl::string_view iv_hex;
absl::string_view key_hex;
};
// Returns a SslOneShotAead from `cipher_name` and `key`.
util::StatusOr<std::unique_ptr<SslOneShotAead>> CipherFromName(
CipherType cipher, const util::SecretData& key) {
switch (cipher) {
case CipherType::kAesGcm: {
return CreateAesGcmOneShotCrypter(key);
}
case CipherType::kAesGcmSiv: {
return CreateAesGcmSivOneShotCrypter(key);
}
case CipherType::kXchacha20Poly1305: {
return CreateXchacha20Poly1305OneShotCrypter(key);
}
}
}
using SslOneShotAeadTest = TestWithParam<SslOneShotAeadTestParams>;
TEST_P(SslOneShotAeadTest, CiphertextPlaintextSize) {
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
EXPECT_EQ((*aead)->CiphertextSize(kMessage.size()),
kMessage.size() + test_param.tag_size);
EXPECT_EQ((*aead)->PlaintextSize(kMessage.size() + test_param.tag_size),
kMessage.size());
// Minimum size.
EXPECT_EQ((*aead)->PlaintextSize(test_param.tag_size), 0);
// Smaller than the minumum.
EXPECT_EQ((*aead)->PlaintextSize(0), 0);
}
// Tests that encryption of `message` with `associated_data`, and `iv` succeeds;
// writes the result in `ciphertext_buffer`.
void DoTestEncrypt(SslOneShotAead* aead, absl::string_view message,
absl::string_view associated_data, size_t tag_size,
absl::string_view iv, absl::Span<char> ciphertext_buffer) {
ASSERT_GE(ciphertext_buffer.size(), message.size() + tag_size);
util::StatusOr<int64_t> res = aead->Encrypt(
message, associated_data, iv, absl::MakeSpan(ciphertext_buffer));
ASSERT_THAT(res, IsOk());
EXPECT_EQ(*res, message.size() + tag_size);
}
// Tests that decryption of `ciphertext_buffer` with `associated_data` and `iv`
// succeeds and equals `message`.
void DoTestDecrypt(SslOneShotAead* aead, absl::string_view message,
absl::string_view associated_data, absl::string_view iv,
absl::string_view ciphertext_buffer) {
std::string plaintext_buff;
subtle::ResizeStringUninitialized(&plaintext_buff, message.size());
util::StatusOr<int64_t> written_bytes = aead->Decrypt(
ciphertext_buffer, associated_data, iv, absl::MakeSpan(plaintext_buff));
ASSERT_THAT(written_bytes, IsOk());
EXPECT_EQ(*written_bytes, message.size());
EXPECT_EQ(plaintext_buff, message);
}
TEST_P(SslOneShotAeadTest, EncryptDecrypt) {
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
std::string iv = absl::HexStringToBytes(test_param.iv_hex);
std::string ciphertext_buffer;
// Length of the message + tag.
subtle::ResizeStringUninitialized(&ciphertext_buffer,
(*aead)->CiphertextSize(kMessage.size()));
DoTestEncrypt(aead->get(), kMessage, kAssociatedData, test_param.tag_size, iv,
absl::MakeSpan(ciphertext_buffer));
DoTestDecrypt(aead->get(), kMessage, kAssociatedData, iv, ciphertext_buffer);
}
// Calculates a new string with the `position`'s byte modified.
std::string ModifyString(absl::string_view input_str, int position) {
std::string modified(input_str.data(), input_str.size());
modified[position / 8] ^= 1 << (position % 8);
return modified;
}
// Tests encryption/decryption with a modified ciphertext.
void DoTestEncryptDecryptWithModifiedCiphertext(SslOneShotAead* aead,
size_t tag_size,
absl::string_view iv) {
std::string ciphertext_buffer;
// Length of the message + tag.
subtle::ResizeStringUninitialized(&ciphertext_buffer,
kMessage.size() + tag_size);
util::StatusOr<int64_t> written_bytes = aead->Encrypt(
kMessage, kAssociatedData, iv, absl::MakeSpan(ciphertext_buffer));
ASSERT_THAT(written_bytes, IsOk());
EXPECT_EQ(*written_bytes, kMessage.size() + tag_size);
std::string plaintext_buffer;
subtle::ResizeStringUninitialized(&plaintext_buffer, kMessage.size());
// Modify the ciphertext.
for (size_t i = 0; i < ciphertext_buffer.size() * 8; i++) {
EXPECT_THAT(
aead->Decrypt(ModifyString(ciphertext_buffer, i), kAssociatedData, iv,
absl::MakeSpan(plaintext_buffer))
.status(),
Not(IsOk()))
<< i;
}
// Modify the associated data.
for (size_t i = 0; i < kAssociatedData.size() * 8; i++) {
EXPECT_THAT(
aead->Decrypt(ciphertext_buffer, ModifyString(kAssociatedData, i), iv,
absl::MakeSpan(plaintext_buffer))
.status(),
Not(IsOk()))
<< i;
}
// Truncate the ciphertext.
for (size_t i = 0; i < ciphertext_buffer.size(); i++) {
std::string truncated_ct(ciphertext_buffer, 0, i);
EXPECT_THAT(aead->Decrypt(truncated_ct, kAssociatedData, iv,
absl::MakeSpan(plaintext_buffer))
.status(),
Not(IsOk()))
<< i;
}
}
TEST_P(SslOneShotAeadTest, TestModification) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
DoTestEncryptDecryptWithModifiedCiphertext(
aead->get(), test_param.tag_size,
absl::HexStringToBytes(test_param.iv_hex));
}
// Make sure that the buffer passed in to the Decrypt routine is cleared if
// decryption fails.
TEST_P(SslOneShotAeadTest, TestBufferClearsIfDecryptionFails) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
const int64_t kCiphertextSize = kMessage.size() + test_param.tag_size;
std::string ciphertext_buffer;
// Length of the message + tag.
subtle::ResizeStringUninitialized(&ciphertext_buffer, kCiphertextSize);
std::string iv = absl::HexStringToBytes(test_param.iv_hex);
util::StatusOr<int64_t> written_bytes = (*aead)->Encrypt(
kMessage, kAssociatedData, iv, absl::MakeSpan(ciphertext_buffer));
ASSERT_THAT(written_bytes, IsOk());
EXPECT_EQ(*written_bytes, kCiphertextSize);
std::string plaintext_buffer;
subtle::ResizeStringUninitialized(&plaintext_buffer, kMessage.size());
const std::string kExpectedClearedPlaintext(plaintext_buffer.size(), '\0');
// Alter the tag.
for (int i = kCiphertextSize - test_param.tag_size; i < kCiphertextSize;
i++) {
std::string modified_ciphertext = ModifyString(ciphertext_buffer, i);
EXPECT_THAT((*aead)
->Decrypt(modified_ciphertext, kAssociatedData, iv,
absl::MakeSpan(plaintext_buffer))
.status(),
Not(IsOk()));
EXPECT_EQ(plaintext_buffer, kExpectedClearedPlaintext);
}
}
void TestDecryptWithEmptyAssociatedData(SslOneShotAead* aead,
absl::string_view ciphertext,
absl::string_view iv) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
std::string plaintext_buffer;
subtle::ResizeStringUninitialized(&plaintext_buffer, kMessage.size());
const absl::string_view empty_associated_data;
std::vector<absl::string_view> values = {empty_associated_data,
absl::string_view(), ""};
for (auto& associated_data : values) {
DoTestDecrypt(aead, kMessage, associated_data, iv, ciphertext);
}
}
void DoTestWithEmptyAssociatedData(SslOneShotAead* aead, absl::string_view iv,
size_t tag_size) {
const absl::string_view empty_associated_data;
std::vector<absl::string_view> values = {empty_associated_data,
absl::string_view(), ""};
for (auto& associated_data : values) {
std::string ciphertext_buffer;
subtle::ResizeStringUninitialized(&ciphertext_buffer,
kMessage.size() + tag_size);
DoTestEncrypt(aead, kMessage, associated_data, tag_size, iv,
absl::MakeSpan(ciphertext_buffer));
TestDecryptWithEmptyAssociatedData(aead, ciphertext_buffer, iv);
}
}
TEST_P(SslOneShotAeadTest, EmptyAssociatedData) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
DoTestWithEmptyAssociatedData(aead->get(),
absl::HexStringToBytes(test_param.iv_hex),
test_param.tag_size);
}
// string_views, with `iv` and `associated_data`.
void DoTestEmptyMessageEncryptDecrypt(
SslOneShotAead* aead, absl::string_view iv, size_t tag_size,
absl::string_view associated_data = kAssociatedData) {
std::string ciphertext_buffer;
subtle::ResizeStringUninitialized(&ciphertext_buffer, tag_size);
{ // Message is a null string_view.
const absl::string_view message;
DoTestEncrypt(aead, message, associated_data, tag_size, iv,
absl::MakeSpan(ciphertext_buffer));
DoTestDecrypt(aead, "", associated_data, iv, ciphertext_buffer);
}
{ // Message is an empty string.
const std::string message = "";
DoTestEncrypt(aead, message, associated_data, tag_size, iv,
absl::MakeSpan(ciphertext_buffer));
DoTestDecrypt(aead, "", associated_data, iv, ciphertext_buffer);
}
{ // Message is a default-constructed string_view.
DoTestEncrypt(aead, absl::string_view(), associated_data, tag_size, iv,
absl::MakeSpan(ciphertext_buffer));
DoTestDecrypt(aead, "", associated_data, iv, ciphertext_buffer);
}
}
TEST_P(SslOneShotAeadTest, EmptyMessage) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
std::string iv = absl::HexStringToBytes(test_param.iv_hex);
DoTestEmptyMessageEncryptDecrypt(aead->get(), iv, test_param.tag_size);
}
TEST_P(SslOneShotAeadTest, EmptyMessageAndAssociatedData) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
std::string iv = absl::HexStringToBytes(test_param.iv_hex);
const absl::string_view default_associated_data;
const absl::string_view empty_associated_data = "";
DoTestEmptyMessageEncryptDecrypt(aead->get(), iv, test_param.tag_size,
default_associated_data);
DoTestEmptyMessageEncryptDecrypt(aead->get(), iv, test_param.tag_size,
/*associated_data=*/absl::string_view());
DoTestEmptyMessageEncryptDecrypt(aead->get(), iv, test_param.tag_size,
empty_associated_data);
}
TEST_P(SslOneShotAeadTest, BufferOverlapEncryptFails) {
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
std::string ciphertext_buffer(kMessage.data(), kMessage.size());
subtle::ResizeStringUninitialized(&ciphertext_buffer,
(*aead)->CiphertextSize(kMessage.size()));
EXPECT_THAT(
(*aead)
->Encrypt(
absl::string_view(ciphertext_buffer).substr(0, kMessage.size()),
kAssociatedData, test_param.iv_hex,
absl::MakeSpan(ciphertext_buffer))
.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
TEST_P(SslOneShotAeadTest, BufferOverlapDecryptFails) {
SslOneShotAeadTestParams test_param = GetParam();
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead = CipherFromName(
test_param.cipher, util::SecretDataFromStringView(
absl::HexStringToBytes(test_param.key_hex)));
ASSERT_THAT(aead, IsOk());
std::string iv = absl::HexStringToBytes(test_param.iv_hex);
std::string ciphertext_buffer;
// Length of the message + tag.
subtle::ResizeStringUninitialized(&ciphertext_buffer,
(*aead)->CiphertextSize(kMessage.size()));
DoTestEncrypt(aead->get(), kMessage, kAssociatedData, test_param.tag_size, iv,
absl::MakeSpan(ciphertext_buffer));
EXPECT_THAT(
(*aead)
->Decrypt(
ciphertext_buffer, kAssociatedData, iv,
absl::MakeSpan(ciphertext_buffer).subspan(0, kMessage.size()))
.status(),
StatusIs(absl::StatusCode::kInvalidArgument));
}
std::vector<SslOneShotAeadTestParams> GetSslOneShotAeadTestParams() {
std::vector<SslOneShotAeadTestParams> params = {
{/*test_name=*/"AesGcm256", /*cipher=*/CipherType::kAesGcm,
/*tag_size=*/kAesGcmTagSizeInBytes,
/*iv_hex=*/kAesGcmIvHex,
/*key_hex=*/k256Key},
{/*test_name=*/"AesGcm128", /*cipher=*/CipherType::kAesGcm,
/*tag_size=*/kAesGcmTagSizeInBytes,
/*iv_hex=*/kAesGcmIvHex,
/*key_hex=*/k128Key}};
if (IsBoringSsl()) {
params.push_back({/*test_name=*/"AesGcmSiv256",
/*cipher=*/CipherType::kAesGcmSiv,
/*tag_size=*/kAesGcmTagSizeInBytes,
/*iv_hex=*/kAesGcmIvHex,
/*key_hex=*/k256Key});
params.push_back({/*test_name=*/"AesGcmSiv128",
/*cipher=*/CipherType::kAesGcmSiv,
/*tag_size=*/kAesGcmTagSizeInBytes,
/*iv_hex=*/kAesGcmIvHex,
/*key_hex=*/k128Key});
params.push_back({/*test_name=*/"Xchacha20Poly1305",
/*cipher=*/CipherType::kXchacha20Poly1305,
/*tag_size=*/kXchacha20Poly1305TagSizeInBytes,
/*iv_hex=*/kXchacha20Poly1305IvHex,
/*key_hex=*/k256Key});
}
return params;
}
INSTANTIATE_TEST_SUITE_P(
SslOneShotAeadTests, SslOneShotAeadTest,
testing::ValuesIn(GetSslOneShotAeadTestParams()),
[](const TestParamInfo<SslOneShotAeadTest::ParamType>& info) {
return info.param.test_name;
});
TEST(SslOneShotAeadTest, AesGcmTestInvalidKeySizes) {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
for (int keysize = 0; keysize < 65; keysize++) {
util::SecretData key(keysize, 'x');
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead =
CreateAesGcmOneShotCrypter(key);
if (keysize == 16 || keysize == 32) {
EXPECT_THAT(aead, IsOk()) << "with key size " << keysize;
} else {
EXPECT_THAT(aead, Not(IsOk())) << "with key size " << keysize;
}
}
}
TEST(SslOneShotAeadTest, AesGcmSivTestInvalidKeySizes) {
if (!IsBoringSsl()) {
GTEST_SKIP() << "AES-GCM-SIV not supported with OpenSSL";
}
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
for (int keysize = 0; keysize < 65; keysize++) {
util::SecretData key(keysize, 'x');
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead =
CreateAesGcmSivOneShotCrypter(key);
if (keysize == 16 || keysize == 32) {
EXPECT_THAT(aead, IsOk()) << "with key size " << keysize;
} else {
EXPECT_THAT(aead, Not(IsOk())) << "with key size " << keysize;
}
}
}
TEST(SslOneShotAeadTest, Xchacha20Poly1305TestInvalidKeySizes) {
if (!IsBoringSsl()) {
GTEST_SKIP() << "Xchacha20-Poly1305 not supported with OpenSSL";
}
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
for (int keysize = 0; keysize < 65; keysize++) {
util::SecretData key(keysize, 'x');
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead =
CreateXchacha20Poly1305OneShotCrypter(key);
if (keysize == 32) {
EXPECT_THAT(aead, IsOk()) << "with key size " << keysize;
} else {
EXPECT_THAT(aead, Not(IsOk())) << "with key size " << keysize;
}
}
}
TEST(SslOneShotAeadTest, Xchacha20Poly1305TestFipsOnly) {
if (!IsBoringSsl()) {
GTEST_SKIP() << "Xchacha20-Poly1305 not supported with OpenSSL";
}
if (!IsFipsModeEnabled()) {
GTEST_SKIP() << "Only supported in FIPS-only mode";
}
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead =
CreateXchacha20Poly1305OneShotCrypter(
util::SecretDataFromStringView(absl::HexStringToBytes(k256Key)));
EXPECT_THAT(aead.status(), StatusIs(absl::StatusCode::kInternal));
}
TEST(SslOneShotAeadTest, AesGcmTestFipsOnly) {
if (IsFipsModeEnabled() && !FIPS_mode()) {
GTEST_SKIP() << "Test should not run in FIPS mode when BoringCrypto is "
"unavailable.";
}
util::SecretData key_128 =
util::SecretDataFromStringView(absl::HexStringToBytes(k128Key));
util::SecretData key_256 =
util::SecretDataFromStringView(absl::HexStringToBytes(k256Key));
EXPECT_THAT(CreateAesGcmOneShotCrypter(key_128), IsOk());
EXPECT_THAT(CreateAesGcmOneShotCrypter(key_256), IsOk());
}
TEST(SslOneShotAeadTest, AesGcmTestTestFipsFailWithoutBoringCrypto) {
if (!IsFipsModeEnabled() || FIPS_mode()) {
GTEST_SKIP()
<< "Test assumes kOnlyUseFips but BoringCrypto is unavailable.";
}
util::SecretData key_128 =
util::SecretDataFromStringView(absl::HexStringToBytes(k128Key));
util::SecretData key_256 =
util::SecretDataFromStringView(absl::HexStringToBytes(k256Key));
EXPECT_THAT(CreateAesGcmOneShotCrypter(key_128).status(),
StatusIs(absl::StatusCode::kInternal));
EXPECT_THAT(CreateAesGcmOneShotCrypter(key_256).status(),
StatusIs(absl::StatusCode::kInternal));
}
TEST(AesGcmSivBoringSslTest, AesGcmTestSivTestFipsOnly) {
if (!IsFipsModeEnabled()) {
GTEST_SKIP() << "Only supported in FIPS-only mode";
}
util::SecretData key_128 =
util::SecretDataFromStringView(absl::HexStringToBytes(k128Key));
util::SecretData key_256 =
util::SecretDataFromStringView(absl::HexStringToBytes(k256Key));
EXPECT_THAT(CreateAesGcmSivOneShotCrypter(key_128).status(),
StatusIs(absl::StatusCode::kInternal));
EXPECT_THAT(CreateAesGcmSivOneShotCrypter(key_256).status(),
StatusIs(absl::StatusCode::kInternal));
}
// Parameters for SslOneShotAeadWycheproofTest.
struct SslOneShotAeadWycheproofTestParams {
std::string test_name;
CipherType cipher;
int nonce_size;
int tag_size;
absl::flat_hash_set<int> key_sizes;
WycheproofTestVector test_vector;
};
class SslOneShotAeadWycheproofTest
: public TestWithParam<SslOneShotAeadWycheproofTestParams> {
public:
void SetUp() override {
if (IsFipsModeEnabled()) {
GTEST_SKIP() << "Not supported in FIPS-only mode";
}
SslOneShotAeadWycheproofTestParams params = GetParam();
const WycheproofTestVector& test_vector = params.test_vector;
if (!params.key_sizes.contains(test_vector.key.size()) ||
test_vector.nonce.size() != params.nonce_size ||
test_vector.tag.size() != params.tag_size) {
GTEST_SKIP() << "Unsupported parameters; key size: "
<< test_vector.key.size()
<< " nonce size: " << test_vector.nonce.size()
<< " tag size: " << test_vector.tag.size();
}
}
};
TEST_P(SslOneShotAeadWycheproofTest, Encrypt) {
SslOneShotAeadWycheproofTestParams params = GetParam();
const WycheproofTestVector& test_vector = params.test_vector;
util::SecretData key = util::SecretDataFromStringView(test_vector.key);
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead =
CipherFromName(params.cipher, key);
ASSERT_THAT(aead, IsOk());
std::string ciphertext_and_tag =
absl::StrCat(test_vector.ct, test_vector.tag);
std::string ciphertext_buffer;
subtle::ResizeStringUninitialized(
&ciphertext_buffer, (*aead)->CiphertextSize(test_vector.msg.size()));
util::StatusOr<int64_t> written_bytes =
(*aead)->Encrypt(test_vector.msg, test_vector.aad, test_vector.nonce,
absl::MakeSpan(ciphertext_buffer));
std::string expected_ciphertext =
absl::StrCat(test_vector.ct, test_vector.tag);
std::cout << test_vector.expected << "\n";
if (test_vector.expected == "valid" || test_vector.expected == "acceptable") {
ASSERT_THAT(written_bytes, IsOk());
EXPECT_EQ(ciphertext_buffer, expected_ciphertext);
} else { // invalid.
// In this case, if the resulting ciphertext/tag are different, the
// testcase is correct.
if (written_bytes.ok()) {
EXPECT_THAT(ciphertext_buffer, Not(Eq(expected_ciphertext)));
} else {
GTEST_SUCCEED();
}
}
}
TEST_P(SslOneShotAeadWycheproofTest, Decrypt) {
SslOneShotAeadWycheproofTestParams params = GetParam();
const WycheproofTestVector& test_vector = params.test_vector;
util::SecretData key = util::SecretDataFromStringView(test_vector.key);
util::StatusOr<std::unique_ptr<SslOneShotAead>> aead =
CipherFromName(params.cipher, key);
ASSERT_THAT(aead, IsOk());
std::string ciphertext_and_tag =
absl::StrCat(test_vector.ct, test_vector.tag);
std::string plaintext_buffer;
subtle::ResizeStringUninitialized(
&plaintext_buffer, (*aead)->PlaintextSize(ciphertext_and_tag.size()));
util::StatusOr<int64_t> written_bytes = (*aead)->Decrypt(
absl::StrCat(test_vector.ct, test_vector.tag), test_vector.aad,
test_vector.nonce, absl::MakeSpan(plaintext_buffer));
if (written_bytes.ok()) {
EXPECT_NE(test_vector.expected, "invalid");
EXPECT_EQ(plaintext_buffer, test_vector.msg);
} else {
EXPECT_THAT(test_vector.expected, Not(AllOf(Eq("valid"), Eq("acceptable"))))
<< "Could not decrypt valid/acceptable tId: " << test_vector.id
<< " iv_size: " << test_vector.nonce.size()
<< " tag_size: " << test_vector.tag.size()
<< " key_size: " << key.size() << "; error: " << written_bytes.status();
}
}
std::vector<SslOneShotAeadWycheproofTestParams> GetWycheproofTestParams() {
std::vector<SslOneShotAeadWycheproofTestParams> params;
for (const WycheproofTestVector& test_vector :
ReadWycheproofTestVectors("aes_gcm_test.json")) {
params.push_back({/*test_name=*/"AesGcm",
/*cipher_name=*/CipherType::kAesGcm,
/*nonce_size=*/12,
/*tag_size=*/16,
/*key_sizes=*/{16, 32}, test_vector});
}
if (IsBoringSsl()) {
for (const WycheproofTestVector& test_vector :
ReadWycheproofTestVectors("aes_gcm_siv_test.json")) {
params.push_back({/*test_name=*/"AesGcmSiv",
/*cipher_name=*/CipherType::kAesGcmSiv,
/*nonce_size=*/12,
/*tag_size=*/16,
/*key_sizes=*/{16, 32}, test_vector});
}
for (const WycheproofTestVector& test_vector :
ReadWycheproofTestVectors("xchacha20_poly1305_test.json")) {
params.push_back({/*test_name=*/"Xchacha20Poly1305",
/*cipher_name=*/CipherType::kXchacha20Poly1305,
/*nonce_size=*/24,
/*tag_size=*/16,
/*key_sizes=*/{32}, test_vector});
}
}
return params;
}
INSTANTIATE_TEST_SUITE_P(
SslOneShotAeadWycheproofTests, SslOneShotAeadWycheproofTest,
ValuesIn(GetWycheproofTestParams()),
[](const TestParamInfo<SslOneShotAeadWycheproofTest::ParamType>& info) {
return absl::StrCat(info.param.test_name, "Tid",
info.param.test_vector.id);
});
} // namespace
} // namespace internal
} // namespace tink
} // namespace crypto