summaryrefslogtreecommitdiff
path: root/src/catlfish.erl
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordberg.se>2014-09-27 15:44:07 +0200
committerLinus Nordberg <linus@nordberg.se>2014-09-27 15:44:07 +0200
commitb5b5c0f43803a2d31fb5bf240e4b70377d367b7a (patch)
tree1d0e9bfeb7772ba7b908ee8512990e20b426b151 /src/catlfish.erl
parent236dab6aa2481aa025a0afbe918e36764d36495b (diff)
wip
Diffstat (limited to 'src/catlfish.erl')
-rw-r--r--src/catlfish.erl151
1 files changed, 122 insertions, 29 deletions
diff --git a/src/catlfish.erl b/src/catlfish.erl
index 2ba9d58..b6856b8 100644
--- a/src/catlfish.erl
+++ b/src/catlfish.erl
@@ -7,32 +7,119 @@
-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>>.
+
+-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, Entry} ->
+ <<Timestamp:64, _LogEntry>> = 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: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}} ->
+ {LeafCertVector, CertChainVector} = unpack_entry(Entry),
+ {[{leaf_input, base64:encode(LeafCertVector)},
+ {extra_data, base64:encode(CertChainVector)},
{audit_path, [base64:encode(X) || X <- Path]}]};
{notfound, Msg} ->
{[{success, false},
@@ -40,20 +127,26 @@ 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) ->
+ %% FIXME: Do this with some beatiful binary matching.
+ LeafCertVectorLen = binary:decode_unsigned(binary_part(Entry, 0, 3)),
+ LeafCertVector = binary_part(Entry, 3, LeafCertVectorLen),
+ CertChainVectorPos = 3 + LeafCertVectorLen,
+ CertChainVector = binary_part(
+ Entry, CertChainVectorPos,
+ byte_size(Entry) - CertChainVectorPos),
+ {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,
+ {LeafCertVector, CertChainVector} = unpack_entry(Entry),
+ [{[{leaf_input, LeafCertVector}, {extra_data, 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>>.