From 15249d58b7a23bf323aa9b4865e1265e50917dd2 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Wed, 4 Mar 2015 17:12:30 +0100 Subject: Save STH instead of calculating a new one each time. Verify incoming STH. --- src/frontend.erl | 31 ++++++++++++++++++++++++----- src/plop.erl | 59 +++++++++++++++++--------------------------------------- src/sign.erl | 22 ++++++++++++++++----- 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/src/frontend.erl b/src/frontend.erl index b2244de..68039c2 100644 --- a/src/frontend.erl +++ b/src/frontend.erl @@ -45,18 +45,30 @@ request(post, "ct/frontend/sendsth", Input) -> {struct, PropList} -> OldSize = db:size(), Treesize = proplists:get_value(<<"tree_size">>, PropList), + Timestamp = proplists:get_value(<<"timestamp">>, PropList), RootHash = base64:decode(proplists:get_value(<<"sha256_root_hash">>, PropList)), + Signature = base64:decode(proplists:get_value(<<"tree_head_signature">>, PropList)), Indexsize = db:indexsize(), if Treesize < OldSize -> html("Size is older than current size", OldSize); - Treesize == OldSize -> - success({[{result, <<"ok">>}]}); + Treesize == 0, OldSize == 0 -> + lager:debug("both old and new size is 0, saving sth"), + OwnRootHash = ht:root(-1), + case {plop:verify_sth(Treesize, Timestamp, RootHash, Signature), OwnRootHash} of + {true, RootHash} -> + ok = plop:save_sth({struct, PropList}), + success({[{result, <<"ok">>}]}); + {false, RootHash} -> + html("Verification failed", hex:bin_to_hexstr(RootHash)); + _ -> + html("Root hash not the same", hex:bin_to_hexstr(OwnRootHash)) + end; Treesize > Indexsize -> html("Has too few entries", Indexsize); true -> - NewEntries = db:leafhash_for_indices(OldSize, Treesize - 1), + NewEntries = get_new_entries(OldSize, Treesize), lager:debug("old size: ~p new size: ~p entries: ~p", [OldSize, Treesize, NewEntries]), @@ -66,10 +78,13 @@ request(post, "ct/frontend/sendsth", Input) -> [] -> ht:load_tree(Treesize - 1), OwnRootHash = ht:root(Treesize - 1), - case OwnRootHash of - RootHash -> + case {plop:verify_sth(Treesize, Timestamp, RootHash, Signature), OwnRootHash} of + {true, RootHash} -> ok = db:set_treesize(Treesize), + ok = plop:save_sth({struct, PropList}), success({[{result, <<"ok">>}]}); + {false, RootHash} -> + html("Verification failed", hex:bin_to_hexstr(RootHash)); _ -> html("Root hash not the same", hex:bin_to_hexstr(OwnRootHash)) end; @@ -91,6 +106,12 @@ request(get, "ct/frontend/missingentries", _Query) -> success({[{result, <<"ok">>}, {entries, lists:map(fun (Entry) -> base64:encode(Entry) end, Missing)}]}). + +get_new_entries(OldSize, Treesize) when OldSize < Treesize -> + db:leafhash_for_indices(OldSize, Treesize - 1); +get_new_entries(OldSize, Treesize) when OldSize == Treesize -> + []. + check_entries(Entries, Start, End) -> lists:foldl(fun ({Hash, Index}, Acc) -> case check_entry(Hash, Index) of diff --git a/src/plop.erl b/src/plop.erl index 5eb0c1f..ebadcc5 100644 --- a/src/plop.erl +++ b/src/plop.erl @@ -27,7 +27,7 @@ -export([start_link/0, stop/0]). -export([get_logid/0, serialise/1]). -export([add/3, sth/0, get/1, get/2, spt/1, consistency/2, inclusion/2, inclusion_and_entry/2]). --export([generate_timestamp/0]). +-export([generate_timestamp/0, save_sth/1, verify_sth/4]). %% API for tests. -export([testing_get_pubkey/0]). %% gen_server callbacks. @@ -115,8 +115,14 @@ add(LogEntry, TreeLeafHash, EntryHash) -> end) end. +save_sth(STH) -> + {ok, STHFile} = application:get_env(plop, sth_path), + lager:debug("writing new sth to ~p: ~p", [STHFile, STH]), + atomic:replacefile(STHFile, mochijson2:encode(STH)). + sth() -> - sth([]). + {ok, STHFile} = application:get_env(plop, sth_path), + mochijson2:decode(atomic:readfile(STHFile)). -spec get(non_neg_integer(), non_neg_integer()) -> [{non_neg_integer(), binary(), binary()}]. @@ -198,7 +204,7 @@ send_http_request(TreeLeafHash, URL, Headers, RequestBody) -> RequestId = make_ref(), spawn(fun () -> case plop_httputil:request("leafhash " ++ mochihex:to_hex(TreeLeafHash), URL, Headers, RequestBody) of - {failure, StatusLine, RespHeaders, Body} -> + {failure, _StatusLine, _RespHeaders, _Body} -> lager:debug("auth check failed"), drop; {success, StatusLine, RespHeaders, Body} -> @@ -293,32 +299,15 @@ handle_call(stop, _From, Plop) -> {stop, normal, stopped, Plop}. -%% @doc Signed Plop Timestamp, conformant to an SCT in RFC6962 3.2 and -%% RFC5246 4.7. - -%% @doc Signed Tree Head as specified in RFC6962 section 3.2. --spec sth(sth_signed() | list()) -> sth(). -sth([]) -> - sth(#sth_signed{timestamp = now}); -sth(#sth_signed{version = Version, timestamp = Timestamp_in}) -> - Timestamp = timestamp(Timestamp_in), - Treesize = ht:size(), - Roothash = ht:root(), - BinToSign = serialise(#sth_signed{ - version = Version, - signature_type = tree_hash, - timestamp = Timestamp, - tree_size = Treesize, - root_hash = Roothash}), - Signature = #signature{ - algorithm = #sig_and_hash_alg{ - hash_alg = sha256, - signature_alg = ecdsa}, - signature = sign:sign_sth(BinToSign)}, - STH = {Treesize, Timestamp, Roothash, Signature}, - %%io:format("STH: ~p~nBinToSign: ~p~nSignature: ~p~nTimestamp: ~p~n", - %% [STH, BinToSign, Signature, Timestamp]), - STH. +verify_sth(Treesize, Timestamp, Roothash, PackedSignature) -> + STH = serialise(#sth_signed{ + version = ?PLOPVERSION, + signature_type = tree_hash, + timestamp = Timestamp, + tree_size = Treesize, + root_hash = Roothash}), + <> = PackedSignature, + sign:verify_sth(STH, Signature). @@ -349,18 +338,6 @@ 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 - now -> - {NowMegaSec, NowSec, NowMicroSec} = now(), - trunc(NowMegaSec * 1.0e9 - + NowSec * 1.0e3 - + NowMicroSec / 1.0e3); - _ -> Timestamp - end. - -spec generate_timestamp() -> integer(). generate_timestamp() -> {NowMegaSec, NowSec, NowMicroSec} = now(), diff --git a/src/sign.erl b/src/sign.erl index b0916fd..167987d 100644 --- a/src/sign.erl +++ b/src/sign.erl @@ -8,7 +8,7 @@ %% API. -export([start_link/0, stop/0]). --export([sign_sct/1, sign_sth/1, get_pubkey/0, get_logid/0]). +-export([sign_sct/1, sign_sth/1, get_pubkey/0, get_logid/0, verify_sth/2]). -export([read_keyfile_ec/1]). %% API for tests. -export([read_keyfile_rsa/2]). @@ -111,19 +111,22 @@ public_key(#'RSAPrivateKey'{modulus = Mod, publicExponent = Exp}) -> remote_sign_request(URL, Request) -> case plop_httputil:request("signing", URL, [{"Content-Type", "text/json"}], list_to_binary(mochijson2:encode(Request))) of - {failure, StatusLine, RespHeaders, Body} -> + {failure, _StatusLine, _RespHeaders, _Body} -> lager:debug("auth check failed"), none; - {success, StatusLine, RespHeaders, Body} -> + {success, {_HttpVersion, StatusCode, _ReasonPhrase}, _RespHeaders, Body} when StatusCode == 200 -> lager:debug("auth check succeeded"), case (catch mochijson2:decode(Body)) of {error, E} -> + lager:error("json parse error: ~p", [E]), none; {struct, PropList} -> base64:decode(proplists:get_value(<<"result">>, PropList)) end; - {noauth, StatusLine, RespHeaders, Body} -> + {noauth, _StatusLine, _RespHeaders, _Body} -> lager:debug("no auth"), + none; + _ -> none end. @@ -163,6 +166,13 @@ get_logid() -> PubKeyfile = application:get_env(plop, log_public_key, none), read_keyfile_ec_logid(PubKeyfile). +verify_sth(STH, Signature) -> + lager:debug("verifying ~p: ~p", [STH, Signature]), + PubKeyfile = application:get_env(plop, log_public_key, none), + PublicKey = read_keyfile_ec(PubKeyfile), + public_key:verify(STH, sha256, Signature, PublicKey). + + %%%%%%%%%%%%%%%%%%%% %% gen_server callbacks. @@ -190,4 +200,6 @@ handle_call({get, pubkey}, _From, State) -> handle_call({sign, Data}, _From, State) -> %% FIXME: Merge RSA and DC. - {reply, signhash_ec(Data, State#state.privkey), State}. + Signature = signhash_ec(Data, State#state.privkey), + lager:debug("signing ~p: ~p", [Data, Signature]), + {reply, Signature, State}. -- cgit v1.1