summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2014-10-08 15:41:38 +0200
committerLinus Nordberg <linus@nordberg.se>2014-10-08 15:41:38 +0200
commite3fd2e3fc202ce18b90ac4ca96ac8264bed79569 (patch)
tree1c9736ab4def74aed1dd2c0975e96accf8f0e54c
parent645885d134798ff091769c0b69bac49f8f50b8a2 (diff)
parentb0766332d2be4272a8d8065de684c61f1ad15c4d (diff)
Merge branch 'plop-if-cleanup2' into origin-master
-rw-r--r--reltool.config6
-rw-r--r--src/catlfish.erl163
2 files changed, 137 insertions, 32 deletions
diff --git a/reltool.config b/reltool.config
index 434345c..80b374d 100644
--- a/reltool.config
+++ b/reltool.config
@@ -1,6 +1,5 @@
%% -*- mode: erlang -*-
{sys, [
- {lib_dirs, [".."]},
{erts, [{mod_cond, derived}, {app_file, strip}]},
{app_file, strip},
{rel, "catlfish", "0.2.0-dev",
@@ -12,6 +11,7 @@
]},
{boot_rel, "catlfish"},
{excl_archive_filters, ["^include$","^priv$","^\\.git$"]},
- {app, catlfish, [{app_file, all}]}
-%% {app, plop, [{mod_cond, app}, {incl_cond, include}, {lib_dir, "../plop"}]}
+ {app, catlfish, [{app_file, all}, {lib_dir, "."}]},
+ {app, plop, [{app_file, all}, {lib_dir, "../plop"}]},
+ {app, jiffy, [{app_file, all}, {lib_dir, "../jiffy"}]}
]}.
diff --git a/src/catlfish.erl b/src/catlfish.erl
index 2ba9d58..bd3c106 100644
--- a/src/catlfish.erl
+++ b/src/catlfish.erl
@@ -7,32 +7,129 @@
-define(PROTOCOL_VERSION, 0).
--spec add_chain(binary(), list()) -> list().
+%%-type signature_type() :: certificate_timestamp | tree_hash | test. % uint8
+-type entry_type() :: x509_entry | precert_entry | test. % uint16
+-type leaf_type() :: timestamped_entry | test. % uint8
+-type leaf_version() :: v1 | v2. % uint8
+
+-record(mtl, {leaf_version :: leaf_version(),
+ leaf_type :: leaf_type(),
+ entry :: timestamped_entry()}).
+-type mtl() :: #mtl{}.
+
+-record(timestamped_entry, {timestamp :: integer(),
+ entry_type :: entry_type(),
+ signed_entry :: binary(),
+ extensions = <<>> :: binary()}).
+-type timestamped_entry() :: #timestamped_entry{}.
+
+-spec serialise(mtl() | timestamped_entry()) -> binary().
+serialise(#timestamped_entry{timestamp = Timestamp} = E) ->
+ list_to_binary(
+ [<<Timestamp:64>>,
+ serialise_entry_type(E#timestamped_entry.entry_type),
+ encode_tls_vector(E#timestamped_entry.signed_entry, 3),
+ encode_tls_vector(E#timestamped_entry.extensions, 2)]);
+serialise(#mtl{leaf_version = LeafVersion,
+ leaf_type = LeafType,
+ entry = TimestampedEntry}) ->
+ list_to_binary(
+ [serialise_leaf_version(LeafVersion),
+ serialise_leaf_type(LeafType),
+ serialise(TimestampedEntry)]).
+
+serialise_leaf_version(v1) ->
+ <<0:8>>;
+serialise_leaf_version(v2) ->
+ <<1:8>>.
+
+serialise_leaf_type(timestamped_entry) ->
+ <<0:8>>.
+%% serialise_leaf_type(_) ->
+%% <<>>.
+
+serialise_entry_type(x509_entry) ->
+ <<0:16>>;
+serialise_entry_type(precert_entry) ->
+ <<1:16>>.
+
+serialise_signature_type(certificate_timestamp) ->
+ <<0:8>>;
+serialise_signature_type(tree_hash) ->
+ <<1:8>>.
+
+build_mtl(Timestamp, LeafCert) ->
+ TSE = #timestamped_entry{timestamp = Timestamp,
+ entry_type = x509_entry,
+ signed_entry = LeafCert},
+ MTL = #mtl{leaf_version = v1,
+ leaf_type = timestamped_entry,
+ entry = TSE},
+ serialise(MTL).
+
+-spec add_chain(binary(), [binary()]) -> nonempty_string().
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})).
+ EntryHash = crypto:hash(sha256, LeafCert),
+ TimestampedEntry =
+ case plop:get(EntryHash) of
+ notfound ->
+ Timestamp = plop:generate_timestamp(),
+ TSE = #timestamped_entry{timestamp = Timestamp,
+ entry_type = x509_entry,
+ signed_entry = LeafCert},
+ MTL = #mtl{leaf_version = v1,
+ leaf_type = timestamped_entry,
+ entry = TSE},
+ ok = plop:add(
+ serialise_logentry(Timestamp, LeafCert, CertChain),
+ ht:leaf_hash(serialise(MTL)),
+ crypto:hash(sha256, LeafCert)),
+ TSE;
+ {_Index, _MTLHash, Entry} ->
+ <<Timestamp:64, _LogEntry/binary>> = Entry,
+ %% TODO: Perform a costly db consistency check against
+ %% unpacked LogEntry (w/ LeafCert and CertChain)
+ #timestamped_entry{timestamp = Timestamp,
+ entry_type = x509_entry,
+ signed_entry = LeafCert}
+ end,
+ SCT_sig =
+ plop:spt(list_to_binary([<<?PROTOCOL_VERSION:8>>,
+ serialise_signature_type(certificate_timestamp),
+ serialise(TimestampedEntry)])),
+ binary_to_list(
+ jiffy:encode(
+ {[{sct_version, ?PROTOCOL_VERSION},
+ {id, base64:encode(plop:get_logid())},
+ {timestamp, TimestampedEntry#timestamped_entry.timestamp},
+ {extensions, base64:encode(<<>>)},
+ {signature, base64:encode(plop:serialise(SCT_sig))}]})).
+
+-spec serialise_logentry(integer(), binary(), [binary()]) -> binary().
+serialise_logentry(Timestamp, LeafCert, CertChain) ->
+ list_to_binary(
+ [<<Timestamp:64>>,
+ list_to_binary(
+ [encode_tls_vector(LeafCert, 3),
+ encode_tls_vector(
+ list_to_binary(
+ [encode_tls_vector(X, 3) || X <- CertChain]), 3)])]).
-spec entries(non_neg_integer(), non_neg_integer()) -> list().
entries(Start, End) ->
- encode_entries(plop:get(Start, End)).
+ binary_to_list(
+ jiffy:encode({[{entries, x_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)},
+ {ok, {Entry, Path}} ->
+ {Timestamp, LeafCertVector, CertChainVector} = unpack_entry(Entry),
+ MTL = build_mtl(Timestamp, LeafCertVector),
+ {[{leaf_input, base64:encode(MTL)},
+ {extra_data, base64:encode(CertChainVector)},
{audit_path, [base64:encode(X) || X <- Path]}]};
{notfound, Msg} ->
{[{success, false},
@@ -40,20 +137,28 @@ entry_and_proof(Index, TreeSize) ->
end)).
%% Private functions.
--spec encode_entries([{mtl(), binary()}]) -> list().
-encode_entries(Entries) ->
- binary_to_list(jiffy:encode({[{entries, unpack_entries(Entries)}]})).
+unpack_entry(Entry) ->
+ <<Timestamp:64, LogEntry/binary>> = Entry,
+ {LeafCertVector, CertChainVector} = decode_tls_vector(LogEntry, 3),
+ {Timestamp, LeafCertVector, CertChainVector}.
--spec unpack_entries([{mtl(), binary()}]) -> list().
-unpack_entries([]) ->
+-spec x_entries([{non_neg_integer(), binary(), binary()}]) -> list().
+x_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) ->
+x_entries([H|T]) ->
+ [_Index, _Hash, Entry] = H,
+ {Timestamp, LeafCertVector, CertChainVector} = unpack_entry(Entry),
+ MTL = build_mtl(Timestamp, LeafCertVector),
+ [{[{leaf_input, base64:encode(MTL)}, {extra_data, base64:encode(CertChainVector)}]} |
+ x_entries(T)].
+
+-spec encode_tls_vector(binary(), non_neg_integer()) -> binary().
+encode_tls_vector(Binary, LengthLen) ->
Length = byte_size(Binary),
<<Length:LengthLen/integer-unit:8, Binary/binary>>.
+
+-spec decode_tls_vector(binary(), non_neg_integer()) -> {binary(), binary()}.
+decode_tls_vector(Binary, LengthLen) ->
+ <<Length:LengthLen/integer-unit:8, Rest/binary>> = Binary,
+ <<ExtractedBinary:Length/binary-unit:8, Rest2/binary>> = Rest,
+ {ExtractedBinary, Rest2}.