Add support for cipher specific key handling using plugins

Add support for cipher specific key handling using plugins

This merge request adds support for cipher specific key handling to cryptsetup. This enables cryptsetup to work with arbitrary key formats, without changing the on disk format of the LUKS headers.

Most ciphers (e.g. AES) used today use simple keys. These simple keys consist of a string of random bits that forms the key. However, there are ciphers that use more complex keys. Such complex keys do not just consist of the effective key, but the key material is encoded or wrapped in some way, and may contain additional information besides the effective key. Such a complex key shall be called 'key token'.

Examples of key tokens:

  • a structure that describes keys consisting of multiple parts, described by some syntax (often ASN.1 sysntax is used in standards) and encoded using encoding rules (e.g. BER or DER)
  • a key wrapped by another key
  • a key together with additional information (length, usage attributes, random values, padding)
  • PKCS#11 key objects comprise a key value and many attributes
  • possibly bound together by a MAC or signature
  • possibly wrapped
  • a key label referencing an entry in a key ring/repository

The simplest form of a key token is a simple key, e.g. an AES key. That way, the keys used with LUKS/cryptsetup today are also key tokens.

Key tokens often need some special handling compared to simple keys:

1.) The size of a key token is typically different from the (cryptographic) size of its effective key.

For example:

  • The size of the wrapped key is (a multiple of) the "block size" of the wrapping key.
  • An AES192 key wrapped by an AES256 key is 256 bits in size.
  • An AES256 key wrapped by an RSA 4K key is 4096 bits in size.
  • A wrapped key might contain additional attributes, for example, to restrict the key usage of keys. These meta-data requires additional space in the key token.
  • The size of key labels is completely independent of the size of the referenced key.

2.) Not every random byte pattern is a valid key token (not even a valid effective key)

For example:

  • RSA keys must be constructed using primes.
  • Some (symmetric) ciphers allow for weak keys that must not be used (e.g. DES, IDEA, RC4, blowfish).
  • If a wrapped key contains a padded effective key, then that padding restricts the viable patterns of the wrapped keys.
  • Similarly if specific attributes are combined/encrypted/MAC-ed with the effective key.
  • Key labels may be restricted to a specific character set.
  • Even some simple symmetric keys have constraints, e.g. parity bits included for DES, 3DES.

3.) An effective key may be represented by multiple key tokens

For example:

  • Keys wrapped by a RSA key (both in OEAP and PKCS#1 v1.5 format) contain random data that is encrypted together with the key to be wrapped.
  • Hence no two wrappings of the same key generated by the same RSA key will be the same.
  • The same effective key may be wrapped using different wrapping keys.
  • Different key labels could reference the same or equivalent keys in a key ring.

Consequences when dealing with key tokens:

1.) Size of a key token:

  • You can not easily determine the size of a key token from its cryptographic size.
  • You need a key token type specific function that 'knows' how to determine the key token size based on the cryptographic size (and cipher mode).
  • You can not easily determine the cryptographic size of an effective key contained in a key token.
  • You need a key token type specific function that 'knows' how to determine the cryptographic size of the effective key contained in a key token.

2.) Key generation

  • You can not simply generate a key token by random.
  • Instead, keys must be generated using a key token type specific function.
  • Alternatively a key token specific function can generate a key token from a given effective key (e.g. derived from a password or passphrase).
  • HSM-specific key tokens may have to be generated inside the HSM.

3.) Generating a digest of a key token to identify the effective key

  • Since the same effective key may be represented by different key tokens, you can not simply build a digest from the key token.
  • You would get different digests, although it is the same effective key.
  • You must build the digest from a "key identification pattern" that is derived from the effective key contained in the key token.

In order to manage disks using ciphers that operate on key tokens we propose to extend cryptsetup with the following cipher plug-in concept:

Cipher Plugin concept:

To add support for key tokens in cryptsetup, we choose a plugin concept, that is very similar to the already existing plugin concepts in LUKS2, e.g. digest plugins, token plugins, etc.

When finding an unknown cipher (that may operate on key tokens), the code tries to load a plugin (shared library) who's name is derived from the name of the cipher used. When a plugin is found for that cipher, then it is registered as cipher plugin. The plugin implements the key token specific functions for that cipher (see crypto_cipher_plugin.h), such as generating keys tokens for this cipher, generating an unique identifier for a key token that can be used to build a volume key digest (for the LUKS key slot), and providing information about the effective key contained in a key token.

The plugin extensions separates cipher or platform-specific code portions from the cryptsetup code. Plugins need therefore not be part of the cryptsetup project. These could be maintained and packaged by platform specific projects. Yet an externally maintained plugin shall be able to be used with cryptsetup and the LUKS format as long as it uses the cryptsetup cipher plugin API.

Note: A cipher used with a plugin must also be available as in-kernel cipher. It must be listed in /proc/crypto, and must be usable via the AF_ALG interface from user space, as on any ciphers currently supported by cryptsetup today.

For the simple ciphers (e.g. AES), no plugin is needed. For all ciphers that do not provide a plugin, a (trivial) default implementation is used, assuming that the key token is an effective key (simple key). That way any existing LUKS volumes continue to work unchanged.