summaryrefslogtreecommitdiffstats
path: root/openpgp/examples/notarize.rs
blob: 1e4939c6355cf95c42cb71484fbdc57dd791fd47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/// Notarizes OpenPGP messages using the openpgp crate, Sequoia's
/// low-level API.

use std::env;
use std::io;

use anyhow::Context;

extern crate sequoia_openpgp as openpgp;
use crate::openpgp::{
    armor,
    Packet,
    parse::{Parse, PacketParserResult},
    serialize::Marshal,
};
use crate::openpgp::serialize::stream::{Message, LiteralWriter, Signer};
use crate::openpgp::policy::StandardPolicy as P;

fn main() -> openpgp::Result<()> {
    let p = &P::new();
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        return Err(anyhow::anyhow!("A simple notarizing filter.\n\n\
                Usage: {} <secret-keyfile> [<secret-keyfile>...] \
                <input >output\n", args[0]));
    }

    // Read the transferable secret keys from the given files.
    let mut keys = Vec::new();
    for filename in &args[1..] {
        let tsk = openpgp::Cert::from_file(filename)
            .context("Failed to read key")?;
        let mut n = 0;

        for key in tsk.keys()
            .with_policy(p, None).alive().revoked(false).for_signing().secret()
            .map(|ka| ka.key())
        {
            keys.push({
                let mut key = key.clone();
                if key.secret().is_encrypted() {
                    let password = rpassword::read_password_from_tty(
                        Some(&format!("Please enter password to decrypt \
                                       {}/{}: ",tsk, key)))?;
                    let algo = key.pk_algo();
                    key.secret_mut()
                        .decrypt_in_place(algo, &password.into())
                        .context("decryption failed")?;
                }
                n += 1;
                key.into_keypair()?
            });
        }

        if n == 0 {
            return Err(anyhow::anyhow!("Found no suitable signing key on {}", tsk));
        }
    }

    // Compose a writer stack corresponding to the output format and
    // packet structure we want.  First, we want the output to be
    // ASCII armored.
    let mut sink = armor::Writer::new(io::stdout(), armor::Kind::Message)
        .context("Failed to create an armored writer.")?;

    // Stream an OpenPGP message.
    let message = Message::new(&mut sink);

    // Now, create a signer that emits the signature(s).
    let mut signer =
        Signer::new(message, keys.pop().context("No key for signing")?);
    for s in keys {
        signer = signer.add_signer(s);
    }
    let mut signer = signer.build().context("Failed to create signer")?;

    // Create a parser for the message to be notarized.
    let mut input = io::stdin();
    let mut ppr
        = openpgp::parse::PacketParser::from_reader(&mut input)
        .context("Failed to build parser")?;

    while let PacketParserResult::Some(mut pp) = ppr {
        if let Err(err) = pp.possible_message() {
            return Err(anyhow::anyhow!("Malformed OpenPGP message: {}", err));
        }

        match pp.packet {
            Packet::PKESK(_) | Packet::SKESK(_) =>
                return Err(anyhow::anyhow!("Encrypted messages are not supported")),
            Packet::OnePassSig(ref ops) =>
                ops.serialize(&mut signer).context("Failed to serialize")?,
            Packet::Literal(_) => {
                // Then, create a literal writer to wrap the data in a
                // literal message packet.
                let mut literal =
                    LiteralWriter::new(signer).build()
                    .context("Failed to create literal writer")?;

                // Copy all the data.
                io::copy(&mut pp, &mut literal)
                    .context("Failed to sign data")?;

                signer = literal.finalize_one()
                    .context("Failed to sign data")?
                    .unwrap();
            },
            Packet::Signature(ref sig) =>
                sig.serialize(&mut signer).context("Failed to serialize")?,
            _ => (),
        }

        ppr = pp.recurse().context("Failed to recurse")?.1;
    }
    if let PacketParserResult::EOF(eof) = ppr {
        if let Err(err) = eof.is_message() {
            return Err(anyhow::anyhow!("Malformed OpenPGP message: {}", err));
        }
    } else {
        unreachable!()
    }

    // Finally, teardown the stack to ensure all the data is written.
    signer.finalize()
        .context("Failed to write data")?;

    // Finalize the armor writer.
    sink.finalize()
        .context("Failed to write data")?;

    Ok(())
}