Wednesday, February 09, 2022

TrueNAS Scale & ZFS Wrapping Key

 I've been playing with TrueNAS scale recently and while the BUI allows you to export/download ZFS wrapping key I wanted to know how to get the key manually. After a quick look at the code I found that the key is stored in sqlite db kept on root file system.

root@truenas[~]# sqlite3 /data/freenas-v1.db
SQLite version 3.34.1 2021-01-20 14:10:07
Enter ".help" for usage hints.
sqlite> select * from storage_encrypteddataset;
The key itself is encrypted so you need to decrypt it first before it can be used with ZFS. A simple python script to do it attached below.
root@truenas[~]# ./
dataset: backup
  key: 16f7677b514ef39bc162312274c76da24221ecc5a2f01e6ba0bhfeec054d9162
(both the encrypted and decrypted keys above have been modified for this blog entry)
root@truenas[~]# cat

# based on /usr/lib/migrate113/freenasUI/system/migrations/

import sys
import base64
from Cryptodome.Cipher import AES
import sqlite3

PWENC_FILE_SECRET = '/data/pwenc_secret'

def pwenc_get_secret():
    with open(PWENC_FILE_SECRET, 'rb') as f:
        secret =
    return secret

def pwenc_decrypt(encrypted=None):
    if not encrypted:
        return ""
    from Cryptodome.Util import Counter
    encrypted = base64.b64decode(encrypted)
    nonce = encrypted[:8]
    encrypted = encrypted[8:]
    cipher =
        AES.MODE_CTR,, prefix=nonce),
    return cipher.decrypt(encrypted).rstrip(PWENC_PADDING).decode('utf8')

if len(sys.argv) == 2:

dbcon = sqlite3.connect('/data/freenas-v1.db')
dbcur = dbcon.cursor()
for row in dbcur.execute('select * from storage_encrypteddataset'):
    ds_id, ds_name, ds_enc_key, kmip_enc_key = row
    #print(ds_id, ds_name, ds_enc_key, pwenc_decrypt(ds_enc_key))
    print(f'dataset: {ds_name}\n  key: {pwenc_decrypt(ds_enc_key)}\n')