1#[cfg(feature = "serialization")]
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "serialization")]
25use serde_big_array::BigArray;
26
27use crate::ffi;
28use alloc::vec::Vec;
29use pqcrypto_traits::sign as primitive;
30use pqcrypto_traits::{Error, Result};
31
32use paste::paste;
33
34#[cfg(feature = "std")]
35use std::fmt;
36
37macro_rules! simple_struct {
38 ($type: ident, $size: expr) => {
39 #[derive(Clone, Copy)]
40 #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
41 pub struct $type(
42 #[cfg_attr(feature = "serialization", serde(with = "BigArray"))] [u8; $size],
43 );
44
45 impl $type {
46 fn new() -> Self {
52 $type([0u8; $size])
53 }
54 }
55
56 impl primitive::$type for $type {
57 #[inline]
59 fn as_bytes(&self) -> &[u8] {
60 &self.0
61 }
62
63 fn from_bytes(bytes: &[u8]) -> Result<Self> {
65 if bytes.len() != $size {
66 Err(Error::BadLength {
67 name: stringify!($type),
68 actual: bytes.len(),
69 expected: $size,
70 })
71 } else {
72 let mut array = [0u8; $size];
73 array.copy_from_slice(bytes);
74 Ok($type(array))
75 }
76 }
77 }
78
79 impl PartialEq for $type {
80 fn eq(&self, other: &Self) -> bool {
82 self.0
83 .iter()
84 .zip(other.0.iter())
85 .try_for_each(|(a, b)| if a == b { Ok(()) } else { Err(()) })
86 .is_ok()
87 }
88 }
89
90 #[cfg(feature = "std")]
91 impl fmt::Debug for $type {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 write!(f, "{} ({} bytes)", stringify!($type), self.0.len())
95 }
96 }
97 };
98}
99
100simple_struct!(PublicKey, ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_PUBLICKEYBYTES);
101simple_struct!(SecretKey, ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_SECRETKEYBYTES);
102
103#[derive(Clone, Copy)]
104#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
105pub struct DetachedSignature(
106 #[cfg_attr(feature = "serialization", serde(with = "BigArray"))]
107 [u8; ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES],
108 usize,
109);
110
111impl DetachedSignature {
113 fn new() -> Self {
114 DetachedSignature([0u8; ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES], 0)
115 }
116}
117
118impl primitive::DetachedSignature for DetachedSignature {
119 #[inline]
121 fn as_bytes(&self) -> &[u8] {
122 &self.0[..self.1]
123 }
124
125 #[inline]
126 fn from_bytes(bytes: &[u8]) -> Result<Self> {
127 let actual = bytes.len();
128 let expected = ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES;
129 if actual > expected {
130 return Err(Error::BadLength {
131 name: "DetachedSignature",
132 actual,
133 expected,
134 });
135 }
136 let mut array = [0u8; ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES];
137 array[..bytes.len()].copy_from_slice(bytes);
138 Ok(DetachedSignature(array, actual))
139 }
140}
141
142#[derive(Clone)]
143#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
144pub struct SignedMessage(Vec<u8>);
145impl primitive::SignedMessage for SignedMessage {
146 #[inline]
148 fn as_bytes(&self) -> &[u8] {
149 self.0.as_slice()
150 }
151
152 #[inline]
154 fn from_bytes(bytes: &[u8]) -> Result<Self> {
155 Ok(SignedMessage(bytes.to_vec()))
156 }
157}
158
159impl SignedMessage {
160 pub fn len(&self) -> usize {
161 self.0.len()
162 }
163}
164
165pub const fn public_key_bytes() -> usize {
167 ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_PUBLICKEYBYTES
168}
169
170pub const fn secret_key_bytes() -> usize {
172 ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_SECRETKEYBYTES
173}
174
175pub const fn signature_bytes() -> usize {
177 ffi::PQCLEAN_MLDSA44_CLEAN_CRYPTO_BYTES
178}
179
180macro_rules! gen_keypair {
181 ($variant:ident) => {{
182 let mut pk = PublicKey::new();
183 let mut sk = SecretKey::new();
184 assert_eq!(
185 unsafe { ffi::$variant(pk.0.as_mut_ptr(), sk.0.as_mut_ptr()) },
186 0
187 );
188 (pk, sk)
189 }};
190}
191
192pub fn keypair() -> (PublicKey, SecretKey) {
194 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
195 {
196 if std::is_x86_feature_detected!("avx2") {
197 return gen_keypair!(PQCLEAN_MLDSA44_AVX2_crypto_sign_keypair);
198 }
199 }
200 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
201 {
202 if true {
206 return gen_keypair!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_keypair);
207 }
208 }
209 gen_keypair!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_keypair)
210}
211
212macro_rules! gen_signature {
213 ($variant:ident, $msg:ident, $sk:ident) => {{
214 let max_len = $msg.len() + signature_bytes();
215 let mut signed_msg = Vec::with_capacity(max_len);
216 let mut smlen: usize = 0;
217 unsafe {
218 paste! {
219 ffi::[<$variant _ctx>](
220 signed_msg.as_mut_ptr(),
221 &mut smlen as *mut usize,
222 $msg.as_ptr(),
223 $msg.len(),
224 core::ptr::null(), 0,
225 $sk.0.as_ptr(),
226 );
227 }
228 debug_assert!(smlen <= max_len, "exceeded vector capacity");
229 signed_msg.set_len(smlen);
230 }
231 SignedMessage(signed_msg)
232 }};
233}
234
235macro_rules! gen_signature_ctx {
236 ($variant:ident, $msg:ident, $ctx:ident, $sk:ident) => {{
237 let max_len = $msg.len() + signature_bytes();
238 let mut signed_msg = Vec::with_capacity(max_len);
239 let mut smlen: usize = 0;
240 unsafe {
241 ffi::$variant(
242 signed_msg.as_mut_ptr(),
243 &mut smlen as *mut usize,
244 $msg.as_ptr(),
245 $msg.len(),
246 $ctx.as_ptr(),
247 $ctx.len(),
248 $sk.0.as_ptr(),
249 );
250 debug_assert!(smlen <= max_len, "exceeded vector capacity");
251 signed_msg.set_len(smlen);
252 }
253 SignedMessage(signed_msg)
254 }};
255}
256
257pub fn sign(msg: &[u8], sk: &SecretKey) -> SignedMessage {
259 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
260 {
261 if std::is_x86_feature_detected!("avx2") {
262 return gen_signature!(PQCLEAN_MLDSA44_AVX2_crypto_sign, msg, sk);
263 }
264 }
265 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
266 {
267 if true {
268 return gen_signature!(PQCLEAN_MLDSA44_AARCH64_crypto_sign, msg, sk);
269 }
270 }
271 gen_signature!(PQCLEAN_MLDSA44_CLEAN_crypto_sign, msg, sk)
272}
273
274pub fn sign_ctx(msg: &[u8], ctx: &[u8], sk: &SecretKey) -> SignedMessage {
276 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
277 {
278 if std::is_x86_feature_detected!("avx2") {
279 return gen_signature_ctx!(PQCLEAN_MLDSA44_AVX2_crypto_sign_ctx, msg, ctx, sk);
280 }
281 }
282 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
283 {
284 if true {
285 return gen_signature_ctx!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_ctx, msg, ctx, sk);
286 }
287 }
288 gen_signature_ctx!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_ctx, msg, ctx, sk)
289}
290
291macro_rules! open_signed {
292 ($variant:ident, $sm:ident, $pk:ident) => {{
293 let mut m: Vec<u8> = Vec::with_capacity($sm.len());
294 let mut mlen: usize = 0;
295 match unsafe {
296 paste! { ffi:: [<$variant _ctx>] (
297 m.as_mut_ptr(),
298 &mut mlen as *mut usize,
299 $sm.0.as_ptr(),
300 $sm.len(),
301 core::ptr::null() as *const u8, 0,
302 $pk.0.as_ptr(),
303 )}
304 } {
305 0 => {
306 unsafe { m.set_len(mlen) };
307 Ok(m)
308 }
309 -1 => Err(primitive::VerificationError::InvalidSignature),
310 _ => Err(primitive::VerificationError::UnknownVerificationError),
311 }
312 }};
313}
314
315macro_rules! open_signed_ctx {
316 ($variant:ident, $sm:ident, $ctx: ident, $pk:ident) => {{
317 let mut m: Vec<u8> = Vec::with_capacity($sm.len());
318 let mut mlen: usize = 0;
319 match unsafe {
320 ffi::$variant(
321 m.as_mut_ptr(),
322 &mut mlen as *mut usize,
323 $sm.0.as_ptr(),
324 $sm.len(),
325 $ctx.as_ptr(),
326 $ctx.len(),
327 $pk.0.as_ptr(),
328 )
329 } {
330 0 => {
331 unsafe { m.set_len(mlen) };
332 Ok(m)
333 }
334 -1 => Err(primitive::VerificationError::InvalidSignature),
335 _ => Err(primitive::VerificationError::UnknownVerificationError),
336 }
337 }};
338}
339
340pub fn open(
342 sm: &SignedMessage,
343 pk: &PublicKey,
344) -> core::result::Result<Vec<u8>, primitive::VerificationError> {
345 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
346 {
347 if std::is_x86_feature_detected!("avx2") {
348 return open_signed!(PQCLEAN_MLDSA44_AVX2_crypto_sign_open, sm, pk);
349 }
350 }
351 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
352 {
353 if true {
354 return open_signed!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_open, sm, pk);
355 }
356 }
357 open_signed!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_open, sm, pk)
358}
359
360pub fn open_ctx(
362 sm: &SignedMessage,
363 ctx: &[u8],
364 pk: &PublicKey,
365) -> core::result::Result<Vec<u8>, primitive::VerificationError> {
366 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
367 {
368 if std::is_x86_feature_detected!("avx2") {
369 return open_signed_ctx!(PQCLEAN_MLDSA44_AVX2_crypto_sign_open_ctx, sm, ctx, pk);
370 }
371 }
372 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
373 {
374 if true {
375 return open_signed_ctx!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_open_ctx, sm, ctx, pk);
376 }
377 }
378 open_signed_ctx!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_open_ctx, sm, ctx, pk)
379}
380
381macro_rules! detached_signature {
382 ($variant:ident, $msg:ident, $sk:ident) => {{
383 let mut sig = DetachedSignature::new();
384 unsafe {
385 paste! {
386 ffi:: [<$variant _ctx >](
387 sig.0.as_mut_ptr(),
388 &mut sig.1 as *mut usize,
389 $msg.as_ptr(),
390 $msg.len(),
391 core::ptr::null(), 0,
392 $sk.0.as_ptr(),
393 );
394 }
395 }
396 sig
397 }};
398}
399
400macro_rules! detached_signature_ctx {
401 ($variant:ident, $msg:ident, $ctx:ident, $sk:ident) => {{
402 let mut sig = DetachedSignature::new();
403 unsafe {
404 ffi::$variant(
405 sig.0.as_mut_ptr(),
406 &mut sig.1 as *mut usize,
407 $msg.as_ptr(),
408 $msg.len(),
409 $ctx.as_ptr(),
410 $ctx.len(),
411 $sk.0.as_ptr(),
412 );
413 }
414 sig
415 }};
416}
417
418pub fn detached_sign(msg: &[u8], sk: &SecretKey) -> DetachedSignature {
420 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
421 {
422 if std::is_x86_feature_detected!("avx2") {
423 return detached_signature!(PQCLEAN_MLDSA44_AVX2_crypto_sign_signature, msg, sk);
424 }
425 }
426 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
427 {
428 if true {
429 return detached_signature!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_signature, msg, sk);
430 }
431 }
432 detached_signature!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_signature, msg, sk)
433}
434
435pub fn detached_sign_ctx(msg: &[u8], ctx: &[u8], sk: &SecretKey) -> DetachedSignature {
437 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
438 {
439 if std::is_x86_feature_detected!("avx2") {
440 return detached_signature_ctx!(
441 PQCLEAN_MLDSA44_AVX2_crypto_sign_signature_ctx,
442 msg,
443 ctx,
444 sk
445 );
446 }
447 }
448 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
449 {
450 if true {
451 return detached_signature_ctx!(
452 PQCLEAN_MLDSA44_AARCH64_crypto_sign_signature_ctx,
453 msg,
454 ctx,
455 sk
456 );
457 }
458 }
459 detached_signature_ctx!(
460 PQCLEAN_MLDSA44_CLEAN_crypto_sign_signature_ctx,
461 msg,
462 ctx,
463 sk
464 )
465}
466
467macro_rules! verify_detached_sig {
468 ($variant:ident, $sig:ident, $msg:ident, $pk:ident) => {{
469 let res = unsafe {
470 paste! {
471 ffi:: [<$variant _ctx >](
472 $sig.0.as_ptr(),
473 $sig.1,
474 $msg.as_ptr(),
475 $msg.len(),
476 core::ptr::null(), 0,
477 $pk.0.as_ptr(),
478 )
479 }
480 };
481 match res {
482 0 => Ok(()),
483 -1 => Err(primitive::VerificationError::InvalidSignature),
484 _ => Err(primitive::VerificationError::UnknownVerificationError),
485 }
486 }};
487}
488
489macro_rules! verify_detached_sig_ctx {
490 ($variant:ident, $sig:ident, $msg:ident, $ctx:ident, $pk:ident) => {{
491 let res = unsafe {
492 ffi::$variant(
493 $sig.0.as_ptr(),
494 $sig.1,
495 $msg.as_ptr(),
496 $msg.len(),
497 $ctx.as_ptr(),
498 $ctx.len(),
499 $pk.0.as_ptr(),
500 )
501 };
502 match res {
503 0 => Ok(()),
504 -1 => Err(primitive::VerificationError::InvalidSignature),
505 _ => Err(primitive::VerificationError::UnknownVerificationError),
506 }
507 }};
508}
509
510pub fn verify_detached_signature(
512 sig: &DetachedSignature,
513 msg: &[u8],
514 pk: &PublicKey,
515) -> core::result::Result<(), primitive::VerificationError> {
516 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
517 {
518 if std::is_x86_feature_detected!("avx2") {
519 return verify_detached_sig!(PQCLEAN_MLDSA44_AVX2_crypto_sign_verify, sig, msg, pk);
520 }
521 }
522 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
523 {
524 if true {
525 return verify_detached_sig!(PQCLEAN_MLDSA44_AARCH64_crypto_sign_verify, sig, msg, pk);
526 }
527 }
528 verify_detached_sig!(PQCLEAN_MLDSA44_CLEAN_crypto_sign_verify, sig, msg, pk)
529}
530
531pub fn verify_detached_signature_ctx(
533 sig: &DetachedSignature,
534 msg: &[u8],
535 ctx: &[u8],
536 pk: &PublicKey,
537) -> core::result::Result<(), primitive::VerificationError> {
538 #[cfg(all(enable_x86_avx2, feature = "avx2"))]
539 {
540 if std::is_x86_feature_detected!("avx2") {
541 return verify_detached_sig_ctx!(
542 PQCLEAN_MLDSA44_AVX2_crypto_sign_verify_ctx,
543 sig,
544 msg,
545 ctx,
546 pk
547 );
548 }
549 }
550 #[cfg(all(enable_aarch64_neon, feature = "neon"))]
551 {
552 if true {
553 return verify_detached_sig_ctx!(
554 PQCLEAN_MLDSA44_AARCH64_crypto_sign_verify_ctx,
555 sig,
556 msg,
557 ctx,
558 pk
559 );
560 }
561 }
562 verify_detached_sig_ctx!(
563 PQCLEAN_MLDSA44_CLEAN_crypto_sign_verify_ctx,
564 sig,
565 msg,
566 ctx,
567 pk
568 )
569}
570
571#[cfg(test)]
572mod test {
573 use super::*;
574 use rand::prelude::*;
575
576 #[test]
577 pub fn test_sign() {
578 let mut rng = rand::rng();
579 let len: u16 = rng.random();
580
581 let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
582 let (pk, sk) = keypair();
583 let sm = sign(&message, &sk);
584 let verifiedmsg = open(&sm, &pk).unwrap();
585 assert!(verifiedmsg == message);
586 }
587
588 #[test]
589 pub fn test_sign_detached() {
590 let mut rng = rand::rng();
591 let len: u16 = rng.random();
592 let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
593
594 let (pk, sk) = keypair();
595 let sig = detached_sign(&message, &sk);
596 assert!(verify_detached_signature(&sig, &message, &pk).is_ok());
597 assert!(!verify_detached_signature(&sig, &message[..message.len() - 1], &pk).is_ok());
598 }
599
600 #[test]
601 pub fn test_sign_ctx() {
602 let mut rng = rand::rng();
603 let len: u16 = rng.random();
604 let ctx = (0..10).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
605
606 let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
607 let (pk, sk) = keypair();
608 let sm = sign_ctx(&message, &ctx, &sk);
609 let verifiedmsg = open_ctx(&sm, &ctx, &pk).unwrap();
610 assert!(verifiedmsg == message);
611 assert!(open(&sm, &pk).is_err());
612 }
613
614 #[test]
615 pub fn test_sign_detached_ctx() {
616 let mut rng = rand::rng();
617 let len: u16 = rng.random();
618 let message = (0..len).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
619 let ctx = (0..10).map(|_| rng.gen::<u8>()).collect::<Vec<_>>();
620
621 let (pk, sk) = keypair();
622 let sig = detached_sign_ctx(&message, &ctx, &sk);
623 assert!(verify_detached_signature_ctx(&sig, &message, &ctx, &pk).is_ok());
624 assert!(
625 !verify_detached_signature_ctx(&sig, &message[..message.len() - 1], &ctx, &pk).is_ok()
626 );
627 assert!(!verify_detached_signature_ctx(
628 &sig,
629 &message[..message.len()],
630 &ctx[..ctx.len() - 1],
631 &pk
632 )
633 .is_ok());
634 assert!(!verify_detached_signature(&sig, &message[..message.len() - 1], &pk).is_ok());
635 }
636}