diff --git a/examples/encrypt.rs b/examples/encrypt.rs index 5d6ed7b3..3e43112a 100644 --- a/examples/encrypt.rs +++ b/examples/encrypt.rs @@ -12,6 +12,7 @@ use cosmian_crypto_core::{ Aes256Gcm, }; +#[allow(dead_code)] /// Generates a new USK and encrypted header and prints them. fn generate_new(cc: &Covercrypt, msk: &mut MasterSecretKey, mpk: &MasterPublicKey) { let ap = AccessPolicy::parse("DPT::FIN && SEC::TOP").unwrap(); diff --git a/src/api.rs b/src/api.rs index b2e48513..e786c751 100644 --- a/src/api.rs +++ b/src/api.rs @@ -10,7 +10,7 @@ use super::{ }; use crate::{ core::{ - primitives::{decaps, encaps, refresh, rekey, setup}, + primitives::{decaps, encaps, full_decaps, refresh, rekey, setup}, MasterPublicKey, MasterSecretKey, UserSecretKey, XEnc, SHARED_SECRET_LENGTH, }, traits::{KemAc, PkeAc}, @@ -147,21 +147,21 @@ impl Covercrypt { ) } - // /// Returns a new encapsulation with the same rights as the one given, along - // /// with a freshly generated shared secret. - // pub fn recaps( - // &self, - // msk: &MasterSecretKey, - // mpk: &MasterPublicKey, - // encapsulation: &XEnc, - // ) -> Result<(Secret<32>, XEnc), Error> { - // let (_ss, rights) = full_decaps(msk, encapsulation)?; - // encaps( - // &mut *self.rng.lock().expect("Mutex lock failed!"), - // mpk, - // &rights, - // ) - // } + /// Returns a new encapsulation with the same rights as the one given, along + /// with a freshly generated shared secret. + pub fn recaps( + &self, + msk: &MasterSecretKey, + mpk: &MasterPublicKey, + encapsulation: &XEnc, + ) -> Result<(Secret<32>, XEnc), Error> { + let (_ss, rights) = full_decaps(msk, encapsulation)?; + encaps( + &mut *self.rng.lock().expect("Mutex lock failed!"), + mpk, + &rights, + ) + } } impl KemAc for Covercrypt { diff --git a/src/core/mod.rs b/src/core/mod.rs index 4cd0bf5c..e086d5cc 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -158,7 +158,7 @@ impl UserId { #[derive(Debug, PartialEq, Eq)] struct TracingSecretKey { s: Scalar, - tracers: LinkedList, + tracers: LinkedList<(Scalar, EcPoint)>, users: HashSet, } @@ -166,7 +166,7 @@ impl TracingSecretKey { fn new_with_level(level: usize, rng: &mut impl CryptoRngCore) -> Result { let s = nike::Scalar::new(rng); let tracers = (0..=level) - .map(|_| R25519::keygen(rng).map(|kp| kp.0)) + .map(|_| R25519::keygen(rng)) .collect::>()?; let users = HashSet::new(); @@ -178,14 +178,18 @@ impl TracingSecretKey { self.tracers.len() - 1 } + fn set_traps(&self, r: &Scalar) -> Vec { + self.tracers.iter().map(|(_, Pi)| Pi * r).collect() + } + /// Generates a new tracer. Returns the associated trap. fn _increase_tracing(&mut self, rng: &mut impl CryptoRngCore) -> Result<(), Error> { - self.tracers.push_back(R25519::keygen(rng)?.0); + self.tracers.push_back(R25519::keygen(rng)?); Ok(()) } /// Drops the oldest tracer and returns it. - fn _decrease_tracing(&mut self) -> Result { + fn _decrease_tracing(&mut self) -> Result<(Scalar, EcPoint), Error> { if self.tracing_level() == MIN_TRACING_LEVEL { Err(Error::OperationNotPermitted(format!( "tracing level cannot be lower than {MIN_TRACING_LEVEL}" @@ -236,7 +240,7 @@ impl TracingSecretKey { /// Generates the associated tracing public key. #[must_use] fn tpk(&self) -> TracingPublicKey { - TracingPublicKey(self.tracers.iter().map(|s| s.into()).collect()) + TracingPublicKey(self.tracers.iter().map(|(_, Pi)| Pi).cloned().collect()) } /// Returns the binding points. @@ -246,7 +250,7 @@ impl TracingSecretKey { /// Generates a new ID and adds it to the list of known user IDs. fn generate_user_id(&mut self, rng: &mut impl CryptoRngCore) -> Result { - if let Some(last_tracer) = self.tracers.back() { + if let Some((last_tracer, _)) = self.tracers.back() { // Generate all but the last marker at random. let mut markers: LinkedList = self .tracers @@ -260,7 +264,7 @@ impl TracingSecretKey { .tracers .iter() .zip(markers.iter()) - .map(|(sk_i, a_i)| sk_i * a_i) + .map(|((sk_i, _), a_i)| sk_i * a_i) .fold(Scalar::zero(), |acc, x_i| &acc + &x_i)) / last_tracer; @@ -279,7 +283,7 @@ impl TracingSecretKey { == id .iter() .zip(self.tracers.iter()) - .map(|(identifier, tracer)| identifier * tracer) + .map(|(identifier, (tracer, _))| identifier * tracer) .sum() } @@ -488,7 +492,6 @@ impl XEnc { self.c.len() - 1 } - #[cfg(any(test, feature = "test-utils"))] pub fn count(&self) -> usize { match &self.encapsulations { Encapsulations::HEncs(vec) => vec.len(), diff --git a/src/core/primitives.rs b/src/core/primitives.rs index a5c644d9..b3a17f92 100644 --- a/src/core/primitives.rs +++ b/src/core/primitives.rs @@ -178,7 +178,7 @@ pub fn usk_keygen( Ok(UserSecretKey { id, - ps: msk.tsk.tracers.iter().map(EcPoint::from).collect(), + ps: msk.tsk.tracers.iter().map(|(_, gi)| gi).cloned().collect(), secrets: coordinate_keys, signature, }) @@ -432,58 +432,108 @@ pub fn decaps( } } -// /// Recover the encapsulated shared secret and set of rights used in the -// /// encapsulation. -// pub fn full_decaps( -// msk: &MasterSecretKey, -// encapsulation: &XEnc, -// ) -> Result<(Secret, HashSet), Error> { -// let A = { -// let c_0 = encapsulation -// .c -// .first() -// .ok_or_else(|| Error::Kem("invalid encapsulation: C is empty".to_string()))?; -// let t_0 = msk -// .tsk -// .tracers -// .front() -// .ok_or_else(|| Error::KeyError("MSK has no tracer".to_string()))?; - -// c_0 * &(&msk.tsk.s / t_0) -// }; - -// let T = { -// let mut hasher = Shake::v256(); -// for c in encapsulation.c.iter() { -// hasher.update(&c.to_bytes()); -// } -// for enc in encapsulation.encapsulations.iter() { -// if let Encapsulation::Hybridized { E, F: _ } = enc { -// hasher.update(&E.serialize()?) -// } -// } -// let mut T = Secret::::new(); -// hasher.finalize(&mut *T); -// T -// }; - -// let mut rights = HashSet::with_capacity(encapsulation.encapsulations.len()); -// let ss = Secret::new(); -// for enc in &encapsulation.encapsulations { -// for (right, secrets) in msk.secrets.iter() { -// for (_, secret) in secrets { -// if let Some(S) = try_decaps(secret, enc, &A, &T)? { -// let (tag, ss) = j_hash(&S, &encapsulation.c, &encapsulation.encapsulations)?; -// if tag == encapsulation.tag { -// drop(ss); -// rights.insert(right.clone()); -// } -// } -// } -// } -// } -// Ok((ss, rights)) -// } +/// Recover the encapsulated shared secret and set of rights used in the +/// encapsulation. +pub fn full_decaps( + msk: &MasterSecretKey, + encapsulation: &XEnc, +) -> Result<(Secret, HashSet), Error> { + let A = { + let c_0 = encapsulation + .c + .first() + .ok_or_else(|| Error::Kem("invalid encapsulation: C is empty".to_string()))?; + let t_0 = msk + .tsk + .tracers + .front() + .map(|(si, _)| si) + .ok_or_else(|| Error::KeyError("MSK has no tracer".to_string()))?; + + c_0 * &(&msk.tsk.s / t_0) + }; + + let T = { + let mut hasher = Shake::v256(); + encapsulation + .c + .iter() + .for_each(|c| hasher.update(&c.to_bytes())); + + if let Encapsulations::HEncs(encs) = &encapsulation.encapsulations { + encs.iter().try_for_each(|(E, _)| { + hasher.update(&E.serialize()?); + Ok::<_, Error>(()) + })?; + } + hasher + }; + + let J = { + let mut hasher = T.clone(); + match &encapsulation.encapsulations { + Encapsulations::HEncs(encs) => encs.iter().for_each(|(_, F)| hasher.update(F)), + Encapsulations::CEncs(encs) => encs.iter().for_each(|F| hasher.update(F)), + } + hasher + }; + + let mut enc_ss = None; + let mut rights = HashSet::with_capacity(encapsulation.count()); + let mut try_decaps = + |right: &Right, K1: &mut EcPoint, K2: Option>, F| { + let S_ij = xor_in_place(H_hash(T.clone(), K1, K2.as_ref()), F); + let (tag_ij, ss) = J_hash(J.clone(), &S_ij); + if encapsulation.tag == tag_ij { + // Fujisaki-Okamoto + let r = G_hash(&S_ij)?; + let c_ij = msk.tsk.set_traps(&r); + if encapsulation.c == c_ij { + K1.zeroize(); + enc_ss = Some(ss); + rights.insert(right.clone()); + } + } + Ok::<_, Error>(()) + }; + + match &encapsulation.encapsulations { + Encapsulations::HEncs(encs) => { + for (E, F) in encs { + for (right, secret_set) in msk.secrets.iter() { + for (is_activated, secret) in secret_set { + if *is_activated { + if let RightSecretKey::Hybridized { sk, dk } = secret { + let mut K1 = R25519::session_key(sk, &A)?; + let K2 = MlKem512::dec(dk, E)?; + try_decaps(right, &mut K1, Some(K2), F)?; + } + } + } + } + } + } + Encapsulations::CEncs(encs) => { + for F in encs { + for (right, secret_set) in msk.secrets.iter() { + for (is_activated, secret) in secret_set { + if *is_activated { + let sk = match secret { + RightSecretKey::Hybridized { sk, .. } => sk, + RightSecretKey::Classic { sk } => sk, + }; + let mut K1 = R25519::session_key(sk, &A)?; + try_decaps(right, &mut K1, None, F)?; + } + } + } + } + } + } + enc_ss + .map(|ss| (ss, rights)) + .ok_or_else(|| Error::Kem("could not open the encapsulation".to_string())) +} /// Updates the MSK such that it has at least one secret per right given, and no /// secret for rights that are not given. Updates hybridization of the remaining diff --git a/src/core/serialization/mod.rs b/src/core/serialization/mod.rs index e389753c..8d9c8e2b 100644 --- a/src/core/serialization/mod.rs +++ b/src/core/serialization/mod.rs @@ -138,15 +138,20 @@ impl Serializable for TracingSecretKey { + to_leb128_len(self.users.len()) + self.users.iter().map(Serializable::length).sum::() + to_leb128_len(self.tracers.len()) - + self.tracers.iter().map(|sk| sk.length()).sum::() + + self + .tracers + .iter() + .map(|(sk, pk)| sk.length() + pk.length()) + .sum::() } fn write(&self, ser: &mut Serializer) -> Result { let mut n = self.s.write(ser)?; n += ser.write_leb128_u64(self.tracers.len() as u64)?; - for sk in &self.tracers { + for (sk, pk) in &self.tracers { n += ser.write(sk)?; + n += ser.write(pk)?; } n = ser.write_leb128_u64(self.users.len() as u64)?; @@ -164,7 +169,8 @@ impl Serializable for TracingSecretKey { let mut tracers = LinkedList::new(); for _ in 0..n_tracers { let sk = de.read()?; - tracers.push_back(sk); + let pk = de.read()?; + tracers.push_back((sk, pk)); } let n_users = ::try_from(de.read_leb128_u64()?)?; diff --git a/src/core/tests.rs b/src/core/tests.rs index e481a017..ceba1b6f 100644 --- a/src/core/tests.rs +++ b/src/core/tests.rs @@ -264,11 +264,11 @@ fn test_reencrypt_with_msk() { assert_eq!(Some(&old_key), decaps(&usk, &old_enc).unwrap().as_ref()); cc.rekey(&mut msk, &ap).unwrap(); - // let new_mpk = msk.mpk().unwrap(); - // let (new_key, new_enc) = cc.recaps(&msk, &new_mpk, &old_enc).unwrap(); - // cc.refresh_usk(&mut msk, &mut usk, true).unwrap(); - // assert_eq!(Some(new_key), decaps(&usk, &new_enc).unwrap()); - // assert_ne!(Some(old_key), decaps(&usk, &new_enc).unwrap()); + let new_mpk = msk.mpk().unwrap(); + let (new_key, new_enc) = cc.recaps(&msk, &new_mpk, &old_enc).unwrap(); + cc.refresh_usk(&mut msk, &mut usk, true).unwrap(); + assert_eq!(Some(new_key), decaps(&usk, &new_enc).unwrap()); + assert_ne!(Some(old_key), decaps(&usk, &new_enc).unwrap()); } #[test]