sequoia_openpgp/types/
features.rs

1use std::fmt;
2
3#[cfg(test)]
4use quickcheck::{Arbitrary, Gen};
5
6use crate::types::Bitfield;
7
8/// Describes the features supported by an OpenPGP implementation.
9///
10/// The feature flags are defined in [Section 5.2.3.32 of RFC 9580],
11/// and [Section 5.2.3.32 of draft-ietf-openpgp-crypto-refresh].
12///
13/// [Section 5.2.3.32 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3.32
14/// [Section 5.2.3.32 of draft-ietf-openpgp-crypto-refresh]: https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#features-subpacket
15///
16/// The feature flags are set by the user's OpenPGP implementation to
17/// signal to any senders what features the implementation supports.
18///
19/// # A note on equality
20///
21/// `PartialEq` compares the serialized form of the two feature sets.
22/// If you prefer to compare two feature sets for semantic equality,
23/// you should use [`Features::normalized_eq`].  The difference
24/// between semantic equality and serialized equality is that semantic
25/// equality ignores differences in the amount of padding.
26///
27///   [`Features::normalized_eq`]: Features::normalized_eq()
28///
29/// # Examples
30///
31/// ```
32/// use sequoia_openpgp as openpgp;
33/// # use openpgp::Result;
34/// use openpgp::cert::prelude::*;
35/// use openpgp::policy::StandardPolicy;
36///
37/// # fn main() -> Result<()> {
38/// let p = &StandardPolicy::new();
39///
40/// let (cert, _) =
41///     CertBuilder::general_purpose(Some("alice@example.org"))
42///     .generate()?;
43/// match cert.with_policy(p, None)?.primary_userid()?.features() {
44///     Some(features) => {
45///         println!("Certificate holder's supported features:");
46///         assert!(features.supports_seipdv1());
47///         assert!(features.supports_seipdv2());
48///     }
49///     None => {
50///         println!("Certificate Holder did not specify any features.");
51/// #       unreachable!();
52///     }
53/// }
54/// # Ok(()) }
55/// ```
56#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
57pub struct Features(Bitfield);
58assert_send_and_sync!(Features);
59
60impl fmt::Debug for Features {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        // Print known features first.
63        let mut need_comma = false;
64        if self.supports_seipdv1() {
65            f.write_str("SEIPDv1")?;
66            need_comma = true;
67        }
68        if self.supports_seipdv2() {
69            if need_comma { f.write_str(", ")?; }
70            f.write_str("SEIPDv2")?;
71            need_comma = true;
72        }
73
74        // Now print any unknown features.
75        for i in self.0.iter_set() {
76            match i {
77                FEATURE_FLAG_SEIPDV1 => (),
78                FEATURE_FLAG_SEIPDV2 => (),
79                i => {
80                    if need_comma { f.write_str(", ")?; }
81                    write!(f, "#{}", i)?;
82                    need_comma = true;
83                }
84            }
85        }
86
87        // Mention any padding, as equality is sensitive to this.
88        if let Some(padding) = self.0.padding_bytes() {
89            if need_comma { f.write_str(", ")?; }
90            write!(f, "+padding({} bytes)", padding)?;
91        }
92
93        Ok(())
94    }
95}
96
97impl Features {
98    /// Creates a new instance from `bytes`.
99    ///
100    /// This does not remove any trailing padding from `bytes`.
101    pub fn new<B>(bytes: B) -> Self
102        where B: AsRef<[u8]>
103    {
104        Features(bytes.as_ref().to_vec().into())
105    }
106
107    /// Returns an empty feature set.
108    pub fn empty() -> Self {
109        Self::new(&[][..])
110    }
111
112    /// Returns a feature set describing Sequoia's capabilities.
113    pub fn sequoia() -> Self {
114        let v : [u8; 1] = [ 0 ];
115
116        Self::new(&v[..])
117            .set_seipdv1()
118            .set_seipdv2()
119    }
120
121    /// Returns a reference to the underlying [`Bitfield`].
122    pub fn as_bitfield(&self) -> &Bitfield {
123        &self.0
124    }
125
126    /// Returns a mutable reference to the underlying [`Bitfield`].
127    pub fn as_bitfield_mut(&mut self) -> &mut Bitfield {
128        &mut self.0
129    }
130
131    /// Compares two feature sets for semantic equality.
132    ///
133    /// `Features` implementation of `PartialEq` compares two feature
134    /// sets for serialized equality.  That is, the `PartialEq`
135    /// implementation considers two feature sets to *not* be equal if
136    /// they have different amounts of padding.  This comparison
137    /// function ignores padding.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// use sequoia_openpgp as openpgp;
143    /// # use openpgp::Result;
144    /// use openpgp::types::Features;
145    ///
146    /// # fn main() -> Result<()> {
147    /// let a = Features::new(&[0x1]);
148    /// let b = Features::new(&[0x1, 0x0]);
149    ///
150    /// assert!(a != b);
151    /// assert!(a.normalized_eq(&b));
152    /// # Ok(()) }
153    /// ```
154    pub fn normalized_eq(&self, other: &Self) -> bool {
155        self.0.normalized_eq(&other.0)
156    }
157
158    /// Returns whether the specified feature flag is set.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// use sequoia_openpgp as openpgp;
164    /// # use openpgp::Result;
165    /// use openpgp::types::Features;
166    ///
167    /// # fn main() -> Result<()> {
168    /// // Feature flags 0 and 3.
169    /// let f = Features::new(&[0x9]);
170    ///
171    /// assert!(f.get(0));
172    /// assert!(! f.get(1));
173    /// assert!(! f.get(2));
174    /// assert!(f.get(3));
175    /// assert!(! f.get(4));
176    /// assert!(! f.get(8));
177    /// assert!(! f.get(80));
178    /// # Ok(()) }
179    /// ```
180    pub fn get(&self, bit: usize) -> bool {
181        self.0.get(bit)
182    }
183
184    /// Sets the specified feature flag.
185    ///
186    /// This also clears any padding (trailing NUL bytes).
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use sequoia_openpgp as openpgp;
192    /// # use openpgp::Result;
193    /// use openpgp::types::Features;
194    ///
195    /// # fn main() -> Result<()> {
196    /// let f = Features::empty().set(0).set(3);
197    ///
198    /// assert!(f.get(0));
199    /// assert!(! f.get(1));
200    /// assert!(! f.get(2));
201    /// assert!(f.get(3));
202    /// assert!(! f.get(4));
203    /// assert!(! f.get(8));
204    /// assert!(! f.get(80));
205    /// # Ok(()) }
206    /// ```
207    pub fn set(mut self, bit: usize) -> Self {
208        self.0.set(bit);
209        self.0.canonicalize();
210        self
211    }
212
213    /// Clears the specified feature flag.
214    ///
215    /// This also clears any padding (trailing NUL bytes).
216    ///
217    /// # Examples
218    ///
219    /// ```
220    /// use sequoia_openpgp as openpgp;
221    /// # use openpgp::Result;
222    /// use openpgp::types::Features;
223    ///
224    /// # fn main() -> Result<()> {
225    /// let f = Features::empty().set(0).set(3).clear(3);
226    ///
227    /// assert!(f.get(0));
228    /// assert!(! f.get(1));
229    /// assert!(! f.get(2));
230    /// assert!(! f.get(3));
231    /// # Ok(()) }
232    /// ```
233    pub fn clear(mut self, bit: usize) -> Self {
234        self.0.clear(bit);
235        self.0.canonicalize();
236        self
237    }
238
239    /// Returns whether the SEIPDv1 feature flag is set.
240    ///
241    /// # Examples
242    ///
243    /// ```
244    /// use sequoia_openpgp as openpgp;
245    /// # use openpgp::Result;
246    /// use openpgp::types::Features;
247    ///
248    /// # fn main() -> Result<()> {
249    /// let f = Features::empty();
250    ///
251    /// assert!(! f.supports_seipdv1());
252    /// # Ok(()) }
253    /// ```
254    pub fn supports_seipdv1(&self) -> bool {
255        self.get(FEATURE_FLAG_SEIPDV1)
256    }
257
258    /// Sets the SEIPDv1 feature flag.
259    ///
260    /// # Examples
261    ///
262    /// ```
263    /// use sequoia_openpgp as openpgp;
264    /// # use openpgp::Result;
265    /// use openpgp::types::Features;
266    ///
267    /// # fn main() -> Result<()> {
268    /// let f = Features::empty().set_seipdv1();
269    ///
270    /// assert!(f.supports_seipdv1());
271    /// # assert!(f.get(0));
272    /// # Ok(()) }
273    /// ```
274    pub fn set_seipdv1(self) -> Self {
275        self.set(FEATURE_FLAG_SEIPDV1)
276    }
277
278    /// Clears the SEIPDv1 feature flag.
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use sequoia_openpgp as openpgp;
284    /// # use openpgp::Result;
285    /// use openpgp::types::Features;
286    ///
287    /// # fn main() -> Result<()> {
288    /// let f = Features::new(&[0x1]);
289    /// assert!(f.supports_seipdv1());
290    ///
291    /// let f = f.clear_seipdv1();
292    /// assert!(! f.supports_seipdv1());
293    /// # Ok(()) }
294    /// ```
295    pub fn clear_seipdv1(self) -> Self {
296        self.clear(FEATURE_FLAG_SEIPDV1)
297    }
298
299    /// Returns whether the SEIPDv2 feature flag is set.
300    ///
301    /// # Examples
302    ///
303    /// ```
304    /// use sequoia_openpgp as openpgp;
305    /// # use openpgp::Result;
306    /// use openpgp::types::Features;
307    ///
308    /// # fn main() -> Result<()> {
309    /// let f = Features::empty();
310    ///
311    /// assert!(! f.supports_seipdv2());
312    /// # Ok(()) }
313    /// ```
314    pub fn supports_seipdv2(&self) -> bool {
315        self.get(FEATURE_FLAG_SEIPDV2)
316    }
317
318    /// Sets the SEIPDv2 feature flag.
319    ///
320    /// # Examples
321    ///
322    /// ```
323    /// use sequoia_openpgp as openpgp;
324    /// # use openpgp::Result;
325    /// use openpgp::types::Features;
326    ///
327    /// # fn main() -> Result<()> {
328    /// let f = Features::empty().set_seipdv2();
329    ///
330    /// assert!(f.supports_seipdv2());
331    /// # assert!(f.get(3));
332    /// # Ok(()) }
333    /// ```
334    pub fn set_seipdv2(self) -> Self {
335        self.set(FEATURE_FLAG_SEIPDV2)
336    }
337
338    /// Clears the SEIPDv2 feature flag.
339    ///
340    /// # Examples
341    ///
342    /// ```
343    /// use sequoia_openpgp as openpgp;
344    /// # use openpgp::Result;
345    /// use openpgp::types::Features;
346    ///
347    /// # fn main() -> Result<()> {
348    /// let f = Features::new(&[0x8]);
349    /// assert!(f.supports_seipdv2());
350    ///
351    /// let f = f.clear_seipdv2();
352    /// assert!(! f.supports_seipdv2());
353    /// # Ok(()) }
354    /// ```
355    pub fn clear_seipdv2(self) -> Self {
356        self.clear(FEATURE_FLAG_SEIPDV2)
357    }
358}
359
360/// Symmetrically Encrypted and Integrity Protected Data packet
361/// version 1.
362const FEATURE_FLAG_SEIPDV1: usize = 0;
363
364/// Symmetrically Encrypted and Integrity Protected Data packet
365/// version 2.
366const FEATURE_FLAG_SEIPDV2: usize = 3;
367
368#[cfg(test)]
369impl Arbitrary for Features {
370    fn arbitrary(g: &mut Gen) -> Self {
371        Self::new(Vec::arbitrary(g))
372    }
373}
374
375#[cfg(test)]
376mod tests {
377    use super::*;
378
379    quickcheck! {
380        fn roundtrip(val: Features) -> bool {
381            let mut q_bytes = val.as_bitfield().as_bytes().to_vec();
382            let q = Features::new(&q_bytes);
383            assert_eq!(val, q);
384            assert!(val.normalized_eq(&q));
385
386            // Add some padding to q.  Make sure they are still equal.
387            q_bytes.push(0);
388            let q = Features::new(&q_bytes);
389            assert!(val != q);
390            assert!(val.normalized_eq(&q));
391
392            q_bytes.push(0);
393            let q = Features::new(&q_bytes);
394            assert!(val != q);
395            assert!(val.normalized_eq(&q));
396
397            true
398        }
399    }
400
401    #[test]
402    fn set_clear() {
403        let a = Features::new(&[ 0x5, 0x1, 0x0, 0xff ]);
404        let b = Features::new(&[])
405            .set(0).set(2)
406            .set(8)
407            .set(24).set(25).set(26).set(27).set(28).set(29).set(30).set(31);
408        assert_eq!(a, b);
409
410        // Clear a bit and make sure they are not equal.
411        let b = b.clear(0);
412        assert!(a != b);
413        assert!(! a.normalized_eq(&b));
414        let b = b.set(0);
415        assert_eq!(a, b);
416        assert!(a.normalized_eq(&b));
417
418        let b = b.clear(8);
419        assert!(a != b);
420        assert!(! a.normalized_eq(&b));
421        let b = b.set(8);
422        assert_eq!(a, b);
423        assert!(a.normalized_eq(&b));
424
425        let b = b.clear(31);
426        assert!(a != b);
427        assert!(! a.normalized_eq(&b));
428        let b = b.set(31);
429        assert_eq!(a, b);
430        assert!(a.normalized_eq(&b));
431
432        // Add a bit.
433        let a = a.set(10);
434        assert!(a != b);
435        assert!(! a.normalized_eq(&b));
436        let b = b.set(10);
437        assert_eq!(a, b);
438        assert!(a.normalized_eq(&b));
439
440        let a = a.set(32);
441        assert!(a != b);
442        assert!(! a.normalized_eq(&b));
443        let b = b.set(32);
444        assert_eq!(a, b);
445        assert!(a.normalized_eq(&b));
446
447        let a = a.set(1000);
448        assert!(a != b);
449        assert!(! a.normalized_eq(&b));
450        let b = b.set(1000);
451        assert_eq!(a, b);
452        assert!(a.normalized_eq(&b));
453    }
454
455    #[test]
456    fn known() {
457        let a = Features::empty().set_seipdv1();
458        let b = Features::new(&[ 0x1 ]);
459        assert_eq!(a, b);
460        assert!(a.normalized_eq(&b));
461
462        let a = Features::empty().set_seipdv2();
463        let b = Features::new(&[ 0x8 ]);
464        assert_eq!(a, b);
465        assert!(a.normalized_eq(&b));
466
467        #[allow(deprecated)]
468        let a = Features::empty().set_seipdv1().set_seipdv2();
469        let b = Features::new(&[ 0x1 | 0x8 ]);
470        assert_eq!(a, b);
471        assert!(a.normalized_eq(&b));
472    }
473}