From 166aa7ec30eaef8e52f390e0447eae174f7b6391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Thu, 23 May 2024 15:14:23 +0200 Subject: [PATCH 1/6] use Kyber CCA --- examples/decrypt.rs | 4 +- src/abe_policy/policy.rs | 1 - src/core/ae.rs | 13 +- src/core/api.rs | 18 +-- src/core/elgamal/mod.rs | 35 +---- src/core/mod.rs | 224 +++++++++++------------------- src/core/postquantum/kyber.rs | 159 +++++++++++---------- src/core/postquantum/mod.rs | 121 +++++++++------- src/core/postquantum/traits.rs | 63 +++++++++ src/core/primitives.rs | 218 +++++++++++++---------------- src/core/serialization/mod.rs | 237 ++++++++++---------------------- src/core/tests.rs | 47 +++++-- src/data_struct/dictionary.rs | 1 - src/data_struct/revision_vec.rs | 2 - src/error.rs | 11 +- 15 files changed, 535 insertions(+), 619 deletions(-) create mode 100644 src/core/postquantum/traits.rs diff --git a/examples/decrypt.rs b/examples/decrypt.rs index 0bfe15ad..080fca22 100644 --- a/examples/decrypt.rs +++ b/examples/decrypt.rs @@ -1,8 +1,8 @@ use cosmian_cover_crypt::core::EncryptedHeader; -const USK: &str = "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"; +const USK: &str = "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"; -const HEADER: &str = "xjVmiELa1aImn5Z5KWZpkALkqkypgonZhhv6ZTCdtUQVjBz09pM7k1qLg9wR6lWiJi49xeIspSln4GoFwM7dJgVvdWk48RlGpUfTEIQX+zk7AQEjIukFORWf4BQTxkQ/CoZ2ivdSK0vYilRIeyOnSZ9rJU9Q7x0Rhih0NMut7KCjhoJdgkUAjilvqr9880EaoS90XhSHvzSFu5dV8i7tWHleUWdUxMiCWF1kG88H7oxGn+8bCgM/7xT8knVEbJ+Ts6m8aU9fy3Za6Of5G/eEU9j0q+DU+U3GFGkJu0IriAPTBoNh34JBe+JTecT5k92AXU7TzJV/rFYKlLVWgGk/DcTHCmZjzqr9uEBHLZWaXGhVlzpjQgqYY/bI03jx6twk78LyKLFTbLJo8f5haYxtH/47Qt5lisXkoo6ACENlOG07C8Y6Q6j/cf8CmWp9qCHcybpnU6IwZpjW8yo+6HULGpqp7cBq8L9bFZp5MYZg1xqZ8pi9rwz6jPfFAOHeX8Ke3kEK2obuy8xncbm+sVnuNLh8jaLfN04WnC3T5d/GHuSpY53zWF4cNO5+Sz04VpE0tW4ZymI6DB5bYZJts4fJEXO1jlywVL9Fea4PVsHFrdxiO72BKSBSvu63NU6khjpndP93Zv1/eyXulkKGE1YKibML/48gClU9h/s7GHqncEQ0VjB5i2C1tm0UV1imbJKspDxAl5VGJoTJZeM+4phFJVL4KpQ8Sny+WA1gTmcl0OHGD9R2YdaAM8g+j/kvCuaK8eoRi/K27GSMJry8DuVu3Zm5K++xatAgZsa9+LaJFtGrPh8Xikgks+NBQeXj3bKOHGEe0lXSmKus2n8S/MWNQr8LTyKbyGdKzSWk9RLk+OtLwRDpkUIwZnliwJJgVp4EMByAt9x56sr+V60av9PDwt0gvvbYnKj8AB4mc8W8gUkDKjCau9f0VU8FjeSd0VJOrQjiH6EY56kNltJnN8mAen2HU5LlhsIWsMnvAT/xOZkDV6OJ3zgGZEZRqJdR7R6era4QeUGBUIELmXJjSOutwmT/Q3lMxqO5MrBcO93FEMzYbGZKsmjPY2D+o8SNtUJnwqFVTn5JZBMLLopQkGnuYxnCE3r3jxLailt4cgg7EJcXZhb8xIr7af1ndu2Ewb4T6Jgc37jK55tOijSjJr7qw88IUAHp/yacJe/LsFbNsxaUtjhtdqKdbm1PBbnNFOv1+O43hcoW7PgxGk7Pt1CUAJ+gI15rAmRo/s7dFdI+TLGGHhnQFXiQO+1fsWydjjR/AV1grEj56NZNKwkH6FbThRI95D2y1V33+Tdg3nu6jbZB6MN1SMnBCURJoPRN5YB5Ed263j7v+XcsLKoXf14QNYuTCPqyigEoWEsd+yyjt2yWTjZtyte+FRdgauG+12GBrXzR6UnuaXWtl7Dl0GPmtFUALnIbUnLwQx5fBwpKluOMTpcrDHUJGupnZCdXuKJD8aF0fqzZjKkkpGMRm7yG32PeqFOzx9gKPnHUT7EOEKXMPK/NIFSXpez9GbDRkzeCXrweMiShzFBb2kGC/CIbFRJJ5wA="; +const HEADER: &str = "/m73Gzm5LhMZmNEpFubAuQJUu3QzyOH1fJCrJynNWprMuzGViQ1nlE6v111VBxt8cnpYD8oR1TAHUBBFm2f0Tpz11KNRhs1KeNLW8Pjs4c9bAQH8CAAVK0PKJXcjuDokdVD1WBYkCZEEXmG508jhFJT7UJ/PiggCLI+hcubhLo+9PVQPCSpkYBmoQQNpDhsKMnRku3NyGnQJHF/3du5LOB5me5ln9BphUiIAWVg6u6UMnD5+TqzLzxqGM85cbh4GloEs/0DvlHv2Uy1GLo51JnvUIysh7TiShikYfE5EH3FQWSJIdrZLhmbUKW3Np47AEfFgNXfu3FWlgbrzbkfnRN7HHBXWJIwlzp+A1xstcO7zHzs0AzwJ8hlk2O111uDW3ppQguKHfwIg1v1LtFxA4pB0SQ207e18yrd91KKIBpSywOgbc9zDKXxD+BA3MFj1xjvlRqzkQCYwrAP4RDYkzZ2V0z8Xwmbkl6jabI6jTKyYlU3s1ZLxxemKxWChWb0VYb4v9l8GAXlO0V+iSplwVFZOOsrWiCFwd521TSQwTVBNnDQJcz0bX9wPrK8HiOKZyVZB8RfPWbH7PhY+2yrvn+QF+GZRKckXU34kJgZBZ9nn3leRo1buvg/RGPw9vMmueLnhmfJhVTSDLnlQ7v+jIHMR3mxsW7SJ7e5YWITkSb3VqFyzdSINc39FzRxNbEXuSkpyqD9DV2LVXmUlVJwmGjiUCB+z/K9j2d0i1SFRLAjG3J3gQl68lexn0WJF47dK9l9PyuCYWa1vmY2+SZW5oJq/fkbX1FUuWcGgtFWoHHCR89e7m2eD20kwVmirjaGgIIwifXXNjrGckoDT82TZTBidnqIZFy1nF4CztBJMrjhPa1a2j2bSJWpS/qjmuUJrkFs7gozNm/cvJfIoek994AwGy9t9Z1WcpL90k9CCdzSpxoaOeV3747SzMUN7fbCfeGg/LqxmBjTViTFEpAWCz50vYqS48nutf4wxcilvFxtvJD6/iH/7dh1x48Q6E/DZ8bAQWml4Usd/g/cu3WR327H5qxazUL28vGDZGi/S4hBtvs6rhk+utZXgbwsHe1/+d4Bdv+PjZkRpgl4tOJl5wtgdgmIQNbZZfegIs51NKF9+OzxasbMoJ9ZY674juZrIwuPafGE63oRldT+v5A8HAMSsO8O+5awKekyoHXooPlD/e0rfw/c0tGHprGNNM0fh1VThll7ZhYfvZlH0sAFxqTlln2xnx3Uw4s6g25iJRs8m7NC4rbWo93fNki+7Fo615YLFvcH4dlVay3705nTjnqbwhxsnYczWpSWtey/1VmPmUxWFxUXfya73Rc242ZbSZzmD2GLAeAHY7QYhQJZzbV6/iC+f+c8hqbE0F1K1xG8UMoA/sXPnfHHZfEMSEcTpR5x6tX4UdB3kfGDM9L0cVkX6b2uvt20C3LHCvVQYn/Fi7iSzRS64tRIqHnk88Sj+9b0/uFigpTh6fjcgRfLr1Ys58qhl/BoBx4JyvGpDKcjGHVBnHcXAY+RguQHUTabqdOBXSKK49ZzSRlTSPdKjYOSqTAY5kfjwYzwMh9HiFdnyV3mXYYukDUjB/3XQO6JPgYsF8oVsnEuzKX6kGw/FN0apflTfQcVOx9wgxge3A10fAA=="; fn main() { use base64::{ diff --git a/src/abe_policy/policy.rs b/src/abe_policy/policy.rs index 320ea04e..f40616de 100644 --- a/src/abe_policy/policy.rs +++ b/src/abe_policy/policy.rs @@ -242,7 +242,6 @@ impl Policy { /// - D1::A1 && D2::B2 /// - D2::A2 /// - D2::B2 -#[allow(dead_code)] pub fn combine( dimensions: &[(&String, &Dimension)], // TODO: signature depends on the HashMap iterator type ) -> Vec<(Vec, EncryptionHint, AttributeStatus)> { diff --git a/src/core/ae.rs b/src/core/ae.rs index 05a785ef..25c9eb8f 100644 --- a/src/core/ae.rs +++ b/src/core/ae.rs @@ -2,6 +2,7 @@ use cosmian_crypto_core::{ reexport::rand_core::CryptoRngCore, Aes256Gcm, Dem, FixedSizeCBytes, Instantiable, Nonce, RandomFixedSizeCBytes, SymmetricKey, }; +use zeroize::Zeroizing; use crate::Error; @@ -9,9 +10,9 @@ use crate::Error; pub trait AE { /// Encrypts the given plaintext `ptx` using the given `key`. fn encrypt( + rng: &mut impl CryptoRngCore, key: &SymmetricKey, ptx: &[u8], - rng: &mut impl CryptoRngCore, ) -> Result, Error>; /// Decrypts the given ciphertext `ctx` using the given `key`. @@ -19,21 +20,24 @@ pub trait AE { /// # Error /// /// Returns an error if the integrity of the ciphertext could not be verified. - fn decrypt(key: &SymmetricKey, ctx: &[u8]) -> Result, Error>; + fn decrypt(key: &SymmetricKey, ctx: &[u8]) -> Result>, Error>; } impl AE<{ Self::KEY_LENGTH }> for Aes256Gcm { fn encrypt( + rng: &mut impl CryptoRngCore, key: &SymmetricKey<{ Self::KEY_LENGTH }>, ptx: &[u8], - rng: &mut impl CryptoRngCore, ) -> Result, Error> { let nonce = Nonce::<{ Self::NONCE_LENGTH }>::new(&mut *rng); let ciphertext = Self::new(key).encrypt(&nonce, ptx, None)?; Ok([nonce.as_bytes(), &ciphertext].concat()) } - fn decrypt(key: &SymmetricKey<{ Self::KEY_LENGTH }>, ctx: &[u8]) -> Result, Error> { + fn decrypt( + key: &SymmetricKey<{ Self::KEY_LENGTH }>, + ctx: &[u8], + ) -> Result>, Error> { if ctx.len() < Self::NONCE_LENGTH { return Err(Error::CryptoCoreError( cosmian_crypto_core::CryptoCoreError::DecryptionError, @@ -43,5 +47,6 @@ impl AE<{ Self::KEY_LENGTH }> for Aes256Gcm { Self::new(key) .decrypt(&nonce, &ctx[Self::NONCE_LENGTH..], None) .map_err(Error::CryptoCoreError) + .map(Zeroizing::new) } } diff --git a/src/core/api.rs b/src/core/api.rs index 647aaeed..dfc13b94 100644 --- a/src/core/api.rs +++ b/src/core/api.rs @@ -4,10 +4,11 @@ use std::{ }; use cosmian_crypto_core::{kdf256, reexport::rand_core::SeedableRng, CsRng, Secret, SymmetricKey}; +use zeroize::Zeroizing; use super::{ ae::AE, - primitives::{mpk_keygen, prune, update_coordinate_keys, usk_keygen}, + primitives::{prune, update_coordinate_keys, usk_keygen}, MIN_TRACING_LEVEL, }; use crate::{ @@ -67,7 +68,7 @@ impl Covercrypt { (EncryptionHint::Classic, AttributeStatus::EncryptDecrypt), )]), )?; - let mpk = mpk_keygen(&msk)?; + let mpk = msk.mpk()?; Ok((msk, mpk)) } @@ -92,8 +93,7 @@ impl Covercrypt { msk, policy.generate_universal_coordinates()?, )?; - let mpk = mpk_keygen(msk)?; - Ok(mpk) + msk.mpk() } /// Generates new keys for each coordinate in the semantic space of the @@ -112,7 +112,7 @@ impl Covercrypt { msk, policy.generate_semantic_space_coordinates(ap)?, )?; - mpk_keygen(msk) + msk.mpk() } /// Removes all but the latest secret of each coordinate in the semantic @@ -130,7 +130,7 @@ impl Covercrypt { msk, &policy.generate_semantic_space_coordinates(access_policy)?, ); - mpk_keygen(msk) + msk.mpk() } /// Generates a USK associated to the given access policy. @@ -255,7 +255,7 @@ pub trait CovercryptPKE { usk: &UserSecretKey, ciphertext: &[u8], enc: &Encapsulation, - ) -> Result>, Error>; + ) -> Result>>, Error>; } impl> CovercryptPKE for Covercrypt { @@ -276,7 +276,7 @@ impl> CovercryptPKE fo let mut sym_key = SymmetricKey::default(); kdf256!(&mut sym_key, &seed); let mut rng = self.rng.lock().expect("poisoned lock"); - let res = E::encrypt(&sym_key, plaintext, &mut *rng)?; + let res = E::encrypt(&mut *rng, &sym_key, plaintext)?; Ok((enc, res)) } @@ -285,7 +285,7 @@ impl> CovercryptPKE fo usk: &UserSecretKey, ciphertext: &[u8], enc: &Encapsulation, - ) -> Result>, Error> { + ) -> Result>>, Error> { if SEED_LENGTH < KEY_LENGTH { return Err(Error::ConversionFailed(format!( "insufficient entropy to generate a {}-byte key from a {}-byte seed", diff --git a/src/core/elgamal/mod.rs b/src/core/elgamal/mod.rs index 7f826a29..00b4ae98 100644 --- a/src/core/elgamal/mod.rs +++ b/src/core/elgamal/mod.rs @@ -31,12 +31,6 @@ use crate::Error; pub struct Keypair(Scalar, Option); impl Keypair { - /// Creates a new keypair. - #[inline(always)] - pub fn new(sk: Scalar, pk: EcPoint) -> Self { - Self(sk, Some(pk)) - } - /// Returns a new random keypair. #[must_use] pub fn random(rng: &mut impl CryptoRngCore) -> Self { @@ -57,18 +51,6 @@ impl Keypair { pub fn pk(&self) -> Option<&EcPoint> { self.1.as_ref() } - - /// Deprecate this keypair. - #[inline(always)] - pub fn deprecate(&mut self) { - self.1 = None - } - - /// Returns true if the given secret key is contained in this keypair. - #[inline(always)] - pub fn contains(&self, sk: &Scalar) -> bool { - &self.0 == sk - } } impl Serializable for Keypair { @@ -178,29 +160,20 @@ pub fn unmask( #[cfg(test)] mod tests { use cosmian_crypto_core::{ - bytes_ser_de::Serializable, - reexport::rand_core::{CryptoRngCore, SeedableRng}, - CsRng, Secret, + bytes_ser_de::Serializable, reexport::rand_core::SeedableRng, CsRng, Secret, }; - use super::{mask, unmask, EcPoint, Keypair, Scalar}; + use super::{mask, unmask, Keypair}; /// Arbitrary plaintext length. const PTX_LENGTH: usize = 108; - /// Generates a random keypair. - pub fn keygen(rng: &mut impl CryptoRngCore) -> Keypair { - let sk = Scalar::new(rng); - let pk = EcPoint::from(&sk); - Keypair::new(sk, pk) - } - #[test] fn test_elgamal_pke() { let mut rng = CsRng::from_entropy(); let ptx = Secret::::random(&mut rng); - let ephemeral_keypair = keygen(&mut rng); - let recipient_keypair = keygen(&mut rng); + let ephemeral_keypair = Keypair::random(&mut rng); + let recipient_keypair = Keypair::random(&mut rng); let mut ctx = [0; PTX_LENGTH]; mask( &mut ctx, diff --git a/src/core/mod.rs b/src/core/mod.rs index b76f1397..55c74a91 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,9 +1,10 @@ use std::{ collections::{HashMap, HashSet, LinkedList}, hash::Hash, + ops::Deref, }; -use cosmian_crypto_core::{reexport::rand_core::CryptoRngCore, SymmetricKey}; +use cosmian_crypto_core::{reexport::rand_core::CryptoRngCore, Aes256Gcm, SymmetricKey}; use crate::{ abe_policy::Coordinate, @@ -28,6 +29,8 @@ mod tests; use elgamal::{EcPoint, Scalar}; pub use encrypted_header::{CleartextHeader, EncryptedHeader}; +use self::postquantum::{MlKemAesPke, PkeTrait}; + /// The length of the secret encapsulated by Covercrypt. /// /// They are 32 bytes long to enable reaching 128 bits of post-quantum security @@ -61,35 +64,57 @@ pub const MIN_TRACING_LEVEL: usize = 1; /// The Covercrypt subkeys hold the DH secret key associated to a coordinate. /// Subkeys can be hybridized, in which case they also hold a PQ-KEM secret key. #[derive(Clone, Debug, PartialEq, Eq)] -enum CoordinateSecretKey { - Hybridized { - postquantum_sk: postquantum::SecretKey, - elgamal_sk: Scalar, - }, - Classic { - elgamal_sk: Scalar, - }, +struct CoordinateSecretKey { + el: Scalar, + pq: Option, +} + +impl CoordinateSecretKey { + /// Generates a new random coordinate keypair cryptographically bound to the + /// Covercrypt binding point `h`. + fn random(rng: &mut impl CryptoRngCore, hybridize: bool) -> Result { + let el = Scalar::new(rng); + let pq = if hybridize { + let (pq, _) = MlKemAesPke.keygen(rng)?; + Some(pq) + } else { + None + }; + Ok(Self { el, pq }) + } + + /// Generates the associated coordinate public key. + #[must_use] + fn public_key(&self, h: &EcPoint) -> CoordinatePublicKey { + let el_pk = h * &self.el; + let pq_pk = self.pq.as_ref().map(|sk| sk.pk()); + CoordinatePublicKey { + el: el_pk, + pq: pq_pk, + } + } + + /// Returns true if this coordinate keypair is hybridized. + fn is_hybridized(&self) -> bool { + self.pq.is_some() + } + + fn drop_hybridization(&mut self) { + self.pq = None; + } } /// The Covercrypt public keys hold the DH secret public key associated to a coordinate. /// Subkeys can be hybridized, in which case they also hold a PQ-KEM public key. #[derive(Clone, Debug, PartialEq, Eq)] -enum CoordinatePublicKey { - Hybridized { - postquantum_pk: postquantum::PublicKey, - elgamal_pk: EcPoint, - }, - Classic { - elgamal_pk: EcPoint, - }, +struct CoordinatePublicKey { + el: EcPoint, + pq: Option, } impl CoordinatePublicKey { pub fn is_hybridized(&self) -> bool { - match self { - Self::Hybridized { .. } => true, - Self::Classic { .. } => false, - } + self.pq.is_some() } pub fn assert_homogeneity(subkeys: &[&Self]) -> Result<(), Error> { @@ -107,110 +132,6 @@ impl CoordinatePublicKey { } } -/// ElGamal keypair optionally hybridized with a post-quantum KEM associated to -/// a coordinate. -#[derive(Debug, Clone, PartialEq, Eq)] -struct CoordinateKeypair { - elgamal_keypair: elgamal::Keypair, - postquantum_keypair: Option, -} - -impl CoordinateKeypair { - /// Generates a new random coordinate keypair cryptographically bound to the - /// Covercrypt binding point `h`. - #[must_use] - fn random(rng: &mut impl CryptoRngCore, h: &EcPoint, hybridize: bool) -> Self { - let elgamal_sk = Scalar::new(rng); - let elgamal_pk = h * &elgamal_sk; - let elgamal_keypair = elgamal::Keypair::new(elgamal_sk, elgamal_pk); - let postquantum_keypair = if hybridize { - Some(postquantum::Keypair::random(rng)) - } else { - None - }; - CoordinateKeypair { - elgamal_keypair, - postquantum_keypair, - } - } - - /// Returns a copy of the public key. - #[must_use] - fn public_key(&self) -> Option { - match ( - self.elgamal_keypair.pk().cloned(), - &self.postquantum_keypair, - ) { - (Some(elgamal_pk), None) => Some(CoordinatePublicKey::Classic { elgamal_pk }), - (Some(elgamal_pk), Some(postquantum_keypair)) => { - let postquantum_pk = postquantum_keypair.pk().clone(); - Some(CoordinatePublicKey::Hybridized { - elgamal_pk, - postquantum_pk, - }) - } - (None, _) => None, - } - } - - /// Returns a copy of the secret key. - #[must_use] - fn secret_key(&self) -> CoordinateSecretKey { - let elgamal_sk = self.elgamal_keypair.sk().clone(); - if let Some(keypair) = &self.postquantum_keypair { - let postquantum_sk = keypair.sk().clone(); - CoordinateSecretKey::Hybridized { - elgamal_sk, - postquantum_sk, - } - } else { - CoordinateSecretKey::Classic { elgamal_sk } - } - } - - /// Returns true if the given coordinate secret key is contained in this keypair. - fn contains(&self, coordinate_sk: &CoordinateSecretKey) -> bool { - match (coordinate_sk, &self.postquantum_keypair) { - (CoordinateSecretKey::Classic { elgamal_sk }, None) => { - self.elgamal_keypair.contains(elgamal_sk) - } - ( - CoordinateSecretKey::Hybridized { - postquantum_sk, - elgamal_sk, - }, - Some(postquantum_keypair), - ) => { - self.elgamal_keypair.contains(elgamal_sk) - && postquantum_keypair.contains(postquantum_sk) - } - (CoordinateSecretKey::Hybridized { .. }, None) - | (CoordinateSecretKey::Classic { .. }, Some(_)) => false, - } - } - - /// Returns true if this coordinate keypair is hybridized. - fn is_hybridized(&self) -> bool { - self.postquantum_keypair.is_some() - } - - /// Drop the ElGamal public key of this coordinate keypair. - /// - /// Future MPK will be generated without any key for this coordinate, thus - /// disabling encryption for this coordinate. - fn drop_encryption_key(&mut self) { - self.elgamal_keypair.deprecate(); - } - - /// Drop the post-quantum part of this coordinate keypair. - /// - /// Future MPK will be generated without post-quantum key, thus disabling - /// hybridized encryption. - fn drop_hybridization(&mut self) { - self.postquantum_keypair = None; - } -} - /// Covercrypt user IDs are used to make user keys unique and traceable. /// /// They are composed of a sequence of `LENGTH` scalars. @@ -343,7 +264,7 @@ impl TracingPublicKey { pub struct MasterSecretKey { s: Scalar, tsk: TracingSecretKey, - coordinate_keypairs: RevisionMap, + coordinate_secrets: RevisionMap, signing_key: Option>, } @@ -421,27 +342,35 @@ impl MasterSecretKey { coordinates: impl Iterator + 'a, ) -> impl Iterator> + 'a { coordinates.map(|coordinate| { - self.coordinate_keypairs + self.coordinate_secrets .get_latest(&coordinate) .ok_or(Error::KeyError(format!( "MSK has no key for coordinate {coordinate:?}" ))) - .map(CoordinateKeypair::secret_key) - .map(|key| (coordinate, key)) + .cloned() + .map(|(_, key)| (coordinate, key)) }) } - /// Returns the most recent public key associated to each coordinate. - fn get_latest_coordinate_pk( - &self, - ) -> impl Iterator + '_ { - self.coordinate_keypairs - .iter() - .filter_map(|(coordinate, keypairs)| { - let pk: Option = - keypairs.front().and_then(|keypair| keypair.public_key()); - pk.map(|pk| (coordinate.clone(), pk)) - }) + /// Generates a new MPK holding the latest public information of each universal coordinate. + pub fn mpk(&self) -> Result { + Ok(MasterPublicKey { + h: self.binding_point(), + tpk: self.tsk.tpk(), + coordinate_keys: self + .coordinate_secrets + .iter() + .filter_map(|(coordinate, secrets)| { + secrets.front().and_then(|(is_activated, s)| { + if *is_activated { + Some((coordinate.clone(), s.public_key(&self.binding_point()))) + } else { + None + } + }) + }) + .collect(), + }) } } @@ -493,7 +422,18 @@ pub struct UserSecretKey { #[derive(Debug, Clone, Hash, PartialEq, Eq)] enum SeedEncapsulation { Classic([u8; SEED_LENGTH]), - Hybridized(postquantum::Ciphertext), + Hybridized(>::Ciphertext), +} + +impl Deref for SeedEncapsulation { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + match self { + SeedEncapsulation::Classic(ctx) => ctx, + SeedEncapsulation::Hybridized(ctx) => ctx, + } + } } /// Covercrypt encapsulation. @@ -509,7 +449,7 @@ enum SeedEncapsulation { pub struct Encapsulation { tag: Tag, traps: Vec, - coordinate_encapsulations: HashSet, + coordinate_encapsulations: Vec, } impl Encapsulation { diff --git a/src/core/postquantum/kyber.rs b/src/core/postquantum/kyber.rs index 56e2c7b3..33e2cd82 100644 --- a/src/core/postquantum/kyber.rs +++ b/src/core/postquantum/kyber.rs @@ -8,16 +8,18 @@ use std::ops::{Deref, DerefMut}; use crate::Error; use cosmian_crypto_core::{bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore, Secret}; use pqc_kyber::{ - indcpa::{indcpa_dec, indcpa_enc, indcpa_keypair}, - KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES, KYBER_SYMBYTES, + KYBER_CIPHERTEXTBYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES, + KYBER_PUBLICKEYBYTES, KYBER_SECRETKEYBYTES, KYBER_SSBYTES, }; +use super::KemTrait; + /// Kyber public key length #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PublicKey(Box<[u8; Self::LENGTH]>); impl PublicKey { - pub const LENGTH: usize = KYBER_INDCPA_PUBLICKEYBYTES; + pub const LENGTH: usize = KYBER_PUBLICKEYBYTES; } impl Deref for PublicKey { @@ -34,6 +36,12 @@ impl DerefMut for PublicKey { } } +impl From<[u8; Self::LENGTH]> for PublicKey { + fn from(bytes: [u8; Self::LENGTH]) -> Self { + Self(Box::new(bytes)) + } +} + impl Serializable for PublicKey { type Error = Error; @@ -61,7 +69,15 @@ impl Serializable for PublicKey { pub struct SecretKey(Secret<{ Self::LENGTH }>); impl SecretKey { - pub const LENGTH: usize = KYBER_INDCPA_SECRETKEYBYTES; + pub const LENGTH: usize = KYBER_SECRETKEYBYTES; + + pub fn pk(&self) -> PublicKey { + const PK_START: usize = KYBER_INDCPA_SECRETKEYBYTES; + const PK_STOP: usize = PK_START + KYBER_INDCPA_PUBLICKEYBYTES; + let mut pk = [0; KYBER_PUBLICKEYBYTES]; + pk.copy_from_slice(&self[PK_START..PK_STOP]); + PublicKey::from(pk) + } } impl Deref for SecretKey { @@ -78,6 +94,12 @@ impl DerefMut for SecretKey { } } +impl From<&mut [u8; Self::LENGTH]> for SecretKey { + fn from(bytes: &mut [u8; Self::LENGTH]) -> Self { + Self(Secret::from_unprotected_bytes(bytes)) + } +} + impl Serializable for SecretKey { type Error = Error; @@ -102,6 +124,10 @@ impl Serializable for SecretKey { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Ciphertext(Box<[u8; Self::LENGTH]>); +impl Ciphertext { + pub const LENGTH: usize = KYBER_CIPHERTEXTBYTES; +} + impl Deref for Ciphertext { type Target = [u8]; @@ -122,8 +148,20 @@ impl Default for Ciphertext { } } -impl Ciphertext { - pub const LENGTH: usize = KYBER_INDCPA_BYTES; +impl From<[u8; Self::LENGTH]> for Ciphertext { + fn from(bytes: [u8; Self::LENGTH]) -> Self { + Self(Box::new(bytes)) + } +} + +impl TryFrom<&[u8]> for Ciphertext { + type Error = Error; + + fn try_from(bytes: &[u8]) -> Result { + let bytes = <[u8; Self::LENGTH]>::try_from(bytes) + .map_err(|e| Error::ConversionFailed(e.to_string()))?; + Ok(Self::from(bytes)) + } } impl Serializable for Ciphertext { @@ -147,68 +185,51 @@ impl Serializable for Ciphertext { } } -/// Generates a new Kyber key pair. -pub fn keygen(rng: &mut impl CryptoRngCore) -> (SecretKey, PublicKey) { - let (mut sk, mut pk) = ( - SecretKey(Secret::new()), - PublicKey(Box::new([0; PublicKey::LENGTH])), - ); - indcpa_keypair(&mut pk, &mut sk, None, rng); - (sk, pk) -} - -/// Encrypts the given secret using a post-quantum secure PKE. -/// -/// # Security -/// -/// The current implementation uses IND-CPA-Kyber 768, but plan for generalizing -/// it is on the way. It provides TODO bits of post-quantum security. -pub fn encrypt( - rng: &mut impl CryptoRngCore, - postquantum_pk: &PublicKey, - ptx: &[u8], -) -> Result { - if KYBER_SYMBYTES != ptx.len() { - return Err(Error::OperationNotPermitted(format!( - "Kyber plaintext needs to be {KYBER_SYMBYTES} bytes: {} given", - ptx.len() - ))); - } - let mut ctx = Ciphertext::default(); - let coin = Secret::::random(rng); - indcpa_enc(&mut ctx, ptx, postquantum_pk, &coin); - Ok(ctx) -} - -/// Decrypts the given secret using a post-quantum secure PKE. -/// -/// # Security -/// -/// The current implementation uses IND-CPA-Kyber 768, but plan for generalizing -/// it is on the way. It provides TODO bits of post-quantum security. -pub fn decrypt(postquantum_sk: &SecretKey, ctx: &Ciphertext) -> Secret { - let mut secret = Secret::::default(); - indcpa_dec(&mut secret, ctx, postquantum_sk); - secret -} - -#[cfg(test)] -mod tests { - use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng, Secret}; - - use super::{decrypt, encrypt, keygen, KYBER_SYMBYTES}; - - #[test] - fn test_kyber() { - let mut rng = CsRng::from_entropy(); - let ptx = Secret::::random(&mut rng); - let keypair = keygen(&mut rng); - let ctx = encrypt(&mut rng, &keypair.1, &ptx) - .expect("failed encryption with keypair {keypair:#?} and plaintext {ptx:#?}"); - let res = decrypt(&keypair.0, &ctx); - assert_eq!( - ptx, res, - "wrong decryption with keypair {keypair:#?} and plaintext {ptx:#?}" - ) +pub type SharedSecret = Secret; + +#[derive(Debug, Default)] +pub struct Kyber; + +impl KemTrait for Kyber { + type Error = Error; + + type Encapsulation = Ciphertext; + + type SecretKey = SecretKey; + + type PublicKey = PublicKey; + + type SharedSecret = SharedSecret; + + fn keygen( + &self, + rng: &mut impl CryptoRngCore, + ) -> Result<(Self::SecretKey, Self::PublicKey), Self::Error> { + let mut keypair = pqc_kyber::keypair(rng); + Ok(( + SecretKey::from(&mut keypair.secret), + PublicKey::from(keypair.public), + )) + } + + fn encaps( + &self, + rng: &mut impl CryptoRngCore, + pk: &Self::PublicKey, + ) -> Result<(Self::SharedSecret, Self::Encapsulation), Self::Error> { + let (enc, mut secret) = pqc_kyber::encapsulate(pk, rng)?; + Ok(( + Secret::from_unprotected_bytes(&mut secret), + Ciphertext::from(enc), + )) + } + + fn decaps( + &self, + sk: &Self::SecretKey, + encapsulation: &Self::Encapsulation, + ) -> Result { + let mut secret = pqc_kyber::decapsulate(encapsulation, sk)?; + Ok(Secret::from_unprotected_bytes(&mut secret)) } } diff --git a/src/core/postquantum/mod.rs b/src/core/postquantum/mod.rs index 7cbe03be..a8b0f6ae 100644 --- a/src/core/postquantum/mod.rs +++ b/src/core/postquantum/mod.rs @@ -1,77 +1,94 @@ mod kyber; +mod traits; -use cosmian_crypto_core::{bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore}; -pub use kyber::{decrypt, encrypt, keygen, Ciphertext, PublicKey, SecretKey}; +use cosmian_crypto_core::{reexport::rand_core::CryptoRngCore, Aes256Gcm, SymmetricKey}; +pub use kyber::{PublicKey, SecretKey}; +pub use traits::{KemTrait, PkeTrait}; use crate::Error; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Keypair(SecretKey, PublicKey); +use super::ae::AE; -impl Keypair { - /// Returns a new random keypair. - #[must_use] - pub fn random(rng: &mut impl CryptoRngCore) -> Self { - let (sk, pk) = keygen(rng); - Self(sk, pk) - } - - /// Returns a reference on the secret key. - #[must_use] - pub fn sk(&self) -> &SecretKey { - &self.0 - } - - /// Returns a reference on the public key. - #[must_use] - pub fn pk(&self) -> &PublicKey { - &self.1 - } - - /// Returns true if the given secret key is contained in this keypair. - pub fn contains(&self, sk: &SecretKey) -> bool { - &self.0 == sk - } -} +#[derive(Debug, Default)] +pub struct MlKemAesPke; -impl Serializable for Keypair { +impl PkeTrait<{ Aes256Gcm::KEY_LENGTH }> for MlKemAesPke { + type Kem = kyber::Kyber; + type Ae = Aes256Gcm; + type Ciphertext = Vec; type Error = Error; - fn length(&self) -> usize { - self.0.length() + self.1.length() + fn keygen( + &self, + rng: &mut impl CryptoRngCore, + ) -> Result< + ( + ::SecretKey, + ::PublicKey, + ), + Self::Error, + > { + kyber::Kyber.keygen(rng) } - fn write( + fn encrypt( &self, - ser: &mut cosmian_crypto_core::bytes_ser_de::Serializer, - ) -> Result { - let mut n = ser.write(&self.0)?; - n += ser.write(&self.1)?; - Ok(n) + rng: &mut impl CryptoRngCore, + pk: &::PublicKey, + ptx: &[u8], + ) -> Result { + let (secret, enc) = kyber::Kyber.encaps(rng, pk)?; + let key = SymmetricKey::derive(&secret, &enc)?; + let ctx = Aes256Gcm::encrypt(rng, &key, ptx)?; + Ok([&*enc, &ctx].concat()) } - fn read(de: &mut cosmian_crypto_core::bytes_ser_de::Deserializer) -> Result { - let sk = de.read()?; - let pk = de.read()?; - Ok(Self(sk, pk)) + fn decrypt( + &self, + sk: &::SecretKey, + ctx: &Self::Ciphertext, + ) -> Result>, Error> { + // A ciphertext contains at least a Kyber encapsulation, an AES MAC and a nonce. + const EXPECTED_LENGTH: usize = + kyber::Ciphertext::LENGTH + Aes256Gcm::MAC_LENGTH + Aes256Gcm::NONCE_LENGTH; + + if ctx.len() < EXPECTED_LENGTH { + return Err(Error::ConversionFailed(format!( + "ML-KEM/AES PKE ciphertext size too small: {} (should be at least {})", + ctx.len(), + EXPECTED_LENGTH + ))); + } + + let encapsulation = kyber::Ciphertext::try_from(&ctx[..kyber::Ciphertext::LENGTH])?; + let aes_ctx = &ctx[kyber::Ciphertext::LENGTH..]; + let secret = kyber::Kyber.decaps(sk, &encapsulation)?; + let key = SymmetricKey::derive(&secret, &encapsulation)?; + Aes256Gcm::decrypt(&key, aes_ctx) } } #[cfg(test)] mod tests { - use cosmian_crypto_core::{ - bytes_ser_de::Serializable, reexport::rand_core::SeedableRng, CsRng, - }; + use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng}; - use super::Keypair; + use super::{MlKemAesPke, PkeTrait}; + + #[test] + fn test_encrypt_decrypt() { + let mut rng = CsRng::from_entropy(); + let pke = MlKemAesPke; + let ptx = b"a secret text"; + let (sk, pk) = pke.keygen(&mut rng).unwrap(); + let ctx = pke.encrypt(&mut rng, &pk, ptx).unwrap(); + let res = pke.decrypt(&sk, &ctx).unwrap(); + assert_eq!(ptx, &**res); + } #[test] - fn test_postquantum_keypair_serialization() { + fn test_pk_from_sk() { let mut rng = CsRng::from_entropy(); - let keypair = Keypair::random(&mut rng); - let bytes = keypair.serialize().unwrap(); - assert_eq!(bytes.len(), keypair.length()); - let keypair_ = Keypair::deserialize(&bytes).unwrap(); - assert_eq!(keypair, keypair_); + let (sk, pk) = MlKemAesPke.keygen(&mut rng).unwrap(); + assert_eq!(pk, sk.pk()); } } diff --git a/src/core/postquantum/traits.rs b/src/core/postquantum/traits.rs new file mode 100644 index 00000000..05cea4b7 --- /dev/null +++ b/src/core/postquantum/traits.rs @@ -0,0 +1,63 @@ +use crate::core::ae::AE; +use cosmian_crypto_core::reexport::rand_core::CryptoRngCore; +use zeroize::Zeroizing; + +pub trait KemTrait { + type Error: std::error::Error; + type Encapsulation; + type SecretKey; + type PublicKey; + type SharedSecret; + + fn keygen( + &self, + rng: &mut impl CryptoRngCore, + ) -> Result<(Self::SecretKey, Self::PublicKey), Self::Error>; + + fn encaps( + &self, + rng: &mut impl CryptoRngCore, + pk: &Self::PublicKey, + ) -> Result<(Self::SharedSecret, Self::Encapsulation), Self::Error>; + + fn decaps( + &self, + sk: &Self::SecretKey, + encapsulation: &Self::Encapsulation, + ) -> Result; +} + +pub trait PkeTrait { + type Ae: AE; + + type Kem: KemTrait; + + type Ciphertext; + + type Error: std::error::Error + From<::Error>; + + #[allow(clippy::type_complexity)] + fn keygen( + &self, + rng: &mut impl CryptoRngCore, + ) -> Result< + ( + ::SecretKey, + ::PublicKey, + ), + Self::Error, + >; + + fn encrypt( + &self, + rng: &mut impl CryptoRngCore, + pk: &::PublicKey, + ptx: &[u8], + ) -> Result; + + fn decrypt( + &self, + sk: &::SecretKey, + ctx: &Self::Ciphertext, + ) -> Result>, Self::Error>; +} diff --git a/src/core/primitives.rs b/src/core/primitives.rs index 320a2893..a43ef7e4 100644 --- a/src/core/primitives.rs +++ b/src/core/primitives.rs @@ -1,4 +1,5 @@ use std::{ + cmp::Ordering, collections::{HashMap, HashSet, LinkedList}, mem::take, }; @@ -6,13 +7,14 @@ use std::{ use cosmian_crypto_core::{ reexport::rand_core::CryptoRngCore, RandomFixedSizeCBytes, Secret, SymmetricKey, }; -use tiny_keccak::{Hasher, IntoXof, Kmac, Xof}; +use tiny_keccak::{Hasher, IntoXof, Kmac, Shake, Xof}; use zeroize::Zeroize; use super::{ - elgamal, postquantum, CoordinateKeypair, CoordinatePublicKey, CoordinateSecretKey, - KmacSignature, TracingSecretKey, MIN_TRACING_LEVEL, SEED_LENGTH, SIGNATURE_LENGTH, - SIGNING_KEY_LENGTH, TAG_LENGTH, + elgamal, + postquantum::{MlKemAesPke, PkeTrait}, + CoordinatePublicKey, CoordinateSecretKey, KmacSignature, TracingSecretKey, MIN_TRACING_LEVEL, + SEED_LENGTH, SIGNATURE_LENGTH, SIGNING_KEY_LENGTH, TAG_LENGTH, }; use crate::{ abe_policy::{AttributeStatus, Coordinate, EncryptionHint}, @@ -21,10 +23,6 @@ use crate::{ Error, }; -/// Additional information to generate symmetric key using the KDF. -// TODO: find a more thoughtful message. -pub(crate) const KEY_GEN_INFO: &[u8] = b"key generation info"; - /// Computes the signature of the given USK using the MSK. fn sign_usk(msk: &MasterSecretKey, usk: &UserSecretKey) -> Option { if let Some(kmac_key) = &msk.signing_key { @@ -37,17 +35,9 @@ fn sign_usk(msk: &MasterSecretKey, usk: &UserSecretKey) -> Option for (coordinate, keys) in usk.coordinate_keys.iter() { kmac.update(coordinate); for k in keys.iter() { - match k { - CoordinateSecretKey::Hybridized { - postquantum_sk, - elgamal_sk, - } => { - kmac.update(postquantum_sk); - kmac.update(elgamal_sk.as_bytes()); - } - CoordinateSecretKey::Classic { elgamal_sk } => { - kmac.update(elgamal_sk.as_bytes()); - } + kmac.update(k.el.as_bytes()); + if let Some(k) = &k.pq { + kmac.update(k); } } } @@ -86,28 +76,19 @@ pub fn setup(rng: &mut impl CryptoRngCore, tracing_level: usize) -> Result::new(rng)), }) } -/// Generates a new MPK holding the latest public information of each universal coordinate. -pub fn mpk_keygen(msk: &MasterSecretKey) -> Result { - Ok(MasterPublicKey { - h: msk.binding_point(), - tpk: msk.tsk.tpk(), - coordinate_keys: msk.get_latest_coordinate_pk().collect(), - }) -} - /// Generates a USK for the given set of coordinates. /// /// The generated key is provided with the last version of the key for each /// coordinate in the given set. The USK can then open any up-to-date key /// encapsulation for any such coordinate (provided the coordinate was not -/// rekeyed). +/// re-keyed in-between). /// -/// If the MSK has a signing key, sign the USK. +/// If the MSK has a signing key, signs the USK. pub fn usk_keygen( rng: &mut impl CryptoRngCore, msk: &mut MasterSecretKey, @@ -115,7 +96,7 @@ pub fn usk_keygen( ) -> Result { let coordinate_keys = msk .get_latest_coordinate_sk(coordinates.into_iter()) - .collect::, Error>>()?; + .collect::, Error>>()?; // Do not generate the ID if an error happens when extracting coordinate secrets. let id = msk.generate_user_id(rng)?; @@ -131,10 +112,33 @@ pub fn usk_keygen( Ok(usk) } +fn eakem_hash( + seed: &Secret, + encapsulations: &[SeedEncapsulation], +) -> Result<([u8; TAG_LENGTH], Secret), Error> { + let mut hasher = Shake::v256(); + + hasher.update(seed); + for enc in encapsulations { + hasher.update(enc); + } + + let mut tag = [0; TAG_LENGTH]; + let mut seed = Secret::::default(); + hasher.squeeze(&mut tag); + hasher.squeeze(&mut seed); + Ok((tag, seed)) +} + /// Generates a Covercrypt encapsulation of a random `SEED_LENGTH`-byte key for /// the coordinate in the encryption set. /// /// Returns both the key and its encapsulation. +/// +/// # Error +/// +/// Returns an error in case the public key is missing for some coordinate or both classic and +/// hybridized coordinate keys are targeted. pub fn encaps( rng: &mut impl CryptoRngCore, mpk: &MasterPublicKey, @@ -153,42 +157,33 @@ pub fn encaps( let seed = Secret::::random(rng); let ephemeral_random = elgamal::Scalar::new(rng); + let traps = mpk.set_traps(&ephemeral_random); - let encaps_classical = |pk| { - let mut elgamal_ctx = [0; SEED_LENGTH]; - elgamal::mask(&mut elgamal_ctx, &ephemeral_random, pk, &seed); - elgamal_ctx - }; - - let mut encaps_hybridized = - |postquantum_pk, elgamal_pk| -> Result { - let mut elgamal_ctx = [0; SEED_LENGTH]; - elgamal::mask(&mut elgamal_ctx, &ephemeral_random, elgamal_pk, &seed); - let postquantum_ctx = postquantum::encrypt(rng, postquantum_pk, &elgamal_ctx)?; - elgamal_ctx.zeroize(); // ElGamal ciphertext is not secure in a post-quantum world - Ok(postquantum_ctx) - }; - - let coordinate_encapsulations = subkeys + let mut coordinate_encapsulations = subkeys .into_iter() .map(|subkey| -> Result { - match subkey { - CoordinatePublicKey::Classic { elgamal_pk } => { - Ok(SeedEncapsulation::Classic(encaps_classical(elgamal_pk))) - } - CoordinatePublicKey::Hybridized { - postquantum_pk, - elgamal_pk, - } => { - encaps_hybridized(postquantum_pk, elgamal_pk).map(SeedEncapsulation::Hybridized) - } + let mut elgamal_ctx = [0; SEED_LENGTH]; + elgamal::mask(&mut elgamal_ctx, &ephemeral_random, &subkey.el, &seed); + if let Some(pq) = &subkey.pq { + let postquantum_ctx = MlKemAesPke.encrypt(rng, pq, &elgamal_ctx)?; + elgamal_ctx.zeroize(); // ElGamal ciphertext is not secure in a post-quantum world + Ok(SeedEncapsulation::Hybridized(postquantum_ctx)) + } else { + Ok(SeedEncapsulation::Classic(elgamal_ctx)) } }) - .collect::, Error>>()?; + .collect::, Error>>()?; - let traps = mpk.set_traps(&ephemeral_random); - let (tag, key) = eakem_hash!(TAG_LENGTH, SEED_LENGTH, &*seed, KEY_GEN_INFO) - .map_err(Error::CryptoCoreError)?; + // Shuffle encapsulations. + coordinate_encapsulations.sort_by(|_, _| { + if rng.next_u32() & 1 == 0 { + Ordering::Less + } else { + Ordering::Greater + } + }); + + let (tag, key) = eakem_hash(&seed, &coordinate_encapsulations)?; Ok(( key, @@ -219,32 +214,27 @@ pub fn decaps( for enc in &encapsulation.coordinate_encapsulations { // The breadth-first search tries all coordinate subkeys in a chronological order. for key in usk.coordinate_keys.bfs() { - let seed: Secret = match (key, enc) { - ( - CoordinateSecretKey::Hybridized { - postquantum_sk, - elgamal_sk, - }, - SeedEncapsulation::Hybridized(ctx), - ) => { - let elgammal_ctx = postquantum::decrypt(postquantum_sk, ctx); - elgamal::unmask(elgamal_sk, &ephemeral_point, &elgammal_ctx)? + let seed: Secret = match (key.pq.as_ref(), enc) { + (Some(pq), SeedEncapsulation::Hybridized(ctx)) => { + let elgamal_ctx = MlKemAesPke.decrypt(pq, ctx); + if let Ok(elgamal_ctx) = elgamal_ctx { + elgamal::unmask(&key.el, &ephemeral_point, &elgamal_ctx)? + } else { + continue; + } } - (CoordinateSecretKey::Classic { elgamal_sk }, SeedEncapsulation::Classic(enc)) => { - elgamal::unmask(elgamal_sk, &ephemeral_point, enc)? + (None, SeedEncapsulation::Classic(enc)) => { + elgamal::unmask(&key.el, &ephemeral_point, enc)? } - (CoordinateSecretKey::Classic { .. }, SeedEncapsulation::Hybridized(_)) - | (CoordinateSecretKey::Hybridized { .. }, SeedEncapsulation::Classic(_)) => { - // It is safe not to try decapsulating if there is a - // hybridization mismatch as it means either the - // encapsulation is not associated to the key coordinate, - // either the encapsulation is not valid. + (None, SeedEncapsulation::Hybridized(_)) + | (Some(_), SeedEncapsulation::Classic(_)) => { + // No need to try when there is a hybridization mismatch. continue; } }; - let (tag, seed) = eakem_hash!(TAG_LENGTH, SEED_LENGTH, &*seed, KEY_GEN_INFO) - .map_err(Error::CryptoCoreError)?; + let (tag, seed) = eakem_hash(&seed, &encapsulation.coordinate_encapsulations)?; + if tag == encapsulation.tag { return Ok(Some(seed)); } @@ -266,17 +256,16 @@ pub fn update_coordinate_keys( msk: &mut MasterSecretKey, coordinates: HashMap, ) -> Result<(), Error> { - let h = msk.binding_point(); - let mut coordinate_keypairs = take(&mut msk.coordinate_keypairs); + let mut coordinate_keypairs = take(&mut msk.coordinate_secrets); coordinate_keypairs.retain(|coordinate| coordinates.contains_key(coordinate)); for (coordinate, (hint, status)) in coordinates { - if let Some(coordinate_keypair) = coordinate_keypairs.get_latest_mut(&coordinate) { + if let Some((is_activated, coordinate_secret)) = + coordinate_keypairs.get_latest_mut(&coordinate) + { + *is_activated = AttributeStatus::EncryptDecrypt == status; if EncryptionHint::Classic == hint { - coordinate_keypair.drop_hybridization(); - } - if AttributeStatus::DecryptOnly == status { - coordinate_keypair.drop_encryption_key(); + coordinate_secret.drop_hybridization(); } } else { if AttributeStatus::DecryptOnly == status { @@ -284,27 +273,11 @@ pub fn update_coordinate_keys( "cannot add decrypt only coordinate key".to_string(), )); } - - let elgamal_sk = elgamal::Scalar::new(rng); - let elgamal_pk = &h * &elgamal_sk; - let elgamal_keypair = elgamal::Keypair::new(elgamal_sk, elgamal_pk); - - let postquantum_keypair = if EncryptionHint::Hybridized == hint { - Some(postquantum::Keypair::random(rng)) - } else { - None - }; - - coordinate_keypairs.insert( - coordinate, - CoordinateKeypair { - elgamal_keypair, - postquantum_keypair, - }, - ); + let secret = CoordinateSecretKey::random(rng, EncryptionHint::Hybridized == hint)?; + coordinate_keypairs.insert(coordinate, (true, secret)); } } - msk.coordinate_keypairs = coordinate_keypairs; + msk.coordinate_secrets = coordinate_keypairs; Ok(()) } @@ -315,22 +288,21 @@ pub fn rekey( msk: &mut MasterSecretKey, target_space: HashSet, ) -> Result<(), Error> { - let h = msk.binding_point(); for coordinate in target_space { - if msk.coordinate_keypairs.contains_key(&coordinate) { + if msk.coordinate_secrets.contains_key(&coordinate) { let is_hybridized = msk - .coordinate_keypairs + .coordinate_secrets .get_latest(&coordinate) - .map(CoordinateKeypair::is_hybridized) + .map(|(_, k)| k.is_hybridized()) .ok_or_else(|| { Error::OperationNotPermitted(format!( "no current key for coordinate {coordinate:#?}" )) })?; - msk.coordinate_keypairs.insert( + msk.coordinate_secrets.insert( coordinate, - CoordinateKeypair::random(rng, &h, is_hybridized), + (true, CoordinateSecretKey::random(rng, is_hybridized)?), ); } else { return Err(Error::OperationNotPermitted( @@ -348,7 +320,7 @@ pub fn rekey( /// This operation *permanently* deletes old keys, this is thus not reversible! pub fn prune(msk: &mut MasterSecretKey, coordinates: &HashSet) { for coordinate in coordinates { - msk.coordinate_keypairs.keep(coordinate, 1); + msk.coordinate_secrets.keep(coordinate, 1); } } @@ -398,21 +370,21 @@ fn refresh_coordinate_keys( coordinate_keys .into_iter() .filter_map(|(coordinate, user_chain)| { - msk.coordinate_keypairs + msk.coordinate_secrets .get(&coordinate) .and_then(|msk_chain| { let mut updated_chain = LinkedList::new(); - let mut msk_keypairs = msk_chain.iter(); + let mut msk_secrets = msk_chain.iter(); let mut usk_secrets = user_chain.into_iter(); let first_secret = usk_secrets.next()?; // Add the most recent secrets from the MSK that do not belong // to the USK at the front of the updated chain (cf Invariant.1) - for keypair in msk_keypairs.by_ref() { - if keypair.contains(&first_secret) { + for (_, msk_secret) in msk_secrets.by_ref() { + if msk_secret == &first_secret { break; } - updated_chain.push_back(keypair.secret_key()); + updated_chain.push_back(msk_secret.clone()); } // Push the first USK secret since it was consumed from the USK @@ -422,9 +394,9 @@ fn refresh_coordinate_keys( // Push the secrets already stored in the USK that also belong // to the MSK keypairs. for coordinate_sk in usk_secrets { - if let Some(keypair) = msk_keypairs.next() { - if keypair.contains(&coordinate_sk) { - updated_chain.push_back(coordinate_sk); + if let Some((_, msk_secret)) = msk_secrets.next() { + if msk_secret == &coordinate_sk { + updated_chain.push_back(msk_secret.clone()); continue; } } diff --git a/src/core/serialization/mod.rs b/src/core/serialization/mod.rs index b7c8e106..3155624f 100644 --- a/src/core/serialization/mod.rs +++ b/src/core/serialization/mod.rs @@ -9,9 +9,8 @@ use cosmian_crypto_core::{ use super::{ elgamal::{EcPoint, Scalar}, - postquantum::{self, PublicKey}, - CoordinateKeypair, CoordinatePublicKey, CoordinateSecretKey, TracingPublicKey, - TracingSecretKey, UserId, SIGNATURE_LENGTH, SIGNING_KEY_LENGTH, TAG_LENGTH, + CoordinatePublicKey, CoordinateSecretKey, TracingPublicKey, TracingSecretKey, UserId, + SIGNATURE_LENGTH, SIGNING_KEY_LENGTH, TAG_LENGTH, }; use crate::{ abe_policy::Coordinate, @@ -53,49 +52,36 @@ impl Serializable for CoordinatePublicKey { type Error = Error; fn length(&self) -> usize { - match self { - CoordinatePublicKey::Hybridized { .. } => { - 1 + SEED_LENGTH + postquantum::PublicKey::LENGTH - } - CoordinatePublicKey::Classic { .. } => 1 + SEED_LENGTH, - } + 1 + self.el.length() + + self + .pq + .as_ref() + .map(Serializable::length) + .unwrap_or_default() } fn write(&self, ser: &mut Serializer) -> Result { - match self { - CoordinatePublicKey::Hybridized { - postquantum_pk, - elgamal_pk, - } => { - let mut n = ser.write_leb128_u64(1)?; - n += ser.write_array(postquantum_pk)?; - n += ser.write_array(&elgamal_pk.to_bytes())?; - Ok(n) - } - CoordinatePublicKey::Classic { elgamal_pk } => { - let mut n = ser.write_leb128_u64(0)?; - n += ser.write_array(&elgamal_pk.to_bytes())?; - Ok(n) - } + let mut n = ser.write_leb128_u64(self.is_hybridized().into())?; + n += ser.write_array(&self.el.to_bytes())?; + if let Some(k) = &self.pq { + n += ser.write(k)?; } + Ok(n) } fn read(de: &mut Deserializer) -> Result { let is_hybridized = de.read_leb128_u64()?; - if 1 == is_hybridized { - Ok(Self::Hybridized { - postquantum_pk: de.read::()?, - elgamal_pk: de.read::()?, - }) + let el = de.read()?; + let pq = if 1 == is_hybridized { + Some(de.read()?) } else if 0 == is_hybridized { - Ok(Self::Classic { - elgamal_pk: de.read::()?, - }) + None } else { - Err(Error::ConversionFailed(format!( + return Err(Error::ConversionFailed(format!( "invalid hybridization flag {is_hybridized}" - ))) - } + ))); + }; + Ok(Self { el, pq }) } } @@ -181,66 +167,20 @@ impl Serializable for TracingSecretKey { } } -impl Serializable for CoordinateKeypair { - type Error = Error; - - fn length(&self) -> usize { - self.elgamal_keypair.length() - + 1 - + self - .postquantum_keypair - .as_ref() - .map(Serializable::length) - .unwrap_or_default() - } - - fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write(&self.elgamal_keypair)?; - if let Some(keypair) = &self.postquantum_keypair { - n += ser.write_leb128_u64(1)?; - n += ser.write(keypair)?; - } else { - n += ser.write_leb128_u64(0)?; - } - Ok(n) - } - - fn read(de: &mut Deserializer) -> Result { - let elgamal_keypair = de.read()?; - let is_hybridized = de.read_leb128_u64()?; - if 1 == is_hybridized { - let postquantum_keypair = de.read()?; - Ok(Self { - elgamal_keypair, - postquantum_keypair: Some(postquantum_keypair), - }) - } else if 0 == is_hybridized { - Ok(Self { - elgamal_keypair, - postquantum_keypair: None, - }) - } else { - Err(Error::ConversionFailed(format!( - "invalid hybridization flag {is_hybridized}" - ))) - } - } -} - impl Serializable for MasterSecretKey { type Error = Error; fn length(&self) -> usize { self.s.length() + self.tsk.length() - + to_leb128_len(self.coordinate_keypairs.len()) + + to_leb128_len(self.coordinate_secrets.len()) + self - .coordinate_keypairs + .coordinate_secrets .iter() .map(|(coordinate, chain)| { coordinate.length() + to_leb128_len(chain.len()) - + chain.iter().map(Serializable::length).sum::() + + chain.iter().map(|(_, k)| 1 + k.length()).sum::() }) .sum::() + self.signing_key.as_ref().map_or_else(|| 0, |key| key.len()) @@ -249,11 +189,12 @@ impl Serializable for MasterSecretKey { fn write(&self, ser: &mut Serializer) -> Result { let mut n = ser.write(&self.s)?; n += ser.write(&self.tsk)?; - n += ser.write_leb128_u64(self.coordinate_keypairs.len() as u64)?; - for (coordinate, chain) in &self.coordinate_keypairs.map { + n += ser.write_leb128_u64(self.coordinate_secrets.len() as u64)?; + for (coordinate, chain) in &self.coordinate_secrets.map { n += ser.write(coordinate)?; n += ser.write_leb128_u64(to_leb128_len(chain.len()) as u64)?; - for sk in chain { + for (is_activated, sk) in chain { + n += ser.write_leb128_u64((*is_activated).into())?; n += ser.write(sk)?; } } @@ -267,21 +208,20 @@ impl Serializable for MasterSecretKey { let s = Scalar::try_from_bytes(de.read_array::<{ Scalar::LENGTH }>()?)?; let tsk = de.read::()?; let n_coordinates = ::try_from(de.read_leb128_u64()?)?; - println!("n_coordinates: {n_coordinates}"); let mut coordinate_keypairs = RevisionMap::with_capacity(n_coordinates); - for i in 0..n_coordinates { - println!("reading coordinate {i}"); + for _ in 0..n_coordinates { let coordinate = de.read()?; let n_keys = ::try_from(de.read_leb128_u64()?)?; - println!("n_keys {n_keys}"); let chain = (0..n_keys) - .map(|_| de.read::()) + .map(|_| -> Result<_, Error> { + let is_activated = de.read_leb128_u64()? == 1; + let sk = de.read::()?; + Ok((is_activated, sk)) + }) .collect::, _>>()?; coordinate_keypairs.map.insert(coordinate, chain); } - println!("HEY"); - let signing_key = if de.value().len() < SIGNING_KEY_LENGTH { None } else { @@ -290,12 +230,10 @@ impl Serializable for MasterSecretKey { )?) }; - println!("OH"); - Ok(Self { s, tsk, - coordinate_keypairs, + coordinate_secrets: coordinate_keypairs, signing_key, }) } @@ -331,52 +269,36 @@ impl Serializable for CoordinateSecretKey { type Error = Error; fn length(&self) -> usize { - 1 + match self { - CoordinateSecretKey::Hybridized { - postquantum_sk, - elgamal_sk, - } => elgamal_sk.length() + postquantum_sk.length(), - CoordinateSecretKey::Classic { elgamal_sk } => elgamal_sk.length(), - } + 1 + self.el.length() + + self + .pq + .as_ref() + .map(Serializable::length) + .unwrap_or_default() } fn write(&self, ser: &mut Serializer) -> Result { - match self { - CoordinateSecretKey::Hybridized { - postquantum_sk, - elgamal_sk, - } => { - let mut n = ser.write_leb128_u64(1)?; - n += ser.write(elgamal_sk)?; - n += ser.write(postquantum_sk)?; - Ok(n) - } - CoordinateSecretKey::Classic { elgamal_sk } => { - let mut n = ser.write_leb128_u64(0)?; - n += ser.write(elgamal_sk)?; - Ok(n) - } + let mut n = ser.write_leb128_u64(self.is_hybridized().into())?; + n += ser.write(&self.el)?; + if let Some(k) = &self.pq { + n += ser.write(k)?; } + Ok(n) } fn read(de: &mut Deserializer) -> Result { let is_hybridized = de.read_leb128_u64()?; - if 1 == is_hybridized { - let elgamal_sk = de.read()?; - let postquantum_sk = de.read()?; - Ok(Self::Hybridized { - postquantum_sk, - elgamal_sk, - }) + let el = de.read()?; + let pq = if 1 == is_hybridized { + Some(de.read()?) } else if 0 == is_hybridized { - Ok(Self::Classic { - elgamal_sk: de.read()?, - }) + None } else { - Err(Error::ConversionFailed(format!( + return Err(Error::ConversionFailed(format!( "invalid hybridization flag {is_hybridized}" - ))) - } + ))); + }; + Ok(Self { el, pq }) } } @@ -448,7 +370,7 @@ impl Serializable for SeedEncapsulation { fn length(&self) -> usize { 1 + match self { Self::Classic(enc) => enc.len(), - Self::Hybridized(enc) => enc.length(), + Self::Hybridized(enc) => to_leb128_len(enc.len()) + enc.len(), } } @@ -461,7 +383,7 @@ impl Serializable for SeedEncapsulation { } Self::Hybridized(enc) => { n += ser.write_leb128_u64(1)?; - n += ser.write(enc)?; + n += ser.write_vec(enc)?; } } Ok(n) @@ -470,7 +392,7 @@ impl Serializable for SeedEncapsulation { fn read(de: &mut Deserializer) -> Result { let is_hybridized = de.read_leb128_u64()?; if is_hybridized == 1 { - de.read::().map(Self::Hybridized) + de.read_vec().map(Self::Hybridized).map_err(Error::from) } else { de.read_array::() .map(Self::Classic) @@ -516,10 +438,10 @@ impl Serializable for Encapsulation { traps.push(trap); } let n_encapsulations = ::try_from(de.read_leb128_u64()?)?; - let mut coordinate_encapsulations = HashSet::with_capacity(n_encapsulations); + let mut coordinate_encapsulations = Vec::with_capacity(n_encapsulations); for _ in 0..n_encapsulations { let enc = de.read::()?; - coordinate_encapsulations.insert(enc); + coordinate_encapsulations.push(enc); } Ok(Self { tag, @@ -618,41 +540,22 @@ mod tests { use crate::{ abe_policy::{AttributeStatus, EncryptionHint}, core::{ - primitives::{encaps, mpk_keygen, setup, update_coordinate_keys, usk_keygen}, + postquantum::{MlKemAesPke, PkeTrait}, + primitives::{encaps, setup, update_coordinate_keys, usk_keygen}, MIN_TRACING_LEVEL, }, }; - #[test] - fn test_coordinate_keypair() { - let mut rng = CsRng::from_entropy(); - let s = Scalar::new(&mut rng); - let h = EcPoint::from(&s); - - { - let ckp = CoordinateKeypair::random(&mut rng, &h, true); - let bytes = ckp.serialize().unwrap(); - assert_eq!(bytes.len(), ckp.length()); - let ckp_ = CoordinateKeypair::deserialize(&bytes).unwrap(); - assert_eq!(ckp, ckp_); - } - - { - let ckp = CoordinateKeypair::random(&mut rng, &h, false); - let bytes = ckp.serialize().unwrap(); - assert_eq!(bytes.len(), ckp.length()); - let ckp_ = CoordinateKeypair::deserialize(&bytes).unwrap(); - assert_eq!(ckp, ckp_); - } - } - #[test] fn test_coordinate_pk() { let mut rng = CsRng::from_entropy(); { let elgamal_pk = EcPoint::from(&Scalar::new(&mut rng)); - let cpk = CoordinatePublicKey::Classic { elgamal_pk }; + let cpk = CoordinatePublicKey { + el: elgamal_pk, + pq: None, + }; let bytes = cpk.serialize().unwrap(); assert_eq!(bytes.len(), cpk.length()); let cpk_ = CoordinatePublicKey::deserialize(&bytes).unwrap(); @@ -661,10 +564,10 @@ mod tests { { let elgamal_pk = EcPoint::from(&Scalar::new(&mut rng)); - let postquantum_pk = postquantum::keygen(&mut rng).1; - let cpk = CoordinatePublicKey::Hybridized { - postquantum_pk, - elgamal_pk, + let (_, postquantum_pk) = MlKemAesPke.keygen(&mut rng).unwrap(); + let cpk = CoordinatePublicKey { + pq: Some(postquantum_pk), + el: elgamal_pk, }; let bytes = cpk.serialize().unwrap(); @@ -718,7 +621,7 @@ mod tests { let mut msk = setup(&mut rng, MIN_TRACING_LEVEL + 2).unwrap(); update_coordinate_keys(&mut rng, &mut msk, universe).unwrap(); - let mpk = mpk_keygen(&msk).unwrap(); + let mpk = msk.mpk().unwrap(); // Check Covercrypt `MasterSecretKey` serialization. { diff --git a/src/core/tests.rs b/src/core/tests.rs index f66818cf..b736cebb 100644 --- a/src/core/tests.rs +++ b/src/core/tests.rs @@ -4,8 +4,8 @@ use cosmian_crypto_core::{reexport::rand_core::SeedableRng, Aes256Gcm, CsRng}; use crate::{ abe_policy::{AccessPolicy, AttributeStatus, Coordinate, EncryptionHint}, - api::{Covercrypt, CovercryptPKE}, - core::primitives::{decaps, encaps, mpk_keygen, refresh, rekey, update_coordinate_keys}, + api::{Covercrypt, CovercryptKEM, CovercryptPKE}, + core::primitives::{decaps, encaps, refresh, rekey, update_coordinate_keys}, test_utils::policy, }; @@ -39,7 +39,7 @@ fn test_encapsulation() { ]), ) .unwrap(); - let mpk = mpk_keygen(&msk).unwrap(); + let mpk = msk.mpk().unwrap(); let (key, enc) = encaps( &mut rng, @@ -80,9 +80,9 @@ fn test_update() { let mut msk = setup(&mut rng, MIN_TRACING_LEVEL).unwrap(); assert_eq!(msk.tsk.users.len(), 0); assert_eq!(msk.tsk.tracing_level(), MIN_TRACING_LEVEL); - assert_eq!(msk.coordinate_keypairs.len(), 0); + assert_eq!(msk.coordinate_secrets.len(), 0); - let mpk = mpk_keygen(&msk).unwrap(); + let mpk = msk.mpk().unwrap(); assert_eq!(mpk.tpk.tracing_level(), MIN_TRACING_LEVEL); assert_eq!(mpk.coordinate_keys.len(), 0); @@ -97,9 +97,9 @@ fn test_update() { }) .collect::>(); update_coordinate_keys(&mut rng, &mut msk, coordinates.clone()).unwrap(); - assert_eq!(msk.coordinate_keypairs.len(), 30); + assert_eq!(msk.coordinate_secrets.len(), 30); - let mpk = mpk_keygen(&msk).unwrap(); + let mpk = msk.mpk().unwrap(); assert_eq!(mpk.coordinate_keys.len(), 30); // Deprecate half coordinates. @@ -115,15 +115,15 @@ fn test_update() { } }); update_coordinate_keys(&mut rng, &mut msk, coordinates.clone()).unwrap(); - assert_eq!(msk.coordinate_keypairs.len(), 30); - let mpk = mpk_keygen(&msk).unwrap(); + assert_eq!(msk.coordinate_secrets.len(), 30); + let mpk = msk.mpk().unwrap(); assert_eq!(mpk.coordinate_keys.len(), 15); // Keep only 10 coordinates. let coordinates = coordinates.into_iter().take(10).collect::>(); update_coordinate_keys(&mut rng, &mut msk, coordinates).unwrap(); - assert_eq!(msk.coordinate_keypairs.len(), 10); - let mpk = mpk_keygen(&msk).unwrap(); + assert_eq!(msk.coordinate_secrets.len(), 10); + let mpk = msk.mpk().unwrap(); assert_eq!(mpk.coordinate_keys.len(), 5); } @@ -155,7 +155,7 @@ fn test_rekey() { ]), ) .unwrap(); - let mpk = mpk_keygen(&msk).unwrap(); + let mpk = msk.mpk().unwrap(); let mut usk_1 = usk_keygen(&mut rng, &mut msk, subspace_1.clone()).unwrap(); let mut usk_2 = usk_keygen(&mut rng, &mut msk, subspace_2.clone()).unwrap(); @@ -173,7 +173,7 @@ fn test_rekey() { // Re-key all space coordinates. rekey(&mut rng, &mut msk, universe).unwrap(); - let mpk = mpk_keygen(&msk).unwrap(); + let mpk = msk.mpk().unwrap(); let (new_key_1, new_enc_1) = encaps(&mut rng, &mpk, &subspace_1).unwrap(); let (new_key_2, new_enc_2) = encaps(&mut rng, &mpk, &subspace_2).unwrap(); @@ -249,7 +249,24 @@ fn test_integrity_check() { } #[test] -fn test_pke() { +fn test_covercrypt_kem() { + let policy = policy().expect("cannot generate policy"); + let ap = AccessPolicy::parse("Department::FIN && Security Level::Top Secret").unwrap(); + let cc = Covercrypt::default(); + let (mut msk, _) = cc.setup().expect("cannot generate master keys"); + let mpk = cc + .update_master_keys(&policy, &mut msk) + .expect("cannot update master keys"); + let usk = cc + .generate_user_secret_key(&mut msk, &ap, &policy) + .expect("cannot generate usk"); + let (secret, enc) = cc.encaps(&mpk, &policy, &ap).unwrap(); + let res = cc.decaps(&usk, &enc).unwrap(); + assert_eq!(secret, res.unwrap()); +} + +#[test] +fn test_covercrypt_pke() { let policy = policy().expect("cannot generate policy"); let ap = AccessPolicy::parse("Department::FIN && Security Level::Top Secret").unwrap(); @@ -274,5 +291,5 @@ fn test_pke() { &enc, ) .expect("cannot decrypt the ciphertext"); - assert_eq!(ptx, ptx1.unwrap()); + assert_eq!(ptx, &*ptx1.unwrap()); } diff --git a/src/data_struct/dictionary.rs b/src/data_struct/dictionary.rs index e8616e2c..ddcd4fea 100644 --- a/src/data_struct/dictionary.rs +++ b/src/data_struct/dictionary.rs @@ -150,7 +150,6 @@ where } /// Returns an iterator over keys and values in insertion order. - #[allow(clippy::map_identity)] // unpack &(x, y) to (&x, &y) pub fn iter(&self) -> impl Iterator { self.entries.iter().map(|(k, v)| (k, v)) } diff --git a/src/data_struct/revision_vec.rs b/src/data_struct/revision_vec.rs index c8fa22ce..d46d1644 100644 --- a/src/data_struct/revision_vec.rs +++ b/src/data_struct/revision_vec.rs @@ -87,13 +87,11 @@ impl RevisionVec { } /// Returns an iterator over each key-chains pair - #[allow(clippy::map_identity)] // unpack &(x, y) to (&x, &y) pub fn iter(&self) -> impl Iterator)> { self.chains.iter().map(|(key, chain)| (key, chain)) } /// Returns an iterator over each key-chains pair that allow modifying chain - #[allow(clippy::map_identity)] // unpack &mut (x, y) to (&x, &mut y) pub fn iter_mut(&mut self) -> impl Iterator)> { self.chains.iter_mut().map(|(ref key, chain)| (key, chain)) } diff --git a/src/error.rs b/src/error.rs index 7977de36..fe93a07c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,9 +3,11 @@ use core::{fmt::Display, num::TryFromIntError}; use cosmian_crypto_core::CryptoCoreError; +use pqc_kyber::KyberError; #[derive(Debug)] pub enum Error { + KyberError(KyberError), CryptoCoreError(CryptoCoreError), KeyError(String), AttributeNotFound(String), @@ -26,7 +28,8 @@ pub enum Error { impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::CryptoCoreError(err) => write!(f, "{err}"), + Self::KyberError(err) => write!(f, "Kyber error: {err}"), + Self::CryptoCoreError(err) => write!(f, "CryptoCore error{err}"), Self::KeyError(err) => write!(f, "{err}"), Self::AttributeNotFound(err) => write!(f, "attribute not found: {err}"), Self::UnsupportedOperator(err) => write!(f, "unsupported operator {err}"), @@ -65,4 +68,10 @@ impl From for Error { } } +impl From for Error { + fn from(e: KyberError) -> Self { + Self::KyberError(e) + } +} + impl std::error::Error for Error {} From 2c0450a7446853298070fcd2f9e1db463ce35bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Thu, 23 May 2024 16:45:14 +0200 Subject: [PATCH 2/6] use pqc_kyber without hazmat feature --- Cargo.toml | 2 +- src/core/postquantum/kyber.rs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d777d307..db28d598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ serialization = [] [dependencies] cosmian_crypto_core = { git="https://github.com/Cosmian/crypto_core.git", branch="develop", default-features = false, features = ["ser", "sha3", "aes", "curve25519"] } -pqc_kyber = { version = "0.4", features = ["std", "hazmat"] } +pqc_kyber = { version = "0.7", features = ["std", "kyber768"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } tiny-keccak = { version = "2.0.2", features = ["shake", "kmac"] } diff --git a/src/core/postquantum/kyber.rs b/src/core/postquantum/kyber.rs index 33e2cd82..798ca1fd 100644 --- a/src/core/postquantum/kyber.rs +++ b/src/core/postquantum/kyber.rs @@ -7,13 +7,13 @@ use std::ops::{Deref, DerefMut}; use crate::Error; use cosmian_crypto_core::{bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore, Secret}; -use pqc_kyber::{ - KYBER_CIPHERTEXTBYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES, - KYBER_PUBLICKEYBYTES, KYBER_SECRETKEYBYTES, KYBER_SSBYTES, -}; +use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES, KYBER_SECRETKEYBYTES, KYBER_SSBYTES}; use super::KemTrait; +const KYBER_INDCPA_PUBLICKEYBYTES: usize = KYBER_PUBLICKEYBYTES; +const KYBER_INDCPA_SECRETKEYBYTES: usize = 1152; + /// Kyber public key length #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PublicKey(Box<[u8; Self::LENGTH]>); @@ -205,7 +205,7 @@ impl KemTrait for Kyber { &self, rng: &mut impl CryptoRngCore, ) -> Result<(Self::SecretKey, Self::PublicKey), Self::Error> { - let mut keypair = pqc_kyber::keypair(rng); + let mut keypair = pqc_kyber::keypair(rng)?; Ok(( SecretKey::from(&mut keypair.secret), PublicKey::from(keypair.public), From ff08af59b00c366df528480987dd2a620ba596d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Thu, 23 May 2024 17:02:59 +0200 Subject: [PATCH 3/6] use instead of recoding it --- src/core/postquantum/kyber.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/core/postquantum/kyber.rs b/src/core/postquantum/kyber.rs index 798ca1fd..60700462 100644 --- a/src/core/postquantum/kyber.rs +++ b/src/core/postquantum/kyber.rs @@ -7,13 +7,12 @@ use std::ops::{Deref, DerefMut}; use crate::Error; use cosmian_crypto_core::{bytes_ser_de::Serializable, reexport::rand_core::CryptoRngCore, Secret}; -use pqc_kyber::{KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES, KYBER_SECRETKEYBYTES, KYBER_SSBYTES}; +use pqc_kyber::{ + public, KYBER_CIPHERTEXTBYTES, KYBER_PUBLICKEYBYTES, KYBER_SECRETKEYBYTES, KYBER_SSBYTES, +}; use super::KemTrait; -const KYBER_INDCPA_PUBLICKEYBYTES: usize = KYBER_PUBLICKEYBYTES; -const KYBER_INDCPA_SECRETKEYBYTES: usize = 1152; - /// Kyber public key length #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PublicKey(Box<[u8; Self::LENGTH]>); @@ -72,11 +71,7 @@ impl SecretKey { pub const LENGTH: usize = KYBER_SECRETKEYBYTES; pub fn pk(&self) -> PublicKey { - const PK_START: usize = KYBER_INDCPA_SECRETKEYBYTES; - const PK_STOP: usize = PK_START + KYBER_INDCPA_PUBLICKEYBYTES; - let mut pk = [0; KYBER_PUBLICKEYBYTES]; - pk.copy_from_slice(&self[PK_START..PK_STOP]); - PublicKey::from(pk) + public(self).into() } } From e7239d7274696b64111042c638c1d280028f4886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Thu, 23 May 2024 17:08:34 +0200 Subject: [PATCH 4/6] remove the `eakem_hash!` macro --- src/core/macros.rs | 31 ------------------------------- src/core/mod.rs | 2 -- 2 files changed, 33 deletions(-) delete mode 100644 src/core/macros.rs diff --git a/src/core/macros.rs b/src/core/macros.rs deleted file mode 100644 index 5815d9af..00000000 --- a/src/core/macros.rs +++ /dev/null @@ -1,31 +0,0 @@ -//! Defines useful macros. - -pub use cosmian_crypto_core::{RandomFixedSizeCBytes, Secret}; -pub use tiny_keccak::{Hasher, Shake, Xof}; - -/// Hashes and extends the given bytes into a tag of size `TAG_LENGTH` and a -/// key of size `KEY_LENGTH`. -/// -/// # Security -/// -/// This hash is based on SHAKE256. -/// -/// # Parameters -/// -/// - `bytes` : input bytes -#[macro_export] -macro_rules! eakem_hash { - ($TAG_LENGTH: ident, $KEY_LENGTH: ident, $($bytes: expr),+) => { - { - let mut hasher = $crate::core::macros::Shake::v256(); - $( - <$crate::core::macros::Shake as $crate::core::macros::Hasher>::update(&mut hasher, $bytes); - )* - let mut tag = [0; $TAG_LENGTH]; - let mut seed = $crate::core::macros::Secret::<$KEY_LENGTH>::default(); - <$crate::core::macros::Shake as $crate::core::macros::Xof>::squeeze(&mut hasher, &mut tag); - <$crate::core::macros::Shake as $crate::core::macros::Hasher>::finalize(hasher, &mut seed); - Ok((tag, seed)) - } - }; -} diff --git a/src/core/mod.rs b/src/core/mod.rs index 55c74a91..1477f35e 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -12,8 +12,6 @@ use crate::{ Error, }; -#[macro_use] -pub mod macros; pub mod ae; pub mod api; mod encrypted_header; From 001c527c4e41df3b0445c1c4c6ebe3baf8b13c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Thu, 6 Jun 2024 20:22:16 +0200 Subject: [PATCH 5/6] Remove `h` from MPK --- src/core/mod.rs | 10 ++++------ src/core/serialization/mod.rs | 8 ++------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/core/mod.rs b/src/core/mod.rs index 1477f35e..e1dd3a02 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -211,7 +211,7 @@ impl TracingSecretKey { } /// Returns true if the given user ID is known. - fn knows(&self, id: &UserId) -> bool { + fn is_known(&self, id: &UserId) -> bool { self.users.contains(id) } @@ -314,7 +314,7 @@ impl MasterSecretKey { /// /// Returns an error if the ID is unknown. fn refresh_id(&mut self, rng: &mut impl CryptoRngCore, id: UserId) -> Result { - if !self.tsk.knows(&id) { + if !self.tsk.is_known(&id) { Err(Error::Tracing("unknown user".to_string())) } else if id.tracing_level() != self.tsk.tracing_level() { let new_id = self.generate_user_id(rng)?; @@ -352,8 +352,8 @@ impl MasterSecretKey { /// Generates a new MPK holding the latest public information of each universal coordinate. pub fn mpk(&self) -> Result { + let h = self.binding_point(); Ok(MasterPublicKey { - h: self.binding_point(), tpk: self.tsk.tpk(), coordinate_keys: self .coordinate_secrets @@ -361,7 +361,7 @@ impl MasterSecretKey { .filter_map(|(coordinate, secrets)| { secrets.front().and_then(|(is_activated, s)| { if *is_activated { - Some((coordinate.clone(), s.public_key(&self.binding_point()))) + Some((coordinate.clone(), s.public_key(&h))) } else { None } @@ -375,12 +375,10 @@ impl MasterSecretKey { /// Covercrypt Public Key (PK). /// /// It is composed of: -/// - the binding point `h`; /// - the tracing public key; /// - the public keys of the universal coordinates. #[derive(Debug, PartialEq, Eq)] pub struct MasterPublicKey { - h: EcPoint, tpk: TracingPublicKey, coordinate_keys: HashMap, } diff --git a/src/core/serialization/mod.rs b/src/core/serialization/mod.rs index 3155624f..c437af77 100644 --- a/src/core/serialization/mod.rs +++ b/src/core/serialization/mod.rs @@ -89,8 +89,7 @@ impl Serializable for MasterPublicKey { type Error = Error; fn length(&self) -> usize { - self.h.length() - + self.tpk.length() + self.tpk.length() + to_leb128_len(self.coordinate_keys.len()) + self .coordinate_keys @@ -100,8 +99,7 @@ impl Serializable for MasterPublicKey { } fn write(&self, ser: &mut Serializer) -> Result { - let mut n = ser.write_array(&self.h.to_bytes())?; - n += ser.write(&self.tpk)?; + let mut n = ser.write(&self.tpk)?; n += ser.write_leb128_u64(self.coordinate_keys.len() as u64)?; for (coordinate, pk) in &self.coordinate_keys { n += ser.write(coordinate)?; @@ -111,7 +109,6 @@ impl Serializable for MasterPublicKey { } fn read(de: &mut Deserializer) -> Result { - let h = de.read::()?; let tpk = de.read::()?; let n_coordinates = ::try_from(de.read_leb128_u64()?)?; let mut coordinate_keys = HashMap::with_capacity(n_coordinates); @@ -121,7 +118,6 @@ impl Serializable for MasterPublicKey { coordinate_keys.insert(coordinate, pk); } Ok(Self { - h, tpk, coordinate_keys, }) From e62f2f61c5cd9018e3afc61ca2d5f0775f20659b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20BR=C3=89ZOT?= Date: Tue, 11 Jun 2024 15:34:45 +0200 Subject: [PATCH 6/6] Use released CryptoCore version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index db28d598..ee8c5e1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ required-features = ["serialization"] serialization = [] [dependencies] -cosmian_crypto_core = { git="https://github.com/Cosmian/crypto_core.git", branch="develop", default-features = false, features = ["ser", "sha3", "aes", "curve25519"] } +cosmian_crypto_core = { version="9.4", default-features = false, features = ["ser", "sha3", "aes", "curve25519"] } pqc_kyber = { version = "0.7", features = ["std", "kyber768"] } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] }