Skip to content
Draft
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
1 change: 1 addition & 0 deletions .github/workflows/lms.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ jobs:
- run: cargo test --no-default-features
- run: cargo test
- run: cargo test --all-features
- run: cargo clippy --all-features --all-targets
10 changes: 7 additions & 3 deletions lms/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ keywords = ["crypto", "signature"]
[dependencies]
digest = "0.11"
hybrid-array = { version = "0.4", features = ["extra-sizes", "zeroize"] }
sha2 = "0.11"
sha2 = { version = "0.11", default-features = false }
static_assertions = "1.1"
rand_core = "0.10"
signature = { version = "3", features = ["alloc", "digest", "rand_core"] }
signature = { version = "3", features = ["digest", "rand_core"] }
typenum = { version = "1.20", features = ["const-generics"] }
zeroize = "1.8"
zeroize = { version = "1.8", default-features = false }

[features]
default = ["alloc"]
alloc = ["signature/alloc"]

[dev-dependencies]
hex = "0.4"
Expand Down
5 changes: 3 additions & 2 deletions lms/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#![warn(unreachable_pub)]

//! LMS in Rust
//!
//! This is a strongly typed implementation of Leighton-Micali signatures. You
//! can find the private key, public key, and signature struct documentations in
//! their respective crates. See [lms] for anything LMS related and [ots] for
//! anything LM-OTS related.

#![warn(unreachable_pub)]
#![cfg_attr(not(test), no_std)]

pub mod error;
pub mod lms;
pub mod ots;
Expand Down
4 changes: 2 additions & 2 deletions lms/src/lms/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! LMS Signing error

