diff options
Diffstat (limited to 'lib/ssl/src/ssl_certificate.erl')
-rw-r--r-- | lib/ssl/src/ssl_certificate.erl | 119 |
1 files changed, 84 insertions, 35 deletions
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index eef6aa875a..2e2b43f564 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -80,7 +80,9 @@ public_key_type/1, foldl_db/3, find_cross_sign_root_paths/4, - handle_cert_auths/4 + handle_cert_auths/4, + available_cert_key_pairs/1, + available_cert_key_pairs/2 ]). %%==================================================================== @@ -307,25 +309,60 @@ find_cross_sign_root_paths([_ | Rest] = Path, CertDbHandle, CertDbRef, Invalidat end. handle_cert_auths(Chain, [], _, _) -> - %% If we have no authorities extension to check we just accept - %% first choice + %% If we have no authorities extension (or corresponding + %% 'certificate_authorities' in the certificate request message in + %% TLS-1.2 is empty) to check we just accept first choice. {ok, Chain}; handle_cert_auths([Cert], CertAuths, CertDbHandle, CertDbRef) -> - {ok, {_, [Cert | _] = EChain}, {_, [_ | DCerts]}} = certificate_chain(Cert, CertDbHandle, CertDbRef, [], both), - case cert_auth_member(cert_subjects(DCerts), CertAuths) of - true -> - {ok, EChain}; - false -> - {error, EChain, not_in_auth_domain} + case certificate_chain(Cert, CertDbHandle, CertDbRef, [], both) of + {ok, {_, [Cert | _] = EChain}, {_, [_ | DCerts]}} -> + case cert_auth_member(cert_issuers(DCerts), CertAuths) of + true -> + {ok, EChain}; + false -> + {error, EChain, not_in_auth_domain} + end; + _ -> + {ok, [Cert]} end; handle_cert_auths([_ | Certs] = EChain, CertAuths, _, _) -> - case cert_auth_member(cert_subjects(Certs), CertAuths) of + case cert_auth_member(cert_issuers(Certs), CertAuths) of true -> {ok, EChain}; false -> {error, EChain, not_in_auth_domain} end. +available_cert_key_pairs(CertKeyGroups) -> + %% To be able to find possible TLS session pre TLS-1.3 + %% that may be reused. At this point the version is + %% not negotiated. + RevAlgos = [dsa, rsa, rsa_pss_pss, ecdsa], + cert_key_group_to_list(RevAlgos, CertKeyGroups, []). + +%% Create the prioritized list of cert key pairs that +%% are availble for use in the negotiated version +available_cert_key_pairs(CertKeyGroups, {3, 4}) -> + RevAlgos = [rsa, rsa_pss_pss, ecdsa, eddsa], + cert_key_group_to_list(RevAlgos, CertKeyGroups, []); +available_cert_key_pairs(CertKeyGroups, {3, 3}) -> + RevAlgos = [dsa, rsa, rsa_pss_pss, ecdsa], + cert_key_group_to_list(RevAlgos, CertKeyGroups, []); +available_cert_key_pairs(CertKeyGroups, {3, N}) when N < 3-> + RevAlgos = [dsa, rsa, ecdsa], + cert_key_group_to_list(RevAlgos, CertKeyGroups, []). + +cert_key_group_to_list([], _, Acc) -> + final_group_list(Acc); +cert_key_group_to_list([Algo| Rest], CertKeyGroups, Acc) -> + CertKeyPairs = maps:get(Algo, CertKeyGroups, []), + cert_key_group_to_list(Rest, CertKeyGroups, CertKeyPairs ++ Acc). + +final_group_list([]) -> + [#{certs => [[]], private_key => #{}}]; +final_group_list(List) -> + List. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -411,7 +448,7 @@ find_alternative_root([Cert | _], CertDbHandle, CertDbRef, InvalidatedList) -> end. find_issuer(#cert{der=DerCert, otp=OtpCert}, CertDbHandle, CertsDbRef, ListDb, InvalidatedList) -> - IsIssuerFun = + IsIssuerFun = fun({_Key, #cert{otp=ErlCertCandidate}}, Acc) -> case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of true -> @@ -431,12 +468,15 @@ find_issuer(#cert{der=DerCert, otp=OtpCert}, CertDbHandle, CertsDbRef, ListDb, I end, Result = case is_reference(CertsDbRef) of - true -> - do_find_issuer(IsIssuerFun, CertDbHandle, ListDb); - false -> + true when ListDb == [] -> + CertEntryList = ssl_pkix_db:select_certentries_by_ref(CertsDbRef, CertDbHandle), + do_find_issuer(IsIssuerFun, CertDbHandle, CertEntryList); + false when ListDb == [] -> {extracted, CertsData} = CertsDbRef, - DB = [Entry || {decoded, Entry} <- CertsData], - do_find_issuer(IsIssuerFun, CertDbHandle, DB) + CertEntryList = [Entry || {decoded, Entry} <- CertsData], + do_find_issuer(IsIssuerFun, CertDbHandle, CertEntryList); + _ -> + do_find_issuer(IsIssuerFun, CertDbHandle, ListDb) end, case Result of issuer_not_found -> @@ -445,6 +485,7 @@ find_issuer(#cert{der=DerCert, otp=OtpCert}, CertDbHandle, CertsDbRef, ListDb, I Result end. + do_find_issuer(IssuerFun, CertDbHandle, CertDb) -> try foldl_db(IssuerFun, CertDbHandle, CertDb) @@ -667,18 +708,18 @@ maybe_shorten_path(Path, PartialChainHandler, Default) -> DerCerts = [Der || #cert{der=Der} <- Path], try PartialChainHandler(DerCerts) of {trusted_ca, Root} -> - new_trusteded_path(Root, Path, Default); + new_trusted_path(Root, Path, Default); unknown_ca -> Default catch _:_ -> Default end. -new_trusteded_path(DerCert, [#cert{der=DerCert}=Cert | Chain], _) -> - {Cert, Chain}; -new_trusteded_path(DerCert, [_ | Rest], Default) -> - new_trusteded_path(DerCert, Rest, Default); -new_trusteded_path(_, [], Default) -> +new_trusted_path(DerCert, [#cert{der=DerCert}=Cert | Path], _) -> + {Cert, Path}; +new_trusted_path(DerCert, [_ | Rest], Default) -> + new_trusted_path(DerCert, Rest, Default); +new_trusted_path(_, [], Default) -> %% User did not pick a cert present %% in the cert chain so ignore Default. @@ -758,21 +799,29 @@ subject(Cert) -> {_Serial,Subject} = public_key:pkix_subject_id(Cert), Subject. -cert_subjects([], Acc) -> - Acc; -cert_subjects([Cert | Rest], Acc) -> - cert_subjects(Rest, [subject(Cert) | Acc]). - -cert_subjects(OTPCerts) -> - cert_subjects(OTPCerts, []). +issuer(Cert) -> + case public_key:pkix_is_self_signed(Cert) of + true -> + subject(Cert); + false -> + case is_binary(Cert) of + true -> + #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), + public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.issuer); + false -> + #'OTPCertificate'{tbsCertificate = TBSCert} = Cert, + public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.issuer) + end + end. -decode_cert_auths(<<>>, Acc) -> +cert_issuers([], Acc) -> Acc; -decode_cert_auths(<<?UINT16(Len), Auth:Len/binary, Rest/binary>>, Acc) -> - NormAut = public_key:pkix_normalize_name(Auth), - decode_cert_auths(Rest, [NormAut | Acc]). +cert_issuers([Cert | Rest], Acc) -> + cert_issuers(Rest, [issuer(Cert) | Acc]). + +cert_issuers(OTPCerts) -> + cert_issuers(OTPCerts, []). -cert_auth_member(ChainSubjects, EncCertAuths) -> - CertAuths = decode_cert_auths(EncCertAuths, []), +cert_auth_member(ChainSubjects, CertAuths) -> CommonAuthorities = sets:intersection(sets:from_list(ChainSubjects), sets:from_list(CertAuths)), not sets:is_empty(CommonAuthorities). |