Skip to main content

mini_sign/
lib.rs

1//
2#![doc = include_str!("../README.md")]
3mod constants;
4use blake2::Digest;
5use std::io::Read;
6
7use constants::*;
8mod errors;
9pub use errors::*;
10pub use public_key::PublicKeyBox;
11use public_key::{PublicKey, RawPk};
12pub use secret_key::SecretKeyBox;
13use signature::Signature;
14mod keypair;
15pub use keypair::KeyPairBox;
16mod public_key;
17mod secret_key;
18mod signature;
19pub use signature::SignatureBox;
20
21use crate::public_key::verify_prehashed;
22use crate::util::validate_comment;
23mod util;
24fn prehash<R>(data_reader: &mut R) -> Result<Vec<u8>>
25where
26    R: Read,
27{
28    let mut hash = blake2::Blake2b512::new();
29    let mut buf = [0; 2048];
30    loop {
31        let n = data_reader.read(&mut buf)?;
32        if n == 0 {
33            break;
34        }
35        hash.update(&buf[..n]);
36    }
37    Ok(hash.finalize().to_vec())
38}
39/// Create a new public key from a string in the minisign format pub key file
40pub fn pub_key_from_str(s: &str) -> Result<PublicKeyBox<'_>> {
41    PublicKeyBox::from_str(s)
42}
43/// Create a new secret key from a string in the minisign format key file
44pub fn sec_key_from_str(s: &str) -> Result<SecretKeyBox<'_>> {
45    SecretKeyBox::from_str(s)
46}
47/// Create a new public key from a secret key
48///
49/// default comment is None
50pub fn pub_key_from_sec_key<'s>(
51    sec_key: &SecretKeyBox<'s>,
52    password: Option<&[u8]>,
53) -> Result<PublicKeyBox<'s>> {
54    let keynum_sk = sec_key.xor_keynum_sk(password)?;
55    PublicKeyBox::new(
56        None,
57        PublicKey::new(
58            sec_key.sig_alg(),
59            keynum_sk.key_id,
60            RawPk(keynum_sk.pub_key),
61        ),
62    )
63}
64
65/// minisign some data
66/// # Arguments
67/// * `pk` - The public key to verify the signature(optional)
68/// * `sk` - The secret key to sign the data
69/// * `password` - The password to decrypt the secret key
70/// * `data_reader` - The data to sign
71/// * `trusted_comment` - The trusted comment for the signature
72/// * `untrusted_comment` - The untrusted comment for the signature
73/// # Returns
74/// A Result containing the signature
75/// # Errors
76/// * `ErrorKind::Io` - If there is an error reading the data
77/// * `ErrorKind::SecretKey` - If there is an error decrypting the secret key,password is wrong
78/// * `ErrorKind::PublicKey` - If the public key is invalid or not matching the secret key
79pub fn sign<'a, R>(
80    pk: Option<&PublicKeyBox>,
81    sk: &SecretKeyBox,
82    password: Option<&[u8]>,
83    mut data_reader: R,
84    trusted_comment: Option<&'a str>,
85    untrusted_comment: Option<&'a str>,
86) -> Result<SignatureBox<'a>>
87where
88    R: Read,
89{
90    validate_comment(trusted_comment, ErrorKind::SignatureError)?;
91    validate_comment(untrusted_comment, ErrorKind::SignatureError)?;
92    let prehashed = prehash(&mut data_reader)?;
93    let sig = sk.sign(&prehashed, password)?;
94    let mut global_data = sig.to_bytes().to_vec();
95    global_data.extend_from_slice(trusted_comment.unwrap_or("").as_bytes());
96    let global_sig = sk.sign(&global_data, password)?;
97    let keynum_sk = sk.xor_keynum_sk(password)?;
98    let signature = Signature::new(SIGALG_PREHASHED, keynum_sk.key_id, sig, global_sig);
99    let sig_box = SignatureBox::new(untrusted_comment, trusted_comment, signature)?;
100    if let Some(pk) = pk {
101        verify_prehashed(pk, &sig_box, &prehashed)?;
102    }
103    Ok(sig_box)
104}
105/// Verify a minisign signature
106/// # Arguments
107/// * `pk` - The public key to verify the signature
108/// * `signature_box` - The signature to verify
109/// * `data_reader` - The data to verify
110/// # Returns
111/// A Result containing a boolean, true if the signature is valid
112/// # Errors
113/// * `ErrorKind::Io` - If there is an error reading the data
114/// * `ErrorKind::PublicKey` - If the public key is invalid or not matching the signature
115/// * `ErrorKind::PrehashedMismatch` - If the signature is not prehashed
116pub fn verify<R>(
117    pk: &PublicKeyBox,
118    signature_box: &SignatureBox,
119    mut data_reader: R,
120) -> Result<bool>
121where
122    R: Read,
123{
124    let prehashed = prehash(&mut data_reader)?;
125    verify_prehashed(pk, signature_box, &prehashed)
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    #[test]
132    fn test() {
133        let KeyPairBox {
134            public_key_box,
135            secret_key_box,
136        } = KeyPairBox::generate(
137            Some(b"password"),
138            Some("pk untrusted comment"),
139            Some("sk untrusted comment"),
140        )
141        .unwrap();
142        let msg = "test";
143        let sig_box = sign(
144            Some(&public_key_box),
145            &secret_key_box,
146            Some(b"password"),
147            msg.as_bytes(),
148            Some("trusted comment"),
149            Some("untrusted comment"),
150        )
151        .unwrap();
152        let v = verify(&public_key_box, &sig_box, msg.as_bytes()).unwrap();
153        assert!(v);
154    }
155    #[test]
156    fn test_sign_rejects_comment_control_characters() {
157        let KeyPairBox {
158            public_key_box,
159            secret_key_box,
160        } = KeyPairBox::generate(Some(b"password"), None, None).unwrap();
161
162        assert!(sign(
163            Some(&public_key_box),
164            &secret_key_box,
165            Some(b"password"),
166            "test".as_bytes(),
167            Some("trusted\ncomment"),
168            Some("untrusted comment"),
169        )
170        .is_err());
171        assert!(sign(
172            Some(&public_key_box),
173            &secret_key_box,
174            Some(b"password"),
175            "test".as_bytes(),
176            Some("trusted comment"),
177            Some("untrusted\0comment"),
178        )
179        .is_err());
180    }
181}