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
2
#[derive(Serialize)]
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
}
61

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

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

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

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

            
193
#[derive(Debug)]
194
pub struct SignatureErrors(pub Vec<anyhow::Error>);
195

            
196
impl std::error::Error for SignatureErrors {
197
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
198
        self.0.first().and_then(|e| e.source())
199
    }
200
}
201

            
202
impl std::fmt::Display for SignatureErrors {
203
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
204
        for item in &self.0 {
205
            item.fmt(f)?;
206
        }
207
        Ok(())
208
    }
209
}
210

            
211
#[derive(Debug)]
212
pub struct MissingKeyError(Signature);
213

            
214
impl std::error::Error for MissingKeyError {}
215

            
216
impl MissingKeyError {
217
    pub fn missing_key(&self) -> Option<KeyHandle> {
218
        self.0.get_issuers().get(0).map(|k| k.to_owned())
219
    }
220
}
221

            
222
impl std::fmt::Display for MissingKeyError {
223
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224
        if let Some(issuer) = self.0.get_issuers().get(0) {
225
            write!(f, "Missing key: {}", issuer)
226
        } else {
227
            write!(f, "Missing key")
228
        }
229
    }
230
}