Skip to content

Commit

Permalink
universal signatures adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
patatoid committed Dec 20, 2024
1 parent 8695a2d commit c333ca1
Show file tree
Hide file tree
Showing 18 changed files with 365 additions and 290 deletions.
43 changes: 36 additions & 7 deletions lib/boruta/adapters/ecto/schemas/client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ defmodule Boruta.Ecto.Client do
jwt_public_key: String.t(),
public_key: String.t(),
private_key: String.t(),
response_mode: String.t()
response_mode: String.t(),
signatures_adapter: String.t()
}

@token_endpoint_auth_methods [
Expand Down Expand Up @@ -94,6 +95,9 @@ defmodule Boruta.Ecto.Client do
"HS256",
"HS384",
"HS512"
],
"universal" => [
"EdDSA"
]
}

Expand Down Expand Up @@ -124,6 +128,8 @@ defmodule Boruta.Ecto.Client do
field(:id_token_signature_alg, :string, default: "RS512")
field(:id_token_kid, :string)

field(:signatures_adapter, :string, default: "Elixir.Boruta.Internal.Signatures")

field(:key_pair_type, :map,
default: %{
"type" => "rsa",
Expand Down Expand Up @@ -190,7 +196,8 @@ defmodule Boruta.Ecto.Client do
:logo_uri,
:metadata,
:response_mode,
:key_pair_type
:signatures_adapter,
:key_pair_type,
])
|> validate_required([:redirect_uris, :key_pair_type])
|> unique_constraint(:id, name: :clients_pkey)
Expand All @@ -214,6 +221,7 @@ defmodule Boruta.Ecto.Client do
)
|> put_assoc(:authorized_scopes, parse_authorized_scopes(attrs))
|> translate_jwk()
|> validate_signatures_adapter()
|> validate_key_pair_type()
|> generate_key_pair()
|> put_secret()
Expand Down Expand Up @@ -250,6 +258,7 @@ defmodule Boruta.Ecto.Client do
:logo_uri,
:metadata,
:response_mode,
:signatures_adapter,
:key_pair_type
])
|> validate_required([
Expand Down Expand Up @@ -278,6 +287,7 @@ defmodule Boruta.Ecto.Client do
|> validate_supported_grant_types()
|> validate_id_token_signature_alg()
|> put_assoc(:authorized_scopes, parse_authorized_scopes(attrs))
|> validate_signatures_adapter()
|> validate_key_pair_type()
|> translate_jwk()
end
Expand Down Expand Up @@ -354,6 +364,19 @@ defmodule Boruta.Ecto.Client do
end
end

defp validate_signatures_adapter(changeset) do
key_pair_type = get_field(changeset, :key_pair_type)

case key_pair_type do
%{"type" => "universal"} ->
validate_inclusion(changeset, :signatures_adapter, [Atom.to_string(Boruta.Universal.Signatures)])
%{"type" => type} when type in ["ec", "rsa"] ->
validate_inclusion(changeset, :signatures_adapter, [Atom.to_string(Boruta.Internal.Signatures)])
_ ->
add_error(changeset, :signatures_adapter, "unknown key pair type")
end
end

defp validate_key_pair_type(changeset) do
key_pair_type = get_field(changeset, :key_pair_type)

Expand Down Expand Up @@ -471,14 +494,18 @@ defmodule Boruta.Ecto.Client do

"universal" ->
with {:ok, did, jwk} <- Did.create("key"),
{:ok, key} <- Universal.Signatures.SigningKey.get_key_by_did(did) do
{:ok, key_id} <- Universal.Signatures.SigningKey.get_key_by_did(did) do
"did:key:" <> key = did
public_key = JOSE.JWK.from_map(jwk)
{_type, public_pem} = JOSE.JWK.to_pem(public_key)

changeset
|> put_change(:private_key, key["id"])
|> put_change(:private_key, key_id["id"])
|> put_change(:public_key, public_pem)
|> put_change(:did, did)
|> put_change(:did, "#{did}##{key}")
|> put_change(:signatures_adapter, Boruta.Universal.Signatures |> Atom.to_string())
|> put_change(:id_token_signature_alg, "EdDSA")
|> put_change(:userinfo_signed_response_alg, "EdDSA")
else
{:error, error} ->
add_error(changeset, :private_key, error)
Expand All @@ -493,6 +520,7 @@ defmodule Boruta.Ecto.Client do
changeset
|> put_change(:public_key, public_pem)
|> put_change(:private_key, private_pem)
|> put_change(:signatures_adapter, Boruta.Internal.Signatures |> Atom.to_string())
end
end

Expand All @@ -518,8 +546,9 @@ defmodule Boruta.Ecto.Client do
{_, jwk} = JOSE.JWK.from_pem(pem) |> JOSE.JWK.to_map()

case Did.create("key", jwk) do
{:ok, did} ->
put_change(changeset, :did, did)
{:ok, did, _jwk} ->
"did:key:" <> key = did
put_change(changeset, :did, "#{did}##{key}")

{:error, error} ->
add_error(changeset, :did, error)
Expand Down
29 changes: 2 additions & 27 deletions lib/boruta/adapters/internal/signatures.ex
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ defmodule Boruta.Internal.Signatures do
HS512: [type: :symmetric, hash_algorithm: :SHA512, binary_size: 32]
]

@spec signature_algorithms() :: list(atom())
def signature_algorithms, do: Keyword.keys(@signature_algorithms)

@spec hash_alg(Client.t()) :: hash_alg :: atom()
def hash_alg(%Client{id_token_signature_alg: signature_alg}),
do: @signature_algorithms[String.to_atom(signature_alg)][:hash_algorithm]
Expand Down Expand Up @@ -83,23 +80,6 @@ defmodule Boruta.Internal.Signatures do
end
end

@spec verify_id_token_signature(id_token :: String.t(), jwk :: JOSE.JWK.t()) ::
:ok | {:error, reason :: String.t()}
def verify_id_token_signature(id_token, jwk) do
case Joken.peek_header(id_token) do
{:ok, %{"alg" => alg}} ->
signer = Joken.Signer.create(alg, %{"pem" => JOSE.JWK.from_map(jwk) |> JOSE.JWK.to_pem()})

case Token.verify(id_token, signer) do
{:ok, claims} -> {:ok, claims}
{:error, reason} -> {:error, inspect(reason)}
end

{:error, reason} ->
{:error, inspect(reason)}
end
end

@spec userinfo_sign(payload :: map(), client :: Client.t()) ::
jwt :: String.t() | {:error, reason :: String.t()}
def userinfo_sign(
Expand Down Expand Up @@ -172,11 +152,6 @@ defmodule Boruta.Internal.Signatures do
end
end

@spec kid_from_private_key(private_pem :: String.t()) :: kid :: String.t()
def kid_from_private_key(private_pem) do
:crypto.hash(:md5, private_pem) |> Base.url_encode64() |> String.slice(0..16)
end

@spec userinfo_signature_type(Client.t()) :: userinfo_token_signature_type :: atom()
def userinfo_signature_type(%Client{userinfo_signed_response_alg: signature_alg}),
do: @signature_algorithms[String.to_atom(signature_alg)][:type]
Expand All @@ -191,7 +166,7 @@ defmodule Boruta.Internal.Signatures do
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.id_token_kid || kid_from_private_key(client.private_key)
kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key)
}}
end

Expand All @@ -211,7 +186,7 @@ defmodule Boruta.Internal.Signatures do
type: :internal,
private_key: client.private_key,
secret: client.secret,
kid: client.did || client.id_token_kid || kid_from_private_key(client.private_key)
kid: client.did || client.id_token_kid || Client.Crypto.kid_from_private_key(client.private_key)
}}
end
end
42 changes: 0 additions & 42 deletions lib/boruta/adapters/signatures.ex

This file was deleted.

Loading

0 comments on commit c333ca1

Please sign in to comment.