summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichał Górny <mgorny@gentoo.org>2017-10-27 22:07:27 +0200
committerMichał Górny <mgorny@gentoo.org>2017-10-27 22:07:27 +0200
commit6e49862c0a3539da2826063c434d523a82fbc461 (patch)
treea26efdb9c5bab94ebc18dca388cf528c54d0ba13
parent3f03960384881dfb41050599f9cb224f3a134a9e (diff)
downloadgemato-6e49862c0a3539da2826063c434d523a82fbc461.tar.gz
openpgp: Support signing keys
-rw-r--r--gemato/exceptions.py10
-rw-r--r--gemato/openpgp.py32
-rw-r--r--tests/test_openpgp.py76
3 files changed, 118 insertions, 0 deletions
diff --git a/gemato/exceptions.py b/gemato/exceptions.py
index 7c75f46..901d0b1 100644
--- a/gemato/exceptions.py
+++ b/gemato/exceptions.py
@@ -79,6 +79,16 @@ class OpenPGPVerificationFailure(Exception):
"OpenPGP verification failed:\n{}".format(output))
+class OpenPGPSigningFailure(Exception):
+ """
+ An exception raised when OpenPGP signing fails.
+ """
+
+ def __init__(self, output):
+ super(OpenPGPSigningFailure, self).__init__(
+ "OpenPGP signing failed:\n{}".format(output))
+
+
class OpenPGPNoImplementation(Exception):
"""
An exception raised when no supported OpenPGP implementation
diff --git a/gemato/openpgp.py b/gemato/openpgp.py
index 607c46f..b316f78 100644
--- a/gemato/openpgp.py
+++ b/gemato/openpgp.py
@@ -74,6 +74,14 @@ class OpenPGPEnvironment(object):
verify_file(f, env=self)
+ def clear_sign_file(self, f, outf, keyid=None):
+ """
+ A convenience wrapper for clear_sign_file(), using this
+ environment.
+ """
+
+ clear_sign_file(f, outf, keyid=keyid, env=self)
+
@property
def home(self):
if self._home is None:
@@ -99,3 +107,27 @@ def verify_file(f, env=None):
f)
if exitst != 0:
raise gemato.exceptions.OpenPGPVerificationFailure(err.decode('utf8'))
+
+
+def clear_sign_file(f, outf, keyid=None, env=None):
+ """
+ Create an OpenPGP cleartext signed message containing the data
+ from open file @f, and writing it into open file @outf.
+ Both files should be open in binary mode and set at the appropriate
+ position. Raises an exception if signing fails.
+
+ Pass @keyid to specify the key to use. If not specified,
+ the implementation will use the default key. Pass @env to use
+ a dedicated OpenPGPEnvironment.
+ """
+
+ args = []
+ if keyid is not None:
+ args += ['--local-user', keyid]
+ exitst, out, err = _spawn_gpg(['--clearsign'] + args,
+ env.home if env is not None else None,
+ f)
+ if exitst != 0:
+ raise gemato.exceptions.OpenPGPSigningFailure(err.decode('utf8'))
+
+ outf.write(out)
diff --git a/tests/test_openpgp.py b/tests/test_openpgp.py
index f9071c9..0ee295d 100644
--- a/tests/test_openpgp.py
+++ b/tests/test_openpgp.py
@@ -36,6 +36,43 @@ jCvJNJ7pU8YnJSRTQDH0PZEupAdzDU/AhGSrBz5+Jr7N0pQIxq4duE/Q
-----END PGP PUBLIC KEY BLOCK-----
'''
+PRIVATE_KEY = u'''
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+
+lQOYBFnwXJMBCACgaTVz+d10TGL9zR920sb0GBFsitAJ5ZFzO4E0cg3SHhwI+reM
+JQ6LLKmHowY/E1dl5FBbnJoRMxXP7/eScQ7HlhYj1gMPN5XiS2pkPwVkmJKBDV42
+DLwoytC+ot0frRTJvSdEPCX81BNMgFiBSpkeZfXqb9XmU03bh6mFnrdd4CsHpTQG
+csVXHK8QKhaxuqmHTALdpSzKCb/r0N/Z3sQExZhfLcBf/9UUVXj44Nwc6ooqZLRi
+zHydxwQdxNu0aOFGEBn9WTi8Slf7MfR/pF0dI8rs9w6zMzVEq0lhDPpKFGDveoGf
+g/+TpvBNXZ7DWH23GM4kID3pk4LLMc24U1PhABEBAAEAB/sEgeBMIXW9ClZvvj9H
+lfWcLz7yF1ZwKMC1BbOENz43LLxp7i2RJQtrErayxnxq8k6u4ML3SAe2OwK+ZIZG
+2aFqL0fw+tb8KvotsSPMrE6o/HaFZMxEZYg19zj1WlsvRCxE3OlJDA2fNJBUQnj6
+LQ/vYDsQOtM+VRHnfMDhLcwGObZnNPMwtmwkHLKWTgyTwAGnLObSheVutVbdyU6+
+wI3UXwAoilW2e+9pKtwaODjqT7pQ2maVSCY4MPGdLQpbPy61COstdpK/hRdI3liL
+uwszdlnT1QhiLsOTHPt4JjYdv2jgDjQobbe/ziKNzFp1eoMHDkbjzAh7oD2FxJcZ
+EYLnBADE5oryW+9GlyYQe3x74QD5BGTZfvJctvEOgUg8BsoIfXJgBzwnEwOD0XBg
+Jcl5qgt3IBH9Fn3JnYMpw12SEG2W4N8VCIBxIkDEBABVJfp1Q7HAJ8GSmzENnvt1
+iaAZPUscaFVpMyuajsCDmyK92NMymGiNAb1H5MU4gaFGaEaajwQA0I7gglsehQA2
+MSyJD0Uj+0b6n9KtiUzjyWEOcITXn4buf4O8Llor8gU0BWuv3hmIcvNsuJfmgXav
+Vxq2UHtiGaO7T9Vk4Sr8MKS9EYrLNbK41Lyb+tjxk3jYjEyFqCDNEtWKIZR4ENdR
+jo5gYKBtuqv1AYYSkflOTeaRlv/kIo8D/jVcyjmO19tNJM8lQE1xCvhp5maXOoSk
+1UoUmDprsKA2Em47J83sVivrIwBySB2n9srQynnV+8I47mX7YzYtNQ6uXdL3p/5e
+FRW+yfqVCShhSfyQdOmJ978UyQEwY0+0hhK372KatmaL9KEkKSuXgsqshv3XiB9y
+u3Su1jw5y2IQNP20D2dlbWF0byB0ZXN0IGtleYkBRgQTAQoAMBYhBIHhLBa9jc1g
+vhgIRRNogOcqexOEBQJZ8FyTAhsDBQsJCg0EAxUKCAIeAQIXgAAKCRATaIDnKnsT
+hCnkB/0fhTH230idhlfZhFbVgTLxrj4rpsGg20K8HkMaWzChsONdKkqYaYuRcm2U
+QZ0Kg5rm9jQsGYuAnzH/7XwmOleY95ycVfBkje9aXF6BEoGick6C/AK5w77vd1kc
+BtJDrT4I7vwD4wRkyUdCkpVMVT4z4aZ7lHJ4ECrrrI/mg0b+sGRyHfXPvIPp7F29
+59L/dpbhBZDfMOFC0A9LBQBJldKFbQLg3xzX4tniz/BBrp7KjTOMKU0sufsedI50
+xc6cvCYCwJElqo86vv69klZHahE/k9nJaUAMjCvJNJ7pU8YnJSRTQDH0PZEupAdz
+DU/AhGSrBz5+Jr7N0pQIxq4duE/Q
+=wOFB
+-----END PGP PRIVATE KEY BLOCK-----
+
+'''
+
+PRIVATE_KEY_ID = b'0x136880E72A7B1384'
+
MALFORMED_PUBLIC_KEY = u'''
-----BEGIN PGP PUBLIC KEY BLOCK-----
@@ -486,3 +523,42 @@ class OpenPGPContextManagerTest(unittest.TestCase):
env.close()
with self.assertRaises(RuntimeError):
env.home
+
+
+class OpenPGPPrivateKeyTest(unittest.TestCase):
+ """
+ Tests performed with the private key available.
+ """
+
+ TEST_STRING = b'The quick brown fox jumps over the lazy dog'
+
+ def setUp(self):
+ self.env = gemato.openpgp.OpenPGPEnvironment()
+ try:
+ self.env.import_key(
+ io.BytesIO(PRIVATE_KEY.encode('utf8')))
+ except gemato.exceptions.OpenPGPNoImplementation as e:
+ raise unittest.SkipTest(str(e))
+ except RuntimeError:
+ raise unittest.SkipTest('Unable to import OpenPGP key')
+
+ def tearDown(self):
+ self.env.close()
+
+ def test_verify_manifest(self):
+ with io.BytesIO(SIGNED_MANIFEST.encode('utf8')) as f:
+ self.env.verify_file(f)
+
+ def test_sign_data(self):
+ with io.BytesIO(self.TEST_STRING) as f:
+ with io.BytesIO() as wf:
+ self.env.clear_sign_file(f, wf)
+ wf.seek(0)
+ self.env.verify_file(wf)
+
+ def test_sign_data_keyid(self):
+ with io.BytesIO(self.TEST_STRING) as f:
+ with io.BytesIO() as wf:
+ self.env.clear_sign_file(f, wf, keyid=PRIVATE_KEY_ID)
+ wf.seek(0)
+ self.env.verify_file(wf)