diff --git a/Cargo.toml b/Cargo.toml index 8bbddb38449..59a969ecb88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,25 +15,20 @@ base-mode-seal = [] serde = ["serde_crate"] algo-all = ["aead-all", "kdf-all", "kem-all"] default = ["algo-all", "base-mode-seal", "base-mode-open"] -aead-all = ["aead-aes-gcm-128", "aead-aes-gcm-256", "aead-chacha-20-poly-1305"] -aead-aes-gcm-128 = [] -aead-aes-gcm-256 = [] -aead-chacha-20-poly-1305 = [] -kdf-all = ["kdf-sha256", "kdf-sha384", "kdf-sha512"] -kdf-sha256 = [] -kdf-sha384 = [] -kdf-sha512 = [] -kem-all = ["kem-dh-p256-hkdf-sha256", "kem-dh-p384-hkdf-sha384", "kem-dh-p521-hkdf-sha512", "kem-x25519-hkdf-sha256"] -kem-dh-p256-hkdf-sha256 = ["hpke/p256"] -kem-dh-p384-hkdf-sha384 = ["hpke/p384"] -kem-dh-p521-hkdf-sha512 = ["hpke/p521"] -kem-x25519-hkdf-sha256 = ["hpke/x25519"] +aead-all = ["aead-aes", "aead-chacha"] +aead-aes = ["hpke/aes"] +aead-chacha = ["hpke/chacha"] +kdf-all = ["kdf-hkdfsha2", "kdf-shake"] +kdf-hkdfsha2 = ["hpke/hkdfsha2"] +kdf-shake = ["hpke/shake"] +kem-all = ["kem-x25519", "kem-nistp", "kem-mlkem"] +kem-x25519 = ["hpke/x25519"] +kem-nistp = ["hpke/nistp"] +kem-mlkem = ["hpke/mlkem"] [dependencies] -rand = "0.9.2" num_enum = "0.7.0" -cfg-if = "1.0.0" -hpke = { version = "0.13.0", default-features = false, features = ["std"] } +hpke = { version = "0.14", default-features = false, features = ["getrandom", "alloc"] } zeroize = "1.6" [dependencies.serde_crate] diff --git a/src/aead.rs b/src/aead.rs index 97e45ffb3ae..1379f6ca039 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -15,13 +15,13 @@ encryption function, as per [RFC9180§7.3](https://www.rfc-editor.org/rfc/rfc918 )] #[cfg_attr(feature = "serde", serde(crate = "serde_crate"))] pub enum Aead { - #[cfg(feature = "aead-aes-gcm-128")] + #[cfg(feature = "aead-aes")] /// AES-128-GCM [GCM](https://doi.org/10.6028/nist.sp.800-38d) AesGcm128 = 1, - #[cfg(feature = "aead-aes-gcm-256")] + #[cfg(feature = "aead-aes")] /// AES-256-GCM [GCM](https://doi.org/10.6028/nist.sp.800-38d) AesGcm256 = 2, - #[cfg(feature = "aead-chacha-20-poly-1305")] + #[cfg(feature = "aead-chacha")] /// ChaCha20Poly1305 [RFC8439](https://www.rfc-editor.org/info/rfc8439) ChaCha20Poly1305 = 3, } @@ -31,11 +31,11 @@ impl FromStr for Aead { fn from_str(s: &str) -> Result { match &*s.to_lowercase().replace('-', "") { - #[cfg(feature = "aead-aes-gcm-128")] + #[cfg(feature = "aead-aes")] "aesgcm128" | "aes128gcm" => Ok(Self::AesGcm128), - #[cfg(feature = "aead-aes-gcm-256")] + #[cfg(feature = "aead-aes")] "aesgcm256" | "aes256gcm" => Ok(Self::AesGcm256), - #[cfg(feature = "aead-chacha-20-poly-1305")] + #[cfg(feature = "aead-chacha")] "chacha20poly1305" => Ok(Self::ChaCha20Poly1305), _ => Err(IdLookupError("aead not recognized")), } @@ -44,10 +44,10 @@ impl FromStr for Aead { /// An iterable slice of [`Aead`] variants pub const AEAD_ALL: &[Aead] = &[ - #[cfg(feature = "aead-aes-gcm-128")] + #[cfg(feature = "aead-aes")] Aead::AesGcm128, - #[cfg(feature = "aead-aes-gcm-256")] + #[cfg(feature = "aead-aes")] Aead::AesGcm256, - #[cfg(feature = "aead-chacha-20-poly-1305")] + #[cfg(feature = "aead-chacha")] Aead::ChaCha20Poly1305, ]; diff --git a/src/base_mode_seal.rs b/src/base_mode_seal.rs index 8917e927728..796f61ff910 100644 --- a/src/base_mode_seal.rs +++ b/src/base_mode_seal.rs @@ -42,13 +42,12 @@ where KdfT: hpke::kdf::Kdf, KemT: hpke::kem::Kem, { - let (encapped_key, ciphertext) = hpke::single_shot_seal::( + let (encapped_key, ciphertext) = hpke::single_shot_seal::( &hpke::OpModeS::Base, &from_bytes(recipient_public_key)?, info, plaintext, aad, - &mut rand::rng(), )?; Ok(EncappedKeyAndCiphertext { diff --git a/src/kdf.rs b/src/kdf.rs index 932ffa9d103..991ba16760e 100644 --- a/src/kdf.rs +++ b/src/kdf.rs @@ -3,8 +3,10 @@ use num_enum::TryFromPrimitive; use std::str::FromStr; /** -Kdf represents an key derivation function, as per +Kdf represents a key derivation function, as per [RFC9180§7.2](https://www.rfc-editor.org/rfc/rfc9180.html#section-7.2) +and +https://www.ietf.org/archive/id/draft-ietf-hpke-pq-05.html#table-1 */ #[non_exhaustive] #[repr(u16)] @@ -15,15 +17,24 @@ Kdf represents an key derivation function, as per )] #[cfg_attr(feature = "serde", serde(crate = "serde_crate"))] pub enum Kdf { - #[cfg(feature = "kdf-sha256")] + #[cfg(feature = "kdf-hkdfsha2")] /// Sha256 [RFC5869](https://www.rfc-editor.org/info/rfc5869) - Sha256 = 1, - #[cfg(feature = "kdf-sha384")] + Sha256 = 0x0001, + #[cfg(feature = "kdf-hkdfsha2")] /// Sha384 [RFC5869](https://www.rfc-editor.org/info/rfc5869) - Sha384 = 2, - #[cfg(feature = "kdf-sha512")] + Sha384 = 0x0002, + #[cfg(feature = "kdf-hkdfsha2")] /// Sha512 [RFC5869](https://www.rfc-editor.org/info/rfc5869) - Sha512 = 3, + Sha512 = 0x0003, + #[cfg(feature = "kdf-shake")] + /// SHAKE128 [RFC 9861](https://www.rfc-editor.org/info/rfc9861/) + Shake128 = 0x0010, + /// SHAKE256 [RFC 9861](https://www.rfc-editor.org/info/rfc9861/) + Shake256 = 0x0011, + /// TurboSHAKE128 [RFC 9861](https://www.rfc-editor.org/info/rfc9861/) + TurboShake128 = 0x0012, + /// TurboSHAKE256 [RFC 9861](https://www.rfc-editor.org/info/rfc9861/) + TurboShake256 = 0x0013, } impl FromStr for Kdf { @@ -31,12 +42,20 @@ impl FromStr for Kdf { fn from_str(s: &str) -> Result { match &*s.to_lowercase().replace('-', "") { - #[cfg(feature = "kdf-sha256")] + #[cfg(feature = "kdf-hkdfsha2")] "hkdfsha256" | "sha256" => Ok(Self::Sha256), - #[cfg(feature = "kdf-sha384")] + #[cfg(feature = "kdf-hkdfsha2")] "hkdfsha384" | "sha384" => Ok(Self::Sha384), - #[cfg(feature = "kdf-sha512")] + #[cfg(feature = "kdf-hkdfsha2")] "hkdfsha512" | "sha512" => Ok(Self::Sha512), + #[cfg(feature = "kdf-shake")] + "shake128" => Ok(Self::Shake128), + #[cfg(feature = "kdf-shake")] + "shake256" => Ok(Self::Shake256), + #[cfg(feature = "kdf-shake")] + "turboshake128" => Ok(Self::TurboShake128), + #[cfg(feature = "kdf-shake")] + "turboshake256" => Ok(Self::TurboShake256), _ => Err(IdLookupError("kdf not recognized")), } } @@ -44,10 +63,18 @@ impl FromStr for Kdf { /// An iterable slice of [`Kdf`] variants pub const KDF_ALL: &[Kdf] = &[ - #[cfg(feature = "kdf-sha256")] + #[cfg(feature = "kdf-hkdfsha2")] Kdf::Sha256, - #[cfg(feature = "kdf-sha384")] + #[cfg(feature = "kdf-hkdfsha2")] Kdf::Sha384, - #[cfg(feature = "kdf-sha512")] + #[cfg(feature = "kdf-hkdfsha2")] Kdf::Sha512, + #[cfg(feature = "kdf-shake")] + Kdf::Shake128, + #[cfg(feature = "kdf-shake")] + Kdf::Shake256, + #[cfg(feature = "kdf-shake")] + Kdf::TurboShake128, + #[cfg(feature = "kdf-shake")] + Kdf::TurboShake256, ]; diff --git a/src/kem.rs b/src/kem.rs index 43ad1c49d66..8be0ec890df 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -4,10 +4,9 @@ use std::str::FromStr; /** Kem represents an asymmetric key encapsulation mechanism, as per -[RFC9180§7.1][section-7.1]. Currently only four of the options listed in -the hpke draft are available. - -[section-7.1]: https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1 +[RFC9180§7.1](https://www.rfc-editor.org/rfc/rfc9180.html#section-7.1) +and +https://www.ietf.org/archive/id/draft-ietf-hpke-pq-05.html#name-updated-ml-kem-entries-for- */ #[non_exhaustive] #[repr(u16)] @@ -19,20 +18,40 @@ the hpke draft are available. #[cfg_attr(feature = "serde", serde(crate = "serde_crate"))] pub enum Kem { /// DHKEM(P-256, HKDF-SHA256) [NISTCurves](https://doi.org/10.6028/nist.fips.186-4) - #[cfg(feature = "kem-dh-p256-hkdf-sha256")] - DhP256HkdfSha256 = 16, + #[cfg(feature = "kem-nistp")] + DhP256HkdfSha256 = 0x0010, /// DHKEM(P-384, HKDF-SHA384) [NISTCurves](https://doi.org/10.6028/nist.fips.186-4) - #[cfg(feature = "kem-dh-p384-hkdf-sha384")] - DhP384HkdfSha384 = 17, + #[cfg(feature = "kem-nistp")] + DhP384HkdfSha384 = 0x0011, /// DHKEM(P-521, HKDF-SHA512) [NISTCurves](https://doi.org/10.6028/nist.fips.186-4) - #[cfg(feature = "kem-dh-p521-hkdf-sha512")] - DhP521HkdfSha512 = 18, + #[cfg(feature = "kem-nistp")] + DhP521HkdfSha512 = 0x0012, /// DHKEM(X25519, HKDF-SHA256) [RFC7748](https://www.rfc-editor.org/info/rfc7748) - #[cfg(feature = "kem-x25519-hkdf-sha256")] - X25519HkdfSha256 = 32, + #[cfg(feature = "kem-x25519")] + X25519HkdfSha256 = 0x0020, + + /// MLKEM-768 [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final) + #[cfg(feature = "kem-mlkem")] + MlKem768 = 0x0041, + + /// MLKEM-1024 [FIPS 203](https://csrc.nist.gov/pubs/fips/203/final) + #[cfg(feature = "kem-mlkem")] + MlKem1024 = 0x0042, + + /// X-Wing (MLKEM768-X25519) + #[cfg(all(feature = "kem-mlkem", feature = "kem-x25519"))] + XWing = 0x647a, + + /// MLKEM768-P256 + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + MlKem768P256 = 0x0050, + + /// MLKEM1024-P384 + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + MlKem1024P384 = 0x0051, } impl FromStr for Kem { @@ -40,23 +59,33 @@ impl FromStr for Kem { fn from_str(s: &str) -> Result { match &*s.to_lowercase().replace('-', "") { - #[cfg(feature = "kem-dh-p256-hkdf-sha256")] + #[cfg(feature = "kem-nistp")] "p256sha256" | "dhkemp256hkdfsha256" | "p256hkdfsha256" | "dhkem(p256, hkdfsha256)" => { Ok(Self::DhP256HkdfSha256) } - #[cfg(feature = "kem-dh-p384-hkdf-sha384")] + #[cfg(feature = "kem-nistp")] "p384sha384" | "dhkemp384hkdfsha384" | "p384hkdfsha384" | "dhkem(p384, hkdfsha384)" => { Ok(Self::DhP384HkdfSha384) } - #[cfg(feature = "kem-dh-p521-hkdf-sha512")] + #[cfg(feature = "kem-nistp")] "p521sha512" | "dhkemp521hkdfsha512" | "p521hkdfsha512" | "dhkem(p521, hkdfsha512)" => { Ok(Self::DhP521HkdfSha512) } - #[cfg(feature = "kem-x25519-hkdf-sha256")] + #[cfg(feature = "kem-x25519")] "x25519sha256" | "dhkemx25519hkdfsha256" | "x25519hkdfsha256" | "dhkem(x25519, hkdfsha256)" => Ok(Self::X25519HkdfSha256), + #[cfg(feature = "kem-mlkem")] + "mlkem768" => Ok(Self::MlKem768), + #[cfg(feature = "kem-mlkem")] + "mlkem1024" => Ok(Self::MlKem1024), + #[cfg(all(feature = "kem-mlkem", feature = "kem-x25519"))] + "xwing" | "mlkem768x25119" | "mlkem768-x25119" => Ok(Self::XWing), + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + "mlkem768p256" | "mlkem768-p256" => Ok(Self::MlKem768P256), + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + "mlkem1024p384" | "mlkem1024-p384" => Ok(Self::MlKem1024P384), _ => Err(IdLookupError("kem not recognized")), } } @@ -72,12 +101,22 @@ impl Kem { /// An iterable slice of [`Kem`] variants pub const KEM_ALL: &[Kem] = &[ - #[cfg(feature = "kem-dh-p256-hkdf-sha256")] + #[cfg(feature = "kem-nistp")] Kem::DhP256HkdfSha256, - #[cfg(feature = "kem-dh-p384-hkdf-sha384")] + #[cfg(feature = "kem-nistp")] Kem::DhP384HkdfSha384, - #[cfg(feature = "kem-dh-p521-hkdf-sha512")] + #[cfg(feature = "kem-nistp")] Kem::DhP521HkdfSha512, - #[cfg(feature = "kem-x25519-hkdf-sha256")] + #[cfg(feature = "kem-x25519")] Kem::X25519HkdfSha256, + #[cfg(feature = "kem-mlkem")] + Kem::MlKem768, + #[cfg(feature = "kem-mlkem")] + Kem::MlKem1024, + #[cfg(all(feature = "kem-mlkem", feature = "kem-x25519"))] + Kem::XWing, + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + Kem::MlKem768P256, + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + Kem::MlKem1024P384, ]; diff --git a/src/keypair.rs b/src/keypair.rs index 962348b9b1a..83469735635 100644 --- a/src/keypair.rs +++ b/src/keypair.rs @@ -31,22 +31,37 @@ impl Keypair { #[must_use] pub fn gen_keypair(kem: Kem) -> Keypair { match kem { - #[cfg(feature = "kem-dh-p256-hkdf-sha256")] + #[cfg(feature = "kem-nistp")] Kem::DhP256HkdfSha256 => gen_kp::(), - #[cfg(feature = "kem-dh-p384-hkdf-sha384")] + #[cfg(feature = "kem-nistp")] Kem::DhP384HkdfSha384 => gen_kp::(), - #[cfg(feature = "kem-dh-p521-hkdf-sha512")] + #[cfg(feature = "kem-nistp")] Kem::DhP521HkdfSha512 => gen_kp::(), - #[cfg(feature = "kem-x25519-hkdf-sha256")] + #[cfg(feature = "kem-x25519")] Kem::X25519HkdfSha256 => gen_kp::(), + + #[cfg(feature = "kem-mlkem")] + Kem::MlKem768 => gen_kp::(), + + #[cfg(feature = "kem-mlkem")] + Kem::MlKem1024 => gen_kp::(), + + #[cfg(all(feature = "kem-mlkem", feature = "kem-x25519"))] + Kem::XWing => gen_kp::(), + + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + Kem::MlKem768P256 => gen_kp::(), + + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + Kem::MlKem1024P384 => gen_kp::(), } } fn gen_kp() -> Keypair { - let (private_key, public_key) = KemT::gen_keypair(&mut rand::rng()); + let (private_key, public_key) = KemT::gen_keypair(); let public_key = public_key.to_bytes().to_vec(); let private_key = private_key.to_bytes().to_vec(); diff --git a/src/lib.rs b/src/lib.rs index 0d02871490b..7e3fd7f3f50 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,18 +16,9 @@ #![doc = include_str!("../README.md")] #![cfg(all( any(feature = "base-mode-open", feature = "base-mode-seal"), - any( - feature = "aead-aes-gcm-128", - feature = "aead-aes-gcm-256", - feature = "aead-chacha-20-poly-1305", - ), - any(feature = "kdf-sha256", feature = "kdf-sha384", feature = "kdf-sha512"), - any( - feature = "kem-dh-p256-hkdf-sha256", - feature = "kem-dh-p384-hkdf-sha384", - feature = "kem-dh-p521-hkdf-sha512", - feature = "kem-x25519-hkdf-sha256", - ), + any(feature = "aead-aes", feature = "aead-chacha",), + any(feature = "kdf-hkdfsha2", feature = "kdf-shake"), + any(feature = "kem-nistp", feature = "kem-x25519", feature = "kem-mlkem",), ))] use hpke::{Deserializable, HpkeError}; diff --git a/src/macros.rs b/src/macros.rs index 86bfa740324..c599f3379f4 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -5,11 +5,11 @@ macro_rules! match_algo { (@aead, $aead:ident, $kdf:ident, $kem:ident, $fn:ident) => { match $aead { - #[cfg(feature = "aead-aes-gcm-128")] + #[cfg(feature = "aead-aes")] $crate::Aead::AesGcm128 => match_algo!(@kdf, hpke::aead::AesGcm128, $kdf, $kem, $fn), - #[cfg(feature = "aead-aes-gcm-256")] + #[cfg(feature = "aead-aes")] $crate::Aead::AesGcm256 => match_algo!(@kdf, hpke::aead::AesGcm256, $kdf, $kem, $fn), - #[cfg(feature = "aead-chacha-20-poly-1305")] + #[cfg(feature = "aead-chacha")] $crate::Aead::ChaCha20Poly1305 => { match_algo!(@kdf, hpke::aead::ChaCha20Poly1305, $kdf, $kem, $fn) } @@ -18,25 +18,43 @@ macro_rules! match_algo { (@kdf, $aead:ty, $kdf:expr, $kem:expr, $fn:ident) => { match $kdf { - #[cfg(feature = "kdf-sha256")] + #[cfg(feature = "kdf-hkdfsha2")] $crate::Kdf::Sha256 => match_algo!(@kem, $aead, hpke::kdf::HkdfSha256, $kem, $fn), - #[cfg(feature = "kdf-sha384")] + #[cfg(feature = "kdf-hkdfsha2")] $crate::Kdf::Sha384 => match_algo!(@kem, $aead, hpke::kdf::HkdfSha384, $kem, $fn), - #[cfg(feature = "kdf-sha512")] + #[cfg(feature = "kdf-hkdfsha2")] $crate::Kdf::Sha512 => match_algo!(@kem, $aead, hpke::kdf::HkdfSha512, $kem, $fn), + #[cfg(feature = "kdf-shake")] + $crate::Kdf::Shake128 => match_algo!(@kem, $aead, hpke::kdf::KdfShake128, $kem, $fn), + #[cfg(feature = "kdf-shake")] + $crate::Kdf::Shake256 => match_algo!(@kem, $aead, hpke::kdf::KdfShake256, $kem, $fn), + #[cfg(feature = "kdf-shake")] + $crate::Kdf::TurboShake128 => match_algo!(@kem, $aead, hpke::kdf::KdfTurboShake128, $kem, $fn), + #[cfg(feature = "kdf-shake")] + $crate::Kdf::TurboShake256 => match_algo!(@kem, $aead, hpke::kdf::KdfTurboShake256, $kem, $fn), } }; (@kem, $aead:ty, $kdf:ty, $kem:expr, $fn:ident) => { match $kem { - #[cfg(feature = "kem-dh-p256-hkdf-sha256")] + #[cfg(feature = "kem-nistp")] $crate::Kem::DhP256HkdfSha256 => $fn::<$aead, $kdf, hpke::kem::DhP256HkdfSha256>, - #[cfg(feature = "kem-dh-p384-hkdf-sha384")] + #[cfg(feature = "kem-nistp")] $crate::Kem::DhP384HkdfSha384 => $fn::<$aead, $kdf, hpke::kem::DhP384HkdfSha384>, - #[cfg(feature = "kem-dh-p521-hkdf-sha512")] + #[cfg(feature = "kem-nistp")] $crate::Kem::DhP521HkdfSha512 => $fn::<$aead, $kdf, hpke::kem::DhP521HkdfSha512>, - #[cfg(feature = "kem-x25519-hkdf-sha256")] + #[cfg(feature = "kem-x25519")] $crate::Kem::X25519HkdfSha256 => $fn::<$aead, $kdf, hpke::kem::X25519HkdfSha256>, + #[cfg(feature = "kem-mlkem")] + $crate::Kem::MlKem768 => $fn::<$aead, $kdf, hpke::kem::MlKem768>, + #[cfg(feature = "kem-mlkem")] + $crate::Kem::MlKem1024 => $fn::<$aead, $kdf, hpke::kem::MlKem1024>, + #[cfg(all(feature = "kem-mlkem", feature = "kem-x25519"))] + $crate::Kem::XWing => $fn::<$aead, $kdf, hpke::kem::XWing>, + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + $crate::Kem::MlKem768P256 => $fn::<$aead, $kdf, hpke::kem::MlKem768P256>, + #[cfg(all(feature = "kem-mlkem", feature = "kem-nistp"))] + $crate::Kem::MlKem1024P384 => $fn::<$aead, $kdf, hpke::kem::MlKem1024P384>, } }; }