spideroak_crypto/
signer.rs

1//! Digital Signatures.
2
3#![forbid(unsafe_code)]
4
5use core::{
6    borrow::Borrow,
7    fmt::{self, Debug},
8    result::Result,
9};
10
11use buggy::Bug;
12
13use crate::{
14    asn1::EncodingError,
15    csprng::Random,
16    import::Import,
17    keys::{PublicKey, SecretKey},
18};
19
20/// An error from a [`Signer`].
21#[derive(Debug, Eq, PartialEq)]
22pub enum SignerError {
23    /// An unknown or internal error has occurred.
24    Other(&'static str),
25    /// The imported signature is invalid.
26    Encoding(EncodingError),
27    /// The signature could not be verified.
28    Verification,
29    /// [`Signer::verify_batch`] was called with different
30    /// lengths for messages, signatures, or verifying keys.
31    InvalidBatchLengths,
32    /// An internal error was discovered.
33    Bug(Bug),
34}
35
36impl fmt::Display for SignerError {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::Other(msg) => write!(f, "{}", msg),
40            Self::Encoding(err) => write!(f, "{}", err),
41            Self::Verification => write!(f, "unable to verify signature"),
42            Self::InvalidBatchLengths => write!(f, "invalid `verify_batch` lengths"),
43            Self::Bug(err) => write!(f, "{err}"),
44        }
45    }
46}
47
48impl core::error::Error for SignerError {
49    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
50        match self {
51            Self::Other(_) => None,
52            Self::Encoding(err) => Some(err),
53            Self::Verification => None,
54            Self::InvalidBatchLengths => None,
55            Self::Bug(err) => Some(err),
56        }
57    }
58}
59
60impl From<EncodingError> for SignerError {
61    fn from(err: EncodingError) -> Self {
62        Self::Encoding(err)
63    }
64}
65
66impl From<Bug> for SignerError {
67    fn from(err: Bug) -> Self {
68        Self::Bug(err)
69    }
70}
71
72/// Signer is a digital signature algorithm.
73///
74/// # Requirements
75///
76/// The algorithm must:
77///
78/// * Have at minimum a 128-bit security level.
79/// * Generate canonical signatures.
80/// * Reject non-canonical signatures.
81/// * Be EUF-CMA secure.
82///
83/// Note that rejecting non-canonical signatures implies strong
84/// EUF-CMA security. However, this API's definition is
85/// intentionally weaker.
86///
87/// Examples of algorithms that fulfill these requirements
88/// include ECDSA with the three NIST prime-order curves (P-256,
89/// P-384, and P521), albeit with minor modifications (like
90/// rejecting s >= N/2).
91pub trait Signer {
92    /// A private key used to create signatures.
93    type SigningKey: SigningKey<Self>;
94    /// A public key used verify signatures.
95    type VerifyingKey: VerifyingKey<Self>;
96    /// A digital signature.
97    type Signature: Signature<Self>;
98
99    /// Verifies all (message, signature, verifying key) tuples
100    /// as a batch.
101    ///
102    /// For some digital signature schemes, batch verification
103    /// can differ from regular signature verification. For
104    /// example, see some [Ed25519 quirks][quirks]. This function
105    /// MUST NOT diverge from regular signature verification.
106    ///
107    /// [quirks]: https://hdevalence.ca/blog/2020-10-04-its-25519am
108    fn verify_batch(
109        msgs: &[&[u8]],
110        sigs: &[Self::Signature],
111        pks: &[Self::VerifyingKey],
112    ) -> Result<(), SignerError> {
113        if msgs.len() != sigs.len() || sigs.len() != pks.len() {
114            return Err(SignerError::InvalidBatchLengths);
115        }
116        for (msg, (sig, pk)) in msgs.iter().zip(sigs.iter().zip(pks)) {
117            pk.verify(msg, sig)?;
118        }
119        Ok(())
120    }
121}
122
123/// An asymmetric secret key used to create digital signatures.
124pub trait SigningKey<T: Signer + ?Sized>: SecretKey + Random {
125    /// Returns the signature over `msg`, which must NOT be
126    /// pre-hashed.
127    fn sign(&self, msg: &[u8]) -> Result<T::Signature, SignerError>;
128
129    /// Returns the public half of the key.
130    fn public(&self) -> Result<T::VerifyingKey, PkError>;
131}
132
133/// Handles Public Key errors
134#[derive(Debug, Eq, PartialEq)]
135pub struct PkError(pub(crate) &'static str);
136
137impl PkError {
138    // Exported for `aranya-crypto`. Do not use.
139    #[doc(hidden)]
140    pub const fn msg(&self) -> &'static str {
141        self.0
142    }
143}
144
145impl fmt::Display for PkError {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        write!(f, "{}", self.0)
148    }
149}
150
151impl core::error::Error for PkError {}
152
153/// An asymmetric public key used to verify digital signatures.
154pub trait VerifyingKey<T: Signer + ?Sized>: PublicKey {
155    /// Reports whether the signature over `msg` is valid.
156    fn verify(&self, msg: &[u8], sig: &T::Signature) -> Result<(), SignerError>;
157}
158
159/// A canonical digital signature.
160pub trait Signature<T: Signer + ?Sized>: Clone + Debug + for<'a> Import<&'a [u8]> {
161    /// The fixed-length byte encoding of the signature.
162    ///
163    /// This should be `[u8; N]` or similar.
164    type Data: Borrow<[u8]> + Clone + Sized;
165
166    /// Returns the byte encoding of the signature.
167    fn export(&self) -> Self::Data;
168}