Skip to content

Commit

Permalink
Included serialisation functions to KES structures.
Browse files Browse the repository at this point in the history
* Seed is now sent as a mutable reference to a slice, so that it can be overwritten with zeroes in place.
* Explicit separation (in different modules) between KES of depth 0 (base case) and the recursive construction
* Included `to_bytes()` and `as_bytes()` for KES key.
* Included `to_bytes()` and `to_bytes()` for KES sig.
  • Loading branch information
iquerejeta committed Oct 12, 2021
1 parent 729234e commit 697bfd6
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 190 deletions.
24 changes: 24 additions & 0 deletions kes-mmm-sumed25519/src/common.rs
Expand Up @@ -63,6 +63,30 @@ impl Seed {
let s2 = Seed::from_slice(&o2);
(s1, s2)
}

/// Function that takes as input a mutable slice, splits it into two, and overwrites the input
/// slice with zeros.
pub fn split_slice(bytes: &mut [u8]) -> ([u8; 32], [u8; 32]) {
let mut left_seed = [0u8; Self::SIZE];
let mut right_seed = [0u8; Self::SIZE];

let mut hleft = sha2::Sha256::default();
let mut hright = sha2::Sha256::default();

hleft.update(&[1]);
hleft.update(&bytes);

hright.update(&[2]);
hright.update(&bytes);

// finalize() consumes the hasher instance.
left_seed.copy_from_slice(hleft.finalize().as_slice());
right_seed.copy_from_slice(hright.finalize().as_slice());

bytes.copy_from_slice(&[0u8; Self::SIZE]);

(left_seed, right_seed)
}
}

/// Structure that represents the depth of the binary tree.
Expand Down
227 changes: 171 additions & 56 deletions kes-mmm-sumed25519/src/erasable_buffer.rs
@@ -1,117 +1,236 @@
//! This module contains the macros to build the KES algorithms.
//! Tentative at making a recursive, and smaller code, which builds a key formed
//! by an array, allowing for a more granular memory management when calling the function.
//! The goal is to provide a similar construction to what is achieved in [sumed25519](./../sumed25519)
//! while maintaining code simplicity, and a smaller crate to facilitate audit and maintenance.

