- Backup from TrueNAS SCALE in .tar format, extracted
- Must have password seed backed up, named as pwenc_secret
- SQLite database from .tar should be named freenas-v1.db
- Python 3.14+
- pycryptodome >=3.23.0, <4.0.0
| #!/usr/bin/env python3 | |
| import base64 | |
| import sys | |
| import sqlite3 | |
| from Crypto.Cipher import AES | |
| from Crypto.Util import Counter | |
| def main(argv): | |
| aes_key = read_aes_key() | |
| keys = load_encrypted_keys() | |
| for disk_serial, encrypted_key in keys.items(): | |
| decrypted = decrypt(encrypted_key, aes_key) | |
| print(disk_serial) | |
| print(decrypted) | |
| print() | |
| return 0 | |
| def read_aes_key(): | |
| with open('pwenc_secret', 'rb') as f: | |
| return f.read() | |
| def load_encrypted_keys(): | |
| d = {} | |
| with sqlite3.connect('freenas-v1.db') as conn: | |
| for row in conn.execute('SELECT adv_sed_passwd FROM system_advanced'): | |
| d["defaultsedpassword"] = row[0] | |
| for row in conn.execute('SELECT disk_serial, disk_passwd FROM storage_disk'): | |
| d[row[0]] = row[1] | |
| return d | |
| def decrypt(encrypted, key): | |
| if not encrypted: | |
| return '' | |
| encrypted = base64.b64decode(encrypted) | |
| nonce = encrypted[:8] | |
| encrypted = encrypted[8:] | |
| cipher = AES.new(key, AES.MODE_CTR, counter=Counter.new(64, prefix=nonce)) | |
| return cipher.decrypt(encrypted).rstrip(b'{').decode('utf8') | |
| if __name__ == '__main__': | |
| sys.exit(main(sys.argv)) |