0

I have tried an approach mentioned here using the following implementation:

from subprocess import PIPE, Popen

process = Popen(['/usr/bin/openssl', 'enc', '-aes-256-cbc', '-a', '-pass', 'pass:asdf'], stdin=PIPE, stdout=PIPE)
process.stdin.write('Hello this is it')
process.stdin.flush()
print(repr(process.stdout.readline()))

but it gets stuck at readline() although I have written and flushed already. I also tried a non-blocking approach mentioned here but that is also blocking on readline(). Following is my code for the later approach:

import sys
import time
from subprocess import PIPE, Popen
from threading import Thread
from Queue import Queue, Empty

def enqueue_output(out, queue):
    for line in iter(out.readline, b''):
        queue.put(line)
    out.close()

def write_to_stdin(process):
    process.stdin.write(b'Hello this is it')

p = Popen(['/usr/bin/openssl', 'enc', '-aes-256-cbc', '-a', '-pass', 'pass:asdf'], stdin=PIPE, stdout=PIPE, bufsize=-1, close_fds=ON_POSIX)
q = Queue()

t2 = Thread(target=write_to_stdin, args=(p,))
t2.daemon = True
t2.start()

t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True  # thread dies with the program
t.start()

try:
    line = q.get(timeout=3) # or q.get(timeout=.1)
except Empty:
    print('no output yet')
else:
    print line

I get no output yet as output.

The only approach working is using:

process.communicate

but this closes the process and we have to re-open the process again. For a large number of messages to encrypt this is taking too much time and I am trying to avoid including any external package for achieving this task. Any help would be highly appreciated Thanks.

2 Answers2

0

Replace the process.stdin.flush(), by process.stdin.close(), like this:

from subprocess import PIPE, Popen

process = Popen(['/usr/bin/openssl', 'enc', '-aes-256-cbc', '-a', '-pass', 'pass:asdf'], stdin=PIPE, stdout=PIPE)
process.stdin.write('Hello this is it')
process.stdin.close()
print(repr(process.stdout.readline()))

This readline() does not block any longer.

The explanation is that openssl enc waits until the end of its standard input, which is reached only when the calling process closes the pipe.

user803422
  • 2,636
  • 2
  • 18
  • 36
  • Got it but how can I reopen the process.stdin without the need for instantiating another Popen(). Is this possible ? because I need to keep communicating without closing the process. – Syed Atyab Hussain Dec 22 '17 at 13:09
  • No, you can't. If you want to cipher separately several chunks of data, you need to run several openssl commands, and therefore several subprocesses. – user803422 Dec 23 '17 at 20:26
  • OK, Thanks a lot. Now I will try using the ctype "https://code.google.com/archive/p/ctypescrypto/" so that there is no need to spawn openssl process. – Syed Atyab Hussain Dec 26 '17 at 07:35
0

Found an alternative method of doing this in linux using ctypescrypto. This doesn't require any additional external dependency (just include all of the given source files in your code as the licence allows us to do so) and in terms of performance it should be quite fast since the functions available are compiled in C.

On Linux for test.py we have to replace:

crypto_dll = os.path.join(r'C:\Python24', 'libeay32.dll')
libcrypto = cdll.LoadLibrary(crypto_dll)

with:

from ctypes.util import find_library

crypto_dll = find_library('crypto')  # In my case its 'libcrypto.so.1.0.0'
libcrypto = cdll.LoadLibrary(crypto_dll)

After changing it the following example works:

import cipher
from ctypes import cdll
from base64 import b64encode
from base64 import b64decode

libcrypto = cdll.LoadLibrary('libcrypto.so.1.0.0')
libcrypto.OpenSSL_add_all_digests()
libcrypto.OpenSSL_add_all_ciphers()

# Encryption
c = cipher.CipherType(libcrypto, 'AES-256', 'CBC')
ce = cipher.Cipher(libcrypto, c, '11111111111111111111111111111111', '1111111111111111', encrypt=True)
encrypted_text = b64encode(ce.finish("Four Five Six"))
print encrypted_text


# Decryption
cd = cipher.Cipher(libcrypto, c, '11111111111111111111111111111111', '1111111111111111', encrypt=False)
plain_text = cd.finish(b64decode(encrypted_text))
print plain_text