use std::fs;
use sequoia_openpgp as openpgp;
use openpgp::cert::KeyBuilder;
use openpgp::policy::StandardPolicy;
use openpgp::types::KeyFlags;
mod common;
use common::Environment;
use common::rotate_subkeys;
const P: &StandardPolicy = &StandardPolicy::new();
#[test]
fn keyring_update_add_certificate() -> anyhow::Result<()> {
let e = Environment::new()?;
let p = e.git_state();
let (alice, alice_pgp) = e.gen("alice", None, None);
let (bob, bob_pgp) = e.gen("bob", None, None);
let (alice_bob, alice_bob_pgp) = e.gen("alice-bob", None, None);
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice_pgp,
"--project-maintainer" ])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice makes herself a release manager.",
&format!("-S{}", alice.fingerprint()),
])?;
let root = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob_pgp,
"--committer" ])?;
e.git(&["add", "openpgp-policy.toml"])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&[
"commit",
"-m", "Alice makes Bob a committer.",
&format!("-S{}", alice.fingerprint()),
])?;
let _c2 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
fs::write(p.join("a"), "aaa.")?;
e.git(&["add", "a"])?;
e.git(&[
"commit",
"-m", "Bob makes a commit.",
&format!("-S{}", bob.fingerprint()),
])?;
let c3 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice_bob_pgp,
"--project-maintainer" ])?;
e.check_export("alice", None, &[ &alice, &alice_bob ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Bob adds a certificate to Alice's keyring.",
&format!("-S{}", bob.fingerprint()),
])?;
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root]).is_err());
e.git(&["reset", "--soft", &c3])?;
e.check_export("alice", None, &[ &alice, &alice_bob ]);
e.check_export("bob", None, &[ &bob ]);
assert_eq!(c3, e.git_current_commit()?);
e.git(&[
"commit",
"-m", "Alice adds the certificate to her keyring.",
&format!("-S{}", alice.fingerprint()),
])?;
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root]).is_ok());
fs::write(p.join("b"), "bbb.")?;
e.git(&["add", "b"])?;
e.git(&[
"commit",
"-m", "Alice makes a commit signed with the new certificate.",
&format!("-S{}", alice_bob.fingerprint()),
])?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
Ok(())
}
#[test]
fn keyring_update_remove_certificate() -> anyhow::Result<()> {
let e = Environment::new()?;
let p = e.git_state();
let (alice1, alice1_pgp) = e.gen("alice1", None, None);
let (alice2, alice2_pgp) = e.gen("alice2", None, None);
let (bob, bob_pgp) = e.gen("bob", None, None);
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice1_pgp,
"--project-maintainer" ])?;
e.check_export("alice", None, &[ &alice1 ]);
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice2_pgp,
"--project-maintainer" ])?;
e.check_export("alice", None, &[ &alice1, &alice2 ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice makes herself a release manager.",
&format!("-S{}", alice1.fingerprint()),
])?;
let root = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob_pgp,
"--committer" ])?;
e.check_export("alice", None, &[ &alice1, &alice2 ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice makes Bob a committer.",
&format!("-S{}", alice2.fingerprint()),
])?;
let _c2 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
fs::write(p.join("a"), "aaa.")?;
e.git(&["add", "a"])?;
e.git(&[
"commit",
"-m", "Bob makes a commit.",
&format!("-S{}", bob.fingerprint()),
])?;
let c3 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
std::fs::remove_file(p.join("openpgp-policy.toml")).expect("can remove");
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice1_pgp,
"--project-maintainer" ])?;
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob_pgp,
"--committer" ])?;
e.check_export("alice", None, &[ &alice1 ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Bob removes one of Alice's certificates.",
&format!("-S{}", bob.fingerprint()),
])?;
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root]).is_err());
e.git(&["reset", "--soft", &c3])?;
e.check_export("alice", None, &[ &alice1 ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice removes one of her certificates.",
&format!("-S{}", alice2.fingerprint()),
])?;
Ok(())
}
#[test]
fn keyring_update_add_packet() -> anyhow::Result<()> {
let e = Environment::new()?;
let p = e.git_state();
let (alice, alice_pgp) = e.gen("alice", None, None);
let (bob, bob_pgp) = e.gen("bob", None, None);
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice_pgp,
"--project-maintainer" ])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice makes herself a release manager.",
&format!("-S{}", alice.fingerprint()),
])?;
let root = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob_pgp,
"--committer" ])?;
e.git(&["add", "openpgp-policy.toml"])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&[
"commit",
"-m", "Alice makes Bob a committer.",
&format!("-S{}", alice.fingerprint()),
])?;
let _c2 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
fs::write(p.join("a"), "aaa.")?;
e.git(&["add", "a"])?;
e.git(&[
"commit",
"-m", "Bob makes a commit.",
&format!("-S{}", bob.fingerprint()),
])?;
let _c3 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
let bob2 = rotate_subkeys(&bob);
assert!(bob.keys().count() < bob2.keys().count());
let bob2_pgp = e.serialize_cert("bob2", &bob2);
e.import(&bob2).expect("can import");
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob2_pgp,
"--committer" ])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ &bob2 ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Bob updates his certificate.",
&format!("-S{}", bob.fingerprint()),
])?;
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root]).is_ok());
let alice_vc = alice.with_policy(P, None).expect("valid cert");
let alice2 = KeyBuilder::new(KeyFlags::signing())
.subkey(alice_vc).unwrap()
.attach_cert().unwrap();
assert!(alice.keys().count() < alice2.keys().count());
let alice2_pgp = e.serialize_cert("alice2", &alice2);
e.import(&alice2).expect("can import");
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice2_pgp,
])?;
e.check_export("alice", None, &[ &alice2 ]);
e.check_export("bob", None, &[ &bob2 ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Bob updates Alice's certificate.",
&format!("-S{}", bob.fingerprint()),
])?;
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root]).is_ok());
Ok(())
}
#[test]
fn keyring_update_remove_packet() -> anyhow::Result<()> {
let e = Environment::new()?;
let p = e.git_state();
let (alice, alice_pgp) = e.gen("alice", None, None);
let (bob, bob_pgp) = e.gen("bob", None, None);
let bob_vc = bob.with_policy(P, None).expect("valid cert");
let bob2 = KeyBuilder::new(KeyFlags::signing())
.subkey(bob_vc).unwrap()
.attach_cert().unwrap();
assert!(bob.keys().count() < bob2.keys().count());
let bob2_pgp = e.serialize_cert("bob2", &bob2);
e.import(&bob2).expect("can import");
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice_pgp,
"--project-maintainer" ])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice makes herself a release manager.",
&format!("-S{}", alice.fingerprint()),
])?;
let root = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob2_pgp,
"--committer" ])?;
e.git(&["add", "openpgp-policy.toml"])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ &bob2 ]);
e.git(&[
"commit",
"-m", "Alice makes Bob a committer.",
&format!("-S{}", alice.fingerprint()),
])?;
let _c2 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
fs::write(p.join("a"), "aaa.")?;
e.git(&["add", "a"])?;
e.git(&[
"commit",
"-m", "Bob makes a commit.",
&format!("-S{}", bob.fingerprint()),
])?;
let c3 = e.git_current_commit()?;
e.git(&["log"])?;
e.sq_git(&["log", "--trust-root", &root])?;
std::fs::remove_file(p.join("openpgp-policy.toml")).expect("can remove");
e.sq_git(&[
"policy",
"authorize",
"alice",
"--cert-file", &alice_pgp,
"--project-maintainer" ])?;
e.sq_git(&[
"policy",
"authorize",
"bob",
"--cert-file", &bob_pgp,
"--committer" ])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Bob strips a user ID from his certificate.",
&format!("-S{}", bob.fingerprint()),
])?;
e.git(&["log"])?;
assert!(e.sq_git(&["log", "--trust-root", &root]).is_err());
e.git(&["reset", "--soft", &c3])?;
e.check_export("alice", None, &[ &alice ]);
e.check_export("bob", None, &[ &bob ]);
e.git(&["add", "openpgp-policy.toml"])?;
e.git(&[
"commit",
"-m", "Alice strips a user ID from Bob's certificate.",
&format!("-S{}", alice.fingerprint()),
])?;
Ok(())
}