diff options
-rw-r--r-- | openpgp/NEWS | 8 | ||||
-rw-r--r-- | openpgp/src/cert.rs | 72 | ||||
-rw-r--r-- | openpgp/src/cert/amalgamation.rs | 504 | ||||
-rw-r--r-- | sq/src/commands/key.rs | 93 |
4 files changed, 583 insertions, 94 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS index 188e6f33..9a0da59d 100644 --- a/openpgp/NEWS +++ b/openpgp/NEWS @@ -14,6 +14,14 @@ - SubpacketAreas::attested_certifications - SubpacketTag::AttestedCertifications - SubpacketValue::AttestedCertifications + - UserAttributeAmalgamation::attest_certifications + - UserIDAmalgamation::attest_certifications + - ValidUserAttributeAmalgamation::attest_certifications + - ValidUserAttributeAmalgamation::attestation_key_signatures + - ValidUserAttributeAmalgamation::attested_certifications + - ValidUserIDAmalgamation::attest_certifications + - ValidUserIDAmalgamation::attestation_key_signatures + - ValidUserIDAmalgamation::attested_certifications * Changes in 1.1.0 ** New functionality - The new regex module provides regular expression support for diff --git a/openpgp/src/cert.rs b/openpgp/src/cert.rs index f7f962ef..162b63e0 100644 --- a/openpgp/src/cert.rs +++ b/openpgp/src/cert.rs @@ -6013,10 +6013,10 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= #[test] fn attested_key_signatures() -> Result<()> { use crate::{ - crypto::hash::Hash, packet::signature::SignatureBuilder, types::*, }; + let p = &crate::policy::StandardPolicy::new(); let (alice, _) = CertBuilder::new() .add_userid("alice@foo.com") @@ -6039,26 +6039,25 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= = bob.userids().next().unwrap().userid().bind( &mut alice_signer, &bob, SignatureBuilder::new(SignatureType::GenericCertification))?; + let bob = bob.insert_packets(vec![ + alice_certifies_bob.clone(), + ])?; - // 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().next().unwrap().userid().hash(&mut h); + assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap() + .certifications().count(), 1); + assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap() + .attested_certifications().count(), 0); - let attestation = SignatureBuilder::new(SignatureType::AttestationKey) - .set_attested_certifications(vec![digest])? - .sign_hash(&mut bob_signer, h)?; + // Have Bob attest that certification. + let attestations = + bob.userids().next().unwrap().attest_certifications( + p, + &mut bob_signer, + vec![&alice_certifies_bob])?; + assert_eq!(attestations.len(), 1); + let attestation = attestations[0].clone(); let bob = bob.insert_packets(vec![ - alice_certifies_bob.clone(), attestation.clone(), ])?; @@ -6067,6 +6066,10 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= Some(&alice_certifies_bob)); assert_eq!(&bob.userids().next().unwrap().bundle().attestations[0], &attestation); + assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap() + .certifications().count(), 1); + assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap() + .attested_certifications().count(), 1); // Check that attested key signatures are kept over merges. let bob_ = bob.clone().merge_public(bob_pristine.clone())?; @@ -6075,6 +6078,8 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= Some(&alice_certifies_bob)); assert_eq!(&bob_.userids().next().unwrap().bundle().attestations[0], &attestation); + assert_eq!(bob_.with_policy(p, None)?.userids().next().unwrap() + .attested_certifications().count(), 1); // And the other way around. let bob_ = bob_pristine.clone().merge_public(bob.clone())?; @@ -6083,6 +6088,33 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= Some(&alice_certifies_bob)); assert_eq!(&bob_.userids().next().unwrap().bundle().attestations[0], &attestation); + assert_eq!(bob_.with_policy(p, None)?.userids().next().unwrap() + .attested_certifications().count(), 1); + + // Have Bob withdraw any prior attestations. + + let attestations = + bob.userids().next().unwrap().attest_certifications( + p, + &mut bob_signer, + &[])?; + assert_eq!(attestations.len(), 1); + let attestation = attestations[0].clone(); + + let bob = bob.insert_packets(vec![ + attestation.clone(), + ])?; + + assert_eq!(bob.bad_signatures().count(), 0); + assert_eq!(bob.userids().next().unwrap().certifications().next(), + Some(&alice_certifies_bob)); + assert_eq!(&bob.userids().next().unwrap().bundle().attestations[0], + &attestation); + assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap() + .certifications().count(), 1); + assert_eq!(bob.with_policy(p, None)?.userids().next().unwrap() + .attested_certifications().count(), 0); + Ok(()) } @@ -6094,6 +6126,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= use crate::{ crypto::hash::Digest, }; + let p = &crate::policy::StandardPolicy::new(); let test = Cert::from_bytes(crate::tests::key("1pa3pc-dkgpg.pgp"))?; assert_eq!(test.bad_signatures().count(), 0); @@ -6131,6 +6164,11 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g= assert!(digests.contains(&digest[..])); } + assert_eq!(test.with_policy(p, None)?.userids().next().unwrap() + .certifications().count(), 1); + assert_eq!(test.with_policy(p, None)?.userids().next().unwrap() + .attested_certifications().count(), 1); + Ok(()) } diff --git a/openpgp/src/cert/amalgamation.rs b/openpgp/src/cert/amalgamation.rs index b7c2306b..1c355aa1 100644 --- a/openpgp/src/cert/amalgamation.rs +++ b/openpgp/src/cert/amalgamation.rs @@ -226,9 +226,11 @@ use std::time; use std::time::SystemTime; use std::clone::Clone; +use std::borrow::Borrow; use crate::{ cert::prelude::*, + crypto::{Signer, hash::{Hash, Digest}}, Error, packet::{ Signature, @@ -249,6 +251,7 @@ use crate::{ HashAlgorithm, KeyServerPreferences, RevocationStatus, + SignatureType, SymmetricAlgorithm, }, }; @@ -886,6 +889,89 @@ impl<'a> UserIDAmalgamation<'a> { pub fn userid(&self) -> &'a UserID { self.component() } + + /// Attests to third-party certifications. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Allows the certificate owner to attest to third party + /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for + /// details. This can be used to address certificate flooding + /// concerns. + /// + /// A policy is needed, because the expiration is updated by + /// updating the current binding signatures. + /// + /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + /// + /// # Examples + /// + /// ``` + /// # use sequoia_openpgp as openpgp; + /// # fn main() -> openpgp::Result<()> { + /// # use openpgp::cert::prelude::*; + /// # use openpgp::packet::signature::SignatureBuilder; + /// # use openpgp::types::*; + /// # let policy = &openpgp::policy::StandardPolicy::new(); + /// let (alice, _) = CertBuilder::new() + /// .add_userid("alice@example.org") + /// .generate()?; + /// let mut alice_signer = + /// alice.primary_key().key().clone().parts_into_secret()? + /// .into_keypair()?; + /// + /// let (bob, _) = CertBuilder::new() + /// .add_userid("bob@example.org") + /// .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@example.org" and + /// // Bob's key. + /// let alice_certifies_bob + /// = bob.userids().next().unwrap().userid().bind( + /// &mut alice_signer, &bob, + /// SignatureBuilder::new(SignatureType::GenericCertification))?; + /// let bob = bob.insert_packets(vec![alice_certifies_bob.clone()])?; + /// + /// // Have Bob attest that certification. + /// let bobs_uid = bob.userids().next().unwrap(); + /// let attestations = + /// bobs_uid.attest_certifications( + /// policy, + /// &mut bob_signer, + /// bobs_uid.certifications())?; + /// let bob = bob.insert_packets(attestations)?; + /// + /// assert_eq!(bob.bad_signatures().count(), 0); + /// assert_eq!(bob.userids().next().unwrap().certifications().next(), + /// Some(&alice_certifies_bob)); + /// # Ok(()) } + /// ``` + pub fn attest_certifications<C, S>(&self, + policy: &dyn Policy, + primary_signer: &mut dyn Signer, + certifications: C) + -> Result<Vec<Signature>> + where C: IntoIterator<Item = S>, + S: Borrow<Signature>, + { + // Hash the components like in a binding signature. + let mut hash = HashAlgorithm::default().context()?; + self.cert().primary_key().hash(&mut hash); + self.userid().hash(&mut hash); + + // Check if there is a previous attestation. If so, we need + // that to robustly override it. + let old = self.clone() + .with_policy(policy, None) + .ok() + .and_then(|v| v.attestation_key_signatures().cloned().next()); + + attest_certifications_common(hash, old, primary_signer, certifications) + } } impl<'a> UserAttributeAmalgamation<'a> { @@ -901,6 +987,136 @@ impl<'a> UserAttributeAmalgamation<'a> { pub fn user_attribute(&self) -> &'a UserAttribute { self.component() } + + /// Attests to third-party certifications. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Allows the certificate owner to attest to third party + /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for + /// details. This can be used to address certificate flooding + /// concerns. + /// + /// A policy is needed, because the expiration is updated by + /// updating the current binding signatures. + /// + /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + /// + /// # Examples + /// + /// See [`UserIDAmalgamation::attest_certifications#examples`]. + /// + /// [`UserIDAmalgamation::attest_certifications#examples`]: type.UserIDAmalgamation.html#examples + // The explicit link works around a bug in rustdoc. + pub fn attest_certifications<C, S>(&self, + policy: &dyn Policy, + primary_signer: &mut dyn Signer, + certifications: C) + -> Result<Vec<Signature>> + where C: IntoIterator<Item = S>, + S: Borrow<Signature>, + { + // Hash the components like in a binding signature. + let mut hash = HashAlgorithm::default().context()?; + self.cert().primary_key().hash(&mut hash); + self.user_attribute().hash(&mut hash); + + // Check if there is a previous attestation. If so, we need + // that to robustly override it. + let old = self.clone() + .with_policy(policy, None) + .ok() + .and_then(|v| v.attestation_key_signatures().cloned().next()); + + attest_certifications_common(hash, old, primary_signer, certifications) + } +} + +/// Attests to third-party certifications. +fn attest_certifications_common<C, S>(hash: Box<dyn Digest>, + old_attestation: Option<Signature>, + primary_signer: &mut dyn Signer, + certifications: C) + -> Result<Vec<Signature>> +where C: IntoIterator<Item = S>, + S: Borrow<Signature>, +{ + use crate::{ + packet::signature::{SignatureBuilder, subpacket::SubpacketArea}, + serialize::MarshalInto, + }; + + let hash_algo = hash.algo(); + let digest_size = hash.digest_size(); + + let mut attestations = Vec::new(); + for certification in certifications.into_iter() { + let mut h = hash_algo.context()?; + certification.borrow().hash_for_confirmation(&mut h); + attestations.push(h.into_digest()?); + } + + // Hashes SHOULD be sorted. + attestations.sort(); + + // All attestation signatures we generate for this component + // should have the same creation time. Fix it now. We also like + // our signatures to be newer than any existing signatures. Do so + // by using the old attestation as template. + let template = if let Some(old) = old_attestation { + let mut s: SignatureBuilder = old.into(); + s.hashed_area_mut().clear(); + s.unhashed_area_mut().clear(); + s + } else { + // Backdate the signature a little so that we can immediately + // override it. + use crate::packet::signature::SIG_BACKDATE_BY; + let creation_time = + time::SystemTime::now() - time::Duration::new(SIG_BACKDATE_BY, 0); + + let template = SignatureBuilder::new(SignatureType::AttestationKey) + .set_signature_creation_time(creation_time)?; + template + }; + + let template = template + .set_hash_algo(hash_algo) + // Important for size calculation. + .pre_sign(primary_signer)?; + + // Compute the available space in the hashed area. For this, + // it is important that template.pre_sign has been called. + let available_space = + SubpacketArea::MAX_SIZE - template.hashed_area().serialized_len(); + + // Reserve space for the subpacket header, length and tag. + const SUBPACKET_HEADER_MAX_LEN: usize = 5 + 1; + + // Compute the chunk size for each signature. + let digests_per_sig = + (available_space - SUBPACKET_HEADER_MAX_LEN) / digest_size; + + // Now create the signatures. + let mut sigs = Vec::new(); + for digests in attestations.chunks(digests_per_sig) { + sigs.push( + template.clone() + .set_attested_certifications(digests)? + .sign_hash(primary_signer, hash.clone())?); + } + + if attestations.is_empty() { + // The certificate owner can withdraw attestations by issuing + // an empty attestation key signature. + assert!(sigs.is_empty()); + sigs.push( + template + .set_attested_certifications(Option::<&[u8]>::None)? + .sign_hash(primary_signer, hash.clone())?); + } + + Ok(sigs) } /// A `ComponentAmalgamation` plus a `Policy` and a reference time. @@ -1002,6 +1218,170 @@ assert_send_and_sync!(ValidComponentAmalgamation<'_, C> where C); /// [`ValidComponentAmalgamation`]: struct.ValidComponentAmalgamation.html pub type ValidUserIDAmalgamation<'a> = ValidComponentAmalgamation<'a, UserID>; +impl<'a> ValidUserIDAmalgamation<'a> { + /// Returns the userid's attested third-party certifications. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Allows the certificate owner to attest to third party + /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for + /// details. This can be used to address certificate flooding + /// concerns. + /// + /// This method only returns signatures that are valid under the + /// current policy and are attested by the certificate holder. + /// + /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + pub fn attested_certifications(&self) + -> impl Iterator<Item=&Signature> + Send + Sync + { + let mut hash_algo = None; + let digests: std::collections::HashSet<_> = + self.attestation_key_signatures() + .filter_map(|sig| { + sig.attested_certifications().ok() + .map(|digest_iter| (sig, digest_iter)) + }) + .flat_map(|(sig, digest_iter)| { + hash_algo = Some(sig.hash_algo()); + digest_iter + }) + .collect(); + + self.certifications() + .filter_map(move |sig| { + let mut hash = hash_algo.and_then(|a| a.context().ok())?; + sig.hash_for_confirmation(&mut hash); + let digest = hash.into_digest().ok()?; + if digests.contains(&digest[..]) { + Some(sig) + } else { + None + } + }) + } + + /// Returns set of active attestation key signatures. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Returns the set of signatures with the newest valid signature + /// creation time. Older signatures are not returned. The sum of + /// all digests in these signatures are the set of attested + /// third-party certifications. + /// + /// This interface is useful for pruning old attestation key + /// signatures when filtering a certificate. + /// + /// Note: This is a low-level interface. Consider using + /// [`ValidUserIDAmalgamation::attested_certifications`] to + /// iterate over all attested certifications. + /// + /// [`ValidUserIDAmalgamation::attested_certifications`]: #method.attested_certifications + // The explicit link works around a bug in rustdoc. + pub fn attestation_key_signatures(&'a self) + -> impl Iterator<Item=&'a Signature> + Send + Sync + { + let mut first = None; + + // The newest valid signature will be returned first. + self.attestations() + // First, filter out any invalid (e.g. too new) signatures. + .filter(move |sig| self.cert.policy().signature( + sig, + HashAlgoSecurity::CollisionResistance).is_ok()) + .take_while(move |sig| { + let time_hash = ( + if let Some(t) = sig.signature_creation_time() { + (t, sig.hash_algo()) + } else { + // Something is off. Just stop. + return false; + }, + sig.hash_algo()); + + if let Some(reference) = first { + // Stop looking once we see an older signature or one + // with a different hash algo. + reference == time_hash + } else { + first = Some(time_hash); + true + } + }) + } + + /// Attests to third-party certifications. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Allows the certificate owner to attest to third party + /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for + /// details. This can be used to address certificate flooding + /// concerns. + /// + /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + /// + /// # Examples + /// + /// ``` + /// # use sequoia_openpgp as openpgp; + /// # fn main() -> openpgp::Result<()> { + /// # use openpgp::cert::prelude::*; + /// # use openpgp::packet::signature::SignatureBuilder; + /// # use openpgp::types::*; + /// # let policy = &openpgp::policy::StandardPolicy::new(); + /// let (alice, _) = CertBuilder::new() + /// .add_userid("alice@example.org") + /// .generate()?; + /// let mut alice_signer = + /// alice.primary_key().key().clone().parts_into_secret()? + /// .into_keypair()?; + /// + /// let (bob, _) = CertBuilder::new() + /// .add_userid("bob@example.org") + /// .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@example.org" and + /// // Bob's key. + /// let alice_certifies_bob + /// = bob.userids().next().unwrap().userid().bind( + /// &mut alice_signer, &bob, + /// SignatureBuilder::new(SignatureType::GenericCertification))?; + /// let bob = bob.insert_packets(vec![alice_certifies_bob.clone()])?; + /// + /// // Have Bob attest that certification. + /// let bobs_uid = bob.userids().next().unwrap(); + /// let attestations = + /// bobs_uid.attest_certifications( + /// policy, + /// &mut bob_signer, + /// bobs_uid.certifications())?; + /// let bob = bob.insert_packets(attestations)?; + /// + /// assert_eq!(bob.bad_signatures().count(), 0); + /// assert_eq!(bob.userids().next().unwrap().certifications().next(), + /// Some(&alice_certifies_bob)); + /// # Ok(()) } + /// ``` + pub fn attest_certifications<C, S>(&self, + primary_signer: &mut dyn Signer, + certifications: C) + -> Result<Vec<Signature>> + where C: IntoIterator<Item = S>, + S: Borrow<Signature>, + { + std::ops::Deref::deref(self) + .attest_certifications(self.policy(), + primary_signer, + certifications) + } +} + /// A Valid User Attribute and its associated data. /// /// A specialized version of [`ValidComponentAmalgamation`]. @@ -1010,6 +1390,130 @@ pub type ValidUserIDAmalgamation<'a> = ValidComponentAmalgamation<'a, UserID>; pub type ValidUserAttributeAmalgamation<'a> = ValidComponentAmalgamation<'a, UserAttribute>; +impl<'a> ValidUserAttributeAmalgamation<'a> { + /// Returns the user attributes's attested third-party certifications. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Allows the certificate owner to attest to third party + /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for + /// details. This can be used to address certificate flooding + /// concerns. + /// + /// This method only returns signatures that are valid under the + /// current policy and are attested by the certificate holder. + /// + /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + pub fn attested_certifications(&self) + -> impl Iterator<Item=&Signature> + Send + Sync + { + let mut hash_algo = None; + let digests: std::collections::HashSet<_> = + self.attestation_key_signatures() + .filter_map(|sig| { + sig.attested_certifications().ok() + .map(|digest_iter| (sig, digest_iter)) + }) + .flat_map(|(sig, digest_iter)| { + hash_algo = Some(sig.hash_algo()); + digest_iter + }) + .collect(); + + self.certifications() + .filter_map(move |sig| { + let mut hash = hash_algo.and_then(|a| a.context().ok())?; + sig.hash_for_confirmation(&mut hash); + let digest = hash.into_digest().ok()?; + if digests.contains(&digest[..]) { + Some(sig) + } else { + None + } + }) + } + + /// Returns set of active attestation key signatures. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Returns the set of signatures with the newest valid signature + /// creation time. Older signatures are not returned. The sum of + /// all digests in these signatures are the set of attested + /// third-party certifications. + /// + /// This interface is useful for pruning old attestation key + /// signatures when filtering a certificate. + /// + /// Note: This is a low-level interface. Consider using + /// [`ValidUserAttributeAmalgamation::attested_certifications`] to + /// iterate over all attested certifications. + /// + /// [`ValidUserAttributeAmalgamation::attested_certifications`]: #method.attested_certifications + // The explicit link works around a bug in rustdoc. + pub fn attestation_key_signatures(&'a self) + -> impl Iterator<Item=&'a Signature> + Send + Sync + { + let mut first = None; + + // The newest valid signature will be returned first. + self.attestations() + // First, filter out any invalid (e.g. too new) signatures. + .filter(move |sig| self.cert.policy().signature( + sig, + HashAlgoSecurity::CollisionResistance).is_ok()) + .take_while(move |sig| { + let time_hash = ( + if let Some(t) = sig.signature_creation_time() { + (t, sig.hash_algo()) + } else { + // Something is off. Just stop. + return false; + }, + sig.hash_algo()); + + if let Some(reference) = first { + // Stop looking once we see an older signature or one + // with a different hash algo. + reference == time_hash + } else { + first = Some(time_hash); + true + } + }) + } + + /// Attests to third-party certifications. + /// + /// This feature is [experimental](crate#experimental-features). + /// + /// Allows the certificate owner to attest to third party + /// certifications. See [Section 5.2.3.30 of RFC 4880bis] for + /// details. This can be used to address certificate flooding + /// concerns. + /// + /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30 + /// + /// # Examples + /// + /// See [`ValidUserIDAmalgamation::attest_certifications#examples`]. + /// + /// [`ValidUserIDAmalgamation::attest_certifications#examples`]: type.ValidUserIDAmalgamation.html#examples + // The explicit link works around a bug in rustdoc. + pub fn attest_certifications<C, S>(&self, + primary_signer: &mut dyn Signer, + certifications: C) + -> Result<Vec<Signature>> + where C: IntoIterator<Item = S>, + S: Borrow<Signature>, + { + std::ops::Deref::deref(self) + .attest_certifications(self.policy(), + primary_signer, + certifications) + } +} + // derive(Clone) doesn't work with generic parameters that don't // implement clone. But, we don't need to require that C implements // Clone, because we're not cloning C, just the reference. diff --git a/sq/src/commands/key.rs b/sq/src/commands/key.rs index 41077cd6..d6f646e7 100644 --- a/sq/src/commands/key.rs +++ b/sq/src/commands/key.rs @@ -424,35 +424,12 @@ fn adopt(config: Config, m: &ArgMatches) -> Result<()> { fn attest_certifications(config: Config, m: &ArgMatches) -> Result<()> { - // XXX: This function has to do some steps manually, because - // Sequoia does not expose this functionality because it has not - // been standardized yet. - use sequoia_openpgp::{ - crypto::hash::{Hash, Digest}, - types::HashAlgorithm, - }; - // Attest to all certifications? let all = ! m.is_present("none"); // All is the default. - // Some configuration. - let hash_algo = HashAlgorithm::default(); - let digest_size = hash_algo.context()?.digest_size(); - let reserve_area_space = 256; // For the other subpackets. - let digests_per_sig = ((1usize << 16) - reserve_area_space) / digest_size; - let input = open_or_stdin(m.value_of("key"))?; let key = Cert::from_reader(input)?; - - // First, remove all attestations. - let key = Cert::from_packets(key.into_packets().filter(|p| { - !matches!( - p, - Packet::Signature(s) if s.typ() == |