def decrypt_file(self, input_file, output_file, passphrase=None, verify=True, armor=None): """ Decrypt the ciphertext stored in `input_file` and store the decrypted data in `output_file`. The files must be already opened with the correct read/write and binary modes. Provide a `passphrase` for symmetric decryption. """ with gpg.Data() as ciphertext: ciphertext.new_from_fd(input_file) with gpg.Data() as plaintext: plaintext.new_from_fd(output_file) if armor is not None: old_armor = self._gpg.armor self._gpg.armor = armor self._decrypt(ciphertext, plaintext, passphrase=passphrase, verify=verify) if armor is not None: self._gpg.armor = old_armor
def gpg_decrypt(self, message, privkey): try: # decrypt with GnuPG with gpg.Context( armor=True, offline=True, pinentry_mode=gpg.constants.PINENTRY_MODE_LOOPBACK) as c: c.set_engine_info(gpg.constants.PROTOCOL_OpenPGP, home_dir=gnupghome) # do we need to import the key? try: c.get_key(privkey.fingerprint, True) except gpg.errors.KeyNotFound: key_data = gpg.Data(string=str(privkey)) gpg.core.gpgme.gpgme_op_import(c.wrapped, key_data) pt, _, _ = c.decrypt(gpg.Data(string=str(message)), verify=False) return pt except gpg.errors.GPGMEError: # if we got here, it's because gpg is screwing with us. The tests seem to pass everywhere except in the buildd. # Until I can find a better fix, here's another bypass return privkey.decrypt(message).message.encode('utf-8')
def UIDExport(keydata, uid_i): """Export only the UID of a key. Unfortunately, GnuPG does not provide smth like --export-uid-only in order to obtain a UID and its signatures.""" log.debug("Deletion of UID %r from %r", uid_i, keydata) if not uid_i >= 1: log.debug("Raising because uid: %r", uid_i) raise ValueError("Expected UID to be >= 1, but is %r", uid_i) ctx = TempContext() ctx.op_import(keydata) result = ctx.op_import_result() if result.considered != 1 or result.imported != 1: raise ValueError("Expected exactly one key in keydata. %r" % result) else: assert len(result.imports) == 1 fpr = result.imports[0].fpr key = ctx.get_key(fpr) uids_to_remove = {i for i in range(1, len(key.uids)+1)} uids_to_remove.remove(uid_i) if uids_to_remove: sink = gpg.Data() ctx.interact(key, GenEdit(del_uids(uids_to_remove)).edit_cb, fnc_value=sink, sink=sink) sink.seek(0, 0) log.debug("Data after UIDExport: %s", sink.read()) uid_data = gpg.Data() ctx.op_export_keys([key], 0, uid_data) uid_data.seek(0, 0) uid_bytes = uid_data.read() log.debug("UID %r: %r", uid_i, uid_bytes) return uid_bytes
def sign_keydata_and_encrypt(keydata, error_cb=None, homedir=None): oldctx = DirectoryContext(homedir) ctx = TempContextWithAgent(oldctx) # We're trying to sign with all available secret keys available_secret_keys = [ key for key in ctx.keylist(secret=True) if not key.disabled or key.revoked or key.invalid or key.expired ] log.debug('Setting available sec keys to: %r', available_secret_keys) ctx.signers = available_secret_keys ctx.op_import(minimise_key(keydata)) result = ctx.op_import_result() if result.considered != 1 and result.imported != 1: raise ValueError("Expected to load exactly one key. %r", result) else: imports = result.imports assert len(imports) == 1 fpr = result.imports[0].fpr key = ctx.get_key(fpr) sink = gpg.Data() # There is op_keysign, but it's only available with gpg 2.1.12 ctx.interact(key, GenEdit(sign_key(error_cb=error_cb)).edit_cb, sink=sink) sink.seek(0, 0) log.debug("Sink after signing: %r", sink.read()) signed_sink = gpg.Data() ctx.set_keylist_mode(gpg.constants.KEYLIST_MODE_SIGS) ctx.armor = True ctx.op_export_keys([key], 0, signed_sink) signed_sink.seek(0, 0) signed_keydata = signed_sink.read() log.debug("Signed Key: %s", signed_keydata) # Do I have to re-get the key to make the signatures known? key = ctx.get_key(fpr) for i, uid in enumerate(key.uids, start=1): if uid.revoked or uid.invalid: continue else: uid_data = UIDExport(signed_keydata, i) # FIXME: Check whether this bug is resolved and the remove this conditional # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=884900 if not crashing_gpgme: log.debug("Data for uid %d: %r, sigs: %r %r", i, uid, uid.signatures, uid_data) ciphertext, _, _ = ctx.encrypt( plaintext=uid_data, recipients=[key], # We probably have to set owner trust # in order for it to work out of the box always_trust=True, sign=False) yield (UID.from_gpgme(uid), ciphertext)
def verify(self): self.progressbar.set_fraction(0) self.progressbar.set_text(_('Verifying Signature')) self.progressbar.show() def gui_raise_sigerror(self, sigerror='MissingErr'): """ :type sigerror: str """ sigerror = 'SIGNATURE VERIFICATION FAILED!\n\nError Code: {0}\n\nYou might be under attack, there might' \ ' be a network\nproblem, or you may be missing a recently added\nTor Browser verification key.' \ '\nClick Start to refresh the keyring and try again. If the message persists report the above' \ ' error code here:\nhttps://github.com/micahflee/torbrowser-launcher/issues'.format(sigerror) self.set_gui('task', sigerror, ['start_over'], False) self.clear_ui() self.build_ui() if gpgme_support: with gpg.Context() as c: c.set_engine_info(gpg.constants.protocol.OpenPGP, home_dir=self.common.paths['gnupg_homedir']) sig = gpg.Data(file=self.common.paths['sig_file']) signed = gpg.Data(file=self.common.paths['tarball_file']) try: c.verify(signature=sig, signed_data=signed) except gpg.errors.BadSignatures as e: result = str(e).split(": ") if result[1] == 'Bad signature': gui_raise_sigerror(self, str(e)) elif result[1] == 'No public key': self.common.refresh_keyring(result[0]) gui_raise_sigerror(self, str(e)) else: self.run_task() else: FNULL = open(os.devnull, 'w') p = subprocess.Popen([ '/usr/bin/gpg', '--homedir', self.common.paths['gnupg_homedir'], '--verify', self.common.paths['sig_file'], self.common.paths['tarball_file'] ], stdout=FNULL, stderr=subprocess.STDOUT) self.pulse_until_process_exits(p) if p.returncode == 0: self.run_task() else: self.common.refresh_keyring() gui_raise_sigerror(self, 'GENERIC_VERIFY_FAIL') if not reactor.running: reactor.run()
def can_unseal(args, init_keys): # If keys provided and not gpg if len(args.get("gpgs", [])) == 0 and len(args.get("keys",[])) > 0: return True # If keys not provided but init and no gpg if len(args.get("gpgs", [])) == 0 and len(init_keys) > 0: return True # If gpg keys provided and init return_value = len(init_keys) > 0 for k in args.get("gpgs", []): if isinstance(k, str): k = k.encode() with gpg.Context(armor=False) as c: c.op_import(gpg.Data(k)) import_result = c.op_import_result() if not import_result: return False key_id = c.get_key(import_result.imports[0].fpr) try: ciphertext, _, _ = c.encrypt(b"_", always_trust=True, sign=False, recipients=[key_id]) plaintext, _, _ = c.decrypt(ciphertext) return_value &= plaintext == b"_" except gpg.errors.GPGMEError: return False return return_value
def _encrypt_payload(self, payload, key_ids): """Encrypts the payload with the given keys""" global legacy_gpg payload = encode_string(payload) self.gpg.armor = True recipient = [self.gpg.get_key(key_id) for key_id in key_ids] for key in recipient: if key.expired: if legacy_gpg: raise gpgme.GpgmeError("Key with user email %s " "is expired!".format( key.uids[0].email)) else: raise gpg.errors.GPGMEError("Key with user email %s " "is expired!".format( key.uids[0].email)) if legacy_gpg: plaintext = BytesIO(payload) ciphertext = BytesIO() self.gpg.encrypt(recipient, gpgme.ENCRYPT_ALWAYS_TRUST, plaintext, ciphertext) return ciphertext.getvalue() else: (ciphertext, encresult, signresult) = self.gpg.encrypt(gpg.Data(string=payload), recipients=recipient, sign=False, always_trust=True) return ciphertext
def gpg_verify_key(self, key): with gpg.Context(offline=True) as c: c.set_engine_info(gpg.constants.PROTOCOL_OpenPGP, home_dir=gnupghome) data, vres = c.verify(gpg.Data(string=str(key))) assert vres
def sign(self, content, mode): try: import gpg except ImportError as error: raise GpgNotInstalled( 'Set create_signatures=no to disable creating signatures.') if isinstance(content, str): raise errors.BzrBadParameterUnicode('content') plain_text = gpg.Data(content) try: output, result = self.context.sign( plain_text, mode={ MODE_DETACH: gpg.constants.sig.mode.DETACH, MODE_CLEAR: gpg.constants.sig.mode.CLEAR, MODE_NORMAL: gpg.constants.sig.mode.NORMAL, }[mode]) except gpg.errors.GPGMEError as error: raise SigningFailed(str(error)) except gpg.errors.InvalidSigners as error: raise SigningFailed(str(error)) return output
def verify(second_try=False): with gpg.Context() as c: c.set_engine_info(gpg.constants.protocol.OpenPGP, home_dir=self.common.paths['gnupg_homedir']) sig = gpg.Data(file=self.common.paths['sig_file']) signed = gpg.Data(file=self.common.paths['tarball_file']) try: c.verify(signature=sig, signed_data=signed) except gpg.errors.BadSignatures as e: if second_try: self.error.emit(str(e)) else: raise Exception else: self.success.emit()
def gpg_message(msg): ret = None with gpg.Context(offline=True) as c: c.set_engine_info(gpg.constants.PROTOCOL_OpenPGP, home_dir=gnupghome) msg, _ = c.verify(gpg.Data(string=str(msg))) ret = bytes(msg) return ret
def single_temp_key(random_userid): with gpg.Context(armor=True) as gpg_context: with gpg.Data() as expkey: dmkey = gpg_context.create_key(algorithm="rsa1024", userid=next(random_userid), encrypt=True) gpg_context.op_export(dmkey.fpr, 0, expkey) expkey.seek(0, os.SEEK_SET) yield expkey.read()
def wrapped(*args, **kwargs): with tempfile.TemporaryDirectory() as tmpdir: shutil.copytree(context.home_dir, f"{tmpdir}/gnupg", ignore=dirs_files_only) kwargs["context"] = gpg.Context(armor=context.armor, home_dir=f"{tmpdir}/gnupg") kwargs["buffer"] = gpg.Data() return f(*args, **kwargs)
def generate_gpg_hvac_vault(gpg_value): with gpg.Context(armor=False) as c, gpg.Data() as expkey: c.op_import(gpg_value) import_result = c.op_import_result() if import_result: c.op_export(import_result.imports[0].fpr, 0, expkey) expkey.seek(0, os.SEEK_SET) return base64.b64encode(expkey.read()).decode() else: return base64.b64encode(b'').decode()
def export_key(self, pattern): """ Export the public key part of the matching the pattern provided in the string `pattern`. The key is returned as a string. """ with gpg.Data() as export_key: self._gpg.op_export(pattern, 0, export_key) return self._read_data(export_key)
def test_sign_and_encrypt(self): # This might be a secret key, too, so we import and export to # get hold of the public portion. keydata = open(self.key_sender_key, "rb").read() # We get the public portion of the key sender = TempContext() sender.op_import(keydata) result = sender.op_import_result() fpr = result.imports[0].fpr sink = gpg.Data() sender.op_export(fpr, 0, sink) sink.seek(0, 0) # This is the key that we will sign public_sender_key = sink.read() keys = get_usable_keys(homedir=self.key_sender_homedir) assert_equal(1, len(keys)) key = keys[0] uids = key.uidslist # Now finally call the function under test uid_encrypted = list(sign_keydata_and_encrypt(public_sender_key, error_cb=None, homedir=self.key_receiver_homedir)) assert_equal(len(uids), len(uid_encrypted)) # We need to explicitly request signatures uids_before = uids assert_equal(len(uids_before), len(sender.get_key(fpr).uids)) sigs_before = [s for l in get_signatures_for_uids_on_key(sender, key).values() for s in l] # FIXME: Refactor this a little bit. # We have duplication of code with the other test below. for uid, uid_enc in zip(uids_before, uid_encrypted): uid_enc_str = uid_enc[0].uid # The test doesn't work so well, because comments # are not rendered :-/ # assert_equal(uid, uid_enc[0]) assert_in(uid.name, uid_enc_str) assert_in(uid.email, uid_enc_str) ciphertext = uid_enc[1] log.debug("Decrypting %r", ciphertext) plaintext, result, vrfy = sender.decrypt(ciphertext) log.debug("Decrypt Result: %r", result) sender.op_import(plaintext) import_result = sender.op_import_result() log.debug("Import Result: %r", import_result) assert_equal(1, import_result.new_signatures) updated_key = sender.get_key(fpr) log.debug("updated key: %r", updated_key) log.debug("updated key sigs: %r", [(uid, uid.signatures) for uid in updated_key.uids]) sigs_after = [s for l in get_signatures_for_uids_on_key(sender, key).values() for s in l] assert_greater(len(sigs_after), len(sigs_before))
def decrypt_text(self, data, passphrase=None, verify=True): """ Decrypt the ciphertext `data`. Provide a `passphrase` for symmetric decryption. The decrypted data is returned as a string. """ with gpg.Data() as sink: self._decrypt(data, sink, passphrase=passphrase, verify=verify) return self._read_data(sink)
def five_temp_key(random_userid): keys = [] with gpg.Context(armor=True) as gpg_context: for _ in range(0, 5): with gpg.Data() as expkey: dmkey = gpg_context.create_key(algorithm="rsa1024", userid=next(random_userid), encrypt=True) gpg_context.op_export(dmkey.fpr, 0, expkey) expkey.seek(0, os.SEEK_SET) keys.append(expkey.read()) yield keys
def test_sign_and_encrypt(self): secret_keydata = open(self.key_sender_key, "r").read() # We get the public portion of the key sender = TempContext() sender.op_import(secret_keydata) result = sender.op_import_result() fpr = result.imports[0].fpr sink = gpg.Data() sender.op_export(fpr, 0, sink) sink.seek(0, 0) # This is the key that we will sign public_sender_key = sink.read() keys = get_usable_secret_keys(homedir=self.key_sender_homedir) assert_equals(1, len(keys)) key = keys[0] uids = key.uidslist # Now finally call the function under test uid_encrypted = list( sign_keydata_and_encrypt(public_sender_key, error_cb=None, homedir=self.key_receiver_homedir)) assert_equals(len(uids), len(uid_encrypted)) # We need to explicitly request signatures sender.set_keylist_mode(gpg.constants.KEYLIST_MODE_SIGS) uids_before = sender.get_key(fpr).uids sigs = [uid.signatures for uid in uids_before] sigs_before = [sig for signatures in sigs for sig in signatures] for uid, uid_enc in zip(uids_before, uid_encrypted): # The test doesn't work so well, because comments # are not rendered :-/ # assert_equals(uid, uid_enc[0]) assert_in(uid.name, uid_enc[0].uid) assert_in(uid.email, uid_enc[0].uid) ciphertext = uid_enc[1] log.debug("Decrypting %r", ciphertext) plaintext, result, vrfy = sender.decrypt(ciphertext) log.debug("Decrypt Result: %r", result) sender.op_import(plaintext) import_result = sender.op_import_result() log.debug("Import Result: %r", import_result) assert_equals(1, import_result.new_signatures) updated_key = sender.get_key(fpr) log.debug("updated key: %r", updated_key) log.debug("updated key sigs: %r", [(uid, uid.signatures) for uid in updated_key.uids]) uids_after = sender.get_key(fpr).uids sigs = [uid.signatures for uid in uids_after] sigs_after = [sig for signatures in sigs for sig in signatures] assert_greater(len(sigs_after), len(sigs_before))
def gpg_verify(self, subject, sig=None, pubkey=None): # verify with GnuPG with gpg.Context(armor=True, offline=True) as c: c.set_engine_info(gpg.constants.PROTOCOL_OpenPGP, home_dir=gnupghome) # do we need to import the key? if pubkey: try: c.get_key(pubkey.fingerprint) except gpg.errors.KeyNotFound: key_data = gpg.Data(string=str(pubkey)) gpg.core.gpgme.gpgme_op_import(c.wrapped, key_data) vargs = [gpg.Data(string=str(subject))] if sig is not None: vargs += [gpg.Data(string=str(sig))] _, vres = c.verify(*vargs) assert vres
def encrypt_file(self, input_file, output_file, recipients=None, passphrase=None, always_trust=False, armor=None): """ Encrypt the plain text stored in `input_file` for the given `recipients` and store the encrypted data in `output_file`. The files must be already opened with the correct read/write (and binary) modes. The recipients may be a single Key object, a list of Key objects, or `None` to encrypt the data symmetrically. Provide a `passphrase` for symmetric encryption. If `always_trust` is `True` then keys in the recipients that are not explicitly marked as trusted are still allowed. If `armor` is not `None` then it overrides the `armor` property set upon construction. This may be useful for encrypting binary data on a channel that supports non-text data since it reduces the required size and network resources. """ with gpg.Data() as plaintext: plaintext.new_from_fd(input_file) with gpg.Data() as ciphertext: ciphertext.new_from_fd(output_file) if armor is not None: old_armor = self._gpg.armor self._gpg.armor = armor self._encrypt(plaintext, ciphertext, recipients, passphrase, always_trust=always_trust) if armor is not None: self._gpg.armor = old_armor
def get_public_key_data(fpr, homedir=None): c = DirectoryContext(homedir) c.armor = True sink = gpg.Data() # FIXME: There will probably be an export() function c.op_export(fpr, 0, sink) sink.seek(0, os.SEEK_SET) keydata = sink.read() log.debug("Exported %r: %r", fpr, keydata) if not keydata: s = "No data to export for {} (in {})".format(fpr, homedir) raise ValueError(s) return keydata
def openpgpkey_from_data(keydata): c = TempContext() c.op_import(gpg.Data(keydata)) result = c.op_import_result() log.debug("Import Result: %s", result) if result.imported != 1: raise ValueError("Keydata did not contain exactly one key, but %r" % result.imported) else: imported = result.imports import_ = imported[0] fpr = import_.fpr key = c.get_key(fpr) return Key.from_gpgme(key)
def export_public_key(keydata): "Returns the public portion of the key even if you provide a private key" # This might be a secret key, too, so we import and export to # get hold of the public portion. ctx = TempContext() ctx.op_import(keydata) result = ctx.op_import_result() fpr = result.imports[0].fpr sink = gpg.Data() ctx.op_export(fpr, 0, sink) sink.seek(0, 0) public_key = sink.read() assert len(public_key) > 0 return public_key
def encrypt_text(self, data, recipients=None, passphrase=None, always_trust=False): """ Encrypt the plain text `data` for the given `recipients`, which may be a single Key object, a list of Key objects, or `None` to encrypt the data symmetrically. Provide a `passphrase` for symmetric encryption. If `always_trust` is `True` then keys in the recipients that are not explicitly marked as trusted are still allowed. The encrypted data is returned as a string. """ with gpg.Data(data) as plaintext: with gpg.Data() as ciphertext: self._encrypt(plaintext, ciphertext, recipients, passphrase, always_trust=always_trust) return self._read_data(ciphertext)
def encrypt_payload(self, payload, gpg_keys): global legacy_gpg payload = encode_string(payload) self._ctx.armor = True if legacy_gpg: plaintext = BytesIO(payload) ciphertext = BytesIO() self._ctx.encrypt(gpg_keys, gpgme.ENCRYPT_ALWAYS_TRUST, plaintext, ciphertext) return ciphertext.getvalue() else: (ciphertext, encresult, signresult) = self._ctx.encrypt(gpg.Data(string=payload), recipients=gpg_keys, sign=False, always_trust=True) return ciphertext
def rawkey2infos(key_fo): pb_dir = tempfile.mkdtemp() keyinfos = [] with pubring_dir(pb_dir), gpg.Context() as ctx: ctx.op_import(key_fo) for key in ctx.keylist(): subkey = _extract_signing_subkey(key) if subkey is None: continue keyinfos.append(Key(key, subkey)) ctx.armor = True for info in keyinfos: with gpg.Data() as sink: ctx.op_export(info.id_, 0, sink) sink.seek(0, os.SEEK_SET) info.raw_key = sink.read() dnf.util.rm_rf(pb_dir) return keyinfos
def import_key_and_check_status(self, key): """Import a GnuPG key and check that the operation was successful. :param str key: A string specifying the key's filepath from ``Common.paths`` :rtype: bool :returns: ``True`` if the key is now within the keyring (or was previously and hasn't changed). ``False`` otherwise. """ if gpgme_support: with gpg.Context() as c: c.set_engine_info(gpg.constants.protocol.OpenPGP, home_dir=self.paths['gnupg_homedir']) impkey = self.paths['signing_keys'][key] try: c.op_import(gpg.Data(file=impkey)) except: return False else: result = c.op_import_result() if result and self.fingerprints[key] in result.imports[ 0].fpr: return True else: return False else: success = False p = subprocess.Popen([ '/usr/bin/gpg', '--status-fd', '2', '--homedir', self.paths['gnupg_homedir'], '--import', self.paths['signing_keys'][key] ], stderr=subprocess.PIPE) p.wait() for output in p.stderr.readlines(): match = gnupg_import_ok_pattern.match(output) if match: if match.group().find(self.fingerprints[key]) >= 0: success = True break return success
def import_key(self, pubkey): """ Import a single public key provided in the string `pubkey`. Returns a tuple of the imported key object and the import result object. If not exactly one key is provided or imported, then a `ValueError` is raised with the import result in its arguments. """ with gpg.Data(pubkey) as import_key: self._gpg.op_import(import_key) result = self._gpg.op_import_result() if result.considered != 1: raise ValueError('Exactly one public key must be provided', result) if result.imported != 1: raise ValueError('Given public key must be valid', result) return self._get_imported_key(result), result
def minimise_key(keydata): "Returns the public key exported under the MINIMAL mode" ctx = TempContext() ctx.op_import(keydata) result = ctx.op_import_result() if result.considered != 1 and result.imported != 1: raise ValueError("Expected to load exactly one key. %r", result) else: imports = [i for i in result.imports if i.status == gpg.constants.IMPORT_NEW] log.debug("Import %r", result) assert len(imports) == 1 fpr = result.imports[0].fpr key = ctx.get_key(fpr) sink = gpg.Data() ctx.op_export_keys([key], gpg.constants.EXPORT_MODE_MINIMAL, sink) sink.seek(0, 0) minimised_key = sink.read() return minimised_key