From 118f836690d501e32e1739bc6ee70f9070fddf76 Mon Sep 17 00:00:00 2001 From: Georgio Nicolas Date: Thu, 29 May 2025 01:19:35 +0200 Subject: [PATCH] Add kkt protocol --- Cargo.lock | 275 +++++++++++++++++++++++++- Cargo.toml | 1 + common/nym-psq-kkr/Cargo.toml | 37 ++++ common/nym-psq-kkr/benches/benches.rs | 135 +++++++++++++ common/nym-psq-kkr/src/error.rs | 21 ++ common/nym-psq-kkr/src/kkt.rs | 224 +++++++++++++++++++++ common/nym-psq-kkr/src/lib.rs | 2 + 7 files changed, 687 insertions(+), 8 deletions(-) create mode 100644 common/nym-psq-kkr/Cargo.toml create mode 100644 common/nym-psq-kkr/benches/benches.rs create mode 100644 common/nym-psq-kkr/src/error.rs create mode 100644 common/nym-psq-kkr/src/kkt.rs create mode 100644 common/nym-psq-kkr/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index df4c5f9fb6..71a4770c28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1457,6 +1457,16 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-models" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "hax-lib", + "pastey", + "rand 0.9.1", +] + [[package]] name = "cosmos-sdk-proto" version = "0.26.1" @@ -3141,6 +3151,40 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "hax-lib" +version = "0.3.0" +source = "git+https://github.com/cryspen/hax/#27ba29ff727fb4c4fffecad5e736181087ce2537" +dependencies = [ + "hax-lib-macros", + "num-bigint", + "num-traits", +] + +[[package]] +name = "hax-lib-macros" +version = "0.3.0" +source = "git+https://github.com/cryspen/hax/#27ba29ff727fb4c4fffecad5e736181087ce2537" +dependencies = [ + "hax-lib-macros-types", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.98", +] + +[[package]] +name = "hax-lib-macros-types" +version = "0.3.0" +source = "git+https://github.com/cryspen/hax/#27ba29ff727fb4c4fffecad5e736181087ce2537" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "hdrhistogram" version = "7.5.4" @@ -3250,7 +3294,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.0", + "rand 0.9.1", "ring", "rustls 0.23.25", "thiserror 2.0.12", @@ -3275,7 +3319,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.0", + "rand 0.9.1", "resolv-conf", "rustls 0.23.25", "smallvec", @@ -4150,6 +4194,193 @@ version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +[[package]] +name = "libcrux-chacha20poly1305" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-poly1305", +] + +[[package]] +name = "libcrux-curve25519" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", +] + +[[package]] +name = "libcrux-ecdh" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-curve25519", + "libcrux-p256", + "rand 0.9.1", +] + +[[package]] +name = "libcrux-ed25519" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-sha2", + "rand_core 0.9.1", +] + +[[package]] +name = "libcrux-hacl-rs" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-macros", +] + +[[package]] +name = "libcrux-hkdf" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-hmac", +] + +[[package]] +name = "libcrux-hmac" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-sha2", +] + +[[package]] +name = "libcrux-intrinsics" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "core-models", + "hax-lib", +] + +[[package]] +name = "libcrux-kem" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-ecdh", + "libcrux-ml-kem", + "libcrux-sha3", + "libcrux-traits", + "rand 0.9.1", +] + +[[package]] +name = "libcrux-macros" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "quote", + "syn 2.0.98", +] + +[[package]] +name = "libcrux-ml-kem" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", + "libcrux-secrets", + "libcrux-sha3", + "rand 0.9.1", +] + +[[package]] +name = "libcrux-p256" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-sha2", +] + +[[package]] +name = "libcrux-platform" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libc", +] + +[[package]] +name = "libcrux-poly1305" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", +] + +[[package]] +name = "libcrux-psq" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-chacha20poly1305", + "libcrux-ed25519", + "libcrux-hkdf", + "libcrux-hmac", + "libcrux-kem", + "libcrux-traits", + "rand 0.9.1", +] + +[[package]] +name = "libcrux-secrets" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "hax-lib", +] + +[[package]] +name = "libcrux-sha2" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "libcrux-hacl-rs", + "libcrux-macros", + "libcrux-traits", +] + +[[package]] +name = "libcrux-sha3" +version = "0.0.2" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "hax-lib", + "libcrux-intrinsics", + "libcrux-platform", +] + +[[package]] +name = "libcrux-traits" +version = "0.0.3-alpha.1" +source = "git+https://github.com/cryspen/libcrux#96abd9f2460ff92811a1c181662590d96c37949c" +dependencies = [ + "rand 0.9.1", +] + [[package]] name = "libm" version = "0.2.11" @@ -6904,6 +7135,29 @@ dependencies = [ "time", ] +[[package]] +name = "nym-psq-kkt" +version = "0.1.0" +dependencies = [ + "arc-swap", + "bytes", + "criterion", + "futures", + "libcrux-kem", + "libcrux-psq", + "libcrux-traits", + "nym-crypto", + "pin-project", + "rand 0.9.1", + "sha2 0.10.9", + "snow", + "strum 0.26.3", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "nym-sdk" version = "0.1.0" @@ -8284,6 +8538,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a8cb46bdc156b1c90460339ae6bfd45ba0394e5effbaa640badb4987fdc261" + [[package]] name = "peg" version = "0.8.4" @@ -8604,7 +8864,7 @@ dependencies = [ "hmac", "md-5", "memchr", - "rand 0.9.0", + "rand 0.9.1", "sha2 0.10.9", "stringprep", ] @@ -8855,7 +9115,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc" dependencies = [ "bytes", "getrandom 0.3.1", - "rand 0.9.0", + "rand 0.9.1", "ring", "rustc-hash", "rustls 0.23.25", @@ -8909,13 +9169,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.1", - "zerocopy 0.8.20", ] [[package]] @@ -10980,7 +11239,7 @@ dependencies = [ "pin-project-lite", "postgres-protocol", "postgres-types", - "rand 0.9.0", + "rand 0.9.1", "socket2", "tokio", "tokio-util", diff --git a/Cargo.toml b/Cargo.toml index 1c7865562d..e738275d2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ members = [ "common/node-tester-utils", "common/nonexhaustive-delayqueue", "common/nym-id", + "common/nym-psq-kkr", "common/nym-metrics", "common/nym_offline_compact_ecash", "common/nymnoise", diff --git a/common/nym-psq-kkr/Cargo.toml b/common/nym-psq-kkr/Cargo.toml new file mode 100644 index 0000000000..ce6e36bbfc --- /dev/null +++ b/common/nym-psq-kkr/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "nym-psq-kkt" +version = "0.1.0" +authors = ["Georgio Nicolas "] +edition = "2021" +license.workspace = true + +[dependencies] +arc-swap = { workspace = true } +bytes = { workspace = true } +futures = { workspace = true } +tracing = { workspace = true } +pin-project = { workspace = true } +sha2 = { workspace = true } +snow = { workspace = true } +strum = { workspace = true, features = ["derive"] } +thiserror = { workspace = true } +tokio = { workspace = true, features = ["full"] } +tokio-util = { workspace = true, features = ["codec"] } + +# internal +nym-crypto = { path = "../crypto", features = ["asymmetric", "serde"]} + +libcrux-traits = { git = "https://github.com/cryspen/libcrux" } +libcrux-kem = { git = "https://github.com/cryspen/libcrux" } +libcrux-psq = { git = "https://github.com/cryspen/libcrux" } +rand = "0.9.1" + +[dev-dependencies] +criterion = {workspace = true} + +[[bench]] +name = "benches" +harness = false + +[lints] +workspace = true diff --git a/common/nym-psq-kkr/benches/benches.rs b/common/nym-psq-kkr/benches/benches.rs new file mode 100644 index 0000000000..bc00821966 --- /dev/null +++ b/common/nym-psq-kkr/benches/benches.rs @@ -0,0 +1,135 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use criterion::{criterion_group, criterion_main, Criterion}; +use libcrux_psq::impls::MlKem768; +use nym_crypto::asymmetric::ed25519; +use nym_psq_kkt::kkt::{ + KKTInitiator, KKTResponder, KKT_REQ_LEN, KKT_RES_LEN_MLKEM768, KKT_TAG_LEN, +}; +use rand::prelude::*; + +pub fn gen_ed25519_keypair(c: &mut Criterion) { + c.bench_function("Generate Ed25519 Keypair", |b| { + b.iter(|| { + let mut s: [u8; 32] = [0u8; 32]; + rand::rng().fill_bytes(&mut s); + ed25519::KeyPair::from_secret(s, 0) + }); + }); +} + +pub fn gen_mlkem768_keypair(c: &mut Criterion) { + c.bench_function("Generate MlKem768 Keypair", |b| { + b.iter(|| { + libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, &mut rand::rng()).unwrap() + }); + }); +} + +pub fn kkt_benchmark(c: &mut Criterion) { + // generate ed25519 keys + let mut secret_initiator: [u8; 32] = [0u8; 32]; + rand::rng().fill_bytes(&mut secret_initiator); + let initiator_ed25519_keypair = ed25519::KeyPair::from_secret(secret_initiator, 0); + + let mut secret_responder: [u8; 32] = [0u8; 32]; + rand::rng().fill_bytes(&mut secret_responder); + let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1); + + // generate kem keypair + let (_, responder_kem_public_key) = + libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, &mut rand::rng()).unwrap(); + + // initialize parties + let initiator: KKTInitiator = + KKTInitiator::init(initiator_ed25519_keypair.private_key()); + + c.bench_function("Initialize Initiator", |b| { + b.iter(|| KKTInitiator::::init(initiator_ed25519_keypair.private_key())); + }); + + let responder: KKTResponder = KKTResponder::init( + responder_ed25519_keypair.private_key(), + &responder_kem_public_key, + ); + + c.bench_function("Initialize Responder", |b| { + b.iter(|| { + KKTResponder::init( + responder_ed25519_keypair.private_key(), + &responder_kem_public_key, + ) + }); + }); + + // create buffers + let mut request_buffer: [u8; KKT_REQ_LEN] = [0u8; KKT_REQ_LEN]; + let mut response_buffer: [u8; KKT_RES_LEN_MLKEM768] = [0u8; KKT_RES_LEN_MLKEM768]; + let mut tag_buffer: [u8; KKT_TAG_LEN] = [0u8; KKT_TAG_LEN]; + + c.bench_function("Initiator: Generate Request", |b| { + b.iter(|| { + initiator.request_kem_pk(&mut request_buffer, &mut tag_buffer); + }); + }); + + // generate request + initiator.request_kem_pk(&mut request_buffer, &mut tag_buffer); + + c.bench_function("Responder: Ingest Request and Generate Response", |b| { + b.iter(|| { + responder + .respond_kem_pk( + &mut response_buffer, + initiator_ed25519_keypair.public_key(), + &request_buffer, + ) + .unwrap(); + }); + }); + + // ingest request, generate response + responder + .respond_kem_pk( + &mut response_buffer, + initiator_ed25519_keypair.public_key(), + &request_buffer, + ) + .unwrap(); + + c.bench_function("Initiator: Ingest Response and Store Key", |b| { + b.iter(|| { + let _ = initiator + .ingest_response_kem_pk::( + &response_buffer, + &tag_buffer, + responder_ed25519_keypair.public_key(), + ) + .unwrap(); + }); + }); + + // ingest response + let received_responder_key = initiator + .ingest_response_kem_pk::( + &response_buffer, + &tag_buffer, + responder_ed25519_keypair.public_key(), + ) + .unwrap(); + + // check if the public key received is the same one that we generated at the start + assert_eq!( + responder_kem_public_key.encode(), + received_responder_key.encode() + ); +} + +criterion_group!( + benches, + gen_ed25519_keypair, + gen_mlkem768_keypair, + kkt_benchmark +); +criterion_main!(benches); diff --git a/common/nym-psq-kkr/src/error.rs b/common/nym-psq-kkr/src/error.rs new file mode 100644 index 0000000000..afcae99db6 --- /dev/null +++ b/common/nym-psq-kkr/src/error.rs @@ -0,0 +1,21 @@ +// Copyright 2025 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum KKTError { + #[error("Signature constructor error")] + SigConstructorError, + + #[error("Signature verification error")] + SigVerifError, + // #[error("Protocol did not complete")] + // ProtocolError, + + // #[error("encountered an IO error: {0}")] + // IoError(#[from] io::Error), + + // #[error("Handshake timeout")] + // HandshakeTimeout(#[from] tokio::time::error::Elapsed), +} diff --git a/common/nym-psq-kkr/src/kkt.rs b/common/nym-psq-kkr/src/kkt.rs new file mode 100644 index 0000000000..3c8f158136 --- /dev/null +++ b/common/nym-psq-kkr/src/kkt.rs @@ -0,0 +1,224 @@ +use std::marker::PhantomData; + +use nym_crypto::asymmetric::ed25519; +use rand::prelude::*; + +use libcrux_kem::MlKem768PublicKey; +use libcrux_psq::{impls::MlKem768, traits::PSQ}; +use libcrux_traits::kem::KEM; + +use crate::error::KKTError; + +const REQ_STR: &[u8] = "KEM_REQ".as_bytes(); +const RES_STR: &[u8] = "KEM_RES".as_bytes(); + +const REQ_LEN: usize = REQ_STR.len() + KKT_TAG_LEN; +const RES_LEN_MLKEM768: usize = REQ_LEN + MlKem768PublicKey::len(); + +pub const KKT_TAG_LEN: usize = 16; +pub const KKT_REQ_LEN: usize = REQ_LEN + ed25519::SIGNATURE_LENGTH; +pub const KKT_RES_LEN_MLKEM768: usize = RES_LEN_MLKEM768 + ed25519::SIGNATURE_LENGTH; + +pub struct KKTInitiator<'a, T: PSQ> { + signing_key: &'a ed25519::PrivateKey, + _t: PhantomData, +} + +impl<'a, T: PSQ> KKTInitiator<'a, T> { + pub fn init(signing_key: &'a ed25519::PrivateKey) -> Self { + Self { + signing_key, + _t: PhantomData, + } + } + pub fn request_kem_pk( + &self, + request_buffer: &mut [u8; KKT_REQ_LEN], + tag_buffer: &mut [u8; KKT_TAG_LEN], + ) { + // request_buffer[0..7] <- request string + request_buffer[0..REQ_STR.len()].copy_from_slice(REQ_STR); + + // tag_buffer <- generate tag + rand::rng().fill_bytes(tag_buffer); + + // request_buffer[7..23] <- tag + request_buffer[REQ_STR.len()..REQ_LEN].copy_from_slice(tag_buffer); + + // sig <- Sign request_buffer[0..23] + let sig = self.signing_key.sign(&request_buffer[0..REQ_LEN]); + + // request_buffer[23..87] <- sig + request_buffer[REQ_LEN..].copy_from_slice(&sig.to_bytes()); + } +} + +impl<'a> KKTInitiator<'a, MlKem768> { + pub fn ingest_response_kem_pk( + &self, + response: &[u8], + tag: &[u8; KKT_TAG_LEN], + responder_verification_key: &ed25519::PublicKey, + ) -> Result { + // TODO: Refactor asserts into errors + // Check size of message + assert_eq!(response.len(), KKT_RES_LEN_MLKEM768); + // Check if the response string is there at the start of the message + assert_eq!(&response[0..RES_STR.len()], RES_STR); + // Check if the tag is the one we expect + assert_eq!( + &response[RES_STR.len()..RES_STR.len() + KKT_TAG_LEN], + &tag[..] + ); + + // Attempt to reconstruct a signature from the received bytes + match ed25519::Signature::from_bytes(&response[RES_LEN_MLKEM768..]) { + Ok(sig) => { + // Attempt to verify signature + match responder_verification_key.verify(&response[0..RES_LEN_MLKEM768], &sig) { + Ok(()) => { + // Extract key from bytes + // (has to be an owned sized array, unless I'm missing some function that works with slices) + let mut key_bytes: [u8; MlKem768PublicKey::len()] = + [0u8; MlKem768PublicKey::len()]; + key_bytes.copy_from_slice( + &response[RES_STR.len() + KKT_TAG_LEN..RES_LEN_MLKEM768], + ); + + // Create key from bytes and return it + Ok(libcrux_kem::PublicKey::MlKem768(MlKem768PublicKey::from( + key_bytes, + ))) + } + Err(_) => Err(KKTError::SigVerifError), + } + } + Err(_) => Err(KKTError::SigConstructorError), + } + } +} + +pub struct KKTResponder<'a, T: PSQ> { + signing_key: &'a ed25519::PrivateKey, + kem_public_key: &'a ::EncapsulationKey, +} + +impl<'a> KKTResponder<'a, MlKem768> { + pub fn init( + signing_key: &'a ed25519::PrivateKey, + kem_public_key: &'a libcrux_kem::PublicKey, + ) -> Self { + Self { + signing_key, + kem_public_key, + } + } + + pub fn respond_kem_pk( + &self, + response_buffer: &mut [u8; KKT_RES_LEN_MLKEM768], + initiator_verification_key: &ed25519::PublicKey, + request: &[u8], + ) -> Result<(), KKTError> { + // TODO: Refactor asserts into errors + // Check request size + assert_eq!(request.len(), KKT_REQ_LEN); + // Check request string + assert_eq!(&request[0..REQ_STR.len()], REQ_STR); + + match ed25519::Signature::from_bytes(&request[REQ_LEN..]) { + Ok(sig) => match initiator_verification_key.verify(&request[0..REQ_LEN], &sig) { + Ok(()) => { + // response_buffer[0..7] <- RES_STR (7 bytes) + response_buffer[0..RES_STR.len()].copy_from_slice(RES_STR); + + // response_buffer[7..23] <- tag (16 bytes, sent by initiator) + response_buffer[RES_STR.len()..REQ_LEN] + .copy_from_slice(&request[REQ_STR.len()..REQ_LEN]); + + // response_buffer[23..1207] <- MlKem768 Public Key (1184 bytes) + response_buffer[REQ_LEN..RES_LEN_MLKEM768] + .copy_from_slice(self.kem_public_key.encode().as_slice()); + + // sign response_buffer[0..1207] + let sig = self.signing_key.sign(&response_buffer[0..RES_LEN_MLKEM768]); + + // response_buffer[1207..1271] <- signature (64 bytes) + response_buffer[RES_LEN_MLKEM768..].copy_from_slice(&sig.to_bytes()); + + Ok(()) + } + Err(_) => Err(KKTError::SigConstructorError), + }, + Err(_) => Err(KKTError::SigVerifError), + } + } +} + +#[cfg(test)] +mod test { + + use crate::kkt::{KKT_REQ_LEN, KKT_RES_LEN_MLKEM768, KKT_TAG_LEN, REQ_LEN, RES_LEN_MLKEM768}; + + use super::{KKTInitiator, KKTResponder}; + use libcrux_psq::impls::MlKem768; + use nym_crypto::asymmetric::ed25519; + use rand::prelude::*; + + #[test] + fn test_kkt_e2e() { + // generate ed25519 keys + let mut secret_initiator: [u8; 32] = [0u8; 32]; + rand::rng().fill_bytes(&mut secret_initiator); + let initiator_ed25519_keypair = ed25519::KeyPair::from_secret(secret_initiator, 0); + + let mut secret_responder: [u8; 32] = [0u8; 32]; + rand::rng().fill_bytes(&mut secret_responder); + let responder_ed25519_keypair = ed25519::KeyPair::from_secret(secret_responder, 1); + + // generate kem keypair + let (_, responder_kem_public_key) = + libcrux_kem::key_gen(libcrux_kem::Algorithm::MlKem768, &mut rand::rng()).unwrap(); + + // initialize parties + let initiator: KKTInitiator = + KKTInitiator::init(initiator_ed25519_keypair.private_key()); + + let responder: KKTResponder = KKTResponder::init( + responder_ed25519_keypair.private_key(), + &responder_kem_public_key, + ); + + // create buffers + let mut request_buffer: [u8; KKT_REQ_LEN] = [0u8; KKT_REQ_LEN]; + let mut response_buffer: [u8; KKT_RES_LEN_MLKEM768] = [0u8; KKT_RES_LEN_MLKEM768]; + let mut tag_buffer: [u8; KKT_TAG_LEN] = [0u8; KKT_TAG_LEN]; + + // generate request + initiator.request_kem_pk(&mut request_buffer, &mut tag_buffer); + + // ingest request, generate response + responder + .respond_kem_pk( + &mut response_buffer, + initiator_ed25519_keypair.public_key(), + &request_buffer, + ) + .unwrap(); + + // ingest response + let received_responder_key = initiator + .ingest_response_kem_pk::( + &response_buffer, + &tag_buffer, + responder_ed25519_keypair.public_key(), + ) + .unwrap(); + + // check if the public key received is the same one that we generated at the start + assert_eq!( + responder_kem_public_key.encode(), + received_responder_key.encode() + ); + } +} diff --git a/common/nym-psq-kkr/src/lib.rs b/common/nym-psq-kkr/src/lib.rs new file mode 100644 index 0000000000..fb8cad10e3 --- /dev/null +++ b/common/nym-psq-kkr/src/lib.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod kkt;