diff --git a/crypto/hpke/hpke.c b/crypto/hpke/hpke.c index cc0def5da..3e9815908 100644 --- a/crypto/hpke/hpke.c +++ b/crypto/hpke/hpke.c @@ -31,12 +31,18 @@ // This file implements draft-irtf-cfrg-hpke-08. -#define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN) +struct evp_hpke_kdf_st { + uint16_t id; + // We only support HKDF-based KDFs. + const EVP_MD *(*hkdf_md_func)(void); +}; -// This is strlen("HPKE") + 3 * sizeof(uint16_t). -#define HPKE_SUITE_ID_LEN 10 +struct evp_hpke_aead_st { + uint16_t id; + const EVP_AEAD *(*aead_func)(void); +}; -#define HPKE_MODE_BASE 0 +#define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN) static const char kHpkeVersionId[] = "HPKE-v1"; @@ -51,20 +57,6 @@ static const uint8_t kX25519SuiteID[] = { 'K', 'E', 'M', EVP_HPKE_DHKEM_X25519_HKDF_SHA256 >> 8, EVP_HPKE_DHKEM_X25519_HKDF_SHA256 & 0x00ff}; -// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE", -// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)). -static int hpke_build_suite_id(uint8_t out[HPKE_SUITE_ID_LEN], uint16_t kdf_id, - uint16_t aead_id) { - CBB cbb; - int ret = CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN) && - add_label_string(&cbb, "HPKE") && - CBB_add_u16(&cbb, EVP_HPKE_DHKEM_X25519_HKDF_SHA256) && - CBB_add_u16(&cbb, kdf_id) && - CBB_add_u16(&cbb, aead_id); - CBB_cleanup(&cbb); - return ret; -} - static int hpke_labeled_extract(const EVP_MD *hkdf_md, uint8_t *out_key, size_t *out_len, const uint8_t *salt, size_t salt_len, const uint8_t *suite_id, @@ -108,81 +100,62 @@ static int hpke_extract_and_expand(const EVP_MD *hkdf_md, uint8_t *out_key, const uint8_t kem_context[KEM_CONTEXT_LEN]) { uint8_t prk[EVP_MAX_MD_SIZE]; size_t prk_len; - static const char kEaePrkLabel[] = "eae_prk"; if (!hpke_labeled_extract(hkdf_md, prk, &prk_len, NULL, 0, kX25519SuiteID, - sizeof(kX25519SuiteID), kEaePrkLabel, dh, + sizeof(kX25519SuiteID), "eae_prk", dh, X25519_PUBLIC_VALUE_LEN)) { return 0; } - static const char kPRKExpandLabel[] = "shared_secret"; if (!hpke_labeled_expand(hkdf_md, out_key, out_len, prk, prk_len, kX25519SuiteID, sizeof(kX25519SuiteID), - kPRKExpandLabel, kem_context, KEM_CONTEXT_LEN)) { + "shared_secret", kem_context, KEM_CONTEXT_LEN)) { return 0; } return 1; } -uint16_t EVP_HPKE_CTX_get_aead_id(const EVP_HPKE_CTX *hpke) { - return hpke->aead_id; -} - -uint16_t EVP_HPKE_CTX_get_kdf_id(const EVP_HPKE_CTX *hpke) { - return hpke->kdf_id; -} +// This is strlen("HPKE") + 3 * sizeof(uint16_t). +#define HPKE_SUITE_ID_LEN 10 -const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id) { - switch (aead_id) { - case EVP_HPKE_AEAD_AES_128_GCM: - return EVP_aead_aes_128_gcm(); - case EVP_HPKE_AEAD_AES_256_GCM: - return EVP_aead_aes_256_gcm(); - case EVP_HPKE_AEAD_CHACHA20POLY1305: - return EVP_aead_chacha20_poly1305(); - } - OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return NULL; +// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE", +// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)). +static int hpke_build_suite_id(const EVP_HPKE_CTX *hpke, + uint8_t out[HPKE_SUITE_ID_LEN]) { + CBB cbb; + int ret = CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN) && + add_label_string(&cbb, "HPKE") && + CBB_add_u16(&cbb, EVP_HPKE_DHKEM_X25519_HKDF_SHA256) && + CBB_add_u16(&cbb, hpke->kdf->id) && + CBB_add_u16(&cbb, hpke->aead->id); + CBB_cleanup(&cbb); + return ret; } -const EVP_MD *EVP_HPKE_get_hkdf_md(uint16_t kdf_id) { - switch (kdf_id) { - case EVP_HPKE_HKDF_SHA256: - return EVP_sha256(); - } - OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return NULL; -} +#define HPKE_MODE_BASE 0 static int hpke_key_schedule(EVP_HPKE_CTX *hpke, const uint8_t *shared_secret, size_t shared_secret_len, const uint8_t *info, size_t info_len) { - // Attempt to get an EVP_AEAD*. - const EVP_AEAD *aead = EVP_HPKE_get_aead(hpke->aead_id); - if (aead == NULL) { - return 0; - } - uint8_t suite_id[HPKE_SUITE_ID_LEN]; - if (!hpke_build_suite_id(suite_id, hpke->kdf_id, hpke->aead_id)) { + if (!hpke_build_suite_id(hpke, suite_id)) { return 0; } // psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) - static const char kPskIdHashLabel[] = "psk_id_hash"; + // TODO(davidben): Precompute this value and store it with the EVP_HPKE_KDF. + const EVP_MD *hkdf_md = hpke->kdf->hkdf_md_func(); uint8_t psk_id_hash[EVP_MAX_MD_SIZE]; size_t psk_id_hash_len; - if (!hpke_labeled_extract(hpke->hkdf_md, psk_id_hash, &psk_id_hash_len, NULL, - 0, suite_id, sizeof(suite_id), kPskIdHashLabel, - NULL, 0)) { + if (!hpke_labeled_extract(hkdf_md, psk_id_hash, &psk_id_hash_len, NULL, 0, + suite_id, sizeof(suite_id), "psk_id_hash", NULL, + 0)) { return 0; } // info_hash = LabeledExtract("", "info_hash", info) - static const char kInfoHashLabel[] = "info_hash"; uint8_t info_hash[EVP_MAX_MD_SIZE]; size_t info_hash_len; - if (!hpke_labeled_extract(hpke->hkdf_md, info_hash, &info_hash_len, NULL, 0, - suite_id, sizeof(suite_id), kInfoHashLabel, info, + if (!hpke_labeled_extract(hkdf_md, info_hash, &info_hash_len, NULL, 0, + suite_id, sizeof(suite_id), "info_hash", info, info_len)) { return 0; } @@ -200,45 +173,37 @@ static int hpke_key_schedule(EVP_HPKE_CTX *hpke, const uint8_t *shared_secret, } // secret = LabeledExtract(shared_secret, "secret", psk) - static const char kSecretExtractLabel[] = "secret"; uint8_t secret[EVP_MAX_MD_SIZE]; size_t secret_len; - if (!hpke_labeled_extract(hpke->hkdf_md, secret, &secret_len, shared_secret, + if (!hpke_labeled_extract(hkdf_md, secret, &secret_len, shared_secret, shared_secret_len, suite_id, sizeof(suite_id), - kSecretExtractLabel, NULL, 0)) { + "secret", NULL, 0)) { return 0; } // key = LabeledExpand(secret, "key", key_schedule_context, Nk) - static const char kKeyExpandLabel[] = "key"; + const EVP_AEAD *aead = hpke->aead->aead_func(); uint8_t key[EVP_AEAD_MAX_KEY_LENGTH]; const size_t kKeyLen = EVP_AEAD_key_length(aead); - if (!hpke_labeled_expand(hpke->hkdf_md, key, kKeyLen, secret, secret_len, - suite_id, sizeof(suite_id), kKeyExpandLabel, context, - context_len)) { - return 0; - } - - // Initialize the HPKE context's AEAD context, storing a copy of |key|. - if (!EVP_AEAD_CTX_init(&hpke->aead_ctx, aead, key, kKeyLen, 0, NULL)) { + if (!hpke_labeled_expand(hkdf_md, key, kKeyLen, secret, secret_len, suite_id, + sizeof(suite_id), "key", context, context_len) || + !EVP_AEAD_CTX_init(&hpke->aead_ctx, aead, key, kKeyLen, + EVP_AEAD_DEFAULT_TAG_LENGTH, NULL)) { return 0; } // base_nonce = LabeledExpand(secret, "base_nonce", key_schedule_context, Nn) - static const char kNonceExpandLabel[] = "base_nonce"; - if (!hpke_labeled_expand(hpke->hkdf_md, hpke->base_nonce, + if (!hpke_labeled_expand(hkdf_md, hpke->base_nonce, EVP_AEAD_nonce_length(aead), secret, secret_len, - suite_id, sizeof(suite_id), kNonceExpandLabel, - context, context_len)) { + suite_id, sizeof(suite_id), "base_nonce", context, + context_len)) { return 0; } // exporter_secret = LabeledExpand(secret, "exp", key_schedule_context, Nh) - static const char kExporterSecretExpandLabel[] = "exp"; - if (!hpke_labeled_expand(hpke->hkdf_md, hpke->exporter_secret, - EVP_MD_size(hpke->hkdf_md), secret, secret_len, - suite_id, sizeof(suite_id), - kExporterSecretExpandLabel, context, context_len)) { + if (!hpke_labeled_expand(hkdf_md, hpke->exporter_secret, EVP_MD_size(hkdf_md), + secret, secret_len, suite_id, sizeof(suite_id), + "exp", context, context_len)) { return 0; } @@ -290,6 +255,33 @@ static int hpke_decap(const EVP_HPKE_CTX *hpke, return 1; } +const EVP_HPKE_KDF *EVP_hpke_hkdf_sha256(void) { + static const EVP_HPKE_KDF kKDF = {EVP_HPKE_HKDF_SHA256, &EVP_sha256}; + return &kKDF; +} + +uint16_t EVP_HPKE_KDF_id(const EVP_HPKE_KDF *kdf) { return kdf->id; } + +const EVP_HPKE_AEAD *EVP_hpke_aes_128_gcm(void) { + static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_128_GCM, + &EVP_aead_aes_128_gcm}; + return &kAEAD; +} + +const EVP_HPKE_AEAD *EVP_hpke_aes_256_gcm(void) { + static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_256_GCM, + &EVP_aead_aes_256_gcm}; + return &kAEAD; +} + +const EVP_HPKE_AEAD *EVP_hpke_chacha20_poly1305(void) { + static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_CHACHA20_POLY1305, + &EVP_aead_chacha20_poly1305}; + return &kAEAD; +} + +uint16_t EVP_HPKE_AEAD_id(const EVP_HPKE_AEAD *aead) { return aead->id; } + void EVP_HPKE_CTX_init(EVP_HPKE_CTX *ctx) { OPENSSL_memset(ctx, 0, sizeof(EVP_HPKE_CTX)); EVP_AEAD_CTX_zero(&ctx->aead_ctx); @@ -300,23 +292,25 @@ void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx) { } int EVP_HPKE_CTX_setup_base_s_x25519(EVP_HPKE_CTX *hpke, uint8_t *out_enc, - size_t out_enc_len, uint16_t kdf_id, - uint16_t aead_id, + size_t out_enc_len, + const EVP_HPKE_KDF *kdf, + const EVP_HPKE_AEAD *aead, 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, + hpke, out_enc, out_enc_len, kdf, aead, 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) { + EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len, + const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, + 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; @@ -331,12 +325,8 @@ int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( } hpke->is_sender = 1; - hpke->kdf_id = kdf_id; - hpke->aead_id = aead_id; - hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id); - if (hpke->hkdf_md == NULL) { - return 0; - } + hpke->kdf = kdf; + hpke->aead = aead; X25519_public_from_private(out_enc, seed); uint8_t shared_secret[SHA256_DIGEST_LENGTH]; if (!hpke_encap(hpke, shared_secret, peer_public_value, seed, out_enc) || @@ -347,13 +337,11 @@ int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( return 1; } -int EVP_HPKE_CTX_setup_base_r_x25519(EVP_HPKE_CTX *hpke, uint16_t kdf_id, - uint16_t aead_id, const uint8_t *enc, - size_t enc_len, const uint8_t *public_key, - size_t public_key_len, - const uint8_t *private_key, - size_t private_key_len, - const uint8_t *info, size_t info_len) { +int EVP_HPKE_CTX_setup_base_r_x25519( + EVP_HPKE_CTX *hpke, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, + const uint8_t *enc, size_t enc_len, const uint8_t *public_key, + size_t public_key_len, const uint8_t *private_key, size_t private_key_len, + const uint8_t *info, size_t info_len) { if (enc_len != X25519_PUBLIC_VALUE_LEN) { OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY); return 0; @@ -365,12 +353,8 @@ int EVP_HPKE_CTX_setup_base_r_x25519(EVP_HPKE_CTX *hpke, uint16_t kdf_id, } hpke->is_sender = 0; - hpke->kdf_id = kdf_id; - hpke->aead_id = aead_id; - hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id); - if (hpke->hkdf_md == NULL) { - return 0; - } + hpke->kdf = kdf; + hpke->aead = aead; uint8_t shared_secret[SHA256_DIGEST_LENGTH]; if (!hpke_decap(hpke, shared_secret, enc, public_key, private_key) || !hpke_key_schedule(hpke, shared_secret, sizeof(shared_secret), info, @@ -398,11 +382,6 @@ static void hpke_nonce(const EVP_HPKE_CTX *hpke, uint8_t *out_nonce, } } -size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke) { - assert(hpke->is_sender); - return EVP_AEAD_max_overhead(hpke->aead_ctx.aead); -} - int EVP_HPKE_CTX_open(EVP_HPKE_CTX *hpke, uint8_t *out, size_t *out_len, size_t max_out_len, const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len) { @@ -455,15 +434,27 @@ int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out, size_t secret_len, const uint8_t *context, size_t context_len) { uint8_t suite_id[HPKE_SUITE_ID_LEN]; - if (!hpke_build_suite_id(suite_id, hpke->kdf_id, hpke->aead_id)) { + if (!hpke_build_suite_id(hpke, suite_id)) { return 0; } - static const char kExportExpandLabel[] = "sec"; - if (!hpke_labeled_expand(hpke->hkdf_md, out, secret_len, - hpke->exporter_secret, EVP_MD_size(hpke->hkdf_md), - suite_id, sizeof(suite_id), kExportExpandLabel, - context, context_len)) { + const EVP_MD *hkdf_md = hpke->kdf->hkdf_md_func(); + if (!hpke_labeled_expand(hkdf_md, out, secret_len, hpke->exporter_secret, + EVP_MD_size(hkdf_md), suite_id, sizeof(suite_id), + "sec", context, context_len)) { return 0; } return 1; } + +size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke) { + assert(hpke->is_sender); + return EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(&hpke->aead_ctx)); +} + +const EVP_HPKE_AEAD *EVP_HPKE_CTX_aead(const EVP_HPKE_CTX *hpke) { + return hpke->aead; +} + +const EVP_HPKE_KDF *EVP_HPKE_CTX_kdf(const EVP_HPKE_CTX *hpke) { + return hpke->kdf; +} diff --git a/crypto/hpke/hpke_test.cc b/crypto/hpke/hpke_test.cc index e4aa93227..82ba229dd 100644 --- a/crypto/hpke/hpke_test.cc +++ b/crypto/hpke/hpke_test.cc @@ -35,6 +35,16 @@ namespace bssl { namespace { +const decltype(&EVP_hpke_aes_128_gcm) kAllAEADs[] = { + &EVP_hpke_aes_128_gcm, + &EVP_hpke_aes_256_gcm, + &EVP_hpke_chacha20_poly1305, +}; + +const decltype(&EVP_hpke_hkdf_sha256) kAllKDFs[] = { + &EVP_hpke_hkdf_sha256, +}; + // HPKETestVector corresponds to one array member in the published // test-vectors.json. class HPKETestVector { @@ -45,24 +55,26 @@ class HPKETestVector { bool ReadFromFileTest(FileTest *t); void Verify() const { - ScopedEVP_HPKE_CTX sender_ctx; - ScopedEVP_HPKE_CTX receiver_ctx; - - ASSERT_GT(secret_key_e_.size(), 0u); + const EVP_HPKE_AEAD *aead = GetAEAD(); + ASSERT_TRUE(aead); + const EVP_HPKE_KDF *kdf = GetKDF(); + ASSERT_TRUE(kdf); // Set up the sender. + ScopedEVP_HPKE_CTX sender_ctx; 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())); + sender_ctx.get(), enc, sizeof(enc), kdf, aead, 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. + ScopedEVP_HPKE_CTX receiver_ctx; ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519( - 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())); + receiver_ctx.get(), kdf, aead, 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()); @@ -70,6 +82,24 @@ class HPKETestVector { } private: + const EVP_HPKE_AEAD *GetAEAD() const { + for (const auto aead : kAllAEADs) { + if (EVP_HPKE_AEAD_id(aead()) == aead_id_) { + return aead(); + } + } + return nullptr; + } + + const EVP_HPKE_KDF *GetKDF() const { + for (const auto kdf : kAllKDFs) { + if (EVP_HPKE_KDF_id(kdf()) == kdf_id_) { + return kdf(); + } + } + return nullptr; + } + void VerifyEncryptions(EVP_HPKE_CTX *sender_ctx, EVP_HPKE_CTX *receiver_ctx) const { for (const Encryption &task : encryptions_) { @@ -212,10 +242,6 @@ TEST(HPKETest, VerifyTestVectors) { // generates new keys for each context. Test this codepath by checking we can // decrypt our own messages. TEST(HPKETest, RoundTrip) { - uint16_t kdf_ids[] = {EVP_HPKE_HKDF_SHA256}; - uint16_t aead_ids[] = {EVP_HPKE_AEAD_AES_128_GCM, EVP_HPKE_AEAD_AES_256_GCM, - EVP_HPKE_AEAD_CHACHA20POLY1305}; - const uint8_t info_a[] = {1, 1, 2, 3, 5, 8}; const uint8_t info_b[] = {42, 42, 42}; const uint8_t ad_a[] = {1, 2, 4, 8, 16}; @@ -228,10 +254,10 @@ TEST(HPKETest, RoundTrip) { uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN]; X25519_keypair(public_key_r, secret_key_r); - for (uint16_t kdf_id : kdf_ids) { - SCOPED_TRACE(kdf_id); - for (uint16_t aead_id : aead_ids) { - SCOPED_TRACE(aead_id); + for (const auto kdf : kAllKDFs) { + SCOPED_TRACE(EVP_HPKE_KDF_id(kdf())); + for (const auto aead : kAllAEADs) { + SCOPED_TRACE(EVP_HPKE_AEAD_id(aead())); for (const Span &info : info_values) { SCOPED_TRACE(Bytes(info)); for (const Span &ad : ad_values) { @@ -240,15 +266,15 @@ TEST(HPKETest, RoundTrip) { ScopedEVP_HPKE_CTX sender_ctx; uint8_t enc[X25519_PUBLIC_VALUE_LEN]; ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519( - sender_ctx.get(), enc, sizeof(enc), kdf_id, aead_id, public_key_r, + sender_ctx.get(), enc, sizeof(enc), kdf(), aead(), public_key_r, sizeof(public_key_r), info.data(), info.size())); // Set up the receiver. ScopedEVP_HPKE_CTX receiver_ctx; ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), kdf_id, aead_id, enc, sizeof(enc), - public_key_r, sizeof(public_key_r), secret_key_r, - sizeof(secret_key_r), info.data(), info.size())); + receiver_ctx.get(), kdf(), aead(), enc, sizeof(enc), public_key_r, + sizeof(public_key_r), secret_key_r, sizeof(secret_key_r), + info.data(), info.size())); const char kCleartextPayload[] = "foobar"; @@ -295,25 +321,21 @@ TEST(HPKETest, X25519EncapSmallOrderPoint) { uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN]; X25519_keypair(public_key_r, secret_key_r); - uint16_t kdf_ids[] = {EVP_HPKE_HKDF_SHA256}; - uint16_t aead_ids[] = {EVP_HPKE_AEAD_AES_128_GCM, EVP_HPKE_AEAD_AES_256_GCM, - EVP_HPKE_AEAD_CHACHA20POLY1305}; - - for (uint16_t kdf_id : kdf_ids) { - SCOPED_TRACE(kdf_id); - for (uint16_t aead_id : aead_ids) { - SCOPED_TRACE(aead_id); + for (const auto kdf : kAllKDFs) { + SCOPED_TRACE(EVP_HPKE_KDF_id(kdf())); + for (const auto aead : kAllAEADs) { + SCOPED_TRACE(EVP_HPKE_AEAD_id(aead())); // Set up the sender, passing in kSmallOrderPoint as |peer_public_value|. ScopedEVP_HPKE_CTX sender_ctx; uint8_t enc[X25519_PUBLIC_VALUE_LEN]; ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519( - sender_ctx.get(), enc, sizeof(enc), kdf_id, aead_id, kSmallOrderPoint, + sender_ctx.get(), enc, sizeof(enc), kdf(), aead(), kSmallOrderPoint, sizeof(kSmallOrderPoint), nullptr, 0)); // Set up the receiver, passing in kSmallOrderPoint as |enc|. ScopedEVP_HPKE_CTX receiver_ctx; ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), kdf_id, aead_id, kSmallOrderPoint, + receiver_ctx.get(), kdf(), aead(), kSmallOrderPoint, sizeof(kSmallOrderPoint), public_key_r, sizeof(public_key_r), secret_key_r, sizeof(secret_key_r), nullptr, 0)); } @@ -333,7 +355,7 @@ TEST(HPKETest, ReceiverInvalidSeal) { // Set up the receiver. ScopedEVP_HPKE_CTX receiver_ctx; ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM, + receiver_ctx.get(), EVP_hpke_hkdf_sha256(), EVP_hpke_aes_128_gcm(), kMockEnc, sizeof(kMockEnc), public_key_r, sizeof(public_key_r), secret_key_r, sizeof(secret_key_r), nullptr, 0)); @@ -360,9 +382,8 @@ TEST(HPKETest, SenderInvalidOpen) { ScopedEVP_HPKE_CTX sender_ctx; uint8_t enc[X25519_PUBLIC_VALUE_LEN]; ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519( - sender_ctx.get(), enc, sizeof(enc), EVP_HPKE_HKDF_SHA256, - EVP_HPKE_AEAD_AES_128_GCM, public_key_r, sizeof(public_key_r), nullptr, - 0)); + sender_ctx.get(), enc, sizeof(enc), EVP_hpke_hkdf_sha256(), + EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0)); // Call Open() on the sender. uint8_t cleartext[128]; @@ -380,9 +401,8 @@ TEST(HPKETest, SetupSenderWrongLengthEnc) { ScopedEVP_HPKE_CTX sender_ctx; uint8_t bogus_enc[X25519_PUBLIC_VALUE_LEN + 5]; ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519( - sender_ctx.get(), bogus_enc, sizeof(bogus_enc), EVP_HPKE_HKDF_SHA256, - EVP_HPKE_AEAD_AES_128_GCM, public_key_r, sizeof(public_key_r), nullptr, - 0)); + sender_ctx.get(), bogus_enc, sizeof(bogus_enc), EVP_hpke_hkdf_sha256(), + EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0)); uint32_t err = ERR_get_error(); EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err)); EXPECT_EQ(EVP_R_INVALID_BUFFER_SIZE, ERR_GET_REASON(err)); @@ -398,7 +418,7 @@ TEST(HPKETest, SetupReceiverWrongLengthEnc) { ScopedEVP_HPKE_CTX receiver_ctx; ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM, + receiver_ctx.get(), EVP_hpke_hkdf_sha256(), EVP_hpke_aes_128_gcm(), bogus_enc, sizeof(bogus_enc), public_key, sizeof(public_key), private_key, sizeof(private_key), nullptr, 0)); uint32_t err = ERR_get_error(); @@ -412,8 +432,8 @@ TEST(HPKETest, SetupSenderWrongLengthPeerPublicValue) { ScopedEVP_HPKE_CTX sender_ctx; uint8_t enc[X25519_PUBLIC_VALUE_LEN]; ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519( - sender_ctx.get(), enc, sizeof(enc), EVP_HPKE_HKDF_SHA256, - EVP_HPKE_AEAD_AES_128_GCM, bogus_public_key_r, sizeof(bogus_public_key_r), + sender_ctx.get(), enc, sizeof(enc), EVP_hpke_hkdf_sha256(), + EVP_hpke_aes_128_gcm(), bogus_public_key_r, sizeof(bogus_public_key_r), nullptr, 0)); uint32_t err = ERR_get_error(); EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err)); @@ -437,7 +457,7 @@ TEST(HPKETest, SetupReceiverWrongLengthKeys) { { // Test base mode with |bogus_public_key|. ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM, + receiver_ctx.get(), EVP_hpke_hkdf_sha256(), EVP_hpke_aes_128_gcm(), enc, sizeof(enc), bogus_public_key, sizeof(bogus_public_key), private_key, sizeof(private_key), nullptr, 0)); uint32_t err = ERR_get_error(); @@ -448,7 +468,7 @@ TEST(HPKETest, SetupReceiverWrongLengthKeys) { { // Test base mode with |bogus_private_key|. ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519( - receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM, + receiver_ctx.get(), EVP_hpke_hkdf_sha256(), EVP_hpke_aes_128_gcm(), enc, sizeof(enc), public_key, sizeof(public_key), bogus_private_key, sizeof(bogus_private_key), nullptr, 0)); uint32_t err = ERR_get_error(); diff --git a/crypto/hpke/internal.h b/crypto/hpke/internal.h index 54382c86f..6dee25ad8 100644 --- a/crypto/hpke/internal.h +++ b/crypto/hpke/internal.h @@ -32,35 +32,44 @@ extern "C" { // // See https://tools.ietf.org/html/draft-irtf-cfrg-hpke-08. -// EVP_HPKE_DHKEM_* are KEM identifiers. -#define EVP_HPKE_DHKEM_X25519_HKDF_SHA256 0x0020 -// EVP_HPKE_AEAD_* are AEAD identifiers. -#define EVP_HPKE_AEAD_AES_128_GCM 0x0001 -#define EVP_HPKE_AEAD_AES_256_GCM 0x0002 -#define EVP_HPKE_AEAD_CHACHA20POLY1305 0x0003 +// Parameters. +// +// An HPKE context is parameterized by KEM, KDF, and AEAD algorithms. + +typedef struct evp_hpke_kdf_st EVP_HPKE_KDF; +typedef struct evp_hpke_aead_st EVP_HPKE_AEAD; + +// The following constants are KEM identifiers. +#define EVP_HPKE_DHKEM_X25519_HKDF_SHA256 0x0020 -// EVP_HPKE_HKDF_* are HKDF identifiers. +// The following constants are KDF identifiers. #define EVP_HPKE_HKDF_SHA256 0x0001 -// EVP_HPKE_MAX_OVERHEAD contains the largest value that -// |EVP_HPKE_CTX_max_overhead| would ever return for any context. -#define EVP_HPKE_MAX_OVERHEAD EVP_AEAD_MAX_OVERHEAD +// The following functions are KDF algorithms which may be used with HPKE. +OPENSSL_EXPORT const EVP_HPKE_KDF *EVP_hpke_hkdf_sha256(void); + +// EVP_HPKE_KDF_id returns the HPKE KDF identifier for |kdf|. +OPENSSL_EXPORT uint16_t EVP_HPKE_KDF_id(const EVP_HPKE_KDF *kdf); + +// The following constants are AEAD identifiers. +#define EVP_HPKE_AES_128_GCM 0x0001 +#define EVP_HPKE_AES_256_GCM 0x0002 +#define EVP_HPKE_CHACHA20_POLY1305 0x0003 + +// The following functions are AEAD algorithms which may be used with HPKE. +OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_hpke_aes_128_gcm(void); +OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_hpke_aes_256_gcm(void); +OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_hpke_chacha20_poly1305(void); + +// EVP_HPKE_AEAD_id returns the HPKE AEAD identifier for |aead|. +OPENSSL_EXPORT uint16_t EVP_HPKE_AEAD_id(const EVP_HPKE_AEAD *aead); // Encryption contexts. // An |EVP_HPKE_CTX| is an HPKE encryption context. -typedef struct evp_hpke_ctx_st { - const EVP_MD *hkdf_md; - EVP_AEAD_CTX aead_ctx; - uint16_t kdf_id; - uint16_t aead_id; - uint8_t base_nonce[EVP_AEAD_MAX_NONCE_LENGTH]; - uint8_t exporter_secret[EVP_MAX_MD_SIZE]; - uint64_t seq; - int is_sender; -} EVP_HPKE_CTX; +typedef struct evp_hpke_ctx_st EVP_HPKE_CTX; // EVP_HPKE_CTX_init initializes an already-allocated |EVP_HPKE_CTX|. The caller // should then use one of the |EVP_HPKE_CTX_setup_*| functions. @@ -74,12 +83,6 @@ OPENSSL_EXPORT void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx); // Setting up HPKE contexts. -// -// In each of the following functions, |hpke| must have been initialized with -// |EVP_HPKE_CTX_init|. |kdf_id| selects the KDF for non-KEM HPKE operations and -// must be one of the |EVP_HPKE_HKDF_*| constants. |aead_id| selects the AEAD -// for the "open" and "seal" operations and must be one of the |EVP_HPKE_AEAD_*| -// constants. // EVP_HPKE_CTX_setup_base_s_x25519 sets up |hpke| as a sender context that can // encrypt for the private key corresponding to |peer_public_value| (the @@ -90,18 +93,19 @@ OPENSSL_EXPORT void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx); // key, to |out_enc|. It will fail if the buffer's size in |out_enc_len| is not // exactly |X25519_PUBLIC_VALUE_LEN|. OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519( - 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); + EVP_HPKE_CTX *hpke, uint8_t *out_enc, size_t out_enc_len, + const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, + 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_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 *hpke, uint8_t *out_enc, size_t out_enc_len, + const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, + 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. @@ -110,10 +114,10 @@ OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519_with_seed_for_testing( // |enc| is the encapsulated shared secret from the sender. If |enc| is invalid, // this function will fail. OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_r_x25519( - EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id, const uint8_t *enc, - size_t enc_len, const uint8_t *public_key, size_t public_key_len, - const uint8_t *private_key, size_t private_key_len, const uint8_t *info, - size_t info_len); + EVP_HPKE_CTX *hpke, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead, + const uint8_t *enc, size_t enc_len, const uint8_t *public_key, + size_t public_key_len, const uint8_t *private_key, size_t private_key_len, + const uint8_t *info, size_t info_len); // Using an HPKE context. @@ -166,28 +170,38 @@ OPENSSL_EXPORT int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out, const uint8_t *context, size_t context_len); +// EVP_HPKE_MAX_OVERHEAD contains the largest value that +// |EVP_HPKE_CTX_max_overhead| would ever return for any context. +#define EVP_HPKE_MAX_OVERHEAD EVP_AEAD_MAX_OVERHEAD + // EVP_HPKE_CTX_max_overhead returns the maximum number of additional bytes // added by sealing data with |EVP_HPKE_CTX_seal|. The |hpke| context must be // set up as a sender. OPENSSL_EXPORT size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke); -// EVP_HPKE_CTX_get_aead_id returns |hpke|'s configured AEAD. The returned value -// is one of the |EVP_HPKE_AEAD_*| constants, or zero if the context has not -// been set up. -OPENSSL_EXPORT uint16_t EVP_HPKE_CTX_get_aead_id(const EVP_HPKE_CTX *hpke); +// EVP_HPKE_CTX_aead returns |hpke|'s configured AEAD, or NULL if the context +// has not been set up. +OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_HPKE_CTX_aead(const EVP_HPKE_CTX *hpke); -// EVP_HPKE_CTX_get_aead_id returns |hpke|'s configured KDF. The returned value -// is one of the |EVP_HPKE_HKDF_*| constants, or zero if the context has not -// been set up. -OPENSSL_EXPORT uint16_t EVP_HPKE_CTX_get_kdf_id(const EVP_HPKE_CTX *hpke); +// EVP_HPKE_CTX_kdf returns |hpke|'s configured KDF, or NULL if the context +// has not been set up. +OPENSSL_EXPORT const EVP_HPKE_KDF *EVP_HPKE_CTX_kdf(const EVP_HPKE_CTX *hpke); -// EVP_HPKE_get_aead returns the AEAD corresponding to |aead_id|, or NULL if -// |aead_id| is not a known AEAD identifier. -OPENSSL_EXPORT const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id); -// EVP_HPKE_get_hkdf_md returns the hash function associated with |kdf_id|, or -// NULL if |kdf_id| is not a known KDF identifier that uses HKDF. -OPENSSL_EXPORT const EVP_MD *EVP_HPKE_get_hkdf_md(uint16_t kdf_id); +// Private structures. +// +// The following structures are exported so their types are stack-allocatable, +// but accessing or modifying their fields is forbidden. + +struct evp_hpke_ctx_st { + const EVP_HPKE_AEAD *aead; + const EVP_HPKE_KDF *kdf; + EVP_AEAD_CTX aead_ctx; + uint8_t base_nonce[EVP_AEAD_MAX_NONCE_LENGTH]; + uint8_t exporter_secret[EVP_MAX_MD_SIZE]; + uint64_t seq; + int is_sender; +}; #if defined(__cplusplus) diff --git a/ssl/encrypted_client_hello.cc b/ssl/encrypted_client_hello.cc index 959017e90..da2331969 100644 --- a/ssl/encrypted_client_hello.cc +++ b/ssl/encrypted_client_hello.cc @@ -12,13 +12,17 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + +#include + #include #include #include #include -#include #include "internal.h" +#include "../crypto/hpke/internal.h" #if defined(OPENSSL_MSAN) @@ -29,6 +33,22 @@ BSSL_NAMESPACE_BEGIN +static const decltype(&EVP_hpke_aes_128_gcm) kSupportedAEADs[] = { + &EVP_hpke_aes_128_gcm, + &EVP_hpke_aes_256_gcm, + &EVP_hpke_chacha20_poly1305, +}; + +static const EVP_HPKE_AEAD *get_ech_aead(uint16_t aead_id) { + for (const auto aead_func : kSupportedAEADs) { + const EVP_HPKE_AEAD *aead = aead_func(); + if (aead_id == EVP_HPKE_AEAD_id(aead)) { + return aead; + } + } + return nullptr; +} + // ssl_client_hello_write_without_extensions serializes |client_hello| into // |out|, omitting the length-prefixed extensions. It serializes individual // fields, starting with |client_hello->version|, and ignores the @@ -316,18 +336,18 @@ bool ssl_client_hello_decrypt( } -bool ECHServerConfig::Init(Span raw, +bool ECHServerConfig::Init(Span ech_config, Span private_key, bool is_retry_config) { assert(!initialized_); is_retry_config_ = is_retry_config; - if (!raw_.CopyFrom(raw)) { + if (!ech_config_.CopyFrom(ech_config)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return false; } - // Read from |raw_| so we can save Spans with the same lifetime as |this|. - CBS reader(raw_); + // Read from |ech_config_| so we can save Spans with the same lifetime as |this|. + CBS reader(ech_config_); uint16_t version; if (!CBS_get_u16(&reader, &version)) { @@ -386,12 +406,9 @@ bool ECHServerConfig::Init(Span raw, OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); return false; } - // This parser fails when it encounters any bytes it does not understand. If - // the config lists any unsupported cipher suites, that is a parse error. - if (kdf_id != EVP_HPKE_HKDF_SHA256 || - (aead_id != EVP_HPKE_AEAD_AES_128_GCM && - aead_id != EVP_HPKE_AEAD_AES_256_GCM && - aead_id != EVP_HPKE_AEAD_CHACHA20POLY1305)) { + // The server promises to support every option in the ECHConfig, so reject + // any unsupported cipher suites. + if (kdf_id != EVP_HPKE_HKDF_SHA256 || get_ech_aead(aead_id) == nullptr) { OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG); return false; } @@ -414,10 +431,14 @@ bool ECHServerConfig::Init(Span raw, return true; } -bool ECHServerConfig::SupportsCipherSuite(uint16_t kdf_id, - uint16_t aead_id) const { +bool ECHServerConfig::SetupContext(EVP_HPKE_CTX *ctx, uint16_t kdf_id, + uint16_t aead_id, + Span enc) const { assert(initialized_); + + // Check the cipher suite is supported by this ECHServerConfig. CBS cbs(cipher_suites_); + bool cipher_ok = false; while (CBS_len(&cbs) != 0) { uint16_t supported_kdf_id, supported_aead_id; if (!CBS_get_u16(&cbs, &supported_kdf_id) || @@ -425,10 +446,30 @@ bool ECHServerConfig::SupportsCipherSuite(uint16_t kdf_id, return false; } if (kdf_id == supported_kdf_id && aead_id == supported_aead_id) { - return true; + cipher_ok = true; + break; } } - return false; + if (!cipher_ok) { + return false; + } + + static const uint8_t kInfoLabel[] = "tls ech"; + ScopedCBB info_cbb; + if (!CBB_init(info_cbb.get(), sizeof(kInfoLabel) + ech_config_.size()) || + !CBB_add_bytes(info_cbb.get(), kInfoLabel, + sizeof(kInfoLabel) /* includes trailing NUL */) || + !CBB_add_bytes(info_cbb.get(), ech_config_.data(), ech_config_.size())) { + OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); + return false; + } + + assert(kdf_id == EVP_HPKE_HKDF_SHA256); + assert(get_ech_aead(aead_id) != NULL); + return EVP_HPKE_CTX_setup_base_r_x25519( + ctx, EVP_hpke_hkdf_sha256(), get_ech_aead(aead_id), enc.data(), + enc.size(), public_key_.data(), public_key_.size(), private_key_, + sizeof(private_key_), CBB_data(info_cbb.get()), CBB_len(info_cbb.get())); } BSSL_NAMESPACE_END diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc index 5d6b3e5c1..5da6b40b9 100644 --- a/ssl/handshake_server.cc +++ b/ssl/handshake_server.cc @@ -621,35 +621,10 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { if (hs->ech_server_config_list) { for (const ECHServerConfig &ech_config : hs->ech_server_config_list->configs) { - // Skip this config if the client-provided config_id does not match or - // if the client indicated an unsupported HPKE ciphersuite. - if (config_id != ech_config.config_id() || - !ech_config.SupportsCipherSuite(kdf_id, aead_id)) { - continue; - } - - static const uint8_t kInfoLabel[] = "tls ech"; - ScopedCBB info_cbb; - if (!CBB_init(info_cbb.get(), - sizeof(kInfoLabel) + ech_config.raw().size()) || - !CBB_add_bytes(info_cbb.get(), kInfoLabel, - sizeof(kInfoLabel) /* includes trailing NUL */) || - !CBB_add_bytes(info_cbb.get(), ech_config.raw().data(), - ech_config.raw().size())) { - OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); - return ssl_hs_error; - } - - // Set up a fresh HPKE context for each decryption attempt. hs->ech_hpke_ctx.Reset(); - - if (CBS_len(&enc) != X25519_PUBLIC_VALUE_LEN || - !EVP_HPKE_CTX_setup_base_r_x25519( - hs->ech_hpke_ctx.get(), kdf_id, aead_id, CBS_data(&enc), - CBS_len(&enc), ech_config.public_key().data(), - ech_config.public_key().size(), ech_config.private_key().data(), - ech_config.private_key().size(), CBB_data(info_cbb.get()), - CBB_len(info_cbb.get()))) { + if (config_id != ech_config.config_id() || + !ech_config.SetupContext(hs->ech_hpke_ctx.get(), kdf_id, aead_id, + enc)) { // Ignore the error and try another ECHConfig. ERR_clear_error(); continue; diff --git a/ssl/internal.h b/ssl/internal.h index a51a0e193..f1085a148 100644 --- a/ssl/internal.h +++ b/ssl/internal.h @@ -1443,33 +1443,27 @@ class ECHServerConfig { bool Init(Span ech_config, Span private_key, bool is_retry_config); - // SupportsCipherSuite returns true when this ECHConfig supports the HPKE - // ciphersuite composed of |kdf_id| and |aead_id|. This function must only be - // called on an initialized object. - bool SupportsCipherSuite(uint16_t kdf_id, uint16_t aead_id) const; + // SetupContext sets up |ctx| for a new connection, given the specified + // HPKE ciphersuite and encapsulated KEM key. It returns true on success and + // false on error. This function may only be called on an initialized object. + bool SetupContext(EVP_HPKE_CTX *ctx, uint16_t kdf_id, uint16_t aead_id, + Span enc) const; - Span raw() const { + Span ech_config() const { assert(initialized_); - return raw_; - } - Span public_key() const { - assert(initialized_); - return public_key_; - } - Span private_key() const { - assert(initialized_); - return MakeConstSpan(private_key_, sizeof(private_key_)); + return ech_config_; } bool is_retry_config() const { assert(initialized_); return is_retry_config_; } uint8_t config_id() const { + assert(initialized_); return config_id_; } private: - Array raw_; + Array ech_config_; Span public_key_; Span cipher_suites_; diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 3d283f1b4..5f79e8605 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -1573,7 +1573,7 @@ TEST(SSLTest, ECHServerConfigListTruncatedPublicKey) { ASSERT_TRUE(MakeECHConfig( &ech_config, 0x42, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, MakeConstSpan(kECHConfigPublicKey, sizeof(kECHConfigPublicKey) - 1), - std::vector{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM}, + std::vector{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM}, /*extensions=*/{})); bssl::UniquePtr ctx(SSL_CTX_new(TLS_method())); @@ -1640,7 +1640,7 @@ TEST(SSLTest, UnsupportedECHConfig) { std::vector ech_config; ASSERT_TRUE(MakeECHConfig( &ech_config, 0x42, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, kECHConfigPublicKey, - std::vector{0x002 /* HKDF-SHA384 */, EVP_HPKE_AEAD_AES_128_GCM}, + std::vector{0x002 /* HKDF-SHA384 */, EVP_HPKE_AES_128_GCM}, /*extensions=*/{})); EXPECT_FALSE(SSL_ECH_SERVER_CONFIG_LIST_add( config_list.get(), /*is_retry_config=*/1, ech_config.data(), @@ -1660,7 +1660,7 @@ TEST(SSLTest, UnsupportedECHConfig) { 0x77, 0x0e, 0xe8, 0xd1, 0xc9, 0xce, 0x0a, 0x8b, 0xb4, 0x6a}; ASSERT_TRUE(MakeECHConfig( &ech_config, 0x42, 0x0010 /* DHKEM(P-256, HKDF-SHA256) */, kP256PublicKey, - std::vector{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM}, + std::vector{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM}, /*extensions=*/{})); EXPECT_FALSE(SSL_ECH_SERVER_CONFIG_LIST_add( config_list.get(), /*is_retry_config=*/1, ech_config.data(), @@ -1670,7 +1670,7 @@ TEST(SSLTest, UnsupportedECHConfig) { static const uint8_t kExtensions[] = {0x00, 0x01, 0x00, 0x00}; ASSERT_TRUE(MakeECHConfig( &ech_config, 0x42, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, kECHConfigPublicKey, - std::vector{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_128_GCM}, + std::vector{EVP_HPKE_HKDF_SHA256, EVP_HPKE_AES_128_GCM}, kExtensions)); EXPECT_FALSE(SSL_ECH_SERVER_CONFIG_LIST_add( config_list.get(), /*is_retry_config=*/1, ech_config.data(), diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc index cc6cf05db..0dc53063e 100644 --- a/ssl/t1_lib.cc +++ b/ssl/t1_lib.cc @@ -618,13 +618,10 @@ static bool ext_ech_add_clienthello_grease(SSL_HANDSHAKE *hs, CBB *out) { return true; } - constexpr uint16_t kdf_id = EVP_HPKE_HKDF_SHA256; - const uint16_t aead_id = EVP_has_aes_hardware() - ? EVP_HPKE_AEAD_AES_128_GCM - : EVP_HPKE_AEAD_CHACHA20POLY1305; - const EVP_AEAD *aead = EVP_HPKE_get_aead(aead_id); - assert(aead != nullptr); - + const uint16_t kdf_id = EVP_HPKE_HKDF_SHA256; + const uint16_t aead_id = EVP_has_aes_hardware() ? EVP_HPKE_AES_128_GCM + : EVP_HPKE_CHACHA20_POLY1305; + constexpr size_t kAEADOverhead = 16; // Both AEADs have a 16-byte tag. uint8_t ech_config_id; RAND_bytes(&ech_config_id, 1); @@ -671,9 +668,9 @@ static bool ext_ech_add_clienthello_grease(SSL_HANDSHAKE *hs, CBB *out) { // range of 96 to 192. Note that this estimate does not fully capture // optional extensions like GREASE, but the rounding gives some leeway. - uint8_t payload[EVP_AEAD_MAX_OVERHEAD + 192]; + uint8_t payload[kAEADOverhead + 192]; const size_t payload_len = - EVP_AEAD_max_overhead(aead) + 32 * random_size(96 / 32, 192 / 32); + kAEADOverhead + 32 * random_size(96 / 32, 192 / 32); assert(payload_len <= sizeof(payload)); RAND_bytes(payload, payload_len); @@ -768,8 +765,8 @@ static bool ext_ech_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { if (!config.is_retry_config()) { continue; } - if (!CBB_add_bytes(&retry_configs, config.raw().data(), - config.raw().size())) { + if (!CBB_add_bytes(&retry_configs, config.ech_config().data(), + config.ech_config().size())) { return false; } } diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc index 676591b43..6b9867d2c 100644 --- a/ssl/tls13_server.cc +++ b/ssl/tls13_server.cc @@ -636,10 +636,10 @@ static enum ssl_hs_wait_t do_read_second_client_hello(SSL_HANDSHAKE *hs) { // Check that ClientECH.cipher_suite is unchanged and that // ClientECH.enc is empty. - if (kdf_id != EVP_HPKE_CTX_get_kdf_id(hs->ech_hpke_ctx.get()) || - aead_id != EVP_HPKE_CTX_get_aead_id(hs->ech_hpke_ctx.get()) || - config_id != hs->ech_config_id || - CBS_len(&enc) > 0) { + if (kdf_id != EVP_HPKE_KDF_id(EVP_HPKE_CTX_kdf(hs->ech_hpke_ctx.get())) || + aead_id != + EVP_HPKE_AEAD_id(EVP_HPKE_CTX_aead(hs->ech_hpke_ctx.get())) || + config_id != hs->ech_config_id || CBS_len(&enc) > 0) { OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER); return ssl_hs_error;