Salchicha (Salchicha v0.2.0)

View Source

A pure-ish Elixir cryptography tool for the Salsa20 and ChaCha20 ciphers.

This library has a handful of crypto functions that are compatible with NaCl/libsodium for encryption and decryption with shared secret keys.

The Salsa20/XSalsa20 ciphers are implemented entirely in Elixir while the Poly1305 MAC function is done through the Erlang :crypto module, which is implemented as a NIF with OpenSSL bindings.

The ChaCha20_Poly1305 AEAD cipher is already supported by the :crypto module, but XChaCha20 is not. The HChaCha20 hash function, an intermediate step for generating an XChaCha20 sub-key, is implemented in Elixir so :crypto can be leveraged for XChaCha20_Poly1305.

The ChaCha20/XChaCha20 ciphers do also have pure Elixir implementations just like Salsa20/XSalsa20, but unless you are concerned with long-running NIFs blocking schedulers, you should prefer to use the versions that fully leverage :crypto NIFs, the behaviour of applicable functions in this module. If you wish to you use the elixir implementations, you can call them directly with the functions available in Salchicha.Chacha ending in _pure.

While this module contains everything you'll need to encrypt and decrypt with XSalsa20_Poly1305 and XChaCha20_Poly1305 authenticated ciphers, the internal modules Salchicha.Salsa and Salchicha.Chacha are documented for educational purposes and expose a few primitives.

Summary

Types

12-byte nonce used by the ChaCha20 (IETF) cipher

24-byte extended nonce used by the XSalsa20 and XChaCha20 ciphers

8-byte nonce used by the Salsa20 cipher

32-byte shared secret key used by all variations of Salsa/ChaCha

Functions

Generates a random 24-byte extended nonce

Generates a random 32-byte key

Encrypts a message with a secret key using the XSalsa20_Poly1305 authenticated cipher.

Decrypts a message that was encrypted with secretbox/3 using the XSalsa20_Poly1305 authenticated cipher.

Decrypts a message that was encrypted in "combined mode" using the XChaCha20_Poly1305 AEAD cipher.

Decrypts a message that was encrypted in "detacheded mode" using the XChaCha20_Poly1305 AEAD cipher.

Encrypts a message with a secret key using the XChaCha20_Poly1305 AEAD cipher in "combined mode".

Encrypts a message with a secret key using the XChaCha20_Poly1305 AEAD cipher in "detached mode".

Types

aad()

@type aad() :: iodata()

chacha_nonce()

@type chacha_nonce() :: <<_::96>>

12-byte nonce used by the ChaCha20 (IETF) cipher

cipher_text()

@type cipher_text() :: binary()

encrypted_message()

@type encrypted_message() :: iodata()

extended_nonce()

@type extended_nonce() :: <<_::192>>

24-byte extended nonce used by the XSalsa20 and XChaCha20 ciphers

message()

@type message() :: iodata()

salsa_nonce()

@type salsa_nonce() :: <<_::64>>

8-byte nonce used by the Salsa20 cipher

secret_key()

@type secret_key() :: <<_::256>>

32-byte shared secret key used by all variations of Salsa/ChaCha

tag()

@type tag() :: <<_::128>>

Functions

generate_nonce()

@spec generate_nonce() :: extended_nonce()

Generates a random 24-byte extended nonce

XSalsa20 and XChaCha20 use a 24-byte nonce, up from the 8 and 8/12 byte nonces of the respective Salsa20 and ChaCha20 ciphers.

You should never reuse the same nonce for a given secret key. 24 bytes are said to be large enough to generate nonces randomly - doing so would be ill-advised with 8-byte nonces since collision would be much more likely.

generate_secret_key()

@spec generate_secret_key() :: secret_key()

Generates a random 32-byte key

secretbox(message, nonce, key)

@spec secretbox(message(), extended_nonce(), secret_key()) :: iolist()

Encrypts a message with a secret key using the XSalsa20_Poly1305 authenticated cipher.

This function behaves like crypto_secretbox() does in NaCl.

Parameters

  • message - Plaintext message to be encrypted
  • nonce - 24-byte extended nonce
  • key - 32-byte secret key

The return value is the cipher text prepended by the 16-byte tag (MAC), compatible with NaCl.

