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
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 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
124pub 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}