Skip to content

Commit

Permalink
feat: add KMAC to attest the authenticity of user keys
Browse files Browse the repository at this point in the history
  • Loading branch information
Hugo Rosenkranz-Costa committed Sep 1, 2023
1 parent 46ef1c3 commit 43a1c9d
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 12 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ cosmian_crypto_core = { version = "9.0.0", default-features = false, features =
pqc_kyber = { version = "0.4", features = ["std", "hazmat"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tiny-keccak = { version = "2.0.2", features = ["shake"] }
tiny-keccak = { version = "2.0.2", features = ["shake", "kmac"] }
zeroize = "1.6.0"

[dev-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion src/core/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl Covercrypt {
usk,
&policy.access_policy_to_current_partitions(access_policy, true)?,
keep_old_accesses,
);
)?;
Ok(())
}

Expand Down
23 changes: 22 additions & 1 deletion src/core/macros.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Defines useful macros.

pub use cosmian_crypto_core::{RandomFixedSizeCBytes, SymmetricKey};
pub use tiny_keccak::{Hasher, Shake, Xof};
pub use tiny_keccak::{Hasher, IntoXof, Kmac, KmacXof, Shake, Xof};

/// Hashes and extends the given bytes into a tag of size `TAG_LENGTH` and a
/// key of size `KEY_LENGTH`.
Expand All @@ -28,3 +28,24 @@ macro_rules! eakem_hash {
}
};
}

/// KMAC hash algorithm used to derive keys.
///
/// - `length` : length of the generated output
/// - `key` : KMAC key
/// - `bytes` : bytes to hash
#[macro_export]
macro_rules! kmac {
($length: ident, $key: expr, $($bytes: expr),+) => {
{
let mut kmac = $crate::core::macros::Kmac::v256($key, b"");
$(
<$crate::core::macros::Kmac as $crate::core::macros::Hasher>::update(&mut kmac, $bytes);
)*
let mut xof = <$crate::core::macros::Kmac as $crate::core::macros::IntoXof>::into_xof(kmac);
let mut res = [0; $length];
<$crate::core::macros::KmacXof as $crate::core::macros::Xof>::squeeze(&mut xof, &mut res);
res
}
};
}
4 changes: 4 additions & 0 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub mod serialization;
/// security.
pub const SYM_KEY_LENGTH: usize = 32;

const KMAC_LENGTH: usize = 32;

/// Length of the `Covercrypt` tag
const TAG_LENGTH: usize = 16;
type Tag = [u8; TAG_LENGTH];
Expand Down Expand Up @@ -65,13 +67,15 @@ pub struct MasterSecretKey {
s: R25519PrivateKey,
s1: R25519PrivateKey,
s2: R25519PrivateKey,
kmac_key: [u8; SYM_KEY_LENGTH],
pub subkeys: HashMap<Partition, (Option<KyberSecretKey>, R25519PrivateKey)>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UserSecretKey {
a: R25519PrivateKey,
b: R25519PrivateKey,
kmac: [u8; KMAC_LENGTH],
pub subkeys: HashSet<(Option<KyberSecretKey>, R25519PrivateKey)>,
}

Expand Down
55 changes: 49 additions & 6 deletions src/core/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use pqc_kyber::{
};
use zeroize::Zeroizing;

use super::{KyberPublicKey, KyberSecretKey, SYM_KEY_LENGTH, TAG_LENGTH};
use super::{KyberPublicKey, KyberSecretKey, KMAC_LENGTH, SYM_KEY_LENGTH, TAG_LENGTH};
use crate::{
abe_policy::{EncryptionHint, Partition},
core::{Encapsulation, KeyEncapsulation, MasterPublicKey, MasterSecretKey, UserSecretKey},
Expand All @@ -30,6 +30,20 @@ fn xor_in_place<const LENGTH: usize>(a: &mut [u8; LENGTH], b: &[u8; LENGTH]) {
}
}

fn compute_subkeys_kmac(
msk_kmac_key: &[u8; SYM_KEY_LENGTH],
subkeys: &HashSet<(Option<KyberSecretKey>, R25519PrivateKey)>,
) -> [u8; KMAC_LENGTH] {
let subkeys_bytes: Vec<Vec<u8>> = subkeys
.iter()
.map(|subkey| match subkey {
(None, pk) => pk.to_bytes().to_vec(),
(Some(ksk), pk) => [ksk, pk.as_bytes()].concat(),
})
.collect();
kmac!(KMAC_LENGTH, msk_kmac_key, &subkeys_bytes.concat())
}

/// Generates the master secret key and master public key of the `Covercrypt`
/// scheme.
///
Expand Down Expand Up @@ -70,11 +84,15 @@ pub fn setup(
sub_pk.insert(partition.clone(), (pk_pq, pk_i));
}

let mut kmac_key = [0; SYM_KEY_LENGTH];
rng.fill_bytes(&mut kmac_key);

(
MasterSecretKey {
s,
s1,
s2,
kmac_key,
subkeys: sub_sk,
},
MasterPublicKey {
Expand All @@ -99,12 +117,26 @@ pub fn keygen(
) -> UserSecretKey {
let a = R25519PrivateKey::new(rng);
let b = &(&msk.s - &(&a * &msk.s1)) / &msk.s2;
let subkeys = decryption_set
let subkeys: HashSet<(Option<KyberSecretKey>, R25519PrivateKey)> = decryption_set
.iter()
.filter_map(|partition| msk.subkeys.get(partition))
.cloned()
.collect();
UserSecretKey { a, b, subkeys }

/*let subkeys_bytes: Vec<Vec<u8>> = subkeys
.iter()
.map(|subkey| match subkey {
(None, pk) => pk.to_bytes().to_vec(),
(Some(ksk), pk) => [ksk, pk.as_bytes()].concat(),
})
.collect();*/
let kmac = compute_subkeys_kmac(&msk.kmac_key, &subkeys);
UserSecretKey {
a,
b,
kmac,
subkeys,
}
}

/// Generates a `Covercrypt` encapsulation of a random symmetric key.
Expand Down Expand Up @@ -307,7 +339,15 @@ pub fn refresh(
usk: &mut UserSecretKey,
decryption_set: &HashSet<Partition>,
keep_old_rights: bool,
) {
) -> Result<(), Error> {
// Check the user subkeys are matching its kmac
let kmac = compute_subkeys_kmac(&msk.kmac_key, &usk.subkeys);
if usk.kmac != kmac {
return Err(Error::KeyError(
"The provided user key is corrupted.".to_string(),
));
}

if !keep_old_rights {
usk.subkeys.drain();
}
Expand All @@ -317,6 +357,9 @@ pub fn refresh(
usk.subkeys.insert(x_i.clone());
}
}

usk.kmac = compute_subkeys_kmac(&msk.kmac_key, &usk.subkeys);
Ok(())
}

#[cfg(test)]
Expand Down Expand Up @@ -402,7 +445,7 @@ mod tests {
&mut dev_usk,
&HashSet::from([dev_partition.clone()]),
false,
);
)?;

// The dev partition matches a hybridized sub-key.
let dev_secret_subkeys = msk.subkeys.get(&dev_partition);
Expand Down Expand Up @@ -525,7 +568,7 @@ mod tests {
&mut usk,
&HashSet::from([partition_2.clone(), partition_4.clone()]),
false,
);
)?;
assert!(!usk
.subkeys
.contains(old_msk.subkeys.get(&partition_1).unwrap()));
Expand Down
24 changes: 21 additions & 3 deletions src/core/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use cosmian_crypto_core::{
};
use pqc_kyber::{KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES};

