Packages
baobab
0.5.2
0.35.1
0.35.0
0.34.0
0.31.0
0.30.0
0.21.2
0.21.1
0.21.0
0.20.0
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.1
0.17.0
0.16.8
0.16.7
0.16.6
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.2
0.15.1
0.15.0
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.2
0.5.1
0.5.0
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.0
Bamboo append-only logs
Current section
Files
Jump to
Current section
Files
lib/baobab/entry/validator.ex
defmodule Baobab.Entry.Validator do
@moduledoc """
Validation of `Baobab.Entry` structs
"""
@doc """
Validate a `Baobab.Entry` struct
Includes validation of its available certificate pool
"""
@spec validate(%Baobab.Entry{}) :: %Baobab.Entry{} | :error
def validate(%Baobab.Entry{seqnum: seq, author: author, log_id: log_id} = entry) do
case valid_entry?(entry) do
false ->
:error
true ->
case verify_chain(
Baobab.certificate_pool(author, seq, {:entry, log_id, true, false}),
{author, log_id},
true
) do
false -> :error
true -> entry
end
end
end
def validate(_), do: :error
defp verify_chain([], _log, answer), do: answer
defp verify_chain(_links, _log, false), do: false
defp verify_chain([seq | rest], {author, log_id} = which, answer) do
truth =
case Baobab.Entry.retrieve(author, seq, {:entry, log_id, false, false}) do
:error -> false
link -> valid_link?(link)
end
verify_chain(rest, which, answer and truth)
end
defp valid_link?(entry) do
valid_sig?(entry) and valid_backlink?(entry) and valid_lipmaalink?(entry)
end
@doc """
Validate a `Baobab.Entry` without full certificate pool verification.
Confirms:
- Signature
- Payload hash
- Backlink
- Lipmaalink
"""
@spec valid_entry?(%Baobab.Entry{}) :: boolean
def valid_entry?(entry) do
valid_sig?(entry) and valid_payload_hash?(entry) and valid_backlink?(entry) and
valid_lipmaalink?(entry)
end
@doc """
Validate the `sig` field of a `Baobab.Entry`
"""
@spec valid_sig?(%Baobab.Entry{}) :: boolean
def valid_sig?(%Baobab.Entry{
sig: sig,
author: author,
seqnum: seq,
log_id: log_id
}) do
wsig = Baobab.Entry.file({author, log_id, seq}, :contents)
Ed25519.valid_signature?(sig, :binary.part(wsig, {0, byte_size(wsig) - 64}), author)
end
@doc """
Validate the `payload_hash` field of a `Baobab.Entry`
"""
@spec valid_payload_hash?(%Baobab.Entry{}) :: boolean
def valid_payload_hash?(%Baobab.Entry{payload: payload, payload_hash: hash}) do
YAMFhash.verify(hash, payload) == ""
end
@doc """
Validate the `lipmaalink` field of a `Baobab.Entry`
"""
@spec valid_lipmaalink?(%Baobab.Entry{}) :: boolean
def valid_lipmaalink?(%Baobab.Entry{seqnum: 1, lipmaalink: nil}), do: true
def valid_lipmaalink?(%Baobab.Entry{author: author, log_id: log_id, seqnum: seq, lipmaalink: ll}) do
case {seq - 1, Lipmaa.linkseq(seq), ll} do
{n, n, nil} ->
true
{n, n, _} ->
false
{_, n, ll} ->
case Baobab.Entry.file({author, log_id, n}, :contents) do
:error -> false
fll -> YAMFhash.verify(ll, fll) == ""
end
end
end
@doc """
Validate the `backlink` field of a `Baobab.Entry`
"""
@spec valid_backlink?(%Baobab.Entry{}) :: boolean
def valid_backlink?(%Baobab.Entry{seqnum: 1, backlink: nil}), do: true
def valid_backlink?(%Baobab.Entry{backlink: nil}), do: false
def valid_backlink?(%Baobab.Entry{author: author, log_id: log_id, seqnum: seq, backlink: bl}) do
case Baobab.Entry.file({author, log_id, seq - 1}, :contents) do
# We don't have it so we cannot check it. We'll say it's OK
# This is required for partial replication to be meaningful.
# I am sure I will come to regret this post-haste
:error -> true
back_entry -> YAMFhash.verify(bl, back_entry) == ""
end
end
end