diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2018-07-05 13:59:18 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2018-07-05 13:59:18 +0200 |
commit | b131379b559f0aa45c33a1dacdc7523ac30a0dd6 (patch) | |
tree | e94649d05fcf3fbf6a00d203ada48df872104436 | |
parent | 91918e633b49b1d71acfbbee93cb82356735f095 (diff) |
openpgp: Introduce and use struct Features.
-rw-r--r-- | openpgp/src/serialize/mod.rs | 2 | ||||
-rw-r--r-- | openpgp/src/subpacket.rs | 101 | ||||
-rw-r--r-- | openpgp/src/tpk.rs | 6 |
3 files changed, 92 insertions, 17 deletions
diff --git a/openpgp/src/serialize/mod.rs b/openpgp/src/serialize/mod.rs index 1f3fe1b5..efa1e95d 100644 --- a/openpgp/src/serialize/mod.rs +++ b/openpgp/src/serialize/mod.rs @@ -468,7 +468,7 @@ impl<'a> Serialize for SubpacketValue<'a> { o.write_all(r)?; }, Features(ref f) => - o.write_all(f)?, + o.write_all(f.as_slice())?, SignatureTarget((pk_algo, hash_algo, ref hash)) => { o.write_all(&[*pk_algo, *hash_algo])?; o.write_all(hash)?; diff --git a/openpgp/src/subpacket.rs b/openpgp/src/subpacket.rs index 608a3bab..c7b9bc2c 100644 --- a/openpgp/src/subpacket.rs +++ b/openpgp/src/subpacket.rs @@ -585,7 +585,7 @@ pub enum SubpacketValue<'a> { /// 1 octet of revocation code, N octets of reason string ReasonForRevocation((u8, &'a [u8])), /// N octets of flags - Features(&'a [u8]), + Features(Features), /// 1-octet public-key algorithm, 1 octet hash algorithm, N octets hash SignatureTarget((u8, u8, &'a [u8])), /// An embedded signature. @@ -622,7 +622,7 @@ impl<'a> SubpacketValue<'a> { KeyFlags(f) => f.0.len(), SignersUserID(u) => u.len(), ReasonForRevocation((_, r)) => 1 + r.len(), - Features(f) => f.len(), + Features(f) => f.0.len(), SignatureTarget((_, _, h)) => 1 + 1 + h.len(), EmbeddedSignature(p) => match p { &Packet::Signature(ref sig) => { @@ -898,7 +898,7 @@ impl<'a> From<SubpacketRaw<'a>> for Subpacket<'a> { SubpacketTag::Features => // N octets of flags - Some(SubpacketValue::Features(raw.value)), + Some(SubpacketValue::Features(Features(raw.value.to_vec()))), SubpacketTag::SignatureTarget => // 1 octet public-key algorithm, 1 octet hash algorithm, @@ -1247,6 +1247,79 @@ const KEY_FLAG_AUTHENTICATE: u8 = 0x20; /// than one person. const KEY_FLAG_GROUP_KEY: u8 = 0x80; + +/// Describes features supported by an OpenPGP implementation. +#[derive(Clone)] +pub struct Features(Vec<u8>); + +impl Default for Features { + fn default() -> Self { + Features(vec![0]) + } +} + +impl PartialEq for Features { + fn eq(&self, other: &Features) -> bool { + // To deal with unknown flags, we do a bitwise comparison. + // First, we need to bring both flag fields to the same + // length. + let len = ::std::cmp::max(self.0.len(), other.0.len()); + let mut mine = vec![0; len]; + let mut hers = vec![0; len]; + &mut mine[..self.0.len()].copy_from_slice(&self.0); + &mut hers[..other.0.len()].copy_from_slice(&other.0); + + mine == hers + } +} + +impl fmt::Debug for Features { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.supports_mdc() { + f.write_str("MDC")?; + } + + Ok(()) + } +} + +impl Features { + /// Grows the vector to the given length. + fn grow(&mut self, target: usize) { + while self.0.len() < target { + self.0.push(0); + } + } + + /// Returns a slice referencing the raw values. + pub(crate) fn as_slice(&self) -> &[u8] { + &self.0 + } + + /// Whether or not MDC is supported. + pub fn supports_mdc(&self) -> bool { + self.0.get(0) + .map(|v0| v0 & FEATURE_FLAG_MDC > 0).unwrap_or(false) + } + + + /// Sets whether or not MDC is supported. + pub fn set_mdc(mut self, v: bool) -> Self { + self.grow(1); + if v { + self.0[0] |= FEATURE_FLAG_MDC; + } else { + self.0[0] &= !FEATURE_FLAG_MDC; + } + self + } +} + +/// The private component of this key may be in the possession of more +/// than one person. +const FEATURE_FLAG_MDC: u8 = 0x01; + + impl Signature { /// Returns the *last* instance of the specified subpacket. fn subpacket<'a>(&'a self, tag: SubpacketTag) -> Option<Subpacket<'a>> { @@ -2036,26 +2109,26 @@ impl Signature { /// /// Note: if the signature contains multiple instances of this /// subpacket, only the last one is considered. - pub fn features(&self) -> Option<&[u8]> { + pub fn features(&self) -> Features { // N octets of flags if let Some(sb) = self.subpacket(SubpacketTag::Features) { if let SubpacketValue::Features(v) = sb.value { - Some(v) + v } else { - None + Features::default() } } else { - None + Features::default() } } /// Sets the value of the Features subpacket, which contains a /// list of features that the user's OpenPGP implementation /// supports. - pub fn set_features(&mut self, features: &[u8]) -> Result<()> { + pub fn set_features(&mut self, features: &Features) -> Result<()> { self.hashed_area.replace(Subpacket::new( - SubpacketValue::Features(features), + SubpacketValue::Features(features.clone()), true)?) } @@ -2293,8 +2366,9 @@ fn accessors() { sig.set_reason_for_revocation(3, b"foobar").unwrap(); assert_eq!(sig.reason_for_revocation(), Some((3, &b"foobar"[..]))); - sig.set_features(b"foobar").unwrap(); - assert_eq!(sig.features(), Some(&b"foobar"[..])); + let feats = Features::default().set_mdc(true); + sig.set_features(&feats).unwrap(); + assert_eq!(sig.features(), feats); let digest = vec![0; hash_algo.context().unwrap().digest_size()]; sig.set_signature_target(pk_algo, hash_algo, &digest).unwrap(); @@ -2519,12 +2593,13 @@ fn subpacket_test_2() { KeyFlags::default().set_certify(true).set_sign(true)) })); - assert_eq!(sig.features(), Some(&[0x01][..])); + assert_eq!(sig.features(), Features::default().set_mdc(true)); assert_eq!(sig.subpacket(SubpacketTag::Features), Some(Subpacket { critical: false, tag: SubpacketTag::Features, - value: SubpacketValue::Features(&[0x01][..]) + value: SubpacketValue::Features( + Features::default().set_mdc(true)) })); let keyid = KeyID::from_hex("F004 B9A4 5C58 6126").unwrap(); diff --git a/openpgp/src/tpk.rs b/openpgp/src/tpk.rs index 8dba72ee..40edd33d 100644 --- a/openpgp/src/tpk.rs +++ b/openpgp/src/tpk.rs @@ -1298,9 +1298,9 @@ impl TPK { self.subkeys.sort_by(|a, b| { // Features. - let a_features = a.selfsigs[0].features().unwrap_or(b""); - let b_features = b.selfsigs[0].features().unwrap_or(b""); - let cmp = a_features.cmp(&b_features); + let a_features = a.selfsigs[0].features(); + let b_features = b.selfsigs[0].features(); + let cmp = a_features.as_slice().cmp(b_features.as_slice()); if cmp != Ordering::Equal { return cmp; } |