Skip to content

Commit

Permalink
wip: feat: refactor key structure
Browse files Browse the repository at this point in the history
  • Loading branch information
tbrezot committed Jan 19, 2024
1 parent f37d53f commit 59ca437
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 74 deletions.
148 changes: 98 additions & 50 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
@@ -1,105 +1,153 @@
//! Implements the core functionalities of `Covercrypt`.
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
hash::Hash,
ops::Deref,
};

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

use crate::{
abe_policy::Partition,
data_struct::{RevisionMap, RevisionVec},
};

use self::pq_kem::kyber::{KyberPublicKey, KyberSecretKey, KYBER_INDCPA_BYTES};

#[macro_use]
pub mod macros;

pub mod api;
pub mod pq_kem;
pub mod primitives;
pub mod serialization;

/// The symmetric key is 32 bytes long to provide 128 bits of post-quantum
/// security.
/// The length of the keys encapsulated by Covercrypt.
///
/// They are 32 bytes long to enable reaching 128 bits of post-quantum security
/// when using it with a sensible DEM.
pub const SYM_KEY_LENGTH: usize = 32;

/// The length of the KMAC key.
/// The length of the KMAC key used to sign user secret keys.
///
/// It is only 16-byte long because no post-quantum security is needed for
/// now. An upgraded signature scheme can still be added later when quantum
/// computers become available.
pub const KMAC_KEY_LENGTH: usize = 16;

/// The length of the KMAC output.
/// The length of the KMAC signature.
const KMAC_LENGTH: usize = 32;

/// KMAC signature is used to guarantee the integrity of the user secret keys.
type KmacSignature = [u8; KMAC_LENGTH];

/// Length of the `Covercrypt` tag
/// Length of the Covercrypt early abort tag
const TAG_LENGTH: usize = 16;
type Tag = [u8; TAG_LENGTH];

/// Kyber public key length
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KyberPublicKey([u8; KYBER_INDCPA_PUBLICKEYBYTES]);
/// Covercrypt early abort tag is used during the decapsulation to verify the
/// integrity of the result.
type Tag = [u8; TAG_LENGTH];

