Skip to main content

oqs/
sig.rs

1//! Signature API
2//!
3//! See [`Sig`] for the main functionality and [`Algorithm`]
4//! for the list of supported algorithms.
5use alloc::vec::Vec;
6
7use core::ptr::{null, NonNull};
8use core::str::FromStr;
9
10#[cfg(not(feature = "std"))]
11use cstr_core::CStr;
12#[cfg(feature = "std")]
13use std::ffi::CStr;
14
15use crate::ffi::sig as ffi;
16use crate::newtype_buffer;
17use crate::*;
18#[cfg(feature = "rustcrypto")]
19use ::signature::Signer as RustCryptoSigner;
20#[cfg(feature = "rustcrypto")]
21use ::signature::Verifier as RustCryptoVerifier;
22
23#[cfg(feature = "serde")]
24use serde::{Deserialize, Serialize};
25
26newtype_buffer!(PublicKey, PublicKeyRef);
27newtype_buffer!(SecretKey, SecretKeyRef);
28newtype_buffer!(Signature, SignatureRef);
29
30/// Message type
31pub type Message = [u8];
32/// Context string type
33pub type CtxStr = [u8];
34
35macro_rules! implement_sigs {
36    { $($(#[$test_attr:meta])* ($feat: literal) $sig: ident: $oqs_id: ident),* $(,)? } => (
37        /// Supported algorithms by liboqs
38        ///
39        /// They may not all be enabled
40        ///
41        /// Optional support for `serde` if that feature is enabled.
42        #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
43        #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
44        #[allow(missing_docs)]
45        pub enum Algorithm {
46            $(
47                $sig,
48            )*
49        }
50
51        fn algorithm_to_id(algorithm: Algorithm) -> *const libc::c_char {
52            let id: &[u8] = match algorithm {
53                $(
54                    Algorithm::$sig => &ffi::$oqs_id[..],
55                )*
56            };
57            id as *const _ as *const libc::c_char
58        }
59
60        impl FromStr for Algorithm {
61            type Err = crate::Error;
62
63            fn from_str(s: &str) -> Result<Self> {
64                $(
65                    if s == Algorithm::$sig.name() {
66                        return Ok(Algorithm::$sig);
67                    }
68                )*
69                Err(crate::Error::AlgorithmParsingError)
70            }
71        }
72
73        $(
74            #[cfg(test)]
75            #[allow(non_snake_case)]
76            mod $sig {
77                use super::*;
78
79                #[test]
80                #[cfg(feature = $feat)]
81                $(#[$test_attr])*
82                fn test_signing() -> Result<()> {
83                    crate::init();
84                    let message = [0u8; 100];
85                    let sig = Sig::new(Algorithm::$sig)?;
86                    let (pk, sk) = sig.keypair()?;
87                    let signature = sig.sign(&message, &sk)?;
88                    sig.verify(&message, &signature, &pk)
89                }
90
91                #[test]
92                #[cfg(feature = $feat)]
93                $(#[$test_attr])*
94                fn test_signing_with_empty_context_string() -> Result<()> {
95                    crate::init();
96                    let message = [0u8; 100];
97                    let ctx_str: [u8; 0] = [];
98                    let sig = Sig::new(Algorithm::$sig)?;
99                    let (pk, sk) = sig.keypair()?;
100                    let signature = sig.sign_with_ctx_str(&message, &ctx_str, &sk)?;
101                    sig.verify_with_ctx_str(&message, &signature, &ctx_str, &pk)
102                }
103
104                #[test]
105                #[cfg(feature = $feat)]
106                $(#[$test_attr])*
107                fn test_signing_with_nonempty_context_string() -> Result<()> {
108                    crate::init();
109                    let message = [0u8; 100];
110                    let ctx_str = [0u8; 100];
111                    let sig = Sig::new(Algorithm::$sig)?;
112                    let (pk, sk) = sig.keypair()?;
113                    if sig.has_ctx_str_support() {
114                        let signature = sig.sign_with_ctx_str(&message, &ctx_str, &sk)?;
115                        sig.verify_with_ctx_str(&message, &signature, &ctx_str, &pk)
116                    } else {
117                        let sig_result = sig.sign_with_ctx_str(&message, &ctx_str, &sk);
118                        // Expect a generic error
119                        let sig_result: Result<()> = match sig_result {
120                            Err(Error::Error) => Ok(()),
121                            Ok(_) => Err(Error::Error),
122                            Err(e) => Err(e)
123                        };
124                        if sig_result.is_ok() {
125                            // get a valid signature with which to test verify
126                            let signature = sig.sign(&message, &sk)?;
127                            // Expect a generic error
128                            match sig.verify_with_ctx_str(&message, &signature, &ctx_str, &pk) {
129                                Err(Error::Error) => Ok(()),
130                                Ok(_) => Err(Error::Error),
131                                Err(e) => Err(e)
132
133                            }
134                        } else {
135                            sig_result
136                        }
137                    }
138                }
139
140                #[test]
141                fn test_enabled() {
142                    crate::init();
143                    if cfg!(feature = $feat) {
144                        assert!(Algorithm::$sig.is_enabled());
145                    } else {
146                        assert!(!Algorithm::$sig.is_enabled())
147                    }
148                }
149
150                #[test]
151                fn test_name() {
152                    let algo = Algorithm::$sig;
153                    // Just make sure the name impl does not panic or crash.
154                    let name = algo.name();
155
156                    #[cfg(feature = "std")]
157                    assert_eq!(name, algo.to_string());
158
159                    // ... And actually contains something.
160                    assert!(!name.is_empty());
161                }
162
163                #[test]
164                fn test_get_algorithm_back() {
165                    let algorithm = Algorithm::$sig;
166                    if algorithm.is_enabled() {
167                        let sig = Sig::new(algorithm).unwrap();
168                        assert_eq!(algorithm, sig.algorithm());
169                    }
170                }
171
172                #[test]
173                fn test_version() {
174                    if let Ok(sig) = Sig::new(Algorithm::$sig) {
175                        // Just make sure the version can be called without panic
176                        let version = sig.version();
177                        // ... And actually contains something.
178                        assert!(!version.is_empty());
179                    }
180                }
181
182                #[test]
183                fn test_from_str() {
184                    let algorithm = Algorithm::$sig;
185                    let name = algorithm.name();
186                    let parsed = Algorithm::from_str(name).unwrap();
187                    assert_eq!(algorithm, parsed);}
188            }
189        )*
190    )
191}
192
193include!("generated_sig_algorithms.rs");
194
195impl Algorithm {
196    /// Returns true if this algorithm is enabled in the linked version
197    /// of liboqs
198    pub fn is_enabled(self) -> bool {
199        unsafe { ffi::OQS_SIG_alg_is_enabled(algorithm_to_id(self)) == 1 }
200    }
201
202    /// Provides a pointer to the id of the algorithm
203    ///
204    /// For use with the FFI api methods
205    pub fn to_id(self) -> *const libc::c_char {
206        algorithm_to_id(self)
207    }
208
209    /// Returns the algorithm's name as a static Rust string.
210    ///
211    /// This is the same as the `to_id`, but as a safe Rust string.
212    pub fn name(&self) -> &'static str {
213        // SAFETY: The id from ffi must be a proper null terminated C string
214        let id = unsafe { CStr::from_ptr(self.to_id()) };
215        id.to_str().expect("OQS algorithm names must be UTF-8")
216    }
217}
218
219/// Signature scheme
220///
221/// # Example
222/// ```rust
223/// # if !cfg!(feature = "ml_dsa") { return; }
224/// use oqs;
225/// oqs::init();
226/// let scheme = oqs::sig::Sig::new(oqs::sig::Algorithm::MlDsa44).unwrap();
227/// let message = [0u8; 100];
228/// let (pk, sk) = scheme.keypair().unwrap();
229/// let signature = scheme.sign(&message, &sk).unwrap();
230/// assert!(scheme.verify(&message, &signature, &pk).is_ok());
231/// ```
232pub struct Sig {
233    algorithm: Algorithm,
234    sig: NonNull<ffi::OQS_SIG>,
235}
236
237unsafe impl Sync for Sig {}
238unsafe impl Send for Sig {}
239
240impl Drop for Sig {
241    fn drop(&mut self) {
242        unsafe { ffi::OQS_SIG_free(self.sig.as_ptr()) };
243    }
244}
245
246#[cfg(feature = "std")]
247impl std::fmt::Display for Algorithm {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        self.name().fmt(f)
250    }
251}
252
253impl core::convert::TryFrom<Algorithm> for Sig {
254    type Error = crate::Error;
255    fn try_from(alg: Algorithm) -> Result<Sig> {
256        Sig::new(alg)
257    }
258}
259
260impl Sig {
261    /// Construct a new algorithm
262    ///
263    /// May fail if the algorithm is not available
264    pub fn new(algorithm: Algorithm) -> Result<Self> {
265        let sig = unsafe { ffi::OQS_SIG_new(algorithm_to_id(algorithm)) };
266        NonNull::new(sig).map_or_else(
267            || Err(Error::AlgorithmDisabled),
268            |sig| Ok(Self { algorithm, sig }),
269        )
270    }
271
272    /// Get the algorithm used by this `Sig`
273    pub fn algorithm(&self) -> Algorithm {
274        self.algorithm
275    }
276
277    /// Get the version of the implementation
278    pub fn version(&self) -> &'static str {
279        let sig = unsafe { self.sig.as_ref() };
280        // SAFETY: The alg_version from ffi must be a proper null terminated C string
281        let cstr = unsafe { CStr::from_ptr(sig.alg_version) };
282        cstr.to_str()
283            .expect("Algorithm version strings must be UTF-8")
284    }
285
286    /// Obtain the claimed nist level
287    pub fn claimed_nist_level(&self) -> u8 {
288        let sig = unsafe { self.sig.as_ref() };
289        sig.claimed_nist_level
290    }
291
292    /// Is this algorithm EUF-CMA?
293    pub fn is_euf_cma(&self) -> bool {
294        let sig = unsafe { self.sig.as_ref() };
295        sig.euf_cma
296    }
297
298    /// Does this algorithm support signing with a context string?
299    pub fn has_ctx_str_support(&self) -> bool {
300        let sig = unsafe { self.sig.as_ref() };
301        sig.sig_with_ctx_support
302    }
303
304    /// Length of the public key
305    pub fn length_public_key(&self) -> usize {
306        let sig = unsafe { self.sig.as_ref() };
307        sig.length_public_key
308    }
309
310    /// Length of the secret key
311    pub fn length_secret_key(&self) -> usize {
312        let sig = unsafe { self.sig.as_ref() };
313        sig.length_secret_key
314    }
315
316    /// Maximum length of a signature
317    pub fn length_signature(&self) -> usize {
318        let sig = unsafe { self.sig.as_ref() };
319        sig.length_signature
320    }
321
322    /// Construct a secret key object from bytes
323    pub fn secret_key_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<SecretKeyRef<'a>> {
324        if buf.len() != self.length_secret_key() {
325            None
326        } else {
327            Some(SecretKeyRef::new(buf))
328        }
329    }
330
331    /// Construct a public key object from bytes
332    pub fn public_key_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<PublicKeyRef<'a>> {
333        if buf.len() != self.length_public_key() {
334            None
335        } else {
336            Some(PublicKeyRef::new(buf))
337        }
338    }
339
340    /// Construct a signature object from bytes
341    pub fn signature_from_bytes<'a>(&self, buf: &'a [u8]) -> Option<SignatureRef<'a>> {
342        if buf.len() > self.length_signature() {
343            None
344        } else {
345            Some(SignatureRef::new(buf))
346        }
347    }
348
349    /// Generate a new keypair
350    pub fn keypair(&self) -> Result<(PublicKey, SecretKey)> {
351        let sig = unsafe { self.sig.as_ref() };
352        let func = sig.keypair.unwrap();
353        let mut pk = PublicKey {
354            bytes: Vec::with_capacity(sig.length_public_key),
355        };
356        let mut sk = SecretKey {
357            bytes: Vec::with_capacity(sig.length_secret_key),
358        };
359        let status = unsafe { func(pk.bytes.as_mut_ptr(), sk.bytes.as_mut_ptr()) };
360        // update the lengths of the vecs
361        unsafe {
362            pk.bytes.set_len(sig.length_public_key);
363            sk.bytes.set_len(sig.length_secret_key);
364        }
365        status_to_result(status)?;
366        Ok((pk, sk))
367    }
368
369    /// Sign a message
370    pub fn sign<'a, S: Into<SecretKeyRef<'a>>>(
371        &self,
372        message: &Message,
373        sk: S,
374    ) -> Result<Signature> {
375        let sk = sk.into();
376        let sig = unsafe { self.sig.as_ref() };
377        let func = sig.sign.unwrap();
378        let mut sig = Signature {
379            bytes: Vec::with_capacity(sig.length_signature),
380        };
381        let mut sig_len = 0;
382        let status = unsafe {
383            func(
384                sig.bytes.as_mut_ptr(),
385                &mut sig_len,
386                message.as_ptr(),
387                message.len(),
388                sk.bytes.as_ptr(),
389            )
390        };
391        status_to_result(status)?;
392        // This is safe to do as it's initialised now.
393        unsafe {
394            sig.bytes.set_len(sig_len);
395        }
396        Ok(sig)
397    }
398
399    /// Sign a message with a context string
400    pub fn sign_with_ctx_str<'a, S: Into<SecretKeyRef<'a>>>(
401        &self,
402        message: &Message,
403        ctx_str: &CtxStr,
404        sk: S,
405    ) -> Result<Signature> {
406        let sk = sk.into();
407        let sig = unsafe { self.sig.as_ref() };
408        let func = sig.sign_with_ctx_str.unwrap();
409        let mut sig = Signature {
410            bytes: Vec::with_capacity(sig.length_signature),
411        };
412        let mut sig_len = 0;
413        // For algorithms without context string support, liboqs
414        // expects the context to be NULL. Converting an empty
415        // slice to a pointer doesn't actually do this.
416        let ctx_str_ptr = if !ctx_str.is_empty() {
417            ctx_str.as_ptr()
418        } else {
419            null()
420        };
421        let status = unsafe {
422            func(
423                sig.bytes.as_mut_ptr(),
424                &mut sig_len,
425                message.as_ptr(),
426                message.len(),
427                ctx_str_ptr,
428                ctx_str.len(),
429                sk.bytes.as_ptr(),
430            )
431        };
432        status_to_result(status)?;
433        // This is safe to do as it's initialised now.
434        unsafe {
435            sig.bytes.set_len(sig_len);
436        }
437        Ok(sig)
438    }
439
440    /// Verify a message
441    pub fn verify<'a, 'b>(
442        &self,
443        message: &Message,
444        signature: impl Into<SignatureRef<'a>>,
445        pk: impl Into<PublicKeyRef<'b>>,
446    ) -> Result<()> {
447        let signature = signature.into();
448        let pk = pk.into();
449        if signature.bytes.len() > self.length_signature()
450            || pk.bytes.len() != self.length_public_key()
451        {
452            return Err(Error::InvalidLength);
453        }
454        let sig = unsafe { self.sig.as_ref() };
455        let func = sig.verify.unwrap();
456        let status = unsafe {
457            func(
458                message.as_ptr(),
459                message.len(),
460                signature.bytes.as_ptr(),
461                signature.len(),
462                pk.bytes.as_ptr(),
463            )
464        };
465        status_to_result(status)
466    }
467
468    /// Verify a message with a context string
469    pub fn verify_with_ctx_str<'a, 'b>(
470        &self,
471        message: &Message,
472        signature: impl Into<SignatureRef<'a>>,
473        ctx_str: &CtxStr,
474        pk: impl Into<PublicKeyRef<'b>>,
475    ) -> Result<()> {
476        let signature = signature.into();
477        let pk = pk.into();
478        if signature.bytes.len() > self.length_signature()
479            || pk.bytes.len() != self.length_public_key()
480        {
481            return Err(Error::InvalidLength);
482        }
483        let sig = unsafe { self.sig.as_ref() };
484        let func = sig.verify_with_ctx_str.unwrap();
485        // For algorithms without context string support, liboqs
486        // expects the context to be NULL. Converting an empty
487        // slice to a pointer doesn't actually do this.
488        let ctx_str_ptr = if !ctx_str.is_empty() {
489            ctx_str.as_ptr()
490        } else {
491            null()
492        };
493        let status = unsafe {
494            func(
495                message.as_ptr(),
496                message.len(),
497                signature.bytes.as_ptr(),
498                signature.len(),
499                ctx_str_ptr,
500                ctx_str.len(),
501                pk.bytes.as_ptr(),
502            )
503        };
504        status_to_result(status)
505    }
506}
507
508#[cfg(feature = "rustcrypto")]
509/// Signer struct containing a signature scheme and a signing key.
510///
511/// # Example
512/// ```rust
513/// # if !cfg!(feature = "ml_dsa") { return; }
514/// use oqs;
515/// use signature::{Signer,Verifier};
516/// oqs::init();
517/// let scheme = oqs::sig::Sig::new(oqs::sig::Algorithm::MlDsa44).unwrap();
518/// let message = [0u8; 100];
519/// let (pk, sk) = scheme.keypair().unwrap();
520/// let signer = oqs::sig::Signer::new(&scheme,sk);
521/// let verifier = oqs::sig::Verifier::new(&scheme,pk);
522/// let signature = signer.try_sign(&message).unwrap();
523/// assert!(verifier.verify(&message, &signature).is_ok());
524/// ```
525#[cfg(feature = "rustcrypto")]
526pub struct Signer<'a> {
527    scheme: &'a Sig,
528    sk: SecretKey,
529}
530
531#[cfg(feature = "rustcrypto")]
532impl<'a> Signer<'a> {
533    /// Creates a new [`Signer`] with the given signature scheme and secret key.
534    pub fn new(scheme: &'a Sig, sk: SecretKey) -> Self {
535        Signer { scheme, sk }
536    }
537}
538
539#[cfg(feature = "rustcrypto")]
540impl<'a> RustCryptoSigner<sig::Signature> for Signer<'a> {
541    fn try_sign(&self, msg: &[u8]) -> core::result::Result<sig::Signature, signature::Error> {
542        match self.scheme.sign(msg, &self.sk) {
543            Ok(s) => Ok(s),
544            Err(_) => Err(signature::Error::new()),
545        }
546    }
547}
548
549#[cfg(feature = "rustcrypto")]
550/// Verifier struct containing a signature scheme and a public verification key.
551///
552/// # Example
553/// ```rust
554/// # if !cfg!(feature = "ml_dsa") { return; }
555/// use oqs;
556/// use signature::{Signer,Verifier};
557/// oqs::init();
558/// let scheme = oqs::sig::Sig::new(oqs::sig::Algorithm::MlDsa44).unwrap();
559/// let message = [0u8; 100];
560/// let (pk, sk) = scheme.keypair().unwrap();
561/// let signer = oqs::sig::Signer::new(&scheme,sk);
562/// let verifier = oqs::sig::Verifier::new(&scheme,pk);
563/// let signature = signer.try_sign(&message).unwrap();
564/// assert!(verifier.verify(&message, &signature).is_ok());
565/// ```
566///
567/// Used to verify signatures for a given message.
568#[cfg(feature = "rustcrypto")]
569pub struct Verifier<'a> {
570    scheme: &'a Sig,
571    pk: PublicKey,
572}
573
574#[cfg(feature = "rustcrypto")]
575impl<'a> Verifier<'a> {
576    /// Creates a new [`Verifier`] with the given signature scheme and public key.
577    pub fn new(scheme: &'a Sig, pk: PublicKey) -> Self {
578        Verifier { scheme, pk }
579    }
580}
581
582#[cfg(feature = "rustcrypto")]
583impl<'a> RustCryptoVerifier<sig::Signature> for Verifier<'a> {
584    fn verify(
585        &self,
586        msg: &[u8],
587        signature: &sig::Signature,
588    ) -> core::result::Result<(), signature::Error> {
589        match self.scheme.verify(msg, signature, &self.pk) {
590            Ok(_) => Ok(()),
591            Err(_) => Err(signature::Error::new()),
592        }
593    }
594}