From 088f3efa93a87e9165ef683a23858c05a57ef57a Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Mon, 30 Oct 2023 03:53:08 -0400 Subject: [PATCH 1/4] Impld secp256k1 DHKEX --- .github/workflows/ci.yml | 6 ++++++ Cargo.toml | 2 ++ src/dhkex.rs | 2 +- src/dhkex/ecdh_nistp.rs | 12 ++++++++++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c79bf13..a93cc6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,12 @@ jobs: toolchain: ${{ matrix.toolchain }} override: true + - name: Run cargo test with just K256 and serde enabled + env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: -D warnings -A dead_code -A unused_imports + run: cargo test --no-default-features --features="k256,serde_impls" + - name: Run cargo test with just X25519 enabled env: CARGO_INCREMENTAL: 0 diff --git a/Cargo.toml b/Cargo.toml index 2979798..d57cefa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ x25519 = ["dep:x25519-dalek"] p384 = ["dep:p384"] p256 = ["dep:p256"] p521 = ["dep:p521"] +k256 = ["dep:k256"] # Include allocating methods like open() and seal() alloc = [] # Includes an implementation of `std::error::Error` for `HpkeError`. Also does what `alloc` does. @@ -35,6 +36,7 @@ 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} diff --git a/src/dhkex.rs b/src/dhkex.rs index e2aca55..87fb0bf 100644 --- a/src/dhkex.rs +++ b/src/dhkex.rs @@ -44,7 +44,7 @@ pub trait DhKeyExchange { ) -> (Self::PrivateKey, Self::PublicKey); } -#[cfg(any(feature = "p256", feature = "p384", feature = "p521"))] +#[cfg(any(feature = "p256", feature = "p384", feature = "p251", feature = "k256"))] pub(crate) mod ecdh_nistp; #[cfg(feature = "x25519")] diff --git a/src/dhkex/ecdh_nistp.rs b/src/dhkex/ecdh_nistp.rs index bbe764c..9d3ff49 100644 --- a/src/dhkex/ecdh_nistp.rs +++ b/src/dhkex/ecdh_nistp.rs @@ -270,6 +270,18 @@ nistp_dhkex!( 0x01 // RFC 9180 §7.1.3: The `bitmask` in DeriveKeyPair to be 0x01 for P-521 ); + +#[cfg(feature = "k256")] +nistp_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}; From 46eb171bf99a4f3187b9270d2fc9a6644e6ade25 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Thu, 16 Nov 2023 01:45:20 -0500 Subject: [PATCH 2/4] Filled in k256 KEM and most automated tests --- src/aead.rs | 29 +++++++++++ src/dhkex.rs | 4 +- src/dhkex/{ecdh_nistp.rs => ecdh_nist.rs} | 60 ++++++++++++++++++++--- src/kem.rs | 8 +++ src/kem/dhkem.rs | 17 +++++-- src/setup.rs | 18 +++++++ src/single_shot.rs | 8 +++ 7 files changed, 133 insertions(+), 11 deletions(-) rename src/dhkex/{ecdh_nistp.rs => ecdh_nist.rs} (91%) diff --git a/src/aead.rs b/src/aead.rs index a927db4..de92a65 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -765,6 +765,35 @@ mod test { ); } + #[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 + ); + } + /// Tests that Serialize::write_exact() panics when given a buffer of incorrect length #[should_panic] #[test] diff --git a/src/dhkex.rs b/src/dhkex.rs index 87fb0bf..bf27779 100644 --- a/src/dhkex.rs +++ b/src/dhkex.rs @@ -44,8 +44,8 @@ pub trait DhKeyExchange { ) -> (Self::PrivateKey, Self::PublicKey); } -#[cfg(any(feature = "p256", feature = "p384", feature = "p251", feature = "k256"))] -pub(crate) mod ecdh_nistp; +#[cfg(any(feature = "p256", feature = "p384", feature = "k256", feature = "k256"))] +pub(crate) mod ecdh_nist; #[cfg(feature = "x25519")] pub(crate) mod x25519; diff --git a/src/dhkex/ecdh_nistp.rs b/src/dhkex/ecdh_nist.rs similarity index 91% rename from src/dhkex/ecdh_nistp.rs rename to src/dhkex/ecdh_nist.rs index 9d3ff49..ff1fb60 100644 --- a/src/dhkex/ecdh_nistp.rs +++ b/src/dhkex/ecdh_nist.rs @@ -1,5 +1,5 @@ // We define all the NIST P- curve ECDH functionalities in one macro -macro_rules! nistp_dhkex { +macro_rules! nist_dhkex { ( $curve_name:expr, $dh_name:ident, @@ -238,7 +238,7 @@ macro_rules! nistp_dhkex { use generic_array::typenum; #[cfg(feature = "p256")] -nistp_dhkex!( +nist_dhkex!( "P-256", DhP256, p256, @@ -249,7 +249,7 @@ nistp_dhkex!( ); #[cfg(feature = "p384")] -nistp_dhkex!( +nist_dhkex!( "P-384", DhP384, p384, @@ -260,7 +260,7 @@ nistp_dhkex!( ); #[cfg(feature = "p521")] -nistp_dhkex!( +nist_dhkex!( "P-521", DhP521, p521, @@ -270,9 +270,8 @@ nistp_dhkex!( 0x01 // RFC 9180 §7.1.3: The `bitmask` in DeriveKeyPair to be 0x01 for P-521 ); - #[cfg(feature = "k256")] -nistp_dhkex!( +nist_dhkex!( "K-256", DhK256, k256, @@ -286,6 +285,8 @@ nistp_dhkex!( 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")] @@ -410,6 +411,32 @@ mod tests { "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 // @@ -497,6 +524,11 @@ mod tests { 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] @@ -515,6 +547,11 @@ mod tests { 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] @@ -533,6 +570,11 @@ mod tests { 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] @@ -557,4 +599,10 @@ mod tests { 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/kem.rs b/src/kem.rs index 734eb3a..c74efe6 100644 --- a/src/kem.rs +++ b/src/kem.rs @@ -223,4 +223,12 @@ mod tests { 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); + } } diff --git a/src/kem/dhkem.rs b/src/kem/dhkem.rs index 008204a..44b7c8b 100644 --- a/src/kem/dhkem.rs +++ b/src/kem/dhkem.rs @@ -364,7 +364,7 @@ impl_dhkem!( impl_dhkem!( dhp256_hkdfsha256, DhP256HkdfSha256, - crate::dhkex::ecdh_nistp::p256::DhP256, + crate::dhkex::ecdh_nist::p256::DhP256, crate::kdf::HkdfSha256, 0x0010, "Represents DHKEM(P-256, HKDF-SHA256)" @@ -375,7 +375,7 @@ impl_dhkem!( impl_dhkem!( dhp384_hkdfsha384, DhP384HkdfSha384, - crate::dhkex::ecdh_nistp::p384::DhP384, + crate::dhkex::ecdh_nist::p384::DhP384, crate::kdf::HkdfSha384, 0x0011, "Represents DHKEM(P-384, HKDF-SHA384)" @@ -386,8 +386,19 @@ impl_dhkem!( impl_dhkem!( dhp521_hkdfsha512, DhP521HkdfSha512, - crate::dhkex::ecdh_nistp::p521::DhP521, + 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)" +); diff --git a/src/setup.rs b/src/setup.rs index ebc5ccd..7d321ee 100644 --- a/src/setup.rs +++ b/src/setup.rs @@ -410,4 +410,22 @@ mod test { 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 + ); + } } diff --git a/src/single_shot.rs b/src/single_shot.rs index fefb622..9dbef2f 100644 --- a/src/single_shot.rs +++ b/src/single_shot.rs @@ -255,4 +255,12 @@ mod test { 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 + ); } From 65a1f2d7a1f31c38c55623d62e44f664203b1d49 Mon Sep 17 00:00:00 2001 From: Michael Rosenberg Date: Thu, 16 Nov 2023 01:46:36 -0500 Subject: [PATCH 3/4] Removed serde from CI --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a93cc6b..c79bf13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,12 +31,6 @@ jobs: toolchain: ${{ matrix.toolchain }} override: true - - name: Run cargo test with just K256 and serde enabled - env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: -D warnings -A dead_code -A unused_imports - run: cargo test --no-default-features --features="k256,serde_impls" - - name: Run cargo test with just X25519 enabled env: CARGO_INCREMENTAL: 0 From f8126a37be549ee98adf7a2405b663d372ff960b Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 28 Mar 2024 22:21:30 -0400 Subject: [PATCH 4/4] Add DHKEM(secp256k1, HKDF-SHA256) to README KEM name listed at iana.org see: https://www.iana.org/assignments/hpke/hpke.xhtml --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2051647..813d811 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ Here are all the primitives listed in the spec. The primitives with checked boxe - [X] DHKEM(P-256, HKDF-SHA256) - [X] DHKEM(P-384, HKDF-SHA384) - [X] DHKEM(P-521, HKDF-SHA512) + - [X] DHKEM(secp256k1, HKDF-SHA256) * KDFs - [X] HKDF-SHA256 - [X] HKDF-SHA384