diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2021-01-12 09:18:19 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2021-01-19 09:00:22 +0100 |
commit | a5acfb3c4c1e0cbb2fcdf38127df8aa40e082bd6 (patch) | |
tree | c1314f7f94041853b6ec732aeda0a18fa716c473 /openpgp/src/cert.rs | |
parent | 4cf73914f253c8c8d46ffc94b73213e0313343b3 (diff) |
openpgp: Support attested key signatures in certs.
- Note that the spec is still in flux, therefore we don't expose it.
Diffstat (limited to 'openpgp/src/cert.rs')
-rw-r--r-- | openpgp/src/cert.rs | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs index 0faa2996..7da3fd35 100644 --- a/openpgp/src/cert.rs +++ b/openpgp/src/cert.rs @@ -331,6 +331,7 @@ impl<C> ComponentBundles<C> // Recall: if a and b are equal, a will be dropped. b.self_signatures.append(&mut a.self_signatures); + b.attestations.append(&mut a.attestations); b.certifications.append(&mut a.certifications); b.self_revocations.append(&mut a.self_revocations); b.other_revocations.append(&mut a.other_revocations); @@ -1805,6 +1806,23 @@ impl Cert { } }, + crate::types::SignatureType__AttestedKey => { + for binding in self.userids.iter_mut() { + check_one!(format!("userid \"{}\"", + String::from_utf8_lossy( + binding.userid().value())), + binding.attestations, sig, + verify_userid_attestation, binding.userid()); + } + + for binding in self.user_attributes.iter_mut() { + check_one!("user attribute", + binding.attestations, sig, + verify_user_attribute_attestation, + binding.user_attribute()); + } + }, + CertificationRevocation => { for binding in self.userids.iter_mut() { check_one!(format!("userid \"{}\"", @@ -2116,6 +2134,8 @@ impl Cert { self.primary.self_signatures.append( &mut other.primary.self_signatures); + self.primary.attestations.append( + &mut other.primary.attestations); self.primary.certifications.append( &mut other.primary.certifications); self.primary.self_revocations.append( @@ -5967,4 +5987,90 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= Ok(()) } + + /// Makes sure that attested key signatures are correctly handled. + #[test] + fn attested_key_signatures() -> Result<()> { + use crate::{ + crypto::hash::Hash, + packet::signature::{SignatureBuilder, subpacket::*}, + types::*, + }; + + let (alice, _) = CertBuilder::new() + .add_userid("alice@foo.com") + .generate()?; + let mut alice_signer = + alice.primary_key().key().clone().parts_into_secret()? + .into_keypair()?; + + let (bob, _) = CertBuilder::new() + .add_userid("bob@bar.com") + .generate()?; + let mut bob_signer = + bob.primary_key().key().clone().parts_into_secret()? + .into_keypair()?; + let bob_pristine = bob.clone(); + + // Have Alice certify the binding between "bob@bar.com" and + // Bob's key. + let alice_certifies_bob + = bob.userids().nth(0).unwrap().userid().bind( + &mut alice_signer, &bob, + SignatureBuilder::new(SignatureType::GenericCertification))?; + + // Have Bob attest that certification. + let hash_algo = HashAlgorithm::default(); + + // First, hash the certification. + let mut h = hash_algo.context()?; + alice_certifies_bob.hash_for_confirmation(&mut h); + let digest = h.into_digest()?; + + // Then, prepare an attested key signature. + let mut h = hash_algo.context()?; + bob.primary_key().key().hash(&mut h); + bob.userids().nth(0).unwrap().userid().hash(&mut h); + + let attestation = SignatureBuilder::new(SignatureType__AttestedKey) + .modify_hashed_area(|mut a| { + a.add(Subpacket::new( + SubpacketValue::Unknown { + tag: SubpacketTag__AttestedCertifications, + body: digest, + }, + true)?)?; + Ok(a) + })? + .sign_hash(&mut bob_signer, h)?; + + let bob = bob.insert_packets(vec![ + alice_certifies_bob.clone(), + attestation.clone(), + ])?; + + assert_eq!(bob.bad_signatures().count(), 0); + assert_eq!(bob.userids().nth(0).unwrap().certifications().nth(0), + Some(&alice_certifies_bob)); + assert_eq!(&bob.userids().nth(0).unwrap().bundle().attestations[0], + &attestation); + + // Check that attested key signatures are kept over merges. + let bob_ = bob.clone().merge_public(bob_pristine.clone())?; + assert_eq!(bob_.bad_signatures().count(), 0); + assert_eq!(bob_.userids().nth(0).unwrap().certifications().nth(0), + Some(&alice_certifies_bob)); + assert_eq!(&bob_.userids().nth(0).unwrap().bundle().attestations[0], + &attestation); + + // And the other way around. + let bob_ = bob_pristine.clone().merge_public(bob.clone())?; + assert_eq!(bob_.bad_signatures().count(), 0); + assert_eq!(bob_.userids().nth(0).unwrap().certifications().nth(0), + Some(&alice_certifies_bob)); + assert_eq!(&bob_.userids().nth(0).unwrap().bundle().attestations[0], + &attestation); + + Ok(()) + } } |