Packages

This is an implementation of Mark Qvist's Reticulum Network Stack, a 'cryptography-based networking stack for building local and wide-area networks with readily available hardware.'

Current section

Files

Jump to
reticulum lib rns crypto.ex
Raw

lib/rns/crypto.ex

defmodule RNS.Crypto do
@moduledoc """
Crypto primitives for Reticulum.
Lazily copied from https://github.com/Sgiath/reticulum.
Usually, you won't use this module but the Identity module, which then uses this module.
"""
@type hash() :: <<_::256>>
@type truncated_hash() :: <<_::128>>
@type key() :: <<_::256>>
@type signature() :: <<_::512>>
@type ratchet() :: <<_::256>>
@hash_len 32
@doc """
https://soatok.blog/2021/11/17/understanding-hkdf/
https://en.wikipedia.org/wiki/HKDF
"""
@spec hkdf(binary(), binary(), binary(), non_neg_integer()) :: <<_::512>>
def hkdf(ikm, salt \\ <<>>, info \\ <<>>, len \\ 64)
when len > 0 and is_binary(ikm) and byte_size(ikm) > 0 do
salt
|> hkdf_extract(ikm)
|> hkdf_expand(info, len)
end
@spec hkdf_extract(binary(), binary()) :: binary()
defp hkdf_extract(<<>>, ikm), do: hkdf_extract(<<0::size(256)>>, ikm)
defp hkdf_extract(salt, ikm), do: hmac(salt, ikm)
@spec hkdf_expand(binary(), binary(), non_neg_integer()) :: binary()
defp hkdf_expand(prk, info, len) do
{<<okm::binary-size(len), _rest::binary>>, _t} =
Enum.reduce(1..ceil(len / @hash_len), {<<>>, <<>>}, fn i, {okm, t} ->
t = hmac(prk, t <> info <> <<i>>)
{okm <> t, t}
end)
okm
end
@spec truncate(binary(), non_neg_integer()) :: binary()
def truncate(data, length) do
<<truncated_data::binary-size(length), _rest::binary>> = data
truncated_data
end
@spec sha256(binary()) :: hash()
def sha256(data), do: :crypto.hash(:sha256, data)
@spec truncated_sha256(binary()) :: truncated_hash()
def truncated_sha256(data) do
truncate(:crypto.hash(:sha256, data), 16)
end
@spec hmac(iodata(), binary()) :: <<_::256>>
def hmac(key, data) do
:crypto.mac(:hmac, :sha256, key, data)
end
@spec ed25519() :: {key(), key()}
def ed25519() do
:crypto.generate_key(:eddsa, :ed25519)
end
@spec x25519() :: {key(), key()}
def x25519() do
:crypto.generate_key(:eddh, :x25519)
end
@spec compute(key(), key()) :: key()
def compute(secret, public) do
:crypto.compute_key(:eddh, public, secret, :x25519)
end
@spec sign(key(), binary()) :: signature()
def sign(key, data) do
:crypto.sign(:eddsa, :none, data, [key, :ed25519])
end
@spec validate?(key(), binary(), signature()) :: boolean()
def validate?(key, data, signature) do
:crypto.verify(:eddsa, :none, data, signature, [key, :ed25519])
end
end