quantacore/
sign.rs

1//! Digital signature operations.
2//!
3//! This module provides post-quantum digital signatures using ML-DSA (Dilithium).
4
5use crate::device::Device;
6use crate::error::{check_error, QuacError, Result};
7use crate::ffi;
8use crate::types::SignAlgorithm;
9
10use zeroize::ZeroizeOnDrop;
11
12/// Signature key pair.
13///
14/// Contains the public and secret keys for digital signatures.
15/// The secret key is automatically zeroed when dropped.
16#[derive(Clone, ZeroizeOnDrop)]
17pub struct SignatureKeyPair {
18    /// Public key bytes
19    #[zeroize(skip)]
20    public_key: Vec<u8>,
21    /// Secret key bytes (zeroized on drop)
22    secret_key: Vec<u8>,
23    /// Algorithm used
24    #[zeroize(skip)]
25    algorithm: SignAlgorithm,
26}
27
28impl SignatureKeyPair {
29    /// Create a new signature key pair.
30    fn new(public_key: Vec<u8>, secret_key: Vec<u8>, algorithm: SignAlgorithm) -> Self {
31        Self {
32            public_key,
33            secret_key,
34            algorithm,
35        }
36    }
37
38    /// Get the public key.
39    pub fn public_key(&self) -> &[u8] {
40        &self.public_key
41    }
42
43    /// Get the secret key.
44    pub fn secret_key(&self) -> &[u8] {
45        &self.secret_key
46    }
47
48    /// Get the algorithm.
49    pub fn algorithm(&self) -> SignAlgorithm {
50        self.algorithm
51    }
52
53    /// Consume the key pair and return the raw bytes.
54    pub fn into_bytes(mut self) -> (Vec<u8>, Vec<u8>) {
55        let pk = std::mem::take(&mut self.public_key);
56        let sk = std::mem::take(&mut self.secret_key);
57        std::mem::forget(self);
58        (pk, sk)
59    }
60}
61
62impl std::fmt::Debug for SignatureKeyPair {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        f.debug_struct("SignatureKeyPair")
65            .field("algorithm", &self.algorithm)
66            .field("public_key_len", &self.public_key.len())
67            .field("secret_key_len", &self.secret_key.len())
68            .finish()
69    }
70}
71
72/// Signature subsystem.
73///
74/// Provides access to post-quantum digital signature operations
75/// using ML-DSA (formerly Dilithium).
76///
77/// # Example
78///
79/// ```no_run
80/// use quantacore::{initialize, open_first_device, SignAlgorithm};
81///
82/// initialize().unwrap();
83/// let device = open_first_device().unwrap();
84/// let sign = device.sign();
85///
86/// // Generate key pair
87/// let keypair = sign.generate_keypair(SignAlgorithm::MlDsa65).unwrap();
88///
89/// // Sign a message
90/// let message = b"Hello, quantum world!";
91/// let signature = sign.sign(keypair.secret_key(), message, SignAlgorithm::MlDsa65).unwrap();
92///
93/// // Verify the signature
94/// let valid = sign.verify(keypair.public_key(), message, &signature, SignAlgorithm::MlDsa65).unwrap();
95/// assert!(valid);
96/// ```
97#[derive(Clone)]
98pub struct Sign {
99    device: Device,
100}
101
102impl Sign {
103    /// Create a new signature subsystem handle.
104    pub(crate) fn new(device: Device) -> Self {
105        Self { device }
106    }
107
108    /// Generate a signature key pair.
109    ///
110    /// # Arguments
111    ///
112    /// * `algorithm` - The signature algorithm to use
113    ///
114    /// # Returns
115    ///
116    /// A `SignatureKeyPair` containing the public and secret keys.
117    pub fn generate_keypair(&self, algorithm: SignAlgorithm) -> Result<SignatureKeyPair> {
118        let pk_size = algorithm.public_key_size();
119        let sk_size = algorithm.secret_key_size();
120
121        let mut public_key = vec![0u8; pk_size];
122        let mut secret_key = vec![0u8; sk_size];
123        let mut pk_len = pk_size;
124        let mut sk_len = sk_size;
125
126        let result = unsafe {
127            ffi::quac_sign_keygen(
128                self.device.handle(),
129                algorithm.to_raw(),
130                public_key.as_mut_ptr(),
131                &mut pk_len,
132                secret_key.as_mut_ptr(),
133                &mut sk_len,
134            )
135        };
136
137        check_error(result)?;
138
139        public_key.truncate(pk_len);
140        secret_key.truncate(sk_len);
141
142        Ok(SignatureKeyPair::new(public_key, secret_key, algorithm))
143    }
144
145    /// Generate ML-DSA-44 key pair.
146    pub fn generate_keypair_44(&self) -> Result<SignatureKeyPair> {
147        self.generate_keypair(SignAlgorithm::MlDsa44)
148    }
149
150    /// Generate ML-DSA-65 key pair.
151    pub fn generate_keypair_65(&self) -> Result<SignatureKeyPair> {
152        self.generate_keypair(SignAlgorithm::MlDsa65)
153    }
154
155    /// Generate ML-DSA-87 key pair.
156    pub fn generate_keypair_87(&self) -> Result<SignatureKeyPair> {
157        self.generate_keypair(SignAlgorithm::MlDsa87)
158    }
159
160    /// Sign a message.
161    ///
162    /// # Arguments
163    ///
164    /// * `secret_key` - The signer's secret key
165    /// * `message` - The message to sign
166    /// * `algorithm` - The signature algorithm to use
167    ///
168    /// # Returns
169    ///
170    /// The digital signature.
171    pub fn sign(
172        &self,
173        secret_key: &[u8],
174        message: &[u8],
175        algorithm: SignAlgorithm,
176    ) -> Result<Vec<u8>> {
177        let sig_size = algorithm.signature_size();
178        let mut signature = vec![0u8; sig_size];
179        let mut sig_len = sig_size;
180
181        let result = unsafe {
182            ffi::quac_sign(
183                self.device.handle(),
184                algorithm.to_raw(),
185                secret_key.as_ptr(),
186                secret_key.len(),
187                message.as_ptr(),
188                message.len(),
189                signature.as_mut_ptr(),
190                &mut sig_len,
191            )
192        };
193
194        check_error(result)?;
195        signature.truncate(sig_len);
196
197        Ok(signature)
198    }
199
200    /// Sign using ML-DSA-44.
201    pub fn sign_44(&self, secret_key: &[u8], message: &[u8]) -> Result<Vec<u8>> {
202        self.sign(secret_key, message, SignAlgorithm::MlDsa44)
203    }
204
205    /// Sign using ML-DSA-65.
206    pub fn sign_65(&self, secret_key: &[u8], message: &[u8]) -> Result<Vec<u8>> {
207        self.sign(secret_key, message, SignAlgorithm::MlDsa65)
208    }
209
210    /// Sign using ML-DSA-87.
211    pub fn sign_87(&self, secret_key: &[u8], message: &[u8]) -> Result<Vec<u8>> {
212        self.sign(secret_key, message, SignAlgorithm::MlDsa87)
213    }
214
215    /// Sign a string message.
216    pub fn sign_str(
217        &self,
218        secret_key: &[u8],
219        message: &str,
220        algorithm: SignAlgorithm,
221    ) -> Result<Vec<u8>> {
222        self.sign(secret_key, message.as_bytes(), algorithm)
223    }
224
225    /// Verify a signature.
226    ///
227    /// # Arguments
228    ///
229    /// * `public_key` - The signer's public key
230    /// * `message` - The original message
231    /// * `signature` - The signature to verify
232    /// * `algorithm` - The signature algorithm to use
233    ///
234    /// # Returns
235    ///
236    /// `true` if the signature is valid, `false` otherwise.
237    pub fn verify(
238        &self,
239        public_key: &[u8],
240        message: &[u8],
241        signature: &[u8],
242        algorithm: SignAlgorithm,
243    ) -> Result<bool> {
244        let result = unsafe {
245            ffi::quac_verify(
246                self.device.handle(),
247                algorithm.to_raw(),
248                public_key.as_ptr(),
249                public_key.len(),
250                message.as_ptr(),
251                message.len(),
252                signature.as_ptr(),
253                signature.len(),
254            )
255        };
256
257        match result {
258            0 => Ok(true),
259            -13 => Ok(false), // VERIFICATION_FAILED
260            _ => {
261                check_error(result)?;
262                Ok(false)
263            }
264        }
265    }
266
267    /// Verify using ML-DSA-44.
268    pub fn verify_44(&self, public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<bool> {
269        self.verify(public_key, message, signature, SignAlgorithm::MlDsa44)
270    }
271
272    /// Verify using ML-DSA-65.
273    pub fn verify_65(&self, public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<bool> {
274        self.verify(public_key, message, signature, SignAlgorithm::MlDsa65)
275    }
276
277    /// Verify using ML-DSA-87.
278    pub fn verify_87(&self, public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<bool> {
279        self.verify(public_key, message, signature, SignAlgorithm::MlDsa87)
280    }
281
282    /// Verify a string message.
283    pub fn verify_str(
284        &self,
285        public_key: &[u8],
286        message: &str,
287        signature: &[u8],
288        algorithm: SignAlgorithm,
289    ) -> Result<bool> {
290        self.verify(public_key, message.as_bytes(), signature, algorithm)
291    }
292
293    /// Verify a signature, returning an error if invalid.
294    ///
295    /// Unlike `verify()` which returns a boolean, this method returns
296    /// an error if verification fails, making it suitable for use with `?`.
297    pub fn verify_or_error(
298        &self,
299        public_key: &[u8],
300        message: &[u8],
301        signature: &[u8],
302        algorithm: SignAlgorithm,
303    ) -> Result<()> {
304        if self.verify(public_key, message, signature, algorithm)? {
305            Ok(())
306        } else {
307            Err(QuacError::VerificationFailed)
308        }
309    }
310}
311
312impl std::fmt::Debug for Sign {
313    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314        f.debug_struct("Sign").finish()
315    }
316}
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn test_keypair_debug_hides_secret() {
324        let kp = SignatureKeyPair::new(
325            vec![1, 2, 3],
326            vec![4, 5, 6],
327            SignAlgorithm::MlDsa65,
328        );
329        let debug = format!("{:?}", kp);
330        assert!(!debug.contains("[4, 5, 6]"));
331        assert!(debug.contains("secret_key_len"));
332    }
333}