use std::fs::File; use std::time::Duration; use assert_cli; use assert_cli::Assert; use tempfile; use tempfile::TempDir; use sequoia_openpgp as openpgp; use openpgp::Result; use openpgp::cert::prelude::*; use openpgp::packet::signature::subpacket::NotationData; use openpgp::packet::signature::subpacket::NotationDataFlags; use openpgp::parse::Parse; use openpgp::policy::StandardPolicy; use openpgp::serialize::Serialize; #[test] fn sq_certify() -> Result<()> { let tmp_dir = TempDir::new().unwrap(); let alice_pgp = tmp_dir.path().join("alice.pgp"); let bob_pgp = tmp_dir.path().join("bob.pgp"); let (alice, _) = CertBuilder::general_purpose(None, Some("alice@example.org")) .generate()?; let mut file = File::create(&alice_pgp)?; alice.as_tsk().serialize(&mut file)?; let (bob, _) = CertBuilder::general_purpose(None, Some("bob@example.org")) .generate()?; let mut file = File::create(&bob_pgp)?; bob.serialize(&mut file)?; // A simple certification. Assert::cargo_binary("sq") .with_args( &["certify", alice_pgp.to_str().unwrap(), bob_pgp.to_str().unwrap(), "bob@example.org", ]) .stdout().satisfies(|output| { let p = &StandardPolicy::new(); let cert = Cert::from_bytes(output).unwrap(); let vc = cert.with_policy(p, None).unwrap(); for ua in vc.userids() { if ua.userid().value() == b"bob@example.org" { let certifications: Vec<_> = ua.certifications().collect(); assert_eq!(certifications.len(), 1); let c = certifications[0]; assert_eq!(c.trust_signature(), None); assert_eq!(c.regular_expressions().count(), 0); assert_eq!(c.revocable().unwrap_or(true), true); assert_eq!(c.exportable_certification().unwrap_or(true), true); // By default, we set a duration. assert!(c.signature_validity_period().is_some()); return true; } } false }, "Bad certification") .unwrap(); // No expiry. Assert::cargo_binary("sq") .with_args( &["certify", alice_pgp.to_str().unwrap(), bob_pgp.to_str().unwrap(), "bob@example.org", "--expires", "never" ]) .stdout().satisfies(|output| { let p = &StandardPolicy::new(); let cert = Cert::from_bytes(output).unwrap(); let vc = cert.with_policy(p, None).unwrap(); for ua in vc.userids() { if ua.userid().value() == b"bob@example.org" { let certifications: Vec<_> = ua.certifications().collect(); assert_eq!(certifications.len(), 1); let c = certifications[0]; assert_eq!(c.trust_signature(), None); assert_eq!(c.regular_expressions().count(), 0); assert_eq!(c.revocable().unwrap_or(true), true); assert_eq!(c.exportable_certification().unwrap_or(true), true); assert!(c.signature_validity_period().is_none()); return true; } } false }, "Bad certification") .unwrap(); // Have alice certify bob@example.org for 0xB0B. Assert::cargo_binary("sq") .with_args( &["certify", alice_pgp.to_str().unwrap(), bob_pgp.to_str().unwrap(), "bob@example.org", "--depth", "10", "--amount", "5", "--regex", "a", "--regex", "b", "--local", "--non-revocable", "--expires-in", "1d", ]) .stdout().satisfies(|output| { let p = &mut StandardPolicy::new(); // XXX: Compat with sequoia-openpgp 1.0.0: use openpgp::packet::signature::subpacket::SubpacketTag; p.accept_critical_subpacket(SubpacketTag::TrustSignature); p.accept_critical_subpacket(SubpacketTag::RegularExpression); let cert = Cert::from_bytes(output).unwrap(); let vc = cert.with_policy(p, None).unwrap(); for ua in vc.userids() { if ua.userid().value() == b"bob@example.org" { let certifications: Vec<_> = ua.certifications().collect(); assert_eq!(certifications.len(), 1); let c = certifications[0]; assert_eq!(c.trust_signature(), Some((10, 5))); assert_eq!(&c.regular_expressions().collect::>()[..], &[ b"a", b"b" ]); assert_eq!(c.revocable(), Some(false)); assert_eq!(c.exportable_certification(), Some(false)); assert_eq!(c.signature_validity_period(), Some(Duration::new(24 * 60 * 60, 0))); return true; } } false }, "Bad certification") .unwrap(); // It should fail if the User ID doesn't exist. Assert::cargo_binary("sq") .with_args( &["certify", alice_pgp.to_str().unwrap(), bob_pgp.to_str().unwrap(), "bob", ]) .fails() .unwrap(); // With a notation. Assert::cargo_binary("sq") .with_args( &["certify", "--notation", "foo", "bar", "--notation", "!foo", "xyzzy", "--notation", "hello@example.org", "1234567890", alice_pgp.to_str().unwrap(), bob_pgp.to_str().unwrap(), "bob@example.org", ]) .stdout().satisfies(|output| { let p = &mut StandardPolicy::new(); let cert = Cert::from_bytes(output).unwrap(); // The standard policy will reject the // certification, because it has an unknown // critical notation. let vc = cert.with_policy(p, None).unwrap(); for ua in vc.userids() { if ua.userid().value() == b"bob@example.org" { let certifications: Vec<_> = ua.certifications().collect(); assert_eq!(certifications.len(), 0); } } // Accept the critical notation. p.good_critical_notations(&["foo"]); let vc = cert.with_policy(p, None).unwrap(); for ua in vc.userids() { if ua.userid().value() == b"bob@example.org" { let certifications: Vec<_> = ua.certifications().collect(); assert_eq!(certifications.len(), 1); let c = certifications[0]; assert_eq!(c.trust_signature(), None); assert_eq!(c.regular_expressions().count(), 0); assert_eq!(c.revocable().unwrap_or(true), true); assert_eq!(c.exportable_certification().unwrap_or(true), true); // By default, we set a duration. assert!(c.signature_validity_period().is_some()); let hr = NotationDataFlags::empty().set_human_readable(); let notations = &mut [ (NotationData::new("foo", "bar", hr.clone()), false), (NotationData::new("foo", "xyzzy", hr.clone()), false), (NotationData::new("hello@example.org", "1234567890", hr), false) ]; for n in c.notation_data() { if n.name() == "salt@notations.sequoia-pgp.org" { continue; } for (m, found) in notations.iter_mut() { if n == m { assert!(!*found); *found = true; } } } for (n, found) in notations.iter() { assert!(found, "Missing: {:?}", n); } return true; } } false }, "Bad certification") .unwrap(); Ok(()) }