diff options
Diffstat (limited to 'src/catlfish.erl')
-rw-r--r-- | src/catlfish.erl | 151 |
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>>. |