HPKE base mode with single-shot API
Add support for HPKE base mode with single-shot API (see RFC 9180) using OpenSSL. This is needed for DPP private introduction protocol. Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
This commit is contained in:
parent
f0273bc814
commit
786ea402bc
3 changed files with 1240 additions and 0 deletions
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue