summaryrefslogtreecommitdiff
path: root/src/plop.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/plop.erl')
-rw-r--r--src/plop.erl134
1 files changed, 113 insertions, 21 deletions
diff --git a/src/plop.erl b/src/plop.erl
index 4af3f25..8af6418 100644
--- a/src/plop.erl
+++ b/src/plop.erl
@@ -7,43 +7,73 @@
%%% to prove that your entry is indeed present in the log.
-module('plop').
--export([start/0, loop/2]).
+-include("plop.hrl").
+-include_lib("public_key/include/public_key.hrl").
+-include_lib("eunit/include/eunit.hrl").
--record(plop, {pubkey :: crypto:rsa_public(),
- privkey :: crypt:rsa_private()}).
+-export([start/2, loop/1, dummy_add/1]).
-start(PlopKey) ->
- Tree = ht:create(),
- register(plop, spawn(plop, loop, [PlopKey, Tree])).
+-record(plop, {pubkey :: public_key:rsa_public_key(),
+ privkey :: public_key:rsa_private_key(),
+ logid :: binary(),
+ hashtree :: ht:head()}).
+
+-spec start(string(), string()) -> pid().
+start(Keyfile, Passphrase) ->
+ {Private_key, Public_key} = read_keyfile(Keyfile, Passphrase),
+ LogID = crypto:hash(sha256, public_key:der_encode('RSAPublicKey', Public_key)),
+ Plop = #plop{pubkey = Public_key,
+ privkey = Private_key,
+ logid = LogID,
+ hashtree = ht:create()},
+ Pid = spawn_link(plop, loop, [Plop]),
+ register(plop, Pid),
+ Pid.
log(Format, Data) ->
io:format(Format, Data).
-loop(PlopKey, Tree) ->
+loop(Plop) ->
receive
{From, quit} ->
- From ! {quit, ok};
+ From ! {ok, quit};
{From, Data} ->
- handle_req(From, Tree, Data),
- loop(Tree);
+ handle_req(From, Plop, Data),
+ loop(Plop);
Unknown ->
log("DEBUG: Received malformed command: ~p~n", [Unknown]),
- loop(Tree)
+ loop(Plop)
end.
-handle_req(From, Tree, Arg) ->
+-spec serialise(plop_entry() | plop_data()) -> iolist().
+serialise(#plop_entry{type = EntryType, entry = Entry}) ->
+ [<<EntryType:16>>, Entry];
+serialise(#plop_data{version = Version,
+ signature_type = Sigtype,
+ timestamp = Timestamp,
+ entry = Entry}) ->
+ [<<Version:8, Sigtype:8, Timestamp:64>>, serialise(Entry)].
+
+handle_req(From,
+ #plop{privkey = Privkey,
+ logid = LogID,
+ hashtree = Tree},
+ Arg) ->
case Arg of
- {add, Data} ->
- From ! spt(ht:append(Tree, Data));
- %% {diff, Tree2} ->
- %% From ! ht:diff(Tree, Tree2);
- {sth} -> % Signed tree head.
- sth(Tree);
+ {add, PlopData = #plop_data{entry = Entry}} when is_record(Entry, plop_entry) ->
+ %% fixme: add Entry to db,
+ H = ht:append(Tree, serialise(Entry)),
+ SPT = spt(LogID, Privkey, PlopData),
+ %%io:format("adding ~p to ~p -> H: ~p, SPT: ~p~n",
+ [Entry, Tree, H, SPT]),
+ From ! {ok, SPT};
+ sth -> % Signed tree head.
+ From ! {ok, sth(Tree)};
Unknown ->
From ! {error, Unknown}
end.
-%% @doc Signed Plop Timestamp.
+%% RFC6962
%% Signed Timestamp
%% struct {
%% Version sct_version;
@@ -62,8 +92,45 @@ handle_req(From, Tree, Arg) ->
%% CtExtensions extensions;
%% };
%% } SignedCertificateTimestamp;
-spt(LogID, Data) ->
- "FIXME: a signed timestamp for " ++ Data.
+%% RRC 5246
+ %% A digitally-signed element is encoded as a struct DigitallySigned:
+ %% struct {
+ %% SignatureAndHashAlgorithm algorithm;
+ %% opaque signature<0..2^16-1>;
+ %% } DigitallySigned;
+
+-define(PLOPVERSION, 1).
+
+%% @doc Signed Plop Timestamp.
+spt(LogID, PrivKey, #plop_data{version = Version, % >= 1
+ signature_type = Sigtype, % >= 0
+ timestamp = Timestamp_in,
+ entry = Entry = #plop_entry{}}) when is_binary(LogID) ->
+ 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(#plop_data{version = Version,
+ signature_type = Sigtype,
+ timestamp = Timestamp,
+ entry = Entry})),
+
+ %% 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}]),
+ SPT = <<?PLOPVERSION:8,
+ LogID/binary,
+ Timestamp:64,
+ Signature/binary>>,
+ %%io:format("SPT: ~p~nBinToSign: ~p~nSignature = ~p~n", [SPT, BinToSign, Signature]),
+ SPT.
%% @doc Signed Tree Head
%% digitally-signed struct {
@@ -75,3 +142,28 @@ spt(LogID, Data) ->
%% } TreeHeadSignature;
sth(Tree) ->
"FIXME: signed tree head for " ++ Tree.
+
+read_keyfile(Filename, Passphrase) ->
+ {ok, PemBin} = file:read_file(Filename),
+ [Entry] = public_key:pem_decode(PemBin),
+ Privatekey = public_key:pem_entry_decode(Entry, Passphrase),
+ {Privatekey, public_key(Privatekey)}.
+
+public_key(#'RSAPrivateKey'{modulus = Mod, publicExponent = Exp}) ->
+ #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}.
+
+%%%%%%%%%%%%%%%%%%%%
+%% Playing around
+dummy_add(String) ->
+ String.
+
+%%%%%%%%%%%%%%%%%%%%
+%% Tests.
+serialise_test_() ->
+ Entry = #plop_entry{type = ?PLOP_ENTRY_TYPE_X509, entry = "foo"},
+ Entry_serialised = <<0:16, "foo">>,
+ [?_assertEqual(Entry_serialised, list_to_binary(serialise(Entry))),
+ ?_assertEqual(<<1:8, 0:8, 0:64, Entry_serialised/binary>>,
+ list_to_binary(serialise(#plop_data{signature_type = 0,
+ timestamp = 0,
+ entry = Entry})))].