Verify signatures of signing keys

This commit is contained in:
Pim Kunis 2021-08-13 13:45:10 +02:00
parent fb59fee754
commit ff3dd38369
2 changed files with 48 additions and 8 deletions

View file

@ -91,6 +91,13 @@ defmodule MatrixServer do
|> Map.drop(waste_fields) |> Map.drop(waste_fields)
end end
def serialize_and_encode(struct) do
# TODO: handle nil values in struct?
struct
|> to_serializable_map()
|> encode_canonical_json()
end
def add_signature(object, key_id, sig) when not is_map_key(object, :signatures) do def add_signature(object, key_id, sig) when not is_map_key(object, :signatures) do
Map.put(object, :signatures, %{MatrixServer.server_name() => %{key_id => sig}}) Map.put(object, :signatures, %{MatrixServer.server_name() => %{key_id => sig}})
end end

View file

@ -17,15 +17,39 @@ defmodule MatrixServerWeb.FederationClient do
Tesla.client([{Tesla.Middleware.BaseUrl, "http://" <> server_name} | @middleware], @adapter) Tesla.client([{Tesla.Middleware.BaseUrl, "http://" <> server_name} | @middleware], @adapter)
end end
@path RouteHelpers.key_path(Endpoint, :get_signing_keys)
def get_signing_keys(client) do def get_signing_keys(client) do
# TODO: Extract into seperate function. # TODO: Which server_name should we take?
# TODO: Check signatures for each verify key. # TODO: Should probably catch enacl exceptions and just return error atom,
with {:ok, %Tesla.Env{body: body}} <- # create seperate function for this.
Tesla.get(client, RouteHelpers.key_path(Endpoint, :get_signing_keys)), with {:ok,
%Ecto.Changeset{valid?: true} = cs <- GetSigningKeys.changeset(body) do %GetSigningKeys{server_name: server_name, verify_keys: verify_keys, signatures: sigs} =
{:ok, Ecto.Changeset.apply_changes(cs)} response} <- tesla_request(:get, client, @path, GetSigningKeys),
{:ok, encoded_body} <- MatrixServer.serialize_and_encode(response) do
# For each verify key, check if there is a matching signature.
# If not, invalidate the whole response.
if Map.has_key?(sigs, server_name) do
server_sigs = sigs[server_name]
all_keys_signed? =
Enum.all?(verify_keys, fn {key_id, %{"key" => key}} ->
with true <- Map.has_key?(server_sigs, key_id),
{:ok, decoded_key} <- MatrixServer.decode_base64(key),
{:ok, decoded_sig} <- MatrixServer.decode_base64(server_sigs[key_id]) do
:enacl.sign_verify_detached(decoded_sig, encoded_body, decoded_key)
else else
_ -> :error _ -> false
end
end)
if all_keys_signed? do
{:ok, response}
else
:error
end
else
:error
end
end end
end end
@ -54,4 +78,13 @@ defmodule MatrixServerWeb.FederationClient do
{"Authorization", "X-Matrix origin=#{origin},key=\"#{key}\",sig=\"#{sig}\""} {"Authorization", "X-Matrix origin=#{origin},key=\"#{key}\",sig=\"#{sig}\""}
end) end)
end end
defp tesla_request(method, client, path, request_schema) do
with {:ok, %Tesla.Env{body: body}} <- Tesla.request(client, url: path, method: method),
%Ecto.Changeset{valid?: true} = cs <- apply(request_schema, :changeset, [body]) do
{:ok, Ecto.Changeset.apply_changes(cs)}
else
_ -> :error
end
end
end end