From 1f63c850a12797c5efd2db8368ef7219ed555ce2 Mon Sep 17 00:00:00 2001 From: Magnus Ahltorp Date: Sun, 8 Mar 2015 03:27:59 +0100 Subject: Cache SCT:s --- src/catlfish.erl | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) (limited to 'src/catlfish.erl') diff --git a/src/catlfish.erl b/src/catlfish.erl index 3956eec..2fd9dc7 100644 --- a/src/catlfish.erl +++ b/src/catlfish.erl @@ -69,10 +69,30 @@ build_mtl(Timestamp, LeafCert) -> entry = TSE}, serialise(MTL). +calc_sct(TimestampedEntry) -> + plop:serialise(plop:spt(list_to_binary([<>, + serialise_signature_type(certificate_timestamp), + serialise(TimestampedEntry)]))). + +get_sct(Hash, TimestampedEntry) -> + case application:get_env(catlfish, sctcache_root_path) of + {ok, RootPath} -> + case perm:readfile(RootPath, Hash) of + Contents when is_binary(Contents) -> + Contents; + noentry -> + SCT = calc_sct(TimestampedEntry), + ok = perm:ensurefile_nosync(RootPath, Hash, SCT), + SCT + end; + _ -> + SCT = calc_sct(TimestampedEntry) + end. + -spec add_chain(binary(), [binary()]) -> nonempty_string(). add_chain(LeafCert, CertChain) -> EntryHash = crypto:hash(sha256, [LeafCert | CertChain]), - TimestampedEntry = + {TimestampedEntry, Hash} = case plop:get(EntryHash) of notfound -> Timestamp = plop:generate_timestamp(), @@ -82,28 +102,27 @@ add_chain(LeafCert, CertChain) -> MTL = #mtl{leaf_version = v1, leaf_type = timestamped_entry, entry = TSE}, + MTLHash = ht:leaf_hash(serialise(MTL)), ok = plop:add( serialise_logentry(Timestamp, LeafCert, CertChain), - ht:leaf_hash(serialise(MTL)), + MTLHash, EntryHash), - TSE; - {_Index, _MTLHash, Entry} -> + {TSE, MTLHash}; + {_Index, MTLHash, Entry} -> <> = 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} + {#timestamped_entry{timestamp = Timestamp, + entry_type = x509_entry, + signed_entry = LeafCert}, + MTLHash} end, - SCT_sig = - plop:spt(list_to_binary([<>, - serialise_signature_type(certificate_timestamp), - serialise(TimestampedEntry)])), + SCT_sig = get_sct(Hash, TimestampedEntry), {[{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))}]}. + {signature, base64:encode(SCT_sig)}]}. -spec serialise_logentry(integer(), binary(), [binary()]) -> binary(). serialise_logentry(Timestamp, LeafCert, CertChain) -> -- cgit v1.1 From d1d2185b420d873a97bc78c5e07482accaf574fc Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 19 Mar 2015 17:49:41 +0100 Subject: Add precert handling. --- src/catlfish.erl | 226 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 165 insertions(+), 61 deletions(-) (limited to 'src/catlfish.erl') diff --git a/src/catlfish.erl b/src/catlfish.erl index 2fd9dc7..3b81baa 100644 --- a/src/catlfish.erl +++ b/src/catlfish.erl @@ -2,7 +2,7 @@ %%% See LICENSE for licensing information. -module(catlfish). --export([add_chain/2, entries/2, entry_and_proof/2]). +-export([add_chain/3, entries/2, entry_and_proof/2]). -export([known_roots/0, update_known_roots/0]). -export([init_cache_table/0]). -include_lib("eunit/include/eunit.hrl"). @@ -21,53 +21,89 @@ -record(timestamped_entry, {timestamp :: integer(), entry_type :: entry_type(), - signed_entry :: binary(), + signed_entry :: signed_x509_entry() | + signed_precert_entry(), extensions = <<>> :: binary()}). -type timestamped_entry() :: #timestamped_entry{}. --spec serialise(mtl() | timestamped_entry()) -> binary(). -serialise(#timestamped_entry{timestamp = Timestamp} = E) -> - list_to_binary( - [<>, - 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)]); +-record(signed_x509_entry, {asn1_cert :: binary()}). +-type signed_x509_entry() :: #signed_x509_entry{}. +-record(signed_precert_entry, {issuer_key_hash :: binary(), + tbs_certificate :: binary()}). +-type signed_precert_entry() :: #signed_precert_entry{}. + +-spec serialise(mtl() | timestamped_entry() | + signed_x509_entry() | signed_precert_entry()) -> binary(). +%% @doc Serialise a MerkleTreeLeaf as per RFC6962 Section 3.4. serialise(#mtl{leaf_version = LeafVersion, leaf_type = LeafType, entry = TimestampedEntry}) -> list_to_binary( [serialise_leaf_version(LeafVersion), serialise_leaf_type(LeafType), - serialise(TimestampedEntry)]). + serialise(TimestampedEntry)]); +%% @doc Serialise a TimestampedEntry as per RFC6962 Section 3.4. +serialise(#timestamped_entry{timestamp = Timestamp, + entry_type = EntryType, + signed_entry = SignedEntry, + extensions = Extensions}) -> + list_to_binary( + [<>, + serialise_entry_type(EntryType), + serialise(SignedEntry), + encode_tls_vector(Extensions, 2)]); +%% @doc Serialise an ASN1.Cert as per RFC6962 Section 3.1. +serialise(#signed_x509_entry{asn1_cert = Cert}) -> + encode_tls_vector(Cert, 3); +%% @doc Serialise a PreCert as per RFC6962 Section 3.2. +serialise(#signed_precert_entry{ + issuer_key_hash = IssuerKeyHash, + tbs_certificate = TBSCertificate}) when is_binary(IssuerKeyHash), + size(IssuerKeyHash) == 32 -> + list_to_binary( + [IssuerKeyHash, + encode_tls_vector(TBSCertificate, 3)]). serialise_leaf_version(v1) -> <<0:8>>; serialise_leaf_version(v2) -> <<1:8>>. +deserialise_leaf_version(<<0:8>>) -> + v1; +deserialise_leaf_version(<<1:8>>) -> + v2. serialise_leaf_type(timestamped_entry) -> <<0:8>>. -%% serialise_leaf_type(_) -> -%% <<>>. +deserialise_leaf_type(<<0:8>>) -> + timestamped_entry. serialise_entry_type(x509_entry) -> <<0:16>>; serialise_entry_type(precert_entry) -> <<1:16>>. +deserialise_entry_type(<<0:16>>) -> + x509_entry; +deserialise_entry_type(<<1:16>>) -> + precert_entry. serialise_signature_type(certificate_timestamp) -> <<0:8>>; serialise_signature_type(tree_hash) -> <<1:8>>. +%% deserialise_signature_type(<<0:8>>) -> +%% certificate_timestamp; +%% deserialise_signature_type(<<1:8>>) -> +%% tree_hash. -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). +%% 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). calc_sct(TimestampedEntry) -> plop:serialise(plop:spt(list_to_binary([<>, @@ -86,37 +122,48 @@ get_sct(Hash, TimestampedEntry) -> SCT end; _ -> - SCT = calc_sct(TimestampedEntry) + calc_sct(TimestampedEntry) end. --spec add_chain(binary(), [binary()]) -> nonempty_string(). -add_chain(LeafCert, CertChain) -> +-spec add_chain(binary(), [binary()], normal|precert) -> nonempty_string(). +add_chain(LeafCert, CertChain, Type) -> EntryHash = crypto:hash(sha256, [LeafCert | CertChain]), + EntryType = case Type of + normal -> x509_entry; + precert -> precert_entry + end, {TimestampedEntry, Hash} = 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}, - MTLHash = ht:leaf_hash(serialise(MTL)), - ok = plop:add( - serialise_logentry(Timestamp, LeafCert, CertChain), - MTLHash, - EntryHash), + TSE = timestamped_entry(Timestamp, EntryType, LeafCert, CertChain), + MTLText = serialise(#mtl{leaf_version = v1, + leaf_type = timestamped_entry, + entry = TSE}), + MTLHash = ht:leaf_hash(MTLText), + ExtraData = + case Type of + normal -> CertChain; + precert -> [LeafCert | CertChain] + end, + LogEntry = + list_to_binary( + [encode_tls_vector(MTLText, 4), + encode_tls_vector( + encode_tls_vector( + list_to_binary( + [encode_tls_vector(C, 3) || C <- ExtraData]), + 3), + 4)]), + ok = plop:add(LogEntry, MTLHash, EntryHash), {TSE, MTLHash}; - {_Index, MTLHash, Entry} -> - <> = 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}, - MTLHash} + {_Index, MTLHash, DBEntry} -> + {MTLText, _ExtraData} = unpack_entry(DBEntry), + MTL = deserialise_mtl(MTLText), + MTLText = serialise(MTL), % verify FIXME: remove + {MTL#mtl.entry, MTLHash} end, + SCT_sig = get_sct(Hash, TimestampedEntry), {[{sct_version, ?PROTOCOL_VERSION}, {id, base64:encode(plop:get_logid())}, @@ -124,15 +171,73 @@ add_chain(LeafCert, CertChain) -> {extensions, base64:encode(<<>>)}, {signature, base64:encode(SCT_sig)}]}. --spec serialise_logentry(integer(), binary(), [binary()]) -> binary(). -serialise_logentry(Timestamp, LeafCert, CertChain) -> - list_to_binary( - [<>, - list_to_binary( - [encode_tls_vector(LeafCert, 3), - encode_tls_vector( - list_to_binary( - [encode_tls_vector(X, 3) || X <- CertChain]), 3)])]). +-spec timestamped_entry(integer(), entry_type(), binary(), binary()) -> + timestamped_entry(). +timestamped_entry(Timestamp, EntryType, LeafCert, CertChain) -> + SignedEntry = + case EntryType of + x509_entry -> + #signed_x509_entry{asn1_cert = LeafCert}; + precert_entry -> + {DetoxedLeafTBSCert, IssuerKeyHash} = + x509:detox(LeafCert, CertChain), + #signed_precert_entry{ + issuer_key_hash = IssuerKeyHash, + tbs_certificate = DetoxedLeafTBSCert} + end, + #timestamped_entry{timestamp = Timestamp, + entry_type = EntryType, + signed_entry = SignedEntry}. + +%% -spec serialise_logentry(integer(), binary(), [binary()]) -> binary(). +%% serialise_logentry(Timestamp, LeafCert, CertChain) -> +%% list_to_binary( +%% [<>, +%% list_to_binary( +%% [encode_tls_vector(LeafCert, 3), +%% encode_tls_vector( +%% list_to_binary( +%% [encode_tls_vector(X, 3) || X <- CertChain]), 3)])]). + +-spec deserialise_mtl(binary()) -> mtl(). +deserialise_mtl(Data) -> + <> = Data, + #mtl{leaf_version = deserialise_leaf_version(LeafVersionBin), + leaf_type = deserialise_leaf_type(LeafTypeBin), + entry = deserialise_timestampedentry(TimestampedEntryBin)}. + +-spec deserialise_timestampedentry(binary()) -> timestamped_entry(). +deserialise_timestampedentry(Data) -> + <> = Data, + EntryType = deserialise_entry_type(EntryTypeBin), + {SignedEntry, ExtensionsBin} = + case EntryType of + x509_entry -> + deserialise_signed_x509_entry(RestData); + precert_entry -> + deserialise_signed_precert_entry(RestData) + end, + {Extensions, <<>>} = decode_tls_vector(ExtensionsBin, 2), + #timestamped_entry{timestamp = Timestamp, + entry_type = EntryType, + signed_entry = SignedEntry, + extensions = Extensions}. + +-spec deserialise_signed_x509_entry(binary()) -> {signed_x509_entry(), binary()}. +deserialise_signed_x509_entry(Data) -> + {E, D} = decode_tls_vector(Data, 3), + {#signed_x509_entry{asn1_cert = E}, D}. + +-spec deserialise_signed_precert_entry(binary()) -> + {signed_precert_entry(), binary()}. +deserialise_signed_precert_entry(Data) -> + <> = Data, + {TBSCertificate, RestData2} = decode_tls_vector(RestData, 3), + {#signed_precert_entry{issuer_key_hash = IssuerKeyHash, + tbs_certificate = TBSCertificate}, + RestData2}. -spec entries(non_neg_integer(), non_neg_integer()) -> list(). entries(Start, End) -> @@ -142,10 +247,9 @@ entries(Start, End) -> entry_and_proof(Index, TreeSize) -> case plop:inclusion_and_entry(Index, TreeSize) of {ok, Entry, Path} -> - {Timestamp, LeafCertVector, CertChainVector} = unpack_entry(Entry), - MTL = build_mtl(Timestamp, LeafCertVector), + {MTL, ExtraData} = unpack_entry(Entry), {[{leaf_input, base64:encode(MTL)}, - {extra_data, base64:encode(CertChainVector)}, + {extra_data, base64:encode(ExtraData)}, {audit_path, [base64:encode(X) || X <- Path]}]}; {notfound, Msg} -> {[{success, false}, @@ -161,20 +265,20 @@ init_cache_table() -> ets:new(?CACHE_TABLE, [set, public, named_table]). %% Private functions. +-spec unpack_entry(binary()) -> {binary(), binary()}. unpack_entry(Entry) -> - <> = Entry, - {LeafCertVector, CertChainVector} = decode_tls_vector(LogEntry, 3), - {Timestamp, LeafCertVector, CertChainVector}. + {MTL, Rest} = decode_tls_vector(Entry, 4), + {ExtraData, <<>>} = decode_tls_vector(Rest, 4), + {MTL, ExtraData}. -spec x_entries([{non_neg_integer(), binary(), binary()}]) -> list(). x_entries([]) -> []; 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)]. + {MTL, ExtraData} = unpack_entry(Entry), + [{[{leaf_input, base64:encode(MTL)}, + {extra_data, base64:encode(ExtraData)}]} | x_entries(T)]. -spec encode_tls_vector(binary(), non_neg_integer()) -> binary(). encode_tls_vector(Binary, LengthLen) -> -- cgit v1.1 From 790cd8a7d02fc8c7ac42b4e1e8aa6a15b38a8288 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Fri, 20 Mar 2015 17:41:29 +0100 Subject: Formatting; remove debug printouts. --- src/catlfish.erl | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) (limited to 'src/catlfish.erl') diff --git a/src/catlfish.erl b/src/catlfish.erl index 3b81baa..50811a1 100644 --- a/src/catlfish.erl +++ b/src/catlfish.erl @@ -91,24 +91,12 @@ serialise_signature_type(certificate_timestamp) -> <<0:8>>; serialise_signature_type(tree_hash) -> <<1:8>>. -%% deserialise_signature_type(<<0:8>>) -> -%% certificate_timestamp; -%% deserialise_signature_type(<<1:8>>) -> -%% tree_hash. - -%% 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). calc_sct(TimestampedEntry) -> - plop:serialise(plop:spt(list_to_binary([<>, - serialise_signature_type(certificate_timestamp), - serialise(TimestampedEntry)]))). + plop:serialise( + plop:spt(list_to_binary([<>, + serialise_signature_type(certificate_timestamp), + serialise(TimestampedEntry)]))). get_sct(Hash, TimestampedEntry) -> case application:get_env(catlfish, sctcache_root_path) of -- cgit v1.1 From 3f9f38468dd9f43a7a71768dbe84dd40723c30c5 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Mon, 23 Mar 2015 15:22:15 +0100 Subject: Add spec's for most functions. NOTE: We're not dialyzer clean yet. --- src/catlfish.erl | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) (limited to 'src/catlfish.erl') diff --git a/src/catlfish.erl b/src/catlfish.erl index 50811a1..2e5ffd4 100644 --- a/src/catlfish.erl +++ b/src/catlfish.erl @@ -87,6 +87,7 @@ deserialise_entry_type(<<0:16>>) -> deserialise_entry_type(<<1:16>>) -> precert_entry. +-spec serialise_signature_type(certificate_timestamp|tree_hash) -> binary(). serialise_signature_type(certificate_timestamp) -> <<0:8>>; serialise_signature_type(tree_hash) -> @@ -177,16 +178,6 @@ timestamped_entry(Timestamp, EntryType, LeafCert, CertChain) -> entry_type = EntryType, signed_entry = SignedEntry}. -%% -spec serialise_logentry(integer(), binary(), [binary()]) -> binary(). -%% serialise_logentry(Timestamp, LeafCert, CertChain) -> -%% list_to_binary( -%% [<>, -%% list_to_binary( -%% [encode_tls_vector(LeafCert, 3), -%% encode_tls_vector( -%% list_to_binary( -%% [encode_tls_vector(X, 3) || X <- CertChain]), 3)])]). - -spec deserialise_mtl(binary()) -> mtl(). deserialise_mtl(Data) -> <