Add sq wot
- Add the `sq wot` subcommand, to expose web of trust functionality. - This is just an import of the `sq-wot` CLI as `sq wot`. The support for using the `gpg` keyring and gpg's ownertrust, however, is removed.
This commit is contained in:
parent
8cf08e2470
commit
47447cd7d0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2873,6 +2873,7 @@ dependencies = [
|
||||
"clap 4.0.32",
|
||||
"clap_complete",
|
||||
"dirs",
|
||||
"dot-writer",
|
||||
"fehler",
|
||||
"itertools 0.10.3",
|
||||
"once_cell",
|
||||
|
@ -31,6 +31,7 @@ maintenance = { status = "actively-developed" }
|
||||
[dependencies]
|
||||
buffered-reader = { version = "1.0.0", default-features = false, features = ["compression-deflate"] }
|
||||
dirs = "4"
|
||||
dot-writer = "0.1.3"
|
||||
sequoia-openpgp = { version = "1.13", default-features = false, features = ["compression-deflate"] }
|
||||
sequoia-autocrypt = { version = "0.25", default-features = false, optional = true }
|
||||
sequoia-net = { version = "0.26", default-features = false }
|
||||
|
7
NEWS
7
NEWS
@ -51,6 +51,13 @@
|
||||
local trust root on the specified bindings.
|
||||
- Add a top-level option, `--keyring`, to allow the user to specify
|
||||
additional keyrings to search for certificates.
|
||||
- Import web of trust subcommands from sq-wot. Specifically, add:
|
||||
- `sq wot authenticate` to authenticate a binding.
|
||||
- `sq wot lookup` to find a certificate with a particular User ID.
|
||||
- `sq wot identify` to list authenticated bindings for a
|
||||
certificate.
|
||||
- `sq wot list` to list authenticated bindings.
|
||||
- `sq wot path` to authenticate and lint a path in a web of trust.
|
||||
* Deprecated functionality
|
||||
- `sq key generate --creation-time TIME` is deprecated in favor of
|
||||
`sq key generate --time TIME`.
|
||||
|
@ -35,8 +35,7 @@ use openpgp::types::RevocationStatus;
|
||||
use sequoia_cert_store as cert_store;
|
||||
use cert_store::Store;
|
||||
|
||||
use sequoia_wot as wot;
|
||||
use wot::store::Store as _;
|
||||
use sequoia_wot::store::Store as _;
|
||||
|
||||
use crate::{
|
||||
Config,
|
||||
@ -66,8 +65,7 @@ pub mod export;
|
||||
pub mod net;
|
||||
pub mod certify;
|
||||
pub mod link;
|
||||
|
||||
use crate::error_chain;
|
||||
pub mod wot;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum GetKeysOptions {
|
||||
@ -451,129 +449,6 @@ pub fn encrypt(opts: EncryptOpts) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Prints the path in the web of trust.
|
||||
fn print_path(path: &wot::PathLints, target_userid: &UserID, prefix: &str)
|
||||
{
|
||||
let certification_count = path.certifications().count();
|
||||
|
||||
eprint!("{}◯ {}", prefix, path.root().key_handle());
|
||||
if certification_count == 0 {
|
||||
eprint!(" {:?}", String::from_utf8_lossy(target_userid.value()));
|
||||
} else if let Some(userid) = path.root().primary_userid() {
|
||||
eprint!(" ({:?})",
|
||||
String::from_utf8_lossy(userid.value()));
|
||||
}
|
||||
eprintln!("");
|
||||
|
||||
for (last, (cert, certification)) in path
|
||||
.certs()
|
||||
.zip(path.certifications())
|
||||
.enumerate()
|
||||
.map(|(j, c)| {
|
||||
if j + 1 == certification_count {
|
||||
(true, c)
|
||||
} else {
|
||||
(false, c)
|
||||
}
|
||||
})
|
||||
{
|
||||
eprint!("{}│ ", prefix);
|
||||
if let Some(certification) = certification.certification() {
|
||||
if certification.amount() < wot::FULLY_TRUSTED {
|
||||
eprint!(" partially certified (amount: {} of {})",
|
||||
certification.amount(), wot::FULLY_TRUSTED);
|
||||
} else {
|
||||
eprint!(" certified");
|
||||
}
|
||||
|
||||
if last {
|
||||
eprint!(" the following binding");
|
||||
} else {
|
||||
eprint!(" the following certificate");
|
||||
}
|
||||
|
||||
eprint!(" on {}",
|
||||
chrono::DateTime::<chrono::Utc>::from(
|
||||
certification.creation_time()).format("%Y-%m-%d"));
|
||||
if let Some(e) = certification.expiration_time() {
|
||||
eprint!(" (expiry: {})",
|
||||
chrono::DateTime::<chrono::Utc>::from(
|
||||
e).format("%Y-%m-%d"));
|
||||
}
|
||||
if certification.depth() > 0.into() {
|
||||
eprint!(" as a");
|
||||
if certification.amount() != wot::FULLY_TRUSTED {
|
||||
eprint!(" partially trusted ({} of {})",
|
||||
certification.amount(), wot::FULLY_TRUSTED);
|
||||
} else {
|
||||
eprint!(" fully trusted");
|
||||
}
|
||||
if certification.depth() == 1.into() {
|
||||
eprint!(" introducer (depth: {})",
|
||||
certification.depth());
|
||||
} else {
|
||||
eprint!(" meta-introducer (depth: {})",
|
||||
certification.depth());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprint!(" No adequate certification found.");
|
||||
}
|
||||
eprintln!("");
|
||||
|
||||
for err in cert.errors().iter().chain(cert.lints()) {
|
||||
for (i, msg) in error_chain(err).into_iter().enumerate() {
|
||||
eprintln!("{}│ {}{}",
|
||||
prefix,
|
||||
if i == 0 { "" } else { " " },
|
||||
msg);
|
||||
}
|
||||
}
|
||||
for err in certification.errors().iter()
|
||||
.chain(certification.lints())
|
||||
{
|
||||
for (i, msg) in error_chain(err).into_iter().enumerate() {
|
||||
eprintln!("{}│ {}{}",
|
||||
prefix,
|
||||
if i == 0 { "" } else { " " },
|
||||
msg);
|
||||
}
|
||||
}
|
||||
|
||||
eprint!("{}{} {}",
|
||||
prefix,
|
||||
if last { "└" } else { "├" },
|
||||
certification.target());
|
||||
|
||||
if last {
|
||||
eprint!(" {:?}",
|
||||
String::from_utf8_lossy(target_userid.value()));
|
||||
} else {
|
||||
if let Some(userid) = certification.target_cert()
|
||||
.and_then(|c| c.primary_userid())
|
||||
{
|
||||
eprint!(" ({:?})",
|
||||
String::from_utf8_lossy(userid.value()));
|
||||
}
|
||||
}
|
||||
eprintln!("");
|
||||
|
||||
if last {
|
||||
let target = path.certs().last().expect("have one");
|
||||
for err in target.errors().iter().chain(target.lints()) {
|
||||
for (i, msg) in error_chain(err).into_iter().enumerate() {
|
||||
eprintln!("{} {}{}",
|
||||
prefix,
|
||||
if i == 0 { "" } else { " " },
|
||||
msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eprintln!("");
|
||||
}
|
||||
|
||||
struct VHelper<'a, 'store> {
|
||||
#[allow(dead_code)]
|
||||
config: &'a Config<'store>,
|
||||
@ -632,9 +507,11 @@ impl<'a, 'store> VHelper<'a, 'store> {
|
||||
}
|
||||
|
||||
fn print_sigs(&mut self, results: &[VerificationResult]) {
|
||||
use crate::commands::wot::output::print_path;
|
||||
use crate::print_error_chain;
|
||||
|
||||
let reference_time = self.config.time;
|
||||
|
||||
use crate::print_error_chain;
|
||||
use self::VerificationError::*;
|
||||
for result in results {
|
||||
let (sig, ka) = match result {
|
||||
@ -705,7 +582,7 @@ impl<'a, 'store> VHelper<'a, 'store> {
|
||||
|
||||
if let Ok(Some(cert_store)) = self.config.cert_store() {
|
||||
// Build the network.
|
||||
let cert_store = wot::store::CertStore::from_store(
|
||||
let cert_store = sequoia_wot::store::CertStore::from_store(
|
||||
cert_store, &self.config.policy, reference_time);
|
||||
|
||||
let userids = if let Some(userid) = sig.signers_user_id() {
|
||||
@ -722,9 +599,9 @@ impl<'a, 'store> VHelper<'a, 'store> {
|
||||
eprintln!("{}{} cannot be authenticated. \
|
||||
It has no User IDs",
|
||||
prefix, cert_fpr);
|
||||
} else if let Ok(n) = wot::Network::new(&cert_store) {
|
||||
let mut q = wot::QueryBuilder::new(&n);
|
||||
q.roots(wot::Roots::new(trust_roots.into_iter()));
|
||||
} else if let Ok(n) = sequoia_wot::Network::new(&cert_store) {
|
||||
let mut q = sequoia_wot::QueryBuilder::new(&n);
|
||||
q.roots(sequoia_wot::Roots::new(trust_roots.into_iter()));
|
||||
let q = q.build();
|
||||
|
||||
let authenticated_userids
|
||||
@ -735,14 +612,15 @@ impl<'a, 'store> VHelper<'a, 'store> {
|
||||
let paths = q.authenticate(
|
||||
userid, cert.fingerprint(),
|
||||
// XXX: Make this user configurable.
|
||||
wot::FULLY_TRUSTED);
|
||||
sequoia_wot::FULLY_TRUSTED);
|
||||
|
||||
let amount = paths.amount();
|
||||
let authenticated = if amount >= wot::FULLY_TRUSTED {
|
||||
let authenticated = if amount >= sequoia_wot::FULLY_TRUSTED {
|
||||
eprintln!("{}Fully authenticated \
|
||||
({} of {}) {}, {}",
|
||||
prefix,
|
||||
amount, wot::FULLY_TRUSTED,
|
||||
amount,
|
||||
sequoia_wot::FULLY_TRUSTED,
|
||||
cert_fpr,
|
||||
userid_str);
|
||||
true
|
||||
@ -750,7 +628,8 @@ impl<'a, 'store> VHelper<'a, 'store> {
|
||||
eprintln!("{}Partially authenticated \
|
||||
({} of {}) {}, {:?} ",
|
||||
prefix,
|
||||
amount, wot::FULLY_TRUSTED,
|
||||
amount,
|
||||
sequoia_wot::FULLY_TRUSTED,
|
||||
cert_fpr,
|
||||
userid_str);
|
||||
false
|
||||
|
552
src/commands/wot.rs
Normal file
552
src/commands/wot.rs
Normal file
@ -0,0 +1,552 @@
|
||||
use anyhow::Context;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyID;
|
||||
use openpgp::Fingerprint;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::Result;
|
||||
use openpgp::packet::UserID;
|
||||
use openpgp::policy::Policy;
|
||||
|
||||
use sequoia_cert_store as cert_store;
|
||||
use cert_store::store::StatusListener;
|
||||
use cert_store::store::StatusUpdate;
|
||||
use cert_store::store::StoreError;
|
||||
|
||||
use sequoia_wot as wot;
|
||||
use wot::store::CertStore;
|
||||
|
||||
pub mod output;
|
||||
|
||||
use crate::sq_cli::wot as wot_cli;
|
||||
use wot_cli::Command;
|
||||
|
||||
use crate::commands::wot as wot_cmd;
|
||||
use wot_cmd::output::print_path;
|
||||
use wot_cmd::output::print_path_header;
|
||||
use wot_cmd::output::print_path_error;
|
||||
|
||||
use crate::Config;
|
||||
|
||||
fn trust_amount(cli: &Command)
|
||||
-> Result<usize>
|
||||
{
|
||||
let amount = if let Some(v) = cli.trust_amount {
|
||||
v as usize
|
||||
} else if cli.full {
|
||||
wot::FULLY_TRUSTED
|
||||
} else if cli.partial {
|
||||
wot::PARTIALLY_TRUSTED
|
||||
} else if cli.double {
|
||||
2 * wot::FULLY_TRUSTED
|
||||
} else {
|
||||
if cli.certification_network {
|
||||
// Look for multiple paths. Specifically, try to find 10
|
||||
// paths.
|
||||
10 * wot::FULLY_TRUSTED
|
||||
} else {
|
||||
wot::FULLY_TRUSTED
|
||||
}
|
||||
};
|
||||
|
||||
Ok(amount)
|
||||
}
|
||||
|
||||
// Returns whether there is a matching self-signed User ID.
|
||||
fn have_self_signed_userid(cert: &wot::CertSynopsis,
|
||||
pattern: &UserID, email: bool)
|
||||
-> bool
|
||||
{
|
||||
if email {
|
||||
if let Ok(Some(pattern)) = pattern.email_normalized() {
|
||||
// userid contains a valid email address.
|
||||
cert.userids().any(|u| {
|
||||
if let Ok(Some(userid)) = u.userid().email_normalized() {
|
||||
pattern == userid
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
cert.userids().any(|u| u.userid() == pattern)
|
||||
}
|
||||
}
|
||||
|
||||
/// Authenticate bindings defined by a Query on a Network
|
||||
fn authenticate<S>(
|
||||
config: &Config,
|
||||
cli: &Command,
|
||||
q: &wot::Query<'_, S>,
|
||||
gossip: bool,
|
||||
userid: Option<&UserID>,
|
||||
certificate: Option<&KeyHandle>,
|
||||
) -> Result<()>
|
||||
where S: wot::store::Store
|
||||
{
|
||||
let required_amount = trust_amount(cli)?;
|
||||
|
||||
let fingerprint: Option<Fingerprint> = if let Some(kh) = certificate {
|
||||
Some(match kh {
|
||||
KeyHandle::Fingerprint(fpr) => fpr.clone(),
|
||||
kh @ KeyHandle::KeyID(_) => {
|
||||
let certs = q.network().lookup_synopses(kh)?;
|
||||
if certs.is_empty() {
|
||||
return Err(StoreError::NotFound(kh.clone()).into());
|
||||
}
|
||||
if certs.len() > 1 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"The Key ID {} is ambiguous. \
|
||||
It could refer to any of the following \
|
||||
certificates: {}.",
|
||||
kh,
|
||||
certs.into_iter()
|
||||
.map(|c| c.fingerprint().to_hex())
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")));
|
||||
}
|
||||
|
||||
certs[0].fingerprint()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let email = cli.subcommand.email();
|
||||
|
||||
let mut bindings = Vec::new();
|
||||
if matches!(userid, Some(_)) && email {
|
||||
let userid = userid.expect("required");
|
||||
|
||||
// First, we check that the supplied User ID is a bare
|
||||
// email address.
|
||||
let email = String::from_utf8(userid.value().to_vec())
|
||||
.context("email address must be valid UTF-8")?;
|
||||
|
||||
let userid_check = UserID::from(format!("<{}>", email));
|
||||
if let Ok(Some(email_check)) = userid_check.email() {
|
||||
if email != email_check {
|
||||
println!("{:?} does not appear to be an email address",
|
||||
email);
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
println!("{:?} does not appear to be an email address",
|
||||
email);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
// Now, iterate over all of the certifications of the target,
|
||||
// and select the bindings where the User ID matches the email
|
||||
// address.
|
||||
bindings = if let Some(fingerprint) = fingerprint.as_ref() {
|
||||
q.network().certified_userids_of(fingerprint)
|
||||
.into_iter()
|
||||
.map(|userid| (fingerprint.clone(), userid))
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
q.network().lookup_synopses_by_email(&email)
|
||||
};
|
||||
|
||||
let email_normalized = userid_check.email_normalized()
|
||||
.expect("checked").expect("checked");
|
||||
bindings = bindings.into_iter()
|
||||
.filter_map(|(fingerprint, userid_other)| {
|
||||
if let Ok(Some(email_other_normalized))
|
||||
= userid_other.email_normalized()
|
||||
{
|
||||
if email_normalized == email_other_normalized {
|
||||
Some((fingerprint, userid_other.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect();
|
||||
} else if let Some(fingerprint) = fingerprint {
|
||||
if let Some(userid) = userid {
|
||||
bindings.push((fingerprint, userid.clone()));
|
||||
} else {
|
||||
// Fingerprint, no User ID.
|
||||
bindings = q.network().certified_userids_of(&fingerprint)
|
||||
.into_iter()
|
||||
.map(|userid| (fingerprint.clone(), userid))
|
||||
.collect();
|
||||
}
|
||||
} else if let Some(userid) = userid {
|
||||
// The caller did not specify a certificate. Find all
|
||||
// bindings with the User ID.
|
||||
bindings = q.network().lookup_synopses_by_userid(userid.clone())
|
||||
.into_iter()
|
||||
.map(|fpr| (fpr, userid.clone()))
|
||||
.collect();
|
||||
} else {
|
||||
// No User ID, no Fingerprint.
|
||||
// List everything.
|
||||
|
||||
bindings = q.network().certified_userids();
|
||||
|
||||
if let wot_cli::Subcommand::List { pattern: Some(pattern), .. } = &cli.subcommand {
|
||||
// Or rather, just User IDs that match the pattern.
|
||||
let pattern = pattern.to_lowercase();
|
||||
|
||||
bindings = bindings
|
||||
.into_iter()
|
||||
.filter(|(_fingerprint, userid)| {
|
||||
if email {
|
||||
// Compare with the normalized email address,
|
||||
// and the raw email address.
|
||||
if let Ok(Some(email)) = userid.email_normalized() {
|
||||
// A normalized email is already lowercase.
|
||||
if email.contains(&pattern) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(Some(email)) = userid.email() {
|
||||
if email.to_lowercase().contains(&pattern) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if let Ok(userid)
|
||||
= std::str::from_utf8(userid.value())
|
||||
{
|
||||
userid.to_lowercase().contains(&pattern)
|
||||
} else {
|
||||
// Ignore User IDs with invalid UTF-8.
|
||||
false
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
};
|
||||
|
||||
// There may be multiple certifications of the same
|
||||
// User ID. Dedup.
|
||||
bindings.sort();
|
||||
bindings.dedup();
|
||||
|
||||
let mut authenticated = false;
|
||||
let mut lint_input = true;
|
||||
|
||||
let mut output = match config.output_format {
|
||||
crate::output::OutputFormat::DOT => {
|
||||
Box::new(output::DotOutputNetwork::new(
|
||||
required_amount,
|
||||
q.roots(),
|
||||
gossip,
|
||||
cli.certification_network,
|
||||
))
|
||||
as Box<dyn output::OutputType>
|
||||
}
|
||||
_ => {
|
||||
Box::new(
|
||||
output::HumanReadableOutputNetwork::new(required_amount, gossip)
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
for (fingerprint, userid) in bindings.iter() {
|
||||
let mut aggregated_amount = 0;
|
||||
|
||||
let paths = if gossip {
|
||||
// Gossip.
|
||||
let paths = q.gossip(
|
||||
fingerprint.clone(), userid.clone());
|
||||
|
||||
// Sort so the shortest paths come first.
|
||||
let mut paths: Vec<_> = paths
|
||||
.into_values()
|
||||
.map(|(path, _amount)| path)
|
||||
.collect();
|
||||
paths.sort_by_key(|path| path.len());
|
||||
|
||||
// This means: exit code is 0, which is what we want when
|
||||
// we've found at least one path.
|
||||
if paths.len() > 0 {
|
||||
authenticated = true;
|
||||
lint_input = false;
|
||||
}
|
||||
|
||||
paths.into_iter()
|
||||
.map(|p| (p, 0))
|
||||
.collect::<Vec<(wot::Path, usize)>>()
|
||||
} else {
|
||||
let paths = q.authenticate(
|
||||
userid.clone(), fingerprint.clone(), required_amount);
|
||||
|
||||
aggregated_amount = paths.amount();
|
||||
if aggregated_amount == 0 {
|
||||
continue;
|
||||
}
|
||||
lint_input = false;
|
||||
if aggregated_amount >= required_amount {
|
||||
authenticated = true;
|
||||
}
|
||||
|
||||
paths.into_iter().collect::<Vec<(wot::Path, usize)>>()
|
||||
};
|
||||
|
||||
output.add_paths(paths, fingerprint, userid, aggregated_amount)?;
|
||||
|
||||
}
|
||||
|
||||
output.finalize()?;
|
||||
|
||||
// We didn't show anything. Try to figure out what was wrong.
|
||||
if lint_input {
|
||||
// See if the target certificate exists.
|
||||
if let Some(kh) = certificate {
|
||||
match q.network().lookup_synopses(kh) {
|
||||
Err(err) => {
|
||||
eprintln!("Looking up target certificate ({}): {}",
|
||||
kh, err);
|
||||
}
|
||||
Ok(certs) => {
|
||||
for cert in certs.iter() {
|
||||
let fpr = cert.fingerprint();
|
||||
let kh = if certs.len() == 1 {
|
||||
KeyHandle::KeyID(KeyID::from(&fpr))
|
||||
} else {
|
||||
KeyHandle::Fingerprint(fpr.clone())
|
||||
};
|
||||
|
||||
// Check if the certificate was revoke.
|
||||
use wot::RevocationStatus;
|
||||
match cert.revocation_status() {
|
||||
RevocationStatus::Soft(_)
|
||||
| RevocationStatus::Hard => {
|
||||
eprintln!("Warning: {} is revoked.", kh);
|
||||
}
|
||||
RevocationStatus::NotAsFarAsWeKnow => (),
|
||||
}
|
||||
|
||||
// Check if the certificate has expired.
|
||||
if let Some(e) = cert.expiration_time() {
|
||||
if e <= q.network().reference_time() {
|
||||
eprintln!("Warning: {} is expired.", kh);
|
||||
}
|
||||
}
|
||||
|
||||
// See if there is a matching self-signed User ID.
|
||||
if let Some(userid) = userid {
|
||||
if ! have_self_signed_userid(cert, userid, email) {
|
||||
eprintln!("Warning: {} is not a \
|
||||
self-signed User ID for {}.",
|
||||
userid, kh);
|
||||
}
|
||||
}
|
||||
|
||||
// See if there are any certifications made on
|
||||
// this certificate.
|
||||
if let Ok(cs) = q.network()
|
||||
.certifications_of(&fpr, 0.into())
|
||||
{
|
||||
if cs.iter().all(|cs| {
|
||||
cs.certifications()
|
||||
.all(|(_userid, certifications)| {
|
||||
certifications.is_empty()
|
||||
})
|
||||
})
|
||||
{
|
||||
eprintln!("Warning: {} has no valid \
|
||||
certifications.",
|
||||
kh);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perhaps the caller specified an email address, but forgot
|
||||
// to add --email. If --email is not present and the
|
||||
// specified User ID looks like an email, try and be helpful.
|
||||
if ! email {
|
||||
if let Some(userid) = userid {
|
||||
if let Ok(email) = std::str::from_utf8(userid.value()) {
|
||||
let userid_check = UserID::from(format!("<{}>", email));
|
||||
if let Ok(Some(email_check)) = userid_check.email() {
|
||||
if email == email_check {
|
||||
eprintln!("WARNING: {} appears to be a bare \
|
||||
email address. Perhaps you forgot \
|
||||
to specify --email.",
|
||||
email);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See if the trust roots exist.
|
||||
if ! gossip {
|
||||
if q.roots().iter().all(|r| {
|
||||
let fpr = r.fingerprint();
|
||||
if let Err(err) = q.network().lookup_synopsis_by_fpr(&fpr) {
|
||||
eprintln!("Looking up trust root ({}): {}.",
|
||||
fpr, err);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
{
|
||||
eprintln!("No trust roots found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ! authenticated {
|
||||
if ! lint_input {
|
||||
eprintln!("Could not authenticate any paths.");
|
||||
} else {
|
||||
eprintln!("No paths found.");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// For `sq-wot path`.
|
||||
fn check_path<'a: 'b, 'b, S>(config: &Config,
|
||||
cli: &Command, q: &wot::Query<'b, S>,
|
||||
policy: &dyn Policy)
|
||||
-> Result<()>
|
||||
where S: wot::store::Store + wot::store::Backend<'a>
|
||||
{
|
||||
tracer!(TRACE, "check_path");
|
||||
|
||||
let required_amount = trust_amount(cli)?;
|
||||
|
||||
let (khs, userid) = if let wot_cli::Subcommand::Path { path, .. } = &cli.subcommand {
|
||||
(path.certs()?, path.userid()?)
|
||||
} else {
|
||||
unreachable!("checked");
|
||||
};
|
||||
|
||||
assert!(khs.len() > 0, "guaranteed by clap");
|
||||
|
||||
let r = q.lint_path(&khs, &userid, required_amount, policy);
|
||||
|
||||
let target_kh = khs.last().expect("have one");
|
||||
|
||||
match r {
|
||||
Ok(path) => {
|
||||
match config.output_format {
|
||||
crate::output::OutputFormat::DOT => {
|
||||
eprintln!(
|
||||
"DOT output for \"sq wot path\" is not yet \
|
||||
implemented!");
|
||||
}
|
||||
_ => {
|
||||
print_path_header(
|
||||
target_kh,
|
||||
&userid,
|
||||
path.amount(),
|
||||
required_amount,
|
||||
);
|
||||
print_path(&path, &userid, " ");
|
||||
}
|
||||
};
|
||||
|
||||
if path.amount() >= required_amount {
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
match config.output_format {
|
||||
crate::output::OutputFormat::DOT => {
|
||||
eprintln!(
|
||||
"DOT output for \"sq wot path\" is not yet \
|
||||
implemented!");
|
||||
}
|
||||
_ => {
|
||||
print_path_header(
|
||||
target_kh,
|
||||
&userid,
|
||||
0,
|
||||
required_amount,
|
||||
);
|
||||
print_path_error(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
struct KeyServerUpdate {
|
||||
}
|
||||
|
||||
impl StatusListener for KeyServerUpdate {
|
||||
fn update(&self, update: &StatusUpdate) {
|
||||
eprintln!("{}", update);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dispatch(config: Config, cli: wot_cli::Command) -> Result<()> {
|
||||
tracer!(TRACE, "wot::dispatch");
|
||||
|
||||
// Build the network.
|
||||
let cert_store = match config.cert_store() {
|
||||
Ok(Some(cert_store)) => cert_store,
|
||||
Ok(None) => {
|
||||
return Err(anyhow::anyhow!("Certificate store has been disabled"));
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(err).context("Opening certificate store");
|
||||
}
|
||||
};
|
||||
|
||||
let cert_store = CertStore::from_store(
|
||||
cert_store, &config.policy, config.time);
|
||||
|
||||
let n = wot::Network::new(cert_store)?;
|
||||
|
||||
let roots = wot::Roots::new(config.trust_roots());
|
||||
let mut q = wot::QueryBuilder::new(&n);
|
||||
q.roots(roots);
|
||||
if cli.certification_network {
|
||||
q.certification_network();
|
||||
}
|
||||
let q = q.build();
|
||||
|
||||
match &cli.subcommand {
|
||||
wot_cli::Subcommand::Authenticate { cert, userid, .. } => {
|
||||
// Authenticate a given binding.
|
||||
authenticate(
|
||||
&config, &cli, &q, cli.gossip, Some(userid), Some(cert))?;
|
||||
}
|
||||
wot_cli::Subcommand::Lookup { userid, .. } => {
|
||||
// Find all authenticated bindings for a given
|
||||
// User ID, list the certificates.
|
||||
authenticate(
|
||||
&config, &cli, &q, cli.gossip, Some(userid), None)?;
|
||||
}
|
||||
wot_cli::Subcommand::Identify { cert, .. } => {
|
||||
// Find and list all authenticated bindings for a given
|
||||
// certificate.
|
||||
authenticate(
|
||||
&config, &cli, &q, cli.gossip, None, Some(cert))?;
|
||||
}
|
||||
wot_cli::Subcommand::List { .. } => {
|
||||
// List all authenticated bindings.
|
||||
authenticate(
|
||||
&config, &cli, &q, cli.gossip, None, None)?;
|
||||
}
|
||||
wot_cli::Subcommand::Path { .. } => {
|
||||
check_path(
|
||||
&config, &cli, &q, &config.policy)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
40
src/commands/wot/output.rs
Normal file
40
src/commands/wot/output.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use openpgp::packet::UserID;
|
||||
use openpgp::Fingerprint;
|
||||
use openpgp::Result;
|
||||
use sequoia_openpgp as openpgp;
|
||||
|
||||
use sequoia_wot as wot;
|
||||
use wot::Path;
|
||||
|
||||
mod dot;
|
||||
pub use dot::DotOutputNetwork;
|
||||
|
||||
mod human_readable;
|
||||
pub use human_readable::print_path;
|
||||
pub use human_readable::print_path_error;
|
||||
pub use human_readable::print_path_header;
|
||||
pub use human_readable::HumanReadableOutputNetwork;
|
||||
|
||||
/// Trait to implement adding of Paths and outputting them in a specific format
|
||||
///
|
||||
/// This trait is implemented to consume a vector of Path, trust amount tuples,
|
||||
/// a target Fingerprint, a target UserID, and aggregated trust amount (for the
|
||||
/// target UserID) to allow further processing and eventual output in a desired
|
||||
/// output format.
|
||||
pub trait OutputType {
|
||||
/// Add Paths for a UserID associated with a Fingerprint
|
||||
///
|
||||
/// Paths are provided in a vector of Path, trust amount tuples.
|
||||
/// The aggregated_amount represents the (total) trust amount (derived from
|
||||
/// the Paths) for the UserID associated with the Fingerprint
|
||||
fn add_paths(
|
||||
&mut self,
|
||||
paths: Vec<(Path, usize)>,
|
||||
fingerprint: &Fingerprint,
|
||||
userid: &UserID,
|
||||
aggregated_amount: usize,
|
||||
) -> Result<()>;
|
||||
|
||||
/// Output the data consumed via add_paths() in a specific output format
|
||||
fn finalize(&mut self) -> Result<()>;
|
||||
}
|
817
src/commands/wot/output/dot.rs
Normal file
817
src/commands/wot/output/dot.rs
Normal file
@ -0,0 +1,817 @@
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Write;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use dot_writer::Attributes;
|
||||
use dot_writer::DotWriter;
|
||||
use dot_writer::Scope;
|
||||
use dot_writer::Shape;
|
||||
|
||||
use openpgp::packet::UserID;
|
||||
use openpgp::Fingerprint;
|
||||
use openpgp::Result;
|
||||
use sequoia_openpgp as openpgp;
|
||||
|
||||
use sequoia_wot as wot;
|
||||
use wot::Depth;
|
||||
use wot::Path;
|
||||
use wot::Roots;
|
||||
use wot::FULLY_TRUSTED;
|
||||
|
||||
use crate::commands::wot::output::OutputType;
|
||||
|
||||
const DOT_INSTRUCTIONS: &'static str = "\
|
||||
//
|
||||
// Example: To convert DOT to SVG (on many systems):
|
||||
//
|
||||
// sq --output-format dot wot ... | dot -Tsvg -o output.svg
|
||||
//
|
||||
// For further information on using graphviz see:
|
||||
// https://graphviz.org/doc/info/command.html
|
||||
";
|
||||
const DOT_ROOT_FILL_COLOR: &'static str = "mediumpurple2";
|
||||
const DOT_TARGET_OK_FILL_COLOR: &'static str = "lightgreen";
|
||||
const DOT_TARGET_FAIL_FILL_COLOR: &'static str = "indianred2";
|
||||
const DOT_NODE_FILL_COLOR: &'static str = "grey";
|
||||
|
||||
/// Return UserID as String and remove (backslash escaped) double quotes
|
||||
///
|
||||
/// In quoted strings in DOT, the only escaped character is double-quote (").
|
||||
/// That is, in quoted strings, the dyad \" is converted to "; all other
|
||||
/// characters are left unchanged. In particular, \\ remains \\. Layout engines
|
||||
/// may apply additional escape sequences.
|
||||
fn escape_userid(userid: &UserID) -> String {
|
||||
format!("{}", userid)
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\"", "\\\"")
|
||||
}
|
||||
|
||||
/// Add a legend graph to an existing Scope
|
||||
///
|
||||
/// The legend graph provides information on color coding of the various nodes,
|
||||
/// as well as the targeted trust amount, whether looking at gossip and whether
|
||||
/// the data is used as a certification network.
|
||||
fn add_legend_graph(
|
||||
container: &mut Scope,
|
||||
required_amount: usize,
|
||||
gossip: bool,
|
||||
certification_network: bool,
|
||||
) {
|
||||
let mut legend = container.cluster();
|
||||
legend.set_label("Graph legend");
|
||||
legend
|
||||
.node_attributes()
|
||||
.set("shape", "note", false)
|
||||
.set_fill_color(dot_writer::Color::White);
|
||||
let mut legend_edges = Vec::new();
|
||||
|
||||
legend_edges.push(format!("\"Trust root\""));
|
||||
legend
|
||||
.node_named(legend_edges.last().expect("Just added a legend node."))
|
||||
.set("fillcolor", DOT_ROOT_FILL_COLOR, false);
|
||||
|
||||
legend_edges.push(format!("\"Intermediate introducer\""));
|
||||
legend
|
||||
.node_named(legend_edges.last().expect("Just added a legend node."))
|
||||
.set("fillcolor", DOT_NODE_FILL_COLOR, false);
|
||||
|
||||
legend_edges.push(format!("\"Authenticated target\""));
|
||||
legend
|
||||
.node_named(legend_edges.last().expect("Just added a legend node."))
|
||||
.set("fillcolor", DOT_TARGET_OK_FILL_COLOR, false);
|
||||
|
||||
legend_edges.push(format!("\"Unauthenticated target\""));
|
||||
legend
|
||||
.node_named(legend_edges.last().expect("Just added a legend node."))
|
||||
.set("fillcolor", DOT_TARGET_FAIL_FILL_COLOR, false);
|
||||
|
||||
legend_edges.push(format!(
|
||||
"\"target trust amount: {}%\"",
|
||||
(100 * required_amount) / FULLY_TRUSTED,
|
||||
));
|
||||
legend.node_named(legend_edges.last().expect("Just added a legend node."));
|
||||
|
||||
if gossip {
|
||||
legend_edges.push(String::from("gossip"));
|
||||
legend.node_named(
|
||||
legend_edges.last().expect("Just added a legend node."),
|
||||
);
|
||||
}
|
||||
|
||||
if certification_network {
|
||||
legend_edges.push(String::from("certification network"));
|
||||
legend.node_named(
|
||||
legend_edges.last().expect("Just added a legend node."),
|
||||
);
|
||||
}
|
||||
|
||||
// internal edges are used for arranging nodes in the cluster
|
||||
// and are therefore invisible
|
||||
let mut edge_attributes = legend.edge_attributes();
|
||||
edge_attributes
|
||||
.set_font_size(0.1)
|
||||
.set_style(dot_writer::Style::Invisible)
|
||||
.set_arrow_size(0.1)
|
||||
.set_arrow_tail(dot_writer::ArrowType::InvEmpty);
|
||||
drop(edge_attributes);
|
||||
|
||||
// add edges for all legend nodes so that they can be arranged within the
|
||||
// cluster
|
||||
for edge in legend_edges.windows(2) {
|
||||
legend.edge(&edge[0], &edge[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/// The output representation of a certification
|
||||
///
|
||||
/// An OutputCertification tracks the issuer's and target's Fingerprint, as well
|
||||
/// as the target's UserID.
|
||||
/// Furthermore, the trust amount and depth of the certification and its
|
||||
/// (optional) creation and expiry timestamps are covered.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OutputCertification {
|
||||
issuer_fingerprint: Fingerprint,
|
||||
target_fingerprint: Fingerprint,
|
||||
target_uid: UserID,
|
||||
creation: SystemTime,
|
||||
expiry: Option<SystemTime>,
|
||||
trust_amount: usize,
|
||||
depth: Depth,
|
||||
}
|
||||
|
||||
impl OutputCertification {
|
||||
pub fn new(
|
||||
issuer_fingerprint: Fingerprint,
|
||||
target_fingerprint: Fingerprint,
|
||||
target_uid: UserID,
|
||||
creation: SystemTime,
|
||||
expiry: Option<SystemTime>,
|
||||
trust_amount: usize,
|
||||
depth: Depth,
|
||||
) -> Self {
|
||||
Self {
|
||||
issuer_fingerprint,
|
||||
target_fingerprint,
|
||||
target_uid,
|
||||
creation,
|
||||
expiry,
|
||||
trust_amount,
|
||||
depth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The output representation of a Path
|
||||
///
|
||||
/// A number uniquely identifies an OutputPath amongst others.
|
||||
#[derive(Debug)]
|
||||
pub struct OutputPath {
|
||||
// The unique number of the OutputPath (an OutputNetwork provides between
|
||||
// 0 and n OutputPaths per target Fingerprint)
|
||||
number: usize,
|
||||
certifications: Vec<OutputCertification>,
|
||||
}
|
||||
|
||||
impl OutputPath {
|
||||
pub fn new(number: usize) -> Self {
|
||||
Self {
|
||||
number,
|
||||
certifications: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the certifications of the OutputPath in an iterator
|
||||
pub fn certifications(&self) -> impl Iterator<Item = &OutputCertification> {
|
||||
self.certifications.iter()
|
||||
}
|
||||
|
||||
/// Add an OutputCertification to the list of certifications
|
||||
pub fn add_certification(
|
||||
&mut self,
|
||||
issuer_fingerprint: Fingerprint,
|
||||
target_fingerprint: Fingerprint,
|
||||
target_uid: UserID,
|
||||
creation: SystemTime,
|
||||
expiry: Option<SystemTime>,
|
||||
trust_amount: usize,
|
||||
depth: Depth,
|
||||
) {
|
||||
self.certifications.push(OutputCertification::new(
|
||||
issuer_fingerprint,
|
||||
target_fingerprint,
|
||||
target_uid,
|
||||
creation,
|
||||
expiry,
|
||||
trust_amount,
|
||||
depth,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// The output representation of a cert
|
||||
///
|
||||
/// It tracks the Fingerprint and UserIDs (as well as their trust amount and
|
||||
/// indicator whether they are a target of a Path) and an indicator whether the
|
||||
/// Fingerprint serves as trust root.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OutputCert {
|
||||
keyhandle: Fingerprint,
|
||||
/// HashMap tracking UserID and accompanying trust amount and whether the
|
||||
/// UserID is the target of a Path
|
||||
userids: HashMap<UserID, (usize, bool)>,
|
||||
// does the cert serve as trust root?
|
||||
is_root: bool,
|
||||
}
|
||||
|
||||
impl OutputCert {
|
||||
pub fn new(
|
||||
keyhandle: &Fingerprint,
|
||||
userid: UserID,
|
||||
trust_amount: usize,
|
||||
is_root: bool,
|
||||
is_target: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
keyhandle: keyhandle.clone(),
|
||||
userids: HashMap::from([(userid, (trust_amount, is_target))]),
|
||||
is_root,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the data for a provided UserID
|
||||
pub fn get_userid_data(&self, userid: &UserID) -> Option<&(usize, bool)> {
|
||||
self.userids.get(userid)
|
||||
}
|
||||
|
||||
/// Add a UserID and its associated data to the list of userids
|
||||
pub fn add_userid_data(&mut self, userid: UserID, data: (usize, bool)) {
|
||||
self.userids.insert(userid, data);
|
||||
}
|
||||
|
||||
/// Update the trust amount of a UserID
|
||||
///
|
||||
/// If no matching UserID is found, it is first created
|
||||
/// (the bool indicating whether the UserID is the target of a Path is set
|
||||
/// to false)
|
||||
pub fn update_trust_amount(
|
||||
&mut self,
|
||||
userid: &UserID,
|
||||
trust_amount: usize,
|
||||
) {
|
||||
match self.userids.get_mut(userid) {
|
||||
Some(userid_data) => {
|
||||
userid_data.0 = trust_amount;
|
||||
}
|
||||
None => {
|
||||
self.add_userid_data(userid.to_owned(), (trust_amount, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update whether the OutputCert serves as trust root
|
||||
///
|
||||
/// Once this value is set to true it is not set to false anymore
|
||||
pub fn set_is_root(&mut self, is_root: bool) {
|
||||
self.is_root = self.is_root || is_root
|
||||
}
|
||||
|
||||
/// Update whether a UserID is the target of a Path
|
||||
///
|
||||
/// Once this value is set to true it is not set to false anymore
|
||||
pub fn set_is_target(&mut self, userid: &UserID, is_target: bool) {
|
||||
if let Some(userid_data) = self.userids.get_mut(userid) {
|
||||
userid_data.1 = userid_data.1 || is_target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The output representation of a Network
|
||||
///
|
||||
/// An OutputNetwork tracks the required trust amount for the network, a list
|
||||
/// of OutputCerts in the network, and a hash map containing key-value pairs
|
||||
/// consisting of Path target Fingerprints and lists of OutputPaths.
|
||||
#[derive(Debug)]
|
||||
pub struct OutputNetwork {
|
||||
required_amount: usize,
|
||||
gossip: bool,
|
||||
certification_network: bool,
|
||||
certs: Vec<OutputCert>,
|
||||
paths: HashMap<Fingerprint, Vec<OutputPath>>,
|
||||
}
|
||||
|
||||
impl OutputNetwork {
|
||||
pub fn new(
|
||||
required_amount: usize,
|
||||
gossip: bool,
|
||||
certification_network: bool,
|
||||
) -> Self {
|
||||
let certs = Vec::new();
|
||||
let paths = HashMap::new();
|
||||
OutputNetwork {
|
||||
required_amount,
|
||||
gossip,
|
||||
certification_network,
|
||||
certs,
|
||||
paths,
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to add an OutputCert and return it
|
||||
///
|
||||
/// If no OutputCert identified by keyhandle exists, one is created.
|
||||
pub fn try_add_cert(
|
||||
&mut self,
|
||||
keyhandle: Fingerprint,
|
||||
userid: UserID,
|
||||
trust_amount: usize,
|
||||
is_root: bool,
|
||||
is_target: bool,
|
||||
) -> &OutputCert {
|
||||
// take `self.certs` out of `self` to help the borrow checker
|
||||
let mut certs = std::mem::replace(&mut self.certs, vec![]);
|
||||
if let Some(cert) = Self::get_mut_cert(&mut certs, &keyhandle) {
|
||||
if cert.get_userid_data(&userid).is_none() {
|
||||
cert.add_userid_data(
|
||||
userid.to_owned(),
|
||||
(trust_amount, is_target),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
certs.push(OutputCert::new(
|
||||
&keyhandle,
|
||||
userid,
|
||||
trust_amount,
|
||||
is_root,
|
||||
is_target,
|
||||
));
|
||||
}
|
||||
self.certs = certs;
|
||||
|
||||
self.get_cert(&keyhandle).expect("A cert was just added")
|
||||
}
|
||||
|
||||
/// Return an OutputCert matching a Fingerprint
|
||||
pub fn get_cert(&self, keyhandle: &Fingerprint) -> Option<&OutputCert> {
|
||||
self.certs.iter().find(|x| &x.keyhandle == keyhandle)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an OutputCert matching a Fingerprint
|
||||
pub fn get_mut_cert<'a>(
|
||||
certs: &'a mut Vec<OutputCert>,
|
||||
keyhandle: &Fingerprint,
|
||||
) -> Option<&'a mut OutputCert> {
|
||||
if let Some(i) = certs.iter().position(|x| &x.keyhandle == keyhandle) {
|
||||
Some(&mut certs[i])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an OutputPath matching a Fingerprint (of an
|
||||
/// OutputCert) and a number (of a specific OutputPath)
|
||||
pub fn get_mut_path(
|
||||
&mut self,
|
||||
fingerprint: &Fingerprint,
|
||||
number: usize,
|
||||
) -> Option<&mut OutputPath> {
|
||||
if let Some(path_list) = self.paths.get_mut(fingerprint) {
|
||||
path_list.iter_mut().filter(|x| x.number == number).last()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an empty OutputPath to the list of paths using a Fingerprint and a
|
||||
/// number
|
||||
pub fn add_path(&mut self, fingerprint: &Fingerprint, number: usize) {
|
||||
if let Some(path_list) = self.paths.get_mut(fingerprint) {
|
||||
path_list.push(OutputPath::new(number));
|
||||
} else {
|
||||
let mut path_list = Vec::new();
|
||||
path_list.push(OutputPath::new(number));
|
||||
self.paths.insert(fingerprint.clone(), path_list);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add an OutputCertification to a list of OutputPaths matching a
|
||||
/// Fingerprint and a path number
|
||||
pub fn add_certification(
|
||||
&mut self,
|
||||
path_target_fingerprint: &Fingerprint,
|
||||
path_number: usize,
|
||||
target_fingerprint: Fingerprint,
|
||||
target_uid: UserID,
|
||||
issuer_fingerprint: Fingerprint,
|
||||
creation: SystemTime,
|
||||
expiry: Option<SystemTime>,
|
||||
trust_amount: usize,
|
||||
depth: Depth,
|
||||
) {
|
||||
self.add_path(&path_target_fingerprint, path_number);
|
||||
if let Some(path) =
|
||||
self.get_mut_path(&path_target_fingerprint, path_number)
|
||||
{
|
||||
path.add_certification(
|
||||
issuer_fingerprint,
|
||||
target_fingerprint,
|
||||
target_uid,
|
||||
creation,
|
||||
expiry,
|
||||
trust_amount,
|
||||
depth,
|
||||
);
|
||||
} else {
|
||||
panic!(
|
||||
"There is no path associated with keyhandle {} and number {}!",
|
||||
target_fingerprint, path_number
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the OutputCerts in an Iterator
|
||||
pub fn certs(&self) -> impl Iterator<Item = &OutputCert> {
|
||||
self.certs.iter()
|
||||
}
|
||||
|
||||
/// Return an Iterator of tuples of Fingerprint and OutputPaths
|
||||
pub fn paths(
|
||||
&self,
|
||||
) -> impl Iterator<Item = (&Fingerprint, &Vec<OutputPath>)> {
|
||||
self.paths.iter()
|
||||
}
|
||||
|
||||
/// Create the edge label for a certification
|
||||
fn create_certification_edge_label(
|
||||
trust_amount: usize,
|
||||
creation: Option<SystemTime>,
|
||||
expiry: Option<SystemTime>,
|
||||
depth: Option<Depth>,
|
||||
) -> String {
|
||||
let mut certification_label = String::new();
|
||||
|
||||
if trust_amount < FULLY_TRUSTED {
|
||||
certification_label.push_str(&format!(
|
||||
"partially certified (amount: {} of 120)",
|
||||
trust_amount,
|
||||
));
|
||||
} else {
|
||||
certification_label.push_str("certified");
|
||||
}
|
||||
|
||||
if let Some(time) = creation {
|
||||
certification_label.push_str(&format!(
|
||||
" on {}",
|
||||
chrono::DateTime::<chrono::Utc>::from(time).format("%Y-%m-%d")
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(time) = expiry {
|
||||
certification_label.push_str(&format!(
|
||||
" (expiry: {})",
|
||||
chrono::DateTime::<chrono::Utc>::from(time).format("%Y-%m-%d")
|
||||
));
|
||||
}
|
||||
|
||||
if creation.is_some() || expiry.is_some() {
|
||||
certification_label.push_str("\n");
|
||||
}
|
||||
|
||||
match depth {
|
||||
Some(Depth::Limit(depth)) => {
|
||||
if depth > 0 {
|
||||
certification_label.push_str(" as a");
|
||||
if trust_amount != FULLY_TRUSTED {
|
||||
certification_label.push_str(&format!(
|
||||
" partially trusted ({} of 120)",
|
||||
trust_amount,
|
||||
));
|
||||
} else {
|
||||
certification_label.push_str(" fully trusted");
|
||||
}
|
||||
if depth == 1 {
|
||||
certification_label.push_str(&format!(
|
||||
" introducer (depth: {})",
|
||||
depth,
|
||||
));
|
||||
} else {
|
||||
certification_label.push_str(&format!(
|
||||
" meta-introducer (depth: {})",
|
||||
depth,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Depth::Unconstrained) => {
|
||||
certification_label.push_str(" as a");
|
||||
if trust_amount != FULLY_TRUSTED {
|
||||
certification_label.push_str(&format!(
|
||||
" partially trusted ({} of 120)",
|
||||
trust_amount,
|
||||
));
|
||||
} else {
|
||||
certification_label.push_str(" fully trusted");
|
||||
}
|
||||
certification_label.push_str(" issuer (depth: infinite)");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
certification_label
|
||||
}
|
||||
|
||||
/// Write the OutputNetwork to an output (in DOT format)
|
||||
pub fn dot(&self, writer: &mut dyn Write) -> Result<()> {
|
||||
let mut output_bytes = Vec::new();
|
||||
let mut dot_writer = DotWriter::from(&mut output_bytes);
|
||||
dot_writer.set_pretty_print(true);
|
||||
|
||||
// the base graph with all relevant node settings
|
||||
let mut base_graph = dot_writer.digraph();
|
||||
base_graph
|
||||
.node_attributes()
|
||||
.set_shape(Shape::Rectangle)
|
||||
.set_style(dot_writer::Style::Filled);
|
||||
|
||||
// container cluster for all further clusters and nodes
|
||||
let mut container = base_graph.cluster();
|
||||
|
||||
for target_cert in self.certs() {
|
||||
let mut cert_cluster = container.cluster();
|
||||
cert_cluster.set("color", DOT_NODE_FILL_COLOR, false);
|
||||
|
||||
// internal edges are used for arranging nodes in the cluster
|
||||
// and are therefore invisible
|
||||
let mut edge_attributes = cert_cluster.edge_attributes();
|
||||
edge_attributes.set_style(dot_writer::Style::Invisible);
|
||||
drop(edge_attributes);
|
||||
|
||||
// sort the UserIDs and accompanying data by reverse amount and
|
||||
// UserID
|
||||
let mut userid_data =
|
||||
target_cert.userids.iter().collect::<Vec<_>>();
|
||||
userid_data.sort_by(|a, b| b.1 .0.cmp(&a.1 .0).then(a.0.cmp(&b.0)));
|
||||
|
||||
// add all edges between Fingerprints and the foreign (to their own
|
||||
// key) UserIDs they are certifying
|
||||
let mut cert_edges = Vec::new();
|
||||
for (userid, (trust_amount, is_target)) in userid_data {
|
||||
let node_name = format!(
|
||||
"\"{}_{}\"",
|
||||
&target_cert.keyhandle,
|
||||
escape_userid(&userid)
|
||||
);
|
||||
|
||||
cert_edges.push(node_name.clone());
|
||||
|
||||
let mut node = cert_cluster.node_named(&node_name);
|
||||
// if it is a trust root or not a target of a path, we do not
|
||||
// need to add trust amount
|
||||
if target_cert.is_root || !is_target {
|
||||
node.set_label(&format!("{}", escape_userid(&userid)));
|
||||
} else {
|
||||
node.set_label(&format!(
|
||||
"{}\n({}%)",
|
||||
escape_userid(&userid),
|
||||
(trust_amount * 100) / FULLY_TRUSTED,
|
||||
));
|
||||
}
|
||||
node.set(
|
||||
"fillcolor",
|
||||
if *is_target {
|
||||
if trust_amount >= &self.required_amount {
|
||||
DOT_TARGET_OK_FILL_COLOR
|
||||
} else {
|
||||
DOT_TARGET_FAIL_FILL_COLOR
|
||||
}
|
||||
} else {
|
||||
DOT_NODE_FILL_COLOR
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// add node for Fingerprint
|
||||
let node_name = format!("\"{}\"", &target_cert.keyhandle);
|
||||
cert_edges.push(node_name.clone());
|
||||
let mut keyhandle_node = cert_cluster.node_named(&node_name);
|
||||
keyhandle_node.set_label(&format!("{}", target_cert.keyhandle));
|
||||
if target_cert.is_root {
|
||||
keyhandle_node.set("fillcolor", DOT_ROOT_FILL_COLOR, false);
|
||||
}
|
||||
drop(keyhandle_node);
|
||||
|
||||
// add edges for all UserID and the Fingerprint nodes so that they
|
||||
// can be arranged within the cluster
|
||||
for edge in cert_edges.windows(2) {
|
||||
cert_cluster.edge(&edge[0], &edge[1]);
|
||||
}
|
||||
}
|
||||
|
||||
// add edges for all certifications of Fingerprints on UserIDs
|
||||
let mut known_certifications = BTreeSet::new();
|
||||
for (_keyhandle, paths) in self.paths() {
|
||||
for path in paths.iter() {
|
||||
for certification in path.certifications() {
|
||||
let entry = format!(
|
||||
"{}_{}_{}",
|
||||
&certification.issuer_fingerprint,
|
||||
&certification.target_fingerprint,
|
||||
&certification.target_uid,
|
||||
);
|
||||
|
||||
if !known_certifications.contains(&entry) {
|
||||
// as gossip output is likely already very convoluted,
|
||||
// do not include self-signatures
|
||||
if !(self.gossip
|
||||
&& &certification.issuer_fingerprint
|
||||
== &certification.target_fingerprint)
|
||||
|| !self.gossip
|
||||
{
|
||||
let edge = container.edge(
|
||||
format!(
|
||||
"\"{}\"",
|
||||
&certification.issuer_fingerprint
|
||||
),
|
||||
format!(
|
||||
"\"{}_{}\"",
|
||||
&certification.target_fingerprint,
|
||||
escape_userid(&certification.target_uid),
|
||||
),
|
||||
);
|
||||
let certification_label =
|
||||
OutputNetwork::create_certification_edge_label(
|
||||
certification.trust_amount,
|
||||
Some(certification.creation),
|
||||
certification.expiry,
|
||||
Some(certification.depth),
|
||||
);
|
||||
// use xlabel when generating gossip output,
|
||||
// so it is less likely to run into init_rank
|
||||
// issues: https://gitlab.com/graphviz/graphviz/-/issues/1213
|
||||
if self.gossip {
|
||||
edge.attributes()
|
||||
.set("xlabel", &certification_label, true)
|
||||
.set("decorate", "true", false);
|
||||
} else {
|
||||
edge.attributes()
|
||||
.set_label(&certification_label)
|
||||
.set("decorate", "true", false);
|
||||
}
|
||||
|
||||
known_certifications.insert(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add a legend graph
|
||||
add_legend_graph(
|
||||
&mut container,
|
||||
self.required_amount,
|
||||
self.gossip,
|
||||
self.certification_network,
|
||||
);
|
||||
|
||||
drop(container);
|
||||
drop(base_graph);
|
||||
if let Ok(data) = String::from_utf8(output_bytes) {
|
||||
writeln!(
|
||||
writer,
|
||||
"// Created by {} {}",
|
||||
env!("CARGO_BIN_NAME"),
|
||||
env!("CARGO_PKG_VERSION")
|
||||
)?;
|
||||
writeln!(writer, "{}", DOT_INSTRUCTIONS)?;
|
||||
writeln!(writer, "{}", data)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// The DOT specific implementation of an OutputNetwork representation
|
||||
///
|
||||
/// DotOutputNetwork tracks an OutputNetwork and the roots for it.
|
||||
pub struct DotOutputNetwork<'a> {
|
||||
output_network: OutputNetwork,
|
||||
roots: &'a Roots,
|
||||
}
|
||||
|
||||
impl<'a> DotOutputNetwork<'a> {
|
||||
/// Create a new DotOutputNetwork
|
||||
pub fn new(
|
||||
required_amount: usize,
|
||||
roots: &'a Roots,
|
||||
gossip: bool,
|
||||
certification_network: bool,
|
||||
) -> Self {
|
||||
let output_network =
|
||||
OutputNetwork::new(required_amount, gossip, certification_network);
|
||||
Self {
|
||||
output_network,
|
||||
roots,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OutputType for DotOutputNetwork<'a> {
|
||||
/// Add paths to the OutputNetwork
|
||||
fn add_paths(
|
||||
&mut self,
|
||||
paths: Vec<(Path, usize)>,
|
||||
fingerprint: &Fingerprint,
|
||||
userid: &UserID,
|
||||
aggregated_amount: usize,
|
||||
) -> Result<()> {
|
||||
match OutputNetwork::get_mut_cert(
|
||||
&mut self.output_network.certs,
|
||||
&fingerprint.to_owned(),
|
||||
) {
|
||||
Some(cert) => {
|
||||
cert.update_trust_amount(userid, aggregated_amount);
|
||||
cert.set_is_root(self.roots.is_root(fingerprint));
|
||||
cert.set_is_target(userid, true);
|
||||
}
|
||||
None => {
|
||||
self.output_network.try_add_cert(
|
||||
fingerprint.to_owned(),
|
||||
userid.to_owned(),
|
||||
aggregated_amount,
|
||||
self.roots.is_root(fingerprint),
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (path_number, (path, _path_trust_amount)) in
|
||||
paths.iter().enumerate()
|
||||
{
|
||||
let issuer_fingerprint = path.root().fingerprint();
|
||||
|
||||
if self.output_network.get_cert(&issuer_fingerprint).is_none() {
|
||||
let certifier_userid = if path.certifications().count() == 0 {
|
||||
userid
|
||||
} else if let Some(userid) = path.root().primary_userid() {
|
||||
userid.userid()
|
||||
} else {
|
||||
userid
|
||||
};
|
||||
self.output_network.try_add_cert(
|
||||
issuer_fingerprint,
|
||||
certifier_userid.to_owned(),
|
||||
0,
|
||||
self.roots.is_root(&path.root().fingerprint()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
// sort the certifications by reverse amount and issuer
|
||||
let mut certifications = path.certifications().collect::<Vec<_>>();
|
||||
certifications.sort_by(|a, b| {
|
||||
b.amount().cmp(&a.amount()).then(
|
||||
a.issuer().fingerprint().cmp(&b.issuer().fingerprint()),
|
||||
)
|
||||
});
|
||||
|
||||
for certification in certifications {
|
||||
let certification_target_userid =
|
||||
if let Some(target_userid) = certification.userid() {
|
||||
target_userid
|
||||
} else {
|
||||
userid
|
||||
};
|
||||
|
||||
let target_cert_is_root =
|
||||
self.roots.is_root(certification.target().fingerprint());
|
||||
|
||||
self.output_network.try_add_cert(
|
||||
certification.target().fingerprint(),
|
||||
certification_target_userid.to_owned(),
|
||||
0,
|
||||
target_cert_is_root,
|
||||
false,
|
||||
);
|
||||
|
||||
self.output_network.add_certification(
|
||||
&path.target().fingerprint(),
|
||||
path_number,
|
||||
certification.target().fingerprint(),
|
||||
certification_target_userid.to_owned(),
|
||||
certification.issuer().fingerprint(),
|
||||
certification.creation_time(),
|
||||
certification.expiration_time(),
|
||||
certification.amount(),
|
||||
certification.depth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the DotOutputNetwork to output
|
||||
fn finalize(&mut self) -> Result<()> {
|
||||
self.output_network.dot(&mut std::io::stdout())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
261
src/commands/wot/output/human_readable.rs
Normal file
261
src/commands/wot/output/human_readable.rs
Normal file
@ -0,0 +1,261 @@
|
||||
use anyhow::Error;
|
||||
|
||||
use openpgp::packet::UserID;
|
||||
use openpgp::Fingerprint;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::Result;
|
||||
use sequoia_openpgp as openpgp;
|
||||
|
||||
use sequoia_wot as wot;
|
||||
use wot::Path;
|
||||
use wot::PathLints;
|
||||
use wot::FULLY_TRUSTED;
|
||||
use wot::PARTIALLY_TRUSTED;
|
||||
|
||||
use crate::error_chain;
|
||||
use crate::commands::wot::output::OutputType;
|
||||
|
||||
/// Prints a Path Error
|
||||
pub fn print_path_error(err: Error) {
|
||||
println!("└ Checking path: {}", err);
|
||||
}
|
||||
|
||||
/// Prints information of a Path for a target UserID associated with a KeyHandle
|
||||
pub fn print_path_header(
|
||||
target_kh: &KeyHandle,
|
||||
target_userid: &UserID,
|
||||
amount: usize,
|
||||
required_amount: usize,
|
||||
) {
|
||||
println!(
|
||||
"[{}] {} {}: {} authenticated ({}%)",
|
||||
if amount >= required_amount {
|
||||
"✓"
|
||||
} else {
|
||||
" "
|
||||
},
|
||||
target_kh,
|
||||
String::from_utf8_lossy(target_userid.value()),
|
||||
if amount >= 2 * FULLY_TRUSTED {
|
||||
"doubly"
|
||||
} else if amount >= FULLY_TRUSTED {
|
||||
"fully"
|
||||
} else if amount >= PARTIALLY_TRUSTED {
|
||||
"partially"
|
||||
} else if amount > 0 {
|
||||
"marginally"
|
||||
} else {
|
||||
"not"
|
||||
},
|
||||
(amount * 100) / FULLY_TRUSTED
|
||||
);
|
||||
}
|
||||
|
||||
/// Prints information on a Path for a UserID
|
||||
pub fn print_path(path: &PathLints, target_userid: &UserID, prefix: &str) {
|
||||
let certification_count = path.certifications().count();
|
||||
|
||||
print!("{}◯ {}", prefix, path.root().key_handle());
|
||||
if certification_count == 0 {
|
||||
print!(" {:?}", String::from_utf8_lossy(target_userid.value()));
|
||||
} else if let Some(userid) = path.root().primary_userid() {
|
||||
print!(" ({:?})", String::from_utf8_lossy(userid.value()));
|
||||
}
|
||||
println!("");
|
||||
|
||||
for (last, (cert, certification)) in path
|
||||
.certs()
|
||||
.zip(path.certifications())
|
||||
.enumerate()
|
||||
.map(|(j, c)| {
|
||||
if j + 1 == certification_count {
|
||||
(true, c)
|
||||
} else {
|
||||
(false, c)
|
||||
}
|
||||
})
|
||||
{
|
||||
print!("{}│ ", prefix);
|
||||
if let Some(certification) = certification.certification() {
|
||||
if certification.amount() < FULLY_TRUSTED {
|
||||
print!(
|
||||
" partially certified (amount: {} of 120)",
|
||||
certification.amount()
|
||||
);
|
||||
} else {
|
||||
print!(" certified");
|
||||
}
|
||||
|
||||
if last {
|
||||
print!(" the following binding");
|
||||
} else {
|
||||
print!(" the following certificate");
|
||||
}
|
||||
|
||||
print!(
|
||||
" on {}",
|
||||
chrono::DateTime::<chrono::Utc>::from(
|
||||
certification.creation_time()
|
||||
)
|
||||
.format("%Y-%m-%d")
|
||||
);
|
||||
if let Some(e) = certification.expiration_time() {
|
||||
print!(
|
||||
" (expiry: {})",
|
||||
chrono::DateTime::<chrono::Utc>::from(e).format("%Y-%m-%d")
|
||||
);
|
||||
}
|
||||
if certification.depth() > 0.into() {
|
||||
print!(" as a");
|
||||
if certification.amount() != FULLY_TRUSTED {
|
||||
print!(
|
||||
" partially trusted ({} of 120)",
|
||||
certification.amount()
|
||||
);
|
||||
} else {
|
||||
print!(" fully trusted");
|
||||
}
|
||||
if certification.depth() == 1.into() {
|
||||
print!(" introducer (depth: {})", certification.depth());
|
||||
} else {
|
||||
print!(
|
||||
" meta-introducer (depth: {})",
|
||||
certification.depth()
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print!(" No adequate certification found.");
|
||||
}
|
||||
println!("");
|
||||
|
||||
for err in cert.errors().iter().chain(cert.lints()) {
|
||||
for (i, msg) in error_chain(err).into_iter().enumerate() {
|
||||
println!(
|
||||
"{}│ {}{}",
|
||||
prefix,
|
||||
if i == 0 { "" } else { " " },
|
||||
msg
|
||||
);
|
||||
}
|
||||
}
|
||||
for err in certification.errors().iter().chain(certification.lints()) {
|
||||
for (i, msg) in error_chain(err).into_iter().enumerate() {
|
||||
println!(
|
||||
"{}│ {}{}",
|
||||
prefix,
|
||||
if i == 0 { "" } else { " " },
|
||||
msg
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
print!(
|
||||
"{}{} {}",
|
||||
prefix,
|
||||
if last { "└" } else { "├" },
|
||||
certification.target()
|
||||
);
|
||||
|
||||
if last {
|
||||
print!(" {:?}", String::from_utf8_lossy(target_userid.value()));
|
||||
} else {
|
||||
if let Some(userid) =
|
||||
certification.target_cert().and_then(|c| c.primary_userid())
|
||||
{
|
||||
print!(" ({:?})", String::from_utf8_lossy(userid.value()));
|
||||
}
|
||||
}
|
||||
println!("");
|
||||
|
||||
if last {
|
||||
let target = path.certs().last().expect("have one");
|
||||
for err in target.errors().iter().chain(target.lints()) {
|
||||
for (i, msg) in error_chain(err).into_iter().enumerate() {
|
||||
println!(
|
||||
"{} {}{}",
|
||||
prefix,
|
||||
if i == 0 { "" } else { " " },
|
||||
msg
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("");
|
||||
}
|
||||
|
||||
/// The human-readable specific implementation of an OutputNetwork
|
||||
///
|
||||
/// HumanReadableOutputNetwork tracks the target trust amount for the network
|
||||
/// and whether it displays "gossip".
|
||||
pub struct HumanReadableOutputNetwork {
|
||||
gossip: bool,
|
||||
required_amount: usize,
|
||||
}
|
||||
|
||||
impl HumanReadableOutputNetwork {
|
||||
/// Create a new HumanReadableOutputNetwork
|
||||
pub fn new(required_amount: usize, gossip: bool) -> Self {
|
||||
Self {
|
||||
required_amount,
|
||||
gossip,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OutputType for HumanReadableOutputNetwork {
|
||||
/// Add paths to the OutputNetwork and display them directly
|
||||
fn add_paths(
|
||||
&mut self,
|
||||
paths: Vec<(Path, usize)>,
|
||||
fingerprint: &Fingerprint,
|
||||
userid: &UserID,
|
||||
aggregated_amount: usize,
|
||||
) -> Result<()> {
|
||||
let kh = KeyHandle::from(fingerprint);
|
||||
if !self.gossip {
|
||||
print_path_header(
|
||||
&kh,
|
||||
userid,
|
||||
aggregated_amount,
|
||||
self.required_amount,
|
||||
);
|
||||
}
|
||||
for (i, (path, amount)) in paths.iter().enumerate() {
|
||||
let prefix = if self.gossip {
|
||||
print_path_header(
|
||||
&kh,
|
||||
userid,
|
||||
aggregated_amount,
|
||||
self.required_amount,
|
||||
);
|
||||
" "
|
||||
} else {
|
||||
if !self.gossip && paths.len() > 1 {
|
||||
println!(
|
||||
" Path #{} of {}, trust amount {}:",
|
||||
i + 1,
|
||||
paths.len(),
|
||||
amount
|
||||
);
|
||||
" "
|
||||
} else {
|
||||
" "
|
||||
}
|
||||
};
|
||||
|
||||
print_path(&path.into(), userid, prefix)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the HumanReadableOutputNetwork to output
|
||||
///
|
||||
/// This function does in fact nothing as we are printing directly in
|
||||
/// add_paths().
|
||||
fn finalize(&mut self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
92
src/log.rs
Normal file
92
src/log.rs
Normal file
@ -0,0 +1,92 @@
|
||||
#![allow(unused_macros)]
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
// Like eprintln!
|
||||
macro_rules! log {
|
||||
($dst:expr $(,)?) => (
|
||||
eprintln!("{}", $dst)
|
||||
);
|
||||
($dst:expr, $($arg:tt)*) => (
|
||||
eprintln!("{}", std::format!($dst, $($arg)*))
|
||||
);
|
||||
}
|
||||
|
||||
// The indent level. It is increased with each call to tracer and
|
||||
// decremented when the tracer goes out of scope.
|
||||
thread_local! {
|
||||
pub static INDENT_LEVEL: RefCell<usize> = RefCell::new(0);
|
||||
}
|
||||
|
||||
// Like eprintln!, but the first argument is a boolean, which
|
||||
// indicates if the string should actually be printed.
|
||||
macro_rules! trace {
|
||||
( $TRACE:expr, $fmt:expr, $($pargs:expr),* ) => {
|
||||
if $TRACE {
|
||||
let indent_level = crate::log::INDENT_LEVEL.with(|i| {
|
||||
*i.borrow()
|
||||
});
|
||||
|
||||
let ws = " ";
|
||||
|
||||
log!("{}{}",
|
||||
&ws[0..std::cmp::min(ws.len(), std::cmp::max(1, indent_level) - 1)],
|
||||
format!($fmt, $($pargs),*));
|
||||
}
|
||||
};
|
||||
( $TRACE:expr, $fmt:expr ) => {
|
||||
trace!($TRACE, $fmt, );
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! tracer {
|
||||
( $TRACE:expr, $func:expr ) => {
|
||||
// Currently, Rust doesn't support $( ... ) in a nested
|
||||
// macro's definition. See:
|
||||
// https://users.rust-lang.org/t/nested-macros-issue/8348/2
|
||||
#[allow(unused)]
|
||||
macro_rules! t {
|
||||
( $fmt:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, $fmt) };
|
||||
( $fmt:expr, $a:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a)) };
|
||||
( $fmt:expr, $a:expr, $b:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j)) };
|
||||
( $fmt:expr, $a:expr, $b:expr, $c:expr, $d:expr, $e:expr, $f:expr, $g:expr, $h:expr, $i:expr, $j:expr, $k:expr ) =>
|
||||
{ trace!($TRACE, "{}: {}", $func, format!($fmt, $a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k)) };
|
||||
}
|
||||
struct Indent {}
|
||||
impl Indent {
|
||||
fn init() -> Self {
|
||||
crate::log::INDENT_LEVEL.with(|i| {
|
||||
i.replace_with(|i| *i + 1);
|
||||
});
|
||||
Indent {}
|
||||
}
|
||||
}
|
||||
impl Drop for Indent {
|
||||
fn drop(&mut self) {
|
||||
crate::log::INDENT_LEVEL.with(|i| {
|
||||
i.replace_with(|i| *i - 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
let _indent = Indent::init();
|
||||
}
|
||||
}
|
@ -27,6 +27,13 @@ pub enum OutputFormat {
|
||||
|
||||
/// Output as JSON.
|
||||
Json,
|
||||
|
||||
/// Output as DOT.
|
||||
///
|
||||
/// This format is supported by a few commands that emit a
|
||||
/// graphical network. In particular, the \"sq wot\" subcommands
|
||||
/// can emit this format.
|
||||
DOT,
|
||||
}
|
||||
|
||||
impl FromStr for OutputFormat {
|
||||
@ -36,6 +43,7 @@ impl FromStr for OutputFormat {
|
||||
match s {
|
||||
"human-readable" => Ok(Self::HumanReadable),
|
||||
"json" => Ok(Self::Json),
|
||||
"dot" => Ok(Self::DOT),
|
||||
_ => Err(anyhow!("unknown output format {:?}", s)),
|
||||
}
|
||||
}
|
||||
@ -181,14 +189,14 @@ impl Model {
|
||||
match self {
|
||||
Self::KeyringListV0(x) => {
|
||||
match format {
|
||||
OutputFormat::HumanReadable => x.human_readable(w)?,
|
||||
OutputFormat::Json => x.json(w)?
|
||||
OutputFormat::Json => x.json(w)?,
|
||||
_ => x.human_readable(w)?,
|
||||
}
|
||||
}
|
||||
Self::WkdUrlV0(x) => {
|
||||
match format {
|
||||
OutputFormat::HumanReadable => x.human_readable(w)?,
|
||||
OutputFormat::Json => x.json(w)?
|
||||
OutputFormat::Json => x.json(w)?,
|
||||
_ => x.human_readable(w)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
src/sq.rs
24
src/sq.rs
@ -52,12 +52,15 @@ use sequoia_wot as wot;
|
||||
use wot::store::Store as _;
|
||||
|
||||
use clap::FromArgMatches;
|
||||
use crate::sq_cli::packet;
|
||||
use sq_cli::SqSubcommands;
|
||||
|
||||
#[macro_use] mod macros;
|
||||
#[macro_use] mod log;
|
||||
|
||||
|
||||
mod sq_cli;
|
||||
use sq_cli::packet;
|
||||
use sq_cli::SqSubcommands;
|
||||
|
||||
mod man;
|
||||
mod commands;
|
||||
pub mod output;
|
||||
@ -345,8 +348,8 @@ pub struct Config<'a> {
|
||||
no_rw_cert_store: bool,
|
||||
cert_store_path: Option<PathBuf>,
|
||||
keyrings: Vec<PathBuf>,
|
||||
// This will be set if the cert store is enabled (--no-cert-store
|
||||
// is not passed), OR --keyring is passed.
|
||||
// This will be set if --no-cert-store is not passed, OR --keyring
|
||||
// is passed.
|
||||
cert_store: OnceCell<cert_store::CertStore<'a>>,
|
||||
|
||||
// The value of --trust-root.
|
||||
@ -752,12 +755,9 @@ impl<'store> Config<'store> {
|
||||
};
|
||||
|
||||
if matches.is_empty() {
|
||||
if error.is_none() {
|
||||
error = Some(anyhow::anyhow!(
|
||||
"No certificates are associated with {:?}",
|
||||
userid));
|
||||
}
|
||||
continue;
|
||||
return Err(anyhow::anyhow!(
|
||||
"No certificates are associated with {:?}",
|
||||
userid));
|
||||
}
|
||||
|
||||
struct Entry {
|
||||
@ -1450,6 +1450,10 @@ fn main() -> Result<()> {
|
||||
SqSubcommands::Link(command) => {
|
||||
commands::link::link(config, command)?
|
||||
}
|
||||
|
||||
SqSubcommands::Wot(command) => {
|
||||
commands::wot::dispatch(config, command)?
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -28,6 +28,7 @@ pub mod revoke;
|
||||
mod sign;
|
||||
mod verify;
|
||||
pub mod wkd;
|
||||
pub mod wot;
|
||||
|
||||
pub mod types;
|
||||
|
||||
@ -110,7 +111,7 @@ store, and the results are merged together."
|
||||
#[clap(
|
||||
long = "output-format",
|
||||
value_name = "FORMAT",
|
||||
value_parser = ["human-readable", "json"],
|
||||
value_parser = ["human-readable", "json", "dot"],
|
||||
default_value = "human-readable",
|
||||
env = "SQ_OUTPUT_FORMAT",
|
||||
help = "Produces output in FORMAT, if possible",
|
||||
@ -202,6 +203,7 @@ pub enum SqSubcommands {
|
||||
Export(export::Command),
|
||||
Certify(certify::Command),
|
||||
Link(link::Command),
|
||||
Wot(wot::Command),
|
||||
|
||||
#[cfg(feature = "autocrypt")]
|
||||
Autocrypt(autocrypt::Command),
|
||||
|
495
src/sq_cli/wot.rs
Normal file
495
src/sq_cli/wot.rs
Normal file
@ -0,0 +1,495 @@
|
||||
/// Command-line parser for sq wot.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use sequoia_openpgp as openpgp;
|
||||
use openpgp::KeyHandle;
|
||||
use openpgp::Result;
|
||||
use openpgp::packet::UserID;
|
||||
|
||||
/// A frontend for Sequoia's web-of-trust engine.
|
||||
///
|
||||
/// This subcommand presents a CLI to query a web-of-trust network.
|
||||
///
|
||||
/// Functionality is grouped and available using subcommands.
|
||||
///
|
||||
/// We use the term `certificate`, or cert for short, to refer to
|
||||
/// OpenPGP keys that do not contain secret key material. We reserve
|
||||
/// the term `key` for OpenPGP certificates that also contain secret
|
||||
/// key material.
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Command {
|
||||
/// Treats all certificates as unreliable trust roots.
|
||||
///
|
||||
/// This option is useful for figuring out what others think about
|
||||
/// a certificate (i.e., gossip or hearsay). In other words, this
|
||||
/// finds arbitrary paths to a particular certificate.
|
||||
///
|
||||
/// Gossip is useful in helping to identify alternative ways to
|
||||
/// authenticate a certificate. For instance, imagine Ed wants to
|
||||
/// authenticate Laura's certificate, but asking her directly is
|
||||
/// inconvenient. Ed discovers that Micah has certified Laura's
|
||||
/// certificate, but Ed hasn't yet authenticated Micah's
|
||||
/// certificate. If Ed is willing to rely on Micah as a trusted
|
||||
/// introducer, and authenticating Micah's certificate is easier
|
||||
/// than authenticating Laura's certificate, then Ed has learned
|
||||
/// about an easier way to authenticate Laura's certificate.
|
||||
///
|
||||
/// EXAMPLES:
|
||||
///
|
||||
/// # Get gossip about a certificate.{n}
|
||||
/// $ sq wot --keyring keyring.pgp \\{n}
|
||||
/// --gossip identify 3217C509292FC67076ECD75C7614269BDDF73B36
|
||||
#[arg(global=true, long)]
|
||||
pub gossip: bool,
|
||||
|
||||
/// Treats the network as a certification network.
|
||||
///
|
||||
/// Normally, `sq wot` treats the web-of-trust network as an
|
||||
/// authentication network where a certification only means that
|
||||
/// the binding is correct, not that the target should be treated
|
||||
/// as a trusted introducer. In a certification network, the
|
||||
/// targets of certifications are treated as trusted introducers
|
||||
/// with infinite depth, and any regular expressions are ignored.
|
||||
/// Note: The trust amount remains unchanged. This is how most
|
||||
/// so-called pgp path-finding algorithms work.
|
||||
#[arg(global=true, long)]
|
||||
pub certification_network: bool,
|
||||
|
||||
/// The required amount of trust.
|
||||
///
|
||||
/// 120 indicates full authentication; values less than 120
|
||||
/// indicate partial authentication. When
|
||||
/// `--certification-network` is passed, this defaults to 1200,
|
||||
/// i.e., sq wot tries to find 10 paths.
|
||||
#[arg(global=true, short='a', long,
|
||||
conflicts_with_all=["partial", "full", "double"])]
|
||||
pub trust_amount: Option<usize>,
|
||||
|
||||
/// Require partial authentication.
|
||||
///
|
||||
/// This is the same as passing `--trust-amount 40`.
|
||||
#[arg(global=true, long,
|
||||
conflicts_with_all=["trust_amount", "full", "double"])]
|
||||
pub partial: bool,
|
||||
|
||||
/// Require full authentication.
|
||||
///
|
||||
/// This is the same as passing `--trust-amount 120`.
|
||||
#[arg(global=true, long,
|
||||
conflicts_with_all=["trust_amount", "partial", "double"])]
|
||||
pub full: bool,
|
||||
|
||||
/// Require double authentication.
|
||||
///
|
||||
/// This is the same as passing `--trust-amount 240`.
|
||||
#[arg(global=true, long,
|
||||
conflicts_with_all=["trust_amount", "partial", "full"])]
|
||||
pub double: bool,
|
||||
|
||||
|
||||
#[command(subcommand)]
|
||||
pub subcommand: Subcommand,
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand, Debug)]
|
||||
pub enum Subcommand {
|
||||
/// Authenticate a binding.
|
||||
///
|
||||
/// Authenticate a binding (a certificate and User ID) by looking
|
||||
/// for a path from the trust roots to the specified binding in
|
||||
/// the web of trust. Because certifications may express
|
||||
/// uncertainty (i.e., certifications may be marked as conveying
|
||||
/// only partial or marginal trust), multiple paths may be needed.
|
||||
///
|
||||
/// If a binding could be authenticated to the specified level (by
|
||||
/// default: fully authenticated, i.e., a trust amount of 120),
|
||||
/// then the exit status is 0. Otherwise the exit status is 1.
|
||||
///
|
||||
/// If any valid paths to the binding are found, they are printed
|
||||
/// on stdout whether they are sufficient to authenticate the
|
||||
/// binding or not.
|
||||
#[command(after_help("\
|
||||
EXAMPLES:
|
||||
|
||||
# Authenticate a binding.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
wot \\
|
||||
--partial \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
authenticate \\
|
||||
C7966E3E7CE67DBBECE5FC154E2AD944CFC78C86 \\
|
||||
'Alice <alice@example.org>'
|
||||
|
||||
# The same as above, but this time generate output in DOT format
|
||||
# and convert it to an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot authenticate \\
|
||||
--partial \\
|
||||
C7966E3E7CE67DBBECE5FC154E2AD944CFC78C86 \\
|
||||
'Alice <alice@example.org>' \\
|
||||
| dot -Tsvg -o alice.pgp
|
||||
|
||||
# Try and authenticate each binding where the User ID has the
|
||||
# specified email address.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot authenticate \\
|
||||
C7966E3E7CE67DBBECE5FC154E2AD944CFC78C86 \\
|
||||
--email 'alice@example.org'
|
||||
|
||||
# The same as above, but this time generate output in DOT format
|
||||
# and convert it to an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot authenticate \\
|
||||
C7966E3E7CE67DBBECE5FC154E2AD944CFC78C86 \\
|
||||
--email 'alice@example.org' \\
|
||||
| dot -Tsvg -o alice.svg
|
||||
"))]
|
||||
Authenticate {
|
||||
#[command(flatten)]
|
||||
email: EmailArg,
|
||||
|
||||
#[command(flatten)]
|
||||
cert: CertArg,
|
||||
|
||||
#[command(flatten)]
|
||||
userid: UserIDArg,
|
||||
},
|
||||
|
||||
/// Lookup the certificates associated with a User ID.
|
||||
///
|
||||
/// Identifies authenticated bindings (User ID and certificate
|
||||
/// pairs) where the User ID matches the specified User ID.
|
||||
///
|
||||
/// If a binding could be authenticated to the specified level (by
|
||||
/// default: fully authenticated, i.e., a trust amount of 120),
|
||||
/// then the exit status is 0. Otherwise the exit status is 1.
|
||||
///
|
||||
/// If a binding could be patially authenticated (i.e., its trust
|
||||
/// amount is greater than 0), then the binding is displayed, even
|
||||
/// if the trust is below the specified threshold.
|
||||
#[command(after_help("\
|
||||
EXAMPLES:
|
||||
|
||||
# Lookup a certificate with the given User ID.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot lookup \\
|
||||
--partial \\
|
||||
'Alice <alice@example.org>'
|
||||
|
||||
# The same as above, but output in DOT format and convert it to
|
||||
# an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot lookup \\
|
||||
--partial \\
|
||||
'Alice <alice@example.org>' \\
|
||||
| dot -Tsvg -o alice.svg
|
||||
|
||||
# Lookup a certificate with the given email address.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot lookup \\
|
||||
--email 'alice@example.org'
|
||||
|
||||
# The same as above, but output in DOT format and convert it to
|
||||
# an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot lookup \\
|
||||
--email 'alice@example.org' \\
|
||||
| dot -Tsvg -o alice.svg
|
||||
"))]
|
||||
Lookup {
|
||||
#[command(flatten)]
|
||||
email: EmailArg,
|
||||
|
||||
#[command(flatten)]
|
||||
userid: UserIDArg,
|
||||
},
|
||||
|
||||
/// Identify a certificate.
|
||||
///
|
||||
/// Identify a certificate by finding authenticated bindings (User
|
||||
/// ID and certificate pairs).
|
||||
///
|
||||
/// If a binding could be authenticated to the specified level (by
|
||||
/// default: fully authenticated, i.e., a trust amount of 120),
|
||||
/// then the exit status is 0. Otherwise the exit status is 1.
|
||||
///
|
||||
/// If a binding could be patially authenticated (i.e., its trust
|
||||
/// amount is greater than 0), then the binding is displayed, even
|
||||
/// if the trust is below the specified threshold.
|
||||
#[command(after_help("\
|
||||
EXAMPLES:
|
||||
|
||||
# Identify a certificate.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
--partial \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot identify \\
|
||||
C7B1406CD2F612E9CE2136156F2DA183236153AE
|
||||
|
||||
# The same as above, but output in DOT format and convert it to
|
||||
# an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
--partial \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot identify \\
|
||||
C7B1406CD2F612E9CE2136156F2DA183236153AE \\
|
||||
| dot -Tsvg -o C7B1406CD2F612E9CE2136156F2DA183236153AE.svg
|
||||
"))]
|
||||
Identify {
|
||||
#[command(flatten)]
|
||||
cert: CertArg,
|
||||
},
|
||||
|
||||
/// List all authenticated bindings (User ID and certificate
|
||||
/// pairs).
|
||||
///
|
||||
/// Only bindings that meet the specified trust amount (by default
|
||||
/// bindings that are fully authenticated, i.e., have a trust
|
||||
/// amount of 120), are shown.
|
||||
///
|
||||
/// Even if no bindings are shown, the exit status is 0.
|
||||
///
|
||||
/// If --email is provided, then a pattern matches if it is a case
|
||||
/// insensitive substring of the email address as-is or the
|
||||
/// normalized email address. Note: unlike the email address, the
|
||||
/// pattern is not normalized. In particular, puny code
|
||||
/// normalization is not done on the pattern.
|
||||
#[command(after_help("\
|
||||
EXAMPLES:
|
||||
|
||||
# List all bindings for example.org that are at least partially
|
||||
# authenticated.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot list \\
|
||||
--partial \\
|
||||
@example.org
|
||||
|
||||
# The same as above, but output in DOT format and convert it to
|
||||
# an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
--trust-root 8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
wot list \\
|
||||
--partial \\
|
||||
@example.org \\
|
||||
| dot -Tsvg -o example_org.svg
|
||||
"))]
|
||||
List {
|
||||
#[command(flatten)]
|
||||
email: EmailArg,
|
||||
|
||||
/// A pattern to select the bindings to authenticate.
|
||||
///
|
||||
/// The pattern is treated as a UTF-8 encoded string and a
|
||||
/// case insensitive substring search (using the current
|
||||
/// locale) is performed against each User ID. If a User ID
|
||||
/// is not valid UTF-8, the binding is ignored.
|
||||
pattern: Option<String>,
|
||||
},
|
||||
/// Verify the specified path.
|
||||
///
|
||||
/// A path is a sequence of certificates starting at the root, and
|
||||
/// a User ID. This function checks that each path segment has a
|
||||
/// valid certification, which also satisfies any constraints
|
||||
/// (trust amount, trust depth, regular expressions).
|
||||
///
|
||||
/// If a valid path is not found, then this subcommand also lints
|
||||
/// the path. In particular, it report if any certifications are
|
||||
/// insufficient, e.g., not enough trust depth, or invalid, e.g.,
|
||||
/// because they use SHA-1, but the use of SHA-1 has been
|
||||
/// disabled.
|
||||
#[command(after_help("\
|
||||
EXAMPLES:
|
||||
|
||||
# Verify that Neal ceritified Justus's certificate for a particular User ID.
|
||||
$ sq --keyring keyring.pgp \\
|
||||
wot path \\
|
||||
8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
CBCD8F030588653EEDD7E2659B7DD433F254904A \\
|
||||
\"Justus Winter <justus@sequoia-pgp.org>\"
|
||||
|
||||
# The same as above, but output in DOT format and convert it to
|
||||
# an SVG using Graphviz's DOT compiler.
|
||||
$ sq --format dot \\
|
||||
--keyring keyring.pgp \\
|
||||
wot path \\
|
||||
8F17777118A33DDA9BA48E62AACB3243630052D9 \\
|
||||
CBCD8F030588653EEDD7E2659B7DD433F254904A \\
|
||||
\"Justus Winter <justus@sequoia-pgp.org>\" \\
|
||||
| dot -Tsvg -o neal--justus.svg
|
||||
"))]
|
||||
Path {
|
||||
#[command(flatten)]
|
||||
email: EmailArg,
|
||||
|
||||
// This should actually be a repeatable positional argument
|
||||
// (Vec<Cert>) followed by a manadatory positional argument (a
|
||||
// User ID), but that is not allowed by Clap v3 and Clap v4
|
||||
// even though it worked fine in Clap v2. (Curiously, it
|
||||
// works in `--release` mode fine and the only error appears
|
||||
// to be one caught by a `debug_assert`).
|
||||
//
|
||||
// https://github.com/clap-rs/clap/issues/3281
|
||||
#[command(flatten)]
|
||||
path: PathArg,
|
||||
},
|
||||
}
|
||||
|
||||
impl Subcommand {
|
||||
pub fn email(&self) -> bool {
|
||||
use Subcommand::*;
|
||||
|
||||
match self {
|
||||
Authenticate { email, .. } => email.email,
|
||||
Lookup { email, .. } => email.email,
|
||||
Identify { .. } => false,
|
||||
Path { email, .. } => email.email,
|
||||
List { email, .. } => email.email,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
pub struct CertArg {
|
||||
/// The fingerprint or Key ID of the certificate to authenticate.
|
||||
#[arg(value_name="FINGERPRINT|KEYID")]
|
||||
cert: KeyHandle
|
||||
}
|
||||
|
||||
impl Deref for CertArg {
|
||||
type Target = KeyHandle;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cert
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
pub struct PathArg {
|
||||
/// A path consists of one or more certificates (designated by
|
||||
/// their fingerprint or Key ID) and ending in the User ID that is
|
||||
/// being authenticated.
|
||||
#[arg(value_names=["FINGERPRINT|KEYID", "USERID"])]
|
||||
elements: Vec<String>,
|
||||
}
|
||||
|
||||
const PATH_DESC: &str = "\
|
||||
A path consists of one or more certificates (identified by their
|
||||
respective fingerprint or Key ID) and a User ID.";
|
||||
|
||||
impl PathArg {
|
||||
fn check(&self) -> Result<()> {
|
||||
if self.elements.len() < 2 {
|
||||
Err(anyhow::anyhow!(
|
||||
"\
|
||||
The following required arguments were not provided:
|
||||
{}<USERID>
|
||||
|
||||
{}
|
||||
|
||||
Usage: sq wot path <FINGERPRINT|KEYID>... <USERID>
|
||||
|
||||
For more information try '--help'",
|
||||
if self.elements.len() == 0 {
|
||||
"<FINGERPRINT|KEYID>\n "
|
||||
} else {
|
||||
""
|
||||
},
|
||||
PATH_DESC))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn certs(&self) -> Result<Vec<KeyHandle>> {
|
||||
self.check()?;
|
||||
|
||||
// Skip the last one. That's the User ID.
|
||||
self.elements[0..self.elements.len() - 1]
|
||||
.iter()
|
||||
.map(|e| {
|
||||
e.parse()
|
||||
.map_err(|err| {
|
||||
anyhow::anyhow!(
|
||||
"Invalid value {:?} for '<FINGERPRINT|KEYID>': {}
|
||||
|
||||
{}
|
||||
|
||||
For more information try '--help'",
|
||||
e, err, PATH_DESC)
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<KeyHandle>>>()
|
||||
}
|
||||
|
||||
pub fn userid(&self) -> Result<UserID> {
|
||||
self.check()?;
|
||||
|
||||
let userid = self.elements.last().expect("just checked");
|
||||
Ok(UserID::from(userid.as_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
pub struct UserIDArg {
|
||||
/// The User ID to authenticate.
|
||||
///
|
||||
/// This is case sensitive, and must be the whole User ID, not
|
||||
/// just a substring or an email address.
|
||||
pub userid: UserID,
|
||||
}
|
||||
|
||||
impl Deref for UserIDArg {
|
||||
type Target = UserID;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.userid
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(clap::Args, Debug)]
|
||||
pub struct EmailArg {
|
||||
/// Changes the USERID parameter to match User IDs with the
|
||||
/// specified email address.
|
||||
///
|
||||
/// Interprets the USERID parameter as an email address, which
|
||||
/// is then used to select User IDs with that email address.
|
||||
///
|
||||
/// Unlike when comparing User IDs, email addresses are first
|
||||
/// normalized by the domain to ASCII using IDNA2008 Punycode
|
||||
/// conversion, and then converting the resulting email
|
||||
/// address to lowercase using the empty locale.
|
||||
///
|
||||
/// If multiple User IDs match, they are each considered in
|
||||
/// turn, and this function returns success if at least one of
|
||||
/// those User IDs can be authenticated. Note: The paths to
|
||||
/// the different User IDs are not combined.
|
||||
#[arg(long)]
|
||||
pub email: bool,
|
||||
}
|
||||
|
||||
impl Deref for EmailArg {
|
||||
type Target = bool;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.email
|
||||
}
|
||||
}
|
||||
|
5
tests/data/keyrings/README.md
Normal file
5
tests/data/keyrings/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
These files are all from sequoia-wot. That repository includes
|
||||
descriptions of each of these files. Do not modify these files
|
||||
without propagating changes back to sequoia-wot.
|
||||
|
||||
https://gitlab.com/sequoia-pgp/sequoia-wot/-/tree/main/tests/data
|
52
tests/data/keyrings/cert-expired.pgp
Normal file
52
tests/data/keyrings/cert-expired.pgp
Normal file
@ -0,0 +1,52 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
Tz+QZA4=
|
||||
=Sesf
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
111
tests/data/keyrings/cert-revoked-hard.pgp
Normal file
111
tests/data/keyrings/cert-revoked-hard.pgp
Normal file
@ -0,0 +1,111 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEXgvhABYJKwYBBAHaRw8BAQdAmBAnRPbQ8kxpYFYNyi/iJs2L/1EvB44dVd0Z
|
||||
KfrP+KXCwAsEHxYKAH0Fgl4L4QADCwkHCRCnUaegUyhjukcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeuunycYEoBL2z0M+LUMX95atzZfka5
|
||||
VurCjWhshCJV6QMVCggCmwECHgEWIQQhmqtmHIqvRSbbwxqnUaegUyhjugAA4AAA
|
||||
/3liOXNzpzUNGAbpFHEvKXpZD5i375uo0+9UF4IT8lpQAP4pXPPq2DSVrGDvdZrc
|
||||
Dzo9aXwNI/+wz5UKPUXn2tZKC80TPGFsaWNlQGV4YW1wbGUub3JnPsLADgQTFgoA
|
||||
gAWCXgvhAAMLCQcJEKdRp6BTKGO6RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZ/6ctGt6wzg5TDkhDYXrkKhHFklkNzFEfy75uOk46fg3AxUK
|
||||
CAKZAQKbAQIeARYhBCGaq2Yciq9FJtvDGqdRp6BTKGO6AABcvQEA4wZMmOev/Row
|
||||
E+yFBQNo6pKYKMImemYEGPG++9ttmZ4A/3yshwGMRxbL+GWshferOFKuqXxHkbB2
|
||||
SLQc5z5KFIgIzjMEXgvhABYJKwYBBAHaRw8BAQdAqjemXq25t94cpIpulUqKR8DM
|
||||
9QIEyd0JLT1b1WtOub3CwL8EGBYKATEFgl4L4QAJEKdRp6BTKGO6RxQAAAAAAB4A
|
||||
IHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZyMaTp6IIJIcan4K0PYZdR80
|
||||
sNuq35+mk10oyWIuiJVKApsCvqAEGRYKAG8Fgl4L4QAJEBUzrVPCfimeRxQAAAAA
|
||||
AB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZyYMx6BTk0pNNBiCQK4S
|
||||
bOsn7Sr+2DGG4WsPrTavJtYRFiEENfd7dUHjFnnJz9mXFTOtU8J+KZ4AAP4eAQDf
|
||||
sigQj6pDNm3DqoYUUTigYrFF+a6+uJaXwAXrTFh/gQD+NVZfKKbWgdtyltwxKEMY
|
||||
nhkOuXee/9uIwZc1UEefHAAWIQQhmqtmHIqvRSbbwxqnUaegUyhjugAA+28A/jiq
|
||||
tP+sy5i463z43clux7HrXc8QR8d3qu16VojI5G99AQDHqaEMDaiHQ0wA8BLIubIw
|
||||
Kro9YvAlXr8rYbxwgQS9BcYzBF4L4QAWCSsGAQQB2kcPAQEHQKYRIkpV0aBAhIkl
|
||||
ETzDmxgXlo/hzcCLhr1JsFdLDkb8wsALBB8WCgB9BYJeC+EAAwsJBwkQg/nBpHWa
|
||||
FtZHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JngbOv6Fgd
|
||||
ebucVO+uiuSIE/dTPnOMh2MkjTn5dfyEaRwDFQoIApsBAh4BFiEERpRSkvj2Q/BX
|
||||
OvcRg/nBpHWaFtYAAA4VAQC/OKE9QSY/1UecvMqndgMlJs9KKfYa0DHv21LBw17A
|
||||
GQEA5tSjkgPyF2h/MsPVwcErodK4DPIUeTk1Nnw06DQEmgvNEjxkYXZlQGV4YW1w
|
||||
bGUub3JnPsLADgQTFgoAgAWCXgvhAAMLCQcJEIP5waR1mhbWRxQAAAAAAB4AIHNh
|
||||
bHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZycXivKq8tnrdR9nFB75kN9eQQhW
|
||||
hTP2wYdP/AK0ZYRQAxUKCAKZAQKbAQIeARYhBEaUUpL49kPwVzr3EYP5waR1mhbW
|
||||
AAAOowEAoQaKLFOGCqer9TZfyDpo9zBVcMsdM6dE/r5IvacVUCABAOvCcL/L+6+i
|
||||
TgHmiuvB3/Mdhy0ni8Q7w/RSnUbrYiYCwsAHBBAWCgB5BYJeg9mABYMJZ5o7A4UB
|
||||
eAkQdUMVfvOxK+lHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Au
|
||||
b3JnSSL/+MxfvpyKJNESkvF7tVtoIsYgZeZ9mTYsK6q8rVQWIQSQ4Cv7A/qgRxTR
|
||||
09h1QxV+87Er6QAA4wUBAJOMv8CUv2ZQlE2scwymYNHWxkdt2OJkBzGKqSslfZ0q
|
||||
AQDnb1O6/giZaDfHSKkeDttOCG/T5BBUHsaqPHdR1S/NAcLABwQQFgoAeQWCXjS/
|
||||
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
|
||||
FAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnzmjmSYYuZSx9
|
||||
sTMeUO8OMRzj7iPRyd/SNdoX/ixCFykDFQoIApsBAh4BFiEEv2gHEBKOa8yyJoFU
|
||||
Vp9fa/uVxUQAACTbAQCMrzPWaCJCWoIpYqilw7jNxMBqvIy2d1o1BWj8N8555QEA
|
||||
yYG0wX4p2DAj+ryycZ9y/POqIDnIgYlwzVo0DJkCeg7NEzxjYXJvbEBleGFtcGxl
|
||||
Lm9yZz7CwA4EExYKAIAFgl4L4QADCwkHCRBWn19r+5XFREcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcme4+iFL+cquSeo+b/p2YaLWqJKAijic
|
||||
6Yu16sXI+QWHwgMVCggCmQECmwECHgEWIQS/aAcQEo5rzLImgVRWn19r+5XFRAAA
|
||||
rZ4A/joE4JRGwMH7QXt9n0v/PRY+xNeTc62PnAEyN2IpOEi/AP0QOQhE6XzFUOG+
|
||||
W/UrkIg9vbktk5EOkHyd4Ak101Y1DcLABwQQFgoAeQWCXjS/gAWDCWeaOwOFAh4J
|
||||
EKdRp6BTKGO6RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y
|
||||
Z6wBIsYeTZS8tmX3//2NOra93WBnWNx6FjuiEnJkrU3mFiEEIZqrZhyKr0Um28Ma
|
||||
p1GnoFMoY7oAAHPJAP9EKb4+0x+IeBNGSfH5hobQ7I1GPlJuvAT6SKHz0sq4IQEA
|
||||
vHIIW+xrByvwnhrX7X3dMSKGqwbhGJcSTsHH5sZNoADOMwReC+EAFgkrBgEEAdpH
|
||||
DwEBB0Cptk293zoT2iuSST2yWmxon6aSgGiJ/OrnuFtNO6xNQcLAvwQYFgoBMQWC
|
||||
XgvhAAkQVp9fa/uVxURHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1w
|
||||
Z3Aub3JnFHJ0x6RDawJeN7JwrJ3Cfhh7wWCnGkDn6yNbsbNQj2kCmwK+oAQZFgoA
|
||||
bwWCXgvhAAkQ5q91RF7gAoZHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9p
|
||||
YS1wZ3Aub3JnLvJPwopgkycqUSXzBTYYrBIHcqNg5Pv9D3odTLOtPyEWIQTLpOHu
|
||||
eQFmZKbTuubmr3VEXuAChgAAy8ABALr8v3m5pUoykqPyzZZdVm4qlq0kAtepSq+x
|
||||
5LpQykRcAPwICbC6BwpU7v4GEcZTWpTtel0IxNSU4XQ3W6WNOMSGBxYhBL9oBxAS
|
||||
jmvMsiaBVFafX2v7lcVEAAA/RAD9EiA5uaBj2YSPE4Mbo4xKRCI02bEdmFYnYpiB
|
||||
OAX/+aYA/RShkFupW3ToidZQQptPk0DW1DjdJawamPKbOmz11woL
|
||||
=RvKX
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
111
tests/data/keyrings/cert-revoked-soft.pgp
Normal file
111
tests/data/keyrings/cert-revoked-soft.pgp
Normal file
@ -0,0 +1,111 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEXgvhABYJKwYBBAHaRw8BAQdA8y8LlHJUsnwhFSABHa3yF1nyAFealUz2kXNY
|
||||
XA2Ocw3CwAwEIBYKAH4Fgl5a+wAJEPKMR1QPosOzRxQAAAAAAB4AIHNhbHRAbm90
|
||||
YXRpb25zLnNlcXVvaWEtcGdwLm9yZ+thHblIkK5D/QLIZJJj/Mm9DGMxmC/mdlDK
|
||||
WBr0vBCxDh0Dc29tZSBtZXNzYWdlFiEETNhzf3bCuJfE8Fjb8oxHVA+iw7MAAICo
|
||||
AQDA+uUFrs+GBvyTt04m6XZai5ldct0sC4xErmLFMuS9UAD9FYz7oIXutX4enk3w
|
||||
h4ZIZy77rSJxymz5Zjwl9T3nGgnCwAsEHxYKAH0Fgl4L4QADCwkHCRDyjEdUD6LD
|
||||
s0cUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmceySxlNKj+
|
||||
C239rumCpdwwBOAZ8vg7OIELRpPsY71GoAMVCggCmwECHgEWIQRM2HN/dsK4l8Tw
|
||||
WNvyjEdUD6LDswAAYQ0BAMv3lxpsOhKpQSnTyzcstlz6KO6esffGO2/UWmT9g72u
|
||||
AQDFNtdmt+68kL+xyzYC6NjakyY68/IWQj+5ZoOrRY6jDc0RPGJvYkBleGFtcGxl
|
||||
Lm9yZz7CwA4EExYKAIAFgl4L4QADCwkHCRDyjEdUD6LDs0cUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdih74hXce4aUP356vH41nuVp1iwZaE
|
||||
vxGdhQwgQxSEhwMVCggCmQECmwECHgEWIQRM2HN/dsK4l8TwWNvyjEdUD6LDswAA
|
||||
HiEBAIc0/rp44l4+Era+BiK3YV2A34quhXe2cwKnVdhQNEqeAP9JVDaGO8MPqgaQ
|
||||
WTtiPauB6Hv17wKHjsE55ETYr8TnB8LABwQQFgoAeQWCXoPZgAWDCWeaOwOFAXgJ
|
||||
EBc436uGh4JiRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y
|
||||
ZwNcOTuNVCIsPtlMm3nLYgkOAeY6b61S9yHdn4yPhKm0FiEEZgN/mLREu6/f6Y6H
|
||||
Fzjfq4aHgmIAAGhuAQDjCDll4c9g/+yzEziWnL/etMC5gu7WKeBgLJsqldrFrgEA
|
||||
yt8Ff2L4SrVhn/inohDKwP0Kh/Zu0qUS0LItMoQqugvCwAcEEBYKAHkFgl40v4AF
|
||||
gwlnmjsDhQJaCRAXON+rhoeCYkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1
|
||||
b2lhLXBncC5vcmcqC54B5MxUjdUYFYc1G0z3IFY9bl9hVmGZYzALO7qunxYhBGYD
|
||||
f5i0RLuv3+mOhxc436uGh4JiAACi+wD+KH+AobqIzKRgsTldN+0ZUZneU3Age13E
|
||||
ZgAaI+Y4YssBAMfKO19CuESpUIBUy08T791o+cbxRgidVfBeGMUl95cGzjMEXgvh
|
||||
ABYJKwYBBAHaRw8BAQdAe14y4wbsugSXTCYbcbtKabDb14nH14K9TBcEfCtAZkXC
|
||||
wL8EGBYKATEFgl4L4QAJEPKMR1QPosOzRxQAAAAAAB4AIHNhbHRAbm90YXRpb25z
|
||||
LnNlcXVvaWEtcGdwLm9yZ6vBgjxxKBwIjqiNrmmJIZtFkwPD4IJEqIINOzhfkXuQ
|
||||
ApsCvqAEGRYKAG8Fgl4L4QAJEBwHgaRywjVWRxQAAAAAAB4AIHNhbHRAbm90YXRp
|
||||
b25zLnNlcXVvaWEtcGdwLm9yZ2n0ohIR9Kt6y2F8OtH8MLzO6Dgjv25Ca4eXH5s3
|
||||
qlDTFiEEFrmn+aCTonj3IYT6HAeBpHLCNVYAAJb5AP0ULxpF8Kh+QlYqiYojpKYo
|
||||
mPTgZqkbc7JY0KwHXNcHkAEAogL9yMeDnbjZiPUWODlf1ED41HprrbZZN7ADpZyl
|
||||
2wgWIQRM2HN/dsK4l8TwWNvyjEdUD6LDswAAb3oBAJ5b/ObUGFEcE6J0lk3ldvGM
|
||||
JEVpF+g6bllozfqdxNdiAP9zL+8yZTrNO9UjKrZoe2n4gKJ2kNQvkydmmzl7Zcbv
|
||||
CcYzBF4L4QAWCSsGAQQB2kcPAQEHQDuz70xSHZdmOlPFOhT7+bGAt9dWJdul3Z6u
|
||||
BAuC0HpiwsALBB8WCgB9BYJeC+EAAwsJBwkQFzjfq4aHgmJHFAAAAAAAHgAgc2Fs
|
||||
dEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn8ei4XbDg0jU6Zxg1qs8L+xWIEzff
|
||||
3a9efsZdcP3ylR0DFQoIApsBAh4BFiEEZgN/mLREu6/f6Y6HFzjfq4aHgmIAACg8
|
||||
AP9jqokwnvBNhm7ObAwC8TdxoxG6mVNhv+uon1WD/PDO6gD8DpzHBrgvuvS87eUA
|
||||
elkuTOJN9m46udFoN0C8iMdLagfNEzxhbGljZUBleGFtcGxlLm9yZz7CwA4EExYK
|
||||
AIAFgl4L4QADCwkHCRAXON+rhoeCYkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5z
|
||||
ZXF1b2lhLXBncC5vcmcKzsyguAPAEjTm2nNpLqC7EmR1ZtTbaOTUhhJKPx/XPAMV
|
||||
CggCmQECmwECHgEWIQRmA3+YtES7r9/pjocXON+rhoeCYgAAIpsBAOIzZ5SLTu8p
|
||||
1n9XT02k6p/IHUraayDaS4e0xQmAvbsdAPwK/lOe8mmLGb1KyAmCeHire3KInouF
|
||||
xS9B9K8Jl5ZUBc4zBF4L4QAWCSsGAQQB2kcPAQEHQJOMaGWUZQ2Y6+3ARQfgUgDE
|
||||
sYz6KvYsYdE1H3kSmh4cwsC/BBgWCgExBYJeC+EACRAXON+rhoeCYkcUAAAAAAAe
|
||||
ACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmf3uhQtGFUerlk/p/WJ3Tzi
|
||||
vPwvVPXq7/BY9V7FyxHdWAKbAr6gBBkWCgBvBYJeC+EACRDKRM4YdYNlNUcUAAAA
|
||||
AAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcZE0a0itWnJUAQZasL
|
||||
28KRz3sU2PVZjQT5a78B3AlU4RYhBGRcBNbLOgUQmQjPsspEzhh1g2U1AAAwwwEA
|
||||
rIO7Vjipyn97PaQGFOVQqRbKMtC6vB0yfurnUDdnCd0BALbDpRjMmqN/mVTjN/7Y
|
||||
PwqsLHlV7w7jiR9b57zH4aoMFiEEZgN/mLREu6/f6Y6HFzjfq4aHgmIAAN6yAP4v
|
||||
8HYJT13wI1roqkHa2VPALr3/z5ZH+b/mxdkqVqcGvgD9FGvSIFTfLkM5xsT8UkMN
|
||||
eCmwW2fYiPyRHEbwmwrU4gLGMwReC+EAFgkrBgEEAdpHDwEBB0BJht+5DDAvd77f
|
||||
DWYxC+RJ0fyO8iZNatnhsQO+NgrRcsLACwQfFgoAfQWCXgvhAAMLCQcJEM5XD5uM
|
||||
fcddRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ6yiaf2l
|
||||
2haGAYCOoJF7ZuJoiVf08U3xBg9TtMMofID7AxUKCAKbAQIeARYhBKtOP47ou9NF
|
||||
l1TXWs5XD5uMfcddAAAd2wD+IuZEOBO+5jezmegAic3Ix/qEdDaP0xXXuG7Rm6ke
|
||||
PPcA/RnxkInASIIKTLgJ0z+cwT9prNp64YZIR1JEBvKhjKEGzRM8Y2Fyb2xAZXhh
|
||||
bXBsZS5vcmc+wsAOBBMWCgCABYJeC+EAAwsJBwkQzlcPm4x9x11HFAAAAAAAHgAg
|
||||
c2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnsBeRfaZEbj5jZx99YWTSDpJd
|
||||
+Sx0fQpoaPgephSzaTADFQoIApkBApsBAh4BFiEEq04/jui700WXVNdazlcPm4x9
|
||||
x10AAIQZAQDLeJYsYektiJlW11IqBK8S8bsQbgdhNlrMUSCeatkdOwD/X/tBYCTd
|
||||
CEp8MS/J4qrorSLKplbgO/hig4ARJQ7s7QHCwAcEEBYKAHkFgl40v4AFgwlnmjsD
|
||||
hQIeCRAXON+rhoeCYkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn
|
||||
cC5vcmcH0LDJ6sbEY7dQUDKcv+yi8PWvNIgeMIsx3ykv93pXlhYhBGYDf5i0RLuv
|
||||
3+mOhxc436uGh4JiAADJSQD+PI0psa36UL1RO7WHbaNwgWQUyYckSwKcpwmr8mOZ
|
||||
vm4BAN/fqcGTO6AtOlGuHhKQH/c2Fk0dNQXF9Hp59ZD0DNAJzjMEXgvhABYJKwYB
|
||||
BAHaRw8BAQdATSzlqzTrECBOHXsEicoRL032B9etxzKyAXstSMGcv7nCwL8EGBYK
|
||||
ATEFgl4L4QAJEM5XD5uMfcddRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVv
|
||||
aWEtcGdwLm9yZx8It8bhfabIAMFdbZOurCWBmlTURyCDlWi8mVuR7y80ApsCvqAE
|
||||
GRYKAG8Fgl4L4QAJEOJv5rkociaORxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZxlqKSUS+dEMoKFNBcldS47ZV0E/4Twq5+p3msD2eTirFiEE
|
||||
so/CHBIV0OmCxBKK4m/muShyJo4AALwqAQD25rC/mnhH09l5ivXZR9GspgPJ7KU0
|
||||
8O+ACXaQ5712XwEAybY5q2sBVk4/PttKN+bzzsbxeEmOtj8vmQhNAE+WmgoWIQSr
|
||||
Tj+O6LvTRZdU11rOVw+bjH3HXQAAxdYBAL76gcz0uV7HBZ//fLF0PSXHysBH0Wt6
|
||||
XPhEsL+70M6RAQCFquFekEv07svx1NzUYKsHvqWum9xtoounNQzhBbBiCMYzBF4L
|
||||
4QAWCSsGAQQB2kcPAQEHQDLRO/CGbiae652WLT9fbwc+MoCCqGtFoKOT6hPlspw5
|
||||
wsALBB8WCgB9BYJeC+EAAwsJBwkQ4k+7G5+tyZlHFAAAAAAAHgAgc2FsdEBub3Rh
|
||||
dGlvbnMuc2VxdW9pYS1wZ3Aub3JnYUdPd0PzfWEYp9ZzD1vmO1ko9VuWN/2GA4Ws
|
||||
9lOTjHQDFQoIApsBAh4BFiEE32pEDtnecjsOvH9Q4k+7G5+tyZkAACfkAP4u3Wj6
|
||||
Yrn7/zVRhd34+j7qUX3Z5snDr1wWp0NGR62TWAD/eJedIfzmPlWloQ1ND4Oz5USY
|
||||
sopmFMy3my/Rkq7ybwPNEjxkYXZlQGV4YW1wbGUub3JnPsLADgQTFgoAgAWCXgvh
|
||||
AAMLCQcJEOJPuxufrcmZRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEt
|
||||
cGdwLm9yZ6UcXqrxmqmolg3unKr/fnN9QL0wPEzjHNX7a6qlIcgfAxUKCAKZAQKb
|
||||
AQIeARYhBN9qRA7Z3nI7Drx/UOJPuxufrcmZAAD63gEA5T4cxzzNwM+IDNEyFU6a
|
||||
Moi0zD2JsLWH6Gq1bklb+dkA/RWfWWBgLrPg07CtKwXLv684Aw0KehSU7/k8M+kW
|
||||
xcQKwsAHBBAWCgB5BYJeg9mABYMJZ5o7A4UBeAkQ8oxHVA+iw7NHFAAAAAAAHgAg
|
||||
c2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnUs2cPBm+gFQsOvLNTt8k2WKa
|
||||
p5TMWlrvkjmoZksuZCQWIQRM2HN/dsK4l8TwWNvyjEdUD6LDswAAidAA/AulYkp5
|
||||
ge98uccUXmvi3MvvL9/GZB2m95O/253i75OfAQDstfI0MSL2jhW3+gws4s25dFPb
|
||||
swpYeYth9TyURIXMBsLABwQQFgoAeQWCXjS/gAWDCWeaOwOFAXgJEM5XD5uMfcdd
|
||||
RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ6LJ8vsZFInJ
|
||||
L2/6GhdYmYXypH68g1AQ/ItDp1g/ioW6FiEEq04/jui700WXVNdazlcPm4x9x10A
|
||||
AOXGAP910RqShZzWprynjZwOs+2RAtTOdLAHaKWPJFPhRU4+0QD/QrKO+SedH1nK
|
||||
skyw+uPVR2WT1Ch1n+lKduh5fDjYOw7CwAcEEBYKAHkFgl40v4AFgwlnmjsDhQE8
|
||||
CRDyjEdUD6LDs0cUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5v
|
||||
cmeGRHfsGr7bFc4IK0V1is+Cu3oQ8/8L4o4R5hK+/lpf8RYhBEzYc392wriXxPBY
|
||||
2/KMR1QPosOzAAC/4AEA0HC5Aa+39nAl8ie7fVWJUHJ2KuLI7qkQQU3k9aY8ajMB
|
||||
AM38MyyKu84f0Ff8TafFVbaNsip3OovWiX+c5EM7ed4NzjMEXgvhABYJKwYBBAHa
|
||||
Rw8BAQdAr6StRwze1t8ycBcEB/LoR4N8VoxPXi9hSPyW1PHeJw7CwL8EGBYKATEF
|
||||
gl4L4QAJEOJPuxufrcmZRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEt
|
||||
cGdwLm9yZ/Nji46QO366570jXS8Qi6RdGIYjZ5SjyEdJG20qWlO5ApsCvqAEGRYK
|
||||
AG8Fgl4L4QAJENIm805wBkc6RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVv
|
||||
aWEtcGdwLm9yZ9G67uBJ4ifXTaBkgaGY4TG4LeRSQHbI1ma3nwsPrCG1FiEEARSn
|
||||
ZpdvRWoe7RdT0ibzTnAGRzoAACSdAQCy56dQFGaR+TF1NBK2N/3fZe1JZ4mbdr6r
|
||||
5qpcyanIcAD/RjZMD4Zg6eW4ltTG+8eENozwVzNfxNPG8m5QUJUR3g8WIQTfakQO
|
||||
2d5yOw68f1DiT7sbn63JmQAA1xgA/1LUIQ7LHcPZZAx/Ab4xq2mA6p2zgk4tdS08
|
||||
/1V3u+RXAQCv5JJWBfkFnSkIFnj/lTGm0OXGSqfy1CDiBM4nw+iwAg==
|
||||
=KuPt
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
82
tests/data/keyrings/certification-network.pgp
Normal file
82
tests/data/keyrings/certification-network.pgp
Normal file
@ -0,0 +1,82 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEY8kujBYJKwYBBAHaRw8BAQdAWTNBNzb2n4kEsVt5MnuxTlHtoKVatVpguukL
|
||||
zByL1NfCwAsEHxYKAH0FgmPJLowDCwkHCRACPMAZc+2d80cUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfOZuykxfZxcuJmM2CQdrLhaX1L56gT
|
||||
VRt06gWe92cwZAMVCggCmwECHgEWIQSaGuk3tcuLxGBIq2MCPMAZc+2d8wAAD68A
|
||||
/jpczJOkjNhtGi1Zmer13q1Ufa3yoSM5Ah9+GnqvtU44AQDr3DWXRfXSiDV1OIRR
|
||||
ZApWuJIh6qTYFgi+vdXVfYknCM0SPGRhdmVAZXhhbXBsZS5vcmc+wsAOBBMWCgCA
|
||||
BYJjyS6MAwsJBwkQAjzAGXPtnfNHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2Vx
|
||||
dW9pYS1wZ3Aub3JnDB7h6it/wA4hVyg7GEZdcMgdRC3qMrQROz3Gz9tRDOcDFQoI
|
||||
ApkBApsBAh4BFiEEmhrpN7XLi8RgSKtjAjzAGXPtnfMAACcZAP46dblIYoHnyLmr
|
||||
0i+D5diKR4jdLhwXRWKOZAfxjIe6lgEAr8/0DUUYWEGgqgjS/EsVRl8Plf4kvvsc
|
||||
4gq3m9w5HADCvQQQFgoAbwWCY8kuyQkQl1V90UfZnJdHFAAAAAAAHgAgc2FsdEBu
|
||||
b3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnfA3OtnGbuuceG0sNijXElTPlJdBD4lWL
|
||||
+hvesdXDeiQWIQSrnvHIljFRmELtVZaXVX3RR9mclwAAL/sA/1KK+ZTRbb4wo/j7
|
||||
vVMd43pUNLxMdH/IXa0wbacR2qTHAP4/9osbkWshzvGEEsz1zNmW4+TRNHZPcdWZ
|
||||
PuUCeKb5B84zBGPJLowWCSsGAQQB2kcPAQEHQPSd6esOsGVxKMhuE+YcnetwK+z0
|
||||
QY+rdzwchYvnFcVfwsAABBgWCgByBYJjyS6MCRACPMAZc+2d80cUAAAAAAAeACBz
|
||||
YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdbLv9YW0qYCTdo+AuOIGwxFwDQ
|
||||
w8NZby2iZXnP/VzRjAKbIBYhBJoa6Te1y4vEYEirYwI8wBlz7Z3zAADp9AEAsdNW
|
||||
IDf7Wa3r48F+9nH66yk/tj+ZYnVVcoFDNKPXEpEBAN8s3XLBpHCFa2Slf8PqnvOY
|
||||
MfN4bUx5JlsHn4zJz24BxjMEY8kujBYJKwYBBAHaRw8BAQdA4cAHLeVcg9bGjBCR
|
||||
tpUVDRwuQC26MvDz52gn7h+nwT/CwAsEHxYKAH0FgmPJLowDCwkHCRA8X1u+a3kM
|
||||
BUcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdLJosueLGr
|
||||
nIfAOKmbwKtmK9XyWkCyadOcZt8TTxv2bwMVCggCmwECHgEWIQSmjfAOuC+cScJ8
|
||||
x3I8X1u+a3kMBQAAmXkBAJqDDwKxwCDVzQs9d/0eqHa/T4Cauh11QpKRZhFMd5bO
|
||||
AP92yCTBrL5NOrkDUIDyuYgtFz9d4Yfma3DkWoIASYieAs0RPGJvYkBleGFtcGxl
|
||||
Lm9yZz7CwA4EExYKAIAFgmPJLowDCwkHCRA8X1u+a3kMBUcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdqzjeSSIWu41AoXOp/0tsY4HLgpIl/
|
||||
xSBcmX0IhGjYXAMVCggCmQECmwECHgEWIQSmjfAOuC+cScJ8x3I8X1u+a3kMBQAA
|
||||
ZeYA/RB9AYJG9xipHkfI14qQAkLoh/oIfDYClLrRZ82QHy1OAQCriagNJAnxLS/k
|
||||
w6AokXsLwrlemsZMH1ZWlqggimxqC8K9BBAWCgBvBYJjyS7ICRDYE2C0wEiSJUcU
|
||||
AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeObgWaD/Y4PsfK
|
||||
S3mxeOaZdUJKyU4a7TXfElnoNwFaaRYhBLKzcSFO9xr9FuQsYtgTYLTASJIlAADF
|
||||
PwEAuM5kIqG11Nqnb2rubGEBu2x741cSKV3uwjkS101g26gA/iqHRsbxEGqgXvuV
|
||||
z9msWCb42Od3f8CMyS7oQUypIQsGzjMEY8kujBYJKwYBBAHaRw8BAQdA09qXxgC7
|
||||
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
|
||||
2DbSqDsEwsABBBAWCgBzBYJjyS7JA4UAPAkQAjzAGXPtnfNHFAAAAAAAHgAgc2Fs
|
||||
dEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnHLXtOBaxT8cR8KlNhRvwRXNRh3lX
|
||||
fxBi1LcB9824rXIWIQSaGuk3tcuLxGBIq2MCPMAZc+2d8wAAsAQA/3P3jDpqvSRo
|
||||
e2O4/2TzOB7aMp3yo+Y2lXyBleRiG30PAQD/hpyl5ELpKxL0nOgN+ddplHfPlVHs
|
||||
rHt2dT1oTM+yC84zBGPJLowWCSsGAQQB2kcPAQEHQC9ciUWozqpsB4350C0fVCVO
|
||||
3s4L9W5L+6d8Te7DlN5owsAABBgWCgByBYJjyS6MCRDYE2C0wEiSJUcUAAAAAAAe
|
||||
ACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfxS2KQRStDtAxDadOOBuSk
|
||||
GQPSetCVkThnSBm1ukpDswKbIBYhBLKzcSFO9xr9FuQsYtgTYLTASJIlAADC0gD+
|
||||
KmMDvPGFR010k23zznYJC8G8gZTO7FXUe1ey/mSbds4BAPKRkKwhig8bW+pWJb2H
|
||||
uqeYZXB6S2p/n1w9F/Wii3QO
|
||||
=eBns
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
103
tests/data/keyrings/multiple-userids-1.pgp
Normal file
103
tests/data/keyrings/multiple-userids-1.pgp
Normal file
@ -0,0 +1,103 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEYVw2ohYJKwYBBAHaRw8BAQdAPDd3I2+R3bAADmF/2PZ4ajvrV6DQCJ+8T2pi
|
||||
1t7SMITCwAsEHxYKAH0FgmFcNqIDCwkHCRCs4gVdYQzqA0cUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdPNo7mSILVpMZH+6YeEYEB4H0rcL2D
|
||||
8PCsIdNzvTOmhQMVCggCmwECHgEWIQTBvGeUpsYoG5aKakGs4gVdYQzqAwAA+CwB
|
||||
ALYuoR7L6HP4Nf0m0rflgh9pbKUhs/l1K9VQ7tcqQnpRAQDhfmNcnUzyuH9FVjXD
|
||||
g+QDnmUAuS4f2RpGom2AT2FUDc0QPGRhdmVAb3RoZXIub3JnPsLADgQTFgoAgAWC
|
||||
YVw2ogMLCQcJEKziBV1hDOoDRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVv
|
||||
aWEtcGdwLm9yZ+0kXWrRCp7lt7AWDWXjAjZ89TIh+/8jFsWZp+iUiHa0AxUKCAKZ
|
||||
AQKbAQIeARYhBMG8Z5SmxigblopqQaziBV1hDOoDAAAtHQD/Vf2QHja6xjJcS0BP
|
||||
LYVAD2CRTEb/bEoVEk1OVcMCFnsBAKz/9hHy56r5wnfK4ENbqky6vAhZ/8//MVSX
|
||||
k2M7r/4BwsADBBAWCgB1BYJhXDbeBYMJZ5o7CRDngGTBK215AkcUAAAAAAAeACBz
|
||||
YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeYN8US6eODPR7kfvRjoEeW3mtP
|
||||
yt1ds41mtzt5Tr8pXhYhBJyjaQe0b+e2ue6WAeeAZMErbXkCAAAw2AD/UmzUCuml
|
||||
9LvE6S/7LVacx/3lpmaDba7xNYGKpPoWTHkBAIWqEIB1CFtWsy8v6q1N4CFW4pQ+
|
||||
AzCwMiPR0k3ZTqsDzjMEYVw2ohYJKwYBBAHaRw8BAQdAgEfyWpL1gySc4IgW+p4N
|
||||
gkKLx+LwzzClMDZuchXJQITCwL8EGBYKATEFgmFcNqIJEKziBV1hDOoDRxQAAAAA
|
||||
AB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ6IwMNVICHbYLCcFpj/W
|
||||
Y7EUfjH+eSXwIVexGjabK6SlApsCvqAEGRYKAG8FgmFcNqIJEMIjwc6epbKuRxQA
|
||||
AAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZw0Rzg97+q1NxZao
|
||||
qrLfl250/8b7ZccLPPa/K5z+BrQrFiEEjFhBRy/Oz8ttpwUvwiPBzp6lsq4AAKvo
|
||||
AQCLlI1xvjAUZSXe8w4mbUGwU98O/F/ywfHrISK3PjXaDgEAyCMaYNSmPjXLzifi
|
||||
WfHdZj4+Bz/avutycHKYfd+wsQsWIQTBvGeUpsYoG5aKakGs4gVdYQzqAwAAposA
|
||||
/iHyiymQfsw5RUiuLNm2AWHrRqJ7GRyq71G1zfKu9zmlAP9bbTEKiZ5fQr1Drpg8
|
||||
eu7RTEkOzsSGeT4sz+dBFi3PBMYzBGFcNqIWCSsGAQQB2kcPAQEHQEcJfUQa7RKp
|
||||
hDEGVCDy70vjMaQfxX9KTZVABTVlZE8pwsALBB8WCgB9BYJhXDaiAwsJBwkQKzgl
|
||||
3AKgX+pHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jnto9D
|
||||
1Hi8CK1cyPuJJofGYM0OTk+QggZrd5AtHhLQF5gDFQoIApsBAh4BFiEEKipKI6fu
|
||||
wRm8C0ZkKzgl3AKgX+oAABzmAP99McELRS3IpQA++u4GWKJB1Uu07K0oF/tEz8pW
|
||||
wlwmowEAnsndbSaeB89gh/KsrwArxqo+hRW1+ytUamM8M4oTOwPNEzxhbGljZUBl
|
||||
eGFtcGxlLm9yZz7CwA4EExYKAIAFgmFcNqIDCwkHCRArOCXcAqBf6kcUAAAAAAAe
|
||||
ACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcu384BpSI7+RW4Z4Tf24jl
|
||||
Gk8i9oh1sG1KWx2fgyXFZQMVCggCmQECmwECHgEWIQQqKkojp+7BGbwLRmQrOCXc
|
||||
AqBf6gAAOP4A/1mxJe3A1TtOS5vaQWPL0I1Jtysz+NVD97QR5bZiBBAkAQCf8Z6f
|
||||
JME9XnWX8TKVo8lhzqubnZkXbzfU/7+yhcvNBc4zBGFcNqIWCSsGAQQB2kcPAQEH
|
||||
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
|
||||
BmAORyXrthQMD8LABwQQFgoAeQWCYVw23gWDCWeaOwOFAngJED38FRq/rYXVRxQA
|
||||
AAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ+NLCfmOtfrpdDHh
|
||||
XaJpTr+FgUarTg/N8eB2cGjp3dasFiEEAxgmEbkbHn4guEjoPfwVGr+thdUAAFmK
|
||||
AQCAe4NyivnvivrzigfqLsw4kFT0qT+bPmLYKex4S2/exQEAs+uZA/9XLtTsh4vd
|
||||
3F12bsN70ocR3hxUOHdp98pHIQ3OMwRhXDaiFgkrBgEEAdpHDwEBB0BbMrenaGBh
|
||||
zwHw0/KNbeIfo3xOGIw7gS9XQoPUg0gRAcLAvwQYFgoBMQWCYVw2ogkQ54BkwStt
|
||||
eQJHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnJkfw7t/i
|
||||
0OgLq7OrweNN1Bl6R4k5Hq6+U8l4KREVKLcCmwK+oAQZFgoAbwWCYVw2ogkQ0VOh
|
||||
kefqDZlHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn5Aqa
|
||||
ybGveCfZO7Apy9LAnV0tTB4f6b/VgeJXiXc5KnMWIQQVTST19kFQCy0jRXbRU6GR
|
||||
5+oNmQAAyNgBAIQDCVx/z0hDZXNw47jcyoY8UVtDA9e7Oo7QdIIPbXzEAP9/MRy3
|
||||
ivHBtoqskLALcDwOpK6r36fDEAYfXL6FUSn8AhYhBJyjaQe0b+e2ue6WAeeAZMEr
|
||||
bXkCAAD+iQEAwOh/nX9ibmwd74yJqe2XGRPkW/hJnL+hBjA0bUksnVcBAImFwwWV
|
||||
0VVaM7ese03qfeFrCrATp2+BuRjWPB4rIFYC
|
||||
=tJ73
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
150
tests/data/keyrings/multiple-userids-2.pgp
Normal file
150
tests/data/keyrings/multiple-userids-2.pgp
Normal file
@ -0,0 +1,150 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEYVw7LxYJKwYBBAHaRw8BAQdAIyDryhHnCv0cRrufsE4DSgNUzf8h2vguogNv
|
||||
LoNw+BnCwAsEHxYKAH0FgmFcOy8DCwkHCRBA+KAUHfJ46kcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmebEHkcRhKfvuXoxUTNrhFtz7/wO92r
|
||||
d7ZHOWqZRO//BgMVCggCmwECHgEWIQTxyZxAGYN3A90XxFRA+KAUHfJ46gAAjqwA
|
||||
/3xmhxa5BKv1xVd6gnSaPeJDOHjFiBiFjQF2u/SZLCanAP4o97ogoXbXVXilGXMY
|
||||
ZBKJOgf7hGMdtqWoehFAXD5uDs0TPGFsaWNlQGV4YW1wbGUub3JnPsLADgQTFgoA
|
||||
gAWCYVw7LwMLCQcJEED4oBQd8njqRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZ6rzGhaXu9mJHzXRqoHiBxzL7xhLknGovA2bo04yEmIhAxUK
|
||||
CAKZAQKbAQIeARYhBPHJnEAZg3cD3RfEVED4oBQd8njqAADaWQEA1ZZZr6SuGluc
|
||||
76SuirpN0Pn/xM8FGzJM3YpfnTNoQ3AA/3PXe3BcXzkpGU1nIkYUrQPBcQ11zqkD
|
||||
duLxV7MNFmsEzjMEYVw7LxYJKwYBBAHaRw8BAQdA36fJe5uw6sDJi7Gg570A61wL
|
||||
xi/aaeS3ZIda9KxzfarCwL8EGBYKATEFgmFcOy8JEED4oBQd8njqRxQAAAAAAB4A
|
||||
IHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZyn7ff5e3BXFZeS1PUtmzxwR
|
||||
iQv+v6LUICPfJoO1pZeOApsCvqAEGRYKAG8FgmFcOy8JEJgiRHhhsA0gRxQAAAAA
|
||||
AB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ8VX9AiVPRqrgmwqTZ8Z
|
||||
QmkTMzh1AkFn+jocsjBbcasUFiEEcqaq90zQpalQVdOHmCJEeGGwDSAAADLUAQC9
|
||||
I9WOIrK2lEJfjKDoG9Ahkz9/hSzR/CQwK/9CSVeiCQEAjEIb6O0VktpN88YABLxF
|
||||
KBHzeqppuDL/bFt2nc7brQEWIQTxyZxAGYN3A90XxFRA+KAUHfJ46gAAkqQA/07+
|
||||
+DkgANW4N/oCmYfn6r1ebgOU/Hpg1AjZ+qLXJYD1AQDRuxhBw+qkVYvmB22z24hQ
|
||||
8erXvRgQj2WkDzKL6IxDA8YzBGFcOy8WCSsGAQQB2kcPAQEHQFf7UtrsNcdD3caO
|
||||
64dTpac9KYyR6KbEv6LwP15/VjkbwsALBB8WCgB9BYJhXDsvAwsJBwkQxzgv1ihc
|
||||
GPBHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JndkeHXkqW
|
||||
dLQf1scTYWce8AdkW/C/SosZ1phhknPvAAsDFQoIApsBAh4BFiEEYsV9kNrSU96g
|
||||
HVqGxzgv1ihcGPAAADWFAQCORCF67zFuoHc1pcP445w0m0Br28vhK5dE+gztdpHk
|
||||
dAD/cajZ4shmmLZhrKUZGzbWUuuwknT7OmWdCHaUcrhuJgnNEDxkYXZlQG90aGVy
|
||||
Lm9yZz7CwA4EExYKAIAFgmFcOy8DCwkHCRDHOC/WKFwY8EcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdOosKM0EuwOie6/qnp4Rq8hDBrTDki
|
||||
FiJZ0byBUrRn9gMVCggCmQECmwECHgEWIQRixX2Q2tJT3qAdWobHOC/WKFwY8AAA
|
||||
a4kBAKqN8sVPTul7Mr3D8zR2cK40ABq5H1LOIqMQv3VRuYPQAP9m9ENyyMK2q24n
|
||||
pwGkbMI82EWrNlPezsR6CX7sOmrGBcLAAwQQFgoAdQWCYVw7bAWDCWeaOwkQ7/ZH
|
||||
fT40jXFHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn0Nzm
|
||||
M8fdfd0x38KrY0K3x+k9dEPD8WYPuED6ViRZh7IWIQRvgpFChCCrU1drq0vv9kd9
|
||||
PjSNcQAAC4wBAIOeQBmNI7C4dVPho7W4j/24NITB5iPnZhz5AXWqHlLOAQDV30ba
|
||||
JqwTp2YuMdXzLcJZcbR4g8J6TkJ9ryF3XzYyBs4zBGFcOy8WCSsGAQQB2kcPAQEH
|
||||
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
|
||||
AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmf/WrI/Pu4CPera
|
||||
wA4RU97PdOPkIW7DQagBB7pRkbGXuQMVCggCmwECHgEWIQRVKLnl2vxRntLjfwN3
|
||||
szLkERRWywAA50QA/1Jnw+pWYgjPcWQjcv2MUiowLD9G3GQ9OtLmB0LWAD0pAP9Z
|
||||
+WGHraSzX6R0FbTCPTVy3ve+sCM0CsBAUPu4+xh7Bs0PPGJvYkBvdGhlci5vcmc+
|
||||
wsAOBBMWCgCABYJhXDsvAwsJBwkQd7My5BEUVstHFAAAAAAAHgAgc2FsdEBub3Rh
|
||||
dGlvbnMuc2VxdW9pYS1wZ3Aub3JnyHe3qxnnB2maicyjGH5rJcHZItL981PbfICu
|
||||
/Dfp7fEDFQoIApkBApsBAh4BFiEEVSi55dr8UZ7S438Dd7My5BEUVssAANYaAQC6
|
||||
6zF39XZW0ecSzPho8wZvrmkVq+tj6twPcSbmDrMTAwD/WG+rvz5DbbDWnGpfofAi
|
||||
208Peq3EcopgGcrcrAoiDQzCwCAEEBYKAJIFgmFcO2wFgwlnmjsDhf9GGIY8W14+
|
||||
XStbQC5db3RoZXJcLm9yZz4kAAkQQPigFB3yeOpHFAAAAAAAHgAgc2FsdEBub3Rh
|
||||
dGlvbnMuc2VxdW9pYS1wZ3Aub3Jnmj+t12RJok4jgGvBIJVYOAIAV2Im5a2gpXfC
|
||||
9lErK4gWIQTxyZxAGYN3A90XxFRA+KAUHfJ46gAAApQA/jwreZ9XXVTnY0jSuPv0
|
||||
DMiXayBclkQJfXJ6YMJ15XnYAQCz57rGpLgO5s9CpKvKL7Qvb8z4P25N/MCW8jLy
|
||||
sz9NBM0OPGJvYkBzb21lLm9yZz7CwAsEExYKAH0FgmFcOy8DCwkHCRB3szLkERRW
|
||||
y0cUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeCjZQsghPg
|
||||
05zEPTm0/jiir/COIosJWpoTTs4WsmfkSQMVCggCmwECHgEWIQRVKLnl2vxRntLj
|
||||
fwN3szLkERRWywAA51MBAMf2i2tQNGa4FB6vFgFoIj7RN67vU1b+tYdLkjbyu68j
|
||||
AP9e+j/pxojIaPgfCdYy1i6+XXST5iG39tiEO2tP+Ob5AMLABwQQFgoAeQWCYVw7
|
||||
bAWDCWeaOwOFATIJEED4oBQd8njqRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZxqNwpim/R4ebnDUFN11BNRZk4Lbfrie17jgERQd6FUTFiEE
|
||||
8cmcQBmDdwPdF8RUQPigFB3yeOoAAAKwAP4zL/H5ctnwVGbDIwhCiKveYlS00VT9
|
||||
ZhFcOkSYNL1JpQD/R0+fEphOqnO8Km6NFfp0seaQxufv2DQ8PziyDhzBgQ3OMwRh
|
||||
XDsvFgkrBgEEAdpHDwEBB0AatGrT0eCqXWtchGougSZrkLz0ebKKM7uc52uw6PH/
|
||||
78LAvwQYFgoBMQWCYVw7LwkQd7My5BEUVstHFAAAAAAAHgAgc2FsdEBub3RhdGlv
|
||||
bnMuc2VxdW9pYS1wZ3Aub3JnCwJL39PITynTJIkX6I7K64OM0+maLTSe9hgvOH16
|
||||
13gCmwK+oAQZFgoAbwWCYVw7LwkQ7zX1XQEj+kdHFAAAAAAAHgAgc2FsdEBub3Rh
|
||||
dGlvbnMuc2VxdW9pYS1wZ3Aub3JnBEetHjSK0yRNmCq3AhI7I8QcxuKN1ehsMfgu
|
||||
W/I+oSwWIQTW7l4nVFa9wl39+0PvNfVdASP6RwAAVWEA/2AMHw+uWsE9EZU8R+az
|
||||
iTwc8XGhiwC6uAkY6fcIGJjVAQDyXvojmvDYzgUeFa1Cuv9RMO/pVEME+hxPpzVz
|
||||
xDhPCBYhBFUoueXa/FGe0uN/A3ezMuQRFFbLAAC8MQD/YTrkHf4f7SUXKS5Owh6K
|
||||
2SbQN1KQofT6cxpV9VVbnJQA/3AdOlzJGDvOQ3J6ebmae713xkL+DHa3+8rk1qPR
|
||||
K8sKxjMEYVw7MBYJKwYBBAHaRw8BAQdA+yt28SOCq2jo63TYeOas+vE+bETIXVGO
|
||||
mssTmNuQbBXCwAsEHxYKAH0FgmFcOzADCwkHCRAYyyvaZUZfA0cUAAAAAAAeACBz
|
||||
YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmegFlZkw7toKdRZLJYOd+WOlbq6
|
||||
QNMEFrG82d3RjsC/ugMVCggCmwECHgEWIQRb7j1B+Fsvy8MA3k4YyyvaZUZfAwAA
|
||||
7csA/ixLkpKfIZVa+Z/CRwOOP5gCyzxUuBxN/CFDW6DOkUTwAQCvFY+hr9aP7pTI
|
||||
F6on/vBF/x57LJxdI8HR7P1nej79Cs0RPGZyYW5rQG90aGVyLm9yZz7CwA0EExYK
|
||||
AIAFgmFcOzADCwkHCRAYyyvaZUZfA0cUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5z
|
||||
ZXF1b2lhLXBncC5vcmcSIKPlUpziFvwPQMxE5ktRznvJAtxBgMLhAdi0Q8GZTwMV
|
||||
CggCmQECmwECHgEWIQRb7j1B+Fsvy8MA3k4YyyvaZUZfAwAAyjIBAOOc09AypGWw
|
||||
3Bi6lfdIsx5GL9KqFTjTQDuUBa/Pe4fYAPiT/qjLQEBIedTR0lePOGWuzo3wUXI8
|
||||
PiB5dMaH684EwsADBBAWCgB1BYJhXDtsBYMJZ5o7CRB3szLkERRWy0cUAAAAAAAe
|
||||
ACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmddV3X22bC+UKPZRTUlkV9i
|
||||
XXFLpzsmh5WZwwwhRaq1kxYhBFUoueXa/FGe0uN/A3ezMuQRFFbLAAB4mQD8CR+m
|
||||
8UKNprFOTTzmMx0EbgUtSrxIuoh6TW4HrBcLj08A/0gAGin+xQUwfGTZqTbXC/Rt
|
||||
gctUPbfvmrWiH+uSCKMGzjMEYVw7MBYJKwYBBAHaRw8BAQdAtXvq0GJmQPbXWw4Z
|
||||
VaHUP2GjcbiJK8DC5VnFY/lYJZvCwL8EGBYKATEFgmFcOzAJEBjLK9plRl8DRxQA
|
||||
AAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ4z2SWv5L2JtFSDk
|
||||
5vbQL0Nv6TSNStwz/wSIxgj/iKiCApsCvqAEGRYKAG8FgmFcOzAJEMAbtiqrB1Cc
|
||||
RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ23sxfrXTzLt
|
||||
FupeU6YELCUKe+Q34qyQaEb0HvXJDVvtFiEEk+4dT4DNa7eYAxiMwBu2KqsHUJwA
|
||||
AGwDAQCxQQdJBIA+EDFu2jCLQkS2iXR2eN/f8saaIRAL1LDW0wD/YcZvgAnz53Km
|
||||
6GV1dhLMG3ORUCSquEuI4ziZA7XuvwQWIQRb7j1B+Fsvy8MA3k4YyyvaZUZfAwAA
|
||||
woQBAK2PwoWiBSikLnSXMHnCjuqxSSGvhgv7UeVeCpq+7tDAAQCiZr/8xobWvBcT
|
||||
fusE1Z+gEM8lr7Qp3BBYhFsabUIvAMYzBGFcOzAWCSsGAQQB2kcPAQEHQKmqY1TU
|
||||
33xTHcKp7wGEYygNTFFzSa+NuYgdeJRf39IbwsALBB8WCgB9BYJhXDswAwsJBwkQ
|
||||
jwSP+DsXNQRHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn
|
||||
+iFCJp8aCPHKKpfzQozgBqoLFMism9bpah8XtW57bdYDFQoIApsBAh4BFiEEDpdN
|
||||
CsugxNj1HXz2jwSP+DsXNQQAAG/VAP4hOYTPP3p4ggHLPrs8I05rrhXdLfdqvd8E
|
||||
Xzo463ThEgEAkZ59YEv4xEQbBnZQNPfPBp1dLCb9WS83MAg9LRfNbAnNEDxlZEBl
|
||||
eGFtcGxlLm9yZz7CwA4EExYKAIAFgmFcOzADCwkHCRCPBI/4Oxc1BEcUAAAAAAAe
|
||||
ACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmc/AuhzWxwGYUhK+fE6MAif
|
||||
c2G8djR7o0FbJI17OUYU4wMVCggCmQECmwECHgEWIQQOl00Ky6DE2PUdfPaPBI/4
|
||||
Oxc1BAAAJaAA/3vhsOIIjwtUBlBe9sx0CML0Ch9SgpZMDdC6VzWgP7iRAQD+mok5
|
||||
ZrUYQtbqz7xzEAlC6K768SOFOmVo21rmJDjADMLAAwQQFgoAdQWCYVw7bAWDCWea
|
||||
OwkQ7/ZHfT40jXFHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Au
|
||||
b3Jna8BzUAwUdJXYkOdU9Kflk8RjN0jLr7Ph19Q2F0fYAC4WIQRvgpFChCCrU1dr
|
||||
q0vv9kd9PjSNcQAAdhoA/2Vwt4OX5WDOPOkDwSDFU5UySLcPUzXvvF1VFpMPqT0F
|
||||
AQCg3hKjNjnkmtHLhfp2vIbJU43AtX6npGX31viZM5gcD84zBGFcOzAWCSsGAQQB
|
||||
2kcPAQEHQKK2XxyLVReZiqBzQ8Wj8qGK7Y32LDvlomwjzfleTNDswsC/BBgWCgEx
|
||||
BYJhXDswCRCPBI/4Oxc1BEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh
|
||||
LXBncC5vcmetVpolfIqsjdAEVj/L9JpR7gBC+K7HOUsomTZFNCUTCwKbAr6gBBkW
|
||||
CgBvBYJhXDswCRDDDVwDNJyG60cUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1
|
||||
b2lhLXBncC5vcmeTyuXjqoyVot9y+zmEaaHyi1jBoxwjkcTRYwdiIS9MUhYhBMgq
|
||||
6BVZ1+ywJrEt3sMNXAM0nIbrAADo8QEA/HKxae4ZaHYFP5fZJO6HVBqqLViTdmSv
|
||||
NIuBgS1mXTcA/Raoi8mNjtWFllYUXMOjjcg68cDNiYKfq5ei3Gs51vAKFiEEDpdN
|
||||
CsugxNj1HXz2jwSP+DsXNQQAANZwAQDeOl8WG6TodPKCfkiuCWezk5Q3xnkqQ/nu
|
||||
/pWAH4JpMAEA3tnFnt3Zt40lmY2qtKsP5wTOw04fwfvxUev5kYl/yA0=
|
||||
=bvGd
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
71
tests/data/keyrings/puny-code.pgp
Normal file
71
tests/data/keyrings/puny-code.pgp
Normal file
@ -0,0 +1,71 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEYb5eGBYJKwYBBAHaRw8BAQdAU1nm6ngUIGEAwJqKHfRU83nPipGZWJawUd2o
|
||||
ZnZKnCvCwAsEHxYKAH0FgmG+XhgDCwkHCRBWK5re5/eJ9kcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdTFuOqygaXJ+MtCylqsUyPftVPHEYc
|
||||
TzSFIo57cJjkKQMVCggCmwECHgEWIQR0MsEjdhuU7FDVDPZWK5re5/eJ9gAAsEgB
|
||||
AJaVBY42rWGwxuW8jWi1r2iZztCAHFp5iFLOg3N7yFJSAP93JIHBPkUWpXDxhwpb
|
||||
TxaLiyU3CB5fPxrk3UMS7x8UDc0TPGNhcm9sQGV4YW1wbGUub3JnPsLADgQTFgoA
|
||||
gAWCYb5eGAMLCQcJEFYrmt7n94n2RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZ4JGKTVlZ/XToSXrlJdySpJmkWVtANeCMgKlDXlbJt4KAxUK
|
||||
CAKZAQKbAQIeARYhBHQywSN2G5TsUNUM9lYrmt7n94n2AADlAQEAnR6msNsYfyyc
|
||||
+f+4cF9xY8zmthltGdH5monJ75EE63oBAILO+WS1m033RpKo7PCz+R0A3r/1JlFf
|
||||
xT9aTLsawPUMwsAHBBAWCgB5BYJhvl5UBYMJZ5o7A4UBZAkQnehn5spqJ1ZHFAAA
|
||||
AAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn1Fe/1KSDN6ysQHt8
|
||||
JeONCHcVmM+04X5DFOE/PuMvvMUWIQR0dnxPKxX1fzOU/Kmd6GfmymonVgAAYNUA
|
||||
/R2fw0BYl/nJQ728PpfEjBct877YSWc5gLyXFiPjcOoCAP0X3uGSeCYQ6sx6hU37
|
||||
DWRXdwO5TStUlrPufxK4xqFqBs4zBGG+XhgWCSsGAQQB2kcPAQEHQJdSHuo9Rski
|
||||
e1lg7qfTsFQqdD8ahdzvjJyyLKTlVCeawsC/BBgWCgExBYJhvl4YCRBWK5re5/eJ
|
||||
9kcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcMWIYlm0sz
|
||||
su//dPXX5+P+wPSLxBjTZyeJjbnsKBa21QKbAr6gBBkWCgBvBYJhvl4YCRDysLla
|
||||
pFSI2kcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmegmG9+
|
||||
fdHcfC9cOJfTcJoYrfLE/KRAyPAimWHaQtfMyxYhBDVzYdZHb+dhxHiHWPKwuVqk
|
||||
VIjaAAA4oAD9Fkr5TbLo+jxJZGcaSV7Xui8ZgKtE9/vnIecOAAry6n8A/06t2bLZ
|
||||
flCI/YQeaGrYeAAIgLPqHijjRtd3VF5CFT0MFiEEdDLBI3YblOxQ1Qz2Viua3uf3
|
||||
ifYAAEXJAPoDZ/TG5aHZx56ovF4zLLpnIY9XiSCGen8DCvdzWVDkvgD/dVnpvOeS
|
||||
EuhiDoFuNTNdxOYOnGz3CXKtZVb/0iLxWAbGMwRhvl4YFgkrBgEEAdpHDwEBB0Dm
|
||||
NeKGrrXbW0nHhyFa3uHM7DCQLrhCu66FeJKYBsII58LACwQfFgoAfQWCYb5eGAML
|
||||
CQcJEJ3oZ+bKaidWRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
|
||||
Lm9yZ7WeWap/5ZvpUu6DNDA624pbjvWY4m2HxXgoutMxiDyzAxUKCAKbAQIeARYh
|
||||
BHR2fE8rFfV/M5T8qZ3oZ+bKaidWAAAeFAEA4e32YWxO+oks23dMkQoZtsPWZsH0
|
||||
socOpSjVyhuHLUQA+wVo+KsiMmM3XmgAxOHPHV1qAeCgT/SHxMWCHAG2dxcFzRM8
|
||||
aMOETlNAYsO8Y2hlci50bGQ+wsAOBBMWCgCABYJhvl4YAwsJBwkQnehn5spqJ1ZH
|
||||
FAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JndsOo3BFVyT4u
|
||||
yNz+nPXg3+fxvKYfeZUOXlnOjXm84RsDFQoIApkBApsBAh4BFiEEdHZ8TysV9X8z
|
||||
lPypnehn5spqJ1YAAMcOAP93G1cwImRitj2chF2yHl3IpZ21iA/1GQofN7HPB8dM
|
||||
OQEAwTg5tIZL+w5N8ISpWSYQ5tv8YM3BL14wCQrXOJz9+wDCwAcEEBYKAHkFgmG+
|
||||
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
|
||||
Lqf39H1ma/rXuEebYHsrScSQtQA=
|
||||
=Rfni
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
31
tests/data/keyrings/sha1.pgp
Normal file
31
tests/data/keyrings/sha1.pgp
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xVgEY7191xYJKwYBBAHaRw8BAQdAt3Jnair5yJx4QkkXlq/BL5OaIv3CNOChNkJS
|
||||
k8pfhzQAAP4thMBQGCbMFzftJi5cCq93+QHkRPfvJjsCqqb1X2GngA97zRE8Ym9i
|
||||
QGV4YW1wbGUub3JnPsKQBBMWCAA4FiEEIxvEq52Mq4bRYizgLAzlVJmO7NsFAmO9
|
||||
fdcCGwEFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQLAzlVJmO7NufhQD9Hs3h
|
||||
bdIz/FipSJcWBwl8kSYIowh9XlvhgYMMBayfoegA/RKnRMaj8RlilQl5gfyUhM9H
|
||||
BIZcjl6sekRukxbDiiQIwnkEEBYIACEWIQS1+gibp2/j4X3BFmCWDlMoZzj5TAUC
|
||||
Y72AHAMFATwACgkQlg5TKGc4+Uw4ewEApnGFy6ayTMra6FBbeoRoHdIlKJRerCvq
|
||||
SkfIxsSW2R4BAKnHPFqlX3XcGIM0krsw4lpnHlxISjjMgFcsoT4YXAMCwnkEEBYC
|
||||
ACEWIQS1+gibp2/j4X3BFmCWDlMoZzj5TAUCY71+oQMFAXgACgkQlg5TKGc4+Uz1
|
||||
AAEAh/TpOxN4AZrYmfl/eA+McYCAlt+6xLwVIZuZwd0dn20BAP2Tj2pTFgOhUpWB
|
||||
g5mj8Zoj8kU2xTlJ80FnexMtGlYExVgEY719zhYJKwYBBAHaRw8BAQdA87Vhi+ek
|
||||
HS8TQ8xU5c2mgB1ezzzdXjDRIPOgSuCiP9AAAP0bejiDjIlbxBf+N7aKzSUpyGlg
|
||||
DVoFj24Dm6CRcjJqcQ5wzRM8YWxpY2VAZXhhbXBsZS5vcmc+wpAEExYIADgWIQS1
|
||||
+gibp2/j4X3BFmCWDlMoZzj5TAUCY719zgIbAQULCQgHAgYVCgkICwIEFgIDAQIe
|
||||
AQIXgAAKCRCWDlMoZzj5TAwBAP9FLJeKOyd9+DnITTOSKusHUb8TL54yFSQUYhGx
|
||||
t92/1gEArci0UDsVzpcddpQzToyQyHcUraQ7FoWPCAnqfpG1qQzFWARjvX3gFgkr
|
||||
BgEEAdpHDwEBB0Az9tiv+Hr2aAv7aPgJXLe1tTbueOoUYPy8K2eJgt1OfQABAKxZ
|
||||
fGL/qwuY4626kefpvDWAVbZ9u2FO+QzuNGO6x4zWEqzNEzxjYXJvbEBleGFtcGxl
|
||||
Lm9yZz7CkAQTFggAOBYhBPq6hIWy1NW/FYKqljqBFed0+phSBQJjvX3gAhsBBQsJ
|
||||
CAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEDqBFed0+phSFwIBAPPjbKWpKK2EhA2B
|
||||
E3pfab2F0D/RBnA3U1u4EeHn95ghAQDE5YbOy67HPVtRJ9+b4wx17RQv+Noxlsou
|
||||
U9MeHvkNDsJ5BBAWCAAhFiEEIxvEq52Mq4bRYizgLAzlVJmO7NsFAmO9gEEDBQE8
|
||||
AAoJECwM5VSZjuzbPp0BAN0Sz7zJPowGPe7Tmy07b5YQi4nNwSIeaAW5NsONX52a
|
||||
AP9jIr9eJk35Zt+frsdjLKkZAHMwtCXL8soz4cnIb7bGBsJ1BBAWAgAdFiEEIxvE
|
||||
q52Mq4bRYizgLAzlVJmO7NsFAmO9fs4ACgkQLAzlVJmO7Nv06gEA5g0gIH7BsKN5
|
||||
vFhBu1jUBVQyOEwkTaELqZpEG33aR/QBAPM2PSocPKKISF7ilaELKsUS2V+WLWmw
|
||||
E2U5yD8GjoML
|
||||
=Up35
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
137
tests/data/keyrings/simple.pgp
Normal file
137
tests/data/keyrings/simple.pgp
Normal file
@ -0,0 +1,137 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEYVwdmxYJKwYBBAHaRw8BAQdAk8bI3OCZQGs6/5RRr0wdXEkUznIxgIh3oLLZ
|
||||
HLmWT3jCwAsEHxYKAH0FgmFcHZsDCwkHCRC8Hc/eraTukEcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcme3NNjp6ZM7I1EjMzjTdxwlz7fWnES3
|
||||
4kL4kwoq+cNiOgMVCggCmwECHgEWIQQ5pHmBbJNLngRk8fS8Hc/eraTukAAAZjkB
|
||||
AO44HIBtXzYNiVUGHJhp3A16Qeiw55hv9acDhUjMnL0gAQDT2eQfFV16L0/GCp9q
|
||||
DharuFPy7MYVEYid9QJ6JH06Bc0RPGJvYkBleGFtcGxlLm9yZz7CwA4EExYKAIAF
|
||||
gmFcHZsDCwkHCRC8Hc/eraTukEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1
|
||||
b2lhLXBncC5vcmfuJdEIyEhbj7f+JMVhtlFcqjgUXEJL5/wOK7yYXmY7jAMVCggC
|
||||
mQECmwECHgEWIQQ5pHmBbJNLngRk8fS8Hc/eraTukAAAt8MBALgsEpMAAtyES4FU
|
||||
CmblPgV+cgRREARciTft9PbnOgaAAQDFqL1d480WU++kyitL5mUKlyhxxjXtoPtm
|
||||
O6gkvwn1D8LABgQQFgoAeQWCYVwd1wWDCWeaOwOFAmQJELwQyc5KaZ2NRxQAAAAA
|
||||
AB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZw9BTBFY2ZZ4SmpzUGQv
|
||||
b4/iWzIPaEeXbxnKen9KUDf3FiEEhdq2VxOy0Kv8Wk8ovBDJzkppnY0AAJC+AQCL
|
||||
VDuN70ntwJxtjysSvzYU9qZDFjcAzP/joMh608ve3QD2NqAlAZ0CI7BgvzNigpN1
|
||||
fR2m1XC0v4yezybC4CQaCs4zBGFcHZsWCSsGAQQB2kcPAQEHQHzVQ0sO6/t83vno
|
||||
pQOQlopwjaHInq/afjZVDmtSJUojwsC/BBgWCgExBYJhXB2bCRC8Hc/eraTukEcU
|
||||
AAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmf9t3J5Nd1DbTwq
|
||||
kRswDnG+YmAGRlMkHfJGZNbskgXZkAKbAr6gBBkWCgBvBYJhXB2bCRBz4vrc/Kki
|
||||
LkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmcNVLVTxaXP
|
||||
7pKtjPxPcsEjmjMbXPu+WOA8esau0VrvJRYhBJUbwjIqxKkiQMs6aXPi+tz8qSIu
|
||||
AACOawD/QHJBthXFyrt0SxGwP7Bk9BGNgKWG9phUqNdv1W5KjYoA/juXnmg0FaJp
|
||||
nR1by2QwZs72quIBLWvRtOg0H97Fhe4NFiEEOaR5gWyTS54EZPH0vB3P3q2k7pAA
|
||||
ACjTAQCmQ+129Tv2tes5A1sqjYLh4J8kNrSEhePW3tdO+3r0fQEA3ZLzdvbj5+AZ
|
||||
21CT4cADJMxcdLC6BE6gpR5ZaYLp/w7GMwRhXB2bFgkrBgEEAdpHDwEBB0AlVYaA
|
||||
e74BKaD49/EjpWy/yEFOS9sALtzaSHp2LOf6I8LACwQfFgoAfQWCYVwdmwMLCQcJ
|
||||
EBoc9Nx/UA8ERxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y
|
||||
ZwLSiIxsTReYqaUtIAE3clTfjh+dr+bZcSqztnWQZizPAxUKCAKbAQIeARYhBENT
|
||||
D5G0UO2yaapYghoc9Nx/UA8EAABovQEAwWhXK0qwwPMSleeK3CpCSbTaIDCtRNlv
|
||||
Ni9VHg0Mm+EBAJtAJ/Qwu4zstLdUPH5r5wZOvL6+6y3TQojaNzhHZH8EzRM8Y2Fy
|
||||
b2xAZXhhbXBsZS5vcmc+wsAOBBMWCgCABYJhXB2bAwsJBwkQGhz03H9QDwRHFAAA
|
||||
AAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnE+Y8FcSUaUx2+O4h
|
||||
Ki55xpI0SMmi5fhNEhv+5nM9z/ADFQoIApkBApsBAh4BFiEEQ1MPkbRQ7bJpqliC
|
||||
Ghz03H9QDwQAAIgSAQD3sp6RyqsAFmTNG5I+ikLltWm26poufP+XRAvq4IyOpAD7
|
||||
BkyMoLElyCF6xyQhepE5dmzfD7yS1IjKXqqDeMhjhgnCwAcEEBYKAHkFgmFcHdcF
|
||||
gwlnmjsDhQFkCRC8Hc/eraTukEcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1
|
||||
b2lhLXBncC5vcmfPlvAWtKtWs1XzWhcTM5d9G4IppIxsK3PFqJQWzZ9plRYhBDmk
|
||||
eYFsk0ueBGTx9Lwdz96tpO6QAACRoQEAqRjKjKYPSGjkbT1Px5nmtyatyjEh5E03
|
||||
4w2S0xap3HEBAKo4l6++5n6MsM3uidRjSEQ6YSKEwFvMcKo21Q10n/wDzjMEYVwd
|
||||
mxYJKwYBBAHaRw8BAQdAq3JFOljC+5o/YeIbKVCMNv0InKvXs0c/ZdOveMSpb2DC
|
||||
wL8EGBYKATEFgmFcHZsJEBoc9Nx/UA8ERxQAAAAAAB4AIHNhbHRAbm90YXRpb25z
|
||||
LnNlcXVvaWEtcGdwLm9yZ0wyHF7F/mRFddf0cEkKgyFKYpVpK7gQOHa3uWvGKhVZ
|
||||
ApsCvqAEGRYKAG8FgmFcHZsJENNtxwK1uV3NRxQAAAAAAB4AIHNhbHRAbm90YXRp
|
||||
b25zLnNlcXVvaWEtcGdwLm9yZ2c7l53peYEI8iwUo9or8BbJnoaq7BaaVPleaqE+
|
||||
iKT9FiEE2a9eh3hW5qi8LmvC023HArW5Xc0AAMmnAP0RkTeNgSFR3F+BY2vg1wzP
|
||||
bU+EIMlB/fmXWaAN+AqmFgEAwPp3isWWkXQxspXQKPrJ/J46VWPaLmdvY6njnPXV
|
||||
IwgWIQRDUw+RtFDtsmmqWIIaHPTcf1APBAAAAhMA/RTMB4s8xvNWP7o/yTBs7uaE
|
||||
AebhJs1qqcxCmtalnjExAP4reluImlsoabGvtD2lrbsSW32MaZCO+lbSpLnxYi3R
|
||||
AsYzBGFcHZsWCSsGAQQB2kcPAQEHQOy1XWhsO+x3EAtj6TCh1i72wxMpZtA+NwXr
|
||||
/cMMio6swsALBB8WCgB9BYJhXB2bAwsJBwkQtDynf3wXavRHFAAAAAAAHgAgc2Fs
|
||||
dEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnldCh3hqCxjuJW+x/NvHO2hmLhAoh
|
||||
IVIYGw0g4K5RmIIDFQoIApsBAh4BFiEEpzGamxZqtTCl+6yKtDynf3wXavQAADwG
|
||||
AQDcJtSYK6DXAMltQGft1tXaxY/jOsGmNYpqJe3rrogbrgEAhGs/m56KfR1JBT3O
|
||||
40+xyRRFt/P6+fncrw36vFrlJQnNEzxlbGxlbkBleGFtcGxlLm9yZz7CwA4EExYK
|
||||
AIAFgmFcHZsDCwkHCRC0PKd/fBdq9EcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5z
|
||||
ZXF1b2lhLXBncC5vcmfO7AQVklUElpHB0uWlCb00GC+t/VpR92E1PkmHPL9TNwMV
|
||||
CggCmQECmwECHgEWIQSnMZqbFmq1MKX7rIq0PKd/fBdq9AAA+c0BAJr1QgfhNax9
|
||||
Wu8M8L2Fm6JcDH/N49UQl8yVp/pQAuJXAQD5VQazt8nXvsy9KP5J1K4kA4IArcIA
|
||||
zYVyIe6ksXCTAcLABwQQFgoAeQWCYVwd1wWDCWeaOwOFAWQJEGd8tw/7/hKBRxQA
|
||||
AAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZyPcvKx5CEPlPwS9
|
||||
XeC/FkwuEeIXwBpfMCKQBO4+mV9VFiEEMp1ar3PccLTj3S0RZ3y3D/v+EoEAAJhw
|
||||
AQDROKCDXEGeZOsEVkatgrJYSFzBGX+xU2NrM6R6CHRywQD+OzbYktS8sAmE7MOd
|
||||
LjgS5RkLue2LSSfSDmXR2r6PGQnOMwRhXB2bFgkrBgEEAdpHDwEBB0B2qg/kQ5ii
|
||||
ZxubFFwaFQ/vCMAMSlMWrPLR/4cc1vTqGMLAvwQYFgoBMQWCYVwdmwkQtDynf3wX
|
||||
avRHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnGTMWmoOh
|
||||
7alBTD9MiAmgrZwqf7hXC5iP+3ZFGRiTreECmwK+oAQZFgoAbwWCYVwdmwkQNhsA
|
||||
H3EgoCdHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jni29b
|
||||
7AKRh3IsUvJwGZaMkXBbYLEE3zZL240KJ5Yd/NAWIQTS2X0aQMxF7OMwBG42GwAf
|
||||
cSCgJwAAlwIBAPMitwdPaDSCipP5PTpn73S6QQ/oakY2GXQQpSILoZ0sAP9ma52e
|
||||
zd3Ef4MS6grWtM7YziFT0TgXJgkqXb9op2K9DRYhBKcxmpsWarUwpfusirQ8p398
|
||||
F2r0AAACigEA2RCf3gA19TtoYYPUeE7wf9CzbeR0plGRhIGYxej2jGwA/RwuDeiI
|
||||
aqwLN99MAY4w5akwDVZOHplXpFms8Ki1WnUHxjMEYVwdmxYJKwYBBAHaRw8BAQdA
|
||||
8uA9nQIUSGk+eVWb/VEr6RMv7IO1CUzEZUseIa5m5YPCwAsEHxYKAH0FgmFcHZsD
|
||||
CwkHCRC8EMnOSmmdjUcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn
|
||||
cC5vcmdxon0OeFIi+jtSAiPy42gVKo2hgFQYBqp/AZ8OpXPNqQMVCggCmwECHgEW
|
||||
IQSF2rZXE7LQq/xaTyi8EMnOSmmdjQAATfwA/iK7Ab91rzETGQXmlRCvH1Yo7oph
|
||||
PmNRa0srdiT4p/ywAQDLsJbZY9MPcVh82DxL1w/vC01zXbO0AGlEYUQDGxBLAc0T
|
||||
PGFsaWNlQGV4YW1wbGUub3JnPsLADgQTFgoAgAWCYVwdmwMLCQcJELwQyc5KaZ2N
|
||||
RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ9+0s4dvx5ca
|
||||
70dHxiLPzovre+NqtglUL6YxrnRAYF1jAxUKCAKZAQKbAQIeARYhBIXatlcTstCr
|
||||
/FpPKLwQyc5KaZ2NAAAZ0gEAunPv0OLMDRsOA5/qVSFrf9ERK9KyGesjs7EQ9Rl5
|
||||
liMA/2xV/X80DwKY0ytoF6Gmqxr5wMSns5ZcvDBbGJl7qM4NzjMEYVwdmxYJKwYB
|
||||
BAHaRw8BAQdAW21wFQDicmTa+zseQ6C8qGXSiGs/v6rnJLgO/LsJQsnCwL8EGBYK
|
||||
ATEFgmFcHZsJELwQyc5KaZ2NRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVv
|
||||
aWEtcGdwLm9yZ/jmilhMLNoEq8L9amKw6JuAQQses4ZsHjd3PaJZgyTeApsCvqAE
|
||||
GRYKAG8FgmFcHZsJEB+OyxG/TbN0RxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZ5d+CLnY3K/5d64iFHXzo8Np6V5zIUs+6S7quw7A3YmwFiEE
|
||||
VIxzLR3oBVQH/bOOH47LEb9Ns3QAAJvyAQCvsq1GfYP+mHKedbX1MpiW4FbYFR1x
|
||||
wzN80NpuF/cX/QD+K9CSFfMYDteF89A8oXGSaPGsK37Zs4znSLyJwgfFfQkWIQSF
|
||||
2rZXE7LQq/xaTyi8EMnOSmmdjQAANu4A/A/p6c2ImEi0PWhzclre1+9/D/eg7/gI
|
||||
iSUC40OiCgthAQDgrEp67PQBPuGBBpEoYumMdlDZWH55zIm6YZEKUvw8AcYzBGFc
|
||||
HZsWCSsGAQQB2kcPAQEHQAXJdxpg5s179q02cQAJFOI7oont4QTI1VEr73jl1bSn
|
||||
wsALBB8WCgB9BYJhXB2bAwsJBwkQ3IapfNLIGdlHFAAAAAAAHgAgc2FsdEBub3Rh
|
||||
dGlvbnMuc2VxdW9pYS1wZ3Aub3JnulTVgQDVcGnq1pIWl911apVKeSdAALgwocRk
|
||||
NlvWKXUDFQoIApsBAh4BFiEEJpMjfSztC7aPEY143IapfNLIGdkAAFO/AP0e2lUg
|
||||
Zge4ZyYUL6mh6pZeQOyzKnzPIFtgYq5Fn0eW/gD/WaK5e4h2As7WbeEBm1KkQ28y
|
||||
ST1RKDP692I1MV2k+AjNEzxmcmFua0BleGFtcGxlLm9yZz7CwA4EExYKAIAFgmFc
|
||||
HZsDCwkHCRDchql80sgZ2UcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh
|
||||
LXBncC5vcmcPnps0Dp5p0YJ/AjuWO0Fr+16rqp8oX6VaEy4uMPIgqgMVCggCmQEC
|
||||
mwECHgEWIQQmkyN9LO0Lto8RjXjchql80sgZ2QAA4ZQBAPUWBaex2nKunWta81+h
|
||||
0n1uQqFSpLx/wFU1MVxkvJIzAQCdthYV3FUMXBhb/PTEW3rfcU4F0Emm5221x6U4
|
||||
sxn+C84zBGFcHZsWCSsGAQQB2kcPAQEHQITURTrPxNzEJGkh1r4YUgD8dfWAkfSH
|
||||
g4MX3piLSolfwsC/BBgWCgExBYJhXB2bCRDchql80sgZ2UcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeYFfx77wKkI80lLycQaD2wVBe8sPaL
|
||||
G8nbBBXidVmB2gKbAr6gBBkWCgBvBYJhXB2bCRBM3zDGnGovIkcUAAAAAAAeACBz
|
||||
YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdf1slPSghIYzgQ7X1X1TUUJWaG
|
||||
xyrHUBfeW8jWbaEKkBYhBCqESu3tyggv1PeZoUzfMMacai8iAABZMQEAxWSNnObr
|
||||
JX8g1D1WL9EqlX8iUK++B5bvMv+Lda5pPNoBAPt8p6l1fEWNKZXtWxnbC9SVSm5J
|
||||
mL8asTSEDDcHZQgDFiEEJpMjfSztC7aPEY143IapfNLIGdkAAGrYAP46iX0UVZC9
|
||||
9NxlZRON4d/+JHKvCrCOaDX4wTEjAFrcogEAqDDPOvZWO0HO8rR0YivR1wU0kwHG
|
||||
Uujn4XGt2u3GygHGMwRhXB2bFgkrBgEEAdpHDwEBB0CLheVc9lxL6EfpgSCeVl+j
|
||||
oMjxZ0TeXufKaFeEwzT0QMLACwQfFgoAfQWCYVwdmwMLCQcJEGd8tw/7/hKBRxQA
|
||||
AAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ6nLVMH/XWlzbxMX
|
||||
e/6vzRsE/gYP32ACMJftuYMSd+K9AxUKCAKbAQIeARYhBDKdWq9z3HC0490tEWd8
|
||||
tw/7/hKBAACulwEA1GMTIrbA8vq2ZPFRwaw8rdGiNWhQxDi9/SNgCBu9HAABAL5u
|
||||
IYWLF62cmWkkXZvnVQybWBoEWqqAo4nd6A8silQAzRI8ZGF2ZUBleGFtcGxlLm9y
|
||||
Zz7CwA4EExYKAIAFgmFcHZsDCwkHCRBnfLcP+/4SgUcUAAAAAAAeACBzYWx0QG5v
|
||||
dGF0aW9ucy5zZXF1b2lhLXBncC5vcmctbryiXEnpUrOLEhcNOX3wAiRbxjWRvCZf
|
||||
oU4FxC86dgMVCggCmQECmwECHgEWIQQynVqvc9xwtOPdLRFnfLcP+/4SgQAAaf4B
|
||||
AIQqi/mq6OzedNxO3YCqVDgnkR9GzPUoNUrm/pSvoXqnAP0VmRN/VR29b8+kDUjM
|
||||
xI46Ogm1fh7vFTtbJju1+ELwDcLABwQQFgoAeQWCYVwd1wWDCWeaOwOFAWQJEBoc
|
||||
9Nx/UA8ERxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZwzy
|
||||
PrSNt7XRHHhvnZAPPeY1TtrtSPZXTpZgXvkf0savFiEEQ1MPkbRQ7bJpqliCGhz0
|
||||
3H9QDwQAAHp6AQCa8V9L2z/hLfFFHEgwzmrPROY2/o6e2XvOUs89zVCBvgEAqzzw
|
||||
NUUDBEu2nT/cYN2cBPNDvE0bJY7rO9LGV/aW5ADOMwRhXB2bFgkrBgEEAdpHDwEB
|
||||
B0ARmJpTF4micHpu3jHAKu/oxdS4mjiJ8Iu1lSEa7Fd1f8LAvwQYFgoBMQWCYVwd
|
||||
mwkQZ3y3D/v+EoFHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Au
|
||||
b3Jn4oTzk5+Is/hGYuMYA+SylOONmJzNgcvz/Dr0RWYoNqYCmwK+oAQZFgoAbwWC
|
||||
YVwdmwkQ0AAL+QhtZcRHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1w
|
||||
Z3Aub3JnnTwrZh/CjtbFdynQbf0mAnySvtMu4wf6rzSjt6lKEfQWIQTF0s1GzpMz
|
||||
cAdIbJLQAAv5CG1lxAAAiW4A/A1MFOPPEqacYtl78AQ62lKtGqd9M5Al9DwhThM0
|
||||
sKXQAP9OM8vuVYiP41YSG/ut3uodHYVkHC0JJRgGyqfxR6nrARYhBDKdWq9z3HC0
|
||||
490tEWd8tw/7/hKBAAA+vwEAmYbvwn2hUttyxJLVPGGTB41d7o8ezMHZmVAvfYE1
|
||||
dRIA/ifzLQTJhmU4tnzAnD5xewYw9uAEcrh5vXoGPVn7+XYJ
|
||||
=0tMR
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
84
tests/data/keyrings/userid-revoked.pgp
Normal file
84
tests/data/keyrings/userid-revoked.pgp
Normal file
@ -0,0 +1,84 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xjMEXgvhABYJKwYBBAHaRw8BAQdAhT+prBx3w03lEnZeQpaj+S12u/rPca03CKfq
|
||||
mwoG2CnCwAsEHxYKAH0Fgl4L4QADCwkHCRBzHOoJLEZfyEcUAAAAAAAeACBzYWx0
|
||||
QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeOw7CuFaS0toxI+CHivZQG3DfGwwo7
|
||||
AhFoa23nqE2jtQMVCggCmwECHgEWIQQBZyu2fktAR+Wk7ApzHOoJLEZfyAAArVQA
|
||||
/308jglcUpVQmIxy1L1M800r/hkBzVrdI2ZDL+/ZJ3xYAP0QO0IUfmzbhJJ2NfMM
|
||||
Ox9Q4bT3MIoL/zUGhjgs+rKkAc0TPGFsaWNlQGV4YW1wbGUub3JnPsLADgQTFgoA
|
||||
gAWCXgvhAAMLCQcJEHMc6gksRl/IRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNl
|
||||
cXVvaWEtcGdwLm9yZyDvQJuJzCsisCLmdrLXbsCpRM/UiS9bUNNedWA0YM2tAxUK
|
||||
CAKZAQKbAQIeARYhBAFnK7Z+S0BH5aTsCnMc6gksRl/IAAAjTwD9EYpc/21MNmLZ
|
||||
8yD6l7LwI5fzaaOGgzQzFQRFIOkN9NoA/ikhz3XnnwK6526jDc2uFetHvtmCZToL
|
||||
Vpt3R+YHoYMMzjMEXgvhABYJKwYBBAHaRw8BAQdA3WF+/meEa42HlKtzJ3lwzTYM
|
||||
hhUzptBhD634osiwuwzCwL8EGBYKATEFgl4L4QAJEHMc6gksRl/IRxQAAAAAAB4A
|
||||
IHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ1cJsO9arW70E7gz+dbG3FMh
|
||||
sbOx1jagrd6xmcT8cVLQApsCvqAEGRYKAG8Fgl4L4QAJEGFKGCgk1kTzRxQAAAAA
|
||||
AB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ48eqVw+tBIAHCld8kRT
|
||||
ZsCvRW76A3fR0B4xkc8qoNXNFiEEp/X9b1/I9HtQVKjCYUoYKCTWRPMAANxOAP9x
|
||||
jIGFSrKovegMzoEM+pA/Kh/wusVSX3rnOmDzhLcrmwD/V9qnMGknYRbVzqxUsb/2
|
||||
hA2ru2O6spWLcWp2Bzp2PAsWIQQBZyu2fktAR+Wk7ApzHOoJLEZfyAAAff0A/3La
|
||||
Uo9qQOvZ0K+X/0A5jZRRWQ9T9oz6snuqq+g1jWMkAQCZreJP5q9dG/OL79Idu1f+
|
||||
TyQ13UUKZrWk4ckTQ5CvDsYzBF4L4QAWCSsGAQQB2kcPAQEHQJ0voRfJUU40amfU
|
||||
pjCzrgFTBklM08Brbpc23Lz4XLz0wsALBB8WCgB9BYJeC+EAAwsJBwkQeLxTl0cL
|
||||
p/BHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnafFLHWhT
|
||||
Fu1UjWpdUc02tcoy7K1fMskS5415q4odmJADFQoIApsBAh4BFiEEIShzu5xMxJ+O
|
||||
Wm/qeLxTl0cLp/AAAPbIAQD480LIE92EH5vh79qSLbAWbqTDTrIW1UasKSwGdsMc
|
||||
OAEAhhMKRSnJx3i6MYBnhIdnGZlvOrYYW+YiIBSm6f6lDQLNEzxjYXJvbEBleGFt
|
||||
cGxlLm9yZz7CwA4EExYKAIAFgl4L4QADCwkHCRB4vFOXRwun8EcUAAAAAAAeACBz
|
||||
YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmdj3ZuCg1W6HM4EaLL7Xju3dM3/
|
||||
P9zwJCQfiXGBvFDn0AMVCggCmQECmwECHgEWIQQhKHO7nEzEn45ab+p4vFOXRwun
|
||||
8AAA1kIA/ipADvmNJ7oCSOVCTX3kiA/fsyJ6+duWev12PkEBsdx7AP91zFfhfoB/
|
||||
08OZKP4n/fIfTBsAudtz3zifef1TvFpKBMLABwQQFgoAeQWCXoPZgAWDCWeaOwOF
|
||||
AVoJEIYb9C/0kMWBRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
|
||||
Lm9yZ/KOl8au2MM+GsyMboZvL1SlBy6lTGsxuR9R46GNeQvMFiEE6kead80HRFjq
|
||||
/la0hhv0L/SQxYEAAOsQAQCGmslokfigitOj9Nt+RGqFFmkrwM3tINfaRs+HRZFi
|
||||
WQD8DOCaP21B8PeJSEMsFUOLRJT2T86hZuUfxR2GRC1NPw7CwAcEEBYKAHkFgl40
|
||||
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
|
||||
a5upBw==
|
||||
=XPdm
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
4528
tests/sq-wot.rs
Normal file
4528
tests/sq-wot.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user