def merge(self, newkey): if newkey.fingerprint != self.fingerprint: logger.critical( "Can't put a key whith the same key_id and different " "fingerprint: %s, %s" % (newkey.fingerprint, self.fingerprint)) raise errors.KeyFingerprintMismatch(newkey.fingerprint) with TempGPGWrapper(gpgbinary=self._gpgbinary) as gpg: gpg.import_keys(self.key_data) gpg.import_keys(newkey.key_data) gpgkey = gpg.list_keys(secret=newkey.private).pop() if gpgkey['expires']: self.expiry_date = datetime.fromtimestamp( int(gpgkey['expires'])) else: self.expiry_date = None self.uids = [] for uid in gpgkey['uids']: self.uids.append(parse_address(uid)) self.length = int(gpgkey['length']) self.key_data = gpg.export_keys(gpgkey['fingerprint'], secret=self.private) if newkey.validation > self.validation: self.validation = newkey.validation if newkey.last_audited_at > self.last_audited_at: self.validation = newkey.last_audited_at self.encr_used = newkey.encr_used or self.encr_used self.sign_used = newkey.sign_used or self.sign_used self.refreshed_at = datetime.now()
def process_key(key_data, gpgbinary, secret=False): with TempGPGWrapper(gpgbinary=gpgbinary) as gpg: try: gpg.import_keys(key_data) info = gpg.list_keys(secret=secret).pop() key = gpg.export_keys(info['fingerprint'], secret=secret) except IndexError: info = {} key = None return info, key
def sign(self, data, privkey, digest_algo='SHA512', clearsign=False, detach=True, binary=False): """ Sign C{data} with C{privkey}. :param data: The data to be signed. :type data: str :param privkey: The private key to be used to sign. :type privkey: OpenPGPKey :param digest_algo: The hash digest to use. :type digest_algo: str :param clearsign: If True, create a cleartext signature. :type clearsign: bool :param detach: If True, create a detached signature. :type detach: bool :param binary: If True, do not ascii armour the output. :type binary: bool :return: The ascii-armored signed data. :rtype: str """ leap_assert_type(privkey, OpenPGPKey) leap_assert(privkey.private is True) # result.fingerprint - contains the fingerprint of the key used to # sign. with TempGPGWrapper(privkey, self._gpgbinary) as gpg: kw = dict(default_key=privkey.fingerprint, digest_algo=digest_algo, clearsign=clearsign, detach=detach, binary=binary) if not GNUPG_NG: kw.pop('digest_algo') kw.pop('default_key') result = gpg.sign(data, **kw) rfprint = privkey.fingerprint privkey = gpg.list_keys(secret=True).pop() kfprint = privkey['fingerprint'] if result.fingerprint is None: raise errors.SignFailed( 'Failed to sign with key %s: %s' % (privkey['fingerprint'], result.stderr)) leap_assert( result.fingerprint == kfprint, 'Signature and private key fingerprints mismatch: ' '%s != %s' % (rfprint, kfprint)) return result.data
def encrypt(self, data, pubkey, passphrase=None, sign=None, cipher_algo='AES256'): """ Encrypt C{data} using public @{pubkey} and sign with C{sign} key. :param data: The data to be encrypted. :type data: str :param pubkey: The key used to encrypt. :type pubkey: OpenPGPKey :param sign: The key used for signing. :type sign: OpenPGPKey :param cipher_algo: The cipher algorithm to use. :type cipher_algo: str :return: A Deferred that will be fired with the encrypted data. :rtype: defer.Deferred :raise EncryptError: Raised if failed encrypting for some reason. """ leap_assert_type(pubkey, OpenPGPKey) leap_assert(pubkey.private is False, 'Key is not public.') keys = [pubkey] if sign is not None: leap_assert_type(sign, OpenPGPKey) leap_assert(sign.private is True) keys.append(sign) with TempGPGWrapper(keys, self._gpgbinary) as gpg: kw = dict(default_key=sign.fingerprint if sign else None, passphrase=passphrase, symmetric=False, cipher_algo=cipher_algo) if not GNUPG_NG: kw.pop('cipher_algo') kw.pop('default_key') kw.update(passphrase='') kw.update(always_trust=True) result = yield from_thread(gpg.encrypt, data, pubkey.fingerprint, **kw) # Here we cannot assert for correctness of sig because the sig is # in the ciphertext. # result.ok - (bool) indicates if the operation succeeded # result.data - (bool) contains the result of the operation try: self._assert_gpg_result_ok(result) defer.returnValue(result.data) except errors.GPGError as e: self.log.warn('Failed to encrypt: %s.' % str(e)) raise errors.EncryptError()
def is_encrypted(self, 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 """ with TempGPGWrapper(gpgbinary=self._gpgbinary) as gpg: gpgutil = GPGUtilities(gpg) return gpgutil.is_encrypted_asym(data)
def signatures(self): """ Get the key signatures :return: the key IDs that have signed the key :rtype: list(str) """ with TempGPGWrapper(keys=[self], gpgbinary=self._gpgbinary) as gpg: res = gpg.list_sigs(self.fingerprint) for uid, sigs in res.sigs.iteritems(): if parse_address(uid) in self.uids: return sigs return []
def decrypt(self, data, privkey, passphrase=None, verify=None): """ Decrypt C{data} using private @{privkey} and verify with C{verify} key. :param data: The data to be decrypted. :type data: str :param privkey: The key used to decrypt. :type privkey: OpenPGPKey :param passphrase: The passphrase for the secret key used for decryption. :type passphrase: str :param verify: The key used to verify a signature. :type verify: OpenPGPKey :return: Deferred that will fire with the decrypted data and if signature verifies (unicode, bool) :rtype: Deferred :raise DecryptError: Raised if failed decrypting for some reason. """ leap_assert(privkey.private is True, 'Key is not private.') keys = [privkey] if verify is not None: leap_assert_type(verify, OpenPGPKey) leap_assert(verify.private is False) keys.append(verify) with TempGPGWrapper(keys, self._gpgbinary) as gpg: try: result = yield from_thread(gpg.decrypt, data, passphrase=passphrase, always_trust=True) self._assert_gpg_result_ok(result) # verify signature sign_valid = False if (verify is not None and result.valid is True and verify.fingerprint == result.pubkey_fingerprint): sign_valid = True defer.returnValue((result.data, sign_valid)) except errors.GPGError as e: self.log.warn('Failed to decrypt: %s.' % str(e)) raise errors.DecryptError(str(e))
def _gen_key(_): with TempGPGWrapper(gpgbinary=self._gpgbinary) as gpg: # TODO: inspect result, or use decorator params = gpg.gen_key_input( key_type='RSA', key_length=4096, name_real=address, name_email=address, name_comment='') logger.info("About to generate keys... " "This might take SOME time.") yield from_thread(gpg.gen_key, params) logger.info("Keys for %s have been successfully " "generated." % (address,)) 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'])) uid_match = False for uid in key['uids']: if re.match('.*<%s>$' % address, uid) is not None: uid_match = True break leap_assert(uid_match, 'Key not correctly bound to address.') # insert both public and private keys in storage deferreds = [] for secret in [True, False]: key = gpg.list_keys(secret=secret).pop() openpgp_key = self._build_key_from_gpg( key, gpg.export_keys(key['fingerprint'], secret=secret), address) d = self.put_key(openpgp_key) deferreds.append(d) yield defer.gatherResults(deferreds)
def is_signed_by(self, other_key): """ Checks if current key was signed by another key. Rather than just relying on the fingerprint being there, we use gpg's --check-sigs with both keys being present in the keychain to check the signature validity. By doing so, relying on the long key id instead of the fingerprint is fine. :param other_key: the other key. :return: True if valid signature could be found. :rtype: bool """ keys = [self, other_key] with TempGPGWrapper(keys=keys, gpgbinary=self._gpgbinary) as gpg: certs = gpg.check_sigs(str(self.fingerprint)).certs for uid, cur_certs in certs.iteritems(): if (parse_address(uid) in other_key.uids and other_key.fingerprint[-16:] in cur_certs): return True return False
def expire(self, seckey, expiration_time='1y', passphrase=None): """ Change expiration for C{key} key pair for the given C{expiration_time} period, from the current day. :param seckey: The secret key of the key pair to have the expiration time changed. :type seckey: OpenPGPKey :param expiration_time: new expiration time from the current day in 'n', 'nw','nm' or 'ny' where n is a number :type expiration_time: str :return: The updated secret key, with new expiry date :rtype: OpenPGPKey :raise KeyExpirationError: Raised if failed to change expiration of key for some reason. """ leap_assert_type(seckey, OpenPGPKey) leap_assert(seckey.private is True, 'Key is not private.') keys = [seckey] try: with TempGPGWrapper(keys, self._gpgbinary) as gpg: result = yield from_thread(gpg.expire, seckey.fingerprint, expiration_time=expiration_time, passphrase=passphrase) if result.status == 'ok': for secret in [False, True]: fetched_key = gpg.list_keys(secret=secret).pop() key_data = gpg.export_keys(seckey.fingerprint, secret=secret) renewed_key = self._build_key_from_gpg( fetched_key, key_data, seckey.address) yield self.put_key(renewed_key) defer.returnValue(renewed_key) except Exception as e: log.warn('Failed to change expiration of key: %s' % str(e)) raise errors.KeyExpirationError(str(e))
def verify(self, data, pubkey, detached_sig=None): """ Verify signed C{data} with C{pubkey}, eventually using C{detached_sig}. :param data: The data to be verified. :type data: str :param pubkey: The public key to be used on verification. :type pubkey: OpenPGPKey :param detached_sig: A detached signature. If given, C{data} is verified against this detached signature. :type detached_sig: str :return: signature matches :rtype: bool """ leap_assert_type(pubkey, OpenPGPKey) leap_assert(pubkey.private is False) with TempGPGWrapper(pubkey, self._gpgbinary) as gpg: result = None if detached_sig is None: result = gpg.verify(data) else: # to verify using a detached sig we have to use # gpg.verify_file(), which receives the data as a binary # stream and the name of a file containing the signature. sf, sfname = tempfile.mkstemp() with os.fdopen(sf, 'w') as sfd: sfd.write(detached_sig) result = gpg.verify_file(io.BytesIO(data), sig_file=sfname) os.unlink(sfname) gpgpubkey = gpg.list_keys().pop() valid = result.valid rfprint = result.fingerprint kfprint = gpgpubkey['fingerprint'] return valid and rfprint == kfprint
def wrapper_init(tmpdir, benchmark, openpgp_keys, monkeypatch, _): pubkey = openpgp_keys[0] privkey = openpgp_keys[2] wrapper = TempGPGWrapper(keys=[pubkey, privkey]) wrapper._build_keyring() return wrapper._gpg, pubkey, privkey
def wrapper_init_exec(fun, tmpdir, benchmark, openpgp_keys, monkeypatch, _): pubkey = openpgp_keys[0] privkey = openpgp_keys[2] wrapper = TempGPGWrapper(keys=[pubkey, privkey]) wrapper._build_keyring() fun((wrapper._gpg, pubkey, privkey))
def wrapper_init_only(tmpdir, benchmark, openpgp_keys, monkeypatch, num_keys): keys = openpgp_keys[0:num_keys] wrapper = TempGPGWrapper(keys=keys) with wrapper as gpg: assert GPG == type(gpg)
def wrapper(keys=None): return TempGPGWrapper(keys=keys)