Skip to content

Commit 2b45717

Browse files
committed
Implement DigestSigner/Verifier
1 parent b648424 commit 2b45717

File tree

5 files changed

+256
-86
lines changed

5 files changed

+256
-86
lines changed

slh-dsa/src/hashes.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,43 @@ mod shake;
77

88
use core::fmt::Debug;
99

10+
use digest::Update;
1011
use hybrid_array::{Array, ArraySize};
12+
use signature::Result;
1113

1214
pub use sha2::*;
1315
pub use shake::*;
1416

1517
use crate::{PkSeed, SkPrf, SkSeed, address::Address};
1618

19+
/// `Digest` parameter for [`DigestSigner`] and [`DigestVerifier`].
20+
///
21+
/// [`DigestSigner`]: signature::DigestSigner
22+
/// [`DigestVerifier`]: signature::DigestVerifier
23+
pub trait HashDigest {
24+
/// The associated `Digest` type.
25+
type Digest: Update;
26+
}
27+
1728
/// A trait specifying the hash functions described in FIPS-205 section 10
18-
pub(crate) trait HashSuite: Sized + Clone + Debug + PartialEq + Eq {
29+
pub(crate) trait HashSuite: HashDigest + Sized + Clone + Debug + PartialEq + Eq {
1930
type N: ArraySize + Debug + Clone + PartialEq + Eq;
2031
type M: ArraySize + Debug + Clone + PartialEq + Eq;
2132

2233
/// Pseudorandom function that generates the randomizer for the randomized hashing of the message to be signed.
2334
fn prf_msg(
2435
sk_prf: &SkPrf<Self::N>,
2536
opt_rand: &Array<u8, Self::N>,
26-
msg: &[&[impl AsRef<[u8]>]],
27-
) -> Array<u8, Self::N>;
37+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
38+
) -> Result<Array<u8, Self::N>>;
2839

2940
/// Hashes a message using a given randomizer
3041
fn h_msg(
3142
rand: &Array<u8, Self::N>,
3243
pk_seed: &PkSeed<Self::N>,
3344
pk_root: &Array<u8, Self::N>,
34-
msg: &[&[impl AsRef<[u8]>]],
35-
) -> Array<u8, Self::M>;
45+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
46+
) -> Result<Array<u8, Self::M>>;
3647

