summaryrefslogtreecommitdiffstats
path: root/openpgp/src/serialize/cert.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/serialize/cert.rs')
-rw-r--r--openpgp/src/serialize/cert.rs845
1 files changed, 845 insertions, 0 deletions
diff --git a/openpgp/src/serialize/cert.rs b/openpgp/src/serialize/cert.rs
new file mode 100644
index 00000000..4503441d
--- /dev/null
+++ b/openpgp/src/serialize/cert.rs
@@ -0,0 +1,845 @@
+use crate::Result;
+use crate::Cert;
+use crate::packet::{key, Signature, Tag};
+use crate::serialize::{
+ PacketRef, Serialize, SerializeInto,
+ generic_serialize_into, generic_export_into,
+};
+
+impl Serialize for Cert {
+ fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
+ self.serialize_common(o, false)
+ }
+
+ fn export(&self, o: &mut dyn std::io::Write) -> Result<()> {
+ self.serialize_common(o, true)
+ }
+}
+
+impl Cert {
+ /// Serializes or exports the Cert.
+ ///
+ /// If `export` is true, then non-exportable signatures are not
+ /// written, and components without any exportable binding
+ /// signature or revocation are not exported.
+ fn serialize_common(&self, o: &mut dyn std::io::Write, export: bool)
+ -> Result<()>
+ {
+ PacketRef::PublicKey(self.primary()).serialize(o)?;
+
+ // Writes a signature if it is exportable or `! export`.
+ let serialize_sig =
+ |o: &mut dyn std::io::Write, sig: &Signature| -> Result<()>
+ {
+ if export {
+ if sig.exportable_certification().unwrap_or(true) {
+ PacketRef::Signature(sig).export(o)?;
+ }
+ } else {
+ PacketRef::Signature(sig).serialize(o)?;
+ }
+ Ok(())
+ };
+
+ for s in self.direct_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in self.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in self.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in self.certifications() {
+ serialize_sig(o, s)?;
+ }
+
+ for u in self.userids() {
+ if export && ! u.self_signatures().iter().chain(u.self_revocations()).any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::UserID(u.userid()).serialize(o)?;
+ for s in u.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for u in self.user_attributes() {
+ if export && ! u.self_signatures().iter().chain(u.self_revocations()).any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::UserAttribute(u.user_attribute()).serialize(o)?;
+ for s in u.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for k in self.subkeys() {
+ if export && ! k.self_signatures().iter().chain(k.self_revocations()).any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::PublicSubkey(k.key()).serialize(o)?;
+ for s in k.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in k.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in k.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in k.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for u in self.unknowns() {
+ if export && ! u.certifications().iter().any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::Unknown(u.unknown()).serialize(o)?;
+
+ for s in u.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for s in self.bad_signatures() {
+ serialize_sig(o, s)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl SerializeInto for Cert {
+ fn serialized_len(&self) -> usize {
+ let mut l = 0;
+ l += PacketRef::PublicKey(self.primary()).serialized_len();
+
+ for s in self.direct_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in self.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in self.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in self.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+
+ for u in self.userids() {
+ l += PacketRef::UserID(u.userid()).serialized_len();
+
+ for s in u.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for u in self.user_attributes() {
+ l += PacketRef::UserAttribute(u.user_attribute()).serialized_len();
+
+ for s in u.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for k in self.subkeys() {
+ l += PacketRef::PublicSubkey(k.key()).serialized_len();
+
+ for s in k.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in k.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in k.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in k.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for u in self.unknowns() {
+ l += PacketRef::Unknown(u.unknown()).serialized_len();
+
+ for s in u.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for s in self.bad_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+
+ l
+ }
+
+ fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
+ generic_serialize_into(self, buf)
+ }
+
+ fn export_into(&self, buf: &mut [u8]) -> Result<usize> {
+ generic_export_into(self, buf)
+ }
+}
+
+impl Cert {
+ /// Derive a [`TSK`] object from this key.
+ ///
+ /// This object writes out secret keys during serialization.
+ ///
+ /// [`TSK`]: serialize/struct.TSK.html
+ pub fn as_tsk<'a>(&'a self) -> TSK<'a> {
+ TSK::new(self)
+ }
+}
+
+/// A reference to a Cert that allows serialization of secret keys.
+///
+/// To avoid accidental leakage `Cert::serialize()` skips secret keys.
+/// To serialize `Cert`s with secret keys, use [`Cert::as_tsk()`] to
+/// create a `TSK`, which is a shim on top of the `Cert`, and serialize
+/// this.
+///
+/// [`Cert::as_tsk()`]: ../struct.Cert.html#method.as_tsk
+///
+/// # Example
+/// ```
+/// # use sequoia_openpgp::{*, cert::*, parse::Parse, serialize::Serialize};
+/// # f().unwrap();
+/// # fn f() -> Result<()> {
+/// let (cert, _) = CertBuilder::new().generate()?;
+/// assert!(cert.is_tsk());
+///
+/// let mut buf = Vec::new();
+/// cert.as_tsk().serialize(&mut buf)?;
+///
+/// let cert_ = Cert::from_bytes(&buf)?;
+/// assert!(cert_.is_tsk());
+/// assert_eq!(cert, cert_);
+/// # Ok(()) }
+pub struct TSK<'a> {
+ cert: &'a Cert,
+ filter: Option<Box<dyn Fn(&'a key::UnspecifiedSecret) -> bool + 'a>>,
+}
+
+impl<'a> TSK<'a> {
+ /// Creates a new view for the given `Cert`.
+ fn new(cert: &'a Cert) -> Self {
+ Self {
+ cert: cert,
+ filter: None,
+ }
+ }
+
+ /// Filters which secret keys to export using the given predicate.
+ ///
+ /// Note that the given filter replaces any existing filter.
+ ///
+ /// # Example
+ /// ```
+ /// # use sequoia_openpgp::{*, cert::*, parse::Parse, serialize::Serialize};
+ /// # f().unwrap();
+ /// # fn f() -> Result<()> {
+ /// let (cert, _) = CertBuilder::new().add_signing_subkey().generate()?;
+ /// assert_eq!(cert.keys_valid().secret().count(), 2);
+ ///
+ /// // Only write out the primary key's secret.
+ /// let mut buf = Vec::new();
+ /// cert.as_tsk()
+ /// .set_filter(
+ /// |k| k == cert.primary()
+ /// .mark_parts_secret_ref().unwrap()
+ /// .mark_role_unspecified_ref())
+ /// .serialize(&mut buf)?;
+ ///
+ /// let cert_ = Cert::from_bytes(&buf)?;
+ /// assert_eq!(cert_.keys_valid().secret().count(), 1);
+ /// assert!(cert_.primary().secret().is_some());
+ /// # Ok(()) }
+ pub fn set_filter<P>(mut self, predicate: P) -> Self
+ where P: 'a + Fn(&'a key::UnspecifiedSecret) -> bool
+ {
+ self.filter = Some(Box::new(predicate));
+ self
+ }
+
+ /// Serializes or exports the Cert.
+ ///
+ /// If `export` is true, then non-exportable signatures are not
+ /// written, and components without any exportable binding
+ /// signature or revocation are not exported.
+ fn serialize_common(&self, o: &mut dyn std::io::Write, export: bool)
+ -> Result<()>
+ {
+ // Writes a signature if it is exportable or `! export`.
+ let serialize_sig =
+ |o: &mut dyn std::io::Write, sig: &Signature| -> Result<()>
+ {
+ if export {
+ if sig.exportable_certification().unwrap_or(true) {
+ PacketRef::Signature(sig).export(o)?;
+ }
+ } else {
+ PacketRef::Signature(sig).serialize(o)?;
+ }
+ Ok(())
+ };
+
+ // Serializes public or secret key depending on the filter.
+ let serialize_key =
+ |o: &mut dyn std::io::Write, key: &'a key::UnspecifiedSecret,
+ tag_public, tag_secret|
+ {
+ let tag = if key.secret().is_some()
+ && self.filter.as_ref().map(|f| f(key)).unwrap_or(true) {
+ tag_secret
+ } else {
+ tag_public
+ };
+
+ match tag {
+ Tag::PublicKey =>
+ PacketRef::PublicKey(key.into()).serialize(o),
+ Tag::PublicSubkey =>
+ PacketRef::PublicSubkey(key.into()).serialize(o),
+ Tag::SecretKey =>
+ PacketRef::SecretKey(key.into()).serialize(o),
+ Tag::SecretSubkey =>
+ PacketRef::SecretSubkey(key.into()).serialize(o),
+ _ => unreachable!(),
+ }
+ };
+ serialize_key(o, self.cert.primary().into(),
+ Tag::PublicKey, Tag::SecretKey)?;
+
+ for s in self.cert.direct_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in self.cert.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in self.cert.certifications() {
+ serialize_sig(o, s)?;
+ }
+ for s in self.cert.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+
+ for u in self.cert.userids() {
+ if export && ! u.self_signatures().iter().chain(u.self_revocations()).any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::UserID(u.userid()).serialize(o)?;
+ for s in u.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for u in self.cert.user_attributes() {
+ if export && ! u.self_signatures().iter().chain(u.self_revocations()).any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::UserAttribute(u.user_attribute()).serialize(o)?;
+ for s in u.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for k in self.cert.subkeys() {
+ if export && ! k.self_signatures().iter().chain(k.self_revocations()).any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ serialize_key(o, k.key().into(),
+ Tag::PublicSubkey, Tag::SecretSubkey)?;
+ for s in k.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in k.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in k.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in k.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for u in self.cert.unknowns() {
+ if export && ! u.certifications().iter().any(
+ |s| s.exportable_certification().unwrap_or(true))
+ {
+ // No exportable selfsig on this component, skip it.
+ continue;
+ }
+
+ PacketRef::Unknown(&u.unknown()).serialize(o)?;
+
+ for s in u.self_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.self_signatures() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.other_revocations() {
+ serialize_sig(o, s)?;
+ }
+ for s in u.certifications() {
+ serialize_sig(o, s)?;
+ }
+ }
+
+ for s in self.cert.bad_signatures() {
+ serialize_sig(o, s)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<'a> Serialize for TSK<'a> {
+ fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> {
+ self.serialize_common(o, false)
+ }
+
+ fn export(&self, o: &mut dyn std::io::Write) -> Result<()> {
+ self.serialize_common(o, true)
+ }
+}
+
+impl<'a> SerializeInto for TSK<'a> {
+ fn serialized_len(&self) -> usize {
+ let mut l = 0;
+
+ // Serializes public or secret key depending on the filter.
+ let serialized_len_key
+ = |key: &'a key::UnspecifiedSecret, tag_public, tag_secret|
+ {
+ let tag = if key.secret().is_some()
+ && self.filter.as_ref().map(|f| f(key)).unwrap_or(true) {
+ tag_secret
+ } else {
+ tag_public
+ };
+
+ let packet = match tag {
+ Tag::PublicKey => PacketRef::PublicKey(key.into()),
+ Tag::PublicSubkey => PacketRef::PublicSubkey(key.into()),
+ Tag::SecretKey => PacketRef::SecretKey(key.into()),
+ Tag::SecretSubkey => PacketRef::SecretSubkey(key.into()),
+ _ => unreachable!(),
+ };
+
+ packet.serialized_len()
+ };
+ l += serialized_len_key(self.cert.primary().into(),
+ Tag::PublicKey, Tag::SecretKey);
+
+ for s in self.cert.direct_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in self.cert.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in self.cert.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in self.cert.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+
+ for u in self.cert.userids() {
+ l += PacketRef::UserID(u.userid()).serialized_len();
+
+ for s in u.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for u in self.cert.user_attributes() {
+ l += PacketRef::UserAttribute(u.user_attribute()).serialized_len();
+
+ for s in u.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for k in self.cert.subkeys() {
+ l += serialized_len_key(k.key().into(),
+ Tag::PublicSubkey, Tag::SecretSubkey);
+
+ for s in k.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in k.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in k.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in k.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for u in self.cert.unknowns() {
+ l += PacketRef::Unknown(u.unknown()).serialized_len();
+
+ for s in u.self_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.self_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.other_revocations() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ for s in u.certifications() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+ }
+
+ for s in self.cert.bad_signatures() {
+ l += PacketRef::Signature(s).serialized_len();
+ }
+
+ l
+ }
+
+ fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
+ generic_serialize_into(self, buf)
+ }
+
+ fn export_into(&self, buf: &mut [u8]) -> Result<usize> {
+ generic_export_into(self, buf)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::vec_truncate;
+ use crate::parse::Parse;
+ use crate::serialize::Serialize;
+ use crate::packet::key;
+
+ /// Demonstrates that public keys and all components are
+ /// serialized.
+ #[test]
+ fn roundtrip_cert() {
+ for test in crate::tests::CERTS {
+ let cert = match Cert::from_bytes(test.bytes) {
+ Ok(t) => t,
+ Err(_) => continue,
+ };
+ assert!(! cert.is_tsk());
+ let buf = cert.as_tsk().to_vec().unwrap();
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+
+ assert_eq!(cert, cert_, "roundtripping {}.pgp failed", test);
+ }
+ }
+
+ /// Demonstrates that secret keys and all components are
+ /// serialized.
+ #[test]
+ fn roundtrip_tsk() {
+ for test in crate::tests::TSKS {
+ let cert = Cert::from_bytes(test.bytes).unwrap();
+ assert!(cert.is_tsk());
+
+ let mut buf = Vec::new();
+ cert.as_tsk().serialize(&mut buf).unwrap();
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+
+ assert_eq!(cert, cert_, "roundtripping {}-private.pgp failed", test);
+
+ // This time, use a trivial filter.
+ let mut buf = Vec::new();
+ cert.as_tsk().set_filter(|_| true).serialize(&mut buf).unwrap();
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+
+ assert_eq!(cert, cert_, "roundtripping {}-private.pgp failed", test);
+ }
+ }
+
+ /// Demonstrates that TSK::serialize() with the right filter
+ /// reduces to Cert::serialize().
+ #[test]
+ fn reduce_to_cert_serialize() {
+ for test in crate::tests::TSKS {
+ let cert = Cert::from_bytes(test.bytes).unwrap();
+ assert!(cert.is_tsk());
+
+ // First, use Cert::serialize().
+ let mut buf_cert = Vec::new();
+ cert.serialize(&mut buf_cert).unwrap();
+
+ // When serializing using TSK::serialize, filter out all
+ // secret keys.
+ let mut buf_tsk = Vec::new();
+ cert.as_tsk().set_filter(|_| false).serialize(&mut buf_tsk).unwrap();
+
+ // Check for equality.
+ let cert_ = Cert::from_bytes(&buf_cert).unwrap();
+ let tsk_ = Cert::from_bytes(&buf_tsk).unwrap();
+ assert_eq!(cert_, tsk_,
+ "reducing failed on {}-private.pgp: not Cert::eq",
+ test);
+
+ // Check for identinty.
+ assert_eq!(buf_cert, buf_tsk,
+ "reducing failed on {}-private.pgp: serialized identity",
+ test);
+ }
+ }
+
+ #[test]
+ fn export() {
+ use crate::Packet;
+ use crate::cert::CertBuilder;
+ use crate::types::{Curve, KeyFlags, SignatureType};
+ use crate::packet::{
+ signature, UserID, user_attribute::{UserAttribute, Subpacket},
+ key::Key4,
+ };
+
+ let (cert, _) = CertBuilder::new().generate().unwrap();
+ let mut keypair = cert.primary().clone().mark_parts_secret()
+ .unwrap().into_keypair().unwrap();
+
+ let key: key::SecretSubkey =
+ Key4::generate_ecc(false, Curve::Cv25519).unwrap().into();
+ let key_binding = key.mark_parts_public_ref().bind(
+ &mut keypair, &cert,
+ signature::Builder::new(SignatureType::SubkeyBinding)
+ .set_key_flags(
+ &KeyFlags::default().set_encrypt_for_transport(true))
+ .unwrap()
+ .set_exportable_certification(false).unwrap(),
+ None).unwrap();
+
+ let uid = UserID::from("foo");
+ let uid_binding = uid.bind(
+ &mut keypair, &cert,
+ signature::Builder::from(
+ cert.primary_key_signature(None).unwrap().clone())
+ .set_type(SignatureType::PositiveCertificate)
+ .set_exportable_certification(false).unwrap(),
+ None).unwrap();
+
+ let ua = UserAttribute::new(&[
+ Subpacket::Unknown(2, b"foo".to_vec().into_boxed_slice()),
+ ]).unwrap();
+ let ua_binding = ua.bind(
+ &mut keypair, &cert,
+ signature::Builder::from(
+ cert.primary_key_signature(None).unwrap().clone())
+ .set_type(SignatureType::PositiveCertificate)
+ .set_exportable_certification(false).unwrap(),
+ None).unwrap();
+
+ let cert = cert.merge_packets(vec![
+ Packet::SecretSubkey(key), key_binding.into(),
+ uid.into(), uid_binding.into(),
+ ua.into(), ua_binding.into(),
+ ]).unwrap();
+
+ assert_eq!(cert.subkeys().count(), 1);
+ assert!(cert.subkeys().nth(0).unwrap().binding_signature(None).is_some());
+ assert_eq!(cert.userids().count(), 1);
+ assert!(cert.userids().nth(0).unwrap().binding_signature(None).is_some());
+ assert_eq!(cert.user_attributes().count(), 1);
+ assert!(cert.user_attributes().nth(0).unwrap().binding_signature(None)
+ .is_some());
+
+ // The binding signature is not exportable, so when we export
+ // and re-parse, we expect the userid to be gone.
+ let mut buf = Vec::new();
+ cert.export(&mut buf).unwrap();
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+ assert_eq!(cert_.subkeys().count(), 0);
+ assert_eq!(cert_.userids().count(), 0);
+ assert_eq!(cert_.user_attributes().count(), 0);
+
+ let mut buf = vec![0; cert.serialized_len()];
+ let l = cert.export_into(&mut buf).unwrap();
+ vec_truncate(&mut buf, l);
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+ assert_eq!(cert_.subkeys().count(), 0);
+ assert_eq!(cert_.userids().count(), 0);
+ assert_eq!(cert_.user_attributes().count(), 0);
+
+ let cert_ = Cert::from_bytes(&cert.export_to_vec().unwrap()).unwrap();
+ assert_eq!(cert_.subkeys().count(), 0);
+ assert_eq!(cert_.userids().count(), 0);
+ assert_eq!(cert_.user_attributes().count(), 0);
+
+ // Same, this time using the armor encoder.
+ let mut buf = Vec::new();
+ cert.armored().export(&mut buf).unwrap();
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+ assert_eq!(cert_.subkeys().count(), 0);
+ assert_eq!(cert_.userids().count(), 0);
+ assert_eq!(cert_.user_attributes().count(), 0);
+
+ let mut buf = vec![0; cert.serialized_len()];
+ let l = cert.armored().export_into(&mut buf).unwrap();
+ vec_truncate(&mut buf, l);
+ let cert_ = Cert::from_bytes(&buf).unwrap();
+ assert_eq!(cert_.subkeys().count(), 0);
+ assert_eq!(cert_.userids().count(), 0);
+ assert_eq!(cert_.user_attributes().count(), 0);
+
+ let cert_ =
+ Cert::from_bytes(&cert.armored().export_to_vec().unwrap()).unwrap();
+ assert_eq!(cert_.subkeys()