sequoia_cert_store/store/
keyserver.rs

1use std::collections::BTreeMap;
2use std::collections::BTreeSet;
3use std::sync::Arc;
4use std::sync::Mutex;
5
6use sequoia_openpgp as openpgp;
7use openpgp::Cert;
8use openpgp::Fingerprint;
9use openpgp::KeyHandle;
10use openpgp::KeyID;
11use openpgp::Result;
12use openpgp::policy::NullPolicy;
13
14use sequoia_net as net;
15use net::reqwest;
16
17use crate::email_to_userid;
18use crate::LazyCert;
19use crate::Store;
20use crate::store::StatusListener;
21use crate::store::StatusUpdate;
22use crate::store::StoreError;
23use crate::store::UserIDQueryParams;
24
25use super::TRACE;
26
27const NP: &NullPolicy = unsafe { &NullPolicy::new() };
28
29// Reliable keyservers.
30/// keys.openpgp.org.
31pub const KEYS_OPENPGP_ORG_URL: &str = "hkps://keys.openpgp.org";
32/// A reliable SKS keyserver.
33pub const SKS_URL: &str = "hkps://keyserver.ubuntu.com";
34/// mailvelope's keyserver.
35pub const MAILVELOPE_URL: &str = "hkps://keys.mailvelope.com";
36/// proton's keyserver.
37pub const PROTON_URL: &str = "hkps://api.protonmail.ch";
38
39/// A keyserver backend.
40pub struct KeyServer<'a> {
41    inner: Mutex<KeyServerInner<'a>>,
42}
43assert_send_and_sync!(KeyServer<'_>);
44
45struct KeyServerInner<'a> {
46    keyserver: net::KeyServer,
47
48    id: String,
49    tx: usize,
50    listeners: Vec<Box<dyn StatusListener + Send + Sync>>,
51
52    // A cache.  We only cache certificates; we don't cache User ID
53    // searches.
54
55    // Primary keys and subkeys.
56    hits_fpr: BTreeMap<Fingerprint, Arc<LazyCert<'a>>>,
57    hits_keyid: BTreeMap<KeyID, Fingerprint>,
58    // What we failed to look up.
59    misses_fpr: BTreeSet<Fingerprint>,
60    misses_keyid: BTreeSet<KeyID>,
61}
62
63impl KeyServer<'_> {
64    /// Returns a new key server instance.
65    pub fn new(url: &str) -> Result<Self> {
66        Ok(Self {
67            inner: Mutex::new(KeyServerInner {
68                keyserver: net::KeyServer::new(url)?,
69                id: if url.len() <= 10 {
70                    // Only prefix "key server" if the URL is short.
71                    format!("key server {}", url)
72                } else {
73                    url.to_string()
74                },
75                tx: 0,
76                listeners: Vec::new(),
77                hits_fpr: Default::default(),
78                hits_keyid: Default::default(),
79                misses_fpr: Default::default(),
80                misses_keyid: Default::default(),
81            }),
82        })
83    }
84
85    /// Returns a key server instance that uses `keys.openpgp.org`.
86    pub fn keys_openpgp_org() -> Result<Self> {
87        Self::new(KEYS_OPENPGP_ORG_URL)
88    }
89
90    /// Returns a key server instance that uses a reliable SKS
91    /// keyserver.
92    pub fn sks() -> Result<Self> {
93        Self::new(SKS_URL)
94    }
95
96    /// Returns a key server instance that uses mailvelope's
97    /// keyserver.
98    pub fn mailvelope() -> Result<Self> {
99        Self::new(MAILVELOPE_URL)
100    }
101
102    /// Returns a key server instance that uses proton's keyserver.
103    pub fn proton() -> Result<Self> {
104        Self::new(PROTON_URL)
105    }
106
107    /// Sends status updates to the listener.
108    pub fn add_listener(&mut self, listener: Box<dyn StatusListener + Send + Sync>) {
109        self.inner.lock().unwrap().listeners.push(listener);
110    }
111}
112
113impl<'a> KeyServerInner<'a> {
114    // Looks for a certificate in the cache.
115    fn check_cache(&self, kh: &KeyHandle)
116        -> Option<Result<Vec<Arc<LazyCert<'a>>>>>
117    {
118        let kh_;
119        let kh = if let KeyHandle::KeyID(keyid) = kh {
120            if let Some(fpr) = self.hits_keyid.get(keyid) {
121                kh_ = KeyHandle::Fingerprint(fpr.clone());
122                &kh_
123            } else if self.misses_keyid.get(keyid).is_some() {
124                return Some(Err(StoreError::NotFound(
125                    KeyHandle::from(kh.clone())).into()));
126            } else {
127                kh
128            }
129        } else {
130            kh
131        };
132        if let KeyHandle::Fingerprint(fpr) = kh {
133            if let Some(cert) = self.hits_fpr.get(fpr) {
134                return Some(Ok(vec![ cert.clone() ]));
135            }
136            if self.misses_fpr.get(fpr).is_some() {
137                return Some(Err(StoreError::NotFound(
138                    KeyHandle::from(kh.clone())).into()));
139            }
140        }
141
142        None
143    }
144
145    // Adds the cert to the in-memory cache.
146    fn cache(&mut self, cert: &Arc<LazyCert<'a>>) {
147        for k in cert.keys() {
148            self.hits_fpr.insert(k.fingerprint(), cert.clone());
149            self.hits_keyid.insert(k.keyid(), k.fingerprint());
150        }
151    }
152
153    /// Deletes the key with the given fingerprint from the cache.
154    fn delete_from_cache(&mut self, cert: &LazyCert<'_>) {
155        for key in cert.keys() {
156            let fp = key.fingerprint();
157            self.hits_fpr.remove(&fp);
158
159            let keyid = KeyID::from(fp);
160            // XXX: Technically, a key ID could map to multiple
161            // fingerprints, so we may be removing the wrong entry here.
162            // But, by using a BTreeMap<KeyID, Fingerprint> we do accept
163            // this kind of loss in the first place, as later cache
164            // insertions will overwrite any existing mappings.
165            self.hits_keyid.remove(&keyid);
166        }
167    }
168}
169
170impl<'a> KeyServer<'a> {
171    /// Deletes the key with the given fingerprint from the cache.
172    pub(crate) fn delete_from_cache<I>(&self, certs: I)
173        where I: Iterator<Item=Arc<LazyCert<'a>>>,
174    {
175        let mut inner = self.inner.lock().unwrap();
176        for cert in certs {
177            inner.delete_from_cache(&cert);
178        }
179    }
180}
181
182/// Sends a status update.
183macro_rules! update {
184    ( $self:expr, $update:ident, $($args:expr),* ) => {{
185        if ! $self.listeners.is_empty() {
186            let update = StatusUpdate::$update(
187                $self.tx, &$self.id, $($args),*);
188
189            for sub in $self.listeners.iter() {
190                sub.update(&update);
191            }
192        }
193    }}
194}
195
196impl<'a> Store<'a> for KeyServer<'a> {
197    fn lookup_by_cert(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
198        let mut certs = self.lookup_by_cert_or_subkey(kh)?;
199
200        // The match may be on a subkey.  Only return the certificates
201        // whose primary key aliases kh.
202        certs.retain(|cert| {
203            kh.aliases(KeyHandle::from(cert.fingerprint()))
204        });
205
206        if certs.is_empty() {
207            Err(StoreError::NotFound(KeyHandle::from(kh.clone())).into())
208        } else {
209            Ok(certs)
210        }
211    }
212
213    fn lookup_by_cert_or_subkey(&self, kh: &KeyHandle) -> Result<Vec<Arc<LazyCert<'a>>>> {
214        tracer!(TRACE, "KeyServer::lookup_by_cert_or_subkey");
215
216        // Check the cache.
217        t!("Looking up {} on keyserver...", kh);
218        let mut inner = self.inner.lock().unwrap();
219        if ! inner.listeners.is_empty() {
220            inner.tx += 1;
221            update!(inner, LookupStarted, kh, None);
222        };
223
224        if let Some(r) = inner.check_cache(kh) {
225            t!("Found in in-memory cache");
226            match r.as_ref() {
227                Ok(certs) => {
228                    update!(inner, LookupFinished, kh,
229                            &certs[..], Some("Found in in-memory cache"));
230                }
231                Err(err) => {
232                    update!(inner, LookupFailed, kh, Some(&err));
233                }
234            }
235            return r;
236        }
237
238        // It's not in the cache, look it up on the key server.
239        let rt = tokio::runtime::Runtime::new().unwrap();
240
241        // The keyserver interface currently only returns a single
242        // result.
243        let r = rt.block_on(async {
244            inner.keyserver.get(kh.clone()).await
245        });
246
247        match r {
248            Ok(certs) => {
249                let certs: Vec<_> =
250                    certs.into_iter()
251                    .filter_map(Result::ok)
252                    .map(|c| Arc::new(LazyCert::from(c))).collect();
253
254                for cert in &certs {
255                    t!("keyserver returned {}", cert.fingerprint());
256
257                    // Add the result to the cache.
258                    inner.cache(cert);
259                }
260
261                // Make sure the key server gave us the right
262                // certificate.
263                let requested: Vec<_> =
264                    certs.iter().filter(
265                        |cert| cert.keys().any(|k| k.key_handle().aliases(kh)))
266                    .cloned()
267                    .collect();
268
269                if ! requested.is_empty() {
270                    update!(inner, LookupFinished, kh, &requested[..], None);
271                    Ok(requested)
272                } else {
273                    let err = KeyServerError::UnexpectedResults(
274                        kh.clone(),
275                        certs.iter().map(|c| c.fingerprint()).collect())
276                        .into();
277                    t!("{}", err);
278                    update!(inner, LookupFailed, kh, Some(&err));
279                    Err(StoreError::NotFound(
280                        KeyHandle::from(kh.clone())).into())
281                }
282            }
283            Err(err) => {
284                t!("keyserver returned an error: {}", err);
285
286                if let Some(net::Error::NotFound)
287                    = err.downcast_ref::<net::Error>()
288                {
289                    update!(inner, LookupFailed, kh, None);
290                    Err(StoreError::NotFound(
291                        KeyHandle::from(kh.clone())).into())
292                } else {
293                    update!(inner, LookupFailed, kh, Some(&err));
294                    Err(err)
295                }
296            }
297        }
298    }
299
300    fn select_userid(&self, query: &UserIDQueryParams, pattern: &str)
301        -> Result<Vec<Arc<LazyCert<'a>>>>
302    {
303        tracer!(TRACE, "KeyServer::select_userid");
304
305        t!("{}", pattern);
306        t!("Looking {:?} up on the keyserver... ", pattern);
307        let mut inner = self.inner.lock().unwrap();
308        if ! inner.listeners.is_empty() {
309            inner.tx += 1;
310            update!(inner, SearchStarted, pattern, None);
311        }
312
313        let email_;
314        let email = if query.email && query.anchor_start && query.anchor_end {
315            match email_to_userid(pattern) {
316                Ok(email) => {
317                    email_ = email.value().to_vec();
318                    Some(String::from_utf8_lossy(&email_))
319                },
320                Err(err) => {
321                    t!("{:?}: invalid email address: {}", pattern, err);
322                    None
323                }
324            }
325        } else {
326            None
327        };
328
329        let rt = tokio::runtime::Runtime::new().unwrap();
330        let (ks, wkd) = rt.block_on(async {
331            // Query the keyserver.
332            let ks = inner.keyserver.search(pattern);
333
334            // And the WKD (if it appears to be an email address).
335            let wkd = async {
336                if let Some(email) = email.as_ref() {
337                    net::wkd::get(&reqwest::Client::new(), email).await
338                } else {
339                    // If it is not an email, it's not an error.
340                    Ok(Vec::new())
341                }
342            };
343
344            tokio::join!(ks, wkd)
345        });
346
347        let mut certs: Vec<Result<Cert>> = Vec::new();
348        match ks {
349            Ok(c) => {
350                t!("Key server returned {} results", c.len());
351                if ! inner.listeners.is_empty() {
352                    let msg = format!("Key server returned {} results", c.len());
353                    update!(inner, SearchStatus, pattern, &msg);
354                }
355                certs.extend(c);
356            },
357            Err(err) => t!("Key server response: {}", err),
358        }
359        match wkd {
360            Ok(c) => {
361                t!("WKD server returned {} results", c.len());
362                if ! inner.listeners.is_empty() {
363                    let msg = format!("WKD server returned {} results",
364                                      c.len());
365                    update!(inner, SearchStatus, pattern, &msg);
366                }
367                certs.extend(c);
368            },
369            Err(err) => t!("WKD server response: {}", err),
370        }
371
372        // Sort, merge, and cache.
373        let mut certs = certs.into_iter().flatten().collect::<Vec<Cert>>();
374        certs.sort_by_key(|c| c.fingerprint());
375        certs.dedup_by(|a, b| {
376            if a.fingerprint() != b.fingerprint() {
377                return false;
378            }
379
380            // b is kept.  So merge into b.
381            match b.clone().merge_public(a.clone()) {
382                Ok(combined) => *b = combined,
383                Err(err) => {
384                    t!("Merging copies of {}: {}",
385                       a.keyid(), err);
386                }
387            }
388
389            true
390        });
391
392        let mut certs: Vec<_> =
393            certs.into_iter().map(|c| Arc::new(LazyCert::from(c))).collect();
394
395        // Add the results to the cache.
396        certs.iter().for_each(|cert| inner.cache(cert));
397
398        // Only keep the certificates that actually satisfy the
399        // constraints.
400        certs.retain(|cert| {
401            let cert = cert.to_cert()
402                .expect("LazyCert should already be canonicalized");
403            query.check_cert(cert, pattern)
404        });
405
406        if certs.is_empty() {
407            update!(inner, SearchFailed, pattern, None);
408            Err(StoreError::NoMatches(pattern.to_string()).into())
409        } else {
410            if TRACE || ! inner.listeners.is_empty() {
411                let msg = format!(
412                    "Got {} results:\n  {}",
413                    certs.len(),
414                    certs.iter()
415                        .map(|cert: &Arc<LazyCert>| {
416                            format!(
417                                "{} ({})",
418                                cert.keyid().to_hex(),
419                                cert.with_policy(NP, None)
420                                    .and_then(|vc| vc.primary_userid())
421                                    .map(|ua| {
422                                        String::from_utf8_lossy(ua.userid().value())
423                                            .into_owned()
424                                    })
425                                    .unwrap_or_else(|_| {
426                                        cert.userids().next()
427                                            .map(|userid| {
428                                                String::from_utf8_lossy(userid.value())
429                                                    .into_owned()
430                                            })
431                                            .unwrap_or("<unknown>".into())
432                                    }))
433                        })
434                        .collect::<Vec<_>>()
435                        .join("\n  "));
436                t!("{}", msg);
437                update!(inner, SearchFinished, pattern, &certs[..], Some(&msg));
438            }
439            Ok(certs)
440        }
441    }
442
443    fn fingerprints<'b>(&'b self) -> Box<dyn Iterator<Item=Fingerprint> + 'b> {
444        // Return the entries in our cache.
445        Box::new(self
446                 .inner.lock().unwrap()
447                 .hits_fpr
448                 .keys()
449                 .cloned()
450                 .collect::<Vec<_>>()
451                 .into_iter())
452    }
453}
454
455/// [`KeyServer`] specific error codes.
456#[non_exhaustive]
457#[derive(thiserror::Error, Debug)]
458pub enum KeyServerError {
459    /// Keyserver returned the wrong certs.
460    #[error("Keyserver returned the wrong certs: {1:?} (wanted: {0})")]
461    UnexpectedResults(KeyHandle, Vec<Fingerprint>),
462}