trussed-dev / trussed-auth

Authentication extension and backend for Trussed
1 stars 3 forks source link

Provide encryption key derived from each PIN #7

Closed sosthene-nitrokey closed 1 year ago

sosthene-nitrokey commented 1 year ago

The idea is for every app to be able to derive a unique symmetric key from validating the password.

Here is the layout:

When the backend is created, the runner can pass it a hardware_ikm: Bytes<N>. The backend loads from storage or generates and stores a random salt.  It then runs HKDF-EXTRACT(salt, hardware_ikm). It then derives an application_key for each client using the client ID as the info parameter: HKDF-EXPAND(exctract, info: client_id). All of this is performed lazily.

The application_key is added to the salt when hashing pins as a form of pepper. The hashing for validation would be done with HMAC(application_key, "verification" || SALT || PIN)[^1]. If the verification is successful, a key is derived using pin_kek = HMAC(application_key, "key derivation" || SALT || PIN). pin_kek is then used to unwrap a fully random 32 byte symmetric key that is then returned to the user.

What key kind should be the derived key? We can't directly create a ChaCha key because we would also need a nonce. Should we use AES-CBC, for which nonce-reuse is not as catastrophic, or should we add a new mechanism like XChaCha that could b used with a random nonce?

[^1]: || means concatenation

daringer commented 1 year ago

How does this key lead to an actual symmetric key for data-encryption?

The resulting (symmetric, derived, "key derivation") key A is then used to encryption the actual data-encryption-key (B), which was generated randomly? We need this second level to avoid re-encrypting the entire (encrypted)-data-set, if the PIN is changed?

Is this correct ?

szszszsz commented 1 year ago
  1. Re original problem, we discussed yesterday nonce randomization
  2. The actual encryption key, KEK, should be generated using standard tooling, and then wrapped with the hardware-sourced and PIN peppered key.
  3. Changing PIN should trigger KEK rewrapping.
szszszsz commented 1 year ago

@sosthene-nitrokey

  1. Is verifying step necessary? I thought wrapping has it via AEAD?
  2. Also there is a typo in the description - app id can't be the pepper, while it is known. Did you mean User PIN here?
sosthene-nitrokey commented 1 year ago

The verification can be useful to go faster when we don't wan't to derive any keys, but I'm thinking we don't gain that much unless we also go without the application_key as a pepper for verification.

client_id isn't the pepper. The application key (output of HKDF(exctract, info: client_id) is the pepper)

The key derived from the PIN should indeed be used to wrap the actual key that is returned to allow changing the PIN without changing the key.

I updated the issue to reflect that.

szszszsz commented 1 year ago

What okm and hardware_ikm stands for?

sosthene-nitrokey commented 1 year ago

I shouldn't have called it okm i renamed it to exctract aka the result of the HKDF-EXCTRACT step. hardware_ikm stands for the initial key material from the hardware registers (or from wherever the runner gets them).

robin-nitrokey commented 1 year ago

Implemented in https://github.com/trussed-dev/trussed-auth/pull/10.