-3

I'm trying to encode the contents of a Python script in Linux. I just started off with a simple script called test.py -

# !/app/logs/Python/bin/python3
# -*- coding: ascii -*-
print ("hi")

Once I have the script, I execute the vim -x test.py and enter the encryption key twice. Then save the file as normal and then execute the script using python test.py

I tried almost all the examples provided in the link here but still i end up getting the below error -

SyntaxError: Non-ASCII character '\x97' in file test.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details

I checked the default encoding using print sys.getdefaultencoding() and it is acsii.

What am i missing here. kindly clarify. I know this question is a duplicate but none of the solutions helped.

sdgd
  • 723
  • 1
  • 17
  • 38
  • 4
    Unless I'm missing something you've encrypted the file, so python won't be able to run it. The complaint is because you're trying to feed garbage (effectively) to the python interpreter. When you `cat test.py` it should reveal that the file no longer has python usable content – Anya Shenanigans Jul 12 '18 at 09:10
  • yes you are right. I've encrypted the file just to hide it from the people. How can this script be made executable ? – sdgd Jul 12 '18 at 09:13
  • 2
    By decrypting it, of course. – Jongware Jul 12 '18 at 09:15
  • 2
    You can make the script executable by decrypting it. If you want to hide the source from 'the people' you need to define the boundaries; then we might be able to make some suggested approaches. e.g. do you want them to be able to run the script also? However this isn't really in the realms of this site. A site like [information security SE](https://security.stackexchange.com) might be be a better place for that kind of question. – Anya Shenanigans Jul 12 '18 at 09:17
  • yes, they should just be able to execute the script and not to view the code. – sdgd Jul 12 '18 at 09:20
  • "Welcome! Unfortunately, I think this question is off-topic for our site as it doesn't appear to be related to InfoSec, question like this is better suited to StackOverflow. " response from the information security SE site :) – sdgd Jul 12 '18 at 09:29
  • 3
    @sdevgd you misunderstood the comment. If you want high-level approaches for hiding information, then post on sec.se. For how to handle bugs in your code, then this is the correct place. – schroeder Jul 12 '18 at 09:46
  • 2
    @sdevgd you took a text file, encrypted it, then tried to get an interpreter to run it. That will not ever work. If your goal is to hide the content of a scripting langauge from those who are running it, then *that* needs to be your question (and your source of research). – schroeder Jul 12 '18 at 09:50

1 Answers1

2

Python knows how to execute clear text Python source code. If you encrypt the source file, it no longer contains valid Python source and cannot be directly executed.

There are 2 possible ways here. First is to only obfuscate your source. You should be aware that obfuscation is not security, and a user could recover some Python source with some work (not necessarily the original source, because comments and doc strings could have been stripped off and variable names could have been changed). You could read How do I protect Python code? and google for python obfuscate to find some possible ways of obfuscating Python source and their trade-offs.

The good news with obfuscated source is that it can be used by anybody without any password.

Second way is to encrypt the source. If you use a decent tool, you can assume that it will be impossible to read or execute the file without knowing the key. In that sense, vim crypto has not the highest possible reputation. In the simplest way (for example with your example vim -x) you will have to decrypt the file to execute it. Unfortunately, good crypto modules are not shipped in a standard Python installation and must be downloaded from pypi. Well known crypto modules include pycrypto and cryptography.

You can then encrypt the most part of the code, and then at run time ask for the key, decrypt it and execute it. Still a serious job but feasible.

Alternatively, you could build in another language (C/C++) a decryptor that decrypts the remaining of the file and feed it into a python interpretor, but functionally, this is only a variant of the above method.


As per your comment I assume that you want to encrypt the source code and decrypt it (with the password) at run time. The princips are:

  • build a Python script that will take another arbitrary Python script, encode it with a secure crypto module and prepend it with some decrypting code.
  • at run time, the prepended code will ask for the password, decrypt the encrypted code exec it

The builder could be (this code uses the cryptography module):

import cryptography.fernet
import cryptography.hazmat.primitives.kdf.pbkdf2
import cryptography.hazmat.primitives.hashes
import cryptography.hazmat.backends
import base64
import os
import sys

# the encryption function
def encrypt_source(infile, outfile, passwd):
    with open(infile, 'rb') as fdin:     # read original file
        plain_data = fdin.read()
    salt, key = gen_key(passwd)          # derive a key from the password
    f = cryptography.fernet.Fernet(key)
    crypted_data = f.encrypt(plain_data) # encrypt the original code
    with open(outfile, "w") as fdout:    # prepend a decoding block
        fdout.write("""#! /usr/bin/env python

import cryptography.fernet
import cryptography.hazmat.primitives.kdf.pbkdf2
import cryptography.hazmat.primitives.hashes
import cryptography.hazmat.backends
import base64
import os

def gen_key(passwd, salt):             # key derivation
    kdf = cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC(
        algorithm = cryptography.hazmat.primitives.hashes.SHA256(),
        length = 32,
        salt = salt,
        iterations = 100000,
        backend = cryptography.hazmat.backends.default_backend()
    )
    return base64.urlsafe_b64encode(kdf.derive(passwd))

passwd = input("Password:")            # ask for the password
salt = base64.decodebytes({})
key = gen_key(passwd.encode(), salt)   # derive the key from the password and the original salt

crypted_source = base64.decodebytes(   # decode (base64) the crypted source
b'''{}'''
)
f = cryptography.fernet.Fernet(key)
plain_source = f.decrypt(crypted_source) # decrypt it
exec(plain_source)                     # and exec it
""".format(base64.encodebytes(salt),
           base64.encodebytes(crypted_data).decode()))

# derive a key from a password and a random salt
def gen_key(passwd, salt=None):        
    if salt is None: salt = os.urandom(16)
    kdf = cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC(
        algorithm = cryptography.hazmat.primitives.hashes.SHA256(),
        length = 32,
        salt = salt,
        iterations = 100000,
        backend = cryptography.hazmat.backends.default_backend()
    )
    return salt, base64.urlsafe_b64encode(kdf.derive(passwd))

if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("Usage {} infile outfile".format(sys.argv[0]))
        sys.exit(1)
    passwd = input("Password:").encode()             # ask for a password
    encrypt_source(sys.argv[1], sys.argv[2], passwd) # and generates an encrypted Python script
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • thank you. Looks like the solution about decrypting the file at run time should help me. I was able to set a key when i was encrypting it, but when i try to run it, it didn't ask for the key. Can you help me on how can i enable this at run time ? – sdgd Jul 13 '18 at 08:29