impl Deref for KyberPublicKey {
type Target = [u8];
/// Number of colluding users needed to escape tracing.
const TRACING_LEVEL: usize = 2;

fn deref(&self) -> &Self::Target {
&self.0
}
/// 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(Debug, PartialEq, Eq)]
enum PublicSubkey {
Hybridized { pki: KyberPublicKey, hi: EcPoint },
Classic { hi: EcPoint },
}

/// Kyber secret key length
#[derive(Debug, Clone, PartialEq, Eq, Hash, ZeroizeOnDrop)]
pub struct KyberSecretKey([u8; KYBER_INDCPA_SECRETKEYBYTES]);

impl Deref for KyberSecretKey {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.0
}
/// 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(Debug, PartialEq, Eq)]
enum SecretSubkey {
Hybridized { ski: KyberSecretKey, xi: Scalar },
Classic { xi: Scalar },
}

pub(super) type PublicSubkey = (Option<KyberPublicKey>, R25519PublicKey);

/// Covercrypt user IDs are used to make user keys unique and traceable.
///
/// They are composed of a sequence of `LENGTH` scalars.
#[derive(Debug, PartialEq, Eq, Hash)]
struct Uid<const LENGTH: usize>([Scalar; LENGTH]);

/// Covercrypt tracing secret key.
///
/// It is composed of:
/// - `TRACING_LEVEL + 1` scalars;
/// - the user IDs.
#[derive(Debug, PartialEq, Eq)]
pub struct MasterPublicKey {
g1: R25519PublicKey,
g2: R25519PublicKey,
pub(crate) subkeys: HashMap<Partition, PublicSubkey>,
struct TracingSecretKey<const TRACING_LEVEL: usize> {
s: Scalar,
scalars: [Scalar; TRACING_LEVEL],
uids: HashSet<Uid<TRACING_LEVEL>>,
}

pub(super) type SecretSubkey = (Option<KyberSecretKey>, R25519PrivateKey);
/// Covercrypt tracing public key.
#[derive(Debug, PartialEq, Eq)]
struct TracingPublicKey<const TRACING_LEVEL: usize>([EcPoint; TRACING_LEVEL]);

/// The Covercrypt Master Secret Key (MSK).
///
/// It is composed of:
/// - the tracing secret key used to produce challenges to trace user keys;
/// - the secret subkeys associated to each universal coordinates.
#[derive(Debug, PartialEq, Eq)]
pub struct MasterSecretKey {
s: R25519PrivateKey,
s1: R25519PrivateKey,
s2: R25519PrivateKey,
pub(crate) subkeys: RevisionMap<Partition, SecretSubkey>,
tsk: TracingSecretKey<TRACING_LEVEL>,
subkeys: RevisionMap<Partition, SecretSubkey>,
kmac_key: Option<SymmetricKey<KMAC_KEY_LENGTH>>,
}

/// Covercrypt Public Key (PK).
///
/// It is composed of:
/// - the tracing public key;
/// - the public subkeys associated to each universal coordinate.
#[derive(Debug, PartialEq, Eq)]
pub struct MasterPublicKey {
tpk: TracingPublicKey<TRACING_LEVEL>,
subkeys: HashMap<Partition, PublicSubkey>,
}

/// Covercrypt User Secret Key (USK).
///
/// It is composed of:
/// - a user ID (pair of scalars);
/// - the subkeys associated to the coordinates derived from the user
/// decryption policy;
/// - a signature from the MSK that guarantees its integrity.
#[derive(Debug, PartialEq, Eq)]
pub struct UserSecretKey {
a: R25519PrivateKey,
b: R25519PrivateKey,
pub(crate) subkeys: RefCell<RevisionVec<Partition, SecretSubkey>>,
kmac: Option<KmacSignature>,
id: Uid<TRACING_LEVEL>,
subkeys: RefCell<RevisionVec<Partition, SecretSubkey>>,
msk_signature: Option<KmacSignature>,
}

/// Encapsulation of a `SYM_KEY_LENGTH`-byte key for a given coordinate.
///
/// In case the security level of the associated coordinate was set to
/// post-quantum secure, the key encapsulation is hybridized. This implies a
/// significant size overhead.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
enum KeyEncapsulation {
ClassicEncapsulation(Box<[u8; SYM_KEY_LENGTH]>),
HybridEncapsulation(Box<[u8; KYBER_INDCPA_BYTES]>),
Classic([u8; SYM_KEY_LENGTH]),
Hybridized([u8; KYBER_INDCPA_BYTES]),
}

/// Covercrypt encapsulation.
///
/// It is created for a subset of universal coordinates. One key encapsulation
/// is created per associated coordinate.
///
/// It is composed of:
/// - the early abort tag;
/// - the tracing EC points;
/// - the coordinate encapsulations.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Encapsulation {
c1: R25519PublicKey,
c2: R25519PublicKey,
tag: Tag,
encs: HashSet<KeyEncapsulation>,
tracing_points: [EcPoint; TRACING_LEVEL],
coordinate_encapsulations: HashSet<KeyEncapsulation>,
}
29 changes: 29 additions & 0 deletions src/core/pq_kem/kyber.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use pqc_kyber::{KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES};
use std::ops::Deref;
use zeroize::Zeroizing;

pub use pqc_kyber::KYBER_INDCPA_BYTES;

/// Kyber public key length
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KyberPublicKey([u8; KYBER_INDCPA_PUBLICKEYBYTES]);

impl Deref for KyberPublicKey {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.0
}
}

/// Kyber secret key.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KyberSecretKey(Zeroizing<[u8; KYBER_INDCPA_SECRETKEYBYTES]>);

impl Deref for KyberSecretKey {
type Target = [u8];

fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
25 changes: 25 additions & 0 deletions src/core/pq_kem/kyber.rs~
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use pqc_kyber::{KYBER_INDCPA_BYTES, KYBER_INDCPA_PUBLICKEYBYTES, KYBER_INDCPA_SECRETKEYBYTES};

/// Kyber public key length
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct KyberPublicKey([u8; KYBER_INDCPA_PUBLICKEYBYTES]);

impl Deref for KyberPublicKey {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.0
}
}

/// Kyber secret key.
#[derive(Debug, Clone, PartialEq, Eq, Hash, ZeroizeOnDrop)]
pub struct KyberSecretKey([u8; KYBER_INDCPA_SECRETKEYBYTES]);

