apt_swarm/
keyring.rs

1use crate::config::Config;
2use crate::errors::*;
3use crate::pgp;
4use crate::pgp::SigningKey;
5use memchr::memchr;
6use sequoia_openpgp::packet::Signature;
7use sequoia_openpgp::types::SignatureType;
8use sequoia_openpgp::{Fingerprint, KeyHandle};
9use serde::{Deserialize, Serialize};
10use std::borrow::Cow;
11use std::collections::BTreeMap;
12
13#[derive(Debug, Clone)]
14pub struct Subkey {
15    pub parent: Fingerprint,
16    pub fingerprint: Fingerprint,
17}
18
19#[derive(Debug, Default, Clone)]
20pub struct Keyring {
21    pub keys: BTreeMap<Fingerprint, pgp::SigningKey>,
22    pub identifiers: BTreeMap<String, Subkey>,
23}
24
25impl Keyring {
26    pub fn load(config: &Config) -> Result<Self> {
27        let mut keyring = Keyring::default();
28        for repository in &config.data.repositories {
29            keyring.add_keyring(repository.keyring.as_bytes())?;
30        }
31        Ok(keyring)
32    }
33
34    pub fn new(keyring: &[u8]) -> Result<Self> {
35        let mut k = Keyring::default();
36        k.add_keyring(keyring)?;
37        Ok(k)
38    }
39
40    pub fn add_keyring(&mut self, keyring: &[u8]) -> Result<()> {
41        let keys = pgp::load(keyring)?;
42        for key in keys {
43            self.register_identifiers(&key);
44            let fingerprint = key.fingerprint.clone();
45            self.keys.insert(fingerprint, key);
46        }
47        Ok(())
48    }
49
50    pub fn register_identifiers(&mut self, key: &SigningKey) {
51        for (id, fp) in &key.key_handles {
52            let id = id.to_string();
53            trace!("Linking identifier for key {:X}: {id:?}", key.fingerprint);
54            self.identifiers.insert(
55                id,
56                Subkey {
57                    parent: key.fingerprint.clone(),
58                    fingerprint: fp.clone(),
59                },
60            );
61        }
62    }
63
64    pub fn all_fingerprints(&self) -> Vec<Fingerprint> {
65        self.keys
66            .values()
67            .flat_map(|k| &k.key_handles)
68            .flat_map(|(k, _)| {
69                if let KeyHandle::Fingerprint(fp) = k {
70                    Some(fp.to_owned())
71                } else {
72                    None
73                }
74            })
75            .collect()
76    }
77
78    pub fn find_signing_key(&self, sig: &Signature) -> Result<(&Fingerprint, &SigningKey)> {
79        for issuer in sig.get_issuers() {
80            debug!("Found issuer in signature packet: {issuer:?}");
81            if let Some(subkey) = self.identifiers.get(&issuer.to_string()) {
82                debug!("Found fingerprint for given issuer: {:?}", subkey.parent);
83                let key = self.keys.get(&subkey.parent).with_context(|| {
84                    anyhow!(
85                        "Failed to get signing key by fingerprint: {:?}",
86                        subkey.parent
87                    )
88                })?;
89                return Ok((&subkey.fingerprint, key));
90            }
91        }
92        bail!("Could not find key for given signature")
93    }
94
95    // TODO: this function normalizes data, this should be taken into account
96    pub fn verify(&self, data: &[u8], sig: &Signature) -> Result<Fingerprint> {
97        let (signer_fp, signing_key) = self.find_signing_key(sig)?;
98
99        let body: Cow<[u8]> = match sig.typ() {
100            SignatureType::Binary => Cow::Borrowed(data),
101            SignatureType::Text => {
102                let mut out = Vec::new();
103
104                let mut bytes = data;
105                while !bytes.is_empty() {
106                    if let Some(idx) = memchr(b'\n', bytes) {
107                        let line = &bytes[..idx];
108                        // TODO: this could be a `\r\n` newline, do we need to check for `\r`?
109                        bytes = &bytes[idx + 1..];
110
111                        out.extend(line);
112                        if !bytes.is_empty() {
113                            out.extend(b"\r\n");
114                        }
115                    } else {
116                        out.extend(bytes);
117                        bytes = &[];
118                    }
119                }
120
121                Cow::Owned(out)
122            }
123            unsupported => bail!("Unsupported signature type: {unsupported:?}"),
124        };
125
126        for key in signing_key.cert.keys() {
127            let key = key.key();
128
129            // TODO: are we sure the issuer fingerprint is always pointing to the right key?
130            let key_fp = key.fingerprint();
131            debug!("Attempting verification with {:X}", key_fp);
132            if key_fp != *signer_fp {
133                debug!("This key was not the issuer, skipping: {:?}", key_fp);
134                continue;
135            }
136
137            sig.clone()
138                .verify_message(key, body)
139                .context("Failed to verify message")?;
140            debug!("Successfully verified signature");
141            return Ok(key_fp);
142        }
143
144        bail!("Signature could not be verified with any of the pgp certificates public keys")
145    }
146
147    pub fn generate_report(&self) -> Result<KeyringReport> {
148        Ok(KeyringReport {
149            keys: self.keys.values().map(KeyReport::generate).collect(),
150        })
151    }
152}
153
154#[derive(Debug, PartialEq, Serialize, Deserialize)]
155pub struct KeyringReport {
156    pub keys: Vec<KeyReport>,
157}
158
159#[derive(Debug, PartialEq, Serialize, Deserialize)]
160pub struct KeyReport {
161    pub primary_fingerprint: String,
162    pub uids: Vec<String>,
163    pub subkeys: Vec<SubkeyReport>,
164}
165
166impl KeyReport {
167    pub fn generate(key: &pgp::SigningKey) -> Self {
168        KeyReport {
169            primary_fingerprint: format!("{:X}", key.fingerprint),
170            uids: key.uids.clone(),
171            subkeys: key.subkeys.iter().map(SubkeyReport::generate).collect(),
172        }
173    }
174}
175
176#[derive(Debug, PartialEq, Serialize, Deserialize)]
177pub struct SubkeyReport {
178    pub fingerprint: String,
179    pub is_primary: bool,
180    pub for_authentication: bool,
181    pub for_certification: bool,
182    pub for_signing: bool,
183    pub for_storage_encryption: bool,
184    pub for_transport_encryption: bool,
185}
186
187impl SubkeyReport {
188    pub fn generate(key: &pgp::Subkey) -> Self {
189        SubkeyReport {
190            fingerprint: format!("{:X}", key.fingerprint),
191            is_primary: key.is_primary,
192            for_authentication: key.for_authentication,
193            for_certification: key.for_certification,
194            for_signing: key.for_signing,
195            for_storage_encryption: key.for_storage_encryption,
196            for_transport_encryption: key.for_transport_encryption,
197        }
198    }
199}