diff options
Diffstat (limited to 'shard/lib/keys.ex')
-rw-r--r-- | shard/lib/keys.ex | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/shard/lib/keys.ex b/shard/lib/keys.ex new file mode 100644 index 0000000..0dc3154 --- /dev/null +++ b/shard/lib/keys.ex @@ -0,0 +1,124 @@ +defmodule Shard.Keys do + @moduledoc""" + Module for saving private keys. + """ + + use Agent + require Salty.Sign.Ed25519, as: Sign + require Logger + + @key_db [Application.get_env(:shard, :data_path), "key_db"] |> Path.join |> String.to_atom + + def start_link(_) do + Agent.start_link(__MODULE__, :init, [], name: __MODULE__) + end + + def init() do + :dets.start + {:ok, @key_db} = :dets.open_file(@key_db, [type: :set]) + + case :dets.lookup(@key_db, :peer) do + [] -> + Logger.info "Generating peer keypair..." + {pk, sk} = gen_keypair(Application.get_env(:shard, :peer_id_suffix)) + :dets.insert @key_db, {:peer, pk, sk} + {pk, sk} + [{:peer, pk, sk}] -> + {pk, sk} + end + end + + defp gen_keypair(suffix, n \\ 0) do + {:ok, pk, sk} = Sign.keypair + if rem(n, 10000) == 0 do + Logger.info "#{n}... expected #{:math.pow(256, byte_size(suffix))}" + end + if check_suffix(pk, suffix) do + {pk, sk} + else + gen_keypair(suffix, n+1) + end + end + + defp check_suffix(pk, suffix) do + :binary.longest_common_suffix([pk, suffix]) == byte_size(suffix) + end + + def get_peer_keypair() do + Agent.get(__MODULE__, &(&1)) + end + + @doc""" + Generate a new keypair for a user identity, and start an Identity Shard for it. + """ + def new_identity() do + {pk, sk} = gen_keypair(Application.get_env(:shard, :identity_suffix)) + :dets.insert @key_db, {pk, sk} + SApp.Identity.start_link(pk) + pk + end + + @doc""" + List the public keys of all identities for which we have a secret key + """ + def list_identities() do + for [{pk, _sk}] <- :dets.match(@key_db, :"$1"), do: pk + end + + @doc""" + Lookup the secret key for a pk and sign a message with it. + + Returns the input value alongside its signature. + + Answer is {:ok, signed} if it worked, or :not_found if we didn't find the key. + """ + def sign(pk, bin) do + case :dets.lookup @key_db, pk do + [{^pk, sk}] -> + Sign.sign(bin, sk) + _ -> {:error, :not_found} + end + end + + @doc""" + Checks the signature appended to a signed message corresponds to a public key. + + If correct, returns {:ok, original_message} + """ + def open(pk, signed) do + if check_suffix(pk, Application.get_env(:shard, :identity_suffix)) do + Sign.open(signed, pk) + else + {:error, :invalid_pk_suffix} + end + end + + @doc""" + Lookup the secret key for a pk and generate a detached signature for a message. + + The original message is not returned. + + Answer is {:ok, signature} if it worked, or :not_found if we didn't find the key. + + """ + def sign_detached(pk, bin) do + case :dets.lookup @key_db, pk do + [{^pk, sk}] -> + Sign.sign_detached(bin, sk) + _ -> {:error, :not_found} + end + end + + @doc""" + Verify a detached signature for a message + + Returns :ok if the signature was correct. + """ + def verify(pk, bin, sign) do + if check_suffix(pk, Application.get_env(:shard, :identity_suffix)) do + Sign.verify_detached(sign, bin, pk) + else + {:error, :invalid_pk_suffix} + end + end +end |