Menu

CryptoOps

Martin Hofmann

KeeChipCard plugin for KeePass

Summary of Cryptographic Operations

Symbols

Name Type Description
d BYTE[] Data on the card (up to, say 256 bytes).
K BYTE[32] 256-bit symmetric encryption/decryption key for plugin's use.
EK BYTE[]BYTE[] Encryption function with key K .
DK BYTE[]BYTE[] Decryption function with key K .
H BYTE[]BYTE[32] SHA-256 hash function.
N CHAR[0..6] PIN for card, up to 6 decimal digits - optional.
P CHAR[] KeePass database password - optional.
S BYTE[16] KeePass password salt, 128 bit.
F BYTE[] Content of the key file - optional.
C BYTE[] The key provided by the plugin (in CPasswordDlg::GetKeyFromProvider).
M BYTE[32] The actual master key used to encrypt/decrypt the database.
X BYTE[32]BYTE[32] KeePass' dictionary attac prevention: multiple rounds of AES, and a then SHA-256.
Y CHAR[1..6]BYTE[32] The plugin's key expansion function, mapping a PIN to a cipher key.

Composition of the master key

According to the documentation, observation and tracing through the source code, the master key M is derived in the following ways, depending on the several possibilities for the composing the master key.

  1. Given only a password P: «If only a password is used (i.e. no key file), the password plus a 128-bit random salt are hashed using SHA- 256 to form the final key [...]»

    M = X( H( P ))

    The salt comes into play only in the key derivation function X .

  2. Given only a key file with content F:

    M = X( F )   if file F is exactly 32 bytes long;
    M = X( H( F ))   otherwise

    As a special case, a key file containing 64 hex digits will be converted to binary (32 bytes) and processed just like the corresponding 32 byte key file.

  3. Given a password P and a key file with content F: «When using both password and key file, the final key is derived as follows: SHA-256(SHA-256(password), key file contents), [...]»

    M = X( H( H( P ) || F ))   if F is 32 bytes long;

    «If the key file doesn't contain exactly 32 bytes (256 bits), they are hashed with SHA-256, too, to form a 256-bit key.»

    M = X( H( H( P ) || H( F )))   otherwise

    What happens to the key C provided by a plugin is less well-documented. It turns out it is not the same way as for key files.

  4. Given no password, but only a plugin-provided key C:

    M = X( H( C ))

    The plugin-provided key is will always be hashed, whatever the size.

  5. Given a password P and a plugin-provided key C:

    M = X( H( H( P ) || H( C )))

    The master key derives from a hash of a 256-bit long concatenation of two hashes, first a hash over the password, second a hash over the plugin-provided key.

The similarity of the last equation (in item 5) and the equations in item 3 should allow us to exchange the plugin-provided key C and the content F of an appropriately generated key file - one of the project's main goals.

The fact that the key provided by the plugin will always be hashed, even if 32 bytes long, is a little nuisance and forces us to provide a 512 bits long value (64 bytes) in some cases: if the provided key is meant to be equivalent to a master key composed of a password P and a key file F, the key provider has to produce

C = H( P ) || H( _F )

which will then be hashed to match case 3 above.

The plugin-provided key

If we can read at least 32 bytes of data d from the card and assume they have enough entropy, we can provide these bytes directly as the key to KeePass:

C = d

If the card is a "found" read-only memory card with low entropy, we might want to use the hash of more than 32 bytes of its content:

C = H( d )

Both approaches have the weakness that the (stolen or copied) card is enough for an adversary to unlock the KeePass database. We can counteract this by using a secret transformation of the card's content, using a key K in the plugin (either hard-coded or set by the user and stored eg in the config file):

C = EK( d )

Now the adversary would need access to both the card's content d and the key K, ie to the user's account on the system.

Furthermore, a PIN N (a password consisting of a few digits) can be used as a second component in the transformation of the raw content d. (However, the few digits of a PIN would not give much protection against exhaustive search; but it could deter a naughty coworker or family member):

C = EY( N )( EK( d ) )

Here N is used to derive the key Y( N ) of a second cipher round. Obviously, the order of the two encryptions could be reversed, or alternatively both N and K could be used to derive a single encryption key, using a different key derivation scheme Y':

C = EY'( N, K )( d )

I'm not sure if any of these variants is better than the others.

The plugin's cipher E as well as the it's key derivation function Y is not yet determined.

The key file for a card

Using the knowledge about the master key construction, we can determine what the content F of a key file should be, so that the key file and the card can used interchangeably to unlock on and the same database: we want to end up with the same master key in both cases, ie when using the key file, we have:

M = X( F )   if length of F is 32 bytes;
M = X( H( F ))   otherwise.

and when using the card, we have:

M = X( H( C ))
= X( H( EY( N )( EK( d ) )))

Thus we can either set F = H( C ) or we use H(F) = H( C ) = H( EY( N )( EK( d ) )), and thus set F = EY( N )( EK( d ) ).


Related

Wiki: Home
Wiki: KeeChipCardWiki