1 use anyhow::Context as _;
2 use std::fs;
3 use std::io;
4 use std::path::PathBuf;
5 use std::time::SystemTime;
6 use tempfile::NamedTempFile;
7
8 extern crate sequoia_openpgp as openpgp;
9 use crate::openpgp::armor;
10 use crate::openpgp::{Packet, Result};
11 use crate::openpgp::packet::Signature;
12 use crate::openpgp::parse::{
13 Parse,
14 PacketParserResult,
15 };
16 use crate::openpgp::serialize::Serialize;
17 use crate::openpgp::serialize::stream::{
18 Message, Armorer, Signer, LiteralWriter,
19 };
20 use crate::openpgp::policy::Policy;
21 use crate::{
22 create_or_stdout,
23 create_or_stdout_pgp,
24 };
25
sign(policy: &dyn Policy, input: &mut dyn io::Read, output_path: Option<&str>, secrets: Vec<openpgp::Cert>, detached: bool, binary: bool, append: bool, notarize: bool, time: Option<SystemTime>, force: bool) -> Result<()>26 pub fn sign(policy: &dyn Policy,
27 input: &mut dyn io::Read, output_path: Option<&str>,
28 secrets: Vec<openpgp::Cert>, detached: bool, binary: bool,
29 append: bool, notarize: bool, time: Option<SystemTime>,
30 force: bool)
31 -> Result<()> {
32 match (detached, append|notarize) {
33 (_, false) | (true, true) =>
34 sign_data(policy, input, output_path, secrets, detached, binary,
35 append, time, force),
36 (false, true) =>
37 sign_message(policy, input, output_path, secrets, binary, notarize,
38 time, force),
39 }
40 }
41
sign_data(policy: &dyn Policy, input: &mut dyn io::Read, output_path: Option<&str>, secrets: Vec<openpgp::Cert>, detached: bool, binary: bool, append: bool, time: Option<SystemTime>, force: bool) -> Result<()>42 fn sign_data(policy: &dyn Policy,
43 input: &mut dyn io::Read, output_path: Option<&str>,
44 secrets: Vec<openpgp::Cert>, detached: bool, binary: bool,
45 append: bool, time: Option<SystemTime>, force: bool)
46 -> Result<()> {
47 let (mut output, prepend_sigs, tmp_path):
48 (Box<dyn io::Write>, Vec<Signature>, Option<PathBuf>) =
49 if detached && append && output_path.is_some() {
50 // First, read the existing signatures.
51 let mut sigs = Vec::new();
52 let mut ppr =
53 openpgp::parse::PacketParser::from_file(output_path.unwrap())?;
54
55 while let PacketParserResult::Some(pp) = ppr {
56 let (packet, ppr_tmp) = pp.recurse()?;
57 ppr = ppr_tmp;
58
59 match packet {
60 Packet::Signature(sig) => sigs.push(sig),
61 p => return Err(
62 anyhow::anyhow!(
63 format!("{} in detached signature", p.tag()))
64 .context("Invalid detached signature").into()),
65 }
66 }
67
68 // Then, create a temporary file to write to. If we are
69 // successful with adding our signature(s), we rename the
70 // file replacing the old one.
71 let tmp_file = NamedTempFile::new_in(
72 PathBuf::from(output_path.unwrap()).parent()
73 .unwrap_or(&PathBuf::from(".")))?;
74 let tmp_path = tmp_file.path().into();
75 (Box::new(tmp_file), sigs, Some(tmp_path))
76 } else {
77 (create_or_stdout(output_path, force)?, Vec::new(), None)
78 };
79
80 let mut keypairs = super::get_signing_keys(&secrets, policy, time)?;
81 if keypairs.is_empty() {
82 return Err(anyhow::anyhow!("No signing keys found"));
83 }
84
85 // Stream an OpenPGP message.
86 // The sink may be a NamedTempFile. Carefully keep a reference so
87 // that we can rename it.
88 let mut message = Message::new(&mut output);
89 if ! binary {
90 message = Armorer::new(message)
91 .kind(if detached {
92 armor::Kind::Signature
93 } else {
94 armor::Kind::Message
95 })
96 .build()?;
97 }
98
99 // When extending a detached signature, prepend any existing
100 // signatures first.
101 for sig in prepend_sigs.into_iter() {
102 Packet::Signature(sig).serialize(&mut message)?;
103 }
104
105 let mut signer = Signer::new(message, keypairs.pop().unwrap());
106 for s in keypairs {
107 signer = signer.add_signer(s);
108 if let Some(time) = time {
109 signer = signer.creation_time(time);
110 }
111 }
112 if detached {
113 signer = signer.detached();
114 }
115 let signer = signer.build().context("Failed to create signer")?;
116
117 let mut writer = if detached {
118 // Detached signatures do not need a literal data packet, just
119 // hash the data as is.
120 signer
121 } else {
122 // We want to wrap the data in a literal data packet.
123 LiteralWriter::new(signer).build()
124 .context("Failed to create literal writer")?
125 };
126
127 // Finally, copy stdin to our writer stack to sign the data.
128 io::copy(input, &mut writer)
129 .context("Failed to sign")?;
130
131 writer.finalize()
132 .context("Failed to sign")?;
133
134 if let Some(path) = tmp_path {
135 // Atomically replace the old file.
136 fs::rename(path,
137 output_path.expect("must be Some if tmp_path is Some"))?;
138 }
139 Ok(())
140 }
141
sign_message(policy: &dyn Policy, input: &mut dyn io::Read, output_path: Option<&str>, secrets: Vec<openpgp::Cert>, binary: bool, notarize: bool, time: Option<SystemTime>, force: bool) -> Result<()>142 fn sign_message(policy: &dyn Policy,
143 input: &mut dyn io::Read, output_path: Option<&str>,
144 secrets: Vec<openpgp::Cert>, binary: bool, notarize: bool,
145 time: Option<SystemTime>, force: bool)
146 -> Result<()> {
147 let mut output =
148 create_or_stdout_pgp(output_path, force,
149 binary,
150 armor::Kind::Message)?;
151 sign_message_(policy, input, &mut output, secrets, notarize, time)?;
152 output.finalize()?;
153 Ok(())
154 }
155
sign_message_(policy: &dyn Policy, input: &mut dyn io::Read, output: &mut dyn io::Write, secrets: Vec<openpgp::Cert>, notarize: bool, time: Option<SystemTime>) -> Result<()>156 fn sign_message_(policy: &dyn Policy,
157 input: &mut dyn io::Read, output: &mut dyn io::Write,
158 secrets: Vec<openpgp::Cert>, notarize: bool,
159 time: Option<SystemTime>)
160 -> Result<()>
161 {
162 let mut keypairs = super::get_signing_keys(&secrets, policy, time)?;
163 if keypairs.is_empty() {
164 return Err(anyhow::anyhow!("No signing keys found"));
165 }
166
167 let mut sink = Message::new(output);
168
169 // Create a parser for the message to be notarized.
170 let mut ppr
171 = openpgp::parse::PacketParser::from_reader(input)
172 .context("Failed to build parser")?;
173
174 // Once we see a signature, we can no longer strip compression.
175 let mut seen_signature = false;
176 #[derive(PartialEq, Eq, Debug)]
177 enum State {
178 InFirstSigGroup,
179 AfterFirstSigGroup,
180 Signing {
181 // Counts how many signatures are being notarized. If
182 // this drops to zero, we pop the signer from the stack.
183 signature_count: isize,
184 },
185 Done,
186 };
187 let mut state =
188 if ! notarize {
189 State::InFirstSigGroup
190 } else {
191 // Pretend we have passed the first signature group so
192 // that we put our signature first.
193 State::AfterFirstSigGroup
194 };
195
196 while let PacketParserResult::Some(mut pp) = ppr {
197 if let Err(err) = pp.possible_message() {
198 return Err(err.context("Malformed OpenPGP message").into());
199 }
200
201 match pp.packet {
202 Packet::PKESK(_) | Packet::SKESK(_) =>
203 return Err(anyhow::anyhow!(
204 "Signing encrypted data is not implemented")),
205
206 Packet::Literal(_) =>
207 if let State::InFirstSigGroup = state {
208 // Cope with messages that have no signatures, or
209 // with a ops packet without the last flag.
210 state = State::AfterFirstSigGroup;
211 },
212
213 // To implement this, we'd need to stream the
214 // compressed data packet inclusive framing, but
215 // currently the partial body filter transparently
216 // removes the framing.
217 //
218 // If you do implement this, there is a half-disabled test
219 // in tests/sq-sign.rs.
220 Packet::CompressedData(_) if seen_signature =>
221 return Err(anyhow::anyhow!(
222 "Signing a compress-then-sign message is not implemented")),
223
224 _ => (),
225 }
226
227 match state {
228 State::AfterFirstSigGroup => {
229 // After the first signature group, we push the signer
230 // onto the writer stack.
231 let mut signer = Signer::new(sink, keypairs.pop().unwrap());
232 for s in keypairs.drain(..) {
233 signer = signer.add_signer(s);
234 if let Some(time) = time {
235 signer = signer.creation_time(time);
236 }
237 }
238 sink = signer.build().context("Failed to create signer")?;
239 state = State::Signing { signature_count: 0, };
240 },
241
242 State::Signing { signature_count } if signature_count == 0 => {
243 // All signatures that are being notarized are
244 // written, pop the signer from the writer stack.
245 sink = sink.finalize_one()
246 .context("Failed to sign data")?
247 .unwrap();
248 state = State::Done;
249 },
250
251 _ => (),
252 }
253
254 if let Packet::Literal(_) = pp.packet {
255 let l = if let Packet::Literal(l) = pp.packet.clone() {
256 l
257 } else {
258 unreachable!()
259 };
260 // Create a literal writer to wrap the data in a literal
261 // message packet.
262 let mut literal = LiteralWriter::new(sink).format(l.format());
263 if let Some(f) = l.filename() {
264 literal = literal.filename(f)?;
265 }
266 if let Some(d) = l.date() {
267 literal = literal.date(d)?;
268 }
269
270 let mut literal = literal.build()
271 .context("Failed to create literal writer")?;
272
273 // Finally, just copy all the data.
274 io::copy(&mut pp, &mut literal)
275 .context("Failed to sign data")?;
276
277 // Pop the literal writer.
278 sink = literal.finalize_one()
279 .context("Failed to sign data")?
280 .unwrap();
281 }
282
283 let (packet, ppr_tmp) = if seen_signature {
284 // Once we see a signature, we can no longer strip
285 // compression.
286 pp.next()
287 } else {
288 pp.recurse()
289 }.context("Parsing failed")?;
290 ppr = ppr_tmp;
291
292 match packet {
293 Packet::OnePassSig(mut ops) => {
294 let was_last = ops.last();
295 match state {
296 State::InFirstSigGroup => {
297 // We want to append our signature here, hence
298 // we set last to false.
299 ops.set_last(false);
300
301 if was_last {
302 // The signature group ends here.
303 state = State::AfterFirstSigGroup;
304 }
305 },
306
307 State::Signing { ref mut signature_count } =>
308 *signature_count += 1,
309
310 _ => (),
311 }
312
313 Packet::OnePassSig(ops).serialize(&mut sink)?;
314 seen_signature = true;
315 },
316
317 Packet::Signature(sig) => {
318 Packet::Signature(sig).serialize(&mut sink)
319 .context("Failed to serialize")?;
320 if let State::Signing { ref mut signature_count } = state {
321 *signature_count -= 1;
322 }
323 },
324 _ => (),
325 }
326 }
327
328 if let PacketParserResult::EOF(eof) = ppr {
329 if let Err(err) = eof.is_message() {
330 return Err(err.context("Malformed OpenPGP message").into());
331 }
332 } else {
333 unreachable!()
334 }
335
336 match state {
337 State::Signing { signature_count } => {
338 assert_eq!(signature_count, 0);
339 sink.finalize()
340 .context("Failed to sign data")?;
341 },
342 State::Done => (),
343 _ => panic!("Unexpected state: {:?}", state),
344 }
345
346 Ok(())
347 }
348