use std::error::Error;
use std::fmt::{Display, Formatter, Result};
use core::error::Error;
use core::fmt::{Display, Formatter, Result};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LmsOutOfPrivateKeys {}
Expand Down
17 changes: 12 additions & 5 deletions lms/src/lms/modes.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//! LMS modes
use crate::ots::modes::LmsOtsMode;
use crate::types::Typecode;
use digest::Digest;
use hybrid_array::ArraySize;
use std::ops::Add;
use std::{
use core::ops::{Add, Mul};
use core::{
marker::PhantomData,
ops::{Shl, Sub},
};
use typenum::{Add1, Shleft, Sub1, U1, U5, U10, U15, U20, U25, bit::B1};
use digest::Digest;
use hybrid_array::ArraySize;
use typenum::{Add1, Prod, Shleft, Sub1, Sum, U1, U5, U8, U10, U15, U20, U25, bit::B1};

/// The basic trait that must be implemented for any valid LMS mode
pub trait LmsMode: Typecode + Clone {
Expand All @@ -20,6 +20,8 @@ pub trait LmsMode: Typecode + Clone {
type TreeLen: ArraySize;
/// `h` as a type
type HLen: ArraySize;
/// Length of an encoded signature, computed as `4 + ots_sig_len + 4 + m*h`
type SigLen: ArraySize;
/// The length of the hash function output as a type
const M: usize;
/// `h` as a [usize]
Expand Down Expand Up @@ -82,11 +84,16 @@ where
U1: Shl<<HLen as Add<B1>>::Output>,
Shleft<U1, <HLen as Add<B1>>::Output>: Sub<B1>,
Sub1<Shleft<U1, <HLen as Add<B1>>::Output>>: ArraySize,
Hasher::OutputSize: Mul<HLen>,
OtsMode::SigLen: Add<Prod<Hasher::OutputSize, HLen>>,
U8: Add<Sum<OtsMode::SigLen, Prod<Hasher::OutputSize, HLen>>>,
Sum<U8, Sum<OtsMode::SigLen, Prod<Hasher::OutputSize, HLen>>>: ArraySize,
{
type OtsMode = OtsMode;
type Hasher = Hasher;
type TreeLen = Sub1<Shleft<U1, Add1<HLen>>>;
type HLen = HLen;
type SigLen = Sum<U8, Sum<OtsMode::SigLen, Prod<Hasher::OutputSize, HLen>>>;
const M: usize = M;
const H: usize = H;
const LEAVES: u32 = 1 << H; // precomputed as 2 to the H power
Expand Down
14 changes: 9 additions & 5 deletions lms/src/lms/private.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::constants::{D_INTR, D_LEAF, ID_LEN};
use crate::error::LmsDeserializeError;
use crate::lms::error::LmsOutOfPrivateKeys;
use crate::lms::{LmsMode, Signature, VerifyingKey};
use crate::ots::SigningKey as OtsPrivateKey;
use crate::types::{Identifier, Typecode};
Expand All @@ -11,8 +10,8 @@ use rand_core::{CryptoRng, TryCryptoRng};
use signature::{Error, RandomizedMultipartSignerMut, RandomizedSignerMut};

use core::array::TryFromSliceError;
use std::cmp::Ordering;
use std::ops::Add;
use core::cmp::Ordering;
use core::ops::Add;
use typenum::{Sum, U28};

/// Opaque struct representing a LMS private key
Expand Down Expand Up @@ -122,7 +121,12 @@ impl<Mode: LmsMode> RandomizedMultipartSignerMut<Signature<Mode>> for SigningKey
msg: &[&[u8]],
) -> Result<Signature<Mode>, Error> {
if self.q >= Mode::LEAVES {
return Err(Error::from_source(LmsOutOfPrivateKeys {}));
#[cfg(feature = "alloc")]
return Err(Error::from_source(
crate::lms::error::LmsOutOfPrivateKeys {},
));
#[cfg(not(feature = "alloc"))]
return Err(Error::new());
}

let mut ots_priv_key =
Expand Down Expand Up @@ -154,7 +158,7 @@ where
fn from(pk: SigningKey<Mode>) -> Self {
// Return u32(type) || u32(otstype) || u32(q) || id || seed
Array::try_from_iter(
std::iter::empty()
core::iter::empty()
.chain(Mode::TYPECODE.to_be_bytes())
.chain(Mode::OtsMode::TYPECODE.to_be_bytes())
.chain(pk.q.to_be_bytes())
Expand Down
10 changes: 5 additions & 5 deletions lms/src/lms/public.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::cmp::Ordering;
use std::ops::Add;
use core::cmp::Ordering;
use core::ops::Add;

use crate::constants::{D_LEAF, ID_LEN};

Expand Down Expand Up @@ -113,7 +113,7 @@ where
fn from(pk: VerifyingKey<Mode>) -> Self {
// Return u32(type) || u32(otstype) || id || k
Array::try_from_iter(
std::iter::empty()
core::iter::empty()
.chain(Mode::TYPECODE.to_be_bytes())
.chain(Mode::OtsMode::TYPECODE.to_be_bytes())
.chain(pk.id)
Expand Down Expand Up @@ -162,7 +162,7 @@ impl<'a, Mode: LmsMode> TryFrom<&'a [u8]> for VerifyingKey<Mode> {

#[cfg(test)]
mod tests {
use std::ops::Add;
use core::ops::Add;

use crate::{
lms::SigningKey,
Expand Down Expand Up @@ -261,7 +261,7 @@ mod tests {

fn test_serialize_deserialize_random<Mode: LmsMode>()
where
VerifyingKey<Mode>: std::fmt::Debug,
VerifyingKey<Mode>: core::fmt::Debug,
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U24>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArraySize,
{
Expand Down
77 changes: 26 additions & 51 deletions lms/src/lms/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ use crate::error::LmsDeserializeError;
use crate::lms::LmsMode;
use crate::ots::Signature as OtsSignature;
use crate::ots::modes::LmsOtsMode;
use hybrid_array::{Array, ArraySize};
use hybrid_array::Array;
use signature::SignatureEncoding;

use std::{
cmp::Ordering,
ops::{Add, Mul},
};
use core::cmp::Ordering;

use typenum::{Prod, Sum, U1, U4};
/// Byte representation of a [`Signature`]
pub type SignatureBytes<Mode> = Array<u8, <Mode as LmsMode>::SigLen>;

/// Opaque struct representing a LMS signature
pub struct Signature<Mode: LmsMode> {
Expand All @@ -39,40 +37,29 @@ impl<Mode: LmsMode> PartialEq for Signature<Mode> {
}
}

impl<Mode: LmsMode> SignatureEncoding for Signature<Mode>
where
<Mode::OtsMode as LmsOtsMode>::PLen: Add<U1>,
<Mode::OtsMode as LmsOtsMode>::NLen: Mul<Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>:
Add<U4>,
Sum<
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
U4,
>: ArraySize,
{
type Repr = Vec<u8>; // TODO: Array
impl<Mode: LmsMode> SignatureEncoding for Signature<Mode> {
type Repr = SignatureBytes<Mode>;
}

impl<Mode: LmsMode> From<Signature<Mode>> for Vec<u8>
where
<Mode::OtsMode as LmsOtsMode>::PLen: Add<U1>,
<Mode::OtsMode as LmsOtsMode>::NLen: Mul<Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>:
Add<U4>,
Sum<
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
U4,
>: ArraySize,
{
impl<Mode: LmsMode> From<Signature<Mode>> for SignatureBytes<Mode> {
fn from(val: Signature<Mode>) -> Self {
let mut sig = Vec::new();
sig.extend_from_slice(&val.q.to_be_bytes());
let lms_sig: Array<u8, _> = val.lmots_sig.into();
sig.extend_from_slice(&lms_sig);
sig.extend_from_slice(&Mode::TYPECODE.to_be_bytes());
for node in val.path {
sig.extend_from_slice(&node);
let mut sig = SignatureBytes::<Mode>::default();
let (q, rest) = sig.split_at_mut(size_of::<u32>());
q.copy_from_slice(&val.q.to_be_bytes());

let (lms_sig, rest) = rest.split_at_mut(Mode::OtsMode::SIG_LEN);
lms_sig.copy_from_slice(&Array::<u8, _>::from(val.lmots_sig));

let (typecode, path) = rest.split_at_mut(size_of::<u32>());
typecode.copy_from_slice(&Mode::TYPECODE.to_be_bytes());

let mut path_chunks = path.chunks_exact_mut(Mode::M);
assert_eq!(path_chunks.len(), val.path.len());
for (buf, node) in (&mut path_chunks).zip(val.path.iter()) {
buf.copy_from_slice(node);
}
assert_eq!(path_chunks.into_remainder().len(), 0);

sig
}
}
Expand Down Expand Up @@ -140,10 +127,8 @@ mod tests {
};
use getrandom::SysRng;
use hex_literal::hex;
use hybrid_array::ArraySize;
use hybrid_array::Array;
use signature::{RandomizedSignerMut, Verifier};
use std::ops::{Add, Mul};
use typenum::{Prod, Sum, U1, U4};

#[test]
fn test_deserialize_kat1() {
Expand Down Expand Up @@ -257,23 +242,13 @@ mod tests {
assert!(pk.verify(&msg[..], &sig).is_ok());
}

fn test_serialize_deserialize_random<Mode: LmsMode>()
where
<Mode::OtsMode as LmsOtsMode>::PLen: Add<U1>,
<Mode::OtsMode as LmsOtsMode>::NLen: Mul<Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>:
Add<U4>,
Sum<
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
U4,
>: ArraySize,
{
fn test_serialize_deserialize_random<Mode: LmsMode>() {
let mut rng = rand_core::UnwrapErr(SysRng);
let mut sk = SigningKey::<Mode>::new(&mut rng);
let pk = sk.public();
let msg = b"Hello, world!";
let sig = sk.sign_with_rng(&mut rng, msg);
let sig_bytes: Vec<_> = sig.clone().into();
let sig_bytes: Array<_, _> = sig.clone().into();
let sig2 = Signature::<Mode>::try_from(&sig_bytes[..]).unwrap();
assert!(pk.verify(msg, &sig2).is_ok());
}
Expand Down
4 changes: 2 additions & 2 deletions lms/src/ots/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! LM-OTS Signing error

use std::error::Error;
use std::fmt::{Display, Formatter, Result};
use core::error::Error;
use core::fmt::{Display, Formatter, Result};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LmsOtsInvalidPrivateKey {}
Expand Down
2 changes: 1 addition & 1 deletion lms/src/ots/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ pub mod tests {
},
};
use core::convert::Infallible;
use core::{matches, ops::Add};
use digest::{Digest, OutputSizeUser};
use getrandom::SysRng;
use hex_literal::hex;
use hybrid_array::{Array, ArraySize};
use rand_core::{TryCryptoRng, TryRng, UnwrapErr};
use signature::{RandomizedSignerMut, Verifier};
use std::{matches, ops::Add};
use typenum::{Sum, U2};

// tests that a signature signed with a private key verifies under
Expand Down
Loading
Loading