summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/plop.hrl36
-rw-r--r--src/db.erl126
-rw-r--r--src/db.hrl19
-rw-r--r--src/ht.erl26
-rw-r--r--src/plop.erl283
-rw-r--r--test/plop_test.erl94
6 files changed, 166 insertions, 418 deletions
diff --git a/include/plop.hrl b/include/plop.hrl
index b62a04a..b04af0d 100644
--- a/include/plop.hrl
+++ b/include/plop.hrl
@@ -5,46 +5,12 @@
%%% database storage, some for interfacing with consumers and some are
%%% for serialisation.
--define(PLOPVERSION, 0).
-
--type signature_type() :: certificate_timestamp | tree_hash | test. % uint8
--type entry_type() :: x509 | precert | test. % uint16
--type leaf_type() :: timestamped_entry | test. % uint8
-
%% @doc Merkle Tree Leaf -- what's sent as 'leaf_input' in response to
%% get-entries requests and also the input to the hash function for
%% leaf hashes in the tree. RFC 6962 sect 3.4.
--record(mtl, {
- version = ?PLOPVERSION :: non_neg_integer(),
- leaf_type = timestamped_entry :: leaf_type(),
- entry :: timestamped_entry()
- }).
--type mtl() :: #mtl{}.
-
--record(spt, {
- version :: non_neg_integer(), % uint8
- logid :: binary(), % SHA-256 over DER encoded public log key
- timestamp :: integer(), % uint64
- signature :: signature()
- }).
--type spt() :: #spt{}.
-
-%% A plop entry with timestamp. Part of the Merkle Tree Leaf
-%% structure.
--record(timestamped_entry, {
- timestamp = now :: now | integer(),
- entry :: plop_entry()
- }).
--type timestamped_entry() :: #timestamped_entry{}.
%% An entry, without the timestamp. This is what we hash over and
%% store in the the database for finding duplicated submissions.
--record(plop_entry, {
- type :: entry_type(),
- data :: binary()
- }).
--type plop_entry() :: #plop_entry{}.
-
-record(sth, {
treesize :: integer(),
timestamp :: integer(),
@@ -69,5 +35,3 @@
signature :: binary()
}).
-type signature() :: #signature{}.
-
--export_type([timestamped_entry/0, mtl/0, entry_type/0]).
diff --git a/src/db.erl b/src/db.erl
index 3ab2d1b..ddebbeb 100644
--- a/src/db.erl
+++ b/src/db.erl
@@ -7,7 +7,8 @@
%% API.
-export([start_link/0, stop/0]).
-export([init_db/0, init_db/1, init_tables/0, init_tables/1]).
--export([add/1, find/2, get_by_index/2, get_by_index_sorted/2, size/0]).
+-export([add/4, size/0]).
+-export([get_by_index/1, get_by_indices/3, get_by_leaf_hash/1, get_by_entry_hash/1]).
%% API for testing.
-export([dump/1, destroy_tables/0, info_tables/0, dump_to_file/1]).
%% gen_server callbacks.
@@ -72,24 +73,31 @@ stop() ->
%%%%%%%%%%%%%%%%%%%%
%% Public API.
--spec add(plop()) -> {atomic, ok}.
-add(Entry) ->
- gen_server:call(?MODULE, {add, Entry}).
+-spec add(binary(), binary(), binary(), non_neg_integer()) -> ok.
+add(LeafHash, EntryHash, Data, Index) ->
+ gen_server:call(?MODULE, {add, {LeafHash, EntryHash, Data, Index}}).
-%% @doc Find one entry.
--spec find(entryhash | mtlhash | index, binary()) ->
- [] | plop() | duplicate_hash_in_db.
-find(Type, Hash) ->
- gen_server:call(?MODULE, {find, Type, Hash}).
+-spec get_by_indices(non_neg_integer(),
+ non_neg_integer(),
+ {sorted, true|false}) ->
+ [{non_neg_integer(), binary(), binary()}].
+get_by_indices(Start, End, {sorted, Sorted}) ->
+ gen_server:call(?MODULE, {get_by_indices, {Start, End, Sorted}}).
--spec get_by_index(non_neg_integer(), non_neg_integer()) -> [{mtl(), binary()}].
-get_by_index(Start, End) ->
- gen_server:call(?MODULE, {get_by_index, {Start, End}}).
+-spec get_by_index(binary()) -> notfound |
+ {non_neg_integer(), binary(), binary()}.
+get_by_index(Index) ->
+ gen_server:call(?MODULE, {get_by_index, Index}).
--spec get_by_index_sorted(non_neg_integer(), non_neg_integer()) ->
- [{mtl(), binary()}].
-get_by_index_sorted(Start, End) ->
- gen_server:call(?MODULE, {get_by_index_sorted, {Start, End}}).
+-spec get_by_leaf_hash(binary()) -> notfound |
+ {non_neg_integer(), binary(), binary()}.
+get_by_leaf_hash(LeafHash) ->
+ gen_server:call(?MODULE, {get_by_leaf_hash, LeafHash}).
+
+-spec get_by_entry_hash(binary()) -> notfound |
+ {non_neg_integer(), binary(), binary()}.
+get_by_entry_hash(EntryHash) ->
+ gen_server:call(?MODULE, {get_by_entry_hash, EntryHash}).
%% Testing and debugging.
dump(Table) ->
@@ -117,50 +125,64 @@ terminate(_Reason, _State) ->
handle_call(stop, _From, State) ->
{stop, normal, stopped, State};
-handle_call({add, Entry}, _From, State) ->
- F = fun() ->
- mnesia:write(Entry)
- end,
- Res = mnesia:transaction(F),
- {reply, Res, State};
-
-handle_call({dump, Table}, _From, State) ->
- F = fun() ->
- Q = qlc:q([E || E <- mnesia:table(Table)]),
- qlc:e(Q)
+handle_call({add, {LeafHash, EntryHash, Data, Index}}, _From, State) ->
+ R = mnesia:transaction(
+ fun() ->
+ mnesia:write(
+ #plop{
+ index = Index,
+ mtlhash = LeafHash,
+ entryhash = EntryHash,
+ logentry = Data})
+ end),
+ {reply, R, State};
+
+handle_call({get_by_indices, {Start, End, Sorted}}, _From, State) ->
+ R = case Sorted of
+ false ->
+ select_index(Start, End);
+ true ->
+ %% FIXME: RAM hog -- how bad is it?
+ lists:sort(select_index(Start, End))
end,
- Res = mnesia:transaction(F),
- {reply, Res, State};
+ {reply, R, State};
-handle_call({find, entryhash, Hash}, _From, State) ->
+handle_call({get_by_index, Index}, _From, State) ->
{reply,
- find_entry(fun() -> mnesia:index_read(plop, Hash, #plop.entryhash) end),
- State};
-handle_call({find, mtlhash, Hash}, _From, State) ->
- {reply,
- find_entry(fun() -> mnesia:index_read(plop, Hash, #plop.mtlhash) end),
+ find_entry(fun() -> mnesia:read(plop, Index) end),
State};
-handle_call({find, index, Index}, _From, State) ->
+
+handle_call({get_by_leaf_hash, LeafHash}, _From, State) ->
{reply,
- find_entry(fun() -> mnesia:read(plop, Index) end),
+ find_entry(fun() ->
+ mnesia:index_read(plop, LeafHash, #plop.mtlhash)
+ end),
State};
-handle_call({get_by_index, {Start, End}}, _From, State) ->
- Res = [{MTL, Extra} || [_Index, MTL, Extra] <- select_index(Start, End)],
- {reply, Res, State};
+handle_call({get_by_entry_hash, EntryHash}, _From, State) ->
+ {reply,
+ find_entry(fun() ->
+ mnesia:index_read(plop, EntryHash, #plop.entryhash)
+ end),
+ State};
-handle_call({get_by_index_sorted, {Start, End}}, _From, State) ->
- %% FIXME: RAM hog -- how bad is it?
- Res = [{MTL, Extra} || [_Index, MTL, Extra] <- lists:sort(select_index(Start, End))],
- {reply, Res, State}.
+handle_call({dump, Table}, _From, State) ->
+ R = mnesia:transaction(
+ fun() ->
+ Q = qlc:q([E || E <- mnesia:table(Table)]),
+ qlc:e(Q)
+ end),
+ {reply, R, State}.
%%%%%%%%%%%%%%%%%%%%
%% Helper functions.
+-spec select_index(non_neg_integer(), non_neg_integer()) ->
+ [{non_neg_integer(), binary(), binary()}].
select_index(Start, End) ->
F = fun() ->
- %% Get index, mtl and extra_data.
- MatchHead = {plop, '$1', '_', '_', '$2', '$3', '_'},
+ %% Get index, mtlhash and logentry.
+ MatchHead = {plop, '$1', '$2', '_', '$3'},
Guard = [{'>=', '$1', Start}, {'=<', '$1', End}],
Result = ['$$'],
mnesia:select(plop, [{MatchHead, Guard, Result}])
@@ -168,11 +190,13 @@ select_index(Start, End) ->
{atomic, Res} = mnesia:transaction(F),
Res.
--spec find_entry(fun()) -> [] | plop() | duplicate_hash_in_db.
+-spec find_entry(fun()) -> notfound |
+ {non_neg_integer(), binary(), binary()} |
+ duplicate.
find_entry(Fun) ->
{atomic, Result} = mnesia:transaction(Fun),
- case length(Result) of
- 0 -> [];
- 1 -> hd(Result);
- _ -> duplicate_hash_in_db % FIXME: log an error?
+ case Result of
+ [] -> notfound;
+ [#plop{index = I, mtlhash = H, logentry = E}] -> {I, H, E};
+ _ -> duplicate
end.
diff --git a/src/db.hrl b/src/db.hrl
index bea9131..9e09b66 100644
--- a/src/db.hrl
+++ b/src/db.hrl
@@ -1,17 +1,10 @@
%%% Copyright (c) 2014, NORDUnet A/S.
%%% See LICENSE for licensing information.
-%% @doc What's stored in the database.
-%% 'index' is the primary key, 'entryhash' and 'mtlhash' are also
-%% indexed, see init_tables/1.
-%% NOTE: Don't change anything here without also fixing
-%% select_index/2, which depends on the order of fields.
+%% @doc What's stored in the database.
+%% 'mtlhash' and 'entryhash' are also indexed, see init_tables/1.
-record(plop, {
- index :: non_neg_integer(), % Primary key.
- entryhash :: binary(), % Hash over #plop_entry{} in mtl.
- mtlhash :: binary(), % Merkle Tree Leaf hash.
- mtl :: mtl(), % Merkle Tree Leaf.
- extra_data :: binary(), % Data not part of mtl.
- spt :: spt() % Signed Plop Timestamp.
- }).
--type plop() :: #plop{}.
+ index :: non_neg_integer(), % Primary key.
+ mtlhash :: binary(), % Merkle Tree Leaf hash.
+ entryhash :: binary(), % Hash for duplicate detection.
+ logentry :: binary()}). % Data.
diff --git a/src/ht.erl b/src/ht.erl
index 74f8ab4..cd4e57c 100644
--- a/src/ht.erl
+++ b/src/ht.erl
@@ -51,8 +51,8 @@ stop() ->
gen_server:call(?MODULE, stop).
size() ->
gen_server:call(?MODULE, size).
-add(Entry) ->
- gen_server:call(?MODULE, {add, Entry}).
+add(Hash) ->
+ gen_server:call(?MODULE, {add, Hash}).
root() ->
gen_server:call(?MODULE, root).
root(Version) ->
@@ -91,8 +91,8 @@ handle_call(stop, _From, State) ->
{stop, normal, stopped, State};
handle_call(size, _From, State) ->
{reply, State#tree.version + 1, State};
-handle_call({add, Entry}, _From, State) ->
- {reply, ok, add(State, Entry)};
+handle_call({add, Hash}, _From, State) ->
+ {reply, ok, add(State, Hash)};
handle_call(root, _From, State) ->
{NewState, Hash} = head(State, State#tree.version),
{reply, Hash, NewState};
@@ -245,11 +245,10 @@ first_left_node(Layer, Index, BAL) ->
false -> {Layer, Index}
end.
-%% @doc Add an entry but don't update the tree.
+%% @doc Add a hash but don't update the tree.
-spec add(tree(), binary()) -> tree().
-add(Tree = #tree{version = V, store = Store}, Entry) ->
- Tree#tree{version = V + 1,
- store = ts:add(Store, 0, mkleafhash(Entry))}.
+add(Tree = #tree{version = V, store = Store}, Hash) ->
+ Tree#tree{version = V + 1, store = ts:add(Store, 0, Hash)}.
%% @doc Return a new tree.
-spec new(list()) -> tree().
@@ -261,13 +260,14 @@ new([-1]) ->
new([]);
%% Initialise tree from db.
new([Version]) when is_integer(Version) ->
- foldl(fun(MTL, Tree) ->
+ foldl(fun(Hash, Tree) ->
%% Return value becomes Tree in next invocation.
- add(Tree, plop:serialise(MTL))
- end, new([]), [X || {X, _} <- db:get_by_index_sorted(0, Version)]);
-%% Initialise tree from List.
+ add(Tree, Hash)
+ end, new([]), [H || {_I, H, _E} <-
+ db:get_by_indices(0, Version, {sorted, true})]);
+%% Initialise tree from List with hashes.
new([List]) when is_list(List) ->
- foldl(fun(SerialisedMTL, Tree) -> add(Tree, SerialisedMTL) end,
+ foldl(fun(Hash, Tree) -> add(Tree, Hash) end,
new([]), List).
update(Tree) ->
diff --git a/src/plop.erl b/src/plop.erl
index 5443183..b59e19b 100644
--- a/src/plop.erl
+++ b/src/plop.erl
@@ -26,7 +26,8 @@
%% API.
-export([start_link/2, stop/0]).
-export([get_logid/0, serialise/1]).
--export([add/2, sth/0, get/2, consistency/2, inclusion/2, inclusion_and_entry/2]).
+-export([add/4, sth/0, get/1, get/2, spt/2, consistency/2, inclusion/2, inclusion_and_entry/2]).
+-export([generate_timestamp/0]).
%% API for tests.
-export([read_keyfile_rsa/2, read_keyfiles_ec/2]).
-export([testing_get_pubkey/0]).
@@ -35,7 +36,7 @@
handle_cast/2, handle_info/2, code_change/3]).
-include("$CTROOT/plop/include/plop.hrl").
--include("db.hrl").
+%%-include("db.hrl").
-include_lib("public_key/include/public_key.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -46,6 +47,12 @@
privkey :: public_key:rsa_private_key(),
logid :: binary()}).
+%%%%% moved from plop.hrl, maybe remove
+-define(PLOPVERSION, 0).
+-type signature_type() :: certificate_timestamp | tree_hash | test. % uint8
+%%%%%
+
+
%% @doc The parts of an STH which is to be signed. Used as the
%% interface to plop:sth/1, for testing.
-record(sth_signed, {
@@ -57,17 +64,6 @@
}).
-type sth_signed() :: #sth_signed{}.
-%% @doc What's signed in an SPT. Used for serialisation before hasning
-%% and signing. FIXME: Overlapping #spt{} -- merge somehow?
--record(spt_signed, {
- version = ?PLOPVERSION :: non_neg_integer(),
- signature_type :: signature_type(),
- timestamp :: integer(),
- entry_type :: entry_type(),
- signed_entry :: binary()
- }).
--type spt_signed() :: #spt_signed{}.
-
start_link(Keyfile, Passphrase) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [Keyfile, Passphrase], []).
@@ -101,22 +97,31 @@ terminate(_Reason, _State) ->
ok.
%%%%%%%%%%%%%%%%%%%%
--spec add(timestamped_entry(), binary()) -> spt().
-add(Entry, ExtraData) ->
- gen_server:call(?MODULE, {add, {Entry, ExtraData}}).
+-spec add(binary(), binary(), binary(), binary()) -> ok.
+add(LogEntry, TreeLeafHash, EntryHash, DataToSign) ->
+ gen_server:call(?MODULE,
+ {add, {LogEntry, TreeLeafHash, EntryHash, DataToSign}}).
+
sth() ->
gen_server:call(?MODULE, {sth, []}).
--spec get(non_neg_integer(), non_neg_integer()) -> [{mtl(), binary()}].
+
+-spec get(non_neg_integer(), non_neg_integer()) ->
+ [{non_neg_integer(), binary(), binary()}].
get(Start, End) ->
- gen_server:call(?MODULE, {get, {Start, End}}).
+ gen_server:call(?MODULE, {get, {index, Start, End}}).
+
+get(Hash) ->
+ gen_server:call(?MODULE, {get, {hash, Hash}}).
+
consistency(TreeSizeFirst, TreeSizeSecond) ->
gen_server:call(?MODULE, {consistency, {TreeSizeFirst, TreeSizeSecond}}).
-spec inclusion(binary(), non_neg_integer()) ->
- {ok, mtl()} | {notfound, string()}.
+ {ok, {binary(), binary()}} | {notfound, string()}.
inclusion(Hash, TreeSize) ->
gen_server:call(?MODULE, {inclusion, {Hash, TreeSize}}).
-spec inclusion_and_entry(non_neg_integer(), non_neg_integer()) ->
- {ok, {mtl(), binary()}} | {notfound, string()}.
+ {ok, {binary(), binary()}} |
+ {notfound, string()}.
inclusion_and_entry(Index, TreeSize) ->
gen_server:call(?MODULE, {inclusion_and_entry, {Index, TreeSize}}).
get_logid() ->
@@ -124,118 +129,61 @@ get_logid() ->
testing_get_pubkey() ->
gen_server:call(?MODULE, {test, pubkey}).
%%%%%%%%%%%%%%%%%%%%
-handle_call(stop, _From, State) ->
- {stop, normal, stopped, State};
-
-%% FIXME: What's the right interface for add()? Need to be able to set
-%% version and signature type, at least. That's missing from
-%% #timestamped_entry, so add it somehow.
-handle_call({add,
- {#timestamped_entry{timestamp = Timestamp_in, entry = Entry},
- ExtraData}},
- _From,
- State = #state{privkey = Privkey, logid = LogID}) ->
- TimestampedEntry = #timestamped_entry{
- timestamp = timestamp(Timestamp_in),
- entry = Entry},
- {ok, SPT} = do_add(TimestampedEntry, ExtraData, Privkey, LogID),
- {reply, SPT, State};
+handle_call(stop, _From, Plop) ->
+ {stop, normal, stopped, Plop};
-handle_call({sth, Data}, _From,
- Plop = #state{privkey = PrivKey}) ->
- {reply, sth(PrivKey, Data), Plop};
+handle_call({get, {index, Start, End}}, _From, Plop) ->
+ {reply, db:get_by_indices(Start, End, {sorted, false}), Plop};
-handle_call({get, {Start, End}}, _From, Plop) ->
- {reply, db:get_by_index(Start, End), Plop};
+handle_call({get, {hash, EntryHash}}, _From, Plop) ->
+ {reply, db:get_by_entry_hash(EntryHash), Plop};
handle_call({get, logid}, _From,
Plop = #state{logid = LogID}) ->
{reply, LogID, Plop};
+handle_call({add, {LogEntry, TreeLeafHash, EntryHash}}, _From, Plop) ->
+ ok = ht:add(TreeLeafHash),
+ ok = db:add(TreeLeafHash, EntryHash, LogEntry, ht:size()),
+ {reply, ok, Plop};
+
+handle_call({sth, Data}, _From,
+ Plop = #state{privkey = PrivKey}) ->
+ {reply, sth(PrivKey, Data), Plop};
+
handle_call({consistency, {First, Second}}, _From, Plop) ->
{reply, ht:consistency(First - 1, Second - 1), Plop};
handle_call({inclusion, {Hash, TreeSize}}, _From, Plop) ->
- R = case db:find(mtlhash, Hash) of
- [] ->
+ R = case db:get_by_entry_hash(Hash) of
+ notfound ->
{notfound, "Unknown hash"}; % FIXME: include Hash
- {plop, I, _EntryHash, _MTLHash, _MTL, _ExtraData, _SPT} ->
+ {I, _MTLHash, _Entry} ->
{ok, I, ht:path(I, TreeSize - 1)}
end,
{reply, R, Plop};
handle_call({inclusion_and_entry, {Index, TreeSize}}, _From, Plop) ->
- R = case db:find(index, Index) of
- [] ->
+ R = case db:get_by_index(Index) of
+ notfound ->
{notfound, "Unknown index"}; % FIXME: include Index
- {plop, I, _EntryHash, _MTLHash, MTL, ExtraData, _SPT} ->
- {ok, MTL, ExtraData, ht:path(I, TreeSize - 1)}
- end,
+ {I, _MTLHash, Entry} ->
+ {ok, Entry, ht:path(I, TreeSize - 1)}
+ end,
{reply, R, Plop};
handle_call({test, pubkey}, _From,
Plop = #state{pubkey = PK}) ->
{reply, PK, Plop}.
-%%%%%%%%%%%%%%%%%%%%
-%% db_get_single_entry(N) ->
-%% [#mtl{entry = #timestamped_entry{entry = #plop_entry{data = Data}}}] =
-%% db:get_by_index(N, N),
-%% Data.
-
--spec do_add(timestamped_entry(),
- binary(),
- public_key:rsa_private_key(),
- binary()) -> {ok, spt()} | {error, any()}.
-do_add(TimestampedEntry, ExtraData, Privkey, LogID) ->
- DB_hash = crypto:hash(sha256,
- serialise(TimestampedEntry#timestamped_entry.entry)),
- Record = db:find(entryhash, DB_hash),
- case Record of
- #plop{index = _I, mtl = MTL, spt = SPT} ->
- %% Costly database consistency checking. FIXME: Remove.
- Record = Record#plop{
- entryhash = DB_hash,
- mtlhash = ht:leaf_hash(serialise(MTL)),
- mtl = MTL},
- {ok, SPT};
- [] ->
- NewSPT = spt(LogID, Privkey, TimestampedEntry),
- MTL = #mtl{entry = TimestampedEntry},
- MTLtext = serialise(MTL),
- DB_data = #plop{index = ht:size(),
- entryhash = DB_hash,
- mtlhash = ht:leaf_hash(MTLtext),
- mtl = MTL,
- extra_data = ExtraData,
- spt = NewSPT},
- {atomic, ok} = db:add(DB_data),
- {ht:add(MTLtext), NewSPT};
- Err -> {error, Err}
- end.
-
%% @doc Signed Plop Timestamp, conformant to an SCT in RFC6962 3.2 and
%% RFC5246 4.7.
--spec spt(binary(), public_key:rsa_private_key(), timestamped_entry()) -> spt().
-spt(LogID, PrivKey, #timestamped_entry{
- timestamp = Timestamp,
- entry = #plop_entry{type = EntryType, data = EntryData}
- }) ->
- BinToSign = serialise(#spt_signed{
- signature_type = certificate_timestamp,
- timestamp = Timestamp,
- entry_type = EntryType,
- signed_entry = EntryData}),
- Signature = #signature{
- algorithm = #sig_and_hash_alg{
- hash_alg = sha256,
- signature_alg = ecdsa},
- signature = signhash(BinToSign, PrivKey)},
- #spt{
- version = ?PLOPVERSION,
- logid = LogID,
- timestamp = Timestamp,
- signature = Signature}.
+-spec spt(public_key:ec_private_key(), binary()) -> signature().
+spt(PrivKey, SerialisedData) ->
+ #signature{algorithm = #sig_and_hash_alg{
+ hash_alg = sha256,
+ signature_alg = ecdsa},
+ signature = signhash(SerialisedData, PrivKey)}.
%% @doc Signed Tree Head as specified in RFC6962 section 3.2.
-spec sth(#'ECPrivateKey'{}, sth_signed() | list()) -> sth().
@@ -336,15 +284,6 @@ signature_type(certificate_timestamp) -> 0;
signature_type(tree_hash) -> 1;
signature_type(test) -> 2.
--spec entry_type(entry_type()) -> integer().
-entry_type(x509) -> 0;
-entry_type(precert) -> 1;
-entry_type(test) -> 2.
-
--spec leaf_type(leaf_type()) -> integer().
-leaf_type(timestamped_entry) -> 0;
-leaf_type(test) -> 1.
-
-spec hash_alg_type(hash_alg_type()) -> integer().
hash_alg_type(none) -> 0;
hash_alg_type(md5) -> 1;
@@ -360,6 +299,7 @@ signature_alg_type(rsa) -> 1;
signature_alg_type(dsa) -> 2;
signature_alg_type(ecdsa) -> 3.
+%% TODO: Remove.
-spec timestamp(now | integer()) -> integer().
timestamp(Timestamp) ->
case Timestamp of
@@ -371,58 +311,15 @@ timestamp(Timestamp) ->
_ -> Timestamp
end.
-serialise_tls_vector(Binary, LengthLen) ->
- Length = byte_size(Binary),
- <<Length:LengthLen/integer-unit:8, Binary/binary>>.
+-spec generate_timestamp() -> integer().
+generate_timestamp() ->
+ {NowMegaSec, NowSec, NowMicroSec} = now(),
+ trunc(NowMegaSec * 1.0e9
+ + NowSec * 1.0e3
+ + NowMicroSec / 1.0e3).
--spec serialise(plop_entry() | timestamped_entry() | mtl() |
- spt() | spt_signed() | sth() | sth_signed() |
- sig_and_hash_alg() | signature()) -> binary().
-serialise(#plop_entry{
- type = TypeAtom,
- data = Data
- }) ->
- EntryType = entry_type(TypeAtom),
- DataVector = serialise_tls_vector(Data, 3),
- <<EntryType:16, DataVector/binary>>;
-serialise(#timestamped_entry{
- timestamp = Timestamp,
- entry = PlopEntry
- }) ->
- Extensions = <<>>,
- list_to_binary([<<Timestamp:64>>, serialise(PlopEntry), serialise_tls_vector(Extensions, 2)]);
-serialise(#spt{
- version = Version,
- logid = LogID,
- timestamp = Timestamp,
- signature = Signature
- }) ->
- list_to_binary([<<Version:8, LogID/binary, Timestamp:64>>,
- serialise(Signature)]);
-serialise(#spt_signed{
- version = Version,
- signature_type = SigtypeAtom,
- timestamp = Timestamp,
- entry_type = EntrytypeAtom,
- signed_entry = Entry
- }) ->
- Sigtype = signature_type(SigtypeAtom),
- Entrytype = entry_type(EntrytypeAtom),
- Extensions = <<>>,
- list_to_binary(
- [<<Version:8,
- Sigtype:8,
- Timestamp:64,
- Entrytype:16>>,
- serialise_tls_vector(Entry, 3),
- serialise_tls_vector(Extensions, 2)]);
-serialise(#mtl{ % Merkle Tree Leaf.
- version = Version,
- leaf_type = TypeAtom,
- entry = TimestampedEntry
- }) ->
- LeafType = leaf_type(TypeAtom),
- list_to_binary([<<Version:8, LeafType:8>>, serialise(TimestampedEntry)]);
+-spec serialise(sth() | sth_signed() | sig_and_hash_alg() | signature()) ->
+ binary().
serialise(#sth_signed{ % Signed Tree Head.
version = Version,
signature_type = SigtypeAtom,
@@ -456,48 +353,12 @@ serialise(#signature{
%%%%%%%%%%%%%%%%%%%%
%% Internal tests. For more tests see ../test/.
-serialise_test_() ->
- [?_assertEqual(
- <<0:8, 0:8, 0:64, 0:16, "foo">>,
- serialise(#spt_signed{
- version = 0,
- signature_type = certificate_timestamp,
- timestamp = 0,
- entry_type = x509,
- signed_entry = <<"foo">>}))].
-add_test() ->
- {ok, S} = init([?TESTPRIVKEYFILE, ?TESTPUBKEYFILE]),
-
- Data1 = <<"some data">>,
- ExtraData1 = <<"some extra data">>,
- {_Tree, SPT} =
- do_add(#timestamped_entry{
- timestamp = 4711,
- entry = #plop_entry{type = test, data = Data1}},
- ExtraData1,
- S#state.privkey,
- S#state.logid),
- {_Tree1, SPT1} =
- do_add(#timestamped_entry{
- timestamp = 4712,
- entry = #plop_entry{type = test, data = Data1}},
- ExtraData1,
- S#state.privkey,
- S#state.logid),
- ?assertEqual(SPT, SPT1),
-
- TE = #timestamped_entry{
- timestamp = 0,
- entry = #plop_entry{type = test, data = <<"some data">>}},
- SPTeq1 = spt(S#state.logid, S#state.privkey, TE),
- SPTeq2 = spt(S#state.logid, S#state.privkey, TE),
- ?assertNotEqual(SPTeq1, SPTeq2), % DSA signatures differ!
- ok.
-
-%% add_random_data(N, Size) ->
-%% lists:foreach(
-%% fun(_) -> plop:add(#timestamped_entry
-%% {entry = #plop_entry
-%% {type = test,
-%% data = crypto:rand_bytes(Size)}}) end,
-%% lists:seq(1, N)).
+%% serialise_test_() ->
+%% [?_assertEqual(
+%% <<0:8, 0:8, 0:64, 0:16, "foo">>,
+%% serialise(#spt_signed{
+%% version = 0,
+%% signature_type = certificate_timestamp,
+%% timestamp = 0,
+%% entry_type = x509,
+%% signed_entry = <<"foo">>}))].
diff --git a/test/plop_test.erl b/test/plop_test.erl
deleted file mode 100644
index ce2a052..0000000
--- a/test/plop_test.erl
+++ /dev/null
@@ -1,94 +0,0 @@
--module(plop_test).
--include("plop.hrl").
--include_lib("eunit/include/eunit.hrl").
-
-%% start_stop_test_() ->
-%% {"The server can be started, stopped and is regsitered",
-%% {setup, fun start/0, fun stop/1, fun is_registered/1}}.
-
-%% "Entries can be added and the STH changes."
-%% FIXME: This way, if a test fails, we don't stop plop. The tests
-%% must run and be validated in strict order though.
-adding_verifying_test() ->
- %%Pid = start(),
- Pubkey = plop:testing_get_pubkey(),
- add_sth_add_sth(Pubkey).
- %%stop(Pid).
-
-%% "Entries can be retrieved."
-get_entries_test_() ->
- Entries = plop:get(1, 2),
- [?_assertEqual(2, length(Entries)),
- ?_assertMatch(#mtl{}, hd(Entries))].
-
-%%% Setup.
-%% start() ->
-%% {ok, Pid} = plop:start_link("../test/rsakey.pem", "sikrit"),
-%% Pid.
-
-%% stop(_) ->
-%% plop:stop().
-
-%%% Tests.
-%% is_registered(Pid) ->
-%% [?_assert(erlang:is_process_alive(Pid)),
-%% ?_assertEqual(Pid, whereis(plop))].
-
-%%% Helpers.
-
-add_sth_add_sth(Pubkey) ->
- add(0, Pubkey),
- STH0 = sth(Pubkey),
- add(1, Pubkey),
- STH1 = sth(Pubkey),
- ?assertNotEqual(STH0, STH1).
-
-add(0, Pubkey) ->
- Msg = crypto:rand_bytes(32),
- Entry = #timestamped_entry{
- timestamp = 4711,
- entry = #plop_entry{
- type = test,
- data = Msg}},
- DataSigned = <<0:8, 0:8, 4711:64, 2:16, Msg/binary>>,
- #spt{
- version = Version,
- timestamp = Timestamp,
- signature = #signature{signature = Signature}
- } = plop:add(Entry),
- ?assertEqual(0, Version),
- ?assertEqual(4711, Timestamp),
- ?assert(public_key:verify(DataSigned, sha256, Signature, Pubkey));
-add(1, Pubkey) ->
- Msg = crypto:rand_bytes(32),
- Entry = #timestamped_entry{
- timestamp = 4712,
- entry = #plop_entry{
- type = test,
- data = Msg}},
- DataSigned = <<0:8, 0:8, 4712:64, 2:16, Msg/binary>>,
- #spt{
- version = Version,
- timestamp = Timestamp,
- signature = #signature{signature = Signature}
- } = plop:add(Entry),
- ?assertEqual(0, Version),
- ?assertEqual(4712, Timestamp),
- ?assert(public_key:verify(DataSigned, sha256, Signature, Pubkey)).
-
-%% TODO
-%% add(2) ->
-%% TestVector = <<>>,
-%% %% Same data as in 0, should not result in new database entry.
-
-sth(Pubkey) ->
- #sth{
- treesize = Treesize,
- timestamp = Timestamp,
- roothash = Roothash,
- signature = #signature{signature = Signature}
- } = STH = plop:sth(),
- Data = list_to_binary([<<0:8, 1:8, Timestamp:64, Treesize:64>>, Roothash]),
- ?assert(public_key:verify(Data, sha256, Signature, Pubkey)),
- STH.
-