Skip to content

Commit

Permalink
feature/2017.04.11_add-token-helpers (#8)
Browse files Browse the repository at this point in the history
* style

* add helpers for symmetric crypto tokens
- add module
- add test

* set log level to debug for tests

* use binary patters in tokens rather than string separator

* add some debug logging

* add more specific debug logging messages for timestamp validation

* add tests for tokens that are too old or in the future

* add initialization vector

* clean up tests

* add doc tests and update module docs

* add update/4 and update docs

* add test for update/4

* add universal_time helper

* remove Timex

* update deps

* update dialyxir

* fix dialyzer errors

* clean up code and fix dialyzer errors

* add applications so dialyzer can find them

* fix specs to make dialyzer happy

* fix compiler warnings

* update version to 0.4.0

* fix compiler warnings

* fix compiler warnings

* remove Pipe

* remove pipe

* remove pipe

* update changelog
  • Loading branch information
ntrepid8 authored Apr 12, 2017
1 parent 4487568 commit b571342
Show file tree
Hide file tree
Showing 14 changed files with 509 additions and 158 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Log

## v0.4.0
- Enhancements
- add helpers for symmetric crypto tokens
- cleanup various & sundry dialyzer/compiler warnings
- Contributors
- [ntrepid8](https://github.com/ntrepid8)

## v0.3.0
- Enhancements
- add CBC mode
Expand Down
47 changes: 23 additions & 24 deletions lib/ex_crypto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule ExCrypto do
AES in GCM and CBC mode. The ExCrypto module attempts to reduce complexity by providing
some sane default values for common operations.
"""
@epoch :calendar.datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}})
@aes_block_size 16
@iv_bit_length 128
@bitlength_error "IV must be exactly 128 bits and key must be exactly 128, 192 or 256 bits"
Expand All @@ -17,14 +18,6 @@ defmodule ExCrypto do
end
end

use Pipe

defmacrop pipe_ok(pipes) do
quote do
pipe_matching(x, {:ok, x}, unquote(pipes))
end
end

defp normalize_error(kind, error, key_and_iv \\ nil) do
key_error = test_key_and_iv_bitlength(key_and_iv)
cond do
Expand All @@ -37,16 +30,12 @@ defmodule ExCrypto do
end
end

defp detail_normalize_error(kind, error) do
{kind, Exception.normalize(kind, error), System.stacktrace}
end

defp test_key_and_iv_bitlength(nil), do: nil
defp test_key_and_iv_bitlength({key, iv}) when bit_size(iv) != 128, do: {:error, @bitlength_error}
defp test_key_and_iv_bitlength({key, iv}) when rem(bit_size(key), 128) == 0, do: nil
defp test_key_and_iv_bitlength({key, iv}) when rem(bit_size(key), 192) == 0, do: nil
defp test_key_and_iv_bitlength({key, iv}) when rem(bit_size(key), 256) == 0, do: nil
defp test_key_and_iv_bitlength({key, iv}), do: {:error, @bitlength_error}
defp test_key_and_iv_bitlength({_key, iv}) when bit_size(iv) != @iv_bit_length, do: {:error, @bitlength_error}
defp test_key_and_iv_bitlength({key, _iv}) when rem(bit_size(key), 128) == 0, do: nil
defp test_key_and_iv_bitlength({key, _iv}) when rem(bit_size(key), 192) == 0, do: nil
defp test_key_and_iv_bitlength({key, _iv}) when rem(bit_size(key), 256) == 0, do: nil
defp test_key_and_iv_bitlength({_key, _iv}), do: {:error, @bitlength_error}

@doc """
Returns random characters. Each character represents 6 bits of entropy.
Expand All @@ -73,9 +62,11 @@ defmodule ExCrypto do
block_chars = 4
block_count = div(num_chars, block_chars)
block_partial = rem(num_chars, block_chars)
if block_partial > 0 do
block_count = block_count + 1
end
block_count =
case block_partial > 0 do
true -> block_count + 1
false -> block_count
end
rand_string = Base.url_encode64(:crypto.strong_rand_bytes(block_count * block_bytes))
String.slice(rand_string, 0, num_chars)
end
Expand Down Expand Up @@ -184,11 +175,11 @@ defmodule ExCrypto do
@spec generate_aes_key(atom, atom) :: {:ok, binary} | {:error, binary}
def generate_aes_key(key_type, key_format) do
case {key_type, key_format} do
{:aes_128, :base64} -> pipe_ok rand_bytes(16) |> url_encode64
{:aes_128, :base64} -> rand_bytes!(16) |> url_encode64
{:aes_128, :bytes} -> rand_bytes(16)
{:aes_192, :base64} -> pipe_ok rand_bytes(24) |> url_encode64
{:aes_192, :base64} -> rand_bytes!(24) |> url_encode64
{:aes_192, :bytes} -> rand_bytes(24)
{:aes_256, :base64} -> pipe_ok rand_bytes(32) |> url_encode64
{:aes_256, :base64} -> rand_bytes!(32) |> url_encode64
{:aes_256, :bytes} -> rand_bytes(32)
_ -> {:error, "invalid key_type/key_format"}
end
Expand Down Expand Up @@ -247,7 +238,7 @@ defmodule ExCrypto do
true
"""
@spec encrypt(binary, binary, binary) :: {:ok, {binary, {binary, binary}}} | {:error, binary}
@spec encrypt(binary, binary) :: {:ok, {binary, binary}} | {:error, binary}
def encrypt(key, clear_text) do
{:ok, initialization_vector} = rand_bytes(16) # new 128 bit random initialization_vector
_encrypt(key, initialization_vector, pad(clear_text, @aes_block_size), :aes_cbc256)
Expand Down Expand Up @@ -277,6 +268,9 @@ defmodule ExCrypto do
true
"""
@spec encrypt(binary, binary, %{initialization_vector: binary}) :: {:ok, {binary, {binary, binary, binary}}} |
{:ok, {binary, binary}} |
{:error, any}
def encrypt(key, clear_text, %{initialization_vector: initialization_vector}) do
_encrypt(key, initialization_vector, pad(clear_text, @aes_block_size), :aes_cbc256)
catch
Expand Down Expand Up @@ -438,4 +432,9 @@ defmodule ExCrypto do
{:ok, {iv, cipher_text, cipher_tag}}
end

@doc false
def universal_time(:unix) do
:calendar.datetime_to_gregorian_seconds(:calendar.universal_time()) - @epoch
end

end
4 changes: 2 additions & 2 deletions lib/ex_crypto/ex_entropy.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ defmodule ExEntropy do
accumulated_occurances = count_occurances(val_accumulator, val_list)

# transform the map of occurrence counts into a list
ao_list = Enum.map(accumulated_occurances, fn {k, v} -> v end)
ao_list = Enum.map(accumulated_occurances, fn {_k, v} -> v end)

# compute Shannon's entropy
shannon_entropy_0(0, length(val_list), length(ao_list), ao_list)
Expand All @@ -30,7 +30,7 @@ defmodule ExEntropy do
shannon_entropy(value, 8) # byte blocks by default
end

defp shannon_entropy_0(entropy, block_count, block_range, []) do
defp shannon_entropy_0(entropy, _block_count, _block_range, []) do
entropy
end

Expand Down
16 changes: 12 additions & 4 deletions lib/ex_crypto/hash.ex
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
defmodule ExCrypto.Hash do
def sha256(data) do
{:ok, :crypto.hash(:sha256, data)}
try do
{:ok, :crypto.hash(:sha256, data)}
rescue
e in ArgumentError -> {:error, e}
end
end

def sha256!(data) do
case sha256(data) do
{:ok, digest} -> digest
{:error, reason} -> raise "sha256 error"
{:error, reason} -> raise "sha256 error: #{inspect reason}"
end
end

def sha512(data) do
{:ok, :crypto.hash(:sha512, data)}
try do
{:ok, :crypto.hash(:sha512, data)}
rescue
e in ArgumentError -> {:error, e}
end
end

def sha512!(data) do
case sha512(data) do
{:ok, digest} -> digest
{:error, reason} -> raise "sha512 error"
{:error, reason} -> raise "sha512 error: #{inspect reason}"
end
end
end
10 changes: 7 additions & 3 deletions lib/ex_crypto/hmac.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ defmodule ExCrypto.HMAC do
end

def hmac(data, key, [type: :sha256]) do
{:ok, :crypto.hmac(:sha256, key, data)}
try do
{:ok, :crypto.hmac(:sha256, key, data)}
rescue
e in ArgumentError -> {:error, e}
end
end

def verify_hmac(data, key, other_mac) do
Expand All @@ -28,5 +32,5 @@ defmodule ExCrypto.HMAC do
{:error, reason} -> {:error, reason}
end
end
end

end
Loading

0 comments on commit b571342

Please sign in to comment.