diff --git a/decrypt_keystore/README.md b/decrypt_keystore/README.md new file mode 100644 index 0000000..eaaded6 --- /dev/null +++ b/decrypt_keystore/README.md @@ -0,0 +1,7 @@ +## Usage +To validate/decrypt your keystore credential, +- update `keystore.json` with your own credentials/keystore +- run: +```sh +python3 ./decrypt.py --password YOUR_PASSWORD_HERE --file keystore.json +``` \ No newline at end of file diff --git a/decrypt_keystore/decrypt.py b/decrypt_keystore/decrypt.py new file mode 100644 index 0000000..c1a8541 --- /dev/null +++ b/decrypt_keystore/decrypt.py @@ -0,0 +1,75 @@ +import json +import argparse +from binascii import unhexlify +from Crypto.Cipher import AES +from Crypto.Protocol.KDF import PBKDF2 +from Crypto.Hash import SHA256, keccak +from Crypto.Util import Counter +from hashlib import sha256 + +def diagnose_keystore(file_path: str, password: str): + with open(file_path, "r") as f: + keystore = json.load(f) + + print(f"๐Ÿ” Diagnosing keystore: {file_path}") + + for address, data in keystore.get("credentials", {}).items(): + print(f"\n๐Ÿ” Address: {address}") + try: + crypto = data["crypto"] + salt = unhexlify(crypto["kdfparams"]["salt"]) + iterations = crypto["kdfparams"]["c"] + ciphertext = unhexlify(crypto["ciphertext"]) + iv = unhexlify(crypto["cipherparams"]["iv"]) + mac_expected = unhexlify(crypto["mac"]) + + # Derive key + derived_key = PBKDF2(password, salt, dkLen=32, count=iterations, hmac_hash_module=SHA256) + + # Ethereum-style MAC + keccak_hash = keccak.new(digest_bits=256) + keccak_hash.update(derived_key[16:32] + ciphertext) + mac_computed = keccak_hash.digest() + + if mac_computed == mac_expected: + print("โœ… MAC matched using keccak256.") + else: + print("โŒ MAC failed with keccak256. Trying SHA256 fallback...") + fallback_mac = sha256(derived_key[:16] + ciphertext).digest() + if fallback_mac == mac_expected: + print("โœ… MAC matched using SHA256 fallback.") + else: + print("โŒ MAC failed with both keccak256 and SHA256.") + return + + # Decrypt + ctr = Counter.new(128, initial_value=int.from_bytes(iv, byteorder="big")) + cipher = AES.new(derived_key[:16], AES.MODE_CTR, counter=ctr) + decrypted = cipher.decrypt(ciphertext) + + print(f"\n๐Ÿงช Raw Decrypted (hex): {decrypted.hex()}") + try: + utf8 = decrypted.decode("utf-8") + print("\n๐Ÿ“œ Decrypted as UTF-8 string:") + print(utf8) + + try: + parsed = json.loads(utf8) + print("\n๐Ÿงพ Decrypted as JSON:") + print(json.dumps(parsed, indent=2)) + except Exception as e: + print("โš ๏ธ Could not parse UTF-8 string as JSON:", e) + + except Exception as e: + print("โš ๏ธ Could not decode decrypted data as UTF-8:", e) + + except Exception as e: + print(f"๐Ÿ’ฅ Error: {e}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Diagnose Waku-style keystore files.") + parser.add_argument("--file", required=True, help="Path to keystore JSON file") + parser.add_argument("--password", required=True, help="Password to decrypt") + + args = parser.parse_args() + diagnose_keystore(args.file, args.password) \ No newline at end of file diff --git a/decrypt_keystore/keystore.json b/decrypt_keystore/keystore.json new file mode 100644 index 0000000..e69de29