encrypted-tables/encrypt_json.py
2025-04-19 22:35:35 -07:00

98 lines
3.4 KiB
Python

import os
import json
import base64
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
# https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet
rescue_msg = {
"readme": "The salt (bytes) and the encrypted object (bytes) are both stored in base64 format.\
The key, 32 bytes long, is derived using PBKDF2 using the salt and your password.\
Decrypt the object using the AES-CBC algorithm with the key.",
"code_to_decrypt": """
import base64
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
def s2b(s:str)->bytes:
return base64.decodebytes(s.encode('utf-8'))
salt = s2b(save_obj["salt"])
obj_encrypted = s2b(save_obj["obj"])
password = input("password:").encode('utf-8')
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
obj_decrypted = Fernet(key).decrypt(obj_encrypted).decode('utf-8')
print(obj_decrypted)
"""
}
def get_fernet(password:bytes, salt:bytes):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=480000,
)
key = base64.urlsafe_b64encode(kdf.derive(password))
return Fernet(key)
def b2s(b:bytes)->str:
return base64.encodebytes(b).decode('utf-8')
def s2b(s:str)->bytes:
# s must be constructed from b2s
return base64.decodebytes(s.encode('utf-8'))
def save_obj(obj, password:bytes, salt:bytes) -> str:
f = get_fernet(password, salt)
save_obj = dict()
save_obj["salt"] = b2s(salt)
obj_encrypted = f.encrypt(json.dumps(obj).encode('utf-8'))
save_obj["obj"] = b2s(obj_encrypted)
save_obj["rescue"] = rescue_msg
return json.dumps(save_obj,indent=4)
def load_obj(s, password):
save_obj = json.loads(s)
salt = s2b(save_obj["salt"])
obj_encrypted = s2b(save_obj["obj"])
f = get_fernet(password, salt)
try:
obj_decrypted = f.decrypt(obj_encrypted)
except InvalidToken:
print("Invalid Password")
exit(-1)
obj = json.loads(obj_decrypted.decode('utf-8'))
return obj
def perform_encrypt(infile, outfile):
password = input("password:").encode('utf-8')
salt = os.urandom(16)
obj = json.load(open(infile,'r'))
d = save_obj(obj, password, salt)
f = open(outfile,'w'); f.write(d); f.close()
def perform_decrypt(infile, outfile):
f = open(infile,'r'); d=f.read(); f.close()
password = input("password:").encode('utf-8')
obj = load_obj(d, password)
json.dump(obj, open(outfile,'w'), indent=4)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('mode', choices=['e', 'd'], help="e for encrypt, d for decrypt")
parser.add_argument('infile')
parser.add_argument('outfile')
args = parser.parse_args()
if args.mode == 'e':
perform_encrypt(args.infile, args.outfile)
elif args.mode == 'd':
perform_decrypt(args.infile, args.outfile)