5 changed files with 73 additions and 1 deletions
@ -0,0 +1,55 @@ |
|||||||
|
defmodule GcIndexRelay.Nostr.Validator do |
||||||
|
@moduledoc """ |
||||||
|
Nostr key and signature validation. |
||||||
|
|
||||||
|
Uses [Curvy](https://hexdocs.pm/curvy/Curvy.html). |
||||||
|
|
||||||
|
Will migrate to [noscrypt](https://www.vaughnnugent.com/resources/software/modules/noscrypt) (via |
||||||
|
NIF integration) at a later date. |
||||||
|
""" |
||||||
|
|
||||||
|
alias GcIndexRelay.Nostr.PubEvent |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Validates a Nostr event ID per [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md). |
||||||
|
""" |
||||||
|
def validate_id(event) when is_struct(event, PubEvent) do |
||||||
|
if valid_id?(event), |
||||||
|
do: {:ok, event}, |
||||||
|
else: {:error, "ID #{event.id} is invalid for event:\n#{event}"} |
||||||
|
end |
||||||
|
|
||||||
|
defp valid_id?(event) |
||||||
|
when is_struct(event, PubEvent) do |
||||||
|
computed_id = compute_id!(event) |
||||||
|
|
||||||
|
event.id == computed_id |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Validates a Nostr event signature per [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md). |
||||||
|
""" |
||||||
|
def validate_signature(event) when is_struct(event, PubEvent) do |
||||||
|
if valid_signature?(event), |
||||||
|
do: {:ok, event}, |
||||||
|
else: {:error, "Signature #{event.sig} is invalid for event:\n#{event}"} |
||||||
|
end |
||||||
|
|
||||||
|
defp valid_signature?(event) when is_struct(event, PubEvent) do |
||||||
|
data = compute_id!(event) |
||||||
|
Curvy.verify(event.sig, data, event.pubkey) |
||||||
|
end |
||||||
|
|
||||||
|
defp compute_id!(event) when is_struct(event, PubEvent) do |
||||||
|
Jason.encode!([0, event.pubkey, event.created_at, event.kind, event.tags, event.content]) |
||||||
|
|> sha256(:lowercase_hex) |
||||||
|
end |
||||||
|
|
||||||
|
defp sha256(data, :binary = output_type) when is_binary(data) and is_atom(output_type), |
||||||
|
do: :crypto.hash(:sha256, data) |
||||||
|
|
||||||
|
defp sha256(data, :lowercase_hex = output_type) when is_binary(data) and is_atom(output_type) do |
||||||
|
sha256(data, :binary) |
||||||
|
|> Base.encode16(case: :lower) |
||||||
|
end |
||||||
|
end |
||||||
Loading…
Reference in new issue