Search code examples
pythonencryptiongnupgbytesio

gnupg - decrypt into Python bytesio stream


How can I select a stream as the output of a decrypt_file operation in gnupg?

The docs and the code seem to suggest this is not possible. If I am correct (see below), what workarounds are possible?

~~~

The documentation seems to suggest it is not possible:

decrypt_file(filename, always_trust=False, passphrase=None, output=None)¶

with "output (str) – A filename to write the decrypted output to."

~~~

Opening up the code, I see:

def decrypt_file(self, file, always_trust=False, passphrase=None,
                 output=None, extra_args=None):
    args = ["--decrypt"]
    if output:  # write the output to a file with the specified name
        self.set_output_without_confirmation(args, output)
    if always_trust:  # pragma: no cover
        args.append("--always-trust")
    if extra_args:
        args.extend(extra_args)
    result = self.result_map['crypt'](self)
    self._handle_io(args, file, result, passphrase, binary=True)
    logger.debug('decrypt result: %r', result.data)
    return result

which points to set_output_without_confirmation, confirming the idea is that you pass a string filename:

def set_output_without_confirmation(self, args, output):
    "If writing to a file which exists, avoid a confirmation message."
    if os.path.exists(output):
        # We need to avoid an overwrite confirmation message
        args.extend(['--yes'])
    args.extend(['--output', no_quote(output)])

Solution

  • To output the decrypted data to a variable use decrypt instead of decrypt_file, as shown here in the "Decrypt a string" paragraph.

    So the original code:

    status = gpg.decrypt_file(input_file, passphrase='my_passphrase', output='my_output_file')
    

    is substituted by:

    decrypted_data = gpg.decrypt(input_file.read(), passphrase='my_passphrase')
    # decrypted_data.data contains the data
    decrypted_stream = io.BytesIO(decrypted_data.data)
    # this is py3, in py2 BytesIO is imported from BytesIO
    

    As an example for the specific use case for csv data, building on this SO post, you could then do:

    my_df = pandas.read_csv(decrypted_stream)