1

I'm trying to use LibSodium to implement Key Wrapping using SIV mode (Synthetic Initialization Vector). I'm using this answer as a guide, but I want be sure I understand it correctly. My biggest doubt was whether to use masterKey or authenticationKey as the message param when generating the auth tag.

Similarly with crypto_stream_xchacha20_xor, it wasn't clear to me whether the message param should be the masterKey, or the encryptionKey.

Does the following pseudocode look sound?

Wrap

argonBytes = crypto_pwhash({length: 32, password: password, …})

masterKey = randombytes_buf(32)

authenticationKey = crypto_kdf_derive_from_key({length: 32, id: 0, context: “authKey”, ikm:argonBytes})

encryptionKey = crypto_kdf_derive_from_key({length: 32, id: 0, context: “encKey”, ikm: argonBytes})

authTag = crypto_generichash({length: 24, message: masterKey, key: authenticationKey})

wrappedMasterKey = authTag + crypto_stream_xchacha20_xor({message: masterKey, nonce: authTag, key: encryptionKey})

Unwrap

argonBytes = crypto_pwhash({length: 32, password: password, …})

authenticationKey = crypto_kdf_derive_from_key({length: 32, id: 0, context: “authKey”, ikm:argonBytes})

encryptionKey = crypto_kdf_derive_from_key({length: 32, id: 0, context: “encKey”, ikm: argonBytes})

nonce = wrappedMasterKey[0,…, 24]

unwrappedMasterKey = crypto_stream_xchacha20_xor({message: wrappedMasterKey[24,…], nonce: nonce, key: encryptionKey})

authTag = crypto_generichash({length: 24, message: unwrappedMasterKey, key: authenticationKey})

if (!memcmp(authTag, nonce)) throw Error

return unwrappedMasterKey

hunter
  • 4,051
  • 6
  • 29
  • 42

1 Answers1

1

My biggest doubt was whether to use masterKey or authenticationKey as the message param when generating the auth tag.

I'd recommend reading this paper, this paper, and this Internet-Draft to solidify your understanding of SIV mode. This generic composition paper also briefly covers SIV mode (scheme A4/B4).

Sorry for recommending so many, but you'll thank me later. You don't have to read everything, just the relevant parts.

TL;DR: The tag gets computed over the (plaintext, nonce, associatedData). If you have no nonce parameter, the encryption is deterministic, which is fine for key wrapping but not generally recommended in other cases.

Then you want to encrypt the plaintext, which is the randomly generated key to wrap in your case.

Does the following pseudocode look sound?

There are a few changes I would make.

Good:

  • Using libsodium.
  • Deriving an authentication key and encryption key from the user's key using a collision-resistant hash function/KDF and domain separation. This is important for AEAD commitment.
  • Using a collision-resistant MAC for the authentication tag. This is again important for AEAD commitment.
  • You're not using S2V, which keeps things simple.

Improvements:

  • Use a 256-bit tag. Truncate this to 192 bits for the nonce but store and verify 256 bits for message authentication.
  • You could append the tag to be more consistent with most AEADs.
  • Make sure the tag comparison is constant time to prevent attacks. There's a helper in libsodium.
  • Before throwing an error if the tags aren't equal, zero the plaintext in a way that can't be optimised away by the compiler. Also, zero the subkeys before returning in both functions.
  • If using this for more than key wrapping, add support for a nonce and associatedData as explained above. They can either be hashed during the key derivation or when calculating the tag. However, if multiple parameters can be variable-length (e.g., the ciphertext and associatedData), you need to be careful to avoid canonicalization attacks. This can be done by encoding their lengths in the KDF/MAC message.
  • You could call crypto_kdf_derive_from_key() once with the name of your application as the context, splitting a 512-bit output into two 256-bit keys. This would be marginally more efficient and helps with the point above. Including the associatedData in the key derivation means it also affects the ciphertext, which is sort of additional nonce reuse protection in some cases.

Here is a C# implementation of XChaCha20-BLAKE2b-SIV that I did a while back, and here is a C implementation using the same algorithms but with S2V. Both use libsodium.

samuel-lucas6
  • 2,211
  • 9
  • 20