From 50ae4f71593b43f4a5e78b9b31cfcd7c447307be Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 10 Aug 2024 12:56:26 -0400 Subject: [PATCH] DRAFT --- Cargo.toml | 24 +- benches/benches.rs | 23 +- examples/agility.rs | 762 -------------------------------------- examples/client_server.rs | 26 +- src/aead.rs | 116 ------ src/dhkex.rs | 6 - src/dhkex/ecdh_nist.rs | 608 ------------------------------ src/dhkex/x25519.rs | 227 ------------ src/kem.rs | 40 -- src/kem/dhkem.rs | 55 --- src/lib.rs | 10 +- src/setup.rs | 92 ----- src/single_shot.rs | 40 -- 13 files changed, 24 insertions(+), 2005 deletions(-) delete mode 100644 examples/agility.rs delete mode 100644 src/dhkex/ecdh_nist.rs delete mode 100644 src/dhkex/x25519.rs diff --git a/Cargo.toml b/Cargo.toml index 848f430..0cce5c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,15 +12,7 @@ keywords = ["cryptography", "crypto", "key-exchange", "encryption", "aead", "sec categories = ["cryptography", "no-std"] [features] -# "p256" enables the use of ECDH-NIST-P256 as a KEM -# "p384" enables the use of ECDH-NIST-P384 as a KEM -# "x25519" enables the use of the X25519 as a KEM -default = ["alloc", "p256", "x25519"] -x25519 = ["dep:x25519-dalek"] -p384 = ["dep:p384"] -p256 = ["dep:p256"] -p521 = ["dep:p521"] -k256 = ["dep:k256"] +default = ["alloc", "secp"] secp = ["bitcoin", "secp256k1/global-context", "secp256k1/rand-std"] # Include allocating methods like open() and seal() alloc = [] @@ -32,20 +24,14 @@ aead = "0.5" aes-gcm = "0.10" bitcoin = { version = "0.32.0", optional = true } secp256k1 = { version = "0.29", optional = true } -# bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin", commit = "0d1cab68eee59f79c3ec76cf393438471b68fe69", optional = true } chacha20poly1305 = "0.10" generic-array = { version = "0.14", default-features = false } digest = "0.10" hkdf = "0.12" hmac = "0.12" rand_core = { version = "0.6", default-features = false } -k256 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdh"], optional = true} -p256 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdh"], optional = true} -p384 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdh"], optional = true} -p521 = { version = "0.13", default-features = false, features = ["arithmetic", "ecdh"], optional = true} sha2 = { version = "0.10", default-features = false } subtle = { version = "2.6", default-features = false } -x25519-dalek = { version = "2", default-features = false, features = ["static_secrets"], optional = true } zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] } [dev-dependencies] @@ -56,14 +42,6 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" rand = { version = "0.8", default-features = false, features = ["getrandom", "std_rng"] } -[[example]] -name = "client_server" -required-features = ["x25519"] - -[[example]] -name = "agility" -required-features = ["p256", "p384", "p521", "x25519"] - # Tell docs.rs to build docs with `--all-features` and `--cfg docsrs` (for nightly docs features) [package.metadata.docs.rs] all-features = true diff --git a/benches/benches.rs b/benches/benches.rs index fe1cf49..3d82ce8 100644 --- a/benches/benches.rs +++ b/benches/benches.rs @@ -210,25 +210,12 @@ where pub fn benches() { let mut c = Criterion::default().configure_from_args(); - // NIST ciphersuite at the 128-bit security level is AES-GCM-128, HKDF-SHA256, and ECDH-P256 - #[cfg(feature = "p256")] - bench_ciphersuite::( - "NIST[seclevel=128]", - &mut c, - ); - - // Non-NIST ciphersuite at the 128-bit security level is ChaCha20Poly1305, HKDF-SHA256, and X25519 - #[cfg(feature = "x25519")] - bench_ciphersuite::< - hpke::aead::ChaCha20Poly1305, - hpke::kdf::HkdfSha256, - hpke::kem::X25519HkdfSha256, - >("Non-NIST[seclevel=128]", &mut c); - #[cfg(feature = "secp")] - bench_ciphersuite::( - "secp", &mut c, - ); + bench_ciphersuite::< + bitcoin_hpke::aead::AesGcm128, + bitcoin_hpke::kdf::HkdfSha256, + bitcoin_hpke::kem::SecpK256HkdfSha256, + >("secp", &mut c); } criterion_main!(benches); diff --git a/examples/agility.rs b/examples/agility.rs deleted file mode 100644 index 0776c87..0000000 --- a/examples/agility.rs +++ /dev/null @@ -1,762 +0,0 @@ -#![allow(dead_code)] -//! Here's the gist of this file: Instead of doing things at the type level, you can use zero-sized -//! types and runtime validity checks to do all of HPKE. This file is a rough idea of how one would -//! go about implementing that. There isn't too much repetition. The main part where you have to -//! get clever is in `agile_setup_*`, where you have to have a match statement with up to 3·3·5 = -//! 45 branches for all the different AEAD-KEM-KDF combinations. Practically speaking, though, -//! that's not a big number, so writing that out and using a macro for the actual work (e.g., -//! `do_setup_sender!`) seems to be the way to go. -//! -//! The other point of this file is to demonstrate how messy crypto agility makes things. Many -//! people have different needs when it comes to agility, so I implore you **DO NOT COPY THIS FILE -//! BLINDLY**. Think about what you actually need, make that instead, and make sure to write lots -//! of runtime checks. - -use hpke::{ - aead::{Aead, AeadCtxR, AeadCtxS, AeadTag, AesGcm128, AesGcm256, ChaCha20Poly1305}, - kdf::{HkdfSha256, HkdfSha384, HkdfSha512, Kdf as KdfTrait}, - kem::{ - DhP256HkdfSha256, DhP384HkdfSha384, DhP521HkdfSha512, Kem as KemTrait, X25519HkdfSha256, - }, - setup_receiver, setup_sender, Deserializable, HpkeError, OpModeR, OpModeS, PskBundle, - Serializable, -}; - -use rand::{rngs::StdRng, CryptoRng, RngCore, SeedableRng}; - -trait AgileAeadCtxS { - fn seal_in_place_detached( - &mut self, - plaintext: &mut [u8], - aad: &[u8], - ) -> Result; - fn seal(&mut self, plaintext: &[u8], aad: &[u8]) -> Result, AgileHpkeError>; -} - -trait AgileAeadCtxR { - fn open_in_place_detached( - &mut self, - ciphertext: &mut [u8], - aad: &[u8], - tag_bytes: &[u8], - ) -> Result<(), AgileHpkeError>; - fn open(&mut self, ciphertext: &[u8], aad: &[u8]) -> Result, AgileHpkeError>; -} - -type AgileAeadTag = Vec; - -#[derive(Debug)] -enum AgileHpkeError { - /// When you don't give an algorithm an array of the length it wants. Error is of the form - /// `((alg1, alg1_location) , (alg2, alg2_location))`. - AlgMismatch((&'static str, &'static str), (&'static str, &'static str)), - /// When you get an algorithm identifier you don't recognize. Error is of the form - /// `(alg, given_id)`. - UnknownAlgIdent(&'static str, u16), - /// Represents an error in the `hpke` crate - HpkeError(HpkeError), -} - -// This just wraps the HpkeError -impl From for AgileHpkeError { - fn from(e: HpkeError) -> AgileHpkeError { - AgileHpkeError::HpkeError(e) - } -} - -impl AgileAeadCtxS for AeadCtxS { - fn seal_in_place_detached( - &mut self, - plaintext: &mut [u8], - aad: &[u8], - ) -> Result, AgileHpkeError> { - self.seal_in_place_detached(plaintext, aad) - .map(|tag| tag.to_bytes().to_vec()) - .map_err(Into::into) - } - fn seal(&mut self, plaintext: &[u8], aad: &[u8]) -> Result, AgileHpkeError> { - self.seal(plaintext, aad).map_err(Into::into) - } -} - -impl AgileAeadCtxR for AeadCtxR { - fn open_in_place_detached( - &mut self, - ciphertext: &mut [u8], - aad: &[u8], - tag_bytes: &[u8], - ) -> Result<(), AgileHpkeError> { - let tag = AeadTag::::from_bytes(tag_bytes)?; - self.open_in_place_detached(ciphertext, aad, &tag) - .map_err(Into::into) - } - fn open(&mut self, ciphertext: &[u8], aad: &[u8]) -> Result, AgileHpkeError> { - self.open(ciphertext, aad).map_err(Into::into) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum AeadAlg { - AesGcm128, - AesGcm256, - ChaCha20Poly1305, -} - -impl AeadAlg { - fn name(&self) -> &'static str { - match self { - AeadAlg::AesGcm128 => "AesGcm128", - AeadAlg::AesGcm256 => "AesGcm256", - AeadAlg::ChaCha20Poly1305 => "ChaCha20Poly1305", - } - } - - fn try_from_u16(id: u16) -> Result { - let res = match id { - 0x01 => AeadAlg::AesGcm128, - 0x02 => AeadAlg::AesGcm256, - 0x03 => AeadAlg::ChaCha20Poly1305, - _ => return Err(AgileHpkeError::UnknownAlgIdent("AeadAlg", id)), - }; - - Ok(res) - } - - fn to_u16(self) -> u16 { - match self { - AeadAlg::AesGcm128 => 0x01, - AeadAlg::AesGcm256 => 0x02, - AeadAlg::ChaCha20Poly1305 => 0x03, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum KdfAlg { - HkdfSha256, - HkdfSha384, - HkdfSha512, -} - -impl KdfAlg { - fn name(&self) -> &'static str { - match self { - KdfAlg::HkdfSha256 => "HkdfSha256", - KdfAlg::HkdfSha384 => "HkdfSha384", - KdfAlg::HkdfSha512 => "HkdfSha512", - } - } - - fn try_from_u16(id: u16) -> Result { - let res = match id { - 0x01 => KdfAlg::HkdfSha256, - 0x02 => KdfAlg::HkdfSha384, - 0x03 => KdfAlg::HkdfSha512, - _ => return Err(AgileHpkeError::UnknownAlgIdent("KdfAlg", id)), - }; - - Ok(res) - } - - fn to_u16(self) -> u16 { - match self { - KdfAlg::HkdfSha256 => 0x01, - KdfAlg::HkdfSha384 => 0x02, - KdfAlg::HkdfSha512 => 0x03, - } - } - - fn get_digest_len(&self) -> usize { - match self { - KdfAlg::HkdfSha256 => 32, - KdfAlg::HkdfSha384 => 48, - KdfAlg::HkdfSha512 => 64, - } - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum KemAlg { - X25519HkdfSha256, - X448HkdfSha512, - DhP256HkdfSha256, - DhP384HkdfSha384, - DhP521HkdfSha512, -} - -impl KemAlg { - fn name(&self) -> &'static str { - match self { - KemAlg::DhP256HkdfSha256 => "DhP256HkdfSha256", - KemAlg::DhP384HkdfSha384 => "DhP384HkdfSha384", - KemAlg::DhP521HkdfSha512 => "DhP521HkdfSha512", - KemAlg::X25519HkdfSha256 => "X25519HkdfSha256", - KemAlg::X448HkdfSha512 => "X448HkdfSha512", - } - } - - fn try_from_u16(id: u16) -> Result { - let res = match id { - 0x10 => KemAlg::DhP256HkdfSha256, - 0x11 => KemAlg::DhP384HkdfSha384, - 0x12 => KemAlg::DhP521HkdfSha512, - 0x20 => KemAlg::X25519HkdfSha256, - 0x21 => KemAlg::X448HkdfSha512, - _ => return Err(AgileHpkeError::UnknownAlgIdent("KemAlg", id)), - }; - - Ok(res) - } - - fn to_u16(self) -> u16 { - match self { - KemAlg::DhP256HkdfSha256 => 0x10, - KemAlg::DhP384HkdfSha384 => 0x11, - KemAlg::DhP521HkdfSha512 => 0x12, - KemAlg::X25519HkdfSha256 => 0x20, - KemAlg::X448HkdfSha512 => 0x21, - } - } - - fn kdf_alg(&self) -> KdfAlg { - match self { - KemAlg::X25519HkdfSha256 => KdfAlg::HkdfSha256, - KemAlg::X448HkdfSha512 => KdfAlg::HkdfSha512, - KemAlg::DhP256HkdfSha256 => KdfAlg::HkdfSha256, - KemAlg::DhP384HkdfSha384 => KdfAlg::HkdfSha384, - KemAlg::DhP521HkdfSha512 => KdfAlg::HkdfSha512, - } - } -} - -#[derive(Clone)] -struct AgilePublicKey { - kem_alg: KemAlg, - pubkey_bytes: Vec, -} - -impl AgilePublicKey { - fn try_lift(&self) -> Result { - Kem::PublicKey::from_bytes(&self.pubkey_bytes).map_err(|e| e.into()) - } -} - -#[derive(Clone)] -struct AgileEncappedKey { - kem_alg: KemAlg, - encapped_key_bytes: Vec, -} - -impl AgileEncappedKey { - fn try_lift(&self) -> Result { - Kem::EncappedKey::from_bytes(&self.encapped_key_bytes).map_err(|e| e.into()) - } -} - -#[derive(Clone)] -struct AgilePrivateKey { - kem_alg: KemAlg, - privkey_bytes: Vec, -} - -impl AgilePrivateKey { - fn try_lift(&self) -> Result { - Kem::PrivateKey::from_bytes(&self.privkey_bytes).map_err(|e| e.into()) - } -} - -#[derive(Clone)] -struct AgileKeypair(AgilePrivateKey, AgilePublicKey); - -impl AgileKeypair { - fn try_lift(&self) -> Result<(Kem::PrivateKey, Kem::PublicKey), AgileHpkeError> { - Ok((self.0.try_lift::()?, self.1.try_lift::()?)) - } - - fn validate(&self) -> Result<(), AgileHpkeError> { - if self.0.kem_alg != self.1.kem_alg { - Err(AgileHpkeError::AlgMismatch( - (self.0.kem_alg.name(), "AgileKeypair::privkey"), - (self.1.kem_alg.name(), "AgileKeypair::pubkey"), - )) - } else { - Ok(()) - } - } -} - -// The leg work of agile_gen_keypair -macro_rules! do_gen_keypair { - ($kem_ty:ty, $kem_alg:ident, $csprng:ident) => {{ - type Kem = $kem_ty; - let kem_alg = $kem_alg; - let csprng = $csprng; - - let (sk, pk) = Kem::gen_keypair(csprng); - let sk = AgilePrivateKey { - kem_alg, - privkey_bytes: sk.to_bytes().to_vec(), - }; - let pk = AgilePublicKey { - kem_alg, - pubkey_bytes: pk.to_bytes().to_vec(), - }; - - AgileKeypair(sk, pk) - }}; -} - -fn agile_gen_keypair(kem_alg: KemAlg, csprng: &mut R) -> AgileKeypair { - match kem_alg { - KemAlg::X25519HkdfSha256 => do_gen_keypair!(X25519HkdfSha256, kem_alg, csprng), - KemAlg::DhP256HkdfSha256 => do_gen_keypair!(DhP256HkdfSha256, kem_alg, csprng), - KemAlg::DhP384HkdfSha384 => do_gen_keypair!(DhP384HkdfSha384, kem_alg, csprng), - KemAlg::DhP521HkdfSha512 => do_gen_keypair!(DhP521HkdfSha512, kem_alg, csprng), - _ => unimplemented!(), - } -} - -#[derive(Clone)] -struct AgileOpModeR<'a> { - kem_alg: KemAlg, - op_mode_ty: AgileOpModeRTy<'a>, -} - -impl<'a> AgileOpModeR<'a> { - fn try_lift(self) -> Result, AgileHpkeError> { - let res = match self.op_mode_ty { - AgileOpModeRTy::Base => OpModeR::Base, - AgileOpModeRTy::Psk(bundle) => OpModeR::Psk(bundle.try_lift::()?), - AgileOpModeRTy::Auth(pk) => OpModeR::Auth(pk.try_lift::()?), - AgileOpModeRTy::AuthPsk(pk, bundle) => { - OpModeR::AuthPsk(pk.try_lift::()?, bundle.try_lift::()?) - } - }; - - Ok(res) - } - - fn validate(&self) -> Result<(), AgileHpkeError> { - match &self.op_mode_ty { - AgileOpModeRTy::Auth(pk) => { - if pk.kem_alg != self.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (self.kem_alg.name(), "AgileOpModeR::kem_alg"), - ( - pk.kem_alg.name(), - "AgileOpModeR::op_mode_ty::AgilePublicKey::kem_alg", - ), - )); - } - } - AgileOpModeRTy::AuthPsk(pk, _) => { - if pk.kem_alg != self.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (self.kem_alg.name(), "AgileOpModeR::kem_alg"), - ( - pk.kem_alg.name(), - "AgileOpModeR::op_mode_ty::AgilePublicKey::kem_alg", - ), - )); - } - } - _ => (), - } - - Ok(()) - } -} - -#[derive(Clone)] -enum AgileOpModeRTy<'a> { - Base, - Psk(AgilePskBundle<'a>), - Auth(AgilePublicKey), - AuthPsk(AgilePublicKey, AgilePskBundle<'a>), -} - -#[derive(Clone)] -struct AgileOpModeS<'a> { - kem_alg: KemAlg, - op_mode_ty: AgileOpModeSTy<'a>, -} - -impl<'a> AgileOpModeS<'a> { - fn try_lift(self) -> Result, AgileHpkeError> { - let res = match self.op_mode_ty { - AgileOpModeSTy::Base => OpModeS::Base, - AgileOpModeSTy::Psk(bundle) => OpModeS::Psk(bundle.try_lift::()?), - AgileOpModeSTy::Auth(keypair) => OpModeS::Auth(keypair.try_lift::()?), - AgileOpModeSTy::AuthPsk(keypair, bundle) => { - OpModeS::AuthPsk(keypair.try_lift::()?, bundle.try_lift::()?) - } - }; - - Ok(res) - } - - fn validate(&self) -> Result<(), AgileHpkeError> { - match &self.op_mode_ty { - AgileOpModeSTy::Auth(keypair) => { - keypair.validate()?; - if keypair.0.kem_alg != self.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (self.kem_alg.name(), "AgileOpModeS::kem_alg"), - ( - keypair.0.kem_alg.name(), - "AgileOpModeS::op_mode_ty::AgilePrivateKey::kem_alg", - ), - )); - } - } - AgileOpModeSTy::AuthPsk(keypair, _) => { - keypair.validate()?; - if keypair.0.kem_alg != self.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (self.kem_alg.name(), "AgileOpModeS::kem_alg"), - ( - keypair.0.kem_alg.name(), - "AgileOpModeS::op_mode_ty::AgilePrivateKey::kem_alg", - ), - )); - } - } - _ => (), - } - - Ok(()) - } -} - -#[derive(Clone)] -enum AgileOpModeSTy<'a> { - Base, - Psk(AgilePskBundle<'a>), - Auth(AgileKeypair), - AuthPsk(AgileKeypair, AgilePskBundle<'a>), -} - -#[derive(Clone, Copy)] -struct AgilePskBundle<'a>(PskBundle<'a>); - -impl<'a> AgilePskBundle<'a> { - fn try_lift(self) -> Result, AgileHpkeError> { - Ok(self.0) - } -} - -// This macro takes in all the supported AEADs, KDFs, and KEMs, and dispatches the given test -// vector to the test case with the appropriate types -macro_rules! hpke_dispatch { - // Step 1: Roll up the AEAD, KDF, and KEM types into tuples. We'll unroll them later - ($to_set:ident, $to_match:ident, - ($( $aead_ty:ident ),*), ($( $kdf_ty:ident ),*), ($( $kem_ty:ident ),*), $rng_ty:ident, - $callback:ident, $( $callback_args:ident ),* ) => { - hpke_dispatch!(@tup1 - $to_set, $to_match, - ($( $aead_ty ),*), ($( $kdf_ty ),*), ($( $kem_ty ),*), $rng_ty, - $callback, ($( $callback_args ),*) - ) - }; - - // Step 2: Expand with respect to every AEAD - (@tup1 - $to_set:ident, $to_match:ident, - ($( $aead_ty:ident ),*), $kdf_tup:tt, $kem_tup:tt, $rng_ty:tt, - $callback:ident, $callback_args:tt) => { - $( - hpke_dispatch!(@tup2 - $to_set, $to_match, - $aead_ty, $kdf_tup, $kem_tup, $rng_ty, - $callback, $callback_args - ); - )* - }; - - // Step 3: Expand with respect to every KDF - (@tup2 - $to_set:ident, $to_match:ident, - $aead_ty:ident, ($( $kdf_ty:ident ),*), $kem_tup:tt, $rng_ty:tt, - $callback:ident, $callback_args:tt) => { - $( - hpke_dispatch!(@tup3 - $to_set, $to_match, - $aead_ty, $kdf_ty, $kem_tup, $rng_ty, - $callback, $callback_args - ); - )* - }; - - // Step 4: Expand with respect to every KEM - (@tup3 - $to_set:ident, $to_match:ident, - $aead_ty:ident, $kdf_ty:ident, ($( $kem_ty:ident ),*), $rng_ty:tt, - $callback:ident, $callback_args:tt) => { - $( - hpke_dispatch!(@base - $to_set, $to_match, - $aead_ty, $kdf_ty, $kem_ty, $rng_ty, - $callback, $callback_args - ); - )* - }; - - // Step 5: Now that we're only dealing with 1 type of each kind, do the dispatch. If the test - // vector matches the IDs of these types, run the test case. - (@base - $to_set:ident, $to_match:ident, - $aead_ty:ident, $kdf_ty:ident, $kem_ty:ident, $rng_ty:ident, - $callback:ident, ($( $callback_args:ident ),*)) => { - if let (AeadAlg::$aead_ty, KemAlg::$kem_ty, KdfAlg::$kdf_ty) = $to_match - { - $to_set = Some($callback::<$aead_ty, $kdf_ty, $kem_ty, $rng_ty>($( $callback_args ),*)); - } - }; -} - -// The leg work of agile_setup_receiver -fn do_setup_sender( - mode: &AgileOpModeS, - pk_recip: &AgilePublicKey, - info: &[u8], - csprng: &mut R, -) -> Result<(AgileEncappedKey, Box), AgileHpkeError> -where - A: 'static + Aead, - Kdf: 'static + KdfTrait, - Kem: 'static + KemTrait, - R: CryptoRng + RngCore, -{ - let kem_alg = mode.kem_alg; - let mode = mode.clone().try_lift::()?; - let pk_recip = pk_recip.try_lift::()?; - - let (encapped_key, aead_ctx) = setup_sender::(&mode, &pk_recip, info, csprng)?; - let encapped_key = AgileEncappedKey { - kem_alg, - encapped_key_bytes: encapped_key.to_bytes().to_vec(), - }; - - Ok((encapped_key, Box::new(aead_ctx))) -} - -fn agile_setup_sender( - aead_alg: AeadAlg, - kdf_alg: KdfAlg, - kem_alg: KemAlg, - mode: &AgileOpModeS, - pk_recip: &AgilePublicKey, - info: &[u8], - csprng: &mut R, -) -> Result<(AgileEncappedKey, Box), AgileHpkeError> { - // Do all the necessary validation - mode.validate()?; - if mode.kem_alg != pk_recip.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (mode.kem_alg.name(), "mode::kem_alg"), - (pk_recip.kem_alg.name(), "pk_recip::kem_alg"), - )); - } - if kem_alg != mode.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (kem_alg.name(), "kem_alg::kem_alg"), - (mode.kem_alg.name(), "mode::kem_alg"), - )); - } - - // The triple we dispatch on - let to_match = (aead_alg, kem_alg, kdf_alg); - - // This gets overwritten by the below macro call. It's None iff dispatch failed. - let mut res: Option), AgileHpkeError>> = None; - - #[rustfmt::skip] - hpke_dispatch!( - res, to_match, - (ChaCha20Poly1305, AesGcm128, AesGcm256), - (HkdfSha256, HkdfSha384, HkdfSha512), - (X25519HkdfSha256, DhP256HkdfSha256, DhP384HkdfSha384, DhP521HkdfSha512), - R, - do_setup_sender, - mode, - pk_recip, - info, - csprng - ); - - if res.is_none() { - panic!("DHKEM({}) isn't impelmented yet!", kem_alg.name()); - } - - res.unwrap() -} - -// The leg work of agile_setup_receiver. The Dummy type parameter is so that it can be used with -// the hpke_dispatch! macro. The macro expects its callback function to have 4 type parameters -fn do_setup_receiver( - mode: &AgileOpModeR, - recip_keypair: &AgileKeypair, - encapped_key: &AgileEncappedKey, - info: &[u8], -) -> Result, AgileHpkeError> -where - A: 'static + Aead, - Kdf: 'static + KdfTrait, - Kem: 'static + KemTrait, -{ - let mode = mode.clone().try_lift::()?; - let (sk_recip, _) = recip_keypair.try_lift::()?; - let encapped_key = encapped_key.try_lift::()?; - - let aead_ctx = setup_receiver::(&mode, &sk_recip, &encapped_key, info)?; - Ok(Box::new(aead_ctx)) -} - -fn agile_setup_receiver( - aead_alg: AeadAlg, - kdf_alg: KdfAlg, - kem_alg: KemAlg, - mode: &AgileOpModeR, - recip_keypair: &AgileKeypair, - encapped_key: &AgileEncappedKey, - info: &[u8], -) -> Result, AgileHpkeError> { - // Do all the necessary validation - recip_keypair.validate()?; - mode.validate()?; - if mode.kem_alg != recip_keypair.0.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (mode.kem_alg.name(), "mode::kem_alg"), - (recip_keypair.0.kem_alg.name(), "recip_keypair::kem_alg"), - )); - } - if kem_alg != mode.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (kem_alg.name(), "kem_alg::kem_alg"), - (mode.kem_alg.name(), "mode::kem_alg"), - )); - } - if recip_keypair.0.kem_alg != encapped_key.kem_alg { - return Err(AgileHpkeError::AlgMismatch( - (recip_keypair.0.kem_alg.name(), "recip_keypair::kem_alg"), - (encapped_key.kem_alg.name(), "encapped_key::kem_alg"), - )); - } - - // The triple we dispatch on - let to_match = (aead_alg, kem_alg, kdf_alg); - - // This gets overwritten by the below macro call. It's None iff dispatch failed. - let mut res: Option, AgileHpkeError>> = None; - - // Dummy type to give to the macro. do_setup_receiver doesn't use an RNG, so it doesn't need a - // concrete RNG type. We give it the unit type to make it happy. - type Unit = (); - - #[rustfmt::skip] - hpke_dispatch!( - res, to_match, - (ChaCha20Poly1305, AesGcm128, AesGcm256), - (HkdfSha256, HkdfSha384, HkdfSha512), - (X25519HkdfSha256, DhP256HkdfSha256, DhP384HkdfSha384, DhP521HkdfSha512), - Unit, - do_setup_receiver, - mode, - recip_keypair, - encapped_key, - info - ); - - if res.is_none() { - panic!("DHKEM({}) isn't impelmented yet!", kem_alg.name()); - } - - res.unwrap() -} - -fn main() { - let mut csprng = StdRng::from_entropy(); - - let supported_aead_algs = &[ - AeadAlg::AesGcm128, - AeadAlg::AesGcm256, - AeadAlg::ChaCha20Poly1305, - ]; - let supported_kem_algs = &[ - KemAlg::X25519HkdfSha256, - KemAlg::DhP256HkdfSha256, - KemAlg::DhP384HkdfSha384, - KemAlg::DhP521HkdfSha512, - ]; - let supported_kdf_algs = &[KdfAlg::HkdfSha256, KdfAlg::HkdfSha384, KdfAlg::HkdfSha512]; - - // For every combination of supported algorithms, test an encryption-decryption round trip - for &aead_alg in supported_aead_algs { - for &kem_alg in supported_kem_algs { - for &kdf_alg in supported_kdf_algs { - let info = b"we're gonna agile him in his clavicle"; - - // Make a random sender keypair and PSK bundle - let sender_keypair = agile_gen_keypair(kem_alg, &mut csprng); - let mut psk_bytes = vec![0u8; kdf_alg.get_digest_len()]; - let psk_id = b"preshared key attempt #5, take 2. action"; - let psk_bundle = { - csprng.fill_bytes(&mut psk_bytes); - AgilePskBundle(PskBundle { - psk: &psk_bytes, - psk_id, - }) - }; - - // Make two agreeing OpModes (AuthPsk is the most complicated, so we're just using - // that). - let op_mode_s_ty = AgileOpModeSTy::AuthPsk(sender_keypair.clone(), psk_bundle); - let op_mode_s = AgileOpModeS { - kem_alg, - op_mode_ty: op_mode_s_ty, - }; - let op_mode_r_ty = AgileOpModeRTy::AuthPsk(sender_keypair.1, psk_bundle); - let op_mode_r = AgileOpModeR { - kem_alg, - op_mode_ty: op_mode_r_ty, - }; - - // Set up the sender's encryption context - let recip_keypair = agile_gen_keypair(kem_alg, &mut csprng); - let (encapped_key, mut aead_ctx1) = agile_setup_sender( - aead_alg, - kdf_alg, - kem_alg, - &op_mode_s, - &recip_keypair.1, - &info[..], - &mut csprng, - ) - .unwrap(); - - // Set up the receivers's encryption context - let mut aead_ctx2 = agile_setup_receiver( - aead_alg, - kdf_alg, - kem_alg, - &op_mode_r, - &recip_keypair, - &encapped_key, - &info[..], - ) - .unwrap(); - - // Test an encryption-decryption round trip - let msg = b"paper boy paper boy"; - let aad = b"all about that paper, boy"; - let ciphertext = aead_ctx1.seal(msg, aad).unwrap(); - let roundtrip_plaintext = aead_ctx2.open(&ciphertext, aad).unwrap(); - - // Assert that the derived plaintext equals the original message - assert_eq!(&roundtrip_plaintext, msg); - } - } - } - - println!("PEAK AGILITY ACHIEVED"); -} diff --git a/examples/client_server.rs b/examples/client_server.rs index 1190a25..147c4fd 100644 --- a/examples/client_server.rs +++ b/examples/client_server.rs @@ -15,10 +15,10 @@ // * Proper error handling. Everything here just panics when an error is encountered. It is up to // the user of this library to do the appropriate thing when a function returns an error. -use hpke::{ +use bitcoin_hpke::{ aead::{AeadTag, ChaCha20Poly1305}, kdf::HkdfSha384, - kem::X25519HkdfSha256, + kem::SecpK256HkdfSha256, Deserializable, Kem as KemTrait, OpModeR, OpModeS, Serializable, }; @@ -27,7 +27,7 @@ use rand::{rngs::StdRng, SeedableRng}; const INFO_STR: &[u8] = b"example session"; // These are the only algorithms we're gonna use for this example -type Kem = X25519HkdfSha256; +type Kem = SecpK256HkdfSha256; type Aead = ChaCha20Poly1305; type Kdf = HkdfSha384; @@ -48,9 +48,13 @@ fn client_encrypt_msg( // Encapsulate a key and use the resulting shared secret to encrypt a message. The AEAD context // is what you use to encrypt. - let (encapped_key, mut sender_ctx) = - hpke::setup_sender::(&OpModeS::Base, server_pk, INFO_STR, &mut csprng) - .expect("invalid server pubkey!"); + let (encapped_key, mut sender_ctx) = bitcoin_hpke::setup_sender::( + &OpModeS::Base, + server_pk, + INFO_STR, + &mut csprng, + ) + .expect("invalid server pubkey!"); // On success, seal_in_place_detached() will encrypt the plaintext in place let mut msg_copy = msg.to_vec(); @@ -81,9 +85,13 @@ fn server_decrypt_msg( .expect("could not deserialize the encapsulated pubkey!"); // Decapsulate and derive the shared secret. This creates a shared AEAD context. - let mut receiver_ctx = - hpke::setup_receiver::(&OpModeR::Base, &server_sk, &encapped_key, INFO_STR) - .expect("failed to set up receiver!"); + let mut receiver_ctx = bitcoin_hpke::setup_receiver::( + &OpModeR::Base, + &server_sk, + &encapped_key, + INFO_STR, + ) + .expect("failed to set up receiver!"); // On success, open_in_place_detached() will decrypt the ciphertext in place let mut ciphertext_copy = ciphertext.to_vec(); diff --git a/src/aead.rs b/src/aead.rs index c4c309a..99a762b 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -677,122 +677,6 @@ mod test { test_invalid_nonce!(test_invalid_nonce_aes256, AesGcm128); test_invalid_nonce!(test_invalid_nonce_chacha, ChaCha20Poly1305); - #[cfg(all(feature = "x25519", any(feature = "alloc", feature = "std")))] - mod x25519_tests { - use super::*; - - test_export_idempotence!(test_export_idempotence_x25519, crate::kem::X25519HkdfSha256); - test_exportonly_panics!( - test_exportonly_panics_x25519_seal, - test_exportonly_panics_x25519_open, - crate::kem::X25519HkdfSha256 - ); - test_overflow!(test_overflow_x25519, crate::kem::X25519HkdfSha256); - - test_ctx_correctness!( - test_ctx_correctness_aes128_x25519, - AesGcm128, - crate::kem::X25519HkdfSha256 - ); - test_ctx_correctness!( - test_ctx_correctness_aes256_x25519, - AesGcm256, - crate::kem::X25519HkdfSha256 - ); - test_ctx_correctness!( - test_ctx_correctness_chacha_x25519, - ChaCha20Poly1305, - crate::kem::X25519HkdfSha256 - ); - } - - #[cfg(all(feature = "p256", any(feature = "alloc", feature = "std")))] - mod p256_tests { - use super::*; - - test_export_idempotence!(test_export_idempotence_p256, crate::kem::DhP256HkdfSha256); - test_exportonly_panics!( - test_exportonly_panics_p256_seal, - test_exportonly_panics_p256_open, - crate::kem::DhP256HkdfSha256 - ); - test_overflow!(test_overflow_p256, crate::kem::DhP256HkdfSha256); - - test_ctx_correctness!( - test_ctx_correctness_aes128_p256, - AesGcm128, - crate::kem::DhP256HkdfSha256 - ); - test_ctx_correctness!( - test_ctx_correctness_aes256_p256, - AesGcm256, - crate::kem::DhP256HkdfSha256 - ); - test_ctx_correctness!( - test_ctx_correctness_chacha_p256, - ChaCha20Poly1305, - crate::kem::DhP256HkdfSha256 - ); - } - - #[cfg(all(feature = "p384", any(feature = "alloc", feature = "std")))] - mod p384_tests { - use super::*; - - test_export_idempotence!(test_export_idempotence_p384, crate::kem::DhP384HkdfSha384); - test_exportonly_panics!( - test_exportonly_panics_p384_seal, - test_exportonly_panics_p384_open, - crate::kem::DhP384HkdfSha384 - ); - test_overflow!(test_overflow_p384, crate::kem::DhP384HkdfSha384); - - test_ctx_correctness!( - test_ctx_correctness_aes128_p384, - AesGcm128, - crate::kem::DhP384HkdfSha384 - ); - test_ctx_correctness!( - test_ctx_correctness_aes256_p384, - AesGcm256, - crate::kem::DhP384HkdfSha384 - ); - test_ctx_correctness!( - test_ctx_correctness_chacha_p384, - ChaCha20Poly1305, - crate::kem::DhP384HkdfSha384 - ); - } - - #[cfg(all(feature = "k256", any(feature = "alloc", feature = "std")))] - mod k256_tests { - use super::*; - - test_export_idempotence!(test_export_idempotence_k256, crate::kem::DhP256HkdfSha256); - test_exportonly_panics!( - test_exportonly_panics_k256_seal, - test_exportonly_panics_k256_open, - crate::kem::DhK256HkdfSha256 - ); - test_overflow!(test_overflow_k256, crate::kem::DhK256HkdfSha256); - - test_ctx_correctness!( - test_ctx_correctness_aes128_k256, - AesGcm128, - crate::kem::DhK256HkdfSha256 - ); - test_ctx_correctness!( - test_ctx_correctness_aes256_k256, - AesGcm256, - crate::kem::DhK256HkdfSha256 - ); - test_ctx_correctness!( - test_ctx_correctness_chacha_k256, - ChaCha20Poly1305, - crate::kem::DhK256HkdfSha256 - ); - } - #[cfg(all(feature = "secp", any(feature = "alloc", feature = "std")))] mod secp_tests { use super::*; diff --git a/src/dhkex.rs b/src/dhkex.rs index 6e8220c..cbda1cc 100644 --- a/src/dhkex.rs +++ b/src/dhkex.rs @@ -44,11 +44,5 @@ pub trait DhKeyExchange { ) -> (Self::PrivateKey, Self::PublicKey); } -#[cfg(any(feature = "p256", feature = "p384", feature = "p521", feature = "k256"))] -pub(crate) mod ecdh_nist; - -#[cfg(feature = "x25519")] -pub(crate) mod x25519; - #[cfg(feature = "secp")] pub(crate) mod secp256k1; diff --git a/src/dhkex/ecdh_nist.rs b/src/dhkex/ecdh_nist.rs deleted file mode 100644 index ff1fb60..0000000 --- a/src/dhkex/ecdh_nist.rs +++ /dev/null @@ -1,608 +0,0 @@ -// We define all the NIST P- curve ECDH functionalities in one macro -macro_rules! nist_dhkex { - ( - $curve_name:expr, - $dh_name:ident, - $curve:ident, - $pubkey_size:ty, - $privkey_size:ty, - $ss_size:ty, - $keygen_bitmask:expr - ) => { - pub(crate) mod $curve { - use super::*; - - use crate::{ - dhkex::{DhError, DhKeyExchange}, - kdf::{labeled_extract, Kdf as KdfTrait, LabeledExpand}, - util::{enforce_equal_len, enforce_outbuf_len, KemSuiteId}, - Deserializable, HpkeError, Serializable, - }; - - use ::$curve as curve_crate; - use curve_crate::elliptic_curve::{ecdh::diffie_hellman, sec1::ToEncodedPoint}; - use generic_array::{typenum::Unsigned, GenericArray}; - use subtle::{Choice, ConstantTimeEq}; - - #[doc = concat!( - "An ECDH ", - $curve_name, - " public key. This is never the point at infinity." - )] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct PublicKey(curve_crate::PublicKey); - - // This is only ever constructed via its Deserializable::from_bytes, which checks for - // the 0 value. Also, the underlying type is zeroize-on-drop. - #[doc = concat!( - "An ECDH ", - $curve_name, - " private key. This is a scalar in the range `[1,p)` where `p` is the group order." - )] - #[derive(Clone, Eq, PartialEq)] - pub struct PrivateKey(curve_crate::SecretKey); - - impl ConstantTimeEq for PrivateKey { - fn ct_eq(&self, other: &Self) -> Choice { - self.0.ct_eq(&other.0) - } - } - - // The underlying type is zeroize-on-drop - /// A bare DH computation result - pub struct KexResult(curve_crate::ecdh::SharedSecret); - - // Everything is serialized and deserialized in uncompressed form - impl Serializable for PublicKey { - type OutputSize = $pubkey_size; - - fn write_exact(&self, buf: &mut [u8]) { - // Check the length is correct and panic if not - enforce_outbuf_len::(buf); - - // Get the uncompressed pubkey encoding - let encoded = self.0.as_affine().to_encoded_point(false); - // Serialize it - buf.copy_from_slice(encoded.as_bytes()); - } - - } - - // Everything is serialized and deserialized in uncompressed form - impl Deserializable for PublicKey { - fn from_bytes(encoded: &[u8]) -> Result { - // In order to parse as an uncompressed curve point, we first make sure the - // input length is correct. This ensures we're receiving the uncompressed - // representation. - enforce_equal_len(Self::OutputSize::to_usize(), encoded.len())?; - - // Now just deserialize. The non-identity invariant is preserved because - // PublicKey::from_sec1_bytes() will error if it receives the point at - // infinity. This is because its submethod, PublicKey::from_encoded_point(), - // does this check explicitly. - let parsed = curve_crate::PublicKey::from_sec1_bytes(encoded) - .map_err(|_| HpkeError::ValidationError)?; - Ok(PublicKey(parsed)) - } - } - - impl Serializable for PrivateKey { - type OutputSize = $privkey_size; - - fn write_exact(&self, buf: &mut [u8]) { - // Check the length is correct and panic if not - enforce_outbuf_len::(buf); - - // SecretKeys already know how to convert to bytes - buf.copy_from_slice(&self.0.to_bytes()); - } - } - - impl Deserializable for PrivateKey { - fn from_bytes(encoded: &[u8]) -> Result { - // Check the length - enforce_equal_len(Self::OutputSize::to_usize(), encoded.len())?; - - // * Invariant: PrivateKey is in [1,p). This is preserved here. - // * SecretKey::from_be_bytes() directly checks that the value isn't zero. And - // its submethod, - // * ScalarCore::from_be_bytes() checks that the value doesn't exceed the - // modulus. - let sk = curve_crate::SecretKey::from_bytes(encoded.into()) - .map_err(|_| HpkeError::ValidationError)?; - - Ok(PrivateKey(sk)) - } - } - - // DH results are serialized in the same way as public keys - impl Serializable for KexResult { - // RFC 9180 §4.1 - // For P-256, P-384, and P-521, the size Ndh of the Diffie-Hellman shared secret is - // equal to 32, 48, and 66, respectively, corresponding to the x-coordinate of the - // resulting elliptic curve point. - type OutputSize = $ss_size; - - fn write_exact(&self, buf: &mut [u8]) { - // Check the length is correct and panic if not - enforce_outbuf_len::(buf); - - // elliptic_curve::ecdh::SharedSecret::raw_secret_bytes returns the serialized - // x-coordinate - buf.copy_from_slice(self.0.raw_secret_bytes()) - } - } - - #[doc = concat!("Represents ECDH functionality over NIST curve ", $curve_name, ".")] - pub struct $dh_name {} - - impl DhKeyExchange for $dh_name { - #[doc(hidden)] - type PublicKey = PublicKey; - #[doc(hidden)] - type PrivateKey = PrivateKey; - #[doc(hidden)] - type KexResult = KexResult; - - /// Converts a private key to a public key - #[doc(hidden)] - fn sk_to_pk(sk: &PrivateKey) -> PublicKey { - // pk = sk·G where G is the generator. This maintains the invariant of the - // public key not being the point at infinity, since ord(G) = p, and sk is not - // 0 mod p (by the invariant we keep on PrivateKeys) - PublicKey(sk.0.public_key()) - } - - /// Does the DH operation. This function is infallible, thanks to invariants on its - /// inputs. - #[doc(hidden)] - fn dh(sk: &PrivateKey, pk: &PublicKey) -> Result { - // Do the DH operation - let dh_res = diffie_hellman(sk.0.to_nonzero_scalar(), pk.0.as_affine()); - - // RFC 9180 §7.1.4: Senders and recipients MUST ensure that dh_res is not the - // point at infinity - // - // This is already true, since: - // 1. pk is not the point at infinity (due to the invariant we keep on - // PublicKeys) - // 2. sk is not 0 mod p (due to the invariant we keep on PrivateKeys) - // 3. Exponentiating a non-identity element of a prime-order group by something - // less than the order yields a non-identity value - // Therefore, dh_res cannot be the point at infinity - Ok(KexResult(dh_res)) - } - - // RFC 9180 §7.1.3: - // def DeriveKeyPair(ikm): - // dkp_prk = LabeledExtract("", "dkp_prk", ikm) - // sk = 0 - // counter = 0 - // while sk == 0 or sk >= order: - // if counter > 255: - // raise DeriveKeyPairError - // bytes = LabeledExpand(dkp_prk, "candidate", - // I2OSP(counter, 1), Nsk) - // bytes[0] = bytes[0] & bitmask - // sk = OS2IP(bytes) - // counter = counter + 1 - // return (sk, pk(sk)) - // where `bitmask` is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521 - - /// Deterministically derives a keypair from the given input keying material and - /// ciphersuite ID. The keying material SHOULD have as many bits of entropy as the - /// bit length of a secret key - #[doc(hidden)] - fn derive_keypair( - suite_id: &KemSuiteId, - ikm: &[u8], - ) -> (PrivateKey, PublicKey) { - // Write the label into a byte buffer and extract from the IKM - let (_, hkdf_ctx) = labeled_extract::(&[], suite_id, b"dkp_prk", ikm); - - // The buffer we hold the candidate scalar bytes in. This is the size of a - // private key. - let mut buf = - GenericArray::::OutputSize>::default(); - - // Try to generate a key 256 times. Practically, this will succeed and return - // early on the first iteration. - for counter in 0u8..=255 { - // This unwrap is fine. It only triggers if buf is way too big. It's only - // 32 bytes. - hkdf_ctx - .labeled_expand(suite_id, b"candidate", &[counter], &mut buf) - .unwrap(); - - // Apply the bitmask - buf[0] &= $keygen_bitmask; - - // Try to convert to a valid secret key. If the conversion succeeded, - // return the keypair. Recall the invariant of PrivateKey: it is a value in - // the range [1,p). - if let Ok(sk) = PrivateKey::from_bytes(&buf) { - let pk = Self::sk_to_pk(&sk); - return (sk, pk); - } - } - - // The code should never ever get here. The likelihood that we get 256 bad - // samples in a row for P-256 is 2^-8192. For P-384 it's (2^-256)^256. - panic!("DeriveKeyPair failed all attempts"); - } - } - } - }; -} - -use generic_array::typenum; - -#[cfg(feature = "p256")] -nist_dhkex!( - "P-256", - DhP256, - p256, - typenum::U65, // RFC 9180 §7.1: Npk of DHKEM(P-256, HKDF-SHA256) is 65 - typenum::U32, // RFC 9180 §7.1: Nsk of DHKEM(P-256, HKDF-SHA256) is 32 - typenum::U32, // RFC 9180 §4.1: Ndh of P-256 is equal to 32 - 0xFF // RFC 9180 §7.1.3: The `bitmask` in DeriveKeyPair to be 0xFF for P-256 -); - -#[cfg(feature = "p384")] -nist_dhkex!( - "P-384", - DhP384, - p384, - typenum::U97, // RFC 9180 §7.1: Npk of DHKEM(P-384, HKDF-SHA384) is 97 - typenum::U48, // RFC 9180 §7.1: Nsk of DHKEM(P-384, HKDF-SHA384) is 48 - typenum::U48, // RFC 9180 §4.1: Ndh of P-384 is equal to 48 - 0xFF // RFC 9180 §7.1.3: The `bitmask` in DeriveKeyPair to be 0xFF for P-384 -); - -#[cfg(feature = "p521")] -nist_dhkex!( - "P-521", - DhP521, - p521, - typenum::U133, // RFC 9180 §7.1: Npk of DHKEM(P-521, HKDF-SHA512) is 133 - typenum::U66, // RFC 9180 §7.1: Nsk of DHKEM(P-521, HKDF-SHA512) is 66 - typenum::U66, // RFC 9180 §4.1: Ndh of P-521 is equal to 66 - 0x01 // RFC 9180 §7.1.3: The `bitmask` in DeriveKeyPair to be 0x01 for P-521 -); - -#[cfg(feature = "k256")] -nist_dhkex!( - "K-256", - DhK256, - k256, - typenum::U65, - typenum::U32, - typenum::U32, - 0xFF -); - -#[cfg(test)] -mod tests { - use crate::{dhkex::DhKeyExchange, test_util::dhkex_gen_keypair, Deserializable, Serializable}; - - #[cfg(feature = "k256")] - use super::k256::DhK256; - #[cfg(feature = "p256")] - use super::p256::DhP256; - #[cfg(feature = "p384")] - use super::p384::DhP384; - #[cfg(feature = "p521")] - use super::p521::DhP521; - - use hex_literal::hex; - use rand::{rngs::StdRng, SeedableRng}; - - // - // Test vectors come from RFC 5903 §8.1, §8.2 and §8.3 - // https://tools.ietf.org/html/rfc5903 - // - - #[cfg(feature = "p256")] - const P256_PRIVKEYS: &[&[u8]] = &[ - &hex!("C88F01F5 10D9AC3F 70A292DA A2316DE5 44E9AAB8 AFE84049 C62A9C57 862D1433"), - &hex!("C6EF9C5D 78AE012A 011164AC B397CE20 88685D8F 06BF9BE0 B283AB46 476BEE53"), - ]; - - // The public keys corresponding to the above private keys, in order - #[cfg(feature = "p256")] - const P256_PUBKEYS: &[&[u8]] = &[ - &hex!( - "04" // Uncompressed - "DAD0B653 94221CF9 B051E1FE CA5787D0 98DFE637 FC90B9EF 945D0C37 72581180" // x-coordinate - "5271A046 1CDB8252 D61F1C45 6FA3E59A B1F45B33 ACCF5F58 389E0577 B8990BB3" // y-coordinate - ), - &hex!( - "04" // Uncompressed - "D12DFB52 89C8D4F8 1208B702 70398C34 2296970A 0BCCB74C 736FC755 4494BF63" // x-coordinate - "56FBF3CA 366CC23E 8157854C 13C58D6A AC23F046 ADA30F83 53E74F33 039872AB" // y-coordinate - ), - ]; - - // The result of DH(privkey0, pubkey1) or equivalently, DH(privkey1, pubkey0) - #[cfg(feature = "p256")] - const P256_DH_RES_XCOORD: &[u8] = - &hex!("D6840F6B 42F6EDAF D13116E0 E1256520 2FEF8E9E CE7DCE03 812464D0 4B9442DE"); - - #[cfg(feature = "p384")] - const P384_PRIVKEYS: &[&[u8]] = &[ - &hex!( - "099F3C70 34D4A2C6 99884D73 A375A67F 7624EF7C 6B3C0F16 0647B674 14DCE655 E35B5380" - "41E649EE 3FAEF896 783AB194" - ), - &hex!( - "41CB0779 B4BDB85D 47846725 FBEC3C94 30FAB46C C8DC5060 855CC9BD A0AA2942 E0308312" - "916B8ED2 960E4BD5 5A7448FC" - ), - ]; - - // The public keys corresponding to the above private keys, in order - #[cfg(feature = "p384")] - const P384_PUBKEYS: &[&[u8]] = &[ - &hex!( - "04" // Uncompressed - "667842D7 D180AC2C DE6F74F3 7551F557 55C7645C 20EF73E3 1634FE72" // x-coordinate - "B4C55EE6 DE3AC808 ACB4BDB4 C88732AE E95F41AA" // ...cont - "9482ED1F C0EEB9CA FC498462 5CCFC23F 65032149 E0E144AD A0241815" // y-coordinate - "35A0F38E EB9FCFF3 C2C947DA E69B4C63 4573A81C" // ...cont - ), - &hex!( - "04" // Uncompressed - "E558DBEF 53EECDE3 D3FCCFC1 AEA08A89 A987475D 12FD950D 83CFA417" // x-coordinate - "32BC509D 0D1AC43A 0336DEF9 6FDA41D0 774A3571" // ...cont - "DCFBEC7A ACF31964 72169E83 8430367F 66EEBE3C 6E70C416 DD5F0C68" // y-coordinate - "759DD1FF F83FA401 42209DFF 5EAAD96D B9E6386C" // ...cont - ), - ]; - - // The result of DH(privkey0, pubkey1) or equivalently, DH(privkey1, pubkey0) - #[cfg(feature = "p384")] - const P384_DH_RES_XCOORD: &[u8] = &hex!( - "11187331 C279962D 93D60424 3FD592CB 9D0A926F 422E4718 7521287E 7156C5C4 D6031355" - "69B9E9D0 9CF5D4A2 70F59746" - ); - - #[cfg(feature = "p521")] - const P521_PRIVKEYS: &[&[u8]] = &[ - &hex!( - "0037ADE9 319A89F4 DABDB3EF 411AACCC A5123C61 ACAB57B5 393DCE47 608172A0" - "95AA85A3 0FE1C295 2C6771D9 37BA9777 F5957B26 39BAB072 462F68C2 7A57382D" - "4A52" - ), - &hex!( - "0145BA99 A847AF43 793FDD0E 872E7CDF A16BE30F DC780F97 BCCC3F07 8380201E" - "9C677D60 0B343757 A3BDBF2A 3163E4C2 F869CCA7 458AA4A4 EFFC311F 5CB15168" - "5EB9" - ), - ]; - - // The public keys corresponding to the above private keys, in order - #[cfg(feature = "p521")] - const P521_PUBKEYS: &[&[u8]] = &[ - &hex!( - "04" // Uncompressed - "0015417E 84DBF28C 0AD3C278 713349DC 7DF153C8 97A1891B D98BAB43 57C9ECBE" // x-coordinate - "E1E3BF42 E00B8E38 0AEAE57C 2D107564 94188594 2AF5A7F4 601723C4 195D176C" // ...cont - "ED3E" // ...cont - "017CAE20 B6641D2E EB695786 D8C94614 6239D099 E18E1D5A 514C739D 7CB4A10A" // y-coordinate - "D8A78801 5AC405D7 799DC75E 7B7D5B6C F2261A6A 7F150743 8BF01BEB 6CA3926F" // ...cont - "9582" // ...cont - ), - &hex!( - "04" // Uncompressed - "00D0B397 5AC4B799 F5BEA16D 5E13E9AF 971D5E9B 984C9F39 728B5E57 39735A21" // x-coordinate - "9B97C356 436ADC6E 95BB0352 F6BE64A6 C2912D4E F2D0433C ED2B6171 640012D9" // ...cont - "460F" // ...cont - "015C6822 6383956E 3BD066E7 97B623C2 7CE0EAC2 F551A10C 2C724D98 52077B87" // y-coordinate - "220B6536 C5C408A1 D2AEBB8E 86D678AE 49CB5709 1F473229 6579AB44 FCD17F0F" // ...cont - "C56A" // ...cont - ), - ]; - - // The result of DH(privkey0, pubkey1) or equivalently, DH(privkey1, pubkey0) - #[cfg(feature = "p521")] - const P521_DH_RES_XCOORD: &[u8] = &hex!( - "01144C7D 79AE6956 BC8EDB8E 7C787C45 21CB086F A64407F9 7894E5E6 B2D79B04" - "D1427E73 CA4BAA24 0A347868 59810C06 B3C715A3 A8CC3151 F2BEE417 996D19F3" - "DDEA" - ); - - #[cfg(feature = "k256")] - const K256_PRIVKEYS: &[&[u8]] = &[ - &hex!("30FBC0D4 1CD01885 333211FF 53B9ED29 BCBDCCC3 FF13625A 82DB61A7 BB8EAE19"), - &hex!("A795C287 C132154A 8B96DC81 DC8B4E2F 02BBBAD7 8DAB0567 B59DB1D1 540751F6"), - ]; - - // The public keys corresponding to the above private keys, in order - #[cfg(feature = "k256")] - const K256_PUBKEYS: &[&[u8]] = &[ - &hex!( - "04" // Uncompressed - "59177516 8F328A2A DBCB887A CD287D55 A1025D7D 2B15E193 7278A5EF D1D48B19" // x-coordinate - "C00CF075 59320E6D 278A71C9 E58BAE5D 9AB041D7 905C6629 1F4D0845 9C946E18" // y-coordinate - ), - &hex!( - "04" // Uncompressed - "3EE73144 07753D1B A296DE29 F07B2CD5 505CA94B 614F127E 71F3C19F C7845DAF" // x-coordinate - "49C9BB4B F4D00D3B 5411C8EB 86D59A2D CADC5A13 115FA9FE F44D1E0B 7EF11CAB" // y-coordinate - ), - ]; - - // The result of DH(privkey0, pubkey1) or equivalently, DH(privkey1, pubkey0) - #[cfg(feature = "k256")] - const K256_DH_RES_XCOORD: &[u8] = - &hex!("3ADDFBC2 B30E3D1B 1DF262A4 D6CECF73 A11DF8BD 93E0EB21 FC11847C 6F3DDBE2"); - - // - // Some helper functions for tests - // - - /// Tests the ECDH op against a known answer - #[allow(dead_code)] - fn test_vector_ecdh( - sk_recip_bytes: &[u8], - pk_sender_bytes: &[u8], - dh_res_xcoord_bytes: &[u8], - ) { - // Deserialize the pubkey and privkey and do a DH operation - let sk_recip = Kex::PrivateKey::from_bytes(&sk_recip_bytes).unwrap(); - let pk_sender = Kex::PublicKey::from_bytes(&pk_sender_bytes).unwrap(); - let derived_dh = Kex::dh(&sk_recip, &pk_sender).unwrap(); - - // Assert that the derived DH result matches the test vector. Recall that the HPKE DH - // result is just the x-coordinate, so that's all we can compare - assert_eq!(derived_dh.to_bytes().as_slice(), dh_res_xcoord_bytes,); - } - - /// Tests that an deserialize-serialize round-trip ends up at the same pubkey - #[allow(dead_code)] - fn test_pubkey_serialize_correctness() { - let mut csprng = StdRng::from_entropy(); - - // We can't do the same thing as in the X25519 tests, since a completely random point - // is not likely to lie on the curve. Instead, we just generate a random point, - // serialize it, deserialize it, and test whether it's the same using impl Eq for - // AffinePoint - - let (_, pubkey) = dhkex_gen_keypair::(&mut csprng); - let pubkey_bytes = pubkey.to_bytes(); - let rederived_pubkey = - ::PublicKey::from_bytes(&pubkey_bytes).unwrap(); - - // See if the re-serialized bytes are the same as the input - assert_eq!(pubkey, rederived_pubkey); - } - - /// Tests the `sk_to_pk` function against known answers - #[allow(dead_code)] - fn test_vector_corresponding_pubkey(sks: &[&[u8]], pks: &[&[u8]]) { - for (sk_bytes, pk_bytes) in sks.iter().zip(pks.iter()) { - // Deserialize the hex values - let sk = Kex::PrivateKey::from_bytes(sk_bytes).unwrap(); - let pk = Kex::PublicKey::from_bytes(pk_bytes).unwrap(); - - // Derive the secret key's corresponding pubkey and check that it matches the given - // pubkey - let derived_pk = Kex::sk_to_pk(&sk); - assert_eq!(derived_pk, pk); - } - } - - /// Tests that an deserialize-serialize round-trip on a DH keypair ends up at the same values - #[allow(dead_code)] - fn test_dh_serialize_correctness() - where - Kex::PrivateKey: PartialEq, - { - let mut csprng = StdRng::from_entropy(); - - // Make a random keypair and serialize it - let (sk, pk) = dhkex_gen_keypair::(&mut csprng); - let (sk_bytes, pk_bytes) = (sk.to_bytes(), pk.to_bytes()); - - // Now deserialize those bytes - let new_sk = Kex::PrivateKey::from_bytes(&sk_bytes).unwrap(); - let new_pk = Kex::PublicKey::from_bytes(&pk_bytes).unwrap(); - - // See if the deserialized values are the same as the initial ones - assert!(new_sk == sk, "private key doesn't serialize correctly"); - assert!(new_pk == pk, "public key doesn't serialize correctly"); - } - - #[cfg(feature = "p256")] - #[test] - fn test_vector_ecdh_p256() { - test_vector_ecdh::(&P256_PRIVKEYS[0], &P256_PUBKEYS[1], &P256_DH_RES_XCOORD); - } - - #[cfg(feature = "p384")] - #[test] - fn test_vector_ecdh_p384() { - test_vector_ecdh::(&P384_PRIVKEYS[0], &P384_PUBKEYS[1], &P384_DH_RES_XCOORD); - } - #[cfg(feature = "k256")] - #[test] - fn test_vector_ecdh_k256() { - test_vector_ecdh::(&K256_PRIVKEYS[0], &K256_PUBKEYS[1], &K256_DH_RES_XCOORD); - } - - #[cfg(feature = "p521")] - #[test] - fn test_vector_ecdh_p521() { - test_vector_ecdh::(&P521_PRIVKEYS[0], &P521_PUBKEYS[1], &P521_DH_RES_XCOORD); - } - - #[cfg(feature = "p256")] - #[test] - fn test_vector_corresponding_pubkey_p256() { - test_vector_corresponding_pubkey::(P256_PRIVKEYS, P256_PUBKEYS); - } - - #[cfg(feature = "p384")] - #[test] - fn test_vector_corresponding_pubkey_p384() { - test_vector_corresponding_pubkey::(P384_PRIVKEYS, P384_PUBKEYS); - } - #[cfg(feature = "k256")] - #[test] - fn test_vector_corresponding_pubkey_k256() { - test_vector_corresponding_pubkey::(K256_PRIVKEYS, K256_PUBKEYS); - } - - #[cfg(feature = "p521")] - #[test] - fn test_vector_corresponding_pubkey_p521() { - test_vector_corresponding_pubkey::(P521_PRIVKEYS, P521_PUBKEYS); - } - - #[cfg(feature = "p256")] - #[test] - fn test_pubkey_serialize_correctness_p256() { - test_pubkey_serialize_correctness::(); - } - - #[cfg(feature = "p384")] - #[test] - fn test_pubkey_serialize_correctness_p384() { - test_pubkey_serialize_correctness::(); - } - #[cfg(feature = "k256")] - #[test] - fn test_pubkey_serialize_correctness_k256() { - test_pubkey_serialize_correctness::(); - } - - #[cfg(feature = "p521")] - #[test] - fn test_pubkey_serialize_correctness_p521() { - test_pubkey_serialize_correctness::(); - } - - #[cfg(feature = "p256")] - #[test] - fn test_dh_serialize_correctness_p256() { - test_dh_serialize_correctness::(); - } - - #[cfg(feature = "p384")] - #[test] - fn test_dh_serialize_correctness_p384() { - test_dh_serialize_correctness::(); - } - - #[cfg(feature = "p521")] - #[test] - fn test_dh_serialize_correctness_p521() { - test_dh_serialize_correctness::(); - } - - #[cfg(feature = "k256")] - #[test] - fn test_dh_serialize_correctness_k256() { - test_dh_serialize_correctness::(); - } -} diff --git a/src/dhkex/x25519.rs b/src/dhkex/x25519.rs deleted file mode 100644 index f3f531a..0000000 --- a/src/dhkex/x25519.rs +++ /dev/null @@ -1,227 +0,0 @@ -use crate::{ - dhkex::{DhError, DhKeyExchange}, - kdf::{labeled_extract, Kdf as KdfTrait, LabeledExpand}, - util::{enforce_equal_len, enforce_outbuf_len, KemSuiteId}, - Deserializable, HpkeError, Serializable, -}; - -use generic_array::typenum::{self, Unsigned}; -use subtle::{Choice, ConstantTimeEq}; - -// We wrap the types in order to abstract away the dalek dep - -/// An X25519 public key -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PublicKey(x25519_dalek::PublicKey); - -// The underlying type is zeroize-on-drop -/// An X25519 private key -#[derive(Clone)] -pub struct PrivateKey(x25519_dalek::StaticSecret); - -impl ConstantTimeEq for PrivateKey { - fn ct_eq(&self, other: &Self) -> Choice { - // We can use to_bytes because StaticSecret is only ever constructed from a clamped scalar - self.0.to_bytes().ct_eq(&other.0.to_bytes()) - } -} - -impl PartialEq for PrivateKey { - fn eq(&self, other: &Self) -> bool { - self.ct_eq(other).into() - } -} -impl Eq for PrivateKey {} - -// The underlying type is zeroize-on-drop -/// A bare DH computation result -pub struct KexResult(x25519_dalek::SharedSecret); - -// Oh I love an excuse to break out type-level integers -impl Serializable for PublicKey { - // RFC 9180 §7.1 Table 2: Npk of DHKEM(X25519, HKDF-SHA256) is 32 - type OutputSize = typenum::U32; - - // Dalek lets us convert pubkeys to [u8; 32] - fn write_exact(&self, buf: &mut [u8]) { - // Check the length is correct and panic if not - enforce_outbuf_len::(buf); - - buf.copy_from_slice(self.0.as_bytes()); - } -} - -impl Deserializable for PublicKey { - // Dalek lets us convert [u8; 32] to pubkeys. Assuming the input length is correct, this - // conversion is infallible, so no ValidationErrors are raised. - fn from_bytes(encoded: &[u8]) -> Result { - // Pubkeys must be 32 bytes - enforce_equal_len(Self::OutputSize::to_usize(), encoded.len())?; - - // Copy to a fixed-size array - let mut arr = [0u8; 32]; - arr.copy_from_slice(encoded); - Ok(PublicKey(x25519_dalek::PublicKey::from(arr))) - } -} - -impl Serializable for PrivateKey { - // RFC 9180 §7.1 Table 2: Nsk of DHKEM(X25519, HKDF-SHA256) is 32 - type OutputSize = typenum::U32; - - // Dalek lets us convert scalars to [u8; 32] - fn write_exact(&self, buf: &mut [u8]) { - // Check the length is correct and panic if not - enforce_outbuf_len::(buf); - - buf.copy_from_slice(self.0.as_bytes()); - } -} -impl Deserializable for PrivateKey { - // Dalek lets us convert [u8; 32] to scalars. Assuming the input length is correct, this - // conversion is infallible, so no ValidationErrors are raised. - fn from_bytes(encoded: &[u8]) -> Result { - // Privkeys must be 32 bytes - enforce_equal_len(Self::OutputSize::to_usize(), encoded.len())?; - - // Copy to a fixed-size array - let mut arr = [0u8; 32]; - arr.copy_from_slice(encoded); - // We don't have to do a zero-check for X25519 private keys. We clamp all private keys upon - // deserialization, and clamped private keys cannot ever be 0 mod curve_order. In fact, - // they can't even be 0 mod q where q is the order of the prime subgroup generated by the - // canonical generator. - // Why? - // A clamped key k is of the form 2^254 + 8j where j is in [0, 2^251-1]. If k = 0 (mod q) - // then k = nq for some n > 0. And since k is a multiple of 8 and q is prime, n must be a - // multiple of 8. However, 8q > 2^257 which is already out of representable range! So k - // cannot be 0 (mod q). - Ok(PrivateKey(x25519_dalek::StaticSecret::from(arr))) - } -} - -impl Serializable for KexResult { - // RFC 9180 §4.1: For X25519 and X448, the size Ndh is equal to 32 and 56, respectively - type OutputSize = typenum::U32; - - // curve25519's point representation is our DH result. We don't have to do anything special. - fn write_exact(&self, buf: &mut [u8]) { - // Check the length is correct and panic if not - enforce_outbuf_len::(buf); - - // Dalek lets us convert shared secrets to to [u8; 32] - buf.copy_from_slice(self.0.as_bytes()); - } -} - -/// Represents ECDH functionality over the X25519 group -pub struct X25519 {} - -impl DhKeyExchange for X25519 { - #[doc(hidden)] - type PublicKey = PublicKey; - #[doc(hidden)] - type PrivateKey = PrivateKey; - #[doc(hidden)] - type KexResult = KexResult; - - /// Converts an X25519 private key to a public key - #[doc(hidden)] - fn sk_to_pk(sk: &PrivateKey) -> PublicKey { - PublicKey(x25519_dalek::PublicKey::from(&sk.0)) - } - - /// Does the DH operation. Returns an error if and only if the DH result was all zeros. This is - /// required by the HPKE spec. The error is converted into the appropriate higher-level error - /// by the caller, i.e., `HpkeError::EncapError` or `HpkeError::DecapError`. - #[doc(hidden)] - fn dh(sk: &PrivateKey, pk: &PublicKey) -> Result { - let res = sk.0.diffie_hellman(&pk.0); - // "Senders and recipients MUST check whether the shared secret is the all-zero value - // and abort if so" - if res.as_bytes().ct_eq(&[0u8; 32]).into() { - Err(DhError) - } else { - Ok(KexResult(res)) - } - } - - // RFC 9180 §7.1.3 - // def DeriveKeyPair(ikm): - // dkp_prk = LabeledExtract("", "dkp_prk", ikm) - // sk = LabeledExpand(dkp_prk, "sk", "", Nsk) - // return (sk, pk(sk)) - - /// Deterministically derives a keypair from the given input keying material and ciphersuite - /// ID. The keying material SHOULD have as many bits of entropy as the bit length of a secret - /// key, i.e., 256. - #[doc(hidden)] - fn derive_keypair(suite_id: &KemSuiteId, ikm: &[u8]) -> (PrivateKey, PublicKey) { - // Write the label into a byte buffer and extract from the IKM - let (_, hkdf_ctx) = labeled_extract::(&[], suite_id, b"dkp_prk", ikm); - // The buffer we hold the candidate scalar bytes in. This is the size of a private key. - let mut buf = [0u8; 32]; - hkdf_ctx - .labeled_expand(suite_id, b"sk", &[], &mut buf) - .unwrap(); - - let sk = x25519_dalek::StaticSecret::from(buf); - let pk = x25519_dalek::PublicKey::from(&sk); - - (PrivateKey(sk), PublicKey(pk)) - } -} - -#[cfg(test)] -mod tests { - use crate::{ - dhkex::{x25519::X25519, Deserializable, DhKeyExchange, Serializable}, - test_util::dhkex_gen_keypair, - }; - use generic_array::typenum::Unsigned; - use rand::{rngs::StdRng, RngCore, SeedableRng}; - - /// Tests that an serialize-deserialize round-trip ends up at the same pubkey - #[test] - fn test_pubkey_serialize_correctness() { - type Kex = X25519; - - let mut csprng = StdRng::from_entropy(); - - // Fill a buffer with randomness - let orig_bytes = { - let mut buf = - [0u8; <::PublicKey as Serializable>::OutputSize::USIZE]; - csprng.fill_bytes(buf.as_mut_slice()); - buf - }; - - // Make a pubkey with those random bytes. Note, that from_bytes() does not clamp the input - // bytes. This is why this test passes. - let pk = ::PublicKey::from_bytes(&orig_bytes).unwrap(); - let pk_bytes = pk.to_bytes(); - - // See if the re-serialized bytes are the same as the input - assert_eq!(orig_bytes.as_slice(), pk_bytes.as_slice()); - } - - /// Tests that an deserialize-serialize round trip on a DH keypair ends up at the same values - #[test] - fn test_dh_serialize_correctness() { - type Kex = X25519; - - let mut csprng = StdRng::from_entropy(); - - // Make a random keypair and serialize it - let (sk, pk) = dhkex_gen_keypair::(&mut csprng); - let (sk_bytes, pk_bytes) = (sk.to_bytes(), pk.to_bytes()); - - // Now deserialize those bytes - let new_sk = ::PrivateKey::from_bytes(&sk_bytes).unwrap(); - let new_pk = ::PublicKey::from_bytes(&pk_bytes).unwrap(); - - // See if the deserialized values are the same as the initial ones - assert!(new_sk == sk, "private key doesn't serialize correctly"); - assert!(new_pk == pk, "public key doesn't serialize correctly"); - } -} diff --git a/src/kem.rs b/src/kem.rs index c7b4991..e78dc33 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -192,46 +192,6 @@ mod tests { }; } - #[cfg(feature = "x25519")] - mod x25519_tests { - use super::*; - - test_encap_correctness!(test_encap_correctness_x25519, crate::kem::X25519HkdfSha256); - test_encapped_serialize!(test_encapped_serialize_x25519, crate::kem::X25519HkdfSha256); - } - - #[cfg(feature = "p256")] - mod p256_tests { - use super::*; - - test_encap_correctness!(test_encap_correctness_p256, crate::kem::DhP256HkdfSha256); - test_encapped_serialize!(test_encapped_serialize_p256, crate::kem::DhP256HkdfSha256); - } - - #[cfg(feature = "p384")] - mod p384_tests { - use super::*; - - test_encap_correctness!(test_encap_correctness_p384, crate::kem::DhP384HkdfSha384); - test_encapped_serialize!(test_encapped_serialize_p384, crate::kem::DhP384HkdfSha384); - } - - #[cfg(feature = "p521")] - mod p521_tests { - use super::*; - - test_encap_correctness!(test_encap_correctness_p521, crate::kem::DhP521HkdfSha512); - test_encapped_serialize!(test_encapped_serialize_p521, crate::kem::DhP521HkdfSha512); - } - - #[cfg(feature = "k256")] - mod k256_tests { - use super::*; - - test_encap_correctness!(test_encap_correctness_k256, crate::kem::DhK256HkdfSha256); - test_encapped_serialize!(test_encapped_serialize_k256, crate::kem::DhK256HkdfSha256); - } - #[cfg(feature = "secp")] mod secp_tests { use super::*; diff --git a/src/kem/dhkem.rs b/src/kem/dhkem.rs index 7313585..421e766 100644 --- a/src/kem/dhkem.rs +++ b/src/kem/dhkem.rs @@ -348,61 +348,6 @@ macro_rules! impl_dhkem { }; } -// Implement DHKEM(X25519, HKDF-SHA256) -#[cfg(feature = "x25519")] -impl_dhkem!( - x25519_hkdfsha256, - X25519HkdfSha256, - crate::dhkex::x25519::X25519, - crate::kdf::HkdfSha256, - 0x0020, - "Represents DHKEM(X25519, HKDF-SHA256)" -); - -// Implement DHKEM(P-256, HKDF-SHA256) -#[cfg(feature = "p256")] -impl_dhkem!( - dhp256_hkdfsha256, - DhP256HkdfSha256, - crate::dhkex::ecdh_nist::p256::DhP256, - crate::kdf::HkdfSha256, - 0x0010, - "Represents DHKEM(P-256, HKDF-SHA256)" -); - -// Implement DHKEM(P-384, HKDF-SHA384) -#[cfg(feature = "p384")] -impl_dhkem!( - dhp384_hkdfsha384, - DhP384HkdfSha384, - crate::dhkex::ecdh_nist::p384::DhP384, - crate::kdf::HkdfSha384, - 0x0011, - "Represents DHKEM(P-384, HKDF-SHA384)" -); - -// Implement DHKEM(P-521, HKDF-SHA512) -#[cfg(feature = "p521")] -impl_dhkem!( - dhp521_hkdfsha512, - DhP521HkdfSha512, - crate::dhkex::ecdh_nist::p521::DhP521, - crate::kdf::HkdfSha512, - 0x0012, - "Represents DHKEM(P-521, HKDF-SHA512)" -); - -// Implement DHKEM(K-256, HKDF-SHA256) -#[cfg(feature = "k256")] -impl_dhkem!( - dhk256_hkdfsha256, - DhK256HkdfSha256, - crate::dhkex::ecdh_nist::k256::DhK256, - crate::kdf::HkdfSha256, - 0x0016, - "Represents DHKEM(K-256, HKDF-SHA256)" -); - // Implement DHKEM(Secp256k1, HKDF-SHA256) #[cfg(feature = "secp")] impl_dhkem!( diff --git a/src/lib.rs b/src/lib.rs index 9ee14a8..5d0d996 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,15 +96,7 @@ pub(crate) use alloc::vec::Vec; // kat_tests tests all the implemented ciphersuites, and thus needs all the dependencies. It also // needs std for file IO. -#[cfg(all( - test, - feature = "std", - feature = "x25519", - feature = "p256", - feature = "p384", - feature = "p521", - feature = "secp" -))] +#[cfg(all(test, feature = "std", feature = "secp"))] mod kat_tests; #[cfg(test)] diff --git a/src/setup.rs b/src/setup.rs index 2c6d891..b057f4c 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -337,98 +337,6 @@ mod test { }; } - #[cfg(feature = "x25519")] - mod x25519_tests { - use super::*; - - test_setup_correctness!( - test_setup_correctness_x25519, - ChaCha20Poly1305, - HkdfSha256, - crate::kem::x25519_hkdfsha256::X25519HkdfSha256 - ); - test_setup_soundness!( - test_setup_soundness_x25519, - ChaCha20Poly1305, - HkdfSha256, - crate::kem::x25519_hkdfsha256::X25519HkdfSha256 - ); - } - - #[cfg(feature = "p256")] - mod p256_tests { - use super::*; - - test_setup_correctness!( - test_setup_correctness_p256, - ChaCha20Poly1305, - HkdfSha256, - crate::kem::dhp256_hkdfsha256::DhP256HkdfSha256 - ); - test_setup_soundness!( - test_setup_soundness_p256, - ChaCha20Poly1305, - HkdfSha256, - crate::kem::dhp256_hkdfsha256::DhP256HkdfSha256 - ); - } - - #[cfg(feature = "p384")] - mod p384_tests { - use super::*; - use crate::kdf::HkdfSha384; - - test_setup_correctness!( - test_setup_correctness_p384, - ChaCha20Poly1305, - HkdfSha384, - crate::kem::dhp384_hkdfsha384::DhP384HkdfSha384 - ); - test_setup_soundness!( - test_setup_soundness_p384, - ChaCha20Poly1305, - HkdfSha384, - crate::kem::dhp384_hkdfsha384::DhP384HkdfSha384 - ); - } - - #[cfg(feature = "p521")] - mod p521_tests { - use super::*; - use crate::kdf::HkdfSha512; - - test_setup_correctness!( - test_setup_correctness_p521, - ChaCha20Poly1305, - HkdfSha512, - crate::kem::dhp521_hkdfsha512::DhP521HkdfSha512 - ); - test_setup_soundness!( - test_setup_soundness_p521, - ChaCha20Poly1305, - HkdfSha512, - crate::kem::dhp521_hkdfsha512::DhP521HkdfSha512 - ); - } - - #[cfg(feature = "k256")] - mod k256_tests { - use super::*; - - test_setup_correctness!( - test_setup_correctness_k256, - ChaCha20Poly1305, - HkdfSha256, - crate::kem::dhk256_hkdfsha256::DhK256HkdfSha256 - ); - test_setup_soundness!( - test_setup_soundness_k256, - ChaCha20Poly1305, - HkdfSha256, - crate::kem::dhk256_hkdfsha256::DhK256HkdfSha256 - ); - } - #[cfg(feature = "secp")] mod secp_tests { use super::*; diff --git a/src/single_shot.rs b/src/single_shot.rs index 83fd60b..e3eabf3 100644 --- a/src/single_shot.rs +++ b/src/single_shot.rs @@ -224,46 +224,6 @@ mod test { }; } - #[cfg(feature = "x25519")] - test_single_shot_correctness!( - test_single_shot_correctness_x25519, - ChaCha20Poly1305, - crate::kdf::HkdfSha256, - crate::kem::x25519_hkdfsha256::X25519HkdfSha256 - ); - - #[cfg(feature = "p256")] - test_single_shot_correctness!( - test_single_shot_correctness_p256, - ChaCha20Poly1305, - crate::kdf::HkdfSha256, - crate::kem::dhp256_hkdfsha256::DhP256HkdfSha256 - ); - - #[cfg(feature = "p384")] - test_single_shot_correctness!( - test_single_shot_correctness_p384, - ChaCha20Poly1305, - crate::kdf::HkdfSha384, - crate::kem::dhp384_hkdfsha384::DhP384HkdfSha384 - ); - - #[cfg(feature = "p521")] - test_single_shot_correctness!( - test_single_shot_correctness_p521, - ChaCha20Poly1305, - crate::kdf::HkdfSha512, - crate::kem::dhp521_hkdfsha512::DhP521HkdfSha512 - ); - - #[cfg(feature = "k256")] - test_single_shot_correctness!( - test_single_shot_correctness_k256, - ChaCha20Poly1305, - crate::kdf::HkdfSha256, - crate::kem::dhk256_hkdfsha256::DhK256HkdfSha256 - ); - #[cfg(feature = "secp")] test_single_shot_correctness!( test_single_shot_correctness_secp,