Last active
December 10, 2024 09:37
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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