Implement the AuthEncap/AuthDecap HPKE modes
Relevant spec bits: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1 https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.3 Change-Id: Iddb151afc92f7a91beb9ca52caceec6cb5383206 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59387 Reviewed-by: Adam Langley <agl@google.com> Auto-Submit: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com>
This commit is contained in:
parent
051f891b26
commit
4c8bcf0da2
@ -53,6 +53,17 @@ struct evp_hpke_kem_st {
|
||||
int (*decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret,
|
||||
size_t *out_shared_secret_len, const uint8_t *enc,
|
||||
size_t enc_len);
|
||||
int (*auth_encap_with_seed)(const EVP_HPKE_KEY *key,
|
||||
uint8_t *out_shared_secret,
|
||||
size_t *out_shared_secret_len, uint8_t *out_enc,
|
||||
size_t *out_enc_len, size_t max_enc,
|
||||
const uint8_t *peer_public_key,
|
||||
size_t peer_public_key_len, const uint8_t *seed,
|
||||
size_t seed_len);
|
||||
int (*auth_decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret,
|
||||
size_t *out_shared_secret_len, const uint8_t *enc,
|
||||
size_t enc_len, const uint8_t *peer_public_key,
|
||||
size_t peer_public_key_len);
|
||||
};
|
||||
|
||||
struct evp_hpke_kdf_st {
|
||||
@ -211,6 +222,76 @@ static int x25519_decap(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int x25519_auth_encap_with_seed(
|
||||
const EVP_HPKE_KEY *key, uint8_t *out_shared_secret,
|
||||
size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len,
|
||||
size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len,
|
||||
const uint8_t *seed, size_t seed_len) {
|
||||
if (max_enc < X25519_PUBLIC_VALUE_LEN) {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
|
||||
return 0;
|
||||
}
|
||||
if (seed_len != X25519_PRIVATE_KEY_LEN) {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
X25519_public_from_private(out_enc, seed);
|
||||
|
||||
uint8_t dh[2 * X25519_SHARED_KEY_LEN];
|
||||
if (peer_public_key_len != X25519_PUBLIC_VALUE_LEN ||
|
||||
!X25519(dh, seed, peer_public_key) ||
|
||||
!X25519(dh + X25519_SHARED_KEY_LEN, key->private_key, peer_public_key)) {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t kem_context[3 * X25519_PUBLIC_VALUE_LEN];
|
||||
OPENSSL_memcpy(kem_context, out_enc, X25519_PUBLIC_VALUE_LEN);
|
||||
OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, peer_public_key,
|
||||
X25519_PUBLIC_VALUE_LEN);
|
||||
OPENSSL_memcpy(kem_context + 2 * X25519_PUBLIC_VALUE_LEN, key->public_key,
|
||||
X25519_PUBLIC_VALUE_LEN);
|
||||
if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret,
|
||||
SHA256_DIGEST_LENGTH, dh, sizeof(dh),
|
||||
kem_context, sizeof(kem_context))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_enc_len = X25519_PUBLIC_VALUE_LEN;
|
||||
*out_shared_secret_len = SHA256_DIGEST_LENGTH;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int x25519_auth_decap(const EVP_HPKE_KEY *key,
|
||||
uint8_t *out_shared_secret,
|
||||
size_t *out_shared_secret_len, const uint8_t *enc,
|
||||
size_t enc_len, const uint8_t *peer_public_key,
|
||||
size_t peer_public_key_len) {
|
||||
uint8_t dh[2 * X25519_SHARED_KEY_LEN];
|
||||
if (enc_len != X25519_PUBLIC_VALUE_LEN ||
|
||||
peer_public_key_len != X25519_PUBLIC_VALUE_LEN ||
|
||||
!X25519(dh, key->private_key, enc) ||
|
||||
!X25519(dh + X25519_SHARED_KEY_LEN, key->private_key, peer_public_key)) {
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t kem_context[3 * X25519_PUBLIC_VALUE_LEN];
|
||||
OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN);
|
||||
OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, key->public_key,
|
||||
X25519_PUBLIC_VALUE_LEN);
|
||||
OPENSSL_memcpy(kem_context + 2 * X25519_PUBLIC_VALUE_LEN, peer_public_key,
|
||||
X25519_PUBLIC_VALUE_LEN);
|
||||
if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret,
|
||||
SHA256_DIGEST_LENGTH, dh, sizeof(dh),
|
||||
kem_context, sizeof(kem_context))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*out_shared_secret_len = SHA256_DIGEST_LENGTH;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const EVP_HPKE_KEM *EVP_hpke_x25519_hkdf_sha256(void) {
|
||||
static const EVP_HPKE_KEM kKEM = {
|
||||
/*id=*/EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
|
||||
@ -222,6 +303,8 @@ const EVP_HPKE_KEM *EVP_hpke_x25519_hkdf_sha256(void) {
|
||||
x25519_generate_key,
|
||||
x25519_encap_with_seed,
|
||||
x25519_decap,
|
||||
x25519_auth_encap_with_seed,
|
||||
x25519_auth_decap,
|
||||
};
|
||||
return &kKEM;
|
||||
}
|
||||
@ -373,8 +456,10 @@ static int hpke_build_suite_id(const EVP_HPKE_CTX *ctx,
|
||||
}
|
||||
|
||||
#define HPKE_MODE_BASE 0
|
||||
#define HPKE_MODE_AUTH 2
|
||||
|
||||
static int hpke_key_schedule(EVP_HPKE_CTX *ctx, const uint8_t *shared_secret,
|
||||
static int hpke_key_schedule(EVP_HPKE_CTX *ctx, uint8_t mode,
|
||||
const uint8_t *shared_secret,
|
||||
size_t shared_secret_len, const uint8_t *info,
|
||||
size_t info_len) {
|
||||
uint8_t suite_id[HPKE_SUITE_ID_LEN];
|
||||
@ -407,7 +492,7 @@ static int hpke_key_schedule(EVP_HPKE_CTX *ctx, const uint8_t *shared_secret,
|
||||
size_t context_len;
|
||||
CBB context_cbb;
|
||||
CBB_init_fixed(&context_cbb, context, sizeof(context));
|
||||
if (!CBB_add_u8(&context_cbb, HPKE_MODE_BASE) ||
|
||||
if (!CBB_add_u8(&context_cbb, mode) ||
|
||||
!CBB_add_bytes(&context_cbb, psk_id_hash, psk_id_hash_len) ||
|
||||
!CBB_add_bytes(&context_cbb, info_hash, info_hash_len) ||
|
||||
!CBB_finish(&context_cbb, NULL, &context_len)) {
|
||||
@ -507,8 +592,8 @@ int EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
|
||||
if (!kem->encap_with_seed(kem, shared_secret, &shared_secret_len, out_enc,
|
||||
out_enc_len, max_enc, peer_public_key,
|
||||
peer_public_key_len, seed, seed_len) ||
|
||||
!hpke_key_schedule(ctx, shared_secret, shared_secret_len, info,
|
||||
info_len)) {
|
||||
!hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, shared_secret_len,
|
||||
info, info_len)) {
|
||||
EVP_HPKE_CTX_cleanup(ctx);
|
||||
return 0;
|
||||
}
|
||||
@ -528,8 +613,79 @@ int EVP_HPKE_CTX_setup_recipient(EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key,
|
||||
uint8_t shared_secret[MAX_SHARED_SECRET_LEN];
|
||||
size_t shared_secret_len;
|
||||
if (!key->kem->decap(key, shared_secret, &shared_secret_len, enc, enc_len) ||
|
||||
!hpke_key_schedule(ctx, shared_secret, shared_secret_len, info,
|
||||
info_len)) {
|
||||
!hpke_key_schedule(ctx, HPKE_MODE_BASE, shared_secret, shared_secret_len,
|
||||
info, info_len)) {
|
||||
EVP_HPKE_CTX_cleanup(ctx);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int EVP_HPKE_CTX_setup_auth_sender(
|
||||
EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
|
||||
const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
|
||||
const uint8_t *peer_public_key, size_t peer_public_key_len,
|
||||
const uint8_t *info, size_t info_len) {
|
||||
uint8_t seed[MAX_SEED_LEN];
|
||||
RAND_bytes(seed, key->kem->seed_len);
|
||||
return EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing(
|
||||
ctx, out_enc, out_enc_len, max_enc, key, kdf, aead, peer_public_key,
|
||||
peer_public_key_len, info, info_len, seed, key->kem->seed_len);
|
||||
}
|
||||
|
||||
int EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing(
|
||||
EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
|
||||
const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
|
||||
const uint8_t *peer_public_key, size_t peer_public_key_len,
|
||||
const uint8_t *info, size_t info_len, const uint8_t *seed,
|
||||
size_t seed_len) {
|
||||
if (key->kem->auth_encap_with_seed == NULL) {
|
||||
// Not all HPKE KEMs support AuthEncap.
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EVP_HPKE_CTX_zero(ctx);
|
||||
ctx->is_sender = 1;
|
||||
ctx->kem = key->kem;
|
||||
ctx->kdf = kdf;
|
||||
ctx->aead = aead;
|
||||
uint8_t shared_secret[MAX_SHARED_SECRET_LEN];
|
||||
size_t shared_secret_len;
|
||||
if (!key->kem->auth_encap_with_seed(
|
||||
key, shared_secret, &shared_secret_len, out_enc, out_enc_len, max_enc,
|
||||
peer_public_key, peer_public_key_len, seed, seed_len) ||
|
||||
!hpke_key_schedule(ctx, HPKE_MODE_AUTH, shared_secret, shared_secret_len,
|
||||
info, info_len)) {
|
||||
EVP_HPKE_CTX_cleanup(ctx);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int EVP_HPKE_CTX_setup_auth_recipient(
|
||||
EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf,
|
||||
const EVP_HPKE_AEAD *aead, const uint8_t *enc, size_t enc_len,
|
||||
const uint8_t *info, size_t info_len, const uint8_t *peer_public_key,
|
||||
size_t peer_public_key_len) {
|
||||
if (key->kem->auth_decap == NULL) {
|
||||
// Not all HPKE KEMs support AuthDecap.
|
||||
OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EVP_HPKE_CTX_zero(ctx);
|
||||
ctx->is_sender = 0;
|
||||
ctx->kem = key->kem;
|
||||
ctx->kdf = kdf;
|
||||
ctx->aead = aead;
|
||||
uint8_t shared_secret[MAX_SHARED_SECRET_LEN];
|
||||
size_t shared_secret_len;
|
||||
if (!key->kem->auth_decap(key, shared_secret, &shared_secret_len, enc,
|
||||
enc_len, peer_public_key, peer_public_key_len) ||
|
||||
!hpke_key_schedule(ctx, HPKE_MODE_AUTH, shared_secret, shared_secret_len,
|
||||
info, info_len)) {
|
||||
EVP_HPKE_CTX_cleanup(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
@ -66,11 +66,26 @@ class HPKETestVector {
|
||||
// Test the sender.
|
||||
ScopedEVP_HPKE_CTX sender_ctx;
|
||||
uint8_t enc[EVP_HPKE_MAX_ENC_LENGTH];
|
||||
size_t enc_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc), kem, kdf, aead,
|
||||
public_key_r_.data(), public_key_r_.size(), info_.data(), info_.size(),
|
||||
secret_key_e_.data(), secret_key_e_.size()));
|
||||
size_t enc_len = 0;
|
||||
switch (mode_) {
|
||||
case Mode::kBase:
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc), kem, kdf, aead,
|
||||
public_key_r_.data(), public_key_r_.size(), info_.data(),
|
||||
info_.size(), secret_key_e_.data(), secret_key_e_.size()));
|
||||
break;
|
||||
case Mode::kAuth: {
|
||||
ScopedEVP_HPKE_KEY sender_key;
|
||||
ASSERT_TRUE(EVP_HPKE_KEY_init(
|
||||
sender_key.get(), kem, secret_key_s_.data(), secret_key_s_.size()));
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc), sender_key.get(), kdf,
|
||||
aead, public_key_r_.data(), public_key_r_.size(), info_.data(),
|
||||
info_.size(), secret_key_e_.data(), secret_key_e_.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(Bytes(enc, enc_len), Bytes(public_key_e_));
|
||||
VerifySender(sender_ctx.get());
|
||||
|
||||
@ -101,9 +116,18 @@ class HPKETestVector {
|
||||
|
||||
// Set up the recipient.
|
||||
ScopedEVP_HPKE_CTX recipient_ctx;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(recipient_ctx.get(), key, kdf,
|
||||
aead, enc, enc_len, info_.data(),
|
||||
info_.size()));
|
||||
switch (mode_) {
|
||||
case Mode::kBase:
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(recipient_ctx.get(), key,
|
||||
kdf, aead, enc, enc_len,
|
||||
info_.data(), info_.size()));
|
||||
break;
|
||||
case Mode::kAuth:
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_recipient(
|
||||
recipient_ctx.get(), key, kdf, aead, enc, enc_len, info_.data(),
|
||||
info_.size(), public_key_s_.data(), public_key_s_.size()));
|
||||
break;
|
||||
}
|
||||
|
||||
VerifyRecipient(recipient_ctx.get());
|
||||
}
|
||||
@ -168,6 +192,11 @@ class HPKETestVector {
|
||||
}
|
||||
}
|
||||
|
||||
enum class Mode {
|
||||
kBase = 0,
|
||||
kAuth = 2,
|
||||
};
|
||||
|
||||
struct Encryption {
|
||||
std::vector<uint8_t> aad;
|
||||
std::vector<uint8_t> ciphertext;
|
||||
@ -180,6 +209,7 @@ class HPKETestVector {
|
||||
std::vector<uint8_t> exported_value;
|
||||
};
|
||||
|
||||
Mode mode_;
|
||||
uint16_t kdf_id_;
|
||||
uint16_t aead_id_;
|
||||
std::vector<uint8_t> context_;
|
||||
@ -188,6 +218,8 @@ class HPKETestVector {
|
||||
std::vector<uint8_t> secret_key_e_;
|
||||
std::vector<uint8_t> public_key_r_;
|
||||
std::vector<uint8_t> secret_key_r_;
|
||||
std::vector<uint8_t> public_key_s_;
|
||||
std::vector<uint8_t> secret_key_s_;
|
||||
std::vector<Encryption> encryptions_;
|
||||
std::vector<Export> exports_;
|
||||
};
|
||||
@ -227,7 +259,6 @@ bool FileTestReadInt(FileTest *file_test, T *out, const std::string &key) {
|
||||
bool HPKETestVector::ReadFromFileTest(FileTest *t) {
|
||||
uint8_t mode = 0;
|
||||
if (!FileTestReadInt(t, &mode, "mode") ||
|
||||
mode != 0 /* mode_base */ ||
|
||||
!FileTestReadInt(t, &kdf_id_, "kdf_id") ||
|
||||
!FileTestReadInt(t, &aead_id_, "aead_id") ||
|
||||
!t->GetBytes(&info_, "info") ||
|
||||
@ -238,6 +269,21 @@ bool HPKETestVector::ReadFromFileTest(FileTest *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case static_cast<int>(Mode::kBase):
|
||||
mode_ = Mode::kBase;
|
||||
break;
|
||||
case static_cast<int>(Mode::kAuth):
|
||||
mode_ = Mode::kAuth;
|
||||
if (!t->GetBytes(&secret_key_s_, "skSm") ||
|
||||
!t->GetBytes(&public_key_s_, "pkSm")) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 1; t->HasAttribute(BuildAttrName("aad", i)); i++) {
|
||||
Encryption encryption;
|
||||
if (!t->GetBytes(&encryption.aad, BuildAttrName("aad", i)) ||
|
||||
@ -282,14 +328,25 @@ TEST(HPKETest, RoundTrip) {
|
||||
Span<const uint8_t> info_values[] = {{nullptr, 0}, info_a, info_b};
|
||||
Span<const uint8_t> ad_values[] = {{nullptr, 0}, ad_a, ad_b};
|
||||
|
||||
const EVP_HPKE_KEM *kem = EVP_hpke_x25519_hkdf_sha256();
|
||||
|
||||
// Generate the recipient's keypair.
|
||||
ScopedEVP_HPKE_KEY key;
|
||||
ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
|
||||
ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), kem));
|
||||
uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
|
||||
size_t public_key_r_len;
|
||||
ASSERT_TRUE(EVP_HPKE_KEY_public_key(key.get(), public_key_r,
|
||||
&public_key_r_len, sizeof(public_key_r)));
|
||||
|
||||
// Generate the sender's keypair, for auth modes.
|
||||
ScopedEVP_HPKE_KEY sender_key;
|
||||
ASSERT_TRUE(
|
||||
EVP_HPKE_KEY_generate(sender_key.get(), kem));
|
||||
uint8_t public_key_s[X25519_PUBLIC_VALUE_LEN];
|
||||
size_t public_key_s_len;
|
||||
ASSERT_TRUE(EVP_HPKE_KEY_public_key(sender_key.get(), public_key_s,
|
||||
&public_key_s_len, sizeof(public_key_r)));
|
||||
|
||||
for (const auto kdf : kAllKDFs) {
|
||||
SCOPED_TRACE(EVP_HPKE_KDF_id(kdf()));
|
||||
for (const auto aead : kAllAEADs) {
|
||||
@ -298,45 +355,70 @@ TEST(HPKETest, RoundTrip) {
|
||||
SCOPED_TRACE(Bytes(info));
|
||||
for (const Span<const uint8_t> &ad : ad_values) {
|
||||
SCOPED_TRACE(Bytes(ad));
|
||||
// Set up the sender.
|
||||
ScopedEVP_HPKE_CTX sender_ctx;
|
||||
uint8_t enc[X25519_PUBLIC_VALUE_LEN];
|
||||
size_t enc_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_sender(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc),
|
||||
EVP_hpke_x25519_hkdf_sha256(), kdf(), aead(), public_key_r,
|
||||
public_key_r_len, info.data(), info.size()));
|
||||
|
||||
// Set up the recipient.
|
||||
ScopedEVP_HPKE_CTX recipient_ctx;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(
|
||||
recipient_ctx.get(), key.get(), kdf(), aead(), enc, enc_len,
|
||||
info.data(), info.size()));
|
||||
auto check_messages = [&](EVP_HPKE_CTX *sender_ctx,
|
||||
EVP_HPKE_CTX *recipient_ctx) {
|
||||
const char kCleartextPayload[] = "foobar";
|
||||
|
||||
const char kCleartextPayload[] = "foobar";
|
||||
// Have sender encrypt message for the recipient.
|
||||
std::vector<uint8_t> ciphertext(
|
||||
sizeof(kCleartextPayload) +
|
||||
EVP_HPKE_CTX_max_overhead(sender_ctx));
|
||||
size_t ciphertext_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_seal(
|
||||
sender_ctx, ciphertext.data(), &ciphertext_len,
|
||||
ciphertext.size(),
|
||||
reinterpret_cast<const uint8_t *>(kCleartextPayload),
|
||||
sizeof(kCleartextPayload), ad.data(), ad.size()));
|
||||
|
||||
// Have sender encrypt message for the recipient.
|
||||
std::vector<uint8_t> ciphertext(
|
||||
sizeof(kCleartextPayload) +
|
||||
EVP_HPKE_CTX_max_overhead(sender_ctx.get()));
|
||||
size_t ciphertext_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_seal(
|
||||
sender_ctx.get(), ciphertext.data(), &ciphertext_len,
|
||||
ciphertext.size(),
|
||||
reinterpret_cast<const uint8_t *>(kCleartextPayload),
|
||||
sizeof(kCleartextPayload), ad.data(), ad.size()));
|
||||
// Have recipient decrypt the message.
|
||||
std::vector<uint8_t> cleartext(ciphertext.size());
|
||||
size_t cleartext_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_open(recipient_ctx, cleartext.data(),
|
||||
&cleartext_len, cleartext.size(),
|
||||
ciphertext.data(), ciphertext_len,
|
||||
ad.data(), ad.size()));
|
||||
|
||||
// Have recipient decrypt the message.
|
||||
std::vector<uint8_t> cleartext(ciphertext.size());
|
||||
size_t cleartext_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_open(recipient_ctx.get(), cleartext.data(),
|
||||
&cleartext_len, cleartext.size(),
|
||||
ciphertext.data(), ciphertext_len,
|
||||
ad.data(), ad.size()));
|
||||
// Verify that decrypted message matches the original.
|
||||
ASSERT_EQ(Bytes(cleartext.data(), cleartext_len),
|
||||
Bytes(kCleartextPayload, sizeof(kCleartextPayload)));
|
||||
};
|
||||
|
||||
// Verify that decrypted message matches the original.
|
||||
ASSERT_EQ(Bytes(cleartext.data(), cleartext_len),
|
||||
Bytes(kCleartextPayload, sizeof(kCleartextPayload)));
|
||||
// Test the base mode.
|
||||
{
|
||||
ScopedEVP_HPKE_CTX sender_ctx;
|
||||
uint8_t enc[X25519_PUBLIC_VALUE_LEN];
|
||||
size_t enc_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_sender(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc), kem, kdf(),
|
||||
aead(), public_key_r, public_key_r_len, info.data(),
|
||||
info.size()));
|
||||
|
||||
ScopedEVP_HPKE_CTX recipient_ctx;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(
|
||||
recipient_ctx.get(), key.get(), kdf(), aead(), enc, enc_len,
|
||||
info.data(), info.size()));
|
||||
|
||||
check_messages(sender_ctx.get(), recipient_ctx.get());
|
||||
}
|
||||
|
||||
// Test the auth mode.
|
||||
{
|
||||
ScopedEVP_HPKE_CTX sender_ctx;
|
||||
uint8_t enc[X25519_PUBLIC_VALUE_LEN];
|
||||
size_t enc_len;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_sender(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc), sender_key.get(),
|
||||
kdf(), aead(), public_key_r, public_key_r_len, info.data(),
|
||||
info.size()));
|
||||
|
||||
ScopedEVP_HPKE_CTX recipient_ctx;
|
||||
ASSERT_TRUE(EVP_HPKE_CTX_setup_auth_recipient(
|
||||
recipient_ctx.get(), key.get(), kdf(), aead(), enc, enc_len,
|
||||
info.data(), info.size(), public_key_s, public_key_s_len));
|
||||
|
||||
check_messages(sender_ctx.get(), recipient_ctx.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -352,6 +434,11 @@ TEST(HPKETest, X25519EncapSmallOrderPoint) {
|
||||
0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32,
|
||||
0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8,
|
||||
};
|
||||
static const uint8_t kValidPoint[32] = {
|
||||
0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1,
|
||||
0xa4, 0x24, 0xb1, 0x5f, 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3,
|
||||
0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 0x1c, 0x4c,
|
||||
};
|
||||
|
||||
ScopedEVP_HPKE_KEY key;
|
||||
ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
|
||||
@ -364,16 +451,32 @@ TEST(HPKETest, X25519EncapSmallOrderPoint) {
|
||||
ScopedEVP_HPKE_CTX sender_ctx;
|
||||
uint8_t enc[X25519_PUBLIC_VALUE_LEN];
|
||||
size_t enc_len;
|
||||
ASSERT_FALSE(EVP_HPKE_CTX_setup_sender(
|
||||
EXPECT_FALSE(EVP_HPKE_CTX_setup_sender(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc),
|
||||
EVP_hpke_x25519_hkdf_sha256(), kdf(), aead(), kSmallOrderPoint,
|
||||
sizeof(kSmallOrderPoint), nullptr, 0));
|
||||
|
||||
// Likewise with auth.
|
||||
EXPECT_FALSE(EVP_HPKE_CTX_setup_auth_sender(
|
||||
sender_ctx.get(), enc, &enc_len, sizeof(enc), key.get(), kdf(),
|
||||
aead(), kSmallOrderPoint, sizeof(kSmallOrderPoint), nullptr, 0));
|
||||
|
||||
// Set up the recipient, passing in kSmallOrderPoint as |enc|.
|
||||
ScopedEVP_HPKE_CTX recipient_ctx;
|
||||
ASSERT_FALSE(EVP_HPKE_CTX_setup_recipient(
|
||||
EXPECT_FALSE(EVP_HPKE_CTX_setup_recipient(
|
||||
recipient_ctx.get(), key.get(), kdf(), aead(), kSmallOrderPoint,
|
||||
sizeof(kSmallOrderPoint), nullptr, 0));
|
||||
|
||||
// Likewise with auth. With auth, a small-order point could appear as
|
||||
// either |enc| or the peer public key.
|
||||
EXPECT_FALSE(EVP_HPKE_CTX_setup_auth_recipient(
|
||||
recipient_ctx.get(), key.get(), kdf(), aead(), kSmallOrderPoint,
|
||||
sizeof(kSmallOrderPoint), nullptr, 0, kValidPoint,
|
||||
sizeof(kValidPoint)));
|
||||
EXPECT_FALSE(EVP_HPKE_CTX_setup_auth_recipient(
|
||||
recipient_ctx.get(), key.get(), kdf(), aead(), kValidPoint,
|
||||
sizeof(kValidPoint), nullptr, 0, kSmallOrderPoint,
|
||||
sizeof(kSmallOrderPoint)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,7 @@ import sys
|
||||
|
||||
HPKE_MODE_BASE = 0
|
||||
HPKE_MODE_PSK = 1
|
||||
HPKE_MODE_AUTH = 2
|
||||
HPKE_DHKEM_X25519_SHA256 = 0x0020
|
||||
HPKE_HKDF_SHA256 = 0x0001
|
||||
HPKE_AEAD_EXPORT_ONLY = 0xffff
|
||||
@ -49,7 +50,7 @@ def read_test_vectors_and_generate_code(json_file_in_path, test_file_out_path):
|
||||
lines = []
|
||||
for test in test_vecs:
|
||||
# Filter out test cases that we don't use.
|
||||
if (test["mode"] != HPKE_MODE_BASE or
|
||||
if (test["mode"] not in (HPKE_MODE_BASE, HPKE_MODE_AUTH) or
|
||||
test["kem_id"] != HPKE_DHKEM_X25519_SHA256 or
|
||||
test["aead_id"] == HPKE_AEAD_EXPORT_ONLY or
|
||||
test["kdf_id"] != HPKE_HKDF_SHA256):
|
||||
@ -57,9 +58,9 @@ def read_test_vectors_and_generate_code(json_file_in_path, test_file_out_path):
|
||||
|
||||
keys = ["mode", "kdf_id", "aead_id", "info", "skRm", "skEm", "pkRm", "pkEm"]
|
||||
|
||||
if test["mode"] == HPKE_MODE_PSK:
|
||||
keys.append("psk")
|
||||
keys.append("psk_id")
|
||||
if test["mode"] == HPKE_MODE_AUTH:
|
||||
keys.append("pkSm")
|
||||
keys.append("skSm")
|
||||
|
||||
for key in keys:
|
||||
lines.append("{} = {}".format(key, str(test[key])))
|
||||
|
@ -249,6 +249,34 @@ OPENSSL_EXPORT int EVP_HPKE_CTX_setup_recipient(
|
||||
const EVP_HPKE_AEAD *aead, const uint8_t *enc, size_t enc_len,
|
||||
const uint8_t *info, size_t info_len);
|
||||
|
||||
// EVP_HPKE_CTX_setup_auth_sender implements the SetupAuthS HPKE operation. It
|
||||
// behaves like |EVP_HPKE_CTX_setup_sender| but authenticates the resulting
|
||||
// context with |key|.
|
||||
OPENSSL_EXPORT int EVP_HPKE_CTX_setup_auth_sender(
|
||||
EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
|
||||
const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
|
||||
const uint8_t *peer_public_key, size_t peer_public_key_len,
|
||||
const uint8_t *info, size_t info_len);
|
||||
|
||||
// EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing behaves like
|
||||
// |EVP_HPKE_CTX_setup_auth_sender|, but takes a seed to behave
|
||||
// deterministically. The seed's format depends on |kem|. For X25519, it is the
|
||||
// sender's ephemeral private key.
|
||||
OPENSSL_EXPORT int EVP_HPKE_CTX_setup_auth_sender_with_seed_for_testing(
|
||||
EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
|
||||
const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
|
||||
const uint8_t *peer_public_key, size_t peer_public_key_len,
|
||||
const uint8_t *info, size_t info_len, const uint8_t *seed, size_t seed_len);
|
||||
|
||||
// EVP_HPKE_CTX_setup_auth_recipient implements the SetupAuthR HPKE operation.
|
||||
// It behaves like |EVP_HPKE_CTX_setup_recipient| but checks the resulting
|
||||
// context was authenticated with |peer_public_key|.
|
||||
OPENSSL_EXPORT int EVP_HPKE_CTX_setup_auth_recipient(
|
||||
EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf,
|
||||
const EVP_HPKE_AEAD *aead, const uint8_t *enc, size_t enc_len,
|
||||
const uint8_t *info, size_t info_len, const uint8_t *peer_public_key,
|
||||
size_t peer_public_key_len);
|
||||
|
||||
|
||||
// Using an HPKE context.
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user