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 ### API pwd = Safetybox.encrypt("helloworld") Safetybox.is_decrypted("goodbyeworld", pwd) # > returns false Safetybox.is_decrypted("helloworld", pwd) # > returns true ## Author Copyright © 2014 Andrew Forward @a4word, aforward@gmail.com Licensed under MIT. """ # Taken from https://gist.github.com/10nin/5713366 def encrypt(nil), do: encrypt("") @doc """ Convert a plaintext string into an encrypted string, for example Safetybox.encrypt("helloworld") """ def encrypt(plaintext) do :crypto.hash(:md5, plaintext) |> :erlang.bitstring_to_list |> Enum.map(&(:io_lib.format("~2.16.0b", [&1]))) |> List.flatten |> :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 """ Lookup the secret and salt from the Application environment when encrypting your string config :myapp, secret: "mysecret", salt: "mysalt" Safetybox.encrypt("helloworld", :myapp) """ def encrypt(plaintext, appname) when is_atom(appname) do secret = Application.get_env(appname, :secret) salt = Application.get_env(appname, :salt) if is_nil(salt) do encrypt(plaintext, secret) else encrypt(plaintext, secret, salt) end end @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) when is_binary(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) when is_binary(secret) 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 """ Lookup the secret and salt from the Application environment when decrypting your string config :myapp, secret: "mysecret", salt: "mysalt" enc = Safetybox.encrypt("helloworld", :myapp) Safetybox.decrypt("helloworld", :myapp) """ def decrypt(obscuretext, appname) when is_atom(appname) do secret = Application.get_env(appname, :secret) salt = Application.get_env(appname, :salt) if is_nil(salt) do decrypt(obscuretext, secret) else decrypt(obscuretext, secret, salt) end end @doc """ Decrypt some obscure text that was generated by encrypt enc = Safetybox.encrypt("helloworld") Safetybox.decrypt(env) """ def decrypt(obscuretext, secret) when is_binary(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) when is_binary(secret) 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