[RFC PATCH] IV Generation algorithms for dm-crypt

From: Binoy Jayan
Date: Mon Nov 21 2016 - 05:12:33 EST

GENIV Template cipher

Currently, the iv generation algorithms are implemented in dm-crypt.c. The goal
is to move these algorithms from the dm layer to the kernel crypto layer by
implementing them as template ciphers so they can be used in relation with
algorithms like aes, and with multiple modes like cbc, ecb etc. As part of this
patchset, the iv-generation code is moved from the dm layer to the crypto layer.
The dm-layer can later be optimized to encrypt larger block sizes in a single
call to the crypto engine.

One challenge in doing so is with the 'essiv' which creates the IV by hashing
the 512-byte sector number. This infact limits the block sizes to 512 bytes.
A way to get around this problem has to be explored. Another thing to note is
that the algorithms shares its context data structures (cipher context and
request context) with the callee, i.e. dm-crypt here. Not sure if this coupling
is accepted. If not, this has to be decoupled. A new crypto api
'crypto_skcipher_set_ctx' defined in 'include/crypto/skcipher.h' which was
initially written for addressing this is not used now. But even if it is used,
the data structure definition would still be shared.

The following ASCII art decomposes the kernel crypto API layers when using the
skcipher with the automated IV generation. The shown example is used by the
DM layer. For other use cases of cbc(aes), the ASCII art applies as well, but
the caller may not use the same with a separate IV generator. In this case, the
caller must generate the IV. The depicted example decomposes <ivgen>(cbc(aes))
based on the generic C implementations (geniv.c, cbc.c and aes-generic.c).
The generic implementation depicts the dependency between the templates ciphers
used in implementing geniv using the kernel crypto API.
Here, <geniv> indicates one of the following algorithms:

1. plain
2. plain64
3. essiv
4. benbi
5. null
6. lmk
7. tcw

It is possible that some streamlined cipher implementations (like AES-NI)
provide implementations merging aspects which in the view of the kernel crypto
API cannot be decomposed into layers any more. Each block in the following
ASCII art is an independent cipher instance obtained from the kernel crypto
API. Each block is accessed by the caller or by other blocks using the API
functions defined by the kernel crypto API for the cipher implementation type.
The blocks below indicate the cipher type as well as the specific logic
implemented in the cipher.

The ASCII art picture also indicates the call structure, i.e. who calls which
component. The arrows point to the invoked block where the caller uses the API
applicable to the cipher type specified for the block. For the purpose of
illustration, here we take the example of the aes mode 'cbc'. However, the IV
generation algorithm could be used with other aes modes like ecb as well.

Geniv implementation

NB: The ASCII art below is best viewed in a fixed-width font.

crypt_convert_block() (DM Layer)
| (1)
+------------+ +-----------+ +-----------+ +-----------+
| | | | | | (2) | |
| skcipher | | skcipher | | skcipher |----+ | skcipher | Blocks for
| (plain/64) | | (benbi) | | (essiv) | | | (null) | lmk, tcw
+------------+ +-----------+ +-----------+ | +-----------+
| | | v |
| (3) | (3) (3) | +-----------+ |
| | | | | |
| | | | ahash | | (3)
| | | | | |
| | | +-----------+ |
| | v | (Crypto API
| | +-----------+ | Layer)
| v | | |
+------------------------> | skcipher | <-------------+
| (cbc) |
+-----------+ (AES Mode Template cipher)
| (4)
| |
| cipher | (Base generic-AES cipher)
| (aes) |

The following call sequence is applicable when the DM layer triggers an
encryption operation with the crypt_convert_block() function. During
configuration, the administrator sets up the use of <geniv>(cbc(aes)) as the
template cipher. 'geniv' can be one among plain, plain64, essiv, benbi, null,
lmk, or tcw which are all implemented as seperate templates.
The following are the template ciphers implemented as part of 'geniv.c'

1. plain(cbc(aes))
2. plain64(cbc(aes))
3. essiv(cbc(aes))
4. benbi(cbc(aes))
5. null(cbc(aes))
6. lmk(cbc(aes))
7. tcw(cbc(aes))

