mod ots;
mod params;
mod tree;
pub use params::{LmotsType, LmsType};
use alloc::vec::Vec;
use params::N;
use crate::rng::{CryptoRng, RngCore};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
InvalidKey,
Exhausted,
InvalidLevels,
Malformed,
}
fn wipe(buf: &mut [u8]) {
for b in buf.iter_mut() {
*b = 0;
}
let _ = core::hint::black_box(&buf);
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct LmsPublicKey {
bytes: Vec<u8>,
}
pub struct LmsPrivateKey {
lms_type: LmsType,
ots_type: LmotsType,
i_id: [u8; 16],
seed: [u8; N],
q: u32,
root: [u8; N],
}
impl LmsPrivateKey {
pub fn from_seed(
lms_type: LmsType,
ots_type: LmotsType,
i_id: &[u8; 16],
seed: &[u8; N],
) -> Self {
let root = tree::compute_root(lms_type, ots_type, i_id, seed);
LmsPrivateKey {
lms_type,
ots_type,
i_id: *i_id,
seed: *seed,
q: 0,
root,
}
}
pub fn generate<R: RngCore + CryptoRng>(
lms_type: LmsType,
ots_type: LmotsType,
rng: &mut R,
) -> Self {
let mut i_id = [0u8; 16];
let mut seed = [0u8; N];
rng.fill_bytes(&mut i_id);
rng.fill_bytes(&mut seed);
Self::from_seed(lms_type, ots_type, &i_id, &seed)
}
pub fn lms_type(&self) -> LmsType {
self.lms_type
}
pub fn ots_type(&self) -> LmotsType {
self.ots_type
}
pub fn public_key(&self) -> LmsPublicKey {
LmsPublicKey {
bytes: tree::encode_public_key(self.lms_type, self.ots_type, &self.i_id, &self.root),
}
}
pub fn remaining(&self) -> u64 {
self.lms_type.leaves().saturating_sub(self.q as u64)
}
pub fn sign<R: RngCore>(&mut self, rng: &mut R, message: &[u8]) -> Result<Vec<u8>, Error> {
let mut c = [0u8; N];
rng.fill_bytes(&mut c);
self.sign_with_c(message, &c)
}
fn sign_with_c(&mut self, message: &[u8], c: &[u8; N]) -> Result<Vec<u8>, Error> {
if self.q as u64 >= self.lms_type.leaves() {
return Err(Error::Exhausted);
}
let sig = tree::sign(
self.lms_type,
self.ots_type,
&self.i_id,
&self.seed,
self.q,
c,
message,
);
self.q += 1;
Ok(sig)
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut v = Vec::with_capacity(4 + 4 + 16 + N + 4);
v.extend_from_slice(&self.lms_type.typecode().to_be_bytes());
v.extend_from_slice(&self.ots_type.typecode().to_be_bytes());
v.extend_from_slice(&self.i_id);
v.extend_from_slice(&self.seed);
v.extend_from_slice(&self.q.to_be_bytes());
v
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != 4 + 4 + 16 + N + 4 {
return Err(Error::Malformed);
}
let lms_type =
LmsType::from_u32(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
.ok_or(Error::Malformed)?;
let ots_type =
LmotsType::from_u32(u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]))
.ok_or(Error::Malformed)?;
let mut i_id = [0u8; 16];
i_id.copy_from_slice(&bytes[8..24]);
let mut seed = [0u8; N];
seed.copy_from_slice(&bytes[24..24 + N]);
let q = u32::from_be_bytes([bytes[24 + N], bytes[25 + N], bytes[26 + N], bytes[27 + N]]);
if q as u64 > lms_type.leaves() {
return Err(Error::Malformed);
}
let root = tree::compute_root(lms_type, ots_type, &i_id, &seed);
Ok(LmsPrivateKey {
lms_type,
ots_type,
i_id,
seed,
q,
root,
})
}
}
impl Drop for LmsPrivateKey {
fn drop(&mut self) {
wipe(&mut self.seed);
wipe(&mut self.i_id);
}
}
impl LmsPublicKey {
pub fn to_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != 24 + N {
return Err(Error::InvalidKey);
}
let lms_ok =
LmsType::from_u32(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
.is_some();
let ots_ok =
LmotsType::from_u32(u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]))
.is_some();
if !lms_ok || !ots_ok {
return Err(Error::InvalidKey);
}
Ok(LmsPublicKey {
bytes: bytes.to_vec(),
})
}
pub fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
tree::verify(&self.bytes, message, signature)
}
}
pub fn verify_lms(public_key: &[u8], message: &[u8], signature: &[u8]) -> bool {
tree::verify(public_key, message, signature)
}
struct HssLevel {
lms_type: LmsType,
ots_type: LmotsType,
i_id: [u8; 16],
seed: [u8; N],
}
impl Drop for HssLevel {
fn drop(&mut self) {
wipe(&mut self.seed);
wipe(&mut self.i_id);
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct HssPublicKey {
bytes: Vec<u8>,
}
pub struct HssPrivateKey {
levels: Vec<HssLevel>,
roots: Vec<[u8; N]>,
q: Vec<u32>,
}
impl HssPrivateKey {
pub fn from_levels(levels: &[(LmsType, LmotsType, [u8; 16], [u8; N])]) -> Result<Self, Error> {
let l = levels.len();
if !(1..=8).contains(&l) {
return Err(Error::InvalidLevels);
}
let mut lv = Vec::with_capacity(l);
let mut roots = Vec::with_capacity(l);
for &(lms_type, ots_type, i_id, seed) in levels {
roots.push(tree::compute_root(lms_type, ots_type, &i_id, &seed));
lv.push(HssLevel {
lms_type,
ots_type,
i_id,
seed,
});
}
Ok(HssPrivateKey {
levels: lv,
roots,
q: alloc::vec![0u32; l],
})
}
pub fn generate<R: RngCore + CryptoRng>(
params: &[(LmsType, LmotsType)],
rng: &mut R,
) -> Result<Self, Error> {
let l = params.len();
if !(1..=8).contains(&l) {
return Err(Error::InvalidLevels);
}
let mut levels = Vec::with_capacity(l);
for &(lms_type, ots_type) in params {
let mut i_id = [0u8; 16];
let mut seed = [0u8; N];
rng.fill_bytes(&mut i_id);
rng.fill_bytes(&mut seed);
levels.push((lms_type, ots_type, i_id, seed));
}
Self::from_levels(&levels)
}
pub fn levels(&self) -> usize {
self.levels.len()
}
pub fn public_key(&self) -> HssPublicKey {
let top = &self.levels[0];
let pub0 = tree::encode_public_key(top.lms_type, top.ots_type, &top.i_id, &self.roots[0]);
let mut bytes = Vec::with_capacity(4 + pub0.len());
bytes.extend_from_slice(&(self.levels.len() as u32).to_be_bytes());
bytes.extend_from_slice(&pub0);
HssPublicKey { bytes }
}
pub fn remaining(&self) -> u64 {
let l = self.levels.len();
let bottom = l - 1;
self.levels[bottom]
.lms_type
.leaves()
.saturating_sub(self.q[bottom] as u64)
}
fn advance(&mut self) {
let bottom = self.levels.len() - 1;
self.q[bottom] += 1;
}
pub fn sign<R: RngCore>(&mut self, rng: &mut R, message: &[u8]) -> Result<Vec<u8>, Error> {
let l = self.levels.len();
if self.remaining() == 0 {
return Err(Error::Exhausted);
}
let mut out = Vec::new();
out.extend_from_slice(&((l - 1) as u32).to_be_bytes());
for i in 0..l {
if i + 1 < l {
self.append_level_signature(&mut out, i, message, None);
} else {
let mut c = [0u8; N];
rng.fill_bytes(&mut c);
self.append_level_signature(&mut out, i, message, Some(&c));
}
}
self.advance();
Ok(out)
}
fn append_level_signature(
&self,
out: &mut Vec<u8>,
i: usize,
message: &[u8],
c: Option<&[u8; N]>,
) {
let l = self.levels.len();
let lv = &self.levels[i];
let signed = if i + 1 < l {
let child = &self.levels[i + 1];
tree::encode_public_key(
child.lms_type,
child.ots_type,
&child.i_id,
&self.roots[i + 1],
)
} else {
message.to_vec()
};
let c = match c {
Some(c) => *c,
None => ots::derive_c(&lv.i_id, &lv.seed, self.q[i], &signed),
};
let sig = tree::sign(
lv.lms_type,
lv.ots_type,
&lv.i_id,
&lv.seed,
self.q[i],
&c,
&signed,
);
out.extend_from_slice(&sig);
if i + 1 < l {
out.extend_from_slice(&signed); }
}
#[cfg(test)]
fn sign_with_cs(&mut self, message: &[u8], c_per_level: &[[u8; N]]) -> Result<Vec<u8>, Error> {
let l = self.levels.len();
if self.remaining() == 0 {
return Err(Error::Exhausted);
}
let mut out = Vec::new();
out.extend_from_slice(&((l - 1) as u32).to_be_bytes());
for (i, c) in c_per_level.iter().enumerate().take(l) {
self.append_level_signature(&mut out, i, message, Some(c));
}
self.advance();
Ok(out)
}
pub fn to_bytes(&self) -> Vec<u8> {
let l = self.levels.len();
let mut v = Vec::with_capacity(4 + l * (4 + 4 + 16 + N + 4));
v.extend_from_slice(&(l as u32).to_be_bytes());
for (i, lv) in self.levels.iter().enumerate() {
v.extend_from_slice(&lv.lms_type.typecode().to_be_bytes());
v.extend_from_slice(&lv.ots_type.typecode().to_be_bytes());
v.extend_from_slice(&lv.i_id);
v.extend_from_slice(&lv.seed);
v.extend_from_slice(&self.q[i].to_be_bytes());
}
v
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() < 4 {
return Err(Error::Malformed);
}
let l = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize;
if !(1..=8).contains(&l) {
return Err(Error::Malformed);
}
let per = 4 + 4 + 16 + N + 4;
if bytes.len() != 4 + l * per {
return Err(Error::Malformed);
}
let mut levels = Vec::with_capacity(l);
let mut roots = Vec::with_capacity(l);
let mut q = Vec::with_capacity(l);
let mut off = 4;
for _ in 0..l {
let lms_type = LmsType::from_u32(u32::from_be_bytes([
bytes[off],
bytes[off + 1],
bytes[off + 2],
bytes[off + 3],
]))
.ok_or(Error::Malformed)?;
let ots_type = LmotsType::from_u32(u32::from_be_bytes([
bytes[off + 4],
bytes[off + 5],
bytes[off + 6],
bytes[off + 7],
]))
.ok_or(Error::Malformed)?;
let mut i_id = [0u8; 16];
i_id.copy_from_slice(&bytes[off + 8..off + 24]);
let mut seed = [0u8; N];
seed.copy_from_slice(&bytes[off + 24..off + 24 + N]);
let qi = u32::from_be_bytes([
bytes[off + 24 + N],
bytes[off + 25 + N],
bytes[off + 26 + N],
bytes[off + 27 + N],
]);
if qi as u64 > lms_type.leaves() {
return Err(Error::Malformed);
}
roots.push(tree::compute_root(lms_type, ots_type, &i_id, &seed));
levels.push(HssLevel {
lms_type,
ots_type,
i_id,
seed,
});
q.push(qi);
off += per;
}
if l >= 2 && q[..l - 1].iter().any(|&qi| qi != 0) {
return Err(Error::Malformed);
}
Ok(HssPrivateKey { levels, roots, q })
}
}
impl HssPublicKey {
pub fn to_bytes(&self) -> &[u8] {
&self.bytes
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
if bytes.len() != 4 + 24 + N {
return Err(Error::InvalidKey);
}
let l = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
if !(1..=8).contains(&l) {
return Err(Error::InvalidKey);
}
LmsPublicKey::from_bytes(&bytes[4..])?;
Ok(HssPublicKey {
bytes: bytes.to_vec(),
})
}
pub fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
verify_hss(&self.bytes, message, signature)
}
}
pub fn verify_hss(public_key: &[u8], message: &[u8], signature: &[u8]) -> bool {
if public_key.len() != 4 + 24 + N || signature.len() < 4 {
return false;
}
let levels = u32::from_be_bytes([public_key[0], public_key[1], public_key[2], public_key[3]]);
let nspk = u32::from_be_bytes([signature[0], signature[1], signature[2], signature[3]]);
if nspk.checked_add(1) != Some(levels) {
return false;
}
let nspk = nspk as usize;
let mut key: Vec<u8> = public_key[4..].to_vec();
let mut off = 4usize;
for _ in 0..nspk {
let sig_len = match lms_sig_len(&signature[off..]) {
Some(n) => n,
None => return false,
};
if off + sig_len > signature.len() {
return false;
}
let sig = &signature[off..off + sig_len];
off += sig_len;
if off + 24 + N > signature.len() {
return false;
}
let next_pub = &signature[off..off + 24 + N];
off += 24 + N;
if !tree::verify(&key, next_pub, sig) {
return false;
}
key = next_pub.to_vec();
}
let sig_len = match lms_sig_len(&signature[off..]) {
Some(n) => n,
None => return false,
};
if off + sig_len != signature.len() {
return false;
}
tree::verify(&key, message, &signature[off..off + sig_len])
}
fn lms_sig_len(buf: &[u8]) -> Option<usize> {
if buf.len() < 8 {
return None;
}
let otssigtype = u32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
let ots_type = LmotsType::from_u32(otssigtype)?;
let ots_len = ots_type.sig_len();
let lms_type_off = 4 + ots_len;
if buf.len() < lms_type_off + 4 {
return None;
}
let sigtype = u32::from_be_bytes([
buf[lms_type_off],
buf[lms_type_off + 1],
buf[lms_type_off + 2],
buf[lms_type_off + 3],
]);
let lms_type = LmsType::from_u32(sigtype)?;
Some(4 + ots_len + 4 + lms_type.h() as usize * N)
}
#[cfg(test)]
mod tests;