[][src]Struct sequoia_openpgp::policy::StandardPolicy

pub struct StandardPolicy<'a> { /* fields omitted */ }

The standard policy.

The standard policy stores when each algorithm in a family of algorithms is no longer considered safe. Attempts to use an algorithm after its cutoff time should fail.

When validating a signature, we normally want to know whether the algorithms used are safe now. That is, we don't use the signature's alleged creation time when considering whether an algorithm is safe, because if an algorithm is discovered to be compromised at time X, then an attacker could forge a message after time X with a signature creation time that is prior to X, which would be incorrectly accepted.

Occasionally, we know that a signature has not been tampered with since some time in the past. We might know this if the signature was stored on some tamper-proof medium. In those cases, it is reasonable to use the time that the signature was saved, since an attacker could not have taken advantage of any weaknesses found after that time.

Examples

A StandardPolicy object can be used to build specialized policies. For example the following policy filters out Persona certifications mimicking what GnuPG does when calculating the Web of Trust.

use sequoia_openpgp as openpgp;
use std::io::{Cursor, Read};
use openpgp::Result;
use openpgp::packet::{Packet, Signature, key::PublicParts};
use openpgp::cert::prelude::*;
use openpgp::parse::Parse;
use openpgp::armor::{Reader, ReaderMode, Kind};
use openpgp::policy::{HashAlgoSecurity, Policy, StandardPolicy};
use openpgp::types::{
   SymmetricAlgorithm,
   AEADAlgorithm,
   SignatureType
};

#[derive(Debug)]
struct RejectPersonaCertificationsPolicy<'a>(StandardPolicy<'a>);

impl Policy for RejectPersonaCertificationsPolicy<'_> {
    fn key(&self, ka: &ValidErasedKeyAmalgamation<PublicParts>)
           -> Result<()>
    {
        self.0.key(ka)
    }

    fn signature(&self, sig: &Signature, sec: HashAlgoSecurity) -> Result<()> {
        if sig.typ() == SignatureType::PersonaCertification {
            Err(anyhow::anyhow!("Persona certifications are ignored."))
        } else {
            self.0.signature(sig, sec)
        }
    }

    fn symmetric_algorithm(&self, algo: SymmetricAlgorithm) -> Result<()> {
        self.0.symmetric_algorithm(algo)
    }

    fn aead_algorithm(&self, algo: AEADAlgorithm) -> Result<()> {
        self.0.aead_algorithm(algo)
    }

    fn packet(&self, packet: &Packet) -> Result<()> {
        self.0.packet(packet)
    }
}

impl RejectPersonaCertificationsPolicy<'_> {
    fn new() -> Self {
        Self(StandardPolicy::new())
    }
}

// this key has one persona certification
let data = r#"
-----BEGIN PGP PUBLIC KEY BLOCK-----

mDMEX7JGrxYJKwYBBAHaRw8BAQdASKGcnowaZBDc2Z3rZZlWb6jEjne9sK76afbJ
trd5Uw+0BlRlc3QgMoiQBBMWCAA4FiEEyZ6oBYFia3z+ooCBqR9BqiGp8AQFAl+y
Rq8CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQqR9BqiGp8ASfxwEAvEb0
bFr7ZgFZSDOITNptm+FEynib8mmLACsvHAmCjvIA+gOaSNyxMW6N59q7/j0sDjp1
aYNgpNFLbYBZpkXXVL0GiHUEERYIAB0WIQTE4QfdkkisIbWVOcHmlsuS3dbWEwUC
X7JG4gAKCRDmlsuS3dbWExEwAQCpqfiVMhjDwVFMsMpwd5r0N/8rAx8/nmgpCsK3
M9TUrAD7BhTYVPRbkJqTZYd9DlLtBcbF3yNPTHlB+F2sFjI+cgo=
=ZfYu
-----END PGP PUBLIC KEY BLOCK-----
"#;

let mut cursor = Cursor::new(&data);
let mut reader = Reader::new(&mut cursor, ReaderMode::Tolerant(Some(Kind::PublicKey)));

let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
let cert = Cert::from_bytes(&buf)?;

let ref sp = StandardPolicy::new();
let u = cert.with_policy(sp, None)?.userids().nth(0).unwrap();

// Under the standard policy the persona certification is visible.
assert_eq!(u.certifications().count(), 1);

// Under our custom policy the persona certification is not available.
let ref p = RejectPersonaCertificationsPolicy::new();
assert_eq!(u.with_policy(p, None)?.certifications().count(), 0);