impl Deref for KyberSecretKey {
type Target = [u8];

fn deref(&self) -> &Self::Target {
&self.0
}
}
1 change: 1 addition & 0 deletions src/core/pq_kem/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod kyber;
1 change: 1 addition & 0 deletions src/core/pq_kem/mod.rs~
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod kyber;
25 changes: 11 additions & 14 deletions src/core/primitives.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//! Implements the cryptographic primitives of `Covercrypt`, based on
//! `bib/Covercrypt.pdf`.
use std::{
cell::RefCell,
collections::{HashMap, HashSet, LinkedList},
Expand Down Expand Up @@ -68,7 +65,7 @@ fn compute_user_key_kmac(msk: &MasterSecretKey, usk: &UserSecretKey) -> Option<K
/// Checks that the provided KMAC matches the user secret key rights
fn verify_user_key_kmac(msk: &MasterSecretKey, usk: &UserSecretKey) -> Result<(), Error> {
let kmac = compute_user_key_kmac(msk, usk);
if usk.kmac != kmac {
if usk.msk_signature != kmac {
return Err(Error::KeyError(
"The provided user key is corrupted.".to_string(),
));
Expand Down Expand Up @@ -232,9 +229,9 @@ pub fn keygen(
a,
b,
subkeys: RefCell::new(subkeys),
kmac: None,
msk_signature: None,
};
usk.kmac = compute_user_key_kmac(msk, &usk);
usk.msk_signature = compute_user_key_kmac(msk, &usk);
Ok(usk)
}

Expand Down Expand Up @@ -268,9 +265,9 @@ pub fn encaps(
let mut coin = Zeroizing::new([0; KYBER_SYMBYTES]);
rng.fill_bytes(&mut *coin);
indcpa_enc(&mut epq_i, &e_i, pk_i, &*coin);
encs.insert(KeyEncapsulation::HybridEncapsulation(Box::new(epq_i)));
encs.insert(KeyEncapsulation::Hybridized(Box::new(epq_i)));
} else {
encs.insert(KeyEncapsulation::ClassicEncapsulation(Box::new(e_i)));
encs.insert(KeyEncapsulation::Classic(Box::new(e_i)));
}
}
// else unknown target partition
Expand Down Expand Up @@ -309,7 +306,7 @@ pub fn decaps(
// partitions.
for (sk_j, x_j) in usk.subkeys.borrow().bfs() {
let e_j = match encapsulation_i {
KeyEncapsulation::HybridEncapsulation(epq_i) => {
KeyEncapsulation::Hybridized(epq_i) => {
if let Some(sk_j) = sk_j {
let mut e_j = [0; SYM_KEY_LENGTH];
indcpa_dec(&mut e_j, &**epq_i, sk_j);
Expand All @@ -319,7 +316,7 @@ pub fn decaps(
continue;
}
}
KeyEncapsulation::ClassicEncapsulation(e_i) => **e_i,
KeyEncapsulation::Classic(e_i) => **e_i,
};
let mut seed = Zeroizing::new([0; SYM_KEY_LENGTH]);
kdf256!(&mut *seed, &(&precomp * x_j).to_bytes());
Expand Down Expand Up @@ -504,7 +501,7 @@ pub fn refresh(
usk.subkeys.replace(new_subkeys);

// Update user key KMAC
usk.kmac = compute_user_key_kmac(msk, usk);
usk.msk_signature = compute_user_key_kmac(msk, usk);

Ok(())
}
Expand Down Expand Up @@ -573,7 +570,7 @@ mod tests {
// The encapsulation holds a unique, hybridized key encapsulation.
assert_eq!(encapsulation.encs.len(), 1);
for key_encapsulation in &encapsulation.encs {
if let KeyEncapsulation::ClassicEncapsulation(_) = key_encapsulation {
if let KeyEncapsulation::Classic(_) = key_encapsulation {
panic!("Wrong hybridization type");
}
}
Expand Down Expand Up @@ -616,7 +613,7 @@ mod tests {
// The developer now has a hybridized key.
assert_eq!(dev_usk.subkeys.borrow().count_elements(), 1);
for key_encapsulation in &encapsulation.encs {
if let KeyEncapsulation::ClassicEncapsulation(_) = key_encapsulation {
if let KeyEncapsulation::Classic(_) = key_encapsulation {
panic!("Wrong hybridization type");
}
}
Expand All @@ -626,7 +623,7 @@ mod tests {
// Client encapsulation holds a unique, classic key encapsulation.
assert_eq!(new_encapsulation.encs.len(), 1);
for key_encapsulation in &new_encapsulation.encs {
if let KeyEncapsulation::HybridEncapsulation(_) = key_encapsulation {
if let KeyEncapsulation::Hybridized(_) = key_encapsulation {
panic!("Wrong hybridization type");
}
}
Expand Down
Loading

0 comments on commit 59ca437

Please sign in to comment.