summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--NEWS.md5
-rw-r--r--ebin/catlfish.app2
-rw-r--r--reltool.config2
-rw-r--r--src/catlfish_app.erl2
-rw-r--r--src/v1.erl105
6 files changed, 87 insertions, 33 deletions
diff --git a/Makefile b/Makefile
index bdfb49f..a9f4683 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/NEWS.md b/NEWS.md
index 232b2b6..51a857c 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -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),
diff --git a/src/v1.erl b/src/v1.erl
index dca1402..5f0afc6 100644
--- a/src/v1.erl
+++ b/src/v1.erl
@@ -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(