AFAICS you’re using a SHA256 HMAC signature (verifier) where it should be an Ed25519 signature/verifier.
I also looked into a couple other language implementations and they all seem to use NaCL/Libsodium or Ed25519 library. In (pure) elixir this could be done with ed25519 | Hex
These crypto things are pretty hard to get right so I always try to get a baseline test case with some parameters of which I know should work. Here I’ve put them in a basic unit test below. To be able to test this easily from a unit test perspective I’ve extracted the core “valid_request?” function into a seperate module.
Something like this seems to work;
defmodule DiscordSignatureVerifierTest do
use ExUnit.Case
test "valid_request? with valid signature" do
expected_signature = "f31a129c4e06d93e195ea019392fc568fa7d63c9b43beb436d75f6826d5e5d36270763ee438f13ad5686ed310e8fa3253426af798927bf69cee2ff21be589109"
timestamp = "1625603592"
body = "this should be a json."
key = "e421dceefff3a9d008b7898fcc0974813201800419d72f36d51e010d6a0acb71"
assert DiscordSignatureVerifier.valid_request?(expected_signature, timestamp, body, key) == :ok
end
end
where the valid_request? plug function itself is then
defmodule DiscordSignatureVerifier do
def valid_request?(expected_signature, timestamp, body, key) do
payload = "#{timestamp}#{body}"
case Ed25519.valid_signature?(from_hex(expected_signature), payload, from_hex(key)) do
true -> :ok
false -> {:error, "Signatures do not match"}
end
end
def from_hex(<<>>), do: ""
def from_hex(s) do
size = div(byte_size(s), 2)
{n, ""} = s |> Integer.parse(16)
zero_pad(:binary.encode_unsigned(n), size)
end
def zero_pad(s, size) when byte_size(s) == size, do: s
def zero_pad(s, size) when byte_size(s) < size, do: zero_pad(<<0>> <> s, size)
end
(from_hex/zero_pad comes from ed25519_ex/test/test_helper.exs at master · mwmiller/ed25519_ex · GitHub )
Hopefully this helps you enough to get on with your project!






















