diff options
Diffstat (limited to 'src/plop.erl')
-rw-r--r-- | src/plop.erl | 132 |
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, |