#983069 lintian: please check that upstream signature is made with a modern hash (warn or error on MD5, SHA1, or RIPEMD160)

#983069#5
Date:
2021-02-18 22:52:07 UTC
From:
To:
Some upstream packages are signed with OpenPGP using old, deprecated
digest algorithms.

See for example xml2rfc having a recent signature made with SHA-1:
https://mailarchive.ietf.org/arch/msg/xml2rfc-dev/G89V9M7_qSGxDVBb0QpSIqzznVc/

If lintian is scanning a package that includes a cryptographic signature
from upstream, it should warn (or produce an error) if that signature
uses a weak cryptographic digest algorithm.  In particular, MD5, SHA1,
and RIPEMD160 should all be considered weak.

likewise, uscan should provide at least a warning (perhaps an error) if
it fetches an OpenPGP signature that appears to be made using a weak
digest.

For both of these cases (uscan and lintian), I say "warn" by default
instead of "error" because of course a package with a weak signature
shouldn't be treated worse than a package with *no* signature.

Some OpenPGP implementations (like "sqop verify" or "sq verify", both
from sequoia) already deprecate recently-made SHA1 signatures.

If you're using gpgv to verify signatures, you can use the --weak-digest
argument, like so:

$ gpgv --weak-digest RIPEMD160 --weak-digest SHA1 --keyring debian/upstream/signing-key.pgp ../xml2rfc_3.5.0.orig.tar.gz.asc ../xml2rfc_3.5.0.orig.tar.gz
gpgv: Signature made Wed 18 Nov 2020 05:20:56 AM EST
gpgv:                using RSA key 4E9B574B8FBB171A
gpgv: Note: signatures using the SHA1 algorithm are rejected
gpgv: Can't check signature: Invalid digest algorithm
2 $

(MD5 is already marked as a "weak digest" by default, so no need to
include it specifically)

Thanks for considering this!

#983069#12
Date:
2021-02-26 12:48:50 UTC
From:
To:
Hi dkg,

That's a great idea! As a first step, I would like to show a
classification tag with the hash algorithm. (It could be used for
statistics.) Can 'gpgv' output such signature characteristics?

The warning you asked for would then take place on top of that—perhaps
with different severities depending how dated the algorithm is.
Thanks!

Kind regards,
Felix Lechner

#983069#17
Date:
2021-02-26 21:22:38 UTC
From:
To:
or python3-pgpy) that could provide this check.

If you're already using gpgv, you might just try verifying the signature
with "gpgv --weak-digest SHA1 --weak-digest RIPEMD160 …" -- that should
fail if the signature is weak, and produce some semi-readable warnings
for the user as well.

If you want to learn a lot more about the signature, you've got a lot of
options, but they're all pretty hairy.

gpgv produces data about the signature on its status FD:

    $ gpg --dearmor < debian/upstream/signing-key.asc > debian/upstream/signing-key.pgp
    $ gpgv --status-fd 3 3>sig.status --keyring debian/upstream/signing-key.pgp ../xml2rfc_3.5.0.orig.tar.gz.asc ../xml2rfc_3.5.0.orig.tar.gz
    gpgv: Signature made Wed 18 Nov 2020 05:20:56 AM EST
    gpgv:                using RSA key 4E9B574B8FBB171A
    gpgv: Good signature from "Henrik Levkowetz <henrik@levkowetz.com>"
    $ awk < sig.status '/^\[GNUPG:\] VALIDSIG/ { print $10 }'
    2
    $

Yes, that "2" means "SHA1" (see
https://tools.ietf.org/html/rfc4880#section-9.4)  (the "print $10" comes
from searching for VALIDSIG in /usr/share/doc/gnupg/DETAILS.gz)

