1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
%%% Copyright (c) 2014, NORDUnet A/S.
%%% See LICENSE for licensing information.
%%% @doc Certificate Transparency (RFC 6962)
-module(v1).
%% API (URL)
-export(['add-chain'/3, 'add-pre-chain'/3, 'get-sth'/3,
'get-sth-consistency'/3, 'get-proof-by-hash'/3, 'get-entries'/3,
'get-roots'/3, 'get-entry-and-proof'/3]).
%% Testing -- FIXME: remove
-export([hello/3]).
-include("$CTROOT/plop/include/plop.hrl").
-define(PROTOCOL_VERSION, 0).
%% 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).
'add-pre-chain'(SessionID, _Env, _Input) ->
niy(SessionID).
'get-sth'(SessionID, _Env, _Input) ->
#sth{
treesize = Treesize,
timestamp = Timestamp,
roothash = Roothash,
signature = Signature} = plop:sth(),
R = [{tree_size, Treesize},
{timestamp, Timestamp},
{sha256_root_hash, base64:encode(Roothash)},
{tree_head_signature, base64:encode(
plop:serialise(Signature))}],
deliver(SessionID, binary_to_list(jiffy:encode({R}))).
'get-sth-consistency'(SessionID, _Env, _Input) ->
niy(SessionID).
'get-proof-by-hash'(SessionID, _Env, _Input) ->
niy(SessionID).
'get-entries'(SessionID, _Env, Input) ->
R = case lists:sort(httpd:parse_query(Input)) of
[{"end", EndInput}, {"start", StartInput}] ->
{Start, _} = string:to_integer(StartInput),
{End, _} = string:to_integer(EndInput),
case lists:member(error, [Start, End]) of
true -> html("get-entries: bad input:", [Start, End]);
_ -> encode_entries(plop:get(Start, End))
end;
_ -> html("get-entries: bad input:", Input)
end,
deliver(SessionID, R).
'get-entry-and-proof'(SessionID, _Env, _Input) ->
niy(SessionID).
'get-roots'(SessionID, _Env, _Input) ->
R = [{certificates, []}], % NIY.
deliver(SessionID, binary_to_list(jiffy:encode({R}))).
%% For testing. FIXME: Remove.
hello(SessionID, Env, Input) ->
Query = httpd:parse_query(Input),
mod_esi:deliver(SessionID, io_lib:format(
"Content-Type: text/html\r\n\r\n" ++
"<html><body>hello again, erlang world" ++
"<p>SessionID: ~p~n" ++
"<p>Env: ~p~n" ++
"<p>Input, raw: ~p~n" ++
"<p>Input, parsed: ~p~n" ++
"</body></html>",
[SessionID, Env, Input, Query])).
%% Private functions.
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([]) ->
[].
html(Text, Input) ->
io_lib:format(
"Content-Type: text/html\r\n\r\n" ++
"<html><body><p>~n" ++
"~s~n" ++
"~p~n" ++
"</body></html>~n", [Text, Input]).
niy(S) ->
mod_esi:deliver(S, html("NIY - Not Yet Implemented|", [])).
-spec deliver(any(), string()) -> ok | {error, _Reason}.
deliver(Session, Data) ->
mod_esi:deliver(Session, Data).
|