diff options
author | Ben Boeckel <mathstuf@users.noreply.github.com> | 2017-05-26 13:31:04 -0400 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2017-05-26 10:31:04 -0700 |
commit | f38d250e508ef933a6f0bf1e0e5be89c23e20559 (patch) | |
tree | d3669e79a944d66486503aa3e56ae11a48b77ad4 | |
parent | 71cc4e2ad1f413be1abde48e7de0df1328e0e37d (diff) | |
download | spack-f38d250e508ef933a6f0bf1e0e5be89c23e20559.tar.gz spack-f38d250e508ef933a6f0bf1e0e5be89c23e20559.tar.bz2 spack-f38d250e508ef933a6f0bf1e0e5be89c23e20559.tar.xz spack-f38d250e508ef933a6f0bf1e0e5be89c23e20559.zip |
gpg: add 'spack gpg subcommand (#3845)
- Add a `spack gpg` subcommand in anticipation of signed binaries.
- GPG keys are stored in var/spack/gpg, and the spack gpg command manages them.
- Docs are included on the command.
-rw-r--r-- | .travis.yml | 2 | ||||
-rw-r--r-- | lib/spack/docs/basic_usage.rst | 64 | ||||
-rw-r--r-- | lib/spack/docs/getting_started.rst | 1 | ||||
-rw-r--r-- | lib/spack/spack/__init__.py | 7 | ||||
-rw-r--r-- | lib/spack/spack/cmd/gpg.py | 168 | ||||
-rw-r--r-- | lib/spack/spack/test/cmd/gpg.py | 181 | ||||
-rw-r--r-- | lib/spack/spack/util/gpg.py | 120 | ||||
-rw-r--r-- | var/spack/gpg.mock/README.md | 3 | ||||
-rw-r--r-- | var/spack/gpg.mock/data/content.txt | 1 | ||||
-rw-r--r-- | var/spack/gpg.mock/data/content.txt.asc | 17 | ||||
-rw-r--r-- | var/spack/gpg.mock/keys/external.key | 30 | ||||
-rw-r--r-- | var/spack/gpg/README.md | 5 |
12 files changed, 599 insertions, 0 deletions
diff --git a/.travis.yml b/.travis.yml index c89bcf1275..ef3ec362c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -71,12 +71,14 @@ addons: - gfortran - mercurial - graphviz + - gnupg2 # Work around Travis's lack of support for Python on OSX before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew ls --versions python > /dev/null || brew install python; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew ls --versions gcc > /dev/null || brew install gcc; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew ls --versions gnupg2 > /dev/null || brew install gnupg2; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then virtualenv venv; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source venv/bin/activate; fi diff --git a/lib/spack/docs/basic_usage.rst b/lib/spack/docs/basic_usage.rst index f25247579b..6eba26a4b5 100644 --- a/lib/spack/docs/basic_usage.rst +++ b/lib/spack/docs/basic_usage.rst @@ -276,6 +276,70 @@ Seeing installed packages We know that ``spack list`` shows you the names of available packages, but how do you figure out which are already installed? +.. _cmd-spack-gpg: + +^^^^^^^^^^^^^ +``spack gpg`` +^^^^^^^^^^^^^ + +Spack has support for signing and verifying packages using GPG keys. A +separate keyring is used for Spack, so any keys available in the user's home +directory are not used. + +^^^^^^^^^^^^^^^^^^ +``spack gpg init`` +^^^^^^^^^^^^^^^^^^ + +When Spack is first installed, its keyring is empty. Keys stored in +:file:`var/spack/gpg` are the default keys for a Spack installation. These +keys may be imported by running ``spack gpg init``. This will import the +default keys into the keyring as trusted keys. + +------------- +Trusting keys +------------- + +Additional keys may be added to the keyring using +``spack gpg trust <keyfile>``. Once a key is trusted, packages signed by the +owner of they key may be installed. + +------------- +Creating keys +------------- + +You may also create your own key so that you may sign your own packages using +``spack gpg create <name> <email>``. By default, the key has no expiration, +but it may be set with the ``--expires <date>`` flag (see the ``gnupg2`` +documentation for accepted date formats). It is also recommended to add a +comment as to the use of the key using the ``--comment <comment>`` flag. The +public half of the key can also be exported for sharing with others so that +they may use packages you have signed using the ``--export <keyfile>`` flag. +Secret keys may also be later exported using the +``spack gpg export <location> [<key>...]`` command. + +------------ +Listing keys +------------ + +In order to list the keys available in the keyring, the +``spack gpg list`` command will list trusted keys with the ``--trusted`` flag +and keys available for signing using ``--signing``. If you would like to +remove keys from your keyring, ``spack gpg untrust <keyid>``. Key IDs can be +email addresses, names, or (best) fingerprints. + +------------------------------ +Signing and Verifying Packages +------------------------------ + +In order to sign a package, ``spack gpg sign <file>`` should be used. By +default, the signature will be written to ``<file>.asc``, but that may be +changed by using the ``--output <file>`` flag. If there is only one signing +key available, it will be used, but if there is more than one, the key to use +must be specified using the ``--key <keyid>`` flag. The ``--clearsign`` flag +may also be used to create a signed file which contains the contents, but it +is not recommended. Signed packages may be verified by using +``spack gpg verify <file>``. + .. _cmd-spack-find: ^^^^^^^^^^^^^^ diff --git a/lib/spack/docs/getting_started.rst b/lib/spack/docs/getting_started.rst index 971d42cea0..75c2f662b5 100644 --- a/lib/spack/docs/getting_started.rst +++ b/lib/spack/docs/getting_started.rst @@ -14,6 +14,7 @@ before Spack is run: 1. Python 2 (2.6 or 2.7) or 3 (3.3 - 3.6) 2. A C/C++ compiler 3. The ``git`` and ``curl`` commands. +4. If using the ``gpg`` subcommand, ``gnupg2`` is required. These requirements can be easily installed on most modern Linux systems; on Macintosh, XCode is required. Spack is designed to run on HPC diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index 27283d10a9..057c54d665 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -68,6 +68,13 @@ opt_path = join_path(prefix, "opt") etc_path = join_path(prefix, "etc") +# GPG paths. +gpg_keys_path = join_path(var_path, "gpg") +mock_gpg_data_path = join_path(var_path, "gpg.mock", "data") +mock_gpg_keys_path = join_path(var_path, "gpg.mock", "keys") +gpg_path = join_path(opt_path, "spack", "gpg") + + #----------------------------------------------------------------------------- # Initial imports (only for use in this file -- see __all__ below.) #----------------------------------------------------------------------------- diff --git a/lib/spack/spack/cmd/gpg.py b/lib/spack/spack/cmd/gpg.py new file mode 100644 index 0000000000..ff511b6520 --- /dev/null +++ b/lib/spack/spack/cmd/gpg.py @@ -0,0 +1,168 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +from spack.util.gpg import Gpg +import spack +import os + +description = "handle GPG actions for spack" +section = "developer" +level = "long" + + +def setup_parser(subparser): + setup_parser.parser = subparser + subparsers = subparser.add_subparsers(help='GPG sub-commands') + + verify = subparsers.add_parser('verify') + verify.add_argument('package', type=str, + help='the package to verify') + verify.add_argument('signature', type=str, nargs='?', + help='the signature file') + verify.set_defaults(func=gpg_verify) + + trust = subparsers.add_parser('trust') + trust.add_argument('keyfile', type=str, + help='add a key to the trust store') + trust.set_defaults(func=gpg_trust) + + untrust = subparsers.add_parser('untrust') + untrust.add_argument('--signing', action='store_true', + help='allow untrusting signing keys') + untrust.add_argument('keys', nargs='+', type=str, + help='remove keys from the trust store') + untrust.set_defaults(func=gpg_untrust) + + sign = subparsers.add_parser('sign') + sign.add_argument('--output', metavar='DEST', type=str, + help='the directory to place signatures') + sign.add_argument('--key', metavar='KEY', type=str, + help='the key to use for signing') + sign.add_argument('--clearsign', action='store_true', + help='if specified, create a clearsign signature') + sign.add_argument('package', type=str, + help='the package to sign') + sign.set_defaults(func=gpg_sign) + + create = subparsers.add_parser('create') + create.add_argument('name', type=str, + help='the name to use for the new key') + create.add_argument('email', type=str, + help='the email address to use for the new key') + create.add_argument('--comment', metavar='COMMENT', type=str, + default='GPG created for Spack', + help='a description for the intended use of the key') + create.add_argument('--expires', metavar='EXPIRATION', type=str, + default='0', help='when the key should expire') + create.add_argument('--export', metavar='DEST', type=str, + help='export the public key to a file') + create.set_defaults(func=gpg_create) + + list = subparsers.add_parser('list') + list.add_argument('--trusted', action='store_true', + help='list trusted keys') + list.add_argument('--signing', action='store_true', + help='list keys which may be used for signing') + list.set_defaults(func=gpg_list) + + init = subparsers.add_parser('init') + init.set_defaults(func=gpg_init) + init.set_defaults(import_dir=spack.gpg_keys_path) + + export = subparsers.add_parser('export') + export.add_argument('location', type=str, + help='where to export keys') + export.add_argument('keys', nargs='*', + help='the keys to export; ' + 'all secret keys if unspecified') + export.set_defaults(func=gpg_export) + + +def gpg_create(args): + if args.export: + old_sec_keys = Gpg.signing_keys() + Gpg.create(name=args.name, email=args.email, + comment=args.comment, expires=args.expires) + if args.export: + new_sec_keys = set(Gpg.signing_keys()) + new_keys = new_sec_keys.difference(old_sec_keys) + Gpg.export_keys(args.export, *new_keys) + + +def gpg_export(args): + keys = args.keys + if not keys: + keys = Gpg.signing_keys() + Gpg.export_keys(args.location, *keys) + + +def gpg_list(args): + Gpg.list(args.trusted, args.signing) + + +def gpg_sign(args): + key = args.key + if key is None: + keys = Gpg.signing_keys() + if len(keys) == 1: + key = keys[0] + elif not keys: + raise RuntimeError('no signing keys are available') + else: + raise RuntimeError('multiple signing keys are available; ' + 'please choose one') + output = args.output + if not output: + output = args.package + '.asc' + # TODO: Support the package format Spack creates. + Gpg.sign(key, args.package, output, args.clearsign) + + +def gpg_trust(args): + Gpg.trust(args.keyfile) + + +def gpg_init(args): + for root, _, filenames in os.walk(args.import_dir): + for filename in filenames: + if not filename.endswith('.key'): + continue + Gpg.trust(os.path.join(root, filename)) + + +def gpg_untrust(args): + Gpg.untrust(args.signing, *args.keys) + + +def gpg_verify(args): + # TODO: Support the package format Spack creates. + signature = args.signature + if signature is None: + signature = args.package + '.asc' + Gpg.verify(signature, args.package) + + +def gpg(parser, args): + if args.func: + args.func(args) diff --git a/lib/spack/spack/test/cmd/gpg.py b/lib/spack/spack/test/cmd/gpg.py new file mode 100644 index 0000000000..cc6d57d91e --- /dev/null +++ b/lib/spack/spack/test/cmd/gpg.py @@ -0,0 +1,181 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## +import argparse +import os.path + +import pytest +import spack +import spack.cmd.gpg as gpg +import spack.util.gpg as gpg_util +from spack.util.executable import ProcessError + + +@pytest.fixture(scope='function') +def testing_gpg_directory(tmpdir): + old_gpg_path = gpg_util.GNUPGHOME + gpg_util.GNUPGHOME = str(tmpdir.join('gpg')) + yield + gpg_util.GNUPGHOME = old_gpg_path + + +def has_gnupg2(): + try: + gpg_util.Gpg.gpg()('--version') + return True + except Exception: + return False + + +@pytest.mark.usefixtures('testing_gpg_directory') +@pytest.mark.skipif(not has_gnupg2(), + reason='These tests require gnupg2') +def test_gpg(tmpdir): + parser = argparse.ArgumentParser() + gpg.setup_parser(parser) + + # Verify a file with an empty keyring. + args = parser.parse_args(['verify', os.path.join( + spack.mock_gpg_data_path, 'content.txt')]) + with pytest.raises(ProcessError): + gpg.gpg(parser, args) + + # Import the default key. + args = parser.parse_args(['init']) + args.import_dir = spack.mock_gpg_keys_path + gpg.gpg(parser, args) + + # List the keys. + # TODO: Test the output here. + args = parser.parse_args(['list', '--trusted']) + gpg.gpg(parser, args) + args = parser.parse_args(['list', '--signing']) + gpg.gpg(parser, args) + + # Verify the file now that the key has been trusted. + args = parser.parse_args(['verify', os.path.join( + spack.mock_gpg_data_path, 'content.txt')]) + gpg.gpg(parser, args) + + # Untrust the default key. + args = parser.parse_args(['untrust', 'Spack testing']) + gpg.gpg(parser, args) + + # Now that the key is untrusted, verification should fail. + args = parser.parse_args(['verify', os.path.join( + spack.mock_gpg_data_path, 'content.txt')]) + with pytest.raises(ProcessError): + gpg.gpg(parser, args) + + # Create a file to test signing. + test_path = tmpdir.join('to-sign.txt') + with open(str(test_path), 'w+') as fout: + fout.write('Test content for signing.\n') + + # Signing without a private key should fail. + args = parser.parse_args(['sign', str(test_path)]) + with pytest.raises(RuntimeError) as exc_info: + gpg.gpg(parser, args) + assert exc_info.value.args[0] == 'no signing keys are available' + + # Create a key for use in the tests. + keypath = tmpdir.join('testing-1.key') + args = parser.parse_args(['create', + '--comment', 'Spack testing key', + '--export', str(keypath), + 'Spack testing 1', + 'spack@googlegroups.com']) + gpg.gpg(parser, args) + keyfp = gpg_util.Gpg.signing_keys()[0] + + # List the keys. + # TODO: Test the output here. + args = parser.parse_args(['list', '--trusted']) + gpg.gpg(parser, args) + args = parser.parse_args(['list', '--signing']) + gpg.gpg(parser, args) + + # Signing with the default (only) key. + args = parser.parse_args(['sign', str(test_path)]) + gpg.gpg(parser, args) + + # Verify the file we just verified. + args = parser.parse_args(['verify', str(test_path)]) + gpg.gpg(parser, args) + + # Export the key for future use. + export_path = tmpdir.join('export.testing.key') + args = parser.parse_args(['export', str(export_path)]) + gpg.gpg(parser, args) + + # Create a second key for use in the tests. + args = parser.parse_args(['create', + '--comment', 'Spack testing key', + 'Spack testing 2', + 'spack@googlegroups.com']) + gpg.gpg(parser, args) + + # List the keys. + # TODO: Test the output here. + args = parser.parse_args(['list', '--trusted']) + gpg.gpg(parser, args) + args = parser.parse_args(['list', '--signing']) + gpg.gpg(parser, args) + + test_path = tmpdir.join('to-sign-2.txt') + with open(str(test_path), 'w+') as fout: + fout.write('Test content for signing.\n') + + # Signing with multiple signing keys is ambiguous. + args = parser.parse_args(['sign', str(test_path)]) + with pytest.raises(RuntimeError) as exc_info: + gpg.gpg(parser, args) + assert exc_info.value.args[0] == \ + 'multiple signing keys are available; please choose one' + + # Signing with a specified key. + args = parser.parse_args(['sign', '--key', keyfp, str(test_path)]) + gpg.gpg(parser, args) + + # Untrusting signing keys needs a flag. + args = parser.parse_args(['untrust', 'Spack testing 1']) + with pytest.raises(ProcessError): + gpg.gpg(parser, args) + + # Untrust the key we created. + args = parser.parse_args(['untrust', '--signing', keyfp]) + gpg.gpg(parser, args) + + # Verification should now fail. + args = parser.parse_args(['verify', str(test_path)]) + with pytest.raises(ProcessError): + gpg.gpg(parser, args) + + # Trust the exported key. + args = parser.parse_args(['trust', str(export_path)]) + gpg.gpg(parser, args) + + # Verification should now succeed again. + args = parser.parse_args(['verify', str(test_path)]) + gpg.gpg(parser, args) diff --git a/lib/spack/spack/util/gpg.py b/lib/spack/spack/util/gpg.py new file mode 100644 index 0000000000..ccaf33519b --- /dev/null +++ b/lib/spack/spack/util/gpg.py @@ -0,0 +1,120 @@ +############################################################################## +# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory. +# +# This file is part of Spack. +# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved. +# LLNL-CODE-647188 +# +# For details, see https://github.com/llnl/spack +# Please also see the LICENSE file for our notice and the LGPL. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License (as +# published by the Free Software Foundation) version 2.1, February 1999. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and +# conditions of the GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +############################################################################## + +import os + +import spack +from spack.util.executable import Executable + + +GNUPGHOME = spack.gpg_path + + +class Gpg(object): + @staticmethod + def gpg(): + # TODO: Support loading up a GPG environment from a built gpg. + gpg = Executable('gpg2') + if not os.path.exists(GNUPGHOME): + os.makedirs(GNUPGHOME) + os.chmod(GNUPGHOME, 0o700) + gpg.add_default_env('GNUPGHOME', GNUPGHOME) + return gpg + + @classmethod + def create(cls, **kwargs): + r, w = os.pipe() + r = os.fdopen(r, 'r') + w = os.fdopen(w, 'w') + w.write(''' + Key-Type: rsa + Key-Length: 4096 + Key-Usage: sign + Name-Real: %(name)s + Name-Email: %(email)s + Name-Comment: %(comment)s + Expire-Date: %(expires)s + %%no-protection + %%commit + ''' % kwargs) + w.close() + cls.gpg()('--gen-key', '--batch', input=r) + r.close() + + @classmethod + def signing_keys(cls): + keys = [] + output = cls.gpg()('--list-secret-keys', '--with-colons', + '--fingerprint', output=str) + for line in output.split('\n'): + if line.startswith('fpr'): + keys.append(line.split(':')[9]) + return keys + + @classmethod + def export_keys(cls, location, *keys): + cls.gpg()('--armor', '--export', '--output', location, *keys) + + @classmethod + def trust(cls, keyfile): + cls.gpg()('--import', keyfile) + + @classmethod + def untrust(cls, signing, *keys): + args = [ + '--yes', + '--batch', + ] + if signing: + signing_args = args + ['--delete-secret-keys'] + list(keys) + cls.gpg()(*signing_args) + args.append('--delete-keys') + args.extend(keys) + cls.gpg()(*args) + + @classmethod + def sign(cls, key, file, output, clearsign=False): + args = [ + '--armor', + '--default-key', key, + '--output', output, + file, + ] + if clearsign: + args.insert(0, '--clearsign') + else: + args.insert(0, '--detach-sign') + cls.gpg()(*args) + + @classmethod + def verify(cls, signature, file): + cls.gpg()('--verify', signature, file) + + @classmethod + def list(cls, trusted, signing): + if trusted: + cls.gpg()('--list-public-keys') + if signing: + cls.gpg()('--list-secret-keys') diff --git a/var/spack/gpg.mock/README.md b/var/spack/gpg.mock/README.md new file mode 100644 index 0000000000..95215a8634 --- /dev/null +++ b/var/spack/gpg.mock/README.md @@ -0,0 +1,3 @@ +# Mock GPG directory + +This directory contains keys and data used in the testing Spack. diff --git a/var/spack/gpg.mock/data/content.txt b/var/spack/gpg.mock/data/content.txt new file mode 100644 index 0000000000..6ab0f567cf --- /dev/null +++ b/var/spack/gpg.mock/data/content.txt @@ -0,0 +1 @@ +This file has a signature signed by an external key. diff --git a/var/spack/gpg.mock/data/content.txt.asc b/var/spack/gpg.mock/data/content.txt.asc new file mode 100644 index 0000000000..7593156113 --- /dev/null +++ b/var/spack/gpg.mock/data/content.txt.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2 + +iQIcBAABCAAGBQJZELiKAAoJENygJBhApdriPvgP/0shBTmx4jg6QaI0zyie8a+R ++L/o9iIV4MqvBI5g+Ti+nktoCSxSOPOYFW4af740A7/43wIML9LK+gIhx/QbCrMb +bNqzyIry9/L6PK1cCuXvd10CT+MCF1P0hdaMtKihdBYB3J8f5y1i30z+a8YWsRsX +tPMVF/HunlpAkSWIpjmbJzFPT1R/UiBHl4VJ+mM3NNZYNIq8ZhKUiXwlQkZ8R8zg +M0IEFkwfFtp7JxnhG7jR0k63cNm3KSocAJpwENy46RKGsAvwvqTzRh4T2MlmQIjH +TC1MA8alJvtSdBHpkKffSU8jLewKHe1H48nc9NifMy04Ni8fSlGZe14Oe7Krqla0 +qWs+XHrGCmSleyiRUQes1MKQ7NhumKEoEaU+q0/c+lUDILZp1TlfvTPg2fzng4M/ +YF6+f+wqM+xY6z1/IloOMHis5oALjARSO88ldrLU4DQp/6jTKJO/+I4uWhMnPkMW ++a3GLWl1CShReHKbWZTLFtdQATZXA8M6wQ8FAsLOmRLb0AlEQ28A8fHrBCCdU2xj +tSG++U1ZUo64cMYQmIMsvIApnkTh7qCkDjaVBP1to3qc83YHncxorydz9ERpuDvP +d1IOHlJyUSM4+sLkCPvH9QyTaJn/x7D/VraznEiptGON7G6G9AgyAzIgYamm1Kwh +UDhbQDFDhLLvUSDGzO3l +=kwo9 +-----END PGP SIGNATURE----- diff --git a/var/spack/gpg.mock/keys/external.key b/var/spack/gpg.mock/keys/external.key new file mode 100644 index 0000000000..d08c90ea7f --- /dev/null +++ b/var/spack/gpg.mock/keys/external.key @@ -0,0 +1,30 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2 + +mQINBFkQuFIBEAC7DiUM7jQ01kaGX+4nguzVeYquBRYoEUiObl5UIVSavMn4I7Oy +aytG+qR26tUpunjEB6ftIQMJSyPKueclUJBaQ9lzQ3WpFC3ItpBNkMxHpiqPa9DX +ddMk2QtJt4TlCWJEdnhR/92mMF+vf7B5/OvFvKOi0P+AwzBHC8IKTxml/UosmeVI +Cs69FzRDXyqQxQAkATmuDmHXPaC6RkDmpVRe3ej+Kr+Xu4vcb/EBHg/vcZkFdSmi +hyOj21/8LQZzcwTg4TSgHzKqbjPtIEQM3NNksvcFYlq2X0ad4cBcxa1Hj5xV8oS/ +bdYOFSdsh3QRROcEeKYVQZhvCR12qS93P4b2egbamBxCQK0Sn6QPIjlR6+Ya2/6p +/hHddF+YVA6HJ22QZjaORf9lImYfYMs1ka2GtgkczOeaFEfcJ96nIa8Qb1jcrOon +/3k/l+Ae09HRCcGB2DgKXw7S+CXKt46Oadp3bIDAyceotGnrG3cVA6A9Lwqy6U/5 +ywry8ETu3wlIR3EAIwM0a/3xCPg3cC/bt9rSqsFcmXyxltGI2CBTWcTqcyjW4VAw +nVI8otBd4yNdimhpxLfx6AaMjA+D+OSltnAZUrp1fSFVhWLpTxLbcTv+HJ/g4U+x ++PAsQ79Hzmzvy/8nOvIprGzY4LCmBPbLUB47Yu761HhYQhkuJiYP1R/GzQARAQAB +tDpTcGFjayB0ZXN0aW5nIChTcGFjayB0ZXN0aW5nIGtleSkgPHNwYWNrQGdvb2ds +ZWdyb3Vwcy5jb20+iQI3BBMBCAAhBQJZELhSAhsDBQsJCAcCBhUICQoLAgQWAgMB +Ah4BAheAAAoJENygJBhApdriOnUP/iLC1ZxyBP3STSVgBBTS1L6FnRAc9ya6eXNT +EwLLoSL0I0srs0sThmhyW38ZamsXYDhggaetShxemcO0BoNAii/oNK9yQoXNF4f6 +7wg2ZxCDuDjp/3VsbiI+kNlH2kj1tQ/M53ak9nYhmwLJFfKzjQBWJiyTwYZwO3MB +QvXBvLIKj6IDS20o+7jbOq8F243vo5/uNHc/6C9eC3i4jzXWVlln2+iN/e5sVt+X +ZiggLK2Goj5CZ7ZjZQvdoH4wKbSPLBg0Lh5FYSih9p0wx0UTEoi0jPqFUDw81duz +IyxjbGASSaUxoz16C2U/olPEAAXeBe4266jRQwTrn+sEIX5FD+RGoryXQ97pV5up +I9wb2anVAMHOf20iYep3vYTjnFG/81ykODm8+I4D/Jj0EEe1E2b0D+7RQ9xKNYxC +fDgY3isXBFzmS6O4h8N27P06yfzQX+zvjPrrHRB7ka2pmDT3M421p2wN0n9aCq1J +8+M5UdpF98A38oosyE53KcItoCUFLgEP3KrWPwvpDUC2sNQAOFiHeitzc+v1iwmD +RScdefCQ8qc2JJdCqMG6M0tlFy6Tw1o0eBYOhhDGa0rq/PQ4NewR2dj+yDXXBGJy +ElR0VChqniMCyd2Q4SDPnhcVrWPTYSKL1MpsL0lXED8TGOdoAHHmQNU8MWhqmdBy +zcWArNUY +=yVqw +-----END PGP PUBLIC KEY BLOCK----- diff --git a/var/spack/gpg/README.md b/var/spack/gpg/README.md new file mode 100644 index 0000000000..122d24f841 --- /dev/null +++ b/var/spack/gpg/README.md @@ -0,0 +1,5 @@ +# GPG Keys + +This directory contains keys that should be trusted by this installation of +Spack. They are imported when running `spack gpg init`, but may also be +imported manually with `spack gpg trust path/to/key`. |