summaryrefslogtreecommitdiff
path: root/src/plop.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/plop.erl')
-rw-r--r--src/plop.erl283
1 files changed, 72 insertions, 211 deletions
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">>}))].