From dc6b383aa4a9cc1e2793c4d9f3be81bd676b7a20 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Sat, 27 Sep 2014 15:45:14 +0200 Subject: wip --- src/plop.erl | 283 +++++++++++++++-------------------------------------------- 1 file changed, 72 insertions(+), 211 deletions(-) (limited to 'src/plop.erl') 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), - <>. +-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), - <>; -serialise(#timestamped_entry{ - timestamp = Timestamp, - entry = PlopEntry - }) -> - Extensions = <<>>, - list_to_binary([<>, serialise(PlopEntry), serialise_tls_vector(Extensions, 2)]); -serialise(#spt{ - version = Version, - logid = LogID, - timestamp = Timestamp, - signature = Signature - }) -> - list_to_binary([<>, - 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( - [<>, - 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([<>, 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">>}))]. -- cgit v1.1