Implementations

impl<'a> StandardPolicy<'a>[src]

pub const fn new() -> Self[src]

Instantiates a new StandardPolicy with the default parameters.

pub fn at<T>(time: T) -> Self where
    T: Into<SystemTime>, 
[src]

Instantiates a new StandardPolicy with parameters appropriate for time.

time is a meta-parameter that selects a security profile that is appropriate for the given point in time. When evaluating an object, the reference time should be set to the time that the object was stored to non-tamperable storage. Since most applications don't record when they received an object, they should conservatively use the current time.

Note that the reference time is a security parameter and is different from the time that the object was allegedly created. Consider evaluating a signature whose Signature Creation Time subpacket indicates that it was created in 2007. Since the subpacket is under the control of the sender, setting the reference time according to the subpacket means that the sender chooses the security profile. If the sender were an attacker, she could have forged this to take advantage of security weaknesses found since 2007. This is why the reference time must be set---at the earliest---to the time that the message was stored to non-tamperable storage. When that is not available, the current time should be used.

pub fn time(&self) -> Option<SystemTime>[src]

Returns the policy's reference time.

The current time is None.

See StandardPolicy::at for details.

pub fn accept_hash(&mut self, h: HashAlgorithm)[src]

Always considers h to be secure.

A cryptographic hash algorithm normally has three security properties:

  • Pre-image resistance,
  • Second pre-image resistance, and
  • Collision resistance.

A hash algorithm should only be unconditionally accepted if it has all three of these properties. See the documentation for HashAlgoSecurity for more details.

pub fn reject_hash(&mut self, h: HashAlgorithm)[src]

Considers h to be insecure in all security contexts.

A cryptographic hash algorithm normally has three security properties:

  • Pre-image resistance,
  • Second pre-image resistance, and
  • Collision resistance.

This method causes the hash algorithm to be considered unsafe in all security contexts.

See the documentation for HashAlgoSecurity for more details.

To express a more nuanced policy, use StandardPolicy::reject_hash_at or StandardPolicy::reject_hash_property_at.

pub fn reject_hash_at<T>(&mut self, h: HashAlgorithm, t: T) where
    T: Into<Option<SystemTime>>, 
[src]

Considers h to be insecure in all security contexts starting at time t.

A cryptographic hash algorithm normally has three security properties:

  • Pre-image resistance,
  • Second pre-image resistance, and
  • Collision resistance.

This method causes the hash algorithm to be considered unsafe in all security contexts starting at time t.

See the documentation for HashAlgoSecurity for more details.

To express a more nuanced policy, use StandardPolicy::reject_hash_property_at.

pub fn reject_hash_property_at<T>(
    &mut self,
    h: HashAlgorithm,
    sec: HashAlgoSecurity,
    t: T
) where
    T: Into<Option<SystemTime>>, 
[src]

Considers h to be insecure starting at t for the specified security property.

A hash algorithm is considered secure if it has all of the following security properties:

  • Pre-image resistance,
  • Second pre-image resistance, and
  • Collision resistance.

Some contexts only require a subset of these security properties. Specifically, if an attacker is unable to influence the data that a user signs, then the hash algorithm only needs second pre-image resistance; it doesn't need collision resistance. See the documentation for HashAlgoSecurity for more details.

This method makes it possible to specify different policies depending on the security requirements.

A cutoff of None means that there is no cutoff and the algorithm has no known vulnerabilities for the specified security policy.

As a rule of thumb, collision resistance is easier to attack than second pre-image resistance. And in practice there are practical attacks against several widely-used hash algorithms' collision resistance, but only theoretical attacks against their second pre-image resistance. Nevertheless, once one property of a hash has been compromised, we want to deprecate its use as soon as it is feasible. Unfortunately, because OpenPGP certificates are long-lived, this can take years.

Given this, we start rejecting MD5 in cases where collision resistance is required in 1997 and completely reject it starting in 2004:

In 1996, Dobbertin announced a collision of the compression function of MD5 (Dobbertin, 1996). While this was not an attack on the full MD5 hash function, it was close enough for cryptographers to recommend switching to a replacement, such as SHA-1 or RIPEMD-160.

MD5CRK ended shortly after 17 August 2004, when collisions for the full MD5 were announced by Xiaoyun Wang, Dengguo Feng, Xuejia Lai, and Hongbo Yu. Their analytical attack was reported to take only one hour on an IBM p690 cluster.

