Revise the deterministic for_test variant of HPKE's SetupBaseS. Although we only support X25519 right now, we may need to support other KEMs in the future. In the general case, a public/private keypair is less meaningful. (If something like NTRU-HRSS even goes here, I guess it'd be the entropy passed to HRSS_encap.) Instead of taking an entire keypair, just take the private key. Perhaps we call it the "seed"? Bug: 410 Change-Id: Ifd6b6ea8ea36e6eca60d303706d6d2620f8c42d4 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47326 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c index f6aca0f..cc0def5 100644 --- a/crypto/hpke/hpke.c +++ b/crypto/hpke/hpke.c
@@ -22,6 +22,7 @@ #include <openssl/err.h> #include <openssl/evp_errors.h> #include <openssl/hkdf.h> +#include <openssl/rand.h> #include <openssl/sha.h> #include "../internal.h" @@ -304,34 +305,27 @@ const uint8_t *peer_public_value, size_t peer_public_value_len, const uint8_t *info, size_t info_len) { + uint8_t seed[X25519_PRIVATE_KEY_LEN]; + RAND_bytes(seed, sizeof(seed)); + return EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( + hpke, out_enc, out_enc_len, kdf_id, aead_id, peer_public_value, + peer_public_value_len, info, info_len, seed, sizeof(seed)); +} + +int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( + EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len, uint16_t kdf_id, + uint16_t aead_id, const uint8_t *peer_public_value, + size_t peer_public_value_len, const uint8_t *info, size_t info_len, + const uint8_t *seed, size_t seed_len) { if (out_enc_len != X25519_PUBLIC_VALUE_LEN) { OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); return 0; } - - // The GenerateKeyPair() step technically belongs in the KEM's Encap() - // function, but we've moved it up a layer to make it easier for tests to - // inject an ephemeral keypair. - uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN]; - X25519_keypair(out_enc, ephemeral_private); - return EVP_HPKE_CTX_setup_base_s_x25519_for_test( - hpke, kdf_id, aead_id, peer_public_value, peer_public_value_len, info, - info_len, ephemeral_private, sizeof(ephemeral_private), out_enc, - out_enc_len); -} - -int EVP_HPKE_CTX_setup_base_s_x25519_for_test( - EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id, - const uint8_t *peer_public_value, size_t peer_public_value_len, - const uint8_t *info, size_t info_len, const uint8_t *ephemeral_private, - size_t ephemeral_private_len, const uint8_t *ephemeral_public, - size_t ephemeral_public_len) { if (peer_public_value_len != X25519_PUBLIC_VALUE_LEN) { OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); return 0; } - if (ephemeral_private_len != X25519_PRIVATE_KEY_LEN || - ephemeral_public_len != X25519_PUBLIC_VALUE_LEN) { + if (seed_len != X25519_PRIVATE_KEY_LEN) { OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); return 0; } @@ -343,9 +337,9 @@ if (hpke->hkdf_md == NULL) { return 0; } + X25519_public_from_private(out_enc, seed); uint8_t shared_secret[SHA256_DIGEST_LENGTH]; - if (!hpke_encap(hpke, shared_secret, peer_public_value, ephemeral_private, - ephemeral_public) || + if (!hpke_encap(hpke, shared_secret, peer_public_value, seed, out_enc) || !hpke_key_schedule(hpke, shared_secret, sizeof(shared_secret), info, info_len)) { return 0;
diff --git a/crypto/hpke/hpke_test.cc b/crypto/hpke/hpke_test.cc index 5b063a3..e4aa932 100644 --- a/crypto/hpke/hpke_test.cc +++ b/crypto/hpke/hpke_test.cc
@@ -51,17 +51,18 @@ ASSERT_GT(secret_key_e_.size(), 0u); // Set up the sender. - ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519_for_test( - sender_ctx.get(), kdf_id_, aead_id_, public_key_r_.data(), - public_key_r_.size(), info_.data(), info_.size(), secret_key_e_.data(), - secret_key_e_.size(), public_key_e_.data(), public_key_e_.size())); + uint8_t enc[X25519_PUBLIC_VALUE_LEN]; + ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( + sender_ctx.get(), enc, sizeof(enc), kdf_id_, aead_id_, + public_key_r_.data(), public_key_r_.size(), info_.data(), info_.size(), + secret_key_e_.data(), secret_key_e_.size())); + EXPECT_EQ(Bytes(enc), Bytes(public_key_e_)); // Set up the receiver. ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), kdf_id_, aead_id_, public_key_e_.data(), - public_key_e_.size(), public_key_r_.data(), public_key_r_.size(), - secret_key_r_.data(), secret_key_r_.size(), info_.data(), - info_.size())); + receiver_ctx.get(), kdf_id_, aead_id_, enc, sizeof(enc), + public_key_r_.data(), public_key_r_.size(), secret_key_r_.data(), + secret_key_r_.size(), info_.data(), info_.size())); VerifyEncryptions(sender_ctx.get(), receiver_ctx.get()); VerifyExports(sender_ctx.get());
diff --git a/crypto/hpke/internal.h b/crypto/hpke/internal.h index cda9b7a..54382c8 100644 --- a/crypto/hpke/internal.h +++ b/crypto/hpke/internal.h
@@ -94,16 +94,14 @@ uint16_t aead_id, const uint8_t *peer_public_value, size_t peer_public_value_len, const uint8_t *info, size_t info_len); -// EVP_HPKE_CTX_setup_base_s_x25519_for_test behaves like -// |EVP_HPKE_CTX_setup_base_s_x25519|, but takes a pre-generated ephemeral -// sender key. The caller ensures that |ephemeral_public| and -// |ephemeral_private| are a valid keypair. -OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519_for_test( - EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id, - const uint8_t *peer_public_value, size_t peer_public_value_len, - const uint8_t *info, size_t info_len, const uint8_t *ephemeral_private, - size_t ephemeral_private_len, const uint8_t *ephemeral_public, - size_t ephemeral_public_len); +// EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing behaves like +// |EVP_HPKE_CTX_setup_base_s_x25519|, but takes a seed value to behave +// deterministically. This seed is the sender's ephemeral X25519 key. +OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( + EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len, uint16_t kdf_id, + uint16_t aead_id, const uint8_t *peer_public_value, + size_t peer_public_value_len, const uint8_t *info, size_t info_len, + const uint8_t *seed, size_t seed_len); // EVP_HPKE_CTX_setup_base_r_x25519 sets up |hpke| as a recipient context that // can decrypt messages. It returns one on success, and zero otherwise.