diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 67210a047..91df6079d 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -1010,6 +1010,16 @@ size_t crypto_ecdh_prime_len(struct crypto_ecdh *ecdh); */ struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len); +/** + * crypto_ec_key_set_priv - Initialize EC key pair from raw key data + * @group: Identifying number for the ECC group + * @raw: Raw key data + * @raw_len: Length of @raw buffer + * Returns: EC key or %NULL on failure + */ +struct crypto_ec_key * crypto_ec_key_set_priv(int group, + const u8 *raw, size_t raw_len); + /** * crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1 * @der: DER encoding of ASN.1 SubjectPublicKeyInfo @@ -1313,6 +1323,58 @@ struct wpabuf * crypto_rsa_oaep_sha256_decrypt(struct crypto_rsa_key *key, */ void crypto_rsa_key_free(struct crypto_rsa_key *key); +enum hpke_mode { + HPKE_MODE_BASE = 0x00, + HPKE_MODE_PSK = 0x01, + HPKE_MODE_AUTH = 0x02, + HPKE_MODE_AUTH_PSK = 0x03, +}; + +enum hpke_kem_id { + HPKE_DHKEM_P256_HKDF_SHA256 = 0x0010, + HPKE_DHKEM_P384_HKDF_SHA384 = 0x0011, + HPKE_DHKEM_P521_HKDF_SHA512 = 0x0012, + HPKE_DHKEM_X5519_HKDF_SHA256 = 0x0020, + HPKE_DHKEM_X448_HKDF_SHA512 = 0x0021, +}; + +enum hpke_kdf_id { + HPKE_KDF_HKDF_SHA256 = 0x0001, + HPKE_KDF_HKDF_SHA384 = 0x0002, + HPKE_KDF_HKDF_SHA512 = 0x0003, +}; + +enum hpke_aead_id { + HPKE_AEAD_AES_128_GCM = 0x0001, + HPKE_AEAD_AES_256_GCM = 0x0002, + HPKE_AEAD_CHACHA20POLY1305 = 0x0003, +}; + +/** + * hpke_base_seal - HPKE base mode single-shot encrypt + * Returns: enc | ct; or %NULL on failure + */ +struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id, + enum hpke_kdf_id kdf_id, + enum hpke_aead_id aead_id, + struct crypto_ec_key *peer_pub, + const u8 *info, size_t info_len, + const u8 *aad, size_t aad_len, + const u8 *pt, size_t pt_len); + +/** + * hpke_base_open - HPKE base mode single-shot decrypt + * @enc_ct: enc | ct + * Returns: pt; or %NULL on failure + */ +struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id, + enum hpke_kdf_id kdf_id, + enum hpke_aead_id aead_id, + struct crypto_ec_key *own_priv, + const u8 *info, size_t info_len, + const u8 *aad, size_t aad_len, + const u8 *enc_ct, size_t enc_ct_len); + /** * crypto_unload - Unload crypto resources * diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c index fafb688b4..4147f414e 100644 --- a/src/crypto/crypto_module_tests.c +++ b/src/crypto/crypto_module_tests.c @@ -2190,6 +2190,285 @@ static int test_extract_expand_hkdf(void) } +#ifdef CONFIG_DPP3 + +static const struct hpke_test { + const char *name; + enum hpke_mode mode; + enum hpke_kem_id kem_id; + enum hpke_kdf_id kdf_id; + enum hpke_aead_id aead_id; + const char *info; + int sk_r_group; + const char *pk_r; + const char *sk_r; + const char *enc; + const char *pt; + const char *aad; + const char *ct; +} hpke_tests[] = { + { + .name = "A.3. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P256_HKDF_SHA256, + .kdf_id = HPKE_KDF_HKDF_SHA256, + .aead_id = HPKE_AEAD_AES_128_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 19, + .pk_r = "04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0", + .sk_r = "f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2", + .enc = "04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434", + }, + { + .name = "A.4. DHKEM(P-256, HKDF-SHA256), HKDF-SHA512, AES-128-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P256_HKDF_SHA256, + .kdf_id = HPKE_KDF_HKDF_SHA512, + .aead_id = HPKE_AEAD_AES_128_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 19, + .pk_r = "04085aa5b665dc3826f9650ccbcc471be268c8ada866422f739e2d531d4a8818a9466bc6b449357096232919ec4fe9070ccbac4aac30f4a1a53efcf7af90610edd", + .sk_r = "3ac8530ad1b01885960fab38cf3cdc4f7aef121eaa239f222623614b4079fb38", + .enc = "0493ed86735bdfb978cc055c98b45695ad7ce61ce748f4dd63c525a3b8d53a15565c6897888070070c1579db1f86aaa56deb8297e64db7e8924e72866f9a472580", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "d3cf4984931484a080f74c1bb2a6782700dc1fef9abe8442e44a6f09044c88907200b332003543754eb51917ba", + }, + { + .name = "A.6. DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P521_HKDF_SHA512, + .kdf_id = HPKE_KDF_HKDF_SHA512, + .aead_id = HPKE_AEAD_AES_256_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 21, + .pk_r = "0401b45498c1714e2dce167d3caf162e45e0642afc7ed435df7902ccae0e84ba0f7d373f646b7738bbbdca11ed91bdeae3cdcba3301f2457be452f271fa6837580e661012af49583a62e48d44bed350c7118c0d8dc861c238c72a2bda17f64704f464b57338e7f40b60959480c0e58e6559b190d81663ed816e523b6b6a418f66d2451ec64", + .sk_r = "01462680369ae375e4b3791070a7458ed527842f6a98a79ff5e0d4cbde83c27196a3916956655523a6a2556a7af62c5cadabe2ef9da3760bb21e005202f7b2462847", + .enc = "040138b385ca16bb0d5fa0c0665fbbd7e69e3ee29f63991d3e9b5fa740aab8900aaeed46ed73a49055758425a0ce36507c54b29cc5b85a5cee6bae0cf1c21f2731ece2013dc3fb7c8d21654bb161b463962ca19e8c654ff24c94dd2898de12051f1ed0692237fb02b2f8d1dc1c73e9b366b529eb436e98a996ee522aef863dd5739d2f29b0", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a", + }, + { /* self-generated test vector for P-384 */ + .name = "custom DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P384_HKDF_SHA384, + .kdf_id = HPKE_KDF_HKDF_SHA384, + .aead_id = HPKE_AEAD_AES_256_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 20, + .pk_r = "049c0e4dcbbb3c80715cafaa1839d0bc3c3adcc95eb8062f84175f9c3cec115e6b799061c65a0605907785c25b3571564706a8ba6a204452b38c7c205db17d328f2353df05d5f1c568e7503331178c36c2d37bbed48401295407face3f8dae5ed8", + .sk_r = "cabffb07d20ffcfdaa043e1de49e1654659e0f0aba5de56523e8b73dc80c579a9e5c89ed3810ec21c4bafcf74ad2a245", + .enc = "04b30bea96d0e51582033b02a4d676d0464a5eb2d858be86cda1c4e6f8b2aa9fb80f5365483f781b1b3a8b3b8efd50b0f7bca16f06d0435fa3da1d671ea0a318b40fe170a074923c651e5dc824966b7b98d0e36bdf932875dae7130369a793cecc", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "ae7feccfea0f8fcd620d15369a28db8701cdc90d55c20efff6296bd441697b0da34671d1f3c4864183e86d27fc", + }, + { /* self-generated test vector for BP-256 */ + .name = "custom PB-256 using DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P256_HKDF_SHA256, + .kdf_id = HPKE_KDF_HKDF_SHA256, + .aead_id = HPKE_AEAD_AES_128_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 28, + .pk_r = "04a2cb9c4cae90cdc1c27516e9f84b6b166e4b1dcc517286268239ddb0bf74cca6390fed092ac4423ab2192b8bb41a4824d908d2053b93fc813830bebac5ce19b9", + .sk_r = "11d9db41c4341166ca52f5a1775595c0bdb4934350daeb7bce659c4b7a40e314", + .enc = "047a25e309c7ee50ec27f13d44734a3ccd8c703e3affcc728513df416511ef9bf02f5e7750e7415de8b5f306ebd3fc88ea9b9368523eb1733a8d82c1a877e5a0f4", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "17c84b3f07f6ffe08ff2be45c709ea782229504aa5b2253876725c6c39f8d8c992304fc5877994f79d6c10d462", + }, + { /* self-generated test vector for BP-384 */ + .name = "custom PB-384 using DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P384_HKDF_SHA384, + .kdf_id = HPKE_KDF_HKDF_SHA384, + .aead_id = HPKE_AEAD_AES_256_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 29, + .pk_r = "041f4199ad28835908079c45d165d55630098be53eb4beede9921f5b2204fa396111f99ac54c56411f7cb2c43ec18d8e604d895027228cf975f5a4b598f189d8fb03e3fefe020258c40d4d1b15fd7587d209925d67a41f9659a8ed6f662fb441e4", + .sk_r = "7017cf8a5a9a81ad4e0d755ccbea27a378b787561f8d5662639850805fefcbaab6b9a15729872abb7dc53d19a6cf77e4", + .enc = "0415d49dedc5bc1ffe9f8de9022c266bb605ec6cd7b77b6ce68974095398856f8aefa4b7abbfbd496b99a2dda3a9c65f1a71b9d40255aa1c7c4205a8b4ef611b96ed29fd2d7b0cde4c0e82058805e6276025cc4fc606f6e5771c31bd9704e9ba0b", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "5f5e9f82bedadec0e9b01a1b304cb48b05c0d6d397b1c8a95ed541218ec54f634a41cbc4066910a409e47b254e", + }, + { /* self-generated test vector for BP-512 */ + .name = "custom PB-512 using DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM", + .mode = HPKE_MODE_BASE, + .kem_id = HPKE_DHKEM_P521_HKDF_SHA512, + .kdf_id = HPKE_KDF_HKDF_SHA512, + .aead_id = HPKE_AEAD_AES_256_GCM, + .info = "4f6465206f6e2061204772656369616e2055726e", + .sk_r_group = 30, + .pk_r = "049e81046a531365a3b5215ac37e7b38f5fa34f86c4eb2e03113b197390a26c555bb007596e131c2541f336eb24a45f44283b5b53fedddfa5642675602fdec17d34120a35efffb44952e32dee7732f2f3245c3314269996b610703a63fb8555a75ca5092690a1125ae8712c1e31fd77aee42bd052e71f9f9459814d6f4065bcea0", + .sk_r = "483b6882608182b296843fa7dfffbdd61ed0372574d4aa32a035c8e33a493927aaf00d42bd9124ebe4df26010b38124668c02b35a749e74845d565734310cfe9", + .enc = "04158d18473aeb3b283d3345b1a87d3de2b192ff9e41b5a98f91daacfb24be72e698cbc04c33078681e507bf346c0ea70c927083a22ca9ea027f420067ee42285b798d95fea51002d097ce28371883202bfd300fb64943669e32c6f1a348087368bb480b757892ebd199a9389978c92cbc44076626d705a771fbbd90c030a6767e", + .pt = "4265617574792069732074727574682c20747275746820626561757479", + .aad = "436f756e742d30", + .ct = "033d91c4514857da5b833635180c1acc09f175cbf44777a7b71e177705cfd17437b1c85d671dd767bb4fe20e2e", + }, +}; + + +static int run_hpke_test(const struct hpke_test *test) +{ + struct wpabuf *info, *pk_r, *sk_r, *enc, *pt, *aad, *ct; + struct wpabuf *res_pt = NULL, *enc_ct = NULL, *res_ct = NULL; + struct crypto_ec_key *own_priv = NULL, *peer_pub = NULL; + int res = -1; + size_t coord_len; + + wpa_printf(MSG_INFO, "- %s", test->name); + + info = wpabuf_parse_bin(test->info); + pk_r = wpabuf_parse_bin(test->pk_r); + sk_r = wpabuf_parse_bin(test->sk_r); + enc = wpabuf_parse_bin(test->enc); + pt = wpabuf_parse_bin(test->pt); + aad = wpabuf_parse_bin(test->aad); + ct = wpabuf_parse_bin(test->ct); + if (!info || !pk_r || !sk_r || !enc || !pt || !aad || !ct) { + wpa_printf(MSG_ERROR, "Could not parse test data"); + goto fail; + } + + /* Receiver - decryption against the test vector */ + + enc_ct = wpabuf_concat(enc, ct); + enc = NULL; + ct = NULL; + if (!enc_ct) + goto fail; + + own_priv = crypto_ec_key_set_priv(test->sk_r_group, wpabuf_head(sk_r), + wpabuf_len(sk_r)); + if (!own_priv) { + wpa_printf(MSG_ERROR, + "HPKE base open - failed to set private key"); + goto fail; + } + + res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id, + own_priv, + wpabuf_head(info), wpabuf_len(info), + wpabuf_head(aad), wpabuf_len(aad), + wpabuf_head(enc_ct), wpabuf_len(enc_ct)); + if (!res_pt) { + wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt"); + wpa_hexdump_buf(MSG_INFO, "pt", res_pt); + goto fail; + } + if (wpabuf_len(res_pt) != wpabuf_len(pt) || + os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt), + wpabuf_len(pt)) != 0) { + wpa_printf(MSG_ERROR, + "HPKE base open - failed - decryption mismatch"); + goto fail; + } + + /* Sender - encryption (randomized algorithm) */ + + if (test->sk_r_group == 19) + coord_len = 32; + else if (test->sk_r_group == 20) + coord_len = 48; + else if (test->sk_r_group == 21) + coord_len = 66; + else if (test->sk_r_group == 28) + coord_len = 32; + else if (test->sk_r_group == 29) + coord_len = 48; + else if (test->sk_r_group == 30) + coord_len = 64; + else + goto fail; + if (wpabuf_len(pk_r) != 1 + 2 * coord_len) { + wpa_printf(MSG_ERROR, "Unexpected pkR length (%zu != %zu)", + wpabuf_len(pk_r), 1 + 2 * coord_len); + goto fail; + } + peer_pub = crypto_ec_key_set_pub(test->sk_r_group, + wpabuf_head_u8(pk_r) + 1, + wpabuf_head_u8(pk_r) + 1 + coord_len, + coord_len); + if (!peer_pub) { + wpa_printf(MSG_ERROR, + "HPKE base open - failed to set public key"); + goto fail; + } + + res_ct = hpke_base_seal(test->kem_id, test->kdf_id, test->aead_id, + peer_pub, + wpabuf_head(info), wpabuf_len(info), + wpabuf_head(aad), wpabuf_len(aad), + wpabuf_head(pt), wpabuf_len(pt)); + if (!res_ct) { + wpa_printf(MSG_ERROR, "HPKE base open - failed to encrypt"); + goto fail; + } + + /* Receiver - decryption (to verify own encryption) */ + + wpabuf_free(res_pt); + res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id, + own_priv, + wpabuf_head(info), wpabuf_len(info), + wpabuf_head(aad), wpabuf_len(aad), + wpabuf_head(res_ct), wpabuf_len(res_ct)); + if (!res_pt) { + wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt own encrypted version"); + goto fail; + } + if (wpabuf_len(res_pt) != wpabuf_len(pt) || + os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt), + wpabuf_len(pt)) != 0) { + wpa_printf(MSG_ERROR, + "HPKE base open - failed - decryption mismatch for own encrypted version"); + wpa_hexdump_buf(MSG_INFO, "pt", res_pt); + goto fail; + } + + res = 0; +fail: + wpabuf_free(info); + wpabuf_free(pk_r); + wpabuf_free(sk_r); + wpabuf_free(enc); + wpabuf_free(pt); + wpabuf_free(aad); + wpabuf_free(ct); + wpabuf_free(enc_ct); + wpabuf_free(res_pt); + wpabuf_free(res_ct); + crypto_ec_key_deinit(own_priv); + return res; +} + +#endif /* CONFIG_DPP3 */ + + +static int test_hpke(void) +{ +#ifdef CONFIG_DPP3 + unsigned int i; + + wpa_printf(MSG_INFO, "RFC 9180 - HPKE"); + for (i = 0; i < ARRAY_SIZE(hpke_tests); i++) { + if (run_hpke_test(&hpke_tests[i]) < 0) + return -1; + } + + wpa_printf(MSG_INFO, "HPKE base open test cases passed"); +#endif /* CONFIG_DPP3 */ + return 0; +} + + static int test_ms_funcs(void) { #ifndef CONFIG_FIPS @@ -2310,6 +2589,7 @@ int crypto_module_tests(void) test_sha384() || test_fips186_2_prf() || test_extract_expand_hkdf() || + test_hpke() || test_ms_funcs()) ret = -1; diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c index 153a1f7aa..b9fc11063 100644 --- a/src/crypto/crypto_openssl.c +++ b/src/crypto/crypto_openssl.c @@ -3039,6 +3039,141 @@ fail: } +struct crypto_ec_key * crypto_ec_key_set_priv(int group, + const u8 *raw, size_t raw_len) +{ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + const char *group_name; + OSSL_PARAM params[4]; + EVP_PKEY_CTX *ctx = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *priv; + EC_POINT *pub = NULL; + EC_GROUP *ec_group = NULL; + size_t len; + u8 *pub_bin = NULL; + u8 *priv_bin = NULL; + int priv_bin_len; + + group_name = crypto_ec_group_2_name(group); + if (!group_name) + return NULL; + + priv = BN_bin2bn(raw, raw_len, NULL); + if (!priv) + return NULL; + priv_bin = os_malloc(raw_len); + if (!priv_bin) + goto fail; + priv_bin_len = BN_bn2lebinpad(priv, priv_bin, raw_len); + if (priv_bin_len < 0) + goto fail; + + ec_group = EC_GROUP_new_by_curve_name(crypto_ec_group_2_nid(group)); + if (!ec_group) + goto fail; + pub = EC_POINT_new(ec_group); + if (!pub || + EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1) + goto fail; + len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, NULL); + if (len == 0) + goto fail; + pub_bin = os_malloc(len); + if (!pub_bin) + goto fail; + len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED, + pub_bin, len, NULL); + if (len == 0) + goto fail; + + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, + (char *) group_name, 0); + params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_PRIV_KEY, + priv_bin, priv_bin_len); + params[2] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, + pub_bin, len); + params[3] = OSSL_PARAM_construct_end(); + + ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL); + if (!ctx || + EVP_PKEY_fromdata_init(ctx) <= 0 || + EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) + goto fail; + +out: + bin_clear_free(priv_bin, raw_len); + os_free(pub_bin); + BN_clear_free(priv); + EVP_PKEY_CTX_free(ctx); + EC_POINT_free(pub); + EC_GROUP_free(ec_group); + return (struct crypto_ec_key *) pkey; + +fail: + EVP_PKEY_free(pkey); + pkey = NULL; + goto out; +#else /* OpenSSL version >= 3.0 */ + EC_KEY *eckey = NULL; + EVP_PKEY *pkey = NULL; + BIGNUM *priv = NULL; + int nid; + const EC_GROUP *ec_group; + EC_POINT *pub = NULL; + + nid = crypto_ec_group_2_nid(group); + if (nid < 0) { + wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group); + return NULL; + } + + eckey = EC_KEY_new_by_curve_name(nid); + priv = BN_bin2bn(raw, raw_len, NULL); + if (!eckey || !priv || + EC_KEY_set_private_key(eckey, priv) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to set EC_KEY: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + ec_group = EC_KEY_get0_group(eckey); + if (!ec_group) + goto fail; + pub = EC_POINT_new(ec_group); + if (!pub || + EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1 || + EC_KEY_set_public_key(eckey, pub) != 1) { + wpa_printf(MSG_ERROR, + "OpenSSL: Failed to set EC_KEY(pub): %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + pkey = EVP_PKEY_new(); + if (!pkey || EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) { + wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY"); + goto fail; + } + +out: + BN_clear_free(priv); + EC_POINT_free(pub); + return (struct crypto_ec_key *) pkey; + +fail: + EC_KEY_free(eckey); + EVP_PKEY_free(pkey); + pkey = NULL; + goto out; +#endif /* OpenSSL version >= 3.0 */ +} + + struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len) { EVP_PKEY *pkey; @@ -4431,6 +4566,769 @@ void crypto_rsa_key_free(struct crypto_rsa_key *key) } +#ifdef CONFIG_DPP3 + +#define HPKE_MAX_SHARED_SECRET_LEN 66 +#define HPKE_MAX_HASH_LEN 64 +#define HPKE_MAX_KEY_LEN 32 +#define HPKE_MAX_NONCE_LEN 12 +#define HPKE_MAX_PUB_LEN (1 + 2 * 66) + +struct hpke_context { + /* KEM */ + enum hpke_kem_id kem_id; + int kem_nid; + int iana_group; + size_t n_pk; + size_t n_secret; + const EVP_MD *kem_h; + size_t kem_n_h; + + /* KDF */ + enum hpke_kdf_id kdf_id; + const EVP_MD *kdf_h; + size_t n_h; + + /* AEAD */ + enum hpke_aead_id aead_id; + const EVP_CIPHER *cipher; + size_t n_k; + size_t n_n; + size_t n_t; + u8 key[HPKE_MAX_KEY_LEN]; + u8 base_nonce[HPKE_MAX_NONCE_LEN]; +}; + + +static void hpke_free_context(struct hpke_context *ctx) +{ + bin_clear_free(ctx, sizeof(*ctx)); +} + + +static struct hpke_context * hpke_get_context(enum hpke_kem_id kem_id, + enum hpke_kdf_id kdf_id, + enum hpke_aead_id aead_id, + struct crypto_ec_key *key) +{ + struct hpke_context *ctx; + int group; + + ctx = os_zalloc(sizeof(*ctx)); + if (!ctx) + return NULL; + + ctx->kem_id = kem_id; + switch (kem_id) { + case HPKE_DHKEM_P256_HKDF_SHA256: + ctx->kem_nid = NID_X9_62_prime256v1; + ctx->iana_group = 19; + ctx->n_pk = 65; + ctx->n_secret = 32; + ctx->kem_h = EVP_sha256(); + ctx->kem_n_h = 32; + break; + case HPKE_DHKEM_P384_HKDF_SHA384: + ctx->kem_nid = NID_secp384r1; + ctx->iana_group = 20; + ctx->n_pk = 97; + ctx->n_secret = 48; + ctx->kem_h = EVP_sha384(); + ctx->kem_n_h = 48; + break; + case HPKE_DHKEM_P521_HKDF_SHA512: + ctx->kem_nid = NID_secp521r1; + ctx->iana_group = 21; + ctx->n_pk = 133; + ctx->n_secret = 64; + ctx->kem_h = EVP_sha512(); + ctx->kem_n_h = 64; + break; + default: + goto fail; + } + + ctx->kdf_id = kdf_id; + switch (kdf_id) { + case HPKE_KDF_HKDF_SHA256: + ctx->kdf_h = EVP_sha256(); + ctx->n_h = 32; + break; + case HPKE_KDF_HKDF_SHA384: + ctx->kdf_h = EVP_sha384(); + ctx->n_h = 48; + break; + case HPKE_KDF_HKDF_SHA512: + ctx->kdf_h = EVP_sha512(); + ctx->n_h = 64; + break; + default: + goto fail; + } + + ctx->aead_id = aead_id; + switch (aead_id) { + case HPKE_AEAD_AES_128_GCM: + ctx->cipher = EVP_aes_128_gcm(); + ctx->n_k = 16; + ctx->n_n = 12; + ctx->n_t = 16; + break; + case HPKE_AEAD_AES_256_GCM: + ctx->cipher = EVP_aes_256_gcm(); + ctx->n_k = 32; + ctx->n_n = 12; + ctx->n_t = 16; + break; + default: + goto fail; + } + + /* Convert BP-256/384/512 to P-256/384/521 for DPP */ + group = crypto_ec_key_group(key); + if (group == 28 && ctx->iana_group == 19) { + ctx->iana_group = 28; + } else if (group == 29 && ctx->iana_group == 20) { + ctx->iana_group = 29; + } else if (group == 30 && ctx->iana_group == 21) { + ctx->iana_group = 30; + ctx->n_pk = 129; + } + if (group != ctx->iana_group) { + wpa_printf(MSG_INFO, "OpenSSL:%s:group mismatch (%d != %d)", + __func__, group, ctx->iana_group); + goto fail; + } + + return ctx; +fail: + hpke_free_context(ctx); + return NULL; +} + + +static size_t hpke_suite_id(struct hpke_context *ctx, bool kem, u8 *suite_id) +{ + size_t suite_id_len; + + if (kem) { + os_memcpy(suite_id, "KEM", 3); + WPA_PUT_BE16(&suite_id[3], ctx->kem_id); + suite_id_len = 5; + } else { + os_memcpy(suite_id, "HPKE", 4); + WPA_PUT_BE16(&suite_id[4], ctx->kem_id); + WPA_PUT_BE16(&suite_id[6], ctx->kdf_id); + WPA_PUT_BE16(&suite_id[8], ctx->aead_id); + suite_id_len = 10; + } + return suite_id_len; +} + + +static int hpke_labeled_extract(struct hpke_context *ctx, bool kem, + const u8 *salt, size_t salt_len, + const char *label, + const u8 *ikm, size_t ikm_len, u8 *prk) +{ + u8 zero[HPKE_MAX_HASH_LEN]; + u8 suite_id[10]; + size_t suite_id_len; + unsigned int mdlen = kem ? ctx->kem_n_h : ctx->n_h; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC *hmac; + OSSL_PARAM params[2]; + EVP_MAC_CTX *hctx; + size_t mlen; + int res; +#else /* OpenSSL version >= 3.0 */ + HMAC_CTX *hctx; + int res; +#endif /* OpenSSL version >= 3.0 */ + + if (!salt || !salt_len) { + salt_len = mdlen; + os_memset(zero, 0, salt_len); + salt = zero; + } + + suite_id_len = hpke_suite_id(ctx, kem, suite_id); + + /* labeled_ikm = concat("HPKE-v1", suite_id, label, ikm) + * return Extract(salt, labeled_ikm) */ + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (!hmac) + return -1; + + params[0] = OSSL_PARAM_construct_utf8_string( + "digest", + (char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0); + params[1] = OSSL_PARAM_construct_end(); + + hctx = EVP_MAC_CTX_new(hmac); + EVP_MAC_free(hmac); + if (!hctx) + return -1; + + if (EVP_MAC_init(hctx, salt, salt_len, params) != 1) + goto fail; + + if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 || + EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 || + EVP_MAC_update(hctx, (const unsigned char *) label, + os_strlen(label)) != 1 || + EVP_MAC_update(hctx, ikm, ikm_len) != 1) + goto fail; + + res = EVP_MAC_final(hctx, prk, &mlen, mdlen); + EVP_MAC_CTX_free(hctx); + + return res == 1 ? 0 : -1; +fail: + EVP_MAC_CTX_free(hctx); + return -1; +#else /* OpenSSL version >= 3.0 */ + hctx = HMAC_CTX_new(); + if (!hctx) + return -1; + res = HMAC_Init_ex(hctx, salt, salt_len, kem ? ctx->kem_h : ctx->kdf_h, + NULL); + if (res != 1) + goto done; + + HMAC_Update(hctx, (const unsigned char *) "HPKE-v1", 7); + HMAC_Update(hctx, suite_id, suite_id_len); + HMAC_Update(hctx, (const unsigned char *) label, os_strlen(label)); + HMAC_Update(hctx, ikm, ikm_len); + + res = HMAC_Final(hctx, prk, &mdlen); +done: + HMAC_CTX_free(hctx); + + return res == 1 ? 0 : -1; +#endif /* OpenSSL version >= 3.0 */ +} + + +static int +hpke_labeled_expand(struct hpke_context *ctx, bool kem, const u8 *prk, + const char *label, const u8 *info, size_t info_len, + u8 *out, size_t out_len) +{ + u8 suite_id[10]; + size_t suite_id_len; + u8 hash[HPKE_MAX_HASH_LEN]; + u8 iter = 0; + size_t label_len = os_strlen(label); + u8 *pos; + size_t left = out_len, clen; + int res = -1; + u8 *labeled_info; + size_t labeled_info_len; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC *hmac; + OSSL_PARAM params[2]; + EVP_MAC_CTX *hctx = NULL; + size_t mdlen; +#else /* OpenSSL version >= 3.0 */ + HMAC_CTX *hctx; + unsigned int mdlen; +#endif /* OpenSSL version >= 3.0 */ + + /* labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id, + * label, info) + * return Expand(prk, labeled_info, L) */ + suite_id_len = hpke_suite_id(ctx, kem, suite_id); + labeled_info_len = 2 + 7 + suite_id_len + label_len + info_len; + labeled_info = os_malloc(labeled_info_len); + if (!labeled_info) + return -1; + pos = labeled_info; + WPA_PUT_BE16(pos, out_len); + pos += 2; + os_memcpy(pos, "HPKE-v1", 7); + pos += 7; + os_memcpy(pos, suite_id, suite_id_len); + pos += suite_id_len; + os_memcpy(pos, label, label_len); + pos += label_len; + if (info && info_len) { + os_memcpy(pos, info, info_len); + pos += info_len; + } + + pos = out; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + hmac = EVP_MAC_fetch(NULL, "HMAC", NULL); + if (!hmac) + return -1; + + params[0] = OSSL_PARAM_construct_utf8_string( + "digest", + (char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0); + params[1] = OSSL_PARAM_construct_end(); +#else /* OpenSSL version >= 3.0 */ + hctx = HMAC_CTX_new(); + if (!hctx) + return -1; +#endif /* OpenSSL version >= 3.0 */ + + while (left > 0) { + mdlen = kem ? ctx->kem_n_h : ctx->n_h; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC_CTX_free(hctx); + hctx = EVP_MAC_CTX_new(hmac); + if (!hctx) + return -1; + + if (EVP_MAC_init(hctx, prk, mdlen, params) != 1) + goto fail; + + if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1) + goto fail; + if (iter == 255) + goto fail; + iter++; + + if (EVP_MAC_update(hctx, labeled_info, labeled_info_len) != 1 || + EVP_MAC_update(hctx, &iter, sizeof(iter)) != 1) + goto fail; + + if (EVP_MAC_final(hctx, hash, &mdlen, mdlen) != 1) + goto fail; +#else /* OpenSSL version >= 3.0 */ + if (HMAC_Init_ex(hctx, prk, mdlen, + kem ? ctx->kem_h : ctx->kdf_h, + NULL) != 1) + goto fail; + + if (iter > 0) + HMAC_Update(hctx, hash, mdlen); + if (iter == 255) + goto fail; + iter++; + HMAC_Update(hctx, labeled_info, labeled_info_len); + HMAC_Update(hctx, &iter, sizeof(iter)); + + if (HMAC_Final(hctx, hash, &mdlen) != 1) + goto fail; + HMAC_CTX_reset(hctx); +#endif /* OpenSSL version >= 3.0 */ + + clen = left > mdlen ? mdlen : left; + os_memcpy(pos, hash, clen); + pos += clen; + left -= clen; + } + res = 0; +fail: +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + EVP_MAC_free(hmac); + EVP_MAC_CTX_free(hctx); +#else /* OpenSSL version >= 3.0 */ + HMAC_CTX_free(hctx); +#endif /* OpenSSL version >= 3.0 */ + os_free(labeled_info); + + return res; +} + + +static int hpke_extract_and_expand(struct hpke_context *ctx, + const u8 *dhss, size_t dhss_len, + const u8 *enc, size_t enc_len, + const u8 *pk_rm, size_t pk_rm_len, + u8 *shared_secret) +{ + u8 kem_context[2 * HPKE_MAX_PUB_LEN]; + u8 eae_prk[HPKE_MAX_HASH_LEN]; + + /* eae_prk = LabeledExtract("", "eae_prk", dh) */ + if (hpke_labeled_extract(ctx, true, NULL, 0, "eae_prk", dhss, dhss_len, + eae_prk) < 0) + return -1; + + if (enc_len > HPKE_MAX_PUB_LEN || pk_rm_len > HPKE_MAX_PUB_LEN) + return -1; + /* kem_context = concat(enc, pkRm) */ + os_memcpy(kem_context, enc, enc_len); + os_memcpy(&kem_context[enc_len], pk_rm, pk_rm_len); + + /* shared_secret = LabeledExpand(eae_prk, "shared_secret", + * kem_context, Nsecret) */ + if (hpke_labeled_expand(ctx, true, eae_prk, "shared_secret", + kem_context, enc_len + pk_rm_len, + shared_secret, ctx->n_secret) < 0) + return -1; + + forced_memzero(eae_prk, sizeof(eae_prk)); + return 0; +} + + +static int hpke_key_schedule(struct hpke_context *ctx, const u8 *shared_secret, + const u8 *info, size_t info_len) +{ + u8 key_schedule_context[1 + 2 * HPKE_MAX_HASH_LEN]; + u8 secret[HPKE_MAX_HASH_LEN]; + int res = -1; + + /* key_schedule_context = concat(mode, psk_id_hash, info_hash) */ + key_schedule_context[0] = HPKE_MODE_BASE; + + /* psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) */ + if (hpke_labeled_extract(ctx, false, NULL, 0, "psk_id_hash", + NULL, 0, &key_schedule_context[1]) < 0) + goto fail; + + /* info_hash = LabeledExtract("", "info_hash", info) */ + if (hpke_labeled_extract(ctx, false, NULL, 0, "info_hash", + info, info_len, + &key_schedule_context[1 + ctx->n_h]) < 0) + goto fail; + + /* secret = LabeledExtract(shared_secret, "secret", psk) */ + if (hpke_labeled_extract(ctx, false, shared_secret, ctx->n_secret, + "secret", NULL, 0, secret) < 0) + goto fail; + + /* key = LabeledExpand(secret, "key", key_schedule_context, Nk) */ + if (hpke_labeled_expand(ctx, false, secret, "key", + key_schedule_context, 1 + 2 * ctx->n_h, + ctx->key, ctx->n_k) < 0) + goto fail; + + /* base_nonce = LabeledExpand(secret, "base_nonce", + * key_schedule_context, Nn) */ + if (hpke_labeled_expand(ctx, false, secret, "base_nonce", + key_schedule_context, 1 + 2 * ctx->n_h, + ctx->base_nonce, ctx->n_n) < 0) + goto fail; + res = 0; +fail: + forced_memzero(key_schedule_context, sizeof(key_schedule_context)); + forced_memzero(secret, sizeof(secret)); + return res; +} + + +static int hpke_encap(struct hpke_context *ctx, struct crypto_ec_key *pk_r, + u8 *shared_secret, u8 *enc) +{ + EVP_PKEY_CTX *pctx = NULL; + struct crypto_ec_key *sk_e; + int res = -1; + u8 dhss[HPKE_MAX_SHARED_SECRET_LEN]; + size_t dhss_len; + struct wpabuf *enc_buf = NULL, *pk_rm = NULL; + + /* skE, pkE = GenerateKeyPair() */ + sk_e = crypto_ec_key_gen(ctx->iana_group); + if (!sk_e) { + wpa_printf(MSG_INFO, "OpenSSL:%s:Could not generate key pair", + __func__); + goto fail; + } + + /* dh = DH(skE, pkR) */ + pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_e, NULL); + if (!pctx || + EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_r) != 1 || + EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 || + dhss_len > HPKE_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + /* enc = SerializePublicKey(pkE) */ + enc_buf = crypto_ec_key_get_pubkey_point(sk_e, 1); + if (!enc_buf) + goto fail; + os_memcpy(enc, wpabuf_head(enc_buf), wpabuf_len(enc_buf)); + + /* pkRm = SerializePublicKey(pkR) */ + pk_rm = crypto_ec_key_get_pubkey_point(pk_r, 1); + if (!pk_rm) + goto fail; + + /* kem_context = concat(enc, pkRm) */ + /* shared_secret = ExtractAndExpand(dh, kem_context) */ + /* return shared_secret, enc */ + res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk, + wpabuf_head(pk_rm), + wpabuf_len(pk_rm), shared_secret); +fail: + forced_memzero(dhss, sizeof(dhss)); + crypto_ec_key_deinit(sk_e); + EVP_PKEY_CTX_free(pctx); + wpabuf_free(enc_buf); + wpabuf_free(pk_rm); + return res; +} + + +static struct wpabuf * +hpke_aead_seal(struct hpke_context *ctx, const u8 *aad, size_t aad_len, + const u8 *pt, size_t pt_len) +{ + EVP_CIPHER_CTX *cctx; + int len = 0; + struct wpabuf *ct = NULL; + + /* No need to xor in sequence number since we support only the + * single-shot API, i.e., base_nonce can be used as-is. */ + + cctx = EVP_CIPHER_CTX_new(); + if (!cctx || + EVP_EncryptInit_ex(cctx, ctx->cipher, NULL, ctx->key, + ctx->base_nonce) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed", + __func__); + goto fail; + } + if (aad && aad_len && + EVP_EncryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate(AAD) failed", + __func__); + goto fail; + } + ct = wpabuf_alloc(pt_len + AES_BLOCK_SIZE + ctx->n_t); + if (!ct) + goto fail; + if (EVP_EncryptUpdate(cctx, wpabuf_put(ct, 0), &len, pt, pt_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate failed", + __func__); + goto fail; + } + wpabuf_put(ct, len); + + if (EVP_EncryptFinal(cctx, wpabuf_put(ct, 0), &len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed", + __func__); + wpabuf_free(ct); + ct = NULL; + goto fail; + } + + if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_GET_TAG, ctx->n_t, + wpabuf_put(ct, ctx->n_t)) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:Could not get tag", + __func__); + wpabuf_free(ct); + ct = NULL; + goto fail; + } +fail: + EVP_CIPHER_CTX_free(cctx); + return ct; +} + + +struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id, + enum hpke_kdf_id kdf_id, + enum hpke_aead_id aead_id, + struct crypto_ec_key *peer_pub, + const u8 *info, size_t info_len, + const u8 *aad, size_t aad_len, + const u8 *pt, size_t pt_len) +{ + struct hpke_context *ctx; + u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN]; + u8 enc[1 + 2 * HPKE_MAX_PUB_LEN]; + struct wpabuf *ct = NULL, *enc_ct = NULL; + + ctx = hpke_get_context(kem_id, kdf_id, aead_id, peer_pub); + if (!ctx) + return NULL; + + /* shared_secret, enc = Encap(pkR) */ + if (hpke_encap(ctx, peer_pub, shared_secret, enc) < 0) + goto fail; + + /* KeyScheduleS(mode_base, shared_secret, info, + * default_psk, default_psk_id) */ + if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0) + goto fail; + + /* ct = ctx.Seal(aad, pt) */ + ct = hpke_aead_seal(ctx, aad, aad_len, pt, pt_len); + if (!ct) + goto fail; + + /* return enc, ct */ + enc_ct = wpabuf_alloc(ctx->n_pk + wpabuf_len(ct)); + if (!enc_ct) + goto fail; + wpabuf_put_data(enc_ct, enc, ctx->n_pk); + wpabuf_put_buf(enc_ct, ct); + +fail: + forced_memzero(shared_secret, sizeof(shared_secret)); + hpke_free_context(ctx); + wpabuf_free(ct); + return enc_ct; +} + + +static int hpke_decap(struct hpke_context *ctx, const u8 *enc, + size_t enc_ct_len, struct crypto_ec_key *sk_r, + u8 *shared_secret) +{ + EVP_PKEY_CTX *pctx = NULL; + struct wpabuf *pk_rm = NULL; + size_t len; + int res = -1; + struct crypto_ec_key *pk_e = NULL; + u8 dhss[HPKE_MAX_SHARED_SECRET_LEN]; + size_t dhss_len; + + /* pkE = DeserializePublicKey(enc) */ + if (enc_ct_len < ctx->n_pk) + return -1; /* not enough room for enc */ + if (enc[0] != 0x04) + return -1; /* not in uncompressed form */ + len = (ctx->n_pk - 1) / 2; + pk_e = crypto_ec_key_set_pub(ctx->iana_group, &enc[1], + &enc[1 + len], len); + if (!pk_e) + return -1; /* invalid public key point */ + /* dh = DH(skR, pkE) */ + pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_r, NULL); + if (!pctx || + EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_e) != 1 || + EVP_PKEY_derive(pctx, NULL, &dhss_len) != 1 || + dhss_len > HPKE_MAX_SHARED_SECRET_LEN || + EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + goto fail; + } + + /* pkRm = SerializePublicKey(pk(skR)) */ + pk_rm = crypto_ec_key_get_pubkey_point(sk_r, 1); + if (!pk_rm) + goto fail; + + /* kem_context = concat(enc, pkRm) */ + /* shared_secret = ExtractAndExpand(dh, kem_context) */ + res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk, + wpabuf_head(pk_rm), + wpabuf_len(pk_rm), shared_secret); +fail: + forced_memzero(dhss, sizeof(dhss)); + crypto_ec_key_deinit(pk_e); + EVP_PKEY_CTX_free(pctx); + wpabuf_free(pk_rm); + return res; +} + + +static struct wpabuf * +hpke_aead_open(struct hpke_context *ctx, const u8 *aad, size_t aad_len, + const u8 *ct, size_t ct_len) +{ + EVP_CIPHER_CTX *cctx; + int len = 0; + const u8 *tag; + struct wpabuf *pt = NULL; + + if (ct_len < ctx->n_t) + return NULL; + tag = ct + ct_len - ctx->n_t; + ct_len -= ctx->n_t; + + /* No need to xor in sequence number since we support only the + * single-shot API, i.e., base_nonce can be used as-is. */ + + cctx = EVP_CIPHER_CTX_new(); + if (!cctx || + EVP_DecryptInit_ex(cctx, ctx->cipher, NULL, ctx->key, + ctx->base_nonce) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed", + __func__); + goto fail; + } + if (aad && aad_len && + EVP_DecryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate(AAD) failed", + __func__); + goto fail; + } + pt = wpabuf_alloc(ct_len + AES_BLOCK_SIZE); + if (!pt) + goto fail; + if (EVP_DecryptUpdate(cctx, wpabuf_put(pt, 0), &len, ct, ct_len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate failed", + __func__); + goto fail; + } + wpabuf_put(pt, len); + + if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG, ctx->n_t, + (void *) tag) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:Could not set tag", + __func__); + wpabuf_free(pt); + pt = NULL; + goto fail; + } + + if (EVP_DecryptFinal(cctx, wpabuf_put(pt, 0), &len) != 1) { + wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed", + __func__); + wpabuf_free(pt); + pt = NULL; + } +fail: + EVP_CIPHER_CTX_free(cctx); + return pt; +} + + +struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id, + enum hpke_kdf_id kdf_id, + enum hpke_aead_id aead_id, + struct crypto_ec_key *own_priv, + const u8 *info, size_t info_len, + const u8 *aad, size_t aad_len, + const u8 *enc_ct, size_t enc_ct_len) +{ + struct hpke_context *ctx; + u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN]; + struct wpabuf *pt = NULL; + + ctx = hpke_get_context(kem_id, kdf_id, aead_id, own_priv); + if (!ctx) + return NULL; + + /* shared_secret = Decap(enc, skR) */ + if (hpke_decap(ctx, enc_ct, enc_ct_len, own_priv, shared_secret) < 0) + goto fail; + + /* KeyScheduleR(mode_base, shared_secret, info, + * default_psk, default_psk_id) */ + if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0) + goto fail; + + /* return ctx.Open(aad, ct) */ + pt = hpke_aead_open(ctx, aad, aad_len, + &enc_ct[ctx->n_pk], enc_ct_len - ctx->n_pk); + +fail: + forced_memzero(shared_secret, sizeof(shared_secret)); + hpke_free_context(ctx); + return pt; +} + +#endif /* CONFIG_DPP3 */ + + void crypto_unload(void) { openssl_unload_legacy_provider();