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