Current section

Files

Jump to
signet lib signet signer cloud_kms.ex
Raw

lib/signet/signer/cloud_kms.ex

if Code.ensure_loaded?(GoogleApi.CloudKMS.V1.Api.Projects) do
defmodule Signet.Signer.CloudKMS do
@moduledoc """
Signer to sign messages from a Google Cloud KMS key.
"""
import Signet.Hash, only: [keccak: 1]
alias GoogleApi.CloudKMS.V1.Api.Projects, as: CloudKMSApi
@doc ~S"""
Get the Ethereum address associated with the given KMS key version.
## Examples
iex> {:ok, address} = Signet.Signer.CloudKMS.get_address("token", "project", "location", "keychain", "key", "version")
iex> Signet.Hex.to_hex(address)
"0xdda641b2a76a4a7c3617815bb13281dd207b74d5"
"""
@spec get_address(term(), String.t(), String.t(), String.t(), String.t(), String.t()) ::
{:ok, binary()} | {:error, String.t()}
def get_address(cred, project, location, keychain, key, version) do
with {:ok, %GoogleApi.CloudKMS.V1.Model.PublicKey{algorithm: algorithm, pem: pem}} <-
CloudKMSApi.cloudkms_projects_locations_key_rings_crypto_keys_crypto_key_versions_get_public_key(
client(cred),
project,
location,
keychain,
key,
version
) do
case algorithm do
"EC_SIGN_SECP256K1_SHA256" ->
[certs] = :public_key.pem_decode(pem)
{{:ECPoint, public_key}, _} = :public_key.pem_entry_decode(certs)
{:ok, Signet.Util.get_eth_address(public_key)}
_ ->
{:error, "Invalid algorithm: #{algorithm}"}
end
end
end
@doc ~S"""
Signs a message with the given KMS key version, after digesting the message with keccak.
## Examples
iex> use Signet.Hex
iex> {:ok, sig} = Signet.Signer.CloudKMS.sign("test", "token", "project", "location", "keychain", "key", "version")
iex> {:ok, recid} = Signet.Recover.find_recid("test", sig, ~h[0xDDA641B2A76A4A7C3617815BB13281DD207B74D5])
iex> Signet.Recover.recover_eth("test", %{sig|recid: recid}) |> Hex.to_address()
"0xDDa641B2A76a4A7c3617815bb13281DD207b74d5"
"""
@spec sign(String.t(), term(), String.t(), String.t(), String.t(), String.t(), String.t()) ::
{:ok, Curvy.Signature.t()} | {:error, String.t()}
def sign(message, cred, project, location, keychain, key, version) when is_binary(message) do
message_hash_enc =
message
|> keccak()
|> Base.encode64()
with {:ok, response} <-
CloudKMSApi.cloudkms_projects_locations_key_rings_crypto_keys_crypto_key_versions_asymmetric_sign(
client(cred),
project,
location,
keychain,
key,
version,
body: %{
digest: %{
sha256: message_hash_enc
}
}
),
{:ok, decoded_sig} <- Base.decode64(response.signature) do
{:ok, Curvy.Signature.parse(decoded_sig)}
end
end
defp client(token) when is_binary(token), do: GoogleApi.CloudKMS.V1.Connection.new(token)
defp client(cred) do
%{token: token, type: "Bearer"} = Goth.fetch!(cred)
GoogleApi.CloudKMS.V1.Connection.new(token)
end
end
end