use crate::common::{Depth, Seed};
use crate::errors::Error;
use crate::sumed25519::{hash, PublicKey};
use crate::traits::{KesSig, KesSk};
use ed25519_dalek::{
Keypair as EdKeypair, SecretKey as EdSecretKey, Signature as EdSignature, Signer, Verifier,
use crate::sumed25519::{
hash, PublicKey, INDIVIDUAL_SECRET_SIZE, PUBLIC_KEY_SIZE, SIGMA_SIZE,
};
use crate::single_kes::{Sum0KesSig, Sum0Kes};
use crate::traits::{KesSig, KesSk};
use std::cmp::Ordering;
use zeroize::Zeroize;

#[derive(Zeroize)]
#[zeroize(drop)]
struct Sum0Kes([u8; 64]);
macro_rules! sum_kes {
($name:ident, $signame:ident, $sk:ident, $sigma:ident, $depth:expr, $doc:expr) => {
#[derive(Zeroize)]
#[zeroize(drop)]
#[doc=$doc]
pub struct $name(
[u8; INDIVIDUAL_SECRET_SIZE + $depth * 32 + $depth * (PUBLIC_KEY_SIZE * 2)],
);

/// Structure that represents a KES signature.
pub struct $signame {
sigma: $sigma,
lhs_pk: PublicKey,
rhs_pk: PublicKey,
}

type Sum0KesSig = EdSignature;
// First we implement the KES traits.
impl KesSk for $name {
type Sig = $signame;

impl KesSk for Sum0Kes {
const SIZE: usize = 64;
type Sig = Sum0KesSig;
/// Function that takes a mutable
fn keygen_kes(master_seed: &mut [u8]) -> (Self, PublicKey) {
let mut data = [0u8; Self::SIZE];
let (mut r0, mut seed) = Seed::split_slice(master_seed);
// We copy the seed before overwriting with zeros (in the `keygen_kes` call).
data[$sk::SIZE..$sk::SIZE + 32].copy_from_slice(&seed);

fn keygen_kes(master_seed: &mut Seed) -> (Self, PublicKey) {
let secret = EdSecretKey::from_bytes(master_seed.as_ref())
.expect("Seed is defined with 32 bytes, so it won't fail.");
let public = (&secret).into();
(
Sum0Kes(EdKeypair { secret, public }.to_bytes()),
PublicKey::from_ed25519_publickey(&public),
)
}
let (sk_0, pk_0) = $sk::keygen_kes(&mut r0);
let (_, pk_1) = $sk::keygen_kes(&mut seed);

fn sign_kes(&self, _: usize, m: &[u8]) -> Sum0KesSig {
let ed_sk = EdKeypair::from_bytes(&self.0).expect("internal error: keypair invalid");
ed_sk.sign(m)
}
let pk = hash(&pk_0, &pk_1);

fn update_kes(&mut self, _: usize) -> Result<(), Error> {
Err(Error::KeyCannotBeUpdatedMore)
}
// We write the keys to the main data.
data[..$sk::SIZE].copy_from_slice(&sk_0.0);
data[$sk::SIZE + 32..$sk::SIZE + 64].copy_from_slice(&pk_0.as_bytes());
data[$sk::SIZE + 64..$sk::SIZE + 96].copy_from_slice(&pk_1.as_bytes());

fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != Self::SIZE {
return Err(Error::InvalidSecretKeySize(bytes.len()));
(Self(data), pk)
}

fn sign_kes(&self, period: usize, m: &[u8]) -> Self::Sig {
let t0 = Depth($depth).half();
let sk = $sk::from_bytes(&self.as_bytes()[..$sk::SIZE]).expect("Invalid key bytes");
let sigma = if period < t0 {
sk.sign_kes(period, m)
} else {
sk.sign_kes(period - t0, m)
};

let lhs_pk =
PublicKey::from_bytes(&self.as_bytes()[$sk::SIZE + 32..$sk::SIZE + 64])
.expect("Won't fail as slice has size 32");
let rhs_pk =
PublicKey::from_bytes(&self.as_bytes()[$sk::SIZE + 64..$sk::SIZE + 96])
.expect("Won't fail as slice has size 32");
$signame {
sigma,
lhs_pk,
rhs_pk,
}
}

fn update_kes(&mut self, period: usize) -> Result<(), Error> {
if period + 1 == Depth($depth).total() {
return Err(Error::KeyCannotBeUpdatedMore);
}

match (period + 1).cmp(&Depth($depth).half()) {
Ordering::Less => {
$sk::from_bytes(&self.as_bytes()[..$sk::SIZE])?.update_kes(period)?
}
Ordering::Equal => {
let updated_key = $sk::keygen_kes(&mut self.0[$sk::SIZE..$sk::SIZE + 32]).0;
self.0[..$sk::SIZE].copy_from_slice(updated_key.as_bytes());
}
Ordering::Greater => $sk::from_bytes(&self.as_bytes()[..$sk::SIZE])?
.update_kes(period - &Depth($depth).half())?,
}

Ok(())
}
}

let mut key = [0u8; Self::SIZE];
key.copy_from_slice(bytes);
Ok(Self(key))
}
impl KesSig for $signame {
fn verify_kes(&self, period: usize, pk: &PublicKey, m: &[u8]) -> Result<(), Error> {
if &hash(&self.lhs_pk, &self.rhs_pk) != pk {
return Err(Error::InvalidHashComparison);
}

fn as_bytes(&self) -> &[u8] {
&self.0
}
}
if period < Depth($depth).half() {
self.sigma.verify_kes(period, &self.lhs_pk, m)?;
} else {
self.sigma
.verify_kes(period - &Depth($depth).half(), &self.rhs_pk, m)?
}

impl KesSig for Sum0KesSig {
fn verify_kes(&self, _: usize, pk: &PublicKey, m: &[u8]) -> Result<(), Error> {
let ed_pk = pk.to_ed25519()?;
ed_pk.verify(m, self).map_err(Error::from)
}
Ok(())
}
}

// And now we implement serialisation
impl $name {
/// Byte size of the KES key
pub const SIZE: usize =
INDIVIDUAL_SECRET_SIZE + $depth * 32 + $depth * (PUBLIC_KEY_SIZE * 2);

/// Convert the slice of bytes into `Self`.
///
/// # Errors
/// The function fails if
/// * `bytes.len()` is not of the expected size
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != Self::SIZE {
return Err(Error::InvalidSecretKeySize(bytes.len()));
}

let mut key = [0u8; Self::SIZE];
key.copy_from_slice(bytes);
Ok(Self(key))
}

/// Convert `Self` into it's byte representation. In particular, the encoding returns
/// the following array of size `Self::SIZE`:
/// ( sk_{-1} || seed || self.lhs_pk || self.rhs_pk )
/// where `sk_{-1}` is the secret secret key of lower depth.
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}

impl $signame {
/// Byte size of the signature
pub const SIZE: usize =
SIGMA_SIZE + $depth * (PUBLIC_KEY_SIZE * 2);

/// Convert the slice of bytes into `Self`.
///
/// # Errors
/// The function fails if
/// * `bytes.len()` is not of the expected size
/// * the bytes in the expected positions of the signature do not represent a valid
/// signature
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != Self::SIZE {
return Err(Error::InvalidSecretKeySize(bytes.len()));
}

let sigma = $sigma::from_bytes(&bytes[..$sigma::SIZE])?;
let lhs_pk = PublicKey::from_bytes(&bytes[$sigma::SIZE..$sigma::SIZE + PUBLIC_KEY_SIZE])?;
let rhs_pk = PublicKey::from_bytes(&bytes[$sigma::SIZE + PUBLIC_KEY_SIZE..$sigma::SIZE + 2 * PUBLIC_KEY_SIZE])?;
Ok(Self {sigma, lhs_pk, rhs_pk})
}

/// Convert `Self` into it's byte representation. In particular, the encoding returns
/// the following array of size `Self::SIZE`:
/// ( self.sigma || self.lhs_pk || self.rhs_pk )
pub fn to_bytes(&self) -> [u8; Self::SIZE] {
let mut data = [0u8; Self::SIZE];
data[..$sigma::SIZE].copy_from_slice(&self.sigma.to_bytes());
data[$sigma::SIZE..$sigma::SIZE + PUBLIC_KEY_SIZE].copy_from_slice(self.lhs_pk.as_ref());
data[$sigma::SIZE + PUBLIC_KEY_SIZE..$sigma::SIZE + 2 * PUBLIC_KEY_SIZE].copy_from_slice(self.rhs_pk.as_ref());

data
}
}
};
}