The following call sequence is now depicted in the ASCII art above:

1. crypt_convert_block invokes crypto_skcipher_encrypt() to trigger encryption
operation of a single block (i.e. sector) with the IV same as the sector no.
For example, with essiv, the IV generation implementation is registered with
a call to 'crypto_register_template(&crypto_essiv_tmpl)'

2. During instantiation of the 'geniv' handle, the IV generation algorithm is
instantiated. For the purpose of illustration, we take the example of essiv.
In this case, the ahash cipher is instantiated to calculate the hash of the
sector to generate the IV.

3. Now, geniv uses the skcipher api calls to invoke the associated cipher. In
our case, during the instantiation of geniv, the cipher handle for cbc is
provided to geniv. The geniv skcipher type implementation now invokes the
skcipher api with the instantiated cbc(aes) cipher handle. During the
instantiation of the cbc(aes) cipher, the cipher type generic-aes is also
instantiated. That means that the SKCIPHER implementation of cbc(aes) only
implements the Cipher-block chaining mode. After performing block chaining
operation, the cipher implementation of aes is invoked. The skcipher of
cbc(aes) now invokes the cipher api with the aes cipher handle to encrypt
one block.


1. Changes to testmgr.c
2. How to encrypt blocks bigger than 512 bytes while using essiv?
As sectors are tied to IV in case of 'essiv'.
Will changing block size make it backward incompatible
and with other platforms (like windows) which support LUKS volumes.
3. Did not move the key management code from dm-crypt to cryto layer
when keycount > 1 as multiple ciphers are instantiated from dm layer
with each cipher instance is allotted a part of the key provided.

Test procedure

The algorithms are tested using 'cryptsetup' utility to create LUKS
compatible volumes on Qemu.

NB: '/dev/sdb' is a second disk volume (configured in qemu)

# One time setup - Format the device compatible with LUKS.
# Choose one of the following IV generation alorithms at a time
cryptsetup -y -c aes-cbc-plain -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes-cbc-plain64 -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes-cbc-essiv:sha256 -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes-cbc-benbi -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes-cbc-null -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes-cbc-lmk -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes-cbc-tcw -s 256 --hash sha256 luksFormat /dev/sdb

# With a keycount
cryptsetup -y -c aes:2-cbc-plain -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes:2-cbc-plain64 -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes:2-cbc-essiv:sha256 -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes:2-cbc-null -s 256 --hash sha256 luksFormat /dev/sdb
cryptsetup -y -c aes:2-cbc-lmk -s 256 --hash sha256 luksFormat /dev/sdb

# Add additional key - optional
cryptsetup luksAddKey /dev/sdb

# The above lists only a limited number of tests with the aes cipher.
# The IV generation algorithms may also be tested with other ciphers as well.

cryptsetup luksDump --dump-master-key /dev/sdb

# create a luks volume and open the device
cryptsetup luksOpen /dev/sdb crypt_fun
dmsetup table --showkeys

# Write some data to the device
cat data.txt > /dev/mapper/crypt_fun

# Read 100 bytes back
dd if=/dev/mapper/crypt_fun of=out.txt bs=100 count=1
cat out.txt

mkfs.ext4 -j /dev/mapper/crypt_fun

# Mount if fs creation succeeds
mount -t ext4 /dev/mapper/crypt_fun /mnt

<-- Use the encrypted file system -->

umount /mnt
cryptsetup luksClose crypt_fun
cryptsetup luksRemoveKey /dev/sdb

This seems to work well. The file system mounts successfully and the files
written to in the file system remain persistent across reboots.

Binoy Jayan (1):
crypto: Add IV generation algorithms

crypto/Kconfig | 8 +
crypto/Makefile | 1 +
crypto/geniv.c | 1113 +++++++++++++++++++++++++++++++++++++++++++++
drivers/md/dm-crypt.c | 725 +++--------------------------
include/crypto/geniv.h | 109 +++++
include/crypto/skcipher.h | 17 +
6 files changed, 1309 insertions(+), 664 deletions(-)
create mode 100644 crypto/geniv.c
create mode 100644 include/crypto/geniv.h

Binoy Jayan