Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 130 additions & 16 deletions crypto/cipher_extra/aead_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <stdint.h>
#include <string.h>

#include <new>
#include <vector>

#include <gtest/gtest.h>
Expand Down Expand Up @@ -45,6 +46,11 @@ constexpr uint32_t kNondeterministic = 1 << 7;
// <openssl/aead.h>. The two must be synchronized.
constexpr uint32_t kConcurrent = 1 << 12;

// kCanCopy indicates that the AEAD supports duplicating an initialized
// |EVP_AEAD_CTX| with |EVP_AEAD_CTX_copy|. AEADs whose state owns external
// resources (e.g. the TLS record-layer AEADs) do not set this.
constexpr uint32_t kCanCopy = 1 << 13;

// RequiresADLength encodes an AD length requirement into flags.
constexpr uint32_t RequiresADLength(size_t length) {
// If we had a more recent C++ version we could assert that the length is
Expand Down Expand Up @@ -79,41 +85,41 @@ struct KnownAEAD {

static const struct KnownAEAD kAEADs[] = {
{"AES_128_GCM", EVP_aead_aes_128_gcm, "aes_128_gcm_tests.txt",
kCanTruncateTags | kVariableNonce | kConcurrent},
kCanTruncateTags | kVariableNonce | kConcurrent | kCanCopy},

{"AES_128_GCM_NIST", EVP_aead_aes_128_gcm, "nist_cavp/aes_128_gcm.txt",
kCanTruncateTags | kVariableNonce | kConcurrent},
kCanTruncateTags | kVariableNonce | kConcurrent | kCanCopy},

{"AES_192_GCM", EVP_aead_aes_192_gcm, "aes_192_gcm_tests.txt",
kCanTruncateTags | kVariableNonce | kConcurrent},
kCanTruncateTags | kVariableNonce | kConcurrent | kCanCopy},

{"AES_256_GCM", EVP_aead_aes_256_gcm, "aes_256_gcm_tests.txt",
kCanTruncateTags | kVariableNonce | kConcurrent},
kCanTruncateTags | kVariableNonce | kConcurrent | kCanCopy},

{"AES_256_GCM_NIST", EVP_aead_aes_256_gcm, "nist_cavp/aes_256_gcm.txt",
kCanTruncateTags | kVariableNonce | kConcurrent},
kCanTruncateTags | kVariableNonce | kConcurrent | kCanCopy},

{"AES_128_GCM_SIV", EVP_aead_aes_128_gcm_siv, "aes_128_gcm_siv_tests.txt",
kConcurrent},
kConcurrent | kCanCopy},

{"AES_256_GCM_SIV", EVP_aead_aes_256_gcm_siv, "aes_256_gcm_siv_tests.txt",
kConcurrent},
kConcurrent | kCanCopy},

{"AES_128_GCM_RandomNonce", EVP_aead_aes_128_gcm_randnonce,
"aes_128_gcm_randnonce_tests.txt",
kNondeterministic | kCanTruncateTags | RequiresMinimumTagLength(13) |
kConcurrent},
kConcurrent | kCanCopy},

{"AES_256_GCM_RandomNonce", EVP_aead_aes_256_gcm_randnonce,
"aes_256_gcm_randnonce_tests.txt",
kNondeterministic | kCanTruncateTags | RequiresMinimumTagLength(13) |
kConcurrent},
kConcurrent | kCanCopy},

{"ChaCha20Poly1305", EVP_aead_chacha20_poly1305,
"chacha20_poly1305_tests.txt", kCanTruncateTags | kConcurrent},
"chacha20_poly1305_tests.txt", kCanTruncateTags | kConcurrent | kCanCopy},

{"XChaCha20Poly1305", EVP_aead_xchacha20_poly1305,
"xchacha20_poly1305_tests.txt", kCanTruncateTags | kConcurrent},
"xchacha20_poly1305_tests.txt", kCanTruncateTags | kConcurrent | kCanCopy},

{"AES_128_CBC_SHA1_TLS", EVP_aead_aes_128_cbc_sha1_tls,
"aes_128_cbc_sha1_tls_tests.txt",
Expand Down Expand Up @@ -156,19 +162,19 @@ static const struct KnownAEAD kAEADs[] = {
kLimitedImplementation | RequiresADLength(11)},

{"AES_128_CTR_HMAC_SHA256", EVP_aead_aes_128_ctr_hmac_sha256,
"aes_128_ctr_hmac_sha256.txt", kCanTruncateTags | kConcurrent},
"aes_128_ctr_hmac_sha256.txt", kCanTruncateTags | kConcurrent | kCanCopy},

