summaryrefslogtreecommitdiff
path: root/src/plop.erl
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/plop.erl
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/plop.erl')
-rw-r--r--src/plop.erl132
1 files changed, 86 insertions, 46 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,