summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/catlfish.erl117
-rw-r--r--src/x509.erl15
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))
].