{"AES_256_CTR_HMAC_SHA256", EVP_aead_aes_256_ctr_hmac_sha256,
"aes_256_ctr_hmac_sha256.txt", kCanTruncateTags | kConcurrent},
"aes_256_ctr_hmac_sha256.txt", kCanTruncateTags | kConcurrent | kCanCopy},

{"AES_128_CCM_BLUETOOTH", EVP_aead_aes_128_ccm_bluetooth,
"aes_128_ccm_bluetooth_tests.txt", kConcurrent},
"aes_128_ccm_bluetooth_tests.txt", kConcurrent | kCanCopy},

{"AES_128_CCM_BLUETOOTH_8", EVP_aead_aes_128_ccm_bluetooth_8,
"aes_128_ccm_bluetooth_8_tests.txt", kConcurrent},
"aes_128_ccm_bluetooth_8_tests.txt", kConcurrent | kCanCopy},

{"AES_128_CCM_Matter", EVP_aead_aes_128_ccm_matter,
"aes_128_ccm_matter_tests.txt", kConcurrent},
"aes_128_ccm_matter_tests.txt", kConcurrent | kCanCopy},
};

class PerAEADTest : public testing::TestWithParam<KnownAEAD> {
Expand Down Expand Up @@ -286,6 +292,114 @@ TEST_P(PerAEADTest, TestVector) {
});
}

TEST_P(PerAEADTest, Copy) {
const uint32_t flags = GetParam().flags;
const bool expect_copyable = (flags & kCanCopy) != 0;

std::vector<uint8_t> key(EVP_AEAD_key_length(aead()), 'k');
const size_t tag_len = EVP_AEAD_DEFAULT_TAG_LENGTH;

// Copying an uninitialized context always fails, regardless of the AEAD.
{
bssl::ScopedEVP_AEAD_CTX uninitialized;
bssl::ScopedEVP_AEAD_CTX dst;
EXPECT_FALSE(EVP_AEAD_CTX_copy(dst.get(), uninitialized.get()));
ERR_clear_error();
}

// Use |init_with_direction| so this also works for the direction-bound TLS
// AEADs, which do not implement |init|.
bssl::ScopedEVP_AEAD_CTX src;
ASSERT_TRUE(EVP_AEAD_CTX_init_with_direction(
src.get(), aead(), key.data(), key.size(), tag_len, evp_aead_seal));

// The result of |EVP_AEAD_CTX_copy| must agree with the advertised
// capability. This keeps |kCanCopy| and the AEAD's |copy| hook in sync.
{
bssl::ScopedEVP_AEAD_CTX dst;
const int copied = EVP_AEAD_CTX_copy(dst.get(), src.get());
ASSERT_EQ(copied == 1, expect_copyable);
}
if (!expect_copyable) {
ERR_clear_error();
return;
}

static const uint8_t kPlaintext[] = "EVP_AEAD_CTX_copy test plaintext";
static const uint8_t kAD[] = {1, 2, 3, 4, 5, 6, 7, 8};
std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(aead()), 'n');

auto seal = [&](const EVP_AEAD_CTX *ctx, std::vector<uint8_t> *out) -> bool {
out->resize(sizeof(kPlaintext) + EVP_AEAD_max_overhead(aead()));
size_t out_len = 0;
if (!EVP_AEAD_CTX_seal(ctx, out->data(), &out_len, out->size(),
nonce.data(), nonce.size(), kPlaintext,
sizeof(kPlaintext), kAD, sizeof(kAD))) {
return false;
}
out->resize(out_len);
return true;
};

auto open_and_check = [&](const EVP_AEAD_CTX *ctx,
const std::vector<uint8_t> &ct) -> bool {
std::vector<uint8_t> out(ct.size());
size_t out_len = 0;
if (!EVP_AEAD_CTX_open(ctx, out.data(), &out_len, out.size(), nonce.data(),
nonce.size(), ct.data(), ct.size(), kAD,
sizeof(kAD))) {
return false;
}
return out_len == sizeof(kPlaintext) &&
OPENSSL_memcmp(out.data(), kPlaintext, sizeof(kPlaintext)) == 0;
};

