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}