use std::str;
use sequoia_openpgp as openpgp;
use openpgp::Result;
use openpgp::packet::UserID;
#[macro_use] mod log;
#[macro_use] mod macros;
pub mod store;
pub use store::Store;
pub use store::StoreUpdate;
mod cert_store;
pub use cert_store::CertStore;
pub use cert_store::AccessMode;
mod lazy_cert;
pub use lazy_cert::LazyCert;
const TRACE: bool = false;
#[allow(unused)]
fn print_error_chain(err: &anyhow::Error) {
let _ = write_error_chain_into(&mut std::io::stderr(), err);
}
fn write_error_chain_into(sink: &mut dyn std::io::Write, err: &anyhow::Error)
-> Result<()> {
writeln!(sink, " {}", err)?;
for cause in err.chain().skip(1) {
writeln!(sink, " because: {}", cause)?;
}
Ok(())
}
pub fn email_to_userid(email: &str) -> Result<UserID> {
let email_check = UserID::from(format!("<{}>", email));
match email_check.email() {
Ok(Some(email_check)) => {
if email != email_check {
return Err(anyhow::anyhow!(
"{:?} does not appear to be an email address",
email));
}
}
Ok(None) => {
return Err(anyhow::anyhow!(
"{:?} does not appear to be an email address",
email));
}
Err(err) => {
return Err(err.context(format!(
"{:?} does not appear to be an email address",
email)));
}
}
let userid = UserID::from(&email[..]);
match userid.email_normalized() {
Err(err) => {
Err(err.context(format!(
"'{}' is not a valid email address", email)))
}
Ok(None) => {
Err(anyhow::anyhow!("'{}' is not a valid email address", email))
}
Ok(Some(_email)) => {
Ok(userid)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::borrow::Cow;
use std::path::PathBuf;
use std::str;
use anyhow::Context;
use openpgp::Fingerprint;
use openpgp::KeyHandle;
use openpgp::KeyID;
use openpgp::Cert;
use openpgp::parse::Parse;
use openpgp::policy::StandardPolicy;
use openpgp::serialize::Serialize;
use openpgp_cert_d as cert_d;
use store::Certs;
use store::Pep;
use store::StoreError;
use store::UserIDQueryParams;
fn certd_merge(new: cert_d::Data, disk: Option<cert_d::Data>)
-> cert_d::Result<cert_d::Data> {
if let Some(disk) = disk {
let new = Cert::from_bytes(&new).expect("valid");
let disk = Cert::from_bytes(&disk).expect("valid");
let merged = new.merge_public(disk).expect("valid");
let mut bytes = Vec::new();
merged.serialize(&mut bytes).expect("valid");
Ok(bytes.into_boxed_slice())
} else {
Ok(new)
}
}
include!("../tests/keyring.rs");
fn test_backend<'a, B>(backend: B)
where B: Store<'a>
{
{
let mut got: Vec<Fingerprint> = backend.fingerprints().collect();
got.sort();
let mut expected: Vec<Fingerprint> = keyring::certs.iter()
.map(|c| c.fingerprint.parse::<Fingerprint>().expect("valid"))
.collect();
expected.sort();
expected.dedup();
assert_eq!(got.len(), expected.len());
assert_eq!(got, expected);
}
{
let mut got: Vec<Fingerprint>
= backend.certs().map(|c| c.fingerprint()).collect();
got.sort();
let mut expected: Vec<Fingerprint> = keyring::certs.iter()
.map(|c| c.fingerprint.parse::<Fingerprint>().expect("valid"))
.collect();
expected.sort();
expected.dedup();
assert_eq!(got.len(), expected.len());
assert_eq!(got, expected);
}
for handle in keyring::certs.iter() {
let fpr: Fingerprint = handle.fingerprint.parse().expect("valid");
let cert = handle.to_cert().expect("valid");
assert_eq!(fpr, cert.fingerprint(),
"{}", handle.base);
let keyid = KeyID::from(fpr.clone());
let got = backend.lookup_by_cert_fpr(&fpr).expect("present");
assert_eq!(got.fingerprint(), fpr,
"{}, by_cert_fpr, primary", handle.base);
for sk in cert.keys().subkeys() {
match backend.lookup_by_cert_fpr(&sk.fingerprint()) {
Ok(got) => {
assert!(
keyring::certs.iter().any(|c| {
c.fingerprint.parse::<Fingerprint>().unwrap()
== got.fingerprint()
}),
"{}, lookup_by_cert_fpr, subkey, unexpectedly got {}",
handle.base, got.fingerprint());
}
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NotFound(_)) => (),
_ => panic!("Expected StoreError::NotFound, \
got: {}",
err),
}
},
}
}
let got = backend.lookup_by_cert(&KeyHandle::from(&keyid))
.expect("present");
assert!(got.into_iter().any(|c| c.fingerprint() == fpr),
"{}, lookup_by_cert, keyid, primary", handle.base);
for sk in cert.keys().subkeys() {
match backend.lookup_by_cert(&KeyHandle::from(sk.keyid())) {
Ok(got) => {
for got in got.into_iter() {
assert!(
keyring::certs.iter().any(|c| {
c.fingerprint.parse::<Fingerprint>()
.unwrap()
== got.fingerprint()
}),
"{}, lookup_by_cert_fpr, subkey, \
unexpectedly got {}",
handle.base, got.fingerprint());
}
}
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NotFound(_)) => (),
_ => panic!("Unexpected failure: {}", err),
}
},
}
}
let got = backend.lookup_by_key(&KeyHandle::from(fpr.clone()))
.expect("present");
assert!(got.into_iter().any(|c| c.fingerprint() == fpr),
"{}, lookup_by_key, with fingerprint, primary",
handle.base);
for sk in cert.keys().subkeys() {
let got = backend.lookup_by_key(
&KeyHandle::from(sk.fingerprint()))
.expect("present");
assert!(got.into_iter().any(|c| c.fingerprint() == fpr),
"{}, lookup_by_key({}), with fingerprint, subkey",
handle.base, sk.fingerprint());
}
let got = backend.lookup_by_key(&KeyHandle::from(keyid.clone()))
.expect("present");
assert!(got.into_iter().any(|c| c.fingerprint() == fpr),
"{}, lookup_by_key, with keyid, primary", handle.base);
for sk in cert.keys().subkeys() {
let got = backend.lookup_by_key(&KeyHandle::from(sk.keyid()))
.expect("present");
assert!(got.into_iter().any(|c| c.fingerprint() == fpr),
"{}, lookup_by_key, with keyid, subkey", handle.base);
}
for ua in cert.userids() {
let userid = ua.userid();
let got = backend.lookup_by_userid(userid)
.expect(&format!("{}, lookup_by_userid({:?})",
handle.base, userid));
assert!(
got.into_iter().any(|c| {
c.userids().any(|u| &u == userid)
}),
"{}, lookup_by_userid({:?})", handle.base, userid);
let pattern = str::from_utf8(userid.value()).expect("utf-8");
let pattern = &pattern[1..pattern.len() - 1];
let pattern = pattern.to_uppercase();
let got = backend.grep_userid(&pattern)
.expect(&format!("{}, grep_userid({:?})",
handle.base, pattern));
assert!(
got.into_iter().any(|c| {
c.userids().any(|u| &u == userid)
}),
"{}, grep_userid({:?})", handle.base, pattern);
for (start, end, ignore_case) in
[(false, false, false),
(false, true, false),
(false, true, true),
( true, false, false),
( true, false, true),
( true, true, false),
( true, true, true)]
{
let result = backend.select_userid(
UserIDQueryParams::new()
.set_email(false)
.set_anchor_start(start)
.set_anchor_end(end)
.set_ignore_case(ignore_case),
&pattern);
match result {
Ok(got) => {
panic!("{}, select_userid({:?}) -> {}",
handle.base, pattern,
got.into_iter()
.map(|c| c.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
}
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NoMatches(_)) => (),
_ => panic!("{}, select_userid({:?}) -> {}",
handle.base, pattern, err),
}
}
}
}
let email = if let Ok(Some(email)) = userid.email() {
email
} else {
continue;
};
assert!(
backend.lookup_by_email(
str::from_utf8(userid.value()).expect("valid utf-8"))
.is_err(),
"{}, lookup_by_email({:?})", handle.base, userid);
let got = backend.lookup_by_email(&email)
.expect(&format!("{}, lookup_by_email({:?})",
handle.base, email));
assert!(
got.into_iter().any(|c| {
c.userids().any(|u| &u == userid)
}),
"{}, lookup_by_email({:?})", handle.base, userid);
let pattern = &email[1..email.len() - 1];
let pattern = pattern.to_uppercase();
let got = backend.grep_email(&pattern)
.expect(&format!("{}, grep_email({:?})",
handle.base, pattern));
assert!(
got.into_iter().any(|c| {
c.userids().any(|u| &u == userid)
}),
"{}, grep_email({:?})", handle.base, pattern);
for (start, end, ignore_case) in
[(false, false, false),
(false, true, false),
(false, true, true),
( true, false, false),
( true, false, true),
( true, true, false),
( true, true, true)]
{
let result = backend.select_userid(
UserIDQueryParams::new()
.set_email(true)
.set_anchor_start(start)
.set_anchor_end(end)
.set_ignore_case(ignore_case),
&pattern);
match result {
Ok(got) => {
panic!("{}, select_userid({:?}) -> {}",
handle.base, pattern,
got.into_iter()
.map(|c| c.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", "));
}
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NoMatches(_)) => (),
_ => panic!("{}, select_userid({:?}) -> {}",
handle.base, pattern, err),
}
}
}
}
let domain = email.rsplit('@').next().expect("have an @");
assert!(
backend.lookup_by_email_domain(
str::from_utf8(userid.value()).expect("valid utf-8"))
.is_err(),
"{}, lookup_by_email_domain({:?})", handle.base, userid);
assert!(
backend.lookup_by_email_domain(&email).is_err(),
"{}, lookup_by_email_domain({:?})", handle.base, email);
let got = backend.lookup_by_email_domain(&domain)
.expect(&format!("{}, lookup_by_email_domain({:?})",
handle.base, domain));
assert!(
got.into_iter().any(|c| {
c.userids().any(|u| &u == userid)
}),
"{}, lookup_by_email_domain({:?})", handle.base, userid);
let pattern = domain.to_uppercase();
let got = backend.lookup_by_email_domain(&pattern)
.expect(&format!("{}, lookup_by_email_domain({:?})",
handle.base, pattern));
assert!(
got.into_iter().any(|c| {
c.userids().any(|u| &u == userid)
}),
"{}, lookup_by_email_domain({:?})", handle.base, pattern);
let pattern = &domain[1..pattern.len() - 1];
let result = backend.lookup_by_email_domain(pattern);
match result {
Ok(got) => {
assert!(
got.into_iter().all(|c| {
c.fingerprint() != fpr
}),
"{}, lookup_by_email_domain({:?}, unexpectedly got {}",
handle.base, pattern, fpr);
}
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NoMatches(_)) => (),
_ => panic!("{}, lookup_by_email_domain({:?}) -> {}",
handle.base, pattern, err),
}
}
}
}
}
let sort_vec = |mut v: Vec<_>| -> Vec<_> {
v.sort();
v
};
assert_eq!(
sort_vec(backend.lookup_by_key(
&"5989D7BE9908AE24799DF6CFBE678043781349F1"
.parse::<KeyHandle>().expect("valid"))
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>()),
sort_vec(
vec![
keyring::alice.fingerprint
.parse::<Fingerprint>().expect("valid"),
keyring::alice2_adopted_alice.fingerprint
.parse::<Fingerprint>().expect("valid"),
]));
assert_eq!(
sort_vec(backend.lookup_by_key(
&"0C346B2B6241263F64E9C7CF1EA300797258A74E"
.parse::<KeyHandle>().expect("valid"))
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>()),
sort_vec(
vec![
keyring::ed.fingerprint
.parse::<Fingerprint>().expect("valid"),
]));
assert_eq!(
sort_vec(backend.lookup_by_key(
&"CD22D4BD99FF10FDA11A83D4213DCB92C95346CE"
.parse::<KeyHandle>().expect("valid"))
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>()),
sort_vec(
vec![
keyring::carol.fingerprint
.parse::<Fingerprint>().expect("valid"),
keyring::david.fingerprint
.parse::<Fingerprint>().expect("valid"),
]));
match backend.lookup_by_cert_fpr(
&"0123 4567 89AB CDEF 0123 4567 89AB CDEF"
.parse::<Fingerprint>().expect("valid"))
{
Ok(cert) => panic!("lookup_by_cert_fpr(not present) -> {}",
cert.fingerprint()),
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NotFound(_)) => (),
_ => panic!("lookup_by_cert(not present) -> {}", err),
}
}
}
match backend.lookup_by_key(
&"0123 4567 89AB CDEF 0123 4567 89AB CDEF"
.parse::<KeyHandle>().expect("valid"))
{
Ok(certs) => panic!("lookup_by_key(not present) -> {}",
certs
.into_iter()
.map(|c| c.fingerprint().to_string())
.collect::<Vec<String>>()
.join(", ")),
Err(err) => {
match err.downcast_ref::<StoreError>() {
Some(StoreError::NotFound(_)) => (),
_ => panic!("lookup_by_cert(not present) -> {}", err),
}
}
}
assert!(
backend.lookup_by_key(
&"0123 4567 89AB CDEF 0123 4567 89AB CDEF"
.parse::<KeyHandle>().expect("valid"))
.is_err());
assert_eq!(
backend.lookup_by_email("hans@xn--bcher-kva.tld")
.expect("present")
.len(),
1);
assert_eq!(
backend.lookup_by_email("hans@bücher.tld")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>(),
vec![ keyring::hans_puny_code.fingerprint
.parse::<Fingerprint>().expect("valid") ]);
assert_eq!(
backend.lookup_by_email("hans@bücher.tl")
.unwrap_or(Vec::new())
.len(),
0);
assert_eq!(
backend.lookup_by_email_domain("xn--bcher-kva.tld")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>(),
vec![ keyring::hans_puny_code.fingerprint
.parse::<Fingerprint>().expect("valid") ]);
assert_eq!(
backend.lookup_by_email_domain("bücher.tld")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>(),
vec![ keyring::hans_puny_code.fingerprint
.parse::<Fingerprint>().expect("valid") ]);
assert_eq!(
backend.lookup_by_email_domain("company.com")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>(),
vec![ keyring::una.fingerprint
.parse::<Fingerprint>().expect("valid") ]);
assert_eq!(
backend.lookup_by_email_domain("sub.company.com")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>(),
vec![ keyring::steve.fingerprint
.parse::<Fingerprint>().expect("valid") ]);
assert_eq!(
sort_vec(backend.lookup_by_email_domain("verein.de")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>()),
sort_vec(
vec![
keyring::alice2_adopted_alice.fingerprint
.parse::<Fingerprint>().expect("valid"),
keyring::carol.fingerprint
.parse::<Fingerprint>().expect("valid"),
]));
assert_eq!(
sort_vec(backend.lookup_by_email_domain("VEREIN.DE")
.expect("present")
.into_iter()
.map(|c| c.fingerprint())
.collect::<Vec<Fingerprint>>()),
sort_vec(
vec![
keyring::alice2_adopted_alice.fingerprint
.parse::<Fingerprint>().expect("valid"),
keyring::carol.fingerprint
.parse::<Fingerprint>().expect("valid"),
]));
}
#[test]
fn certd() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let path = tempfile::tempdir()?;
let certd = cert_d::CertD::with_base_dir(&path)
.map_err(|err| {
let err = anyhow::Error::from(err)
.context(format!("While opening the certd {:?}", path));
print_error_chain(&err);
err
})?;
for cert in keyring::certs.iter() {
let bytes = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&bytes,
openpgp::armor::ReaderMode::VeryTolerant);
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
certd
.insert(bytes.into_boxed_slice(), certd_merge)
.with_context(|| {
format!("{} ({})", cert.base, cert.fingerprint)
})
.expect("can insert");
}
drop (certd);
let certd = store::certd::CertD::open(&path).expect("exists");
test_backend(certd);
Ok(())
}
#[test]
fn cert_store() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let path = tempfile::tempdir()?;
let certd = cert_d::CertD::with_base_dir(&path)
.map_err(|err| {
let err = anyhow::Error::from(err)
.context(format!("While opening the certd {:?}", path));
print_error_chain(&err);
err
})?;
for cert in keyring::certs.iter() {
let bytes = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&bytes,
openpgp::armor::ReaderMode::VeryTolerant);
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
certd
.insert(bytes.into_boxed_slice(), certd_merge)
.with_context(|| {
format!("{} ({})", cert.base, cert.fingerprint)
})
.expect("can insert");
}
drop(certd);
let cert_store = CertStore::open(&path).expect("exists");
test_backend(cert_store);
Ok(())
}
#[test]
fn cert_store_layered() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let mut paths: Vec<tempfile::TempDir> = Vec::new();
let mut cert_store = CertStore::empty();
for cert in keyring::certs.iter() {
let path = tempfile::tempdir()?;
let certd = cert_d::CertD::with_base_dir(&path)
.map_err(|err| {
let err = anyhow::Error::from(err)
.context(format!("While opening the certd {:?}", path));
print_error_chain(&err);
err
})?;
let bytes = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&bytes,
openpgp::armor::ReaderMode::VeryTolerant);
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
certd
.insert(bytes.into_boxed_slice(), certd_merge)
.with_context(|| {
format!("{} ({})", cert.base, cert.fingerprint)
})
.expect("can insert");
drop(certd);
let certd = store::CertD::open(&path).expect("valid");
cert_store.add_backend(Box::new(certd), AccessMode::Always);
paths.push(path);
}
test_backend(cert_store);
Ok(())
}
#[test]
fn certs() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let mut bytes = Vec::new();
for cert in keyring::certs.iter() {
let binary = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&binary,
openpgp::armor::ReaderMode::VeryTolerant);
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
}
let backend = store::Certs::from_bytes(&bytes)
.expect("valid");
test_backend(backend);
Ok(())
}
#[test]
fn certd_with_prefetch() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let path = tempfile::tempdir()?;
let certd = cert_d::CertD::with_base_dir(&path)
.map_err(|err| {
let err = anyhow::Error::from(err)
.context(format!("While opening the certd {:?}", path));
print_error_chain(&err);
err
})?;
for cert in keyring::certs.iter() {
let bytes = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&bytes,
openpgp::armor::ReaderMode::VeryTolerant);
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
certd
.insert(bytes.into_boxed_slice(), certd_merge)
.with_context(|| {
format!("{} ({})", cert.base, cert.fingerprint)
})
.expect("can insert");
}
drop (certd);
let mut certd = store::CertD::open(&path).expect("exists");
certd.prefetch_all();
test_backend(certd);
Ok(())
}
#[test]
fn certs_with_prefetch() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let mut bytes = Vec::new();
for cert in keyring::certs.iter() {
let binary = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&binary,
openpgp::armor::ReaderMode::VeryTolerant);
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
}
let mut backend = store::Certs::from_bytes(&bytes)
.expect("valid");
backend.prefetch_all();
test_backend(backend);
Ok(())
}
#[test]
fn keyrings() -> Result<()> {
let mut cert_store = CertStore::empty();
let mut base = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
base.push("tests");
cert_store.add_keyrings(
keyring::certs.iter().map(|c| {
PathBuf::from(&base).join(c.filename)
}))?;
test_backend(cert_store);
Ok(())
}
#[test]
fn pep() -> Result<()> {
use std::io::Read;
assert_eq!(keyring::certs.len(), 12);
let mut bytes = Vec::new();
for cert in keyring::certs.iter() {
let binary = cert.bytes();
let mut reader = openpgp::armor::Reader::from_bytes(
&binary,
openpgp::armor::ReaderMode::VeryTolerant);
reader.read_to_end(&mut bytes)
.expect(&format!("{}", cert.base));
}
let mut backend = Pep::from_bytes(&bytes).expect("valid");
backend.prefetch_all();
test_backend(backend);
Ok(())
}
fn test_store_update<'a, B>(mut backend: B) -> Result<()>
where B: store::StoreUpdate<'a>
{
let p = &StandardPolicy::new();
let signing_cert =
Cert::from_bytes(&keyring::halfling_signing.bytes())
.expect("valid");
let fpr = signing_cert.fingerprint();
assert_eq!(signing_cert.keys().count(), 3);
let signing_vc = signing_cert.with_policy(p, None).expect("ok");
let signing_fpr = signing_vc.keys().subkeys()
.for_signing()
.map(|ka| ka.fingerprint())
.collect::<Vec<Fingerprint>>();
assert_eq!(signing_fpr.len(), 1);
let signing_fpr = KeyHandle::from(
signing_fpr.into_iter().next().expect("have one"));
let auth_fpr = signing_vc.keys().subkeys()
.for_authentication()
.map(|ka| ka.fingerprint())
.collect::<Vec<Fingerprint>>();
assert_eq!(auth_fpr.len(), 1);
let auth_fpr = KeyHandle::from(
auth_fpr.into_iter().next().expect("have one"));
let encryption_cert =
Cert::from_bytes(&keyring::halfling_encryption.bytes())
.expect("valid");
assert_eq!(fpr, encryption_cert.fingerprint());
assert_eq!(encryption_cert.keys().count(), 3);
let encryption_vc = encryption_cert.with_policy(p, None).expect("ok");
let encryption_fpr = encryption_vc.keys().subkeys()
.for_transport_encryption()
.map(|ka| ka.fingerprint())
.collect::<Vec<Fingerprint>>();
assert_eq!(encryption_fpr.len(), 1);
let encryption_fpr = KeyHandle::from(
encryption_fpr.into_iter().next().expect("have one"));
assert_ne!(signing_fpr, encryption_fpr);
let auth2_fpr = encryption_vc.keys().subkeys()
.for_authentication()
.map(|ka| ka.fingerprint())
.collect::<Vec<Fingerprint>>();
assert_eq!(auth2_fpr.len(), 1);
let auth2_fpr = KeyHandle::from(
auth2_fpr.into_iter().next().expect("have one"));
assert_eq!(auth_fpr, auth2_fpr);
let merged_cert = signing_cert.clone()
.merge_public(encryption_cert.clone()).expect("ok");
let check = |backend: &B, have_enc: bool, cert: &Cert| {
let r = backend.lookup_by_cert(&KeyHandle::from(fpr.clone())).unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
let r = backend.lookup_by_key(&signing_fpr).unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
let r = backend.lookup_by_key(&auth_fpr).unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
if have_enc {
let r = backend.lookup_by_key(&encryption_fpr).unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
} else {
assert!(backend.lookup_by_key(&encryption_fpr).is_err());
}
let r = backend.lookup_by_userid(
&UserID::from("<regis@pup.com>")).unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
let r = backend.lookup_by_userid(
&UserID::from("Halfling <signing@halfling.org>")).unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
if have_enc {
let r = backend.lookup_by_userid(
&UserID::from("Halfling <encryption@halfling.org>"))
.unwrap();
assert_eq!(r.len(), 1);
assert_eq!(r[0].to_cert().expect("ok"), cert);
} else {
assert!(backend.lookup_by_key(&encryption_fpr).is_err());
}
};
backend.update(Cow::Owned(LazyCert::from(signing_cert.clone())))
.expect("ok");
check(&backend, false, &signing_cert);
backend.update(Cow::Owned(LazyCert::from(encryption_cert.clone())))
.expect("ok");
check(&backend, true, &merged_cert);
backend.update(Cow::Owned(LazyCert::from(signing_cert.clone())))
.expect("ok");
check(&backend, true, &merged_cert);
Ok(())
}
#[test]
fn test_store_update_cert_store() -> Result<()> {
let path = tempfile::tempdir()?;
let cert_store = CertStore::open(&path).expect("exists");
test_store_update(cert_store)
}
#[test]
fn test_store_update_certs() -> Result<()> {
let certs = Certs::empty();
test_store_update(certs)
}
#[test]
fn test_store_update_pep() -> Result<()> {
let certs = Pep::empty()?;
test_store_update(certs)
}
}