From 48c479944a4c7c0a75a8ae44fec39ea734708cad Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 30 May 2013 22:51:47 -0400 Subject: remove build generated files --- .../pyshared/leap/common/keymanager/__init__.py | 286 ------------- .../pyshared/leap/common/keymanager/errors.py | 46 --- .../share/pyshared/leap/common/keymanager/gpg.py | 397 ------------------ .../share/pyshared/leap/common/keymanager/keys.py | 230 ----------- .../pyshared/leap/common/keymanager/openpgp.py | 459 --------------------- 5 files changed, 1418 deletions(-) delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py delete mode 100644 debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py (limited to 'debian/python-leap.common/usr/share/pyshared/leap/common/keymanager') diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py deleted file mode 100644 index d6dbb8a..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/__init__.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- -# __init__.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Key Manager is a Nicknym agent for LEAP client. -""" - -import requests - -try: - import simplejson as json -except ImportError: - import json # noqa - -from leap.common.check import leap_assert -from leap.common.keymanager.errors import ( - KeyNotFound, - NoPasswordGiven, -) -from leap.common.keymanager.keys import ( - build_key_from_dict, -) -from leap.common.keymanager.openpgp import ( - OpenPGPKey, - OpenPGPScheme, - encrypt_sym, -) - - -TAGS_INDEX = 'by-tags' -TAGS_AND_PRIVATE_INDEX = 'by-tags-and-private' -INDEXES = { - TAGS_INDEX: ['tags'], - TAGS_AND_PRIVATE_INDEX: ['tags', 'bool(private)'], -} - - -class KeyManager(object): - - def __init__(self, address, nickserver_url, soledad, token=None): - """ - Initialize a Key Manager for user's C{address} with provider's - nickserver reachable in C{url}. - - @param address: The address of the user of this Key Manager. - @type address: str - @param url: The URL of the nickserver. - @type url: str - @param soledad: A Soledad instance for local storage of keys. - @type soledad: leap.soledad.Soledad - """ - self._address = address - self._nickserver_url = nickserver_url - self._soledad = soledad - self.token = token - self._wrapper_map = { - OpenPGPKey: OpenPGPScheme(soledad), - # other types of key will be added to this mapper. - } - self._init_indexes() - self._fetcher = requests - - # - # utilities - # - - def _key_class_from_type(self, ktype): - """ - Return key class from string representation of key type. - """ - return filter( - lambda klass: str(klass) == ktype, - self._wrapper_map).pop() - - def _init_indexes(self): - """ - Initialize the database indexes. - """ - # Ask the database for currently existing indexes. - db_indexes = dict(self._soledad.list_indexes()) - # Loop through the indexes we expect to find. - for name, expression in INDEXES.items(): - if name not in db_indexes: - # The index does not yet exist. - self._soledad.create_index(name, *expression) - continue - if expression == db_indexes[name]: - # The index exists and is up to date. - continue - # The index exists but the definition is not what expected, so we - # delete it and add the proper index expression. - self._soledad.delete_index(name) - self._soledad.create_index(name, *expression) - - def _get_dict_from_http_json(self, path): - """ - Make a GET HTTP request and return a dictionary containing the - response. - """ - response = self._fetcher.get(self._nickserver_url+path) - leap_assert(response.status_code == 200, 'Invalid response.') - leap_assert( - response.headers['content-type'].startswith('application/json') - is True, - 'Content-type is not JSON.') - return response.json() - - # - # key management - # - - def send_key(self, ktype, send_private=False, password=None): - """ - Send user's key of type C{ktype} to provider. - - Public key bound to user's is sent to provider, which will sign it and - replace any prior keys for the same address in its database. - - If C{send_private} is True, then the private key is encrypted with - C{password} and sent to server in the same request, together with a - hash string of user's address and password. The encrypted private key - will be saved in the server in a way it is publicly retrievable - through the hash string. - - @param ktype: The type of the key. - @type ktype: KeyType - - @raise httplib.HTTPException: - @raise KeyNotFound: If the key was not found both locally and in - keyserver. - """ - # prepare the public key bound to address - pubkey = self.get_key( - self._address, ktype, private=False, fetch_remote=False) - data = { - 'address': self._address, - 'keys': [ - json.loads(pubkey.get_json()), - ] - } - # prepare the private key bound to address - if send_private: - if password is None or password == '': - raise NoPasswordGiven('Can\'t send unencrypted private keys!') - privkey = self.get_key( - self._address, ktype, private=True, fetch_remote=False) - privkey = json.loads(privkey.get_json()) - privkey.key_data = encrypt_sym(privkey.key_data, password) - data['keys'].append(privkey) - self._fetcher.put( - self._nickserver_url + '/key/' + self._address, - data=data, - auth=(self._address, self._token)) - - def get_key(self, address, ktype, private=False, fetch_remote=True): - """ - Return a key of type C{ktype} bound to C{address}. - - First, search for the key in local storage. If it is not available, - then try to fetch from nickserver. - - @param address: The address bound to the key. - @type address: str - @param ktype: The type of the key. - @type ktype: KeyType - @param private: Look for a private key instead of a public one? - @type private: bool - - @return: A key of type C{ktype} bound to C{address}. - @rtype: EncryptionKey - @raise KeyNotFound: If the key was not found both locally and in - keyserver. - """ - leap_assert( - ktype in self._wrapper_map, - 'Unkown key type: %s.' % str(ktype)) - try: - return self._wrapper_map[ktype].get_key(address, private=private) - except KeyNotFound: - if fetch_remote is False: - raise - # fetch keys from server and discard unwanted types. - keys = filter(lambda k: isinstance(k, ktype), - self.fetch_keys_from_server(address)) - if len(keys) is 0: - raise KeyNotFound() - leap_assert( - len(keys) == 1, - 'Got more than one key of type %s for %s.' % - (str(ktype), address)) - self._wrapper_map[ktype].put_key(keys[0]) - return self._wrapper_map[ktype].get_key(address, private=private) - - def fetch_keys_from_server(self, address): - """ - Fetch keys bound to C{address} from nickserver. - - @param address: The address bound to the keys. - @type address: str - - @return: A list of keys bound to C{address}. - @rtype: list of EncryptionKey - @raise KeyNotFound: If the key was not found on nickserver. - @raise httplib.HTTPException: - """ - keydata = self._get_dict_from_http_json('/key/%s' % address) - leap_assert( - keydata['address'] == address, - "Fetched key for wrong address.") - keys = [] - for key in keydata['keys']: - keys.append( - build_key_from_dict( - self._key_class_from_type(key['type']), - address, - key)) - return keys - - def get_all_keys_in_local_db(self, private=False): - """ - Return all keys stored in local database. - - @return: A list with all keys in local db. - @rtype: list - """ - return map( - lambda doc: build_key_from_dict( - self._key_class_from_type(doc.content['type']), - doc.content['address'], - doc.content), - self._soledad.get_from_index( - TAGS_AND_PRIVATE_INDEX, - 'keymanager-key', - '1' if private else '0')) - - def refresh_keys(self): - """ - Fetch keys from nickserver and update them locally. - """ - addresses = set(map( - lambda doc: doc.address, - self.get_all_keys_in_local_db(private=False))) - # TODO: maybe we should not attempt to refresh our own public key? - for address in addresses: - for key in self.fetch_keys_from_server(address): - self._wrapper_map[key.__class__].put_key(key) - - def gen_key(self, ktype): - """ - Generate a key of type C{ktype} bound to the user's address. - - @param ktype: The type of the key. - @type ktype: KeyType - - @return: The generated key. - @rtype: EncryptionKey - """ - return self._wrapper_map[ktype].gen_key(self._address) - - # - # Token setter/getter - # - - def _get_token(self): - return self._token - - def _set_token(self, token): - self._token = token - - token = property( - _get_token, _set_token, doc='The auth token.') diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py deleted file mode 100644 index 1cf506e..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/errors.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -# errors.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Errors and exceptions used by the Key Manager. -""" - - -class KeyNotFound(Exception): - """ - Raised when key was no found on keyserver. - """ - - -class KeyAlreadyExists(Exception): - """ - Raised when attempted to create a key that already exists. - """ - - -class KeyAttributesDiffer(Exception): - """ - Raised when trying to delete a key but the stored key differs from the key - passed to the delete_key() method. - """ - -class NoPasswordGiven(Exception): - """ - Raised when trying to perform some action that needs a password without - providing one. - """ diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py deleted file mode 100644 index f3e6453..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/gpg.py +++ /dev/null @@ -1,397 +0,0 @@ -# -*- coding: utf-8 -*- -# gpgwrapper.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -A GPG wrapper used to handle OpenPGP keys. - -This is a temporary class that will be superseded by the a revised version of -python-gnupg. -""" - - -import os -import gnupg -import re -from gnupg import ( - logger, - _is_sequence, - _make_binary_stream, -) - - -class ListPackets(): - """ - Handle status messages for --list-packets. - """ - - def __init__(self, gpg): - """ - Initialize the packet listing handling class. - - @param gpg: GPG object instance. - @type gpg: gnupg.GPG - """ - self.gpg = gpg - self.nodata = None - self.key = None - self.need_passphrase = None - self.need_passphrase_sym = None - self.userid_hint = None - - def handle_status(self, key, value): - """ - Handle one line of the --list-packets status message. - - @param key: The status message key. - @type key: str - @param value: The status message value. - @type value: str - """ - # TODO: write tests for handle_status - if key == 'NODATA': - self.nodata = True - if key == 'ENC_TO': - # This will only capture keys in our keyring. In the future we - # may want to include multiple unknown keys in this list. - self.key, _, _ = value.split() - if key == 'NEED_PASSPHRASE': - self.need_passphrase = True - if key == 'NEED_PASSPHRASE_SYM': - self.need_passphrase_sym = True - if key == 'USERID_HINT': - self.userid_hint = value.strip().split() - - -class GPGWrapper(gnupg.GPG): - """ - This is a temporary class for handling GPG requests, and should be - replaced by a more general class used throughout the project. - """ - - GNUPG_HOME = os.environ['HOME'] + "/.config/leap/gnupg" - GNUPG_BINARY = "/usr/bin/gpg" # this has to be changed based on OS - - def __init__(self, gpgbinary=GNUPG_BINARY, gnupghome=GNUPG_HOME, - verbose=False, use_agent=False, keyring=None, options=None): - """ - Initialize a GnuPG process wrapper. - - @param gpgbinary: Name for GnuPG binary executable. - @type gpgbinary: C{str} - @param gpghome: Full pathname to directory containing the public and - private keyrings. - @type gpghome: C{str} - @param keyring: Name of alternative keyring file to use. If specified, - the default keyring is not used. - @param verbose: Should some verbose info be output? - @type verbose: bool - @param use_agent: Should pass `--use-agent` to GPG binary? - @type use_agent: bool - @param keyring: Path for the keyring to use. - @type keyring: str - @options: A list of additional options to pass to the GPG binary. - @type options: list - - @raise: RuntimeError with explanation message if there is a problem - invoking gpg. - """ - gnupg.GPG.__init__(self, gnupghome=gnupghome, gpgbinary=gpgbinary, - verbose=verbose, use_agent=use_agent, - keyring=keyring, options=options) - self.result_map['list-packets'] = ListPackets - - def find_key_by_email(self, email, secret=False): - """ - Find user's key based on their email. - - @param email: Email address of key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - for uid in key['uids']: - if re.search(email, uid): - return key - raise LookupError("GnuPG public key for email %s not found!" % email) - - def find_key_by_subkey(self, subkey, secret=False): - """ - Find user's key based on a subkey fingerprint. - - @param email: Subkey fingerprint of the key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - for sub in key['subkeys']: - if sub[0] == subkey: - return key - raise LookupError( - "GnuPG public key for subkey %s not found!" % subkey) - - def find_key_by_keyid(self, keyid, secret=False): - """ - Find user's key based on the key ID. - - @param email: The key ID of the key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - if keyid == key['keyid']: - return key - raise LookupError( - "GnuPG public key for keyid %s not found!" % keyid) - - def find_key_by_fingerprint(self, fingerprint, secret=False): - """ - Find user's key based on the key fingerprint. - - @param email: The fingerprint of the key being searched for. - @type email: str - @param secret: Should we search for a secret key? - @type secret: bool - - @return: The fingerprint of the found key. - @rtype: str - """ - for key in self.list_keys(secret=secret): - if fingerprint == key['fingerprint']: - return key - raise LookupError( - "GnuPG public key for fingerprint %s not found!" % fingerprint) - - def encrypt(self, data, recipient, sign=None, always_trust=True, - passphrase=None, symmetric=False): - """ - Encrypt data using GPG. - - @param data: The data to be encrypted. - @type data: str - @param recipient: The address of the public key to be used. - @type recipient: str - @param sign: Should the encrypted content be signed? - @type sign: bool - @param always_trust: Skip key validation and assume that used keys - are always fully trusted? - @type always_trust: bool - @param passphrase: The passphrase to be used if symmetric encryption - is desired. - @type passphrase: str - @param symmetric: Should we encrypt to a password? - @type symmetric: bool - - @return: An object with encrypted result in the `data` field. - @rtype: gnupg.Crypt - """ - # TODO: devise a way so we don't need to "always trust". - return gnupg.GPG.encrypt(self, data, recipient, sign=sign, - always_trust=always_trust, - passphrase=passphrase, - symmetric=symmetric, - cipher_algo='AES256') - - def decrypt(self, data, always_trust=True, passphrase=None): - """ - Decrypt data using GPG. - - @param data: The data to be decrypted. - @type data: str - @param always_trust: Skip key validation and assume that used keys - are always fully trusted? - @type always_trust: bool - @param passphrase: The passphrase to be used if symmetric encryption - is desired. - @type passphrase: str - - @return: An object with decrypted result in the `data` field. - @rtype: gnupg.Crypt - """ - # TODO: devise a way so we don't need to "always trust". - return gnupg.GPG.decrypt(self, data, always_trust=always_trust, - passphrase=passphrase) - - def send_keys(self, keyserver, *keyids): - """ - Send keys to a keyserver - - @param keyserver: The keyserver to send the keys to. - @type keyserver: str - @param keyids: The key ids to send. - @type keyids: list - - @return: A list of keys sent to server. - @rtype: gnupg.ListKeys - """ - # TODO: write tests for this. - # TODO: write a SendKeys class to handle status for this. - result = self.result_map['list'](self) - gnupg.logger.debug('send_keys: %r', keyids) - data = gnupg._make_binary_stream("", self.encoding) - args = ['--keyserver', keyserver, '--send-keys'] - args.extend(keyids) - self._handle_io(args, data, result, binary=True) - gnupg.logger.debug('send_keys result: %r', result.__dict__) - data.close() - return result - - def encrypt_file(self, file, recipients, sign=None, - always_trust=False, passphrase=None, - armor=True, output=None, symmetric=False, - cipher_algo=None): - """ - Encrypt the message read from the file-like object 'file'. - - @param file: The file to be encrypted. - @type data: file - @param recipient: The address of the public key to be used. - @type recipient: str - @param sign: Should the encrypted content be signed? - @type sign: bool - @param always_trust: Skip key validation and assume that used keys - are always fully trusted? - @type always_trust: bool - @param passphrase: The passphrase to be used if symmetric encryption - is desired. - @type passphrase: str - @param armor: Create ASCII armored output? - @type armor: bool - @param output: Path of file to write results in. - @type output: str - @param symmetric: Should we encrypt to a password? - @type symmetric: bool - @param cipher_algo: Algorithm to use. - @type cipher_algo: str - - @return: An object with encrypted result in the `data` field. - @rtype: gnupg.Crypt - """ - args = ['--encrypt'] - if symmetric: - args = ['--symmetric'] - if cipher_algo: - args.append('--cipher-algo %s' % cipher_algo) - else: - args = ['--encrypt'] - if not _is_sequence(recipients): - recipients = (recipients,) - for recipient in recipients: - args.append('--recipient "%s"' % recipient) - if armor: # create ascii-armored output - set to False for binary - args.append('--armor') - if output: # write the output to a file with the specified name - if os.path.exists(output): - os.remove(output) # to avoid overwrite confirmation message - args.append('--output "%s"' % output) - if sign: - args.append('--sign --default-key "%s"' % sign) - if always_trust: - args.append("--always-trust") - result = self.result_map['crypt'](self) - self._handle_io(args, file, result, passphrase=passphrase, binary=True) - logger.debug('encrypt result: %r', result.data) - return result - - def list_packets(self, data): - """ - List the sequence of packets. - - @param data: The data to extract packets from. - @type data: str - - @return: An object with packet info. - @rtype ListPackets - """ - args = ["--list-packets"] - result = self.result_map['list-packets'](self) - self._handle_io( - args, - _make_binary_stream(data, self.encoding), - result, - ) - return result - - def encrypted_to(self, data): - """ - Return the key to which data is encrypted to. - - @param data: The data to be examined. - @type data: str - - @return: The fingerprint of the key to which data is encrypted to. - @rtype: str - """ - # TODO: make this support multiple keys. - result = self.list_packets(data) - if not result.key: - raise LookupError( - "Content is not encrypted to a GnuPG key!") - try: - return self.find_key_by_keyid(result.key) - except: - return self.find_key_by_subkey(result.key) - - def is_encrypted_sym(self, data): - """ - Say whether some chunk of data is encrypted to a symmetric key. - - @param data: The data to be examined. - @type data: str - - @return: Whether data is encrypted to a symmetric key. - @rtype: bool - """ - result = self.list_packets(data) - return bool(result.need_passphrase_sym) - - def is_encrypted_asym(self, data): - """ - Say whether some chunk of data is encrypted to a private key. - - @param data: The data to be examined. - @type data: str - - @return: Whether data is encrypted to a private key. - @rtype: bool - """ - result = self.list_packets(data) - return bool(result.key) - - def is_encrypted(self, data): - """ - Say whether some chunk of data is encrypted to a key. - - @param data: The data to be examined. - @type data: str - - @return: Whether data is encrypted to a key. - @rtype: bool - """ - return self.is_encrypted_asym(data) or self.is_encrypted_sym(data) diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py deleted file mode 100644 index 2e6bfe9..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/keys.py +++ /dev/null @@ -1,230 +0,0 @@ -# -*- coding: utf-8 -*- -# keys.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Abstact key type and encryption scheme representations. -""" - - -try: - import simplejson as json -except ImportError: - import json # noqa -import re - - -from hashlib import sha256 -from abc import ABCMeta, abstractmethod -from leap.common.check import leap_assert - - -# -# Key handling utilities -# - -def is_address(address): - """ - Return whether the given C{address} is in the form user@provider. - - @param address: The address to be tested. - @type address: str - @return: Whether C{address} is in the form user@provider. - @rtype: bool - """ - return bool(re.match('[\w.-]+@[\w.-]+', address)) - - -def build_key_from_dict(kClass, address, kdict): - """ - Build an C{kClass} key bound to C{address} based on info in C{kdict}. - - @param address: The address bound to the key. - @type address: str - @param kdict: Dictionary with key data. - @type kdict: dict - @return: An instance of the key. - @rtype: C{kClass} - """ - leap_assert(address == kdict['address'], 'Wrong address in key data.') - return kClass( - address, - key_id=kdict['key_id'], - fingerprint=kdict['fingerprint'], - key_data=kdict['key_data'], - private=kdict['private'], - length=kdict['length'], - expiry_date=kdict['expiry_date'], - first_seen_at=kdict['first_seen_at'], - last_audited_at=kdict['last_audited_at'], - validation=kdict['validation'], # TODO: verify for validation. - ) - - -def keymanager_doc_id(ktype, address, private=False): - """ - Return the document id for the document containing a key for - C{address}. - - @param address: The type of the key. - @type address: KeyType - @param address: The address bound to the key. - @type address: str - @param private: Whether the key is private or not. - @type private: bool - @return: The document id for the document that stores a key bound to - C{address}. - @rtype: str - """ - leap_assert(is_address(address), "Wrong address format: %s" % address) - ktype = str(ktype) - visibility = 'private' if private else 'public' - return sha256('keymanager-'+address+'-'+ktype+'-'+visibility).hexdigest() - - -# -# Abstraction for encryption keys -# - -class EncryptionKey(object): - """ - Abstract class for encryption keys. - - A key is "validated" if the nicknym agent has bound the user address to a - public key. Nicknym supports three different levels of key validation: - - * Level 3 - path trusted: A path of cryptographic signatures can be traced - from a trusted key to the key under evaluation. By default, only the - provider key from the user's provider is a "trusted key". - * level 2 - provider signed: The key has been signed by a provider key for - the same domain, but the provider key is not validated using a trust - path (i.e. it is only registered) - * level 1 - registered: The key has been encountered and saved, it has no - signatures (that are meaningful to the nicknym agent). - """ - - __metaclass__ = ABCMeta - - def __init__(self, address, key_id=None, fingerprint=None, - key_data=None, private=None, length=None, expiry_date=None, - validation=None, first_seen_at=None, last_audited_at=None): - self.address = address - self.key_id = key_id - self.fingerprint = fingerprint - self.key_data = key_data - self.private = private - self.length = length - self.expiry_date = expiry_date - self.validation = validation - self.first_seen_at = first_seen_at - self.last_audited_at = last_audited_at - - def get_json(self): - """ - Return a JSON string describing this key. - - @return: The JSON string describing this key. - @rtype: str - """ - return json.dumps({ - 'address': self.address, - 'type': str(self.__class__), - 'key_id': self.key_id, - 'fingerprint': self.fingerprint, - 'key_data': self.key_data, - 'private': self.private, - 'length': self.length, - 'expiry_date': self.expiry_date, - 'validation': self.validation, - 'first_seen_at': self.first_seen_at, - 'last_audited_at': self.last_audited_at, - 'tags': ['keymanager-key'], - }) - - -# -# Encryption schemes -# - -class EncryptionScheme(object): - """ - Abstract class for Encryption Schemes. - - A wrapper for a certain encryption schemes should know how to get and put - keys in local storage using Soledad, how to generate new keys and how to - find out about possibly encrypted content. - """ - - __metaclass__ = ABCMeta - - def __init__(self, soledad): - """ - Initialize this Encryption Scheme. - - @param soledad: A Soledad instance for local storage of keys. - @type soledad: leap.soledad.Soledad - """ - self._soledad = soledad - - @abstractmethod - def get_key(self, address, private=False): - """ - Get key from local storage. - - @param address: The address bound to the key. - @type address: str - @param private: Look for a private key instead of a public one? - @type private: bool - - @return: The key bound to C{address}. - @rtype: EncryptionKey - @raise KeyNotFound: If the key was not found on local storage. - """ - pass - - @abstractmethod - def put_key(self, key): - """ - Put a key in local storage. - - @param key: The key to be stored. - @type key: EncryptionKey - """ - pass - - @abstractmethod - def gen_key(self, address): - """ - Generate a new key. - - @param address: The address bound to the key. - @type address: str - - @return: The key bound to C{address}. - @rtype: EncryptionKey - """ - pass - - @abstractmethod - def delete_key(self, key): - """ - Remove C{key} from storage. - - @param key: The key to be removed. - @type key: EncryptionKey - """ - pass diff --git a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py b/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py deleted file mode 100644 index e2ffe76..0000000 --- a/debian/python-leap.common/usr/share/pyshared/leap/common/keymanager/openpgp.py +++ /dev/null @@ -1,459 +0,0 @@ -# -*- coding: utf-8 -*- -# openpgp.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# 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 -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - - -""" -Infrastructure for using OpenPGP keys in Key Manager. -""" - - -import re -import tempfile -import shutil - -from leap.common.check import leap_assert -from leap.common.keymanager.errors import ( - KeyNotFound, - KeyAlreadyExists, - KeyAttributesDiffer -) -from leap.common.keymanager.keys import ( - EncryptionKey, - EncryptionScheme, - is_address, - keymanager_doc_id, - build_key_from_dict, -) -from leap.common.keymanager.gpg import GPGWrapper - - -# -# Utility functions -# - -def encrypt_sym(data, passphrase): - """ - Encrypt C{data} with C{passphrase}. - - @param data: The data to be encrypted. - @type data: str - @param passphrase: The passphrase used to encrypt C{data}. - @type passphrase: str - - @return: The encrypted data. - @rtype: str - """ - - def _encrypt_cb(gpg): - return gpg.encrypt( - data, None, passphrase=passphrase, symmetric=True).data - - return _safe_call(_encrypt_cb) - - -def decrypt_sym(data, passphrase): - """ - Decrypt C{data} with C{passphrase}. - - @param data: The data to be decrypted. - @type data: str - @param passphrase: The passphrase used to decrypt C{data}. - @type passphrase: str - - @return: The decrypted data. - @rtype: str - """ - - def _decrypt_cb(gpg): - return gpg.decrypt(data, passphrase=passphrase).data - - return _safe_call(_decrypt_cb) - - -def encrypt_asym(data, key): - """ - Encrypt C{data} using public @{key}. - - @param data: The data to be encrypted. - @type data: str - @param key: The key used to encrypt. - @type key: OpenPGPKey - - @return: The encrypted data. - @rtype: str - """ - leap_assert(key.private is False, 'Key is not public.') - - def _encrypt_cb(gpg): - return gpg.encrypt( - data, key.fingerprint, symmetric=False).data - - return _safe_call(_encrypt_cb, key.key_data) - - -def decrypt_asym(data, key): - """ - Decrypt C{data} using private @{key}. - - @param data: The data to be decrypted. - @type data: str - @param key: The key used to decrypt. - @type key: OpenPGPKey - - @return: The decrypted data. - @rtype: str - """ - leap_assert(key.private is True, 'Key is not private.') - - def _decrypt_cb(gpg): - return gpg.decrypt(data).data - - return _safe_call(_decrypt_cb, key.key_data) - - -def is_encrypted(data): - """ - Return whether C{data} was encrypted using OpenPGP. - - @param data: The data we want to know about. - @type data: str - - @return: Whether C{data} was encrypted using this wrapper. - @rtype: bool - """ - - def _is_encrypted_cb(gpg): - return gpg.is_encrypted(data) - - return _safe_call(_is_encrypted_cb) - - -def is_encrypted_sym(data): - """ - Return whether C{data} was encrypted using a public OpenPGP key. - - @param data: The data we want to know about. - @type data: str - - @return: Whether C{data} was encrypted using this wrapper. - @rtype: bool - """ - - def _is_encrypted_cb(gpg): - return gpg.is_encrypted_sym(data) - - return _safe_call(_is_encrypted_cb) - - -def is_encrypted_asym(data): - """ - Return whether C{data} was asymmetrically encrypted using OpenPGP. - - @param data: The data we want to know about. - @type data: str - - @return: Whether C{data} was encrypted using this wrapper. - @rtype: bool - """ - - def _is_encrypted_cb(gpg): - return gpg.is_encrypted_asym(data) - - return _safe_call(_is_encrypted_cb) - - -def _build_key_from_gpg(address, key, key_data): - """ - Build an OpenPGPKey for C{address} based on C{key} from - local gpg storage. - - ASCII armored GPG key data has to be queried independently in this - wrapper, so we receive it in C{key_data}. - - @param address: The address bound to the key. - @type address: str - @param key: Key obtained from GPG storage. - @type key: dict - @param key_data: Key data obtained from GPG storage. - @type key_data: str - @return: An instance of the key. - @rtype: OpenPGPKey - """ - return OpenPGPKey( - address, - key_id=key['keyid'], - fingerprint=key['fingerprint'], - key_data=key_data, - private=True if key['type'] == 'sec' else False, - length=key['length'], - expiry_date=key['expires'], - validation=None, # TODO: verify for validation. - ) - - -def _build_unitary_gpgwrapper(key_data=None): - """ - Return a temporary GPG wrapper keyring containing exactly zero or one - keys. - - Temporary unitary keyrings allow the to use GPG's facilities for exactly - one key. This function creates an empty temporary keyring and imports - C{key_data} if it is not None. - - @param key_data: ASCII armored key data. - @type key_data: str - @return: A GPG wrapper with a unitary keyring. - @rtype: gnupg.GPG - """ - tmpdir = tempfile.mkdtemp() - gpg = GPGWrapper(gnupghome=tmpdir) - leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty.') - if key_data: - gpg.import_keys(key_data) - leap_assert( - len(gpg.list_keys()) is 1, - 'Unitary keyring has wrong number of keys: %d.' - % len(gpg.list_keys())) - return gpg - - -def _destroy_unitary_gpgwrapper(gpg): - """ - Securely erase a unitary keyring. - - @param gpg: A GPG wrapper instance. - @type gpg: gnupg.GPG - """ - for secret in [True, False]: - for key in gpg.list_keys(secret=secret): - gpg.delete_keys( - key['fingerprint'], - secret=secret) - leap_assert(len(gpg.list_keys()) is 0, 'Keyring not empty!') - # TODO: implement some kind of wiping of data or a more secure way that - # does not write to disk. - shutil.rmtree(gpg.gnupghome) - - -def _safe_call(callback, key_data=None, **kwargs): - """ - Run C{callback} in an unitary keyring containing C{key_data}. - - @param callback: Function whose first argument is the gpg keyring. - @type callback: function(gnupg.GPG) - @param key_data: ASCII armored key data. - @type key_data: str - @param **kwargs: Other eventual parameters for the callback. - @type **kwargs: **dict - - @return: The results of the callback. - @rtype: str or bool - """ - gpg = _build_unitary_gpgwrapper(key_data) - val = callback(gpg, **kwargs) - _destroy_unitary_gpgwrapper(gpg) - return val - - -# -# The OpenPGP wrapper -# - -class OpenPGPKey(EncryptionKey): - """ - Base class for OpenPGP keys. - """ - - -class OpenPGPScheme(EncryptionScheme): - """ - A wrapper for OpenPGP keys. - """ - - def __init__(self, soledad): - """ - Initialize the OpenPGP wrapper. - - @param soledad: A Soledad instance for key storage. - @type soledad: leap.soledad.Soledad - """ - EncryptionScheme.__init__(self, soledad) - - def gen_key(self, address): - """ - Generate an OpenPGP keypair bound to C{address}. - - @param address: The address bound to the key. - @type address: str - @return: The key bound to C{address}. - @rtype: OpenPGPKey - @raise KeyAlreadyExists: If key already exists in local database. - """ - # make sure the key does not already exist - leap_assert(is_address(address), 'Not an user address: %s' % address) - try: - self.get_key(address) - raise KeyAlreadyExists(address) - except KeyNotFound: - pass - - def _gen_key_cb(gpg): - params = gpg.gen_key_input( - key_type='RSA', - key_length=4096, - name_real=address, - name_email=address, - name_comment='Generated by LEAP Key Manager.') - gpg.gen_key(params) - pubkeys = gpg.list_keys() - # assert for new key characteristics - leap_assert( - len(pubkeys) is 1, # a unitary keyring! - 'Keyring has wrong number of keys: %d.' % len(pubkeys)) - key = gpg.list_keys(secret=True).pop() - leap_assert( - len(key['uids']) is 1, # with just one uid! - 'Wrong number of uids for key: %d.' % len(key['uids'])) - leap_assert( - re.match('.*<%s>$' % address, key['uids'][0]) is not None, - 'Key not correctly bound to address.') - # insert both public and private keys in storage - for secret in [True, False]: - key = gpg.list_keys(secret=secret).pop() - openpgp_key = _build_key_from_gpg( - address, key, - gpg.export_keys(key['fingerprint'], secret=secret)) - self.put_key(openpgp_key) - - _safe_call(_gen_key_cb) - return self.get_key(address, private=True) - - def get_key(self, address, private=False): - """ - Get key bound to C{address} from local storage. - - @param address: The address bound to the key. - @type address: str - @param private: Look for a private key instead of a public one? - @type private: bool - - @return: The key bound to C{address}. - @rtype: OpenPGPKey - @raise KeyNotFound: If the key was not found on local storage. - """ - leap_assert(is_address(address), 'Not an user address: %s' % address) - doc = self._get_key_doc(address, private) - if doc is None: - raise KeyNotFound(address) - return build_key_from_dict(OpenPGPKey, address, doc.content) - - def put_key_raw(self, data): - """ - Put key contained in raw C{data} in local storage. - - @param data: The key data to be stored. - @type data: str - """ - # TODO: add more checks for correct key data. - leap_assert(data is not None, 'Data does not represent a key.') - - def _put_key_raw_cb(gpg): - - privkey = None - pubkey = None - try: - privkey = gpg.list_keys(secret=True).pop() - except IndexError: - pass - pubkey = gpg.list_keys(secret=False).pop() # unitary keyring - # extract adress from first uid on key - match = re.match('.*<([\w.-]+@[\w.-]+)>.*', pubkey['uids'].pop()) - leap_assert(match is not None, 'No user address in key data.') - address = match.group(1) - if privkey is not None: - match = re.match( - '.*<([\w.-]+@[\w.-]+)>.*', privkey['uids'].pop()) - leap_assert(match is not None, 'No user address in key data.') - privaddress = match.group(1) - leap_assert( - address == privaddress, - 'Addresses in pub and priv key differ.') - leap_assert( - pubkey['fingerprint'] == privkey['fingerprint'], - 'Fingerprints for pub and priv key differ.') - # insert private key in storage - openpgp_privkey = _build_key_from_gpg( - address, privkey, - gpg.export_keys(privkey['fingerprint'], secret=True)) - self.put_key(openpgp_privkey) - # insert public key in storage - openpgp_pubkey = _build_key_from_gpg( - address, pubkey, - gpg.export_keys(pubkey['fingerprint'], secret=False)) - self.put_key(openpgp_pubkey) - - _safe_call(_put_key_raw_cb, data) - - def put_key(self, key): - """ - Put C{key} in local storage. - - @param key: The key to be stored. - @type key: OpenPGPKey - """ - doc = self._get_key_doc(key.address, private=key.private) - if doc is None: - self._soledad.create_doc_from_json( - key.get_json(), - doc_id=keymanager_doc_id( - OpenPGPKey, key.address, key.private)) - else: - doc.set_json(key.get_json()) - self._soledad.put_doc(doc) - - def _get_key_doc(self, address, private=False): - """ - Get the document with a key (public, by default) bound to C{address}. - - If C{private} is True, looks for a private key instead of a public. - - @param address: The address bound to the key. - @type address: str - @param private: Whether to look for a private key. - @type private: bool - @return: The document with the key or None if it does not exist. - @rtype: leap.soledad.backends.leap_backend.LeapDocument - """ - return self._soledad.get_doc( - keymanager_doc_id(OpenPGPKey, address, private)) - - def delete_key(self, key): - """ - Remove C{key} from storage. - - @param key: The key to be removed. - @type key: EncryptionKey - """ - leap_assert(key.__class__ is OpenPGPKey, 'Wrong key type.') - stored_key = self.get_key(key.address, private=key.private) - if stored_key is None: - raise KeyNotFound(key) - if stored_key.__dict__ != key.__dict__: - raise KeyAttributesDiffer(key) - doc = self._soledad.get_doc( - keymanager_doc_id(OpenPGPKey, key.address, key.private)) - self._soledad.delete_doc(doc) -- cgit v1.2.3