sequoia_openpgp/policy.rs
1//! A mechanism to specify policy.
2//!
3//! A major goal of the Sequoia OpenPGP crate is to be policy free.
4//! However, many mid-level operations build on low-level primitives.
5//! For instance, finding a certificate's primary User ID means
6//! examining each of its User IDs and their current self-signature.
7//! Some algorithms are considered broken (e.g., MD5) and some are
8//! considered weak (e.g. SHA-1). When dealing with data from an
9//! untrusted source, for instance, callers will often prefer to
10//! ignore signatures that rely on these algorithms even though [RFC
11//! 4880] says that "\[i\]mplementations MUST implement SHA-1." When
12//! trying to decrypt old archives, however, users probably don't want
13//! to ignore keys using MD5, even though [Section 9.5 of RFC 9580]
14//! deprecates MD5.
15//!
16//! Rather than not provide this mid-level functionality, the `Policy`
17//! trait allows callers to specify their preferred policy. This can be
18//! highly customized by providing a custom implementation of the
19//! `Policy` trait, or it can be slightly refined by tweaking the
20//! `StandardPolicy`'s parameters.
21//!
22//! When implementing the `Policy` trait, it is *essential* that the
23//! functions are [pure]. That is, if the same `Policy` is used
24//! to determine whether a given `Signature` is valid, it must always
25//! return the same value.
26//!
27//! [Section 9.5 of RFC 9580]: https://www.rfc-editor.org/rfc/rfc9580.html#section-9.5
28//! [pure]: https://en.wikipedia.org/wiki/Pure_function
29use std::fmt;
30use std::time::{SystemTime, Duration};
31use std::u32;
32
33use anyhow::Context;
34
35use crate::{
36 cert::prelude::*,
37 Error,
38 Packet,
39 packet::{
40 key,
41 Signature,
42 signature::subpacket::{
43 SubpacketTag,
44 SubpacketValue,
45 },
46 Tag,
47 },
48 Result,
49 types,
50 types::{
51 AEADAlgorithm,
52 HashAlgorithm,
53 SignatureType,
54 SymmetricAlgorithm,
55 Timestamp,
56 },
57};
58
59#[macro_use] mod cutofflist;
60use cutofflist::{
61 CutoffList,
62 REJECT,
63 ACCEPT,
64 VersionedCutoffList,
65};
66
67/// A policy for cryptographic operations.
68pub trait Policy : fmt::Debug + Send + Sync {
69 /// Returns an error if the signature violates the policy.
70 ///
71 /// This function performs the last check before the library
72 /// decides that a signature is valid. That is, after the library
73 /// has determined that the signature is well-formed, alive, not
74 /// revoked, etc., it calls this function to allow you to
75 /// implement any additional policy. For instance, you may reject
76 /// signatures that make use of cryptographically insecure
77 /// algorithms like SHA-1.
78 ///
79 /// Note: Whereas it is generally better to reject suspicious
80 /// signatures, one should be more liberal when considering
81 /// revocations: if you reject a revocation certificate, it may
82 /// inadvertently make something else valid!
83 fn signature(&self, _sig: &Signature, _sec: HashAlgoSecurity) -> Result<()> {
84 Err(Error::PolicyViolation(
85 "By default all signatures are rejected.".into(), None).into())
86 }
87
88 /// Returns an error if the key violates the policy.
89 ///
90 /// This function performs one of the last checks before a
91 /// `KeyAmalgamation` or a related data structures is turned into
92 /// a `ValidKeyAmalgamation`, or similar.
93 ///
94 /// Internally, the library always does this before using a key.
95 /// The sole exception is when creating a key using `CertBuilder`.
96 /// In that case, the primary key is not validated before it is
97 /// used to create any binding signatures.
98 ///
99 /// Thus, you can prevent keys that make use of insecure
100 /// algorithms, don't have a sufficiently high security margin
101 /// (e.g., 1024-bit RSA keys), are on a bad list, etc. from being
102 /// used here.
103 ///
104 /// If you implement this function, make sure to consider the Key
105 /// Derivation Function and Key Encapsulation parameters of ECDH
106 /// keys, see [`PublicKey::ECDH`].
107 ///
108 /// [`PublicKey::ECDH`]: crate::crypto::mpi::PublicKey::ECDH
109 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
110 -> Result<()>
111 {
112 Err(Error::PolicyViolation(
113 "By default all keys are rejected.".into(), None).into())
114 }
115
116 /// Returns an error if the symmetric encryption algorithm
117 /// violates the policy.
118 ///
119 /// This function performs the last check before an encryption
120 /// container is decrypted by the streaming decryptor.
121 ///
122 /// With this function, you can prevent the use of insecure
123 /// symmetric encryption algorithms.
124 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
125 Err(Error::PolicyViolation(
126 "By default all symmetric algorithms are rejected.".into(), None).into())
127 }
128
129 /// Returns an error if the AEAD mode violates the policy.
130 ///
131 /// This function performs the last check before an encryption
132 /// container is decrypted by the streaming decryptor.
133 ///
134 /// With this function, you can prevent the use of insecure AEAD
135 /// constructions.
136 ///
137 /// This feature is [experimental](super#experimental-features).
138 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
139 Err(Error::PolicyViolation(
140 "By default all AEAD algorithms are rejected.".into(), None).into())
141 }
142
143 /// Returns an error if the packet violates the policy.
144 ///
145 /// This function performs the last check before a packet is
146 /// considered by the streaming verifier and decryptor.
147 ///
148 /// With this function, you can prevent the use of insecure
149 /// encryption containers, notably the *Symmetrically Encrypted
150 /// Data Packet*.
151 fn packet(&self, _packet: &Packet) -> Result<()> {
152 Err(Error::PolicyViolation(
153 "By default all packets are rejected.".into(), None).into())
154 }
155}
156
157/// Whether the signed data requires a hash algorithm with collision
158/// resistance.
159///
160/// Since the context of a signature is not passed to
161/// `Policy::signature`, it is not possible to determine from that
162/// function whether the signature requires a hash algorithm with
163/// collision resistance. This enum indicates this.
164///
165/// In short, many self signatures only require second pre-image
166/// resistance. This can be used to extend the life of hash
167/// algorithms whose collision resistance has been partially
168/// compromised. Be careful. Read the background and the warning
169/// before accepting the use of weak hash algorithms!
170///
171/// # Warning
172///
173/// Although distinguishing whether signed data requires collision
174/// resistance can be used to permit the continued use of a hash
175/// algorithm in certain situations, once attacks against a hash
176/// algorithm are known, it is imperative to retire the use of the
177/// hash algorithm as soon as it is feasible. Cryptoanalytic attacks
178/// improve quickly, as demonstrated by the attacks on SHA-1.
179///
180/// # Background
181///
182/// Cryptographic hash functions normally have three security
183/// properties:
184///
185/// - Pre-image resistance,
186/// - Second pre-image resistance, and
187/// - Collision resistance.
188///
189/// A hash algorithm has pre-image resistance if given a hash `h`, it
190/// is impractical for an attacker to find a message `m` such that `h
191/// = hash(m)`. In other words, a hash algorithm has pre-image
192/// resistance if it is hard to invert. A hash algorithm has second
193/// pre-image resistance if it is impractical for an attacker to find
194/// a second message with the same hash as the first. That is, given
195/// `m1`, it is hard for an attacker to find an `m2` such that
196/// `hash(m1) = hash(m2)`. And, a hash algorithm has collision
197/// resistance if it is impractical for an attacker to find two
198/// messages with the same hash. That is, it is hard for an attacker
199/// to find an `m1` and an `m2` such that `hash(m1) = hash(m2)`.
200///
201/// In the context of verifying an OpenPGP signature, we don't need a
202/// hash algorithm with pre-image resistance. Pre-image resistance is
203/// only required when the message is a secret, e.g., a password. We
204/// always need a hash algorithm with second pre-image resistance,
205/// because an attacker must not be able to repurpose an arbitrary
206/// signature, i.e., create a collision with respect to a *known*
207/// hash. And, we need collision resistance when a signature is over
208/// data that could have been influenced by an attacker: if an
209/// attacker creates a pair of colliding messages and convinces the
210/// user to sign one of them, then the attacker can copy the signature
211/// to the other message.
212///
213/// Collision resistance implies second pre-image resistance, but not
214/// vice versa. If an attacker can find a second message with the
215/// same hash as some known message, they can also create a collision
216/// by choosing an arbitrary message and using their pre-image attack
217/// to find a colliding message. Thus, a context that requires
218/// collision resistance also requires second pre-image resistance.
219///
220/// Because collision resistance is with respect to two arbitrary
221/// messages, collision resistance is always susceptible to a
222/// [birthday paradox]. This means that the security margin of a hash
223/// algorithm's collision resistance is half of the security margin of
224/// its second pre-image resistance. And, in practice, the collision
225/// resistance of industry standard hash algorithms has been
226/// practically attacked multiple times. In the context of SHA-1,
227/// Wang et al. described how to find collisions in SHA-1 in their
228/// 2005 paper [Finding Collisions in the Full SHA-1]. In 2017,
229/// Stevens et al. published [The First Collision for Full SHA-1],
230/// which demonstrates the first practical attack on SHA-1's collision
231/// resistance, an identical-prefix collision attack. This attack
232/// only gives the attacker limited control over the content of the
233/// collided messages, which limits its applicability. However, in
234/// 2020, Leurent and Peyrin published [SHA-1 is a Shambles], which
235/// demonstrates a practical chosen-prefix collision attack. This
236/// attack gives the attacker complete control over the prefixes of
237/// the collided messages.
238///
239/// [birthday paradox]: https://en.wikipedia.org/wiki/Birthday_attack#Digital_signature_susceptibility
240/// [Finding Collisions in the Full SHA-1]: https://link.springer.com/chapter/10.1007/11535218_2
241/// [The first collision for full SHA-1]: https://shattered.io/
242/// [SHA-1 is a Shambles]: https://sha-mbles.github.io/
243///
244/// A chosen-prefix collision attack works as follows: an attacker
245/// chooses two arbitrary message prefixes, and then searches for
246/// so-called near collision blocks. These near collision blocks
247/// cause the internal state of the hashes to converge and eventually
248/// result in a collision, i.e., an identical hash value. The attack
249/// described in the [SHA-1 is a Shambles] paper requires 8 to 10 near
250/// collision blocks (512 to 640 bytes) to fully synchronize the
251/// internal state.
252///
253/// SHA-1 is a [Merkle-Damgård hash function]. This means that the
254/// hash function processes blocks one after the other, and the
255/// internal state of the hash function at any given point only
256/// depends on earlier blocks in the stream. A consequence of this is
257/// that it is possible to append a common suffix to the collided
258/// messages without any additional computational effort. That is, if
259/// `hash(m1) = hash(m2)`, then it necessarily holds that `hash(m1 ||
260/// suffix) = hash(m2 || suffix)`. This is called a [length extension
261/// attack].
262///
263/// [Merkle-Damgård hash function]: https://en.wikipedia.org/wiki/Merkle%E2%80%93Damg%C3%A5rd_construction
264/// [length extension attack]: https://en.wikipedia.org/wiki/Length_extension_attack
265///
266/// Thus, the [SHA-1 is a Shambles] attack solves the following:
267///
268/// ```text
269/// hash(m1 || collision blocks 1 || suffix) = hash(m2 || collision blocks 2 || suffix)
270/// ```
271///
272/// Where `m1`, `m2`, and `suffix` are controlled by the attacker, and
273/// only the collision blocks are controlled by the algorithm.
274///
275/// If an attacker can convince an OpenPGP user to sign a message of
276/// their choosing (some `m1 || collision blocks 1 || suffix`), then
277/// the attacker also has a valid signature from the victim for a
278/// colliding message (some `m2 || collision blocks 2 || suffix`).
279///
280/// The OpenPGP format imposes some additional constraints on the
281/// attacker. Although the attacker may control the message, the
282/// signature is also over a [signature packet], and a trailer.
283/// Specifically, [the following is signed] when signing a document:
284///
285/// ```text
286/// hash(document || sig packet || 0x04 || sig packet len)
287/// ```
288///
289/// and the [following is signed] when signing a binding signature:
290///
291/// ```text
292/// hash(public key || subkey || sig packet || 0x04 || sig packet len)
293/// ```
294///
295/// [signature packet]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.3
296/// [the following is signed]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.4
297///
298/// Since the signature packet is chosen by the victim's OpenPGP
299/// implementation, the attacker may be able to predict it, but they
300/// cannot store the collision blocks there. Thus, the signature
301/// packet is necessarily part of the common suffix, and the collision
302/// blocks must occur earlier in the stream.
303///
304/// This restriction on the signature packet means that an attacker
305/// cannot convince the victim to sign a document, and then transfer
306/// that signature to a colliding binding signature. These signatures
307/// necessarily have different [signature packet]s: the value of the
308/// [signature type] field is different. And, as just described, for
309/// this attack, the signature packets must be identical, because they
310/// are part of the common suffix. Finally, the trailer, which
311/// contains the signature packet's length, prevents hiding a
312/// signature in a signature.
313///
314/// [signature type]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.2.1
315///
316/// Given this, if we know for a given signature type that an attacker
317/// cannot control any of the data that is signed, then that type of
318/// signature does not need collision resistance; it is still
319/// vulnerable to an attack on the hash's second pre-image resistance
320/// (a collision with a specific message), but not one on its
321/// collision resistance (a collision with any message). This is the
322/// case for binding signatures, and direct key signatures. But, it
323/// is not normally the case for documents (the attacker may be able
324/// to control the content of the document), certifications (the
325/// attacker may be able to control the key packet, the User ID
326/// packet, or the User Attribute packet), or certificate revocations
327/// (the attacker may be able to control the key packet).
328///
329/// Certification signatures and revocations signatures can be further
330/// divided into self signatures and third-party signatures. If an
331/// attacker can convince a victim into signing a third-party
332/// signature, as was done in the [SHA-1 is a Shambles], they may be
333/// able to transfer the signature to a colliding self signature. If
334/// we can show that an attacker can't collide a self signature, and a
335/// third-party signature, then we may be able to show that self
336/// signatures don't require collision resistance. The same
337/// consideration holds for revocations and third-party revocations.
338///
339/// We first consider revocations, which are more straightforward.
340/// The attack is the following: an attacker creates a fake
341/// certificate (A), and sets the victim as a designated revoker.
342/// They then ask the victim to revoke their certificate (V). The
343/// attacker than transfers the signature to a colliding self
344/// revocation, which causes the victim's certificate (V) to be
345/// revoked.
346///
347/// A revocation is over a public key packet and a signature packet.
348/// In this scenario, the attacker controls the fake certificate (A)
349/// and thus the public key packet that the victim actually signs.
350/// But the victim's public key packet is determined by their
351/// certificate (V). Thus, the attacker would have to insert the near
352/// collision blocks in the signature packet, which, as we argued
353/// before, is not possible. Thus, it is safe to only use a hash with
354/// pre-image resistance to protect a self-revocation.
355///
356/// We now turn to self signatures. The attack is similar to the
357/// [SHA-1 is a Shambles] attack. An attacker creates a certificate
358/// (A) and convinces the victim to sign it. The attacker can then
359/// transfer the third-party certification to a colliding self
360/// signature for the victim's certificate (V). If successful, this
361/// attack allows the attacker to add a User ID or a User Attribute to
362/// the victim's certificate (V). This can confuse people who use the
363/// victim's certificate. For instance, if the attacker adds the
364/// identity `alice@example.org` to the victim's certificate, and Bob
365/// receives a message signed using the victim's certificate (V), he
366/// may think that Alice signed the message instead of the victim.
367/// Bob won't be tricked if he uses strong authentication, but many
368/// OpenPGP users use weak authentication (e.g., TOFU) or don't
369/// authenticate keys at all.
370///
371/// A certification is over a public key packet, a User ID or User
372/// Attribute packet, and a signature packet. The attacker controls
373/// the fake certificate (A) and therefore the public key packet, and
374/// the User ID or User Attribute packet that the victim signs.
375/// However, to trick the victim, the User ID packet or User Attribute
376/// packet needs to correspond to an identity that the attacker
377/// appears to control. Thus, if the near collision blocks are stored
378/// in the User ID or User Attribute packet of A, they have to be
379/// hidden to avoid making the victim suspicious. This is
380/// straightforward for User Attributes, which are currently images,
381/// and have many places to hide this type of data. However, User IDs
382/// are normally [UTF-8 encoded RFC 2822 mailbox]es, which makes
383/// hiding half a kilobyte of binary data impractical. The attacker
384/// does not control the victim's public key (in V). But, they do
385/// control the malicious User ID or User Attribute that they want to
386/// attack to the victim's certificate (V). But again, the near
387/// collision blocks have to be hidden in order to trick Bob, the
388/// second victim. Thus, the attack has two possibilities: they can
389/// hide the near collision blocks in the fake public key (in A), and
390/// the User ID or User Attribute (added to V); or, they can hide them
391/// in the fake User IDs or User Attributes (in A and the one added to
392/// V).
393///
394/// As evidenced by the [SHA-1 is a Shambles] attack, it is possible
395/// to hide near collision blocks in User Attribute packets. Thus,
396/// this attack can be used to transfer a third-party certification
397/// over a User Attribute to a self signature over a User Attribute.
398/// As such, self signatures over User Attributes need collision
399/// resistance.
400///
401/// The final case to consider is hiding the near collision blocks in
402/// the User ID that the attacker wants to add to the victim's
403/// certificate. Again, it is possible to store the near collision
404/// blocks there. However, there are two mitigating factors. First,
405/// there is no place to hide the blocks. As such, the user must be
406/// convinced to ignore them. Second, a User ID is structure: it
407/// normally contains a [UTF-8 encoded RFC 2822 mailbox]. Thus, if we
408/// only consider valid UTF-8 strings, and limit the maximum size, we
409/// can dramatically increase the workfactor, which can extend the life
410/// of a hash algorithm whose collision resistance has been weakened.
411///
412/// [UTF-8 encoded RFC 2822 mailbox]: https://www.rfc-editor.org/rfc/rfc9580.html#section-5.11
413#[derive(Debug, Clone, Copy, Eq, PartialEq)]
414pub enum HashAlgoSecurity {
415 /// The signed data only requires second pre-image resistance.
416 ///
417 /// If a signature is over data that an attacker cannot influence,
418 /// then the hash function does not need to provide collision
419 /// resistance. This is **only** the case for:
420 ///
421 /// - Subkey binding signatures
422 /// - Primary key binding signatures
423 /// - Self revocations
424 ///
425 /// Due to the structure of User IDs (they are normally short,
426 /// UTF-8 encoded RFC 2822 mailboxes), self signatures over short,
427 /// reasonable User IDs (**not** User Attributes) also don't
428 /// require strong collision resistance. Thus, we also only
429 /// require a signature with second pre-image resistance for:
430 ///
431 /// - Self signatures over reasonable User IDs
432 SecondPreImageResistance,
433 /// The signed data requires collision resistance.
434 ///
435 /// If a signature is over data that an attacker can influence,
436 /// then the hash function must provide collision resistance.
437 /// This is the case for documents, third-party certifications,
438 /// and third-party revocations.
439 ///
440 /// Note: collision resistance implies second pre-image
441 /// resistance. Thus, when evaluating whether a hash algorithm
442 /// has collision resistance, we also check whether it has second
443 /// pre-image resistance.
444 CollisionResistance,
445}
446
447impl Default for HashAlgoSecurity {
448 /// The default is the most conservative policy.
449 fn default() -> Self {
450 HashAlgoSecurity::CollisionResistance
451 }
452}
453
454/// The standard policy.
455///
456/// The standard policy stores when each algorithm in a family of
457/// algorithms is no longer considered safe. Attempts to use an
458/// algorithm after its cutoff time should fail.
459///
460/// A `StandardPolicy` can be configured using Rust. Sometimes it is
461/// useful to configure it via a configuration file. This can be done
462/// using the [`sequoia-policy-config`] crate.
463///
464/// [`sequoia-policy-config`]: https://docs.rs/sequoia-policy-config/latest/sequoia_policy_config/
465///
466/// It is recommended to support using a configuration file when the
467/// program should respect the system's crypto policy. This is
468/// required on Fedora, for instance. See the [Fedora Crypto
469/// Policies] project for more information.
470///
471/// [Fedora]: https://gitlab.com/redhat-crypto/fedora-crypto-policies
472///
473/// When validating a signature, we normally want to know whether the
474/// algorithms used are safe *now*. That is, we don't use the
475/// signature's alleged creation time when considering whether an
476/// algorithm is safe, because if an algorithm is discovered to be
477/// compromised at time X, then an attacker could forge a message
478/// after time X with a signature creation time that is prior to X,
479/// which would be incorrectly accepted.
480///
481/// Occasionally, we know that a signature has not been tampered with
482/// since some time in the past. We might know this if the signature
483/// was stored on some tamper-proof medium. In those cases, it is
484/// reasonable to use the time that the signature was saved, since an
485/// attacker could not have taken advantage of any weaknesses found
486/// after that time.
487///
488/// # Examples
489///
490/// A `StandardPolicy` object can be used to build specialized policies.
491/// For example the following policy filters out Persona certifications mimicking
492/// what GnuPG does when calculating the Web of Trust.
493///
494/// ```rust
495/// use sequoia_openpgp as openpgp;
496/// use std::io::{Cursor, Read};
497/// use openpgp::Result;
498/// use openpgp::packet::{Packet, Signature, key::PublicParts};
499/// use openpgp::cert::prelude::*;
500/// use openpgp::parse::Parse;
501/// use openpgp::armor::{Reader, ReaderMode, Kind};
502/// use openpgp::policy::{HashAlgoSecurity, Policy, StandardPolicy};
503/// use openpgp::types::{
504/// SymmetricAlgorithm,
505/// AEADAlgorithm,
506/// SignatureType
507/// };
508///
509/// #[derive(Debug)]
510/// struct RejectPersonaCertificationsPolicy<'a>(StandardPolicy<'a>);
511///
512/// impl Policy for RejectPersonaCertificationsPolicy<'_> {
513/// fn key(&self, ka: &ValidErasedKeyAmalgamation<PublicParts>)
514/// -> Result<()>
515/// {
516/// self.0.key(ka)
517/// }
518///
519/// fn signature(&self, sig: &Signature, sec: HashAlgoSecurity) -> Result<()> {
520/// if sig.typ() == SignatureType::PersonaCertification {
521/// Err(anyhow::anyhow!("Persona certifications are ignored."))
522/// } else {
523/// self.0.signature(sig, sec)
524/// }
525/// }
526///
527/// fn symmetric_algorithm(&self, algo: SymmetricAlgorithm) -> Result<()> {
528/// self.0.symmetric_algorithm(algo)
529/// }
530///
531/// fn aead_algorithm(&self, algo: AEADAlgorithm) -> Result<()> {
532/// self.0.aead_algorithm(algo)
533/// }
534///
535/// fn packet(&self, packet: &Packet) -> Result<()> {
536/// self.0.packet(packet)
537/// }
538/// }
539///
540/// impl RejectPersonaCertificationsPolicy<'_> {
541/// fn new() -> Self {
542/// Self(StandardPolicy::new())
543/// }
544/// }
545///
546/// # fn main() -> Result<()> {
547/// // this key has one persona certification
548/// let data = r#"
549/// -----BEGIN PGP PUBLIC KEY BLOCK-----
550///
551/// mDMEX7JGrxYJKwYBBAHaRw8BAQdASKGcnowaZBDc2Z3rZZlWb6jEjne9sK76afbJ
552/// trd5Uw+0BlRlc3QgMoiQBBMWCAA4FiEEyZ6oBYFia3z+ooCBqR9BqiGp8AQFAl+y
553/// Rq8CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQqR9BqiGp8ASfxwEAvEb0
554/// bFr7ZgFZSDOITNptm+FEynib8mmLACsvHAmCjvIA+gOaSNyxMW6N59q7/j0sDjp1
555/// aYNgpNFLbYBZpkXXVL0GiHUEERYIAB0WIQTE4QfdkkisIbWVOcHmlsuS3dbWEwUC
556/// X7JG4gAKCRDmlsuS3dbWExEwAQCpqfiVMhjDwVFMsMpwd5r0N/8rAx8/nmgpCsK3
557/// M9TUrAD7BhTYVPRbkJqTZYd9DlLtBcbF3yNPTHlB+F2sFjI+cgo=
558/// =ZfYu
559/// -----END PGP PUBLIC KEY BLOCK-----
560/// "#;
561///
562/// let mut cursor = Cursor::new(&data);
563/// let mut reader = Reader::from_reader(&mut cursor, ReaderMode::Tolerant(Some(Kind::PublicKey)));
564///
565/// let mut buf = Vec::new();
566/// reader.read_to_end(&mut buf)?;
567/// let cert = Cert::from_bytes(&buf)?;
568///
569/// let ref sp = StandardPolicy::new();
570/// let u = cert.with_policy(sp, None)?.userids().nth(0).unwrap();
571///
572/// // Under the standard policy the persona certification is visible.
573/// assert_eq!(u.certifications().count(), 1);
574///
575/// // Under our custom policy the persona certification is not available.
576/// let ref p = RejectPersonaCertificationsPolicy::new();
577/// assert_eq!(u.with_policy(p, None)?.certifications().count(), 0);
578/// #
579/// # Ok(())
580/// # }
581/// ```
582#[derive(Clone, Debug)]
583pub struct StandardPolicy<'a> {
584 // The time. If None, the current time is used.
585 time: Option<Timestamp>,
586
587 // Hash algorithms.
588 collision_resistant_hash_algos:
589 CollisionResistantHashCutoffList,
590 second_pre_image_resistant_hash_algos:
591 SecondPreImageResistantHashCutoffList,
592 hash_revocation_tolerance: types::Duration,
593
594 // Critical subpacket tags.
595 critical_subpackets: SubpacketTagCutoffList,
596
597 // Critical notation good-list.
598 good_critical_notations: &'a [&'a str],
599
600 // Packet types.
601 packet_tags: PacketTagCutoffList,
602
603 // Symmetric algorithms.
604 symmetric_algos: SymmetricAlgorithmCutoffList,
605
606 // AEAD algorithms.
607 aead_algos: AEADAlgorithmCutoffList,
608
609 // Asymmetric algorithms.
610 asymmetric_algos: AsymmetricAlgorithmCutoffList,
611}
612
613assert_send_and_sync!(StandardPolicy<'_>);
614
615impl<'a> Default for StandardPolicy<'a> {
616 fn default() -> Self {
617 Self::new()
618 }
619}
620
621impl<'a> From<&'a StandardPolicy<'a>> for Option<&'a dyn Policy> {
622 fn from(p: &'a StandardPolicy<'a>) -> Self {
623 Some(p as &dyn Policy)
624 }
625}
626
627// Signatures that require a hash with collision Resistance and second
628// Pre-image Resistance. See the documentation for HashAlgoSecurity
629// for more details.
630a_cutoff_list!(CollisionResistantHashCutoffList, HashAlgorithm, 15,
631 [
632 REJECT, // 0. Not assigned.
633 Some(Timestamp::Y1997M2), // 1. MD5
634 Some(Timestamp::Y2013M2), // 2. SHA-1
635 Some(Timestamp::Y2013M2), // 3. RIPE-MD/160
636 REJECT, // 4. Reserved.
637 REJECT, // 5. Reserved.
638 REJECT, // 6. Reserved.
639 REJECT, // 7. Reserved.
640 ACCEPT, // 8. SHA256
641 ACCEPT, // 9. SHA384
642 ACCEPT, // 10. SHA512
643 ACCEPT, // 11. SHA224
644 ACCEPT, // 12. SHA3-256
645 REJECT, // 13. Reserved.
646 ACCEPT, // 14. SHA3-512
647 ]);
648// Signatures that *only* require a hash with Second Pre-image
649// Resistance. See the documentation for HashAlgoSecurity for more
650// details.
651a_cutoff_list!(SecondPreImageResistantHashCutoffList, HashAlgorithm, 15,
652 [
653 REJECT, // 0. Not assigned.
654 Some(Timestamp::Y2004M2), // 1. MD5
655 Some(Timestamp::Y2023M2), // 2. SHA-1
656 Some(Timestamp::Y2013M2), // 3. RIPE-MD/160
657 REJECT, // 4. Reserved.
658 REJECT, // 5. Reserved.
659 REJECT, // 6. Reserved.
660 REJECT, // 7. Reserved.
661 ACCEPT, // 8. SHA256
662 ACCEPT, // 9. SHA384
663 ACCEPT, // 10. SHA512
664 ACCEPT, // 11. SHA224
665 ACCEPT, // 12. SHA3-256
666 REJECT, // 13. Reserved.
667 ACCEPT, // 14. SHA3-512
668 ]);
669
670a_cutoff_list!(SubpacketTagCutoffList, SubpacketTag, 40,
671 [
672 REJECT, // 0. Reserved.
673 REJECT, // 1. Reserved.
674 ACCEPT, // 2. SignatureCreationTime.
675 ACCEPT, // 3. SignatureExpirationTime.
676 ACCEPT, // 4. ExportableCertification.
677 ACCEPT, // 5. TrustSignature.
678 ACCEPT, // 6. RegularExpression.
679 // Note: Even though we don't explicitly honor the
680 // Revocable flag, we don't support signature
681 // revocations, hence it is safe to ACCEPT it.
682 ACCEPT, // 7. Revocable.
683 REJECT, // 8. Reserved.
684 ACCEPT, // 9. KeyExpirationTime.
685 REJECT, // 10. PlaceholderForBackwardCompatibility.
686 ACCEPT, // 11. PreferredSymmetricAlgorithms.
687 ACCEPT, // 12. RevocationKey.
688 REJECT, // 13. Reserved.
689 REJECT, // 14. Reserved.
690 REJECT, // 15. Reserved.
691 ACCEPT, // 16. Issuer.
692 REJECT, // 17. Reserved.
693 REJECT, // 18. Reserved.
694 REJECT, // 19. Reserved.
695 ACCEPT, // 20. NotationData.
696 ACCEPT, // 21. PreferredHashAlgorithms.
697 ACCEPT, // 22. PreferredCompressionAlgorithms.
698 ACCEPT, // 23. KeyServerPreferences.
699 ACCEPT, // 24. PreferredKeyServer.
700 ACCEPT, // 25. PrimaryUserID.
701 ACCEPT, // 26. PolicyURI.
702 ACCEPT, // 27. KeyFlags.
703 ACCEPT, // 28. SignersUserID.
704 ACCEPT, // 29. ReasonForRevocation.
705 ACCEPT, // 30. Features.
706 REJECT, // 31. SignatureTarget.
707 ACCEPT, // 32. EmbeddedSignature.
708 ACCEPT, // 33. IssuerFingerprint.
709 REJECT, // 34. Reserved (PreferredAEADAlgorithms).
710 ACCEPT, // 35. IntendedRecipient.
711 REJECT, // 36. Reserved.
712 ACCEPT, // 37. ApprovedCertifications.
713 REJECT, // 38. Reserved.
714 ACCEPT, // 39. PreferredAEADCiphersuites.
715 ]);
716
717a_cutoff_list!(AsymmetricAlgorithmCutoffList, AsymmetricAlgorithm, 31,
718 [
719 Some(Timestamp::Y2014M2), // 0. RSA1024.
720 ACCEPT, // 1. RSA2048.
721 ACCEPT, // 2. RSA3072.
722 ACCEPT, // 3. RSA4096.
723 Some(Timestamp::Y2014M2), // 4. ElGamal1024.
724 Some(Timestamp::Y2025M2), // 5. ElGamal2048.
725 Some(Timestamp::Y2025M2), // 6. ElGamal3072.
726 Some(Timestamp::Y2025M2), // 7. ElGamal4096.
727 Some(Timestamp::Y2014M2), // 8. DSA1024.
728 Some(Timestamp::Y2030M2), // 9. DSA2048.
729 Some(Timestamp::Y2030M2), // 10. DSA3072.
730 Some(Timestamp::Y2030M2), // 11. DSA4096.
731 ACCEPT, // 12. NistP256.
732 ACCEPT, // 13. NistP384.
733 ACCEPT, // 14. NistP521.
734 ACCEPT, // 15. BrainpoolP256.
735 ACCEPT, // 16. BrainpoolP384.
736 ACCEPT, // 17. BrainpoolP512.
737 ACCEPT, // 18. Cv25519.
738 ACCEPT, // 19. X25519.
739 ACCEPT, // 20. X448.
740 ACCEPT, // 21. Ed25519.
741 ACCEPT, // 22. Ed448.
742 ACCEPT, // 23. EdDSA (i.e., Legacy Ed25519).
743 ACCEPT, // 24. MLDSA65_Ed25519.
744 ACCEPT, // 25. MLDSA87_Ed448.
745 ACCEPT, // 26. SLHDSA128s.
746 ACCEPT, // 27. SLHDSA128f.
747 ACCEPT, // 28. SLHDSA256s.
748 ACCEPT, // 29. MLKEM768_X25519.
749 ACCEPT, // 30. MLKEM1024_X448.
750 ]);
751
752a_cutoff_list!(SymmetricAlgorithmCutoffList, SymmetricAlgorithm, 14,
753 [
754 REJECT, // 0. Unencrypted.
755 Some(Timestamp::Y2025M2), // 1. IDEA.
756 Some(Timestamp::Y2017M2), // 2. TripleDES.
757 Some(Timestamp::Y2025M2), // 3. CAST5.
758 ACCEPT, // 4. Blowfish.
759 REJECT, // 5. Reserved.
760 REJECT, // 6. Reserved.
761 ACCEPT, // 7. AES128.
762 ACCEPT, // 8. AES192.
763 ACCEPT, // 9. AES256.
764 ACCEPT, // 10. Twofish.
765 ACCEPT, // 11. Camellia128.
766 ACCEPT, // 12. Camellia192.
767 ACCEPT, // 13. Camellia256.
768 ]);
769
770a_cutoff_list!(AEADAlgorithmCutoffList, AEADAlgorithm, 4,
771 [
772 REJECT, // 0. Reserved.
773 ACCEPT, // 1. EAX.
774 ACCEPT, // 2. OCB.
775 ACCEPT, // 3. GCM.
776 ]);
777
778a_versioned_cutoff_list!(PacketTagCutoffList, Tag, 22,
779 [
780 REJECT, // 0. Reserved.
781 ACCEPT, // 1. PKESK.
782 ACCEPT, // 2. Signature.
783 ACCEPT, // 3. SKESK.
784 ACCEPT, // 4. OnePassSig.
785 ACCEPT, // 5. SecretKey.
786 ACCEPT, // 6. PublicKey.
787 ACCEPT, // 7. SecretSubkey.
788 ACCEPT, // 8. CompressedData.
789 Some(Timestamp::Y2004M2), // 9. SED.
790 ACCEPT, // 10. Marker.
791 ACCEPT, // 11. Literal.
792 ACCEPT, // 12. Trust.
793 ACCEPT, // 13. UserID.
794 ACCEPT, // 14. PublicSubkey.
795 REJECT, // 15. Not assigned.
796 REJECT, // 16. Not assigned.
797 ACCEPT, // 17. UserAttribute.
798 ACCEPT, // 18. SEIP.
799 ACCEPT, // 19. MDC.
800 REJECT, // 20. "v5" AED.
801 ACCEPT, // 21. Padding.
802 ],
803 // The versioned list overrides the unversioned list. So we only
804 // need to tweak the above.
805 //
806 // Note: this list must be sorted and the tag and version must be unique!
807 2,
808 [
809 (Tag::Signature, 3, Some(Timestamp::Y2021M2)),
810 (Tag::Signature, 5, REJECT), // "v5" Signatures.
811 ]);
812
813// We need to convert a `SystemTime` to a `Timestamp` in
814// `StandardPolicy::reject_hash_at`. Unfortunately, a `SystemTime`
815// can represent a larger range of time than a `Timestamp` can. Since
816// the times passed to this function are cutoff points, and we only
817// compare them to OpenPGP timestamps, any `SystemTime` that is prior
818// to the Unix Epoch is equivalent to the Unix Epoch: it will reject
819// all timestamps. Similarly, any `SystemTime` that is later than the
820// latest time representable by a `Timestamp` is equivalent to
821// accepting all time stamps, which is equivalent to passing None.
822fn system_time_cutoff_to_timestamp(t: SystemTime) -> Option<Timestamp> {
823 let t = t
824 .duration_since(SystemTime::UNIX_EPOCH)
825 // An error can only occur if the SystemTime is less than the
826 // reference time (SystemTime::UNIX_EPOCH). Map that to
827 // SystemTime::UNIX_EPOCH, as above.
828 .unwrap_or_else(|_| Duration::new(0, 0));
829 let t = t.as_secs();
830 if t > u32::MAX as u64 {
831 // Map to None, as above.
832 None
833 } else {
834 Some((t as u32).into())
835 }
836}
837
838impl<'a> StandardPolicy<'a> {
839 /// Instantiates a new `StandardPolicy` with the default parameters.
840 pub const fn new() -> Self {
841 const EMPTY_LIST: &[&str] = &[];
842 Self {
843 time: None,
844 collision_resistant_hash_algos:
845 CollisionResistantHashCutoffList::Default(),
846 second_pre_image_resistant_hash_algos:
847 SecondPreImageResistantHashCutoffList::Default(),
848 // There are 365.2425 days in a year. Use a reasonable
849 // approximation.
850 hash_revocation_tolerance:
851 types::Duration::seconds((7 * 365 + 2) * 24 * 60 * 60),
852 critical_subpackets: SubpacketTagCutoffList::Default(),
853 good_critical_notations: EMPTY_LIST,
854 asymmetric_algos: AsymmetricAlgorithmCutoffList::Default(),
855 symmetric_algos: SymmetricAlgorithmCutoffList::Default(),
856 aead_algos: AEADAlgorithmCutoffList::Default(),
857 packet_tags: PacketTagCutoffList::Default(),
858 }
859 }
860
861 /// Instantiates a new `StandardPolicy` with parameters
862 /// appropriate for `time`.
863 ///
864 /// `time` is a meta-parameter that selects a security profile
865 /// that is appropriate for the given point in time. When
866 /// evaluating an object, the reference time should be set to the
867 /// time that the object was stored to non-tamperable storage.
868 /// Since most applications don't record when they received an
869 /// object, they should conservatively use the current time.
870 ///
871 /// Note that the reference time is a security parameter and is
872 /// different from the time that the object was allegedly created.
873 /// Consider evaluating a signature whose `Signature Creation
874 /// Time` subpacket indicates that it was created in 2007. Since
875 /// the subpacket is under the control of the sender, setting the
876 /// reference time according to the subpacket means that the
877 /// sender chooses the security profile. If the sender were an
878 /// attacker, she could have forged this to take advantage of
879 /// security weaknesses found since 2007. This is why the
880 /// reference time must be set---at the earliest---to the time
881 /// that the message was stored to non-tamperable storage. When
882 /// that is not available, the current time should be used.
883 pub fn at<T>(time: T) -> Self
884 where T: Into<SystemTime>,
885 {
886 let time = time.into();
887 let mut p = Self::new();
888 p.time = Some(system_time_cutoff_to_timestamp(time)
889 // Map "ACCEPT" to the end of time (None
890 // here means the current time).
891 .unwrap_or(Timestamp::MAX));
892 p
893 }
894
895 /// Returns the policy's reference time.
896 ///
897 /// The current time is None.
898 ///
899 /// See [`StandardPolicy::at`] for details.
900 ///
901 /// [`StandardPolicy::at`]: StandardPolicy::at()
902 pub fn time(&self) -> Option<SystemTime> {
903 self.time.map(Into::into)
904 }
905
906 /// Always considers `h` to be secure.
907 ///
908 /// A cryptographic hash algorithm normally has three security
909 /// properties:
910 ///
911 /// - Pre-image resistance,
912 /// - Second pre-image resistance, and
913 /// - Collision resistance.
914 ///
915 /// A hash algorithm should only be unconditionally accepted if it
916 /// has all three of these properties. See the documentation for
917 /// [`HashAlgoSecurity`] for more details.
918 pub fn accept_hash(&mut self, h: HashAlgorithm) {
919 self.accept_hash_property(h, HashAlgoSecurity::CollisionResistance);
920 self.accept_hash_property(h, HashAlgoSecurity::SecondPreImageResistance);
921 }
922
923 /// Considers hash algorithm `h` to be secure for the specified
924 /// security property `sec`.
925 ///
926 /// For instance, an application may choose to allow an algorithm
927 /// like SHA-1 in contexts like User ID binding signatures where
928 /// only [second preimage
929 /// resistance][`HashAlgoSecurity::SecondPreImageResistance`] is
930 /// required but not in contexts like signatures over data where
931 /// [collision
932 /// resistance][`HashAlgoSecurity::CollisionResistance`] is also
933 /// required. Whereas SHA-1's collision resistance is
934 /// [definitively broken](https://shattered.io/), depending on the
935 /// application's threat model, it may be acceptable to continue
936 /// to accept SHA-1 in these specific contexts.
937 pub fn accept_hash_property(&mut self, h: HashAlgorithm, sec: HashAlgoSecurity)
938 {
939 self.reject_hash_property_at(h, sec, None);
940 }
941
942 /// Considers `h` to be insecure in all security contexts.
943 ///
944 /// A cryptographic hash algorithm normally has three security
945 /// properties:
946 ///
947 /// - Pre-image resistance,
948 /// - Second pre-image resistance, and
949 /// - Collision resistance.
950 ///
951 /// This method causes the hash algorithm to be considered unsafe
952 /// in all security contexts.
953 ///
954 /// See the documentation for [`HashAlgoSecurity`] for more
955 /// details.
956 ///
957 ///
958 /// To express a more nuanced policy, use
959 /// [`StandardPolicy::reject_hash_at`] or
960 /// [`StandardPolicy::reject_hash_property_at`].
961 ///
962 /// [`StandardPolicy::reject_hash_at`]: StandardPolicy::reject_hash_at()
963 /// [`StandardPolicy::reject_hash_property_at`]: StandardPolicy::reject_hash_property_at()
964 pub fn reject_hash(&mut self, h: HashAlgorithm) {
965 self.collision_resistant_hash_algos.set(h, REJECT);
966 self.second_pre_image_resistant_hash_algos.set(h, REJECT);
967 }
968
969 /// Considers all hash algorithms to be insecure.
970 ///
971 /// Causes all hash algorithms to be considered insecure in all
972 /// security contexts.
973 ///
974 /// This is useful when using a good list to determine what
975 /// algorithms are allowed.
976 pub fn reject_all_hashes(&mut self) {
977 self.collision_resistant_hash_algos.reject_all();
978 self.second_pre_image_resistant_hash_algos.reject_all();
979 }
980
981 /// Considers `h` to be insecure in all security contexts starting
982 /// at time `t`.
983 ///
984 /// A cryptographic hash algorithm normally has three security
985 /// properties:
986 ///
987 /// - Pre-image resistance,
988 /// - Second pre-image resistance, and
989 /// - Collision resistance.
990 ///
991 /// This method causes the hash algorithm to be considered unsafe
992 /// in all security contexts starting at time `t`.
993 ///
994 /// See the documentation for [`HashAlgoSecurity`] for more
995 /// details.
996 ///
997 ///
998 /// To express a more nuanced policy, use
999 /// [`StandardPolicy::reject_hash_property_at`].
1000 ///
1001 /// [`StandardPolicy::reject_hash_property_at`]: StandardPolicy::reject_hash_property_at()
1002 pub fn reject_hash_at<T>(&mut self, h: HashAlgorithm, t: T)
1003 where T: Into<Option<SystemTime>>,
1004 {
1005 let t = t.into().and_then(system_time_cutoff_to_timestamp);
1006 self.collision_resistant_hash_algos.set(h, t);
1007 self.second_pre_image_resistant_hash_algos.set(h, t);
1008 }
1009
1010 /// Considers `h` to be insecure starting at `t` for the specified
1011 /// security property.
1012 ///
1013 /// A hash algorithm is considered secure if it has all of the
1014 /// following security properties:
1015 ///
1016 /// - Pre-image resistance,
1017 /// - Second pre-image resistance, and
1018 /// - Collision resistance.
1019 ///
1020 /// Some contexts only require a subset of these security
1021 /// properties. Specifically, if an attacker is unable to
1022 /// influence the data that a user signs, then the hash algorithm
1023 /// only needs second pre-image resistance; it doesn't need
1024 /// collision resistance. See the documentation for
1025 /// [`HashAlgoSecurity`] for more details.
1026 ///
1027 ///
1028 /// This method makes it possible to specify different policies
1029 /// depending on the security requirements.
1030 ///
1031 /// A cutoff of `None` means that there is no cutoff and the
1032 /// algorithm has no known vulnerabilities for the specified
1033 /// security policy.
1034 ///
1035 /// As a rule of thumb, collision resistance is easier to attack
1036 /// than second pre-image resistance. And in practice there are
1037 /// practical attacks against several widely-used hash algorithms'
1038 /// collision resistance, but only theoretical attacks against
1039 /// their second pre-image resistance. Nevertheless, once one
1040 /// property of a hash has been compromised, we want to deprecate
1041 /// its use as soon as it is feasible. Unfortunately, because
1042 /// OpenPGP certificates are long-lived, this can take years.
1043 ///
1044 /// Given this, we start rejecting [MD5] in cases where collision
1045 /// resistance is required in 1997 and completely reject it
1046 /// starting in 2004:
1047 ///
1048 /// > In 1996, Dobbertin announced a collision of the
1049 /// > compression function of MD5 (Dobbertin, 1996). While this
1050 /// > was not an attack on the full MD5 hash function, it was
1051 /// > close enough for cryptographers to recommend switching to
1052 /// > a replacement, such as SHA-1 or RIPEMD-160.
1053 /// >
1054 /// > MD5CRK ended shortly after 17 August 2004, when collisions
1055 /// > for the full MD5 were announced by Xiaoyun Wang, Dengguo
1056 /// > Feng, Xuejia Lai, and Hongbo Yu. Their analytical attack
1057 /// > was reported to take only one hour on an IBM p690 cluster.
1058 /// >
1059 /// > (Accessed Feb. 2020.)
1060 ///
1061 /// [MD5]: https://en.wikipedia.org/wiki/MD5
1062 ///
1063 /// And we start rejecting [SHA-1] in cases where collision
1064 /// resistance is required in 2013, and completely reject it in
1065 /// 2023:
1066 ///
1067 /// > Since 2005 SHA-1 has not been considered secure against
1068 /// > well-funded opponents, as of 2010 many organizations have
1069 /// > recommended its replacement. NIST formally deprecated use
1070 /// > of SHA-1 in 2011 and disallowed its use for digital
1071 /// > signatures in 2013. As of 2020, attacks against SHA-1 are
1072 /// > as practical as against MD5; as such, it is recommended to
1073 /// > remove SHA-1 from products as soon as possible and use
1074 /// > instead SHA-256 or SHA-3. Replacing SHA-1 is urgent where
1075 /// > it's used for signatures.
1076 /// >
1077 /// > (Accessed Feb. 2020.)
1078 ///
1079 /// [SHA-1]: https://en.wikipedia.org/wiki/SHA-1
1080 ///
1081 /// There are two main reasons why we have decided to accept SHA-1
1082 /// for so long. First, as of the end of 2020, there are still a
1083 /// large number of [certificates that rely on SHA-1]. Second,
1084 /// Sequoia uses a variant of SHA-1 called [SHA1CD], which is able
1085 /// to detect and *mitigate* the known attacks on SHA-1's
1086 /// collision resistance.
1087 ///
1088 /// [certificates that rely on SHA-1]: https://gitlab.com/sequoia-pgp/sequoia/-/issues/595
1089 /// [SHA1CD]: https://github.com/cr-marcstevens/sha1collisiondetection
1090 ///
1091 /// Since RIPE-MD is structured similarly to SHA-1, we
1092 /// conservatively consider it to be broken as well. But, because
1093 /// it is not widely used in the OpenPGP ecosystem, we don't make
1094 /// provisions for it.
1095 ///
1096 /// Note: if a context indicates that it requires collision
1097 /// resistance, then it requires both collision resistance and
1098 /// second pre-image resistance, and both policies must indicate
1099 /// that the hash algorithm can be safely used at the specified
1100 /// time.
1101 pub fn reject_hash_property_at<T>(&mut self, h: HashAlgorithm,
1102 sec: HashAlgoSecurity, t: T)
1103 where T: Into<Option<SystemTime>>,
1104 {
1105 let t = t.into().and_then(system_time_cutoff_to_timestamp);
1106 match sec {
1107 HashAlgoSecurity::CollisionResistance =>
1108 self.collision_resistant_hash_algos.set(h, t),
1109 HashAlgoSecurity::SecondPreImageResistance =>
1110 self.second_pre_image_resistant_hash_algos.set(h, t),
1111 }
1112 }
1113
1114 /// Returns the cutoff time for the specified hash algorithm and
1115 /// security policy.
1116 pub fn hash_cutoff(&self, h: HashAlgorithm, sec: HashAlgoSecurity)
1117 -> Option<SystemTime>
1118 {
1119 match sec {
1120 HashAlgoSecurity::CollisionResistance =>
1121 self.collision_resistant_hash_algos.cutoff(h),
1122 HashAlgoSecurity::SecondPreImageResistance =>
1123 self.second_pre_image_resistant_hash_algos.cutoff(h),
1124 }.map(|t| t.into())
1125 }
1126
1127 /// Sets the amount of time to continue to accept revocation
1128 /// certificates after a hash algorithm should be rejected.
1129 ///
1130 /// Using [`StandardPolicy::reject_hash_at`], it is possible to
1131 /// indicate when a hash algorithm's security has been
1132 /// compromised, and, as such, should no longer be accepted.
1133 ///
1134 /// [`StandardPolicy::reject_hash_at`]: StandardPolicy::reject_hash_at()
1135 ///
1136 /// Applying this policy to revocation certificates can have some
1137 /// unfortunate side effects. In particular, if a certificate has
1138 /// been revoked using a revocation certificate that relies on a
1139 /// broken hash algorithm, but the most recent self signature uses
1140 /// a strong acceptable hash algorithm, then rejecting the
1141 /// revocation certificate would mean considering the certificate
1142 /// to not be revoked! This would be a catastrophe if the secret
1143 /// key material were compromised.
1144 ///
1145 /// Unfortunately, this happens in practice. A common example
1146 /// appears to be a certificate that has been updated many times,
1147 /// and is then revoked using a revocation certificate that was
1148 /// generated when the certificate was generated.
1149 ///
1150 /// Since the consequences of allowing an invalid revocation
1151 /// certificate are significantly less severe (a denial of
1152 /// service) than ignoring a valid revocation certificate
1153 /// (compromised confidentiality, integrity, and authentication),
1154 /// this option makes it possible to accept revocations using weak
1155 /// hash algorithms longer than other types of signatures.
1156 ///
1157 /// By default, the standard policy accepts revocation
1158 /// certificates seven years after the hash they are using was
1159 /// initially compromised.
1160 pub fn hash_revocation_tolerance<D>(&mut self, d: D)
1161 where D: Into<types::Duration>
1162 {
1163 self.hash_revocation_tolerance = d.into();
1164 }
1165
1166 /// Sets the amount of time to continue to accept revocation
1167 /// certificates after a hash algorithm should be rejected.
1168 ///
1169 /// See [`StandardPolicy::hash_revocation_tolerance`] for details.
1170 ///
1171 /// [`StandardPolicy::hash_revocation_tolerance`]: StandardPolicy::hash_revocation_tolerance()
1172 pub fn get_hash_revocation_tolerance(&self) -> types::Duration {
1173 self.hash_revocation_tolerance
1174 }
1175
1176 /// Always considers `s` to be secure.
1177 pub fn accept_critical_subpacket(&mut self, s: SubpacketTag) {
1178 self.critical_subpackets.set(s, ACCEPT);
1179 }
1180
1181 /// Always considers `s` to be insecure.
1182 pub fn reject_critical_subpacket(&mut self, s: SubpacketTag) {
1183 self.critical_subpackets.set(s, REJECT);
1184 }
1185
1186 /// Considers all critical subpackets to be insecure.
1187 ///
1188 /// This is useful when using a good list to determine what
1189 /// critical subpackets are allowed.
1190 pub fn reject_all_critical_subpackets(&mut self) {
1191 self.critical_subpackets.reject_all();
1192 }
1193
1194 /// Considers `s` to be insecure starting at `cutoff`.
1195 ///
1196 /// A cutoff of `None` means that there is no cutoff and the
1197 /// subpacket has no known vulnerabilities.
1198 ///
1199 /// By default, we accept all critical subpackets that Sequoia
1200 /// understands and honors.
1201 pub fn reject_critical_subpacket_at<C>(&mut self, s: SubpacketTag,
1202 cutoff: C)
1203 where C: Into<Option<SystemTime>>,
1204 {
1205 self.critical_subpackets.set(
1206 s,
1207 cutoff.into().and_then(system_time_cutoff_to_timestamp));
1208 }
1209
1210 /// Returns the cutoff times for the specified subpacket tag.
1211 pub fn critical_subpacket_cutoff(&self, s: SubpacketTag)
1212 -> Option<SystemTime> {
1213 self.critical_subpackets.cutoff(s).map(|t| t.into())
1214 }
1215
1216 /// Sets the list of accepted critical notations.
1217 ///
1218 /// By default, we reject all critical notations.
1219 pub fn good_critical_notations(&mut self, good_list: &'a [&'a str]) {
1220 self.good_critical_notations = good_list;
1221 }
1222
1223 /// Always considers `s` to be secure.
1224 pub fn accept_asymmetric_algo(&mut self, a: AsymmetricAlgorithm) {
1225 self.asymmetric_algos.set(a, ACCEPT);
1226 }
1227
1228 /// Always considers `s` to be insecure.
1229 pub fn reject_asymmetric_algo(&mut self, a: AsymmetricAlgorithm) {
1230 self.asymmetric_algos.set(a, REJECT);
1231 }
1232
1233 /// Considers all asymmetric algorithms to be insecure.
1234 ///
1235 /// This is useful when using a good list to determine what
1236 /// algorithms are allowed.
1237 pub fn reject_all_asymmetric_algos(&mut self) {
1238 self.asymmetric_algos.reject_all();
1239 }
1240
1241 /// Considers `a` to be insecure starting at `cutoff`.
1242 ///
1243 /// A cutoff of `None` means that there is no cutoff and the
1244 /// algorithm has no known vulnerabilities.
1245 ///
1246 /// By default, we reject the use of asymmetric key sizes lower
1247 /// than 2048 bits starting in 2014 following [NIST Special
1248 /// Publication 800-131A].
1249 ///
1250 /// [NIST Special Publication 800-131A]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf
1251 pub fn reject_asymmetric_algo_at<C>(&mut self, a: AsymmetricAlgorithm,
1252 cutoff: C)
1253 where C: Into<Option<SystemTime>>,
1254 {
1255 self.asymmetric_algos.set(
1256 a,
1257 cutoff.into().and_then(system_time_cutoff_to_timestamp));
1258 }
1259
1260 /// Returns the cutoff times for the specified hash algorithm.
1261 pub fn asymmetric_algo_cutoff(&self, a: AsymmetricAlgorithm)
1262 -> Option<SystemTime> {
1263 self.asymmetric_algos.cutoff(a).map(|t| t.into())
1264 }
1265
1266 /// Always considers `s` to be secure.
1267 pub fn accept_symmetric_algo(&mut self, s: SymmetricAlgorithm) {
1268 self.symmetric_algos.set(s, ACCEPT);
1269 }
1270
1271 /// Always considers `s` to be insecure.
1272 pub fn reject_symmetric_algo(&mut self, s: SymmetricAlgorithm) {
1273 self.symmetric_algos.set(s, REJECT);
1274 }
1275
1276 /// Considers all symmetric algorithms to be insecure.
1277 ///
1278 /// This is useful when using a good list to determine what
1279 /// algorithms are allowed.
1280 pub fn reject_all_symmetric_algos(&mut self) {
1281 self.symmetric_algos.reject_all();
1282 }
1283
1284 /// Considers `s` to be insecure starting at `cutoff`.
1285 ///
1286 /// A cutoff of `None` means that there is no cutoff and the
1287 /// algorithm has no known vulnerabilities.
1288 ///
1289 /// By default, we reject the use of TripleDES (3DES) starting in
1290 /// the year 2017. While 3DES is still a ["MUST implement"]
1291 /// algorithm in RFC4880, released in 2007, there are plenty of
1292 /// other symmetric algorithms defined in RFC4880, and it says
1293 /// AES-128 SHOULD be implemented. Support for other algorithms
1294 /// in OpenPGP implementations is [excellent]. We chose 2017 as
1295 /// the cutoff year because [NIST deprecated 3DES] that year.
1296 ///
1297 /// ["MUST implement"]: https://www.rfc-editor.org/rfc/rfc9580.html#section-9.3
1298 /// [excellent]: https://tests.sequoia-pgp.org/#Symmetric_Encryption_Algorithm_support
1299 /// [NIST deprecated 3DES]: https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA
1300 pub fn reject_symmetric_algo_at<C>(&mut self, s: SymmetricAlgorithm,
1301 cutoff: C)
1302 where C: Into<Option<SystemTime>>,
1303 {
1304 self.symmetric_algos.set(
1305 s,
1306 cutoff.into().and_then(system_time_cutoff_to_timestamp));
1307 }
1308
1309 /// Returns the cutoff times for the specified hash algorithm.
1310 pub fn symmetric_algo_cutoff(&self, s: SymmetricAlgorithm)
1311 -> Option<SystemTime> {
1312 self.symmetric_algos.cutoff(s).map(|t| t.into())
1313 }
1314
1315 /// Always considers `s` to be secure.
1316 ///
1317 /// This feature is [experimental](super#experimental-features).
1318 pub fn accept_aead_algo(&mut self, a: AEADAlgorithm) {
1319 self.aead_algos.set(a, ACCEPT);
1320 }
1321
1322 /// Always considers `s` to be insecure.
1323 ///
1324 /// This feature is [experimental](super#experimental-features).
1325 pub fn reject_aead_algo(&mut self, a: AEADAlgorithm) {
1326 self.aead_algos.set(a, REJECT);
1327 }
1328
1329 /// Considers all AEAD algorithms to be insecure.
1330 ///
1331 /// This is useful when using a good list to determine what
1332 /// algorithms are allowed.
1333 pub fn reject_all_aead_algos(&mut self) {
1334 self.aead_algos.reject_all();
1335 }
1336
1337 /// Considers `a` to be insecure starting at `cutoff`.
1338 ///
1339 /// A cutoff of `None` means that there is no cutoff and the
1340 /// algorithm has no known vulnerabilities.
1341 ///
1342 /// By default, we accept all AEAD modes.
1343 ///
1344 /// This feature is [experimental](super#experimental-features).
1345 pub fn reject_aead_algo_at<C>(&mut self, a: AEADAlgorithm,
1346 cutoff: C)
1347 where C: Into<Option<SystemTime>>,
1348 {
1349 self.aead_algos.set(
1350 a,
1351 cutoff.into().and_then(system_time_cutoff_to_timestamp));
1352 }
1353
1354 /// Returns the cutoff times for the specified hash algorithm.
1355 ///
1356 /// This feature is [experimental](super#experimental-features).
1357 pub fn aead_algo_cutoff(&self, a: AEADAlgorithm)
1358 -> Option<SystemTime> {
1359 self.aead_algos.cutoff(a).map(|t| t.into())
1360 }
1361
1362 /// Always accept the specified version of the packet.
1363 ///
1364 /// If a packet does not have a version field, then its version is
1365 /// `0`.
1366 pub fn accept_packet_tag_version(&mut self, tag: Tag, version: u8) {
1367 self.packet_tags.set_versioned(tag, version, ACCEPT);
1368 }
1369
1370 /// Always accept packets with the given tag independent of their
1371 /// version.
1372 ///
1373 /// If you previously set a cutoff for a specific version of a
1374 /// packet, this overrides that.
1375 pub fn accept_packet_tag(&mut self, tag: Tag) {
1376 self.packet_tags.set_unversioned(tag, ACCEPT);
1377 }
1378
1379 /// Always reject the specified version of the packet.
1380 ///
1381 /// If a packet does not have a version field, then its version is
1382 /// `0`.
1383 pub fn reject_packet_tag_version(&mut self, tag: Tag, version: u8) {
1384 self.packet_tags.set_versioned(tag, version, REJECT);
1385 }
1386
1387 /// Always reject packets with the given tag.
1388 pub fn reject_packet_tag(&mut self, tag: Tag) {
1389 self.packet_tags.set_unversioned(tag, REJECT);
1390 }
1391
1392 /// Considers all packets to be insecure.
1393 ///
1394 /// This is useful when using a good list to determine what
1395 /// packets are allowed.
1396 pub fn reject_all_packet_tags(&mut self) {
1397 self.packet_tags.reject_all();
1398 }
1399
1400 /// Start rejecting the specified version of packets with the
1401 /// given tag at `t`.
1402 ///
1403 /// A cutoff of `None` means that there is no cutoff and the
1404 /// packet has no known vulnerabilities.
1405 ///
1406 /// By default, we consider the *Symmetrically Encrypted Data
1407 /// Packet* (SED) insecure in messages created in the year 2004 or
1408 /// later. The rationale here is that *Symmetrically Encrypted
1409 /// Integrity Protected Data Packet* (SEIP) can be downgraded to
1410 /// SED packets, enabling attacks exploiting the malleability of
1411 /// the CFB stream (see [EFAIL]).
1412 ///
1413 /// [EFAIL]: https://en.wikipedia.org/wiki/EFAIL
1414 ///
1415 /// We chose 2004 as a cutoff-date because [Debian 3.0] (Woody),
1416 /// released on 2002-07-19, was the first release of Debian to
1417 /// ship a version of GnuPG that emitted SEIP packets by default.
1418 /// The first version that emitted SEIP packets was [GnuPG 1.0.3],
1419 /// released on 2000-09-18. Mid 2002 plus an 18 months grace
1420 /// period of people still using older versions is 2004.
1421 ///
1422 /// [Debian 3.0]: https://www.debian.org/News/2002/20020719
1423 /// [GnuPG 1.0.3]: https://lists.gnupg.org/pipermail/gnupg-announce/2000q3/000075.html
1424 pub fn reject_packet_tag_version_at<C>(&mut self, tag: Tag, version: u8,
1425 cutoff: C)
1426 where C: Into<Option<SystemTime>>,
1427 {
1428 self.packet_tags.set_versioned(
1429 tag, version,
1430 cutoff.into().and_then(system_time_cutoff_to_timestamp));
1431 }
1432
1433 /// Start rejecting packets with the given tag at `t`.
1434 ///
1435 /// See the documentation for
1436 /// [`StandardPolicy::reject_packet_tag_version_at`].
1437 pub fn reject_packet_tag_at<C>(&mut self, tag: Tag, cutoff: C)
1438 where C: Into<Option<SystemTime>>,
1439 {
1440 self.packet_tags.set_unversioned(
1441 tag,
1442 cutoff.into().and_then(system_time_cutoff_to_timestamp));
1443 }
1444
1445 /// Returns the cutoff for the specified version of the specified
1446 /// packet tag.
1447 ///
1448 /// This first considers the versioned cutoff list. If there is
1449 /// no entry in the versioned list, it fallsback to the
1450 /// unversioned cutoff list. If there is also no entry there,
1451 /// then it falls back to the default.
1452 pub fn packet_tag_version_cutoff(&self, tag: Tag, version: u8)
1453 -> Option<SystemTime>
1454 {
1455 self.packet_tags.cutoff(tag, version).map(|t| t.into())
1456 }
1457}
1458
1459impl<'a> Policy for StandardPolicy<'a> {
1460 fn signature(&self, sig: &Signature, sec: HashAlgoSecurity) -> Result<()> {
1461 let time = self.time.unwrap_or_else(Timestamp::now);
1462
1463 let rev = matches!(sig.typ(), SignatureType::KeyRevocation
1464 | SignatureType::SubkeyRevocation
1465 | SignatureType::CertificationRevocation);
1466
1467 // Note: collision resistance requires 2nd pre-image resistance.
1468 if sec == HashAlgoSecurity::CollisionResistance {
1469 if rev {
1470 self
1471 .collision_resistant_hash_algos
1472 .check(sig.hash_algo(), time,
1473 Some(self.hash_revocation_tolerance))
1474 .with_context(|| format!(
1475 "Policy rejected revocation signature ({}) requiring \
1476 collision resistance", sig.typ()))?
1477 } else {
1478 self
1479 .collision_resistant_hash_algos
1480 .check(sig.hash_algo(), time, None)
1481 .with_context(|| format!(
1482 "Policy rejected non-revocation signature ({}) requiring \
1483 collision resistance", sig.typ()))?
1484 }
1485 }
1486
1487 if rev {
1488 self
1489 .second_pre_image_resistant_hash_algos
1490 .check(sig.hash_algo(), time,
1491 Some(self.hash_revocation_tolerance))
1492 .with_context(|| format!(
1493 "Policy rejected revocation signature ({}) requiring \
1494 second pre-image resistance", sig.typ()))?
1495 } else {
1496 self
1497 .second_pre_image_resistant_hash_algos
1498 .check(sig.hash_algo(), time, None)
1499 .with_context(|| format!(
1500 "Policy rejected non-revocation signature ({}) requiring \
1501 second pre-image resistance", sig.typ()))?
1502 }
1503
1504 for csp in sig.hashed_area().iter().filter(|sp| sp.critical()) {
1505 self.critical_subpackets.check(csp.tag(), time, None)
1506 .context("Policy rejected critical signature subpacket")?;
1507 if let SubpacketValue::NotationData(n) = csp.value() {
1508 if ! self.good_critical_notations.contains(&n.name()) {
1509 return Err(anyhow::Error::from(
1510 Error::PolicyViolation(
1511 format!("Critical notation {:?}",
1512 n.name()), None))
1513 .context("Policy rejected critical notation"));
1514 }
1515 }
1516 }
1517
1518 Ok(())
1519 }
1520
1521 fn key(&self, ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
1522 -> Result<()>
1523 {
1524 use crate::types::PublicKeyAlgorithm::{self, *};
1525 use crate::crypto::mpi::PublicKey;
1526
1527 #[allow(deprecated)]
1528 let a = match (ka.key().pk_algo(), ka.key().mpis().bits()) {
1529 // RSA.
1530 (RSAEncryptSign, Some(b))
1531 | (RSAEncrypt, Some(b))
1532 | (RSASign, Some(b))
1533 if b < 2048 => AsymmetricAlgorithm::RSA1024,
1534 (RSAEncryptSign, Some(b))
1535 | (RSAEncrypt, Some(b))
1536 | (RSASign, Some(b))
1537 if b < 3072 => AsymmetricAlgorithm::RSA2048,
1538 (RSAEncryptSign, Some(b))
1539 | (RSAEncrypt, Some(b))
1540 | (RSASign, Some(b))
1541 if b < 4096 => AsymmetricAlgorithm::RSA3072,
1542 (RSAEncryptSign, Some(_))
1543 | (RSAEncrypt, Some(_))
1544 | (RSASign, Some(_))
1545 => AsymmetricAlgorithm::RSA4096,
1546 (RSAEncryptSign, None)
1547 | (RSAEncrypt, None)
1548 | (RSASign, None) => unreachable!(),
1549
1550 // ElGamal.
1551 (ElGamalEncryptSign, Some(b))
1552 | (ElGamalEncrypt, Some(b))
1553 if b < 2048 => AsymmetricAlgorithm::ElGamal1024,
1554 (ElGamalEncryptSign, Some(b))
1555 | (ElGamalEncrypt, Some(b))
1556 if b < 3072 => AsymmetricAlgorithm::ElGamal2048,
1557 (ElGamalEncryptSign, Some(b))
1558 | (ElGamalEncrypt, Some(b))
1559 if b < 4096 => AsymmetricAlgorithm::ElGamal3072,
1560 (ElGamalEncryptSign, Some(_))
1561 | (ElGamalEncrypt, Some(_))
1562 => AsymmetricAlgorithm::ElGamal4096,
1563 (ElGamalEncryptSign, None)
1564 | (ElGamalEncrypt, None) => unreachable!(),
1565
1566 // DSA.
1567 (DSA, Some(b))
1568 if b < 2048 => AsymmetricAlgorithm::DSA1024,
1569 (DSA, Some(b))
1570 if b < 3072 => AsymmetricAlgorithm::DSA2048,
1571 (DSA, Some(b))
1572 if b < 4096 => AsymmetricAlgorithm::DSA3072,
1573 (DSA, Some(_))
1574 => AsymmetricAlgorithm::DSA4096,
1575 (DSA, None) => unreachable!(),
1576
1577 // ECC.
1578 (ECDH, _) | (ECDSA, _) | (EdDSA, _) => {
1579 let curve = match ka.key().mpis() {
1580 PublicKey::EdDSA { curve, .. } => curve,
1581 PublicKey::ECDSA { curve, .. } => curve,
1582 PublicKey::ECDH { curve, .. } => curve,
1583 _ => unreachable!(),
1584 };
1585 use crate::types::Curve;
1586 match curve {
1587 Curve::NistP256 => AsymmetricAlgorithm::NistP256,
1588 Curve::NistP384 => AsymmetricAlgorithm::NistP384,
1589 Curve::NistP521 => AsymmetricAlgorithm::NistP521,
1590 Curve::BrainpoolP256 => AsymmetricAlgorithm::BrainpoolP256,
1591 Curve::BrainpoolP384 => AsymmetricAlgorithm::BrainpoolP384,
1592 Curve::BrainpoolP512 => AsymmetricAlgorithm::BrainpoolP512,
1593 Curve::Ed25519 => AsymmetricAlgorithm::EdDSA,
1594 Curve::Cv25519 => AsymmetricAlgorithm::Cv25519,
1595 Curve::Unknown(_) => AsymmetricAlgorithm::Unknown,
1596 }
1597 },
1598
1599 (PublicKeyAlgorithm::X25519, _) => AsymmetricAlgorithm::X25519,
1600 (PublicKeyAlgorithm::X448, _) => AsymmetricAlgorithm::X448,
1601 (PublicKeyAlgorithm::Ed25519, _) => AsymmetricAlgorithm::Ed25519,
1602 (PublicKeyAlgorithm::Ed448, _) => AsymmetricAlgorithm::Ed448,
1603
1604 (PublicKeyAlgorithm::MLDSA65_Ed25519, _) =>
1605 AsymmetricAlgorithm::MLDSA65_Ed25519,
1606 (PublicKeyAlgorithm::MLDSA87_Ed448, _) =>
1607 AsymmetricAlgorithm::MLDSA87_Ed448,
1608
1609 (PublicKeyAlgorithm::SLHDSA128s, _) =>
1610 AsymmetricAlgorithm::SLHDSA128s,
1611 (PublicKeyAlgorithm::SLHDSA128f, _) =>
1612 AsymmetricAlgorithm::SLHDSA128f,
1613 (PublicKeyAlgorithm::SLHDSA256s, _) =>
1614 AsymmetricAlgorithm::SLHDSA256s,
1615
1616 (PublicKeyAlgorithm::MLKEM768_X25519, _) =>
1617 AsymmetricAlgorithm::MLKEM768_X25519,
1618 (PublicKeyAlgorithm::MLKEM1024_X448, _) =>
1619 AsymmetricAlgorithm::MLKEM1024_X448,
1620
1621 (PublicKeyAlgorithm::Private(_), _)
1622 | (PublicKeyAlgorithm::Unknown(_), _)
1623 => AsymmetricAlgorithm::Unknown,
1624 };
1625
1626 let time = self.time.unwrap_or_else(Timestamp::now);
1627 self.asymmetric_algos.check(a, time, None)
1628 .context("Policy rejected asymmetric algorithm")?;
1629
1630 // Check ECDH KDF and KEK parameters.
1631 if let PublicKey::ECDH { hash, sym, .. } = ka.key().mpis() {
1632 self.symmetric_algorithm(*sym)
1633 .context("Policy rejected ECDH \
1634 key encapsulation algorithm")?;
1635
1636 // RFC6637 says:
1637 //
1638 // > Refer to Section 13 for the details regarding the
1639 // > choice of the KEK algorithm, which SHOULD be one of
1640 // > three AES algorithms.
1641 //
1642 // Furthermore, GnuPG rejects anything other than AES.
1643 // I checked the SKS dump, and there are no keys out
1644 // there that use a different KEK algorithm.
1645 match sym {
1646 SymmetricAlgorithm::AES128
1647 | SymmetricAlgorithm::AES192
1648 | SymmetricAlgorithm::AES256
1649 => (), // Good.
1650 _ =>
1651 return Err(anyhow::Error::from(
1652 Error::PolicyViolation(sym.to_string(), None))
1653 .context("Policy rejected ECDH \
1654 key encapsulation algorithm")),
1655 }
1656
1657 // For use in a KDF the hash algorithm does not
1658 // necessarily be collision resistant, but this is the
1659 // weakest property that we otherwise care for, so
1660 // (somewhat arbitrarily) use this.
1661 self
1662 .collision_resistant_hash_algos
1663 .check(*hash, time, None)
1664 .context("Policy rejected ECDH \
1665 key derivation hash function")?;
1666 }
1667
1668 Ok(())
1669 }
1670
1671 fn packet(&self, packet: &Packet) -> Result<()> {
1672 let time = self.time.unwrap_or_else(Timestamp::now);
1673 self.packet_tags
1674 .check(
1675 packet.tag(),
1676 packet.version().unwrap_or(0),
1677 time, None)
1678 .context("Policy rejected packet type")
1679 }
1680
1681 fn symmetric_algorithm(&self, algo: SymmetricAlgorithm) -> Result<()> {
1682 let time = self.time.unwrap_or_else(Timestamp::now);
1683 self.symmetric_algos.check(algo, time, None)
1684 .context("Policy rejected symmetric encryption algorithm")
1685 }
1686
1687 fn aead_algorithm(&self, algo: AEADAlgorithm) -> Result<()> {
1688 let time = self.time.unwrap_or_else(Timestamp::now);
1689 self.aead_algos.check(algo, time, None)
1690 .context("Policy rejected authenticated encryption algorithm")
1691 }
1692}
1693
1694/// Asymmetric encryption algorithms.
1695///
1696/// This type is for refining the [`StandardPolicy`] with respect to
1697/// asymmetric algorithms. In contrast to [`PublicKeyAlgorithm`], it
1698/// does not concern itself with the use (encryption or signing), and
1699/// it does include key sizes (if applicable) and elliptic curves.
1700///
1701/// [`PublicKeyAlgorithm`]: crate::types::PublicKeyAlgorithm
1702///
1703/// Key sizes put into are buckets, rounding down to the nearest
1704/// bucket. For example, a 3253-bit RSA key is categorized as
1705/// `RSA3072`.
1706#[non_exhaustive]
1707#[allow(non_camel_case_types)]
1708#[derive(Clone, Debug, PartialEq, Eq, Copy)]
1709pub enum AsymmetricAlgorithm {
1710 /// RSA with key sizes up to 2048-1 bit.
1711 RSA1024,
1712 /// RSA with key sizes up to 3072-1 bit.
1713 RSA2048,
1714 /// RSA with key sizes up to 4096-1 bit.
1715 RSA3072,
1716 /// RSA with key sizes larger or equal to 4096 bit.
1717 RSA4096,
1718 /// ElGamal with key sizes up to 2048-1 bit.
1719 ElGamal1024,
1720 /// ElGamal with key sizes up to 3072-1 bit.
1721 ElGamal2048,
1722 /// ElGamal with key sizes up to 4096-1 bit.
1723 ElGamal3072,
1724 /// ElGamal with key sizes larger or equal to 4096 bit.
1725 ElGamal4096,
1726 /// DSA with key sizes up to 2048-1 bit.
1727 DSA1024,
1728 /// DSA with key sizes up to 3072-1 bit.
1729 DSA2048,
1730 /// DSA with key sizes up to 4096-1 bit.
1731 DSA3072,
1732 /// DSA with key sizes larger or equal to 4096 bit.
1733 DSA4096,
1734 /// NIST curve P-256.
1735 NistP256,
1736 /// NIST curve P-384.
1737 NistP384,
1738 /// NIST curve P-521.
1739 NistP521,
1740 /// brainpoolP256r1.
1741 BrainpoolP256,
1742 /// brainpoolP384r1.
1743 BrainpoolP384,
1744 /// brainpoolP512r1.
1745 BrainpoolP512,
1746 /// D.J. Bernstein's Curve25519.
1747 Cv25519,
1748 /// X25519 (RFC 7748).
1749 X25519,
1750 /// X448 (RFC 7748).
1751 X448,
1752 /// Ed25519 (RFC 8032).
1753 Ed25519,
1754 /// Ed448 (RFC 8032).
1755 Ed448,
1756 /// EdDSA (v4 Ed25519Legacy)
1757 EdDSA,
1758
1759 /// Composite signature algorithm using ML-DSA-65 and Ed25519.
1760 MLDSA65_Ed25519,
1761
1762 /// Composite signature algorithm using ML-DSA-87 and Ed448.
1763 MLDSA87_Ed448,
1764
1765 /// SLH-DSA signature algorithm 128 bit, small signatures.
1766 SLHDSA128s,
1767
1768 /// SLH-DSA signature algorithm 128 bit, fast signatures.
1769 SLHDSA128f,
1770
1771 /// SLH-DSA signature algorithm 256 bit, small signatures.
1772 SLHDSA256s,
1773
1774 /// Composite KEM using ML-KEM-768 and X25519.
1775 MLKEM768_X25519,
1776
1777 /// Composite KEM using ML-KEM-1024 and X448.
1778 MLKEM1024_X448,
1779
1780 /// Unknown algorithm.
1781 Unknown,
1782}
1783assert_send_and_sync!(AsymmetricAlgorithm);
1784
1785const ASYMMETRIC_ALGORITHM_VARIANTS: [AsymmetricAlgorithm; 31] = [
1786 AsymmetricAlgorithm::RSA1024,
1787 AsymmetricAlgorithm::RSA2048,
1788 AsymmetricAlgorithm::RSA3072,
1789 AsymmetricAlgorithm::RSA4096,
1790 AsymmetricAlgorithm::ElGamal1024,
1791 AsymmetricAlgorithm::ElGamal2048,
1792 AsymmetricAlgorithm::ElGamal3072,
1793 AsymmetricAlgorithm::ElGamal4096,
1794 AsymmetricAlgorithm::DSA1024,
1795 AsymmetricAlgorithm::DSA2048,
1796 AsymmetricAlgorithm::DSA3072,
1797 AsymmetricAlgorithm::DSA4096,
1798 AsymmetricAlgorithm::NistP256,
1799 AsymmetricAlgorithm::NistP384,
1800 AsymmetricAlgorithm::NistP521,
1801 AsymmetricAlgorithm::BrainpoolP256,
1802 AsymmetricAlgorithm::BrainpoolP384,
1803 AsymmetricAlgorithm::BrainpoolP512,
1804 AsymmetricAlgorithm::Cv25519,
1805 AsymmetricAlgorithm::X25519,
1806 AsymmetricAlgorithm::X448,
1807 AsymmetricAlgorithm::Ed25519,
1808 AsymmetricAlgorithm::Ed448,
1809 AsymmetricAlgorithm::EdDSA,
1810 AsymmetricAlgorithm::MLDSA65_Ed25519,
1811 AsymmetricAlgorithm::MLDSA87_Ed448,
1812 AsymmetricAlgorithm::SLHDSA128s,
1813 AsymmetricAlgorithm::SLHDSA128f,
1814 AsymmetricAlgorithm::SLHDSA256s,
1815 AsymmetricAlgorithm::MLKEM768_X25519,
1816 AsymmetricAlgorithm::MLKEM1024_X448,
1817];
1818
1819impl AsymmetricAlgorithm {
1820 /// Returns an iterator over all valid variants.
1821 ///
1822 /// Returns an iterator over all known variants. This does not
1823 /// include the [`AsymmetricAlgorithm::Unknown`] variant.
1824 pub fn variants() -> impl Iterator<Item=AsymmetricAlgorithm> {
1825 ASYMMETRIC_ALGORITHM_VARIANTS.iter().cloned()
1826 }
1827}
1828
1829impl std::fmt::Display for AsymmetricAlgorithm {
1830 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1831 write!(f, "{:?}", self)
1832 }
1833}
1834
1835impl From<AsymmetricAlgorithm> for u8 {
1836 fn from(a: AsymmetricAlgorithm) -> Self {
1837 use self::AsymmetricAlgorithm::*;
1838 match a {
1839 RSA1024 => 0,
1840 RSA2048 => 1,
1841 RSA3072 => 2,
1842 RSA4096 => 3,
1843 ElGamal1024 => 4,
1844 ElGamal2048 => 5,
1845 ElGamal3072 => 6,
1846 ElGamal4096 => 7,
1847 DSA1024 => 8,
1848 DSA2048 => 9,
1849 DSA3072 => 10,
1850 DSA4096 => 11,
1851 NistP256 => 12,
1852 NistP384 => 13,
1853 NistP521 => 14,
1854 BrainpoolP256 => 15,
1855 BrainpoolP384 => 16,
1856 BrainpoolP512 => 17,
1857 Cv25519 => 18,
1858 X25519 => 19,
1859 X448 => 20,
1860 Ed25519 => 21,
1861 Ed448 => 22,
1862 EdDSA => 23,
1863 MLDSA65_Ed25519 => 24,
1864 MLDSA87_Ed448 => 25,
1865 SLHDSA128s => 26,
1866 SLHDSA128f => 27,
1867 SLHDSA256s => 28,
1868 MLKEM768_X25519 => 29,
1869 MLKEM1024_X448 => 30,
1870 Unknown => 255,
1871 }
1872 }
1873}
1874
1875/// The Null Policy.
1876///
1877/// Danger, here be dragons.
1878///
1879/// This policy imposes no additional policy, i.e., accepts
1880/// everything. This includes the MD5 hash algorithm, and SED
1881/// packets.
1882///
1883/// The Null policy has a limited set of valid use cases, e.g., packet statistics.
1884/// For other purposes, it is more advisable to use the [`StandardPolicy`] and
1885/// adjust it by selectively allowing items considered insecure by default, e.g.,
1886/// via [`StandardPolicy::accept_hash`] function. If this is still too inflexible
1887/// consider creating a specialized policy based on the [`StandardPolicy`] as
1888/// [the example for `StandardPolicy`] illustrates.
1889///
1890/// [`StandardPolicy::accept_hash`]: StandardPolicy::accept_hash()
1891/// [the example for `StandardPolicy`]: StandardPolicy#examples
1892#[derive(Debug)]
1893pub struct NullPolicy {
1894}
1895
1896assert_send_and_sync!(NullPolicy);
1897
1898impl NullPolicy {
1899 /// Instantiates a new `NullPolicy`.
1900 pub const unsafe fn new() -> Self {
1901 NullPolicy {}
1902 }
1903}
1904
1905impl Policy for NullPolicy {
1906 fn signature(&self, _sig: &Signature, _sec: HashAlgoSecurity) -> Result<()> {
1907 Ok(())
1908 }
1909
1910 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
1911 -> Result<()>
1912 {
1913 Ok(())
1914 }
1915
1916 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
1917 Ok(())
1918 }
1919
1920 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
1921 Ok(())
1922 }
1923
1924 fn packet(&self, _packet: &Packet) -> Result<()> {
1925 Ok(())
1926 }
1927
1928}
1929
1930#[cfg(test)]
1931mod test {
1932 use std::io::Read;
1933 use std::time::Duration;
1934
1935 use super::*;
1936 use crate::Error;
1937 use crate::crypto::SessionKey;
1938 use crate::packet::key::Key4;
1939 use crate::packet::signature;
1940 use crate::packet::{PKESK, SKESK};
1941 use crate::parse::Parse;
1942 use crate::parse::stream::DecryptionHelper;
1943 use crate::parse::stream::DecryptorBuilder;
1944 use crate::parse::stream::DetachedVerifierBuilder;
1945 use crate::parse::stream::MessageLayer;
1946 use crate::parse::stream::MessageStructure;
1947 use crate::parse::stream::VerificationHelper;
1948 use crate::parse::stream::VerifierBuilder;
1949 use crate::policy::StandardPolicy as P;
1950 use crate::types::Curve;
1951 use crate::types::KeyFlags;
1952 use crate::types::SymmetricAlgorithm;
1953
1954 // Test that the constructor is const.
1955 const _A_STANDARD_POLICY: StandardPolicy = StandardPolicy::new();
1956
1957 #[test]
1958 fn binding_signature() {
1959 let p = &P::new();
1960
1961 // A primary and two subkeys.
1962 let (cert, _) = CertBuilder::new()
1963 .add_signing_subkey()
1964 .add_transport_encryption_subkey()
1965 .generate().unwrap();
1966
1967 assert_eq!(cert.keys().with_policy(p, None).count(), 3);
1968
1969 // Reject all direct key signatures.
1970 #[derive(Debug)]
1971 struct NoDirectKeySigs;
1972 impl Policy for NoDirectKeySigs {
1973 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
1974 -> Result<()>
1975 {
1976 use crate::types::SignatureType::*;
1977
1978 match sig.typ() {
1979 DirectKey => Err(anyhow::anyhow!("direct key!")),
1980 _ => Ok(()),
1981 }
1982 }
1983
1984 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
1985 -> Result<()>
1986 {
1987 Ok(())
1988 }
1989
1990 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
1991 Ok(())
1992 }
1993
1994 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
1995 Ok(())
1996 }
1997
1998 fn packet(&self, _packet: &Packet) -> Result<()> {
1999 Ok(())
2000 }
2001 }
2002
2003 let p = &NoDirectKeySigs {};
2004 assert_eq!(cert.keys().with_policy(p, None).count(), 0);
2005
2006 // Reject all subkey signatures.
2007 #[derive(Debug)]
2008 struct NoSubkeySigs;
2009 impl Policy for NoSubkeySigs {
2010 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
2011 -> Result<()>
2012 {
2013 use crate::types::SignatureType::*;
2014
2015 match sig.typ() {
2016 SubkeyBinding => Err(anyhow::anyhow!("subkey signature!")),
2017 _ => Ok(()),
2018 }
2019 }
2020
2021 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2022 -> Result<()>
2023 {
2024 Ok(())
2025 }
2026
2027 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2028 Ok(())
2029 }
2030
2031 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2032 Ok(())
2033 }
2034
2035 fn packet(&self, _packet: &Packet) -> Result<()> {
2036 Ok(())
2037 }
2038 }
2039
2040 let p = &NoSubkeySigs {};
2041 assert_eq!(cert.keys().with_policy(p, None).count(), 1);
2042 }
2043
2044 #[test]
2045 fn revocation() -> Result<()> {
2046 use crate::cert::prelude::*;
2047 use crate::types::SignatureType;
2048 use crate::types::ReasonForRevocation;
2049
2050 let p = &P::new();
2051
2052 // A primary and two subkeys.
2053 let (cert, _) = CertBuilder::new()
2054 .add_userid("Alice")
2055 .add_signing_subkey()
2056 .add_transport_encryption_subkey()
2057 .generate()?;
2058
2059 // Make sure we have all keys and all user ids.
2060 assert_eq!(cert.keys().with_policy(p, None).count(), 3);
2061 assert_eq!(cert.userids().with_policy(p, None).count(), 1);
2062
2063 // Reject all user id signatures.
2064 #[derive(Debug)]
2065 struct NoPositiveCertifications;
2066 impl Policy for NoPositiveCertifications {
2067 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
2068 -> Result<()>
2069 {
2070 use crate::types::SignatureType::*;
2071 match sig.typ() {
2072 PositiveCertification =>
2073 Err(anyhow::anyhow!("positive certification!")),
2074 _ => Ok(()),
2075 }
2076 }
2077
2078 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2079 -> Result<()>
2080 {
2081 Ok(())
2082 }
2083
2084 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2085 Ok(())
2086 }
2087
2088 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2089 Ok(())
2090 }
2091
2092 fn packet(&self, _packet: &Packet) -> Result<()> {
2093 Ok(())
2094 }
2095 }
2096 let p = &NoPositiveCertifications {};
2097 assert_eq!(cert.userids().with_policy(p, None).count(), 0);
2098
2099
2100 // Revoke it.
2101 let mut keypair = cert.primary_key().key().clone()
2102 .parts_into_secret()?.into_keypair()?;
2103 let ca = cert.userids().next().unwrap();
2104
2105 // Generate the revocation for the first and only UserID.
2106 let revocation =
2107 UserIDRevocationBuilder::new()
2108 .set_reason_for_revocation(
2109 ReasonForRevocation::KeyRetired,
2110 b"Left example.org.")?
2111 .build(&mut keypair, &cert, ca.userid(), None)?;
2112 assert_eq!(revocation.typ(), SignatureType::CertificationRevocation);
2113
2114 // Now merge the revocation signature into the Cert.
2115 let cert = cert.insert_packets(revocation.clone())?.0;
2116
2117 // Check that it is revoked.
2118 assert_eq!(cert.userids().with_policy(p, None).revoked(false).count(), 0);
2119
2120 // Reject all user id signatures.
2121 #[derive(Debug)]
2122 struct NoCertificationRevocation;
2123 impl Policy for NoCertificationRevocation {
2124 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
2125 -> Result<()>
2126 {
2127 use crate::types::SignatureType::*;
2128 match sig.typ() {
2129 CertificationRevocation =>
2130 Err(anyhow::anyhow!("certification certification!")),
2131 _ => Ok(()),
2132 }
2133 }
2134
2135 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2136 -> Result<()>
2137 {
2138 Ok(())
2139 }
2140
2141 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2142 Ok(())
2143 }
2144
2145 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2146 Ok(())
2147 }
2148
2149 fn packet(&self, _packet: &Packet) -> Result<()> {
2150 Ok(())
2151 }
2152 }
2153 let p = &NoCertificationRevocation {};
2154
2155 // Check that the user id is no longer revoked.
2156 assert_eq!(cert.userids().with_policy(p, None).revoked(false).count(), 1);
2157
2158
2159 // Generate the revocation for the first subkey.
2160 let subkey = cert.keys().subkeys().next().unwrap();
2161 let revocation =
2162 SubkeyRevocationBuilder::new()
2163 .set_reason_for_revocation(
2164 ReasonForRevocation::KeyRetired,
2165 b"Smells funny.").unwrap()
2166 .build(&mut keypair, &cert, subkey.key(), None)?;
2167 assert_eq!(revocation.typ(), SignatureType::SubkeyRevocation);
2168
2169 // Now merge the revocation signature into the Cert.
2170 assert_eq!(cert.keys().with_policy(p, None).revoked(false).count(), 3);
2171 let cert = cert.insert_packets(revocation.clone())?.0;
2172 assert_eq!(cert.keys().with_policy(p, None).revoked(false).count(), 2);
2173
2174 // Reject all subkey revocations.
2175 #[derive(Debug)]
2176 struct NoSubkeyRevocation;
2177 impl Policy for NoSubkeyRevocation {
2178 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
2179 -> Result<()>
2180 {
2181 use crate::types::SignatureType::*;
2182 match sig.typ() {
2183 SubkeyRevocation =>
2184 Err(anyhow::anyhow!("subkey revocation!")),
2185 _ => Ok(()),
2186 }
2187 }
2188
2189 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2190 -> Result<()>
2191 {
2192 Ok(())
2193 }
2194
2195 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2196 Ok(())
2197 }
2198
2199 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2200 Ok(())
2201 }
2202
2203 fn packet(&self, _packet: &Packet) -> Result<()> {
2204 Ok(())
2205 }
2206 }
2207 let p = &NoSubkeyRevocation {};
2208
2209 // Check that the key is no longer revoked.
2210 assert_eq!(cert.keys().with_policy(p, None).revoked(false).count(), 3);
2211
2212 Ok(())
2213 }
2214
2215
2216 #[test]
2217 fn binary_signature() -> Result<()> {
2218 #[derive(PartialEq, Debug)]
2219 struct VHelper {
2220 good: usize,
2221 errors: usize,
2222 keys: Vec<Cert>,
2223 }
2224
2225 impl VHelper {
2226 fn new(keys: Vec<Cert>) -> Self {
2227 VHelper {
2228 good: 0,
2229 errors: 0,
2230 keys,
2231 }
2232 }
2233 }
2234
2235 impl VerificationHelper for VHelper {
2236 fn get_certs(&mut self, _ids: &[crate::KeyHandle])
2237 -> Result<Vec<Cert>>
2238 {
2239 Ok(self.keys.clone())
2240 }
2241
2242 fn check(&mut self, structure: MessageStructure) -> Result<()>
2243 {
2244 for layer in structure {
2245 match layer {
2246 MessageLayer::SignatureGroup { ref results } =>
2247 for result in results {
2248 eprintln!("result: {:?}", result);
2249 match result {
2250 Ok(_) => self.good += 1,
2251 Err(_) => self.errors += 1,
2252 }
2253 }
2254 MessageLayer::Compression { .. } => (),
2255 _ => unreachable!(),
2256 }
2257 }
2258
2259 Ok(())
2260 }
2261 }
2262
2263 impl DecryptionHelper for VHelper {
2264 fn decrypt(&mut self, _: &[PKESK], _: &[SKESK],
2265 _: Option<SymmetricAlgorithm>,
2266 _: &mut dyn FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool)
2267 -> Result<Option<Cert>>
2268 {
2269 unreachable!();
2270 }
2271 }
2272
2273 // Reject all data (binary) signatures.
2274 #[derive(Debug)]
2275 struct NoBinarySigantures;
2276 impl Policy for NoBinarySigantures {
2277 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
2278 -> Result<()>
2279 {
2280 use crate::types::SignatureType::*;
2281 eprintln!("{:?}", sig.typ());
2282 match sig.typ() {
2283 Binary =>
2284 Err(anyhow::anyhow!("binary!")),
2285 _ => Ok(()),
2286 }
2287 }
2288
2289 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2290 -> Result<()>
2291 {
2292 Ok(())
2293 }
2294
2295 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2296 Ok(())
2297 }
2298
2299 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2300 Ok(())
2301 }
2302
2303 fn packet(&self, _packet: &Packet) -> Result<()> {
2304 Ok(())
2305 }
2306 }
2307 let no_binary_signatures = &NoBinarySigantures {};
2308
2309 // Reject all subkey signatures.
2310 #[derive(Debug)]
2311 struct NoSubkeySigs;
2312 impl Policy for NoSubkeySigs {
2313 fn signature(&self, sig: &Signature, _sec: HashAlgoSecurity)
2314 -> Result<()>
2315 {
2316 use crate::types::SignatureType::*;
2317
2318 match sig.typ() {
2319 SubkeyBinding => Err(anyhow::anyhow!("subkey signature!")),
2320 _ => Ok(()),
2321 }
2322 }
2323
2324 fn key(&self, _ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2325 -> Result<()>
2326 {
2327 Ok(())
2328 }
2329
2330 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2331 Ok(())
2332 }
2333
2334 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2335 Ok(())
2336 }
2337
2338 fn packet(&self, _packet: &Packet) -> Result<()> {
2339 Ok(())
2340 }
2341 }
2342 let no_subkey_signatures = &NoSubkeySigs {};
2343
2344 let standard = &P::new();
2345
2346 let keys = [
2347 "neal.pgp",
2348 ].iter()
2349 .map(|f| Cert::from_bytes(crate::tests::key(f)).unwrap())
2350 .collect::<Vec<_>>();
2351 let data = "messages/signed-1.pgp";
2352
2353 let reference = crate::tests::manifesto();
2354
2355
2356
2357 // Test Verifier.
2358
2359 // Standard policy => ok.
2360 let h = VHelper::new(keys.clone());
2361 let mut v = VerifierBuilder::from_bytes(crate::tests::file(data))?
2362 .with_policy(standard, crate::frozen_time(), h)?;
2363 assert!(v.message_processed());
2364 assert_eq!(v.helper_ref().good, 1);
2365 assert_eq!(v.helper_ref().errors, 0);
2366
2367 let mut content = Vec::new();
2368 v.read_to_end(&mut content).unwrap();
2369 assert_eq!(reference.len(), content.len());
2370 assert_eq!(reference, &content[..]);
2371
2372
2373 // Kill the subkey.
2374 let h = VHelper::new(keys.clone());
2375 let mut v = VerifierBuilder::from_bytes(crate::tests::file(data))?
2376 .with_policy(no_subkey_signatures, crate::frozen_time(), h)?;
2377 assert!(v.message_processed());
2378 assert_eq!(v.helper_ref().good, 0);
2379 assert_eq!(v.helper_ref().errors, 1);
2380
2381 let mut content = Vec::new();
2382 v.read_to_end(&mut content).unwrap();
2383 assert_eq!(reference.len(), content.len());
2384 assert_eq!(reference, &content[..]);
2385
2386
2387 // Kill the data signature.
2388 let h = VHelper::new(keys.clone());
2389 let mut v = VerifierBuilder::from_bytes(crate::tests::file(data))?
2390 .with_policy(no_binary_signatures, crate::frozen_time(), h)?;
2391 assert!(v.message_processed());
2392 assert_eq!(v.helper_ref().good, 0);
2393 assert_eq!(v.helper_ref().errors, 1);
2394
2395 let mut content = Vec::new();
2396 v.read_to_end(&mut content).unwrap();
2397 assert_eq!(reference.len(), content.len());
2398 assert_eq!(reference, &content[..]);
2399
2400
2401
2402 // Test Decryptor.
2403
2404 // Standard policy.
2405 let h = VHelper::new(keys.clone());
2406 let mut v = DecryptorBuilder::from_bytes(crate::tests::file(data))?
2407 .with_policy(standard, crate::frozen_time(), h)?;
2408 assert!(v.message_processed());
2409 assert_eq!(v.helper_ref().good, 1);
2410 assert_eq!(v.helper_ref().errors, 0);
2411
2412 let mut content = Vec::new();
2413 v.read_to_end(&mut content).unwrap();
2414 assert_eq!(reference.len(), content.len());
2415 assert_eq!(reference, &content[..]);
2416
2417
2418 // Kill the subkey.
2419 let h = VHelper::new(keys.clone());
2420 let mut v = DecryptorBuilder::from_bytes(crate::tests::file(data))?
2421 .with_policy(no_subkey_signatures, crate::frozen_time(), h)?;
2422 assert!(v.message_processed());
2423 assert_eq!(v.helper_ref().good, 0);
2424 assert_eq!(v.helper_ref().errors, 1);
2425
2426 let mut content = Vec::new();
2427 v.read_to_end(&mut content).unwrap();
2428 assert_eq!(reference.len(), content.len());
2429 assert_eq!(reference, &content[..]);
2430
2431
2432 // Kill the data signature.
2433 let h = VHelper::new(keys.clone());
2434 let mut v = DecryptorBuilder::from_bytes(crate::tests::file(data))?
2435 .with_policy(no_binary_signatures, crate::frozen_time(), h)?;
2436 assert!(v.message_processed());
2437 assert_eq!(v.helper_ref().good, 0);
2438 assert_eq!(v.helper_ref().errors, 1);
2439
2440 let mut content = Vec::new();
2441 v.read_to_end(&mut content).unwrap();
2442 assert_eq!(reference.len(), content.len());
2443 assert_eq!(reference, &content[..]);
2444 Ok(())
2445 }
2446
2447 #[test]
2448 fn hash_algo() -> Result<()> {
2449 use crate::types::RevocationStatus;
2450 use crate::types::ReasonForRevocation;
2451
2452 const SECS_IN_YEAR : u64 = 365 * 24 * 60 * 60;
2453
2454 // A `const fn` is only guaranteed to be evaluated at compile
2455 // time if the result is assigned to a `const` variable. Make
2456 // sure that works.
2457 const DEFAULT : StandardPolicy = StandardPolicy::new();
2458
2459 let (cert, _) = CertBuilder::new()
2460 .add_userid("Alice")
2461 .generate()?;
2462
2463 let algo = cert.primary_key()
2464 .binding_signature(&DEFAULT, None).unwrap().hash_algo();
2465
2466 eprintln!("{:?}", algo);
2467
2468 // Create a revoked version.
2469 let mut keypair = cert.primary_key().key().clone()
2470 .parts_into_secret()?.into_keypair()?;
2471 let rev = cert.revoke(
2472 &mut keypair,
2473 ReasonForRevocation::KeyCompromised,
2474 b"It was the maid :/")?;
2475 let cert_revoked = cert.clone().insert_packets(rev)?.0;
2476
2477 match cert_revoked.revocation_status(&DEFAULT, None) {
2478 RevocationStatus::Revoked(sigs) => {
2479 assert_eq!(sigs.len(), 1);
2480 assert_eq!(sigs[0].hash_algo(), algo);
2481 }
2482 _ => panic!("not revoked"),
2483 }
2484
2485
2486 // Reject the hash algorithm unconditionally.
2487 let mut reject : StandardPolicy = StandardPolicy::new();
2488 reject.reject_hash(algo);
2489 assert!(cert.primary_key()
2490 .binding_signature(&reject, None).is_err());
2491 assert_match!(RevocationStatus::NotAsFarAsWeKnow
2492 = cert_revoked.revocation_status(&reject, None));
2493
2494 // Reject the hash algorithm next year.
2495 let mut reject : StandardPolicy = StandardPolicy::new();
2496 reject.reject_hash_at(
2497 algo,
2498 crate::now().checked_add(Duration::from_secs(SECS_IN_YEAR)));
2499 reject.hash_revocation_tolerance(0);
2500 cert.primary_key().binding_signature(&reject, None)?;
2501 assert_match!(RevocationStatus::Revoked(_)
2502 = cert_revoked.revocation_status(&reject, None));
2503
2504 // Reject the hash algorithm last year.
2505 let mut reject : StandardPolicy = StandardPolicy::new();
2506 reject.reject_hash_at(
2507 algo,
2508 crate::now().checked_sub(Duration::from_secs(SECS_IN_YEAR)));
2509 reject.hash_revocation_tolerance(0);
2510 assert!(cert.primary_key()
2511 .binding_signature(&reject, None).is_err());
2512 assert_match!(RevocationStatus::NotAsFarAsWeKnow
2513 = cert_revoked.revocation_status(&reject, None));
2514
2515 // Reject the hash algorithm for normal signatures last year,
2516 // and revocations next year.
2517 let mut reject : StandardPolicy = StandardPolicy::new();
2518 reject.reject_hash_at(
2519 algo,
2520 crate::now().checked_sub(Duration::from_secs(SECS_IN_YEAR)));
2521 reject.hash_revocation_tolerance(2 * SECS_IN_YEAR as u32);
2522 assert!(cert.primary_key()
2523 .binding_signature(&reject, None).is_err());
2524 assert_match!(RevocationStatus::Revoked(_)
2525 = cert_revoked.revocation_status(&reject, None));
2526
2527 // Accept algo, but reject the algos with id - 1 and id + 1.
2528 let mut reject : StandardPolicy = StandardPolicy::new();
2529 let algo_u8 : u8 = algo.into();
2530 assert!(algo_u8 != 0u8);
2531 reject.reject_hash_at(
2532 (algo_u8 - 1).into(),
2533 crate::now().checked_sub(Duration::from_secs(SECS_IN_YEAR)));
2534 reject.reject_hash_at(
2535 (algo_u8 + 1).into(),
2536 crate::now().checked_sub(Duration::from_secs(SECS_IN_YEAR)));
2537 reject.hash_revocation_tolerance(0);
2538 cert.primary_key().binding_signature(&reject, None)?;
2539 assert_match!(RevocationStatus::Revoked(_)
2540 = cert_revoked.revocation_status(&reject, None));
2541
2542 // Reject the hash algorithm since before the Unix epoch.
2543 // Since the earliest representable time using a Timestamp is
2544 // the Unix epoch, this is equivalent to rejecting everything.
2545 let mut reject : StandardPolicy = StandardPolicy::new();
2546 reject.reject_hash_at(
2547 algo,
2548 crate::now().checked_sub(Duration::from_secs(SECS_IN_YEAR)));
2549 reject.hash_revocation_tolerance(0);
2550 assert!(cert.primary_key()
2551 .binding_signature(&reject, None).is_err());
2552 assert_match!(RevocationStatus::NotAsFarAsWeKnow
2553 = cert_revoked.revocation_status(&reject, None));
2554
2555 // Reject the hash algorithm after the end of time that is
2556 // representable by a Timestamp (2106). This should accept
2557 // everything.
2558 let mut reject : StandardPolicy = StandardPolicy::new();
2559 reject.reject_hash_at(
2560 algo,
2561 SystemTime::UNIX_EPOCH.checked_add(Duration::from_secs(500 * SECS_IN_YEAR)));
2562 reject.hash_revocation_tolerance(0);
2563 cert.primary_key().binding_signature(&reject, None)?;
2564 assert_match!(RevocationStatus::Revoked(_)
2565 = cert_revoked.revocation_status(&reject, None));
2566
2567 Ok(())
2568 }
2569
2570 #[test]
2571 fn key_verify_self_signature() -> Result<()> {
2572 let p = &P::new();
2573
2574 #[derive(Debug)]
2575 struct NoRsa;
2576 impl Policy for NoRsa {
2577 fn key(&self, ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2578 -> Result<()>
2579 {
2580 use crate::types::PublicKeyAlgorithm::*;
2581
2582 eprintln!("algo: {}", ka.key().pk_algo());
2583 if ka.key().pk_algo() == RSAEncryptSign {
2584 Err(anyhow::anyhow!("RSA!"))
2585 } else {
2586 Ok(())
2587 }
2588 }
2589
2590 fn signature(&self, _sig: &Signature, _sec: HashAlgoSecurity) -> Result<()> {
2591 Ok(())
2592 }
2593
2594 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2595 Ok(())
2596 }
2597
2598 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2599 Ok(())
2600 }
2601
2602 fn packet(&self, _packet: &Packet) -> Result<()> {
2603 Ok(())
2604 }
2605 }
2606 let norsa = &NoRsa {};
2607
2608 // Generate a certificate with an RSA primary and two RSA
2609 // subkeys.
2610 let (cert,_) = CertBuilder::new()
2611 .set_cipher_suite(CipherSuite::RSA2k)
2612 .add_signing_subkey()
2613 .add_signing_subkey()
2614 .generate()?;
2615 assert_eq!(cert.keys().with_policy(p, None).count(), 3);
2616 assert_eq!(cert.keys().with_policy(norsa, None).count(), 0);
2617 assert!(cert.primary_key().with_policy(p, None).is_ok());
2618 assert!(cert.primary_key().with_policy(norsa, None).is_err());
2619
2620 // Generate a certificate with an ECC primary, an ECC subkey,
2621 // and an RSA subkey.
2622 let (cert,_) = CertBuilder::new()
2623 .set_cipher_suite(CipherSuite::Cv25519)
2624 .add_signing_subkey()
2625 .generate()?;
2626
2627 let pk = cert.primary_key().key().parts_as_secret()?;
2628 let subkey: key::SecretSubkey
2629 = Key4::generate_rsa(2048)?.into();
2630 let binding = signature::SignatureBuilder::new(SignatureType::SubkeyBinding)
2631 .set_key_flags(KeyFlags::empty().set_transport_encryption())?
2632 .sign_subkey_binding(&mut pk.clone().into_keypair()?,
2633 pk.parts_as_public(), &subkey)?;
2634
2635 let cert = cert.insert_packets(
2636 vec![ Packet::from(subkey), binding.into() ])?.0;
2637
2638 assert_eq!(cert.keys().with_policy(p, None).count(), 3);
2639 assert_eq!(cert.keys().with_policy(norsa, None).count(), 2);
2640 assert!(cert.primary_key().with_policy(p, None).is_ok());
2641 assert!(cert.primary_key().with_policy(norsa, None).is_ok());
2642
2643 // Generate a certificate with an RSA primary, an RSA subkey,
2644 // and an ECC subkey.
2645 let (cert,_) = CertBuilder::new()
2646 .set_cipher_suite(CipherSuite::RSA2k)
2647 .add_signing_subkey()
2648 .generate()?;
2649
2650 let pk = cert.primary_key().key().parts_as_secret()?;
2651 let subkey: key::SecretSubkey
2652 = key::Key6::generate_ecc(true, Curve::Ed25519)?.into();
2653 let binding = signature::SignatureBuilder::new(SignatureType::SubkeyBinding)
2654 .set_key_flags(KeyFlags::empty().set_transport_encryption())?
2655 .sign_subkey_binding(&mut pk.clone().into_keypair()?,
2656 pk.parts_as_public(), &subkey)?;
2657
2658 let cert = cert.insert_packets(
2659 vec![ Packet::from(subkey), binding.into() ])?.0;
2660
2661 assert_eq!(cert.keys().with_policy(p, None).count(), 3);
2662 assert_eq!(cert.keys().with_policy(norsa, None).count(), 0);
2663 assert!(cert.primary_key().with_policy(p, None).is_ok());
2664 assert!(cert.primary_key().with_policy(norsa, None).is_err());
2665
2666 // Generate a certificate with an ECC primary and two ECC
2667 // subkeys.
2668 let (cert,_) = CertBuilder::new()
2669 .set_cipher_suite(CipherSuite::Cv25519)
2670 .add_signing_subkey()
2671 .add_signing_subkey()
2672 .generate()?;
2673 assert_eq!(cert.keys().with_policy(p, None).count(), 3);
2674 assert_eq!(cert.keys().with_policy(norsa, None).count(), 3);
2675 assert!(cert.primary_key().with_policy(p, None).is_ok());
2676 assert!(cert.primary_key().with_policy(norsa, None).is_ok());
2677
2678 Ok(())
2679 }
2680
2681 #[test]
2682 fn key_verify_binary_signature() -> Result<()> {
2683 use crate::packet::signature;
2684 use crate::serialize::SerializeInto;
2685 use crate::Packet;
2686 use crate::types::KeyFlags;
2687
2688 let p = &P::new();
2689
2690 #[derive(Debug)]
2691 struct NoRsa;
2692 impl Policy for NoRsa {
2693 fn key(&self, ka: &ValidErasedKeyAmalgamation<key::PublicParts>)
2694 -> Result<()>
2695 {
2696 use crate::types::PublicKeyAlgorithm::*;
2697
2698 eprintln!("algo: {} is {}",
2699 ka.key().fingerprint(), ka.key().pk_algo());
2700 if ka.key().pk_algo() == RSAEncryptSign {
2701 Err(anyhow::anyhow!("RSA!"))
2702 } else {
2703 Ok(())
2704 }
2705 }
2706
2707 fn signature(&self, _sig: &Signature, _sec: HashAlgoSecurity) -> Result<()> {
2708 Ok(())
2709 }
2710
2711 fn symmetric_algorithm(&self, _algo: SymmetricAlgorithm) -> Result<()> {
2712 Ok(())
2713 }
2714
2715 fn aead_algorithm(&self, _algo: AEADAlgorithm) -> Result<()> {
2716 Ok(())
2717 }
2718
2719 fn packet(&self, _packet: &Packet) -> Result<()> {
2720 Ok(())
2721 }
2722 }
2723 let norsa = &NoRsa {};
2724
2725 #[derive(PartialEq, Debug)]
2726 struct VHelper {
2727 good: usize,
2728 errors: usize,
2729 keys: Vec<Cert>,
2730 }
2731
2732 impl VHelper {
2733 fn new(keys: Vec<Cert>) -> Self {
2734 VHelper {
2735 good: 0,
2736 errors: 0,
2737 keys,
2738 }
2739 }
2740 }
2741
2742 impl VerificationHelper for VHelper {
2743 fn get_certs(&mut self, _ids: &[crate::KeyHandle])
2744 -> Result<Vec<Cert>>
2745 {
2746 Ok(self.keys.clone())
2747 }
2748
2749 fn check(&mut self, structure: MessageStructure) -> Result<()>
2750 {
2751 for layer in structure {
2752 match layer {
2753 MessageLayer::SignatureGroup { ref results } =>
2754 for result in results {
2755 match result {
2756 Ok(_) => self.good += 1,
2757 Err(e) => {
2758 eprintln!("{}", e);
2759 self.errors += 1
2760 },
2761 }
2762 }
2763 MessageLayer::Compression { .. } => (),
2764 _ => unreachable!(),
2765 }
2766 }
2767
2768 Ok(())
2769 }
2770 }
2771
2772 impl DecryptionHelper for VHelper {
2773 fn decrypt(&mut self, _: &[PKESK], _: &[SKESK],
2774 _: Option<SymmetricAlgorithm>,
2775 _: &mut dyn FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool)
2776 -> Result<Option<Cert>>
2777 {
2778 unreachable!();
2779 }
2780 }
2781
2782 // Sign msg using cert's first subkey, return the signature.
2783 fn sign_and_verify(p: &dyn Policy, cert: &Cert, good: bool) {
2784 eprintln!("Expect verification to be {}",
2785 if good { "good" } else { "bad" });
2786 for (i, k) in cert.keys().enumerate() {
2787 eprintln!(" {}. {}", i, k.key().fingerprint());
2788 }
2789
2790 let msg = b"Hello, World";
2791
2792 // We always use the first subkey.
2793 let key = cert.keys().nth(1).unwrap().key();
2794 let mut keypair = key.clone()
2795 .parts_into_secret().unwrap()
2796 .into_keypair().unwrap();
2797
2798 // Create a signature.
2799 let sig =
2800 signature::SignatureBuilder::new(SignatureType::Binary)
2801 .sign_message(&mut keypair, msg).unwrap();
2802
2803 // Make sure the signature is ok.
2804 sig.verify_message(key, msg).unwrap();
2805
2806 // Turn it into a detached signature.
2807 let sig = Packet::from(sig).to_vec().unwrap();
2808
2809 let h = VHelper::new(vec![ cert.clone() ]);
2810 let mut v = DetachedVerifierBuilder::from_bytes(&sig).unwrap()
2811 .with_policy(p, None, h).unwrap();
2812 v.verify_bytes(msg).unwrap();
2813 assert_eq!(v.helper_ref().good, if good { 1 } else { 0 });
2814 assert_eq!(v.helper_ref().errors, if good { 0 } else { 1 });
2815 }
2816
2817
2818 // A certificate with an ECC primary and an ECC signing
2819 // subkey.
2820 eprintln!("Trying ECC primary, ECC sub:");
2821 let (cert,_) = CertBuilder::new()
2822 .set_cipher_suite(CipherSuite::Cv25519)
2823 .add_subkey(KeyFlags::empty().set_signing(), None,
2824 None)
2825 .generate()?;
2826
2827 assert_eq!(cert.keys().with_policy(p, None).count(), 2);
2828 assert_eq!(cert.keys().with_policy(norsa, None).count(), 2);
2829 assert!(cert.primary_key().with_policy(p, None).is_ok());
2830 assert!(cert.primary_key().with_policy(norsa, None).is_ok());
2831
2832 sign_and_verify(p, &cert, true);
2833 sign_and_verify(norsa, &cert, true);
2834
2835 // A certificate with an RSA primary and an RCC signing
2836 // subkey.
2837 eprintln!("Trying RSA primary, ECC sub:");
2838 let (cert,_) = CertBuilder::new()
2839 .set_cipher_suite(CipherSuite::RSA2k)
2840 .add_subkey(KeyFlags::empty().set_signing(), None,
2841 CipherSuite::Cv25519)
2842 .generate()?;
2843
2844 assert_eq!(cert.keys().with_policy(p, None).count(), 2);
2845 assert_eq!(cert.keys().with_policy(norsa, None).count(), 0);
2846 assert!(cert.primary_key().with_policy(p, None).is_ok());
2847 assert!(cert.primary_key().with_policy(norsa, None).is_err());
2848
2849 sign_and_verify(p, &cert, true);
2850 sign_and_verify(norsa, &cert, false);
2851
2852 // A certificate with an ECC primary and an RSA signing
2853 // subkey.
2854 eprintln!("Trying ECC primary, RSA sub:");
2855 let (cert,_) = CertBuilder::new()
2856 .set_cipher_suite(CipherSuite::Cv25519)
2857 .add_subkey(KeyFlags::empty().set_signing(), None,
2858 CipherSuite::RSA2k)
2859 .generate()?;
2860
2861 assert_eq!(cert.keys().with_policy(p, None).count(), 2);
2862 assert_eq!(cert.keys().with_policy(norsa, None).count(), 1);
2863 assert!(cert.primary_key().with_policy(p, None).is_ok());
2864 assert!(cert.primary_key().with_policy(norsa, None).is_ok());
2865
2866 sign_and_verify(p, &cert, true);
2867 sign_and_verify(norsa, &cert, false);
2868
2869 Ok(())
2870 }
2871
2872 #[test]
2873 fn reject_seip_packet() -> Result<()> {
2874 #[derive(PartialEq, Debug)]
2875 struct Helper {}
2876 impl VerificationHelper for Helper {
2877 fn get_certs(&mut self, _: &[crate::KeyHandle])
2878 -> Result<Vec<Cert>> {
2879 unreachable!()
2880 }
2881
2882 fn check(&mut self, _: MessageStructure) -> Result<()> {
2883 unreachable!()
2884 }
2885 }
2886
2887 impl DecryptionHelper for Helper {
2888 fn decrypt(&mut self, _: &[PKESK], _: &[SKESK],
2889 _: Option<SymmetricAlgorithm>,
2890 _: &mut dyn FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool)
2891 -> Result<Option<Cert>>
2892 {
2893 Ok(None)
2894 }
2895 }
2896
2897 let p = &P::new();
2898 let r = DecryptorBuilder::from_bytes(crate::tests::message(
2899 "encrypted-to-testy.pgp"))?
2900 .with_policy(p, crate::frozen_time(), Helper {});
2901 match r {
2902 Ok(_) => panic!(),
2903 Err(e) => assert_match!(Error::MissingSessionKey(_)
2904 = e.downcast().unwrap()),
2905 }
2906
2907 // Reject the SEIP packet.
2908 let p = &mut P::new();
2909 p.reject_packet_tag(Tag::SEIP);
2910 let r = DecryptorBuilder::from_bytes(crate::tests::message(
2911 "encrypted-to-testy.pgp"))?
2912 .with_policy(p, crate::frozen_time(), Helper {});
2913 match r {
2914 Ok(_) => panic!(),
2915 Err(e) => assert_match!(Error::PolicyViolation(_, _)
2916 = e.downcast().unwrap()),
2917 }
2918 Ok(())
2919 }
2920
2921 #[test]
2922 fn reject_cipher() -> Result<()> {
2923 struct Helper {}
2924 impl VerificationHelper for Helper {
2925 fn get_certs(&mut self, _: &[crate::KeyHandle])
2926 -> Result<Vec<Cert>> {
2927 Ok(Default::default())
2928 }
2929
2930 fn check(&mut self, _: MessageStructure) -> Result<()> {
2931 Ok(())
2932 }
2933 }
2934
2935 impl DecryptionHelper for Helper {
2936 fn decrypt(&mut self, pkesks: &[PKESK], _: &[SKESK],
2937 algo: Option<SymmetricAlgorithm>,
2938 decrypt: &mut dyn FnMut(Option<SymmetricAlgorithm>, &SessionKey) -> bool)
2939 -> Result<Option<Cert>>
2940 {
2941 let p = &P::new();
2942 let mut pair = Cert::from_bytes(
2943 crate::tests::key("testy-private.pgp"))?
2944 .keys().with_policy(p, None)
2945 .for_transport_encryption().secret().next().unwrap()
2946 .key().clone().into_keypair()?;
2947 pkesks[0].decrypt(&mut pair, algo)
2948 .map(|(algo, session_key)| decrypt(algo, &session_key));
2949 Ok(None)
2950 }
2951 }
2952
2953 let p = &P::new();
2954 DecryptorBuilder::from_bytes(crate::tests::message(
2955 "encrypted-to-testy-no-compression.pgp"))?
2956 .with_policy(p, crate::frozen_time(), Helper {})?;
2957
2958 // Reject the AES256.
2959 let p = &mut P::new();
2960 p.reject_symmetric_algo(SymmetricAlgorithm::AES256);
2961 let r = DecryptorBuilder::from_bytes(crate::tests::message(
2962 "encrypted-to-testy-no-compression.pgp"))?
2963 .with_policy(p, crate::frozen_time(), Helper {});
2964 match r {
2965 Ok(_) => panic!(),
2966 Err(e) => assert_match!(Error::PolicyViolation(_, _)
2967 = e.downcast().unwrap()),
2968 }
2969 Ok(())
2970 }
2971
2972 #[test]
2973 fn reject_asymmetric_algos() -> Result<()> {
2974 let cert = Cert::from_bytes(crate::tests::key("neal.pgp"))?;
2975 let p = &mut P::new();
2976 let t = crate::frozen_time();
2977
2978 assert_eq!(cert.with_policy(p, t).unwrap().keys().count(), 4);
2979 p.reject_asymmetric_algo(AsymmetricAlgorithm::RSA1024);
2980 assert_eq!(cert.with_policy(p, t).unwrap().keys().count(), 4);
2981 p.reject_asymmetric_algo(AsymmetricAlgorithm::RSA2048);
2982 assert_eq!(cert.with_policy(p, t).unwrap().keys().count(), 1);
2983 Ok(())
2984 }
2985
2986 #[test]
2987 fn reject_all_hashes() -> Result<()> {
2988 let mut p = StandardPolicy::new();
2989
2990 let set_variants = [
2991 HashAlgorithm::MD5,
2992 HashAlgorithm::Unknown(234),
2993 ];
2994 let check_variants = [
2995 HashAlgorithm::SHA512,
2996 HashAlgorithm::Unknown(239),
2997 ];
2998
2999 // Accept a few hashes explicitly.
3000 for v in set_variants.iter().cloned() {
3001 p.accept_hash(v);
3002 assert_eq!(
3003 p.hash_cutoff(
3004 v,
3005 HashAlgoSecurity::SecondPreImageResistance),
3006 ACCEPT.map(Into::into));
3007 assert_eq!(
3008 p.hash_cutoff(
3009 v,
3010 HashAlgoSecurity::CollisionResistance),
3011 ACCEPT.map(Into::into));
3012 }
3013
3014 // Reject all hashes.
3015 p.reject_all_hashes();
3016
3017 for v in set_variants.iter().chain(check_variants.iter()).cloned() {
3018 assert_eq!(
3019 p.hash_cutoff(
3020 v,
3021 HashAlgoSecurity::SecondPreImageResistance),
3022 REJECT.map(Into::into));
3023 assert_eq!(
3024 p.hash_cutoff(
3025 v,
3026 HashAlgoSecurity::CollisionResistance),
3027 REJECT.map(Into::into));
3028 }
3029
3030 Ok(())
3031 }
3032
3033 macro_rules! reject_all_check {
3034 ($reject_all:ident, $accept_one:ident, $cutoff:ident,
3035 $set_variants:expr, $check_variants:expr) => {
3036 #[test]
3037 fn $reject_all() -> Result<()> {
3038 let mut p = StandardPolicy::new();
3039
3040 // Accept a few hashes explicitly.
3041 for v in $set_variants.iter().cloned() {
3042 p.$accept_one(v);
3043 assert_eq!(p.$cutoff(v), ACCEPT.map(Into::into));
3044 }
3045
3046 // Reject all hashes.
3047 p.$reject_all();
3048
3049 for v in $set_variants.iter()
3050 .chain($check_variants.iter()).cloned()
3051 {
3052 assert_eq!(
3053 p.$cutoff(v),
3054 REJECT.map(Into::into));
3055 }
3056 Ok(())
3057 }
3058 }
3059 }
3060
3061 reject_all_check!(reject_all_critical_subpackets,
3062 accept_critical_subpacket,
3063 critical_subpacket_cutoff,
3064 &[ SubpacketTag::TrustSignature,
3065 SubpacketTag::Unknown(252) ],
3066 &[ SubpacketTag::Unknown(253),
3067 SubpacketTag::SignatureCreationTime ]);
3068
3069 reject_all_check!(reject_all_asymmetric_algos,
3070 accept_asymmetric_algo,
3071 asymmetric_algo_cutoff,
3072 &[ AsymmetricAlgorithm::RSA3072,
3073 AsymmetricAlgorithm::Cv25519 ],
3074 &[ AsymmetricAlgorithm::Unknown,
3075 AsymmetricAlgorithm::NistP256 ]);
3076
3077 reject_all_check!(reject_all_symmetric_algos,
3078 accept_symmetric_algo,
3079 symmetric_algo_cutoff,
3080 &[ SymmetricAlgorithm::Unencrypted,
3081 SymmetricAlgorithm::Unknown(252) ],
3082 &[ SymmetricAlgorithm::AES256,
3083 SymmetricAlgorithm::Unknown(230) ]);
3084
3085 reject_all_check!(reject_all_aead_algos,
3086 accept_aead_algo,
3087 aead_algo_cutoff,
3088 &[ AEADAlgorithm::OCB ],
3089 &[ AEADAlgorithm::EAX ]);
3090
3091 #[test]
3092 fn reject_all_packets() -> Result<()> {
3093 let mut p = StandardPolicy::new();
3094
3095 let set_variants = [
3096 (Tag::SEIP, 4),
3097 (Tag::Unknown(252), 17),
3098 ];
3099 let check_variants = [
3100 (Tag::Signature, 4),
3101 (Tag::Unknown(230), 9),
3102 ];
3103
3104 // Accept a few packets explicitly.
3105 for (t, v) in set_variants.iter().cloned() {
3106 p.accept_packet_tag_version(t, v);
3107 assert_eq!(
3108 p.packet_tag_version_cutoff(t, v),
3109 ACCEPT.map(Into::into));
3110 }
3111
3112 // Reject all hashes.
3113 p.reject_all_packet_tags();
3114
3115 for (t, v) in set_variants.iter().chain(check_variants.iter()).cloned() {
3116 assert_eq!(
3117 p.packet_tag_version_cutoff(t, v),
3118 REJECT.map(Into::into));
3119 }
3120
3121 Ok(())
3122 }
3123
3124 #[test]
3125 fn packet_versions() -> Result<()> {
3126 // Accept the version of a packet. Optionally make sure a
3127 // different version is not accepted.
3128 fn accept_and_check(p: &mut StandardPolicy,
3129 tag: Tag,
3130 accept_versions: &[u8],
3131 good_versions: &[u8],
3132 bad_versions: &[u8]) {
3133 for v in accept_versions {
3134 p.accept_packet_tag_version(tag, *v);
3135 assert_eq!(
3136 p.packet_tag_version_cutoff(tag, *v),
3137 ACCEPT.map(Into::into));
3138 }
3139
3140 for v in good_versions.iter() {
3141 assert_eq!(
3142 p.packet_tag_version_cutoff(tag, *v),
3143 ACCEPT.map(Into::into));
3144 }
3145 for v in bad_versions.iter() {
3146 assert_eq!(
3147 p.packet_tag_version_cutoff(tag, *v),
3148 REJECT.map(Into::into));
3149 }
3150 }
3151
3152 use rand::seq::SliceRandom;
3153 let mut rng = rand::rng();
3154
3155 let mut all_versions = (0..=u8::MAX).collect::<Vec<_>>();
3156 all_versions.shuffle(&mut rng);
3157 let all_versions = &all_versions[..];
3158 let mut not_v5 = all_versions.iter()
3159 .filter(|&&v| v != 5)
3160 .cloned()
3161 .collect::<Vec<_>>();
3162 not_v5.shuffle(&mut rng);
3163 let not_v5 = ¬_v5[..];
3164
3165 let p = &mut StandardPolicy::new();
3166 p.reject_all_packet_tags();
3167
3168 // First only use the versioned interfaces.
3169 accept_and_check(p, Tag::Signature, &[3], &[], &[4, 5]);
3170 accept_and_check(p, Tag::Signature, &[4], &[3], &[5]);
3171
3172 // Only use an unversioned policy.
3173 accept_and_check(p, Tag::SEIP,
3174 &[], // set to accept
3175 &[], // good
3176 all_versions, // bad
3177 );
3178 p.accept_packet_tag(Tag::SEIP);
3179 accept_and_check(p, Tag::SEIP,
3180 &[], // set to accept
3181 all_versions, // good
3182 &[], // bad
3183 );
3184
3185 // Set an unversioned policy and then a versioned policy.
3186 accept_and_check(p, Tag::PKESK,
3187 &[], // set to accept
3188 &[], // good
3189 all_versions, // bad
3190 );
3191 p.accept_packet_tag(Tag::PKESK);
3192 accept_and_check(p, Tag::PKESK,
3193 &[], // set to accept
3194 &(0..u8::MAX).collect::<Vec<_>>()[..], // good
3195 &[], // bad
3196 );
3197 p.reject_packet_tag_version(Tag::PKESK, 5);
3198 accept_and_check(p, Tag::PKESK,
3199 &[], // set to accept
3200 not_v5, // good
3201 &[5], // bad
3202 );
3203
3204 // Set a versioned policy and then an unversioned policy.
3205 // Make sure that the versioned policy is cleared by the
3206 // unversioned policy.
3207 accept_and_check(p, Tag::SKESK,
3208 &[], // set to accept
3209 &[], // good
3210 all_versions, // bad
3211 );
3212 p.accept_packet_tag_version(Tag::SKESK, 5);
3213 accept_and_check(p, Tag::SKESK,
3214 &[], // set to accept
3215 &[5], // good
3216 not_v5, // bad
3217 );
3218 p.reject_packet_tag(Tag::SKESK);
3219 // All versions should be bad now...
3220 accept_and_check(p, Tag::SKESK,
3221 &[], // set to accept
3222 &[], // good
3223 all_versions, // bad
3224 );
3225
3226 Ok(())
3227 }
3228}