ExCrypto

The ExCrypto module exposes a subset of functionality from the Erlang crypto module with the goal of making it easier to include strong cryptography in your Elixir applications.

This module provides functions for symmetric-key cryptographic operations using AES in GCM mode. The ExCrypto module attempts to reduce complexity by providing some sane default values for common operations.

Summary

Functions

Split and decode the three parts of an encrypted payload and encode using Base.url_decode64

Returns a clear-text string decrypted with AES in GCM mode

Join the three parts of an encrypted payload and encode using Base.url_encode64

Same as encrypt/4 except the initialization_vector is automatically

Returns a string of random where the length is equal to integer

Returns random characters. Each character represents 6 bits of entropy

Returns a random integer between low and high

Functions

decode_payload(encoded_parts)

Specs

decode_payload(binary) ::
  {:ok, {binary, binary, binary}} |
  {:error, binary}

Split and decode the three parts of an encrypted payload and encode using Base.url_decode64.

Examples

iex> clear_text = "my-clear-text"
iex> auth_data = "my-auth-data"
iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, {ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text)
iex> {:ok, encoded_payload} = ExCrypto.encode_payload(init_vec, cipher_text, cipher_tag)
iex> assert(String.valid?(encoded_payload))
true
iex> {:ok, {d_init_vec, d_cipher_text, d_cipher_tag}} = ExCrypto.decode_payload(encoded_payload)
iex> assert(d_init_vec == init_vec)
true
iex> assert(d_cipher_text == cipher_text)
true
iex> assert(d_cipher_tag == cipher_tag)
true
decrypt(key, authentication_data, initialization_vector, cipher_text, cipher_tag)

Specs

decrypt(binary, binary, binary, binary, binary) ::
  {:ok, binary} |
  {:error, binary}

Returns a clear-text string decrypted with AES in GCM mode.

At a high level decryption using AES in GCM mode looks like this:

key + init_vec + auth_data + cipher_text + cipher_tag -> clear_text

Examples

iex> clear_text = "my-clear-text"
iex> auth_data = "my-auth-data"
iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text)
iex> {init_vec, cipher_text, cipher_tag} = payload
iex> {:ok, val} = ExCrypto.decrypt(aes_256_key, auth_data, init_vec, cipher_text, cipher_tag)
iex> assert(val == clear_text)
true
encode_payload(initialization_vector, cipher_text, cipher_tag)

Specs

encode_payload(binary, binary, binary) ::
  {:ok, binary} |
  {:error, binary}

Join the three parts of an encrypted payload and encode using Base.url_encode64.

This produces a Unicode payload string like this:

init_vec   <> cipher_text <> cipher_tag
[128 bits] <>  [?? bits]  <> [128 bits]

This format is convenient to include in HTTP request bodies. It can also be used with JSON transport formats.

Examples

iex> clear_text = "my-clear-text"
iex> auth_data = "my-auth-data"
iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, {ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text)
iex> {:ok, encoded_payload} = ExCrypto.encode_payload(init_vec, cipher_text, cipher_tag)
iex> assert(String.valid?(encoded_payload))
true
encrypt(key, authentication_data, clear_text)

Specs

encrypt(binary, binary, binary) ::
  {:ok, {binary, {binary, binary, binary}}} |
  {:error, binary}

Same as encrypt/4 except the initialization_vector is automatically.

A 128 bit initialization_vector is generated automatically by encrypt/3. It returns a tuple containing the initialization_vector, the cipher_text and the cipher_tag.

Examples

iex> clear_text = "my-clear-text"
iex> auth_data = "my-auth-data"
iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text)
iex> {init_vec, cipher_text, cipher_tag} = payload
iex> assert(is_bitstring(cipher_text))
true
iex> assert(bit_size(cipher_tag) == 128)
true
encrypt(key, authentication_data, initialization_vector, clear_text)

Specs