// A copy must be usable independently of the source, in both directions.
bssl::ScopedEVP_AEAD_CTX copy;
ASSERT_TRUE(EVP_AEAD_CTX_copy(copy.get(), src.get()));

std::vector<uint8_t> ct_src, ct_copy;
ASSERT_TRUE(seal(src.get(), &ct_src));
ASSERT_TRUE(seal(copy.get(), &ct_copy));
EXPECT_TRUE(open_and_check(copy.get(), ct_src));
EXPECT_TRUE(open_and_check(src.get(), ct_copy));

// For deterministic AEADs the copy must reproduce the source byte-for-byte.
if (!(flags & kNondeterministic)) {
EXPECT_EQ(Bytes(ct_src), Bytes(ct_copy));
}

// Self-copy is a no-op and must leave the context usable.
ASSERT_TRUE(EVP_AEAD_CTX_copy(src.get(), src.get()));
std::vector<uint8_t> ct_self;
ASSERT_TRUE(seal(src.get(), &ct_self));
EXPECT_TRUE(open_and_check(src.get(), ct_self));

// Copy between contexts whose |state| fields have different 16-byte
// alignment. |a| is 16-byte aligned; |b| is placed a further 8 bytes past a
// 16-byte boundary so exactly one of the two has an over-aligned |state|.
// This exercises the |state_offset| relocation path that AES-GCM-SIV's
// assembly implementation depends on; a verbatim |state| copy would silently
// break there. The two contexts must not overlap.
alignas(16) uint8_t storage[3 * sizeof(EVP_AEAD_CTX) + 32];
const size_t b_off =
((sizeof(EVP_AEAD_CTX) + 15) & ~static_cast<size_t>(15)) + 8;
EVP_AEAD_CTX *a = new (storage) EVP_AEAD_CTX;
EVP_AEAD_CTX *b = new (storage + b_off) EVP_AEAD_CTX;
EVP_AEAD_CTX_zero(a);
EVP_AEAD_CTX_zero(b);
ASSERT_TRUE(EVP_AEAD_CTX_init_with_direction(
a, aead(), key.data(), key.size(), tag_len, evp_aead_seal));
ASSERT_TRUE(EVP_AEAD_CTX_copy(b, a));
std::vector<uint8_t> ct_a, ct_b;
ASSERT_TRUE(seal(a, &ct_a));
ASSERT_TRUE(seal(b, &ct_b));
EXPECT_TRUE(open_and_check(b, ct_a));
EXPECT_TRUE(open_and_check(a, ct_b));
EVP_AEAD_CTX_cleanup(a);
EVP_AEAD_CTX_cleanup(b);
}

