Happy SHA1 Rejection Day

By Justus | February 1, 2023

Today is the day Sequoia’s StandardPolicy starts rejecting SHA1-based signatures by default. This change will affect existing programs based on Sequoia, as the SHA1 deprecation has been committed to and baked into the code three years ago. Therefore, all programs using sequoia-openpgp version 0.15 and up will now reject SHA1-based signatures by default.

Three years ago, we changed how Sequoia evaluates signatures. The problem we tried to address is that cryptographic algorithms like hash algorithms do not age well: theoretical attacks are refined over time, and with growing computational resources, some become feasible.

The commit quotes Wikipedia, in 2020, saying:

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.

However, through experimentation we soon discovered, that many widely-used OpenPGP certificates were still using SHA-1 in their binding signatures (binding signatures, or self signatures, are assertions made by the certificate’s primary key that bind components like user ids and subkeys to the certificate). Outright rejecting these signatures makes it impossible to use the certificates, making the policy too strict to be useful in the real world.

Therefore, we came up with a more nuanced approach. SHA-1’s collision resistance had been broken (and there are published attacks: the SHAttered attack produced two different PDF files with the same SHA-1 hash digest, and the sha-mbles attack leverages a collision to attack the Web-Of-Trust authentication scheme).

But, since SHA-1 is still pre-image resistant, every collision-based attack requires that the attacker controls a part (the prefix) of the data being hashed. That means that binding signatures which are computed over the primary key and user id or subkey, neither of which are attacker controlled, are still secure.

The solution we came up with is to reject SHA-1 if attacker controlled input is hashed, and to allow it otherwise – at least a little while longer. The cut-off dates we came up with for SHA-1 were the year 2013 and 2023, respectively.

Three years ago we set this plan in motion, and here we are, in February of 2023, today, when Sequoia’s StandardPolicy starts rejecting Signatures using SHA-1 even if the input is not attacker controlled, like in binding signatures. It is very likely that you came here because you encountered a problem because of that. If so, please be assured that we are doing this to improve the security of the ecosystem, and many other implementations agree that we need to start rejecting SHA-1-based signatures. In the same vein, Fedora is rejecting SHA-1 now.


  1. Avoid using SHA-1 in all new signatures including those used in OpenPGP certificates.

  2. If you have an existing OpenPGP certificate that uses SHA-1 in binding signatures, Sequoia’s keyring-linter can re-create the weak binding signatures if the primary key’s secret key material is available. As the name implies, the keyring-linter can also check for these and other issues in all the OpenPGP certificates you have.

    $ sudo apt install sq-keyring-linter
    $ gpg --export B61A342DFFAFB0E24D6C49EBAC72C6E3538BB486 | sq inspect
    -: OpenPGP Certificate.
        Fingerprint: B61A342DFFAFB0E24D6C49EBAC72C6E3538BB486
                     Invalid: No binding signature at time 2023-02-01T15:38:09Z
        Public-key algo: RSA
    $ gpg --export-secret-key B61A342DFFAFB0E24D6C49EBAC72C6E3538BB486 \
        | sq-keyring-linter --fix | gpg --import
    Certificate AC72C6E3538BB486 is not valid under the standard policy: No binding signature at time 2023-02-01T15:20:02Z
    Certificate AC72C6E3538BB486 contains a User ID ("Alice <alice@example.org>") protected by SHA-1
    Certificate AC72C6E3538BB486, key 5DAE37DEA1C8606E uses a SHA-1-protected binding signature.
    Examined 1 certificate.
      0 certificates are invalid and were not linted. (GOOD)
      1 certificate was linted.
      1 of the 1 certificates (100%) has at least one issue. (BAD)
    0 of the linted certificates were revoked.
      0 of the 0 certificates has revocation certificates that are weaker than the certificate and should be recreated. (GOOD)
    0 of the linted certificates were expired.
    1 of the non-revoked linted certificate has at least one non-revoked User ID:
      1 has at least one User ID protected by SHA-1. (BAD)
      1 has all User IDs protected by SHA-1. (BAD)
    1 of the non-revoked linted certificates has at least one non-revoked, live subkey:
      1 has at least one non-revoked, live subkey with a binding signature that uses SHA-1. (BAD)
    0 of the non-revoked linted certificates have at least one non-revoked, live, signing-capable subkey:
      0 certificates have at least one non-revoked, live, signing-capable subkey with a strong binding signature, but a backsig that uses SHA-1. (GOOD)
    $ gpg --export B61A342DFFAFB0E24D6C49EBAC72C6E3538BB486 | sq inspect
    -: OpenPGP Certificate.
        Fingerprint: B61A342DFFAFB0E24D6C49EBAC72C6E3538BB486
        Public-key algo: RSA
  3. Finally, if you have exhausted all alternatives, you can tune the StandardPolicy so that it accepts SHA-1 a little while longer:

    let mut p = StandardPolicy::new();
    let y2024m2: Timestamp = 1706745600.into();