(Accessed Feb. 2020.)

And we start rejecting SHA-1 in cases where collision resistance is required in 2013, and completely reject it in 2023:

Since 2005 SHA-1 has not been considered secure against well-funded opponents, as of 2010 many organizations have recommended its replacement. NIST formally deprecated use of SHA-1 in 2011 and disallowed its use for digital signatures in 2013. As of 2020, attacks against SHA-1 are as practical as against MD5; as such, it is recommended to remove SHA-1 from products as soon as possible and use instead SHA-256 or SHA-3. Replacing SHA-1 is urgent where it's used for signatures.

(Accessed Feb. 2020.)

There are two main reasons why we have decided to accept SHA-1 for so long. First, as of the end of 2020, there are still a large number of certificates that rely on SHA-1. Second, Sequoia uses a variant of SHA-1 called SHA1CD, which is able to detect and mitigate the known attacks on SHA-1's collision resistance.

Since RIPE-MD is structured similarly to SHA-1, we conservatively consider it to be broken as well. But, because it is not widely used in the OpenPGP ecosystem, we don't make provisions for it.

Note: if a context indicates that it requires collision resistance, then it requires both collision resistance and second pre-image resistance, and both policies must indicate that the hash algorithm can be safely used at the specified time.

pub fn hash_cutoff(
    &self,
    h: HashAlgorithm,
    sec: HashAlgoSecurity
) -> Option<SystemTime>
[src]

Returns the cutoff time for the specified hash algorithm and security policy.

pub fn hash_revocation_tolerance<D>(&mut self, d: D) where
    D: Into<Duration>, 
[src]

Sets the amount of time to continue to accept revocation certificates after a hash algorithm should be rejected.

Using StandardPolicy::reject_hash_at, it is possible to indicate when a hash algorithm's security has been compromised, and, as such, should no longer be accepted.

Applying this policy to revocation certificates can have some unfortunate side effects. In particular, if a certificate has been revoked using a revocation certificate that relies on a broken hash algorithm, but the most recent self signature uses a strong acceptable hash algorithm, then rejecting the revocation certificate would mean considering the certificate to not be revoked! This would be a catastrophe if the secret key material were compromised.

Unfortunately, this happens in practice. A common example appears to be a certificate that has been updated many times, and is then revoked using a revocation certificate that was generated when the certificate was generated.

Since the consequences of allowing an invalid revocation certificate are significantly less severe (a denial of service) than ignoring a valid revocation certificate (compromised confidentiality, integrity, and authentication), this option makes it possible to accept revocations using weak hash algorithms longer than other types of signatures.

By default, the standard policy accepts revocation certificates seven years after the hash they are using was initially compromised.

pub fn get_hash_revocation_tolerance(&self) -> Duration[src]

Sets the amount of time to continue to accept revocation certificates after a hash algorithm should be rejected.

See StandardPolicy::hash_revocation_tolerance for details.

pub fn accept_critical_subpacket(&mut self, s: SubpacketTag)[src]

Always considers s to be secure.

pub fn reject_critical_subpacket(&mut self, s: SubpacketTag)[src]

Always considers s to be insecure.

pub fn reject_critical_subpacket_at<C>(&mut self, s: SubpacketTag, cutoff: C) where
    C: Into<Option<SystemTime>>, 
[src]

Considers s to be insecure starting at cutoff.

A cutoff of None means that there is no cutoff and the subpacket has no known vulnerabilities.

By default, we accept all critical subpackets that Sequoia understands and honors.

pub fn critical_subpacket_cutoff(&self, s: SubpacketTag) -> Option<SystemTime>[src]

Returns the cutoff times for the specified subpacket tag.

pub fn good_critical_notations(&mut self, good_list: &'a [&'a str])[src]

Sets the list of accepted critical notations.

By default, we reject all critical notations.

pub fn accept_asymmetric_algo(&mut self, a: AsymmetricAlgorithm)[src]

Always considers s to be secure.

pub fn reject_asymmetric_algo(&mut self, a: AsymmetricAlgorithm)[src]

Always considers s to be insecure.

pub fn reject_asymmetric_algo_at<C>(
    &mut self,
    a: AsymmetricAlgorithm,
    cutoff: C
) where
    C: Into<Option<SystemTime>>, 
[src]

Considers a to be insecure starting at cutoff.

A cutoff of None means that there is no cutoff and the algorithm has no known vulnerabilities.

