Skip to content

Instantly share code, notes, and snippets.

@mudssrali
Last active December 18, 2023 13:13
Show Gist options
  • Save mudssrali/53154b3e6c2ceaf124cf6788b2c17403 to your computer and use it in GitHub Desktop.
Save mudssrali/53154b3e6c2ceaf124cf6788b2c17403 to your computer and use it in GitHub Desktop.
Elixir implementation of AES encryption and decryption using erlang's :crypto.crypto_one_time with initialization vector
defmodule Cipher.AES do
@moduledoc """
Functions related to encrypting and decrypting data using the Advanced
Encryption Standard (AES).
"""
@block_size 16
@secret_key "put something secret here"
@doc """
Encrypt the given `data` with AES-256 in CBC mode using `key` and `iv`
PKCS#7 padding will be added to `data`
"""
@spec encrypt_aes_256_cbc(String.t()) :: String.t()
def encrypt_aes_256_cbc(plain_text) do
secret_key_hash = make_hash(@secret_key, 32)
# create Initialisation Vector
iv = :crypto.strong_rand_bytes(@block_size)
padded_text = pad_pkcs7(plain_text, @block_size)
encrypted_text = :crypto.crypto_one_time(:aes_256_cbc, secret_key_hash, iv, padded_text, true)
# concatenate IV for decryption
encrypted_text = iv <> encrypted_text
Base.encode64(encrypted_text)
end
@doc """
Decrypt the given `data` with AES-256 in CBC mode using `key` and `iv`
PKCS#7 padding will be removed
"""
@spec decrypt_aes_256_cbc(String.t()) :: String.t()
def decrypt_aes_256_cbc(cipher_text) do
secret_key_hash = make_hash(@secret_key, 32)
{:ok, ciphertext} = Base.decode64(cipher_text)
<<iv::binary-16, ciphertext::binary>> = ciphertext
decrypted_text = :crypto.crypto_one_time(:aes_256_cbc, secret_key_hash, iv, ciphertext, false)
unpad_pkcs7(decrypted_text)
end
@doc """
Pad the `message` by extending it to the nearest `blocksize` boundary,
appending the number of bytes of padding to the end of the block.
If the original `message` is a multiple of `blocksize`, an additional block
of bytes with value `blocksize` is added.
## Examples
iex> Crypto.AES.pad_pkcs7("HELLO", 16)
<<72, 69, 76, 76, 79, 3, 3, 3>>
iex> Crypto.AES.pad_pkcs7("HELLO", 16)
<<72, 69, 76, 76, 79, 5, 5, 5, 5, 5>>
"""
@spec pad_pkcs7(String.t(), non_neg_integer()) :: String.t()
def pad_pkcs7(message, blocksize) do
pad = blocksize - rem(byte_size(message), blocksize)
message <> to_string(List.duplicate(pad, pad))
end
@doc """
Remove the PKCS#7 padding from the end of `data`.
## Examples
iex> Crypto.AES.unpad_pkcs7(<<72, 69, 76, 76, 79, 3, 3, 3>>)
"HELLO"
"""
@spec unpad_pkcs7(String.t()) :: binary()
def unpad_pkcs7(data) do
<<pad>> = binary_part(data, byte_size(data), -1)
binary_part(data, 0, byte_size(data) - pad)
end
@spec make_hash(String.t(), non_neg_integer()) :: binary()
defp make_hash(text, length) do
:crypto.hash(:sha256, text)
|> Base.url_encode64()
|> binary_part(0, length)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment