Packages
plug
1.0.0
1.20.3
1.20.2
1.20.1
retired
1.20.0
retired
1.19.5
1.19.4
1.19.3
1.19.2
1.19.1
1.19.0
1.18.5
1.18.4
1.18.3
1.18.2
1.18.1
1.18.0
1.17.4
1.17.3
1.17.2
1.17.1
1.17.0
1.16.6
1.16.5
1.16.4
1.16.3
1.16.2
1.16.1
1.16.0
1.15.6
1.15.5
1.15.4
1.15.3
1.15.2
1.15.1
1.15.0
1.14.2
1.14.1
1.14.0
1.13.6
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
retired
1.13.0
retired
1.12.1
1.12.0
1.11.1
1.11.0
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.0
1.8.3
1.8.2
1.8.1
1.8.0
1.7.2
1.7.1
1.7.0
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.5.1
1.5.0
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.0
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.14.0
0.13.1
0.13.0
0.12.2
0.12.1
0.12.0
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
Compose web applications with functions
Security advisory:
This version has known vulnerabilities.
View advisories
Current section
Files
Jump to
Current section
Files
lib/plug/crypto/message_encryptor.ex
defmodule Plug.Crypto.MessageEncryptor do
@moduledoc ~S"""
`MessageEncryptor` is a simple way to encrypt values which get stored
somewhere you don't trust.
The cipher text and initialization vector are base64 encoded and
returned to you.
This can be used in situations similar to the `MessageVerifier`, but where
you don't want users to be able to determine the value of the payload.
## Example
secret_key_base = "072d1e0157c008193fe48a670cce031faa4e..."
encrypted_cookie_salt = "encrypted cookie"
encrypted_signed_cookie_salt = "signed encrypted cookie"
secret = KeyGenerator.generate(secret_key_base, encrypted_cookie_salt)
sign_secret = KeyGenerator.generate(secret_key_base, encrypted_signed_cookie_salt)
data = "José"
encrypted = MessageEncryptor.encrypt_and_sign(data, secret, sign_secret)
decrypted = MessageEncryptor.verify_and_decrypt(encrypted, secret, sign_secret)
decrypted # => "José"
"""
alias Plug.Crypto.MessageVerifier
@doc """
Encrypts and signs a message.
"""
def encrypt_and_sign(message, secret, sign_secret, cipher \\ :aes_cbc256)
when is_binary(message) and is_binary(secret) and is_binary(sign_secret) do
iv = :crypto.strong_rand_bytes(16)
message
|> pad_message
|> encrypt(cipher, secret, iv)
|> Base.encode64()
|> Kernel.<>("--#{Base.encode64(iv)}")
|> MessageVerifier.sign(sign_secret)
end
@doc """
Decrypts and verifies a message.
We need to verify the message in order to avoid padding attacks.
Reference: http://www.limited-entropy.com/padding-oracle-attacks
"""
def verify_and_decrypt(encrypted, secret, sign_secret, cipher \\ :aes_cbc256)
when is_binary(encrypted) and is_binary(secret) and is_binary(sign_secret) do
case MessageVerifier.verify(encrypted, sign_secret) do
{:ok, verified} ->
[encrypted, iv] = String.split(verified, "--")
case Base.decode64(encrypted) do
{:ok, encrypted} ->
case Base.decode64(iv) do
{:ok, iv} ->
encrypted |> decrypt(cipher, secret, iv) |> unpad_message
:error ->
:error
end
:error ->
:error
end
:error ->
:error
end
end
defp encrypt(message, cipher, secret, iv) do
:crypto.block_encrypt(cipher, trim_secret(secret), iv, message)
end
defp decrypt(encrypted, cipher, secret, iv) do
:crypto.block_decrypt(cipher, trim_secret(secret), iv, encrypted)
end
defp pad_message(msg) do
bytes_remaining = rem(byte_size(msg), 16)
padding_size = 16 - bytes_remaining
msg <> :binary.copy(<<padding_size>>, padding_size)
end
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>>, padding_size) do
{:ok, binary_part(msg, 0, msg_size - padding_size)}
else
:error
end
else
:error
end
end
defp trim_secret(secret) do
case byte_size(secret) do
large when large > 32 -> :binary.part(secret, 0, 32)
_ -> secret
end
end
end