By default, we reject the use of asymmetric key sizes lower than 2048 bits starting in 2014 following NIST Special Publication 800-131A.

pub fn asymmetric_algo_cutoff(
    &self,
    a: AsymmetricAlgorithm
) -> Option<SystemTime>
[src]

Returns the cutoff times for the specified hash algorithm.

pub fn accept_symmetric_algo(&mut self, s: SymmetricAlgorithm)[src]

Always considers s to be secure.

pub fn reject_symmetric_algo(&mut self, s: SymmetricAlgorithm)[src]

Always considers s to be insecure.

pub fn reject_symmetric_algo_at<C>(&mut self, s: SymmetricAlgorithm, cutoff: C) where
    C: Into<Option<SystemTime>>, 
[src]

Considers s to be insecure starting at cutoff.

A cutoff of None means that there is no cutoff and the algorithm has no known vulnerabilities.

By default, we reject the use of TripleDES (3DES) starting in the year 2017. While 3DES is still a "MUST implement" algorithm in RFC4880, released in 2007, there are plenty of other symmetric algorithms defined in RFC4880, and it says AES-128 SHOULD be implemented. Support for other algorithms in OpenPGP implementations is excellent. We chose 2017 as the cutoff year because NIST deprecated 3DES that year.

pub fn symmetric_algo_cutoff(&self, s: SymmetricAlgorithm) -> Option<SystemTime>[src]

Returns the cutoff times for the specified hash algorithm.

pub fn accept_aead_algo(&mut self, a: AEADAlgorithm)[src]

Always considers s to be secure.

This feature is experimental.

pub fn reject_aead_algo(&mut self, a: AEADAlgorithm)[src]

Always considers s to be insecure.

This feature is experimental.

pub fn reject_aead_algo_at<C>(&mut self, a: AEADAlgorithm, cutoff: C) where
    C: Into<Option<SystemTime>>, 
[src]

Considers a to be insecure starting at cutoff.

A cutoff of None means that there is no cutoff and the algorithm has no known vulnerabilities.

By default, we accept all AEAD modes.

This feature is experimental.

pub fn aead_algo_cutoff(&self, a: AEADAlgorithm) -> Option<SystemTime>[src]

Returns the cutoff times for the specified hash algorithm.

This feature is experimental.

pub fn accept_packet_tag(&mut self, tag: Tag)[src]

Always accept packets with the given tag.

pub fn reject_packet_tag(&mut self, tag: Tag)[src]

Always reject packets with the given tag.

pub fn reject_packet_tag_at<C>(&mut self, tag: Tag, cutoff: C) where
    C: Into<Option<SystemTime>>, 
[src]

Start rejecting packets with the given tag at t.

A cutoff of None means that there is no cutoff and the packet has no known vulnerabilities.

By default, we consider the Symmetrically Encrypted Data Packet (SED) insecure in messages created in the year 2004 or later. The rationale here is that Symmetrically Encrypted Integrity Protected Data Packet (SEIP) can be downgraded to SED packets, enabling attacks exploiting the malleability of the CFB stream (see EFAIL).

We chose 2004 as a cutoff-date because Debian 3.0 (Woody), released on 2002-07-19, was the first release of Debian to ship a version of GnuPG that emitted SEIP packets by default. The first version that emitted SEIP packets was GnuPG 1.0.3, released on 2000-09-18. Mid 2002 plus a 18 months grace period of people still using older versions is 2004.

pub fn packet_tag_cutoff(&self, tag: Tag) -> Option<SystemTime>[src]

Returns the cutoff times for the specified hash algorithm.

Trait Implementations

impl<'a> Clone for StandardPolicy<'a>[src]

impl<'a> Debug for StandardPolicy<'a>[src]

impl<'a> Default for StandardPolicy<'a>[src]

impl<'a> From<&'a StandardPolicy<'a>> for Option<&'a dyn Policy>[src]

impl<'a> Policy for StandardPolicy<'a>[src]

Auto Trait Implementations

impl<'a> RefUnwindSafe for StandardPolicy<'a>

impl<'a> Send for StandardPolicy<'a>

impl<'a> Sync for StandardPolicy<'a>

impl<'a> Unpin for StandardPolicy<'a>

impl<'a> UnwindSafe for StandardPolicy<'a>

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> DynClone for T where
    T: Clone
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> Same<T> for T

type Output = T

Should always be Self

impl<T> ToOwned for T where
    T: Clone
[src]

type Owned = T

The resulting type after obtaining ownership.

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.