diff --git a/lib/safetybox.ex b/lib/safetybox.ex index ef340ce..29f8392 100644 --- a/lib/safetybox.ex +++ b/lib/safetybox.ex @@ -1,7 +1,16 @@ defmodule Safetybox do + alias Cryptex.KeyGenerator, as: K + alias Cryptex.MessageEncryptor, as: E + alias Cryptex.MessageVerifier, as: V + + @global_salt "4Yw7kLLAjFfxtqHzttpRhmPrEqqXqqrMHdgmcgUNDq" + @global_secret "FzxZRcf3xHxerDWXECwpWJVu2dwfyagTyPHmXcVpkv" + @moduledoc """ # Safetybox a collection of security oriented functions + # This is oriented towards individuals that store passwords + # directly in their code, or some other not-so-hard-to-find location ## Dependency { :safetybox, "~> 0.1" } ## Usage @@ -32,10 +41,73 @@ defmodule Safetybox do |> :erlang.list_to_bitstring end + @doc """ + If you don't yet need a secret or a salt (e.g. very early prototype), but you want some thigns encrypted + then use the :default secret. Better than nothing, but not much. + Safetybox.encrypt("helloworld", :default) + """ + def encrypt(plaintext, :default), do: encrypt(plaintext, @global_secret, @global_salt) + + @doc """ + One tiny secret for a developer, one giant leap in security. Pick a secret within your application + (or ENV variable), to better prevent your strings being decrypted + Safetybox.encrypt("helloworld","mysecretkey") + """ + def encrypt(plaintext, secret), do: encrypt(plaintext, secret, @global_salt) + + @doc """ + Convert a plaintext string into a decryptable string by providing a secret + Safetybox.encrypt("helloworld","mysecretkey", "mysecretsalt") + """ + def encrypt(plaintext, secret, salt) do + secret + |> crypt(salt) + |> E.encrypt_and_sign(plaintext) + end + + @doc """ + Decrypt some obscure text that was generated by encrypt + enc = Safetybox.encrypt("helloworld") + Safetybox.decrypt(env) + """ + def decrypt(obscuretext), do: decrypt(obscuretext, @global_secret, @global_salt) + + @doc """ + Decrypt some obscure text that was generated by encrypt + enc = Safetybox.encrypt("helloworld") + Safetybox.decrypt(env) + """ + def decrypt(obscuretext, secret), do: decrypt(obscuretext, secret, @global_salt) + + @doc """ + Take an encrypted string, and convert it back to plaintext, must use the same secret and salt + enc = Safetybox.encrypt("helloworld","mysecretkey", "mysecretsalt") + Safetybox.encrypt("helloworld","mysecretkey", "mysecretsalt") + """ + def decrypt(obscuretext, secret, salt) do + secret + |> crypt(salt) + |> decrypt_and_verify(obscuretext) + end + @doc """ Compare a plaintext string to it's encrypted version to test if they match. Safetybox.is_decrypted("helloworld", "dasdf!@#CASD") """ def is_decrypted(plaintext, encrypted), do: encrypt(plaintext) == encrypted + + defp crypt(secret, salt) do + secret + |> K.generate(salt) + |> E.new(K.generate(secret, "signed #{salt}")) + end + + defp decrypt_and_verify(cryptor, obscuretext) do + case V.verify(cryptor.sign_secret, obscuretext, :null) do + :error -> :error + {:ok, _unsigned} -> E.decrypt_and_verify(cryptor, obscuretext) + end + end + end diff --git a/mix.exs b/mix.exs index 21fb09b..bf0011e 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Safetybox.Mixfile do def project do [ app: :safetybox, - version: "0.0.2", + version: "0.1.0", elixir: "~> 1.0", deps: deps, @@ -41,6 +41,7 @@ defmodule Safetybox.Mixfile do [ {:ex_doc, "~> 0.6", only: :dev}, {:earmark, "~> 0.1"}, + {:cryptex, "~> 0.0.1"}, ] end end diff --git a/mix.lock b/mix.lock index 4c0ec9b..df15e48 100644 --- a/mix.lock +++ b/mix.lock @@ -1,2 +1,3 @@ -%{"earmark": {:hex, :earmark, "0.1.12"}, +%{"cryptex": {:hex, :cryptex, "0.0.1"}, + "earmark": {:hex, :earmark, "0.1.12"}, "ex_doc": {:hex, :ex_doc, "0.6.2"}} diff --git a/test/safetybox_test.exs b/test/safetybox_test.exs index 340ce36..7f8ef89 100644 --- a/test/safetybox_test.exs +++ b/test/safetybox_test.exs @@ -26,4 +26,30 @@ defmodule SafetyboxTest do assert S.is_decrypted("", S.encrypt("")) == true end + test "encrypt / decrypt (default)" do + assert S.decrypt(S.encrypt("helloworld", :default)) == "helloworld" + assert S.decrypt(S.encrypt("goodbyeworld", :default)) == "goodbyeworld" + end + + test "encrypt / decrypt (secret)" do + assert S.decrypt(S.encrypt("helloworld", "mysecret"), "yoursecret") == :error + assert S.decrypt(S.encrypt("goodbyeworld", "mysecret"), "yoursecret") != "goodbyeworld" + + assert S.decrypt(S.encrypt("helloworld", "mysecret"), "mysecret") == "helloworld" + assert S.decrypt(S.encrypt("goodbyeworld", "mysecret"), "mysecret") == "goodbyeworld" + end + + test "encrypt / decrypt (secret, salt)" do + assert S.decrypt(S.encrypt("helloworld", "mysecret", "mysalt"), "yoursecret", "mysalt") == :error + assert S.decrypt(S.encrypt("goodbyeworld", "mysecret", "mysalt"), "yoursecret", "mysalt") != "goodbyeworld" + assert S.decrypt(S.encrypt("helloworld", "mysecret", "mysalt"), "mysalt", "yoursalt") != "helloworld" + assert S.decrypt(S.encrypt("goodbyeworld", "mysecret", "mysalt"), "mysalt", "yoursalt") != "goodbyeworld" + assert S.decrypt(S.encrypt("helloworld", "mysecret", "mysalt"), "yoursecret", "yoursalt") != "helloworld" + assert S.decrypt(S.encrypt("goodbyeworld", "mysecret", "mysalt"), "yoursecret", "yoursalt") != "goodbyeworld" + + assert S.decrypt(S.encrypt("helloworld", "mysecret", "mysalt"), "mysecret", "mysalt") == "helloworld" + assert S.decrypt(S.encrypt("goodbyeworld", "mysecret", "mysalt"), "mysecret", "mysalt") == "goodbyeworld" + end + + end