sum_kes_buff!(
sum_kes!(
Sum1Kes,
Sum1KesSig,
Sum0Kes,
Sum0KesSig,
1,
"KES implementation with depth 1"
);
sum_kes_buff!(
sum_kes!(
Sum2Kes,
Sum2KesSig,
Sum1Kes,
Sum1KesSig,
2,
"KES implementation with depth 2"
);
sum_kes_buff!(
sum_kes!(
Sum3Kes,
Sum3KesSig,
Sum2Kes,
Sum2KesSig,
3,
"KES implementation with depth 3"
);
sum_kes_buff!(
sum_kes!(
Sum4Kes,
Sum4KesSig,
Sum3Kes,
Sum3KesSig,
4,
"KES implementation with depth 4"
);
sum_kes_buff!(
sum_kes!(
Sum5Kes,
Sum5KesSig,
Sum4Kes,
Sum4KesSig,
5,
"KES implementation with depth 5"
);
sum_kes_buff!(
sum_kes!(
Sum6Kes,
Sum6KesSig,
Sum5Kes,
Sum5KesSig,
6,
"KES implementation with depth 6"
);
sum_kes_buff!(
sum_kes!(
Sum7Kes,
Sum7KesSig,
Sum6Kes,
Expand All @@ -126,9 +245,7 @@ mod test {

#[test]
fn buff_single() {
let mut seed = Seed::from_bytes([0u8; 32]);

let (mut skey, pkey) = Sum1Kes::keygen_kes(&mut seed);
let (mut skey, pkey) = Sum1Kes::keygen_kes(&mut [0u8; 32]);
let dummy_message = b"tilin";
let sigma = skey.sign_kes(0, dummy_message);

Expand All @@ -140,9 +257,7 @@ mod test {

#[test]
fn buff_4() {
let mut seed = Seed::from_bytes([0u8; 32]);

let (mut skey, pkey) = Sum4Kes::keygen_kes(&mut seed);
let (mut skey, pkey) = Sum4Kes::keygen_kes(&mut [0u8; 32]);
let dummy_message = b"tilin";
let sigma = skey.sign_kes(0, dummy_message);

Expand Down
2 changes: 1 addition & 1 deletion kes-mmm-sumed25519/src/lib.rs
Expand Up @@ -14,7 +14,7 @@ extern crate quickcheck_macros;

mod common;
#[macro_use]
mod macros;
mod single_kes;
pub mod erasable_buffer;
pub mod errors;
pub mod sumed25519;
Expand Down

0 comments on commit 697bfd6

Please sign in to comment.