Skip to content

Commit d71a001

Browse files
committed
Add test coverage for slh_dsa
Signed-off-by: Jakub Jelen <[email protected]>
1 parent a7bee1b commit d71a001

File tree

1 file changed

+343
-0
lines changed

1 file changed

+343
-0
lines changed

cryptoki/tests/slh_dsa.rs

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
// Copyright 2025 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
mod common;
4+
5+
use crate::common::{init_pins, USER_PIN};
6+
use cryptoki::context::Function;
7+
use cryptoki::error::{Error, RvError};
8+
use cryptoki::mechanism::dsa::{HashSignAdditionalContext, HedgeType, SignAdditionalContext};
9+
use cryptoki::mechanism::{Mechanism, MechanismType};
10+
use cryptoki::object::{Attribute, AttributeType, SlhDsaParameterSetType};
11+
use cryptoki::session::UserType;
12+
use cryptoki::types::AuthPin;
13+
use serial_test::serial;
14+
15+
use testresult::TestResult;
16+
17+
#[test]
18+
#[serial]
19+
fn slh_dsa() -> TestResult {
20+
let (pkcs11, slot) = init_pins();
21+
// PKCS#11 3.2 API is not supported by this token. Skip
22+
if !pkcs11.is_fn_supported(Function::VerifySignature) {
23+
/* return Ignore(); */
24+
println!("SKIP: The PKCS#11 module does not support VerifySignature API");
25+
return Ok(());
26+
}
27+
28+
let session = pkcs11.open_rw_session(slot)?;
29+
30+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
31+
32+
let mechanism = Mechanism::SlhDsaKeyPairGen;
33+
34+
let pub_key_template = vec![
35+
Attribute::Token(true),
36+
Attribute::ParameterSet(SlhDsaParameterSetType::SHA2_256F.into()),
37+
Attribute::Verify(true),
38+
];
39+
40+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
41+
42+
let (public, private) =
43+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
44+
45+
// without optional context
46+
let mechanism = Mechanism::SlhDsa(SignAdditionalContext::new(HedgeType::Preferred, None));
47+
48+
let data = [0xFF, 0x55, 0xDD];
49+
50+
let signature1 = session.sign(&mechanism, private, &data)?;
51+
52+
session.verify(&mechanism, public, &data, &signature1)?;
53+
54+
// also using the new API
55+
session.verify_signature_init(&mechanism, public, &signature1)?;
56+
session.verify_signature(&data)?;
57+
58+
// With Context + Deterministic Hedge
59+
let context = [
60+
0xEE, 0x0B, 0x3F, 0x67, 0x9F, 0xB5, 0x0F, 0x59, 0xAD, 0x31, 0x32, 0x8A, 0xAF, 0x4E, 0x70,
61+
0x2C, 0xCF, 0x60, 0x92, 0xDA, 0x47, 0x94, 0xDC, 0xF0, 0x7C, 0x8, 0xEA, 0x27, 0x8B, 0x34,
62+
0x22, 0x8A, 0x41,
63+
];
64+
let mechanism = Mechanism::SlhDsa(SignAdditionalContext::new(
65+
HedgeType::DeterministicRequired,
66+
Some(&context),
67+
));
68+
69+
let signature2 = session.sign(&mechanism, private, &data)?;
70+
let signature3 = session.sign(&mechanism, private, &data)?;
71+
// Deterministic signature
72+
assert_eq!(signature2, signature3);
73+
74+
session.verify(&mechanism, public, &data, &signature2)?;
75+
76+
// also using the new API
77+
session.verify_signature_init(&mechanism, public, &signature2)?;
78+
session.verify_signature(&data)?;
79+
80+
// the signature from previous step should fail to verify with different context
81+
let result = session.verify(&mechanism, public, &data, &signature1);
82+
assert!(result.is_err());
83+
assert!(matches!(
84+
result.unwrap_err(),
85+
Error::Pkcs11(_, Function::Verify)
86+
));
87+
88+
// also using the new API
89+
session.verify_signature_init(&mechanism, public, &signature1)?;
90+
let result = session.verify_signature(&data);
91+
assert!(result.is_err());
92+
assert!(matches!(
93+
result.unwrap_err(),
94+
Error::Pkcs11(_, Function::VerifySignature)
95+
));
96+
97+
// Test converting ParameterSet attributes back to algorithm specific values
98+
let param_attribute = session
99+
.get_attributes(public, &[AttributeType::ParameterSet])?
100+
.remove(0);
101+
let param: SlhDsaParameterSetType = if let Attribute::ParameterSet(num) = param_attribute {
102+
num.into()
103+
} else {
104+
panic!("Expected ParameterSet attribute.");
105+
};
106+
assert_eq!(param, SlhDsaParameterSetType::SHA2_256F);
107+
108+
// delete keys
109+
session.destroy_object(public)?;
110+
session.destroy_object(private)?;
111+
112+
Ok(())
113+
}
114+
115+
#[test]
116+
#[serial]
117+
fn slh_dsa_multipart() -> TestResult {
118+
let (pkcs11, slot) = init_pins();
119+
// PKCS#11 3.2 API is not supported by this token. Skip
120+
if !pkcs11.is_fn_supported(Function::VerifySignature) {
121+
/* return Ignore(); */
122+
println!("SKIP: The PKCS#11 module does not support VerifySignature API");
123+
return Ok(());
124+
}
125+
126+
let session = pkcs11.open_rw_session(slot)?;
127+
128+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
129+
130+
let mechanism = Mechanism::SlhDsaKeyPairGen;
131+
132+
let pub_key_template = vec![
133+
Attribute::Token(true),
134+
Attribute::ParameterSet(SlhDsaParameterSetType::SHA2_192S.into()),
135+
Attribute::Verify(true),
136+
];
137+
138+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
139+
140+
let (public, private) =
141+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
142+
143+
// without optional context
144+
let mechanism = Mechanism::SlhDsa(SignAdditionalContext::new(HedgeType::Required, None));
145+
146+
// data to sign
147+
let data = [
148+
0x1E, 0x5A, 0x78, 0xAD, 0x64, 0xDF, 0x22, 0x9A, 0xA2, 0x2F, 0xD7, 0x94, 0xEC, 0x0E, 0x82,
149+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
150+
0x36, 0x71,
151+
];
152+
153+
session.sign_init(&mechanism, private)?;
154+
for part in data.chunks(10) {
155+
session.sign_update(part)?;
156+
}
157+
let signature = session.sign_final()?;
158+
159+
// verification of multi-part signature
160+
session.verify_init(&mechanism, public)?;
161+
for part in data.chunks(10) {
162+
session.verify_update(part)?;
163+
}
164+
session.verify_final(&signature)?;
165+
166+
// but works with the new API
167+
session.verify_signature_init(&mechanism, public, &signature)?;
168+
for part in data.chunks(10) {
169+
session.verify_signature_update(part)?;
170+
}
171+
session.verify_signature_final()?;
172+
173+
// delete keys
174+
session.destroy_object(public)?;
175+
session.destroy_object(private)?;
176+
177+
Ok(())
178+
}
179+
180+
#[test]
181+
#[serial]
182+
fn slh_dsa_hash() -> TestResult {
183+
let (pkcs11, slot) = init_pins();
184+
// PKCS#11 3.2 API is not supported by this token. Skip
185+
if !pkcs11.is_fn_supported(Function::VerifySignature) {
186+
/* return Ignore(); */
187+
println!("SKIP: The PKCS#11 module does not support VerifySignature API");
188+
return Ok(());
189+
}
190+
191+
let session = pkcs11.open_rw_session(slot)?;
192+
193+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
194+
195+
let mechanism = Mechanism::SlhDsaKeyPairGen;
196+
197+
let pub_key_template = vec![
198+
Attribute::Token(true),
199+
Attribute::ParameterSet(SlhDsaParameterSetType::SHA2_128S.into()),
200+
Attribute::Verify(true),
201+
];
202+
203+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
204+
205+
let (public, private) =
206+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
207+
208+
// without optional context
209+
let mechanism = Mechanism::HashSlhDsa(HashSignAdditionalContext::new(
210+
HedgeType::Preferred,
211+
None,
212+
MechanismType::SHA384,
213+
));
214+
215+
// data to sign is already sha384 hash!
216+
let data = [
217+
0x1E, 0x5A, 0x78, 0xAD, 0x64, 0xDF, 0x22, 0x9A, 0xA2, 0x2F, 0xD7, 0x94, 0xEC, 0x0E, 0x82,
218+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
219+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
220+
0x36, 0x71, 0x31,
221+
];
222+
223+
// the hash SLH-DSA does not support multi-part operation
224+
session.sign_init(&mechanism, private)?;
225+
let result = session.sign_update(&data[..10]);
226+
assert!(result.is_err());
227+
assert!(matches!(
228+
result.unwrap_err(),
229+
Error::Pkcs11(RvError::OperationNotInitialized, Function::SignUpdate)
230+
));
231+
232+
// this should do with one-shot
233+
let signature = session.sign(&mechanism, private, &data)?;
234+
235+
// verification of multi-part signature does not work here either
236+
session.verify_init(&mechanism, public)?;
237+
let result = session.verify_update(&data[..10]);
238+
assert!(result.is_err());
239+
assert!(matches!(
240+
result.unwrap_err(),
241+
Error::Pkcs11(RvError::OperationNotInitialized, Function::VerifyUpdate)
242+
));
243+
244+
// this should do with one-shot
245+
session.verify(&mechanism, public, &data, &signature)?;
246+
247+
// multipart verification does not work with the new API either
248+
session.verify_signature_init(&mechanism, public, &signature)?;
249+
let result = session.verify_signature_update(&data[..10]);
250+
assert!(result.is_err());
251+
assert!(matches!(
252+
result.unwrap_err(),
253+
Error::Pkcs11(
254+
RvError::OperationNotInitialized,
255+
Function::VerifySignatureUpdate
256+
)
257+
));
258+
259+
// should work with one-shot new API
260+
session.verify_signature_init(&mechanism, public, &signature)?;
261+
session.verify_signature(&data)?;
262+
263+
// delete keys
264+
session.destroy_object(public)?;
265+
session.destroy_object(private)?;
266+
267+
Ok(())
268+
}
269+
270+
#[test]
271+
#[serial]
272+
fn slh_dsa_hashes() -> TestResult {
273+
let (pkcs11, slot) = init_pins();
274+
// PKCS#11 3.2 API is not supported by this token. Skip
275+
if !pkcs11.is_fn_supported(Function::VerifySignature) {
276+
/* return Ignore(); */
277+
println!("SKIP: The PKCS#11 module does not support VerifySignature API");
278+
return Ok(());
279+
}
280+
281+
let session = pkcs11.open_rw_session(slot)?;
282+
283+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
284+
285+
let mechanism = Mechanism::SlhDsaKeyPairGen;
286+
287+
let pub_key_template = vec![
288+
Attribute::Token(true),
289+
Attribute::ParameterSet(SlhDsaParameterSetType::SHA2_128F.into()),
290+
Attribute::Verify(true),
291+
];
292+
293+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
294+
295+
let (public, private) =
296+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
297+
298+
// without optional context
299+
let mechanism =
300+
Mechanism::HashSlhDsaSha3_224(SignAdditionalContext::new(HedgeType::Required, None));
301+
302+
let data = [
303+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
304+
0x1E, 0x5A, 0x78, 0xAD, 0x64, 0xDF, 0x22, 0x9A, 0xA2, 0x2F, 0xD7, 0x94, 0xEC, 0x0E, 0x82,
305+
];
306+
307+
// first try multipart
308+
session.sign_init(&mechanism, private)?;
309+
for part in data.chunks(10) {
310+
session.sign_update(part)?;
311+
}
312+
let signature = session.sign_final()?;
313+
314+
// this should do with one-shot
315+
let signature2 = session.sign(&mechanism, private, &data)?;
316+
317+
// first try multipart
318+
session.verify_init(&mechanism, public)?;
319+
for part in data.chunks(10) {
320+
session.verify_update(part)?;
321+
}
322+
session.verify_final(&signature)?;
323+
324+
// this should do with one-shot
325+
session.verify(&mechanism, public, &data, &signature)?;
326+
327+
// first try multipart
328+
session.verify_signature_init(&mechanism, public, &signature2)?;
329+
for part in data.chunks(10) {
330+
session.verify_signature_update(part)?;
331+
}
332+
session.verify_signature_final()?;
333+
334+
// should work with one-shot new API
335+
session.verify_signature_init(&mechanism, public, &signature2)?;
336+
session.verify_signature(&data)?;
337+
338+
// delete keys
339+
session.destroy_object(public)?;
340+
session.destroy_object(private)?;
341+
342+
Ok(())
343+
}

0 commit comments

Comments
 (0)