1
use anyhow::anyhow;
2
use sequoia_openpgp::packet::{Signature, Tag};
3
use sequoia_openpgp::parse::stream::{
4
    DetachedVerifierBuilder, GoodChecksum, MessageLayer, MessageStructure, VerificationError,
5
    VerificationHelper,
6
};
7
use sequoia_openpgp::policy::{AsymmetricAlgorithm, StandardPolicy};
8
use sequoia_openpgp::KeyHandle;
9
use sequoia_openpgp::{cert::raw::RawCertParser, parse::Parse, Cert};
10
use serde::Serialize;
11
use std::path::Path;
12
use std::process::{Command, Stdio};
13

            
14
type Result<T> = anyhow::Result<T>;
15

            
16
20
fn read_keys(gpgdir: &Path) -> Result<Vec<u8>> {
17
20
    let gpgdir = gpgdir.to_str().unwrap();
18
20
    let exe = "gpg";
19
20
    let output = Command::new(exe)
20
20
        .args(["--batch", "--homedir", gpgdir, "--export", "-a"])
21
20
        .stdout(Stdio::piped())
22
20
        .stderr(Stdio::piped())
23
20
        .output()
24
20
        .map_err(|e| match e.kind() {
25
            std::io::ErrorKind::NotFound => anyhow!("Executable {} was not found.", exe),
26
            _ => anyhow::Error::from(e),
27
20
        })?;
28
20
    if !output.status.success() {
29
        return Err(anyhow!(
30
            "Could load public keys: {}",
31
            &String::from_utf8_lossy(&output.stderr)
32
        ));
33
20
    }
34
20
    Ok(output.stdout)
35
20
}
36

            
37
20
pub fn load_keys(gpgdir: &Path) -> Result<Vec<Cert>> {
38
20
    let keys = read_keys(gpgdir)?;
39
20
    let parser = RawCertParser::from_bytes(&keys)?;
40
20
    let mut certs = Vec::new();
41
40
    for cert in parser {
42
20
        let cert = cert?;
43
20
        certs.push(Cert::try_from(cert)?);
44
    }
45
20
    println!("Loaded {} keys", certs.len());
46
20
    Ok(certs)
47
20
}
48

            
49
4
#[derive(Serialize, PartialEq)]
50
pub struct PgpSignature {
51
    pub payload: Vec<u8>,
52
    pub signature: Vec<u8>,
53
}
54

            
55
pub struct ValidatedSignature {
56
    /// The parsed signature
57
    pub signature: Signature,
58
    /// The certificates corresponding to keys from the signature
59
    pub certificates: Vec<(KeyHandle, Cert)>,
60
    /// Errors that occurred during the validation
61
    pub errors: Vec<anyhow::Error>,
62
}
63

            
64
impl ValidatedSignature {
65
    fn from_helper(helper: Helper) -> sequoia_openpgp::Result<Self> {
66
120
        if let Some(signature) = helper.signature {
67
120
            signature.typ();
68
120
            return Ok(Self {
69
120
                signature,
70
120
                certificates: helper
71
120
                    .used_certs
72
120
                    .into_iter()
73
120
                    .map(|(id, pos)| (id, helper.certs[pos].to_owned()))
74
120
                    .collect(),
75
120
                errors: helper.errors,
76
120
            });
77
        }
78
        Err(anyhow!("no (valid) signature"))
79
120
    }
80
}
81

            
82
// Check a signature
83
// The required public key is retrieved from `certs`. If there is no
84
// matching key or the required certificate is missing, an error is
85
// returned.
86
120
pub fn check_signature(
87
120
    certs: &[Cert],
88
120
    signature: &PgpSignature,
89
120
) -> sequoia_openpgp::Result<ValidatedSignature> {
90
120
    let mut p = StandardPolicy::new();
91
120
    // accept all versions of the signature tag
92
120
    // PGP 2.6.x only accepts version 3 signatures.
93
120
    p.accept_packet_tag(Tag::Signature);
94
120
    // git uses SHA1 which is insecure in the standard policy
95
120
    p.accept_hash(sequoia_openpgp::types::HashAlgorithm::SHA1);
96
120
    p.accept_asymmetric_algo(AsymmetricAlgorithm::DSA1024);
97
120
    let builder = DetachedVerifierBuilder::from_bytes(&signature.signature)?;
98
120
    let h = Helper {
99
120
        certs,
100
120
        signature: None,
101
120
        used_certs: Vec::new(),
102
120
        errors: Vec::new(),
103
120
    };
104
120
    let mut v = builder.with_policy(&p, None, h)?;
105
120
    v.verify_bytes(&signature.payload)?;
106
120
    let helper = v.into_helper();
107
120
    ValidatedSignature::from_helper(helper)
108
120
}
109

            
110
struct Helper<'a> {
111
    certs: &'a [Cert],
