Skip to content

Commit bd97d28

Browse files
committed
tests: Add ML-DSA tests
Signed-off-by: Jakub Jelen <[email protected]>
1 parent 56b1427 commit bd97d28

File tree

1 file changed

+353
-0
lines changed

1 file changed

+353
-0
lines changed

cryptoki/tests/ml_dsa.rs

+353
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
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::mldsa::{HashSignAdditionalContext, HedgeType, SignAdditionalContext};
9+
use cryptoki::mechanism::{Mechanism, MechanismType};
10+
use cryptoki::object::{Attribute, MlDsaParameterSetType};
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 ml_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+
print!("SKIP: The PKCS#11 module does not support VerifySignature API");
25+
return Ok(());
26+
}
27+
28+
// open a session
29+
let session = pkcs11.open_rw_session(slot)?;
30+
31+
// log in the session
32+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
33+
34+
let mechanism = Mechanism::MlDsaKeyPairGen;
35+
36+
// pub key template
37+
let pub_key_template = vec![
38+
Attribute::Token(true),
39+
Attribute::MlDsaParameterSet(MlDsaParameterSetType::ML_DSA_65),
40+
Attribute::Verify(true),
41+
];
42+
43+
// priv key template
44+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
45+
46+
// generate a key pair
47+
let (public, private) =
48+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
49+
50+
// without optional context
51+
let mechanism = Mechanism::MlDsa(SignAdditionalContext::new(HedgeType::Preferred, None));
52+
53+
// data to sign
54+
let data = [0xFF, 0x55, 0xDD];
55+
56+
let signature1 = session.sign(&mechanism, private, &data)?;
57+
58+
session.verify(&mechanism, public, &data, &signature1)?;
59+
60+
// also using the new API
61+
session.verify_signature_init(&mechanism, public, &signature1)?;
62+
session.verify_signature(&data)?;
63+
64+
// With Context + Deterministic Hedge
65+
let context = [
66+
0xEE, 0x0B, 0x3F, 0x67, 0x9F, 0xB5, 0x0F, 0x59, 0xAD, 0x31, 0x32, 0x8A, 0xAF, 0x4E, 0x70,
67+
0x2C, 0xCF, 0x60, 0x92, 0xDA, 0x47, 0x94, 0xDC, 0xF0, 0x7C, 0x8, 0xEA, 0x27, 0x8B, 0x34,
68+
0x22, 0x8A, 0x41,
69+
];
70+
let mechanism = Mechanism::MlDsa(SignAdditionalContext::new(
71+
HedgeType::DeterministicRequired,
72+
Some(&context),
73+
));
74+
75+
let signature2 = session.sign(&mechanism, private, &data)?;
76+
let signature3 = session.sign(&mechanism, private, &data)?;
77+
// Deterministic signature
78+
assert_eq!(signature2, signature3);
79+
80+
session.verify(&mechanism, public, &data, &signature2)?;
81+
82+
// also using the new API
83+
session.verify_signature_init(&mechanism, public, &signature2)?;
84+
session.verify_signature(&data)?;
85+
86+
// the signature from previous step should fail to verify with different context
87+
let result = session.verify(&mechanism, public, &data, &signature1);
88+
assert!(result.is_err());
89+
assert!(matches!(
90+
result.unwrap_err(),
91+
Error::Pkcs11(_, Function::Verify)
92+
));
93+
94+
// also using the new API
95+
session.verify_signature_init(&mechanism, public, &signature1)?;
96+
let result = session.verify_signature(&data);
97+
assert!(result.is_err());
98+
assert!(matches!(
99+
result.unwrap_err(),
100+
Error::Pkcs11(_, Function::VerifySignature)
101+
));
102+
103+
// delete keys
104+
session.destroy_object(public)?;
105+
session.destroy_object(private)?;
106+
107+
Ok(())
108+
}
109+
110+
#[test]
111+
#[serial]
112+
fn ml_dsa_multipart() -> TestResult {
113+
let (pkcs11, slot) = init_pins();
114+
// PKCS#11 3.2 API is not supported by this token. Skip
115+
if !pkcs11.is_fn_supported(Function::VerifySignature) {
116+
/* return Ignore(); */
117+
print!("SKIP: The PKCS#11 module does not support VerifySignature API");
118+
return Ok(());
119+
}
120+
121+
// open a session
122+
let session = pkcs11.open_rw_session(slot)?;
123+
124+
// log in the session
125+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
126+
127+
let mechanism = Mechanism::MlDsaKeyPairGen;
128+
129+
// pub key template
130+
let pub_key_template = vec![
131+
Attribute::Token(true),
132+
Attribute::MlDsaParameterSet(MlDsaParameterSetType::ML_DSA_87),
133+
Attribute::Verify(true),
134+
];
135+
136+
// priv key template
137+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
138+
139+
// generate a key pair
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::MlDsa(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 ml_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+
print!("SKIP: The PKCS#11 module does not support VerifySignature API");
188+
return Ok(());
189+
}
190+
191+
// open a session
192+
let session = pkcs11.open_rw_session(slot)?;
193+
194+
// log in the session
195+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
196+
197+
let mechanism = Mechanism::MlDsaKeyPairGen;
198+
199+
// pub key template
200+
let pub_key_template = vec![
201+
Attribute::Token(true),
202+
Attribute::MlDsaParameterSet(MlDsaParameterSetType::ML_DSA_44),
203+
Attribute::Verify(true),
204+
];
205+
206+
// priv key template
207+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
208+
209+
// generate a key pair
210+
let (public, private) =
211+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
212+
213+
// without optional context
214+
let mechanism = Mechanism::HashMlDsa(HashSignAdditionalContext::new(
215+
HedgeType::Preferred,
216+
None,
217+
MechanismType::SHA384,
218+
));
219+
220+
// data to sign is already sha384 hash!
221+
let data = [
222+
0x1E, 0x5A, 0x78, 0xAD, 0x64, 0xDF, 0x22, 0x9A, 0xA2, 0x2F, 0xD7, 0x94, 0xEC, 0x0E, 0x82,
223+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
224+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
225+
0x36, 0x71, 0x31,
226+
];
227+
228+
// the hash ML-DSA does not support multi-part operation
229+
session.sign_init(&mechanism, private)?;
230+
let result = session.sign_update(&data[..10]);
231+
assert!(result.is_err());
232+
assert!(matches!(
233+
result.unwrap_err(),
234+
Error::Pkcs11(RvError::OperationNotInitialized, Function::SignUpdate)
235+
));
236+
237+
// this should do with one-shot
238+
let signature = session.sign(&mechanism, private, &data)?;
239+
240+
// verification of multi-part signature does not work here either
241+
session.verify_init(&mechanism, public)?;
242+
let result = session.verify_update(&data[..10]);
243+
assert!(result.is_err());
244+
assert!(matches!(
245+
result.unwrap_err(),
246+
Error::Pkcs11(RvError::OperationNotInitialized, Function::VerifyUpdate)
247+
));
248+
249+
// this should do with one-shot
250+
session.verify(&mechanism, public, &data, &signature)?;
251+
252+
// multipart verification does not work with the new API either
253+
session.verify_signature_init(&mechanism, public, &signature)?;
254+
let result = session.verify_signature_update(&data[..10]);
255+
assert!(result.is_err());
256+
assert!(matches!(
257+
result.unwrap_err(),
258+
Error::Pkcs11(
259+
RvError::OperationNotInitialized,
260+
Function::VerifySignatureUpdate
261+
)
262+
));
263+
264+
// should work with one-shot new API
265+
session.verify_signature_init(&mechanism, public, &signature)?;
266+
session.verify_signature(&data)?;
267+
268+
// delete keys
269+
session.destroy_object(public)?;
270+
session.destroy_object(private)?;
271+
272+
Ok(())
273+
}
274+
275+
#[test]
276+
#[serial]
277+
fn ml_dsa_hashes() -> TestResult {
278+
let (pkcs11, slot) = init_pins();
279+
// PKCS#11 3.2 API is not supported by this token. Skip
280+
if !pkcs11.is_fn_supported(Function::VerifySignature) {
281+
/* return Ignore(); */
282+
print!("SKIP: The PKCS#11 module does not support VerifySignature API");
283+
return Ok(());
284+
}
285+
286+
// open a session
287+
let session = pkcs11.open_rw_session(slot)?;
288+
289+
// log in the session
290+
session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?;
291+
292+
let mechanism = Mechanism::MlDsaKeyPairGen;
293+
294+
// pub key template
295+
let pub_key_template = vec![
296+
Attribute::Token(true),
297+
Attribute::MlDsaParameterSet(MlDsaParameterSetType::ML_DSA_65),
298+
Attribute::Verify(true),
299+
];
300+
301+
// priv key template
302+
let priv_key_template = vec![Attribute::Token(true), Attribute::Sign(true)];
303+
304+
// generate a key pair
305+
let (public, private) =
306+
session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?;
307+
308+
// without optional context
309+
let mechanism =
310+
Mechanism::HashMlDsaSha3_224(SignAdditionalContext::new(HedgeType::Required, None));
311+
312+
let data = [
313+
0xD0, 0xF6, 0x99, 0x53, 0x11, 0x8C, 0x09, 0xD1, 0x34, 0xDF, 0xA2, 0x0F, 0x1C, 0xC6, 0x4A,
314+
0x1E, 0x5A, 0x78, 0xAD, 0x64, 0xDF, 0x22, 0x9A, 0xA2, 0x2F, 0xD7, 0x94, 0xEC, 0x0E, 0x82,
315+
];
316+
317+
// first try multipart
318+
session.sign_init(&mechanism, private)?;
319+
for part in data.chunks(10) {
320+
session.sign_update(part)?;
321+
}
322+
let signature = session.sign_final()?;
323+
324+
// this should do with one-shot
325+
let signature2 = session.sign(&mechanism, private, &data)?;
326+
327+
// first try multipart
328+
session.verify_init(&mechanism, public)?;
329+
for part in data.chunks(10) {
330+
session.verify_update(part)?;
331+
}
332+
session.verify_final(&signature)?;
333+
334+
// this should do with one-shot
335+
session.verify(&mechanism, public, &data, &signature)?;
336+
337+
// first try multipart
338+
session.verify_signature_init(&mechanism, public, &signature2)?;
339+
for part in data.chunks(10) {
340+
session.verify_signature_update(part)?;
341+
}
342+
session.verify_signature_final()?;
343+
344+
// should work with one-shot new API
345+
session.verify_signature_init(&mechanism, public, &signature2)?;
346+
session.verify_signature(&data)?;
347+
348+
// delete keys
349+
session.destroy_object(public)?;
350+
session.destroy_object(private)?;
351+
352+
Ok(())
353+
}

0 commit comments

Comments
 (0)