wkd: add support
This commit is contained in:
parent
2e1956fcfa
commit
deb3a0373b
|
@ -663,7 +663,7 @@ dependencies = [
|
|||
"fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -675,6 +675,7 @@ dependencies = [
|
|||
"time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zbase32 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -689,7 +690,7 @@ dependencies = [
|
|||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"indicatif 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"multipart 0.16.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pathdiff 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2360,6 +2361,11 @@ name = "yansi"
|
|||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "zbase32"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
|
@ -2630,3 +2636,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
"checksum yansi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d60c3b48c9cdec42fb06b3b84b5b087405e1fa1c644a1af3930e4dfafe93de48"
|
||||
"checksum yansi 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9fc79f4a1e39857fc00c3f662cbf2651c771f00e9c15fe2abc341806bd46bd71"
|
||||
"checksum zbase32 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9079049688da5871a7558ddacb7f04958862c703e68258594cb7a862b5e33f"
|
||||
|
|
|
@ -22,6 +22,7 @@ idna = "0.1"
|
|||
fs2 = "0.4"
|
||||
walkdir = "2.2"
|
||||
chrono = "0.4"
|
||||
zbase32 = "0.1.2"
|
||||
|
||||
[lib]
|
||||
name = "hagrid_database"
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
|
|||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
use tempfile;
|
||||
use url;
|
||||
use url::form_urlencoded;
|
||||
use pathdiff::diff_paths;
|
||||
use std::time::SystemTime;
|
||||
|
||||
|
@ -17,6 +17,8 @@ use types::{Email, Fingerprint, KeyID};
|
|||
use sync::FlockMutexGuard;
|
||||
use Result;
|
||||
|
||||
use wkd;
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use openpgp::Cert;
|
||||
|
@ -29,10 +31,12 @@ pub struct Filesystem {
|
|||
keys_dir_full: PathBuf,
|
||||
keys_dir_quarantined: PathBuf,
|
||||
keys_dir_published: PathBuf,
|
||||
keys_dir_published_wkd: PathBuf,
|
||||
keys_dir_log: PathBuf,
|
||||
|
||||
links_dir_by_fingerprint: PathBuf,
|
||||
links_dir_by_keyid: PathBuf,
|
||||
links_dir_wkd_by_email: PathBuf,
|
||||
links_dir_by_email: PathBuf,
|
||||
|
||||
dry_run: bool,
|
||||
|
@ -82,18 +86,22 @@ impl Filesystem {
|
|||
let keys_dir_quarantined = keys_internal_dir.join("quarantined");
|
||||
let keys_dir_log = keys_internal_dir.join("log");
|
||||
let keys_dir_published = keys_external_dir.join("pub");
|
||||
let keys_dir_published_wkd = keys_external_dir.join("wkd");
|
||||
create_dir_all(&keys_dir_full)?;
|
||||
create_dir_all(&keys_dir_quarantined)?;
|
||||
create_dir_all(&keys_dir_published)?;
|
||||
create_dir_all(&keys_dir_published_wkd)?;
|
||||
create_dir_all(&keys_dir_log)?;
|
||||
|
||||
let links_dir = keys_external_dir.join("links");
|
||||
let links_dir_by_keyid = links_dir.join("by-keyid");
|
||||
let links_dir_by_fingerprint = links_dir.join("by-fpr");
|
||||
let links_dir_by_email = links_dir.join("by-email");
|
||||
let links_dir_wkd_by_email = links_dir.join("wkd");
|
||||
create_dir_all(&links_dir_by_keyid)?;
|
||||
create_dir_all(&links_dir_by_fingerprint)?;
|
||||
create_dir_all(&links_dir_by_email)?;
|
||||
create_dir_all(&links_dir_wkd_by_email)?;
|
||||
|
||||
info!("Opened filesystem database.");
|
||||
info!("keys_internal_dir: '{}'", keys_internal_dir.display());
|
||||
|
@ -106,12 +114,14 @@ impl Filesystem {
|
|||
|
||||
keys_dir_full,
|
||||
keys_dir_published,
|
||||
keys_dir_published_wkd,
|
||||
keys_dir_quarantined,
|
||||
keys_dir_log,
|
||||
|
||||
links_dir_by_keyid,
|
||||
links_dir_by_fingerprint,
|
||||
links_dir_by_email,
|
||||
links_dir_wkd_by_email,
|
||||
|
||||
dry_run,
|
||||
})
|
||||
|
@ -135,6 +145,12 @@ impl Filesystem {
|
|||
self.keys_dir_published.join(path_split(&hex))
|
||||
}
|
||||
|
||||
/// Returns the path to the given Fingerprint.
|
||||
fn fingerprint_to_path_published_wkd(&self, fingerprint: &Fingerprint) -> PathBuf {
|
||||
let hex = fingerprint.to_string();
|
||||
self.keys_dir_published_wkd.join(path_split(&hex))
|
||||
}
|
||||
|
||||
/// Returns the path to the given KeyID.
|
||||
fn link_by_keyid(&self, keyid: &KeyID) -> PathBuf {
|
||||
let hex = keyid.to_string();
|
||||
|
@ -149,12 +165,24 @@ impl Filesystem {
|
|||
|
||||
/// Returns the path to the given Email.
|
||||
fn link_by_email(&self, email: &Email) -> PathBuf {
|
||||
let email =
|
||||
url::form_urlencoded::byte_serialize(email.as_str().as_bytes())
|
||||
let email = form_urlencoded::byte_serialize(email.as_str().as_bytes())
|
||||
.collect::<String>();
|
||||
self.links_dir_by_email.join(path_split(&email))
|
||||
}
|
||||
|
||||
/// Returns the WKD path to the given Email.
|
||||
fn link_wkd_by_email(&self, email: &Email) -> PathBuf {
|
||||
let (encoded_local_part, domain) = wkd::encode_wkd(email.as_str()).unwrap();
|
||||
let encoded_domain = form_urlencoded::byte_serialize(domain.as_bytes())
|
||||
.collect::<PathBuf>();
|
||||
|
||||
[
|
||||
&self.links_dir_wkd_by_email,
|
||||
&encoded_domain,
|
||||
&path_split(&encoded_local_part)
|
||||
].iter().collect()
|
||||
}
|
||||
|
||||
fn read_from_path(&self, path: &Path, allow_internal: bool) -> Option<String> {
|
||||
use std::fs;
|
||||
|
||||
|
@ -170,6 +198,21 @@ impl Filesystem {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_from_path_bytes(&self, path: &Path, allow_internal: bool) -> Option<Vec<u8>> {
|
||||
use std::fs;
|
||||
|
||||
if !path.starts_with(&self.keys_external_dir) &&
|
||||
!(allow_internal && path.starts_with(&self.keys_internal_dir)) {
|
||||
panic!("Attempted to access file outside expected dirs!");
|
||||
}
|
||||
|
||||
if path.exists() {
|
||||
fs::read(path).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the Fingerprint the given path is pointing to.
|
||||
pub fn path_to_fingerprint(path: &Path) -> Option<Fingerprint> {
|
||||
use std::str::FromStr;
|
||||
|
@ -188,7 +231,7 @@ impl Filesystem {
|
|||
fn path_to_email(path: &Path) -> Option<Email> {
|
||||
use std::str::FromStr;
|
||||
let merged = path_merge(path);
|
||||
let decoded = url::form_urlencoded::parse(merged.as_bytes()).next()?.0;
|
||||
let decoded = form_urlencoded::parse(merged.as_bytes()).next()?.0;
|
||||
Email::from_str(&decoded).ok()
|
||||
}
|
||||
|
||||
|
@ -204,6 +247,52 @@ impl Filesystem {
|
|||
}
|
||||
}
|
||||
|
||||
fn link_email_vks(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
||||
let path = self.fingerprint_to_path_published(fpr);
|
||||
let link = self.link_by_email(&email);
|
||||
let target = diff_paths(&path, link.parent().unwrap()).unwrap();
|
||||
|
||||
if link == target {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
symlink(&target, ensure_parent(&link)?)
|
||||
}
|
||||
|
||||
fn link_email_wkd(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
||||
let path = self.fingerprint_to_path_published_wkd(fpr);
|
||||
let link = self.link_wkd_by_email(&email);
|
||||
let target = diff_paths(&path, link.parent().unwrap()).unwrap();
|
||||
|
||||
if link == target {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
symlink(&target, ensure_parent(&link)?)
|
||||
}
|
||||
|
||||
fn unlink_email_vks(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
||||
let link = self.link_by_email(&email);
|
||||
|
||||
let expected = diff_paths(
|
||||
&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()
|
||||
).unwrap();
|
||||
|
||||
symlink_unlink_with_check(&link, &expected)
|
||||
}
|
||||
|
||||
fn unlink_email_wkd(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
||||
let link = self.link_wkd_by_email(&email);
|
||||
|
||||
let expected = diff_paths(
|
||||
&self.fingerprint_to_path_published_wkd(fpr),
|
||||
link.parent().unwrap()
|
||||
).unwrap();
|
||||
|
||||
symlink_unlink_with_check(&link, &expected)
|
||||
}
|
||||
|
||||
fn open_logfile(&self, file_name: &str) -> Result<File> {
|
||||
let file_path = self.keys_dir_log.join(file_name);
|
||||
Ok(OpenOptions::new()
|
||||
|
@ -275,6 +364,16 @@ fn symlink(symlink_content: &Path, symlink_name: &Path) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn symlink_unlink_with_check(link: &Path, expected: &Path) -> Result<()> {
|
||||
if let Ok(target) = read_link(&link) {
|
||||
if target == expected {
|
||||
remove_file(link)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Database for Filesystem {
|
||||
type MutexGuard = FlockMutexGuard;
|
||||
|
||||
|
@ -324,6 +423,21 @@ impl Database for Filesystem {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn move_tmp_to_published_wkd(&self, file: Option<NamedTempFile>, fpr: &Fingerprint) -> Result<()> {
|
||||
if self.dry_run {
|
||||
return Ok(());
|
||||
}
|
||||
let target = self.fingerprint_to_path_published_wkd(fpr);
|
||||
if let Some(file) = file {
|
||||
set_permissions(file.path(), Permissions::from_mode(0o644))?;
|
||||
file.persist(ensure_parent(&target)?)?;
|
||||
} else if target.exists() {
|
||||
remove_file(target)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_to_quarantine(&self, fpr: &Fingerprint, content: &[u8]) -> Result<()> {
|
||||
let mut tempfile = tempfile::Builder::new()
|
||||
.prefix("key")
|
||||
|
@ -400,32 +514,15 @@ impl Database for Filesystem {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let link = self.link_by_email(&email);
|
||||
let target = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
self.link_email_vks(email, fpr)?;
|
||||
self.link_email_wkd(email, fpr)?;
|
||||
|
||||
if link == target {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
symlink(&target, ensure_parent(&link)?)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unlink_email(&self, email: &Email, fpr: &Fingerprint) -> Result<()> {
|
||||
let link = self.link_by_email(&email);
|
||||
|
||||
match read_link(&link) {
|
||||
Ok(target) => {
|
||||
let expected = diff_paths(&self.fingerprint_to_path_published(fpr),
|
||||
link.parent().unwrap()).unwrap();
|
||||
|
||||
if target == expected {
|
||||
remove_file(link)?;
|
||||
}
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
|
||||
self.unlink_email_vks(email, fpr)?;
|
||||
self.unlink_email_wkd(email, fpr)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -493,6 +590,12 @@ impl Database for Filesystem {
|
|||
self.read_from_path(&path, false)
|
||||
}
|
||||
|
||||
// XXX: slow
|
||||
fn by_email_wkd(&self, email: &Email) -> Option<Vec<u8>> {
|
||||
let path = self.link_wkd_by_email(&email);
|
||||
self.read_from_path_bytes(&path, false)
|
||||
}
|
||||
|
||||
// XXX: slow
|
||||
fn by_kid(&self, kid: &KeyID) -> Option<String> {
|
||||
let path = self.link_by_keyid(kid);
|
||||
|
@ -525,6 +628,22 @@ impl Database for Filesystem {
|
|||
}
|
||||
)?;
|
||||
|
||||
self.perform_checks(&self.keys_dir_published, &mut tpks,
|
||||
|_, tpk, primary_fp| {
|
||||
// check that certificate exists in published wkd path
|
||||
let path_wkd = self.fingerprint_to_path_published_wkd(&primary_fp);
|
||||
let should_wkd_exist = tpk.userids().next().is_some();
|
||||
|
||||
if should_wkd_exist && !path_wkd.exists() {
|
||||
return Err(format_err!("Missing wkd for fp {}", primary_fp));
|
||||
};
|
||||
if !should_wkd_exist && path_wkd.exists() {
|
||||
return Err(format_err!("Incorrectly present wkd for fp {}", primary_fp));
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
)?;
|
||||
|
||||
// check that all subkeys are linked
|
||||
self.perform_checks(&self.keys_dir_published, &mut tpks,
|
||||
|_, tpk, primary_fp| {
|
||||
|
@ -560,6 +679,11 @@ impl Database for Filesystem {
|
|||
return Err(format_err!(
|
||||
"Missing link to key {} for email {}", primary_fp, email));
|
||||
}
|
||||
let email_wkd_path = self.link_wkd_by_email(&email);
|
||||
if !email_wkd_path.exists() {
|
||||
return Err(format_err!(
|
||||
"Missing wkd link to key {} for email {}", primary_fp, email));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use std::convert::TryFrom;
|
|||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use openpgp::serialize::SerializeInto;
|
||||
|
||||
use chrono::prelude::Utc;
|
||||
|
||||
extern crate failure;
|
||||
|
@ -23,6 +25,7 @@ extern crate url;
|
|||
extern crate hex;
|
||||
extern crate walkdir;
|
||||
extern crate chrono;
|
||||
extern crate zbase32;
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
|
@ -37,6 +40,7 @@ use openpgp::{
|
|||
pub mod types;
|
||||
use types::{Email, Fingerprint, KeyID};
|
||||
|
||||
pub mod wkd;
|
||||
pub mod sync;
|
||||
|
||||
mod fs;
|
||||
|
@ -175,6 +179,7 @@ pub trait Database: Sync + Send {
|
|||
fn by_fpr(&self, fpr: &Fingerprint) -> Option<String>;
|
||||
fn by_kid(&self, kid: &KeyID) -> Option<String>;
|
||||
fn by_email(&self, email: &Email) -> Option<String>;
|
||||
fn by_email_wkd(&self, email: &Email) -> Option<Vec<u8>>;
|
||||
|
||||
/// Complex operation that updates a Cert in the database.
|
||||
///
|
||||
|
@ -319,6 +324,7 @@ pub trait Database: Sync + Send {
|
|||
// database consistency might be compromised!
|
||||
self.move_tmp_to_full(full_tpk_tmp, &fpr_primary)?;
|
||||
self.move_tmp_to_published(published_tpk_tmp, &fpr_primary)?;
|
||||
self.regenerate_wkd(&fpr_primary, &published_tpk_clean)?;
|
||||
|
||||
let published_tpk_changed = published_tpk_old
|
||||
.map(|tpk| tpk != published_tpk_clean)
|
||||
|
@ -464,6 +470,8 @@ pub trait Database: Sync + Send {
|
|||
let published_tpk_tmp = self.write_to_temp(&tpk_to_string(&published_tpk_clean)?)?;
|
||||
|
||||
self.move_tmp_to_published(published_tpk_tmp, &fpr_primary)?;
|
||||
self.regenerate_wkd(fpr_primary, &published_tpk_clean)?;
|
||||
|
||||
self.update_write_log(&fpr_primary);
|
||||
|
||||
if let Err(e) = self.link_email(&email_new, &fpr_primary) {
|
||||
|
@ -548,6 +556,8 @@ pub trait Database: Sync + Send {
|
|||
let published_tpk_tmp = self.write_to_temp(&tpk_to_string(&published_tpk_clean)?)?;
|
||||
|
||||
self.move_tmp_to_published(published_tpk_tmp, &fpr_primary)?;
|
||||
self.regenerate_wkd(fpr_primary, &published_tpk_clean)?;
|
||||
|
||||
self.update_write_log(&fpr_primary);
|
||||
|
||||
for unpublished_email in unpublished_emails {
|
||||
|
@ -593,6 +603,8 @@ pub trait Database: Sync + Send {
|
|||
.flatten()
|
||||
.collect();
|
||||
|
||||
self.regenerate_wkd(fpr_primary, &tpk)?;
|
||||
|
||||
let fingerprints = tpk_get_linkable_fprs(&tpk);
|
||||
|
||||
let fpr_checks = fingerprints
|
||||
|
@ -624,6 +636,21 @@ pub trait Database: Sync + Send {
|
|||
}
|
||||
}
|
||||
|
||||
fn regenerate_wkd(
|
||||
&self,
|
||||
fpr_primary: &Fingerprint,
|
||||
published_tpk: &Cert
|
||||
) -> Result<()> {
|
||||
let published_wkd_tpk_tmp = if published_tpk.userids().next().is_some() {
|
||||
Some(self.write_to_temp(&published_tpk.to_vec()?)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.move_tmp_to_published_wkd(published_wkd_tpk_tmp, fpr_primary)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_link_fpr(&self, fpr: &Fingerprint, target: &Fingerprint) -> Result<Option<Fingerprint>>;
|
||||
|
||||
fn by_fpr_full(&self, fpr: &Fingerprint) -> Option<String>;
|
||||
|
@ -632,6 +659,7 @@ pub trait Database: Sync + Send {
|
|||
fn write_to_temp(&self, content: &[u8]) -> Result<NamedTempFile>;
|
||||
fn move_tmp_to_full(&self, content: NamedTempFile, fpr: &Fingerprint) -> Result<()>;
|
||||
fn move_tmp_to_published(&self, content: NamedTempFile, fpr: &Fingerprint) -> Result<()>;
|
||||
fn move_tmp_to_published_wkd(&self, content: Option<NamedTempFile>, fpr: &Fingerprint) -> Result<()>;
|
||||
fn write_to_quarantine(&self, fpr: &Fingerprint, content: &[u8]) -> Result<()>;
|
||||
fn write_log_append(&self, filename: &str, fpr_primary: &Fingerprint) -> Result<()>;
|
||||
|
||||
|
|
|
@ -31,6 +31,16 @@ use std::fs;
|
|||
use TpkStatus;
|
||||
use EmailAddressStatus;
|
||||
|
||||
fn check_mail_none(db: &impl Database, email: &Email) {
|
||||
assert!(db.by_email(&email).is_none());
|
||||
assert!(db.by_email_wkd(&email).is_none());
|
||||
}
|
||||
|
||||
fn check_mail_some(db: &impl Database, email: &Email) {
|
||||
assert!(db.by_email(&email).is_some());
|
||||
assert!(db.by_email_wkd(&email).is_some());
|
||||
}
|
||||
|
||||
pub fn test_uid_verification(db: &mut impl Database, log_path: &Path) {
|
||||
let str_uid1 = "Test A <test_a@example.com>";
|
||||
let str_uid2 = "Test B <test_b@example.com>";
|
||||
|
@ -70,8 +80,8 @@ pub fn test_uid_verification(db: &mut impl Database, log_path: &Path) {
|
|||
}
|
||||
|
||||
// fail to fetch by uid
|
||||
assert!(db.by_email(&email1).is_none());
|
||||
assert!(db.by_email(&email2).is_none());
|
||||
check_mail_none(db, &email1);
|
||||
check_mail_none(db, &email2);
|
||||
|
||||
// verify 1st uid
|
||||
db.set_email_published(&fpr, &email1).unwrap();
|
||||
|
@ -290,7 +300,7 @@ pub fn test_regenerate(db: &mut impl Database, log_path: &Path) {
|
|||
check_log_entry(log_path, &fpr);
|
||||
|
||||
db.regenerate_links(&fpr).unwrap();
|
||||
assert!(db.by_email(&email1).is_none());
|
||||
check_mail_none(db, &email1);
|
||||
assert!(db.by_fpr(&fpr).is_some());
|
||||
assert!(db.by_fpr(&fpr_sign).is_some());
|
||||
assert!(db.by_fpr(&fpr_encrypt).is_none());
|
||||
|
@ -369,7 +379,7 @@ pub fn test_uid_replacement(db: &mut impl Database, log_path: &Path) {
|
|||
|
||||
// verify 1st uid
|
||||
db.set_email_published(&fpr1, &email1).unwrap();
|
||||
assert!(db.by_email(&email1).is_some());
|
||||
check_mail_some(db, &email1);
|
||||
assert_eq!(Cert::from_bytes(db.by_email(&email1).unwrap().as_bytes()).unwrap()
|
||||
.fingerprint(), pgp_fpr1);
|
||||
|
||||
|
@ -380,7 +390,7 @@ pub fn test_uid_replacement(db: &mut impl Database, log_path: &Path) {
|
|||
|
||||
// verify uid on other key
|
||||
db.set_email_published(&fpr2, &email1).unwrap();
|
||||
assert!(db.by_email(&email1).is_some());
|
||||
check_mail_some(db, &email1);
|
||||
assert_eq!(Cert::from_bytes(db.by_email(&email1).unwrap().as_bytes()).unwrap()
|
||||
.fingerprint(), pgp_fpr2);
|
||||
|
||||
|
@ -526,8 +536,8 @@ pub fn test_upload_revoked_tpk(db: &mut impl Database, log_path: &Path) {
|
|||
db.merge(tpk.clone()).unwrap();
|
||||
db.set_email_published(&fpr, &email1).unwrap();
|
||||
|
||||
assert!(db.by_email(&email1).is_some());
|
||||
assert!(db.by_email(&email2).is_none());
|
||||
check_mail_some(db, &email1);
|
||||
check_mail_none(db, &email2);
|
||||
|
||||
tpk = tpk.merge_packets(vec![revocation.into()]).unwrap();
|
||||
match tpk.revoked(None) {
|
||||
|
@ -547,8 +557,8 @@ pub fn test_upload_revoked_tpk(db: &mut impl Database, log_path: &Path) {
|
|||
unparsed_uids: 0,
|
||||
}, tpk_status);
|
||||
|
||||
assert!(db.by_email(&email1).is_none());
|
||||
assert!(db.by_email(&email2).is_none());
|
||||
check_mail_none(db, &email1);
|
||||
check_mail_none(db, &email2);
|
||||
}
|
||||
|
||||
pub fn test_uid_revocation(db: &mut impl Database, log_path: &Path) {
|
||||
|
@ -584,8 +594,8 @@ pub fn test_uid_revocation(db: &mut impl Database, log_path: &Path) {
|
|||
db.set_email_published(&fpr, &tpk_status.email_status[1].0).unwrap();
|
||||
|
||||
// fetch both uids
|
||||
assert!(db.by_email(&email1).is_some());
|
||||
assert!(db.by_email(&email2).is_some());
|
||||
check_mail_some(db, &email1);
|
||||
check_mail_some(db, &email2);
|
||||
|
||||
thread::sleep(time::Duration::from_secs(2));
|
||||
|
||||
|
@ -615,8 +625,8 @@ pub fn test_uid_revocation(db: &mut impl Database, log_path: &Path) {
|
|||
}, tpk_status);
|
||||
|
||||
// Fail to fetch by the revoked uid, ok by the non-revoked one.
|
||||
assert!(db.by_email(&email1).is_some());
|
||||
assert!(db.by_email(&email2).is_none());
|
||||
check_mail_some(db, &email1);
|
||||
check_mail_none(db, &email2);
|
||||
}
|
||||
|
||||
/* FIXME I couldn't get this to work.
|
||||
|
@ -709,7 +719,7 @@ pub fn test_unlink_uid(db: &mut impl Database, log_path: &Path) {
|
|||
|
||||
db.merge(tpk.clone()).unwrap().into_tpk_status();
|
||||
db.set_email_published(&fpr, &email).unwrap();
|
||||
assert!(db.by_email(&email).is_some());
|
||||
check_mail_some(db, &email);
|
||||
|
||||
// Create a 2nd key with same uid, and revoke the uid.
|
||||
let tpk_evil = CertBuilder::new().add_userid(uid).generate().unwrap().0;
|
||||
|
@ -841,8 +851,8 @@ pub fn test_same_email_1(db: &mut impl Database, log_path: &Path) {
|
|||
}, tpk_status2);
|
||||
|
||||
// fetch by both user ids. We should get nothing.
|
||||
assert!(&db.by_email(&email1).is_none());
|
||||
assert!(&db.by_email(&email2).is_none());
|
||||
check_mail_none(db, &email1);
|
||||
check_mail_none(db, &email2);
|
||||
}
|
||||
|
||||
// If a key has multiple user ids with the same email address, make
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
use crate::openpgp::types::HashAlgorithm;
|
||||
use zbase32;
|
||||
use super::Result;
|
||||
|
||||
// cannibalized from
|
||||
// https://gitlab.com/sequoia-pgp/sequoia/blob/master/net/src/wkd.rs
|
||||
|
||||
pub fn encode_wkd(address: impl AsRef<str>) -> Result<(String,String)> {
|
||||
let (local_part, domain) = split_address(address)?;
|
||||
|
||||
let local_part_encoded = encode_local_part(local_part);
|
||||
|
||||
Ok((local_part_encoded, domain))
|
||||
}
|
||||
|
||||
fn split_address(email_address: impl AsRef<str>) -> Result<(String,String)> {
|
||||
let email_address = email_address.as_ref();
|
||||
let v: Vec<&str> = email_address.split('@').collect();
|
||||
if v.len() != 2 {
|
||||
Err(failure::err_msg("Malformed email address".to_owned()))?;
|
||||
};
|
||||
|
||||
// Convert to lowercase without tailoring, i.e. without taking any
|
||||
// locale into account. See:
|
||||
// https://doc.rust-lang.org/std/primitive.str.html#method.to_lowercase
|
||||
let local_part = v[0].to_lowercase();
|
||||
let domain = v[1].to_lowercase();
|
||||
|
||||
Ok((local_part, domain))
|
||||
}
|
||||
|
||||
fn encode_local_part<S: AsRef<str>>(local_part: S) -> String {
|
||||
let local_part = local_part.as_ref();
|
||||
|
||||
let mut digest = vec![0; 20];
|
||||
let mut ctx = HashAlgorithm::SHA1.context().expect("must be implemented");
|
||||
ctx.update(local_part.as_bytes());
|
||||
ctx.digest(&mut digest);
|
||||
|
||||
// After z-base-32 encoding 20 bytes, it will be 32 bytes long.
|
||||
zbase32::encode_full_bytes(&digest[..])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn encode_local_part_succed() {
|
||||
let encoded_part = encode_local_part("test1");
|
||||
assert_eq!("stnkabub89rpcphiz4ppbxixkwyt1pic", encoded_part);
|
||||
assert_eq!(32, encoded_part.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn email_address_from() {
|
||||
let (local_part, domain) = split_address("test1@example.com").unwrap();
|
||||
assert_eq!(local_part, "test1");
|
||||
assert_eq!(domain, "example.com");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue