#!/usr/bin/python3
# ====================================================================
# password vault 02 - vault encrypt/decrypt
# ====================================================================
# stackoverflow.com/questions/61607367/how-to-encrypt-json-in-python
# --------------------------------------------------------------------
# pip install cryptography
# or on windows:
# python -m pip install cryptography
# --------------------------------------------------------------------
# https://pypi.org/project/cryptography/
#---------------------------------------------------------------------
# Fernet is ideal for encrypting data that easily fits in memory.
# This means that the complete message contents must be available
# in memory, making Fernet generally unsuitable for very large.
# ====================================================================
import sys
import json
from io import StringIO
from cryptography.fernet import Fernet
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
# -------------------------------------------------------------------
# ---- write JSON file
# -------------------------------------------------------------------
def output_json_file(outfile,jdata):
with open(outfile,'w') as ofile:
ofile.write(json.dumps(jdata))
# -------------------------------------------------------------------
# ---- read JSON file
# -------------------------------------------------------------------
def input_json_file(infile):
with open(infile,'r') as ifile:
jdata = json.load(ifile)
return jdata
# -------------------------------------------------------------------
# ---- write encrypted byte data
# -------------------------------------------------------------------
def output_encrypted_json(outfile,edata):
##print('----------')
##print(f'outfile = {outfile}')
with open(outfile,'wb') as ofile:
l = ofile.write(edata)
##print(f'encrypted data len = {l} bytes')
return
# -------------------------------------------------------------------
# ---- read encrypted byte data
# -------------------------------------------------------------------
def input_encrypted_json(infile):
##print('----------')
##print(f'infile = {infile}')
with open(infile,'rb') as ifile:
edata = ifile.read()
##print(f'edata = {len(edata)} bytes')
return edata
#---------------------------------------------------------------------
# ---- write key to a file
# --------------------------------------------------------------------
def output_key(keyfile,key):
with open(keyfile,'wb') as ofile:
ofile.write(key)
return
# -------------------------------------------------------------------
# ---- read key from file
# -------------------------------------------------------------------
def input_key(keyfile):
with open(keyfile,'rb') as ifile:
key = ifile.read()
return key
# --------------------------------------------------------------------
#---- generates key from the password
# --------------------------------------------------------------------
def key_from_password(password):
bpass = password.encode('utf-8')
salt = b'password salt'
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend())
key = base64.urlsafe_b64encode(kdf.derive(bpass))
return key
# ---------------------------------------------------------------------
# ---- encrypt dictionary
# ---------------------------------------------------------------------
def encrypt_dictionary(oldict,password,outfile=None):
##print('----------')
##print(f'oldict = {oldict}')
# ---- convert dictionary to json
jdata = json.dumps(oldict)
##print('----------')
##print(f'jdata type = {type(jdata)}')
##print(f'jdata = {jdata}')
# ---- convert json to bytes
bdata = jdata.encode('utf=-8')
##print('----------')
##print(f'bdata type = {type(bdata)}')
##print(f'bdata = {bdata}')
# ---- get password key
key = key_from_password(password)
##print('----------')
##print(f'password = {password}')
##print(f'key = {key}')
#---- encrypt byte data
fernet = Fernet(key)
edata = fernet.encrypt(bdata)
##print('----------')
##print(f'edata type = {type(edata)}')
##print(f'edata = {edata}')
# ---- write encrypted json
output_encrypted_json(outfile,edata)
return
# --------------------------------------------------------------------
# ---- decrypt dictionary
# --------------------------------------------------------------------
def decrypt_dictionary(password,infile):
# ---- read encrypted json
edata = input_encrypted_json(infile)
# ---- get key based on password
key = key_from_password(password)
##print('----------')
##print(f'password = {password}')
##print(f'key = {key}')
#---- decrypt edata using the key
fernet = Fernet(key)
##bdata = fernet.decrypt(edata)
##print('----------')
##print(f'bdata type = {type(bdata)}')
##print(f'bdata = {bdata}')
#---- convert byte data to a json
jdata = bdata.decode('utf-8')
##print('----------')
##print(f'jdata type = {type(jdata)}')
##print(f'jdata = {jdata}')
#---- convert to dictionary
newdict = json.loads(jdata)
##print('----------')
##print(f'newdict type = {type(newdict)}')
##print(f'newdict = {newdict}')
return newdict
# --------------------------------------------------------------------
# ---- main
# --------------------------------------------------------------------
if __name__ == '__main__':
err = False
password = 'happy days'
filename = 'password_vault.dat'
# ---- test data
oldict = {
"dbname" : "test db",
"update" : "march 9, 1944",
"entries":
[
{ "color":"red", "value":0xf00 },
{ "color":"green", "value":0x0f0 },
{ "color":"blue", "value":0x00f },
{ "color":"cyan", "value":0x0ff },
{ "color":"magenta","value":0xf0f },
{ "color":"yellow", "value":0xff0 },
{ "color":"black", "value":0x000 },
] }
print()
print('====encrype dictionary===================================')
print()
try:
encrypt_dictionary(oldict,password,filename)
except:
print('Encryption error')
err = True
print()
print('====decrypt dictionary===================================')
print()
password = 'bad days'
olddict = {}
try:
newdict = decrypt_dictionary(password,filename)
##except BaseException as ex:
## print(f'Exception Name: {type(ex).__name__}')
## print(f'Exception Desc: {ex}')
## err = True
except BaseException as ex:
print('Decryption error')
print(f'Exception Name: {type(ex).__name__}')
print('probably a bad password')
err = True
#---- accessing new dictionary
if not err:
print()
for e in newdict['entries']:
print(f'entry = {e}')
print(f'color = {e["color"]} value = {e["value"]:#06x}')
print()
print('the end')