summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <neal@pep.foundation>2020-07-22 11:46:08 +0200
committerNeal H. Walfield <neal@pep.foundation>2020-07-22 11:46:08 +0200
commit67d35136fb70226a962232dfe483d83d23c143b5 (patch)
treee12e69dce38678927c09fc85cfc4e9dad9b2ba4c
parent8cc0d7b85f979276157d5b51c6ee54d404c57d7b (diff)
openpgp: Rework the Features implementation and interface.
- Change Features to only store the raw value, a vector of bytes. Extract features on demand. - Add methods to query and manipulate unknown features. - Change `PartialEq` to implement serialized equality (like most other data structures). Add `normalized_eq` to implement semantic equality.
-rw-r--r--openpgp/src/cert/mod.rs6
-rw-r--r--openpgp/src/packet/signature/subpacket.rs8
-rw-r--r--openpgp/src/serialize.rs4
-rw-r--r--openpgp/src/types/features.rs517
-rw-r--r--openpgp/src/types/mod.rs2
5 files changed, 420 insertions, 117 deletions
diff --git a/openpgp/src/cert/mod.rs b/openpgp/src/cert/mod.rs
index ae42dc24..8d7c0349 100644
--- a/openpgp/src/cert/mod.rs
+++ b/openpgp/src/cert/mod.rs
@@ -5130,7 +5130,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
// Some(KeyServerPreferences::new(&[])));
assert_eq!(userid.features(),
- Some(Features::new(&[]).set_mdc(true)));
+ Some(Features::new(&[]).set_mdc()));
} else {
panic!("two user ids");
}
@@ -5159,7 +5159,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
Some(KeyServerPreferences::new(&[0x80])));
assert_eq!(userid.features(),
- Some(Features::new(&[]).set_mdc(true)));
+ Some(Features::new(&[]).set_mdc()));
// Using the certificate should choose the primary user
// id, which is this one (because it is lexicographically
@@ -5205,7 +5205,7 @@ Pu1xwz57O4zo1VYf6TqHJzVC3OMvMUM2hhdecMUe5x6GorNaj6g=
Some(KeyServerPreferences::new(&[0x80])));
assert_eq!(userid.features(),
- Some(Features::new(&[]).set_mdc(true)));
+ Some(Features::new(&[]).set_mdc()));
} else {
panic!("two user ids");
}
diff --git a/openpgp/src/packet/signature/subpacket.rs b/openpgp/src/packet/signature/subpacket.rs
index 169a2c14..4049b15c 100644
--- a/openpgp/src/packet/signature/subpacket.rs
+++ b/openpgp/src/packet/signature/subpacket.rs
@@ -2699,13 +2699,13 @@ fn accessors() {
assert_eq!(sig_.reason_for_revocation(),
Some((ReasonForRevocation::KeyRetired, &b"foobar"[..])));
- let feats = Features::default().set_mdc(true);
+ let feats = Features::default().set_mdc();
sig = sig.set_features(&feats).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash.clone()).unwrap();
assert_eq!(sig_.features().unwrap(), feats);
- let feats = Features::default().set_aead(true);
+ let feats = Features::default().set_aead();
sig = sig.set_features(&feats).unwrap();
let sig_ =
sig.clone().sign_hash(&mut keypair, hash.clone()).unwrap();
@@ -2981,13 +2981,13 @@ fn subpacket_test_2() {
KeyFlags::default().set_certification(true).set_signing(true))
}));
- assert_eq!(sig.features().unwrap(), Features::default().set_mdc(true));
+ assert_eq!(sig.features().unwrap(), Features::default().set_mdc());
assert_eq!(sig.subpacket(SubpacketTag::Features),
Some(&Subpacket {
length: 2.into(),
critical: false,
value: SubpacketValue::Features(
- Features::default().set_mdc(true))
+ Features::default().set_mdc())
}));
let keyid = "F004 B9A4 5C58 6126".parse().unwrap();
diff --git a/openpgp/src/serialize.rs b/openpgp/src/serialize.rs
index 0640f3cc..f75658da 100644
--- a/openpgp/src/serialize.rs
+++ b/openpgp/src/serialize.rs
@@ -1401,7 +1401,7 @@ impl Marshal for SubpacketValue {
o.write_all(reason)?;
},
Features(ref f) =>
- o.write_all(&f.to_vec())?,
+ o.write_all(&f.as_slice())?,
SignatureTarget { pk_algo, hash_algo, ref digest } => {
o.write_all(&[(*pk_algo).into(), (*hash_algo).into()])?;
o.write_all(digest)?;
@@ -1459,7 +1459,7 @@ impl MarshalInto for SubpacketValue {
KeyFlags(ref f) => f.to_vec().len(),
SignersUserID(ref uid) => uid.len(),
ReasonForRevocation { ref reason, .. } => 1 + reason.len(),
- Features(ref f) => f.to_vec().len(),
+ Features(ref f) => f.as_slice().len(),
SignatureTarget { ref digest, .. } => 2 + digest.len(),
EmbeddedSignature(sig) => sig.serialized_len(),
IssuerFingerprint(ref fp) => match fp {
diff --git a/openpgp/src/types/features.rs b/openpgp/src/types/features.rs
index 9756ca70..22514d09 100644
--- a/openpgp/src/types/features.rs
+++ b/openpgp/src/types/features.rs
@@ -4,18 +4,26 @@ use std::hash::{Hash, Hasher};
#[cfg(any(test, feature = "quickcheck"))]
use quickcheck::{Arbitrary, Gen};
-/// Describes features supported by an OpenPGP implementation.
+/// Describes the features supported by an OpenPGP implementation.
///
-/// The features are defined in [Section 5.2.3.24 of RFC 4880], and
-/// [Section 5.2.3.25 of RFC 4880bis].
+/// The feature flags are defined in [Section 5.2.3.24 of RFC 4880],
+/// and [Section 5.2.3.25 of RFC 4880bis].
///
/// [Section 5.2.3.24 of RFC 4880]: https://tools.ietf.org/html/rfc4880#section-5.2.3.24
/// [Section 5.2.3.25 of RFC 4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-5.2.3.25
///
+/// The feature flags are set by the user's OpenPGP implementation to
+/// signal to any senders what features the implementation supports.
+///
/// # A note on equality
///
-/// `PartialEq` is implements semantic equality, i.e. it ignores
-/// padding.
+/// `PartialEq` compares the serialized form of the two feature sets.
+/// If you prefer to compare two feature sets for semantic equality,
+/// you should use [`Features::normalized_eq`]. The difference
+/// between semantic equality and serialized equality is that semantic
+/// equality ignores differences in the amount of padding.
+///
+/// [`Features::normalized_eq`]: #method.normalized_eq
///
/// # Examples
///
@@ -44,59 +52,70 @@ use quickcheck::{Arbitrary, Gen};
/// }
/// # Ok(()) }
/// ```
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
pub struct Features{
- mdc: bool,
- aead: bool,
- unknown: Box<[u8]>,
- /// Original length, including trailing zeros.
- pad_to: usize,
+ raw: Vec<u8>,
}
impl Default for Features {
fn default() -> Self {
- Features{
- mdc: false,
- aead: false,
- unknown: Default::default(),
- pad_to: 0,
+ Features {
+ raw: Vec::new(),
}
}
}
impl fmt::Debug for Features {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let mut dirty = false;
+ // Print known features first.
+ let mut need_comma = false;
if self.supports_mdc() {
f.write_str("MDC")?;
- dirty = true;
+ need_comma = true;
}
if self.supports_aead() {
- if dirty { f.write_str(", ")?; }
+ if need_comma { f.write_str(", ")?; }
f.write_str("AEAD")?;
- dirty = true;
+ need_comma = true;
}
- if ! self.unknown.is_empty() {
- if dirty { f.write_str(", ")?; }
- f.write_str("+0x")?;
- f.write_str(
- &crate::fmt::hex::encode_pretty(&self.unknown))?;
- dirty = true;
+
+ // Now print any unknown features.
+ for i in self.raw.iter()
+ .flat_map(|b| {
+ (0..8).into_iter().map(move |i| {
+ b & (1 << i) != 0
+ })
+ })
+ .enumerate()
+ .filter_map(|(i, v)| if v { Some(i) } else { None })
+ {
+ match i {
+ FEATURE_FLAG_MDC => (),
+ FEATURE_FLAG_AEAD => (),
+ i => {
+ if need_comma { f.write_str(", ")?; }
+ write!(f, "#{}", i)?;
+ need_comma = true;
+ }
+ }
}
- if self.pad_to > FEATURE_FLAGS_N_KNOWN_BYTES + self.unknown.len() {
- if dirty { f.write_str(", ")?; }
- write!(f, "+padding({} bytes)", self.pad_to - self.unknown.len())?;
+
+ // Mention any padding, as equality is sensitive to this.
+ let mut padding = 0;
+ for i in (0..self.raw.len()).rev() {
+ if self.raw[i] == 0 {
+ padding += 1;
+ } else {
+ break;
+ }
}
- Ok(())
- }
-}
+ if padding > 0 {
+ if need_comma { f.write_str(", ")?; }
+ write!(f, "+padding({} bytes)", padding)?;
+ }
-impl PartialEq for Features {
- fn eq(&self, other: &Self) -> bool {
- self.mdc == other.mdc
- && self.aead == other.aead
- && self.unknown == other.unknown
+ Ok(())
}
}
@@ -104,110 +123,317 @@ impl Eq for Features {}
impl Hash for Features {
fn hash<H: Hasher>(&self, state: &mut H) {
- self.mdc.hash(state);
- self.aead.hash(state);
- self.unknown.hash(state);
+ self.raw.hash(state);
}
}
impl Features {
- /// Creates a new instance from `bits`.
- pub fn new<B: AsRef<[u8]>>(bits: B) -> Self {
- let bits = bits.as_ref();
- let mut pad_to = 0;
-
- let mdc = bits.get(0)
- .map(|x| x & FEATURE_FLAG_MDC != 0).unwrap_or(false);
- let aead = bits.get(0)
- .map(|x| x & FEATURE_FLAG_AEAD != 0).unwrap_or(false);
- let unk = if bits.is_empty() {
- Box::default()
- } else {
- let mut cpy = Vec::from(bits);
+ /// Creates a new instance from `bytes`.
+ ///
+ /// This does not remove any trailing padding from `bytes`.
+ pub fn new<B>(bytes: B) -> Self
+ where B: AsRef<[u8]>
+ {
+ Features{
+ raw: bytes.as_ref().to_vec(),
+ }
+ }
+
+ /// Returns an empty feature set.
+ pub fn empty() -> Self {
+ Self::new(&[][..])
+ }
+
+ /// Returns a feature set describing Sequoia's capabilities.
+ pub fn sequoia() -> Self {
+ let v : [u8; 1] = [ 0 ];
- cpy[0] &= (FEATURE_FLAG_MDC | FEATURE_FLAG_AEAD) ^ 0xff;
+ Self::new(&v[..]).set_mdc()
+ }
- pad_to = crate::types::bitfield_remove_padding(&mut cpy);
- cpy.into_boxed_slice()
+ /// Compares two feature sets for semantic equality.
+ ///
+ /// `Features`' implementation of `PartialEq` compares two feature
+ /// sets for serialized equality. That is, the `PartialEq`
+ /// implementation considers two feature sets to *not* be equal if
+ /// they have different amounts of padding. This comparison
+ /// function ignores padding.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let a = Features::new(&[ 0x1 ]);
+ /// let b = Features::new(&[ 0x1, 0x0 ]);
+ ///
+ /// assert!(a != b);
+ /// assert!(a.normalized_eq(&b));
+ /// # Ok(()) }
+ /// ```
+ pub fn normalized_eq(&self, other: &Self) -> bool {
+ let (small, big) = if self.raw.len() < other.raw.len() {
+ (self, other)
+ } else {
+ (other, self)
};
- Features{
- mdc, aead, unknown: unk, pad_to,
+ for (s, b) in small.raw.iter().zip(big.raw.iter()) {
+ if s != b {
+ return false;
+ }
}
- }
- /// Returns an feature set describing Sequoia.
- pub fn sequoia() -> Self {
- Features{
- mdc: true,
- aead: false,
- unknown: Default::default(),
- pad_to: 0,
+ for &b in &big.raw[small.raw.len()..] {
+ if b != 0 {
+ return false;
+ }
}
+
+ true
}
- /// Returns a slice referencing the raw values.
- pub(crate) fn to_vec(&self) -> Vec<u8> {
- let mut ret = if self.unknown.is_empty() {
- vec![0]
+ /// Returns a slice containing the raw values.
+ pub(crate) fn as_slice(&self) -> &[u8] {
+ &self.raw
+ }
+
+ /// Returns whether the specified feature flag is set.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// // Feature flags 0 and 2.
+ /// let f = Features::new(&[ 0x5 ]);
+ ///
+ /// assert!(f.check(0));
+ /// assert!(! f.check(1));
+ /// assert!(f.check(2));
+ /// assert!(! f.check(3));
+ /// assert!(! f.check(8));
+ /// assert!(! f.check(80));
+ /// # assert!(f.supports_mdc());
+ /// # assert!(! f.supports_aead());
+ /// # Ok(()) }
+ /// ```
+ pub fn check(&self, bit: usize) -> bool {
+ let byte = bit / 8;
+
+ if byte >= self.raw.len() {
+ // Unset bits are false.
+ false
} else {
- self.unknown.clone().into()
- };
+ (self.raw[byte] & (1 << (bit % 8))) != 0
+ }
+ }
- if self.mdc { ret[0] |= FEATURE_FLAG_MDC; }
- if self.aead { ret[0] |= FEATURE_FLAG_AEAD; }
+ /// Remove any trailing padding.
+ fn clear_padding(mut self) -> Self {
+ while self.raw.len() > 0 && self.raw[self.raw.len() - 1] == 0 {
+ self.raw.truncate(self.raw.len() - 1);
+ }
+
+ self
+ }
- // Corner case: empty flag field. We initialized ret to
- // vec![0] for easy setting of flags. See if any of the above
- // was set.
- if ret.len() == 1 && ret[0] == 0 {
- // Nope. Trim this byte.
- ret.pop();
+ /// Sets the specified feature flag.
+ ///
+ /// This also clears any padding (trailing NUL bytes).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::empty().set(0).set(2);
+ ///
+ /// assert!(f.check(0));
+ /// assert!(! f.check(1));
+ /// assert!(f.check(2));
+ /// assert!(! f.check(3));
+ /// # assert!(f.supports_mdc());
+ /// # assert!(! f.supports_aead());
+ /// # Ok(()) }
+ /// ```
+ pub fn set(mut self, bit: usize) -> Self {
+ let byte = bit / 8;
+ while self.raw.len() <= byte {
+ self.raw.push(0);
}
+ self.raw[byte] |= 1 << (bit % 8);
- for _ in ret.len()..self.pad_to {
- ret.push(0);
+ self.clear_padding()
+ }
+
+ /// Clears the specified feature flag.
+ ///
+ /// This also clears any padding (trailing NUL bytes).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::empty().set(0).set(2).clear(2);
+ ///
+ /// assert!(f.check(0));
+ /// assert!(! f.check(1));
+ /// assert!(! f.check(2));
+ /// assert!(! f.check(3));
+ /// # assert!(f.supports_mdc());
+ /// # assert!(! f.supports_aead());
+ /// # Ok(()) }
+ /// ```
+ pub fn clear(mut self, bit: usize) -> Self {
+ let byte = bit / 8;
+ if byte < self.raw.len() {
+ self.raw[byte] &= !(1 << (bit % 8));
}
- ret
+ self.clear_padding()
}
- /// Whether or not MDC is supported.
+ /// Returns whether the MDC feature flag is set.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::empty();
+ ///
+ /// assert!(! f.supports_mdc());
+ /// # Ok(()) }
+ /// ```
pub fn supports_mdc(&self) -> bool {
- self.mdc
+ self.check(FEATURE_FLAG_MDC)
}
- /// Sets whether or not MDC is supported.
- pub fn set_mdc(mut self, v: bool) -> Self {
- self.mdc = v;
- self
+ /// Sets the MDC feature flag.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::empty().set_mdc();
+ ///
+ /// assert!(f.supports_mdc());
+ /// # assert!(f.check(0));
+ /// # Ok(()) }
+ /// ```
+ pub fn set_mdc(self) -> Self {
+ self.set(FEATURE_FLAG_MDC)
+ }
+
+ /// Clears the MDC feature flag.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::new(&[ 0x1 ]);
+ /// assert!(f.supports_mdc());
+ ///
+ /// let f = f.clear_mdc();
+ /// assert!(! f.supports_mdc());
+ /// # Ok(()) }
+ /// ```
+ pub fn clear_mdc(self) -> Self {
+ self.clear(FEATURE_FLAG_MDC)
}
- /// Whether or not AEAD is supported.
+ /// Returns whether the AEAD feature flag is set.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::empty();
///
- /// This feature is [experimental](../index.html#experimental-features).
+ /// assert!(! f.supports_aead());
+ /// # Ok(()) }
+ /// ```
pub fn supports_aead(&self) -> bool {
- self.aead
+ self.check(FEATURE_FLAG_AEAD)
}
- /// Sets whether or not AEAD is supported.
+ /// Sets the AEAD feature flag.
///
- /// This feature is [experimental](../index.html#experimental-features).
- pub fn set_aead(mut self, v: bool) -> Self {
- self.aead = v;
- self
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::empty().set_aead();
+ ///
+ /// assert!(f.supports_aead());
+ /// # assert!(f.check(1));
+ /// # Ok(()) }
+ /// ```
+ pub fn set_aead(self) -> Self {
+ self.set(FEATURE_FLAG_AEAD)
+ }
+
+ /// Clears the AEAD feature flag.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use sequoia_openpgp as openpgp;
+ /// # use openpgp::Result;
+ /// use openpgp::types::Features;
+ ///
+ /// # fn main() -> Result<()> {
+ /// let f = Features::new(&[ 0x2 ]);
+ /// assert!(f.supports_aead());
+ ///
+ /// let f = f.clear_aead();
+ /// assert!(! f.supports_aead());
+ /// # Ok(()) }
+ /// ```
+ pub fn clear_aead(self) -> Self {
+ self.clear(FEATURE_FLAG_AEAD)
}
}
/// Modification Detection (packets 18 and 19).
-const FEATURE_FLAG_MDC: u8 = 0x01;
+const FEATURE_FLAG_MDC: usize = 0;
/// AEAD Encrypted Data Packet (packet 20) and version 5 Symmetric-Key
/// Encrypted Session Key Packets (packet 3).
-const FEATURE_FLAG_AEAD: u8 = 0x02;
-
-/// Number of bytes with known flags.
-const FEATURE_FLAGS_N_KNOWN_BYTES: usize = 1;
+const FEATURE_FLAG_AEAD: usize = 1;
#[cfg(any(test, feature = "quickcheck"))]
impl Arbitrary for Features {
@@ -222,15 +448,92 @@ mod tests {
quickcheck! {
fn roundtrip(val: Features) -> bool {
- let q = Features::new(&val.to_vec());
+ let mut q = Features::new(val.as_slice());
assert_eq!(val, q);
+ assert!(val.normalized_eq(&q));
+
+ // Add some padding to q. Make sure they are still equal.
+ q.raw.push(0);
+ assert!(val != q);
+ assert!(val.normalized_eq(&q));
- // Check that equality ignores padding.
- let mut val_without_padding = val.clone();
- val_without_padding.pad_to = val.unknown.len();
- assert_eq!(val, val_without_padding);
+ q.raw.push(0);
+ assert!(val != q);
+ assert!(val.normalized_eq(&q));
true
}
}
+
+ #[test]
+ fn set_clear() {
+ let a = Features::new(&[ 0x5, 0x1, 0x0, 0xff ]);
+ let b = Features::new(&[])
+ .set(0).set(2)
+ .set(8)
+ .set(24).set(25).set(26).set(27).set(28).set(29).set(30).set(31);
+ assert_eq!(a, b);
+
+ // Clear a bit and make sure they are not equal.
+ let b = b.clear(0);
+ assert!(a != b);
+ assert!(! a.normalized_eq(&b));
+ let b = b.set(0);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ let b = b.clear(8);
+ assert!(a != b);
+ assert!(! a.normalized_eq(&b));
+ let b = b.set(8);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ let b = b.clear(31);
+ assert!(a != b);
+ assert!(! a.normalized_eq(&b));
+ let b = b.set(31);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ // Add a bit.
+ let a = a.set(10);
+ assert!(a != b);
+ assert!(! a.normalized_eq(&b));
+ let b = b.set(10);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ let a = a.set(32);
+ assert!(a != b);
+ assert!(! a.normalized_eq(&b));
+ let b = b.set(32);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ let a = a.set(1000);
+ assert!(a != b);
+ assert!(! a.normalized_eq(&b));
+ let b = b.set(1000);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+ }
+
+ #[test]
+ fn known() {
+ let a = Features::empty().set_mdc();
+ let b = Features::new(&[ 0x1 ]);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ let a = Features::empty().set_aead();
+ let b = Features::new(&[ 0x2 ]);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+
+ let a = Features::empty().set_mdc().set_aead();
+ let b = Features::new(&[ 0x1 | 0x2 ]);
+ assert_eq!(a, b);
+ assert!(a.normalized_eq(&b));
+ }
}
diff --git a/openpgp/src/types/mod.rs b/openpgp/src/types/mod.rs
index 51f8acca..d98e2464 100644
--- a/openpgp/src/types/mod.rs
+++ b/openpgp/src/types/mod.rs
@@ -737,7 +737,7 @@ impl Arbitrary for SymmetricAlgorithm {
/// use openpgp::types::{Features, HashAlgorithm, AEADAlgorithm, SignatureType};
///
/// # fn main() -> openpgp::Result<()> {
-/// let features = Features::default().set_aead(true);
+/// let features = Features::default().set_aead();
/// let mut builder = SignatureBuilder::new(SignatureType::DirectKey)
/// .set_features(&features)?
/// .set_preferred_aead_algorithms(vec![