From c14dfa92cd6265524700ef4d7d97c2ec12e36bb0 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Wed, 24 Sep 2014 14:27:59 +0200 Subject: Make cert chains and CtExtensions variable length (TLS) vectors. Also move some CT-specific code to new file catlfish.erl. --- src/catlfish.erl | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/v1.erl | 61 +++++--------------------------------------------------- 2 files changed, 64 insertions(+), 56 deletions(-) create mode 100644 src/catlfish.erl diff --git a/src/catlfish.erl b/src/catlfish.erl new file mode 100644 index 0000000..2ba9d58 --- /dev/null +++ b/src/catlfish.erl @@ -0,0 +1,59 @@ +%%% Copyright (c) 2014, NORDUnet A/S. +%%% See LICENSE for licensing information. + +-module(catlfish). +-export([add_chain/2, entries/2, entry_and_proof/2]). +-include("$CTROOT/plop/include/plop.hrl"). + +-define(PROTOCOL_VERSION, 0). + +-spec add_chain(binary(), list()) -> list(). +add_chain(LeafCert, CertChain) -> + Entry = #plop_entry{type = x509, data = LeafCert}, + EDVectors = [serialise_tls_vector(X, 3) || X <- CertChain], + ExtraData = serialise_tls_vector(list_to_binary(EDVectors), 3), + SPT = plop:add(#timestamped_entry{entry = Entry}, ExtraData), + 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})). + +-spec entries(non_neg_integer(), non_neg_integer()) -> list(). +entries(Start, End) -> + encode_entries(plop:get(Start, End)). + +-spec entry_and_proof(non_neg_integer(), non_neg_integer()) -> list(). +entry_and_proof(Index, TreeSize) -> + binary_to_list( + jiffy:encode( + case plop:inclusion_and_entry(Index, TreeSize) of + {ok, MTL, Extra, Path} -> + {[{leaf_input, base64:encode(plop:serialise(MTL))}, + %% Extra data is already in TLS vector format. + {extra_data, base64:encode(Extra)}, + {audit_path, [base64:encode(X) || X <- Path]}]}; + {notfound, Msg} -> + {[{success, false}, + {error_message, list_to_binary(Msg)}]} + end)). + +%% Private functions. +-spec encode_entries([{mtl(), binary()}]) -> list(). +encode_entries(Entries) -> + 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)]. + +-spec serialise_tls_vector(binary(), non_neg_integer()) -> binary(). +serialise_tls_vector(Binary, LengthLen) -> + Length = byte_size(Binary), + <>. diff --git a/src/v1.erl b/src/v1.erl index 5f0afc6..304b0a8 100644 --- a/src/v1.erl +++ b/src/v1.erl @@ -8,11 +8,8 @@ -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) -> @@ -25,16 +22,7 @@ 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})); + catlfish:add_chain(LeafCert, CertChain); Invalid -> html("add-chain: chain is not a list: ", [Invalid]) end; @@ -109,7 +97,7 @@ deliver(SessionID, R). 'get-entries'(SessionID, _Env, Input) -> - %% TODO: Limit the number of returned entreis (i.e. start-end) to + %% TODO: Limit the number of returned entries (i.e. start-end) to %% something reasonable. R = case lists:sort(httpd:parse_query(Input)) of [{"end", EndInput}, {"start", StartInput}] -> @@ -117,7 +105,7 @@ {End, _} = string:to_integer(EndInput), case lists:member(error, [Start, End]) of true -> html("get-entries: bad input:", [Start, End]); - false -> encode_entries(plop:get(Start, End)) + false -> catlfish:entries(Start, End) end; _ -> html("get-entries: bad input:", Input) end, @@ -132,20 +120,7 @@ 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)) + false -> catlfish:entry_and_proof(Index, TreeSize) end; _ -> html("get-entry-and-proof: bad input:", Input) end, @@ -155,33 +130,7 @@ 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" ++ - "hello again, erlang world" ++ - "

SessionID: ~p~n" ++ - "

Env: ~p~n" ++ - "

Input, raw: ~p~n" ++ - "

Input, parsed: ~p~n" ++ - "", - [SessionID, Env, Input, Query])). - %% Private functions. --spec encode_entries([{mtl(), binary()}]) -> list(). -encode_entries(Entries) -> - 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( "Content-Type: text/html\r\n\r\n" ++ @@ -191,7 +140,7 @@ html(Text, Input) -> "~n", [Text, Input]). niy(S) -> - mod_esi:deliver(S, html("NIY - Not Yet Implemented|", [])). + mod_esi:deliver(S, html("NIY - Not Implemented Yet|", [])). -spec deliver(any(), string()) -> ok | {error, _Reason}. deliver(Session, Data) -> -- cgit v1.1