1
\$\begingroup\$

Its meant to password protect python scripts by using encryption It should use os.urandom(40)'s output in hex as the salt. The kdf (cryptography.io) is using Scrypt (cryptography.io) password input is using getpass.getpass. It's on my GitHub: Python Script Locker (GitHub), and is required to work with both Python 2 and 3.

#!/usr/bin/env python
import sys
def exitmsg(msg):
    print(msg)
    input("Press ENTER to exit the script")
    sys.exit()
if sys.version_info<(3,0,0):
    def input(string):
         return raw_input(string)
import base64
import binascii
import os
import getpass
try:
    from cryptography.fernet import Fernet
except ImportError:
    exitmsg("cryptography not installed install it with pip install cryptography via cmd or powershell (On Windows)")
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
print("PyLock beta v1.0.3 https://github.com/OfficialNoob/Python-Script-Locker")
salt = binascii.hexlify(os.urandom(40))
kdf = Scrypt(salt=salt,length=32,n=2**14,r=8,p=1,backend=default_backend())
loc = input("Script to use: ")
try:
    fscript = open(loc)
except IOError:
    exitmsg("Unable to read file")
script = fscript.read()
fscript.close
print("Can be used to overwrite your script")
sloc = input("Save as: ")
nc = '''#!/usr/bin/env python
#Made using PyLock https://github.com/OfficialNoob/Python-Script-Locker
import sys
def exitmsg(msg):
    print(msg)
    input("Press ENTER to exit")
    sys.exit()
if sys.version_info<(3,0,0):
    def input(string):
         return raw_input(string)
import getpass
import base64
try:
    from cryptography.fernet import Fernet
except ImportError:
    exitmsg("cryptography not installed install it with pip install cryptography via cmd or powershell (On Windows)")
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
kdf = Scrypt(salt=%s,length=32,n=2**14,r=8,p=1,backend=default_backend())
try:
    exec(Fernet(base64.urlsafe_b64encode(kdf.derive(getpass.getpass("Password to use: ").encode()))).decrypt(%s))
except Exception as ex:
    if(type(ex).__name__ == "InvalidToken"):
        exitmsg("Wrong password (-:")
    print(ex)''' % (salt, Fernet(base64.urlsafe_b64encode(kdf.derive(getpass.getpass("Password to use: ").encode()))).encrypt(script.encode()))
try:
    f = open(sloc,"w+")
    f.write(nc)
except IOError:
    exitmsg("Unable to write to file")
f.close
exitmsg("Your file has been created")
\$\endgroup\$
0

2 Answers 2

3
\$\begingroup\$

The good news is that as far as I can tell, the cryptography is correct (password-based key derivation function, random salt). The bad news is that your code is hard to read, and so it's hard to make sure that it's correct.

Please read PEP 8. It's good advice. The first thing one sees is 62 lines and not a single blank line. Use blank lines between function definitions. And on a related note, use auxiliary functions with a clearly-defined task. Avoid very long lines (80 columns maximum is a good idea), and break up long chains like Fernet(base64.urlsafe_b64encode(kdf.derive(getpass.getpass("Password to use: ").encode()))).encrypt(script.encode())) into manageable pieces. Because the code is hard to read, don't expect a complete review.

def exitmsg(msg):
    print(msg)
    input("Press ENTER to exit")
    sys.exit()

Don't require the user to press a key to exit the program. And don't print anything when you don't have anything to say. Standard output is for program output and standard error is for useful messages. This function doesn't do anything useful. Replace exitmsg(msg) by sys.exit().

    def input(string):

Don't override standard functions.

    exitmsg("cryptography not installed install it with pip install cryptography via cmd or powershell (On Windows)")

punctuation is important your message is incomprehensible language is not just a bunch of words thrown together

        exitmsg("Wrong password (-:")

Print error messages to standard error, not standard output. And when there's an error, exit with a nonzero status.

loc = input("Script to use: ")

Don't read script arguments from standard input. Read them from sys.argv. That's what it's for.

try:
    exec(…)
except Exception as ex:
    if(type(ex).__name__ == "InvalidToken"):
        exitmsg("Wrong password (-:")
    print(ex)

You're reporting a “wrong password” error for a certain class of errors. It's not clear to me what you're trying to do with InvalidToken. Rather than hope that the wrong key will result in a particular kind of parse error, start the ciphertext with a known value and check that known value. Since you know that the ciphertext is Python code, start the ciphertext wih a Python comment line.

plaintext = fernet.decrypt(ciphertext)
if not ciphertext.startswith('# OfficialNoob PasswordScriptLocker ciphertext version 000000001\n'):
    raise IncorrectPassword

And once again, error messages go on standard error, and the script must exit with a nonzero status on error.


Of course the whole thing is pretty pointless. Entering a password on each use of the script would be annoying. Using gpg to decrypt the script once and for all would be more flexible (you can use keys instead of a password), more convenient, and more secure (less risk of implementation errors).

\$\endgroup\$
4
  • \$\begingroup\$ Also reading from sys.argv would result in the password being in the command history \$\endgroup\$
    – user172978
    Commented Jul 2, 2018 at 9:21
  • \$\begingroup\$ cryptography.io/en/latest/fernet/?highlight=InvalidToken it seems okay to be used \$\endgroup\$
    – user172978
    Commented Jul 2, 2018 at 9:42
  • \$\begingroup\$ And last thing I used exitmsg so you can read the error before it closes but its pointless if your already in cmd/powershell (windows) \$\endgroup\$
    – user172978
    Commented Jul 2, 2018 at 9:46
  • \$\begingroup\$ code-maven.com/never-use-input-in-python2 thats why I overridden input() for python2 \$\endgroup\$
    – user172978
    Commented Jul 2, 2018 at 10:35
2
\$\begingroup\$

fscript.close doesn't do anything. Perhaps you meant to call fscript.close(). In any case, the preferred way to open a file is to use a with block, so that Python always takes care of closing the file for you:

try:
    with open("Script to use: ") as fscript:
        script = fscript.read()
except IOError:
    exitmsg("Unable to read file")
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.