diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | NEWS.md | 5 | ||||
-rw-r--r-- | ebin/catlfish.app | 2 | ||||
-rw-r--r-- | reltool.config | 2 | ||||
-rw-r--r-- | src/catlfish_app.erl | 2 | ||||
-rw-r--r-- | src/v1.erl | 105 |
6 files changed, 87 insertions, 33 deletions
@@ -6,7 +6,9 @@ release: rm -rf rel mkdir rel ./makerelease.erl - (cd rel ; ln -s ../../plop/Mnesia.nonode@nohost . ; ln -s ../../plop/test .) + (cd rel; \ + ln -s ../../plop/Mnesia.nonode@nohost .; \ + ln -s ../../plop/test .) cp httpd_props.conf rel mkdir rel/catlfish cp -r webroot rel/catlfish @@ -1,3 +1,8 @@ +# Changes in version 0.1.1 - 2014-09-20 + +catlfish version 0.1.1 fixes a major bug in add-chain decoding and +implements get-entry-and-proof. + # catlfish version 0.1.0 "PoC" - 2014-09-15 catlfish version 0.1.0 is the proof of concept release. diff --git a/ebin/catlfish.app b/ebin/catlfish.app index e1807c5..5e3b7ee 100644 --- a/ebin/catlfish.app +++ b/ebin/catlfish.app @@ -6,7 +6,7 @@ %% starting beam. {application, catlfish, [{description, "catlfish -- Certificate Transparency Log Server"}, - {vsn, "0.0.1"}, + {vsn, "0.1.1"}, {modules, [v1, catlfish_app]}, {applications, [kernel, stdlib, plop, inets, jiffy]}, {mod, {catlfish_app, []}}]}. diff --git a/reltool.config b/reltool.config index 1f7297e..c0e1b2c 100644 --- a/reltool.config +++ b/reltool.config @@ -3,7 +3,7 @@ {lib_dirs, [".."]}, {erts, [{mod_cond, derived}, {app_file, strip}]}, {app_file, strip}, - {rel, "catlfish", "1.0.0", + {rel, "catlfish", "0.1.1", [ kernel, stdlib, diff --git a/src/catlfish_app.erl b/src/catlfish_app.erl index f4b67ad..9c9bdad 100644 --- a/src/catlfish_app.erl +++ b/src/catlfish_app.erl @@ -19,7 +19,7 @@ dummy() -> start(_StartType, _StartArgs) -> io:format("starting catlfish~n", []), InetsResult = inets:start(httpd, [{proplist_file, "httpd_props.conf"}]), - io:format("catlfish: tried to start inets service:~p~n", [InetsResult]), + io:format("catlfish: tried to start inets service: ~p~n", [InetsResult]), Pid = spawn(fun () -> dummy() end), @@ -16,22 +16,31 @@ %% Public functions, i.e. part of URL. 'add-chain'(SessionID, _Env, Input) -> - Res = case (catch jiffy:decode(Input)) of - {error, E} -> html("add-chain: bad input:", E); - {[{<<"chain">>, Chain}]} -> - Entry = #plop_entry{type = x509, - data = list_to_binary(Chain)}, - SPT = plop:add(#timestamped_entry{entry = Entry}), - R = [{sct_version, ?PROTOCOL_VERSION}, - {id, base64:encode(SPT#spt.logid)}, - {timestamp, SPT#spt.timestamp}, - {extensions, base64:encode("")}, - {signature, base64:encode( - plop:serialise(SPT#spt.signature))}], - binary_to_list(jiffy:encode({R})); - _ -> html("add-chain: missing input: chain", Input) - end, - deliver(SessionID, Res). + R = case (catch jiffy:decode(Input)) of + {error, E} -> + html("add-chain: bad input:", E); + {[{<<"chain">>, ChainBase64}]} -> + case (catch [base64:decode(X) || X <- ChainBase64]) of + {'EXIT', _} -> + html("add-chain: invalid base64-encoded chain: ", + [ChainBase64]); + [LeafCert | CertChain] -> + Entry = #plop_entry{type = x509, data = LeafCert}, + SPT = plop:add(#timestamped_entry{entry = Entry}, + list_to_binary(CertChain)), + R2 = [{sct_version, ?PROTOCOL_VERSION}, + {id, base64:encode(SPT#spt.logid)}, + {timestamp, SPT#spt.timestamp}, + {extensions, base64:encode("")}, + {signature, base64:encode( + plop:serialise(SPT#spt.signature))}], + binary_to_list(jiffy:encode({R2})); + Invalid -> + html("add-chain: chain is not a list: ", [Invalid]) + end; + _ -> html("add-chain: missing input: chain", Input) + end, + deliver(SessionID, R). 'add-pre-chain'(SessionID, _Env, _Input) -> niy(SessionID). @@ -82,17 +91,26 @@ html("get-proof-by-hash: bad input:", [HashInput, TreeSizeInput]); false -> - {Index, Path} = plop:inclusion(Hash, TreeSize), binary_to_list( jiffy:encode( - {[{leaf_index, Index}, - {audit_path, [base64:encode(X) || X <- Path]}]})) + case plop:inclusion(Hash, TreeSize) of + {ok, Index, Path} -> + {[{leaf_index, Index}, + {audit_path, + [base64:encode(X) || X <- Path]}]}; + {notfound, Msg} -> + %% FIXME: http status 400 + {[{success, false}, + {error_message, list_to_binary(Msg)}]} + end)) end; - _ -> html("get-sth-proof-by-hash: bad input:", Input) + _ -> html("get-proof-by-hash: bad input:", Input) end, deliver(SessionID, R). 'get-entries'(SessionID, _Env, Input) -> + %% TODO: Limit the number of returned entreis (i.e. start-end) to + %% something reasonable. R = case lists:sort(httpd:parse_query(Input)) of [{"end", EndInput}, {"start", StartInput}] -> {Start, _} = string:to_integer(StartInput), @@ -105,8 +123,33 @@ end, deliver(SessionID, R). -'get-entry-and-proof'(SessionID, _Env, _Input) -> - niy(SessionID). +'get-entry-and-proof'(SessionID, _Env, Input) -> + R = case lists:sort(httpd:parse_query(Input)) of + [{"leaf_index", IndexInput}, {"tree_size", TreeSizeInput}] -> + {Index, _} = string:to_integer(IndexInput), + {TreeSize, _} = string:to_integer(TreeSizeInput), + case lists:member(error, [Index, TreeSize]) of + true -> + html("get-entry-and-proof: not integers: ", + [IndexInput, TreeSizeInput]); + false -> + binary_to_list( + jiffy:encode( + case plop:inclusion_and_more(Index, TreeSize) of + {ok, Leaf, Chain, Path} -> + {[{leaf_input, + base64:encode(plop:serialise(Leaf))}, + {extra_data, base64:encode(Chain)}, + {audit_path, + [base64:encode(X) || X <- Path]}]}; + {notfound, Msg} -> + {[{success, false}, + {error_message, list_to_binary(Msg)}]} + end)) + end; + _ -> html("get-entry-and-proof: bad input:", Input) + end, + deliver(SessionID, R). 'get-roots'(SessionID, _Env, _Input) -> R = [{certificates, []}], % NIY. @@ -126,14 +169,18 @@ hello(SessionID, Env, Input) -> [SessionID, Env, Input, Query])). %% Private functions. +-spec encode_entries([{mtl(), binary()}]) -> list(). encode_entries(Entries) -> - binary_to_list(jiffy:encode({[{entries, encode_entries2(Entries)}]})). -encode_entries2([H|T]) -> - LeafInput = base64:encode(plop:serialise(H)), - ExtraData = base64:encode(""), - [{[{leaf_input, LeafInput}, {extra_data, ExtraData}]} | encode_entries2(T)]; -encode_entries2([]) -> - []. + binary_to_list(jiffy:encode({[{entries, unpack_entries(Entries)}]})). + +-spec unpack_entries([{mtl(), binary()}]) -> list(). +unpack_entries([]) -> + []; +unpack_entries([H|T]) -> + {MTL, Extra} = H, + LeafInput = base64:encode(plop:serialise(MTL)), + ExtraData = base64:encode(Extra), + [{[{leaf_input, LeafInput}, {extra_data, ExtraData}]} | unpack_entries(T)]. html(Text, Input) -> io_lib:format( |