Who Needs a Tweak? Meet Full Disk Encryption

XEX-based tweaked-codebook mode with ciphertext stealing

Who Needs a Tweak? Meet Full Disk Encryption

XEX-based tweaked-codebook mode with ciphertext stealing

I was discussing with an industry professional about the storing of files on an encrypted USB device, and I said, “But how are the files encrypted on the device?”, “I don’t know”, “Is it AES, and what’s the key size?”, “I don’t know”. I thus sometimes worry about the depth of knowledge applied within some areas of cybersecurity. To me, this is similar to an architect not actually knowing the strength of the bricks that they are designing with. So let’s have a look at what actually happens with full-disk encryption.

Before we start, it is important to know that AES encryption deals with 128-bit (16 bytes) encryption blocks, whereas disks typically deal with 512-byte segments. We must thus fit 32 AES blocks into our disk segments (Figure 1). Sometimes we will have empty blocks, so we must fill these with “ciphertext stealing” blocks.

With non-full disk encryption, we typically create an AES key and encrypt the file. The encryption key is typically generated from a passphrase, and uses a key derivation function (KDF). We can also create a key pair (a public key and a private key), and then encrypt the key with our public key, and then decrypt it with our private key. With this we are thus encrypting at a file level, and which will be fairly inefficient, as we need to read the whole file in at a time. With full disk encryption, we typically read from sectors on the disk, and in a random manner.

The smallest addressable element of a disk is a block — and which is typically around 512 bytes — and these are arranged into sectors:


Disk /dev/sda: 640.1 GB, 640135028736 bytes
255 heads, 63 sectors/track, 77825 cylinders, total 1250263728 sectors
Units = sectors of 1 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00064a1a

Device Boot Start End Blocks Id System
/dev/sda1 63 2040254 1020096 83 Linux
/dev/sda2 2040255 22523129 10241437+ 82 Linux swap / Solaris
/dev/sda4 22523191 1250258624 613867717 5 Extended

Two of the most common modes of block encryption are Electronic Code Book (EBC) and Cipher Block Chaining (CBC). With ECB we basically take blocks of 128 bits, and then cipher these:

Unfortunately the same input in the blocks will lead to the same cipher output, and leaves us open to a range of cipher attacks. With CBC the blocks are chained together, and where the output of one block feeds into another one. The IV makes sure that for the same input plaintext, we will have a different ciphertext. We initially take an IV and then EX-OR it with the first plaintext block, and then encrypt this. This block is then EX-OR’ed with the next plain text block, and so it continues:

Within a disk system, this is a problem, as we need to be able to randomly access sectors, without having to read all the other sectors. XTS (XEX-based tweaked-codebook mode with ciphertext stealing) is a fairly common method which overcomes these problems. For this it uses a “tweak”, and where we take an encryption key, and encrypt the sector number, and use this to X-OR the data within the sector.

We first define two keys (K1 and K2). If we use 128-bit AES, then we generate two keys which are 128 bits long. Next we define an input block (P) and a sector number (i) and a block number (j), and create a tweak (X ):

Note, ⊗ is a multiplication (within a finite field of 127). The ciphertext (C) for each block is then:

With ⊕, we have an EX-OR operation. To decrypt, we do the opposite:

The plaintext (P) for each block is then:

In this way, we just need the encryption key, and the sector number, in order to encrypt and decrypt. The tweak acts as an IV. This process is illustrated by Figure 2.

Figure 2: Reference: [here]

Overall a sector of a size of 512 bytes will store 32, 16-byte AES-blocks (a block in AES is 128 bits long). In this way, for a single sector, we call the encrypt/decrypt method 32 times using the same i value, but different j values (0 to 31).

Most fully encrypted disks now use the AES-XTS block cipher mode, and which is defined in IEEE Standard 1619. It is now implemented in many USB encrypted devices, and with Microsoft Bitlocker for Windows 10, TrueCrypt, VeraCrypt, OpenSSL, and Mac OSX FileVault.

The following is some sample Go code [here]:

package main
import (
"golang.org/x/crypto/xts"
"crypto/aes"
"encoding/hex"
"crypto/sha256"
"bytes"
"fmt"
"os"
"strconv"
)
func PKCS5Padding(ciphertext []byte, bSize int) []byte {
padding := bSize - len(ciphertext)%bSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func PKCS5Trimming(encrypt []byte) []byte {
padding := encrypt[len(encrypt)-1]
return encrypt[:len(encrypt)-int(padding)]
}
func getSHA1(s string) []byte {
        hasher := sha256.New()
hasher.Write([]byte(s))
key:=hasher.Sum(nil)
return key
}

func main() {
var sector uint64
    password:="test"

    message:="hello"
sector=100

    argCount := len(os.Args[1:])
    if (argCount>0) {message = string(os.Args[1])}
if (argCount>1) {password = string(os.Args[2])}
if (argCount>2) {sector,_ = strconv.ParseUint(os.Args[3],10,64)}
    key:=getSHA1(password)

    c, _ := xts.NewCipher(aes.NewCipher,key )

        fmt.Printf("Message:\t%s\nPassword:\t%s\nSector:\t\t%d\n\n\nKey (hex):\t%s\n",message,password,sector,hex.EncodeToString(key))

    plaintext := PKCS5Padding([]byte(message),16)

    ciphertext := make([]byte, len(plaintext))
c.Encrypt(ciphertext, plaintext, sector)
        fmt.Printf("Cipher:\t\t%s\n",hex.EncodeToString(ciphertext))
    decrypted := make([]byte, len(ciphertext))
c.Decrypt(decrypted, ciphertext, sector)
fmt.Printf("Decrypted:\t%s",string(PKCS5Trimming(decrypted)))
}

A sample run is [here]:

Message:    Hello
Password: qwerty
Sector: 202

Key (hex):  65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5
Cipher: 99beddc752083a8b190f3fdfb8b09872
Decrypted: Hello

Conclusions

If you are a Cybersecurity professional, and you give advice on using encrypted USB sticks, or advise on using Microsoft BitLocker for full disk encryption, make sure you actually know how it works. Within BitLocker, the Tweak keys are protected by the Volume Master Key (VMK):

XTS is not perfect, but it is the de-facto full disk encryption mode. You should always try and encrypt your files at their source, and not rely on full disk encryption.