did.ex (4318B)
1 # Zenflows is designed to implement the Valueflows vocabulary, 2 # written and maintained by srfsh <info@dyne.org>. 3 # Copyright (C) 2021-2023 Dyne.org foundation <foundation@dyne.org>. 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU Affero General Public License as published by 7 # the Free Software Foundation, either version 3 of the License, or 8 # (at your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU Affero General Public License for more details. 14 # 15 # You should have received a copy of the GNU Affero General Public License 16 # along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 defmodule Zenflows.DID do 19 @moduledoc """ 20 A module to interact with the did controller instances over HTTPS. 21 """ 22 23 alias Zenflows.VF.Person 24 25 def child_spec(_) do 26 Supervisor.child_spec( 27 {Zenflows.HTTPC, 28 name: __MODULE__, 29 scheme: scheme(), 30 host: host(), 31 port: port(), 32 }, 33 id: __MODULE__) 34 end 35 36 # Execute a Zencode specified by `name` with JSON data `data`. 37 @spec exec(String.t(), map()) :: {:ok, map()} | {:error, term()} 38 defp exec(name, post_data) do 39 Zenflows.Restroom.request(&Zenflows.HTTPC.request(__MODULE__, &1, &2, &3, &4), 40 "/v1/sandbox/#{name}", post_data) 41 end 42 43 @spec did_id(Person.t()) :: String.t() 44 defp did_id(person) do 45 "did:dyne:ifacer:#{person.eddsa_public_key}" 46 end 47 48 @spec get_did(Person.t()) :: {:ok, map()} | {:error, term()} 49 def get_did(person) do 50 with {:ok, %{status: stat, data: body}} when stat == 200 <- 51 Zenflows.HTTPC.request(__MODULE__, "GET", 52 "/dids/#{did_id(person)}") do 53 case Jason.decode(body) do 54 {:ok, data} -> {:ok, %{"created" => false, "did" => data}} 55 {:error, err} -> {:error, err} 56 end 57 else 58 _err -> {:error, "DID not found"} 59 end 60 end 61 62 @spec request_new_did(Person.t()) :: {:ok, map()} | {:error, term()} 63 def request_new_did(person) do 64 did_request = %{ 65 "proof" => %{ 66 "type" => "EcdsaSecp256k1Signature2019", 67 "proofPurpose" => "assertionMethod" 68 }, 69 "@context" => [ 70 "https://www.w3.org/ns/did/v1", 71 "https://w3id.org/security/suites/ed25519-2018/v1", 72 "https://w3id.org/security/suites/secp256k1-2019/v1", 73 "https://w3id.org/security/suites/secp256k1-2020/v1", 74 "https://dyne.github.io/W3C-DID/specs/ReflowBLS12381.json", 75 %{ 76 "description" => "https://schema.org/description", 77 "identifier" => "https://schema.org/identifier" 78 } 79 ], 80 "did_spec" => "ifacer", 81 "signer_did_spec" => "ifacer.A", 82 "identity" => "Ifacer user test", 83 "ifacer_id" => %{"identifier" => person.id}, 84 "bitcoin_public_key" => person.bitcoin_public_key, 85 "ecdh_public_key" => person.ecdh_public_key, 86 "eddsa_public_key" => person.eddsa_public_key, 87 "ethereum_address" => person.ethereum_address, 88 "reflow_public_key" => person.reflow_public_key, 89 "timestamp" => 90 DateTime.utc_now() |> DateTime.to_unix(:millisecond) |> to_string 91 } 92 93 with {:ok, did} <- 94 Zenflows.Restroom.exec("pubkeys-request-signed", 95 Map.merge(did_request, keyring())), 96 {:ok, did_signed} <- exec("pubkeys-accept.chain", did) 97 do 98 {:ok, %{"created" => true, "did" => did_signed}} 99 end 100 end 101 102 @spec claim(Ecto.Repo.t(), %{person: Person.t()}) :: {:ok, map()} | {:error, term()} 103 def claim(_repo, %{person: person}) do 104 if keyring() == nil do 105 {:error, "DID Controller not configured"} 106 else 107 case get_did(person) do 108 {:ok, did} -> {:ok, did} 109 _ -> request_new_did(person) 110 end 111 end 112 end 113 114 # Return the scheme of did from the configs. 115 @spec scheme() :: :http | :https 116 defp scheme() do 117 Keyword.fetch!(conf(), :did_uri).scheme 118 end 119 120 # Return the hostname of did from the configs. 121 @spec host() :: String.t() 122 defp host() do 123 Keyword.fetch!(conf(), :did_uri).host 124 end 125 126 # Return the port of did from the configs. 127 @spec port() :: non_neg_integer() 128 defp port() do 129 Keyword.fetch!(conf(), :did_uri).port 130 end 131 132 # Return the private keyring of the server from the configs. 133 @spec keyring() :: nil | map() 134 defp keyring() do 135 Keyword.fetch!(conf(), :did_keyring) 136 end 137 138 # Return the application configurations of this module. 139 @spec conf() :: Keyword.t() 140 defp conf() do 141 Application.fetch_env!(:zenflows, __MODULE__) 142 end 143 end