summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gemato/exceptions.py8
-rw-r--r--gemato/openpgp.py34
-rw-r--r--tests/test_openpgp.py22
3 files changed, 53 insertions, 11 deletions
diff --git a/gemato/exceptions.py b/gemato/exceptions.py
index 24451dc..aa4e499 100644
--- a/gemato/exceptions.py
+++ b/gemato/exceptions.py
@@ -195,6 +195,14 @@ class OpenPGPUnknownSigFailure(OpenPGPRuntimeError):
f'{self.output}')
+class OpenPGPUntrustedSigFailure(OpenPGPRuntimeError):
+ """OpenPGP verification failed due to untrusted signing key"""
+
+ def __str__(self):
+ return (f'Good OpenPGP signature made using untrusted key:\n'
+ f'{self.output}')
+
+
class OpenPGPSigningFailure(OpenPGPRuntimeError):
"""
An exception raised when OpenPGP signing fails.
diff --git a/gemato/openpgp.py b/gemato/openpgp.py
index 83d5d05..b1100c1 100644
--- a/gemato/openpgp.py
+++ b/gemato/openpgp.py
@@ -25,6 +25,7 @@ from gemato.exceptions import (
OpenPGPKeyListingError,
OpenPGPKeyRefreshError,
OpenPGPUnknownSigFailure,
+ OpenPGPUntrustedSigFailure,
OpenPGPSigningFailure,
)
@@ -70,7 +71,7 @@ class OpenPGPSystemEnvironment:
def close(self):
pass
- def import_key(self, keyfile):
+ def import_key(self, keyfile, trust=True):
"""
Import a public key from open file @keyfile. The file should
be open for reading in binary mode, and oriented
@@ -127,6 +128,7 @@ class OpenPGPSystemEnvironment:
raise OpenPGPVerificationFailure(err.decode('utf8'))
is_good = False
+ is_trusted = False
sig_data = None
# process the output of gpg to find the exact result
@@ -146,10 +148,18 @@ class OpenPGPSystemEnvironment:
pkfp = spl[11].decode('utf8')
sig_data = OpenPGPSignatureData(fp, ts, expts, pkfp)
+ elif line.startswith(b'[GNUPG:] TRUST_'):
+ spl = line.split(b' ', 2)
+ if spl[1] in (b'TRUST_MARGINAL',
+ b'TRUST_FULL',
+ b'TRUST_ULTIMATE'):
+ is_trusted = True
# require both GOODSIG and VALIDSIG
if not is_good or sig_data is None:
raise OpenPGPUnknownSigFailure(err.decode('utf8'))
+ if not is_trusted:
+ raise OpenPGPUntrustedSigFailure(err.decode('utf8'))
return sig_data
def clear_sign_file(self, f, outf, keyid=None):
@@ -223,8 +233,8 @@ debug-level guru
with open(os.path.join(self._home, 'gpg.conf'), 'w') as f:
f.write('''# autogenerated by gemato
-# we are using an isolated keyring, so always trust our keys
-trust-model always
+# we set validity directly on keys
+trust-model direct
''')
with open(os.path.join(self._home, 'gpg-agent.conf'), 'w') as f:
f.write(f'''# autogenerated by gemato
@@ -273,12 +283,26 @@ debug-level guru
f'{self._home}')
self._home = None
- def import_key(self, keyfile):
+ def import_key(self, keyfile, trust=True):
exitst, out, err = self._spawn_gpg(
- [GNUPG, '--batch', '--import'], keyfile.read())
+ [GNUPG, '--batch', '--import', '--status-fd', '1'],
+ keyfile.read())
if exitst != 0:
raise OpenPGPKeyImportError(err.decode('utf8'))
+ if trust:
+ fprs = set()
+ for line in out.splitlines():
+ if line.startswith(b'[GNUPG:] IMPORT_OK'):
+ fprs.add(line.split(b' ')[3].decode('ASCII'))
+
+ ownertrust = ''.join(f'{fpr}:6:\n' for fpr in fprs).encode('utf8')
+ exitst, out, err = self._spawn_gpg(
+ [GNUPG, '--batch', '--import-ownertrust'],
+ ownertrust)
+ if exitst != 0:
+ raise OpenPGPKeyImportError(err.decode('utf8'))
+
def list_keys(self):
"""
List fingerprints and UIDs of all keys in keyring
diff --git a/tests/test_openpgp.py b/tests/test_openpgp.py
index edeb0b9..dbb71e5 100644
--- a/tests/test_openpgp.py
+++ b/tests/test_openpgp.py
@@ -22,10 +22,10 @@ from gemato.exceptions import (
OpenPGPKeyImportError,
OpenPGPKeyRefreshError,
OpenPGPRuntimeError,
+ OpenPGPUntrustedSigFailure,
)
from gemato.manifest import ManifestFile
from gemato.openpgp import (
- GNUPG,
OpenPGPEnvironment,
OpenPGPSystemEnvironment,
)
@@ -338,11 +338,8 @@ class OpenPGPMockedSystemEnvironment(OpenPGPSystemEnvironment):
self._tmpdir = None
os.environ.pop('GNUPGHOME', None)
- def import_key(self, keyfile):
- exitst, out, err = self._spawn_gpg(
- [GNUPG, '--batch', '--import'], keyfile.read())
- if exitst != 0:
- raise OpenPGPKeyImportError(err.decode('utf8'))
+ def import_key(self, keyfile, trust=True):
+ OpenPGPEnvironment.import_key(self, keyfile, trust=trust)
@pytest.fixture(params=[OpenPGPEnvironment,
@@ -420,6 +417,19 @@ def test_verify_manifest(openpgp_env, manifest_var, key_var, expected):
pytest.skip(str(e))
+def test_verify_untrusted_key():
+ try:
+ openpgp_env = OpenPGPMockedSystemEnvironment()
+ with io.BytesIO(VALID_PUBLIC_KEY) as f:
+ openpgp_env.import_key(f, trust=False)
+
+ with io.StringIO(SIGNED_MANIFEST) as f:
+ with pytest.raises(OpenPGPUntrustedSigFailure):
+ openpgp_env.verify_file(f)
+ except OpenPGPNoImplementation as e:
+ pytest.skip(str(e))
+
+
@pytest.mark.parametrize('manifest_var,key_var,expected',
MANIFEST_VARIANTS)
def test_manifest_load(openpgp_env, manifest_var, key_var, expected):