mirror of
https://github.com/saltstack/salt.git
synced 2025-04-16 09:40:20 +00:00
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:
parent
6b1a49e341
commit
4d617dd44a
3 changed files with 490 additions and 14 deletions
1
changelog/63166.added.md
Normal file
1
changelog/63166.added.md
Normal file
|
@ -0,0 +1 @@
|
|||
Added signed_by_any/signed_by_all parameters to gpg.verify
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Reference in a new issue