minisign/
lib.rs

1#![doc = include_str!("../README.md")]
2#![allow(
3    clippy::inherent_to_string,
4    clippy::wrong_self_convention,
5    clippy::derivable_impls,
6    clippy::field_reassign_with_default,
7    clippy::vec_init_then_push
8)]
9
10mod constants;
11mod crypto;
12mod errors;
13mod helpers;
14mod keynum;
15mod keypair;
16mod public_key;
17mod secret_key;
18mod signature;
19mod signature_bones;
20mod signature_box;
21
22#[cfg(test)]
23mod tests;
24
25use std::io::{self, Read, Seek, Write};
26
27use getrandom::getrandom;
28
29pub use crate::constants::*;
30use crate::crypto::blake2b::Blake2b;
31use crate::crypto::ed25519;
32pub use crate::errors::*;
33use crate::helpers::*;
34pub use crate::keypair::*;
35pub use crate::public_key::*;
36pub use crate::secret_key::*;
37use crate::signature::*;
38pub use crate::signature_bones::*;
39pub use crate::signature_box::*;
40
41fn prehash<R>(data_reader: &mut R) -> Result<Vec<u8>>
42where
43    R: Read,
44{
45    let mut h = vec![0u8; PREHASH_BYTES];
46    let mut buf = vec![0u8; 65536];
47    let mut state = Blake2b::new(PREHASH_BYTES);
48    loop {
49        let len = data_reader.read(&mut buf)?;
50        if len == 0 {
51            break;
52        }
53        state.update(&buf[..len]);
54    }
55    state.finalize(&mut h);
56    Ok(h)
57}
58
59/// Compute a signature.
60///
61/// # Arguments
62///
63/// * `pk` - an optional public key. If provided, it must be the public key from
64///   the original key pair.
65/// * `sk` - the secret key
66/// * `data_reader` - the source of the data to be signed
67/// * `trusted_comment` - overrides the default trusted comment
68/// * `untrusted_comment` - overrides the default untrusted comment
69pub fn sign<R>(
70    pk: Option<&PublicKey>,
71    sk: &SecretKey,
72    mut data_reader: R,
73    trusted_comment: Option<&str>,
74    untrusted_comment: Option<&str>,
75) -> Result<SignatureBox>
76where
77    R: Read,
78{
79    let data = prehash(&mut data_reader)?;
80    let trusted_comment = match trusted_comment {
81        Some(trusted_comment) => trusted_comment.to_string(),
82        None => format!("timestamp:{}", unix_timestamp()),
83    };
84    let untrusted_comment = match untrusted_comment {
85        Some(untrusted_comment) => untrusted_comment.to_string(),
86        None => DEFAULT_COMMENT.to_string(),
87    };
88    let mut signature = Signature::default();
89    signature.sig_alg = SIGALG_PREHASHED;
90
91    signature.keynum.copy_from_slice(&sk.keynum_sk.keynum[..]);
92    let mut z = vec![0; 64];
93    getrandom(&mut z)?;
94    let signature_raw = ed25519::signature(&data, &sk.keynum_sk.sk, Some(&z));
95    signature.sig.copy_from_slice(&signature_raw[..]);
96
97    let mut sig_and_trusted_comment: Vec<u8> = vec![];
98    sig_and_trusted_comment.extend(signature.sig.iter());
99    sig_and_trusted_comment.extend(trusted_comment.as_bytes().iter());
100
101    getrandom(&mut z)?;
102    let global_sig = ed25519::signature(&sig_and_trusted_comment, &sk.keynum_sk.sk, Some(&z));
103    if let Some(pk) = pk {
104        if !ed25519::verify(&sig_and_trusted_comment, &pk.keynum_pk.pk[..], &global_sig) {
105            return Err(PError::new(
106                ErrorKind::Verify,
107                format!(
108                    "Could not verify signature with the provided public key ID: {:016X}",
109                    load_u64_le(&pk.keynum_pk.keynum[..])
110                ),
111            ));
112        }
113    }
114    let signature_box = SignatureBox {
115        untrusted_comment,
116        signature,
117        sig_and_trusted_comment: Some(sig_and_trusted_comment),
118        global_sig: Some(global_sig.to_vec()),
119        is_prehashed: true,
120    };
121    Ok(signature_box)
122}
123
124/// Verify a signature using a public key.
125///
126/// # Arguments
127///
128/// * `pk` - the public key
129/// * `signature_box` - the signature and its metadata
130/// * `data_reader` - the data source
131/// * `quiet` - use `false` to output status information to `stderr`
132/// * `output` - use `true` to output a copy of the data to `stdout`
133/// * `allow_legacy` - accept signatures from legacy versions of minisign
134pub fn verify<R>(
135    pk: &PublicKey,
136    signature_box: &SignatureBox,
137    mut data_reader: R,
138    quiet: bool,
139    output: bool,
140    allow_legacy: bool,
141) -> Result<()>
142where
143    R: Read + Seek,
144{
145    let data = if signature_box.is_prehashed() {
146        prehash(&mut data_reader)?
147    } else {
148        let mut data = vec![];
149        data_reader.read_to_end(&mut data)?;
150        data
151    };
152    let sig = &signature_box.signature;
153    if sig.keynum != pk.keynum_pk.keynum {
154        return Err(PError::new(
155            ErrorKind::Verify,
156            format!(
157                "Signature key id: {:016X} is different from public key: {:016X}",
158                load_u64_le(&sig.keynum[..]),
159                load_u64_le(&pk.keynum_pk.keynum[..])
160            ),
161        ));
162    }
163    if !allow_legacy && !signature_box.is_prehashed() {
164        return Err(PError::new(
165            ErrorKind::Verify,
166            "Legacy signatures are not accepted",
167        ));
168    }
169    if !ed25519::verify(&data, &pk.keynum_pk.pk, &sig.sig) {
170        return Err(PError::new(
171            ErrorKind::Verify,
172            "Signature verification failed",
173        ));
174    }
175    match (
176        &signature_box.sig_and_trusted_comment,
177        &signature_box.global_sig,
178    ) {
179        (Some(sig_and_trusted_comment), Some(global_sig)) => {
180            if !ed25519::verify(sig_and_trusted_comment, &pk.keynum_pk.pk, &global_sig[..]) {
181                return Err(PError::new(
182                    ErrorKind::Verify,
183                    "Comment signature verification failed",
184                ));
185            }
186        }
187        (None, None) => {}
188        _ => {
189            return Err(PError::new(
190                ErrorKind::Verify,
191                "Inconsistent signature presence for trusted comment presence",
192            ))
193        }
194    };
195    if !quiet {
196        eprintln!("Signature and comment signature verified");
197        if signature_box.global_sig.is_some() {
198            eprintln!("Trusted comment: {}", signature_box.trusted_comment()?);
199        }
200    }
201    if output {
202        data_reader.rewind()?;
203        let mut buf = vec![0; 65536];
204        loop {
205            let len = data_reader.read(&mut buf)?;
206            if len == 0 {
207                break;
208            }
209            io::stdout().write_all(&buf[..len])?;
210        }
211        io::stdout().flush()?;
212    }
213    Ok(())
214}