diff --git a/lib/plug_rails_cookie_session_store/message_encryptor.ex b/lib/plug_rails_cookie_session_store/message_encryptor.ex index 5cbb4e9..68fefbb 100644 --- a/lib/plug_rails_cookie_session_store/message_encryptor.ex +++ b/lib/plug_rails_cookie_session_store/message_encryptor.ex @@ -47,6 +47,7 @@ defmodule PlugRailsCookieSessionStore.MessageEncryptor do {:ok, verified} -> [encrypted, iv] = String.split(verified, "--") |> Enum.map(&Base.decode64!/1) encrypted |> decrypt(cipher, secret, iv) |> unpad_message + :error -> :error end @@ -60,6 +61,7 @@ defmodule PlugRailsCookieSessionStore.MessageEncryptor do iv = :crypto.strong_rand_bytes(16) {message, auth_tag} = encrypt_aead(pad_message(message), cipher, secret, iv) + message |> Base.encode64() |> Kernel.<>("--#{Base.encode64(iv)}") @@ -71,9 +73,16 @@ defmodule PlugRailsCookieSessionStore.MessageEncryptor do """ def authenticate_and_decrypt(encrypted, secret, cipher \\ :aes_gcm) when is_binary(encrypted) and is_binary(secret) do - [encrypted, iv, auth_tag] = String.split(encrypted, "--") |> Enum.map(&Base.decode64!/1) - result = decrypt_aead(encrypted, cipher, secret, iv, auth_tag) - {:ok, result} + case String.split(encrypted, "--") |> Enum.map(&Base.decode64/1) do + [{:ok, encrypted}, {:ok, iv}, {:ok, auth_tag}] -> + case decrypt_aead(encrypted, cipher, secret, iv, auth_tag) do + :error -> :error + result -> {:ok, result} + end + + _ -> + :error + end end defp encrypt(message, cipher, secret, iv) do @@ -104,8 +113,10 @@ defmodule PlugRailsCookieSessionStore.MessageEncryptor do defp unpad_message(msg) do padding_size = :binary.last(msg) + if padding_size <= 16 do msg_size = byte_size(msg) + if binary_part(msg, msg_size, -padding_size) == :binary.copy(<>, padding_size) do {:ok, binary_part(msg, 0, msg_size - padding_size)} else diff --git a/test/plug_rails_cookie_session_store_test/message_encryptor_test.exs b/test/plug_rails_cookie_session_store_test/message_encryptor_test.exs index ef8d3ae..292fef3 100644 --- a/test/plug_rails_cookie_session_store_test/message_encryptor_test.exs +++ b/test/plug_rails_cookie_session_store_test/message_encryptor_test.exs @@ -63,18 +63,20 @@ defmodule PlugRailsCookieSessionStore.MessageEncryptorTest do assert decrypted == :error decrypted = ME.authenticate_and_decrypt(encrypted, @right) - assert decrypted == {:ok, data} + padding = <<2, 2>> + assert decrypted == {:ok, data <> padding} end test "it uses only the first 32 bytes to authenticate and encrypt/decrypt" do data = <<0, "helloworld", 0>> + padding = <<4, 4, 4, 4>> encrypted = ME.encrypt_and_authenticate(<<0, "helloworld", 0>>, @large) decrypted = ME.authenticate_and_decrypt(encrypted, @large) - assert decrypted == {:ok, data} + assert decrypted == {:ok, data <> padding} decrypted = ME.authenticate_and_decrypt(encrypted, @right) - assert decrypted == {:ok, data} + assert decrypted == {:ok, data <> padding} decrypted = ME.verify_and_decrypt(encrypted, @right, @right) assert decrypted == :error @@ -82,9 +84,24 @@ defmodule PlugRailsCookieSessionStore.MessageEncryptorTest do encrypted = ME.encrypt_and_authenticate(<<0, "helloworld", 0>>, @right) decrypted = ME.authenticate_and_decrypt(encrypted, @large) - assert decrypted == {:ok, data} + assert decrypted == {:ok, data <> padding} decrypted = ME.authenticate_and_decrypt(encrypted, @right) - assert decrypted == {:ok, data} + assert decrypted == {:ok, data <> padding} + end + + test "it returns :error when decrypting an invalid rails 6 message" do + encrypted = + ME.encrypt_and_authenticate(<<0, "helloworld", 0>>, @large) + # make it invalid but still base64 valid + |> String.replace(~r/[abcdef]/, "x") + + decrypted = ME.authenticate_and_decrypt(encrypted, @right) + assert decrypted == :error + end + + test "it returns :error when parts are not valid base64" do + decrypted = ME.authenticate_and_decrypt("a--b--c", @right) + assert decrypted == :error end end