diff --git a/hm_pyhelper/tests/test_util_pgp.py b/hm_pyhelper/tests/test_util_pgp.py new file mode 100644 index 0000000..12d16d7 --- /dev/null +++ b/hm_pyhelper/tests/test_util_pgp.py @@ -0,0 +1,55 @@ +"Test cases for util.gpg module." + +import unittest +import json + +from hm_pyhelper.util.pgp import get_payload_from_clearsigned_message + + +SAMPLE_CLEARSIGNED_MESSAGE = """ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +{ + "key1": "value1", + "key2": [1, 2, 3, 4] +} + +-----BEGIN PGP SIGNATURE----- + +iQGzBAEBCgAdFiEEfSH7Wj/J4hZxOpWqMy5fwG12lo0FAmIDUGEACgkQMy5fwG12 +lo0WDwv+Nn8ffqY3oKOQv11eRkn+w4NcAp5XFqwYz0e5e+DEfjoYYTVAEgUdK1gF +W2u8Jed9rW710A6yYXHlIZWKSHsQ1sob5lK3R/r+/lFrXLYYwgFGBWJmph/wuiQW +OuZPpenOPNNjh37xxxxwxMj2kqUHKfJ489H2xOpPqpA4tWRKAspQCkv/AFVctcr5 +1gWhJ5M5Mw/W6mgLswzonpsRy9M+vknuiDJ9F/Qe2hWUBJ0p7Si7YJkrynF3Oiqz +3D3JCiZAmoHq31hxO3bU7Sltf0lp1E7rG7mx7l0Pxq3fzNgaA9IW30NuF9J3YohK +Cv4pl11BYuUsBiQZktKcUwAsNj+UmZTnFBWACn4444+tFvs/tgG3wFZA33y3HAkl +6X9WGEa4d+DXeMgk2hr5oMRI9tZWqhNFqpu96CzrjJDxchgpeYjSCFiiX3po6Gyd +vH8uvB5hNKzj1vwqEtyyyyikUrPBe+273VeXNB+npF4LRok1MBjHZ49oZd2GZKBl +vo5u8szs +=i6qt +-----END PGP SIGNATURE----- +""" + + +class TestUtilPGP(unittest.TestCase): + + def test_empty_file(self): + with self.assertRaises(RuntimeError) as exp: + get_payload_from_clearsigned_message("") + + assert str(exp.exception) == \ + 'Invalid message format, no --BEGIN PGP SIGNED MESSAGE-- header' + + def test_invalid_clearsigned_payload(self): + with self.assertRaises(RuntimeError) as exp: + get_payload_from_clearsigned_message("Just\nSome\nMessage\na\nb\nc") + + assert str(exp.exception) == \ + 'Invalid message format, no --BEGIN PGP SIGNED MESSAGE-- header' + + def test_payload_extraction(self): + payload = get_payload_from_clearsigned_message(SAMPLE_CLEARSIGNED_MESSAGE) + d = json.loads(payload) + assert d['key1'] == 'value1' + assert d['key2'] == [1, 2, 3, 4] diff --git a/hm_pyhelper/util/__init__.py b/hm_pyhelper/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hm_pyhelper/util/pgp.py b/hm_pyhelper/util/pgp.py new file mode 100644 index 0000000..697e868 --- /dev/null +++ b/hm_pyhelper/util/pgp.py @@ -0,0 +1,35 @@ +"""Utility code to work with GPG signed messages.""" + + +def get_payload_from_clearsigned_message(message: str) -> str: + """ + Given a message in clearsign format removes signature and marker strings like + -----BEGIN PGP SIGNATURE-----, -----BEGIN PGP SIGNED MESSAGE----- to extract + the original payload. + + :param message: The message containing the signature and the payload. + :return: Extracted payload as string. Calling code is responsible for + converting it to proper data type. + """ + lines = message.strip().split('\n') + + if len(lines) < 5 \ + or lines[0] != '-----BEGIN PGP SIGNED MESSAGE-----' \ + or lines[1].startswith('Hash:') is False: + raise RuntimeError("Invalid message format, no --BEGIN PGP SIGNED MESSAGE-- header") + + start_idx = 3 # Payload starts from 3rd line in clearsigned messages + end_idx = None + prev_line = "" + + for idx, line in enumerate(lines[3:]): + if line.strip() == '-----BEGIN PGP SIGNATURE-----' and prev_line.strip() == "": + end_idx = idx + start_idx + break + + prev_line = line + + if end_idx is None: + raise RuntimeError("Invalid message format, no --BEGIN PGP SIGNATURE-- section") + + return "\n".join(lines[start_idx: end_idx-1]) diff --git a/setup.py b/setup.py index 439920b..7608f10 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ setup( name='hm_pyhelper', - version='0.13.10', + version='0.13.11', author="Nebra Ltd", author_email="support@nebra.com", description="Helium Python Helper",