Files
nutshell/cashu/core/crypto/aes.py
callebtc e02e4bbf49 mint: add seed decrypt (#403)
* mint: add seed decrypt

* add mint seed decryoption and migration tool
2024-02-05 16:22:53 +01:00

66 lines
2.1 KiB
Python

import base64
from hashlib import sha256
from Cryptodome import Random
from Cryptodome.Cipher import AES
BLOCK_SIZE = 16
class AESCipher:
"""This class is compatible with crypto-js/aes.js
Encrypt and decrypt in Javascript using:
import AES from "crypto-js/aes.js";
import Utf8 from "crypto-js/enc-utf8.js";
AES.encrypt(decrypted, password).toString()
AES.decrypt(encrypted, password).toString(Utf8);
"""
def __init__(self, key: str, description=""):
self.key: str = key
self.description = description + " "
def pad(self, data):
length = BLOCK_SIZE - (len(data) % BLOCK_SIZE)
return data + (chr(length) * length).encode()
def unpad(self, data):
return data[: -(data[-1] if isinstance(data[-1], int) else ord(data[-1]))]
def bytes_to_key(self, data, salt, output=48):
# extended from https://gist.github.com/gsakkis/4546068
assert len(salt) == 8, len(salt)
data += salt
key = sha256(data).digest()
final_key = key
while len(final_key) < output:
key = sha256(key + data).digest()
final_key += key
return final_key[:output]
def decrypt(self, encrypted: str) -> str: # type: ignore
"""Decrypts a string using AES-256-CBC."""
encrypted = base64.urlsafe_b64decode(encrypted) # type: ignore
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = self.bytes_to_key(self.key.encode(), salt, 32 + 16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
try:
return self.unpad(aes.decrypt(encrypted[16:])).decode() # type: ignore
except UnicodeDecodeError:
raise ValueError("Wrong passphrase")
def encrypt(self, message: bytes) -> str:
salt = Random.new().read(8)
key_iv = self.bytes_to_key(self.key.encode(), salt, 32 + 16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
return base64.urlsafe_b64encode(
b"Salted__" + salt + aes.encrypt(self.pad(message))
).decode()