Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update pqc kyber #128

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Security fix

- Update Kyber dependecy due to a [timing vulnerability](https://github.com/Argyle-Software/kyber/issues/108)

### Features

- Change `Axis` to `Dimension` with a clear distinction between `Ordered` and `Unordered`.
Expand Down
17 changes: 12 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ hybridized_bench = []

[dependencies]
base64 = { version = "0.21.0", optional = true }
cosmian_crypto_core = { version = "9.2.0", default-features = false, features = ["ser", "sha3", "aes", "curve25519"] }
pqc_kyber = { version = "0.4", features = ["std", "hazmat"] }
cosmian_crypto_core = { version = "9.3", default-features = false, features = [
"ser",
"sha3",
"aes",
"curve25519",
] }
pqc_kyber = { version = "0.7", features = ["std", "hazmat"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tiny-keccak = { version = "2.0.2", features = ["shake", "kmac"] }
tiny-keccak = { version = "2.0", features = ["shake", "kmac"] }
zeroize = "1.6.0"

[dev-dependencies]
base64 = { version = "0.21.0" }
criterion = { version = "0.4", features = ["html_reports"], default_features = false }
base64 = { version = "0.21" }
criterion = { version = "0.5", features = [
"html_reports",
], default_features = false }
4 changes: 2 additions & 2 deletions src/core/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ impl Covercrypt {
&self,
policy: &Policy,
) -> Result<(MasterSecretKey, MasterPublicKey), Error> {
Ok(setup(
setup(
&mut *self.rng.lock().expect("Mutex lock failed!"),
policy.generate_all_partitions()?,
))
)
}

/// Updates the master keys according to this new policy.
Expand Down
4 changes: 3 additions & 1 deletion src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use std::{
};

use cosmian_crypto_core::{R25519PrivateKey, R25519PublicKey, SymmetricKey};
use pqc_kyber::{KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES};
use pqc_kyber::indcpa::{
KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES,
};
use zeroize::ZeroizeOnDrop;

use crate::{
Expand Down
65 changes: 38 additions & 27 deletions src/core/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ use cosmian_crypto_core::{
R25519PrivateKey, R25519PublicKey, RandomFixedSizeCBytes, SymmetricKey,
};
use pqc_kyber::{
indcpa::{indcpa_dec, indcpa_enc, indcpa_keypair},
KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES, KYBER_SYMBYTES,
indcpa::{
indcpa_dec, indcpa_enc, indcpa_keypair, KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES,
KYBER_INDCPA_SECRETKEYBYTES,
},
KYBER_SYMBYTES,
};
use tiny_keccak::{Hasher, IntoXof, Kmac, Xof};
use zeroize::Zeroizing;
Expand Down Expand Up @@ -77,13 +80,16 @@ fn verify_user_key_kmac(msk: &MasterSecretKey, usk: &UserSecretKey) -> Result<()
}

/// Returns newly generated public and private Kyber key pair.
fn create_kyber_key_pair(rng: &mut impl CryptoRngCore) -> (KyberPublicKey, KyberSecretKey) {
fn create_kyber_key_pair(
rng: &mut impl CryptoRngCore,
) -> Result<(KyberPublicKey, KyberSecretKey), Error> {
let (mut sk, mut pk) = (
KyberSecretKey([0; KYBER_INDCPA_SECRETKEYBYTES]),
KyberPublicKey([0; KYBER_INDCPA_PUBLICKEYBYTES]),
);
indcpa_keypair(&mut pk.0, &mut sk.0, None, rng);
(pk, sk)
indcpa_keypair(&mut pk.0, &mut sk.0, None, rng)
.map_err(|e| Error::KeyError(format!("Unable to generate kyber key pair: {e}")))?;
Ok((pk, sk))
}

/// Returns a newly generated pair of public and private subkeys with optional
Expand All @@ -92,17 +98,17 @@ fn create_subkey_pair(
rng: &mut impl CryptoRngCore,
h: &R25519CurvePoint,
is_hybridized: EncryptionHint,
) -> (PublicSubkey, SecretSubkey) {
) -> Result<(PublicSubkey, SecretSubkey), Error> {
let sk_i = R25519PrivateKey::new(rng);
let pk_i = h * &sk_i;

let (pk_pq, sk_pq) = if is_hybridized.into() {
let (pk, sk) = create_kyber_key_pair(rng);
let (pk, sk) = create_kyber_key_pair(rng)?;
(Some(pk), Some(sk))
} else {
(None, None)
};
((pk_pq, pk_i), (sk_pq, sk_i))
Ok(((pk_pq, pk_i), (sk_pq, sk_i)))
}

/// Update a pair of public and private subkeys of a `ReadWrite` partition.
Expand All @@ -124,7 +130,7 @@ fn update_subkey_pair(
match (&pk_pq, &sk_pq) {
(None, _) => {
// generate a new Kyber key pair
let (pk, sk) = create_kyber_key_pair(rng);
let (pk, sk) = create_kyber_key_pair(rng)?;
pk_pq.replace(pk);
sk_pq.replace(sk);
}
Expand All @@ -145,13 +151,14 @@ fn update_master_subkey(
_h: &R25519CurvePoint,
msk: &mut SecretSubkey,
is_hybridized: EncryptionHint,
) {
) -> Result<(), Error> {
let (sk_pq, _) = msk;
// Add Kyber key if needed
if is_hybridized.into() && sk_pq.is_none() {
let (_, sk) = create_kyber_key_pair(rng);
let (_, sk) = create_kyber_key_pair(rng)?;
sk_pq.replace(sk);
}
Ok(())
}

/// Generates the master secret key and master public key of the `Covercrypt`
Expand All @@ -164,7 +171,7 @@ fn update_master_subkey(
pub fn setup(
rng: &mut impl CryptoRngCore,
partitions: HashMap<Partition, (EncryptionHint, AttributeStatus)>,
) -> (MasterSecretKey, MasterPublicKey) {
) -> Result<(MasterSecretKey, MasterPublicKey), Error> {
let s = R25519PrivateKey::new(rng);
let s1 = R25519PrivateKey::new(rng);
let s2 = R25519PrivateKey::new(rng);
Expand All @@ -176,7 +183,7 @@ pub fn setup(
let mut sub_pk = HashMap::with_capacity(partitions.len());

for (partition, (is_hybridized, write_status)) in partitions {
let (public_subkey, secret_subkey) = create_subkey_pair(rng, &h, is_hybridized);
let (public_subkey, secret_subkey) = create_subkey_pair(rng, &h, is_hybridized)?;
sub_sk.insert(partition.clone(), secret_subkey);
if write_status == EncryptDecrypt {
sub_pk.insert(partition, public_subkey);
Expand All @@ -185,7 +192,7 @@ pub fn setup(

let kmac_key = Some(SymmetricKey::<KMAC_KEY_LENGTH>::new(rng));

(
Ok((
MasterSecretKey {
s,
s1,
Expand All @@ -198,7 +205,7 @@ pub fn setup(
g2,
subkeys: sub_pk,
},
)
))
}

/// Generates a user secret key for the given decryption set.
Expand Down Expand Up @@ -382,15 +389,15 @@ pub fn update(
(EncryptDecrypt, Some(public_subkey)) => {
update_subkey_pair(rng, &h, public_subkey, secret_subkey, is_hybridized)?;
}
(DecryptOnly, None) => update_master_subkey(rng, &h, secret_subkey, is_hybridized),
(DecryptOnly, None) => update_master_subkey(rng, &h, secret_subkey, is_hybridized)?,
(DecryptOnly, Some(_)) => {
mpk.subkeys.remove(&partition);
update_master_subkey(rng, &h, secret_subkey, is_hybridized);
update_master_subkey(rng, &h, secret_subkey, is_hybridized)?;
}
}
} else {
// generate new keys
let (public_subkey, secret_subkey) = create_subkey_pair(rng, &h, is_hybridized);
let (public_subkey, secret_subkey) = create_subkey_pair(rng, &h, is_hybridized)?;
msk.subkeys.insert(partition.clone(), secret_subkey);
if write_status == EncryptDecrypt {
mpk.subkeys.insert(partition, public_subkey);
Expand Down Expand Up @@ -423,7 +430,7 @@ pub fn rekey(
.and_then(|(sk_i, _)| sk_i.as_ref())
.is_some(),
);
let (public_subkey, secret_subkey) = create_subkey_pair(rng, &h, is_hybridized);
let (public_subkey, secret_subkey) = create_subkey_pair(rng, &h, is_hybridized)?;
msk.subkeys.insert(coordinate.clone(), secret_subkey);

// update public subkey if partition is not read only
Expand Down Expand Up @@ -510,16 +517,18 @@ pub fn refresh(

#[cfg(test)]
mod tests {
use cosmian_crypto_core::{
bytes_ser_de::Serializable, reexport::rand_core::SeedableRng, CsRng,
};

#[cfg(feature = "serialization")]
use cosmian_crypto_core::bytes_ser_de::Serializable;

use cosmian_crypto_core::{reexport::rand_core::SeedableRng, CsRng};

use super::*;

#[test]
fn test_kyber() {
let mut rng = CsRng::from_entropy();
let keypair = pqc_kyber::keypair(&mut rng);
let keypair = pqc_kyber::keypair(&mut rng).unwrap();
let (ct, ss) = pqc_kyber::encapsulate(&keypair.public, &mut rng).unwrap();
let res = pqc_kyber::decapsulate(&ct, &keypair.secret).unwrap();
assert_eq!(ss, res, "Decapsulation failed!");
Expand Down Expand Up @@ -550,7 +559,7 @@ mod tests {
// secure random number generator
let mut rng = CsRng::from_entropy();
// setup scheme
let (mut msk, mut mpk) = setup(&mut rng, partitions_set);
let (mut msk, mut mpk) = setup(&mut rng, partitions_set)?;

// The admin partition matches a hybridized sub-key.
let admin_secret_subkeys = msk.subkeys.get_latest(&admin_partition);
Expand Down Expand Up @@ -673,7 +682,7 @@ mod tests {
// secure random number generator
let mut rng = CsRng::from_entropy();
// setup scheme
let (mut msk, mut mpk) = setup(&mut rng, partitions_set);
let (mut msk, mut mpk) = setup(&mut rng, partitions_set)?;

// now remove partition 1 and add partition 3
let partition_3 = Partition(b"3".to_vec());
Expand All @@ -698,6 +707,7 @@ mod tests {
}

#[test]
#[cfg(feature = "serialization")]
fn test_user_key_refresh() -> Result<(), Error> {
let partition_1 = Partition(b"1".to_vec());
let partition_2 = Partition(b"2".to_vec());
Expand All @@ -720,7 +730,7 @@ mod tests {
// secure random number generator
let mut rng = CsRng::from_entropy();
// setup scheme
let (mut msk, mut mpk) = setup(&mut rng, partitions_set);
let (mut msk, mut mpk) = setup(&mut rng, partitions_set)?;
// create a user key with access to partition 1 and 2
let mut usk = keygen(
&mut rng,
Expand Down Expand Up @@ -787,6 +797,7 @@ mod tests {
}

#[test]
#[cfg(feature = "serialization")]
fn test_user_key_kmac() -> Result<(), Error> {
let partition_1 = Partition(b"1".to_vec());
let partition_2 = Partition(b"2".to_vec());
Expand All @@ -804,7 +815,7 @@ mod tests {
// secure random number generator
let mut rng = CsRng::from_entropy();
// setup scheme
let (msk, _) = setup(&mut rng, partitions_set);
let (msk, _) = setup(&mut rng, partitions_set)?;
// create a user key with access to partition 1 and 2
let mut usk = keygen(&mut rng, &msk, &HashSet::from([partition_1, partition_2]))?;

Expand Down
2 changes: 1 addition & 1 deletion src/core/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ mod tests {
let target_set = HashSet::from([admin_partition, dev_partition]);
let mut rng = CsRng::from_entropy();

let (msk, mpk) = setup(&mut rng, partitions_set);
let (msk, mpk) = setup(&mut rng, partitions_set)?;

// Check Covercrypt `MasterSecretKey` serialization.
let bytes = msk.serialize()?;
Expand Down
7 changes: 6 additions & 1 deletion src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,16 @@ pub fn policy() -> Result<Policy, Error> {

#[cfg(test)]
mod tests {
#[cfg(feature = "serialization")]
use cosmian_crypto_core::bytes_ser_de::Serializable;

#[cfg(feature = "serialization")]
use crate::UserSecretKey;

use super::*;
use crate::{
abe_policy::{AccessPolicy, Attribute, LegacyPolicy, Partition},
Covercrypt, EncryptedHeader, UserSecretKey,
Covercrypt, EncryptedHeader,
};

#[test]
Expand Down Expand Up @@ -139,6 +143,7 @@ mod tests {
}

#[test]
#[cfg(feature = "serialization")]
fn test_refresh_user_key() -> Result<(), Error> {
let policy = policy()?;
let cover_crypt = Covercrypt::default();
Expand Down
Loading