Salchicha (Salchicha v0.1.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.

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 lower level functions.

Summary

Functions

Generates a random 24-byte 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()

cipher_text()

@type cipher_text() :: binary()

encrypted_message()

@type encrypted_message() :: iodata()

message()

@type message() :: iodata()

nonce()

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

secret_key()

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

tag()

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

Functions

generate_nonce()

@spec generate_nonce() :: nonce()

Generates a random 24-byte nonce

XSalsa20 and XChaCha20 use a 24-byte nonce (initialization vector), 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(), 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(), 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(), 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(),
  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(), 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(), 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.