From b608d3168f1b2fb599a2f4cbf9178bcf3246a21c Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 13:21:55 -0400 Subject: [PATCH 01/14] add ECDH based KEMs --- Cargo.lock | 433 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + dhkem/Cargo.toml | 32 +++ dhkem/src/arithmetic.rs | 64 ++++++ dhkem/src/lib.rs | 43 ++++ dhkem/src/x25519_kem.rs | 51 +++++ 6 files changed, 624 insertions(+) create mode 100644 dhkem/Cargo.toml create mode 100644 dhkem/src/arithmetic.rs create mode 100644 dhkem/src/lib.rs create mode 100644 dhkem/src/x25519_kem.rs diff --git a/Cargo.lock b/Cargo.lock index 8d9f1f5..6fb5cf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,48 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "belt-block" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9aa1eef3994e2ccd304a78fe3fea4a73e5792007f85f09b79bb82143ca5f82b" + +[[package]] +name = "belt-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc405b3b8472f6e019aedf942fdee9516a0546d12e053d3744416e8f21ddb8a" +dependencies = [ + "belt-block", + "digest", +] + +[[package]] +name = "bign256" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a060e09443574e5518c7eced1ef6ff33496be5aee6dc101f99102039d3922eff" +dependencies = [ + "belt-hash", + "crypto-bigint", + "elliptic-curve", + "primeorder", + "rfc6979", + "signature", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -108,6 +150,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -184,6 +232,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -194,6 +254,62 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "fiat-crypto", + "platforms", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "dhkem" +version = "0.1.0" +dependencies = [ + "bign256", + "elliptic-curve", + "k256", + "kem", + "p192", + "p224", + "p256", + "p384", + "p521", + "rand_core", + "sm2", + "x25519-dalek", +] + [[package]] name = "digest" version = "0.10.7" @@ -201,7 +317,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", ] [[package]] @@ -210,6 +342,43 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" + [[package]] name = "generic-array" version = "0.14.7" @@ -218,6 +387,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -231,6 +401,17 @@ dependencies = [ "wasi", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "half" version = "2.4.0" @@ -259,6 +440,24 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "hybrid-array" version = "0.2.0-rc.8" @@ -303,6 +502,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "keccak" version = "0.1.5" @@ -375,6 +588,93 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "p192" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b0533bc6c238f2669aab8db75ae52879dc74e88d6bd3685bd4022a00fa85cd2" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sec1", +] + +[[package]] +name = "p224" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30c06436d66652bc2f01ade021592c80a2aad401570a18aa18b82e440d2b9aa1" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core", + "sha2", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "platforms" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" + [[package]] name = "plotters" version = "0.3.5" @@ -409,6 +709,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -506,6 +815,25 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "ryu" version = "1.0.17" @@ -521,6 +849,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "serde" version = "1.0.197" @@ -552,6 +900,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha3" version = "0.10.8" @@ -562,6 +921,54 @@ dependencies = [ "keccak", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "sm2" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b22092ef242a118f03ee41dc46b2720c0ca076f544116dbc915cacf532cfaa" +dependencies = [ + "elliptic-curve", + "primeorder", + "rfc6979", + "signature", + "sm3", +] + +[[package]] +name = "sm3" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebb9a3b702d0a7e33bc4d85a14456633d2b165c2ad839c5fd9a8417c1ab15860" +dependencies = [ + "digest", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "2.0.52" @@ -778,8 +1185,34 @@ version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +[[package]] +name = "x25519-dalek" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + [[package]] name = "zeroize" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index c6c8220..bd8b8df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = [ + "dhkem", "ml-kem", ] diff --git a/dhkem/Cargo.toml b/dhkem/Cargo.toml new file mode 100644 index 0000000..f771e8c --- /dev/null +++ b/dhkem/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "dhkem" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +kem = "0.3.0-pre.0" +rand_core = "0.6.4" +x25519 = { version = "2.0.1", package = "x25519-dalek", optional = true } +elliptic-curve = { version = "0.13.8", optional = true } +bign256 = { version = "0.13.1", optional = true } +k256 = { version = "0.13.3", optional = true } +p192 = { version = "0.13.0", optional = true } +p224 = { version = "0.13.2", optional = true } +p256 = { version = "0.13.2", optional = true } +p384 = { version = "0.13.0", optional = true } +p521 = { version = "0.13.3", optional = true } +sm2 = { version = "0.13.3", optional = true } + +[features] +arithmetic = ["dep:elliptic-curve", "elliptic-curve/ecdh"] +x25519 = ["dep:x25519", "x25519/reusable_secrets"] +bign256 = ["dep:bign256", "arithmetic"] +k256 = ["dep:k256", "arithmetic"] +p192 = ["dep:p192", "arithmetic"] +p224 = ["dep:p224", "arithmetic"] +p256 = ["dep:p256", "arithmetic"] +p384 = ["dep:p384", "arithmetic"] +p521 = ["dep:p521", "arithmetic"] +sm2 = ["dep:sm2", "arithmetic"] diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs new file mode 100644 index 0000000..d0b23bf --- /dev/null +++ b/dhkem/src/arithmetic.rs @@ -0,0 +1,64 @@ +use crate::{DhKem, DhKemProxy}; +use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret}; +use elliptic_curve::{CurveArithmetic, PublicKey}; +use kem::{Decapsulate, Encapsulate}; +use rand_core::CryptoRngCore; +use std::marker::PhantomData; + +pub struct ArithmeticKem(PhantomData); + +impl Encapsulate>, DhKemProxy>> + for DhKemProxy> +where + C: CurveArithmetic, +{ + type Error = (); + + fn encapsulate( + &self, + rng: &mut impl CryptoRngCore, + ) -> Result<(DhKemProxy>, DhKemProxy>), Self::Error> { + // ECDH encapsulation involves creating a new ephemeral key pair and then doing DH + let sk = EphemeralSecret::random(rng); + let pk = sk.public_key(); + let ss = sk.diffie_hellman(&self.0); + + Ok((DhKemProxy(pk), DhKemProxy(ss))) + } +} + +impl Decapsulate>, DhKemProxy>> + for DhKemProxy> +where + C: CurveArithmetic, +{ + type Error = (); + + fn decapsulate( + &self, + encapsulated_key: &DhKemProxy>, + ) -> Result>, Self::Error> { + let ss = self.0.diffie_hellman(&encapsulated_key.0); + + Ok(DhKemProxy(ss)) + } +} + +impl DhKem for ArithmeticKem +where + C: CurveArithmetic, +{ + type DecapsulatingKey = DhKemProxy>; + type EncapsulatingKey = DhKemProxy>; + type EncapsulatedKey = DhKemProxy>; + type SharedSecret = DhKemProxy>; + + fn random_keypair( + rng: &mut impl CryptoRngCore, + ) -> (Self::DecapsulatingKey, Self::EncapsulatingKey) { + let sk = EphemeralSecret::random(rng); + let pk = PublicKey::from(&sk); + + (DhKemProxy(sk), DhKemProxy(pk)) + } +} diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs new file mode 100644 index 0000000..f294cfe --- /dev/null +++ b/dhkem/src/lib.rs @@ -0,0 +1,43 @@ +use kem::{Decapsulate, Encapsulate}; +use rand_core::CryptoRngCore; + +pub struct DhKemProxy(X); + +pub trait DhKem { + type DecapsulatingKey: Decapsulate; + type EncapsulatingKey: Encapsulate; + type EncapsulatedKey; + type SharedSecret; + + fn random_keypair( + rng: &mut impl CryptoRngCore, + ) -> (Self::DecapsulatingKey, Self::EncapsulatingKey); +} + +#[cfg(feature = "arithmetic")] +mod arithmetic; + +#[cfg(feature = "x25519")] +mod x25519_kem; +#[cfg(feature = "x25519")] +pub use x25519_kem::X25519; + +#[cfg(feature = "bign256")] +pub type BignP256 = arithmetic::ArithmeticKem; +#[cfg(feature = "k256")] +pub type Secp256k1 = arithmetic::ArithmeticKem; +#[cfg(feature = "p192")] +pub type NistP192 = arithmetic::ArithmeticKem; +#[cfg(feature = "p224")] +pub type NistP224 = arithmetic::ArithmeticKem; +#[cfg(feature = "p256")] +pub type NistP256 = arithmetic::ArithmeticKem; +// include an additional alias Secp256r1 = NistP256 +#[cfg(feature = "p256")] +pub type Secp256r1 = arithmetic::ArithmeticKem; +#[cfg(feature = "p384")] +pub type NistP384 = arithmetic::ArithmeticKem; +#[cfg(feature = "p521")] +pub type NistP521 = arithmetic::ArithmeticKem; +#[cfg(feature = "sm2")] +pub type Sm2 = arithmetic::ArithmeticKem; diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs new file mode 100644 index 0000000..54a3ff5 --- /dev/null +++ b/dhkem/src/x25519_kem.rs @@ -0,0 +1,51 @@ +use crate::{DhKem, DhKemProxy}; +use kem::{Decapsulate, Encapsulate}; +use rand_core::CryptoRngCore; +use x25519::{PublicKey, ReusableSecret, SharedSecret}; + +pub struct X25519; + +impl Encapsulate, DhKemProxy> for DhKemProxy { + type Error = (); + + fn encapsulate( + &self, + rng: &mut impl CryptoRngCore, + ) -> Result<(DhKemProxy, DhKemProxy), Self::Error> { + // ECDH encapsulation involves creating a new ephemeral key pair and then doing DH + let sk = ReusableSecret::random_from_rng(rng); + let pk = PublicKey::from(&sk); + let ss = sk.diffie_hellman(&self.0); + + Ok((DhKemProxy(pk), DhKemProxy(ss))) + } +} + +impl Decapsulate, DhKemProxy> for DhKemProxy { + type Error = (); + + fn decapsulate( + &self, + encapsulated_key: &DhKemProxy, + ) -> Result, Self::Error> { + let ss = self.0.diffie_hellman(&encapsulated_key.0); + + Ok(DhKemProxy(ss)) + } +} + +impl DhKem for X25519 { + type DecapsulatingKey = DhKemProxy; + type EncapsulatingKey = DhKemProxy; + type EncapsulatedKey = DhKemProxy; + type SharedSecret = DhKemProxy; + + fn random_keypair( + rng: &mut impl CryptoRngCore, + ) -> (Self::DecapsulatingKey, Self::EncapsulatingKey) { + let sk = ReusableSecret::random_from_rng(rng); + let pk = PublicKey::from(&sk); + + (DhKemProxy(sk), DhKemProxy(pk)) + } +} From 6bc3f0cc70814c9cff93b48bf35b9597b51a2b1f Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 13:25:54 -0400 Subject: [PATCH 02/14] add fields to Cargo.toml --- dhkem/Cargo.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dhkem/Cargo.toml b/dhkem/Cargo.toml index f771e8c..98dae97 100644 --- a/dhkem/Cargo.toml +++ b/dhkem/Cargo.toml @@ -1,7 +1,15 @@ [package] name = "dhkem" +description = """ +Key Encapsulation Mechanism (KEM) adapters for Elliptic Curve Diffie Hellman (ECDH) protocols +""" version = "0.1.0" edition = "2021" +rust-version = "1.74" +license = "Apache-2.0 OR MIT" +repository = "https://github.com/RustCrypto/KEMs/tree/master/dhkem" +categories = ["cryptography"] +keywords = ["crypto", "ecdh", "ecc"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 614353f376ec314f0fd06f09da7ede5964af44eb Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 13:59:45 -0400 Subject: [PATCH 03/14] add simple keygen -> encapsulate -> decapsulate test --- Cargo.lock | 1 + dhkem/Cargo.toml | 3 ++ dhkem/src/arithmetic.rs | 10 +++++++ dhkem/src/lib.rs | 12 ++++++++ dhkem/src/tests.rs | 66 +++++++++++++++++++++++++++++++++++++++++ dhkem/src/x25519_kem.rs | 7 +++++ 6 files changed, 99 insertions(+) create mode 100644 dhkem/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 6fb5cf6..d1caff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,6 +305,7 @@ dependencies = [ "p256", "p384", "p521", + "rand", "rand_core", "sm2", "x25519-dalek", diff --git a/dhkem/Cargo.toml b/dhkem/Cargo.toml index 98dae97..4007ae4 100644 --- a/dhkem/Cargo.toml +++ b/dhkem/Cargo.toml @@ -38,3 +38,6 @@ p256 = ["dep:p256", "arithmetic"] p384 = ["dep:p384", "arithmetic"] p521 = ["dep:p521", "arithmetic"] sm2 = ["dep:sm2", "arithmetic"] + +[dev-dependencies] +rand = "0.8.5" diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index d0b23bf..229e30e 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -62,3 +62,13 @@ where (DhKemProxy(sk), DhKemProxy(pk)) } } + +#[cfg(test)] +impl crate::SecretBytes for DhKemProxy> +where + C: CurveArithmetic, +{ + fn as_slice(&self) -> &[u8] { + self.0.raw_secret_bytes().as_slice() + } +} diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index f294cfe..8fd5b6f 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -3,12 +3,21 @@ use rand_core::CryptoRngCore; pub struct DhKemProxy(X); +#[cfg(test)] +pub trait SecretBytes { + fn as_slice(&self) -> &[u8]; +} + pub trait DhKem { type DecapsulatingKey: Decapsulate; type EncapsulatingKey: Encapsulate; type EncapsulatedKey; + #[cfg(not(test))] type SharedSecret; + #[cfg(test)] + type SharedSecret: SecretBytes; + fn random_keypair( rng: &mut impl CryptoRngCore, ) -> (Self::DecapsulatingKey, Self::EncapsulatingKey); @@ -41,3 +50,6 @@ pub type NistP384 = arithmetic::ArithmeticKem; pub type NistP521 = arithmetic::ArithmeticKem; #[cfg(feature = "sm2")] pub type Sm2 = arithmetic::ArithmeticKem; + +#[cfg(test)] +mod tests; diff --git a/dhkem/src/tests.rs b/dhkem/src/tests.rs new file mode 100644 index 0000000..7ac3514 --- /dev/null +++ b/dhkem/src/tests.rs @@ -0,0 +1,66 @@ +use crate::{DhKem, SecretBytes}; +use kem::{Decapsulate, Encapsulate}; +use rand::thread_rng; + +fn test_kem() { + let mut rng = thread_rng(); + let (sk, pk) = K::random_keypair(&mut rng); + let (ek, ss1) = pk.encapsulate(&mut rng).expect("never fails"); + let ss2 = sk.decapsulate(&ek).expect("never fails"); + + assert_eq!(ss1.as_slice(), ss2.as_slice()); +} + +#[cfg(feature = "x25519")] +#[test] +fn test_x25519() { + test_kem::(); +} + +#[cfg(feature = "bign256")] +#[test] +fn test_bign256() { + test_kem::(); +} + +#[cfg(feature = "k256")] +#[test] +fn test_k256() { + test_kem::(); +} + +#[cfg(feature = "p192")] +#[test] +fn test_p192() { + test_kem::(); +} + +#[cfg(feature = "p224")] +#[test] +fn test_p224() { + test_kem::(); +} + +#[cfg(feature = "p256")] +#[test] +fn test_p256() { + test_kem::(); +} + +#[cfg(feature = "p384")] +#[test] +fn test_p384() { + test_kem::(); +} + +#[cfg(feature = "p521")] +#[test] +fn test_p521() { + test_kem::(); +} + +#[cfg(feature = "sm2")] +#[test] +fn test_sm2() { + test_kem::(); +} diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index 54a3ff5..472c651 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -49,3 +49,10 @@ impl DhKem for X25519 { (DhKemProxy(sk), DhKemProxy(pk)) } } + +#[cfg(test)] +impl crate::SecretBytes for DhKemProxy { + fn as_slice(&self) -> &[u8] { + self.0.as_bytes().as_slice() + } +} From 5af04377dea4eee2152b8c2dbaa710456659077a Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 14:14:55 -0400 Subject: [PATCH 04/14] make newtypes for the different kinds of wrappers needed --- dhkem/src/arithmetic.rs | 34 +++++++++++++++++----------------- dhkem/src/lib.rs | 9 ++++++++- dhkem/src/x25519_kem.rs | 34 +++++++++++++++++++--------------- 3 files changed, 44 insertions(+), 33 deletions(-) diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index 229e30e..ef24cb9 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -1,5 +1,5 @@ -use crate::{DhKem, DhKemProxy}; -use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret}; +use crate::{Decapsulator, DhKem, EncapsulatedKey, Encapsulator, SharedSecret}; +use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret as EcdhSecret}; use elliptic_curve::{CurveArithmetic, PublicKey}; use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; @@ -7,8 +7,8 @@ use std::marker::PhantomData; pub struct ArithmeticKem(PhantomData); -impl Encapsulate>, DhKemProxy>> - for DhKemProxy> +impl Encapsulate>, SharedSecret>> + for Encapsulator> where C: CurveArithmetic, { @@ -17,18 +17,18 @@ where fn encapsulate( &self, rng: &mut impl CryptoRngCore, - ) -> Result<(DhKemProxy>, DhKemProxy>), Self::Error> { + ) -> Result<(EncapsulatedKey>, SharedSecret>), Self::Error> { // ECDH encapsulation involves creating a new ephemeral key pair and then doing DH let sk = EphemeralSecret::random(rng); let pk = sk.public_key(); let ss = sk.diffie_hellman(&self.0); - Ok((DhKemProxy(pk), DhKemProxy(ss))) + Ok((EncapsulatedKey(pk), SharedSecret(ss))) } } -impl Decapsulate>, DhKemProxy>> - for DhKemProxy> +impl Decapsulate>, SharedSecret>> + for Decapsulator> where C: CurveArithmetic, { @@ -36,11 +36,11 @@ where fn decapsulate( &self, - encapsulated_key: &DhKemProxy>, - ) -> Result>, Self::Error> { + encapsulated_key: &EncapsulatedKey>, + ) -> Result>, Self::Error> { let ss = self.0.diffie_hellman(&encapsulated_key.0); - Ok(DhKemProxy(ss)) + Ok(SharedSecret(ss)) } } @@ -48,10 +48,10 @@ impl DhKem for ArithmeticKem where C: CurveArithmetic, { - type DecapsulatingKey = DhKemProxy>; - type EncapsulatingKey = DhKemProxy>; - type EncapsulatedKey = DhKemProxy>; - type SharedSecret = DhKemProxy>; + type DecapsulatingKey = Decapsulator>; + type EncapsulatingKey = Encapsulator>; + type EncapsulatedKey = EncapsulatedKey>; + type SharedSecret = SharedSecret>; fn random_keypair( rng: &mut impl CryptoRngCore, @@ -59,12 +59,12 @@ where let sk = EphemeralSecret::random(rng); let pk = PublicKey::from(&sk); - (DhKemProxy(sk), DhKemProxy(pk)) + (Decapsulator(sk), Encapsulator(pk)) } } #[cfg(test)] -impl crate::SecretBytes for DhKemProxy> +impl crate::SecretBytes for SharedSecret> where C: CurveArithmetic, { diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index 8fd5b6f..d5659e4 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -1,7 +1,14 @@ use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; -pub struct DhKemProxy(X); +/// Newtype for a piece of data that may be encapsulated +pub struct Encapsulator(X); +/// Newtype for a piece of data that may be decapsulated +pub struct Decapsulator(X); +/// Newtype for a shared secret +pub struct SharedSecret(X); +/// Newtype for an encapsulated key +pub struct EncapsulatedKey(X); #[cfg(test)] pub trait SecretBytes { diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index 472c651..665c5d6 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -1,44 +1,48 @@ -use crate::{DhKem, DhKemProxy}; +use crate::{Decapsulator, DhKem, EncapsulatedKey, Encapsulator, SharedSecret}; use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; -use x25519::{PublicKey, ReusableSecret, SharedSecret}; +use x25519::{PublicKey, ReusableSecret, SharedSecret as X25519Secret}; pub struct X25519; -impl Encapsulate, DhKemProxy> for DhKemProxy { +impl Encapsulate, SharedSecret> + for Encapsulator +{ type Error = (); fn encapsulate( &self, rng: &mut impl CryptoRngCore, - ) -> Result<(DhKemProxy, DhKemProxy), Self::Error> { + ) -> Result<(EncapsulatedKey, SharedSecret), Self::Error> { // ECDH encapsulation involves creating a new ephemeral key pair and then doing DH let sk = ReusableSecret::random_from_rng(rng); let pk = PublicKey::from(&sk); let ss = sk.diffie_hellman(&self.0); - Ok((DhKemProxy(pk), DhKemProxy(ss))) + Ok((EncapsulatedKey(pk), SharedSecret(ss))) } } -impl Decapsulate, DhKemProxy> for DhKemProxy { +impl Decapsulate, SharedSecret> + for Decapsulator +{ type Error = (); fn decapsulate( &self, - encapsulated_key: &DhKemProxy, - ) -> Result, Self::Error> { + encapsulated_key: &EncapsulatedKey, + ) -> Result, Self::Error> { let ss = self.0.diffie_hellman(&encapsulated_key.0); - Ok(DhKemProxy(ss)) + Ok(SharedSecret(ss)) } } impl DhKem for X25519 { - type DecapsulatingKey = DhKemProxy; - type EncapsulatingKey = DhKemProxy; - type EncapsulatedKey = DhKemProxy; - type SharedSecret = DhKemProxy; + type DecapsulatingKey = Decapsulator; + type EncapsulatingKey = Encapsulator; + type EncapsulatedKey = EncapsulatedKey; + type SharedSecret = SharedSecret; fn random_keypair( rng: &mut impl CryptoRngCore, @@ -46,12 +50,12 @@ impl DhKem for X25519 { let sk = ReusableSecret::random_from_rng(rng); let pk = PublicKey::from(&sk); - (DhKemProxy(sk), DhKemProxy(pk)) + (Decapsulator(sk), Encapsulator(pk)) } } #[cfg(test)] -impl crate::SecretBytes for DhKemProxy { +impl crate::SecretBytes for SharedSecret { fn as_slice(&self) -> &[u8] { self.0.as_bytes().as_slice() } From 27f452da62e6d604b21a57c61a16524fc9458325 Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 14:20:59 -0400 Subject: [PATCH 05/14] encapsulated keys and shared secrets can be represented by native type --- dhkem/src/arithmetic.rs | 31 +++++++++++++------------------ dhkem/src/lib.rs | 4 ---- dhkem/src/x25519_kem.rs | 33 +++++++++++++-------------------- 3 files changed, 26 insertions(+), 42 deletions(-) diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index ef24cb9..a639024 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -1,5 +1,5 @@ -use crate::{Decapsulator, DhKem, EncapsulatedKey, Encapsulator, SharedSecret}; -use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret as EcdhSecret}; +use crate::{Decapsulator, DhKem, Encapsulator}; +use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret}; use elliptic_curve::{CurveArithmetic, PublicKey}; use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; @@ -7,8 +7,7 @@ use std::marker::PhantomData; pub struct ArithmeticKem(PhantomData); -impl Encapsulate>, SharedSecret>> - for Encapsulator> +impl Encapsulate, SharedSecret> for Encapsulator> where C: CurveArithmetic, { @@ -17,30 +16,26 @@ where fn encapsulate( &self, rng: &mut impl CryptoRngCore, - ) -> Result<(EncapsulatedKey>, SharedSecret>), Self::Error> { + ) -> Result<(PublicKey, SharedSecret), Self::Error> { // ECDH encapsulation involves creating a new ephemeral key pair and then doing DH let sk = EphemeralSecret::random(rng); let pk = sk.public_key(); let ss = sk.diffie_hellman(&self.0); - Ok((EncapsulatedKey(pk), SharedSecret(ss))) + Ok((pk, ss)) } } -impl Decapsulate>, SharedSecret>> - for Decapsulator> +impl Decapsulate, SharedSecret> for Decapsulator> where C: CurveArithmetic, { type Error = (); - fn decapsulate( - &self, - encapsulated_key: &EncapsulatedKey>, - ) -> Result>, Self::Error> { - let ss = self.0.diffie_hellman(&encapsulated_key.0); + fn decapsulate(&self, encapsulated_key: &PublicKey) -> Result, Self::Error> { + let ss = self.0.diffie_hellman(&encapsulated_key); - Ok(SharedSecret(ss)) + Ok(ss) } } @@ -50,8 +45,8 @@ where { type DecapsulatingKey = Decapsulator>; type EncapsulatingKey = Encapsulator>; - type EncapsulatedKey = EncapsulatedKey>; - type SharedSecret = SharedSecret>; + type EncapsulatedKey = PublicKey; + type SharedSecret = SharedSecret; fn random_keypair( rng: &mut impl CryptoRngCore, @@ -64,11 +59,11 @@ where } #[cfg(test)] -impl crate::SecretBytes for SharedSecret> +impl crate::SecretBytes for SharedSecret where C: CurveArithmetic, { fn as_slice(&self) -> &[u8] { - self.0.raw_secret_bytes().as_slice() + self.raw_secret_bytes().as_slice() } } diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index d5659e4..97b0c54 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -5,10 +5,6 @@ use rand_core::CryptoRngCore; pub struct Encapsulator(X); /// Newtype for a piece of data that may be decapsulated pub struct Decapsulator(X); -/// Newtype for a shared secret -pub struct SharedSecret(X); -/// Newtype for an encapsulated key -pub struct EncapsulatedKey(X); #[cfg(test)] pub trait SecretBytes { diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index 665c5d6..d4ff410 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -1,48 +1,41 @@ -use crate::{Decapsulator, DhKem, EncapsulatedKey, Encapsulator, SharedSecret}; +use crate::{Decapsulator, DhKem, Encapsulator}; use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; -use x25519::{PublicKey, ReusableSecret, SharedSecret as X25519Secret}; +use x25519::{PublicKey, ReusableSecret, SharedSecret}; pub struct X25519; -impl Encapsulate, SharedSecret> - for Encapsulator -{ +impl Encapsulate for Encapsulator { type Error = (); fn encapsulate( &self, rng: &mut impl CryptoRngCore, - ) -> Result<(EncapsulatedKey, SharedSecret), Self::Error> { + ) -> Result<(PublicKey, SharedSecret), Self::Error> { // ECDH encapsulation involves creating a new ephemeral key pair and then doing DH let sk = ReusableSecret::random_from_rng(rng); let pk = PublicKey::from(&sk); let ss = sk.diffie_hellman(&self.0); - Ok((EncapsulatedKey(pk), SharedSecret(ss))) + Ok((pk, ss)) } } -impl Decapsulate, SharedSecret> - for Decapsulator -{ +impl Decapsulate for Decapsulator { type Error = (); - fn decapsulate( - &self, - encapsulated_key: &EncapsulatedKey, - ) -> Result, Self::Error> { - let ss = self.0.diffie_hellman(&encapsulated_key.0); + fn decapsulate(&self, encapsulated_key: &PublicKey) -> Result { + let ss = self.0.diffie_hellman(&encapsulated_key); - Ok(SharedSecret(ss)) + Ok(ss) } } impl DhKem for X25519 { type DecapsulatingKey = Decapsulator; type EncapsulatingKey = Encapsulator; - type EncapsulatedKey = EncapsulatedKey; - type SharedSecret = SharedSecret; + type EncapsulatedKey = PublicKey; + type SharedSecret = SharedSecret; fn random_keypair( rng: &mut impl CryptoRngCore, @@ -55,8 +48,8 @@ impl DhKem for X25519 { } #[cfg(test)] -impl crate::SecretBytes for SharedSecret { +impl crate::SecretBytes for SharedSecret { fn as_slice(&self) -> &[u8] { - self.0.as_bytes().as_slice() + self.as_bytes().as_slice() } } From b2d8b9fa230098f59bd57a0dcaf488cffb3ca135 Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 14:23:19 -0400 Subject: [PATCH 06/14] add workflow for dhkem --- .github/workflows/dhkem.yml | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .github/workflows/dhkem.yml diff --git a/.github/workflows/dhkem.yml b/.github/workflows/dhkem.yml new file mode 100644 index 0000000..3c8a58d --- /dev/null +++ b/.github/workflows/dhkem.yml @@ -0,0 +1,68 @@ +name: dhkem + +on: + pull_request: + paths: + - ".github/workflows/dhkem.yml" + - "dhkem/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: dhkem + +env: + RUSTFLAGS: "-Dwarnings" + CARGO_INCREMENTAL: 0 + +jobs: + set-msrv: + uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master + with: + msrv: 1.74.0 + + minimal-versions: + # temporarily disabled as requested by Tony (https://github.com/RustCrypto/KEMs/pull/15#pullrequestreview-2006378802) + if: false + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} + + test: + needs: set-msrv + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - ${{needs.set-msrv.outputs.msrv}} + - stable + steps: + - uses: actions/checkout@v4 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features + + cross: + needs: set-msrv + strategy: + matrix: + include: + - target: powerpc-unknown-linux-gnu + rust: ${{needs.set-msrv.outputs.msrv}} + - target: powerpc-unknown-linux-gnu + rust: stable + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - uses: RustCrypto/actions/cross-install@master + - run: cross test --release --target ${{ matrix.target }} --all-features From 34ef25565748482a43eb1dc553231b5415a0d721 Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 14:53:04 -0400 Subject: [PATCH 07/14] add documentation around the DhKem trait and the crate itself --- dhkem/src/lib.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index 97b0c54..2d50572 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -1,3 +1,14 @@ +//! # Diffie-Hellman (DH) based Key Encapsulation Mechanisms (KEM) +//! +//! This crate provides a KEM interface for DH protocols as specified in +//! [RFC9180](https://datatracker.ietf.org/doc/html/rfc9180#name-dh-based-kem-dhkem) +//! without the shared secret extraction process. In particular, `Encaps(pk)` in the +//! RFC returns the encapsulated key and an extracted shared secret, while our +//! implementation leaves the extraction process up to the user. This type of KEM +//! construction is currently being used in HPKE, as per the RFC, and in the current +//! draft of the [TLS KEM +//! combiner](https://datatracker.ietf.org/doc/html/draft-ietf-tls-hybrid-design-10). + use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; @@ -11,23 +22,36 @@ pub trait SecretBytes { fn as_slice(&self) -> &[u8]; } +/// This is a trait that all KEM models should implement, and should probably be +/// promoted to the kem crate itself. It specifies the types of encapsulating and +/// decapsulating keys created by key generation, the shared secret type, and the +/// encapsulated key type pub trait DhKem { + /// The type that will implement [`Decapsulate`] type DecapsulatingKey: Decapsulate; + + /// The type that will implement [`Encapsulate`] type EncapsulatingKey: Encapsulate; + + /// The type of the encapsulated key type EncapsulatedKey; + #[cfg(not(test))] + /// The type of the shared secret type SharedSecret; #[cfg(test)] type SharedSecret: SecretBytes; + /// Generates a new (decapsulating key, encapsulating key) keypair for the KEM + /// model fn random_keypair( rng: &mut impl CryptoRngCore, ) -> (Self::DecapsulatingKey, Self::EncapsulatingKey); } #[cfg(feature = "arithmetic")] -mod arithmetic; +pub mod arithmetic; #[cfg(feature = "x25519")] mod x25519_kem; From 8d0de42a774590dc10e88f54e29eac1341d59e7f Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 15:42:16 -0400 Subject: [PATCH 08/14] add some generally useful functions to the wrapper --- dhkem/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index 2d50572..66f42fb 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -13,10 +13,50 @@ use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; /// Newtype for a piece of data that may be encapsulated +#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] pub struct Encapsulator(X); /// Newtype for a piece of data that may be decapsulated +#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] pub struct Decapsulator(X); +impl AsRef for Encapsulator { + fn as_ref(&self) -> &X { + &self.0 + } +} + +impl From for Encapsulator { + fn from(value: X) -> Self { + Self(value) + } +} + +impl AsRef for Decapsulator { + fn as_ref(&self) -> &X { + &self.0 + } +} + +impl From for Decapsulator { + fn from(value: X) -> Self { + Self(value) + } +} + +impl Encapsulator { + /// Consumes `self` and returns the wrapped value + pub fn into_inner(self) -> X { + self.0 + } +} + +impl Decapsulator { + /// Consumes `self` and returns the wrapped value + pub fn into_inner(self) -> X { + self.0 + } +} + #[cfg(test)] pub trait SecretBytes { fn as_slice(&self) -> &[u8]; From 51e0650097b220b14297d49bed18f2d7a8674847 Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 15:53:29 -0400 Subject: [PATCH 09/14] allow dead test_kem --- dhkem/src/tests.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dhkem/src/tests.rs b/dhkem/src/tests.rs index 7ac3514..08d4067 100644 --- a/dhkem/src/tests.rs +++ b/dhkem/src/tests.rs @@ -2,6 +2,9 @@ use crate::{DhKem, SecretBytes}; use kem::{Decapsulate, Encapsulate}; use rand::thread_rng; +// we need this because if the crate is compiled with no features this function never +// gets used +#[allow(dead_code)] fn test_kem() { let mut rng = thread_rng(); let (sk, pk) = K::random_keypair(&mut rng); From e3e0b14e95cb133acf3a4caefe99c82169d3122b Mon Sep 17 00:00:00 2001 From: Will Song Date: Thu, 18 Apr 2024 22:40:25 -0400 Subject: [PATCH 10/14] move SecretBytes trait to tests --- dhkem/src/arithmetic.rs | 10 ---------- dhkem/src/lib.rs | 9 --------- dhkem/src/tests.rs | 28 ++++++++++++++++++++++++++-- dhkem/src/x25519_kem.rs | 7 ------- 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index a639024..ec6c727 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -57,13 +57,3 @@ where (Decapsulator(sk), Encapsulator(pk)) } } - -#[cfg(test)] -impl crate::SecretBytes for SharedSecret -where - C: CurveArithmetic, -{ - fn as_slice(&self) -> &[u8] { - self.raw_secret_bytes().as_slice() - } -} diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index 66f42fb..9784026 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -57,11 +57,6 @@ impl Decapsulator { } } -#[cfg(test)] -pub trait SecretBytes { - fn as_slice(&self) -> &[u8]; -} - /// This is a trait that all KEM models should implement, and should probably be /// promoted to the kem crate itself. It specifies the types of encapsulating and /// decapsulating keys created by key generation, the shared secret type, and the @@ -76,13 +71,9 @@ pub trait DhKem { /// The type of the encapsulated key type EncapsulatedKey; - #[cfg(not(test))] /// The type of the shared secret type SharedSecret; - #[cfg(test)] - type SharedSecret: SecretBytes; - /// Generates a new (decapsulating key, encapsulating key) keypair for the KEM /// model fn random_keypair( diff --git a/dhkem/src/tests.rs b/dhkem/src/tests.rs index 08d4067..659d8e4 100644 --- a/dhkem/src/tests.rs +++ b/dhkem/src/tests.rs @@ -1,11 +1,35 @@ -use crate::{DhKem, SecretBytes}; +use crate::DhKem; use kem::{Decapsulate, Encapsulate}; use rand::thread_rng; +trait SecretBytes { + fn as_slice(&self) -> &[u8]; +} + +#[cfg(feature = "x25519")] +impl SecretBytes for x25519::SharedSecret { + fn as_slice(&self) -> &[u8] { + self.as_bytes().as_slice() + } +} + +#[cfg(feature = "arithmetic")] +impl SecretBytes for elliptic_curve::ecdh::SharedSecret +where + C: elliptic_curve::CurveArithmetic, +{ + fn as_slice(&self) -> &[u8] { + self.raw_secret_bytes().as_slice() + } +} + // we need this because if the crate is compiled with no features this function never // gets used #[allow(dead_code)] -fn test_kem() { +fn test_kem() +where + ::SharedSecret: SecretBytes, +{ let mut rng = thread_rng(); let (sk, pk) = K::random_keypair(&mut rng); let (ek, ss1) = pk.encapsulate(&mut rng).expect("never fails"); diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index d4ff410..00fe307 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -46,10 +46,3 @@ impl DhKem for X25519 { (Decapsulator(sk), Encapsulator(pk)) } } - -#[cfg(test)] -impl crate::SecretBytes for SharedSecret { - fn as_slice(&self) -> &[u8] { - self.as_bytes().as_slice() - } -} From 373e0f40b8cc8d48b37823428aa2f92618558060 Mon Sep 17 00:00:00 2001 From: Will Song Date: Fri, 19 Apr 2024 13:51:11 -0400 Subject: [PATCH 11/14] rename Encapsulator/Decapsulator to Dh* to be more usage specific and add docsrs support --- Cargo.lock | 1 + dhkem/Cargo.toml | 7 +++++++ dhkem/src/arithmetic.rs | 12 ++++++------ dhkem/src/lib.rs | 40 ++++++++++++++++++++++++++++++++-------- dhkem/src/x25519_kem.rs | 12 ++++++------ 5 files changed, 52 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1caff5..e149b4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,6 +309,7 @@ dependencies = [ "rand_core", "sm2", "x25519-dalek", + "zeroize", ] [[package]] diff --git a/dhkem/Cargo.toml b/dhkem/Cargo.toml index 4007ae4..91ac0a2 100644 --- a/dhkem/Cargo.toml +++ b/dhkem/Cargo.toml @@ -26,8 +26,10 @@ p256 = { version = "0.13.2", optional = true } p384 = { version = "0.13.0", optional = true } p521 = { version = "0.13.3", optional = true } sm2 = { version = "0.13.3", optional = true } +zeroize = { version = "1.7.0", optional = true } [features] +default = ["zeroize"] arithmetic = ["dep:elliptic-curve", "elliptic-curve/ecdh"] x25519 = ["dep:x25519", "x25519/reusable_secrets"] bign256 = ["dep:bign256", "arithmetic"] @@ -38,6 +40,11 @@ p256 = ["dep:p256", "arithmetic"] p384 = ["dep:p384", "arithmetic"] p521 = ["dep:p521", "arithmetic"] sm2 = ["dep:sm2", "arithmetic"] +zeroize = ["dep:zeroize"] [dev-dependencies] rand = "0.8.5" + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index ec6c727..9678185 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -1,4 +1,4 @@ -use crate::{Decapsulator, DhKem, Encapsulator}; +use crate::{DhDecapsulator, DhKem, DhEncapsulator}; use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret}; use elliptic_curve::{CurveArithmetic, PublicKey}; use kem::{Decapsulate, Encapsulate}; @@ -7,7 +7,7 @@ use std::marker::PhantomData; pub struct ArithmeticKem(PhantomData); -impl Encapsulate, SharedSecret> for Encapsulator> +impl Encapsulate, SharedSecret> for DhEncapsulator> where C: CurveArithmetic, { @@ -26,7 +26,7 @@ where } } -impl Decapsulate, SharedSecret> for Decapsulator> +impl Decapsulate, SharedSecret> for DhDecapsulator> where C: CurveArithmetic, { @@ -43,8 +43,8 @@ impl DhKem for ArithmeticKem where C: CurveArithmetic, { - type DecapsulatingKey = Decapsulator>; - type EncapsulatingKey = Encapsulator>; + type DecapsulatingKey = DhDecapsulator>; + type EncapsulatingKey = DhEncapsulator>; type EncapsulatedKey = PublicKey; type SharedSecret = SharedSecret; @@ -54,6 +54,6 @@ where let sk = EphemeralSecret::random(rng); let pk = PublicKey::from(&sk); - (Decapsulator(sk), Encapsulator(pk)) + (DhDecapsulator(sk), DhEncapsulator(pk)) } } diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index 9784026..e207fef 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(docsrs, feature(doc_auto_cfg))] + //! # Diffie-Hellman (DH) based Key Encapsulation Mechanisms (KEM) //! //! This crate provides a KEM interface for DH protocols as specified in @@ -11,52 +13,74 @@ use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; +#[cfg(feature = "zeroize")] +use zeroize::{Zeroize, ZeroizeOnDrop}; /// Newtype for a piece of data that may be encapsulated #[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] -pub struct Encapsulator(X); +pub struct DhEncapsulator(X); /// Newtype for a piece of data that may be decapsulated #[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Default)] -pub struct Decapsulator(X); +pub struct DhDecapsulator(X); -impl AsRef for Encapsulator { +impl AsRef for DhEncapsulator { fn as_ref(&self) -> &X { &self.0 } } -impl From for Encapsulator { +impl From for DhEncapsulator { fn from(value: X) -> Self { Self(value) } } -impl AsRef for Decapsulator { +impl AsRef for DhDecapsulator { fn as_ref(&self) -> &X { &self.0 } } -impl From for Decapsulator { +impl From for DhDecapsulator { fn from(value: X) -> Self { Self(value) } } -impl Encapsulator { +impl DhEncapsulator { /// Consumes `self` and returns the wrapped value pub fn into_inner(self) -> X { self.0 } } -impl Decapsulator { +impl DhDecapsulator { /// Consumes `self` and returns the wrapped value pub fn into_inner(self) -> X { self.0 } } +#[cfg(feature = "zeroize")] +impl Zeroize for DhEncapsulator { + fn zeroize(&mut self) { + self.0.zeroize() + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for DhDecapsulator { + fn zeroize(&mut self) { + self.0.zeroize() + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for DhEncapsulator {} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for DhDecapsulator {} + /// This is a trait that all KEM models should implement, and should probably be /// promoted to the kem crate itself. It specifies the types of encapsulating and /// decapsulating keys created by key generation, the shared secret type, and the diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index 00fe307..a7ab6b9 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -1,11 +1,11 @@ -use crate::{Decapsulator, DhKem, Encapsulator}; +use crate::{DhDecapsulator, DhKem, DhEncapsulator}; use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; use x25519::{PublicKey, ReusableSecret, SharedSecret}; pub struct X25519; -impl Encapsulate for Encapsulator { +impl Encapsulate for DhEncapsulator { type Error = (); fn encapsulate( @@ -21,7 +21,7 @@ impl Encapsulate for Encapsulator { } } -impl Decapsulate for Decapsulator { +impl Decapsulate for DhDecapsulator { type Error = (); fn decapsulate(&self, encapsulated_key: &PublicKey) -> Result { @@ -32,8 +32,8 @@ impl Decapsulate for Decapsulator { } impl DhKem for X25519 { - type DecapsulatingKey = Decapsulator; - type EncapsulatingKey = Encapsulator; + type DecapsulatingKey = DhDecapsulator; + type EncapsulatingKey = DhEncapsulator; type EncapsulatedKey = PublicKey; type SharedSecret = SharedSecret; @@ -43,6 +43,6 @@ impl DhKem for X25519 { let sk = ReusableSecret::random_from_rng(rng); let pk = PublicKey::from(&sk); - (Decapsulator(sk), Encapsulator(pk)) + (DhDecapsulator(sk), DhEncapsulator(pk)) } } From 3ae52196f1cd1cd424eca29b290260c11d469fc1 Mon Sep 17 00:00:00 2001 From: Will Song Date: Fri, 19 Apr 2024 13:52:16 -0400 Subject: [PATCH 12/14] cargo fmt --- dhkem/src/arithmetic.rs | 2 +- dhkem/src/x25519_kem.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index 9678185..aa87b0e 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -1,4 +1,4 @@ -use crate::{DhDecapsulator, DhKem, DhEncapsulator}; +use crate::{DhDecapsulator, DhEncapsulator, DhKem}; use elliptic_curve::ecdh::{EphemeralSecret, SharedSecret}; use elliptic_curve::{CurveArithmetic, PublicKey}; use kem::{Decapsulate, Encapsulate}; diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index a7ab6b9..9246155 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -1,4 +1,4 @@ -use crate::{DhDecapsulator, DhKem, DhEncapsulator}; +use crate::{DhDecapsulator, DhEncapsulator, DhKem}; use kem::{Decapsulate, Encapsulate}; use rand_core::CryptoRngCore; use x25519::{PublicKey, ReusableSecret, SharedSecret}; From adf4eb737f38278e14084ae1180a2e065da081d9 Mon Sep 17 00:00:00 2001 From: Will Song Date: Fri, 19 Apr 2024 16:01:20 -0400 Subject: [PATCH 13/14] add test vector from hpke --- Cargo.lock | 3 + dhkem/Cargo.toml | 3 + dhkem/src/hpke_p256_test.rs | 107 ++++++++++++++++++++++++++++++++++++ dhkem/src/lib.rs | 4 ++ 4 files changed, 117 insertions(+) create mode 100644 dhkem/src/hpke_p256_test.rs diff --git a/Cargo.lock b/Cargo.lock index e149b4b..5b9d018 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,6 +298,8 @@ version = "0.1.0" dependencies = [ "bign256", "elliptic-curve", + "hex-literal", + "hkdf", "k256", "kem", "p192", @@ -307,6 +309,7 @@ dependencies = [ "p521", "rand", "rand_core", + "sha2", "sm2", "x25519-dalek", "zeroize", diff --git a/dhkem/Cargo.toml b/dhkem/Cargo.toml index 91ac0a2..c040cc0 100644 --- a/dhkem/Cargo.toml +++ b/dhkem/Cargo.toml @@ -44,6 +44,9 @@ zeroize = ["dep:zeroize"] [dev-dependencies] rand = "0.8.5" +hex-literal = "0.4.1" +hkdf = "0.12.4" +sha2 = "0.10.8" [package.metadata.docs.rs] all-features = true diff --git a/dhkem/src/hpke_p256_test.rs b/dhkem/src/hpke_p256_test.rs new file mode 100644 index 0000000..a1cb151 --- /dev/null +++ b/dhkem/src/hpke_p256_test.rs @@ -0,0 +1,107 @@ +use crate::{DhKem, NistP256}; +use elliptic_curve::sec1::ToEncodedPoint; +use hex_literal::hex; +use hkdf::Hkdf; +use kem::{Decapsulate, Encapsulate}; +use rand_core::{CryptoRng, RngCore}; +use sha2::Sha256; + +/// Constant RNG for testing purposes only. +struct ConstantRng<'a>(pub &'a [u8]); + +impl<'a> RngCore for ConstantRng<'a> { + fn next_u32(&mut self) -> u32 { + let (head, tail) = self.0.split_at(4); + self.0 = tail; + u32::from_be_bytes(head.try_into().unwrap()) + } + + fn next_u64(&mut self) -> u64 { + let (head, tail) = self.0.split_at(8); + self.0 = tail; + u64::from_be_bytes(head.try_into().unwrap()) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + let (hd, tl) = self.0.split_at(dest.len()); + dest.copy_from_slice(hd); + self.0 = tl; + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + if dest.len() > self.0.len() { + return Err(rand_core::Error::new("not enough bytes")); + } + let (hd, tl) = self.0.split_at(dest.len()); + dest.copy_from_slice(hd); + self.0 = tl; + Ok(()) + } +} + +// this is only ever ok for testing +impl CryptoRng for ConstantRng<'_> {} + +fn labeled_extract(salt: &[u8], label: &[u8], ikm: &[u8]) -> Vec { + let labeled_ikm = [b"HPKE-v1".as_slice(), b"KEM\x00\x10".as_slice(), label, ikm].concat(); + Hkdf::::extract(Some(salt), &labeled_ikm).0.to_vec() +} + +fn labeled_expand(prk: &[u8], label: &[u8], info: &[u8], l: u16) -> Vec { + let labeled_info = [ + &l.to_be_bytes(), + b"HPKE-v1".as_slice(), + b"KEM\x00\x10".as_slice(), + label, + info, + ] + .concat(); + let mut out = Vec::with_capacity(l as usize); + out.resize(l as usize, 0); + Hkdf::::from_prk(prk) + .unwrap() + .expand(&labeled_info, &mut out) + .expect("ok"); + out +} + +fn extract_and_expand(dh: ::SharedSecret, kem_context: &[u8]) -> Vec { + let eae_prk = labeled_extract(b"", b"eae_prk", dh.raw_secret_bytes()); + labeled_expand(&eae_prk, b"shared_secret", kem_context, 32) +} + +#[test] +// section A.3.1 https://datatracker.ietf.org/doc/html/rfc9180#appendix-A.3.1 +fn test_dhkem_p256_hkdf_sha256() { + let pke_hex = hex!( + "04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b32\ + 5ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4" + ); + let pkr_hex = hex!( + "04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f70\ + 6a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0" + ); + let shared_secret_hex = + hex!("c0d26aeab536609a572b07695d933b589dcf363ff9d93c93adea537aeabb8cb8"); + + let (skr, pkr) = NistP256::random_keypair(&mut ConstantRng(&hex!( + "f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2" + ))); + assert_eq!(pkr.0.to_encoded_point(false).as_bytes(), &pkr_hex); + + let (pke, ss1) = pkr + .encapsulate(&mut ConstantRng(&hex!( + "4995788ef4b9d6132b249ce59a77281493eb39af373d236a1fe415cb0c2d7beb" + ))) + .expect("never fails"); + assert_eq!(pke.to_encoded_point(false).as_bytes(), &pke_hex); + + let ss2 = skr.decapsulate(&pke).expect("never fails"); + + assert_eq!(ss1.raw_secret_bytes(), ss2.raw_secret_bytes()); + + let kem_context = [pke_hex, pkr_hex].concat(); + let shared_secret = extract_and_expand(ss1, &kem_context); + + assert_eq!(&shared_secret, &shared_secret_hex); +} diff --git a/dhkem/src/lib.rs b/dhkem/src/lib.rs index e207fef..058374f 100644 --- a/dhkem/src/lib.rs +++ b/dhkem/src/lib.rs @@ -135,3 +135,7 @@ pub type Sm2 = arithmetic::ArithmeticKem; #[cfg(test)] mod tests; + +#[cfg(test)] +#[cfg(feature = "p256")] +mod hpke_p256_test; From fc57d90e6877c00280b28f38c804063bf509147e Mon Sep 17 00:00:00 2001 From: Will Song Date: Fri, 19 Apr 2024 16:02:47 -0400 Subject: [PATCH 14/14] clippy --- dhkem/src/arithmetic.rs | 2 +- dhkem/src/x25519_kem.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dhkem/src/arithmetic.rs b/dhkem/src/arithmetic.rs index aa87b0e..c76aaa0 100644 --- a/dhkem/src/arithmetic.rs +++ b/dhkem/src/arithmetic.rs @@ -33,7 +33,7 @@ where type Error = (); fn decapsulate(&self, encapsulated_key: &PublicKey) -> Result, Self::Error> { - let ss = self.0.diffie_hellman(&encapsulated_key); + let ss = self.0.diffie_hellman(encapsulated_key); Ok(ss) } diff --git a/dhkem/src/x25519_kem.rs b/dhkem/src/x25519_kem.rs index 9246155..daaf7c6 100644 --- a/dhkem/src/x25519_kem.rs +++ b/dhkem/src/x25519_kem.rs @@ -25,7 +25,7 @@ impl Decapsulate for DhDecapsulator { type Error = (); fn decapsulate(&self, encapsulated_key: &PublicKey) -> Result { - let ss = self.0.diffie_hellman(&encapsulated_key); + let ss = self.0.diffie_hellman(encapsulated_key); Ok(ss) }