From b5b5c0f43803a2d31fb5bf240e4b70377d367b7a Mon Sep 17 00:00:00 2001
From: Linus Nordberg <linus@nordberg.se>
Date: Sat, 27 Sep 2014 15:44:07 +0200
Subject: wip

---
 src/catlfish.erl | 151 ++++++++++++++++++++++++++++++++++++++++++++-----------
 src/v1.erl       |   2 +-
 2 files changed, 123 insertions(+), 30 deletions(-)

(limited to 'src')

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>>.
diff --git a/src/v1.erl b/src/v1.erl
index 304b0a8..b58516d 100644
--- a/src/v1.erl
+++ b/src/v1.erl
@@ -82,7 +82,7 @@
                         binary_to_list(
                           jiffy:encode(
                             case plop:inclusion(Hash, TreeSize) of
-                                {ok, Index, Path} ->
+                                {ok, {Index, Path}} ->
                                     {[{leaf_index, Index},
                                       {audit_path,
                                        [base64:encode(X) || X <- Path]}]};
-- 
cgit v1.1