summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Górny <mgorny@gentoo.org>2023-01-22 20:04:06 +0100
committerMichał Górny <mgorny@gentoo.org>2023-01-22 20:04:06 +0100
commit8e3032b2fdbecfac770e552ee126ca4304128238 (patch)
tree039c7a8ac4b26c3595d5c9cc1e79259be5c20eb3
parent2d998654c691be83c3796192d97a308c506e5848 (diff)
downloadgemato-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.py27
-rw-r--r--tests/test_openpgp.py40
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))