lms: convert to hybrid-array (#865)

This commit is contained in:
Arthur Gautier
2024-09-30 12:12:00 -07:00
committed by GitHub
parent e87f60d47c
commit 87a6da8435
23 changed files with 134 additions and 373 deletions
+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
toolchain:
- 1.72.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
@@ -42,7 +42,7 @@ jobs:
- macos-latest
- windows-latest
toolchain:
- 1.72.0 # MSRV
- 1.81.0 # MSRV
- stable
runs-on: ${{ matrix.platform }}
steps:
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
rust:
- 1.73.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
@@ -42,7 +42,7 @@ jobs:
strategy:
matrix:
rust:
- 1.73.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
rust:
- 1.73.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
- thumbv7em-none-eabi
- wasm32-unknown-unknown
rust:
- 1.72.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
@@ -39,7 +39,7 @@ jobs:
strategy:
matrix:
rust:
- 1.72.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
+1 -1
View File
@@ -22,7 +22,7 @@ jobs:
strategy:
matrix:
rust:
- 1.75.0 # MSRV
- 1.81.0 # MSRV
- stable
steps:
- uses: actions/checkout@v4
+1 -1
View File
@@ -29,6 +29,6 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.75.0
toolchain: 1.81.0
components: clippy
- run: cargo clippy --all-features -- -D warnings
Generated
+6 -7
View File
@@ -488,7 +488,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
"zeroize",
]
[[package]]
@@ -564,9 +563,9 @@ dependencies = [
[[package]]
name = "hybrid-array"
version = "0.2.0-rc.9"
version = "0.2.0-rc.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d306b679262030ad8813a82d4915fc04efff97776e4db7f8eb5137039d56400"
checksum = "a5a41e5b0754cae5aaf7915f1df1147ba8d316fc6e019cfcc00fbaba96d5e030"
dependencies = [
"typenum",
"zeroize",
@@ -654,15 +653,15 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "lms-signature"
version = "0.0.1"
version = "0.1.0-pre"
dependencies = [
"digest 0.10.7",
"generic-array",
"digest 0.11.0-pre.9",
"hex",
"hex-literal",
"hybrid-array",
"rand",
"rand_core",
"sha2 0.10.8",
"sha2 0.11.0-pre.4",
"signature",
"static_assertions",
"typenum",
+1 -1
View File
@@ -13,7 +13,7 @@ homepage = "https://github.com/RustCrypto/signatures/tree/master/dsa"
repository = "https://github.com/RustCrypto/signatures"
categories = ["cryptography", "no-std"]
keywords = ["crypto", "nist", "signature"]
rust-version = "1.72"
rust-version = "1.81"
[dependencies]
digest = "=0.11.0-pre.9"
+1 -1
View File
@@ -14,7 +14,7 @@ readme = "README.md"
categories = ["cryptography", "no-std"]
keywords = ["crypto", "ecc", "nist", "secp256k1", "signature"]
edition = "2021"
rust-version = "1.73"
rust-version = "1.81"
[dependencies]
elliptic-curve = { version = "0.14.0-rc.1", default-features = false, features = ["digest", "sec1"] }
+8 -8
View File
@@ -30,10 +30,7 @@ pub use pkcs8::der::{
use core::fmt;
#[cfg(feature = "pem")]
use {
alloc::string::{String, ToString},
core::str,
};
use core::str;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
@@ -323,10 +320,13 @@ impl str::FromStr for PublicKeyBytes {
}
#[cfg(feature = "pem")]
impl ToString for PublicKeyBytes {
fn to_string(&self) -> String {
self.to_public_key_pem(Default::default())
.expect("PEM serialization error")
impl fmt::Display for PublicKeyBytes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(
&self
.to_public_key_pem(Default::default())
.expect("PEM serialization error"),
)
}
}
-225
View File
@@ -1,225 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cpufeatures"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "crypto-common"
version = "0.2.0-pre.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc17eb697364b18256ec92675ebe6b7b153d2f1041e568d74533c5d0fc1ca162"
dependencies = [
"hybrid-array",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common 0.1.6",
]
[[package]]
name = "digest"
version = "0.11.0-pre.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3be3c52e023de5662dc05a32f747d09a1d6024fdd1f64b0850e373269efb43"
dependencies = [
"crypto-common 0.2.0-pre.3",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
"zeroize",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "hex-literal"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46"
[[package]]
name = "hybrid-array"
version = "0.2.0-pre.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27fbaf242418fe980caf09ed348d5a6aeabe71fc1bd8bebad641f4591ae0a46d"
dependencies = [
"typenum",
]
[[package]]
name = "libc"
version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "lms"
version = "0.1.0"
dependencies = [
"anyhow",
"digest 0.10.7",
"generic-array",
"hex",
"hex-literal",
"rand",
"rand_core",
"sha2",
"signature",
"static_assertions",
"typenum",
"zeroize",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "sha2"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.10.7",
]
[[package]]
name = "signature"
version = "2.3.0-pre.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68a9b544e1447a755027450a51bc29aa51772e071bd839aca18136a7dab1b93e"
dependencies = [
"digest 0.11.0-pre.3",
"rand_core",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "zeroize"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
+5 -5
View File
@@ -1,21 +1,21 @@
[package]
name = "lms-signature"
description = "Pure Rust implementation of Leighton-Micali Hash-Based Signatures (RFC 8554)"
version = "0.0.1"
version = "0.1.0-pre"
edition = "2021"
license = "Apache-2.0 OR MIT"
homepage = "https://github.com/RustCrypto/signatures/tree/master/lms"
repository = "https://github.com/RustCrypto/signatures"
readme = "README.md"
rust-version = "1.73"
rust-version = "1.81"
categories = ["cryptography"]
keywords = ["crypto", "signature"]
[dependencies]
digest = "0.10.7"
generic-array = { version = "0.14.4", features = ["zeroize"] }
digest = "=0.11.0-pre.9"
hybrid-array = { version = "0.2.0-rc.11", features = ["extra-sizes", "zeroize"] }
rand = "0.8.5"
sha2 = "0.10.8"
sha2 = "=0.11.0-pre.4"
static_assertions = "1.1.0"
rand_core = "0.6.4"
signature = { version = "2.3.0-pre.0", features = ["digest", "std", "rand_core"] }
+9 -10
View File
@@ -2,8 +2,7 @@
use crate::ots::modes::LmsOtsMode;
use crate::types::Typecode;
use digest::Digest;
use digest::Output;
use generic_array::ArrayLength;
use hybrid_array::ArraySize;
use std::ops::Add;
use std::{
marker::PhantomData,
@@ -18,9 +17,9 @@ pub trait LmsMode: Typecode + Clone {
/// The underlying LM-OTS mode
type OtsMode: LmsOtsMode;
/// Length of the internal Merkle tree, computed as `2^(h+1)-1`
type TreeLen: ArrayLength<Output<Self::Hasher>>;
type TreeLen: ArraySize;
/// `h` as a type
type HLen: ArrayLength<Output<Self::Hasher>>;
type HLen: ArraySize;
/// The length of the hash function output as a type
const M: usize;
/// `h` as a [usize]
@@ -35,7 +34,7 @@ pub trait LmsMode: Typecode + Clone {
pub struct LmsModeInternal<
OtsMode: LmsOtsMode,
Hasher: Digest,
HLen: ArrayLength<Output<Hasher>>,
HLen: ArraySize,
const M: usize,
const H: usize,
const TC: u32,
@@ -48,7 +47,7 @@ pub struct LmsModeInternal<
impl<
OtsMode: LmsOtsMode,
Hasher: Digest,
TreeLen: ArrayLength<Output<Hasher>>,
TreeLen: ArraySize,
const M: usize,
const H: usize,
const TC: u32,
@@ -62,7 +61,7 @@ impl<
impl<
OtsMode: LmsOtsMode,
Hasher: Digest,
TreeLen: ArrayLength<Output<Hasher>>,
TreeLen: ArraySize,
const M: usize,
const H: usize,
const TC: u32,
@@ -73,7 +72,7 @@ impl<
impl<
OtsMode: LmsOtsMode,
Hasher: Digest,
HLen: ArrayLength<Output<Hasher>>,
HLen: ArraySize,
const M: usize,
const H: usize,
const TC: u32,
@@ -82,7 +81,7 @@ where
HLen: Add<typenum::B1>,
U1: Shl<<HLen as Add<B1>>::Output>,
Shleft<U1, <HLen as Add<B1>>::Output>: Sub<B1>,
Sub1<Shleft<U1, <HLen as Add<B1>>::Output>>: ArrayLength<Output<Hasher>>,
Sub1<Shleft<U1, <HLen as Add<B1>>::Output>>: ArraySize,
{
type OtsMode = OtsMode;
type Hasher = Hasher;
@@ -97,7 +96,7 @@ where
impl<
Hasher: Digest,
OtsMode: LmsOtsMode,
TreeLen: ArrayLength<Output<Hasher>>,
TreeLen: ArraySize,
const M: usize,
const H: usize,
const TC: u32,
+22 -19
View File
@@ -6,10 +6,11 @@ use crate::ots::SigningKey as OtsPrivateKey;
use crate::types::{Identifier, Typecode};
use digest::{Digest, Output, OutputSizeUser};
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::{Array, ArraySize};
use rand::{CryptoRng, Rng};
use signature::{Error, RandomizedSignerMut};
use core::array::TryFromSliceError;
use std::cmp::Ordering;
use std::ops::Add;
use typenum::{Sum, U28};
@@ -23,7 +24,7 @@ use typenum::{Sum, U28};
pub struct SigningKey<Mode: LmsMode> {
id: Identifier,
seed: Output<Mode::Hasher>, // Re-generate the leaf privkeys as-needed from a seed
auth_tree: GenericArray<Output<Mode::Hasher>, Mode::TreeLen>, // TODO: Decide whether/when to precompute
auth_tree: Array<Output<Mode::Hasher>, Mode::TreeLen>, // TODO: Decide whether/when to precompute
q: u32,
}
@@ -36,27 +37,26 @@ impl<Mode: LmsMode> SigningKey<Mode> {
let mut seed = Output::<Mode::Hasher>::default();
rng.fill_bytes(seed.as_mut());
Self::new_from_seed(id, seed)
Self::new_from_seed(id, seed).expect("size invariant violation")
}
// Returns a new LMS private key generated pseudorandomly from an identifier
// and secret seed. The seed must be equal to the hash output length of the
// LMS mode ([Mode::M])
//
// TODO: Return error rather than panic? Or just make the input a
// GenericArray? This is the algorithm from Appendix A of
// <https://datatracker.ietf.org/doc/html/rfc8554#appendix-A>
pub fn new_from_seed(id: Identifier, seed: impl AsRef<[u8]>) -> Self {
pub fn new_from_seed(
id: Identifier,
seed: impl AsRef<[u8]>,
) -> Result<Self, TryFromSliceError> {
//let seed = seed.as_ref();
let seed = GenericArray::clone_from_slice(seed.as_ref());
let seed = Array::try_from(seed.as_ref())?;
let mut sk = Self {
id,
seed,
auth_tree: GenericArray::default(),
auth_tree: Array::default(),
q: 0, // we set q = 0 when generating keys; it will change
};
sk.gen_pk_tree(); // TODO: Use lazy generation / MTT
sk
Ok(sk)
}
/// Generates a Merkle tree of OTS public key hashes, using the indexing scheme of RFC 8554 offset by 1
@@ -135,14 +135,14 @@ impl<Mode: LmsMode> RandomizedSignerMut<Signature<Mode>> for SigningKey<Mode> {
/// Converts a [PrivateKey] into its byte representation
impl<Mode: LmsMode> From<SigningKey<Mode>>
for GenericArray<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U28>>
for Array<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U28>>
where
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U28>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U28>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U28>: ArraySize,
{
fn from(pk: SigningKey<Mode>) -> Self {
// Return u32(type) || u32(otstype) || u32(q) || id || seed
GenericArray::from_exact_iter(
Array::try_from_iter(
std::iter::empty()
.chain(Mode::TYPECODE.to_be_bytes())
.chain(Mode::OtsMode::TYPECODE.to_be_bytes())
@@ -188,8 +188,8 @@ impl<'a, Mode: LmsMode> TryFrom<&'a [u8]> for SigningKey<Mode> {
let mut key = Self {
q: u32::from_be_bytes(q.try_into().expect("ok")),
id: id.try_into().expect("ok"),
seed: GenericArray::clone_from_slice(seed),
auth_tree: GenericArray::default(),
seed: Array::try_from(seed).expect("ok"),
auth_tree: Array::default(),
};
key.gen_pk_tree();
Ok(key)
@@ -215,7 +215,8 @@ mod tests {
let id = hex!("d08fabd4a2091ff0a8cb4ed834e74534");
let expected_k = hex!("32a58885cd9ba0431235466bff9651c6c92124404d45fa53cf161c28f1ad5a8e");
let lms_priv = SigningKey::<LmsSha256M32H10<LmsOtsSha256N32W4>>::new_from_seed(id, seed);
let lms_priv =
SigningKey::<LmsSha256M32H10<LmsOtsSha256N32W4>>::new_from_seed(id, seed).unwrap();
let lms_pub = lms_priv.public();
assert_eq!(lms_pub.k(), expected_k);
assert_eq!(lms_pub.id(), &id);
@@ -229,7 +230,8 @@ mod tests {
let id = hex!("215f83b7ccb9acbcd08db97b0d04dc2b");
let expected_k = hex!("a1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7");
let lms_priv = SigningKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::new_from_seed(id, seed);
let lms_priv =
SigningKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::new_from_seed(id, seed).unwrap();
let lms_pub = lms_priv.public();
assert_eq!(lms_pub.k(), expected_k);
assert_eq!(lms_pub.id(), &id);
@@ -339,7 +341,8 @@ mod tests {
let id = hex!("215f83b7ccb9acbcd08db97b0d04dc2b");
let _expected_k = hex!("a1cd035833e0e90059603f26e07ad2aad152338e7a5e5984bcd5f7bb4eba40b7");
let mut lms_priv = SigningKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::new_from_seed(id, seed);
let mut lms_priv =
SigningKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::new_from_seed(id, seed).unwrap();
lms_priv.q = 4;
let _lms_pub = lms_priv.public();
+13 -14
View File
@@ -8,7 +8,7 @@ use crate::lms::Signature;
use crate::types::Typecode;
use crate::{constants::D_INTR, lms::LmsMode};
use digest::{Digest, OutputSizeUser};
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::{Array, ArraySize};
use signature::{Error, Verifier};
use typenum::{Sum, U24};
@@ -99,14 +99,14 @@ impl<Mode: LmsMode> Verifier<Signature<Mode>> for VerifyingKey<Mode> {
/// Converts a [`VerifyingKey`] into its byte representation
impl<Mode: LmsMode> From<VerifyingKey<Mode>>
for GenericArray<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>>
for Array<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>>
where
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U24>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArraySize,
{
fn from(pk: VerifyingKey<Mode>) -> Self {
// Return u32(type) || u32(otstype) || id || k
GenericArray::from_exact_iter(
Array::try_from_iter(
std::iter::empty()
.chain(Mode::TYPECODE.to_be_bytes())
.chain(Mode::OtsMode::TYPECODE.to_be_bytes())
@@ -149,7 +149,7 @@ impl<'a, Mode: LmsMode> TryFrom<&'a [u8]> for VerifyingKey<Mode> {
Ok(Self {
id: id.try_into().unwrap(),
k: GenericArray::clone_from_slice(k),
k: Array::try_from(k).expect("size invariant violation"),
})
}
}
@@ -165,8 +165,8 @@ mod tests {
ots::{LmsOtsSha256N32W4, LmsOtsSha256N32W8},
};
use digest::OutputSizeUser;
use generic_array::{ArrayLength, GenericArray};
use hex_literal::hex;
use hybrid_array::{Array, ArraySize};
use typenum::{Sum, U24};
// RFC 8554 Appendix F. Test Case 1
@@ -222,7 +222,7 @@ mod tests {
#[test]
fn test_kat1_round_trip() {
let pk = VerifyingKey::<LmsSha256M32H5<LmsOtsSha256N32W8>>::try_from(&KAT1[..]).unwrap();
let pk_serialized: GenericArray<u8, _> = pk.clone().into();
let pk_serialized: Array<u8, _> = pk.clone().into();
let bytes = pk_serialized.as_slice();
assert_eq!(bytes, &KAT1[..]);
}
@@ -244,9 +244,10 @@ mod tests {
c92124404d45fa53cf161c28f1ad5a8e
"
);
let lms_priv = SigningKey::<LmsSha256M32H10<LmsOtsSha256N32W4>>::new_from_seed(id, seed);
let lms_priv =
SigningKey::<LmsSha256M32H10<LmsOtsSha256N32W4>>::new_from_seed(id, seed).unwrap();
let lms_pub = lms_priv.public();
let lms_pub_serialized: GenericArray<u8, _> = lms_pub.into();
let lms_pub_serialized: Array<u8, _> = lms_pub.into();
let bytes = lms_pub_serialized.as_slice();
assert_eq!(bytes, &expected_pubkey[..]);
}
@@ -255,15 +256,13 @@ mod tests {
where
VerifyingKey<Mode>: std::fmt::Debug,
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U24>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArraySize,
{
let rng = rand::thread_rng();
let lms_priv = SigningKey::<Mode>::new(rng);
let lms_pub = lms_priv.public();
let lms_pub_serialized: GenericArray<
u8,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>,
> = lms_pub.clone().into();
let lms_pub_serialized: Array<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>> =
lms_pub.clone().into();
let bytes = lms_pub_serialized.as_slice();
let lms_pub_deserialized = VerifyingKey::<Mode>::try_from(bytes).unwrap();
assert_eq!(lms_pub, lms_pub_deserialized);
+9 -9
View File
@@ -4,7 +4,7 @@ use crate::error::LmsDeserializeError;
use crate::lms::LmsMode;
use crate::ots::modes::LmsOtsMode;
use crate::ots::Signature as OtsSignature;
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::{Array, ArraySize};
use signature::SignatureEncoding;
use std::{
@@ -18,7 +18,7 @@ use typenum::{Prod, Sum, U1, U4};
pub struct Signature<Mode: LmsMode> {
pub(crate) q: u32, // TODO: do these really need to be public?
pub(crate) lmots_sig: OtsSignature<Mode::OtsMode>,
pub(crate) path: GenericArray<digest::Output<Mode::Hasher>, Mode::HLen>,
pub(crate) path: Array<digest::Output<Mode::Hasher>, Mode::HLen>,
}
// manual implementation is required to not require bounds on Mode
@@ -48,9 +48,9 @@ where
Sum<
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
U4,
>: ArrayLength<u8>,
>: ArraySize,
{
type Repr = Vec<u8>; // TODO: GenericArray
type Repr = Vec<u8>; // TODO: Array
}
impl<Mode: LmsMode> From<Signature<Mode>> for Vec<u8>
@@ -62,12 +62,12 @@ where
Sum<
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
U4,
>: ArrayLength<u8>,
>: ArraySize,
{
fn from(val: Signature<Mode>) -> Self {
let mut sig = Vec::new();
sig.extend_from_slice(&val.q.to_be_bytes());
let lms_sig: GenericArray<u8, _> = val.lmots_sig.into();
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 {
@@ -121,7 +121,7 @@ impl<Mode: LmsMode> TryFrom<&[u8]> for Signature<Mode> {
// Path length is already validated by initial length check
let path = path
.chunks_exact(Mode::M)
.map(GenericArray::clone_from_slice)
.map(|chunk| Array::try_from(chunk).expect("size invariant violation"))
.collect();
Ok(Self {
@@ -139,8 +139,8 @@ mod tests {
use crate::lms::modes::*;
use crate::lms::{Signature, SigningKey, VerifyingKey};
use crate::ots::modes::*;
use generic_array::ArrayLength;
use hex_literal::hex;
use hybrid_array::ArraySize;
use rand::thread_rng;
use signature::{RandomizedSignerMut, Verifier};
use typenum::{Prod, Sum, U1, U4};
@@ -264,7 +264,7 @@ mod tests {
Sum<
Prod<<Mode::OtsMode as LmsOtsMode>::NLen, Sum<<Mode::OtsMode as LmsOtsMode>::PLen, U1>>,
U4,
>: ArrayLength<u8>,
>: ArraySize,
{
let mut rng = thread_rng();
let mut sk = SigningKey::<Mode>::new(&mut rng);
+5 -5
View File
@@ -24,8 +24,8 @@ pub mod tests {
use crate::ots::private::SigningKey;
use digest::Digest;
use digest::OutputSizeUser;
use generic_array::{ArrayLength, GenericArray};
use hex_literal::hex;
use hybrid_array::{Array, ArraySize};
use rand::thread_rng;
use rand_core::{CryptoRng, RngCore};
use signature::RandomizedSignerMut;
@@ -39,7 +39,7 @@ pub mod tests {
fn test_sign<Mode: LmsOtsMode>()
where
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U2>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArraySize,
{
let mut rng = thread_rng();
let mut sk = SigningKey::<Mode>::new(0, [0xcc; ID_LEN], &mut rng);
@@ -63,7 +63,7 @@ pub mod tests {
fn test_sign_fail_verify<Mode: LmsOtsMode>()
where
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U2>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArraySize,
{
let mut rng = thread_rng();
let mut sk = SigningKey::<Mode>::new(0, [0xcc; ID_LEN], &mut rng);
@@ -175,8 +175,8 @@ pub mod tests {
let msg = "The enumeration in the Constitution, of certain rights, shall not be construed to deny or disparage others retained by the people.\n".as_bytes();
let sig = sk.try_sign_with_rng(&mut rng, msg).unwrap();
assert_eq!(&sig.c, GenericArray::from_slice(&c));
assert_eq!(&sig.y[0], GenericArray::from_slice(&y0));
assert_eq!(sig.c, Array::from(c));
assert_eq!(sig.y[0], Array::from(y0));
}
#[test]
+15 -28
View File
@@ -1,7 +1,7 @@
use crate::ots::util::coefs;
use crate::types::Typecode;
use digest::{Digest, Output};
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::{Array, ArraySize};
use sha2::Sha256;
use static_assertions::const_assert_eq;
use std::marker::PhantomData;
@@ -13,9 +13,9 @@ pub trait LmsOtsMode: Typecode {
/// The underlying hash function
type Hasher: Digest;
/// The length of the hash function output as a type
type NLen: ArrayLength<u8>;
type NLen: ArraySize;
/// The value of P as a type
type PLen: ArrayLength<Output<Self::Hasher>> + ArrayLength<u8>;
type PLen: ArraySize;
/// The length of the hash function output as a [usize]
const N: usize;
/// The Winternitz window, which should be a value that divides 8
@@ -32,7 +32,7 @@ pub trait LmsOtsMode: Typecode {
const SIG_LEN: usize;
/// Expands a message into its Winternitz coefficients and checksum
fn expand(message: &Output<Self::Hasher>) -> GenericArray<u8, Self::PLen> {
fn expand(message: &Output<Self::Hasher>) -> Array<u8, Self::PLen> {
// Returns an array containing Coefs(message, w, U) || Coefs(checksum, w, V)
// where Coefs(M, w, L) is an array containing coef(M, i, w) for each i in 0..L
// See RFC 8554 section 3.1.3.
@@ -40,7 +40,7 @@ pub trait LmsOtsMode: Typecode {
// Expand the message into its coefficients
// Immediately allocates full expanded length, but only the first U coefficients are used
// in this step
let mut arr: GenericArray<u8, <Self as LmsOtsMode>::PLen> = GenericArray::default();
let mut arr: Array<u8, <Self as LmsOtsMode>::PLen> = Array::default();
for (i, c) in coefs(message, Self::W).enumerate().take(Self::U) {
arr[i] = c;
}
@@ -70,21 +70,12 @@ pub trait LmsOtsMode: Typecode {
}
#[derive(Debug)]
pub struct LmsOtsModeInternal<
Hasher: Digest,
const W: usize,
PP: ArrayLength<GenericArray<u8, Hasher::OutputSize>> + ArrayLength<u8>,
const TC: u32,
> {
pub struct LmsOtsModeInternal<Hasher: Digest, const W: usize, PP: ArraySize, const TC: u32> {
_phantomdata: PhantomData<(Hasher, PP)>,
}
impl<
Hasher: Digest,
const W: usize,
PP: ArrayLength<GenericArray<u8, Hasher::OutputSize>> + ArrayLength<u8>,
const TC: u32,
> Typecode for LmsOtsModeInternal<Hasher, W, PP, TC>
impl<Hasher: Digest, const W: usize, PP: ArraySize, const TC: u32> Typecode
for LmsOtsModeInternal<Hasher, W, PP, TC>
{
const TYPECODE: u32 = TC;
}
@@ -94,12 +85,8 @@ impl<
///
/// NLen and N are calculated using the associated OutputSize of the given Digest, as specified by
/// https://datatracker.ietf.org/doc/html/rfc8554#section-4.1
impl<
Hasher: Digest,
const W: usize,
PP: ArrayLength<GenericArray<u8, Hasher::OutputSize>> + ArrayLength<u8>,
const TC: u32,
> LmsOtsMode for LmsOtsModeInternal<Hasher, W, PP, TC>
impl<Hasher: Digest, const W: usize, PP: ArraySize, const TC: u32> LmsOtsMode
for LmsOtsModeInternal<Hasher, W, PP, TC>
{
type Hasher = Hasher;
type NLen = Hasher::OutputSize;
@@ -177,13 +164,13 @@ const_assert_eq!(LmsOtsSha256N32W8::SIG_LEN, 1124);
#[cfg(test)]
mod test {
use generic_array::GenericArray;
use hybrid_array::Array;
use super::LmsOtsMode;
#[test]
fn test_checksum_zero_w1() {
let arr = [0u8; super::LmsOtsSha256N32W1::N];
let cksm = super::LmsOtsSha256N32W1::expand(GenericArray::from_slice(&arr));
let cksm = super::LmsOtsSha256N32W1::expand(&Array::from(arr));
assert_eq!(
&cksm[super::LmsOtsSha256N32W1::U..],
&[1, 0, 0, 0, 0, 0, 0, 0, 0]
@@ -193,7 +180,7 @@ mod test {
#[test]
fn test_checksum_ones_w1() {
let arr = [255u8; super::LmsOtsSha256N32W1::N];
let cksm = super::LmsOtsSha256N32W1::expand(GenericArray::from_slice(&arr));
let cksm = super::LmsOtsSha256N32W1::expand(&Array::from(arr));
assert_eq!(
&cksm[super::LmsOtsSha256N32W1::U..],
&[0, 0, 0, 0, 0, 0, 0, 0, 0]
@@ -203,14 +190,14 @@ mod test {
#[test]
fn test_checksum_ten_w4() {
let arr = [0xaa; super::LmsOtsSha256N32W4::N];
let cksm = super::LmsOtsSha256N32W4::expand(GenericArray::from_slice(&arr));
let cksm = super::LmsOtsSha256N32W4::expand(&Array::from(arr));
assert_eq!(&cksm[super::LmsOtsSha256N32W4::U..], &[0x01, 0x04, 0x00]);
}
#[test]
fn test_expand_zero_w8() {
let arr = [0u8; super::LmsOtsSha256N32W8::N];
let expanded = super::LmsOtsSha256N32W8::expand(GenericArray::from_slice(&arr));
let expanded = super::LmsOtsSha256N32W8::expand(&Array::from(arr));
let mut expected = [0u8; super::LmsOtsSha256N32W8::P];
expected[super::LmsOtsSha256N32W8::U] = 0x1f;
expected[super::LmsOtsSha256N32W8::U + 1] = 0xe0;
+6 -7
View File
@@ -6,8 +6,7 @@ use crate::ots::signature::Signature;
use crate::types::Identifier;
use digest::{Digest, Output};
use generic_array::sequence::GenericSequence;
use generic_array::GenericArray;
use hybrid_array::Array;
use rand_core::CryptoRngCore;
use signature::{Error, RandomizedSignerMut};
use zeroize::Zeroize;
@@ -19,19 +18,19 @@ use zeroize::Zeroize;
pub struct SigningKey<Mode: LmsOtsMode> {
q: u32,
id: Identifier,
x: GenericArray<Output<Mode::Hasher>, Mode::PLen>,
x: Array<Output<Mode::Hasher>, Mode::PLen>,
valid: bool,
}
impl<Mode: LmsOtsMode> SigningKey<Mode> {
/// Generate a private key, expanded pseudorandomly from a seed generated by `rng`.
/// Uses the algorithm from appendix A <https://datatracker.ietf.org/doc/html/rfc8554#appendix-A>
// a key part of this code working is the DerefMut impl for GenericArray which we abuse in a similar manner to
// a key part of this code working is the DerefMut impl for Array which we abuse in a similar manner to
// generic_array::ArrayBuilder's internal implementation
/// If LM-OTS is being used directly, q MUST be set to the all-zero value
/// <https://datatracker.ietf.org/doc/html/rfc8554#section-4>
pub fn new(q: u32, id: Identifier, rng: &mut impl CryptoRngCore) -> Self {
let mut seed: GenericArray<u8, Mode::NLen> = GenericArray::default();
let mut seed: Array<u8, Mode::NLen> = Array::default();
rng.fill_bytes(&mut seed);
Self::new_from_seed(q, id, seed)
}
@@ -40,7 +39,7 @@ impl<Mode: LmsOtsMode> SigningKey<Mode> {
/// according to Appendix A of <https://datatracker.ietf.org/doc/html/rfc8554#appendix-A>
pub fn new_from_seed(q: u32, id: Identifier, seed: impl AsRef<[u8]>) -> Self {
let seed = seed.as_ref();
let x = GenericArray::generate(|i| {
let x = Array::from_fn(|i| {
Mode::Hasher::new()
.chain_update(id)
.chain_update(q.to_be_bytes())
@@ -134,7 +133,7 @@ impl<Mode: LmsOtsMode> RandomizedSignerMut<Signature<Mode>> for SigningKey<Mode>
}
tmp
});
let y = GenericArray::from_iter(y);
let y = Array::from_iter(y);
let sig = Signature { c, y };
+8 -8
View File
@@ -8,7 +8,7 @@ use crate::ots::signature::Signature;
use crate::types::Identifier;
use digest::{Output, OutputSizeUser};
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::{Array, ArraySize};
use signature::{Error, Verifier};
use std::cmp::Ordering;
use std::ops::Add;
@@ -44,7 +44,7 @@ impl<Mode: LmsOtsMode> Verifier<Signature<Mode>> for VerifyingKey<Mode>
where
// required to concat Q and cksm(Q)
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U2>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U2>: ArraySize,
{
// this implements algorithm 4a of https://datatracker.ietf.org/doc/html/rfc8554#section-4.6
fn verify(&self, msg: &[u8], signature: &Signature<Mode>) -> Result<(), Error> {
@@ -62,14 +62,14 @@ where
/// Converts a [`VerifyingKey`] into its byte representation
impl<Mode: LmsOtsMode> From<VerifyingKey<Mode>>
for GenericArray<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>>
for Array<u8, Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>>
where
<Mode::Hasher as OutputSizeUser>::OutputSize: Add<U24>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArrayLength<u8>,
Sum<<Mode::Hasher as OutputSizeUser>::OutputSize, U24>: ArraySize,
{
fn from(pk: VerifyingKey<Mode>) -> Self {
// Return u32str(type) || I || u32str(q) || K
GenericArray::from_exact_iter(
Array::try_from_iter(
std::iter::empty()
.chain(Mode::TYPECODE.to_be_bytes())
.chain(pk.id)
@@ -108,7 +108,7 @@ impl<'a, Mode: LmsOtsMode> TryFrom<&'a [u8]> for VerifyingKey<Mode> {
Ok(Self {
q: u32::from_be_bytes(q.try_into().expect("ok")),
id: i.try_into().expect("ok"),
k: GenericArray::clone_from_slice(k),
k: Array::try_from(k).expect("ok"),
})
}
}
@@ -122,14 +122,14 @@ mod tests {
use crate::ots::modes::{LmsOtsSha256N32W4, LmsOtsSha256N32W8};
use crate::ots::private::SigningKey;
use crate::ots::public::VerifyingKey;
use generic_array::GenericArray;
use hybrid_array::Array;
use rand::thread_rng;
#[test]
fn test_serde() {
let pk =
SigningKey::<LmsOtsSha256N32W8>::new(0, [0xbb; ID_LEN], &mut thread_rng()).public();
let pk_serialized: GenericArray<u8, _> = pk.clone().into();
let pk_serialized: Array<u8, _> = pk.clone().into();
let bytes = pk_serialized.as_slice();
let pk_deserialized = VerifyingKey::<LmsOtsSha256N32W8>::try_from(bytes);
+15 -15
View File
@@ -5,7 +5,7 @@ use crate::error::LmsDeserializeError;
use crate::ots::modes::LmsOtsMode;
use crate::types::Identifier;
use digest::Digest;
use generic_array::{ArrayLength, GenericArray};
use hybrid_array::{Array, ArraySize};
use signature::SignatureEncoding;
use std::cmp::Ordering;
use std::ops::{Add, Mul};
@@ -17,7 +17,7 @@ use super::VerifyingKey;
/// Opaque struct representing a LM-OTS signature
pub struct Signature<Mode: LmsOtsMode> {
pub(crate) c: digest::Output<Mode::Hasher>,
pub(crate) y: GenericArray<digest::Output<Mode::Hasher>, Mode::PLen>,
pub(crate) y: Array<digest::Output<Mode::Hasher>, Mode::PLen>,
}
// manual implementation is required to not require bounds on Mode
@@ -37,11 +37,9 @@ impl<Mode: LmsOtsMode> PartialEq for Signature<Mode> {
}
}
/// Useful type alias to get the [`GenericArray`] representation
pub type Output<Mode> = GenericArray<
u8,
Sum<Prod<<Mode as LmsOtsMode>::NLen, Sum<<Mode as LmsOtsMode>::PLen, U1>>, U4>,
>;
/// Useful type alias to get the [`Array`] representation
pub type Output<Mode> =
Array<u8, Sum<Prod<<Mode as LmsOtsMode>::NLen, Sum<<Mode as LmsOtsMode>::PLen, U1>>, U4>>;
/// Converts a [`Signature`] into its byte representation
impl<Mode: LmsOtsMode> From<Signature<Mode>> for Output<Mode>
@@ -50,10 +48,10 @@ where
Mode::PLen: Add<U1>,
Mode::NLen: Mul<Sum<Mode::PLen, U1>>,
Prod<Mode::NLen, Sum<Mode::PLen, U1>>: Add<U4>,
Sum<Prod<Mode::NLen, Sum<Mode::PLen, U1>>, U4>: ArrayLength<u8>,
Sum<Prod<Mode::NLen, Sum<Mode::PLen, U1>>, U4>: ArraySize,
{
fn from(sig: Signature<Mode>) -> Self {
GenericArray::from_exact_iter(
Array::try_from_iter(
std::iter::empty()
.chain(Mode::TYPECODE.to_be_bytes())
.chain(sig.c.clone())
@@ -88,12 +86,14 @@ impl<'a, Mode: LmsOtsMode> TryFrom<&'a [u8]> for Signature<Mode> {
// sig is now guaranteed to be of the form C || y[0] || ... || y[p - 1]
let (c, y) = sig.split_at(Mode::N);
let c = GenericArray::clone_from_slice(c);
let c = Array::try_from(c).expect("size invariant violation");
// ys is an iterator over GenericArray<u8, Mode::NLen> of length p
let ys = y.chunks_exact(Mode::N).map(GenericArray::clone_from_slice);
// ys is an iterator over Array<u8, Mode::NLen> of length p
let ys = y
.chunks_exact(Mode::N)
.map(|chunk| Array::try_from(chunk).expect("size invariant violation"));
debug_assert!(ys.len() == Mode::P);
let y = GenericArray::from_iter(ys);
let y = Array::from_iter(ys);
Ok(Self { c, y })
}
@@ -124,7 +124,7 @@ impl<Mode: LmsOtsMode> Signature<Mode> {
.chain_update(q.to_be_bytes())
.chain_update(D_PBLC);
let mut tmp = GenericArray::default();
let mut tmp = Array::default();
for (i, a) in Mode::expand(&msg_hash).into_iter().enumerate() {
tmp.clone_from(&self.y[i]);
@@ -158,7 +158,7 @@ where
Mode::PLen: Add<U1>,
Mode::NLen: Mul<Sum<Mode::PLen, U1>>,
Prod<Mode::NLen, Sum<Mode::PLen, U1>>: Add<U4>,
Sum<Prod<Mode::NLen, Sum<Mode::PLen, U1>>, U4>: ArrayLength<u8>,
Sum<Prod<Mode::NLen, Sum<Mode::PLen, U1>>, U4>: ArraySize,
{
type Repr = Output<Mode>;
}
+1 -1
View File
@@ -13,7 +13,7 @@ readme = "README.md"
categories = ["cryptography", "no-std"]
keywords = ["dsa", "ecdsa", "signature"]
edition = "2021"
rust-version = "1.72"
rust-version = "1.81"
[dependencies]
hmac = { version = "=0.13.0-pre.4", default-features = false, features = ["reset"] }
+1 -1
View File
@@ -6,7 +6,7 @@ FIPS-205 standard
"""
version = "0.1.0"
edition = "2021"
rust-version = "1.75"
rust-version = "1.81"
license = "Apache-2.0 OR MIT"
homepage = "https://github.com/RustCrypto/signatures/tree/master/slh-dsa"
repository = "https://github.com/RustCrypto/signatures"