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:
David Benjamin 2023-04-28 17:59:18 -04:00 committed by Boringssl LUCI CQ
parent 051f891b26
commit 4c8bcf0da2
5 changed files with 3497 additions and 56 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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])))

View File

@ -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.
//