Skip to content

Commit 62022da

Browse files
authored
Allow custom AAD to be provided to message encryptor (#33)
1 parent 9e6961a commit 62022da

2 files changed

Lines changed: 24 additions & 18 deletions

File tree

lib/plug/crypto/message_encryptor.ex

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,25 @@ defmodule Plug.Crypto.MessageEncryptor do
2929

3030
@doc """
3131
Encrypts a message using authenticated encryption.
32+
33+
A custom authentication message can be provided.
34+
It defaults to "A128GCM" for backwards compatibility.
3235
"""
33-
def encrypt(message, secret, sign_secret)
34-
when is_binary(message) and byte_size(secret) > 0 and is_binary(sign_secret) do
35-
aes128_gcm_encrypt(message, secret, sign_secret)
36+
def encrypt(message, aad \\ "A128GCM", secret, sign_secret)
37+
when is_binary(message) and is_binary(aad) and byte_size(secret) > 0 and
38+
is_binary(sign_secret) do
39+
aes128_gcm_encrypt(message, aad, secret, sign_secret)
3640
rescue
3741
e -> reraise e, Plug.Crypto.prune_args_from_stacktrace(__STACKTRACE__)
3842
end
3943

4044
@doc """
4145
Decrypts a message using authenticated encryption.
4246
"""
43-
def decrypt(encrypted, secret, sign_secret)
44-
when is_binary(encrypted) and byte_size(secret) > 0 and is_binary(sign_secret) do
45-
aes128_gcm_decrypt(encrypted, secret, sign_secret)
47+
def decrypt(encrypted, aad \\ "A128GCM", secret, sign_secret)
48+
when is_binary(encrypted) and is_binary(aad) and byte_size(secret) > 0 and
49+
is_binary(sign_secret) do
50+
aes128_gcm_decrypt(encrypted, aad, secret, sign_secret)
4651
rescue
4752
e -> reraise e, Plug.Crypto.prune_args_from_stacktrace(__STACKTRACE__)
4853
end
@@ -51,19 +56,18 @@ defmodule Plug.Crypto.MessageEncryptor do
5156
#
5257
# A random 128-bit content encryption key (CEK) is generated for
5358
# every message which is then encrypted with `aes_gcm_key_wrap/3`.
54-
defp aes128_gcm_encrypt(plain_text, secret, sign_secret) when bit_size(secret) > 256 do
55-
aes128_gcm_encrypt(plain_text, binary_part(secret, 0, 32), sign_secret)
59+
defp aes128_gcm_encrypt(plain_text, aad, secret, sign_secret) when bit_size(secret) > 256 do
60+
aes128_gcm_encrypt(plain_text, aad, binary_part(secret, 0, 32), sign_secret)
5661
end
5762

58-
defp aes128_gcm_encrypt(plain_text, secret, sign_secret)
63+
defp aes128_gcm_encrypt(plain_text, aad, secret, sign_secret)
5964
when is_binary(plain_text) and bit_size(secret) in [128, 192, 256] and
6065
is_binary(sign_secret) do
6166
key = :crypto.strong_rand_bytes(16)
6267
iv = :crypto.strong_rand_bytes(12)
63-
aad = "A128GCM"
6468
{cipher_text, cipher_tag} = block_encrypt(:aes_gcm, key, iv, {aad, plain_text})
6569
encrypted_key = aes_gcm_key_wrap(key, secret, sign_secret)
66-
encode_token(aad, encrypted_key, iv, cipher_text, cipher_tag)
70+
encode_token("A128GCM", encrypted_key, iv, cipher_text, cipher_tag)
6771
end
6872

6973
# Verifies and decrypts a message using AES128-GCM mode.
@@ -72,15 +76,15 @@ defmodule Plug.Crypto.MessageEncryptor do
7276
#
7377
# The encrypted content encryption key (CEK) is decrypted
7478
# with `aes_gcm_key_unwrap/3`.
75-
defp aes128_gcm_decrypt(cipher_text, secret, sign_secret) when bit_size(secret) > 256 do
76-
aes128_gcm_decrypt(cipher_text, binary_part(secret, 0, 32), sign_secret)
79+
defp aes128_gcm_decrypt(cipher_text, aad, secret, sign_secret) when bit_size(secret) > 256 do
80+
aes128_gcm_decrypt(cipher_text, aad, binary_part(secret, 0, 32), sign_secret)
7781
end
7882

79-
defp aes128_gcm_decrypt(cipher_text, secret, sign_secret)
83+
defp aes128_gcm_decrypt(cipher_text, aad, secret, sign_secret)
8084
when is_binary(cipher_text) and bit_size(secret) in [128, 192, 256] and
8185
is_binary(sign_secret) do
8286
case decode_token(cipher_text) do
83-
{aad = "A128GCM", encrypted_key, iv, cipher_text, cipher_tag}
87+
{"A128GCM", encrypted_key, iv, cipher_text, cipher_tag}
8488
when bit_size(iv) === 96 and bit_size(cipher_tag) === 128 ->
8589
encrypted_key
8690
|> aes_gcm_key_unwrap(secret, sign_secret)
@@ -120,7 +124,6 @@ defmodule Plug.Crypto.MessageEncryptor do
120124
:error, :notsup -> raise_notsup(cipher)
121125
end
122126

123-
# TODO: remove when we reqwuire OTP 24 (since it has similar alias handling)
124127
defp cipher_alias(:aes_gcm, 128), do: :aes_128_gcm
125128
defp cipher_alias(:aes_gcm, 192), do: :aes_192_gcm
126129
defp cipher_alias(:aes_gcm, 256), do: :aes_256_gcm

test/plug/crypto/message_encryptor_test.exs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ defmodule Plug.Crypto.MessageEncryptorTest do
1111

1212
test "it encrypts/decrypts a message" do
1313
data = <<0, "hełłoworld", 0>>
14-
encrypted = ME.encrypt(<<0, "hełłoworld", 0>>, @right, @right)
14+
encrypted = ME.encrypt(<<0, "hełłoworld", 0>>, "right aad", @right, @right)
1515

1616
decrypted = ME.decrypt(encrypted, @wrong, @wrong)
1717
assert decrypted == :error
@@ -22,7 +22,10 @@ defmodule Plug.Crypto.MessageEncryptorTest do
2222
decrypted = ME.decrypt(encrypted, @wrong, @right)
2323
assert decrypted == :error
2424

25-
decrypted = ME.decrypt(encrypted, @right, @right)
25+
decrypted = ME.decrypt(encrypted, "wrong aad", @right, @right)
26+
assert decrypted == :error
27+
28+
decrypted = ME.decrypt(encrypted, "right aad", @right, @right)
2629
assert decrypted == {:ok, data}
2730
end
2831

0 commit comments

Comments
 (0)