Last active
April 13, 2016 20:47
-
-
Save averagesecurityguy/170e1caf58553a6696eeb7d6b458fd05 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
encrypt.py is the Python module that does the encryption and decryption using PySodium. You will need to install libsodium and PySodium before you can use this module. | |
encrypt_file.py - This script uses the encrypt.py module to encrypt a configuration file and store a password hash in a password file. It will print out the salt value needed for the open_file.py script. | |
open_file.py - This script uses the encrypt.py module to decrypt a configuration file if the password given matches. | |
On first use do the following: | |
1. Run encrypt_file.py with the password you want to use. | |
2. Copy the salt value given to the open_file.py script. | |
3. Delete the plaintext configuration file. | |
4. Run open_file.py with the password used in #1. The plaintext configuration file will be printed. | |
To change the password on the configuration file do the following: | |
1. Run open_file.py with the old password and save the results to a file. | |
2. Delete the old encrypted configuration file. | |
3. Run encrypt_file.py with the new password and the plaintext configuration file. | |
4. Copy the salt value given to the open_file.py script. | |
5. Delete the plaintext configuration file. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import pysodium | |
import base64 | |
import sys | |
SALTLEN = pysodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES | |
HASHOPS = pysodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE | |
HASHMEM = pysodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE | |
KEYSIZE = pysodium.crypto_secretbox_KEYBYTES | |
NONSIZE = pysodium.crypto_secretbox_NONCEBYTES | |
def encode(str): | |
return base64.b64encode(str) | |
def decode(b64): | |
return base64.b64decode(b64) | |
def encrypt(data, key): | |
""" | |
Encrypt the specified data using the key. | |
""" | |
nonce = pysodium.randombytes(NONSIZE) | |
encrypted = pysodium.crypto_secretbox(data, nonce, decode(key)) | |
return encode(nonce), encode(encrypted) | |
def decrypt(cipher, nonce, key): | |
""" | |
Decrypt the specified cipher using the nonce and key. | |
""" | |
return pysodium.crypto_secretbox_open(decode(cipher), | |
decode(nonce), | |
decode(key)) | |
def generate_hash(password, salt=None): | |
""" | |
Get a password hash of size KEYSIZE based on the password and salt. | |
""" | |
if salt is None: | |
salt = pysodium.randombytes(SALTLEN) | |
hash = pysodium.crypto_pwhash_scryptsalsa208sha256(KEYSIZE, | |
password, | |
salt, | |
HASHOPS, | |
HASHMEM) | |
return encode(salt), encode(hash) | |
def write_password(filename, salt, hash): | |
""" | |
Write the salt and passsword hash to the specified file. | |
""" | |
f = open(filename, 'w') | |
f.write('{0}:{1}\n'.format(salt, hash)) | |
f.close() | |
def read_password(filename): | |
""" | |
Read the salt and password hash from the specified file. | |
""" | |
salt = '' | |
hash = '' | |
f = open(filename) | |
for line in f: | |
line = line.rstrip('\n') | |
salt, hash = line.split(':') | |
break | |
f.close() | |
return salt, hash | |
def encrypt_file(filename, key): | |
""" | |
Encrypt the specified file with the given key. | |
""" | |
f = open(filename) | |
data = f.read() | |
f.close() | |
nonce, enc_data = encrypt(data, key) | |
f = open(filename, 'w') | |
f.write('{0}:{1}\n'.format(nonce, enc_data)) | |
f.close() | |
def decrypt_file(filename, key): | |
""" | |
Decrypt the specified file with the given key. | |
""" | |
f = open(filename) | |
data = f.read() | |
f.close() | |
nonce, enc_data = data.split(':') | |
return decrypt(enc_data, nonce, key) | |
def compare_password(filename, password): | |
""" | |
Compare the given password to the stored password hash. | |
Read the password hash from the file, generate the password hash using | |
the salt stored in the file, and compare the two hashes. If they match, | |
the given password and the stored password are the same. | |
""" | |
salt1, hash1 = read_password(filename) | |
salt2, hash2 = generate_hash(password, decode(salt1)) | |
return hash1 == hash2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import encrypt | |
import sys | |
if len(sys.argv) != 4: | |
print('Usage: ./encrypt_file.py conf_file pass_file password') | |
sys.exit(1) | |
conf_file = sys.argv[1] | |
pass_file = sys.argv[2] | |
password = sys.argv[3] | |
# Generate one password hash and write it to a file. This will be used to | |
# test whether the user provide password is correct. | |
salt1, hash1 = encrypt.generate_hash(password) | |
encrypt.write_password(pass_file, salt1, hash1) | |
# Generate a second password hash and encrypt the file with the hash | |
# value. Print the salt value so it can be used to calculate the | |
# decryption key (hash) later. | |
salt2, hash2 = encrypt.generate_hash(password) | |
encrypt.encrypt_file(conf_file, hash2) | |
print(salt2) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
My brother has the following use case: He has a program that he and a group of others run regularly. He wants some of the users to be able to modify the configuration file and some users to not. I told him to use file permissions but he was insistent that he wanted to use an encrypted configuration file and a shared password. This is the advice I gave him, please tell me if I'm way off base. | |
1. Create the plaintext config file. | |
2. Generate a password hash using scrypt along with a random salt. | |
3. Generate a second password hash using scrypt along with a second random salt. | |
4. Store the first password and salt in a password file | |
5. Store the second salt in the Python script. | |
6. Encrypt the config file with the second password hash and delete the plaintext file. | |
7. Run your program, ask the user for the password. Use the salt from the password file and the user supplied password to generate a password hash. | |
a. If the password hashes do not match, error out of the program. | |
b. If the password hashes do match then generate the second password hash using the password and the stored salt. | |
8. Decrypt the config file using the second password hash, modify it, encrypt it again and write it back to disk. | |
I would recommend using pySodium, which is a wrapper around libsodium. You particularly want to look at the crypto_pwhash_* methods. https://github.com/stef/pysodium | |
You could create one program that just generates the encrypted config file, password hash file, and salt for your Python script. Then just put the password check into the primary script. Then if the password gets shared you can use the first program to regenerate everything and you just need to update the salt in the primary program. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import encrypt | |
import sys | |
# This is the salt value given by the encrypt file script. | |
SALT = 'PaP3btPlvqDFWBQkvhhc09+PW0/p5KQbvhNCnb/2rdk=' | |
if len(sys.argv) != 4: | |
print('Usage: ./open_file.py enc_file pass_file password') | |
sys.exit(1) | |
enc_file = sys.argv[1] | |
pass_file = sys.argv[2] | |
password = sys.argv[3] | |
# Compare the given password with the password hash stored in the password | |
# file. | |
if encrypt.compare_password(pass_file, password) is True: | |
dsalt, dhash = encrypt.generate_hash(password, encrypt.decode(SALT)) | |
data = encrypt.decrypt_file(enc_file, dhash) | |
print(data) | |
else: | |
print('Bad password.') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
So it's mostly to prevent tampering by unauthroized users? I'd just sign the configuration files, encrypt the private key with a password and require those that have access to have that password (or for best effect: encrypt the storage of said key behind a key manager that is encrypted using a password-based key derivation function, so you can rotate that key without having to reencrypt your private key, so you're not handing out access to the private key directly), and check that they were signed prior to running.
If they're scripts and you're worried they can remove the signing check -- they can just modify the script to dump config variables and rebuild the configuration file unencrypted anyway.
As for implementation:
It appears in step #4 you're storing the resulting hash and the salt, it sounds like you're storing the password just reading that, but reading how the application works (and the source) it appears you're storing the resulting hash, I'd just make that clear.
As far as encryption/decryption goes, it should work from a user encrypting/decrypting that has the master key... but how is the application supposed to read the file to use it for execution? This is typically why permissions are used instead or in conjection with encryption, because if the application can decrypt it running under the user's security context, so can the user. Without much insight into how the application leverages this it's hard to tell if it'll work beyond just encrypting/decrypting files.
Maybe I'm missing something but that's my take.