libsignify/
lib.rs

1//! Create cryptographic signatures for files and verify them.
2//!
3//! This is based on [signify], the OpenBSD tool to sign and verify signatures on files.
4//! It is based on the [Ed25519 public-key signature system][ed25519] by Bernstein et al.
5//!
6//! `libsignify` can verify and create signatures that are interoperable with BSD signify.
7//! You can read more about the ideas and concepts behind `signify` in [Securing OpenBSD From Us To You](https://www.openbsd.org/papers/bsdcan-signify.html).
8//!
9//! This crate is `#![no_std]` by default, but still relies on `liballoc` so your platform must
10//! provide an allocator to use `libsignify`.
11//!
12//! To enable support for `std::error::Error`, enable the `std` feature.
13//!
14//! ## Examples
15//! A simple CLI that verifies some example data:
16//! ```rust
17#![doc = include_str!("../examples/basic.rs")]
18//! ```
19//!
20//! [signify]: https://github.com/aperezdc/signify
21//! [ed25519]: https://ed25519.cr.yp.to/
22#![warn(missing_docs)]
23#![deny(rustdoc::broken_intra_doc_links)]
24#![no_std]
25
26extern crate alloc;
27
28pub mod consts;
29pub use consts::KeyNumber;
30
31mod encoding;
32pub use encoding::Codeable;
33
34mod errors;
35pub use errors::{Error, FormatError};
36
37mod key;
38pub use key::{NewKeyOpts, PrivateKey, PublicKey, Signature};
39
40#[cfg(test)]
41pub(crate) mod test_utils;
42
43use ed25519_dalek::{Signer as _, Verifier as _};
44
45impl PrivateKey {
46    /// Signs a message with this secret key and returns the signature.
47    pub fn sign(&self, msg: &[u8]) -> Signature {
48        // This panics because signing is otherwise infallible if the key is valid.
49        //
50        // All constructors of `PrivateKey` return a valid one, so this is better then forcing
51        // a caller to handle an impossible error.
52        let keypair = PrivateKey::from_key_bytes(&self.complete_key)
53            .expect("invalid private keypair used for signing");
54        let sig = keypair.sign(msg).to_bytes();
55        Signature::new(self.keynum, sig)
56    }
57}
58
59impl PublicKey {
60    /// Use this key to verify that the provided signature for the given message
61    /// is authentic.
62    ///
63    /// # Errors
64    ///
65    /// This method errors if this key's number didn't match the ID of the key
66    /// which created the signature or if the signature couldn't be verified.
67    pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
68        let current_keynum = self.keynum();
69        let expected_keynum = signature.keynum;
70
71        if expected_keynum != current_keynum {
72            return Err(Error::MismatchedKey {
73                expected: expected_keynum,
74                found: current_keynum,
75            });
76        }
77
78        // Both the key data and signature data are not verified yet,
79        // so the ed25519 math can still go wrong.
80        // In that case all we need to communicate is that it was a bad signature.
81
82        let public_key = ed25519_dalek::VerifyingKey::from_bytes(&self.key())
83            .map_err(|_| Error::BadSignature)?;
84        let signature = ed25519_dalek::Signature::from_bytes(&signature.signature());
85        public_key
86            .verify(msg, &signature)
87            .map_err(|_| Error::BadSignature)
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94    use crate::test_utils::StepperRng;
95
96    const MSG: &[u8] = b"signify!!!";
97
98    #[test]
99    fn check_signature_roundtrip() {
100        let mut rng = StepperRng::default();
101
102        let secret_key = PrivateKey::generate(&mut rng, NewKeyOpts::NoEncryption).unwrap();
103        let public_key = secret_key.public();
104        let signature = secret_key.sign(MSG);
105
106        assert_eq!(signature.signer_keynum(), public_key.keynum());
107
108        assert!(public_key.verify(MSG, &signature).is_ok());
109    }
110
111    #[test]
112    fn check_signature_mismatched_keynum() {
113        let mut rng = StepperRng::default();
114
115        let secret_key = PrivateKey::generate(&mut rng, NewKeyOpts::NoEncryption).unwrap();
116        let public_key = secret_key.public();
117        let mut signature = secret_key.sign(MSG);
118
119        let wrong_keynum = KeyNumber::new([0u8; KeyNumber::LEN]);
120
121        signature.keynum = wrong_keynum;
122
123        assert_eq!(
124            public_key.verify(MSG, &signature),
125            Err(Error::MismatchedKey {
126                expected: wrong_keynum,
127                found: public_key.keynum()
128            })
129        )
130    }
131
132    #[test]
133    fn check_malformed_publickey() {
134        let mut rng = StepperRng::default();
135
136        let secret_key = PrivateKey::generate(&mut rng, NewKeyOpts::NoEncryption).unwrap();
137        let mut public_key = secret_key.public();
138        let signature = secret_key.sign(MSG);
139
140        // Mess the public key up so its not a curve point anymore.
141        public_key.key = [
142            136, 95, 131, 189, 208, 168, 196, 163, 180, 145, 35, 42, 113, 108, 172, 178, 62, 108,
143            7, 205, 20, 215, 240, 50, 149, 237, 146, 32, 181, 180, 91, 255,
144        ];
145
146        assert_eq!(public_key.verify(MSG, &signature), Err(Error::BadSignature));
147    }
148
149    #[test]
150    fn check_malformed_signature() {
151        let mut rng = StepperRng::default();
152
153        let secret_key = PrivateKey::generate(&mut rng, NewKeyOpts::NoEncryption).unwrap();
154        let public_key = secret_key.public();
155        let mut signature = secret_key.sign(MSG);
156
157        let real_sig = signature.sig;
158
159        // Make the signature fail the basic validations.
160        signature.sig = [255u8; consts::SIG_LEN];
161
162        assert_eq!(public_key.verify(MSG, &signature), Err(Error::BadSignature));
163
164        signature.sig = real_sig;
165        // Slightly modify the signature so that full verification fails.
166        signature.sig[20] = 3;
167
168        assert_eq!(public_key.verify(MSG, &signature), Err(Error::BadSignature));
169    }
170}