Add signed_by_[any|all] parameters to gpg.verify (#63168)

* Add test for issue 63145

* Make gpg.verify respect gnupghome

* Add `signed_by_any`/`signed_by_all` params to gpg.verify

* Reconsider status check

* Fix tests

* Plug verification issue after status check reconsideration

Since missing pubkeys also cause the previous signature's
fingerprint to be overwritten, this would have led to
situations where the check could have passed when it
should not have.

* Cleanup logic

* Workaround python-gnupg issue

https://github.com/vsajip/python-gnupg/issues/214

* Adapt offical fix for workaround

ee94a7ecc1
That was very fast.

* Backport more meaningful test

* Update versionadded

* Avoid exception on import w/ missing gnupg lib

* Do not apply workaround on fixed versions

* Correct bool comparison

* Account for subkeys

sig_info["fingerprint"] contains the actual signing key's fingerprint,
which might be a subkey. The primary key's fingerprint is always found in
sig_info["pubkey_fingerprint"]. In cases where a signing subkey was
used, the intended behavior is still comparison with the primary key.

* Bump versionadded

* Rename changelog files to .md
This commit is contained in:
jeanluc 2023-05-09 19:24:48 +00:00 committed by GitHub
parent 6b1a49e341
commit 4d617dd44a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 490 additions and 14 deletions

1
changelog/63166.added.md Normal file
View file

@ -0,0 +1 @@
Added signed_by_any/signed_by_all parameters to gpg.verify

View file

@ -1,6 +1,6 @@
"""
Manage a GPG keychains, add keys, create keys, retrieve keys from keyservers.
Sign, encrypt and sign plus encrypt text and files.
Manage GPG keychains, add keys, create keys, retrieve keys from keyservers.
Sign, encrypt, sign plus encrypt and verify text and files.
.. versionadded:: 2015.5.0
@ -22,6 +22,7 @@ import time
import salt.utils.files
import salt.utils.path
import salt.utils.stringutils
import salt.utils.versions
from salt.exceptions import SaltInvocationError
log = logging.getLogger(__name__)
@ -503,7 +504,7 @@ def delete_key(
use_passphrase=True,
):
"""
Get a key from the GPG keychain
Delete a key from the GPG keychain.
keyid
The keyid of the key to be deleted.
@ -1102,7 +1103,14 @@ def sign(
def verify(
text=None, user=None, filename=None, gnupghome=None, signature=None, trustmodel=None
text=None,
user=None,
filename=None,
gnupghome=None,
signature=None,
trustmodel=None,
signed_by_any=None,
signed_by_all=None,
):
"""
Verify a message or file
@ -1138,6 +1146,22 @@ def verify(
.. versionadded:: 2019.2.0
signed_by_any
A list of key fingerprints from which any valid signature
will mark verification as passed. If none of the provided
keys signed the data, verification will fail. Optional.
Note that this does not take into account trust.
.. versionadded:: 3007.0
signed_by_all
A list of key fingerprints whose signatures are required
for verification to pass. If a single provided key did
not sign the data, verification will fail. Optional.
Note that this does not take into account trust.
.. versionadded:: 3007.0
CLI Example:
.. code-block:: bash
@ -1163,6 +1187,15 @@ def verify(
if trustmodel:
extra_args.extend(["--trust-model", trustmodel])
if signed_by_any or signed_by_all:
# batch mode stops processing on the first invalid signature.
# This ensures all signatures are evaluated for validity.
extra_args.append("--no-batch")
# workaround https://github.com/vsajip/python-gnupg/issues/214
# This issue should be fixed in versions greater than 0.5.0.
if salt.utils.versions.version_cmp(gnupg.__version__, "0.5.0") <= 0:
gpg.result_map["verify"] = FixedVerify
if text:
verified = gpg.verify(text, extra_args=extra_args)
elif filename:
@ -1177,16 +1210,90 @@ def verify(
else:
raise SaltInvocationError("filename or text must be passed.")
ret = {}
if verified.trust_level is not None:
if not (signed_by_any or signed_by_all):
ret = {}
if verified.trust_level is not None:
ret["res"] = True
ret["username"] = verified.username
ret["key_id"] = verified.key_id
ret["trust_level"] = VERIFY_TRUST_LEVELS[str(verified.trust_level)]
ret["message"] = "The signature is verified."
else:
ret["res"] = False
ret["message"] = "The signature could not be verified."
return ret
signatures = [
{
"username": sig.get("username"),
"key_id": sig["keyid"],
"fingerprint": sig["pubkey_fingerprint"],
"trust_level": VERIFY_TRUST_LEVELS[str(sig["trust_level"])]
if "trust_level" in sig
else None,
"status": sig["status"],
}
for sig in verified.sig_info.values()
]
ret = {"res": False, "message": "", "signatures": signatures}
# be very explicit and do not default to result = True below
any_check = all_check = False
if signed_by_any:
if not isinstance(signed_by_any, list):
signed_by_any = [signed_by_any]
any_signed = False
for signer in signed_by_any:
signer = str(signer)
try:
if any(
x["trust_level"] is not None and str(x["fingerprint"]) == signer
for x in signatures
):
any_signed = True
break
except (KeyError, IndexError):
pass
if not any_signed:
ret["res"] = False
ret[
"message"
] = "None of the public keys listed in signed_by_any provided a valid signature"
return ret
any_check = True
if signed_by_all:
if not isinstance(signed_by_all, list):
signed_by_all = [signed_by_all]
for signer in signed_by_all:
signer = str(signer)
try:
if any(
x["trust_level"] is not None and str(x["fingerprint"]) == signer
for x in signatures
):
continue
except (KeyError, IndexError):
pass
ret["res"] = False
ret[
"message"
] = f"Public key {signer} has not provided a valid signature, but was listed in signed_by_all"
return ret
all_check = True
if bool(signed_by_any) is any_check and bool(signed_by_all) is all_check:
ret["res"] = True
ret["username"] = verified.username
ret["key_id"] = verified.key_id
ret["trust_level"] = VERIFY_TRUST_LEVELS[str(verified.trust_level)]
ret["message"] = "The signature is verified."
else:
ret["res"] = False
ret["message"] = "The signature could not be verified."
ret["message"] = "All required keys have provided a signature"
return ret
ret["res"] = False
ret[
"message"
] = "Something went wrong while checking for specific signers. This is most likely a bug"
return ret
@ -1395,3 +1502,22 @@ def decrypt(
log.error(result.stderr)
return ret
if HAS_GPG_BINDINGS:
class FixedVerify(gnupg.Verify):
"""
This is a workaround for https://github.com/vsajip/python-gnupg/issues/214.
It ensures invalid or otherwise unverified signatures are not
merged into sig_info in any way.
https://github.com/vsajip/python-gnupg/commit/ee94a7ecc1a86484c9f02337e2bbdd05fd32b383
"""
def handle_status(self, key, value):
if "NEWSIG" == key:
self.signature_id = None
super().handle_status(key, value)
if key in self.TRUST_LEVELS:
self.signature_id = None

View file

@ -97,6 +97,31 @@ pDEmK8EhJDvV/9o0lnhm/9w=
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def key_a_priv():
return """\
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQHYBGOH8R0BBACb1xGmsPqP8mQsDDiW7eRUB21TDA1/9GCPFHc7BOWAguIip6At
8RcBXZHryo96qfuOYgRiy52pX1yHFRlyRgAgfrTx4LHx2l0g0D0n5l+o22eqR6LE
pr9bU4sWPjUycxi+4Qy89rAF022Jk213hPYn1lpOjXCMGYr1G4TUQTPisQARAQAB
AAP7BlQ9nKcZI/24hQPxi+qpMGL1VQ87IKBWiBURExHrtrSKFdV4N0lwcV8hGSIK
wfTzmRigvDjwBCQR9E/+brJKWLdGmmHjYHIU3m4fz26E4UlxEu2XfxZOSPKPTnzh
GqVSjmZ9TDdr5Ykpz5SyQ1YOUS9iRI6O5Dp0c4+6n2gyTYECAMQPCa8UnoHw1jgw
JHnK+XM3jinqgIOMS66i5nCGe3PItaAOvPIwA0lyl2Io06lGuiSVIqbJIUTsf2Mv
y14eJnECAMt8O6gMsjJdZ/dU9srqz4ZatPUHtQm2KBnvk311PmeErJ1FiiAqXTVq
Q9y3GvkEnENeuC/ac0XztiHsEC2eIEEB/iu1i5sP3zUZZnBNDbsmDZEy+HKHm8lL
Vg1+hHUdznMMmJ/PKq+WlB3KvdNzhEFd+0R+ylfRTMWnhNMWxL1atNyYC7QtS2V5
IEEgKEdlbmVyYXRlZCBieSBTYWx0U3RhY2spIDxrZXlhQGV4YW1wbGU+iNEEEwEI
ADsWIQTvA3ZfWe6QSTDIp4FVOoKgWMDHlQUCY4fxHQIbLwULCQgHAgIiAgYVCgkI
CwIEFgIDAQIeBwIXgAAKCRBVOoKgWMDHldREBACC47Aj7j2UbQpvL4GzK/eh/caD
G+FLVBrv7KyPTzxrPkZW3SmpV7dJtFW+f+PILrJm9a6/3gp0vdHuEJ4TPb8lqQJE
njDESx14BITy30f+hOoPQjlqwxr0Qy+fkgIQZ4uxN5Bvs5hiKFNCPscksbqkMSYr
wSEkO9X/2jSWeGb/3A==
=lVXx
-----END PGP PRIVATE KEY BLOCK-----"""
@pytest.fixture
def key_a_sig():
return """\
@ -110,6 +135,221 @@ cOm6/gNVIVm3Uoe9mKXESRvpMYmNUp2UM7ZzzBstK/ViVR82UA==
-----END PGP SIGNATURE-----"""
@pytest.fixture
def key_b_fp():
return "118B4FAB78038CB2DF7B69E20F6C422647465C93"
@pytest.fixture
def key_b_pub():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY4fxNQEEAOgAzbpheJrOq4il5BrMVtP1G1kU94QX2+xLXEgW/wPdE4HD6Zbg
vliIg18v7Na4x8ubWy/7CkXC83EJ8SoSqcCccvuKjIWsm6tfeCidNstNCjewFMUR
7ZOQmAe/I2JAlz2SgNxS3ZDiCZpGkxqE0GZ+1N7Mz2WHImnExG149RVHABEBAAG0
LUtleSBCIChHZW5lcmF0ZWQgYnkgU2FsdFN0YWNrKSA8a2V5YkBleGFtcGxlPojR
BBMBCAA7FiEEEYtPq3gDjLLfe2niD2xCJkdGXJMFAmOH8TUCGy8FCwkIBwICIgIG
FQoJCAsCBBYCAwECHgcCF4AACgkQD2xCJkdGXJNR3AQAk5ZoN+/ViIX3vA/LbXPn
2VE1E7ETTeIGqsb5f98UfjIbYfkNE8+OtnPxnDbSOPWBEOT+XPPjmxnE0a2UNTfn
ECO71/ZUiyC3ZN50IZ0vgzwBH+DeIV6PDAAun5FGx4RI7v6n0CPlrUcWKYe8wY1F
COflOxnEyLVHXnX8wUIzZwo=
=Hq0X
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def key_b_priv():
return """\
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQHYBGOH8TUBBADoAM26YXiazquIpeQazFbT9RtZFPeEF9vsS1xIFv8D3ROBw+mW
4L5YiINfL+zWuMfLm1sv+wpFwvNxCfEqEqnAnHL7ioyFrJurX3gonTbLTQo3sBTF
Ee2TkJgHvyNiQJc9koDcUt2Q4gmaRpMahNBmftTezM9lhyJpxMRtePUVRwARAQAB
AAP5ARivbnABmWhchYCiwbnjKSljNvosHJ5seCaUIq34squWaRGH5rBccJiPrQvD
qJvmnWCp+CjNZTysH/eUcz+6xmoefzsB9YPfyWKYCM6yNaBbFceVER+TCRoLjlZi
Nw7VtD6t8Rrxq9T7fykmNU7PD1hOKEXacaHN04jCFtPsXdkCAPK9LkE3c9nVNxUS
17AtviMm2/9nC0wu+9W/Tub0VZjtsanwTyA87uwNlxrOyUeopZZrzSioJEa6b/Hq
RtUmvR8CAPSteh9vXM9D94jpmWzz2Hx1uo8OCDk86KovbEFLqNTEnnbaG8eXOH68
Y0BPKrLRY9lY1zkeudw+pPyg6Q1CetkB/0VbGbfswR15WkiAJnd/Cln07upDUYLD
bQ4C/w7YbfQndmnJ84gHqQH2T4xgULlEvl3+rg+x7vg4z1X7St31hD+kqLQtS2V5
IEIgKEdlbmVyYXRlZCBieSBTYWx0U3RhY2spIDxrZXliQGV4YW1wbGU+iNEEEwEI
ADsWIQQRi0+reAOMst97aeIPbEImR0ZckwUCY4fxNQIbLwULCQgHAgIiAgYVCgkI
CwIEFgIDAQIeBwIXgAAKCRAPbEImR0Zck1HcBACTlmg379WIhfe8D8ttc+fZUTUT
sRNN4gaqxvl/3xR+Mhth+Q0Tz462c/GcNtI49YEQ5P5c8+ObGcTRrZQ1N+cQI7vX
9lSLILdk3nQhnS+DPAEf4N4hXo8MAC6fkUbHhEju/qfQI+WtRxYph7zBjUUI5+U7
GcTItUdedfzBQjNnCg==
=kzV7
-----END PGP PRIVATE KEY BLOCK-----"""
@pytest.fixture
def key_b_sig():
return """\
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQQRi0+reAOMst97aeIPbEImR0ZckwUCY4f0gQAKCRAPbEImR0Zc
kwj5A/9Fj7JFdzu5CGvB7MGYPPUm7cBh231QuSLndlSFvv3ZBrvU9906fstzjQRB
6x4m1uc04bckEoZ/2WE6r5dP39sdvzlyMhGq5qAJZZiuiZF2EjzMnQ/dJoXYM8pM
674JLQ2VjsWCZjp7s3JnPh6+0VL9IoDx3QsfPSBKBjOHOSj0SA==
=1sKz
-----END PGP SIGNATURE-----"""
@pytest.fixture
def key_c_fp():
return "96F136AC4C92D78DAF33105E35C03186001C6E31"
@pytest.fixture
def key_c_pub():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY4f2GgEEALToT23wZfLGM/JGCV4pWRlIXXqLwwEBSXral92HvsUjC8Vqsh1z
1n0K8/vIpS9OH2Q21emtht4y36rbahy+w6wRc1XXjPQ28Pyd+8v/jSKy/NKW3g+y
ZoB22vj4L35pAu/G6xs9+pKsLHGjMo+LsWZNEZ2Ar06aYA0dbTb0AqYfABEBAAG0
LUtleSBDIChHZW5lcmF0ZWQgYnkgU2FsdFN0YWNrKSA8a2V5Y0BleGFtcGxlPojR
BBMBCAA7FiEElvE2rEyS142vMxBeNcAxhgAcbjEFAmOH9hoCGy8FCwkIBwICIgIG
FQoJCAsCBBYCAwECHgcCF4AACgkQNcAxhgAcbjH2WAP/RtlUfN/novwmxxma6Zom
P6skFnCcRCs0vMU3OnNwuxZt9B+j0sUTu6noGi04Gcbd0eQs7v57DQHcRhNidZU/
8BJv5jD6E2yuzLK9lON+Yhgc6Pg6raA3hBeCY2HuzTEQLAThyV7ihboNILo7FJwo
y9KvnTFP2+oeDX2Z/m4SoWw=
=81Kb
-----END PGP PUBLIC KEY BLOCK-----"""
# the signature was made on different data
@pytest.fixture
def key_c_fail_sig():
return """\
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQSW8TasTJLXja8zEF41wDGGABxuMQUCY4f2pwAKCRA1wDGGABxu
MdtOA/0ROhdh1VgfZn94PWgcrlurwYE0ftI3AXbSI00FsrWviF8WZQtp7YjrUC3t
/a/P1UYZkbBR0EeKwbKGmersq8mfif1qejWpQXpmmx011KQ0AFl7vdZUtcj2n454
KQE7MX8ePBchpSyhmvXzEezyw2FJp+YVOUKhDsc1vbNWTxGYJw==
=6tkK
-----END PGP SIGNATURE-----"""
@pytest.fixture
def key_d_fp():
return "0B33DC666FA1211311850D4F5F3CAF4AE7BF949D"
@pytest.fixture
def key_d_pub():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY4gB/AEEAK2iwXXY4rbykGMFsLxdcnUdWtglKBXyQl5Y2Mj71UedgGTZVNWP
4dvUnQNqthPCu3hFyD2I+USa80OuYw5k9MPMqZcu3xmJKneaEX07r829G2T8yllz
vgKd2+4aUZHsKFz9fId7lraCDhSpmS0zLeTab6jNDfYecVJB8JaQpJrNABEBAAG0
LUtleSBEIChHZW5lcmF0ZWQgYnkgU2FsdFN0YWNrKSA8a2V5ZEBleGFtcGxlPojR
BBMBCAA7FiEECzPcZm+hIRMRhQ1PXzyvSue/lJ0FAmOIAfwCGy8FCwkIBwICIgIG
FQoJCAsCBBYCAwECHgcCF4AACgkQXzyvSue/lJ373AQAqKGhEfZ8Kx0s7L+T+6Ug
orxgvvQhbcc+eciA9alAptstkt9qg03yaii1InwJE5HjLNqOu8DWnhuBDTquWQUm
CWKUEicI6eZCFHVrjAcUKJcp/Lne9Ny/r1+14eE2vlsMf3oy0n0OpOTC+KhJzDpz
Mq2tZaSibjzjqQ32LiNJi3c=
=rjff
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def key_d_sig():
return """\
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQQLM9xmb6EhExGFDU9fPK9K57+UnQUCY4gCXwAKCRBfPK9K57+U
nYk/BACe4YUPaxHRuky8ya7k27P+RTynbuUGJ1w3AHaO1VXDTvoNTioyaqeDpO6C
NsMl5bIa7GkYEaeM/OPWQ9WlWPno6+nwOEL7/818J+JgxWVsLtGJNlYzuEwI7tDV
FUXLma7dvOw13LLY2RjtgaVbePTO+H+uCrAa4/O0YEIAE2C2fA==
=3m4o
-----END PGP SIGNATURE-----"""
@pytest.fixture
def key_e_fp():
return "2401C402776328D78D6B4C5D67D35BC98502D9B9"
# expires 2022-12-01
@pytest.fixture
def key_e_pub():
return """\
-----BEGIN PGP PUBLIC KEY BLOCK-----
mI0EY4gjEQEEAKYpWezZQWiAUDvAMcMhBkjHGY2fM4MMiXc6+fRbNV4VCL9TtJYE
gjccYVu44DtIYQzMVimrPQ6xepUmFRalezCG0OO4v25Ciwyeg8LX+Tb3kyAYFAxi
qLXAJyr3aZ/539xBak/Vf5xdURIi7WF5qBGQxd87tRDDqyPFnr87JJtFABEBAAG0
LUtleSBFIChHZW5lcmF0ZWQgYnkgU2FsdFN0YWNrKSA8a2V5ZUBleGFtcGxlPojX
BBMBCABBFiEEJAHEAndjKNeNa0xdZ9NbyYUC2bkFAmOIIxECGy8FCQAAZh8FCwkI
BwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQZ9NbyYUC2bmn1QP/WPVhj1bC9/9R
hifv29MG9maRNIkuEkZKtRJj7HMSaamD5IOtGoyMuBwicb38n2Z2KQZUiJbvyZTt
PS328F8YSUSyWQKqmhwL0iLlnDzx8l/nFr5tiss2b/ZzjlMP4iXtAgEdVMJnfjrM
J7xvL0cNSsHha4hUIrekvzM+SNwYkzs=
=nue4
-----END PGP PUBLIC KEY BLOCK-----"""
@pytest.fixture
def key_e_priv():
return """\
-----BEGIN PGP PRIVATE KEY BLOCK-----
lQHYBGOIIxEBBACmKVns2UFogFA7wDHDIQZIxxmNnzODDIl3Ovn0WzVeFQi/U7SW
BII3HGFbuOA7SGEMzFYpqz0OsXqVJhUWpXswhtDjuL9uQosMnoPC1/k295MgGBQM
Yqi1wCcq92mf+d/cQWpP1X+cXVESIu1heagRkMXfO7UQw6sjxZ6/OySbRQARAQAB
AAP/Se3mE8qKHpvQlvUpbt83s5PaW7e0rJ8cXo8//SfDs+t5696rX3/8C9c1viCg
q9/FRnN39qw4y1vN5aR/B4dzKmlycWZ5Kk5NewRfNAyvD85uXA2VDDTCZQ7qDlwZ
A24s95xjWWC2urhMszYgWbT9Kzk4Q1aNhPIEs1QgYvIujjUCAMX8+w5FRrOZWYP+
K8qL9ZPUAbUIdg+dCN1rTQvrCbM2FtZquFw89EyzT+qmBkfHCKpczC0XHdqnhsZU
sGTdqXcCANbZEFCNldv6FwhYsDBitP48shoGk3SIsAIpzP3klz+3Eut6LCTzS5T4
t9OVEIpKCP5Ci5m/BoTO/rbKhKt5ECMB/14pYNJcKtolEwvE6w5j6h5f72N+3dTs
qGTpDjfpgrSTmpoZcqDcFg0ycfwFyDzBVI+Hn+6njc/FtMxH3JAA2kug6LQtS2V5
IEUgKEdlbmVyYXRlZCBieSBTYWx0U3RhY2spIDxrZXllQGV4YW1wbGU+iNcEEwEI
AEEWIQQkAcQCd2Mo141rTF1n01vJhQLZuQUCY4gjEQIbLwUJAABmHwULCQgHAgIi
AgYVCgkICwIEFgIDAQIeBwIXgAAKCRBn01vJhQLZuafVA/9Y9WGPVsL3/1GGJ+/b
0wb2ZpE0iS4SRkq1EmPscxJpqYPkg60ajIy4HCJxvfyfZnYpBlSIlu/JlO09Lfbw
XxhJRLJZAqqaHAvSIuWcPPHyX+cWvm2KyzZv9nOOUw/iJe0CAR1Uwmd+OswnvG8v
Rw1KweFriFQit6S/Mz5I3BiTOw==
=49zT
-----END PGP PRIVATE KEY BLOCK-----"""
@pytest.fixture
def key_e_exp_sig():
return """\
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQQkAcQCd2Mo141rTF1n01vJhQLZuQUCY4gjPgAKCRBn01vJhQLZ
ueNkBACa2JjXZjatAbW4wXZ6RKqgluNwvC12EzFbQK2oGh1JC9htS4mVYE2aroup
E5XNzlr8R1z9b5HmuqqSniTNXG5xpxDLvD+rZK3Ww6AIs5FR2R9e0Kr0kMayQFdO
UDJsV8u8GFP+76oGy4G4GU1NlcqWpmIAzst0G4q4ipx891Z10A==
=G8tZ
-----END PGP SIGNATURE-----"""
@pytest.fixture
def key_f_fp():
return "D7D3F617EBD2113059914876F7D0B975BC8E7ED1"
# no pubkey for this one
@pytest.fixture
def key_f_sig():
return """\
-----BEGIN PGP SIGNATURE-----
iLMEAAEIAB0WIQTX0/YX69IRMFmRSHb30Ll1vI5+0QUCY4it3wAKCRD30Ll1vI5+
0Qf8A/4yI5nX0Q7kfR6rHvcDgB9HFlimiE15HOd8pW0RWK0YWvTtn+5vWyOZalS/
oHvSiXZCYIgjllvMSJbgxj22GrH621B5Q+SBSkQOVLLU8gBADcU0FAShV5BXgm35
kBGl+/D1MBJLt6q8GZWHMWIHOX4GN28A/PEemaKg3dZHEtPM3w==
=su9m
-----END PGP SIGNATURE-----"""
@pytest.fixture(params=["a"])
def sig(request, tmp_path):
sigs = "\n".join(request.getfixturevalue(f"key_{x}_sig") for x in request.param)
@ -124,7 +364,7 @@ def gnupg(gpghome):
return gnupglib.GPG(gnupghome=str(gpghome))
@pytest.fixture(params=["a"])
@pytest.fixture(params=["abcde"])
def pubkeys_present(gnupg, request):
pubkeys = [request.getfixturevalue(f"key_{x}_pub") for x in request.param]
fingerprints = [request.getfixturevalue(f"key_{x}_fp") for x in request.param]
@ -136,6 +376,115 @@ def pubkeys_present(gnupg, request):
# cleanup is taken care of by gpghome and tmp_path
@pytest.mark.parametrize(
"sig,expected",
[
(["a"], True),
(["c_fail"], False),
],
indirect=["sig"],
)
@pytest.mark.usefixtures("pubkeys_present")
def test_gpg_verify(gpg, gpghome, signed_data, sig, expected, key_a_fp):
res = gpg.verify(filename=str(signed_data), gnupghome=str(gpghome), signature=sig)
assert res["res"] is expected
if not expected:
assert "could not be verified" in res["message"]
else:
assert "is verified" in res["message"]
assert "key_id" in res
assert res["key_id"] == key_a_fp[-16:]
@pytest.mark.parametrize(
"sig,by,expected",
[
("a", "ab", True),
("bd", "ab", True),
("bd", "ad", True),
("a", "bcde", False),
(["c_fail"], "bc", False),
(["c_fail", "b"], "bc", True),
# Bad signatures partly overwrite the preceding good one currently.
# https://github.com/vsajip/python-gnupg/issues/214
# The module contains a workaround for this issue though.
# The following would not have an issue since we only rely on trust_level and fingerprint.
(["b", "c_fail"], "bc", True),
(["e_exp"], "e", False),
(["e_exp", "a"], "a", True),
(["a", "e_exp"], "a", True),
("f", "abcde", False),
("fa", "abcde", True),
# The above mentioned issue also affects signatures
# whose pubkeys are absent from the keychain, but they
# also overwrite the previous one's fingerprint (which we compare to).
# So the following would fail without the workaround:
("af", "abcde", True),
# Without the workaround, the following would be accepted (= test failure),
# even though the `f` signature was never verified because the pubkey
# was missing.
("af", "f", False),
],
indirect=["sig"],
)
@pytest.mark.usefixtures("pubkeys_present")
def test_gpg_verify_signed_by_any(
gpg, gpghome, signed_data, sig, by, expected, request
):
fps = [request.getfixturevalue(f"key_{x}_fp") for x in by]
res = gpg.verify(
filename=str(signed_data),
gnupghome=str(gpghome),
signature=sig,
signed_by_any=fps,
)
assert res["res"] is expected
@pytest.mark.parametrize(
"sig,by,expected",
[
("a", "a", True),
("bd", "a", False),
("ad", "ad", True),
("ad", "ad", True),
("ad", "ac", False),
(["a", "c_fail"], "ac", False),
# Bad signatures partly overwrite the preceding good one currently.
# https://github.com/vsajip/python-gnupg/issues/214
# The module contains a workaround for this issue though.
# The following would not have an issue since we only rely on trust_level and fingerprint.
(["a", "b", "c_fail"], "ab", True),
(["a", "b", "c_fail"], "abc", False),
(["c_fail", "a", "b"], "ab", True),
("fad", "da", True),
("fd", "da", False),
# The above mentioned issue also affects signatures
# whose pubkeys are absent from the keychain, but they
# also overwrite the previous one's fingerprint (which we compare to).
# So the following would fail without the workaround:
("dfa", "da", True),
# Without the workaround, the following would be accepted (= test fail),
# even though the `f` signature was never verified because the pubkey
# was missing.
("abf", "af", False),
],
indirect=["sig"],
)
@pytest.mark.usefixtures("pubkeys_present")
def test_gpg_verify_signed_by_all(
gpg, gpghome, signed_data, sig, by, expected, request
):
fps = [request.getfixturevalue(f"key_{x}_fp") for x in by]
res = gpg.verify(
filename=str(signed_data),
gnupghome=str(gpghome),
signature=sig,
signed_by_all=fps,
)
assert res["res"] is expected
@pytest.mark.usefixtures("pubkeys_present")
def test_verify(gpghome, gpg, sig, signed_data, key_a_fp):
res = gpg.verify(