Current section
Files
Jump to
Current section
Files
lib/firebird/phoenix/csrf_wasm.ex
defmodule Firebird.Phoenix.CsrfWasm do
@moduledoc """
WASM-accelerated CSRF token generation and validation.
Uses a compiled WASM module for constant-time token comparison
and HMAC-based token signing, providing timing-attack resistant
CSRF protection.
## Examples
{:ok, csrf} = Firebird.load("fixtures/phoenix_csrf.wasm")
# Generate a token
{:ok, token} = CsrfWasm.generate_token(csrf, "my_secret_key")
# Verify a token
{:ok, :valid} = CsrfWasm.verify_token(csrf, token, "my_secret_key")
# Constant-time comparison
{:ok, true} = CsrfWasm.constant_compare(csrf, "abc", "abc")
{:ok, false} = CsrfWasm.constant_compare(csrf, "abc", "def")
"""
alias Firebird.Phoenix.WasmHelper
@doc """
Generate a CSRF token signed with the given secret key.
Returns a base64-encoded token containing random bytes and an HMAC signature.
"""
@spec generate_token(pid(), String.t()) :: {:ok, String.t()} | {:error, term()}
def generate_token(instance, secret_key) when is_binary(secret_key) do
WasmHelper.call_string(instance, "generate_token", secret_key)
end
@doc """
Verify a CSRF token against the secret key.
Uses constant-time comparison to prevent timing attacks.
Returns `{:ok, :valid}` or `{:ok, :invalid}` with reason.
"""
@spec verify_token(pid(), String.t(), String.t()) :: {:ok, :valid | :invalid} | {:error, term()}
def verify_token(instance, token, secret_key) when is_binary(token) and is_binary(secret_key) do
input = "#{token}|#{secret_key}"
case WasmHelper.call_string(instance, "verify_token", input) do
{:ok, "valid"} -> {:ok, :valid}
{:ok, "invalid" <> _} -> {:ok, :invalid}
{:error, reason} -> {:error, reason}
end
end
@doc """
Sign a message with a secret key using HMAC-like signing.
Returns a hex-encoded signature.
"""
@spec sign_message(pid(), String.t(), String.t()) :: {:ok, String.t()} | {:error, term()}
def sign_message(instance, message, secret_key) do
input = "#{message}|#{secret_key}"
WasmHelper.call_string(instance, "sign_message", input)
end
@doc """
Compare two strings in constant time.
Prevents timing attacks by ensuring comparison takes the same
amount of time regardless of where strings differ.
"""
@spec constant_compare(pid(), String.t(), String.t()) :: {:ok, boolean()} | {:error, term()}
def constant_compare(instance, a, b) when is_binary(a) and is_binary(b) do
input = "#{a}|#{b}"
case WasmHelper.call_string(instance, "constant_compare", input) do
{:ok, "match"} -> {:ok, true}
{:ok, "no_match"} -> {:ok, false}
{:error, reason} -> {:error, reason}
end
end
@doc """
Seed the WASM PRNG for token generation.
Should be called once at startup with a random seed value.
"""
@spec seed(pid(), String.t()) :: :ok | {:error, term()}
def seed(instance, seed_value) when is_binary(seed_value) do
case WasmHelper.call_string(instance, "seed_rng", seed_value) do
{:ok, _} -> :ok
{:error, reason} -> {:error, reason}
end
end
end