Python/POpen/gpg: Supply passphrase and encryption text both through stdin or file descriptor
Question: I'm trying to remote control gpg through a python program via `POpen`.  
I have a file that contains encrypted data which I want to decrypt, modify and
write back to disk re-encrypted.  
Currently I am storing the decrypted information in a temporary file (which I
`shred` when the program ends). Then I perform my modifications to that file
and then re-encrypt it using a function, which pipes the passphrase through
`stdin`.  
The code for this is as follows:

    
    
    def encrypt(source, dest, passphrase, cipher=None):
      """Encrypts the source file.
      @param source Source file, that should be encrypted.
      @param dest Destination file.
      @param passphrase Passphrase to be used.
      @param cipher Cipher to use. If None or empty string gpg's default cipher is
      used.
      """
      phraseecho = Popen(("echo", passphrase), stdout=subprocess.PIPE)
    
      gpgargs = [
              "gpg",
              "-c",
              "--passphrase-fd", "0", # read passphrase from stdin
              "--output", dest,
              "--batch",
              "--force-mdc"]
      if not cipher is None and len(cipher) > 0:
          gpgargs.extend(("--cipher-algo", cipher))
    
      gpgargs.append(source)
    
      encrypter = Popen(
              gpgargs,
              stdin=phraseecho.stdout,
              stdout=subprocess.PIPE,
              stderr=subprocess.PIPE)
      stdout, stderr = encrypter.communicate()
      rc = encrypter.returncode
      if not rc == 0:
          raise RuntimeError(
                  "Calling gpg failed with return code %d: %s" % (rc, stderr))
    

This works perfectly well, but I'm fairly sure that storing potentionally
sensitive, decrypted data in a temporary file is a rather big security flaw.  
So I want to rewrite my encryption/decryption functions in a way, that enables
them to work completely in memory without storing sensitive data on disk.  
Decryption works straight forward by also piping the passphrase via `stdin`
and capturing `stdout` for the decrypted data.

Encryption on the other hand drives me mad, since I can't just pipe the
passphrase AND the message to `stdin'...at least

    
    
    encrypter.stdin.write("%s\n%s" % (passphrase, message))
    

didn't work.  
My next best guess is to supply the file-descriptor of some kind of in-memory
file/pipe/socket or whatever as `--passphrase-fd` argument. The thing is: I
don't know if there even is a thing such as in-memory files or if sockets
would apply, since I never used them.

Can anybody help out or point me to a better solution for my problem?  
The solution does not have to be portable - I'm totally fine with Linux only
approaches.

Thanks in advance...

Edit:  
Thanks a lot to both of you, Lars and ryran. Both solutions work perfectly!
Unfortunately I can only accept one


Answer: Chris: Since you have a simple-ish example of using os.pipe thanks to Lars,
I'll offer what [Pyrite](https://github.com/ryran/pyrite/) (my GTK frontend
for gpg) does as well in the hope that more code examples are better. My use
case is a little more complicated than yours due to the gui aspect -- I
actually use a dictionary for input and output, and I have code to launch gpg
with stdin as input and code that launches it with files as input, among other
complications.

That warning said, I start with the gpg commandline in a list just like you
do; however, instead of using `--passphrase-fd 0`, I create a custom file
descriptor via `os.pipe()` to send the passphrase before loading the `Popen()`
instance, which has `stdin=subprocess.PIPE` for the input data. Following are
some relevant (modified) excerpts from pyrite's
[crypt_interface](https://github.com/ryran/pyrite/blob/master/modules/crypt_interface.py)
module.

    
    
    #!/usr/bin/env python
    # Adapted excerpts from Pyrite <http://github.com/ryran/pyrite>
    
    from subprocess import Popen, PIPE, check_output
    ...
     # I/O dictionary obj
     self.io = dict(
        stdin='',   # Stores input text for subprocess
        stdout='',  # Stores stdout stream from subprocess
        stderr=0,   # Stores tuple of r/w file descriptors for stderr stream
        gstatus=0,  # Stores tuple of r/w file descriptors for gpg-status stream
        infile=0,   # Input filename for subprocess
        outfile=0)  # Output filename for subprocess
    ...
    cmd = ['gpg']
    fd_pwd_R, fd_pwd_W = os.pipe()
    os.write(fd_pwd_W, passwd)
    os.close(fd_pwd_W)
    cmd.append('--passphrase-fd')
    cmd.append(str(fd_pwd_R))
    ...
    # If working direct with files, setup our Popen instance with no stdin
    if self.io['infile']:
        self.childprocess = Popen(cmd, stdout=PIPE, stderr=self.io['stderr'][3])
    # Otherwise, only difference for Popen is we need the stdin pipe
    else:
        self.childprocess = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=self.io['stderr'][4])
    
    # Time to communicate! Save output for later
    self.io['stdout'] = self.childprocess.communicate(input=self.io['stdin'])[0]
    
    # Clear stdin from our dictionary asap, in case it's huge
    self.io['stdin'] = ''
    
    # Close os file descriptors
    if fd_pwd_R:
        os.close(fd_pwd_R)
    time.sleep(0.1)  # Sleep a bit to ensure everything gets read
    os.close(self.io['stderr'][5])
    if self.io['gstatus']:
        os.close(self.io['gstatus'][6])
    ...
    

The function that calls all that waits until the `self.childprocess` object
has a `returncode` attribute and assuming the returncode was `0` and that the
input was text (and not a file), it then reads gpg's stdout from that
dictionary and prints it to the screen.

Happy to answer questions or try to help from my limited experience. Can find
my contact info by following links.

Edit: You might also find
[a4crypt](https://github.com/ryran/b19scripts/blob/master/a4crypt.py)
instructive as it is a much simpler frontend for gpg -- it was the project I
started in order to learn python, and later mothballed after I "completed" (if
there is such a thing) pyrite.