3748
/// PRF that is used to generate the secret values in WOTS+ and FORS private keys.
3849
fn prf_sk(
@@ -76,7 +87,11 @@ mod tests {
7687
let opt_rand = Array::<u8, H::N>::from_fn(|_| 1);
7788
let msg = [2u8; 32];
7889

79-
let result = H::prf_msg(&sk_prf, &opt_rand, &[&[msg]]);
90+
let result = H::prf_msg(&sk_prf, &opt_rand, &|digest| {
91+
digest.update(&msg);
92+
Ok(())
93+
})
94+
.unwrap();
8095

8196
assert_eq!(result.as_slice(), expected);
8297
}
@@ -87,7 +102,11 @@ mod tests {
87102
let pk_root = Array::<u8, H::N>::from_fn(|_| 2);
88103
let msg = [3u8; 32];
89104

90-
let result = H::h_msg(&rand, &pk_seed, &pk_root, &[&[msg]]);
105+
let result = H::h_msg(&rand, &pk_seed, &pk_root, &|digest| {
106+
digest.update(&msg);
107+
Ok(())
108+
})
109+
.unwrap();
91110

92111
assert_eq!(result.as_slice(), expected);
93112
}

slh-dsa/src/hashes/sha2.rs

Lines changed: 70 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33

44
use core::fmt::Debug;
55

6-
use crate::hashes::HashSuite;
6+
use crate::hashes::{HashDigest, HashSuite};
77
use crate::{
88
ParameterSet, address::Address, fors::ForsParams, hypertree::HypertreeParams, wots::WotsParams,
99
xmss::XmssParams,
1010
};
1111
use crate::{PkSeed, SkPrf, SkSeed};
1212
use const_oid::db::fips205;
13-
use digest::{Digest, KeyInit, Mac};
14-
use hmac::Hmac;
13+
use digest::{Digest, KeyInit, Mac, Output, OutputSizeUser, Update};
14+
use hmac::{EagerHash, Hmac};
1515
use hybrid_array::{Array, ArraySize};
1616
use sha2::{Sha256, Sha512};
17+
use signature::Result;
1718
use typenum::{Diff, Sum, U, U16, U24, U30, U32, U34, U39, U42, U47, U49, U64, U128};
1819

1920
/// Implementation of the MGF1 XOF
@@ -44,6 +45,36 @@ pub struct Sha2L1<N, M> {
4445
_m: core::marker::PhantomData<M>,
4546
}
4647

48+
/// `Digest` implementation [`Sha2L1`] and [`Sha2L35`].
49+
pub struct Sha2Digest<H: EagerHash>(Inner<H>);
50+
51+
enum Inner<H: EagerHash> {
52+
Hmac(Hmac<H>),
53+
Hash(H),
54+
}
55+
56+
impl<H: EagerHash> Update for Sha2Digest<H> {
57+
fn update(&mut self, data: &[u8]) {
58+
match &mut self.0 {
59+
Inner::Hmac(hmac) => Mac::update(hmac, data),
60+
Inner::Hash(hash) => Digest::update(hash, data),
61+
}
62+
}
63+
}
64+
65+
impl<H: EagerHash<Core: OutputSizeUser<OutputSize = H::OutputSize>>> Sha2Digest<H> {
66+
fn finalize(self) -> Output<H> {
67+
match self.0 {
68+
Inner::Hmac(hmac) => hmac.finalize().into_bytes(),
69+
Inner::Hash(hash) => hash.finalize(),
70+
}
71+
}
72+
}
73+
74+
impl<N: ArraySize, M: ArraySize> HashDigest for Sha2L1<N, M> {
75+
type Digest = Sha2Digest<Sha256>;
76+
}
77+
4778
impl<N: ArraySize, M: ArraySize> HashSuite for Sha2L1<N, M>
4879
where
4980
N: core::ops::Add<N>,
@@ -61,35 +92,31 @@ where
6192
fn prf_msg(
6293
sk_prf: &SkPrf<Self::N>,
6394
opt_rand: &Array<u8, Self::N>,
64-
msg: &[&[impl AsRef<[u8]>]],
65-
) -> Array<u8, Self::N> {
66-
let mut mac = Hmac::<Sha256>::new_from_slice(sk_prf.as_ref()).unwrap();
95+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
96+
) -> Result<Array<u8, Self::N>> {
97+
let mut mac = Sha2Digest(Inner::Hmac(
98+
Hmac::<Sha256>::new_from_slice(sk_prf.as_ref()).unwrap(),
99+
));
67100
mac.update(opt_rand.as_slice());
68-
msg.iter()
69-
.copied()
70-
.flatten()
71-
.for_each(|msg_part| mac.update(msg_part.as_ref()));
72-
let result = mac.finalize().into_bytes();
73-
Array::clone_from_slice(&result[..Self::N::USIZE])
101+
msg(&mut mac)?;
102+
let result = mac.finalize();
103+
Ok(Array::clone_from_slice(&result[..Self::N::USIZE]))
74104
}
75105

76106
fn h_msg(
77107
rand: &Array<u8, Self::N>,
78108
pk_seed: &PkSeed<Self::N>,
79109
pk_root: &Array<u8, Self::N>,
80-
msg: &[&[impl AsRef<[u8]>]],
81-
) -> Array<u8, Self::M> {
82-
let mut h = Sha256::new();
110+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
111+
) -> Result<Array<u8, Self::M>> {
112+
let mut h = Sha2Digest(Inner::Hash(Sha256::new()));
83113
h.update(rand);
84-
h.update(pk_seed);
114+
h.update(pk_seed.as_ref());
85115
h.update(pk_root);
86-
msg.iter()
87-
.copied()
88-
.flatten()
89-
.for_each(|msg_part| h.update(msg_part.as_ref()));
116+
msg(&mut h)?;
90117
let result = Array(h.finalize().into());
91118
let seed = rand.clone().concat(pk_seed.0.clone()).concat(result);
92-
mgf1::<Sha256, Self::M>(&seed)
119+
Ok(mgf1::<Sha256, Self::M>(&seed))
93120
}
94121

95122
fn prf_sk(
@@ -117,7 +144,8 @@ where
117144
.chain_update(pk_seed)
118145
.chain_update(&zeroes)
119146
.chain_update(adrs.compressed());
120-
m.iter().for_each(|x| sha.update(x.as_slice()));
147+
m.iter()
148+
.for_each(|x| Update::update(&mut sha, x.as_slice()));
121149
let hash = sha.finalize();
122150
Array::clone_from_slice(&hash[..Self::N::USIZE])
123151
}
@@ -210,6 +238,10 @@ pub struct Sha2L35<N, M> {
210238
_m: core::marker::PhantomData<M>,
211239
}
212240

241+
impl<N: ArraySize, M: ArraySize> HashDigest for Sha2L35<N, M> {
242+
type Digest = Sha2Digest<Sha512>;
243+
}
244+
213245
impl<N: ArraySize, M: ArraySize> HashSuite for Sha2L35<N, M>
214246
where
215247
N: core::ops::Add<N>,
@@ -229,35 +261,31 @@ where
229261
fn prf_msg(
230262
sk_prf: &SkPrf<Self::N>,
231263
opt_rand: &Array<u8, Self::N>,
232-
msg: &[&[impl AsRef<[u8]>]],
233-
) -> Array<u8, Self::N> {
234-
let mut mac = Hmac::<Sha512>::new_from_slice(sk_prf.as_ref()).unwrap();
264+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
265+
) -> Result<Array<u8, Self::N>> {
266+
let mut mac = Sha2Digest(Inner::Hmac(
267+
Hmac::<Sha512>::new_from_slice(sk_prf.as_ref()).unwrap(),
268+
));
235269
mac.update(opt_rand.as_slice());
236-
msg.iter()
237-
.copied()
238-
.flatten()
239-
.for_each(|msg_part| mac.update(msg_part.as_ref()));
240-
let result = mac.finalize().into_bytes();
241-
Array::clone_from_slice(&result[..Self::N::USIZE])
270+
msg(&mut mac)?;
271+
let result = mac.finalize();
272+
Ok(Array::clone_from_slice(&result[..Self::N::USIZE]))
242273
}
243274

244275
fn h_msg(
245276
rand: &Array<u8, Self::N>,
246277
pk_seed: &PkSeed<Self::N>,
247278
pk_root: &Array<u8, Self::N>,
248-
msg: &[&[impl AsRef<[u8]>]],
249-
) -> Array<u8, Self::M> {
250-
let mut h = Sha512::new();
279+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
280+
) -> Result<Array<u8, Self::M>> {
281+
let mut h = Sha2Digest(Inner::Hash(Sha512::new()));
251282
h.update(rand);
252-
h.update(pk_seed);
283+
h.update(pk_seed.as_ref());
253284
h.update(pk_root);
254-
msg.iter()
255-
.copied()
256-
.flatten()
257-
.for_each(|msg_part| h.update(msg_part.as_ref()));
285+
msg(&mut h)?;
258286
let result = Array(h.finalize().into());
259287
let seed = rand.clone().concat(pk_seed.0.clone()).concat(result);
260-
mgf1::<Sha512, Self::M>(&seed)
288+
Ok(mgf1::<Sha512, Self::M>(&seed))
261289
}
262290

263291
fn prf_sk(
@@ -285,7 +313,8 @@ where
285313
.chain_update(pk_seed)
286314
.chain_update(&zeroes)
287315
.chain_update(adrs.compressed());
288-
m.iter().for_each(|x| sha.update(x.as_slice()));
316+
m.iter()
317+
.for_each(|x| Update::update(&mut sha, x.as_slice()));
289318
let hash = sha.finalize();
290319
Array::clone_from_slice(&hash[..Self::N::USIZE])
291320
}

slh-dsa/src/hashes/shake.rs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use core::fmt::Debug;
22

33
use crate::address::Address;
44
use crate::fors::ForsParams;
5-
use crate::hashes::HashSuite;
5+
use crate::hashes::{HashDigest, HashSuite};
66
use crate::hypertree::HypertreeParams;
77
use crate::wots::WotsParams;
88
use crate::xmss::XmssParams;
@@ -13,6 +13,7 @@ use hybrid_array::typenum::consts::{U16, U30, U32};
1313
use hybrid_array::typenum::{U24, U34, U39, U42, U47, U49};
1414
use hybrid_array::{Array, ArraySize};
1515
use sha3::Shake256;
16+
use signature::Result;
1617
use typenum::U;
1718

1819
/// Implementation of the component hash functions using SHAKE256
@@ -24,6 +25,10 @@ pub struct Shake<N, M> {
2425
_m: core::marker::PhantomData<M>,
2526
}
2627

28+
impl<N: ArraySize, M: ArraySize> HashDigest for Shake<N, M> {
29+
type Digest = Shake256;
30+
}
31+
2732
impl<N: ArraySize, M: ArraySize> HashSuite for Shake<N, M>
2833
where
2934
N: Debug + Clone + PartialEq + Eq,
@@ -35,37 +40,31 @@ where
3540
fn prf_msg(
3641
sk_prf: &SkPrf<Self::N>,
3742
opt_rand: &Array<u8, Self::N>,
38-
msg: &[&[impl AsRef<[u8]>]],
39-
) -> Array<u8, Self::N> {
43+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
44+
) -> Result<Array<u8, Self::N>> {
4045
let mut hasher = Shake256::default();
4146
hasher.update(sk_prf.as_ref());
4247
hasher.update(opt_rand.as_slice());
43-
msg.iter()
44-
.copied()
45-
.flatten()
46-
.for_each(|msg_part| hasher.update(msg_part.as_ref()));
48+
msg(&mut hasher)?;
4749
let mut output = Array::<u8, Self::N>::default();
4850
hasher.finalize_xof_into(&mut output);
49-
output
51+
Ok(output)
5052
}
5153

5254
fn h_msg(
5355
rand: &Array<u8, Self::N>,
5456
pk_seed: &PkSeed<Self::N>,
5557
pk_root: &Array<u8, Self::N>,
56-
msg: &[&[impl AsRef<[u8]>]],
57-
) -> Array<u8, Self::M> {
58+
msg: &impl Fn(&mut Self::Digest) -> Result<()>,
59+
) -> Result<Array<u8, Self::M>> {
5860
let mut hasher = Shake256::default();
5961
hasher.update(rand.as_slice());
6062
hasher.update(pk_seed.as_ref());
6163
hasher.update(pk_root.as_ref());
62-
msg.iter()
63-
.copied()
64-
.flatten()
65-
.for_each(|msg_part| hasher.update(msg_part.as_ref()));
64+
msg(&mut hasher)?;
6665
let mut output = Array::<u8, Self::M>::default();
6766
hasher.finalize_xof_into(&mut output);
68-
output
67+
Ok(output)
6968
}
7069

7170
fn prf_sk(
@@ -280,7 +279,11 @@ mod tests {
280279

281280
let expected = hex!("bc5c062307df0a41aeeae19ad655f7b2");
282281

283-
let result = H::prf_msg(&sk_prf, &opt_rand, &[&[msg]]);
282+
let result = H::prf_msg(&sk_prf, &opt_rand, &|digest| {
283+
digest.update(&msg);
284+
Ok(())
285+
})
286+
.unwrap();
284287

285288
assert_eq!(result.as_slice(), expected);
286289
}

0 commit comments

Comments
 (0)