diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2020-02-10 12:48:47 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2020-02-10 15:18:30 +0100 |
commit | 7eae54c9fff3062aa8f3a4c3011a8a7e6890d3e1 (patch) | |
tree | ad72787ee58684554bb84f1a8da266b889827dc4 | |
parent | 99202aa7cc79337aa0bcd2895c0a57b8fe2308da (diff) |
openpgp: New type RevocationKey.
- See #431.
-rw-r--r-- | openpgp/src/cert/amalgamation.rs | 7 | ||||
-rw-r--r-- | openpgp/src/packet/signature/subpacket.rs | 53 | ||||
-rw-r--r-- | openpgp/src/parse/parse.rs | 13 | ||||
-rw-r--r-- | openpgp/src/serialize/mod.rs | 27 | ||||
-rw-r--r-- | openpgp/src/types/mod.rs | 2 | ||||
-rw-r--r-- | openpgp/src/types/revocation_key.rs | 98 | ||||
-rw-r--r-- | tool/src/commands/dump.rs | 11 |
7 files changed, 153 insertions, 58 deletions
diff --git a/openpgp/src/cert/amalgamation.rs b/openpgp/src/cert/amalgamation.rs index 4db585f3..ce6a0849 100644 --- a/openpgp/src/cert/amalgamation.rs +++ b/openpgp/src/cert/amalgamation.rs @@ -6,14 +6,13 @@ use crate::{ Cert, cert::components::ComponentBundle, Error, - Fingerprint, packet::Signature, Result, RevocationStatus, policy::Policy, types::{ + RevocationKey, KeyFlags, - PublicKeyAlgorithm, }, }; @@ -358,9 +357,7 @@ pub trait Amalgamation<'a> { /// 5.2.3.3 of RFC 4880]. /// /// [Section 5.2.3.3 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.2.3.3 - fn revocation_key(&self) -> Option<(u8, - PublicKeyAlgorithm, - Fingerprint)> { + fn revocation_key(&self) -> Option<&'a RevocationKey> { self.map(|s| s.revocation_key()) } } diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs index c423a1f1..010795d1 100644 --- a/openpgp/src/packet/signature/subpacket.rs +++ b/openpgp/src/packet/signature/subpacket.rs @@ -89,6 +89,7 @@ use crate::types::{ KeyServerPreferences, PublicKeyAlgorithm, ReasonForRevocation, + RevocationKey, SymmetricAlgorithm, Timestamp, }; @@ -633,19 +634,7 @@ pub enum SubpacketValue { PreferredSymmetricAlgorithms(Vec<SymmetricAlgorithm>), /// 1 octet of class, 1 octet of public-key algorithm ID, 20 octets of /// fingerprint - RevocationKey { - /// Class octet must have bit 0x80 set. If the bit 0x40 is - /// set, then this means that the revocation information is - /// sensitive. Other bits are for future expansion to other - /// kinds of authorizations. - class: u8, - - /// XXX: RFC4880 says nothing about this. - pk_algo: PublicKeyAlgorithm, - - /// Fingerprint of authorized key. - fp: Fingerprint, - }, + RevocationKey(RevocationKey), /// 8-octet Key ID Issuer(KeyID), /// The notation has a name and a value, each of @@ -1103,16 +1092,10 @@ impl SubpacketArea { /// /// Note: if the signature contains multiple instances of this /// subpacket, only the last one is considered. - pub fn revocation_key(&self) -> Option<(u8, - PublicKeyAlgorithm, - Fingerprint)> { - // 1 octet of class, 1 octet of public-key algorithm ID, 20 or - // 32 octets of fingerprint. + pub fn revocation_key(&self) -> Option<&RevocationKey> { if let Some(sb) = self.subpacket(SubpacketTag::RevocationKey) { - if let SubpacketValue::RevocationKey { - class, pk_algo, fp, - } = &sb.value { - Some((*class, *pk_algo, fp.clone())) + if let SubpacketValue::RevocationKey(rk) = &sb.value { + Some(rk) } else { None } @@ -1962,14 +1945,9 @@ impl signature::Builder { /// Sets the value of the Revocation Key subpacket, which contains /// a designated revoker. - pub fn set_revocation_key(mut self, class: u8, pk_algo: PublicKeyAlgorithm, - fp: Fingerprint) -> Result<Self> { + pub fn set_revocation_key(mut self, rk: RevocationKey) -> Result<Self> { self.hashed_area.replace(Subpacket::new( - SubpacketValue::RevocationKey { - class: class, - pk_algo: pk_algo, - fp: fp, - }, + SubpacketValue::RevocationKey(rk), true)?)?; Ok(self) @@ -2327,11 +2305,11 @@ fn accessors() { assert_eq!(sig_.preferred_symmetric_algorithms(), Some(&pref[..])); let fp = Fingerprint::from_bytes(b"bbbbbbbbbbbbbbbbbbbb"); - sig = sig.set_revocation_key(2, pk_algo, fp.clone()).unwrap(); + let rk = RevocationKey::new(pk_algo, fp.clone(), true); + sig = sig.set_revocation_key(rk.clone()).unwrap(); let sig_ = sig.clone().sign_hash(&mut keypair, hash.clone()).unwrap(); - assert_eq!(sig_.revocation_key(), - Some((2, pk_algo.into(), fp.clone()))); + assert_eq!(sig_.revocation_key(), Some(&rk)); sig = sig.set_issuer(fp.clone().into()).unwrap(); let sig_ = @@ -2797,17 +2775,14 @@ fn subpacket_test_2() { let fp = Fingerprint::from_hex( "361A96BDE1A65B6D6C25AE9FF004B9A45C586126").unwrap(); - assert_eq!(sig.revocation_key(), - Some((128, PublicKeyAlgorithm::RSAEncryptSign, fp.clone()))); + let rk = RevocationKey::new(PublicKeyAlgorithm::RSAEncryptSign, + fp.clone(), false); + assert_eq!(sig.revocation_key(), Some(&rk)); assert_eq!(sig.subpacket(SubpacketTag::RevocationKey), Some(&Subpacket { length: 23.into(), critical: false, - value: SubpacketValue::RevocationKey { - class: 0x80, - pk_algo: PublicKeyAlgorithm::RSAEncryptSign, - fp: fp, - }, + value: SubpacketValue::RevocationKey(rk), })); diff --git a/openpgp/src/parse/parse.rs b/openpgp/src/parse/parse.rs index 8848e292..52531262 100644 --- a/openpgp/src/parse/parse.rs +++ b/openpgp/src/parse/parse.rs @@ -39,6 +39,7 @@ use crate::types::{ KeyFlags, KeyServerPreferences, PublicKeyAlgorithm, + RevocationKey, SignatureType, SymmetricAlgorithm, Timestamp, @@ -1280,12 +1281,12 @@ impl Subpacket { "Short revocation key subpacket".into()) .into()); } - SubpacketValue::RevocationKey { - class: php.parse_u8("class")?, - pk_algo: php.parse_u8("pk algo")?.into(), - fp: Fingerprint::from_bytes(&php.parse_bytes("fingerprint", - len - 2)?), - } + let class = php.parse_u8("class")?; + let pk_algo = php.parse_u8("pk algo")?.into(); + let fp = Fingerprint::from_bytes( + &php.parse_bytes("fingerprint", len - 2)?); + SubpacketValue::RevocationKey( + RevocationKey::from_bits(pk_algo, fp, class)?) }, SubpacketTag::Issuer => SubpacketValue::Issuer( diff --git a/openpgp/src/serialize/mod.rs b/openpgp/src/serialize/mod.rs index a672817c..3c97704e 100644 --- a/openpgp/src/serialize/mod.rs +++ b/openpgp/src/serialize/mod.rs @@ -41,6 +41,7 @@ use crate::packet::signature::subpacket::{ }; use crate::packet::prelude::*; use crate::types::{ + RevocationKey, Timestamp, }; @@ -1016,10 +1017,7 @@ impl Serialize for SubpacketValue { for a in p { o.write_all(&[(*a).into()])?; }, - RevocationKey { ref class, ref pk_algo, ref fp } => { - o.write_all(&[*class, (*pk_algo).into()])?; - o.write_all(fp.as_slice())?; - }, + RevocationKey(rk) => rk.serialize(o)?, Issuer(ref id) => o.write_all(id.as_slice())?, NotationData(nd) => { @@ -1100,7 +1098,7 @@ impl SerializeInto for SubpacketValue { Revocable(_) => 1, KeyExpirationTime(_) => 4, PreferredSymmetricAlgorithms(ref p) => p.len(), - RevocationKey { ref fp, .. } => 2 + fp.serialized_len(), + RevocationKey(rk) => rk.serialized_len(), Issuer(ref id) => id.serialized_len(), NotationData(nd) => 4 + 2 + 2 + nd.name().len() + nd.value().len(), PreferredHashAlgorithms(ref p) => p.len(), @@ -1136,6 +1134,25 @@ impl SerializeInto for SubpacketValue { } } +impl Serialize for RevocationKey { + fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { + let (pk_algo, fp) = self.revoker(); + o.write_all(&[self.class(), (pk_algo).into()])?; + o.write_all(fp.as_slice())?; + Ok(()) + } +} + +impl SerializeInto for RevocationKey { + fn serialized_len(&self) -> usize { + 1 + 1 + self.revoker().1.as_slice().len() + } + + fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> { + generic_serialize_into(self, buf) + } +} + impl Serialize for Signature { fn serialize(&self, o: &mut dyn std::io::Write) -> Result<()> { match self { diff --git a/openpgp/src/types/mod.rs b/openpgp/src/types/mod.rs index ac6811d5..58b7b03b 100644 --- a/openpgp/src/types/mod.rs +++ b/openpgp/src/types/mod.rs @@ -16,6 +16,8 @@ mod features; pub use self::features::Features; mod key_flags; pub use self::key_flags::KeyFlags; +mod revocation_key; +pub use revocation_key::RevocationKey; mod server_preferences; pub use self::server_preferences::KeyServerPreferences; mod timestamp; diff --git a/openpgp/src/types/revocation_key.rs b/openpgp/src/types/revocation_key.rs new file mode 100644 index 00000000..c96910a2 --- /dev/null +++ b/openpgp/src/types/revocation_key.rs @@ -0,0 +1,98 @@ +use crate::{ + Error, + Fingerprint, + Result, + types::{ + PublicKeyAlgorithm, + }, +}; + +/// Designates a key as a valid third-party revoker. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct RevocationKey { + /// XXX: RFC4880 says nothing about this. + pk_algo: PublicKeyAlgorithm, + + /// Fingerprint of authorized key. + fp: Fingerprint, + + /// Indicates that the relation between revoker and revokee is + /// of a sensitive nature. + sensitive: bool, + + /// Other bits are for future expansion to other kinds of + /// authorizations. + unknown: u8, +} + +impl RevocationKey { + /// Creates a new instance. + pub fn new(pk_algo: PublicKeyAlgorithm, fp: Fingerprint, sensitive: bool) + -> Self + { + RevocationKey { + pk_algo, fp, sensitive, unknown: 0, + } + } + + /// Creates a new instance from `bits`. + pub fn from_bits(pk_algo: PublicKeyAlgorithm, fp: Fingerprint, class: u8) + -> Result<Self> { + if class & REVOCATION_KEY_FLAG_MUST_BE_SET == 0 { + return Err(Error::InvalidArgument( + "Most significant bit of class must be set".into()).into()); + } + let sensitive = class & REVOCATION_KEY_FLAG_SENSITIVE > 0; + let unknown = class & REVOCATION_KEY_MASK_UNKNOWN; + Ok(RevocationKey { + pk_algo, fp, sensitive, unknown, + }) + } + + /// Returns the `class` octet, the sum of all flags. + pub fn class(&self) -> u8 { + (REVOCATION_KEY_FLAG_MUST_BE_SET + | if self.sensitive() { + REVOCATION_KEY_FLAG_SENSITIVE + } else { + 0 + } + | self.unknown) + } + + /// Returns the revoker's identity. + pub fn revoker(&self) -> (PublicKeyAlgorithm, &Fingerprint) { + (self.pk_algo, &self.fp) + } + + /// Sets the revoker's identity. + pub fn set_revoker(&mut self, pk_algo: PublicKeyAlgorithm, fp: Fingerprint) + -> (PublicKeyAlgorithm, Fingerprint) { + let pk_algo = std::mem::replace(&mut self.pk_algo, pk_algo); + let fp = std::mem::replace(&mut self.fp, fp); + (pk_algo, fp) + } + + /// Returns whether or not the relation between revoker and + /// revokee is of a sensitive nature. + pub fn sensitive(&self) -> bool { + self.sensitive + } + + /// Sets whether or not the relation between revoker and revokee + /// is of a sensitive nature. + pub fn set_sensitive(mut self, v: bool) -> Self { + self.sensitive = v; + self + } +} + +/// This bit must be set. +const REVOCATION_KEY_FLAG_MUST_BE_SET: u8 = 0x80; + +/// Relation is of a sensitive nature. +const REVOCATION_KEY_FLAG_SENSITIVE: u8 = 0x40; + +/// Mask covering the unknown bits. +const REVOCATION_KEY_MASK_UNKNOWN: u8 = ! (REVOCATION_KEY_FLAG_MUST_BE_SET + | REVOCATION_KEY_FLAG_SENSITIVE); diff --git a/tool/src/commands/dump.rs b/tool/src/commands/dump.rs index aed7fb15..349255b9 100644 --- a/tool/src/commands/dump.rs +++ b/tool/src/commands/dump.rs @@ -760,10 +760,15 @@ impl PacketDumper { write!(output, "{} Symmetric algo preferences: {}", i, c.iter().map(|c| format!("{:?}", c)) .collect::<Vec<String>>().join(", "))?, - RevocationKey{class, pk_algo, ref fp} => + RevocationKey(rk) => { + let (pk_algo, fp) = rk.revoker(); write!(output, - "{} Revocation key: class {} algo {} fingerprint {}", i, - class, pk_algo, fp)?, + "{} Revocation key: {}/{}", i, + fp, pk_algo)?; + if rk.sensitive() { + write!(output, ", sensitive")?; + } + }, Issuer(ref is) => write!(output, "{} Issuer: {}", i, is)?, NotationData(ref n) => |