encrypt(binary, binary, binary, binary) ::
  {:ok, {binary, {binary, binary, binary}}} |
  {:error, binary}

Encrypt a binary with AES in GCM mode.

Returns a tuple containing the initialization_vector, the cipher_text and the cipher_tag.

At a high level encryption using AES in GCM mode looks like this:

key + init_vec + auth_data + clear_text -> cipher_text + cipher_tag

Examples

iex> clear_text = "my-clear-text"
iex> auth_data = "my-auth-data"
iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> {:ok, iv} = ExCrypto.rand_bytes(16)
iex> {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, iv, clear_text)
iex> {iv, cipher_text, cipher_tag} = payload
iex> assert(is_bitstring(cipher_text))
true
iex> assert(bit_size(cipher_tag) == 128)
true
generate_aes_key(key_type, key_format)

Specs

generate_aes_key(atom, atom) ::
  {:ok, binary} |
  {:error, binary}

Returns an AES key.

Accepts a key_type (:aes_128|:aes_192|:aes_256) and key_format (:base64|:bytes) to determine type of key to produce.

Examples

iex> {:ok, key} = ExCrypto.generate_aes_key(:aes_256, :bytes)
iex> assert bit_size(key) == 256
true

iex> {:ok, key} = ExCrypto.generate_aes_key(:aes_256, :base64)
iex> assert String.length(key) == 44
true

iex> {:ok, key} = ExCrypto.generate_aes_key(:aes_192, :bytes)
iex> assert bit_size(key) == 192
true

iex> {:ok, key} = ExCrypto.generate_aes_key(:aes_192, :base64)
iex> assert String.length(key) == 32
true

iex> {:ok, key} = ExCrypto.generate_aes_key(:aes_128, :bytes)
iex> assert bit_size(key) == 128
true

iex> {:ok, key} = ExCrypto.generate_aes_key(:aes_128, :base64)
iex> assert String.length(key) == 24
true
rand_bytes(length)

Specs

rand_bytes(integer) ::
  {:ok, binary} |
  {:error, binary}

Returns a string of random where the length is equal to integer.

Examples

iex> {:ok, rand_bytes} = ExCrypto.rand_bytes(16)
iex> assert(byte_size(rand_bytes) == 16)
true
iex> assert(bit_size(rand_bytes) == 128)
true

iex> {:ok, rand_bytes} = ExCrypto.rand_bytes(24)
iex> assert(byte_size(rand_bytes) == 24)
true
iex> assert(bit_size(rand_bytes) == 192)
true

iex> {:ok, rand_bytes} = ExCrypto.rand_bytes(32)
iex> assert(byte_size(rand_bytes) == 32)
true
iex> assert(bit_size(rand_bytes) == 256)
true
rand_chars(num_chars)

Specs

rand_chars(integer) :: String.t

Returns random characters. Each character represents 6 bits of entropy.

Accepts an integer to determine the number of random characters to return.

Examples

iex> rand_string = ExCrypto.rand_chars(24)
iex> assert(String.length(rand_string) == 24)
true

iex> rand_string = ExCrypto.rand_chars(32)
iex> assert(String.length(rand_string) == 32)
true

iex> rand_string = ExCrypto.rand_chars(44)
iex> assert(String.length(rand_string) == 44)
true
rand_int(low, high)

Specs

rand_int(integer, integer) :: integer

Returns a random integer between low and high.

Accepts two integer arguments for the low and high boundaries. The low argument must be less than the high argument.

Examples

iex> rand_int = ExCrypto.rand_int(2, 20)
iex> assert(rand_int > 1)
true
iex> assert(rand_int < 20)
true

iex> rand_int = ExCrypto.rand_int(23, 99)
iex> assert(rand_int > 23)
true
iex> assert(rand_int < 99)
true

iex> rand_int = ExCrypto.rand_int(212, 736)
iex> assert(rand_int > 212)
true
iex> assert(rand_int < 736)
true