summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2021-04-20 15:52:25 +0200
committerJustus Winter <justus@sequoia-pgp.org>2021-04-26 13:13:22 +0200
commit0c349869786194214aca9ccb5f66640b28163f79 (patch)
tree5e2895d67d7fe9c58c99c7bf6ae9eb7ff6655809
parent7961a663239567089508c7962a6c77d22b588c3a (diff)
openpgp: Expose support for attested certifications.
- This is a low-level interface. We will provide nicer abstractions in a followup. - See #335.
-rw-r--r--openpgp/NEWS6
-rw-r--r--openpgp/src/cert.rs30
-rw-r--r--openpgp/src/packet/signature.rs47
-rw-r--r--openpgp/src/packet/signature/subpacket.rs136
-rw-r--r--openpgp/src/parse.rs45
-rw-r--r--openpgp/src/policy.rs4
-rw-r--r--openpgp/src/serialize.rs7
-rw-r--r--sq/src/commands/dump.rs11
-rw-r--r--sq/src/commands/key.rs30
9 files changed, 210 insertions, 106 deletions
diff --git a/openpgp/NEWS b/openpgp/NEWS
index 782a5cb9..63b7b4f1 100644
--- a/openpgp/NEWS
+++ b/openpgp/NEWS
@@ -2,6 +2,12 @@
#+TITLE: sequoia-openpgp NEWS – history of user-visible changes
#+STARTUP: content hidestars
+* Changes in 1.2.0
+** New functionality
+ - SignatureBuilder::set_attested_certifications
+ - SubpacketAreas::attested_certifications
+ - SubpacketTag::AttestedCertifications
+ - SubpacketValue::AttestedCertifications
* 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 dad0b220..6339f311 100644
--- a/openpgp/src/cert.rs
+++ b/openpgp/src/cert.rs
@@ -6012,7 +6012,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
fn attested_key_signatures() -> Result<()> {
use crate::{
crypto::hash::Hash,
- packet::signature::{SignatureBuilder, subpacket::*},
+ packet::signature::SignatureBuilder,
types::*,
};
@@ -6052,15 +6052,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
bob.userids().next().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)
- })?
+ .set_attested_certifications(vec![digest])?
.sign_hash(&mut bob_signer, h)?;
let bob = bob.insert_packets(vec![
@@ -6098,7 +6090,6 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
fn attested_key_signatures_dkgpg() -> Result<()> {
const DUMP: bool = false;
use crate::{
- packet::signature::subpacket::*,
crypto::hash::Digest,
};
@@ -6112,23 +6103,16 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
let attestation =
&test.userids().next().unwrap().bundle().attestations[0];
- let digest_size = attestation.hash_algo().context()?.digest_size();
- let digests = if let Some(SubpacketValue::Unknown { body, .. }) =
- attestation.subpacket(SubpacketTag__AttestedCertifications)
- .map(|sp| sp.value())
- {
- body.chunks(digest_size).map(|d| d.to_vec()).collect::<Vec<_>>()
- } else {
- unreachable!("Valid attestation signatures contain one");
- };
-
if DUMP {
- for (i, d) in digests.iter().enumerate() {
+ for (i, d) in attestation.attested_certifications()?.enumerate() {
crate::fmt::hex::Dumper::new(std::io::stderr(), "")
.write(d, format!("expected digest {}", i))?;
}
}
+ let digests: std::collections::HashSet<_> =
+ attestation.attested_certifications()?.collect();
+
for (i, certification) in
test.userids().next().unwrap().certifications().enumerate()
{
@@ -6142,7 +6126,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
.write(&digest, format!("computed digest {}", i))?;
}
- assert!(digests.contains(&digest));
+ assert!(digests.contains(&digest[..]));
}
Ok(())
diff --git a/openpgp/src/packet/signature.rs b/openpgp/src/packet/signature.rs
index c824681a..36b308a2 100644
--- a/openpgp/src/packet/signature.rs
+++ b/openpgp/src/packet/signature.rs
@@ -2214,6 +2214,7 @@ impl crate::packet::Signature {
| SignatureTarget
| PreferredAEADAlgorithms
| IntendedRecipient
+ | AttestedCertifications
| Reserved(_)
=> false,
Issuer
@@ -2812,8 +2813,6 @@ impl Signature {
R: key::KeyRole,
{
use crate::types::SignatureType__AttestedKey;
- use crate::packet::signature::subpacket
- ::SubpacketTag__AttestedCertifications;
if self.typ() != SignatureType__AttestedKey {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
@@ -2821,29 +2820,14 @@ impl Signature {
let mut hash = self.hash_algo().context()?;
- if self.hashed_area()
- .subpackets(SubpacketTag__AttestedCertifications).count() != 1
- || self.unhashed_area()
- .subpackets(SubpacketTag__AttestedCertifications).count() != 0
+ if self.attested_certifications()?
+ .any(|d| d.len() != hash.digest_size())
{
return Err(Error::BadSignature(
- "Wrong number of attested certification subpackets".into())
+ "Wrong number of bytes in certification subpacket".into())
.into());
}
- if let SubpacketValue::Unknown { body, .. } =
- self.subpacket(SubpacketTag__AttestedCertifications).unwrap()
- .value()
- {
- if body.len() % hash.digest_size() != 0 {
- return Err(Error::BadSignature(
- "Wrong number of bytes in certification subpacket".into())
- .into());
- }
- } else {
- unreachable!("Selected attested certifications, got wrong value");
- }
-
self.hash_userid_binding(&mut hash, pk, userid);
self.verify_digest(signer, &hash.into_digest()?[..])
}
@@ -2954,8 +2938,6 @@ impl Signature {
R: key::KeyRole,
{
use crate::types::SignatureType__AttestedKey;
- use crate::packet::signature::subpacket
- ::SubpacketTag__AttestedCertifications;
if self.typ() != SignatureType__AttestedKey {
return Err(Error::UnsupportedSignatureType(self.typ()).into());
@@ -2963,29 +2945,14 @@ impl Signature {
let mut hash = self.hash_algo().context()?;
- if self.hashed_area()
- .subpackets(SubpacketTag__AttestedCertifications).count() != 1
- || self.unhashed_area()
- .subpackets(SubpacketTag__AttestedCertifications).count() != 0
+ if self.attested_certifications()?
+ .any(|d| d.len() != hash.digest_size())
{
return Err(Error::BadSignature(
- "Wrong number of attested certification subpackets".into())
+ "Wrong number of bytes in certification subpacket".into())
.into());
}
- if let SubpacketValue::Unknown { body, .. } =
- self.subpacket(SubpacketTag__AttestedCertifications).unwrap()
- .value()
- {
- if body.len() % hash.digest_size() != 0 {
- return Err(Error::BadSignature(
- "Wrong number of bytes in certification subpacket".into())
- .into());
- }
- } else {
- unreachable!("Selected attested certifications, got wrong value");
- }
-
self.hash_user_attribute_binding(&mut hash, pk, ua);
self.verify_digest(signer, &hash.into_digest()?[..])
}
diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs
index bd522bbe..2f9570ef 100644
--- a/openpgp/src/packet/signature/subpacket.rs
+++ b/openpgp/src/packet/signature/subpacket.rs
@@ -317,6 +317,17 @@ pub enum SubpacketTag {
///
/// [Section 5.2.3.29 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09.html#section-5.2.3.29
IntendedRecipient,
+ /// The Attested Certifications subpacket (proposed).
+ ///
+ /// Allows the certificate holder to attest to third party
+ /// certifications, allowing them to be distributed with the
+ /// certificate. This can be used to address certificate flooding
+ /// concerns.
+ ///
+ /// See [Section 5.2.3.30 of RFC 4880bis] for details.
+ ///
+ /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30
+ AttestedCertifications,
/// Reserved subpacket tag.
Reserved(u8),
/// Private subpacket tag.
@@ -326,11 +337,6 @@ pub enum SubpacketTag {
}
assert_send_and_sync!(SubpacketTag);
-/// The proposed Attested Certifications subpacket.
-#[allow(non_upper_case_globals)]
-pub(crate) const SubpacketTag__AttestedCertifications: SubpacketTag =
- SubpacketTag::Unknown(37);
-
impl fmt::Display for SubpacketTag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
@@ -367,6 +373,7 @@ impl From<u8> for SubpacketTag {
33 => SubpacketTag::IssuerFingerprint,
34 => SubpacketTag::PreferredAEADAlgorithms,
35 => SubpacketTag::IntendedRecipient,
+ 37 => SubpacketTag::AttestedCertifications,
0| 1| 8| 13| 14| 15| 17| 18| 19 => SubpacketTag::Reserved(u),
100..=110 => SubpacketTag::Private(u),
_ => SubpacketTag::Unknown(u),
@@ -404,6 +411,7 @@ impl From<SubpacketTag> for u8 {
SubpacketTag::IssuerFingerprint => 33,
SubpacketTag::PreferredAEADAlgorithms => 34,
SubpacketTag::IntendedRecipient => 35,
+ SubpacketTag::AttestedCertifications => 37,
SubpacketTag::Reserved(u) => u,
SubpacketTag::Private(u) => u,
SubpacketTag::Unknown(u) => u,
@@ -1575,6 +1583,17 @@ pub enum SubpacketValue {
///
/// [Section 5.2.3.29 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09.html#section-5.2.3.29
IntendedRecipient(Fingerprint),
+ /// The Attested Certifications subpacket (proposed).
+ ///
+ /// Allows the certificate holder to attest to third party
+ /// certifications, allowing them to be distributed with the
+ /// certificate. This can be used to address certificate flooding
+ /// concerns.
+ ///
+ /// See [Section 5.2.3.30 of RFC 4880bis] for details.
+ ///
+ /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30
+ AttestedCertifications(Vec<Box<[u8]>>),
}
assert_send_and_sync!(SubpacketValue);
@@ -1669,6 +1688,7 @@ impl SubpacketValue {
PreferredAEADAlgorithms(_) =>
SubpacketTag::PreferredAEADAlgorithms,
IntendedRecipient(_) => SubpacketTag::IntendedRecipient,
+ AttestedCertifications(_) => SubpacketTag::AttestedCertifications,
Unknown { tag, .. } => *tag,
}
}
@@ -3652,6 +3672,59 @@ impl SubpacketAreas {
}
})
}
+
+ /// Returns the digests of attested certifications.
+ ///
+ /// This feature is [experimental](crate#experimental-features).
+ ///
+ /// Allows the certificate holder to attest to third party
+ /// certifications, allowing them to be distributed with the
+ /// certificate. This can be used to address certificate flooding
+ /// concerns.
+ ///
+ /// Note: The maximum size of the hashed signature subpacket area
+ /// constrains the number of attestations that can be stored in a
+ /// signature. If the certificate holder attested to more
+ /// certifications, the digests are split across multiple attested
+ /// key signatures with the same creation time.
+ ///
+ /// The standard strongly suggests that the digests should be
+ /// sorted. However, this function returns the digests in the
+ /// order they are stored in the subpacket, which may not be
+ /// sorted.
+ ///
+ /// To address both issues, collect all digests from all attested
+ /// key signatures with the most recent creation time into a data
+ /// structure that allows efficient lookups, such as [`HashSet`]
+ /// or [`BTreeSet`].
+ ///
+ /// See [Section 5.2.3.30 of RFC 4880bis] for details.
+ ///
+ /// [`HashSet`]: std::collections::HashSet
+ /// [`BTreeSet`]: std::collections::BTreeSet
+ /// [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)
+ -> Result<impl Iterator<Item=&[u8]> + Send + Sync>
+ {
+ if self.hashed_area()
+ .subpackets(SubpacketTag::AttestedCertifications).count() > 1
+ || self.unhashed_area()
+ .subpackets(SubpacketTag::AttestedCertifications).count() != 0
+ {
+ return Err(Error::BadSignature(
+ "Wrong number of attested certification subpackets".into())
+ .into());
+ }
+
+ Ok(self.subpackets(SubpacketTag::AttestedCertifications)
+ .flat_map(|sb| {
+ match sb.value() {
+ SubpacketValue::AttestedCertifications(digests) =>
+ digests.iter().map(|d| d.as_ref()),
+ _ => unreachable!(),
+ }
+ }))
+ }
}
impl TryFrom<Signature> for Signature4 {
@@ -6892,6 +6965,59 @@ impl signature::SignatureBuilder {
Ok(self)
}
+
+ /// Adds an attested certifications subpacket.
+ ///
+ /// This feature is [experimental](crate#experimental-features).
+ ///
+ /// Allows the certificate holder to attest to third party
+ /// certifications, allowing them to be distributed with the
+ /// certificate. This can be used to address certificate flooding
+ /// concerns.
+ ///
+ /// Sorts the digests and adds an [Attested Certification
+ /// subpacket] to the hashed subpacket area. The digests must be
+ /// calculated using the same hash algorithm that is used in the
+ /// resulting signature. To attest a signature, hash it with
+ /// [`super::Signature::hash_for_confirmation`].
+ ///
+ /// Note: The maximum size of the hashed signature subpacket area
+ /// constrains the number of attestations that can be stored in a
+ /// signature. If you need to attest to more certifications,
+ /// split the digests into chunks and create multiple attested key
+ /// signatures with the same creation time.
+ ///
+ /// See [Section 5.2.3.30 of RFC 4880bis] for details.
+ ///
+ /// [Section 5.2.3.30 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30
+ /// [Attested Certification subpacket]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-10.html#section-5.2.3.30
+ pub fn set_attested_certifications<A, C>(mut self, certifications: C)
+ -> Result<Self>
+ where C: IntoIterator<Item = A>,
+ A: AsRef<[u8]>,
+ {
+ let mut digests: Vec<_> = certifications.into_iter()
+ .map(|d| d.as_ref().to_vec().into_boxed_slice())
+ .collect();
+
+ if let Some(first) = digests.get(0) {
+ if digests.iter().any(|d| d.len() != first.len()) {
+ return Err(Error::InvalidOperation(
+ "Inconsistent digest algorithm used".into()).into());
+ }
+ }
+
+ // Hashes SHOULD be sorted. This optimizes lookups for the
+ // consumer and provides a canonical form.
+ digests.sort_unstable();
+
+ self.hashed_area_mut().replace(
+ Subpacket::new(
+ SubpacketValue::AttestedCertifications(digests),
+ true)?)?;
+
+ Ok(self)
+ }
}
#[test]
diff --git a/openpgp/src/parse.rs b/openpgp/src/parse.rs
index cdf9fb19..a410632b 100644
--- a/openpgp/src/parse.rs
+++ b/openpgp/src/parse.rs
@@ -1331,15 +1331,18 @@ impl Signature4 {
let typ = php_try!(php.parse_u8("type"));
let pk_algo: PublicKeyAlgorithm = php_try!(php.parse_u8("pk_algo")).into();
- let hash_algo = php_try!(php.parse_u8("hash_algo"));
+ let hash_algo: HashAlgorithm =
+ php_try!(php.parse_u8("hash_algo")).into();
let hashed_area_len = php_try!(php.parse_be_u16("hashed_area_len"));
let hashed_area
= php_try!(SubpacketArea::parse(&mut php,
- hashed_area_len as usize));
+ hashed_area_len as usize,
+ hash_algo));
let unhashed_area_len = php_try!(php.parse_be_u16("unhashed_area_len"));
let unhashed_area
= php_try!(SubpacketArea::parse(&mut php,
- unhashed_area_len as usize));
+ unhashed_area_len as usize,
+ hash_algo));
let digest_prefix1 = php_try!(php.parse_u8("digest_prefix1"));
let digest_prefix2 = php_try!(php.parse_u8("digest_prefix2"));
if ! pk_algo.for_signing() {
@@ -1348,7 +1351,6 @@ impl Signature4 {
let mpis = php_try!(
crypto::mpi::Signature::_parse(pk_algo, &mut php));
- let hash_algo = hash_algo.into();
let typ = typ.into();
let need_hash = HashingMode::for_signature(hash_algo, typ);
let mut pp = php.ok(Packet::Signature(Signature4::new(
@@ -1511,12 +1513,15 @@ fn signature_parser_test () {
impl SubpacketArea {
// Parses a subpacket area.
- fn parse<'a, T: 'a + BufferedReader<Cookie>>(php: &mut PacketHeaderParser<T>, mut limit: usize)
- -> Result<Self>
+ fn parse<'a, T>(php: &mut PacketHeaderParser<T>,
+ mut limit: usize,
+ hash_algo: HashAlgorithm)
+ -> Result<Self>
+ where T: 'a + BufferedReader<Cookie>,
{
let mut packets = Vec::new();
while limit > 0 {
- let p = Subpacket::parse(php, limit)?;
+ let p = Subpacket::parse(php, limit, hash_algo)?;
assert!(limit >= p.length.len() + p.length.serialized_len());
limit -= p.length.len() + p.length.serialized_len();
packets.push(p);
@@ -1528,8 +1533,12 @@ impl SubpacketArea {
impl Subpacket {
// Parses a raw subpacket.
- fn parse<'a, T: 'a + BufferedReader<Cookie>>(php: &mut PacketHeaderParser<T>, limit: usize)
- -> Result<Self> {
+ fn parse<'a, T>(php: &mut PacketHeaderParser<T>,
+ limit: usize,
+ hash_algo: HashAlgorithm)
+ -> Result<Self>
+ where T: 'a + BufferedReader<Cookie>,
+ {
let length = SubpacketLength::parse(&mut php.reader)?;
php.field("subpacket length", length.serialized_len());
let len = length.len() as usize;
@@ -1745,6 +1754,24 @@ impl Subpacket {
_ => Fingerprint::Invalid(bytes.into()),
})
},
+ SubpacketTag::AttestedCertifications => {
+ // If we don't know the hash algorithm, put all digest
+ // into one bucket. That way, at least it will
+ // roundtrip. It will never verify, because we don't
+ // know the hash.
+ let digest_size =
+ hash_algo.context().map(|c| c.digest_size())
+ .unwrap_or(len);
+
+ if len % digest_size != 0 {
+ return Err(Error::BadSignature(
+ "Wrong number of bytes in certification subpacket".into())
+ .into());
+ }
+ let bytes = php.parse_bytes("attested crts", len)?;
+ SubpacketValue::AttestedCertifications(
+ bytes.chunks(digest_size).map(Into::into).collect())
+ },
SubpacketTag::Reserved(_)
| SubpacketTag::PlaceholderForBackwardCompatibility
| SubpacketTag::Private(_)
diff --git a/openpgp/src/policy.rs b/openpgp/src/policy.rs
index 75e5c5b8..70d097b2 100644
--- a/openpgp/src/policy.rs
+++ b/openpgp/src/policy.rs
@@ -635,7 +635,7 @@ a_cutoff_list!(SecondPreImageResistantHashCutoffList, HashAlgorithm, 12,
ACCEPT, // 11. SHA224
]);
-a_cutoff_list!(SubpacketTagCutoffList, SubpacketTag, 36,
+a_cutoff_list!(SubpacketTagCutoffList, SubpacketTag, 38,
[
REJECT, // 0. Reserved.
REJECT, // 1. Reserved.
@@ -676,6 +676,8 @@ a_cutoff_list!(SubpacketTagCutoffList, SubpacketTag, 36,
ACCEPT, // 33. IssuerFingerprint.
ACCEPT, // 34. PreferredAEADAlgorithms.
ACCEPT, // 35. IntendedRecipient.
+ REJECT, // 36. Reserved.
+ ACCEPT, // 37. AttestedCertifications.
]);
a_cutoff_list!(AsymmetricAlgorithmCutoffList, AsymmetricAlgorithm, 18,
diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs
index 6890533f..d13d461c 100644
--- a/openpgp/src/serialize.rs
+++ b/openpgp/src/serialize.rs
@@ -1483,6 +1483,11 @@ impl Marshal for SubpacketValue {
_ => return Err(Error::InvalidArgument(
"Unknown kind of fingerprint".into()).into()),
}
+ AttestedCertifications(digests) => {
+ for digest in digests {
+ o.write_all(digest)?;
+ }
+ },
Unknown { body, .. } =>
o.write_all(body)?,
}
@@ -1530,6 +1535,8 @@ impl MarshalInto for SubpacketValue {
// Educated guess for unknown versions.
Fingerprint::Invalid(_) => 1 + fp.as_bytes().len(),
},
+ AttestedCertifications(digests) =>
+ digests.iter().map(|d| d.len()).sum(),
Unknown { body, .. } => body.len(),
}
}
diff --git a/sq/src/commands/dump.rs b/sq/src/commands/dump.rs
index 782344aa..081760a6 100644
--- a/sq/src/commands/dump.rs
+++ b/sq/src/commands/dump.rs
@@ -868,6 +868,17 @@ impl PacketDumper {
.collect::<Vec<String>>().join(", "))?,
IntendedRecipient(ref fp) =>
write!(output, "{} Intended Recipient: {}", i, fp)?,
+ AttestedCertifications(digests) => {
+ write!(output, "{} Attested Certifications:", i)?;
+ if digests.is_empty() {
+ writeln!(output, " None")?;
+ } else {
+ writeln!(output)?;
+ for d in digests {
+ writeln!(output, "{} {}", i, hex::encode(d))?;
+ }
+ }
+ },
// SubpacketValue is non-exhaustive.
u => writeln!(output, "{} Unknown variant: {:?}", i, u)?,
diff --git a/sq/src/commands/key.rs b/sq/src/commands/key.rs
index 5dd45809..6073e886 100644
--- a/sq/src/commands/key.rs
+++ b/sq/src/commands/key.rs
@@ -429,15 +429,11 @@ fn attest_certifications(config: Config, m: &ArgMatches)
// been standardized yet.
use sequoia_openpgp::{
crypto::hash::{Hash, Digest},
- packet::signature::subpacket::*,
types::HashAlgorithm,
};
#[allow(non_upper_case_globals)]
const SignatureType__AttestedKey: SignatureType =
SignatureType::Unknown(0x16);
- #[allow(non_upper_case_globals)]
- const SubpacketTag__AttestedCertifications: SubpacketTag =
- SubpacketTag::Unknown(37);
// Attest to all certifications?
let all = ! m.is_present("none"); // All is the default.
@@ -495,21 +491,10 @@ fn attest_certifications(config: Config, m: &ArgMatches)
uid.hash(&mut hash);
for digests in attestations.chunks(digests_per_sig) {
- let mut body = Vec::with_capacity(digest_size * digests.len());
- digests.iter().for_each(|d| body.extend(d));
-
attestation_signatures.push(
SignatureBuilder::new(SignatureType__AttestedKey)
.set_signature_creation_time(t)?
- .modify_hashed_area(|mut a| {
- a.add(Subpacket::new(
- SubpacketValue::Unknown {
- tag: SubpacketTag__AttestedCertifications,
- body,
- },
- true)?)?;
- Ok(a)
- })?
+ .set_attested_certifications(digests)?
.sign_hash(&mut pk_signer, hash.clone())?);
}
}
@@ -538,21 +523,10 @@ fn attest_certifications(config: Config, m: &ArgMatches)
ua.hash(&mut hash);
for digests in attestations.chunks(digests_per_sig) {
- let mut body = Vec::with_capacity(digest_size * digests.len());
- digests.iter().for_each(|d| body.extend(d));
-
attestation_signatures.push(
SignatureBuilder::new(SignatureType__AttestedKey)
.set_signature_creation_time(t)?
- .modify_hashed_area(|mut a| {
- a.add(Subpacket::new(
- SubpacketValue::Unknown {
- tag: SubpacketTag__AttestedCertifications,
- body,
- },
- true)?)?;
- Ok(a)
- })?
+ .set_attested_certifications(digests)?
.sign_hash(&mut pk_signer, hash.clone())?);
}
}