diff --git a/rust/bssl-crypto/src/hpke.rs b/rust/bssl-crypto/src/hpke.rs index aa120e478..2259140d8 100644 --- a/rust/bssl-crypto/src/hpke.rs +++ b/rust/bssl-crypto/src/hpke.rs @@ -78,8 +78,10 @@ use alloc::vec::Vec; /// Supported KEM algorithms with values detailed in RFC 9180. #[derive(Clone, Copy)] pub enum Kem { - #[allow(missing_docs)] - X25519HkdfSha256 = 32, + /// KEM using DHKEM P-256 and HKDF-SHA256. + P256HkdfSha256 = 16, // 0x0010 + /// KEM using DHKEM X25519 and HKDF-SHA256. + X25519HkdfSha256 = 32, // 0x0020 } impl Kem { @@ -87,11 +89,21 @@ impl Kem { // Safety: this function returns a pointer to static data. unsafe { match self { + Kem::P256HkdfSha256 => bssl_sys::EVP_hpke_p256_hkdf_sha256(), Kem::X25519HkdfSha256 => bssl_sys::EVP_hpke_x25519_hkdf_sha256(), } } } + + fn from_rfc_id(n: u16) -> Option { + match n { + n if n == Kem::P256HkdfSha256 as u16 => Some(Self::P256HkdfSha256), + n if n == Kem::X25519HkdfSha256 as u16 => Some(Self::X25519HkdfSha256), + _ => None, + } + } + /// Generate a public and private key for this KEM. pub fn generate_keypair(&self) -> (Vec, Vec) { let mut key = scoped::EvpHpkeKey::new(); @@ -196,13 +208,12 @@ pub struct Params { impl Params { /// New `Params` from KEM, KDF, and AEAD enums. - pub fn new(_kem: Kem, _kdf: Kdf, aead: Aead) -> Self { - // Safety: EVP_hpke_x25519_hkdf_sha256 and EVP_hpke_hkdf_sha256 just - // return pointers to static data. + pub fn new(kem: Kem, _kdf: Kdf, aead: Aead) -> Self { + // Safety: EVP_hpke_hkdf_sha256 just returns pointer to static data. unsafe { Self { - // Only one KEM and KDF are supported thus far. - kem: bssl_sys::EVP_hpke_x25519_hkdf_sha256(), + kem: kem.as_ffi_ptr(), + // Only one KDF is supported thus far. kdf: bssl_sys::EVP_hpke_hkdf_sha256(), aead: aead.as_ffi_ptr(), } @@ -211,11 +222,11 @@ impl Params { /// New `Params` from KEM, KDF, and AEAD IDs as detailed in RFC 9180. pub fn new_from_rfc_ids(kem_id: u16, kdf_id: u16, aead_id: u16) -> Option { - let kem = Kem::X25519HkdfSha256; + let kem = Kem::from_rfc_id(kem_id)?; let kdf = Kdf::HkdfSha256; let aead = Aead::from_rfc_id(aead_id)?; - if kem_id != kem as u16 || kdf_id != kdf as u16 { + if kdf_id != kdf as u16 { return None; } Some(Self::new(kem, kdf, aead)) @@ -439,7 +450,7 @@ impl RecipientContext { #[cfg(test)] mod test { use super::*; - use crate::test_helpers::decode_hex; + use crate::test_helpers::{decode_hex, decode_hex_into_vec}; struct TestVector { kem_id: u16, @@ -447,9 +458,9 @@ mod test { aead_id: u16, info: [u8; 20], seed_for_testing: [u8; 32], // skEm - recipient_pub_key: [u8; 32], // pkRm + recipient_pub_key: Vec, // pkRm recipient_priv_key: [u8; 32], // skRm - encapsulated_key: [u8; 32], // enc + encapsulated_key: Vec, // enc plaintext: [u8; 29], // pt associated_data: [u8; 7], // aad ciphertext: [u8; 45], // ct @@ -465,9 +476,9 @@ mod test { aead_id: 1, info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), seed_for_testing: decode_hex("52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"), - recipient_pub_key: decode_hex("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"), + recipient_pub_key: decode_hex_into_vec("3948cfe0ad1ddb695d780e59077195da6c56506b027329794ab02bca80815c4d"), recipient_priv_key: decode_hex("4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8"), - encapsulated_key: decode_hex("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"), + encapsulated_key: decode_hex_into_vec("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431"), plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), associated_data: decode_hex("436f756e742d30"), ciphertext: decode_hex("f938558b5d72f1a23810b4be2ab4f84331acc02fc97babc53a52ae8218a355a96d8770ac83d07bea87e13c512a"), @@ -484,9 +495,9 @@ mod test { aead_id: 3, info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), seed_for_testing: decode_hex("f4ec9b33b792c372c1d2c2063507b684ef925b8c75a42dbcbf57d63ccd381600"), - recipient_pub_key: decode_hex("4310ee97d88cc1f088a5576c77ab0cf5c3ac797f3d95139c6c84b5429c59662a"), + recipient_pub_key: decode_hex_into_vec("4310ee97d88cc1f088a5576c77ab0cf5c3ac797f3d95139c6c84b5429c59662a"), recipient_priv_key: decode_hex("8057991eef8f1f1af18f4a9491d16a1ce333f695d4db8e38da75975c4478e0fb"), - encapsulated_key: decode_hex("1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a"), + encapsulated_key: decode_hex_into_vec("1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a"), plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), associated_data: decode_hex("436f756e742d30"), ciphertext: decode_hex("1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28"), @@ -495,9 +506,28 @@ mod test { } } + // https://www.rfc-editor.org/rfc/rfc9180.html#appendix-A.3 + fn p256_hkdf_sha256_hkdf_sha256_aes_128_gcm() -> TestVector { + TestVector { + kem_id: 16, + kdf_id: 1, + aead_id: 1, + info: decode_hex("4f6465206f6e2061204772656369616e2055726e"), + seed_for_testing: decode_hex("4270e54ffd08d79d5928020af4686d8f6b7d35dbe470265f1f5aa22816ce860e"), + recipient_pub_key: decode_hex_into_vec("04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0"), + recipient_priv_key: decode_hex("f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2"), + encapsulated_key: decode_hex_into_vec("04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4"), + plaintext: decode_hex("4265617574792069732074727574682c20747275746820626561757479"), + associated_data: decode_hex("436f756e742d30"), + ciphertext: decode_hex("5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434"), + exporter_context: decode_hex("54657374436f6e74657874"), + exported_value: decode_hex("d8f1ea7942adbba7412c6d431c62d01371ea476b823eb697e1f6e6cae1dab85a"), + } + } + #[test] fn all_algorithms() { - let kems = vec![Kem::X25519HkdfSha256]; + let kems = vec![Kem::X25519HkdfSha256, Kem::P256HkdfSha256]; let kdfs = vec![Kdf::HkdfSha256]; let aeads = vec![Aead::Aes128Gcm, Aead::Aes256Gcm, Aead::Chacha20Poly1305]; let plaintext: &[u8] = b"plaintext"; @@ -579,6 +609,7 @@ mod test { for test in vec![ x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), + p256_hkdf_sha256_hkdf_sha256_aes_128_gcm(), ] { let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); @@ -601,6 +632,7 @@ mod test { for test in vec![ x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), + p256_hkdf_sha256_hkdf_sha256_aes_128_gcm(), ] { let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); @@ -622,6 +654,7 @@ mod test { for test in vec![ x25519_hkdf_sha256_hkdf_sha256_aes_128_gcm(), x25519_hkdf_sha256_hkdf_sha256_chacha20_poly1305(), + p256_hkdf_sha256_hkdf_sha256_aes_128_gcm(), ] { let params = Params::new_from_rfc_ids(test.kem_id, test.kdf_id, test.aead_id).unwrap(); @@ -641,7 +674,8 @@ mod test { &test.recipient_priv_key, &test.encapsulated_key, &test.info, - ).unwrap(); + ) + .unwrap(); assert_eq!( test.exported_value.as_ref(), recipient_ctx.export(&test.exporter_context, test.exported_value.len())