aboutsummaryrefslogtreecommitdiff
path: root/shard/lib/keys.ex
diff options
context:
space:
mode:
Diffstat (limited to 'shard/lib/keys.ex')
-rw-r--r--shard/lib/keys.ex124
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