112
    signature: Option<Signature>,
113
    // certificates that are used by the current signature
114
    // They are index into `certs`
115
    used_certs: Vec<(KeyHandle, usize)>,
116
    errors: Vec<anyhow::Error>,
117
}
118
impl<'a> VerificationHelper for Helper<'a> {
119
    /// This method will be called at most once per message.
120
    /// a missing certificate for an id is not an error
121
120
    fn get_certs(&mut self, ids: &[KeyHandle]) -> sequoia_openpgp::Result<Vec<Cert>> {
122
120
        self.used_certs.clear();
123
120
        let mut certs = Vec::with_capacity(ids.len());
124
240
        for id in ids {
125
120
            for (pos, cert) in self.certs.iter().enumerate() {
126
30
                if cert.keys().any(|key| key.key().key_handle() == *id) {
127
30
                    self.used_certs.push((id.clone(), pos));
128
30
                    certs.push(cert.to_owned());
129
30
                }
130
            }
131
        }
132
120
        Ok(certs)
133
120
        // when debugging certs that cannot be found use the next line
134
120
        // Ok(self.certs.to_vec())
135
120
    }
136
    /// This method will be called at most once per message.
137
120
    fn check(&mut self, structure: MessageStructure) -> sequoia_openpgp::Result<()> {
138
        // in detached signatures, there is only one layer that is a
139
        // SignatureGroup
140
120
        for layer in structure {
141
120
            if let MessageLayer::SignatureGroup { results } = layer {
142
120
                self.errors.clear();
143
240
                for result in results {
144
120
                    if self.signature.is_some() {
145
                        return Err(anyhow!("Multiple signatures found."));
146
120
                    }
147
120
                    self.signature = Some(check_result(&mut self.errors, result));
148
                }
149
120
                return Ok(());
150
            }
151
        }
152
        panic!("MessageStructure should only have one layer with a SignatureGroup when `check` is called for a detached signature.");
153
120
    }
154
}
155

            
156
// Check the result of a signature match and place any errors into
157
// `result`.
158
120
fn check_result(
159
120
    errors: &mut Vec<anyhow::Error>,
160
120
    result: std::result::Result<GoodChecksum, VerificationError>,
161
120
) -> Signature {
162
120
    match result {
163
20
        Ok(checksum) => checksum.sig.clone(),
164
100
        Err(e) => match e {
165
            VerificationError::MalformedSignature { sig, error } => {
166
                errors.push(error);
167
                sig.clone()
168
            }
169
90
            VerificationError::MissingKey { sig } => {
170
90
                errors.push(MissingKeyError(sig.clone()).into());
171
90
                sig.clone()
172
            }
173
            VerificationError::UnboundKey {
174
                sig,
175
                cert: _,
176
                error,
177
            } => {
178
                errors.push(error);
179
                sig.clone()
180
            }
181
            VerificationError::BadKey { sig, ka: _, error } => {
182
                errors.push(error);
183
                sig.clone()
184
            }
185
10
            VerificationError::BadSignature { sig, ka: _, error } => {
186
10
                errors.push(error);
187
10
                sig.clone()
188
            }
189
        },
190
    }
191
120
}
192

            
193
#[derive(Debug)]
194
pub struct MissingKeyError(Signature);
195

            
196
impl std::error::Error for MissingKeyError {}
197

            
198
impl MissingKeyError {
199
90
    pub fn missing_key(&self) -> Option<KeyHandle> {
200
90
        self.0.get_issuers().get(0).map(|k| k.to_owned())
201
90
    }
202
}
203

            
204
impl std::fmt::Display for MissingKeyError {
205
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206
        if let Some(issuer) = self.0.get_issuers().get(0) {
207
            write!(f, "Missing key: {}", issuer)
208
        } else {
209
            write!(f, "Missing key")
210
        }
211
    }
212
}