Add an option to sq link add to temporarily accept a binding
- Add an option to `sq link add`, `--temporary`, to temporarily accept a binding. - This creates a fully trusted certification that expires after a week, and a second certification that is one second older, which doesn't expire, but is only partially trusted (trust amount = 40) so that the user remembers this decision.
This commit is contained in:
parent
96a65b4b97
commit
4ae448cef8
@ -1,5 +1,5 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::time::SystemTime;
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
|
||||||
@ -445,30 +445,6 @@ pub fn add(mut config: Config, c: link::AddCommand)
|
|||||||
// Creation time.
|
// Creation time.
|
||||||
builder = builder.set_signature_creation_time(config.time)?;
|
builder = builder.set_signature_creation_time(config.time)?;
|
||||||
|
|
||||||
match (expires, expires_in) {
|
|
||||||
(None, None) =>
|
|
||||||
// Default expiration: never.
|
|
||||||
(),
|
|
||||||
(Some(t), None) if t == "never" =>
|
|
||||||
// The default is no expiration; there is nothing to do.
|
|
||||||
(),
|
|
||||||
(Some(t), None) => {
|
|
||||||
let expiration = SystemTime::from(
|
|
||||||
crate::parse_iso8601(
|
|
||||||
&t, chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap())?);
|
|
||||||
let validity = expiration.duration_since(config.time)?;
|
|
||||||
builder = builder.set_signature_validity_period(validity)?;
|
|
||||||
},
|
|
||||||
(None, Some(d)) if d == "never" =>
|
|
||||||
// The default is no expiration; there is nothing to do.
|
|
||||||
(),
|
|
||||||
(None, Some(d)) => {
|
|
||||||
let d = parse_duration(&d)?;
|
|
||||||
builder = builder.set_signature_validity_period(d)?;
|
|
||||||
},
|
|
||||||
(Some(_), Some(_)) => unreachable!("conflicting args"),
|
|
||||||
}
|
|
||||||
|
|
||||||
let notations = parse_notations(c.notation)?;
|
let notations = parse_notations(c.notation)?;
|
||||||
for (critical, n) in notations {
|
for (critical, n) in notations {
|
||||||
builder = builder.add_notation(
|
builder = builder.add_notation(
|
||||||
@ -478,6 +454,45 @@ pub fn add(mut config: Config, c: link::AddCommand)
|
|||||||
critical)?;
|
critical)?;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let builders: Vec<SignatureBuilder> = if c.temporary {
|
||||||
|
// Make the partially trusted link one second younger. When
|
||||||
|
// the fully trusted link expired, then this link will come
|
||||||
|
// into effect. If the user has fully linked the binding in
|
||||||
|
// the meantime, then this won't override that, which is
|
||||||
|
// exactly what we want.
|
||||||
|
let mut partial = builder.clone();
|
||||||
|
partial = partial.set_signature_creation_time(
|
||||||
|
config.time - Duration::new(1, 0))?;
|
||||||
|
partial = partial.set_trust_signature(trust_depth, 40)?;
|
||||||
|
|
||||||
|
builder = builder.set_signature_validity_period(
|
||||||
|
Duration::new(7 * 24 * 60 * 60, 0))?;
|
||||||
|
|
||||||
|
vec![ builder, partial ]
|
||||||
|
} else if let Some(t) = expires {
|
||||||
|
if t == "never" {
|
||||||
|
// The default is no expiration; there is nothing to do.
|
||||||
|
} else {
|
||||||
|
let expiration = SystemTime::from(
|
||||||
|
crate::parse_iso8601(
|
||||||
|
&t, chrono::NaiveTime::from_hms_opt(0, 0, 0).unwrap())?);
|
||||||
|
let validity = expiration.duration_since(config.time)?;
|
||||||
|
builder = builder.set_signature_validity_period(validity)?;
|
||||||
|
}
|
||||||
|
vec![ builder ]
|
||||||
|
} else if let Some(d) = expires_in {
|
||||||
|
if d == "never" {
|
||||||
|
// The default is no expiration; there is nothing to do.
|
||||||
|
} else {
|
||||||
|
let d = parse_duration(&d)?;
|
||||||
|
builder = builder.set_signature_validity_period(d)?;
|
||||||
|
}
|
||||||
|
vec![ builder ]
|
||||||
|
} else {
|
||||||
|
// The default is no expiration; there is nothing to do.
|
||||||
|
vec![ builder ]
|
||||||
|
};
|
||||||
|
|
||||||
// Sign it.
|
// Sign it.
|
||||||
let signers = get_certification_keys(
|
let signers = get_certification_keys(
|
||||||
&[trust_root], &config.policy, None, Some(config.time), None)
|
&[trust_root], &config.policy, None, Some(config.time), None)
|
||||||
@ -535,11 +550,14 @@ pub fn add(mut config: Config, c: link::AddCommand)
|
|||||||
|
|
||||||
let changed = diff_link(
|
let changed = diff_link(
|
||||||
&active_certification,
|
&active_certification,
|
||||||
&builder, config.time);
|
&builders[0], config.time);
|
||||||
|
|
||||||
if ! changed && config.force {
|
if ! changed && config.force {
|
||||||
eprintln!(" Link parameters are unchanged, but \
|
eprintln!(" Link parameters are unchanged, but \
|
||||||
updating anyway as \"--force\" was specified.");
|
updating anyway as \"--force\" was specified.");
|
||||||
|
} else if c.temporary {
|
||||||
|
eprintln!(" Creating a temporary link, \
|
||||||
|
which expires in a week.");
|
||||||
} else if ! changed {
|
} else if ! changed {
|
||||||
eprintln!(" Link parameters are unchanged, no update \
|
eprintln!(" Link parameters are unchanged, no update \
|
||||||
needed (specify \"--force\" to update anyway).");
|
needed (specify \"--force\" to update anyway).");
|
||||||
@ -556,16 +574,25 @@ pub fn add(mut config: Config, c: link::AddCommand)
|
|||||||
eprintln!("Linking {} and {:?}.",
|
eprintln!("Linking {} and {:?}.",
|
||||||
cert.fingerprint(), userid_str());
|
cert.fingerprint(), userid_str());
|
||||||
|
|
||||||
let sig = builder.clone().sign_userid_binding(
|
let mut sigs = builders.iter()
|
||||||
&mut signer,
|
.map(|builder| {
|
||||||
cert.primary_key().key(),
|
builder.clone().sign_userid_binding(
|
||||||
&userid)
|
&mut signer,
|
||||||
.with_context(|| {
|
cert.primary_key().key(),
|
||||||
format!("Creating certification for {:?}", userid_str())
|
&userid)
|
||||||
})?;
|
.with_context(|| {
|
||||||
|
format!("Creating certification for {:?}",
|
||||||
|
userid_str())
|
||||||
|
})
|
||||||
|
.map(Into::into)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<Packet>>>()?;
|
||||||
|
|
||||||
eprintln!();
|
eprintln!();
|
||||||
Ok(vec![ Packet::from(userid.clone()), Packet::from(sig) ])
|
|
||||||
|
let mut packets = vec![ Packet::from(userid.clone()) ];
|
||||||
|
packets.append(&mut sigs);
|
||||||
|
Ok(packets)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Vec<Packet>>>>()?
|
.collect::<Result<Vec<Vec<Packet>>>>()?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -131,7 +131,8 @@ $ sq link add --ca --amount 60 0123456789ABCDEF
|
|||||||
$ sq link retract 0123456789ABCDEF
|
$ sq link retract 0123456789ABCDEF
|
||||||
",
|
",
|
||||||
)]
|
)]
|
||||||
#[clap(group(ArgGroup::new("expiration-group").args(&["expires", "expires_in"])))]
|
#[clap(group(ArgGroup::new("expiration-group")
|
||||||
|
.args(&["expires", "expires_in", "temporary"])))]
|
||||||
pub struct AddCommand {
|
pub struct AddCommand {
|
||||||
#[clap(
|
#[clap(
|
||||||
short = 'd',
|
short = 'd',
|
||||||
@ -203,6 +204,19 @@ pub struct AddCommand {
|
|||||||
being human readable."
|
being human readable."
|
||||||
)]
|
)]
|
||||||
pub notation: Vec<String>,
|
pub notation: Vec<String>,
|
||||||
|
|
||||||
|
#[clap(
|
||||||
|
long = "temporary",
|
||||||
|
conflicts_with_all = &[ "amount" ],
|
||||||
|
help = "Temporarily accepts the binding",
|
||||||
|
long_help =
|
||||||
|
"Temporarily accepts the binding. Creates a fully
|
||||||
|
trust link between a certificate and one or more
|
||||||
|
User IDs for a week. After that, the link is
|
||||||
|
automatically downgraded to a partially trusted link
|
||||||
|
(trust = 40).",
|
||||||
|
)]
|
||||||
|
pub temporary: bool,
|
||||||
#[clap(
|
#[clap(
|
||||||
long = "expires",
|
long = "expires",
|
||||||
value_name = "TIME",
|
value_name = "TIME",
|
||||||
|
204
tests/sq-link.rs
204
tests/sq-link.rs
@ -29,10 +29,15 @@ static TIME: OnceCell<Mutex<chrono::DateTime<chrono::Utc>>> = OnceCell::new();
|
|||||||
fn tick() -> String {
|
fn tick() -> String {
|
||||||
let t = TIME.get_or_init(|| Mutex::new(chrono::Utc::now()));
|
let t = TIME.get_or_init(|| Mutex::new(chrono::Utc::now()));
|
||||||
let mut t = t.lock().unwrap();
|
let mut t = t.lock().unwrap();
|
||||||
*t = *t + chrono::Duration::seconds(1);
|
*t = *t + chrono::Duration::seconds(10);
|
||||||
t.format("%Y-%m-%dT%H:%M:%SZ").to_string()
|
t.format("%Y-%m-%dT%H:%M:%SZ").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the "current" time.
|
||||||
|
fn now() -> chrono::DateTime<chrono::Utc> {
|
||||||
|
*TIME.get_or_init(|| Mutex::new(chrono::Utc::now())).lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
// Imports a certificate.
|
// Imports a certificate.
|
||||||
fn sq_import(cert_store: &str, files: &[&str], stdin: Option<&str>)
|
fn sq_import(cert_store: &str, files: &[&str], stdin: Option<&str>)
|
||||||
{
|
{
|
||||||
@ -75,6 +80,7 @@ fn sq_gen_key(cert_store: Option<&str>, userids: &[&str], file: &str) -> Cert
|
|||||||
|
|
||||||
// Verifies a signed message.
|
// Verifies a signed message.
|
||||||
fn sq_verify(cert_store: Option<&str>,
|
fn sq_verify(cert_store: Option<&str>,
|
||||||
|
time: Option<chrono::DateTime<chrono::Utc>>,
|
||||||
trust_roots: &[&str],
|
trust_roots: &[&str],
|
||||||
signer_files: &[&str],
|
signer_files: &[&str],
|
||||||
msg_pgp: &str,
|
msg_pgp: &str,
|
||||||
@ -89,7 +95,12 @@ fn sq_verify(cert_store: Option<&str>,
|
|||||||
for trust_root in trust_roots {
|
for trust_root in trust_roots {
|
||||||
cmd.args(&["--trust-root", trust_root]);
|
cmd.args(&["--trust-root", trust_root]);
|
||||||
}
|
}
|
||||||
cmd.args(["verify", "--time", &tick()]);
|
let time = if let Some(time) = time {
|
||||||
|
time.format("%Y-%m-%dT%H:%M:%SZ").to_string()
|
||||||
|
} else {
|
||||||
|
tick()
|
||||||
|
};
|
||||||
|
cmd.args(["verify", "--time", &time]);
|
||||||
for signer_file in signer_files {
|
for signer_file in signer_files {
|
||||||
cmd.args(&["--signer-file", signer_file]);
|
cmd.args(&["--signer-file", signer_file]);
|
||||||
}
|
}
|
||||||
@ -272,7 +283,7 @@ fn sq_link_add_retract() -> Result<()> {
|
|||||||
// None of the certificates can be authenticated so verifying the
|
// None of the certificates can be authenticated so verifying the
|
||||||
// messages should fail.
|
// messages should fail.
|
||||||
for data in data.iter() {
|
for data in data.iter() {
|
||||||
sq_verify(Some(&certd), &[], &[], &data.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &data.sig_file, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Have Alice certify Bob as a trusted introducer and have Bob
|
// Have Alice certify Bob as a trusted introducer and have Bob
|
||||||
@ -288,58 +299,58 @@ fn sq_link_add_retract() -> Result<()> {
|
|||||||
// signatures Alice as the trust root. And Bob's and Carols' with
|
// signatures Alice as the trust root. And Bob's and Carols' with
|
||||||
// Bob as the trust root.
|
// Bob as the trust root.
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[&alice_fpr], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&alice_fpr], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[&alice_fpr], &[], &bob.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&alice_fpr], &[], &bob.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[&alice_fpr], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&alice_fpr], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[&alice_fpr], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&alice_fpr], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[&bob_fpr], &[], &alice.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&bob_fpr], &[], &alice.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[&bob_fpr], &[], &bob.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&bob_fpr], &[], &bob.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[&bob_fpr], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&bob_fpr], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[&bob_fpr], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&bob_fpr], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[&carol_fpr], &[], &alice.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&carol_fpr], &[], &alice.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[&carol_fpr], &[], &bob.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&carol_fpr], &[], &bob.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[&carol_fpr], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&carol_fpr], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[&carol_fpr], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&carol_fpr], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[&dave_fpr], &[], &alice.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&dave_fpr], &[], &alice.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[&dave_fpr], &[], &bob.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&dave_fpr], &[], &bob.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[&dave_fpr], &[], &carol.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[&dave_fpr], &[], &carol.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[&dave_fpr], &[], &dave.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[&dave_fpr], &[], &dave.sig_file, 1, 0);
|
||||||
|
|
||||||
// Let's accept Alice, but not (yet) as a trusted introducer. We
|
// Let's accept Alice, but not (yet) as a trusted introducer. We
|
||||||
// should now be able to verify Alice's signature, but not Bob's.
|
// should now be able to verify Alice's signature, but not Bob's.
|
||||||
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &[], true);
|
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &[], true);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &bob.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &bob.sig_file, 0, 1);
|
||||||
|
|
||||||
// Accept Alice as a trusted introducer. We should be able to
|
// Accept Alice as a trusted introducer. We should be able to
|
||||||
// verify Alice, Bob, and Carol's signatures.
|
// verify Alice, Bob, and Carol's signatures.
|
||||||
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &["--ca", "*"], true);
|
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &["--ca", "*"], true);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
// Retract the acceptance for Alice. If we don't specify a trust
|
// Retract the acceptance for Alice. If we don't specify a trust
|
||||||
// root, none of the signatures should verify.
|
// root, none of the signatures should verify.
|
||||||
sq_retract(&certd, &alice_fpr, &[ &alice_userid ]);
|
sq_retract(&certd, &alice_fpr, &[ &alice_userid ]);
|
||||||
|
|
||||||
for data in data.iter() {
|
for data in data.iter() {
|
||||||
sq_verify(Some(&certd), &[], &[], &data.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &data.sig_file, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accept Alice as a trusted introducer again. We should be able
|
// Accept Alice as a trusted introducer again. We should be able
|
||||||
// to verify Alice, Bob, and Carol's signatures.
|
// to verify Alice, Bob, and Carol's signatures.
|
||||||
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &["--ca", "*"], true);
|
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &["--ca", "*"], true);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
// Have Bob certify Dave. Now Dave's signature should also
|
// Have Bob certify Dave. Now Dave's signature should also
|
||||||
// verify.
|
// verify.
|
||||||
@ -347,29 +358,29 @@ fn sq_link_add_retract() -> Result<()> {
|
|||||||
&dave.cert.fingerprint().to_string(), dave_userid,
|
&dave.cert.fingerprint().to_string(), dave_userid,
|
||||||
None, None);
|
None, None);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &dave.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &dave.sig_file, 1, 0);
|
||||||
|
|
||||||
// Change Alice's acceptance to just be a normal certification.
|
// Change Alice's acceptance to just be a normal certification.
|
||||||
// We should only be able to verify her signature.
|
// We should only be able to verify her signature.
|
||||||
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &[], true);
|
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &[], true);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &bob.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &bob.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[], &[], &carol.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &carol.sig_file, 0, 1);
|
||||||
sq_verify(Some(&certd), &[], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
// Change Alice's acceptance to be a ca, but only for example.org,
|
// Change Alice's acceptance to be a ca, but only for example.org,
|
||||||
// i.e., not for Dave.
|
// i.e., not for Dave.
|
||||||
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &["--ca", "example.org"],
|
sq_link(&certd, &alice_fpr, &[ &alice_userid ], &["--ca", "example.org"],
|
||||||
true);
|
true);
|
||||||
|
|
||||||
sq_verify(Some(&certd), &[], &[], &alice.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &alice.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &bob.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &bob.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &carol.sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &carol.sig_file, 1, 0);
|
||||||
sq_verify(Some(&certd), &[], &[], &dave.sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &dave.sig_file, 0, 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -404,37 +415,37 @@ fn sq_link_add_retract() -> Result<()> {
|
|||||||
// If we don't use --petname, than a self-signed User ID must
|
// If we don't use --petname, than a self-signed User ID must
|
||||||
// exist.
|
// exist.
|
||||||
sq_link(&certd, &ed_fpr, &[ "--userid", "bob@example.com" ], &[], false);
|
sq_link(&certd, &ed_fpr, &[ "--userid", "bob@example.com" ], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
sq_link(&certd, &ed_fpr, &[ "--email", "bob@example.com" ], &[], false);
|
sq_link(&certd, &ed_fpr, &[ "--email", "bob@example.com" ], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
sq_link(&certd, &ed_fpr, &[ "bob@example.com" ], &[], false);
|
sq_link(&certd, &ed_fpr, &[ "bob@example.com" ], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
// We should only create links if all the supplied User IDs are
|
// We should only create links if all the supplied User IDs are
|
||||||
// valid.
|
// valid.
|
||||||
sq_link(&certd, &ed_fpr, &[
|
sq_link(&certd, &ed_fpr, &[
|
||||||
"--userid", "ed@some.org", "--userid", "bob@example.com"
|
"--userid", "ed@some.org", "--userid", "bob@example.com"
|
||||||
], &[], false);
|
], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
sq_link(&certd, &ed_fpr, &[
|
sq_link(&certd, &ed_fpr, &[
|
||||||
"--userid", "ed@some.org", "--email", "bob@example.com"
|
"--userid", "ed@some.org", "--email", "bob@example.com"
|
||||||
], &[], false);
|
], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
sq_link(&certd, &ed_fpr, &[
|
sq_link(&certd, &ed_fpr, &[
|
||||||
"--userid", "ed@some.org", "bob@example.com"
|
"--userid", "ed@some.org", "bob@example.com"
|
||||||
], &[], false);
|
], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
// Pass an email address to --userid. This shouldn't match
|
// Pass an email address to --userid. This shouldn't match
|
||||||
// either.
|
// either.
|
||||||
sq_link(&certd, &ed_fpr, &[
|
sq_link(&certd, &ed_fpr, &[
|
||||||
"--userid", "ed@other.org"
|
"--userid", "ed@other.org"
|
||||||
], &[], false);
|
], &[], false);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
// Link all User IDs individually.
|
// Link all User IDs individually.
|
||||||
sq_link(&certd, &ed_fpr, &[
|
sq_link(&certd, &ed_fpr, &[
|
||||||
@ -442,21 +453,21 @@ fn sq_link_add_retract() -> Result<()> {
|
|||||||
"--email", "ed@example.org",
|
"--email", "ed@example.org",
|
||||||
"--userid", "ed@some.org",
|
"--userid", "ed@some.org",
|
||||||
], &[], true);
|
], &[], true);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 1, 0);
|
||||||
|
|
||||||
// Retract the links one at a time.
|
// Retract the links one at a time.
|
||||||
sq_retract(&certd, &ed_fpr, &[ "ed@other.org" ]);
|
sq_retract(&certd, &ed_fpr, &[ "ed@other.org" ]);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 1, 0);
|
||||||
|
|
||||||
sq_retract(&certd, &ed_fpr, &[ "Ed <ed@example.org>" ]);
|
sq_retract(&certd, &ed_fpr, &[ "Ed <ed@example.org>" ]);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 1, 0);
|
||||||
|
|
||||||
sq_retract(&certd, &ed_fpr, &[ "Eddie <ed@example.org>" ]);
|
sq_retract(&certd, &ed_fpr, &[ "Eddie <ed@example.org>" ]);
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 1, 0);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 1, 0);
|
||||||
|
|
||||||
sq_retract(&certd, &ed_fpr, &[ "ed@some.org" ]);
|
sq_retract(&certd, &ed_fpr, &[ "ed@some.org" ]);
|
||||||
// Now the certificate should no longer be authenticated.
|
// Now the certificate should no longer be authenticated.
|
||||||
sq_verify(Some(&certd), &[], &[], &ed_sig_file, 0, 1);
|
sq_verify(Some(&certd), None, &[], &[], &ed_sig_file, 0, 1);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -587,3 +598,88 @@ fn sq_link_update_detection() -> Result<()> {
|
|||||||
let _ = bytes;
|
let _ = bytes;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that sq link add --temporary works.
|
||||||
|
#[test]
|
||||||
|
fn sq_link_add_temporary() -> Result<()> {
|
||||||
|
let dir = TempDir::new()?;
|
||||||
|
|
||||||
|
let certd = dir.path().join("cert.d").display().to_string();
|
||||||
|
std::fs::create_dir(&certd).expect("mkdir works");
|
||||||
|
|
||||||
|
let alice_pgp = dir.path().join("alice.pgp").display().to_string();
|
||||||
|
let alice_userid = "<alice@example.org>";
|
||||||
|
let alice = sq_gen_key(Some(&certd), &[ alice_userid ], &alice_pgp);
|
||||||
|
let alice_fpr = alice.fingerprint().to_string();
|
||||||
|
let alice_cert_pgp = dir.path().join("cert.d")
|
||||||
|
.join(&alice_fpr[0..2].to_ascii_lowercase())
|
||||||
|
.join(&alice_fpr[2..].to_ascii_lowercase());
|
||||||
|
|
||||||
|
let alice_sig_file = dir.path().join("alice.sig").display().to_string();
|
||||||
|
Command::cargo_bin("sq")
|
||||||
|
.unwrap()
|
||||||
|
.arg("--no-cert-store")
|
||||||
|
.arg("sign")
|
||||||
|
.args(["--signer-file", &alice_pgp])
|
||||||
|
.args(["--output", &alice_sig_file])
|
||||||
|
.args(["--time", &tick()])
|
||||||
|
.arg(&artifact("messages/a-cypherpunks-manifesto.txt"))
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Reads and returns file. Asserts that old and the new contexts
|
||||||
|
// are the same (or not).
|
||||||
|
let compare = |old: Vec<u8>, file: &Path, same: bool| -> Vec<u8> {
|
||||||
|
let new = std::fs::read(file).unwrap();
|
||||||
|
if same {
|
||||||
|
assert_eq!(old, new, "file unexpectedly changed");
|
||||||
|
} else {
|
||||||
|
assert_ne!(old, new, "file unexpectedly stayed the same");
|
||||||
|
}
|
||||||
|
new
|
||||||
|
};
|
||||||
|
let bytes = std::fs::read(&alice_cert_pgp).unwrap();
|
||||||
|
|
||||||
|
sq_verify(Some(&certd), None, &[], &[], &alice_sig_file, 0, 1);
|
||||||
|
|
||||||
|
let output = sq_link(&certd, &alice_fpr, &[], &["--temporary", "--all"], true);
|
||||||
|
assert!(output.2.contains("Linking "),
|
||||||
|
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
||||||
|
let bytes = compare(bytes, &alice_cert_pgp, false);
|
||||||
|
|
||||||
|
// Now it is fully trusted.
|
||||||
|
sq_verify(Some(&certd), None, &[], &[], &alice_sig_file, 1, 0);
|
||||||
|
|
||||||
|
// In 6 days, too.
|
||||||
|
sq_verify(Some(&certd),
|
||||||
|
Some(now() + chrono::Duration::seconds(6 * 24 * 60 * 60)),
|
||||||
|
&[], &[], &alice_sig_file, 1, 0);
|
||||||
|
|
||||||
|
// But in 8 days it will only be partially trusted.
|
||||||
|
sq_verify(Some(&certd),
|
||||||
|
Some(now() + chrono::Duration::seconds(8 * 24 * 60 * 60)),
|
||||||
|
&[], &[], &alice_sig_file, 0, 1);
|
||||||
|
|
||||||
|
|
||||||
|
// Now mark it as fully trusted. It should be trusted now, in 6
|
||||||
|
// days and in 8 days.
|
||||||
|
let output = sq_link(&certd, &alice_fpr, &[], &["--all"], true);
|
||||||
|
assert!(output.2.contains("was already linked"),
|
||||||
|
"stdout:\n{}\nstderr:\n{}", output.1, output.2);
|
||||||
|
eprintln!("{:?}", output);
|
||||||
|
let bytes = compare(bytes, &alice_cert_pgp, false);
|
||||||
|
|
||||||
|
sq_verify(Some(&certd), None, &[], &[], &alice_sig_file, 1, 0);
|
||||||
|
|
||||||
|
sq_verify(Some(&certd),
|
||||||
|
Some(now() + chrono::Duration::seconds(6 * 24 * 60 * 60)),
|
||||||
|
&[], &[], &alice_sig_file, 1, 0);
|
||||||
|
|
||||||
|
sq_verify(Some(&certd),
|
||||||
|
Some(now() + chrono::Duration::seconds(8 * 24 * 60 * 60)),
|
||||||
|
&[], &[], &alice_sig_file, 1, 0);
|
||||||
|
|
||||||
|
let _bytes = bytes;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user