diff options
author | Michał Górny <mgorny@gentoo.org> | 2018-01-16 13:18:53 +0100 |
---|---|---|
committer | Michał Górny <mgorny@gentoo.org> | 2018-01-16 13:18:53 +0100 |
commit | 3d282360742212fc3670003943a0e41788e7b795 (patch) | |
tree | 8ac068a132577b8a7be5f48a51326642740efa81 | |
parent | 8e150e18bc0617102e53c353d5ee300afee5a704 (diff) | |
download | gemato-3d282360742212fc3670003943a0e41788e7b795.tar.gz |
openpgp: Refactor to use class for system environment as well
-rw-r--r-- | gemato/cli.py | 36 | ||||
-rw-r--r-- | gemato/manifest.py | 16 | ||||
-rw-r--r-- | gemato/openpgp.py | 141 | ||||
-rw-r--r-- | gemato/recursiveloader.py | 4 |
4 files changed, 122 insertions, 75 deletions
diff --git a/gemato/cli.py b/gemato/cli.py index 6b3ab9e..7459841 100644 --- a/gemato/cli.py +++ b/gemato/cli.py @@ -43,11 +43,19 @@ def do_verify(args, argp): kwargs['fail_handler'] = verify_failure if not args.openpgp_verify: init_kwargs['verify_openpgp'] = False - with gemato.openpgp.OpenPGPEnvironment() as env: + + # use isolated environment if key is specified; + # system environment otherwise + if args.openpgp_key is not None: + env_class = gemato.openpgp.OpenPGPEnvironment + else: + env_class = gemato.openpgp.OpenPGPSystemEnvironment + + with env_class() as env: if args.openpgp_key is not None: with io.open(args.openpgp_key, 'rb') as f: env.import_key(f) - init_kwargs['openpgp_env'] = env + init_kwargs['openpgp_env'] = env start = timeit.default_timer() try: @@ -113,11 +121,19 @@ def do_update(args, argp): args.profile) if args.sign is not None: init_kwargs['sign_openpgp'] = args.sign - with gemato.openpgp.OpenPGPEnvironment() as env: + + # use isolated environment if key is specified; + # system environment otherwise + if args.openpgp_key is not None: + env_class = gemato.openpgp.OpenPGPEnvironment + else: + env_class = gemato.openpgp.OpenPGPSystemEnvironment + + with env_class() as env: if args.openpgp_key is not None: with io.open(args.openpgp_key, 'rb') as f: env.import_key(f) - init_kwargs['openpgp_env'] = env + init_kwargs['openpgp_env'] = env start = timeit.default_timer() try: @@ -204,11 +220,19 @@ def do_create(args, argp): args.profile) if args.sign is not None: init_kwargs['sign_openpgp'] = args.sign - with gemato.openpgp.OpenPGPEnvironment() as env: + + # use isolated environment if key is specified; + # system environment otherwise + if args.openpgp_key is not None: + env_class = gemato.openpgp.OpenPGPEnvironment + else: + env_class = gemato.openpgp.OpenPGPSystemEnvironment + + with env_class() as env: if args.openpgp_key is not None: with io.open(args.openpgp_key, 'rb') as f: env.import_key(f) - init_kwargs['openpgp_env'] = env + init_kwargs['openpgp_env'] = env start = timeit.default_timer() try: diff --git a/gemato/manifest.py b/gemato/manifest.py index 37d5288..39a8655 100644 --- a/gemato/manifest.py +++ b/gemato/manifest.py @@ -356,8 +356,8 @@ class ManifestFile(object): in text mode, and oriented at the beginning. If @verify_openpgp is True and the Manifest contains an OpenPGP - signature, the signature will be verified. Provide @openpgp_env - to perform the verification in specific environment. + signature, the signature will be verified. In that case, + @openpgp_env needs to be provided. If the verification succeeds, the openpgp_signed property will be set to True. If it fails or OpenPGP is not available, @@ -434,8 +434,9 @@ class ManifestFile(object): "Manifest terminated early, inside signature") if verify_openpgp and state == ManifestState.POST_SIGNED_DATA: + assert openpgp_env with io.StringIO(openpgp_data) as f: - gemato.openpgp.verify_file(f, env=openpgp_env) + openpgp_env.verify_file(f) self.openpgp_signed = True def dump(self, f, sign_openpgp=None, openpgp_keyid=None, @@ -449,8 +450,9 @@ class ManifestFile(object): If it is None (the default), the file will be signed if it was originally signed with a valid signature. - @openpgp_keyid and @openpgp_env specify the key - and the environment to use for signing. + If @openpgp_keyid is not None, it specifies the key to use + for signing. If @sign_openpgp is not False, @openpgp_env must + provide an OpenPGP environment for signing. If @sort is True, the entries are sorted prior to dumping. """ @@ -462,12 +464,12 @@ class ManifestFile(object): self.entries = sorted(self.entries) if sign_openpgp: + assert openpgp_env with io.StringIO() as data: # get the plain data into a stream self.dump(data, sign_openpgp=False) data.seek(0) - gemato.openpgp.clear_sign_file(data, f, - keyid=openpgp_keyid, env=openpgp_env) + openpgp_env.clear_sign_file(data, f, keyid=openpgp_keyid) else: for e in self.entries: f.write(u' '.join(e.to_list()) + '\n') diff --git a/gemato/openpgp.py b/gemato/openpgp.py index aadc9e5..2d6dd84 100644 --- a/gemato/openpgp.py +++ b/gemato/openpgp.py @@ -42,7 +42,76 @@ def _spawn_gpg(options, env_instance, stdin): return (p.wait(), out, err) -class OpenPGPEnvironment(object): +class OpenPGPEnvironmentBase(object): + """ + Base class for OpenPGP environment. + """ + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_cb): + pass + + def close(self): + pass + + def import_key(self, keyfile): + """ + Import a public key from open file @keyfile. The file should + be open for reading in binary mode, and oriented + at the beginning. + """ + + raise NotImplementedError('import_key() is not implemented by this OpenPGP provider') + + def verify_file(self, f): + """ + 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. + """ + + raise NotImplementedError('verify_file() is not implemented by this OpenPGP provider') + + def clear_sign_file(self, f, outf, keyid=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 text 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. + """ + + raise NotImplementedError('clear_sign_file() is not implemented by this OpenPGP provider') + + +class OpenPGPSystemEnvironment(OpenPGPEnvironmentBase): + """ + The system environment for OpenPGP routines. + """ + + def verify_file(self, f): + exitst, out, err = _spawn_gpg(['--verify'], None, f.read().encode('utf8')) + if exitst != 0: + raise gemato.exceptions.OpenPGPVerificationFailure(err.decode('utf8')) + + def clear_sign_file(self, f, outf, keyid=None): + args = [] + if keyid is not None: + args += ['--local-user', keyid] + exitst, out, err = _spawn_gpg(['--clearsign'] + args, None, + f.read().encode('utf8')) + if exitst != 0: + raise gemato.exceptions.OpenPGPSigningFailure(err.decode('utf8')) + + outf.write(out.decode('utf8')) + + +class OpenPGPEnvironment(OpenPGPEnvironmentBase): """ An isolated environment for OpenPGP routines. Used to get reliable verification results independently of user configuration. @@ -64,9 +133,6 @@ class OpenPGPEnvironment(object): disable-scdaemon ''') - def __enter__(self): - return self - def __exit__(self, exc_type, exc_value, exc_cb): if self._home is not None: self.close() @@ -95,30 +161,25 @@ disable-scdaemon self._home = None def import_key(self, keyfile): - """ - Import a public key from open file @keyfile. The file should - be open for reading in binary mode, and oriented - at the beginning. - """ - exitst, out, err = _spawn_gpg(['--import'], self, keyfile.read()) if exitst != 0: raise RuntimeError('Unable to import key: {}'.format(err.decode('utf8'))) def verify_file(self, f): - """ - A convenience wrapper for verify_file(), using this environment. - """ - - verify_file(f, env=self) + exitst, out, err = _spawn_gpg(['--verify'], self, f.read().encode('utf8')) + if exitst != 0: + raise gemato.exceptions.OpenPGPVerificationFailure(err.decode('utf8')) def clear_sign_file(self, f, outf, keyid=None): - """ - A convenience wrapper for clear_sign_file(), using this - environment. - """ + args = [] + if keyid is not None: + args += ['--local-user', keyid] + exitst, out, err = _spawn_gpg(['--clearsign'] + args, self, + f.read().encode('utf8')) + if exitst != 0: + raise gemato.exceptions.OpenPGPSigningFailure(err.decode('utf8')) - clear_sign_file(f, outf, keyid=keyid, env=self) + outf.write(out.decode('utf8')) @property def home(self): @@ -126,43 +187,3 @@ disable-scdaemon raise RuntimeError( 'OpenPGPEnvironment has been closed') return self._home - - -def verify_file(f, env=None): - """ - 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. - - Note that this function does not distinguish whether the key - is trusted, and is subject to user configuration. To get reliable - results, prepare a dedicated OpenPGPEnvironment and pass it as @env. - """ - - exitst, out, err = _spawn_gpg(['--verify'], env, f.read().encode('utf8')) - 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 text 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, - f.read().encode('utf8')) - if exitst != 0: - raise gemato.exceptions.OpenPGPSigningFailure(err.decode('utf8')) - - outf.write(out.decode('utf8')) diff --git a/gemato/recursiveloader.py b/gemato/recursiveloader.py index befda32..35c304a 100644 --- a/gemato/recursiveloader.py +++ b/gemato/recursiveloader.py @@ -28,8 +28,8 @@ class ManifestLoader(object): @root_directory specifies top directory of Manifest tree. If @verify_openpgp is True and a Manifest contain an OpenPGP - signature, the signature will be verified. Provide @openpgp_env - to perform the verification in specific environment. + signature, the signature will be verified. @openpgp_env + is the OpenPGP environment to use. """ self.root_directory = root_directory self.verify_openpgp = verify_openpgp |