use super::{KyberPublicKey, KyberSecretKey, TAG_LENGTH};
use super::{KyberPublicKey, KyberSecretKey, KMAC_LENGTH, TAG_LENGTH};
use crate::{
abe_policy::Partition,
core::{
Expand Down Expand Up @@ -80,6 +80,7 @@ impl Serializable for MasterSecretKey {

fn length(&self) -> usize {
let mut length = 3 * R25519PrivateKey::LENGTH
+ SYM_KEY_LENGTH
+ to_leb128_len(self.subkeys.len())
+ self.subkeys.len() * R25519PrivateKey::LENGTH;
for (partition, (sk_i, _)) in &self.subkeys {
Expand All @@ -96,6 +97,7 @@ impl Serializable for MasterSecretKey {
let mut n = ser.write_array(&self.s1.to_bytes())?;
n += ser.write_array(&self.s2.to_bytes())?;
n += ser.write_array(&self.s.to_bytes())?;
n += ser.write_array(&self.kmac_key)?;
n += ser.write_leb128_u64(self.subkeys.len() as u64)?;
for (partition, (sk_i, x_i)) in &self.subkeys {
n += ser.write_vec(partition)?;
Expand All @@ -116,6 +118,8 @@ impl Serializable for MasterSecretKey {
let s2 =
R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?;
let s = R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?;
let kmac_key = de.read_array::<{ SYM_KEY_LENGTH }>()?;

let n_partitions = <usize>::try_from(de.read_leb128_u64()?)?;
let mut subkeys = HashMap::with_capacity(n_partitions);
for _ in 0..n_partitions {
Expand All @@ -132,7 +136,13 @@ impl Serializable for MasterSecretKey {
(sk_i, R25519PrivateKey::try_from_bytes(x_i)?),
);
}
Ok(Self { s, s1, s2, subkeys })
Ok(Self {
s,
s1,
s2,
kmac_key,
subkeys,
})
}
}

Expand All @@ -141,6 +151,7 @@ impl Serializable for UserSecretKey {

fn length(&self) -> usize {
let mut length = 2 * R25519PrivateKey::LENGTH
+ KMAC_LENGTH
+ to_leb128_len(self.subkeys.len())
+ self.subkeys.len() * R25519PrivateKey::LENGTH;
for (sk_i, _) in &self.subkeys {
Expand All @@ -155,6 +166,7 @@ impl Serializable for UserSecretKey {
fn write(&self, ser: &mut Serializer) -> Result<usize, Self::Error> {
let mut n = ser.write_array(&self.a.to_bytes())?;
n += ser.write_array(&self.b.to_bytes())?;
n += ser.write_array(&self.kmac)?;
n += ser.write_leb128_u64(self.subkeys.len() as u64)?;
for (sk_i, x_i) in &self.subkeys {
if let Some(sk_i) = sk_i {
Expand All @@ -171,6 +183,7 @@ impl Serializable for UserSecretKey {
fn read(de: &mut Deserializer) -> Result<Self, Self::Error> {
let a = R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?;
let b = R25519PrivateKey::try_from_bytes(de.read_array::<{ R25519PrivateKey::LENGTH }>()?)?;
let kmac = de.read_array::<{ KMAC_LENGTH }>()?;
let n_partitions = <usize>::try_from(de.read_leb128_u64()?)?;
let mut subkeys = HashSet::with_capacity(n_partitions);
for _ in 0..n_partitions {
Expand All @@ -183,7 +196,12 @@ impl Serializable for UserSecretKey {
let x_i = de.read_array::<{ R25519PrivateKey::LENGTH }>()?;
subkeys.insert((sk_i, R25519PrivateKey::try_from_bytes(x_i)?));
}
Ok(Self { a, b, subkeys })
Ok(Self {
a,
b,
kmac,
subkeys,
})
}
}

Expand Down

0 comments on commit 43a1c9d

Please sign in to comment.