Packages
stellar_sdk
0.11.2
0.23.0
0.22.0
0.21.2
0.21.1
0.21.0
0.20.0
0.19.0
0.18.1
0.18.0
0.17.1
0.17.0
0.16.1
0.16.0
0.15.1
0.15.0
0.14.0
0.13.1
0.13.0
0.12.0
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.2
0.10.1
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.1
0.8.0
0.7.1
0.7.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
Elixir SDK for the Stellar network.
Current section
Files
Jump to
Current section
Files
lib/tx_build/signature.ex
defmodule Stellar.TxBuild.Signature do
@moduledoc """
`Signature` struct definition.
"""
alias Stellar.KeyPair
alias StellarBase.XDR.{
DecoratedSignature,
Signature,
SignatureHint,
PublicKey,
PublicKeyType,
UInt256
}
@behaviour Stellar.TxBuild.XDR
@type type :: :ed25519 | :hash_x | :signed_payload
@type key :: String.t() | {String.t(), String.t()}
@type raw_key :: binary() | {binary(), binary()}
@type t :: %__MODULE__{
type: type(),
key: key(),
raw_key: raw_key(),
hint: binary()
}
defstruct [:type, :key, :raw_key, :hint]
@impl true
def new(args, opts \\ [])
def new({_public_key, secret}, _opts), do: new(ed25519: secret)
def new([ed25519: secret], _opts) do
case KeyPair.validate_secret_seed(secret) do
:ok -> build_signature(ed25519: secret)
error -> error
end
end
def new([hash_x: preimage], _opts) do
case validate_preimage(preimage) do
:ok -> build_signature(hash_x: preimage)
error -> error
end
end
def new([signed_payload: [payload: payload, ed25519: secret]], _opts) do
with :ok <- validate_payload(payload),
:ok <- KeyPair.validate_secret_seed(secret),
do: build_signature(signed_payload: {payload, secret})
end
@spec to_xdr(signature :: t(), base_signature :: binary()) :: DecoratedSignature.t()
def to_xdr(%__MODULE__{type: :ed25519, key: secret, hint: hint}, base_signature) do
base_signature
|> KeyPair.sign(secret)
|> decorated_signature(hint)
end
def to_xdr(%__MODULE__{} = signature, _base_signature), do: to_xdr(signature)
@impl true
def to_xdr(%__MODULE__{
type: :signed_payload,
key: {_payload, secret},
raw_key: {raw_payload, _raw_secret},
hint: hint
})
when byte_size(raw_payload) < 4 do
zeros_needed = 4 - byte_size(raw_payload)
<<raw_payload::binary, 0::zeros_needed*8>>
|> KeyPair.sign(secret)
|> decorated_signature(hint)
end
def to_xdr(%__MODULE__{
type: :signed_payload,
key: {_payload, secret},
raw_key: {raw_payload, _raw_secret},
hint: hint
}) do
raw_payload
|> KeyPair.sign(secret)
|> decorated_signature(hint)
end
def to_xdr(%__MODULE__{raw_key: raw_key, hint: hint}),
do: decorated_signature(raw_key, hint)
@spec decorated_signature(raw_signature :: binary(), hint :: binary()) :: DecoratedSignature.t()
defp decorated_signature(raw_signature, hint) do
signature = Signature.new(raw_signature)
hint
|> SignatureHint.new()
|> DecoratedSignature.new(signature)
end
@spec build_signature([{type(), String.t() | tuple()}]) :: t()
defp build_signature(ed25519: secret) do
raw_secret = KeyPair.raw_secret_seed(secret)
signature_hint = signature_hint(ed25519: raw_secret)
%__MODULE__{
type: :ed25519,
key: secret,
raw_key: raw_secret,
hint: signature_hint
}
end
defp build_signature(hash_x: preimage) do
raw_preimage = Base.decode16!(preimage, case: :lower)
signature_hint = signature_hint(hash_x: raw_preimage)
%__MODULE__{
type: :hash_x,
key: preimage,
raw_key: raw_preimage,
hint: signature_hint
}
end
defp build_signature(signed_payload: {payload, secret}) do
raw_payload = Base.decode16!(payload, case: :lower)
raw_secret = KeyPair.raw_secret_seed(secret)
{public_key, _secret} = KeyPair.from_secret_seed(secret)
signature_hint =
public_key
|> KeyPair.raw_public_key()
|> KeyPair.signature_hint_for_signed_payload(raw_payload)
%__MODULE__{
type: :signed_payload,
key: {payload, secret},
raw_key: {raw_payload, raw_secret},
hint: signature_hint
}
end
@spec signature_hint([{type(), binary()}]) :: binary()
defp signature_hint(ed25519: raw_secret) do
key_type = PublicKeyType.new(:PUBLIC_KEY_TYPE_ED25519)
raw_secret
|> Ed25519.derive_public_key()
|> UInt256.new()
|> PublicKey.new(key_type)
|> PublicKey.encode_xdr!()
|> extract_hint()
end
defp signature_hint(hash_x: raw_preimage) do
:sha256
|> :crypto.hash(raw_preimage)
|> extract_hint()
end
@spec extract_hint(raw_key :: binary()) :: binary()
defp extract_hint(raw_key) do
bytes_size = byte_size(raw_key)
binary_part(raw_key, bytes_size, -4)
end
@spec validate_preimage(preimage :: String.t()) :: :ok | {:error, :invalid_preimage}
defp validate_preimage(preimage) do
with {:ok, raw_preimage} <- Base.decode16(preimage, case: :lower),
32 <- byte_size(raw_preimage) do
:ok
else
_ -> {:error, :invalid_preimage}
end
end
@spec validate_payload(payload :: String.t()) :: :ok | {:error, :invalid_payload}
defp validate_payload(payload) do
with {:ok, raw_payload} <- Base.decode16(payload, case: :lower),
size <- byte_size(raw_payload),
true <- size <= 32 do
:ok
else
_ -> {:error, :invalid_payload}
end
end
end