EVP_HPKE_KEM: Implement DHKEM(P256, HKDF-SHA256) using EVP_KEM_DHKEM Implement the other existing DHKEM as an EVP_KEM_DHKEM, reusing the generic DHKEM functions. P-256 keys are (de)serialized using the EC EVP_PKEY APIs, (for now) preserving HPKE's existing error code for invalid private keys. Bug: 535883377 Change-Id: I0e10d4295283b7406ba42e2f018a1dac6a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/103049 Commit-Queue: Lily Chen <chlily@google.com> Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/hpke/hpke.cc b/crypto/hpke/hpke.cc index f8837b4..0dd95ec 100644 --- a/crypto/hpke/hpke.cc +++ b/crypto/hpke/hpke.cc
@@ -26,6 +26,7 @@ #include <openssl/curve25519.h> #include <openssl/digest.h> #include <openssl/ec.h> +#include <openssl/ec_key.h> #include <openssl/err.h> #include <openssl/evp.h> #include <openssl/evp_errors.h> @@ -51,7 +52,7 @@ #define MAX_SEED_LEN XWING_SEED_LEN #define MAX_SHARED_SECRET_LEN SHA256_DIGEST_LENGTH #define MAX_DHKEM_DH_SHARED_KEY_LEN X25519_SHARED_KEY_LEN -#define MAX_DHKEM_PUBLIC_KEY_LEN X25519_PUBLIC_VALUE_LEN +#define MAX_DHKEM_PUBLIC_KEY_LEN 65 // kP256PublicValueLen namespace { @@ -641,299 +642,151 @@ return &kKEM; } -#define P256_PRIVATE_KEY_LEN 32 -#define P256_PUBLIC_KEY_LEN 65 -#define P256_PUBLIC_VALUE_LEN 65 -#define P256_SEED_LEN 32 -#define P256_SHARED_KEY_LEN 32 +namespace { -static int p256_public_from_private(uint8_t out_pub[P256_PUBLIC_VALUE_LEN], - const uint8_t priv[P256_PRIVATE_KEY_LEN]) { - const EC_GROUP *const group = EC_group_p256(); - const uint8_t kAllZeros[P256_PRIVATE_KEY_LEN] = {0}; - EC_SCALAR private_scalar; - EC_JACOBIAN public_point; - EC_AFFINE public_point_affine; +constexpr size_t kP256PrivateKeyLen = 32; +constexpr size_t kP256PublicValueLen = 65; +constexpr size_t kP256SeedLen = 32; +constexpr size_t kP256SharedKeyLen = 32; - if (CRYPTO_memcmp(kAllZeros, priv, sizeof(kAllZeros)) == 0) { +// RFC 9180's SerializePublicKey format for DHKEM(P-256, HKDF-SHA256) is the +// uncompressed X9.62 point, and its private keys are the big-endian scalar. +EVP_PKEY *p256_deserialize_private_key(const EVP_PKEY_ALG *alg, + const uint8_t *in, size_t len) { + const uint8_t kAllZeros[kP256PrivateKeyLen] = {0}; + if (len != kP256PrivateKeyLen || + CRYPTO_memcmp(kAllZeros, in, sizeof(kAllZeros)) == 0) { OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + return nullptr; + } + return EVP_PKEY_from_ec_private_scalar(alg, in, len); +} + +int p256_serialize_public_key(const EVP_PKEY *pkey, uint8_t *out, + size_t *out_len) { + CBB cbb; + CBB_init_fixed(&cbb, out, *out_len); + if (!EVP_PKEY_marshal_ec_uncompressed_point(&cbb, pkey)) { return 0; } - - if (!ec_scalar_from_bytes(group, &private_scalar, priv, - P256_PRIVATE_KEY_LEN) || - !ec_point_mul_scalar_base(group, &public_point, &private_scalar) || - !ec_jacobian_to_affine(group, &public_point_affine, &public_point)) { - return 0; - } - - size_t out_len_x, out_len_y; - out_pub[0] = POINT_CONVERSION_UNCOMPRESSED; - ec_felem_to_bytes(group, &out_pub[1], &out_len_x, &public_point_affine.X); - ec_felem_to_bytes(group, &out_pub[33], &out_len_y, &public_point_affine.Y); + *out_len = CBB_len(&cbb); return 1; } -static int p256_init_key(EVP_HPKE_KEY *key, const uint8_t *priv_key, - size_t priv_key_len) { - if (priv_key_len != P256_PRIVATE_KEY_LEN) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); +int p256_serialize_private_key(const EVP_PKEY *pkey, uint8_t *out, + size_t *out_len) { + CBB cbb; + CBB_init_fixed(&cbb, out, *out_len); + if (!EVP_PKEY_marshal_ec_private_scalar(&cbb, pkey)) { return 0; } - - if (!p256_public_from_private(key->public_key, priv_key)) { - return 0; - } - - OPENSSL_memcpy(key->private_key, priv_key, priv_key_len); + *out_len = CBB_len(&cbb); return 1; } -static int p256_private_key_from_seed(uint8_t out_priv[P256_PRIVATE_KEY_LEN], - const uint8_t *seed, size_t seed_len) { +EVP_PKEY *p256_derive_key_pair(const EVP_PKEY_ALG *alg, uint16_t kem_suite_id, + Span<const uint8_t> ikm) { // https://www.rfc-editor.org/rfc/rfc9180.html#name-derivekeypair - auto suite_id = hpke_kem_suite_id(EVP_HPKE_DHKEM_P256_HKDF_SHA256); - uint8_t dkp_prk[32]; + auto suite_id = hpke_kem_suite_id(kem_suite_id); + uint8_t dkp_prk[SHA256_DIGEST_LENGTH]; size_t dkp_prk_len; if (!hpke_labeled_extract(EVP_sha256(), dkp_prk, &dkp_prk_len, nullptr, 0, - suite_id.data(), suite_id.size(), "dkp_prk", seed, - seed_len)) { - return 0; + suite_id.data(), suite_id.size(), "dkp_prk", + ikm.data(), ikm.size())) { + return nullptr; } assert(dkp_prk_len == sizeof(dkp_prk)); - const EC_GROUP *const group = EC_group_p256(); - EC_SCALAR private_scalar; - for (unsigned counter = 0; counter < 256; counter++) { + // Rejection sampling. + uint8_t priv[kP256PrivateKeyLen]; + for (size_t counter = 0; counter < 256; counter++) { const uint8_t counter_byte = counter & 0xff; - if (!hpke_labeled_expand(EVP_sha256(), out_priv, P256_PRIVATE_KEY_LEN, - dkp_prk, sizeof(dkp_prk), suite_id.data(), - suite_id.size(), "candidate", &counter_byte, + if (!hpke_labeled_expand(EVP_sha256(), priv, sizeof(priv), dkp_prk, + sizeof(dkp_prk), suite_id.data(), suite_id.size(), + "candidate", &counter_byte, sizeof(counter_byte))) { - return 0; + return nullptr; } - + EC_SCALAR private_scalar; // `ec_scalar_from_bytes` checks that the scalar is less than the order. - if (ec_scalar_from_bytes(group, &private_scalar, out_priv, - P256_PRIVATE_KEY_LEN) && + if (ec_scalar_from_bytes(group, &private_scalar, priv, sizeof(priv)) && !ec_scalar_is_zero(group, &private_scalar)) { - return 1; + return EVP_PKEY_from_ec_private_scalar(alg, priv, sizeof(priv)); } } - // This happens with probability of 2^-(32*256). OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return 0; + return nullptr; } -static int p256_derive_key(EVP_HPKE_KEY *key, Span<const uint8_t> ikm) { - if (!p256_private_key_from_seed(key->private_key, ikm.data(), ikm.size()) || - !p256_public_from_private(key->public_key, key->private_key)) { - return 0; - } - return 1; +// The P-256 encap entropy is ikmE, the input to DeriveKeyPair. +EVP_PKEY *p256_key_from_encap_entropy(const EVP_PKEY_ALG *alg, + const uint8_t *in, size_t len) { + return p256_derive_key_pair(alg, EVP_HPKE_DHKEM_P256_HKDF_SHA256, + Span(in, len)); } -static int p256_generate_key(EVP_HPKE_KEY *key) { - uint8_t seed[P256_SEED_LEN]; - RAND_bytes(seed, sizeof(seed)); - return p256_derive_key(key, seed); +bool p256_check_key(const EVP_KEM *kem, const EVP_PKEY *key) { + const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(key); + return ec_key != nullptr && + EC_GROUP_cmp(EC_KEY_get0_group(ec_key), EC_group_p256(), + /*ignored=*/nullptr) == 0; } -static int p256(uint8_t out_dh[P256_SHARED_KEY_LEN], - const uint8_t my_private[P256_PRIVATE_KEY_LEN], - const uint8_t their_public[P256_PUBLIC_VALUE_LEN]) { - const EC_GROUP *const group = EC_group_p256(); - EC_SCALAR private_scalar; - EC_FELEM x, y; - EC_JACOBIAN shared_point, their_point; - EC_AFFINE their_point_affine, shared_point_affine; +static constexpr HpkeKeyMethod kP256KeyMethod = { + /*hpke_kem_id=*/EVP_HPKE_DHKEM_P256_HKDF_SHA256, + /*public_key_len=*/kP256PublicValueLen, + /*private_key_len=*/kP256PrivateKeyLen, + /*alg_func=*/&EVP_pkey_ec_p256, + /*deserialize_private_key=*/&p256_deserialize_private_key, + /*serialize_private_key=*/&p256_serialize_private_key, + /*deserialize_public_key=*/&EVP_PKEY_from_ec_uncompressed_point, + /*serialize_public_key=*/&p256_serialize_public_key, + /*derive_key_pair=*/&p256_derive_key_pair, +}; - if (their_public[0] != POINT_CONVERSION_UNCOMPRESSED || - !ec_felem_from_bytes(group, &x, &their_public[1], 32) || - !ec_felem_from_bytes(group, &y, &their_public[33], 32) || - !ec_point_set_affine_coordinates(group, &their_point_affine, &x, &y) || - !ec_scalar_from_bytes(group, &private_scalar, my_private, - P256_PRIVATE_KEY_LEN)) { - OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return 0; - } - - ec_affine_to_jacobian(group, &their_point, &their_point_affine); - if (!ec_point_mul_scalar(group, &shared_point, &their_point, - &private_scalar) || - !ec_jacobian_to_affine(group, &shared_point_affine, &shared_point)) { - OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return 0; - } - - size_t out_len; - ec_felem_to_bytes(group, out_dh, &out_len, &shared_point_affine.X); - assert(out_len == P256_SHARED_KEY_LEN); - return 1; +const EVP_KEM *evp_kem_dhkem_p256_hkdf_sha256() { + static constexpr EVP_KEM_DHKEM kDhkemP256 = { + /*EVP_KEM=*/{ + /*pkey_id=*/EVP_PKEY_EC, + /*ciphertext_len=*/kP256PublicValueLen, + /*secret_len=*/SHA256_DIGEST_LENGTH, + /*entropy_len=*/kP256SeedLen, + /*check_key=*/&p256_check_key, + /*encap=*/&dhkem_encap, + /*encap_external_entropy=*/&dhkem_encap_external_entropy, + /*decap=*/&dhkem_decap, + }, + /*dh_shared_key_len=*/kP256SharedKeyLen, + /*key_method=*/&kP256KeyMethod, + /*hkdf_digest_func=*/&EVP_sha256, + /*key_from_encap_entropy=*/&p256_key_from_encap_entropy, + }; + return &kDhkemP256; } -static int p256_encap_with_seed(const EVP_HPKE_KEM *kem, - 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 < P256_PUBLIC_VALUE_LEN) { - OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); - return 0; - } - if (seed_len != P256_SEED_LEN) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); - return 0; - } - uint8_t private_key[P256_PRIVATE_KEY_LEN]; - if (!p256_private_key_from_seed(private_key, seed, seed_len) || - !p256_public_from_private(out_enc, private_key)) { - return 0; - } - - uint8_t dh[P256_SHARED_KEY_LEN]; - if (peer_public_key_len != P256_PUBLIC_VALUE_LEN || - !p256(dh, private_key, peer_public_key)) { - OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); - return 0; - } - - uint8_t kem_context[2 * P256_PUBLIC_VALUE_LEN]; - OPENSSL_memcpy(kem_context, out_enc, P256_PUBLIC_VALUE_LEN); - OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, peer_public_key, - P256_PUBLIC_VALUE_LEN); - if (!dhkem_extract_and_expand(kem->id, EVP_sha256(), out_shared_secret, - SHA256_DIGEST_LENGTH, dh, sizeof(dh), - kem_context, sizeof(kem_context))) { - return 0; - } - - *out_enc_len = P256_PUBLIC_VALUE_LEN; - *out_shared_secret_len = SHA256_DIGEST_LENGTH; - return 1; -} - -static int p256_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) { - uint8_t dh[P256_SHARED_KEY_LEN]; - if (enc_len != P256_PUBLIC_VALUE_LEN || // - !p256(dh, key->private_key, enc)) { - OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); - return 0; - } - - uint8_t kem_context[2 * P256_PUBLIC_VALUE_LEN]; - OPENSSL_memcpy(kem_context, enc, P256_PUBLIC_VALUE_LEN); - OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, key->public_key, - P256_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; -} - -static int p256_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 < P256_PUBLIC_VALUE_LEN) { - OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE); - return 0; - } - if (seed_len != P256_SEED_LEN) { - OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); - return 0; - } - uint8_t private_key[P256_PRIVATE_KEY_LEN]; - if (!p256_private_key_from_seed(private_key, seed, seed_len) || - !p256_public_from_private(out_enc, private_key)) { - return 0; - } - - uint8_t dh[2 * P256_SHARED_KEY_LEN]; - if (peer_public_key_len != P256_PUBLIC_VALUE_LEN || - !p256(dh, private_key, peer_public_key) || - !p256(dh + P256_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 * P256_PUBLIC_VALUE_LEN]; - OPENSSL_memcpy(kem_context, out_enc, P256_PUBLIC_VALUE_LEN); - OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, peer_public_key, - P256_PUBLIC_VALUE_LEN); - OPENSSL_memcpy(kem_context + 2 * P256_PUBLIC_VALUE_LEN, key->public_key, - P256_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 = P256_PUBLIC_VALUE_LEN; - *out_shared_secret_len = SHA256_DIGEST_LENGTH; - return 1; -} - -static int p256_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 * P256_SHARED_KEY_LEN]; - if (enc_len != P256_PUBLIC_VALUE_LEN || - peer_public_key_len != P256_PUBLIC_VALUE_LEN || - !p256(dh, key->private_key, enc) || - !p256(dh + P256_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 * P256_PUBLIC_VALUE_LEN]; - OPENSSL_memcpy(kem_context, enc, P256_PUBLIC_VALUE_LEN); - OPENSSL_memcpy(kem_context + P256_PUBLIC_VALUE_LEN, key->public_key, - P256_PUBLIC_VALUE_LEN); - OPENSSL_memcpy(kem_context + 2 * P256_PUBLIC_VALUE_LEN, peer_public_key, - P256_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; -} +} // namespace const EVP_HPKE_KEM *EVP_hpke_p256_hkdf_sha256() { static const EVP_HPKE_KEM kKEM = { /*id=*/EVP_HPKE_DHKEM_P256_HKDF_SHA256, - // TODO(crbug.com/503758094): Expose this as an EVP_KEM. - /*evp_kem_func=*/nullptr, - /*key_method=*/nullptr, - /*public_key_len=*/P256_PUBLIC_KEY_LEN, - /*private_key_len=*/P256_PRIVATE_KEY_LEN, - /*seed_len=*/P256_SEED_LEN, - /*enc_len=*/P256_PUBLIC_VALUE_LEN, - p256_init_key, - p256_generate_key, - p256_derive_key, - p256_encap_with_seed, - p256_decap, - p256_auth_encap_with_seed, - p256_auth_decap, - /*evp_auth_encap=*/nullptr, - /*evp_auth_encap_external_entropy=*/nullptr, - /*evp_auth_decap=*/nullptr, + /*evp_kem_func=*/&evp_kem_dhkem_p256_hkdf_sha256, + /*key_method=*/&kP256KeyMethod, + /*public_key_len=*/0, + /*private_key_len=*/0, + /*seed_len=*/0, + /*enc_len=*/0, + /*init_key=*/nullptr, + /*generate_key=*/nullptr, + /*derive_key=*/nullptr, + /*encap_with_seed=*/nullptr, + /*decap=*/nullptr, + /*auth_encap_with_seed=*/nullptr, + /*auth_decap=*/nullptr, + /*evp_auth_encap=*/&dhkem_auth_encap, + /*evp_auth_encap_external_entropy=*/&dhkem_auth_encap_external_entropy, + /*evp_auth_decap=*/&dhkem_auth_decap, }; return &kKEM; }