Returns an iolist/0 to reduce binary copies. Call IO.iodata_to_binary/1 if you need a single binary.

secretbox_open(cipher_message, nonce, key)

@spec secretbox_open(encrypted_message(), extended_nonce(), secret_key()) ::
  iolist() | :error

Decrypts a message that was encrypted with secretbox/3 using the XSalsa20_Poly1305 authenticated cipher.

This function behaves like crypto_secretbox_open() does in NaCl.

Parameters

  • cipher_message - The encrypted message (tag prepended to cipher text)
  • nonce - 24-byte extended nonce
  • key - 32-byte secret key

The return value is the decrypted plaintext (as an iolist) or :error if authentication failed.

Returns an iolist/0 to reduce binary copies. Call IO.iodata_to_binary/1 if you need the message as a binary.

xchacha20_poly1305_decrypt(cipher_message, nonce, key, aad \\ <<>>)

@spec xchacha20_poly1305_decrypt(
  encrypted_message(),
  extended_nonce(),
  secret_key(),
  aad()
) ::
  binary() | :error

Decrypts a message that was encrypted in "combined mode" using the XChaCha20_Poly1305 AEAD cipher.

This function behaves like crypto_aead_xchacha20poly1305_ietf_decrypt() does in libsodium.

Parameters

  • message - The encrypted message (tag appended to cipher text)
  • nonce - 24-byte extended nonce
  • key - 32-byte secret key
  • aad - Additional authenticated data (defaults to <<>> i.e. no AAD)

The return value is the decrypted plaintext as a binary or :error if authentication failed.

xchacha20_poly1305_decrypt_detached(cipher_text, nonce, key, aad \\ <<>>, tag)

@spec xchacha20_poly1305_decrypt_detached(
  encrypted_message(),
  extended_nonce(),
  secret_key(),
  aad(),
  tag()
) :: binary() | :error

Decrypts a message that was encrypted in "detacheded mode" using the XChaCha20_Poly1305 AEAD cipher.

This function behaves like crypto_aead_xchacha20poly1305_ietf_decrypt_detached() does in libsodium.

Parameters

  • cipher_text - The encrypted message (only the cipher text, not appended with the tag)
  • nonce - 24-byte extended nonce
  • key - 32-byte secret key
  • aad - Additional authenticated data (defaults to <<>> i.e. no AAD)
  • tag - 16-byte Poly1305 authentication tag or MAC

The return value is the decrypted plaintext as a binary or :error if authentication failed.

This function differs from xchacha20_poly1305_decrypt/4 by returning the tag and cipher text separately

This "detached mode" function differs from the "combined mode" xchacha20_poly1305_decrypt/4 in that the cipher text and tag are supplied as separate parameters, not combined as a single message.

xchacha20_poly1305_encrypt(message, nonce, key, aad \\ <<>>)

@spec xchacha20_poly1305_encrypt(message(), extended_nonce(), secret_key(), aad()) ::
  iolist()

Encrypts a message with a secret key using the XChaCha20_Poly1305 AEAD cipher in "combined mode".

This function behaves like crypto_aead_xchacha20poly1305_ietf_encrypt() does in libsodium.

Parameters

  • message - Plaintext message to be encrypted
  • nonce - 24-byte extended nonce
  • key - 32-byte secret key
  • aad - Additional authenticated data (defaults to <<>> i.e. no AAD)

The return value is the cipher text appended by the 16-byte tag (MAC), i.e. "combined mode".

Returns an iolist/0 to reduce binary copies. Call IO.iodata_to_binary/1 if you need a single binary.

xchacha20_poly1305_encrypt_detached(message, nonce, key, aad \\ <<>>)

@spec xchacha20_poly1305_encrypt_detached(
  message(),
  extended_nonce(),
  secret_key(),
  aad()
) ::
  {cipher_text(), tag()}

Encrypts a message with a secret key using the XChaCha20_Poly1305 AEAD cipher in "detached mode".

This function behaves like crypto_aead_xchacha20poly1305_ietf_encrypt_detached() does in libsodium.

Parameters

This "detached mode" function differs from the "combined mode" xchacha20_poly1305_encrypt/4 by returning the tag and cipher text separately in a tuple in the form {cipher_text, tag}. Both cipher_text and tag will already be binaries.