79 lines
1.9 KiB
Elixir
79 lines
1.9 KiB
Elixir
|
defmodule MatrixServer.SigningServer do
|
||
|
use GenServer
|
||
|
|
||
|
alias MatrixServer.OrderedMap
|
||
|
|
||
|
# TODO: only support one signing key for now.
|
||
|
@signing_key_id "ed25519:1"
|
||
|
|
||
|
## Interface
|
||
|
|
||
|
def start_link(opts) do
|
||
|
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
|
||
|
end
|
||
|
|
||
|
def sign_event(event) do
|
||
|
GenServer.call(__MODULE__, {:sign_event, event})
|
||
|
end
|
||
|
|
||
|
## Implementation
|
||
|
|
||
|
@impl true
|
||
|
def init(_opts) do
|
||
|
{public_key, private_key} = get_keys()
|
||
|
{:ok, %{public_key: public_key, private_key: private_key}}
|
||
|
end
|
||
|
|
||
|
# https://blog.swwomm.com/2020/09/elixir-ed25519-signatures-with-enacl.html
|
||
|
@impl true
|
||
|
def handle_call(
|
||
|
{:sign_event, event},
|
||
|
_from,
|
||
|
%{private_key: private_key} = state
|
||
|
) do
|
||
|
ordered_map =
|
||
|
event
|
||
|
|> Map.drop([:signatures, :unsigned])
|
||
|
|> OrderedMap.from_map()
|
||
|
|
||
|
case Jason.encode(ordered_map) do
|
||
|
{:ok, json} ->
|
||
|
signature =
|
||
|
json
|
||
|
|> :enacl.sign_detached(private_key)
|
||
|
|> MatrixServer.encode_unpadded_base64()
|
||
|
|
||
|
signature_map = %{@signing_key_id => signature}
|
||
|
servername = MatrixServer.server_name()
|
||
|
|
||
|
event =
|
||
|
Map.update(event, :signatures, %{servername => signature_map}, fn signatures ->
|
||
|
Map.put(signatures, servername, signature_map)
|
||
|
end)
|
||
|
|
||
|
{:reply, event, state}
|
||
|
|
||
|
{:error, _msg} ->
|
||
|
{:reply, {:error, :json_encode}, state}
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# TODO: not sure if there is a better way to do this...
|
||
|
defp get_keys do
|
||
|
raw_priv_key =
|
||
|
Application.get_env(:matrix_server, :private_key_file)
|
||
|
|> File.read!()
|
||
|
|
||
|
"-----BEGIN OPENSSH PRIVATE KEY-----\n" <> rest = raw_priv_key
|
||
|
|
||
|
%{public: public, secret: private} =
|
||
|
String.split(rest, "\n")
|
||
|
|> Enum.take_while(&(&1 != "-----END OPENSSH PRIVATE KEY-----"))
|
||
|
|> Enum.join()
|
||
|
|> Base.decode64!()
|
||
|
|> :enacl.sign_seed_keypair()
|
||
|
|
||
|
{public, private}
|
||
|
end
|
||
|
end
|