From 75d3b7abba57ed5099b302ccbba506d8f175bc9d Mon Sep 17 00:00:00 2001 From: Michał Górny Date: Sun, 22 Jan 2023 11:26:26 +0100 Subject: openpgp: Allow verifying a subset of signatures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Górny --- gemato/openpgp.py | 17 +++++++++++++++++ tests/test_openpgp.py | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/gemato/openpgp.py b/gemato/openpgp.py index 8c1db59..16a369e 100644 --- a/gemato/openpgp.py +++ b/gemato/openpgp.py @@ -167,12 +167,18 @@ class SystemGPGEnvironment: def verify_file(self, f: typing.IO[str], + require_all_good: bool = True, ) -> OpenPGPSignatureList: """ Perform an OpenPGP verification of Manifest data in open file @f. The file should be open in text mode and set at the beginning (or start of signed part). Raises 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 verificatin to succeed. Otherwise, a single good signature + is considered sufficient. """ exitst, out, err = self._spawn_gpg( @@ -231,6 +237,17 @@ class SystemGPGEnvironment: raise OpenPGPUnknownSigFailure( err.decode('utf8', errors='backslashreplace')) + # bad signature causes failure even without require_all_good + for sig in sig_list: + if sig.sig_status == OpenPGPSignatureStatus.BAD: + raise OpenPGPVerificationFailure( + err.decode("utf8", errors="backslashreplace"), sig) + + if not require_all_good: + if any(x.sig_status == OpenPGPSignatureStatus.GOOD and + x.valid_sig and x.trusted_sig for x in sig_list): + return sig_list + for sig in sig_list: if sig.sig_status == OpenPGPSignatureStatus.GOOD: pass diff --git a/tests/test_openpgp.py b/tests/test_openpgp.py index 6702471..22ef6cf 100644 --- a/tests/test_openpgp.py +++ b/tests/test_openpgp.py @@ -484,6 +484,38 @@ def test_verify_manifest(openpgp_env, manifest_var, key_var, expected): pytest.skip(str(e)) +def test_verify_one_out_of_two(): + try: + with MockedSystemGPGEnvironment() as openpgp_env: + with io.BytesIO(VALID_PUBLIC_KEY) as f: + openpgp_env.import_key(f) + + with io.StringIO(TWO_SIGNATURE_MANIFEST) as f: + sig = openpgp_env.verify_file(f, require_all_good=False) + + assert sig == [ + OpenPGPSignatureData( + fingerprint="81E12C16BD8DCD60BE180845136880E72A7B1384", + timestamp=datetime.datetime(2023, 1, 21, 17, 14, 44), + expire_timestamp=None, + primary_key_fingerprint="81E12C16BD8DCD60BE18" + "0845136880E72A7B1384", + sig_status=OpenPGPSignatureStatus.GOOD, + valid_sig=True, + trusted_sig=True), + OpenPGPSignatureData( + fingerprint="", + timestamp=None, + expire_timestamp=None, + primary_key_fingerprint="", + sig_status=OpenPGPSignatureStatus.NO_PUBLIC_KEY, + valid_sig=False, + trusted_sig=False), + ] + except OpenPGPNoImplementation as e: + pytest.skip(str(e)) + + def test_verify_untrusted_key(): try: with MockedSystemGPGEnvironment() as openpgp_env: -- cgit v1.2.3