diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2024-01-18 12:51:18 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2024-01-18 13:07:07 +0100 |
commit | 9582ba1a068402e880a1cf3e5b4bb481733953fb (patch) | |
tree | d093b1331a1790c6e037b9afc66add041177b0ae /openpgp/src | |
parent | b34c523015b1a59cf05391e78d16e394a3c98c84 (diff) |
openpgp: Serialize TSKs without secrets with appropriate label.
- Previously, we emitted the PRIVATE KEY label when armoring TSKs
that don't in fact contain secrets, confusing users.
- See https://gitlab.com/sequoia-pgp/sequoia-sq/-/issues/14
- Fixes #1075.
Diffstat (limited to 'openpgp/src')
-rw-r--r-- | openpgp/src/serialize/cert.rs | 34 | ||||
-rw-r--r-- | openpgp/src/serialize/cert_armored.rs | 13 |
2 files changed, 44 insertions, 3 deletions
diff --git a/openpgp/src/serialize/cert.rs b/openpgp/src/serialize/cert.rs index 2702fda6..764a16ac 100644 --- a/openpgp/src/serialize/cert.rs +++ b/openpgp/src/serialize/cert.rs @@ -280,6 +280,17 @@ impl<'a> TSK<'a> { } } + /// Returns whether we're emitting secret (sub)key packets when + /// serializing. + /// + /// Computes whether we have secrets, taking the filter into + /// account, and whether we are emitting stubs. This can be used + /// to determine the correct armor label to use. + pub(crate) fn emits_secret_key_packets(&self) -> bool { + self.emit_stubs + || self.cert.keys().secret().any(|skb| (self.filter)(skb.key())) + } + /// Filters which secret keys to export using the given predicate. /// /// Note that the given filter replaces any existing filter. @@ -985,4 +996,27 @@ mod test { Ok(()) } + + #[test] + fn issue_1075() -> Result<()> { + let cert = Cert::from_bytes(crate::tests::key("testy.pgp"))?; + let tsk = Cert::from_bytes(crate::tests::key("testy-private.pgp"))?; + + // Filters out secrets. + let no_secrets = |_| false; + + assert!(! cert.as_tsk().emits_secret_key_packets()); + assert!(cert.as_tsk().emit_secret_key_stubs(true) + .emits_secret_key_packets()); + assert!(tsk.as_tsk().emits_secret_key_packets()); + assert!(! tsk.as_tsk().set_filter(no_secrets) + .emits_secret_key_packets()); + + assert!(cert.armored().to_vec()? != tsk.as_tsk().armored().to_vec()?); + assert_eq!(cert.armored().to_vec()?, + cert.as_tsk().armored().to_vec()?); + assert_eq!(cert.armored().to_vec()?, + tsk.as_tsk().set_filter(no_secrets).armored().to_vec()?); + Ok(()) + } } diff --git a/openpgp/src/serialize/cert_armored.rs b/openpgp/src/serialize/cert_armored.rs index a3060e8d..63642954 100644 --- a/openpgp/src/serialize/cert_armored.rs +++ b/openpgp/src/serialize/cert_armored.rs @@ -143,8 +143,11 @@ impl<'a> Encoder<'a> { let (prelude, headers) = match self { Encoder::Cert(cert) => (armor::Kind::PublicKey, cert.armor_headers()), - Encoder::TSK(ref tsk) => - (armor::Kind::SecretKey, tsk.cert.armor_headers()), + Encoder::TSK(tsk) => if tsk.emits_secret_key_packets() { + (armor::Kind::SecretKey, tsk.cert.armor_headers()) + } else { + (armor::Kind::PublicKey, tsk.cert.armor_headers()) + }, }; // Convert the Vec<String> into Vec<(&str, &str)> @@ -202,7 +205,11 @@ impl<'a> MarshalInto for Encoder<'a> { let word = match self { Self::Cert(_) => "PUBLIC", - Self::TSK(_) => "PRIVATE", + Self::TSK(tsk) => if tsk.emits_secret_key_packets() { + "PRIVATE" + } else { + "PUBLIC" + }, }.len(); "-----BEGIN PGP ".len() + word + " KEY BLOCK-----\n\n".len() |