Current section

13 Versions

Jump to

Compare versions

5 files changed
+26 additions
-19 deletions
  @@ -1,5 +1,9 @@
1 1 # Changelog
2 2
3 + ## v1.2.4 (2023-03-10)
4 +
5 + * Allow AAD to be given as argument on message encryptor
6 +
3 7 ## v1.2.3 (2022-08-19)
4 8
5 9 * Remove warnings on Elixir v1.14
  @@ -14,4 +14,4 @@
14 14 [{<<"GitHub">>,<<"https://github.com/elixir-plug/plug_crypto">>}]}.
15 15 {<<"name">>,<<"plug_crypto">>}.
16 16 {<<"requirements">>,[]}.
17 - {<<"version">>,<<"1.2.3">>}.
17 + {<<"version">>,<<"1.2.4">>}.
  @@ -29,10 +29,14 @@ defmodule Plug.Crypto.MessageEncryptor do
29 29
30 30 @doc """
31 31 Encrypts a message using authenticated encryption.
32 +
33 + A custom authentication message can be provided.
34 + It defaults to "A128GCM" for backwards compatibility.
32 35 """
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)
36 40 rescue
37 41 e -> reraise e, Plug.Crypto.prune_args_from_stacktrace(__STACKTRACE__)
38 42 end
  @@ -40,9 +44,10 @@ defmodule Plug.Crypto.MessageEncryptor do
40 44 @doc """
41 45 Decrypts a message using authenticated encryption.
42 46 """
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)
46 51 rescue
47 52 e -> reraise e, Plug.Crypto.prune_args_from_stacktrace(__STACKTRACE__)
48 53 end
  @@ -51,19 +56,18 @@ defmodule Plug.Crypto.MessageEncryptor do
51 56 #
52 57 # A random 128-bit content encryption key (CEK) is generated for
53 58 # 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)
56 61 end
57 62
58 - defp aes128_gcm_encrypt(plain_text, secret, sign_secret)
63 + defp aes128_gcm_encrypt(plain_text, aad, secret, sign_secret)
59 64 when is_binary(plain_text) and bit_size(secret) in [128, 192, 256] and
60 65 is_binary(sign_secret) do
61 66 key = :crypto.strong_rand_bytes(16)
62 67 iv = :crypto.strong_rand_bytes(12)
63 - aad = "A128GCM"
64 68 {cipher_text, cipher_tag} = block_encrypt(:aes_gcm, key, iv, {aad, plain_text})
65 69 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)
67 71 end
68 72
69 73 # Verifies and decrypts a message using AES128-GCM mode.
  @@ -72,15 +76,15 @@ defmodule Plug.Crypto.MessageEncryptor do
72 76 #
73 77 # The encrypted content encryption key (CEK) is decrypted
74 78 # 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)
77 81 end
78 82
79 - defp aes128_gcm_decrypt(cipher_text, secret, sign_secret)
83 + defp aes128_gcm_decrypt(cipher_text, aad, secret, sign_secret)
80 84 when is_binary(cipher_text) and bit_size(secret) in [128, 192, 256] and
81 85 is_binary(sign_secret) do
82 86 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}
84 88 when bit_size(iv) === 96 and bit_size(cipher_tag) === 128 ->
85 89 encrypted_key
86 90 |> aes_gcm_key_unwrap(secret, sign_secret)
  @@ -120,7 +124,6 @@ defmodule Plug.Crypto.MessageEncryptor do
120 124 :error, :notsup -> raise_notsup(cipher)
121 125 end
122 126
123 - # TODO: remove when we reqwuire OTP 24 (since it has similar alias handling)
124 127 defp cipher_alias(:aes_gcm, 128), do: :aes_128_gcm
125 128 defp cipher_alias(:aes_gcm, 192), do: :aes_192_gcm
126 129 defp cipher_alias(:aes_gcm, 256), do: :aes_256_gcm
  @@ -77,7 +77,7 @@ defmodule Plug.Crypto.MessageVerifier do
77 77
78 78 defp decode_token(token) do
79 79 with [protected, payload, signature] <- String.split(token, ".", parts: 3),
80 - plain_text = protected <> "." <> payload,
80 + plain_text = [protected, ?., payload],
81 81 {:ok, protected} <- Base.url_decode64(protected, padding: false),
82 82 {:ok, payload} <- Base.url_decode64(payload, padding: false),
83 83 {:ok, signature} <- Base.url_decode64(signature, padding: false) do
  @@ -1,7 +1,7 @@
1 1 defmodule Plug.Crypto.MixProject do
2 2 use Mix.Project
3 3
4 - @version "1.2.3"
4 + @version "1.2.4"
5 5 @description "Crypto-related functionality for the web"
6 6 @source_url "https://github.com/elixir-plug/plug_crypto"