Created
October 5, 2018 04:51
-
-
Save alex-polosky/dbe636a007767f1ba93e37a4b079c54e to your computer and use it in GitHub Desktop.
Generating key stores for database
This file contains 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
import bcrypt | |
from cryptography.hazmat.primitives.asymmetric import padding | |
from cryptography.hazmat.primitives.asymmetric import rsa | |
from cryptography.hazmat.backends import default_backend | |
from cryptography.hazmat.primitives import hashes | |
from cryptography.hazmat.primitives import serialization | |
from cryptography.hazmat.primitives.asymmetric import utils | |
class settings(object): | |
SECURE_STORE = { | |
'PRIVATE_KEY_FILE': None, | |
'PUBLIC_KEY': None, | |
'PUBLIC_KEY_FILE': None, | |
'PUBLIC_EXPONENT': 65337, | |
'KEY_SIZE': 2048, | |
'HASH_SIZE': 256, | |
'BLOCK_SIZE': 190 | |
} | |
_BACKEND = default_backend() | |
if settings.SECURE_STORE['HASH_SIZE'] == 256: | |
_ALGORITHM = hashes.SHA256() | |
else: | |
raise ValueError('Unrecognized hash size in settings (expected one of (256,))') | |
_PAD = padding.OAEP( | |
mgf=padding.MGF1(algorithm=_ALGORITHM), | |
algorithm=_ALGORITHM, | |
label=None | |
) | |
def gen_salt(): | |
return bcrypt.gensalt() | |
def get_private_key_bytes(private_key, password=None, salt=None): | |
encryption = serialization.NoEncryption() | |
if password: | |
if salt: | |
encryption = serialization.BestAvailableEncryption(salt + password) | |
else: | |
encryption = serialization.BestAvailableEncryption(password) | |
return private_key.private_bytes( | |
serialization.Encoding.PEM, | |
serialization.PrivateFormat.PKCS8, | |
encryption | |
) | |
def get_public_key_bytes(public_key): | |
return public_key.public_bytes( | |
serialization.Encoding.PEM, | |
serialization.PublicFormat.PKCS1 | |
) | |
def gen_keys(password=None, salt=None): | |
private_key = rsa.generate_private_key( | |
public_exponent=settings.SECURE_STORE['PUBLIC_EXPONENT'], | |
key_size=settings.SECURE_STORE['KEY_SIZE'], | |
backend=_BACKEND | |
) | |
private_key_bytes = get_private_key_bytes(private_key, password, salt) | |
public_key = private_key.public_key() | |
public_key_bytes = get_public_key_bytes(public_key) | |
return private_key_bytes, public_key_bytes | |
def load_keys(prk, puk, password=None, salt=None): | |
pwd = None | |
if password: | |
if salt: | |
pwd = salt + password | |
else: | |
pwd = password | |
private_key = serialization.load_pem_private_key( | |
prk, | |
password=pwd, | |
backend=default_backend() | |
) | |
public_key = serialization.load_pem_public_key( | |
puk, | |
backend=default_backend() | |
) | |
return private_key, public_key | |
def aencrypt(message, public_key): | |
enc = b'' | |
for i in range(0, len(message), settings.SECURE_STORE['BLOCK_SIZE']): | |
enc += public_key.encrypt(message[i:i + settings.SECURE_STORE['BLOCK_SIZE']], _PAD) | |
return enc | |
def adecrypt(encrypted, private_key): | |
dec = b'' | |
for i in range(0, len(encrypted), settings.SECURE_STORE['HASH_SIZE']): | |
dec += private_key.decrypt(encrypted[i:i + settings.SECURE_STORE['HASH_SIZE']], _PAD) | |
return dec | |
def encrypt_key_store(private_key, public_key, password=None, salt=None, server_public_key=None): | |
return ( | |
salt, | |
bcrypt.hashpw(password, salt), | |
get_public_key_bytes(public_key), | |
aencrypt(get_private_key_bytes(private_key, password, salt), server_public_key) | |
if server_public_key | |
else get_private_key_bytes(private_key, password, salt) | |
) | |
def decrypt_key_store(password, salt, hashed, public_key_bytes, private_key_bytes, server_private_key=None): | |
if password: | |
if not bcrypt.checkpw(password, hashed): | |
raise ValueError('Invalid password') | |
if server_private_key: | |
prk = adecrypt(private_key_bytes, server_private_key) | |
else: | |
prk = private_key_bytes | |
return load_keys(prk, public_key_bytes, password, salt) | |
def gen_key_store(password, server_public_key=None): | |
salt = gen_salt() | |
prk, puk = load_keys(*gen_keys(password, salt), password, salt) | |
return encrypt_key_store(prk, puk, password, salt, server_public_key) | |
sprk, spuk = load_keys(*gen_keys()) | |
a = gen_key_store(b'asdf', spuk) # This is what can be safely stored in the database | |
b = decrypt_key_store(b'asdf', *a, sprk) | |
#### TODO: | |
# Load private / public keys for server from file | |
# Comments explaining everything | |
# Actual test script | |
# Eh |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For example, calling the following:
gen_key_store(b'This is my abnormally long password because I care about security hey!23456790%$234./p[][][{}{}[][]@$#(@*#%')
generates the following output, which is in the form
salt, hashed, public_key_bytes, private_key_encrypted_by_password_that_can_be_encrypted_by_server_bytes
: