2

I want to run the following command using Python on a Mac OS X computer:

openssl enc -aes-128-cbc -K $(echo -n 'blabla1' | xxd -p) -iv blabla2 -in /tmp/clair -nopad -out /tmp/crypte1 -d

Here is what I have tried:

clef = 'blabla1'
iv = 'blabla2'
arguments = ['openssl','enc', '-aes-128-cbc', '-K $(echo -n \'%s\' | xxd -p)' % clef ,'-iv' ,'%s' % iv,'-in', '/tmp/clair','-nopad','-out','/tmp/crypte1','-d']
execute = Popen(arguments, stdout=PIPE)
out, err = execute.communicate()

The command works fine from a terminal but I get an error from the Python script:

unknown option '-K $(echo -n 'blabla1' | xxd -p)'

I have tried several variants of python shell functions (os.system for example), but I have a problem in each case.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Bob5421
  • 7,757
  • 14
  • 81
  • 175

2 Answers2

4

There are a number of things that users often take for granted when using a shell. Some examples are:

  • Variable expansion (${var})
  • Input/output redirection (cmd > out < in)
  • Pipes (cmd1 | cmd2)
  • Subshell creation ($(cmd))

All of these are features that the shell sets up before passing the actual command to the system. Python's subprocess module does not do any of these things for you automatically, but it does give you the tools to emulate them.

You have correctly redirected your output to a pipe for your Python process to pick up. However, the subshell created by $(echo -n 'blabla1' | xxd -p) is not something that will get processed the way you want without a shell. There are two simple workarounds.

  1. The quick and dirty solution is to pass the entire command line in as a string to subprocess.Popen and set shell=True:

    execute = Popen("openssl enc -aes-128-cbc -K $(echo -n 'blabla1' | xxd -p) "
                    "-iv blabla2 -in /tmp/clair -nopad -out /tmp/crypte1 -d",
                    shell=True)
    

    This will pass the string to a shell instead of directly to the system, thereby giving you access all the behaviors that you expect from a shell. This approach has major security problems, and is not generally recommended. However, it is very easy, and it will get you started.

  2. Implement the convenience of $(... | ...) in your code directly by capturing the output of xxd to a string using subprocess.run. This is lengthier, but probably more robust and portable in the long run. It is certainly the option I would chose in a production environment:

    from subprocess import Popen, run
    
    value = run(['xxd', '-p'], input='blabla1', universal_newlines=True).stdout
    execute = Popen(['openssl', 'enc', '-aes-128-cbc', '-K', value,
                     '-iv', 'blabla2', '-in', '/tmp/clair', '-nopad',
                     '-out', '/tmp/crypte1', '-d'])
    

Since you are setting up your own pipes, you don't need to call echo any more (you never did really, xxd -p <<< 'blabla1' would have worked fine), and -K needs to be a separate argument.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • I have tried your solution 1. I get this error: "hex string is too long invalid hex key value". And i have no error if i launch directly from my terminal – Bob5421 Mar 23 '18 at 14:11
  • Can you tell me the difference between running `echo ... | xxd -p` from the command line and running `run(['xxd', '-p'], input='blabla1', universal_newlines=True).stdout` in the Python shell? – Mad Physicist Mar 23 '18 at 14:14
  • @Bob5421. I'm assuming you were referring to the second option by the way. – Mad Physicist Mar 23 '18 at 14:16
  • @Bob5421. Could you also please edit a list of the alternatives you've tried and the errors they gave you into your question? – Mad Physicist Mar 23 '18 at 14:18
  • In fact i am wondering if the problem is not that some chars are not encoded on 1 byte – Bob5421 Mar 23 '18 at 16:21
  • @Bob5421. I'd love to help you figure it out, but I kinda need the information I requested to do that. – Mad Physicist Mar 23 '18 at 16:24
0

subprocess adds quotes " to the argument "-K $(echo -n 'blabla1' | xxd -p)". You can check by

print(subprocess.list2cmdline(execute.args))

Output:

openssl enc -aes-128-cbc "-K $(echo -n 'blabla1' | xxd -p)" -iv blabla2 -in /tmp/clair -nopad -out /tmp/crypte1 -d
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Sumit Jha
  • 1,601
  • 11
  • 18
  • That is a very minor issue and does not address the actual problem with the subshell OP is trying to create. – Mad Physicist Mar 23 '18 at 13:53
  • Well, of course its not full solution but it addresses the problem of `Popen(arguments, stdout=PIPE)` with subsell. Thank you for your comprehensive answser. – Sumit Jha Mar 23 '18 at 13:59
  • 1
    I've removed my downvote. You are right that this is not really a minor issue, and you did address the immediate question, even if the question is incomplete. – Mad Physicist Mar 23 '18 at 14:01