From 830a17df532ebdb383c20ca2b15645f7f54484e2 Mon Sep 17 00:00:00 2001 From: madclaws Date: Sun, 29 Oct 2023 12:16:17 +0530 Subject: [PATCH] feat: added ucan token validate function --- lib/ex_ucan.ex | 5 +++++ lib/ex_ucan/core/plugins.ex | 10 ++++++++++ lib/ex_ucan/core/structs.ex | 1 - lib/ex_ucan/core/token.ex | 28 +++++++++++++++++++++++----- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/lib/ex_ucan.ex b/lib/ex_ucan.ex index 41e141e..e432e04 100644 --- a/lib/ex_ucan.ex +++ b/lib/ex_ucan.ex @@ -2,9 +2,14 @@ defmodule ExUcan do @moduledoc """ Documentation for `ExUcan`. """ + alias ExUcan.Plugins.Ed25519 alias ExUcan.Core.Token alias ExUcan.Core.Structs.Ucan + def create_keypair() do + Ed25519.Keypair.create() + end + @spec build(struct(), map()) :: {:ok, Ucan.t()} | {:error, String.t()} def build(keypair, _params) do Token.build(%{ diff --git a/lib/ex_ucan/core/plugins.ex b/lib/ex_ucan/core/plugins.ex index bf56774..de3afc4 100644 --- a/lib/ex_ucan/core/plugins.ex +++ b/lib/ex_ucan/core/plugins.ex @@ -1,10 +1,20 @@ defmodule ExUcan.Core.Plugins do + alias ExUcan.Plugins.Ed25519.Crypto # TODO: docs @spec verify_issuer_alg(String.t(), String.t()) :: boolean() def verify_issuer_alg(did, jwt_alg) do end + @spec verify_signature(String.t(), String.t(), String.t()) :: boolean() + def verify_signature(did, data, signature) do + {:ok, public_key} = Crypto.did_to_publickey(did) + IO.inspect(public_key) + IO.inspect(data) + IO.inspect(signature) + :public_key.verify(data, :ignored, signature, {:ed_pub, :ed25519, public_key}) + end + @spec parseDidMethod(String.t()) :: String.t() defp parseDidMethod(did) do parts = String.split(did, ":") diff --git a/lib/ex_ucan/core/structs.ex b/lib/ex_ucan/core/structs.ex index 722ce08..d60c4d2 100644 --- a/lib/ex_ucan/core/structs.ex +++ b/lib/ex_ucan/core/structs.ex @@ -58,6 +58,5 @@ defmodule ExUcan.Core.Structs.Ucan do signature: String.t() } - @derive Jason.Encoder defstruct [:header, :payload, :signed_data, :signature] end diff --git a/lib/ex_ucan/core/token.ex b/lib/ex_ucan/core/token.ex index d34ead0..37b4666 100644 --- a/lib/ex_ucan/core/token.ex +++ b/lib/ex_ucan/core/token.ex @@ -2,6 +2,8 @@ defmodule ExUcan.Core.Token do @moduledoc """ Creates and manages UCAN tokens """ + alias ExUcan.Core.Plugins + alias ExUnit.DuplicateDescribeError alias ExUcan.Core.Structs.UcanHeader alias ExUcan.Core.Keymaterial alias ExUcan.Core.Structs.Ucan @@ -76,8 +78,17 @@ defmodule ExUcan.Core.Token do "#{ucan.signed_data}.#{ucan.signature}" end + # TODO: Refactor thiess @spec validate(String.t()) :: {:ok, Ucan.t()} | {:error, String.t()} def validate(encoded_ucan) do + with {:ok, {header, payload}} <- parse_encoded_ucan(encoded_ucan), + false <- is_expired?(payload), + false <- is_too_early?(payload) do + [encoded_header, encoded_payload, encoded_sign] = String.split(encoded_ucan, ".") + {:ok, signature} = Base.url_decode64(encoded_sign, padding: false) + data = "#{encoded_header}.#{encoded_payload}" + Plugins.verify_signature(payload.iss, data, signature) + end end defp add_nonce(true), do: Utils.generate_nonce() @@ -92,6 +103,7 @@ defmodule ExUcan.Core.Token do signed_data = "#{encoded_header}.#{encoded_payload}" signature = Keymaterial.sign(keypair, signed_data) + IO.inspect(signature) %Ucan{ header: header, @@ -108,8 +120,14 @@ defmodule ExUcan.Core.Token do |> Base.url_encode64(padding: false) end - @spec is_expired?() :: boolean() - defp is_expired?() do + @spec is_expired?(UcanPayload.t()) :: boolean() + defp is_expired?(%UcanPayload{} = ucan_payload) do + ucan_payload.exp < DateTime.utc_now() |> DateTime.to_unix() + end + + @spec is_too_early?(UcanPayload.t()) :: boolean() + defp is_too_early?(%UcanPayload{nbf: nbf}) do + nbf > DateTime.utc_now() |> DateTime.to_unix() end @spec parse_encoded_ucan(String.t()) :: @@ -118,11 +136,11 @@ defmodule ExUcan.Core.Token do opts = [padding: false] with {:ok, {header, payload, sign}} <- tear_into_parts(encoded_ucan), - {:ok, decoded_header} <- Base.url_decode64(header, opts) |> IO.inspect(), - {:ok, header} <- Jason.decode(decoded_header, keys: :atoms) |> IO.inspect(), + {:ok, decoded_header} <- Base.url_decode64(header, opts), + {:ok, header} <- Jason.decode(decoded_header, keys: :atoms), {:ok, decoded_payload} <- Base.url_decode64(payload, opts), {:ok, payload} <- Jason.decode(decoded_payload, keys: :atoms) do - {:ok, struct(UcanHeader, header), struct(UcanPayload, payload)} + {:ok, {struct(UcanHeader, header), struct(UcanPayload, payload)}} end end