ssh_key/
signature.rs

1//! Signatures (e.g. CA signatures over SSH certificates)
2
3use crate::{
4    checked::CheckedSum, decode::Decode, encode::Encode, private, public, reader::Reader,
5    writer::Writer, Algorithm, Error, MPInt, PrivateKey, PublicKey, Result,
6};
7use alloc::vec::Vec;
8use core::fmt;
9use signature::{Signer, Verifier};
10
11#[cfg(feature = "ed25519")]
12use crate::{private::Ed25519Keypair, public::Ed25519PublicKey};
13
14#[cfg(feature = "p256")]
15use crate::{
16    private::{EcdsaKeypair, EcdsaPrivateKey},
17    public::EcdsaPublicKey,
18    EcdsaCurve,
19};
20
21#[cfg(feature = "rsa")]
22use {
23    crate::{private::RsaKeypair, public::RsaPublicKey, HashAlg},
24    rsa::PublicKey as _,
25    sha2::{Digest, Sha256, Sha512},
26};
27
28const DSA_SIGNATURE_SIZE: usize = 40;
29const ED25519_SIGNATURE_SIZE: usize = 64;
30
31/// Digital signature (e.g. DSA, ECDSA, Ed25519).
32///
33/// These are used as part of the OpenSSH certificate format to represent
34/// signatures by certificate authorities (CAs).
35///
36/// From OpenSSH's [PROTOCOL.certkeys] specification:
37///
38/// > Signatures are computed and encoded according to the rules defined for
39/// > the CA's public key algorithm ([RFC4253 section 6.6] for ssh-rsa and
40/// > ssh-dss, [RFC5656] for the ECDSA types, and [RFC8032] for Ed25519).
41///
42/// RSA signature support is implemented using the SHA2 family extensions as
43/// described in [RFC8332].
44///
45/// [PROTOCOL.certkeys]: https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD
46/// [RFC4253 section 6.6]: https://datatracker.ietf.org/doc/html/rfc4253#section-6.6
47/// [RFC5656]: https://datatracker.ietf.org/doc/html/rfc5656
48/// [RFC8032]: https://datatracker.ietf.org/doc/html/rfc8032
49/// [RFC8332]: https://datatracker.ietf.org/doc/html/rfc8332
50#[derive(Clone, Eq, PartialEq, PartialOrd, Ord)]
51#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
52pub struct Signature {
53    /// Signature algorithm.
54    algorithm: Algorithm,
55
56    /// Raw signature serialized as algorithm-specific byte encoding.
57    data: Vec<u8>,
58}
59
60impl Signature {
61    /// Create a new signature with the given algorithm and raw signature data.
62    ///
63    /// See specifications in toplevel [`Signature`] documentation for how to
64    /// format the raw signature data for a given algorithm.
65    ///
66    /// # Returns
67    /// - [`Error::Length`] if the signature is not the correct length.
68    pub fn new(algorithm: Algorithm, data: impl Into<Vec<u8>>) -> Result<Self> {
69        let data = data.into();
70
71        // Validate signature is well-formed per OpensSH encoding
72        match algorithm {
73            Algorithm::Dsa if data.len() == DSA_SIGNATURE_SIZE => (),
74            Algorithm::Ecdsa { curve } => {
75                let reader = &mut data.as_slice();
76
77                for _ in 0..2 {
78                    let component = MPInt::decode(reader)?;
79
80                    if component.as_positive_bytes().ok_or(Error::Crypto)?.len()
81                        != curve.field_size()
82                    {
83                        return Err(Error::Length);
84                    }
85                }
86
87                if !reader.is_finished() {
88                    return Err(Error::Length);
89                }
90            }
91            Algorithm::Ed25519 if data.len() == ED25519_SIGNATURE_SIZE => (),
92            Algorithm::Rsa { hash: Some(_) } => (),
93            _ => return Err(Error::Length),
94        }
95
96        Ok(Self { algorithm, data })
97    }
98
99    /// Placeholder signature used by the certificate builder.
100    ///
101    /// This is guaranteed generate an error if anything attempts to encode it.
102    pub(crate) fn placeholder() -> Self {
103        Self {
104            algorithm: Algorithm::default(),
105            data: Vec::new(),
106        }
107    }
108
109    /// Check if this signature is the placeholder signature.
110    pub(crate) fn is_placeholder(&self) -> bool {
111        self.algorithm == Algorithm::default() && self.data.is_empty()
112    }
113}
114
115impl Signature {
116    /// Get the [`Algorithm`] associated with this signature.
117    pub fn algorithm(&self) -> Algorithm {
118        self.algorithm
119    }
120
121    /// Get the raw signature as bytes.
122    pub fn as_bytes(&self) -> &[u8] {
123        &self.data
124    }
125}
126
127impl AsRef<[u8]> for Signature {
128    fn as_ref(&self) -> &[u8] {
129        self.as_bytes()
130    }
131}
132
133impl Decode for Signature {
134    fn decode(reader: &mut impl Reader) -> Result<Self> {
135        let algorithm = Algorithm::decode(reader)?;
136        let data = Vec::decode(reader)?;
137        Self::new(algorithm, data)
138    }
139}
140
141impl Encode for Signature {
142    fn encoded_len(&self) -> Result<usize> {
143        [
144            self.algorithm().encoded_len()?,
145            4, // signature data length prefix (uint32)
146            self.as_bytes().len(),
147        ]
148        .checked_sum()
149    }
150
151    fn encode(&self, writer: &mut impl Writer) -> Result<()> {
152        if self.is_placeholder() {
153            return Err(Error::Length);
154        }
155
156        self.algorithm().encode(writer)?;
157        self.as_bytes().encode(writer)
158    }
159}
160
161impl signature::Signature for Signature {
162    fn from_bytes(bytes: &[u8]) -> signature::Result<Self> {
163        Self::try_from(bytes).map_err(|_| signature::Error::new())
164    }
165}
166
167/// Decode [`Signature`] from an [`Algorithm`]-prefixed OpenSSH-encoded bytestring.
168impl TryFrom<&[u8]> for Signature {
169    type Error = Error;
170
171    fn try_from(mut bytes: &[u8]) -> Result<Self> {
172        Self::decode(&mut bytes)
173    }
174}
175
176impl fmt::Debug for Signature {
177    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178        write!(
179            f,
180            "Signature {{ algorithm: {:?}, data: {:X} }}",
181            self.algorithm, self
182        )
183    }
184}
185
186impl fmt::LowerHex for Signature {
187    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188        for byte in self.as_ref() {
189            write!(f, "{:02x}", byte)?;
190        }
191        Ok(())
192    }
193}
194
195impl fmt::UpperHex for Signature {
196    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197        for byte in self.as_ref() {
198            write!(f, "{:02X}", byte)?;
199        }
200        Ok(())
201    }
202}
203
204impl Signer<Signature> for PrivateKey {
205    fn try_sign(&self, message: &[u8]) -> signature::Result<Signature> {
206        self.key_data().try_sign(message)
207    }
208}
209
210impl Signer<Signature> for private::KeypairData {
211    #[allow(unused_variables)]
212    fn try_sign(&self, message: &[u8]) -> signature::Result<Signature> {
213        match self {
214            #[cfg(feature = "p256")]
215            Self::Ecdsa(keypair) => keypair.try_sign(message),
216            #[cfg(feature = "ed25519")]
217            Self::Ed25519(keypair) => keypair.try_sign(message),
218            #[cfg(feature = "rsa")]
219            Self::Rsa(keypair) => keypair.try_sign(message),
220            _ => Err(signature::Error::new()),
221        }
222    }
223}
224
225impl Verifier<Signature> for PublicKey {
226    fn verify(&self, message: &[u8], signature: &Signature) -> signature::Result<()> {
227        self.key_data().verify(message, signature)
228    }
229}
230
231impl Verifier<Signature> for public::KeyData {
232    #[allow(unused_variables)]
233    fn verify(&self, message: &[u8], signature: &Signature) -> signature::Result<()> {
234        match self {
235            #[cfg(feature = "p256")]
236            Self::Ecdsa(pk) => pk.verify(message, signature),
237            #[cfg(feature = "ed25519")]
238            Self::Ed25519(pk) => pk.verify(message, signature),
239            #[cfg(feature = "rsa")]
240            Self::Rsa(pk) => pk.verify(message, signature),
241            _ => Err(signature::Error::new()),
242        }
243    }
244}
245
246#[cfg(feature = "ed25519")]
247#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
248impl TryFrom<Signature> for ed25519_dalek::Signature {
249    type Error = Error;
250
251    fn try_from(signature: Signature) -> Result<ed25519_dalek::Signature> {
252        ed25519_dalek::Signature::try_from(&signature)
253    }
254}
255
256#[cfg(feature = "ed25519")]
257#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
258impl TryFrom<&Signature> for ed25519_dalek::Signature {
259    type Error = Error;
260
261    fn try_from(signature: &Signature) -> Result<ed25519_dalek::Signature> {
262        match signature.algorithm {
263            Algorithm::Ed25519 => Ok(ed25519_dalek::Signature::try_from(signature.as_bytes())?),
264            _ => Err(Error::Algorithm),
265        }
266    }
267}
268
269#[cfg(feature = "ed25519")]
270#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
271impl Signer<Signature> for Ed25519Keypair {
272    fn try_sign(&self, message: &[u8]) -> signature::Result<Signature> {
273        let signature = ed25519_dalek::Keypair::try_from(self)?.sign(message);
274
275        Ok(Signature {
276            algorithm: Algorithm::Ed25519,
277            data: signature.as_ref().to_vec(),
278        })
279    }
280}
281
282#[cfg(feature = "ed25519")]
283#[cfg_attr(docsrs, doc(cfg(feature = "ed25519")))]
284impl Verifier<Signature> for Ed25519PublicKey {
285    fn verify(&self, message: &[u8], signature: &Signature) -> signature::Result<()> {
286        let signature = ed25519_dalek::Signature::try_from(signature)?;
287        ed25519_dalek::PublicKey::try_from(self)?.verify(message, &signature)
288    }
289}
290
291#[cfg(feature = "p256")]
292#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
293impl TryFrom<p256::ecdsa::Signature> for Signature {
294    type Error = Error;
295
296    fn try_from(signature: p256::ecdsa::Signature) -> Result<Signature> {
297        Signature::try_from(&signature)
298    }
299}
300
301#[cfg(feature = "p256")]
302#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
303impl TryFrom<&p256::ecdsa::Signature> for Signature {
304    type Error = Error;
305
306    fn try_from(signature: &p256::ecdsa::Signature) -> Result<Signature> {
307        let (r, s) = signature.as_ref().split_at(32);
308        let mut data = Vec::with_capacity(74); // 32 * 2 + 4 * 2 + 1 * 2
309        MPInt::from_positive_bytes(r)?.encode(&mut data)?;
310        MPInt::from_positive_bytes(s)?.encode(&mut data)?;
311
312        Ok(Signature {
313            algorithm: Algorithm::Ecdsa {
314                curve: EcdsaCurve::NistP256,
315            },
316            data,
317        })
318    }
319}
320
321#[cfg(feature = "p256")]
322#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
323impl TryFrom<Signature> for p256::ecdsa::Signature {
324    type Error = Error;
325
326    fn try_from(signature: Signature) -> Result<p256::ecdsa::Signature> {
327        p256::ecdsa::Signature::try_from(&signature)
328    }
329}
330
331#[cfg(feature = "p256")]
332#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
333impl TryFrom<&Signature> for p256::ecdsa::Signature {
334    type Error = Error;
335
336    fn try_from(signature: &Signature) -> Result<p256::ecdsa::Signature> {
337        match signature.algorithm {
338            Algorithm::Ecdsa {
339                curve: EcdsaCurve::NistP256,
340            } => {
341                let reader = &mut signature.as_bytes();
342                let r = MPInt::decode(reader)?;
343                let s = MPInt::decode(reader)?;
344
345                match (r.as_positive_bytes(), s.as_positive_bytes()) {
346                    (Some(r), Some(s)) if r.len() == 32 && s.len() == 32 => {
347                        Ok(p256::ecdsa::Signature::from_scalars(
348                            *p256::FieldBytes::from_slice(r),
349                            *p256::FieldBytes::from_slice(s),
350                        )?)
351                    }
352                    _ => Err(Error::Crypto),
353                }
354            }
355            _ => Err(Error::Algorithm),
356        }
357    }
358}
359
360#[cfg(feature = "p256")]
361#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
362impl Signer<Signature> for EcdsaKeypair {
363    fn try_sign(&self, message: &[u8]) -> signature::Result<Signature> {
364        match self {
365            Self::NistP256 { private, .. } => private.try_sign(message),
366            _ => Err(signature::Error::new()),
367        }
368    }
369}
370
371#[cfg(feature = "p256")]
372#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
373impl Signer<Signature> for EcdsaPrivateKey<32> {
374    fn try_sign(&self, message: &[u8]) -> signature::Result<Signature> {
375        Ok(p256::ecdsa::SigningKey::from_bytes(self.as_ref())?
376            .try_sign(message)?
377            .try_into()?)
378    }
379}
380
381#[cfg(feature = "p256")]
382#[cfg_attr(docsrs, doc(cfg(feature = "p256")))]
383impl Verifier<Signature> for EcdsaPublicKey {
384    fn verify(&self, message: &[u8], signature: &Signature) -> signature::Result<()> {
385        match signature.algorithm {
386            Algorithm::Ecdsa {
387                curve: EcdsaCurve::NistP256,
388            } => {
389                let verifying_key = p256::ecdsa::VerifyingKey::try_from(self)?;
390                let signature = p256::ecdsa::Signature::try_from(signature)?;
391                verifying_key.verify(message, &signature)
392            }
393            _ => Err(signature::Error::new()),
394        }
395    }
396}
397
398#[cfg(feature = "rsa")]
399#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
400impl Signer<Signature> for RsaKeypair {
401    fn try_sign(&self, message: &[u8]) -> signature::Result<Signature> {
402        let padding = rsa::padding::PaddingScheme::PKCS1v15Sign {
403            hash: Some(rsa::hash::Hash::SHA2_512),
404        };
405        let digest = sha2::Sha512::digest(message);
406        let data = rsa::RsaPrivateKey::try_from(self)?
407            .sign(padding, digest.as_ref())
408            .map_err(|_| signature::Error::new())?;
409
410        Ok(Signature {
411            algorithm: Algorithm::Rsa {
412                hash: Some(HashAlg::Sha512),
413            },
414            data,
415        })
416    }
417}
418
419#[cfg(feature = "rsa")]
420#[cfg_attr(docsrs, doc(cfg(feature = "rsa")))]
421impl Verifier<Signature> for RsaPublicKey {
422    fn verify(&self, message: &[u8], signature: &Signature) -> signature::Result<()> {
423        let key = rsa::RsaPublicKey::try_from(self)?;
424
425        match signature.algorithm {
426            Algorithm::Rsa {
427                hash: Some(HashAlg::Sha256),
428            } => {
429                let digest = Sha256::digest(message);
430                let padding = rsa::padding::PaddingScheme::PKCS1v15Sign {
431                    hash: Some(rsa::hash::Hash::SHA2_256),
432                };
433                key.verify(padding, digest.as_ref(), signature.as_bytes())
434                    .map_err(|_| signature::Error::new())
435            }
436            Algorithm::Rsa {
437                hash: Some(HashAlg::Sha512),
438            } => {
439                let padding = rsa::padding::PaddingScheme::PKCS1v15Sign {
440                    hash: Some(rsa::hash::Hash::SHA2_512),
441                };
442                let digest = Sha512::digest(message);
443                key.verify(padding, digest.as_ref(), signature.as_bytes())
444                    .map_err(|_| signature::Error::new())
445            }
446            _ => Err(signature::Error::new()),
447        }
448    }
449}
450
451#[cfg(test)]
452mod tests {
453    use super::Signature;
454    use crate::{encode::Encode, Algorithm, EcdsaCurve, Error, HashAlg};
455    use alloc::vec::Vec;
456    use hex_literal::hex;
457
458    #[cfg(feature = "ed25519")]
459    use {
460        super::Ed25519Keypair,
461        signature::{Signer, Verifier},
462    };
463
464    const DSA_SIGNATURE: &[u8] = &hex!("000000077373682d6473730000002866725bf3c56100e975e21fff28a60f73717534d285ea3e1beefc2891f7189d00bd4d94627e84c55c");
465    const ECDSA_SHA2_P256_SIGNATURE: &[u8] = &hex!("0000001365636473612d736861322d6e6973747032353600000048000000201298ab320720a32139cda8a40c97a13dc54ce032ea3c6f09ea9e87501e48fa1d0000002046e4ac697a6424a9870b9ef04ca1182cd741965f989bd1f1f4a26fd83cf70348");
466    const ED25519_SIGNATURE: &[u8] = &hex!("0000000b7373682d65643235353139000000403d6b9906b76875aef1e7b2f1e02078a94f439aebb9a4734da1a851a81e22ce0199bbf820387a8de9c834c9c3cc778d9972dcbe70f68d53cc6bc9e26b02b46d04");
467    const RSA_SHA512_SIGNATURE: &[u8] = &hex!("0000000c7273612d736861322d3531320000018085a4ad1a91a62c00c85de7bb511f38088ff2bce763d76f4786febbe55d47624f9e2cffce58a680183b9ad162c7f0191ea26cab001ac5f5055743eced58e9981789305c208fc98d2657954e38eb28c7e7f3fbe92393a14324ed77aebb772a41aa7a107b38cb9bd1d9ad79b275135d1d7e019bb1d56d74f2450be6db0771f48f6707d3fcf9789592ca2e55595acc16b6e8d0139b56c5d1360b3a1e060f4151a3d7841df2c2a8c94d6f8a1bf633165ee0bcadac5642763df0dd79d3235ae5506595145f199d8abe8f9980411bf70a16e30f273736324d047043317044c36374d6a5ed34cac251e01c6795e4578393f9090bf4ae3e74a0009275a197315fc9c62f1c9aec1ba3b2d37c3b207e5500df19e090e7097ebc038fb9c9e35aea9161479ba6b5190f48e89e1abe51e8ec0e120ef89776e129687ca52d1892c8e88e6ef062a7d96b8a87682ca6a42ff1df0cdf5815c3645aeed7267ca7093043db0565e0f109b796bf117b9d2bb6d6debc0c67a4c9fb3aae3e29b00c7bd70f6c11cf53c295ff");
468
469    /// Example test vector for signing.
470    #[cfg(feature = "ed25519")]
471    const EXAMPLE_MSG: &[u8] = b"Hello, world!";
472
473    #[test]
474    fn decode_dsa() {
475        let signature = Signature::try_from(DSA_SIGNATURE).unwrap();
476        assert_eq!(Algorithm::Dsa, signature.algorithm());
477    }
478
479    #[test]
480    fn decode_ecdsa_sha2_p256() {
481        let signature = Signature::try_from(ECDSA_SHA2_P256_SIGNATURE).unwrap();
482        assert_eq!(
483            Algorithm::Ecdsa {
484                curve: EcdsaCurve::NistP256
485            },
486            signature.algorithm()
487        );
488    }
489
490    #[test]
491    fn decode_ed25519() {
492        let signature = Signature::try_from(ED25519_SIGNATURE).unwrap();
493        assert_eq!(Algorithm::Ed25519, signature.algorithm());
494    }
495
496    #[test]
497    fn decode_rsa() {
498        let signature = Signature::try_from(RSA_SHA512_SIGNATURE).unwrap();
499        assert_eq!(
500            Algorithm::Rsa {
501                hash: Some(HashAlg::Sha512)
502            },
503            signature.algorithm()
504        );
505    }
506
507    #[test]
508    fn encode_dsa() {
509        let signature = Signature::try_from(DSA_SIGNATURE).unwrap();
510
511        let mut result = Vec::new();
512        signature.encode(&mut result).unwrap();
513        assert_eq!(DSA_SIGNATURE, &result);
514    }
515
516    #[test]
517    fn encode_ecdsa_sha2_p256() {
518        let signature = Signature::try_from(ECDSA_SHA2_P256_SIGNATURE).unwrap();
519
520        let mut result = Vec::new();
521        signature.encode(&mut result).unwrap();
522        assert_eq!(ECDSA_SHA2_P256_SIGNATURE, &result);
523    }
524
525    #[test]
526    fn encode_ed25519() {
527        let signature = Signature::try_from(ED25519_SIGNATURE).unwrap();
528
529        let mut result = Vec::new();
530        signature.encode(&mut result).unwrap();
531        assert_eq!(ED25519_SIGNATURE, &result);
532    }
533
534    #[cfg(feature = "ed25519")]
535    #[test]
536    fn sign_and_verify_ed25519() {
537        let keypair = Ed25519Keypair::from_seed(&[42; 32]);
538        let signature = keypair.sign(EXAMPLE_MSG);
539        assert!(keypair.public.verify(EXAMPLE_MSG, &signature).is_ok());
540    }
541
542    #[test]
543    fn placeholder() {
544        assert!(!Signature::try_from(ED25519_SIGNATURE)
545            .unwrap()
546            .is_placeholder());
547
548        let placeholder = Signature::placeholder();
549        assert!(placeholder.is_placeholder());
550
551        let mut writer = Vec::new();
552        assert_eq!(placeholder.encode(&mut writer), Err(Error::Length));
553    }
554}