struct KnownTLSLegacyAEAD {
const char name[40];
const EVP_CIPHER *(*func)(void);
Expand Down
2 changes: 2 additions & 0 deletions crypto/cipher_extra/e_aesctrhmac.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ static const EVP_AEAD aead_aes_128_ctr_hmac_sha256 = {
NULL /* tag_len */,
NULL /* serialize_state */,
NULL /* deserialize_state */,
aead_ctx_copy_state_trivial /* copy */,
};

static const EVP_AEAD aead_aes_256_ctr_hmac_sha256 = {
Expand All @@ -264,6 +265,7 @@ static const EVP_AEAD aead_aes_256_ctr_hmac_sha256 = {
NULL /* tag_len */,
NULL /* serialize_state */,
NULL /* deserialize_state */,
aead_ctx_copy_state_trivial /* copy */,
};

const EVP_AEAD *EVP_aead_aes_128_ctr_hmac_sha256(void) {
Expand Down
22 changes: 22 additions & 0 deletions crypto/cipher_extra/e_aesgcmsiv.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ static int aead_aes_gcm_siv_asm_init(EVP_AEAD_CTX *ctx, const uint8_t *key,

static void aead_aes_gcm_siv_asm_cleanup(EVP_AEAD_CTX *ctx) {}

static int aead_aes_gcm_siv_asm_copy(EVP_AEAD_CTX *out,
const EVP_AEAD_CTX *in) {
// The asm context is over-aligned within |state| and is located via
// |state_offset|, which is derived from the runtime address of |state|.
// A verbatim copy of |state| would misplace it whenever |out| and |in| have
// different alignment, so re-derive the offset for |out| and relocate the
// sub-context to the destination's aligned slot.
out->state_offset = (uint8_t)(((uintptr_t)&out->state) & 8);
struct aead_aes_gcm_siv_asm_ctx *out_ctx = asm_ctx_from_ctx(out);
const struct aead_aes_gcm_siv_asm_ctx *in_ctx = asm_ctx_from_ctx(in);
if (in_ctx == NULL) {
OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_INITIALIZATION_ERROR);
return 0;
}
OPENSSL_memcpy(out_ctx, in_ctx, sizeof(*out_ctx));
return 1;
}

// aesgcmsiv_polyval_horner updates the POLYVAL value in |in_out_poly| to
// include a number (|in_blocks|) of 16-byte blocks of data from |in|, given
// the POLYVAL key in |key|.
Expand Down Expand Up @@ -516,6 +534,7 @@ static const EVP_AEAD aead_aes_128_gcm_siv_asm = {
NULL /* tag_len */,
NULL /* serialize_state */,
NULL /* deserialize_state */,
aead_aes_gcm_siv_asm_copy /* copy */,
};

static const EVP_AEAD aead_aes_256_gcm_siv_asm = {
Expand All @@ -536,6 +555,7 @@ static const EVP_AEAD aead_aes_256_gcm_siv_asm = {
NULL /* tag_len */,
NULL /* serialize_state */,
NULL /* deserialize_state */,
aead_aes_gcm_siv_asm_copy /* copy */,
};

#endif // X86_64 && !NO_ASM && !WINDOWS
Expand Down Expand Up @@ -805,6 +825,7 @@ static const EVP_AEAD aead_aes_128_gcm_siv = {
NULL /* tag_len */,
NULL /* serialize_state */,
NULL /* deserialize_state */,
aead_ctx_copy_state_trivial /* copy */,
};

static const EVP_AEAD aead_aes_256_gcm_siv = {
Expand All @@ -825,6 +846,7 @@ static const EVP_AEAD aead_aes_256_gcm_siv = {
NULL /* tag_len */,
NULL /* serialize_state */,
NULL /* deserialize_state */,
aead_ctx_copy_state_trivial /* copy */,
};

#if defined(AES_GCM_SIV_ASM)
Expand Down
2 changes: 2 additions & 0 deletions crypto/cipher_extra/e_chacha20poly1305.c
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ static const EVP_AEAD aead_chacha20_poly1305 = {
NULL, // tag_len
NULL, // serialize_state
NULL, // deserialize_state
aead_ctx_copy_state_trivial, // copy
};

static const EVP_AEAD aead_xchacha20_poly1305 = {
Expand All @@ -363,6 +364,7 @@ static const EVP_AEAD aead_xchacha20_poly1305 = {
NULL, // tag_len
NULL, // serialize_state
NULL, // deserialize_state
aead_ctx_copy_state_trivial, // copy
};

const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
Expand Down
10 changes: 10 additions & 0 deletions crypto/cipher_extra/e_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha1_tls = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_aes_128_cbc_sha1_tls_implicit_iv = {
Expand All @@ -504,6 +505,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha1_tls_implicit_iv = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_aes_256_cbc_sha1_tls = {
Expand All @@ -524,6 +526,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha1_tls = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_aes_256_cbc_sha1_tls_implicit_iv = {
Expand All @@ -544,6 +547,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha1_tls_implicit_iv = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_aes_128_cbc_sha256_tls = {
Expand All @@ -564,6 +568,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha256_tls = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_aes_128_cbc_sha256_tls_implicit_iv = {
Expand All @@ -584,6 +589,7 @@ static const EVP_AEAD aead_aes_128_cbc_sha256_tls_implicit_iv = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_aes_256_cbc_sha384_tls = {
Expand All @@ -604,6 +610,7 @@ static const EVP_AEAD aead_aes_256_cbc_sha384_tls = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_des_ede3_cbc_sha1_tls = {
Expand All @@ -624,6 +631,7 @@ static const EVP_AEAD aead_des_ede3_cbc_sha1_tls = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_des_ede3_cbc_sha1_tls_implicit_iv = {
Expand All @@ -644,6 +652,7 @@ static const EVP_AEAD aead_des_ede3_cbc_sha1_tls_implicit_iv = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

static const EVP_AEAD aead_null_sha1_tls = {
Expand All @@ -664,6 +673,7 @@ static const EVP_AEAD aead_null_sha1_tls = {
aead_tls_tag_len,
NULL /* serialize_state */,
NULL /* deserialize_state */,
NULL /* copy */,
};

const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls(void) {
Expand Down
Loading
Loading