diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/catlfish.erl | 117 | ||||
-rw-r--r-- | src/x509.erl | 15 |
2 files changed, 67 insertions, 65 deletions
diff --git a/src/catlfish.erl b/src/catlfish.erl index e48f788..d940147 100644 --- a/src/catlfish.erl +++ b/src/catlfish.erl @@ -124,20 +124,7 @@ add_to_db(Type, LeafCert, CertChain, EntryHash) -> 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)]), + LogEntry = pack_entry(Type, MTLText, LeafCert, CertChain), ok = plop:add(LogEntry, MTLHash, EntryHash), {TSE, MTLHash}. @@ -157,7 +144,7 @@ add_chain(LeafCert, CertChain, Type) -> exit({internalerror, "Rate limiting"}) end; {_Index, MTLHash, DBEntry} -> - {MTLText, _ExtraData} = unpack_entry(DBEntry), + {_Type, MTLText, _Cert, _Chain} = unpack_entry(DBEntry), MTL = deserialise_mtl(MTLText), MTLText = serialise(MTL), % verify FIXME: remove {MTL#mtl.entry, MTLHash} @@ -228,6 +215,18 @@ deserialise_signed_precert_entry(Data) -> tbs_certificate = TBSCertificate}, RestData2}. +serialise_extra_data(Type, Cert, Chain) -> + EncodedChain = encode_tls_vector( + list_to_binary( + [encode_tls_vector(C, 3) || C <- Chain]), 3), + case Type of + normal -> + EncodedChain; + precert -> + list_to_binary( + [encode_tls_vector(Cert, 3), EncodedChain]) + end. + -spec entries(non_neg_integer(), non_neg_integer()) -> {[{entries, list()},...]}. entries(Start, End) -> {[{entries, x_entries(plop:get(Start, End))}]}. @@ -236,8 +235,9 @@ entries(Start, End) -> entry_and_proof(Index, TreeSize) -> case plop:inclusion_and_entry(Index, TreeSize) of {ok, Entry, Path} -> - {MTL, ExtraData} = unpack_entry(Entry), - {[{leaf_input, base64:encode(MTL)}, + {Type, MTLText, Cert, Chain} = unpack_entry(Entry), + ExtraData = serialise_extra_data(Type, Cert, Chain), + {[{leaf_input, base64:encode(MTLText)}, {extra_data, base64:encode(ExtraData)}, {audit_path, [base64:encode(X) || X <- Path]}]}; {notfound, Msg} -> @@ -253,29 +253,6 @@ init_cache_table() -> end, ets:new(?CACHE_TABLE, [set, public, named_table]). -deserialise_extra_data(<<>>) -> - []; -deserialise_extra_data(ExtraData) -> - {E, Rest} = decode_tls_vector(ExtraData, 3), - [E | deserialise_extra_data(Rest)]. - -chain_from_mtl_extradata(MTL, ExtraData) -> - TimestampedEntry = MTL#mtl.entry, - Chain = deserialise_extra_data(ExtraData), - case TimestampedEntry#timestamped_entry.entry_type of - x509_entry -> - SignedEntry = TimestampedEntry#timestamped_entry.signed_entry, - [SignedEntry#signed_x509_entry.asn1_cert | Chain]; - precert_entry -> - Chain - end. - -mtl_and_extra_from_entry(Entry) -> - {MTLText, ExtraDataPacked} = unpack_entry(Entry), - {ExtraData, <<>>} = decode_tls_vector(ExtraDataPacked, 3), - MTL = deserialise_mtl(MTLText), - {MTL, ExtraData}. - verify_mtl(MTL, LeafCert, CertChain) -> Timestamp = MTL#mtl.entry#timestamped_entry.timestamp, EntryType = MTL#mtl.entry#timestamped_entry.entry_type, @@ -293,15 +270,14 @@ verify_entry(Entry) -> RootCerts = known_roots(), verify_entry(Entry, RootCerts). -verify_entry(Entry, RootCerts) -> - {MTL, ExtraData} = mtl_and_extra_from_entry(Entry), - Chain = chain_from_mtl_extradata(MTL, ExtraData), - - case x509:normalise_chain(RootCerts, Chain) of - {ok, [LeafCert|CertChain]} -> - case verify_mtl(MTL, LeafCert, CertChain) of +%% Used from plop. +verify_entry(PackedEntry, RootCerts) -> + {_Type, MTLText, Cert, Chain} = unpack_entry(PackedEntry), + case x509:normalise_chain(RootCerts, [Cert | Chain]) of + {ok, [Cert | FullChain]} -> + case verify_mtl(deserialise_mtl(MTLText), Cert, FullChain) of ok -> - {ok, ht:leaf_hash(serialise(MTL))}; + {ok, ht:leaf_hash(MTLText)}; error -> {error, "MTL verification failed"} end; @@ -309,24 +285,51 @@ verify_entry(Entry, RootCerts) -> {error, Reason} end. -entryhash_from_entry(Entry) -> - {MTL, ExtraData} = mtl_and_extra_from_entry(Entry), - Chain = chain_from_mtl_extradata(MTL, ExtraData), - crypto:hash(sha256, Chain). +%% Used from plop. +entryhash_from_entry(PackedEntry) -> + {_Type, _MTLText, Cert, Chain} = unpack_entry(PackedEntry), + crypto:hash(sha256, [Cert | Chain]). %% Private functions. --spec unpack_entry(binary()) -> {binary(), binary()}. +-spec pack_entry(normal|precert, binary(), binary(), [binary()]) -> binary(). +pack_entry(Type, MTLText, EndEntityCert, CertChain) -> + list_to_binary( + [tlv:encode(<<"MTL1">>, MTLText), + tlv:encode(case Type of + normal -> <<"EEC1">>; + precert -> <<"PRC1">> + end, EndEntityCert), + tlv:encode(<<"CHN1">>, + list_to_binary( + [tlv:encode(<<"X509">>, E) || E <- CertChain]))]). + +-spec unpack_entry(binary()) -> {normal|precert, binary(), binary(), [binary()]}. unpack_entry(Entry) -> - {MTL, Rest} = decode_tls_vector(Entry, 4), - {ExtraData, <<>>} = decode_tls_vector(Rest, 4), - {MTL, ExtraData}. + {<<"MTL1">>, MTLText, Rest1} = tlv:decode(Entry), + {EECType, EndEntityCert, Rest2} = tlv:decode(Rest1), + Type = case EECType of + <<"EEC1">> -> + normal; + <<"PRC1">> -> + precert + end, + {<<"CHN1">>, PackedChain, _Rest3} = tlv:decode(Rest2), % Ignore rest. + Chain = unpack_certchain(PackedChain), + {Type, MTLText, EndEntityCert, Chain}. + +unpack_certchain(<<>>) -> + []; +unpack_certchain(Data) -> + {<<"X509">>, Unpacked, Rest} = tlv:decode(Data), + [Unpacked | unpack_certchain(Rest)]. -spec x_entries([{non_neg_integer(), binary(), binary()}]) -> list(). x_entries([]) -> []; x_entries([H|T]) -> {_Index, _Hash, Entry} = H, - {MTL, ExtraData} = unpack_entry(Entry), + {Type, MTL, Cert, Chain} = unpack_entry(Entry), + ExtraData = serialise_extra_data(Type, Cert, Chain), [{[{leaf_input, base64:encode(MTL)}, {extra_data, base64:encode(ExtraData)}]} | x_entries(T)]. diff --git a/src/x509.erl b/src/x509.erl index 7bbfb8e..279d9b9 100644 --- a/src/x509.erl +++ b/src/x509.erl @@ -71,10 +71,9 @@ detox(LeafDer, ChainDer) -> %% argument is irrelevant. %% %% Return {false, Reason} or {true, ListWithRoot}. Note that -%% ListWithRoot is the empty list when the root of the chain is found -%% amongst the acceptable root certs. Otherwise it contains exactly -%% one element, a CA cert from the acceptable root certs signing the -%% root of the chain. +%% ListWithRoot allways contain exactly one element -- a CA cert from +%% first argument (AcceptableRootCerts) signing the root of the +%% chain. FIXME: Any point in returning this as a list? normalise_chain(_, _, MaxChainLength) when MaxChainLength =< 0 -> %% Chain too long. {false, chain_too_long}; @@ -83,7 +82,7 @@ normalise_chain(AcceptableRootCerts, [TopCert], MaxChainLength) -> case lists:member(TopCert, AcceptableRootCerts) of true -> %% Top cert is part of chain. - {true, []}; + {true, [TopCert]}; false when MaxChainLength =< 1 -> %% Chain too long. {false, chain_too_long}; @@ -485,14 +484,14 @@ chain_test(C0, C1) -> %% Chain too long. ?_assertMatch({false, chain_too_long}, normalise_chain([C1], [C0], 1)), %% Root in chain and in trust store. - ?_assertEqual({true, []}, normalise_chain([C1], [C0, C1], 2)), + ?_assertEqual({true, [C1]}, normalise_chain([C1], [C0, C1], 2)), %% Chain too long. ?_assertMatch({false, chain_too_long}, normalise_chain([C1], [C0, C1], 1)), %% Root not in trust store. ?_assertMatch({false, root_unknown}, normalise_chain([], [C0, C1], 10)), %% Selfsigned. Actually OK. - ?_assertMatch({true, []}, normalise_chain([C0], [C0], 10)), - ?_assertMatch({true, []}, normalise_chain([C0], [C0], 1)), + ?_assertMatch({true, [C0]}, normalise_chain([C0], [C0], 10)), + ?_assertMatch({true, [C0]}, normalise_chain([C0], [C0], 1)), %% Max chain length 0 is not OK. ?_assertMatch({false, chain_too_long}, normalise_chain([C0], [C0], 0)) ]. |