diff options
-rw-r--r-- | ebin/plop.app | 2 | ||||
-rw-r--r-- | include/plop.hrl | 21 | ||||
-rw-r--r-- | src/db.erl | 15 | ||||
-rw-r--r-- | src/db.hrl | 4 | ||||
-rw-r--r-- | src/plop.erl | 176 | ||||
-rw-r--r-- | test/eckey-public.pem | 4 | ||||
-rw-r--r-- | test/eckey.pem | 8 | ||||
-rw-r--r-- | test/plop_test.erl | 108 |
8 files changed, 204 insertions, 134 deletions
diff --git a/ebin/plop.app b/ebin/plop.app index 55d5648..dd40311 100644 --- a/ebin/plop.app +++ b/ebin/plop.app @@ -3,7 +3,7 @@ [{description, "The plop store"}, {vsn, "0.0.1"}, {modules, [plop_app, plop_sup, plop, db, ht, hex]}, - {applications, [kernel, stdlib, mnesia]}, % crypto, public_key, mnesia + {applications, [kernel, stdlib, mnesia]}, % crypto, public_key {registered, [plop, db]}, {mod, {plop_app, []}} % <-- key file and pass phrase ]}. diff --git a/include/plop.hrl b/include/plop.hrl index 30a5385..8115374 100644 --- a/include/plop.hrl +++ b/include/plop.hrl @@ -35,21 +35,22 @@ }). -type spt_signed() :: #spt_signed{}. -%% %% Part of interface to plop:add/1. -%% -record(plop_entry, { -%% type :: entry_type(), -%% data :: binary() -%% }). -%% -type plop_entry() :: #plop_entry{}. - -%% A data entry. +%% A plop entry with timestamp. Part of the Merkle Tree Leaf +%% structure. -record(timestamped_entry, { timestamp = now :: now | integer(), - entry_type :: entry_type(), - entry :: binary() + entry :: plop_entry() }). -type timestamped_entry() :: #timestamped_entry{}. +%% An entry, without the timestamp. This is what we hash over and +%% store in the the database for finding duplicated submissions. +-record(plop_entry, { + type :: entry_type(), + data :: binary() + }). +-type plop_entry() :: #plop_entry{}. + %% @doc The parts of an STH which is to be signed. Used as the %% interface to plop:sth/1, for testing. Should probably be internal %% to plop, if that can be arranged wrt testing. @@ -51,8 +51,7 @@ dump_to_file(Filename) -> mnesia:dump_to_textfile(Filename). init(_Args) -> - {mnesia:wait_for_tables([plop], 5000), - []}. + {mnesia:wait_for_tables([plop], 5000), []}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). @@ -100,4 +99,14 @@ handle_call({dump, Table}, _From, State) -> Res = mnesia:transaction(F), {reply, Res, State}; handle_call({find, Hash}, _From, State) -> - {reply, mnesia:dirty_read({plop, Hash}), State}. + F = fun() -> + mnesia:select(plop, + [{#plop{hash = Hash, _='_'}, [], ['$_']}]) + end, + {atomic, Result} = mnesia:transaction(F), + Record = case length(Result) of + 0 -> []; + 1 -> hd(Result); + _ -> duplicate_hash_in_db % FIXME: log an error + end, + {reply, Record, State}. @@ -2,7 +2,7 @@ %% 'index' is the primary key, 'hash' is also indexed. -record(plop, { index :: non_neg_integer(), % Primary key. - hash :: binary(), % Hash over mtl. - mtl :: mtl(), % Merkle Tree Leaf, an #mtl{}. + hash :: binary(), % Hash over #plop_entry{} in mtl. + mtl :: mtl(), % Merkle Tree Leaf, an #mtl{}. spt_text :: binary() % Signed Plop Timestamp, an #spt_on_wire{}. }). diff --git a/src/plop.erl b/src/plop.erl index 63545f0..5ca595f 100644 --- a/src/plop.erl +++ b/src/plop.erl @@ -16,6 +16,8 @@ -export([add/1, sth/0]). %% API for tests. -export([sth/1]). +-export([read_keyfile_rsa/2, read_keyfiles_ec/2]). +-export([testing_get_pubkey/0]). %% gen_server callbacks. -export([init/1, handle_call/3, terminate/2, handle_cast/2, handle_info/2, code_change/3]). @@ -26,8 +28,8 @@ -include_lib("eunit/include/eunit.hrl"). -define(PLOPVERSION, 1). --define(TESTKEYFILE, "src/test/rsakey.pem"). --define(TESTKEYPASSPHRASE, "sikrit"). +-define(TESTPRIVKEYFILE, "test/eckey.pem"). +-define(TESTPUBKEYFILE, "test/eckey-public.pem"). -record(state, {pubkey :: public_key:rsa_public_key(), privkey :: public_key:rsa_private_key(), @@ -35,7 +37,7 @@ hashtree :: ht:head()}). start_link() -> - start_link(?TESTKEYFILE, ?TESTKEYPASSPHRASE). % FIXME: Remove. + start_link(?TESTPRIVKEYFILE, ?TESTPUBKEYFILE). % FIXME: Remove. start_link(Keyfile, Passphrase) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Keyfile, Passphrase], []). @@ -43,10 +45,16 @@ stop() -> gen_server:call(?MODULE, stop). %%%%%%%%%%%%%%%%%%%% -init([Keyfile, Passphrase]) -> - {Private_key, Public_key} = read_keyfile(Keyfile, Passphrase), - LogID = crypto:hash(sha256, - public_key:der_encode('RSAPublicKey', Public_key)), +init([PubKeyfile, PrivKeyfile]) -> + %% Read RSA keypair. + %% {Private_key, Public_key} = read_keyfile_rsa(Keyfile, Passphrase), + %% LogID = crypto:hash(sha256, + %% public_key:der_encode('RSAPublicKey', Public_key)), + %% Read EC keypair. + {Private_key, Public_key} = read_keyfiles_ec(PubKeyfile, PrivKeyfile), + LogID = crypto:hash(sha256, public_key:der_encode( + 'ECPoint', + element(2, element(1, Public_key)))), % FIXME! {ok, #state{pubkey = Public_key, privkey = Private_key, logid = LogID, @@ -74,6 +82,8 @@ sth() -> sth(Data) -> gen_server:call(?MODULE, {sth, Data}). +testing_get_pubkey() -> + gen_server:call(?MODULE, {test, pubkey}). %%%%%%%%%%%%%%%%%%%% handle_call(stop, _From, State) -> {stop, normal, stopped, State}; @@ -86,35 +96,46 @@ handle_call({add, #timestamped_entry{} = TimestampedEntry}, logid = LogID, hashtree = Tree}) -> {NewTree, SPT} = do_add(TimestampedEntry, Privkey, LogID, Tree), - io:format("Index: ~p~nSPT: ~p~n", [ht:size(NewTree), SPT]), {reply, SPT, State#state{hashtree = NewTree}}; handle_call({sth, Data}, _From, Plop = #state{privkey = PrivKey, hashtree = Tree}) -> - {reply, sth(PrivKey, Tree, Data), Plop}. + {reply, sth(PrivKey, Tree, Data), Plop}; + +handle_call({test, pubkey}, _From, + Plop = #state{pubkey = PK}) -> + {reply, PK, Plop}. %%%%%%%%%%%%%%%%%%%% -spec do_add(timestamped_entry(), public_key:rsa_private_key(), binary(), any()) -> {any(), binary()}. -do_add(TimestampedEntry, Privkey, LogID, Tree) -> - MTL = #mtl{entry = TimestampedEntry}, - MTL_text = serialise(MTL), - DB_hash = crypto:hash(sha256, MTL_text), - case db:find(DB_hash) of - #plop{index = I, mtl = M, spt_text = SPT} -> - io:format("Found entry: index=~p, MTL: ~p, SPT: ~p~n", [I, M, SPT]), - %% DB consistency check: - %%Record = #plop{hash = H, spt = serialise(Data)}, % FIXME: Remove. +do_add(TimestampedEntry = #timestamped_entry{entry = PlopEntry}, + Privkey, LogID, Tree) -> + DB_hash = crypto:hash(sha256, serialise(PlopEntry)), + Record = db:find(DB_hash), + case Record of + #plop{index = I, mtl = M = #mtl{entry = E}, spt_text = SPT} -> + io:format("Found entry: index=~p~nMTL: ~p~nSPT: ~p~n", [I, M, SPT]), + Record = Record#plop{ % DB consistency checking. + hash = DB_hash, + mtl = #mtl{entry = + #timestamped_entry{ + timestamp = E#timestamped_entry.timestamp, + entry = PlopEntry} + }}, {Tree, SPT}; % State not changed, cached SPT. _ -> NewSPT = spt(LogID, Privkey, TimestampedEntry), + MTL = #mtl{entry = TimestampedEntry}, + io:format("Creating new entry: index=~p~ndb hash: ~p~nMTL: ~p~nSPT: ~p~n", + [ht:size(Tree) + 1, DB_hash, MTL, NewSPT]), DB_data = #plop{index = ht:size(Tree) + 1, hash = DB_hash, mtl = MTL, spt_text = NewSPT}, db:add(DB_data), - {ht:append(Tree, MTL_text), % New tree. + {ht:append(Tree, serialise(MTL)), % New tree. NewSPT} % New SPT. end. @@ -123,8 +144,7 @@ do_add(TimestampedEntry, Privkey, LogID, Tree) -> -spec spt(binary(), public_key:rsa_private_key(), timestamped_entry()) -> binary(). spt(LogID, PrivKey, #timestamped_entry{ timestamp = Timestamp_in, - entry_type = EntryType, - entry = Entry + entry = #plop_entry{type = EntryType, data = EntryData} }) -> Timestamp = timestamp(Timestamp_in), BinToSign = @@ -133,18 +153,16 @@ spt(LogID, PrivKey, #timestamped_entry{ signature_type = certificate_timestamp, timestamp = Timestamp, entry_type = EntryType, - signed_entry = Entry})), + signed_entry = EntryData})), Signature = signhash(BinToSign, PrivKey), SPT = serialise(#spt_on_wire{ version = ?PLOPVERSION, logid = LogID, timestamp = Timestamp, signature = Signature}), - io:format("SPT: ~p~nBinToSign: ~p~nSignature = ~p~n", - [SPT, BinToSign, Signature]), list_to_binary(SPT). -%% @doc Signed Tree Head as described in RFC6962 section 3.2. +%% @doc Signed Tree Head as specified in RFC6962 section 3.2. sth(PrivKey, Tree, []) -> sth(PrivKey, Tree, #sth{timestamp = now}); sth(PrivKey, Tree, #sth{version = Version, timestamp = Timestamp_in}) -> @@ -166,24 +184,64 @@ sth(PrivKey, Tree, #sth{version = Version, timestamp = Timestamp_in}) -> [STH, BinToSign, Signature, Timestamp]), STH. -read_keyfile(Filename, Passphrase) -> +%% TODO: Merge the keyfile reading functions. + +%% Read one password protected PEM file with an RSA keypair. +read_keyfile_rsa(Filename, Passphrase) -> {ok, PemBin} = file:read_file(Filename), - [Entry] = public_key:pem_decode(PemBin), - Privatekey = public_key:pem_entry_decode(Entry, Passphrase), + [KeyPem] = public_key:pem_decode(PemBin), % Use first entry. + Privatekey = decode_key(KeyPem, Passphrase), {Privatekey, public_key(Privatekey)}. +%% Read two PEM files, one with a private EC key and one with the +%% corresponding public EC key. +read_keyfiles_ec(PrivkeyFile, Pubkeyfile) -> + {ok, PemBinPriv} = file:read_file(PrivkeyFile), + [OTPPubParamsPem, PrivkeyPem] = public_key:pem_decode(PemBinPriv), + Privatekey = decode_key(PrivkeyPem), + + {_, ParamsBin, ParamsEnc} = OTPPubParamsPem, + PubParamsPem = {'EcpkParameters', ParamsBin, ParamsEnc}, + Params = public_key:pem_entry_decode(PubParamsPem), + + {ok, PemBinPub} = file:read_file(Pubkeyfile), + [SPKIPem] = public_key:pem_decode(PemBinPub), + %% SPKI is missing #'AlgorithmIdentifier' so pem_entry_decode won't do. + %% Publickey = public_key:pem_entry_decode(SPKIPem), + #'SubjectPublicKeyInfo'{algorithm = AlgoDer} = SPKIPem, + SPKI = public_key:der_decode('SubjectPublicKeyInfo', AlgoDer), + #'SubjectPublicKeyInfo'{subjectPublicKey = {_, Octets}} = SPKI, + Point = #'ECPoint'{point = Octets}, + Publickey = {Point, Params}, + + {Privatekey, Publickey}. + +decode_key(Entry) -> + public_key:pem_entry_decode(Entry). +decode_key(Entry, Passphrase) -> + public_key:pem_entry_decode(Entry, Passphrase). + public_key(#'RSAPrivateKey'{modulus = Mod, publicExponent = Exp}) -> #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}. --spec signhash(iolist() | binary(), public_key:rsa_private_key()) -> 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}]). + +%% FIXME: Merge RSA and DC. +signhash(D, K) -> + signhash_ec(D, K). + +-spec signhash_ec(iolist() | binary(), public_key:ec_private_key()) -> binary(). +signhash_ec(Data, PrivKey) -> + public_key:sign(Data, sha256, PrivKey). + +%% -spec signhash_rsa(iolist() | binary(), public_key:rsa_private_key()) -> binary(). +%% signhash_rsa(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}]). %%%%%%%%%%%%%%%%%%%% %% Serialisation of data. @@ -216,16 +274,20 @@ timestamp(Timestamp) -> _ -> Timestamp end. --spec serialise(timestamped_entry() | spt_on_wire() | spt_signed() | mtl() | sth()) -> iolist(). +-spec serialise(plop_entry() | timestamped_entry() | spt_on_wire() | spt_signed() | mtl() | sth()) -> iolist(). +serialise(#plop_entry{ + type = TypeAtom, + data = Data + }) -> + EntryType = entry_type(TypeAtom), + [<<EntryType:16, + Data/binary>>]; serialise(#timestamped_entry{ timestamp = Timestamp, - entry_type = TypeAtom, - entry = Entry + entry = PlopEntry }) -> - EntryType = entry_type(TypeAtom), - [<<Timestamp:64, - EntryType:16, - Entry/binary>>]; + [<<Timestamp:64>>, + serialise(PlopEntry)]; serialise(#spt_on_wire{ version = Version, logid = LogID, @@ -285,14 +347,30 @@ serialise_test_() -> timestamp = 0, entry_type = x509, signed_entry = <<"foo">>})))]. -add_test_() -> - {ok, S} = init([?TESTKEYFILE, ?TESTKEYPASSPHRASE]), +%%add_test_() -> +add_test() -> + {ok, S} = init([?TESTPRIVKEYFILE, ?TESTPUBKEYFILE]), + + Data1 = <<"some data">>, {_Tree, SPT} = do_add(#timestamped_entry{ timestamp = 4711, - entry_type = test, - entry = <<"some data">>}, + entry = #plop_entry{type = test, data = Data1}}, + S#state.privkey, + S#state.logid, + S#state.hashtree), + {_Tree1, SPT1} = + do_add(#timestamped_entry{ + timestamp = 4712, + entry = #plop_entry{type = test, data = Data1}}, S#state.privkey, S#state.logid, S#state.hashtree), - [?_assertEqual(<<"fixme:SPT">>, SPT)]. + ?assertEqual(SPT, SPT1), + + TE = #timestamped_entry{ + timestamp = 0, + entry = #plop_entry{type = test, data = <<"some data">>}}, + SPTeq1 = spt(S#state.logid, S#state.privkey, TE), + SPTeq2 = spt(S#state.logid, S#state.privkey, TE), + ?assertNotEqual(SPTeq1, SPTeq2). % DSA signatures differ! diff --git a/test/eckey-public.pem b/test/eckey-public.pem new file mode 100644 index 0000000..d952d7e --- /dev/null +++ b/test/eckey-public.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4qWq6afhBUi0OdcWUYhyJLNXTkGq +Q9PMS5lqoCgkV2h1ZvpNjBH2u8UbgcOQwqDo66z6BWQJGolozZYmNHE2kQ== +-----END PUBLIC KEY----- diff --git a/test/eckey.pem b/test/eckey.pem new file mode 100644 index 0000000..ed24cfa --- /dev/null +++ b/test/eckey.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIMM/FjZ4FSzfENTTwGpTve6CP+IVrY7p8OKV634uJI/foAoGCCqGSM49 +AwEHoUQDQgAE4qWq6afhBUi0OdcWUYhyJLNXTkGqQ9PMS5lqoCgkV2h1ZvpNjBH2 +u8UbgcOQwqDo66z6BWQJGolozZYmNHE2kQ== +-----END EC PRIVATE KEY----- diff --git a/test/plop_test.erl b/test/plop_test.erl index 79dbbd3..cbbc85c 100644 --- a/test/plop_test.erl +++ b/test/plop_test.erl @@ -2,20 +2,20 @@ -include("plop.hrl"). -include_lib("eunit/include/eunit.hrl"). -start_stop_test_() -> - {"The server can be started, stopped and is regsitered", - {setup, fun start/0, fun stop/1, fun is_registered/1}}. +%% start_stop_test_() -> +%% {"The server can be started, stopped and is regsitered", +%% {setup, fun start/0, fun stop/1, fun is_registered/1}}. %% "Entries can be added and the STH changes." %% FIXME: This way, if a test fails, we don't stop plop. The tests %% must run and be validated in strict order though. adding_verifying_test() -> - Pid = start(), - add(0), - sth(0), - add(1), - %% sth(), - stop(Pid). + %%Pid = start(), + Pubkey = plop:testing_get_pubkey(), + add(0, Pubkey), + add(1, Pubkey), + sth(0, Pubkey). + %%stop(Pid). %%% Setup. start() -> @@ -31,49 +31,29 @@ is_registered(Pid) -> ?_assertEqual(Pid, whereis(plop))]. %%% Helpers. -add(0) -> - 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,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>>, +add(0, Pubkey) -> + Msg = <<"some data">>, Entry = #timestamped_entry{timestamp = 4711, - entry_type = test, - entry = <<"some data">>}, + entry = #plop_entry{type = test, + data = Msg}}, SPT = plop:add(Entry), - ?assertEqual(TestVector, SPT); -add(1) -> - 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,104,141,82,14,84,52, - 131,244,51,145,16,7,238,168,117,8,184,95,165,94,116,234,87,145,43,39,223,243, - 33,159,238,239,195,203,246,232,147,125,234,34,147,83,254,253,248,133,49,81, - 80,7,104,23,24,147,24,116,147,183,20,58,165,53,147,196,226,250,135,18,115, - 182,139,194,190,60,97,103,240,188,86,184,194,21,75,79,136,84,62,53,123,44, - 236,244,24,190,207,193,42,156,230,135,174,90,195,89,174,185,228,129,148,78, - 255,168,104,73,142,85,11,239,222,227,213,208,99,31,12,177,223,187,11,216,119, - 29,231,67,82,140,103,181,173,71,246,112,57,121,153,204,1,249,251,172,26,77, - 96,223,129,102,14,160,115,10,87,105,234,21,99,65,125,198,35,104,160,43,25,74, - 159,64,236,226,126,208,88,199,60,12,88,36,214,174,110,147,215,142,1,205,77, - 116,119,47,222,87,84,99,78,131,212,247,138,156,190,211,244,184,140,46,202,13, - 217,28,20,109,8,129,62,226,37,51,123,94,151,151,47,96,111,122,118,178,242,14, - 213,35,184,204,165,157,199,1,210,74,243,180,36,85,163,69,166,79,136>>, + <<Version:8, _LogID:256, Timestamp:64, Signature/binary>> = SPT, + Signed = <<1:8, 0:8, 4711:64, 2:16, Msg/binary>>, + ?assertEqual(1, Version), + ?assertEqual(4711, Timestamp), + ?assert(public_key:verify(Signed, sha256, Signature, Pubkey)); + +add(1, Pubkey) -> + Msg = <<"some more data">>, Entry = #timestamped_entry{timestamp = 4712, - entry_type = test, - entry = <<"some more data">>}, - SPT = plop:add(Entry), - %%io:format(element(2, file:open("foo", write)), "~p", [SPT]), - ?assertEqual(TestVector, SPT). + entry = #plop_entry{type = test, + data = Msg}}, + <<Version:8, _LogID:256, Timestamp:64, Signature/binary>> = plop:add(Entry), + Signed = <<1:8, 0:8, 4712:64, 2:16, Msg/binary>>, + ?assertEqual(1, Version), + ?assertEqual(4712, Timestamp), + ?assert(public_key:verify(Signed, sha256, Signature, Pubkey)). + %% add(2) -> %% TestVector = <<>>, %% %% Same data as in 0, should not result in new database entry. @@ -82,23 +62,13 @@ add(1) -> %% ?assertEqual(TestVector, SPT), %% ?assertEqual(fixme, fixme). -sth(0) -> - TestVector = - <<0,0,0,0,0,0,0,1,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,16,126,61,255,54,243,5,185,250,149,18,30,228,16,48,168,252,213,27,205, - 254,157,72,230,112,65,150,187,18,215,17,249,72,18,38,159,217,49,159,177,153, - 175,86,139,158,29,24,202,126,203,88,216,19,205,237,172,48,9,113,228,231,170, - 131,38,155,185,188,232,215,15,54,93,254,173,100,13,115,172,161,7,106,226,180, - 168,81,245,47,10,59,14,25,26,23,80,11,227,147,115,216,173,93,63,232,50,213, - 43,148,71,149,104,32,10,217,108,182,194,88,12,153,187,42,190,154,203,114,200, - 24,137,106,65,51,25,162,178,24,199,155,215,208,115,5,239,64,189,69,0,196,55, - 211,91,12,83,132,131,84,92,146,124,125,117,74,62,7,162,230,37,13,45,122,183, - 112,207,227,240,152,190,181,168,96,210,252,59,144,12,141,46,18,18,51,226,14, - 218,17,255,212,136,198,154,69,64,232,234,249,2,232,45,165,206,157,195,77,254, - 126,173,10,12,184,21,55,111,183,15,2,251,177,220,139,35,20,148,219,137,78, - 187,221,242,23,254,196,182,98,110,150,95,126,53,42,243,123,198,30,247,79,17, - 172,129>>, - STH = plop:sth(#sth{timestamp = 4711}), - io:format(element(2, file:open("testdata", write)), "~p", [STH]), - ?assertEqual(TestVector, STH). +sth(0, Pubkey) -> + STH = plop:sth(#sth{}), + %%io:format(element(2, file:open("testdata", write)), "~p", [STH]), + <<Treesize:64, + Timestamp:64, + Roothash:256, + Signature/binary>> = STH, + ?assertEqual(2, Treesize), + Data = <<1:8, 1:8, Timestamp:64, Treesize:64, Roothash:256>>, + ?assert(public_key:verify(Data, sha256, Signature, Pubkey)). |