summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2014-04-26 19:01:41 +0200
committerLinus Nordberg <linus@nordu.net>2014-04-26 19:01:41 +0200
commit2f733d1018c9602ecf8bac5e2516e69285120563 (patch)
tree767f19ac68b924e07a02fe8943ce51be081380cb /src
parentd87227c82699ec3eefed4708a7c38df2c6e877cd (diff)
Add STH support, with failing tests due to gen_server testing woes.
Move things out of spt() for reuse by sth().
Diffstat (limited to 'src')
-rw-r--r--src/plop.erl132
-rw-r--r--src/plop.hrl30
-rw-r--r--src/test/plop_test.erl68
3 files changed, 149 insertions, 81 deletions
diff --git a/src/plop.erl b/src/plop.erl
index 4515d25..390299d 100644
--- a/src/plop.erl
+++ b/src/plop.erl
@@ -12,6 +12,8 @@
%% API.
-export([start_link/0, start_link/2, stop/0]).
-export([add/1, sth/0]).
+%% API for tests.
+-export([sth/1]).
%% gen_server callbacks.
-export([init/1, handle_call/3, terminate/2,
handle_cast/2, handle_info/2, code_change/3]).
@@ -57,66 +59,73 @@ terminate(_Reason, _State) ->
ok.
%%%%%%%%%%%%%%%%%%%%
-add(Data) when is_record(Data, plop_data) ->
+add(Data) when is_record(Data, spt) ->
gen_server:call(?MODULE, {add, Data}).
sth() ->
- gen_server:call(?MODULE, sth).
-%%%%%%%%%%%%%%%%%%%%
+ gen_server:call(?MODULE, {sth, []}).
+sth(Data) ->
+ gen_server:call(?MODULE, {sth, Data}).
+%%%%%%%%%%%%%%%%%%%%
handle_call(stop, _From, State) ->
{stop, normal, stopped, State};
-handle_call({add, Data = #plop_data{entry = Entry}},
- _From,
+
+handle_call({add, Data = #spt{entry = Entry}}, _From,
Plop = #plop{privkey = Privkey,
logid = LogID,
hashtree = Tree}) ->
%% fixme: add Entry to db,
- ht:append(Tree, serialise(Entry)),
+ NewTree = ht:append(Tree, serialise(Entry)),
+ io:format("Tree: ~p~nNewTree: ~p~n", [Tree, NewTree]),
SPT = spt(LogID, Privkey, Data),
- {reply, SPT, Plop};
-handle_call(sth, _From, Plop = #plop{hashtree = Tree}) ->
- {reply, sth(Tree), Plop}.
+ {reply, SPT, Plop#plop{hashtree = NewTree}};
+
+handle_call({sth, Data}, _From,
+ Plop = #plop{privkey = PrivKey,
+ hashtree = Tree}) ->
+ {reply, sth(PrivKey, Tree, Data), Plop}.
%%%%%%%%%%%%%%%%%%%%
%% @doc Signed Plop Timestamp according to RFC6962 3.2 and RFC5246 4.7.
-spt(LogID, PrivKey, Data = #plop_data{timestamp = Timestamp_in}) ->
- Timestamp =
- case Timestamp_in of
- now ->
- {NowMegaSec, NowSec, NowMicroSec} = now(),
- trunc(NowMegaSec * 1.0e9
- + NowSec * 1.0e3
- + NowMicroSec / 1.0e3);
- _ -> Timestamp_in
- end,
- BinToSign = list_to_binary(serialise(Data)),
-
- %% Was going to just sign/3 the hash but looking at
- %% digitally_signed() in lib/ssl/src/ssl_handshake.erl it seems
- %% like we should rather use (undocumented) encrypt_private/3.
- %public_key:sign(hash(sha256, BinToSign), sha256, PrivKey)
- Signature = public_key:encrypt_private(crypto:hash(sha256, BinToSign),
- PrivKey,
- [{rsa_pad, rsa_pkcs1_padding}]),
+-spec spt(binary(), binary(), spt()) -> binary().
+spt(LogID, PrivKey, Data = #spt{timestamp = Timestamp_in}) ->
+ Timestamp = timestamp(Timestamp_in),
+ BinToSign =
+ list_to_binary(serialise(Data#spt{
+ signature_type = certificate_timestamp,
+ timestamp = Timestamp})),
+ Signature = signhash(BinToSign, PrivKey),
SPT = <<?PLOPVERSION:8,
LogID/binary,
Timestamp:64,
Signature/binary>>,
- %%io:format("SPT: ~p~nBinToSign: ~p~nSignature = ~p~n", [SPT, BinToSign, Signature]),
+ %%io:format("SPT: ~p~nBinToSign: ~p~nSignature = ~p~n",
+ %% [SPT, BinToSign, Signature]),
SPT.
-%% @doc Signed Tree Head
- %% digitally-signed struct {
- %% Version version;
- %% SignatureType signature_type = tree_hash;
- %% uint64 timestamp;
- %% uint64 tree_size;
- %% opaque sha256_root_hash[32];
- %% } TreeHeadSignature;
-sth(Tree) ->
- "FIXME: signed tree head for " ++ Tree.
+%% @doc Signed Tree Head as described in RFC6962 section 3.2.
+sth(PrivKey, Tree, []) ->
+ sth(PrivKey, Tree, #sth{timestamp = now});
+sth(PrivKey, Tree, #sth{version = Version, timestamp = Timestamp_in}) ->
+ Timestamp = timestamp(Timestamp_in),
+ Treesize = ht:size(Tree),
+ Roothash = ht:tree_hash(Tree),
+ BinToSign =
+ list_to_binary(serialise(#sth{version = Version,
+ signature_type = tree_hash,
+ timestamp = Timestamp,
+ tree_size = Treesize,
+ root_hash = Roothash})),
+ Signature = signhash(BinToSign, PrivKey),
+ STH = <<Treesize:64,
+ Timestamp:64,
+ Roothash/binary,
+ Signature/binary>>,
+ %% io:format("STH: ~p~nBinToSign: ~p~nSignature: ~p~nTimestamp: ~p~n",
+ %% [STH, BinToSign, Signature, Timestamp]),
+ STH.
read_keyfile(Filename, Passphrase) ->
{ok, PemBin} = file:read_file(Filename),
@@ -127,20 +136,51 @@ read_keyfile(Filename, Passphrase) ->
public_key(#'RSAPrivateKey'{modulus = Mod, publicExponent = Exp}) ->
#'RSAPublicKey'{modulus = Mod, publicExponent = Exp}.
--spec serialise(plop_data() | plop_entry()) -> iolist().
-serialise(#plop_data{version = Version,
- signature_type = SigtypeAtom,
- timestamp = Timestamp,
- entry = Entry}) ->
+-spec signhash(iolist() | binary(), binary()) -> binary().
+signhash(Data, PrivKey) ->
+ %% Was going to just crypto:sign/3 the hash but looking at
+ %% digitally_signed() in lib/ssl/src/ssl_handshake.erl it seems
+ %% like we should rather use (undocumented) encrypt_private/3.
+ %public_key:sign(hash(sha256, BinToSign), sha256, PrivKey)
+ public_key:encrypt_private(crypto:hash(sha256, Data),
+ PrivKey,
+ [{rsa_pad, rsa_pkcs1_padding}]).
+
+-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 serialise(spt() | sth() | plop_entry()) -> iolist().
+serialise(#spt{version = Version,
+ signature_type = SigtypeAtom,
+ timestamp = Timestamp,
+ entry = Entry}) ->
Sigtype = signature_type(SigtypeAtom),
[<<Version:8, Sigtype:8, Timestamp:64>>, serialise(Entry)];
serialise(#plop_entry{type = TypeAtom, data = Data}) ->
Type = entry_type(TypeAtom),
- [<<Type:16>>, Data].
+ [<<Type:16>>, Data];
+serialise(#sth{version = Version,
+ signature_type = SigtypeAtom,
+ timestamp = Timestamp,
+ tree_size = Treesize,
+ root_hash = Roothash}) ->
+ Sigtype = signature_type(SigtypeAtom),
+ [<<Version:8, Sigtype:8, Timestamp:64, Treesize:64, Roothash/binary>>].
+-spec signature_type(signature_type()) -> integer().
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.
@@ -150,7 +190,7 @@ entry_type(test) -> 2.
serialise_test_() ->
[?_assertEqual(
<<1:8, 0:8, 0:64, 0:16, "foo">>,
- list_to_binary(serialise(#plop_data{
+ list_to_binary(serialise(#spt{
signature_type = certificate_timestamp,
timestamp = 0,
entry = #plop_entry{type = x509,
diff --git a/src/plop.hrl b/src/plop.hrl
index bfd900b..7275f5a 100644
--- a/src/plop.hrl
+++ b/src/plop.hrl
@@ -1,21 +1,29 @@
-%% A plop_entry has a type and some data.
-%% A plop_data record has the meta data necessary for constructing a
-%% signed timestamp.
+-type signature_type() :: certificate_timestamp | tree_hash | test.
+-type entry_type() :: x509 | precert | test.
--record(plop_data, {
+%% @doc The parts of an SPT which is to be signed.
+-record(spt, {
version = 1 :: integer(),
- signature_type = certificate_timestamp :: certificate_timestamp |
- tree_hash |
- test,
+ signature_type :: signature_type(),
timestamp = now :: 'now' | integer(),
entry :: plop_entry()
}).
+-type spt() :: #spt{}.
+
-record(plop_entry, {
- type = x509 :: x509 | precert | test,
+ type :: entry_type(),
data = <<>> :: binary()
}).
-
--type plop_data() :: #plop_data{}.
-type plop_entry() :: #plop_entry{}.
--export_type([plop_data/0, plop_entry/0]).
+%% @doc The parts of an STH which is to be signed.
+-record(sth, {
+ version = 1 :: integer(),
+ signature_type :: signature_type(),
+ timestamp = now :: 'now' | integer(),
+ tree_size :: integer(),
+ root_hash :: binary() % sha256
+ }).
+-type sth() :: #sth{}.
+
+-export_type([plop_entry/0, entry_type/0]).
diff --git a/src/test/plop_test.erl b/src/test/plop_test.erl
index b453301..e682b28 100644
--- a/src/test/plop_test.erl
+++ b/src/test/plop_test.erl
@@ -3,40 +3,60 @@
-include_lib("eunit/include/eunit.hrl").
adding_test_() ->
- {"Verifies startup and adding log entries.",
+ {"Verifies startup, adding log entries and STH's.",
{setup, % TODO: use 'foreach'
fun start/0,
fun stop/1,
fun (Arg) ->
- [test_add(Arg)]
+ [test_add(Arg), test_sth(Arg),
+ test_add(Arg),
+ test_sth(Arg)] % <-- should fail!
end}}.
start() ->
+ io:format("Starting.~n"),
plop:start_link("test/rsakey.pem", "sikrit").
stop(_Pid) ->
- [?_assertEqual({ok, plop}, plop:stop())].
+ [?_assertEqual({qok, plop}, plop:stop())].
test_add(_Pid) ->
TestVector =
- <<1,247,141,118,3,148,171,128,29,143,106,97,200,179,204,166,242,98,70,185,
- 231,78,193,39,12,245,82,254,230,136,69,69,0,0,0,0,0,0,0,18,103,71,39,45,
- 246,185,69,39,104,49,16,150,96,168,113,65,36,147,66,168,111,142,244,248,
- 11,108,194,21,223,116,136,222,26,163,97,48,219,95,0,43,82,83,197,76,120,
- 166,210,62,103,219,185,216,102,111,217,48,18,100,24,180,17,226,107,180,
- 101,221,143,50,132,243,45,181,74,217,231,119,222,8,37,8,94,155,113,29,
- 115,237,147,115,133,21,63,145,44,115,22,72,237,76,0,14,38,140,193,213,
- 178,112,35,63,183,26,36,160,52,72,17,202,235,114,22,51,25,7,181,32,75,
- 122,184,47,236,22,184,155,189,253,39,71,149,74,169,234,41,62,89,217,49,
- 144,134,206,92,112,126,96,199,131,214,17,74,236,193,188,56,150,91,12,
- 157,93,192,124,192,79,89,164,194,135,159,136,202,198,115,236,76,90,233,
- 225,109,97,2,194,182,222,4,242,183,44,83,132,240,67,192,128,86,115,236,
- 84,193,120,213,10,25,198,189,197,147,117,151,103,12,6,1,80,37,237,125,
- 233,158,237,1,93,202,223,88,245,234,34,113,157,92,39,186,103,89,66,14,
- 78,168,208,141,78,183,57,28,196,252,251,249,153,203>>,
- Entry = #plop_entry{type = test,
- data = <<"some data">>},
- PlopData = #plop_data{signature_type = test,
- timestamp = 4711,
- entry = Entry},
- [?_assertEqual(TestVector, plop:add(PlopData))].
+ <<1,247,141,118,3,148,171,128,29,143,106,97,200,179,204,166,242,98,70,185,231,
+ 78,193,39,12,245,82,254,230,136,69,69,0,0,0,0,0,0,0,18,103,69,73,8,105,107,
+ 47,97,130,137,92,201,148,11,68,203,103,216,217,249,38,109,208,23,55,107,21,
+ 110,128,207,151,46,4,178,228,74,5,247,64,180,85,122,236,127,97,226,50,124,
+ 212,251,227,65,248,18,36,124,252,103,24,35,99,180,207,126,63,116,149,21,86,
+ 255,197,248,212,93,100,123,161,159,94,29,112,23,246,98,3,124,89,135,234,71,
+ 246,21,93,152,214,209,58,25,52,132,219,22,0,38,237,226,118,1,168,86,218,18,
+ 112,227,11,25,199,15,151,246,253,7,91,72,88,169,164,79,143,160,157,241,168,
+ 15,230,1,216,93,67,24,230,106,203,61,115,100,172,238,165,236,198,222,33,126,
+ 12,163,226,165,161,232,106,39,94,93,247,2,164,163,72,34,236,228,168,53,19,
+ 128,111,78,34,54,166,95,78,11,131,241,191,254,82,225,72,68,111,229,169,24,75,
+ 90,254,167,119,10,136,211,20,178,251,244,124,87,223,61,102,244,143,98,213,59,
+ 217,84,80,64,22,209,1,63,64,185,63,13,115,43,36,143,93,19,206,234,100,181,
+ 203,214,189,144,145,21,247,165,125,192,43,94,247,209,81,50,100>>,
+ Entry = #plop_entry{type = test, data = <<"some data">>},
+ SPT = #spt{signature_type = test, timestamp = 4711, entry = Entry},
+ [?_assertEqual(TestVector, plop:add(SPT))].
+
+test_sth(_Pid) ->
+ TestVector =
+ <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,103,93,90,159,157,211,129,96,54,161,145,226,
+ 218,28,127,43,87,221,243,153,101,255,249,156,114,234,50,84,163,183,64,215,
+ 227,112,208,11,52,174,79,151,153,101,56,175,228,12,222,146,138,32,222,199,
+ 187,222,38,67,121,85,204,137,41,61,226,88,128,19,77,73,1,208,145,83,142,28,
+ 130,99,147,213,29,103,113,232,20,209,250,76,198,16,4,249,65,26,232,149,1,21,
+ 42,95,229,251,167,126,220,212,74,195,215,225,22,145,98,51,11,28,83,30,213,15,
+ 42,162,135,251,71,195,145,165,194,247,181,213,47,126,95,232,111,231,49,46,
+ 150,177,118,142,28,230,228,167,54,209,42,177,108,62,39,236,85,153,176,117,
+ 111,61,220,120,49,159,89,185,220,212,53,71,69,144,188,200,16,29,234,255,242,
+ 126,223,87,146,219,248,164,180,20,229,161,227,24,32,209,180,122,61,195,149,
+ 49,3,195,162,218,56,86,185,200,17,188,82,23,64,67,187,160,167,13,175,233,211,
+ 226,72,198,182,6,6,65,123,76,10,81,66,55,159,120,162,56,102,233,35,102,153,
+ 214,238,116,108,181,106,159,157,241,101,120,35,107,152,36,193,241,211,210,72,
+ 184,166,220,155,198,238,246,47,181,89,80,29,52,222,181,25,81,76,123,249,225,
+ 5,192,233,133,131>>,
+ STH = plop:sth(#sth{timestamp = 4711}),
+ %%io:format(element(2, file:open("foo", write)), "~p", [STH]),
+ [?_assertEqual(TestVector, STH)].