When an intermediate certificate is available in the keyring, gpgsm tries to use it for path finding, even if a shorter path to a root cert is available. This causes signature validation failure.
This demonstration uses certificates from draft-ietf-lamps-samples-04, plus a novel cross-certifying CA.
If we assign the sample RSA CA (ca.rsa.crt) as a valid root authority, then the verification of a PKCS#7 signedData object succeeds (TRUST_FULLY).
If we then *also* load the cross-signed intermediate (ca.rsa.cross2.crt) into the homedir, then verification of the same signedData object will fail with invalid certification chain: Missing issuer certificate (TRUST_UNDEFINED 185).
Here's the reproducer:
workdir=$(mktemp -d) mkdir -m 0700 "$workdir/gpg" export GNUPGHOME="$workdir/gpg" echo C4:C7:D3:DF:10:C5:C9:F8:D2:34:1D:8C:69:B7:30:D6:EB:BE:9E:DD S relax > "$workdir/gpg/trustlist.txt" gpgsm --import ca.rsa.crt gpgsm --verify --status-fd 3 3>without-intermediate-cert.status <signed-data.p7 gpgsm --import ca.rsa.cross2.crt gpgsm --verify --status-fd 3 3>with-intermediate-cert.status <signed-data.p7
The different status descriptions look like:
$ head -v *.status ==> with-intermediate.status <== [GNUPG:] NEWSIG [GNUPG:] VERIFICATION_COMPLIANCE_MODE 23 [GNUPG:] GOODSIG 32933B035357A3D852DB338B1F27DA2405548976 /CN=Alice Lovelace/OU=LAMPS WG/O=IETF [GNUPG:] VALIDSIG 32933B035357A3D852DB338B1F27DA2405548976 2021-02-20 20210220T150102 20520927T065418 0 0 1 8 00 [GNUPG:] TRUST_UNDEFINED 185 ==> without-intermediate.status <== [GNUPG:] NEWSIG [GNUPG:] VERIFICATION_COMPLIANCE_MODE 23 [GNUPG:] GOODSIG 32933B035357A3D852DB338B1F27DA2405548976 /CN=Alice Lovelace/OU=LAMPS WG/O=IETF [GNUPG:] VALIDSIG 32933B035357A3D852DB338B1F27DA2405548976 2021-02-20 20210220T150102 20520927T065418 0 0 1 8 00 [GNUPG:] TRUST_FULLY 0 shell $
Note that ca.rsa.crt and ca.rsa.cross2.crt share the same subject key. But ca.rsa.crt is a self-signed root, and ca.rsa.cross2.crt is cross-signed by a different CA. As the draft explains, the signing key can be validated against two root CA certs, either directly by the issuing authority:
╔════════════╗ ┌────────────────┐ ║ ca.rsa.crt ╟─→│ alice.sign.crt │ ╚════════════╝ └────────────────┘
Or by a cross-signed intermediate CA certificate:
╔═════════════╗ ┌───────────────────┐ ┌────────────────┐ ║ ca.rsa2.crt ╟─→│ ca.rsa.cross2.crt ├─→│ alice.sign.crt │ ╚═════════════╝ └───────────────────┘ └────────────────┘
In the reproducer above, ca.rsa2.crt is unknown to gpgsm, but the mere presence of ca.rsa.cross2.crt in the keyring prevents successful signature validation.
gpgsm should not consider a signature less valid just because it knows of an unusable cross-signed intermediate CA certificate.
(edited to simplify the steps to reproduce; edited again to avoid bringing ed25519 into the mix)