Current section
Files
Jump to
Current section
Files
lib/utils/crypto.ex
defmodule Forge.Crypto do
@moduledoc """
Crypto related functions
"""
@doc """
Generate an Ethereum compatible sha3
iex> Forge.Crypto.sha3("hello")
<<28, 138, 255, 149, 6, 133, 194, 237, 75, 195, 23, 79, 52, 114, 40, 123, 86, 217, 81, 123, 156, 148, 129, 39, 49, 154, 9, 167, 163, 109, 234, 200>>
iex> Forge.Crypto.sha3("hello", :upper)
"1C8AFF950685C2ED4BC3174F3472287B56D9517B9C948127319A09A7A36DEAC8"
iex> Forge.Crypto.sha3("hello", :lower)
"1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8"
"""
@spec sha3(binary()) :: binary()
def sha3(data), do: :keccakf1600.hash(:sha3_256, data)
@spec sha3(binary(), :upper | :lower) :: binary()
def sha3(data, :upper), do: data |> sha3() |> Base.encode16()
def sha3(data, :lower), do: data |> sha3() |> Base.encode16(case: :lower)
@doc """
Sign data with private key
"""
@spec ecdsa_sign!(binary(), binary()) :: binary()
def ecdsa_sign!(data, <<encoded_private_key::binary-size(64)>>) do
ecdsa_sign!(data, Base.decode16!(encoded_private_key))
end
def ecdsa_sign!(data, <<private_key::binary-size(32)>>) do
{:ok, signature} = :libsecp256k1.ecdsa_sign(data, private_key, :default, <<>>)
signature
end
@doc """
Verify signature with public key
"""
@spec ecdsa_verify(binary(), binary(), binary()) :: :ok | :error
def ecdsa_verify(data, signature, <<encoded_public_key::binary-size(130)>>) do
ecdsa_verify(data, signature, Base.decode16!(encoded_public_key))
end
def ecdsa_verify(data, signature, <<4::size(8), _::binary-size(64)>> = public_key) do
:libsecp256k1.ecdsa_verify(data, signature, public_key)
end
@doc """
Create a random private key
"""
@spec create_private_key :: String.t()
def create_private_key, do: :crypto.strong_rand_bytes(32)
@doc """
Create a public key based on private key
"""
@spec create_public_key(String.t()) :: String.t()
def create_public_key(<<encoded_private_key::binary-size(64)>>) do
encoded_private_key |> Base.decode16!() |> create_public_key()
end
def create_public_key(private_key) do
{:ok, public_key} = :libsecp256k1.ec_pubkey_create(private_key, :uncompressed)
public_key
end
@doc """
Create wallet address based on public key
"""
@spec create_wallet_address(String.t()) :: String.t()
def create_wallet_address(<<encoded_public_key::binary-size(130)>>) do
encoded_public_key |> Base.decode16!() |> create_wallet_address()
end
def create_wallet_address(public_key) do
<<4::size(8), key::binary-size(64)>> = public_key
<<_::binary-size(12), address::binary-size(20)>> = sha3(key)
Base.encode16(address, case: :lower)
end
@doc """
Generate a tendermint tx hash based on tx data
iex> Forge.Crypto.get_tm_hash("hello")
"2CF24DBA5FB0A30E26E83B2AC5B9E29E1B161E5C1FA7425E73043362938B9824"
"""
@spec get_tm_hash(String.t()) :: String.t()
def get_tm_hash(data) do
# <<hash::binary-size(20), _::binary>> = :crypto.hash(:sha256, data)
# in 0.26.0 tendermint changed this to use full hash
:sha256 |> :crypto.hash(data) |> Base.encode16()
end
@spec block_encrypt(binary(), binary()) :: [iv: String.t(), ciphertext: String.t()]
def block_encrypt(cleartext, key) do
AES256.encrypt(cleartext, key)
end
def block_decrypt(cipher, key, iv) do
AES256.decrypt(cipher, key, iv)
end
end