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 CONNECT_TIMEOUT: Duration = Duration::new(5, 0);
|
||||||
const REQUEST_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).
|
/// 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>> {
|
-> anyhow::Result<Vec<Cert>> {
|
||||||
|
|
||||||
log::debug!("remote lookup for {}", email);
|
log::debug!("remote lookup for {:?}", query);
|
||||||
|
|
||||||
let mut jobs = JoinSet::new();
|
let mut jobs = JoinSet::new();
|
||||||
let http_client = sequoia_net::reqwest::Client::builder()
|
let http_client = sequoia_net::reqwest::Client::builder()
|
||||||
@@ -111,25 +132,32 @@ pub async fn lookup_certificates<'hc>(context: &HuskContext<'hc>, email: &str)
|
|||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
// local function to check certificates for usability
|
// 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 result = Vec::new();
|
||||||
let mut seen: HashSet<Fingerprint> = HashSet::new();
|
let mut seen: HashSet<Fingerprint> = HashSet::new();
|
||||||
|
|
||||||
for cert in certs.into_iter().flatten() {
|
for cert in certs.into_iter().flatten() {
|
||||||
if let Ok(vc) = cert.with_policy(policy, None) {
|
if let Ok(vc) = cert.with_policy(policy, None) {
|
||||||
|
|
||||||
// check if the returned certificate contains a userid with the
|
let mut found = false;
|
||||||
// email address in question.
|
match query.clone() {
|
||||||
let mut userid_found = false;
|
OnlineQuery::Email(email) => {
|
||||||
for userid in vc.userids() {
|
// check if the returned certificate contains a userid with the
|
||||||
if let Ok(Some(u)) = userid.userid().email() {
|
// email address in question.
|
||||||
if u == email {
|
for userid in vc.userids() {
|
||||||
userid_found = true;
|
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).
|
// check if the certificate can be used (for encryption).
|
||||||
vc.keys()
|
vc.keys()
|
||||||
.supported()
|
.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()) {
|
if let Ok(ks) = KeyServer::with_client(keyserver.as_str(), http_client.clone()) {
|
||||||
|
|
||||||
let e = email.to_string();
|
|
||||||
let p = StandardPolicy::new();
|
let p = StandardPolicy::new();
|
||||||
|
let query = query.clone();
|
||||||
|
|
||||||
jobs.spawn(async move {
|
jobs.spawn(async move {
|
||||||
let mut partial_result = Vec::new();
|
let mut partial_result = Vec::new();
|
||||||
|
|
||||||
if let Ok(certs) = ks.search(e.as_str()).await {
|
let q = query.clone();
|
||||||
partial_result = collect_certs(certs, e, &p);
|
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
|
// WKD and DANE need an email address
|
||||||
let e = email.to_string();
|
if let OnlineQuery::Email(_) = query {
|
||||||
let p = StandardPolicy::new();
|
let p = StandardPolicy::new();
|
||||||
jobs.spawn(async move {
|
let q = query.clone();
|
||||||
let mut partial_result = Vec::new();
|
|
||||||
|
|
||||||
if let Ok(certs) = wkd::get(&http_client.clone(), e.as_str()).await {
|
// WKD
|
||||||
partial_result = collect_certs(certs, e, &p);
|
jobs.spawn(async move {
|
||||||
}
|
let mut partial_result = Vec::new();
|
||||||
partial_result
|
|
||||||
});
|
|
||||||
|
|
||||||
// DANE
|
if let OnlineQuery::Email(e) = q.clone() {
|
||||||
let e = email.to_string();
|
if let Ok(certs) = wkd::get(&http_client.clone(), e.as_str()).await {
|
||||||
let p = StandardPolicy::new();
|
partial_result = collect_certs(certs, q, &p);
|
||||||
jobs.spawn(async move {
|
}
|
||||||
let mut partial_result = Vec::new();
|
}
|
||||||
|
partial_result
|
||||||
|
});
|
||||||
|
|
||||||
if let Ok(certs) = dane::get(e.as_str()).await {
|
// DANE
|
||||||
partial_result = collect_certs(certs, e, &p);
|
let p = StandardPolicy::new();
|
||||||
}
|
let q = query.clone();
|
||||||
partial_result
|
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
|
// join the partial results. As the same certificate might be returned by
|
||||||
// several sources, dedup and merge the certificates.
|
// 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<_>>());
|
introducer.cert.self_signed_userids().map(|u| u.userid()).collect::<Vec<_>>());
|
||||||
if introducer.can_introduce(format!("<{}>", email).as_str()) {
|
if introducer.can_introduce(format!("<{}>", email).as_str()) {
|
||||||
log::debug!(" introducer regex matches!");
|
log::debug!(" introducer regex matches!");
|
||||||
match lookup_certificates(context, email).await {
|
match lookup_email(context, email).await {
|
||||||
Ok(certs) => {
|
Ok(certs) => {
|
||||||
for cert in certs {
|
for cert in certs {
|
||||||
if introducer.is_introducing(&cert) {
|
if introducer.is_introducing(&cert) {
|
||||||
|
|||||||
Reference in New Issue
Block a user