Refactor online certificate lookup
- Allow searching for fingerprints - needed for the updater mechanism.
This commit is contained in:
+84
-37
@@ -96,12 +96,33 @@ const USER_AGENT: &'static str = concat!("husk/", env!("CARGO_PKG_VERSION"));
|
||||
const CONNECT_TIMEOUT: Duration = Duration::new(5, 0);
|
||||
const REQUEST_TIMEOUT: Duration = Duration::new(5, 0);
|
||||
|
||||
/// Try to fetch certificates for `email` from online sources (keyservers, wkd,
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OnlineQuery {
|
||||
Fingerprint(Fingerprint),
|
||||
Email(String),
|
||||
}
|
||||
|
||||
/// Try to fetch certificates for email from online sources (keyservers, wkd,
|
||||
/// dane).
|
||||
pub async fn lookup_certificates<'hc>(context: &HuskContext<'hc>, email: &str)
|
||||
pub async fn lookup_email<'hc>(context: &HuskContext<'hc>, email: &str)
|
||||
-> anyhow::Result<Vec<Cert>> {
|
||||
query_online_sources(context, OnlineQuery::Email(email.into())).await
|
||||
}
|
||||
|
||||
/// Try to fetch certificates for fingerprint from keyservers. wkd
|
||||
/// and dane can only be queried for email addresses.
|
||||
pub async fn lookup_fingerprint<'hc>(context: &HuskContext<'hc>, fpr: &str)
|
||||
-> anyhow::Result<Vec<Cert>> {
|
||||
let fpr = fpr.parse::<Fingerprint>()?;
|
||||
query_online_sources(context, OnlineQuery::Fingerprint(fpr)).await
|
||||
}
|
||||
|
||||
/// Try to fetch certificates for query from online sources (keyservers, wkd,
|
||||
/// dane).
|
||||
async fn query_online_sources<'hc>(context: &HuskContext<'hc>, query: OnlineQuery)
|
||||
-> anyhow::Result<Vec<Cert>> {
|
||||
|
||||
log::debug!("remote lookup for {}", email);
|
||||
log::debug!("remote lookup for {:?}", query);
|
||||
|
||||
let mut jobs = JoinSet::new();
|
||||
let http_client = sequoia_net::reqwest::Client::builder()
|
||||
@@ -111,25 +132,32 @@ pub async fn lookup_certificates<'hc>(context: &HuskContext<'hc>, email: &str)
|
||||
.build()?;
|
||||
|
||||
// local function to check certificates for usability
|
||||
let collect_certs = |certs: Vec<Result<Cert, anyhow::Error>>, email: String, policy: &StandardPolicy| {
|
||||
let collect_certs = |certs: Vec<Result<Cert, anyhow::Error>>, query: OnlineQuery, policy: &StandardPolicy| {
|
||||
let mut result = Vec::new();
|
||||
let mut seen: HashSet<Fingerprint> = HashSet::new();
|
||||
|
||||
for cert in certs.into_iter().flatten() {
|
||||
if let Ok(vc) = cert.with_policy(policy, None) {
|
||||
|
||||
// check if the returned certificate contains a userid with the
|
||||
// email address in question.
|
||||
let mut userid_found = false;
|
||||
for userid in vc.userids() {
|
||||
if let Ok(Some(u)) = userid.userid().email() {
|
||||
if u == email {
|
||||
userid_found = true;
|
||||
let mut found = false;
|
||||
match query.clone() {
|
||||
OnlineQuery::Email(email) => {
|
||||
// check if the returned certificate contains a userid with the
|
||||
// email address in question.
|
||||
for userid in vc.userids() {
|
||||
if let Ok(Some(u)) = userid.userid().email() {
|
||||
if u == email {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
OnlineQuery::Fingerprint(_) => {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if userid_found {
|
||||
if found {
|
||||
// check if the certificate can be used (for encryption).
|
||||
vc.keys()
|
||||
.supported()
|
||||
@@ -156,43 +184,62 @@ pub async fn lookup_certificates<'hc>(context: &HuskContext<'hc>, email: &str)
|
||||
|
||||
if let Ok(ks) = KeyServer::with_client(keyserver.as_str(), http_client.clone()) {
|
||||
|
||||
let e = email.to_string();
|
||||
let p = StandardPolicy::new();
|
||||
let query = query.clone();
|
||||
|
||||
jobs.spawn(async move {
|
||||
let mut partial_result = Vec::new();
|
||||
|
||||
if let Ok(certs) = ks.search(e.as_str()).await {
|
||||
partial_result = collect_certs(certs, e, &p);
|
||||
let q = query.clone();
|
||||
match query {
|
||||
OnlineQuery::Email(e) => {
|
||||
if let Ok(certs) = ks.search(e.as_str()).await {
|
||||
partial_result = collect_certs(certs, q, &p);
|
||||
}
|
||||
partial_result
|
||||
},
|
||||
OnlineQuery::Fingerprint(f) => {
|
||||
if let Ok(certs) = ks.get(f).await {
|
||||
partial_result = collect_certs(certs, q, &p);
|
||||
}
|
||||
partial_result
|
||||
},
|
||||
}
|
||||
partial_result
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// WKD
|
||||
let e = email.to_string();
|
||||
let p = StandardPolicy::new();
|
||||
jobs.spawn(async move {
|
||||
let mut partial_result = Vec::new();
|
||||
// WKD and DANE need an email address
|
||||
if let OnlineQuery::Email(_) = query {
|
||||
let p = StandardPolicy::new();
|
||||
let q = query.clone();
|
||||
|
||||
if let Ok(certs) = wkd::get(&http_client.clone(), e.as_str()).await {
|
||||
partial_result = collect_certs(certs, e, &p);
|
||||
}
|
||||
partial_result
|
||||
});
|
||||
// WKD
|
||||
jobs.spawn(async move {
|
||||
let mut partial_result = Vec::new();
|
||||
|
||||
// DANE
|
||||
let e = email.to_string();
|
||||
let p = StandardPolicy::new();
|
||||
jobs.spawn(async move {
|
||||
let mut partial_result = Vec::new();
|
||||
if let OnlineQuery::Email(e) = q.clone() {
|
||||
if let Ok(certs) = wkd::get(&http_client.clone(), e.as_str()).await {
|
||||
partial_result = collect_certs(certs, q, &p);
|
||||
}
|
||||
}
|
||||
partial_result
|
||||
});
|
||||
|
||||
if let Ok(certs) = dane::get(e.as_str()).await {
|
||||
partial_result = collect_certs(certs, e, &p);
|
||||
}
|
||||
partial_result
|
||||
});
|
||||
// DANE
|
||||
let p = StandardPolicy::new();
|
||||
let q = query.clone();
|
||||
jobs.spawn(async move {
|
||||
let mut partial_result = Vec::new();
|
||||
|
||||
if let OnlineQuery::Email(e) = q {
|
||||
if let Ok(certs) = dane::get(e.as_str()).await {
|
||||
partial_result = collect_certs(certs, query, &p);
|
||||
}
|
||||
}
|
||||
partial_result
|
||||
});
|
||||
}
|
||||
|
||||
// join the partial results. As the same certificate might be returned by
|
||||
// several sources, dedup and merge the certificates.
|
||||
@@ -240,7 +287,7 @@ pub async fn get_certificates<'hc>(context: &HuskContext<'hc>, email: &str)
|
||||
introducer.cert.self_signed_userids().map(|u| u.userid()).collect::<Vec<_>>());
|
||||
if introducer.can_introduce(format!("<{}>", email).as_str()) {
|
||||
log::debug!(" introducer regex matches!");
|
||||
match lookup_certificates(context, email).await {
|
||||
match lookup_email(context, email).await {
|
||||
Ok(certs) => {
|
||||
for cert in certs {
|
||||
if introducer.is_introducing(&cert) {
|
||||
|
||||
Reference in New Issue
Block a user