So, that is rather opaque.  Other techniques include sq, pgpdump, hot
(from hopenpgp-tools), and "gpg --list-packets"

    $ sq packet dump ../xml2rfc_3.5.0.orig.tar.gz.asc
    Signature Packet, old CTB, 540 bytes
        Version: 4
        Type: Binary
        Pk algo: RSA (Encrypt or Sign)
        Hash algo: SHA1
        Hashed area:
          Signature creation time: 2020-11-18 10:20:56 UTC
        Unhashed area:
          Issuer: 4E9B 574B 8FBB 171A
        Digest prefix: D29F
        Level: 0 (signature over data)

    $ pgpdump ../xml2rfc_3.5.0.orig.tar.gz.asc
    Old: Signature Packet(tag 2)(540 bytes)
            Ver 4 - new
            Sig type - Signature of a binary document(0x00).
            Pub alg - RSA Encrypt or Sign(pub 1)
            Hash alg - SHA1(hash 2)
            Hashed Sub: signature creation time(sub 2)(4 bytes)
                    Time - Wed Nov 18 05:20:56 EST 2020
            Sub: issuer key ID(sub 16)(8 bytes)
                    Key ID - 0x4E9B574B8FBB171A
            Hash left 2 bytes - d2 9f
            RSA m^d mod n(4094 bits) - ...
                    -> PKCS-1
    $ gpg --list-packets < ../xml2rfc_3.5.0.orig.tar.gz.asc
    # off=0 ctb=89 tag=2 hlen=3 plen=540
    :signature packet: algo 1, keyid 4E9B574B8FBB171A
            version 4, created 1605694856, md5len 0, sigclass 0x00
            digest algo 2, begin of digest d2 9f
            hashed subpkt 2 len 4 (sig created 2020-11-18)
            subpkt 16 len 8 (issuer key ID 4E9B574B8FBB171A)
            data: [4094 bits]
    $ hot dearmor < ../xml2rfc_3.5.0.orig.tar.gz.asc  |  hot dump --output-format YAML
    hot (hopenpgp-tools) 0.23.6
    Copyright (C) 2012-2021  Clint Adams
    hot comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions.
    hot (hopenpgp-tools) 0.23.6
    Copyright (C) 2012-2021  Clint Adams
    hot comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions.
    signature:
    - tag: BinarySig
    - tag: RSA
    - tag: SHA1
    - - _sspCriticality: false
        _sspPayload:
          sigCreationTime:
            unThirtyTwoBitTimeStamp: 1605694856
    - - _sspCriticality: false
        _sspPayload:
          issuer:
            eoki: 4E9B574B8FBB171A
    - 53919
    - - unMPI: […]
    $

Warning: GnuPG upstream has explicitly said for years that *no one*
should try to programmatically parse the output of --list-packets.

The most detailed set of categories i can imagine wanting want are (in
order of severity, worse ones first):

 a) no signature is available / signature does not validate from
   debian/upstream/signing-key.asc

 b) only available signature depends on MD5

 c) only available signature depends on SHA1 or RIPEMD160

 d) valid signature with any other OpenPGP digest

If we want to collapse to fewer categories, my main priority is that (d)
stands apart from any of (a), (b), or (c).

In no case should lintian suggest that a signature that depends on a
weak digest (b) or (c) is *worse* than having no signature at all (a)

If you want to look at other cryptographic checks like this, maybe you
want to warn based on the contents of the OpenPGP certificates in
debian/upstream/signing-key.pgp.

Things that would be worth warning or errors on on:

 - No signing-capable keys (primary key or subkeys)

 - Weak primary keys: error on RSA/DSA keys < 2048 bits

 - Weak signing-capable keys (primary or subkeys): error on RSA/DSA keys
   < 2048 bits, maybe warn on RSA/DSA keys < 3072 bits (note: you can
   ignore non-signing-capable subkeys for this test)

 - Self-sigs in the certificate made using weak digest algorithms

If hopenpgp-tools is installed, one of the easiest things you can do
there is something like:

    hot dearmor < debian/upstream/signing-key.asc  | hokey lint --output-format JSON

and look for items that have "color" attribute either "Yellow" or "Red"
(since this is a signing-key, it's probably ok to ignore "Red" for
"keyHasEncryptionCapableSubkey")

Hope this has some useful pointers in it.