sequoia_cert_store/store/
keyserver.rs1use 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
29pub const KEYS_OPENPGP_ORG_URL: &str = "hkps://keys.openpgp.org";
32pub const SKS_URL: &str = "hkps://keyserver.ubuntu.com";
34pub const MAILVELOPE_URL: &str = "hkps://keys.mailvelope.com";
36pub const PROTON_URL: &str = "hkps://api.protonmail.ch";
38
39pub 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 hits_fpr: BTreeMap<Fingerprint, Arc<LazyCert<'a>>>,
57 hits_keyid: BTreeMap<KeyID, Fingerprint>,
58 misses_fpr: BTreeSet<Fingerprint>,
60 misses_keyid: BTreeSet<KeyID>,
61}
62
63impl KeyServer<'_> {
64 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 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 pub fn keys_openpgp_org() -> Result<Self> {
87 Self::new(KEYS_OPENPGP_ORG_URL)
88 }
89
90 pub fn sks() -> Result<Self> {
93 Self::new(SKS_URL)
94 }
95
96 pub fn mailvelope() -> Result<Self> {
99 Self::new(MAILVELOPE_URL)
100 }
101
102 pub fn proton() -> Result<Self> {
104 Self::new(PROTON_URL)
105 }
106
107 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 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 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 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 self.hits_keyid.remove(&keyid);
166 }
167 }
168}
169
170impl<'a> KeyServer<'a> {
171 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
182macro_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 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 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 let rt = tokio::runtime::Runtime::new().unwrap();
240
241 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 inner.cache(cert);
259 }
260
261 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 let ks = inner.keyserver.search(pattern);
333
334 let wkd = async {
336 if let Some(email) = email.as_ref() {
337 net::wkd::get(&reqwest::Client::new(), email).await
338 } else {
339 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 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 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 certs.iter().for_each(|cert| inner.cache(cert));
397
398 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 Box::new(self
446 .inner.lock().unwrap()
447 .hits_fpr
448 .keys()
449 .cloned()
450 .collect::<Vec<_>>()
451 .into_iter())
452 }
453}
454
455#[non_exhaustive]
457#[derive(thiserror::Error, Debug)]
458pub enum KeyServerError {
459 #[error("Keyserver returned the wrong certs: {1:?} (wanted: {0})")]
461 UnexpectedResults(KeyHandle, Vec<Fingerprint>),
462}