Skip to content

Commit

Permalink
Initial work on MessageVerifier
Browse files Browse the repository at this point in the history
  • Loading branch information
José Valim committed Aug 24, 2012
1 parent b222861 commit 28895e9
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 12 deletions.
1 change: 1 addition & 0 deletions lib/dynamo.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
defmodule Dynamo do
def start do
:application.start(:mimetypes)
:application.start(:crypto)
:application.start(:dynamo)
end
end
2 changes: 1 addition & 1 deletion lib/dynamo/app.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ defmodule Dynamo.App do
@dynamo_app true
@before_compile unquote(__MODULE__)

use Dynamo.Support.Once
use Dynamo.Utils.Once

use_once Dynamo.App.Config
use_once Dynamo.App.NotFound
Expand Down
2 changes: 1 addition & 1 deletion lib/dynamo/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ defmodule Dynamo.Router do
raise "use Dynamo.App after Dynamo.Router"
end

use Dynamo.Support.Once
use Dynamo.Utils.Once

use_once Dynamo.Router.Base
use_once Dynamo.Router.Callbacks
Expand Down
63 changes: 63 additions & 0 deletions lib/dynamo/utils/message_verifier.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
defmodule Dynamo.Utils.MessageVerifier do
@moduledoc """
`MessageVerifier` makes it easy to generate and verify messages
which are signed to prevent tampering.
For example, the session store uses this verifier to send data
to the client. Although the data can be read by the client, he
cannot tamper it.
"""

@doc """
Decodes and verifies the encoded binary was not tampared with.
"""
def verify(encoded, secret) do
case Binary.split(encoded, "--") do
[content, digest] when content != "" and digest != "" ->
if secure_compare(digest(secret, content), digest) do
{ :ok, content /> :base64.decode /> binary_to_term }
else
:error
end
_ ->
:error
end
end

@doc """
Generates an encoded and signed binary for the given term.
"""
def generate(term, secret) do
encoded = term /> term_to_binary /> :base64.encode
encoded <> "--" <> digest(secret, encoded)
end

defp digest(secret, data) do
<<mac | 160 - :integer>> = :crypto.sha_mac(secret, data)
:erlang.integer_to_list(mac, 16) /> list_to_binary
end

@doc """
Comapres the two binaries completely, byte by byte,
to avoid timing attacks.
"""
def secure_compare(left, right) do
if size(left) == size(right) do
compare_each(left, right, true)
else
false
end
end

defp compare_each(<<h, left | :binary>>, <<h, right | :binary>>, acc) do
compare_each(left, right, acc)
end

defp compare_each(<<_, left | :binary>>, <<_, right | :binary>>, _acc) do
compare_each(left, right, false)
end

defp compare_each(<<>>, <<>>, acc) do
acc
end
end
2 changes: 1 addition & 1 deletion lib/dynamo/support/once.ex → lib/dynamo/utils/once.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule Dynamo.Support.Once do
defmodule Dynamo.Utils.Once do
@moduledoc """
A convenience that allows a module to be used
just once via `use_once`.
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ defmodule Dynamo.Mixfile do
end

def application do
[applications: [:mimetypes]]
[applications: [:crypto, :mimetypes]]
end
end
29 changes: 29 additions & 0 deletions test/dynamo/utils/message_verifier_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Code.require_file "../../../test_helper.exs", __FILE__

defmodule Dynamo.Utils.MessageVerifierTest do
use ExUnit.Case

alias Dynamo.Utils.MessageVerifier, as: MV

test "generates a signed message" do
[content, encoded] = Binary.split MV.generate(:hello, "secret"), "--"
assert content /> :base64.decode /> binary_to_term == :hello
assert size(encoded) == 40
end

test "verifies a signed message" do
signed = MV.generate(:hello, "secret")
assert MV.verify(signed, "secret") == { :ok, :hello }
end

test "does not verify a signed message if secret changed" do
signed = MV.generate(:hello, "secret")
assert MV.verify(signed, "secreto") == :error
end

test "does not verify a tampered message" do
[_, encoded] = Binary.split MV.generate(:hello, "secret"), "--"
content = :bye /> term_to_binary /> :base64.encode
assert MV.verify(content <> "--" <> encoded, "secret") == :error
end
end
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
Code.require_file "../../../test_helper.exs", __FILE__

defmodule Dynamo.Support.Incrementer do
defmodule Dynamo.Utils.Incrementer do
defmacro __using__(_) do
quote do
@counter 2
end
end
end

defmodule Dynamo.Support.OnceTest do
defmodule Dynamo.Utils.OnceTest do
use ExUnit.Case

Module.register_attribute __MODULE__, :counter, accumulate: true
@counter 1

use Dynamo.Support.Once
use_once Dynamo.Support.Incrementer
use_once Dynamo.Support.Incrementer
use Dynamo.Utils.Once
use_once Dynamo.Utils.Incrementer
use_once Dynamo.Utils.Incrementer

use Dynamo.Support.Once
use_once Dynamo.Support.Incrementer
use_once Dynamo.Support.Incrementer
use Dynamo.Utils.Once
use_once Dynamo.Utils.Incrementer
use_once Dynamo.Utils.Incrementer

def snapshot do
@counter
Expand Down

0 comments on commit 28895e9

Please sign in to comment.