Salchicha.Chacha (Salchicha v0.1.0)

View Source

Implementation of the ChaCha20 and XChaCha20 Ciphers

Internal module

This module is not intended to be used directly and is documented for completeness and curious cryptographic cats.

Purpose

Erlang's :crypto module supports the chacha20_poly1305 AEAD stream cipher. Analogously to Salsa20 and XSalsa20, XChaCha20 is a way to use 192-bit nonces with ChaCha20 by hashing the key and part of the extended nonce to generate a sub-key, which is used as the input key for ChaCha20.

To leverage the crypto module, we had to implement the HChaCha20 hash function in elixir to then pass the resulting sub-key to the :crypto.crypto_one_time_aead/7.

Implementation

The HChaCha20 function takes the first 16-bytes of the extended 24-byte XChaCha20 nonce, expands the key and the 16-byte nonce slice into a block in place of the block count and usual smaller nonce. That block has 20 rounds of mutation, and instead of summing the block with its starting state as is done with keystream generation, 8 of the 16 bytes are taken and used as the sub-key, which is the input key for the chacha20 cipher.

Even though we've implemented the bulk of what's needed to generate chacha20 key streams for encryption and decryption, we're only using this module to generate the inputs to use the :crypto module's chacha20_poly1305 functionality in the capacity of xchacha20.

This is all in service of leveraging the performance benefits of the the NIF crypto functions, which are necessarily going to be more performant than anything implemented in pure elixir/erlang like the :kcl package.

ChaCha20 is a variant of the Salsa20 cipher. I will discuss in greater detail the implementation in the Salchicha.Salsa module, where much is applicable here.

References for Salsa family of ciphers

Performance considerations

After the XChaCha20 sub-key is generated in elixir, the crypto NIF function performs the heavy lifting. Performance should be speedy.

Summary

Functions

hchacha20(key, nonce)

HChaCha20 hash function for deriving a sub-key for XChaCha20.

xchacha20_poly1305_decrypt(cipher_text, nonce, key, aad, tag)

@spec xchacha20_poly1305_decrypt(
  binary(),
  <<_::192>>,
  <<_::256>>,
  binary(),
  <<_::128>>
) ::
  binary() | :error

xchacha20_poly1305_encrypt(plain_text, nonce, key, aad)

@spec xchacha20_poly1305_encrypt(binary(), <<_::192>>, <<_::256>>, binary()) ::
  {binary(), <<_::128>>}