diff --git a/lib/ex_crypto.ex b/lib/ex_crypto.ex index f4e0b10..7e52616 100644 --- a/lib/ex_crypto.ex +++ b/lib/ex_crypto.ex @@ -20,18 +20,24 @@ defmodule ExCrypto do defp normalize_error(kind, error, key_and_iv \\ nil) do key_error = test_key_and_iv_bitlength(key_and_iv) + cond do key_error -> key_error + %{message: message} = Exception.normalize(kind, error) -> {:error, message} + x = Exception.normalize(kind, error) -> - {kind, x, System.stacktrace} + {kind, x, System.stacktrace()} end end defp test_key_and_iv_bitlength(nil), do: nil - 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 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 @@ -56,17 +62,19 @@ defmodule ExCrypto do iex> assert(String.length(rand_string) == 44) true """ - @spec rand_chars(integer) :: String.t + @spec rand_chars(integer) :: String.t() def rand_chars(num_chars) do block_bytes = 3 block_chars = 4 block_count = div(num_chars, block_chars) block_partial = rem(num_chars, block_chars) + 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 @@ -99,7 +107,7 @@ defmodule ExCrypto do """ @spec rand_int(integer, integer) :: integer def rand_int(low, high) do - :crypto.rand_uniform(low, high) + low + :rand.uniform(high - low + 1) - 1 end @doc """ @@ -204,22 +212,22 @@ defmodule ExCrypto do iex> auth_data = "my-auth-data" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) iex> {:ok, iv} = ExCrypto.rand_bytes(16) - iex> {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, iv, clear_text) - iex> {iv, cipher_text, cipher_tag} = payload + iex> {:ok, {_ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, iv, clear_text) + iex> {_iv, cipher_text, cipher_tag} = payload iex> assert(is_bitstring(cipher_text)) true iex> assert(bit_size(cipher_tag) == 128) true """ - @spec encrypt(binary, binary, binary, binary) :: {:ok, {binary, {binary, binary, binary}}} | {:error, binary} + @spec encrypt(binary, binary, binary, binary) :: + {:ok, {binary, {binary, binary, binary}}} | {:error, binary} def encrypt(key, authentication_data, initialization_vector, clear_text) do _encrypt(key, initialization_vector, {authentication_data, clear_text}, :aes_gcm) catch kind, error -> normalize_error(kind, error) end - @doc """ Encrypt a `binary` with AES in CBC mode. @@ -233,14 +241,15 @@ defmodule ExCrypto do iex> clear_text = "my-clear-text" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) - iex> {:ok, {iv, cipher_text}} = ExCrypto.encrypt(aes_256_key, clear_text) + iex> {:ok, {_iv, cipher_text}} = ExCrypto.encrypt(aes_256_key, clear_text) iex> assert(is_bitstring(cipher_text)) true """ @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 + # new 128 bit random initialization_vector + {:ok, initialization_vector} = rand_bytes(16) _encrypt(key, initialization_vector, pad(clear_text, @aes_block_size), :aes_cbc256) catch kind, error -> @@ -248,7 +257,6 @@ defmodule ExCrypto do normalize_error(kind, error, {key, initialization_vector}) end - @doc """ Encrypt a `binary` with AES in CBC mode providing explicit IV via map. @@ -263,41 +271,21 @@ defmodule ExCrypto do iex> clear_text = "my-clear-text" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) iex> {:ok, init_vec} = ExCrypto.rand_bytes(16) - iex> {:ok, {iv, cipher_text}} = ExCrypto.encrypt(aes_256_key, clear_text, %{initialization_vector: init_vec}) + iex> {:ok, {_iv, cipher_text}} = ExCrypto.encrypt(aes_256_key, clear_text, %{initialization_vector: init_vec}) iex> assert(is_bitstring(cipher_text)) true """ - @spec encrypt(binary, binary, %{initialization_vector: binary}) :: {:ok, {binary, {binary, binary, binary}}} | - {:ok, {binary, binary}} | - {:error, any} + @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 kind, error -> normalize_error(kind, error, {key, initialization_vector}) end - defp _encrypt(key, initialization_vector, encryption_payload, algorithm) do - case :crypto.block_encrypt(algorithm, key, initialization_vector, encryption_payload) do - {cipher_text, cipher_tag} -> - {authentication_data, _clear_text} = encryption_payload - {:ok, {authentication_data, {initialization_vector, cipher_text, cipher_tag}}} - <> -> - {:ok, {initialization_vector, cipher_text }} - x -> {:error, x} - end - end - - def pad(data, block_size) do - to_add = block_size - rem(byte_size(data), block_size) - data <> to_string(:string.chars(to_add, to_add)) - end - - def unpad(data) do - to_remove = :binary.last(data) - :binary.part(data, 0, byte_size(data) - to_remove) - end - @doc """ Same as `encrypt/4` except the `initialization_vector` is automatically generated. @@ -309,20 +297,46 @@ defmodule ExCrypto do iex> clear_text = "my-clear-text" iex> auth_data = "my-auth-data" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) - iex> {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) - iex> {init_vec, cipher_text, cipher_tag} = payload + iex> {:ok, {_ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) + iex> {_init_vec, cipher_text, cipher_tag} = payload iex> assert(is_bitstring(cipher_text)) true iex> assert(bit_size(cipher_tag) == 128) true """ - @spec encrypt(binary, binary, binary) :: {:ok, {binary, {binary, binary, binary}}} | {:error, binary} + @spec encrypt(binary, binary, binary) :: + {:ok, {binary, {binary, binary, binary}}} | {:error, binary} def encrypt(key, authentication_data, clear_text) do - {:ok, initialization_vector} = rand_bytes(16) # new 128 bit random initialization_vector + # new 128 bit random initialization_vector + {:ok, initialization_vector} = rand_bytes(16) _encrypt(key, initialization_vector, {authentication_data, clear_text}, :aes_gcm) end + defp _encrypt(key, initialization_vector, encryption_payload, algorithm) do + case :crypto.block_encrypt(algorithm, key, initialization_vector, encryption_payload) do + {cipher_text, cipher_tag} -> + {authentication_data, _clear_text} = encryption_payload + {:ok, {authentication_data, {initialization_vector, cipher_text, cipher_tag}}} + + <> -> + {:ok, {initialization_vector, cipher_text}} + + x -> + {:error, x} + end + end + + def pad(data, block_size) do + to_add = block_size - rem(byte_size(data), block_size) + data <> to_string(:string.chars(to_add, to_add)) + end + + def unpad(data) do + to_remove = :binary.last(data) + :binary.part(data, 0, byte_size(data) - to_remove) + end + @doc """ Returns a clear-text string decrypted with AES in GCM mode. @@ -335,7 +349,7 @@ defmodule ExCrypto do iex> clear_text = "my-clear-text" iex> auth_data = "my-auth-data" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) - iex> {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) + iex> {:ok, {_ad, payload}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) iex> {init_vec, cipher_text, cipher_tag} = payload iex> {:ok, val} = ExCrypto.decrypt(aes_256_key, auth_data, init_vec, cipher_text, cipher_tag) iex> assert(val == clear_text) @@ -346,7 +360,6 @@ defmodule ExCrypto do _decrypt(key, initialization_vector, {authentication_data, cipher_text, cipher_tag}, :aes_gcm) end - @doc """ Returns a clear-text string decrypted with AES256 in CBC mode. @@ -392,7 +405,7 @@ defmodule ExCrypto do iex> clear_text = "my-clear-text" iex> auth_data = "my-auth-data" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) - iex> {:ok, {ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) + iex> {:ok, {_ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) iex> {:ok, encoded_payload} = ExCrypto.encode_payload(init_vec, cipher_text, cipher_tag) iex> assert(String.valid?(encoded_payload)) true @@ -410,7 +423,7 @@ defmodule ExCrypto do iex> clear_text = "my-clear-text" iex> auth_data = "my-auth-data" iex> {:ok, aes_256_key} = ExCrypto.generate_aes_key(:aes_256, :bytes) - iex> {:ok, {ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) + iex> {:ok, {_ad, {init_vec, cipher_text, cipher_tag}}} = ExCrypto.encrypt(aes_256_key, auth_data, clear_text) iex> {:ok, encoded_payload} = ExCrypto.encode_payload(init_vec, cipher_text, cipher_tag) iex> assert(String.valid?(encoded_payload)) true @@ -427,7 +440,7 @@ defmodule ExCrypto do {:ok, decoded_parts} = Base.url_decode64(encoded_parts) decoded_length = byte_size(decoded_parts) iv = Kernel.binary_part(decoded_parts, 0, 16) - cipher_text = Kernel.binary_part(decoded_parts, 16, (decoded_length-32)) + cipher_text = Kernel.binary_part(decoded_parts, 16, decoded_length - 32) cipher_tag = Kernel.binary_part(decoded_parts, decoded_length, -16) {:ok, {iv, cipher_text, cipher_tag}} end @@ -436,5 +449,4 @@ defmodule ExCrypto do def universal_time(:unix) do :calendar.datetime_to_gregorian_seconds(:calendar.universal_time()) - @epoch end - end diff --git a/lib/ex_crypto/ex_entropy.ex b/lib/ex_crypto/ex_entropy.ex index 4d27a71..a6e4f97 100644 --- a/lib/ex_crypto/ex_entropy.ex +++ b/lib/ex_crypto/ex_entropy.ex @@ -1,5 +1,4 @@ defmodule ExEntropy do - @doc """ Compute the Shannon entropy of a binary value. @@ -13,7 +12,7 @@ defmodule ExEntropy do # convert the binary value into a list with exponent as one of [1, 8] val_list = gen_val_list(value, exponent) - val_range = round :math.pow(2, exponent) - 1 + val_range = round(:math.pow(2, exponent) - 1) val_accumulator = for x <- 0..val_range, into: %{}, do: {x, 0} # accumulate occurrence counts @@ -27,19 +26,22 @@ defmodule ExEntropy do end def shannon_entropy(value) when is_binary(value) do - shannon_entropy(value, 8) # byte blocks by default + # byte blocks by default + shannon_entropy(value, 8) end defp shannon_entropy_0(entropy, _block_count, _block_range, []) do entropy end - defp shannon_entropy_0(entropy, block_count, block_range, [h|t]) do + defp shannon_entropy_0(entropy, block_count, block_range, [h | t]) do case h do - 0 -> shannon_entropy_0(entropy, block_count, block_range, t) - _ -> + 0 -> + shannon_entropy_0(entropy, block_count, block_range, t) + + _ -> p = 1.0 * h / block_count - udpated_entropy = entropy - (p * (:math.log(p) / :math.log(block_range))) + udpated_entropy = entropy - p * (:math.log(p) / :math.log(block_range)) shannon_entropy_0(udpated_entropy, block_count, block_range, t) end end @@ -48,19 +50,32 @@ defmodule ExEntropy do accumulator end - defp count_occurances(accumulator, [h|t]) do + defp count_occurances(accumulator, [h | t]) do c_0 = Map.get(accumulator, h, 0) - count_occurances(Map.put(accumulator, h, c_0+1), t) + count_occurances(Map.put(accumulator, h, c_0 + 1), t) end defp gen_val_list(value, exponent) do case exponent do - 1 -> for << x::1 <- value >>, do: x # bits - 8 -> for << x::8 <- value >>, do: x # bytes - 10 -> for << x::10 <- value >>, do: x # kilobytes - 16 -> for << x::16 <- value >>, do: x # hex - 20 -> for << x::20 <- value >>, do: x # megabytes + # bits + 1 -> + for <>, do: x + + # bytes + 8 -> + for <>, do: x + + # kilobytes + 10 -> + for <>, do: x + + # hex + 16 -> + for <>, do: x + + # megabytes + 20 -> + for <>, do: x end end - -end \ No newline at end of file +end diff --git a/lib/ex_crypto/hash.ex b/lib/ex_crypto/hash.ex index 5b6c264..a99b8fd 100644 --- a/lib/ex_crypto/hash.ex +++ b/lib/ex_crypto/hash.ex @@ -10,7 +10,7 @@ defmodule ExCrypto.Hash do def sha256!(data) do case sha256(data) do {:ok, digest} -> digest - {:error, reason} -> raise "sha256 error: #{inspect reason}" + {:error, reason} -> raise "sha256 error: #{inspect(reason)}" end end @@ -25,7 +25,7 @@ defmodule ExCrypto.Hash do def sha512!(data) do case sha512(data) do {:ok, digest} -> digest - {:error, reason} -> raise "sha512 error: #{inspect reason}" + {:error, reason} -> raise "sha512 error: #{inspect(reason)}" end end end diff --git a/lib/ex_crypto/hmac.ex b/lib/ex_crypto/hmac.ex index 1195d23..90bd7fe 100644 --- a/lib/ex_crypto/hmac.ex +++ b/lib/ex_crypto/hmac.ex @@ -1,5 +1,4 @@ defmodule ExCrypto.HMAC do - def hmac(data, key) do hmac(data, key, type: :sha256) end @@ -11,7 +10,7 @@ defmodule ExCrypto.HMAC do end end - def hmac(data, key, [type: :sha256]) do + def hmac(data, key, type: :sha256) do try do {:ok, :crypto.hmac(:sha256, key, data)} rescue @@ -23,14 +22,16 @@ defmodule ExCrypto.HMAC do verify_hmac(data, key, other_mac, type: :sha256) end - def verify_hmac(data, key, other_mac, [type: :sha256]) do + def verify_hmac(data, key, other_mac, type: :sha256) do case hmac(data, key, type: :sha256) do - {:ok, my_mac} -> case my_mac === other_mac do - true -> {:ok, true} - false -> {:ok, false} - end - {:error, reason} -> {:error, reason} + {:ok, my_mac} -> + case my_mac === other_mac do + true -> {:ok, true} + false -> {:ok, false} + end + + {:error, reason} -> + {:error, reason} end end - end diff --git a/lib/ex_crypto/token.ex b/lib/ex_crypto/token.ex index 41cf626..76ba4df 100644 --- a/lib/ex_crypto/token.ex +++ b/lib/ex_crypto/token.ex @@ -21,7 +21,7 @@ defmodule ExCrypto.Token do iex> ttl = (15 * 60) # 15 minute TTL (in seconds) iex> {:ok, verified_payload} = ExCrypto.Token.verify(token, secret, ttl) iex> decoded_verified_payload = Poison.decode!(verified_payload) - iex> decoded_verified_payload == payload + iex> assert(decoded_verified_payload == payload) iex> Map.get(decoded_verified_payload, "user_id") 12345 @@ -38,14 +38,15 @@ defmodule ExCrypto.Token do require Logger # type specs - @type option :: {:divider, String.t} | - {:date_time, {{integer, integer, integer}, {integer, integer, integer}}} + @type option :: + {:divider, String.t()} + | {:date_time, {{integer, integer, integer}, {integer, integer, integer}}} @type options :: [option] @type token :: binary @type payload :: binary @epoch :calendar.datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}) - @fifteen_min_in_seconds 15*60 + @fifteen_min_in_seconds 15 * 60 @doc """ Generate a signed token that carries of timestamp of when it was signed. @@ -64,10 +65,12 @@ defmodule ExCrypto.Token do sig_dt = Keyword.get(opts, :date_time, :calendar.universal_time()) sig_ts = dt_to_ts(sig_dt) {:ok, iv} = ExCrypto.rand_bytes(16) + case HMAC.hmac([iv, "#{sig_ts}", payload], secret) do {:ok, mac} -> - encoded_token = encode_token([iv, payload, sig_ts, mac]) + encoded_token = encode_token([iv, payload, sig_ts, mac]) {:ok, encoded_token} + {:error, reason} -> {:error, reason} end @@ -106,11 +109,11 @@ defmodule ExCrypto.Token do sig_ts_challenge = dt_to_ts(sig_dt_challenge) with {:ok, [iv, payload, sig_ts_raw, mac]} <- decode_token(token), - {:ok, sig_ts} <- validate_sig_ts(sig_ts_raw, ttl, sig_ts_challenge) - do + {:ok, sig_ts} <- validate_sig_ts(sig_ts_raw, ttl, sig_ts_challenge) do case HMAC.verify_hmac([iv, "#{sig_ts}", payload], secret, mac) do {:ok, true} -> {:ok, payload} + _ -> Logger.debug("HMAC failed to validate") {:error, :invalid_token} @@ -169,7 +172,7 @@ defmodule ExCrypto.Token do # first verify the token to ensure it's good to start with with {:ok, payload} <- verify(token, secret, ttl, opts), {:ok, update_token} <- create(payload, secret, opts), - do: {:ok, {update_token, payload}} + do: {:ok, {update_token, payload}} end @doc """ @@ -191,6 +194,7 @@ defmodule ExCrypto.Token do case token do <<_mac::bits-size(256), _iv::bits-size(128), _sig_ts::integer-size(64), _payload::binary>> -> true + _other -> false end @@ -207,15 +211,19 @@ defmodule ExCrypto.Token do case Base.url_decode64(encoded_token) do {:ok, bin_token} -> decode_token_0(bin_token) + _ -> Logger.debug("token was not encoded with valid URL safe base64 encoding") {:error, :invalid_token} end end - defp decode_token_0(<>) do + defp decode_token_0( + <> + ) do {:ok, [iv, payload, sig_ts, mac]} end + defp decode_token_0(_invalid_token) do Logger.debug("token does not have the correct binary structure") {:error, :invalid_token} @@ -224,20 +232,19 @@ defmodule ExCrypto.Token do defp validate_sig_ts(sig_ts, ttl, now_ts) do cond do # too old - (sig_ts + ttl) < now_ts -> + sig_ts + ttl < now_ts -> Logger.debug("timestamp #{sig_ts} with ttl #{ttl} is too old") {:error, :invalid_token} # in future - (now_ts + @fifteen_min_in_seconds) < sig_ts -> + now_ts + @fifteen_min_in_seconds < sig_ts -> Logger.debug("timestamp #{sig_ts} with ttl #{ttl} is in the future") {:error, :invalid_token} # valid ## signature timestamp plus TTL is in the future (not expired) - (sig_ts + ttl) > now_ts ## signature timestamp alone is not more than 15 minutes in the future (sanity) - and sig_ts < (now_ts + @fifteen_min_in_seconds) -> + sig_ts + ttl > now_ts and sig_ts < now_ts + @fifteen_min_in_seconds -> {:ok, sig_ts} # signature timestamp is outside the valid range diff --git a/lib/ex_public_key.ex b/lib/ex_public_key.ex index edb5637..419e161 100644 --- a/lib/ex_public_key.ex +++ b/lib/ex_public_key.ex @@ -22,12 +22,12 @@ defmodule ExPublicKey do case Exception.normalize(kind, error) do %{message: message} -> {:error, message} + x -> - {kind, x, System.stacktrace} + {kind, x, System.stacktrace()} end end - @doc """ Loads PEM string from the specified file path and returns a `ExPublicKey.RSAPrivateKey` or a `ExPublicKey.RSAPublicKey` key. Optionally, a passphrase can be given to decode the PEM certificate. @@ -42,6 +42,7 @@ defmodule ExPublicKey do case File.read(file_path) do {:ok, key_string} -> ExPublicKey.loads(key_string, passphrase) + {:error, reason} -> {:error, reason} end @@ -62,6 +63,7 @@ defmodule ExPublicKey do case load(file_path, passphrase) do {:ok, key} -> key + {:error, reason} -> raise ExCrypto.Error, reason: reason end @@ -87,9 +89,10 @@ defmodule ExPublicKey do """ def loads(pem_string, passphrase \\ nil) do pem_entries = :public_key.pem_decode(pem_string) + with {:ok, pem_entry} <- validate_pem_length(pem_entries), {:ok, rsa_key} <- load_pem_entry(pem_entry, passphrase), - do: sort_key_tup(rsa_key) + do: sort_key_tup(rsa_key) end @doc """ @@ -104,12 +107,13 @@ defmodule ExPublicKey do case loads(pem_string, passphrase) do {:ok, key} -> key + {:error, reason} -> raise ExCrypto.Error, reason: reason end end - defp load_pem_entry(pem_entry, passphrase \\ nil) do + defp load_pem_entry(pem_entry, passphrase) do cond do is_binary(passphrase) -> load_pem_entry(pem_entry, String.to_charlist(passphrase)) @@ -129,10 +133,15 @@ defmodule ExPublicKey do case elem(key_tup, 0) do :RSAPrivateKey -> {:ok, ExPublicKey.RSAPrivateKey.from_sequence(key_tup)} + :RSAPublicKey -> {:ok, ExPublicKey.RSAPublicKey.from_sequence(key_tup)} + x -> - {:error, "invalid argument, expected one of[ExPublicKey.RSAPublicKey, ExPublicKey.RSAPrivateKey], found: #{x}"} + {:error, + "invalid argument, expected one of[ExPublicKey.RSAPublicKey, ExPublicKey.RSAPrivateKey], found: #{ + x + }"} end end @@ -145,7 +154,7 @@ defmodule ExPublicKey do def sign(msg, sha, private_key) do with {:ok, priv_key_sequence} <- ExPublicKey.RSAPrivateKey.as_sequence(private_key), - do: sign_0(priv_key_sequence, msg, sha) + do: sign_0(priv_key_sequence, msg, sha) end def sign(msg, private_key) do @@ -160,7 +169,7 @@ defmodule ExPublicKey do def verify(msg, sha, signature, public_key) do with {:ok, pub_key_sequence} <- ExPublicKey.RSAPublicKey.as_sequence(public_key), - do: verify_0(pub_key_sequence, msg, sha, signature) + do: verify_0(pub_key_sequence, msg, sha, signature) end def verify(msg, signature, public_key) do @@ -176,10 +185,11 @@ defmodule ExPublicKey do def encrypt_private(clear_text, private_key, opts \\ []) do url_safe = Keyword.get(opts, :url_safe, true) + with {:ok, priv_key_sequence} <- ExPublicKey.RSAPrivateKey.as_sequence(private_key), {:ok, cipher_bytes} <- encrypt_private_0(priv_key_sequence, clear_text), - encoded_cipher_text = encode(cipher_bytes, url_safe), - do: {:ok, encoded_cipher_text} + encoded_cipher_text = encode(cipher_bytes, url_safe), + do: {:ok, encoded_cipher_text} end defp encrypt_public_0(rsa_pub_key_seq, clear_text) do @@ -191,10 +201,11 @@ defmodule ExPublicKey do def encrypt_public(clear_text, public_key, opts \\ []) do url_safe = Keyword.get(opts, :url_safe, true) + with {:ok, pub_key_sequence} <- ExPublicKey.RSAPublicKey.as_sequence(public_key), {:ok, cipher_bytes} <- encrypt_public_0(pub_key_sequence, clear_text), - encoded_cipher_text = encode(cipher_bytes, url_safe), - do: {:ok, encoded_cipher_text} + encoded_cipher_text = encode(cipher_bytes, url_safe), + do: {:ok, encoded_cipher_text} end defp decrypt_private_0(cipher_bytes, private_key) do @@ -213,9 +224,11 @@ defmodule ExPublicKey do def decrypt_private(cipher_text, private_key, opts \\ []) do url_safe = Keyword.get(opts, :url_safe, true) + with {:ok, decoded_cipher_text} <- decode(cipher_text, url_safe), - {:ok, [cipher_bytes, rsa_priv_key_seq]} <- decrypt_private_0(decoded_cipher_text, private_key), - do: decrypt_private_1([cipher_bytes, rsa_priv_key_seq]) + {:ok, [cipher_bytes, rsa_priv_key_seq]} <- + decrypt_private_0(decoded_cipher_text, private_key), + do: decrypt_private_1([cipher_bytes, rsa_priv_key_seq]) end defp decrypt_public_0(cipher_bytes, public_key) do @@ -234,26 +247,19 @@ defmodule ExPublicKey do def decrypt_public(cipher_text, public_key, opts \\ []) do url_safe = Keyword.get(opts, :url_safe, true) + with {:ok, decoded_cipher_text} <- decode(cipher_text, url_safe), - {:ok, [cipher_bytes, rsa_pub_key_seq]} <- decrypt_public_0(decoded_cipher_text, public_key), - do: decrypt_public_1([cipher_bytes, rsa_pub_key_seq]) + {:ok, [cipher_bytes, rsa_pub_key_seq]} <- + decrypt_public_0(decoded_cipher_text, public_key), + do: decrypt_public_1([cipher_bytes, rsa_pub_key_seq]) end def generate_key, do: generate_key(:rsa, 2048, 65537) def generate_key(bits), do: generate_key(:rsa, bits, 65537) def generate_key(bits, public_exp), do: generate_key(:rsa, bits, public_exp) - def generate_key(bits, public_exp), do: generate_key(:rsa, bits, public_exp) - def generate_key(:rsa, bits, public_exp), do: generate_key(:rsa, bits, public_exp, otp_has_rsa_gen_support()) - def generate_key(:rsa, bits, public_exp, false) do - # Fallback support for OTP 18 & 19. - generate_rsa_openssl_fallback(bits) - end - def generate_key(:rsa, bits, public_exp, true) do - new_rsa_key = - :public_key.generate_key({:rsa, bits, public_exp}) - |> ExPublicKey.RSAPrivateKey.from_sequence() - {:ok, new_rsa_key} - end + + def generate_key(:rsa, bits, public_exp), + do: generate_key(:rsa, bits, public_exp, otp_has_rsa_gen_support()) @doc """ Generate a new key. @@ -265,12 +271,25 @@ defmodule ExPublicKey do """ def generate_key(type, bits, public_exp) do - {:ok, :public_key.generate_key({type, bits, public_exp}) } + {:ok, :public_key.generate_key({type, bits, public_exp})} catch kind, error -> ExPublicKey.normalize_error(kind, error) end + def generate_key(:rsa, bits, _public_exp, false) do + # Fallback support for OTP 18 & 19. + generate_rsa_openssl_fallback(bits) + end + + def generate_key(:rsa, bits, public_exp, true) do + new_rsa_key = + :public_key.generate_key({:rsa, bits, public_exp}) + |> ExPublicKey.RSAPrivateKey.from_sequence() + + {:ok, new_rsa_key} + end + @doc """ Extract the public part of a private string and return the results as a ExPublicKey.RSAPublicKey struct. @@ -280,7 +299,10 @@ defmodule ExPublicKey do """ def public_key_from_private_key(private_key = %ExPublicKey.RSAPrivateKey{}) do - {:ok, ExPublicKey.RSAPublicKey.from_sequence({:RSAPublicKey, private_key.public_modulus, private_key.public_exponent})} + {:ok, + ExPublicKey.RSAPublicKey.from_sequence( + {:RSAPublicKey, private_key.public_modulus, private_key.public_exponent} + )} end @doc """ @@ -293,12 +315,12 @@ defmodule ExPublicKey do """ def pem_encode(key = %ExPublicKey.RSAPrivateKey{}) do with {:ok, key_sequence} <- ExPublicKey.RSAPrivateKey.as_sequence(key), - do: pem_entry_encode(key_sequence, :RSAPrivateKey) + do: pem_entry_encode(key_sequence, :RSAPrivateKey) end def pem_encode(key = %ExPublicKey.RSAPublicKey{}) do with {:ok, key_sequence} <- ExPublicKey.RSAPublicKey.as_sequence(key), - do: pem_entry_encode(key_sequence, :RSAPublicKey) + do: pem_entry_encode(key_sequence, :RSAPublicKey) end # Helpers @@ -313,6 +335,7 @@ defmodule ExPublicKey do defp decode(encoded_payload, _url_safe = true) do Base.url_decode64(encoded_payload) end + defp decode(encoded_payload, _url_safe = false) do Base.decode64(encoded_payload) end @@ -320,6 +343,7 @@ defmodule ExPublicKey do defp encode(payload_bytes, _url_safe = true) do Base.url_encode64(payload_bytes) end + defp encode(payload_bytes, _url_safe = false) do Base.encode64(payload_bytes) end @@ -329,7 +353,7 @@ defmodule ExPublicKey do Application.spec(:public_key, :vsn) |> Kernel.to_string() |> String.split(".") - |> Enum.map(fn(i) -> + |> Enum.map(fn i -> {i_int, _} = Integer.parse(i) i_int end) diff --git a/lib/ex_public_key/ex_rsa_private_key.ex b/lib/ex_public_key/ex_rsa_private_key.ex index 3cbaba3..6880476 100644 --- a/lib/ex_public_key/ex_rsa_private_key.ex +++ b/lib/ex_public_key/ex_rsa_private_key.ex @@ -1,33 +1,31 @@ defmodule ExPublicKey.RSAPrivateKey do - - defstruct [ - version: nil, - public_modulus: nil, - public_exponent: nil, - private_exponent: nil, - prime_one: nil, - prime_two: nil, - exponent_one: nil, - exponent_two: nil, - ctr_coefficient: nil, - other_prime_infos: nil, - ] + defstruct version: nil, + public_modulus: nil, + public_exponent: nil, + private_exponent: nil, + prime_one: nil, + prime_two: nil, + exponent_one: nil, + exponent_two: nil, + ctr_coefficient: nil, + other_prime_infos: nil @type t :: %ExPublicKey.RSAPrivateKey{ - version: atom, - public_modulus: integer, - public_exponent: integer, - private_exponent: integer, - prime_one: integer, - prime_two: integer, - exponent_one: integer, - exponent_two: integer, - ctr_coefficient: integer, - other_prime_infos: atom - } + version: atom, + public_modulus: integer, + public_exponent: integer, + private_exponent: integer, + prime_one: integer, + prime_two: integer, + exponent_one: integer, + exponent_two: integer, + ctr_coefficient: integer, + other_prime_infos: atom + } def from_sequence(rsa_key_seq) do - %ExPublicKey.RSAPrivateKey{} |> struct( + %ExPublicKey.RSAPrivateKey{} + |> struct( version: maybe_convert_version_to_atom(elem(rsa_key_seq, 1)), public_modulus: elem(rsa_key_seq, 2), public_exponent: elem(rsa_key_seq, 3), @@ -37,28 +35,30 @@ defmodule ExPublicKey.RSAPrivateKey do exponent_one: elem(rsa_key_seq, 7), exponent_two: elem(rsa_key_seq, 8), ctr_coefficient: elem(rsa_key_seq, 9), - other_prime_infos: elem(rsa_key_seq, 10), + other_prime_infos: elem(rsa_key_seq, 10) ) end def as_sequence(rsa_private_key) do case rsa_private_key do %__MODULE__{} -> - {:ok, { - :RSAPrivateKey, - Map.get(rsa_private_key, :version), - Map.get(rsa_private_key, :public_modulus), - Map.get(rsa_private_key, :public_exponent), - Map.get(rsa_private_key, :private_exponent), - Map.get(rsa_private_key, :prime_one), - Map.get(rsa_private_key, :prime_two), - Map.get(rsa_private_key, :exponent_one), - Map.get(rsa_private_key, :exponent_two), - Map.get(rsa_private_key, :ctr_coefficient), - Map.get(rsa_private_key, :other_prime_infos), - }} + {:ok, + { + :RSAPrivateKey, + Map.get(rsa_private_key, :version), + Map.get(rsa_private_key, :public_modulus), + Map.get(rsa_private_key, :public_exponent), + Map.get(rsa_private_key, :private_exponent), + Map.get(rsa_private_key, :prime_one), + Map.get(rsa_private_key, :prime_two), + Map.get(rsa_private_key, :exponent_one), + Map.get(rsa_private_key, :exponent_two), + Map.get(rsa_private_key, :ctr_coefficient), + Map.get(rsa_private_key, :other_prime_infos) + }} + _ -> - {:error, "invalid ExPublicKey.RSAPrivateKey: #{inspect rsa_private_key}"} + {:error, "invalid ExPublicKey.RSAPrivateKey: #{inspect(rsa_private_key)}"} end end @@ -67,5 +67,4 @@ defmodule ExPublicKey.RSAPrivateKey do # This conversion ensures it is always the symbol. defp maybe_convert_version_to_atom(0), do: :"two-prime" defp maybe_convert_version_to_atom(version), do: version - end diff --git a/lib/ex_public_key/ex_rsa_public_key.ex b/lib/ex_public_key/ex_rsa_public_key.ex index 94a46e2..b426bb1 100644 --- a/lib/ex_public_key/ex_rsa_public_key.ex +++ b/lib/ex_public_key/ex_rsa_public_key.ex @@ -1,19 +1,17 @@ defmodule ExPublicKey.RSAPublicKey do - - defstruct [ - version: nil, - public_modulus: nil, - public_exponent: nil, - ] + defstruct version: nil, + public_modulus: nil, + public_exponent: nil @type t :: %ExPublicKey.RSAPublicKey{ - version: atom, - public_modulus: integer, - public_exponent: integer - } + version: atom, + public_modulus: integer, + public_exponent: integer + } def from_sequence(rsa_key_seq) do - %ExPublicKey.RSAPublicKey{} |> struct( + %ExPublicKey.RSAPublicKey{} + |> struct( public_modulus: elem(rsa_key_seq, 1), public_exponent: elem(rsa_key_seq, 2) ) @@ -22,14 +20,15 @@ defmodule ExPublicKey.RSAPublicKey do def as_sequence(rsa_public_key) do case rsa_public_key do %ExPublicKey.RSAPublicKey{} -> - {:ok, { - :RSAPublicKey, - Map.get(rsa_public_key, :public_modulus), - Map.get(rsa_public_key, :public_exponent), - }} + {:ok, + { + :RSAPublicKey, + Map.get(rsa_public_key, :public_modulus), + Map.get(rsa_public_key, :public_exponent) + }} + _ -> {:error, "invalid ExPublicKey.RSAPublicKey: #{rsa_public_key}"} end end - end diff --git a/lib/exception.ex b/lib/exception.ex index 562cc77..0d7bbb7 100644 --- a/lib/exception.ex +++ b/lib/exception.ex @@ -1,5 +1,5 @@ defmodule ExCrypto.Error do - defexception [reason: nil] + defexception reason: nil def message(exception) do "ExCrypto.Error: #{exception.reason}" diff --git a/mix.exs b/mix.exs index bd3d134..fd3846b 100644 --- a/mix.exs +++ b/mix.exs @@ -2,23 +2,26 @@ defmodule ExCrypto.Mixfile do use Mix.Project def project do - [app: :ex_crypto, - version: "0.7.1", - name: "ExCrypto", - elixir: ">= 1.4.2", - description: description(), - package: package(), - deps: deps(), - docs: [extras: ["README.md"]] - ] + [ + app: :ex_crypto, + version: "0.7.1", + name: "ExCrypto", + elixir: ">= 1.4.2", + description: description(), + package: package(), + deps: deps(), + docs: [extras: ["README.md"]] + ] end def application do - [applications: applications(Mix.env)] + [applications: applications(Mix.env())] end + defp applications(:test) do applications(:prod) end + defp applications(_) do [:logger, :crypto, :public_key] end @@ -42,7 +45,10 @@ defmodule ExCrypto.Mixfile do files: ["lib", "mix.exs", "README*", "LICENSE*", "CHANGELOG*"], maintainers: ["Josh Austin"], licenses: ["MIT"], - links: %{"Github" => "https://github.com/ntrepid8/ex_crypto", - "Docs" => "https://hexdocs.pm/ex_crypto/readme.html"}] + links: %{ + "Github" => "https://github.com/ntrepid8/ex_crypto", + "Docs" => "https://hexdocs.pm/ex_crypto/readme.html" + } + ] end end diff --git a/test/ex_crypto_hmac_test.exs b/test/ex_crypto_hmac_test.exs index ad79fae..82cebf4 100644 --- a/test/ex_crypto_hmac_test.exs +++ b/test/ex_crypto_hmac_test.exs @@ -32,7 +32,7 @@ defmodule ExCryptoHMACTest do assert(is_binary(mac)) {:ok, mac_is_valid} = ExCrypto.HMAC.verify_hmac(context[:data], context[:aes_128_key], mac) - assert(mac_is_valid) + assert(mac_is_valid) end test "hmac_verify with invalid MAC", context do @@ -44,8 +44,9 @@ defmodule ExCryptoHMACTest do assert(invalid_mac !== nil) assert(is_binary(invalid_mac)) - {:ok, mac_is_valid} = ExCrypto.HMAC.verify_hmac(context[:data], context[:aes_128_key], invalid_mac) - assert(mac_is_valid === false) + {:ok, mac_is_valid} = + ExCrypto.HMAC.verify_hmac(context[:data], context[:aes_128_key], invalid_mac) + + assert(mac_is_valid === false) end - -end \ No newline at end of file +end diff --git a/test/ex_crypto_test.exs b/test/ex_crypto_test.exs index b30b9a7..a7fff68 100644 --- a/test/ex_crypto_test.exs +++ b/test/ex_crypto_test.exs @@ -7,18 +7,18 @@ defmodule ExCryptoTest do end def run_rand_char_test() do - rand_char_count = :crypto.rand_uniform(1, 100) + rand_char_count = :rand.uniform(100) rand_string = ExCrypto.rand_chars(rand_char_count) assert(String.length(rand_string) == rand_char_count) end test "generate random characters" do - for n <- 1..100, do: run_rand_char_test() + for _n <- 1..100, do: run_rand_char_test() end test "generate random integers and test randomness" do - set_size = 100000 - random_ints = for n <- 1..set_size, do: ExCrypto.rand_int(1, 100) + set_size = 100_000 + random_ints = for _n <- 1..set_size, do: ExCrypto.rand_int(0, 100) # do cursory check for randomness, average should be very near 50 average = Enum.sum(random_ints) / set_size @@ -73,7 +73,7 @@ defmodule ExCryptoTest do # encrypt {:ok, {ad, payload}} = ExCrypto.encrypt(aes_128_key, a_data, iv, clear_text) - {c_iv, cipher_text, cipher_tag} = payload + {_c_iv, cipher_text, cipher_tag} = payload assert(clear_text != cipher_text) # decrypt @@ -124,7 +124,7 @@ defmodule ExCryptoTest do a_data = "the auth and associated data" # encrypt - {:ok, {ad, payload}} = ExCrypto.encrypt(aes_256_key, a_data, clear_text) + {:ok, {_ad, payload}} = ExCrypto.encrypt(aes_256_key, a_data, clear_text) {iv, cipher_text, cipher_tag} = payload assert(byte_size(iv) == 16) assert(byte_size(cipher_tag) == 16) @@ -169,8 +169,9 @@ defmodule ExCryptoTest do {:ok, bad_iv} = ExCrypto.rand_bytes(17) clear_text = "secret_message" # encrypt - {:error, error_message} = ExCrypto.encrypt(aes_256_key, clear_text, %{initialization_vector: bad_iv}) + {:error, error_message} = + ExCrypto.encrypt(aes_256_key, clear_text, %{initialization_vector: bad_iv}) + assert(is_binary(error_message)) end - end diff --git a/test/ex_entropy_test.exs b/test/ex_entropy_test.exs index 3886618..b85b8c0 100644 --- a/test/ex_entropy_test.exs +++ b/test/ex_entropy_test.exs @@ -3,8 +3,11 @@ defmodule ExEntropyTest do defp generate_repeat_list(accumulator, value, count) when is_list(accumulator) do case count do - c when c >= 0 -> generate_repeat_list(List.insert_at(accumulator, -1, value), value, count-1) - c when c < 0 -> accumulator + c when c >= 0 -> + generate_repeat_list(List.insert_at(accumulator, -1, value), value, count - 1) + + c when c < 0 -> + accumulator end end @@ -25,21 +28,22 @@ defmodule ExEntropyTest do end test "shannon_entropy with strong_rand_bytes and 2^10" do - rand_bytes = :crypto.strong_rand_bytes(1024*1000) + rand_bytes = :crypto.strong_rand_bytes(1024 * 1000) shannon_entropy = ExEntropy.shannon_entropy(rand_bytes, 10) assert(shannon_entropy > 0.99) end test "shannon_entropy with strong_rand_bytes and 2^16" do - rand_bytes = :crypto.strong_rand_bytes(1024*1000) + rand_bytes = :crypto.strong_rand_bytes(1024 * 1000) shannon_entropy = ExEntropy.shannon_entropy(rand_bytes, 16) assert(shannon_entropy > 0.99) end test "shannon_entropy with strong_rand_bytes and 2^20" do - rand_bytes = :crypto.strong_rand_bytes(1024*1000) + rand_bytes = :crypto.strong_rand_bytes(1024 * 1000) shannon_entropy = ExEntropy.shannon_entropy(rand_bytes, 20) - assert(shannon_entropy > 0.90) # this is taxing on the entropy pool + # this is taxing on the entropy pool + assert(shannon_entropy > 0.90) end test "shannon_entropy with known low entropy bytes and 2^8" do @@ -48,9 +52,11 @@ defmodule ExEntropyTest do {:ok, bin_val_1} = ExCrypto.rand_bytes(1) # use the two random numbers to generate a low entropy binary string - bytes_val_2 = Enum.join(generate_repeat_list(bin_val_0, 127)) - <> Enum.join(generate_repeat_list(bin_val_1, 127)) - assert(is_binary bytes_val_2) + bytes_val_2 = + Enum.join(generate_repeat_list(bin_val_0, 127)) <> + Enum.join(generate_repeat_list(bin_val_1, 127)) + + assert(is_binary(bytes_val_2)) assert(byte_size(bytes_val_2) == 256) # measure the entropy in the low entropy string and assert it is low @@ -67,9 +73,11 @@ defmodule ExEntropyTest do {:ok, bin_val_1} = ExCrypto.rand_bytes(1) # use the two random numbers to generate a low entropy binary string - bytes_val_2 = Enum.join(generate_repeat_list(bin_val_0, 127)) - <> Enum.join(generate_repeat_list(bin_val_1, 127)) - assert(is_binary bytes_val_2) + bytes_val_2 = + Enum.join(generate_repeat_list(bin_val_0, 127)) <> + Enum.join(generate_repeat_list(bin_val_1, 127)) + + assert(is_binary(bytes_val_2)) assert(byte_size(bytes_val_2) == 256) # measure the entropy in the low entropy string and assert it is low diff --git a/test/ex_public_key_test.exs b/test/ex_public_key_test.exs index 08f0c34..c8d1fea 100644 --- a/test/ex_public_key_test.exs +++ b/test/ex_public_key_test.exs @@ -3,8 +3,6 @@ defmodule ExPublicKeyTest do alias ExPublicKey.RSAPublicKey, as: RSAPublicKey alias ExPublicKey.RSAPrivateKey, as: RSAPrivateKey - @epoch :calendar.datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}) - setup do # generate a RSA key pair # generate a unique temp file name @@ -14,30 +12,44 @@ defmodule ExPublicKeyTest do rsa_secure_private_key_path = "/tmp/test_ex_crypto_rsa_secure_private_key_#{rand_string}.pem" # generate the RSA private key with openssl - System.cmd( - "openssl", ["genrsa", "-out", rsa_private_key_path, "2048"]) + System.cmd("openssl", ["genrsa", "-out", rsa_private_key_path, "2048"]) # export the RSA public key to a file with openssl - System.cmd( - "openssl", ["rsa", "-in", rsa_private_key_path, "-outform", "PEM", "-pubout", "-out", rsa_public_key_path]) + System.cmd("openssl", [ + "rsa", + "-in", + rsa_private_key_path, + "-outform", + "PEM", + "-pubout", + "-out", + rsa_public_key_path + ]) # generate a passphrase protected RSA private key with openssl - System.cmd( - "openssl", ["genrsa", "-out", rsa_secure_private_key_path, "-passout", "pass:#{rand_string}", "2048"]) - - on_exit fn -> + System.cmd("openssl", [ + "genrsa", + "-out", + rsa_secure_private_key_path, + "-passout", + "pass:#{rand_string}", + "2048" + ]) + + on_exit(fn -> # cleanup: delete the temp keys File.rm!(rsa_private_key_path) File.rm!(rsa_public_key_path) File.rm!(rsa_secure_private_key_path) - end - - {:ok, [ - rsa_private_key_path: rsa_private_key_path, - rsa_public_key_path: rsa_public_key_path, - rsa_secure_private_key_path: rsa_secure_private_key_path, - passphrase: rand_string, - ]} + end) + + {:ok, + [ + rsa_private_key_path: rsa_private_key_path, + rsa_public_key_path: rsa_public_key_path, + rsa_secure_private_key_path: rsa_secure_private_key_path, + passphrase: rand_string + ]} end test "read RSA keys in PEM format", context do @@ -55,8 +67,8 @@ defmodule ExPublicKeyTest do assert(rsa_pub_key.__struct__ == RSAPublicKey) end - test "converts RSA keys to PEM format and back", context do - {:ok, rsa_priv_key} = ExPublicKey.generate_key + test "converts RSA keys to PEM format and back", _context do + {:ok, rsa_priv_key} = ExPublicKey.generate_key() {:ok, priv_key_string} = ExPublicKey.pem_encode(rsa_priv_key) rsa_priv_key_decoded = ExPublicKey.loads!(priv_key_string) @@ -71,7 +83,6 @@ defmodule ExPublicKeyTest do assert(secure_rsa_priv_key.__struct__ == RSAPrivateKey) end - test "try random string in key loads function and observe ExCrypto.Error" do assert_raise ExCrypto.Error, fn -> ExPublicKey.loads!(ExCrypto.rand_chars(1000)) @@ -85,7 +96,7 @@ defmodule ExPublicKeyTest do msg = "This is a test message to sign, complete with some entropy (#{rand_chars})." {:ok, signature} = ExPublicKey.sign(msg, rsa_priv_key) {:ok, valid} = ExPublicKey.verify(msg, signature, rsa_pub_key) - IO.inspect valid + IO.inspect(valid) assert(valid) end @@ -94,7 +105,7 @@ defmodule ExPublicKeyTest do {:ok, rsa_pub_key} = ExPublicKey.load(context[:rsa_public_key_path]) rand_chars = ExCrypto.rand_chars(16) plain_text = "This is a test message to encrypt, complete with some entropy (#{rand_chars})." - + {:ok, cipher_text} = ExPublicKey.encrypt_public(plain_text, rsa_pub_key) assert(cipher_text != plain_text) @@ -131,8 +142,8 @@ defmodule ExPublicKeyTest do assert(decrypted_plain_text == plain_text) end - test "RSA private_key encrypt and RSA public_key decrypt using generated keys", context do - {:ok, rsa_priv_key} = ExPublicKey.generate_key + test "RSA private_key encrypt and RSA public_key decrypt using generated keys", _context do + {:ok, rsa_priv_key} = ExPublicKey.generate_key() {:ok, rsa_pub_key} = ExPublicKey.public_key_from_private_key(rsa_priv_key) rand_chars = ExCrypto.rand_chars(16) plain_text = "This is a test message to encrypt, complete with some entropy (#{rand_chars})." @@ -163,14 +174,17 @@ defmodule ExPublicKeyTest do test "provoke exception from Erlang that must be handled" do case ExPublicKey.sign("chuck norris", :sha256, "not really a key") do {:ok, signature} -> - assert false, "this should have provoked an error: #{inspect signature}" + assert false, "this should have provoked an error: #{inspect(signature)}" + {:error, reason} -> - IO.inspect reason + IO.inspect(reason) assert true, "the right error was provoked: #{reason}" - {:error, error, stack_trace} -> - IO.inspect error + + {:error, error, _stack_trace} -> + IO.inspect(error) assert false, "the wrong error was provoked: #{error.message}" - x -> + + _x -> # IO.inspect x assert false, "something else happened" end @@ -182,7 +196,7 @@ defmodule ExPublicKeyTest do rsa_pub_key = ExPublicKey.load!(context[:rsa_public_key_path]) # the JSON - msg = %{"name_first"=>"Chuck","name_last"=>"Norris"} + msg = %{"name_first" => "Chuck", "name_last" => "Norris"} # serialize the JSON msg_serialized = Poison.encode!(msg) @@ -193,12 +207,11 @@ defmodule ExPublicKeyTest do # add a time-stamp ts_msg_serialized = "#{ts}|#{msg_serialized}" - # generate a secure hash using SHA256 and sign the message with the private key {:ok, signature} = ExPublicKey.sign(ts_msg_serialized, rsa_priv_key) # combine payload - payload = "#{ts}|#{msg_serialized}|#{Base.url_encode64 signature}" + payload = "#{ts}|#{msg_serialized}|#{Base.url_encode64(signature)}" # pretend transmit the message via HTTPS... # pretend receive the message via HTTPS... @@ -207,19 +220,19 @@ defmodule ExPublicKeyTest do parts = String.split(payload, "|") recv_ts = Enum.fetch!(parts, 0) recv_msg_serialized = Enum.fetch!(parts, 1) - {:ok, recv_sig} = Enum.fetch!(parts, 2) |> Base.url_decode64 + {:ok, recv_sig} = Enum.fetch!(parts, 2) |> Base.url_decode64() # pretend ensure the time-stamp is not too old (or from the future)... # it should probably no more than 5 minutes old, and no more than 15 minutes in the future # verify the signature - {:ok, sig_valid} = ExPublicKey.verify("#{recv_ts}|#{recv_msg_serialized}", recv_sig, rsa_pub_key) + {:ok, sig_valid} = + ExPublicKey.verify("#{recv_ts}|#{recv_msg_serialized}", recv_sig, rsa_pub_key) + assert(sig_valid) # un-serialize the JSON recv_msg_unserialized = Poison.Parser.parse!(recv_msg_serialized) assert(msg == recv_msg_unserialized) - end - end diff --git a/test/ex_token_test.exs b/test/ex_token_test.exs index ac15a6d..23669c9 100644 --- a/test/ex_token_test.exs +++ b/test/ex_token_test.exs @@ -14,7 +14,8 @@ defmodule ExCrypto.ExTokenTest do {:ok, token} = result # verify the token - ttl = 15*60 # 15 minute TTL + # 15 minute TTL + ttl = 15 * 60 result = ExCrypto.Token.verify(token, secret, ttl) assert assert match?({:ok, _}, result) {:ok, v_payload} = result @@ -24,7 +25,7 @@ defmodule ExCrypto.ExTokenTest do test "Token.create/3 and Token.verify/4 (token too old)" do now_dt = :calendar.universal_time() now_seconds = :calendar.datetime_to_gregorian_seconds(now_dt) - past_seconds = now_seconds - (30*60) + past_seconds = now_seconds - 30 * 60 past_dt = :calendar.gregorian_seconds_to_datetime(past_seconds) payload = %{"foo" => "bar", "spam" => "eggs"} @@ -38,7 +39,8 @@ defmodule ExCrypto.ExTokenTest do {:ok, token} = result # verify the token - ttl = 15*60 # 15 minute TTL + # 15 minute TTL + ttl = 15 * 60 result = ExCrypto.Token.verify(token, secret, ttl) assert match?({:error, _}, result) end @@ -46,7 +48,7 @@ defmodule ExCrypto.ExTokenTest do test "Token.create/3 and Token.verify/4 (token in future)" do now_dt = :calendar.universal_time() now_seconds = :calendar.datetime_to_gregorian_seconds(now_dt) - future_seconds = now_seconds + (30*60) + future_seconds = now_seconds + 30 * 60 future_dt = :calendar.gregorian_seconds_to_datetime(future_seconds) payload = %{"foo" => "bar", "spam" => "eggs"} @@ -60,7 +62,8 @@ defmodule ExCrypto.ExTokenTest do {:ok, token} = result # verify the token - ttl = 15*60 # 15 minute TTL + # 15 minute TTL + ttl = 15 * 60 result = ExCrypto.Token.verify(token, secret, ttl) assert match?({:error, _}, result) end @@ -68,7 +71,8 @@ defmodule ExCrypto.ExTokenTest do test "Token.update/4 (basic)" do now_dt = :calendar.universal_time() now_seconds = :calendar.datetime_to_gregorian_seconds(now_dt) - past_seconds = now_seconds - (10*60) # 10 min + # 10 min + past_seconds = now_seconds - 10 * 60 past_dt = :calendar.gregorian_seconds_to_datetime(past_seconds) payload = %{"foo" => "bar", "spam" => "eggs"} @@ -82,30 +86,33 @@ defmodule ExCrypto.ExTokenTest do {:ok, token} = result # verify the token (pass with 15 min TTL) - ttl = 15*60 # 15 minute TTL + # 15 minute TTL + ttl = 15 * 60 result = ExCrypto.Token.verify(token, secret, ttl) assert match?({:ok, _}, result) {:ok, v_payload} = result assert v_payload == encoded_payload # verify the token (fail with 5 min TTL) - ttl = 5*60 # 5 minute TTL + # 5 minute TTL + ttl = 5 * 60 result = ExCrypto.Token.verify(token, secret, ttl) assert match?({:error, _}, result) # update the token - ttl = 15*60 # 15 minute TTL + # 15 minute TTL + ttl = 15 * 60 result = ExCrypto.Token.update(token, secret, ttl) assert match?({:ok, {_, _}}, result) {:ok, {update_token, update_payload}} = result assert update_payload == encoded_payload # verify the updated token (pass with 5 min TTL) - ttl = 5*60 # 5 minute TTL + # 5 minute TTL + ttl = 5 * 60 result = ExCrypto.Token.verify(update_token, secret, ttl) assert match?({:ok, _}, result) {:ok, v_payload} = result assert v_payload == encoded_payload end - end