Skip to main content

mldsa_native_rs/wrapper/
signature_encoding.rs

1pub use signature::SignatureEncoding;
2
3use generic_array::GenericArray;
4
5use super::ParameterSet;
6use super::utils::typenum::Unsigned;
7
8/// Represents a signature using the specified parameter set.
9///
10/// # Usage
11///
12/// ```rust
13/// # use mldsa_native_rs::*;
14/// # let sk = SigningKey::<P>::new().expect("Keygen failed");
15/// use parameter_sets::ML_DSA_44 as P;
16///
17/// let msg: &[u8] = b"Hello, world!";
18///
19/// let signature = sk.sign(msg);
20///
21/// // There are different ways to obtain a byte representation of the
22/// // signature.
23/// let signature_encoding = signature.to_bytes();
24/// let encoded_signature_via_SignatureEncoding: &[u8] = &signature_encoding;
25/// let encoded_signature_via_AsBytes = signature.as_bytes();
26/// assert_eq!(encoded_signature_via_SignatureEncoding, encoded_signature_via_AsBytes);
27///
28/// let encoded_signature = encoded_signature_via_AsBytes;
29///
30/// let recv_sig: Signature<P> = encoded_signature.try_into()
31///     .expect("Failed to parse the received signature");
32/// assert_eq!(recv_sig, signature);
33///
34/// // Equivalently, using the FromBytes trait
35/// let decoded_signature = Signature::<P>::from_bytes(encoded_signature)
36///     .expect("Failed to decode the received signature");
37/// assert_eq!(decoded_signature, signature);
38/// ```
39///
40/// If the crate is built without the `rand` feature, then the `sign` method
41/// (which takes only the `msg` as an argument) will not be available. In that
42/// case, you must generate your own random bytes and use the signing methods
43/// provided by the [`SeededSigner`](`super::signing_key::SeededSigner`) trait.
44#[derive(Clone, Debug, PartialEq, Eq)]
45pub struct Signature<P: ParameterSet> {
46    pub(super) sig: GenericArray<u8, <P as crate::SignatureLen>::LEN>,
47}
48
49impl<P: ParameterSet> signature::SignatureEncoding for Signature<P> {
50    type Repr = GenericArray<u8, <P as crate::SignatureLen>::LEN>;
51}
52
53// Implement TryFrom<&[u8]> for Signature<P>
54impl<P: ParameterSet> TryFrom<&[u8]> for Signature<P> {
55    type Error = signature::Error;
56
57    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
58        if bytes.len() != <<P as crate::SignatureLen>::LEN>::USIZE {
59            return Err(signature::Error::default());
60        }
61        let arr = GenericArray::from_slice(bytes).clone();
62        Ok(Signature { sig: arr })
63    }
64}
65
66// Implement From<Signature<P>> for GenericArray<u8, <P as crate::SignatureLen>::LEN>
67impl<P: ParameterSet> From<Signature<P>> for GenericArray<u8, <P as crate::SignatureLen>::LEN> {
68    fn from(sig: Signature<P>) -> Self {
69        sig.sig
70    }
71}
72
73impl<P: ParameterSet> AsRef<[u8]> for Signature<P> {
74    fn as_ref(&self) -> &[u8] {
75        self.sig.as_ref()
76    }
77}
78
79/// Prepare the domain separation prefix for pure ML-DSA signing with the
80/// context string `context`.
81pub(crate) fn prepare_domain_separation_prefix<P: ParameterSet>(
82    context: &[u8],
83) -> Result<Vec<u8>, signature::Error> {
84    use crate::ffi;
85    let mut prefix = [0u8; ffi::MLD_DOMAIN_SEPARATION_MAX_BYTES as usize];
86    let ret = unsafe {
87        P::PREPARE_DOMAIN_SEPARATION_PREFIX_FN(
88            prefix.as_mut_ptr(),
89            // the ph and phlen arguments are only used for pre-hashed ML-DSA
90            std::ptr::null(),
91            0,
92            context.as_ptr(),
93            context.len(),
94            ffi::MLD_PREHASH_NONE as ffi::c_int,
95        )
96    };
97    match ret {
98        0 => Err(signature::Error::default()),
99        len => Ok(Vec::from(&prefix[0..len])),
100    }
101}