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::fill as 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
59pub 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 if sk.is_encrypted() {
80 return Err(PError::new(
81 ErrorKind::EncryptedKey,
82 "Cannot sign with an encrypted secret key. The key must be decrypted first. Options include: SecretKeyBox::into_secret_key(password), SecretKey::from_box(box, password), SecretKey::from_file(path, password), or use KeyPair::generate_unencrypted_keypair() for passwordless keys.",
83 ));
84 }
85 let data = prehash(&mut data_reader)?;
86 let trusted_comment = match trusted_comment {
87 Some(trusted_comment) => trusted_comment.to_string(),
88 None => format!("timestamp:{}", unix_timestamp()),
89 };
90 let untrusted_comment = match untrusted_comment {
91 Some(untrusted_comment) => untrusted_comment.to_string(),
92 None => DEFAULT_COMMENT.to_string(),
93 };
94 let mut signature = Signature::default();
95 signature.sig_alg = SIGALG_PREHASHED;
96
97 signature.keynum.copy_from_slice(&sk.keynum_sk.keynum[..]);
98 let mut z = vec![0; 64];
99 getrandom(&mut z)?;
100 let signature_raw = ed25519::signature(&data, &sk.keynum_sk.sk, Some(&z));
101 signature.sig.copy_from_slice(&signature_raw[..]);
102
103 let mut sig_and_trusted_comment: Vec<u8> = vec![];
104 sig_and_trusted_comment.extend(signature.sig.iter());
105 sig_and_trusted_comment.extend(trusted_comment.as_bytes().iter());
106
107 getrandom(&mut z)?;
108 let global_sig = ed25519::signature(&sig_and_trusted_comment, &sk.keynum_sk.sk, Some(&z));
109 if let Some(pk) = pk {
110 if !ed25519::verify(&sig_and_trusted_comment, &pk.keynum_pk.pk[..], &global_sig) {
111 return Err(PError::new(
112 ErrorKind::Verify,
113 format!(
114 "Could not verify signature with the provided public key ID: {:016X}",
115 load_u64_le(&pk.keynum_pk.keynum[..])
116 ),
117 ));
118 }
119 }
120 let signature_box = SignatureBox {
121 untrusted_comment,
122 signature,
123 sig_and_trusted_comment: Some(sig_and_trusted_comment),
124 global_sig: Some(global_sig.to_vec()),
125 is_prehashed: true,
126 };
127 Ok(signature_box)
128}
129
130pub fn verify<R>(
141 pk: &PublicKey,
142 signature_box: &SignatureBox,
143 mut data_reader: R,
144 quiet: bool,
145 output: bool,
146 allow_legacy: bool,
147) -> Result<()>
148where
149 R: Read + Seek,
150{
151 let data = if signature_box.is_prehashed() {
152 prehash(&mut data_reader)?
153 } else {
154 let mut data = vec![];
155 data_reader.read_to_end(&mut data)?;
156 data
157 };
158 let sig = &signature_box.signature;
159 if sig.keynum != pk.keynum_pk.keynum {
160 return Err(PError::new(
161 ErrorKind::Verify,
162 format!(
163 "Signature key id: {:016X} is different from public key: {:016X}",
164 load_u64_le(&sig.keynum[..]),
165 load_u64_le(&pk.keynum_pk.keynum[..])
166 ),
167 ));
168 }
169 if !allow_legacy && !signature_box.is_prehashed() {
170 return Err(PError::new(
171 ErrorKind::Verify,
172 "Legacy signatures are not accepted",
173 ));
174 }
175 if !ed25519::verify(&data, &pk.keynum_pk.pk, &sig.sig) {
176 return Err(PError::new(
177 ErrorKind::Verify,
178 "Signature verification failed",
179 ));
180 }
181 match (
182 &signature_box.sig_and_trusted_comment,
183 &signature_box.global_sig,
184 ) {
185 (Some(sig_and_trusted_comment), Some(global_sig)) => {
186 if !ed25519::verify(sig_and_trusted_comment, &pk.keynum_pk.pk, &global_sig[..]) {
187 return Err(PError::new(
188 ErrorKind::Verify,
189 "Comment signature verification failed",
190 ));
191 }
192 }
193 (None, None) => {}
194 _ => {
195 return Err(PError::new(
196 ErrorKind::Verify,
197 "Inconsistent signature presence for trusted comment presence",
198 ))
199 }
200 };
201 if !quiet {
202 eprintln!("Signature and comment signature verified");
203 if signature_box.global_sig.is_some() {
204 eprintln!("Trusted comment: {}", signature_box.trusted_comment()?);
205 }
206 }
207 if output {
208 data_reader.rewind()?;
209 let mut buf = vec![0; 65536];
210 loop {
211 let len = data_reader.read(&mut buf)?;
212 if len == 0 {
213 break;
214 }
215 io::stdout().write_all(&buf[..len])?;
216 }
217 io::stdout().flush()?;
218 }
219 Ok(())
220}