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

            
37
24
pub fn load_keys(gpgdir: &Path) -> Result<Vec<Cert>> {
38
24
    let keys = read_keys(gpgdir)?;
39
24
    let parser = RawCertParser::from_bytes(&keys)?;
40
24
    let mut certs = Vec::new();
41
48
    for cert in parser {
42
24
        let cert = cert?;
43
24
        certs.push(Cert::try_from(cert)?);
44
    }
45
24
    println!("Loaded {} keys", certs.len());
46
24
    Ok(certs)
47
24
}
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
180
        if let Some(signature) = helper.signature {
67
180
            signature.typ();
68
180
            return Ok(Self {
69
180
                signature,
70
180
                certificates: helper
71
180
                    .used_certs
72
180
                    .into_iter()
73
180
                    .map(|(id, pos)| (id, helper.certs[pos].to_owned()))
74
180
                    .collect(),
75
180
                errors: helper.errors,
76
180
            });
77
        }
78
        Err(anyhow!("no (valid) signature"))
79
180
    }
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
180
pub fn check_signature(
87
180
    certs: &[Cert],
88
180
    signature: &PgpSignature,
89
180
) -> sequoia_openpgp::Result<ValidatedSignature> {
90
180
    let mut p = StandardPolicy::new();
91
180
    // accept all versions of the signature tag
92
180
    // PGP 2.6.x only accepts version 3 signatures.
93
180
    p.accept_packet_tag(Tag::Signature);
94
180
    // git uses SHA1 which is insecure in the standard policy
95
180
    p.accept_hash(sequoia_openpgp::types::HashAlgorithm::SHA1);
96
180
    p.accept_asymmetric_algo(AsymmetricAlgorithm::DSA1024);
97
180
    let builder = DetachedVerifierBuilder::from_bytes(&signature.signature)?;
98
180
    let h = Helper {
99
180
        certs,
100
180
        signature: None,
101
180
        used_certs: Vec::new(),
102
180
        errors: Vec::new(),
103
180
    };
104
180
    let mut v = builder.with_policy(&p, None, h)?;
105
180
    v.verify_bytes(&signature.payload)?;
106
180
    let helper = v.into_helper();
107
180
    ValidatedSignature::from_helper(helper)
108
180
}
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
180
    fn get_certs(&mut self, ids: &[KeyHandle]) -> sequoia_openpgp::Result<Vec<Cert>> {
122
180
        self.used_certs.clear();
123
180
        let mut certs = Vec::with_capacity(ids.len());
124
360
        for id in ids {
125
180
            for (pos, cert) in self.certs.iter().enumerate() {
126
60
                if cert.keys().any(|key| key.key().key_handle() == *id) {
127
36
                    self.used_certs.push((id.clone(), pos));
128
36
                    certs.push(cert.to_owned());
129
36
                }
130
            }
131
        }
132
180
        Ok(certs)
133
180
        // when debugging certs that cannot be found use the next line
134
180
        // Ok(self.certs.to_vec())
135
180
    }
136
    /// This method will be called at most once per message.
137
180
    fn check(&mut self, structure: MessageStructure) -> sequoia_openpgp::Result<()> {
138
        // in detached signatures, there is only one layer that is a
139
        // SignatureGroup
140
180
        for layer in structure {
141
180
            if let MessageLayer::SignatureGroup { results } = layer {
142
180
                self.errors.clear();
143
360
                for result in results {
144
180
                    if self.signature.is_some() {
145
                        return Err(anyhow!("Multiple signatures found."));
146
180
                    }
147
180
                    self.signature = Some(check_result(&mut self.errors, result));
148
                }
149
180
                return Ok(());
150
            }
151
        }
152
        panic!("MessageStructure should only have one layer with a SignatureGroup when `check` is called for a detached signature.");
153
180
    }
154
}
155

            
156
// Check the result of a signature match and place any errors into
157
// `result`.
158
180
fn check_result(
159
180
    errors: &mut Vec<anyhow::Error>,
160
180
    result: std::result::Result<GoodChecksum, VerificationError>,
161
180
) -> Signature {
162
180
    match result {
163
24
        Ok(checksum) => checksum.sig.clone(),
164
156
        Err(e) => match e {
165
            VerificationError::MalformedSignature { sig, error } => {
166
                errors.push(error);
167
                sig.clone()
168
            }
169
144
            VerificationError::MissingKey { sig } => {
170
144
                errors.push(MissingKeyError(sig.clone()).into());
171
144
                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
12
            VerificationError::BadSignature { sig, ka: _, error } => {
186
12
                errors.push(error);
187
12
                sig.clone()
188
            }
189
        },
190
    }
191
180
}
192

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

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

            
198
impl MissingKeyError {
199
144
    pub fn missing_key(&self) -> Option<KeyHandle> {
200
144
        self.0.get_issuers().get(0).map(|k| k.to_owned())
201
144
    }
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
}