Crate sequoia_policy_config

source ·
Expand description

Configures a StandardPolicy using a configuration file.

Sequoia’s StandardPolicy can be configured using Rust. As with most things, Sequoia’s low-level library avoids imposing a policy on users of the library, like where a configuration file should be or even what format it should have. When necessary, it is up to the application to provide an interface, and to configure the policy appropriately.

This library provides a high-level interface that parses a configuration file, and returns a configured StandardPolicy.

§Format

The file format is based on TOML. It contains several sections, one for hash algorithms, one for asymmetric algorithms, etc.

§Forward Compatibility

This parser is strict, but we want the configuration format to be forwards compatible so that the same configuration file can be used with different versions of the parser.

§Dealing with New Keys

Unknown sections and unknown keys cause a parse error. To allow configuration files a degree of backwards compatibility, it is possible to set the per-map ignore_invalid key to the key or the list of keys that should be ignored if they are not recognized. For instance, if Sequoia adds support for SHA4, then you could do the following to unconditionally reject SHA4 while ensuring that the configuration is still readable by older versions of Sequoia that don’t know about SHA4:

[hash_algorithms]
ignore_invalid = [ "sha4" ]
sha4 = never

(The same principle applies to sections.)

§Dealing with Type Changes

Most keys are string types. In the future, we may want to make a given algorithm or data structure’s configuration more nuanced. A logical way to do this is to change the key from taking a string type to taking a map type.

To support this type of change, all keys that take a string are also recognized as maps with a single key, default_disposition. Thus, if key is ever extended in this way, key.default_disposition can be used to control the configuration of older versions, and new versions can use the configuration parameters.

For instance, imagine that AES128 is found to be vulnerable to an attack called foo in certain, detectable situations. We could extend AES128 with a new key (foo) that is respected when those conditions are detected. This can be expressed in the following, backwards compatible manner:

[symmetric_algorithms]
aes128.default_disposition = "always"
aes128.foo = "2023-01-01"
aes128.ignore_invalid = "foo"

§Cutoff Times

Most settings take a so-called cutoff time. The cutoff time is the time at which an algorithm (e.g., the broken SHA-1 hash algorithm) or a data structure (e.g. the obsolete SED packet) should no longer be considered valid. Using a cutoff time provides more nuance than simply marking an algorithm as invalid. In particular, it allows sun setting algorithms that have been weakened, but are not yet completely broken, and using data that has been saved to a trusted medium before its security was broken.

Cutoff times are expressed using TOML’s datetime datatype, which is an RFC 3339 formatted timestamp. The following variants are valid:

Offset datetime:

[hash_algorithms]
sha1 = 2010-01-01T00:00:00Z

Local datetime:

[hash_algorithms]
sha1 = 2010-01-01T00:00:00+00:00

Local date (interpreted as midnight in UTC):

[hash_algorithms]
sha1 = 2010-01-01

The local time format is not supported as it doesn’t make sense in this context.

Two special values are also supported: always means that the algorithm or data structure should always be considered valid, and never means that the algorithm or data structure should never be considered valid. These values are checked for in a case-sensitive manner.

[hash_algorithms]
sha1 = "never"

§Default Disposition

In some situations, it is desirable to only allow a fixed set of algorithms. Using the cutoff mechanism, it is possible to accept or reject each of the known algorithms, but unknown algorithms, i.e., those that Sequoia will add in the future, and will likely enable by default, can’t be rejected in this way, because their name is—by definition—not yet known.

To accommodate this usage, it is possible to set an algorithm class or data structure class’s default disposition using the default_disposition key. Currently, only one value is supported for this key: never. If this key is present in a section, then that key is processed first, and all algorithms are set to be rejected. The rest of the keys are then processed as usual.

The following example shows how to only allow the SHA256 and SHA512 hash functions. Even if a theoretical SHA4 hash function is added to Sequoia, it will be rejected by this configuration.

[hash_algorithms]
sha256.collision_resistance = "always"
sha256.second_preimage_resistance = "always"
sha512.collision_resistance = "always"
sha512.second_preimage_resistance = "always"
default_disposition = "never"

§Sections

§Hash Functions

Hash algorithms are used to ensure multiple properties. Of particular relevance in the context of OpenPGP are collision resistance, and second preimage resistance. In some contexts like self signatures, only second preimage resistance is required. In other contexts, both properties are required. As collision resistance is much easier to attack, these two properties can be set separately.

You configure just the second preimage resistance cutoff by setting the ALGO.second_preimage_resistance key. You configure just the collision resistance cutoff by setting the ALGO.collision_resistance key. Setting the ALGO key is shorthand for setting both.

A hash algorithm’s key is the lower-case version of the value returned by the Display name. For instance, SHA1 is sha1.

[hash_algorithms]
md5 = "never"
sha1.second_preimage_resistance = 2030-01-01
sha1.collision_resistance = 2022-01-01
sha512 = "always"

§Symmetric Algorithms

Like hash algorithms, symmetric algorithms can be rejected outright or have a cutoff date. They don’t have any subkeys like collision_resistance, so there is only one way to set the cutoff: using the algo key.

The unencrypted variant, the unknown variants, and the private variants cannot currently be set.

[symmetric_algorithms]
cast5 = "never"
aes128 = "always"

§Asymmetric Algorithms

Like symmetric algorithms, asymmetric algorithms can be rejected outright or have a cutoff date.

The unknown variants, and the private variants cannot currently be set.

[asymmetric_algorithms]
rsa1024 = "never"
rsa2048 = 2028-01-01

§AEAD Algorithms

Like symmetric algorithms, AEAD algorithms can be rejected outright or have a cutoff date.

The unknown variants, and the private variants cannot currently be set.

[aead_algorithms]
eax = "never"
ocb = "always"

§Packets

Packets can be rejected outright or have a cutoff date. The SED packet is, for instance, considered broken, and messages that use it should generally be rejected unless they are known to not be from an attacker, e.g., because they were stored on a trusted medium before the attack was feasible.

It is also possible to reject particular versions of a packet. In this case, the packet is a map and the fields vX where X is a u8 can be used to set the cutoff for version X of the packet. This mechanism is only supported for packets that actually are versioned, and only for known versions. (Unknown versions can still be set in a forwards compatible way using the ignore_invalid key.)

The packets are named after the names of the Packet variants.

The reserved packet, the unknown variants, and the private variants cannot currently be set.

[packets]
sed = "never"
seip = 2028-01-01

signature.v3 = 2017-01-01
signature.v4 = 2030-01-01
signature.v6 = "always"
# v6 signatures are coming, but not yet recognized.
signature.ignore_invalid = "v6"

§Examples

The following example shows how to use a configuration file to configure a StandardPolicy:

use sequoia_openpgp as openpgp;
use openpgp::policy::HashAlgoSecurity;
use openpgp::types::HashAlgorithm;

use sequoia_policy_config::ConfiguredStandardPolicy;

let mut p = ConfiguredStandardPolicy::new();
p.parse_bytes(b"[hash_algorithms]
    sha1.collision_resistance = \"never\"")?;
let p = &p.build();

assert_eq!(p.hash_cutoff(HashAlgorithm::SHA1,
                         HashAlgoSecurity::CollisionResistance),
           Some(std::time::UNIX_EPOCH));

Structs§

Enums§