For Disk Encryption, Why Doesn’t The Disk Re-encrypt With The New Key When I Change My Password?

Those who innovate often do not take things for granted and often probe things to understand what actually happens under the hood. So…

Photo by 铮 夏 on Unsplash

For Disk Encryption, Why Doesn’t The Disk Re-encrypt With The New Key When I Change My Password?

Those who innovate often do not take things for granted and often probe things to understand what actually happens under the hood. So, let’s pose a simple question …

In TrueCrypt (now known as VeraCrypt) we have disk encryption, why when I change my password for access does it not actually change the encryption key for the disk encryption, or does it?

TrueCrypt creates an encrypted volume, and where the user just has to reveal their password in order to access it. In order to convert a password into an encryption key, we often use a key derivation function (KDF) such as PBKDF2 or bcrypt, so why is it not the case that we have to re-encrypt the whole of the encrypted disk with the new symmetric key? Well, TrueCrypt uses two keys: a data encryption key and a key-encryption key (KEK). The data encryption key stays the same for every password change and is used to perform the encryption on the data on the disk. Whereas the KEK protects the data encryption key.

In the following, we have a data encryption key, and which will not change (unless there is a reset of the disk). We now need a way to protect this key for someone examining the disk. For this Bob uses a password and a salt value (Figure 1), and generates the master key using PBKDF2. The PBKDF2 is a robust method against hash cracking and does this by implementing a number of hashing rounds. The KEK is then used to wrap the key so that it can be stored on the disk. Along with this, we need the salt value used for the key generation. When the data encryption key is required, we feed in Bob’s password and the salt value and unwrap the key. The unwrapped key should be the key used to encrypt the disk.

Figure 1: Using key wrapping to protect a key

The KEK is then the master key, and can be changed at any time and generated by the user's password. The wrapped version of the data encryption key is then protected when it is stored on the system, and where the user can export the wrapped key as a backup, and then regenerate the KEK using their password.

The following is the code [here]:

import binascii
import struct
from Crypto.Cipher import AES
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256
from Crypto.Random import get_random_bytes
import sys
QUAD = struct.Struct('>Q')
# code from https://github.com/kurtbrose/aes_keywrap/blob/master/aes_keywrap.py
def aes_unwrap_key(kek, wrapped):
n = len(wrapped)//8 - 1
#NOTE: R[0] is never accessed, left in for consistency with RFC indices
R = [None]+[wrapped[i*8:i*8+8] for i in range(1, n+1)]
A = QUAD.unpack(wrapped[:8])[0]
decrypt = AES.new(kek, AES.MODE_ECB).decrypt
for j in range(5,-1,-1): #counting down
for i in range(n, 0, -1): #(n, n-1, ..., 1)
ciphertext = QUAD.pack(A^(n*j+i)) + R[i]
B = decrypt(ciphertext)
A = QUAD.unpack(B[:8])[0]
R[i] = B[8:]
return b"".join(R[1:]), A

# code from https://github.com/kurtbrose/aes_keywrap/blob/master/aes_keywrap.py
def aes_wrap_key(kek, plaintext, iv=0xa6a6a6a6a6a6a6a6):
n = len(plaintext)//8
R = [None]+[plaintext[i*8:i*8+8] for i in range(0, n)]
A = iv
encrypt = AES.new(kek, AES.MODE_ECB).encrypt
for j in range(6):
for i in range(1, n+1):
B = encrypt(QUAD.pack(A) + R[i])
A = QUAD.unpack(B[:8])[0] ^ (n*j + i)
R[i] = B[8:]
return QUAD.pack(A) + b"".join(R[1:])

kek="000102030405060708090A0B0C0D0E0F"
key="00112233445566778899AABBCCDDEEFF"
password="qwerty"
if (len(sys.argv)>1):
password=str(sys.argv[1])
if (len(sys.argv)>2):
key=str(sys.argv[2])
KEY = binascii.unhexlify(key)
salt = get_random_bytes(16)
KEK = PBKDF2(password, salt, 32, count=1000, hmac_hash_module=SHA256)
wrapped=aes_wrap_key(KEK,KEY)
rtn,iv=aes_unwrap_key(KEK,wrapped)
print (f"Password: {password}, Salt: {binascii.hexlify(salt)}")
print ("\nKEK: ",binascii.hexlify(KEK))
print ("Key: ",binascii.hexlify(KEY))
print ("\nWrapped Key: ",binascii.hexlify(wrapped))
print ("Unwrapped key: ",binascii.hexlify(rtn))

A sample run is [here]:

Password: qwerty123, Salt: b'df338bcdadb6bc1c473a46699d864f0a'
KEK:  b'd7788ae4ac32e1a941fa3a801e4fd3939e0e1532165854eecd218af5d6c16526'
Key: b'00112233445566778899aabbccddeeff'
Wrapped Key:  b'62cff9afe2b10a1802b7a361c2c292b3fdde6217063305a6'
Unwrapped key: b'00112233445566778899aabbccddeeff'