replace encryption layer and add masked byte

This commit is contained in:
Georgio Nicolas
2026-02-12 20:05:03 +01:00
parent 854ab369a6
commit 66ecccbfe5
7 changed files with 588 additions and 370 deletions
+1 -2
View File
@@ -7,13 +7,12 @@ license.workspace = true
publish = false
[dependencies]
blake3 = { workspace = true }
thiserror = { workspace = true }
num_enum = { workspace = true }
strum = { workspace = true }
# internal
nym-crypto = { path = "../crypto", features = ["asymmetric", "hashing", "serde"] }
nym-crypto = { path = "../crypto", features = ["hashing"] }
nym-kkt-ciphersuite = { workspace = true, features = ["digests"] }
libcrux-kem = { workspace = true }
+6 -21
View File
@@ -7,7 +7,6 @@
use criterion::{Criterion, criterion_group, criterion_main};
use nym_crypto::asymmetric::ed25519;
use nym_kkt::{
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM, SignatureScheme},
context::KKTMode,
@@ -17,22 +16,11 @@ use nym_kkt::{
hash_encapsulation_key,
},
session::{
anonymous_initiator_process, initiator_ingest_response, initiator_process,
responder_ingest_message, responder_process,
initiator_ingest_response, initiator_process, responder_ingest_message, responder_process,
},
};
use rand09::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];
rand09::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(|| {
@@ -107,12 +95,14 @@ pub fn kkt_benchmark(c: &mut Criterion) {
c.bench_function(
&format!("{kem}, {hash_function} | Anonymous Initiator: Generate Request",),
|b| {
b.iter(|| anonymous_initiator_process(&mut rng, ciphersuite).unwrap());
b.iter(|| {
initiator_process(&mut rng, KKTMode::OneWay, ciphersuite, None).unwrap()
});
},
);
let (mut i_context, i_frame) =
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
initiator_process(&mut rng, KKTMode::OneWay, ciphersuite, None).unwrap();
c.bench_function(
&format!(
@@ -419,10 +409,5 @@ pub fn kkt_benchmark(c: &mut Criterion) {
}
}
criterion_group!(
benches,
gen_ed25519_keypair,
gen_mlkem768_keypair,
kkt_benchmark
);
criterion_group!(benches, gen_mlkem768_keypair, kkt_benchmark);
criterion_main!(benches);
+246 -92
View File
@@ -1,15 +1,76 @@
use libcrux_chacha20poly1305::TAG_LEN;
use libcrux_psq::handshake::types::DHPublicKey;
use libcrux_psq::handshake::types::{DHKeyPair, DHPrivateKey, DHPublicKey};
use nym_crypto::hkdf::blake3::{derive_key_blake3, derive_key_blake3_multi_input};
use zeroize::Zeroize;
use nym_kkt_ciphersuite::x25519::PUBLIC_KEY_LENGTH;
use rand09::{CryptoRng, RngCore};
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::{
ciphersuite::EncapsulationKey, context::KKTRole, encryption::KKTSessionSecret, error::KKTError,
frame::KKT_SESSION_ID_LEN,
ciphersuite::EncapsulationKey,
context::{KKTContext, KKTRole},
error::KKTError,
frame::{KKT_SESSION_ID_LEN, KKTFrame},
masked_byte::{MASKED_BYTE_LEN, MaskedByte},
};
const MAX_PAYLOAD_LEN: usize = 65535;
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct KKTCarrier(Carrier);
impl KKTCarrier {
fn read_message(&mut self) {
// if we are reading a message and we have not read or written a message before
// this means that we are a responder who is
// reading the initiator's first handshake message
if self.0.rx_counter() == 1 && self.0.tx_counter() == 1 {
// in this case, we parse out the eph public key, the nonce, and the version hash
// we decode to see if the hash is
}
// if we are reading a message and we have written a message before
// this means that we are an initiator
// who is reading the responder's handshake message
if self.0.rx_counter() == 1 && self.0.tx_counter() > 1 {}
}
fn write_message(&mut self) {}
// Generate carrier and encrypt first message
// the first message would look like this
// (outer_header || eph_pk ||
// fn init<R>(
// rng: &mut R,
// responder_public_key: &DHPublicKey,
// payload: &[u8],
// header: Option<&[u8]>,
// ) -> Result<(Self, Vec<u8>), KKTError>
// where
// R: RngCore + CryptoRng,
// {
// let ephemeral_keypair = DHKeyPair::new(rng);
// let shared_secret = ephemeral_keypair
// .sk()
// .diffie_hellman(responder_public_key)
// .map_err(|_| KKTError::X25519Error {
// info: "Key Derivation Error",
// })?;
// let carrier = Carrier::from_secret(&shared_secret.as_ref(), context);
// match header {
// Some(header) => {}
// }
// }
fn respond(responder_private_key: &DHPrivateKey, expects_header: bool) -> Self {
todo!()
}
}
// This is arbitrary
pub const MAX_PAYLOAD_LEN: usize = 1_000_000;
const CARRIER_KDF_INFO_TX: &str = "CARRIER_V1_KDF_RX";
const CARRIER_KDF_INFO_RX: &str = "CARRIER_V1_KDF_TX";
const KKT_CARRIER_CONTEXT: &[u8] = b"CARRIER_V1_KKT_V1_KDF";
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct Carrier {
tx_key: [u8; 32],
rx_key: [u8; 32],
@@ -17,6 +78,11 @@ pub struct Carrier {
rx_counter: u64,
}
pub enum CarrierRole {
Initiator,
Responder,
}
fn increment_nonce(nonce: &mut u64) -> Result<(), KKTError> {
match nonce.checked_add(1) {
Some(incremented_nonce) => {
@@ -37,51 +103,154 @@ fn as_nonce_bytes(nonce: u64) -> [u8; 12] {
}
impl Carrier {
pub fn from_session_secret(
mut session_secret: KKTSessionSecret,
role: KKTRole,
kkt_session_id: &[u8; KKT_SESSION_ID_LEN],
responder_public_key: &DHPublicKey,
responder_encapsulation_key: &EncapsulationKey,
) -> Result<Self, KKTError> {
let mut salt = derive_key_blake3_multi_input(
"nym-kkt-carrier-kdf-main",
&[
responder_encapsulation_key.encode().as_ref(),
responder_public_key.as_ref(),
],
kkt_session_id,
);
let k1 = derive_key_blake3(
"nym-kkt-carrier-kdf-initiator",
session_secret.as_bytes().as_ref(),
&salt,
);
let k2 = derive_key_blake3(
"nym-kkt-carrier-kdf-responder",
session_secret.as_bytes().as_ref(),
&salt,
);
salt.zeroize();
session_secret.zeroize();
Ok(match role {
KKTRole::Initiator => Self {
tx_key: k1,
rx_key: k2,
tx_counter: 1,
rx_counter: 1,
},
KKTRole::Responder => Self {
tx_key: k2,
rx_key: k1,
tx_counter: 1,
rx_counter: 1,
},
})
fn init(tx_key: [u8; 32], rx_key: [u8; 32]) -> Self {
Self {
tx_key: tx_key,
rx_key: rx_key,
tx_counter: 1,
rx_counter: 1,
}
}
pub fn new<R>(
rng: &mut R,
remote_public_key: &DHPublicKey,
context: &[u8],
) -> Result<(Self, DHPublicKey), KKTError>
where
R: RngCore + CryptoRng,
{
let ephemeral_keypair = DHKeyPair::new(rng);
let shared_secret = ephemeral_keypair
.sk()
.diffie_hellman(remote_public_key)
.map_err(|_| KKTError::X25519Error {
info: "Key Derivation Error",
})?;
Ok((
Self::from_secret_slice(shared_secret.as_ref(), context),
ephemeral_keypair.pk,
))
}
pub(crate) fn tx_counter(&self) -> u64 {
self.tx_counter
}
pub(crate) fn rx_counter(&self) -> u64 {
self.rx_counter
}
pub fn new_kkt_responder(
responder_keypair: &DHKeyPair,
message: &[u8],
supported_versions: &[u8],
) -> Result<(Carrier, KKTFrame, KKTContext), KKTError> {
let mut initiator_public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [0; PUBLIC_KEY_LENGTH];
initiator_public_key_bytes.clone_from_slice(&message[0..PUBLIC_KEY_LENGTH]);
// check mask
// todo: deal with this
let masked_byte =
MaskedByte::try_from(&message[PUBLIC_KEY_LENGTH..PUBLIC_KEY_LENGTH + MASKED_BYTE_LEN])
.unwrap();
let mut mask = Vec::from(&initiator_public_key_bytes);
mask.extend_from_slice(responder_keypair.pk.as_ref());
// todo: deal with this
let byte = masked_byte.unmask(&mask).unwrap();
// todo: derive version from byte
if supported_versions.iter().find(|x| *x == &byte).is_some() {
// now that the version is ok, we can try dh
let initiator_public_key = DHPublicKey::from_bytes(&initiator_public_key_bytes);
let shared_secret = responder_keypair
.sk()
.diffie_hellman(&initiator_public_key)
.map_err(|_| KKTError::X25519Error {
info: "Key Derivation Error",
})?;
let mut context = Vec::from(masked_byte.as_slice());
context.extend_from_slice(&KKT_CARRIER_CONTEXT);
context.extend_from_slice(&initiator_public_key.as_ref());
context.extend_from_slice(&responder_keypair.pk.as_ref());
let mut carrier = Self::from_secret_slice(shared_secret.as_ref(), &context).flip_keys();
let decrypted_message =
carrier.decrypt(&message[PUBLIC_KEY_LENGTH + MASKED_BYTE_LEN..])?;
let (frame, context) = KKTFrame::from_bytes(&decrypted_message)?;
Ok((carrier, frame, context))
} else {
panic!("unsupported protocol version");
}
}
pub fn new_kkt_initiator<R>(
rng: &mut R,
responder_public_key: &DHPublicKey,
version_byte: u8,
kkt_frame: &KKTFrame,
) -> Result<(Self, Vec<u8>), KKTError>
where
R: CryptoRng + RngCore,
{
let ephemeral_keypair = DHKeyPair::new(rng);
let shared_secret = ephemeral_keypair
.sk()
.diffie_hellman(responder_public_key)
.map_err(|_| KKTError::X25519Error {
info: "Key Derivation Error",
})?;
let mut mask = Vec::from(ephemeral_keypair.pk.as_ref());
mask.extend_from_slice(responder_public_key.as_ref());
let masked_byte = MaskedByte::new(version_byte, &mask);
let mut context = Vec::from(masked_byte.as_slice());
context.extend_from_slice(&KKT_CARRIER_CONTEXT);
context.extend_from_slice(&ephemeral_keypair.pk.as_ref());
context.extend_from_slice(&responder_public_key.as_ref());
let mut carrier = Self::from_secret_slice(shared_secret.as_ref(), &context);
let mut full_kkt_message = Vec::from(ephemeral_keypair.pk.as_ref());
full_kkt_message.extend_from_slice(masked_byte.as_slice());
let encrypted_kkt_frame = carrier.encrypt(&kkt_frame.to_bytes())?;
full_kkt_message.extend_from_slice(&encrypted_kkt_frame);
Ok((carrier, full_kkt_message))
}
pub fn from_secret_slice(secret: &[u8], context: &[u8]) -> Self {
let tx_key = derive_key_blake3(CARRIER_KDF_INFO_TX, secret, &context);
let rx_key = derive_key_blake3(CARRIER_KDF_INFO_RX, secret, &context);
Self::init(tx_key, rx_key)
}
pub fn from_secret(mut secret: [u8; 32], context: &[u8]) -> Self {
let tx_key = derive_key_blake3(CARRIER_KDF_INFO_TX, secret.as_ref(), &context);
let rx_key = derive_key_blake3(CARRIER_KDF_INFO_RX, secret.as_ref(), &context);
secret.zeroize();
Self::init(tx_key, rx_key)
}
fn flip_keys(self) -> Self {
Self {
tx_key: self.rx_key,
rx_key: self.tx_key,
tx_counter: self.rx_counter,
rx_counter: self.tx_counter,
}
}
pub fn encrypt(&mut self, plaintext: &[u8]) -> Result<Vec<u8>, KKTError> {
if plaintext.len() > MAX_PAYLOAD_LEN {
return Err(KKTError::AEADError {
@@ -128,13 +297,14 @@ mod tests {
KKT_RESPONSE_AAD,
carrier::Carrier,
ciphersuite::EncapsulationKey,
encryption::*,
context::{KKTMode, KKTRole},
frame::KKTFrame,
key_utils::{
generate_keypair_libcrux, generate_keypair_mceliece, generate_keypair_mlkem,
generate_keypair_x25519, hash_encapsulation_key,
},
session::{
anonymous_initiator_process, initiator_ingest_response, responder_ingest_message,
initiator_ingest_response, initiator_process, responder_ingest_message,
responder_process,
},
};
@@ -143,7 +313,6 @@ mod tests {
#[test]
fn test_e2e() {
let mut rng = rand09::rng();
// generate ed25519 keys
// generate responder x25519 keys
let responder_x25519_keypair = generate_keypair_x25519(&mut rng);
@@ -192,7 +361,7 @@ mod tests {
let r_kem_key_bytes = responder_kem_public_key.encode();
let _i_dir_hash = hash_encapsulation_key(
let i_dir_hash = hash_encapsulation_key(
&ciphersuite.hash_function(),
ciphersuite.hash_len(),
&i_kem_key_bytes,
@@ -204,19 +373,24 @@ mod tests {
&r_kem_key_bytes,
);
// OneWay
let (mut i_context, i_frame) =
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
initiator_process(&mut rng, KKTMode::OneWay, ciphersuite, None).unwrap();
// encryption - initiator frame
let (i_session_secret, i_bytes) =
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.pk, &i_frame)
.unwrap();
let (mut i_carrier, i_bytes) = Carrier::new_kkt_initiator(
&mut rng,
&responder_x25519_keypair.pk,
1u8,
&i_frame,
)
.unwrap();
// decryption - initiator frame
let (r_session_secret, i_frame_r, i_context_r) =
decrypt_initial_kkt_frame(responder_x25519_keypair.sk(), &i_bytes).unwrap();
let (mut r_carrier, i_frame_r, i_context_r) =
Carrier::new_kkt_responder(&responder_x25519_keypair, &i_bytes, &[1]).unwrap();
let (mut r_context, _) =
responder_ingest_message(&i_context_r, None, &i_frame_r).unwrap();
@@ -229,14 +403,12 @@ mod tests {
.unwrap();
// encryption - responder frame
let r_bytes =
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
.unwrap();
let r_bytes = r_carrier.encrypt(&r_frame.to_bytes()).unwrap();
// decryption - responder frame
let (i_frame_r, i_context_r) =
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
KKTFrame::from_bytes(&i_carrier.decrypt(&r_bytes).unwrap()).unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
@@ -248,47 +420,29 @@ mod tests {
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes);
let mut initiator_carrier = Carrier::from_session_secret(
i_session_secret,
i_context.role(),
&i_frame.session_id(),
&responder_x25519_keypair.pk,
&i_obtained_key,
)
.unwrap();
let mut responder_carrier = Carrier::from_session_secret(
r_session_secret,
r_context.role(),
&r_frame.session_id(),
&responder_x25519_keypair.pk,
&responder_kem_public_key,
)
.unwrap();
let test1 = b"test1: i>r #1";
let ct1 = initiator_carrier.encrypt(test1).unwrap();
let pt1 = responder_carrier.decrypt(&ct1).unwrap();
let ct1 = i_carrier.encrypt(test1).unwrap();
let pt1 = r_carrier.decrypt(&ct1).unwrap();
assert_eq!(pt1, test1);
let test2 = b"test2: r>i #1";
let ct2 = initiator_carrier.encrypt(test2).unwrap();
let pt2 = responder_carrier.decrypt(&ct2).unwrap();
let ct2 = i_carrier.encrypt(test2).unwrap();
let pt2 = r_carrier.decrypt(&ct2).unwrap();
assert_eq!(pt2, test2);
let test3 = b"test3: i>r #2";
let ct3 = initiator_carrier.encrypt(test3).unwrap();
let pt3 = responder_carrier.decrypt(&ct3).unwrap();
let ct3 = i_carrier.encrypt(test3).unwrap();
let pt3 = r_carrier.decrypt(&ct3).unwrap();
assert_eq!(pt3, test3);
let test4 = b"test4: i>r #3";
let ct4 = initiator_carrier.encrypt(test4).unwrap();
let pt4 = responder_carrier.decrypt(&ct4).unwrap();
let ct4 = i_carrier.encrypt(test4).unwrap();
let pt4 = r_carrier.decrypt(&ct4).unwrap();
assert_eq!(pt4, test4);
let test5 = b"test5: r>i #2";
let ct5 = initiator_carrier.encrypt(test5).unwrap();
let pt5 = responder_carrier.decrypt(&ct5).unwrap();
let ct5 = i_carrier.encrypt(test5).unwrap();
let pt5 = r_carrier.decrypt(&ct5).unwrap();
assert_eq!(pt5, test5);
}
}
+12
View File
@@ -54,6 +54,18 @@ pub enum KKTError {
LibcruxError,
}
#[derive(Error, Debug)]
pub enum MaskedByteError {
#[error(
"Invalid Masked Byte Length: Expected({}), Actual({}).",
expected,
actual
)]
InvalidLength { expected: usize, actual: usize },
#[error("Failed to Unmask Byte.")]
Failure,
}
impl From<libcrux_kem::Error> for KKTError {
fn from(err: libcrux_kem::Error) -> Self {
match err {
+159 -239
View File
@@ -3,12 +3,13 @@
pub mod ciphersuite;
pub mod context;
pub mod encryption;
// pub mod encryption;
pub mod error;
pub mod frame;
pub mod key_utils;
// pub mod kkt;
pub mod carrier;
pub mod masked_byte;
pub mod rekey;
pub mod session;
@@ -22,202 +23,170 @@ pub(crate) const KKT_INITIAL_FRAME_AAD: &[u8] = b"KKT_INITIAL_FRAME";
mod test {
use crate::{
KKT_RESPONSE_AAD,
carrier::Carrier,
ciphersuite::{Ciphersuite, EncapsulationKey, HashFunction, KEM},
encryption::{
decrypt_initial_kkt_frame, decrypt_kkt_frame, encrypt_initial_kkt_frame,
encrypt_kkt_frame,
},
context::KKTMode,
frame::KKTFrame,
key_utils::{
generate_keypair_libcrux, generate_keypair_mceliece, generate_keypair_mlkem,
generate_keypair_x25519, hash_encapsulation_key,
},
session::{
anonymous_initiator_process, initiator_ingest_response, initiator_process,
responder_ingest_message, responder_process,
initiator_ingest_response, initiator_process, responder_ingest_message,
responder_process,
},
};
#[test]
fn test_kkt_psq_e2e_clear() {
fn test_kkt_psq_e2e() {
let mut rng = rand09::rng();
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
for hash_function in [
HashFunction::Blake3,
HashFunction::SHA256,
HashFunction::Shake128,
HashFunction::Shake256,
] {
let ciphersuite = Ciphersuite::resolve_ciphersuite(
kem,
hash_function,
crate::ciphersuite::SignatureScheme::Ed25519,
None,
)
.unwrap();
// generate kem public keys
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
KEM::MlKem768 => (
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
),
KEM::XWing => (
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
EncapsulationKey::XWing(generate_keypair_libcrux(&mut rng, kem).unwrap().1),
),
KEM::X25519 => (
EncapsulationKey::X25519(
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
),
EncapsulationKey::X25519(
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
),
),
KEM::McEliece => (
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
),
};
let i_kem_key_bytes = initiator_kem_public_key.encode();
let r_kem_key_bytes = responder_kem_public_key.encode();
let i_dir_hash = hash_encapsulation_key(
&ciphersuite.hash_function(),
ciphersuite.hash_len(),
&i_kem_key_bytes,
);
let r_dir_hash = hash_encapsulation_key(
&ciphersuite.hash_function(),
ciphersuite.hash_len(),
&r_kem_key_bytes,
);
// Anonymous Initiator, OneWay
{
let (mut i_context, i_frame) =
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
let i_frame_bytes = i_frame.to_bytes();
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
let (mut r_context, _) =
responder_ingest_message(&r_context, None, &i_frame_r).unwrap();
let r_frame = responder_process(
&mut r_context,
i_frame_r.session_id(),
&responder_kem_public_key,
)
.unwrap();
let r_bytes = r_frame.to_bytes();
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
&i_frame_r,
&i_context_r,
&r_dir_hash,
)
.unwrap();
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
}
// Initiator, OneWay
{
let (mut i_context, i_frame) = initiator_process(
&mut rng,
crate::context::KKTMode::OneWay,
ciphersuite,
for encryption in [false, true] {
for kem in [KEM::MlKem768, KEM::XWing, KEM::X25519, KEM::McEliece] {
for hash_function in [
HashFunction::Blake3,
HashFunction::SHA256,
HashFunction::Shake128,
HashFunction::Shake256,
] {
let ciphersuite = Ciphersuite::resolve_ciphersuite(
kem,
hash_function,
crate::ciphersuite::SignatureScheme::Ed25519,
None,
)
.unwrap();
let i_frame_bytes = i_frame.to_bytes();
// generate kem public keys
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
let (responder_kem_public_key, initiator_kem_public_key) = match kem {
KEM::MlKem768 => (
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
EncapsulationKey::MlKem768(generate_keypair_mlkem(&mut rng).1),
),
KEM::XWing => (
EncapsulationKey::XWing(
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
),
EncapsulationKey::XWing(
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
),
),
KEM::X25519 => (
EncapsulationKey::X25519(
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
),
EncapsulationKey::X25519(
generate_keypair_libcrux(&mut rng, kem).unwrap().1,
),
),
KEM::McEliece => (
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
EncapsulationKey::McEliece(generate_keypair_mceliece(&mut rng).1),
),
};
let (mut r_context, r_obtained_key) =
responder_ingest_message(&r_context, None, &i_frame_r).unwrap();
let i_kem_key_bytes = initiator_kem_public_key.encode();
assert!(r_obtained_key.is_none());
let r_kem_key_bytes = responder_kem_public_key.encode();
let r_frame = responder_process(
&mut r_context,
i_frame_r.session_id(),
&responder_kem_public_key,
)
.unwrap();
let i_dir_hash = hash_encapsulation_key(
&ciphersuite.hash_function(),
ciphersuite.hash_len(),
&i_kem_key_bytes,
);
let r_bytes = r_frame.to_bytes();
let r_dir_hash = hash_encapsulation_key(
&ciphersuite.hash_function(),
ciphersuite.hash_len(),
&r_kem_key_bytes,
);
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
// OneWay
{
let (mut i_context, i_frame) =
initiator_process(&mut rng, KKTMode::OneWay, ciphersuite, None)
.unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
&i_frame_r,
&i_context_r,
&r_dir_hash,
)
.unwrap();
let i_frame_bytes = i_frame.to_bytes();
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
}
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
// Initiator, Mutual
{
let (mut i_context, i_frame) = initiator_process(
&mut rng,
crate::context::KKTMode::Mutual,
ciphersuite,
Some(&initiator_kem_public_key),
)
.unwrap();
let (mut r_context, r_obtained_key) =
responder_ingest_message(&r_context, None, &i_frame_r).unwrap();
let i_frame_bytes = i_frame.to_bytes();
assert!(r_obtained_key.is_none());
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
let r_frame = responder_process(
&mut r_context,
i_frame_r.session_id(),
&responder_kem_public_key,
)
.unwrap();
let (mut r_context, r_obtained_key) =
responder_ingest_message(&r_context, Some(&i_dir_hash), &i_frame_r)
.unwrap();
let r_bytes = r_frame.to_bytes();
assert_eq!(r_obtained_key.unwrap().encode(), i_kem_key_bytes);
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
let r_frame = responder_process(
&mut r_context,
i_frame_r.session_id(),
&responder_kem_public_key,
)
.unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
&i_frame_r,
&i_context_r,
&r_dir_hash,
)
.unwrap();
let r_bytes = r_frame.to_bytes();
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
}
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
// Mutual
{
let (mut i_context, i_frame) = initiator_process(
&mut rng,
KKTMode::Mutual,
ciphersuite,
Some(&initiator_kem_public_key),
)
.unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
&i_frame_r,
&i_context_r,
&r_dir_hash,
)
.unwrap();
let i_frame_bytes = i_frame.to_bytes();
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
let (i_frame_r, r_context) = KKTFrame::from_bytes(&i_frame_bytes).unwrap();
let (mut r_context, r_obtained_key) =
responder_ingest_message(&r_context, Some(&i_dir_hash), &i_frame_r)
.unwrap();
assert_eq!(r_obtained_key.unwrap().encode(), i_kem_key_bytes);
let r_frame = responder_process(
&mut r_context,
i_frame_r.session_id(),
&responder_kem_public_key,
)
.unwrap();
let r_bytes = r_frame.to_bytes();
let (i_frame_r, i_context_r) = KKTFrame::from_bytes(&r_bytes).unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
&i_frame_r,
&i_context_r,
&r_dir_hash,
)
.unwrap();
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
}
}
}
}
}
#[test]
fn test_kkt_psq_e2e_encrypted() {
fn test_kkt_psq_e2e_encrypted_carrier() {
let mut rng = rand09::rng();
// generate responder x25519 keys
@@ -279,21 +248,25 @@ mod test {
&r_kem_key_bytes,
);
// Anonymous Initiator, OneWay
// OneWay
{
let (mut i_context, i_frame) =
anonymous_initiator_process(&mut rng, ciphersuite).unwrap();
initiator_process(&mut rng, KKTMode::OneWay, ciphersuite, None).unwrap();
// encryption - initiator frame
let (i_session_secret, i_bytes) =
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.pk, &i_frame)
.unwrap();
let (mut i_carrier, i_bytes) = Carrier::new_kkt_initiator(
&mut rng,
&responder_x25519_keypair.pk,
1u8,
&i_frame,
)
.unwrap();
// decryption - initiator frame
let (r_session_secret, i_frame_r, i_context_r) =
decrypt_initial_kkt_frame(responder_x25519_keypair.sk(), &i_bytes).unwrap();
let (mut r_carrier, i_frame_r, i_context_r) =
Carrier::new_kkt_responder(&responder_x25519_keypair, &i_bytes, &[1])
.unwrap();
let (mut r_context, _) =
responder_ingest_message(&i_context_r, None, &i_frame_r).unwrap();
@@ -306,14 +279,12 @@ mod test {
.unwrap();
// encryption - responder frame
let r_bytes =
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
.unwrap();
let r_bytes = r_carrier.encrypt(&r_frame.to_bytes()).unwrap();
// decryption - responder frame
let (i_frame_r, i_context_r) =
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
KKTFrame::from_bytes(&i_carrier.decrypt(&r_bytes).unwrap()).unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
@@ -325,86 +296,37 @@ mod test {
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
}
// Initiator, OneWay
// Mutual
{
let (mut i_context, i_frame) = initiator_process(
&mut rng,
crate::context::KKTMode::OneWay,
ciphersuite,
None,
)
.unwrap();
// encryption - initiator frame
let (i_session_secret, i_bytes) =
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.pk, &i_frame)
.unwrap();
// decryption - initiator frame
let (r_session_secret, i_frame_r, r_context) =
decrypt_initial_kkt_frame(responder_x25519_keypair.sk(), &i_bytes).unwrap();
let (mut r_context, r_obtained_key) =
responder_ingest_message(&r_context, None, &i_frame_r).unwrap();
assert!(r_obtained_key.is_none());
let r_frame = responder_process(
&mut r_context,
i_frame_r.session_id(),
&responder_kem_public_key,
)
.unwrap();
// encryption - responder frame
let r_bytes =
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
.unwrap();
// decryption - responder frame
let (i_frame_r, i_context_r) =
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
&i_frame_r,
&i_context_r,
&r_dir_hash,
)
.unwrap();
assert_eq!(i_obtained_key.encode(), r_kem_key_bytes)
}
// Initiator, Mutual
{
let (mut i_context, i_frame) = initiator_process(
&mut rng,
crate::context::KKTMode::Mutual,
KKTMode::Mutual,
ciphersuite,
Some(&initiator_kem_public_key),
)
.unwrap();
// encryption - initiator frame
let (i_session_secret, i_bytes) =
encrypt_initial_kkt_frame(&mut rng, &responder_x25519_keypair.pk, &i_frame)
.unwrap();
let (mut i_carrier, i_bytes) = Carrier::new_kkt_initiator(
&mut rng,
&responder_x25519_keypair.pk,
1u8,
&i_frame,
)
.unwrap();
// decryption - initiator frame
let (r_session_secret, i_frame_r, i_context_r) =
decrypt_initial_kkt_frame(responder_x25519_keypair.sk(), &i_bytes).unwrap();
let (mut r_context, r_obtained_key) =
responder_ingest_message(&i_context_r, Some(&i_dir_hash), &i_frame_r)
let (mut r_carrier, i_frame_r, i_context_r) =
Carrier::new_kkt_responder(&responder_x25519_keypair, &i_bytes, &[1])
.unwrap();
assert_eq!(r_obtained_key.unwrap().encode(), i_kem_key_bytes);
let (mut r_context, _) = responder_ingest_message(
&i_context_r,
Some(i_dir_hash.as_slice()),
&i_frame_r,
)
.unwrap();
let r_frame = responder_process(
&mut r_context,
@@ -414,14 +336,12 @@ mod test {
.unwrap();
// encryption - responder frame
let r_bytes =
encrypt_kkt_frame(&mut rng, &r_session_secret, &r_frame, KKT_RESPONSE_AAD)
.unwrap();
let r_bytes = r_carrier.encrypt(&r_frame.to_bytes()).unwrap();
// decryption - responder frame
let (i_frame_r, i_context_r) =
decrypt_kkt_frame(&i_session_secret, &r_bytes, KKT_RESPONSE_AAD).unwrap();
KKTFrame::from_bytes(&i_carrier.decrypt(&r_bytes).unwrap()).unwrap();
let i_obtained_key = initiator_ingest_response(
&mut i_context,
+164
View File
@@ -0,0 +1,164 @@
use nym_crypto::{blake3, hmac::hmac::digest::ExtendableOutput};
use crate::error::{
MaskedByteError,
MaskedByteError::{Failure, InvalidLength},
};
pub const MASKED_BYTE_LEN: usize = 16;
pub const MASKED_BYTE_CONTEXT_STR: &[u8] = b"NYM_MASKED_BYTE_V1";
#[derive(Clone, Copy)]
pub struct MaskedByte([u8; MASKED_BYTE_LEN]);
impl MaskedByte {
/// Mask a byte by hashing it with some mask.
/// Outputs Blake3_Hash(MASKED_BYTE_CONTEXT_STR || mask || 0xFF || byte)
pub fn new(byte: u8, mask: &[u8]) -> Self {
let mut output: [u8; MASKED_BYTE_LEN] = [0u8; MASKED_BYTE_LEN];
let mut hasher = blake3::Hasher::new();
hasher.update(MASKED_BYTE_CONTEXT_STR);
hasher.update(mask);
// avoid zero update
hasher.update(&[0xFF, byte]);
hasher.finalize_xof_into(&mut output);
Self(output)
}
/// Unmasks a byte by trial hashing.
/// This function runs Blake3_Hash(MASKED_BYTE_CONTEXT_STR || mask || 0xFF).
/// This Hasher state is then cloned updated with `i: u8` in (0..=u8::max).
/// If we find an `i` which yields back the hash input, then we found the masked byte.
/// Otherwise, the function returns an error.
pub fn unmask(&self, mask: &[u8]) -> Result<u8, MaskedByteError> {
let mut buf: [u8; MASKED_BYTE_LEN] = [0u8; MASKED_BYTE_LEN];
let mut hasher = blake3::Hasher::new();
hasher.update(MASKED_BYTE_CONTEXT_STR);
hasher.update(mask);
// avoid zero update
hasher.update(&[0xFF]);
for i in 0..=u8::MAX {
let mut t_hasher = hasher.clone();
t_hasher.update(&[i]);
t_hasher.finalize_xof_into(&mut buf);
if buf == self.0 {
return Ok(i);
}
}
return Err(Failure);
}
pub fn as_slice<'a>(&'a self) -> &'a [u8] {
&self.0
}
pub fn to_bytes(self) -> [u8; 16] {
self.0
}
}
impl From<[u8; MASKED_BYTE_LEN]> for MaskedByte {
fn from(value: [u8; MASKED_BYTE_LEN]) -> Self {
MaskedByte(value)
}
}
impl From<&[u8; MASKED_BYTE_LEN]> for MaskedByte {
fn from(value: &[u8; MASKED_BYTE_LEN]) -> Self {
MaskedByte(value.to_owned())
}
}
impl TryFrom<&[u8]> for MaskedByte {
type Error = MaskedByteError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() != MASKED_BYTE_LEN {
Err(InvalidLength {
expected: MASKED_BYTE_LEN,
actual: value.len(),
})
} else {
Ok(Self::from(value.as_chunks::<MASKED_BYTE_LEN>().0[0]))
}
}
}
#[cfg(test)]
mod test {
use crate::masked_byte::MASKED_BYTE_LEN;
use super::MaskedByte;
use rand09::{Rng, RngCore, rng};
#[test]
fn test_masking() {
let mut mask: [u8; 256] = [0u8; 256];
let mut wire_bytes: [u8; MASKED_BYTE_LEN];
// why not
for i in 0..=u8::MAX {
// gen mask
rng().fill_bytes(&mut mask);
let masked_byte = MaskedByte::new(i, &mask);
wire_bytes = masked_byte.to_bytes();
let decoded_masked_byte = MaskedByte::from(wire_bytes);
let output = decoded_masked_byte.unmask(&mask).unwrap();
assert_eq!(i, output);
// flip bit
let mut with_flipped_bit = decoded_masked_byte.to_bytes();
let byte_idx: usize = rng().random_range(0..MASKED_BYTE_LEN);
let bit_idx = rng().random_range(0..8);
with_flipped_bit[byte_idx] ^= 1 << bit_idx;
let decoded_masked_byte = MaskedByte::from(with_flipped_bit);
assert!(decoded_masked_byte.unmask(&mask).is_err());
}
}
#[test]
fn test_decoding() {
let mut mask: [u8; 256] = [0u8; 256];
// gen mask
rng().fill_bytes(&mut mask);
let byte = rng().random();
let masked_byte = MaskedByte::new(byte, &mask);
let wire_bytes: [u8; MASKED_BYTE_LEN] = masked_byte.to_bytes();
// should succeed
let decoded_masked_byte = MaskedByte::try_from(wire_bytes.as_slice()).unwrap();
let output = decoded_masked_byte.unmask(&mask).unwrap();
assert_eq!(byte, output);
let empty_slice: &[u8] = &[];
// should fail
assert!(MaskedByte::try_from(empty_slice).is_err());
let mut wire_bytes_messy = Vec::from(wire_bytes);
// add more one more byte
wire_bytes_messy.push(0x42);
assert!(wire_bytes_messy.len() == MASKED_BYTE_LEN + 1);
// should fail
assert!(MaskedByte::try_from(wire_bytes_messy.as_slice()).is_err());
// pop the added byte
_ = wire_bytes_messy.pop();
assert!(wire_bytes_messy.len() == MASKED_BYTE_LEN);
// should succeed
assert!(MaskedByte::try_from(wire_bytes_messy.as_slice()).is_ok());
// pop one more byte
_ = wire_bytes_messy.pop();
assert!(wire_bytes_messy.len() == MASKED_BYTE_LEN - 1);
// should fail
assert!(MaskedByte::try_from(wire_bytes_messy.as_slice()).is_err());
}
}
-16
View File
@@ -43,22 +43,6 @@ where
Ok((context, KKTFrame::new(context_bytes, body, session_id)))
}
pub fn anonymous_initiator_process<R>(
rng: &mut R,
ciphersuite: Ciphersuite,
) -> Result<(KKTContext, KKTFrame), KKTError>
where
R: CryptoRng + RngCore,
{
let context = KKTContext::new(KKTRole::Initiator, KKTMode::OneWay, ciphersuite);
let context_bytes = context.encode()?;
let mut session_id = [0u8; KKT_SESSION_ID_LEN];
rng.fill_bytes(&mut session_id);
Ok((context, KKTFrame::new(context_bytes, &[], session_id)))
}
pub fn initiator_ingest_response(
own_context: &mut KKTContext,
remote_frame: &KKTFrame,