diff options
author | Michał Górny <mgorny@gentoo.org> | 2023-01-22 20:04:06 +0100 |
---|---|---|
committer | Michał Górny <mgorny@gentoo.org> | 2023-01-22 20:04:06 +0100 |
commit | 8e3032b2fdbecfac770e552ee126ca4304128238 (patch) | |
tree | 039c7a8ac4b26c3595d5c9cc1e79259be5c20eb3 | |
parent | 2d998654c691be83c3796192d97a308c506e5848 (diff) | |
download | gemato-8e3032b2fdbecfac770e552ee126ca4304128238.tar.gz |
openpgp: Add a function to verify detached signature
Signed-off-by: Michał Górny <mgorny@gentoo.org>
-rw-r--r-- | gemato/openpgp.py | 27 | ||||
-rw-r--r-- | tests/test_openpgp.py | 40 |
2 files changed, 59 insertions, 8 deletions
diff --git a/gemato/openpgp.py b/gemato/openpgp.py index 1150f59..cce47ee 100644 --- a/gemato/openpgp.py +++ b/gemato/openpgp.py @@ -19,6 +19,8 @@ import typing import urllib.parse import warnings +from pathlib import Path + from gemato.exceptions import ( OpenPGPNoImplementation, OpenPGPVerificationFailure, @@ -274,7 +276,7 @@ class SystemGPGEnvironment: If require_all_good is True and the file contains multiple OpenPGP signatures, all signatures have to be good and trusted in order - for the verificatin to succeed. Otherwise, a single good signature + for the verification to succeed. Otherwise, a single good signature is considered sufficient. """ @@ -283,6 +285,29 @@ class SystemGPGEnvironment: f.read().encode('utf8')) return self._process_gpg_verify_output(out, err, require_all_good) + def verify_detached(self, + signature_file: Path, + data_file: Path, + require_all_good: bool = True, + ) -> OpenPGPSignatureList: + """ + Verify the file against a detached signature + + Verify the data from data_file against the detached signature + from signature_file. Both files are specified by Path. + Raise an exception if the verification fails. + + If require_all_good is True and the file contains multiple OpenPGP + signatures, all signatures have to be good and trusted in order + for the verification to succeed. Otherwise, a single good signature + is considered sufficient. + """ + + _, out, err = self._spawn_gpg( + [GNUPG, "--batch", "--status-fd", "1", "--verify", + str(signature_file), str(data_file)]) + return self._process_gpg_verify_output(out, err, require_all_good) + def clear_sign_file(self, f, outf, keyid=None): """ Create an OpenPGP cleartext signed message containing the data diff --git a/tests/test_openpgp.py b/tests/test_openpgp.py index 22ef6cf..640e590 100644 --- a/tests/test_openpgp.py +++ b/tests/test_openpgp.py @@ -2,6 +2,7 @@ # (c) 2017-2023 Michał Górny # Licensed under the terms of 2-clause BSD license +import base64 import contextlib import datetime import io @@ -191,13 +192,7 @@ n4XmpdPvu+UdAHpQIGzKoNOEDJpZ5CzPLhYa5KgZiJhpYsDXgg== -----END PGP SIGNATURE----- """ -TWO_SIGNATURE_MANIFEST = f""" ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA256 - -{COMMON_MANIFEST_TEXT} ------BEGIN PGP SIGNATURE----- - +TWO_SIGNATURES = """ iQFHBAABCAAxFiEEgeEsFr2NzWC+GAhFE2iA5yp7E4QFAmPMHYQTHGdlbWF0b0Bl eGFtcGxlLmNvbQAKCRATaIDnKnsThCDWB/95B9njv423M94uRdpPqSNqTpAokNhy V0hjnhpiqnY85iFdL1Zc/rvhuxYbZezrig3dqctLseWYcx2mINBTLZqWHk5/NKEm @@ -208,6 +203,16 @@ zApRg8cetid6/SIzUSwiVqBt7i8noYWbgaazNt3HDlGq55v21dkOhmrXiIkEABYI ADEWIQR1jj6cjPscaH2bJCVTcI9ps0i0zAUCY8wd6BMcc2Vjb25kQGV4YW1wbGUu Y29tAAoJEFNwj2mzSLTMHKcA/0QbVl3PafYp45PFFo2e/knGKJKrm8D4bUH9wS5h dchVAP0RSzkUQPP7Zs+2uHQItkqbXJyrBBHOqjGzeh39sWVuAw== +""" + +TWO_SIGNATURE_MANIFEST = f""" +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +{COMMON_MANIFEST_TEXT} +-----BEGIN PGP SIGNATURE----- + +{TWO_SIGNATURES} =wG4b -----END PGP SIGNATURE----- """ @@ -1108,3 +1113,24 @@ def test_verify_require_secure_cli(base_tree, caplog, require_secure): assert retval == expected if expected == 1: assert str(ManifestInsecureHashes(["MD5"])) in caplog.text + + +def test_verify_detached(tmp_path): + try: + with MockedSystemGPGEnvironment() as openpgp_env: + with io.BytesIO(TWO_SIGNATURE_PUBLIC_KEYS) as f: + openpgp_env.import_key(f) + + with open(tmp_path / "data.bin", "wb") as f: + f.write(b"\r\n".join(COMMON_MANIFEST_TEXT.encode("utf8") + .splitlines())) + with open(tmp_path / "sig.bin", "wb") as f: + f.write(base64.b64decode(TWO_SIGNATURES)) + + sig = openpgp_env.verify_detached( + tmp_path / "sig.bin", tmp_path / "data.bin", + require_all_good=False) + + assert_signature(sig, "TWO_SIGNATURE_MANIFEST") + except OpenPGPNoImplementation as e: + pytest.skip(str(e)) |