summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/frontend.erl31
-rw-r--r--src/plop.erl59
-rw-r--r--src/sign.erl22
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}),
+ <<HashAlg:8, SignatureAlg:8, SigLen:16, Signature/binary>> = 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}.