diff options
Diffstat (limited to 'lib/public_key/src/public_key.erl')
-rw-r--r-- | lib/public_key/src/public_key.erl | 177 |
1 files changed, 152 insertions, 25 deletions
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index d5df53e848..fa999c5ab9 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -42,7 +42,8 @@ pkix_issuer_id/2, pkix_normalize_name/1, pkix_path_validation/3, - ssh_decode/2, ssh_encode/2 + ssh_decode/2, ssh_encode/2, + pkix_crls_validate/3 ]). -type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' @@ -428,9 +429,9 @@ pkix_verify(DerCert, #'RSAPublicKey'{} = RSAKey) verify(PlainText, DigestType, Signature, RSAKey). %%-------------------------------------------------------------------- --spec pkix_is_issuer(Cert::binary()| #'OTPCertificate'{}, - IssuerCert::binary()| - #'OTPCertificate'{}) -> boolean(). +-spec pkix_is_issuer(Cert :: der_encoded()| #'OTPCertificate'{} | #'CertificateList'{}, + IssuerCert :: der_encoded()| + #'OTPCertificate'{}) -> boolean(). %% %% Description: Checks if <IssuerCert> issued <Cert>. %%-------------------------------------------------------------------- @@ -443,7 +444,11 @@ pkix_is_issuer(Cert, IssuerCert) when is_binary(IssuerCert) -> pkix_is_issuer(#'OTPCertificate'{tbsCertificate = TBSCert}, #'OTPCertificate'{tbsCertificate = Candidate}) -> pubkey_cert:is_issuer(TBSCert#'OTPTBSCertificate'.issuer, - Candidate#'OTPTBSCertificate'.subject). + Candidate#'OTPTBSCertificate'.subject); +pkix_is_issuer(#'CertificateList'{tbsCertList = TBSCRL}, + #'OTPCertificate'{tbsCertificate = Candidate}) -> + pubkey_cert:is_issuer(Candidate#'OTPTBSCertificate'.subject, + pubkey_cert_records:transform(TBSCRL#'TBSCertList'.issuer, decode)). %%-------------------------------------------------------------------- -spec pkix_is_self_signed(Cert::binary()| #'OTPCertificate'{}) -> boolean(). @@ -511,7 +516,7 @@ pkix_normalize_name(Issuer) -> pkix_path_validation(PathErr, [Cert | Chain], Options0) when is_atom(PathErr)-> {VerifyFun, Userstat0} = proplists:get_value(verify_fun, Options0, ?DEFAULT_VERIFYFUN), - Otpcert = pkix_decode_cert(Cert, otp), + Otpcert = otp_cert(Cert), Reason = {bad_cert, PathErr}, try VerifyFun(Otpcert, Reason, Userstat0) of {valid, Userstate} -> @@ -537,6 +542,20 @@ pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options) Options), path_validation(CertChain, ValidationState). + +pkix_crls_validate(OtpCert, [{_,_,_} |_] = DPAndCRLs, Options) -> + pkix_crls_validate(OtpCert, DPAndCRLs, DPAndCRLs, + Options, pubkey_crl:init_revokation_state()); + +pkix_crls_validate(OtpCert, DPAndCRLs0, Options) -> + CallBack = proplists:get_value(update_crl, Options, fun(_, CurrCRL) -> + CurrCRL + end), + DPAndCRLs = sort_dp_crls(DPAndCRLs0, CallBack), + pkix_crls_validate(OtpCert, DPAndCRLs, DPAndCRLs, + Options, pubkey_crl:init_revokation_state()). + + %%-------------------------------------------------------------------- -spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}]. %% @@ -611,12 +630,13 @@ path_validation([DerCert | Rest], ValidationState = #path_validation_state{ {error, Reason} end; -path_validation([DerCert | _] = Path, +path_validation([Cert | _] = Path, #path_validation_state{user_state = UserState0, verify_fun = VerifyFun} = ValidationState) -> Reason = {bad_cert, max_path_length_reached}, - OtpCert = pkix_decode_cert(DerCert, otp), + OtpCert = otp_cert(Cert), + try VerifyFun(OtpCert, Reason, UserState0) of {valid, UserState} -> path_validation(Path, @@ -630,7 +650,7 @@ path_validation([DerCert | _] = Path, {error, Reason} end. -validate(DerCert, #path_validation_state{working_issuer_name = Issuer, +validate(Cert, #path_validation_state{working_issuer_name = Issuer, working_public_key = Key, working_public_key_parameters = KeyParams, @@ -641,31 +661,31 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer, verify_fun = VerifyFun} = ValidationState0) -> - OtpCert = pkix_decode_cert(DerCert, otp), + OtpCert = otp_cert(Cert), - UserState1 = pubkey_cert:validate_time(OtpCert, UserState0, VerifyFun), + {ValidationState1, UserState1} = + pubkey_cert:validate_extensions(OtpCert, ValidationState0, UserState0, + VerifyFun), - UserState2 = pubkey_cert:validate_issuer(OtpCert, Issuer, UserState1, VerifyFun), + %% We want the key_usage extension to be checked before we validate + %% other things so that CRL validation errors will comply to standard + %% test suite description - UserState3 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last, - UserState2,VerifyFun), + UserState2 = pubkey_cert:validate_time(OtpCert, UserState1, VerifyFun), - UserState4 = pubkey_cert:validate_revoked_status(OtpCert, UserState3, VerifyFun), - - {ValidationState1, UserState5} = - pubkey_cert:validate_extensions(OtpCert, ValidationState0, UserState4, - VerifyFun), + UserState3 = pubkey_cert:validate_issuer(OtpCert, Issuer, UserState2, VerifyFun), - %% We want the key_usage extension to be checked before we validate - %% the signature. - UserState6 = pubkey_cert:validate_signature(OtpCert, DerCert, - Key, KeyParams, UserState5, VerifyFun), + UserState4 = pubkey_cert:validate_names(OtpCert, Permit, Exclude, Last, + UserState3, VerifyFun), + + UserState5 = pubkey_cert:validate_signature(OtpCert, der_cert(Cert), + Key, KeyParams, UserState4, VerifyFun), UserState = case Last of false -> - pubkey_cert:verify_fun(OtpCert, valid, UserState6, VerifyFun); + pubkey_cert:verify_fun(OtpCert, valid, UserState5, VerifyFun); true -> pubkey_cert:verify_fun(OtpCert, valid_peer, - UserState6, VerifyFun) + UserState5, VerifyFun) end, ValidationState = @@ -676,3 +696,110 @@ validate(DerCert, #path_validation_state{working_issuer_name = Issuer, sized_binary(Binary) -> Size = size(Binary), <<?UINT32(Size), Binary/binary>>. + +otp_cert(Der) when is_binary(Der) -> + pkix_decode_cert(Der, otp); +otp_cert(#'OTPCertificate'{} =Cert) -> + Cert. + +der_cert(#'OTPCertificate'{} = Cert) -> + pkix_encode('OTPCertificate', Cert, otp); +der_cert(Der) when is_binary(Der) -> + Der. + +pkix_crls_validate(_, [],_, _, _) -> + {bad_cert, revocation_status_undetermined}; +pkix_crls_validate(OtpCert, [{DP, CRL, DeltaCRL} | Rest], All, Options, RevokedState0) -> + CallBack = proplists:get_value(update_crl, Options, fun(_, CurrCRL) -> + CurrCRL + end), + case pubkey_crl:fresh_crl(DP, CRL, CallBack) of + {fresh, CRL} -> + do_pkix_crls_validate(OtpCert, [{DP, CRL, DeltaCRL} | Rest], + All, Options, RevokedState0); + {fresh, NewCRL} -> + NewAll = [{DP, NewCRL, DeltaCRL} | All -- [{DP, CRL, DeltaCRL}]], + do_pkix_crls_validate(OtpCert, [{DP, NewCRL, DeltaCRL} | Rest], + NewAll, Options, RevokedState0); + no_fresh_crl -> + pkix_crls_validate(OtpCert, Rest, All, Options, RevokedState0) + end. + +do_pkix_crls_validate(OtpCert, [{DP, CRL, DeltaCRL} | Rest], All, Options, RevokedState0) -> + OtherDPCRLs = All -- [{DP, CRL, DeltaCRL}], + case pubkey_crl:validate(OtpCert, OtherDPCRLs, DP, CRL, DeltaCRL, Options, RevokedState0) of + {undetermined, _, _} when Rest == []-> + {bad_cert, revocation_status_undetermined}; + {undetermined, _, RevokedState} when Rest =/= []-> + pkix_crls_validate(OtpCert, Rest, All, Options, RevokedState); + {finished, unrevoked} -> + valid; + {finished, Status} -> + {bad_cert, Status} + end. + +sort_dp_crls(DpsAndCrls, FreshCB) -> + Sorted = do_sort_dp_crls(DpsAndCrls, dict:new()), + sort_crls(Sorted, FreshCB, []). + +do_sort_dp_crls([], Dict) -> + dict:to_list(Dict); +do_sort_dp_crls([{DP, CRL} | Rest], Dict0) -> + Dict = try dict:fetch(DP, Dict0) of + _ -> + dict:append(DP, CRL, Dict0) + catch _:_ -> + dict:store(DP, [CRL], Dict0) + end, + do_sort_dp_crls(Rest, Dict). + +sort_crls([], _, Acc) -> + Acc; + +sort_crls([{DP, AllCRLs} | Rest], FreshCB, Acc)-> + {DeltaCRLs, CRLs} = do_sort_crls(AllCRLs), + DpsAndCRLs = combine(CRLs, DeltaCRLs, DP, FreshCB, []), + sort_crls(Rest, FreshCB, DpsAndCRLs ++ Acc). + +do_sort_crls(CRLs) -> + lists:partition(fun({_, CRL}) -> + pubkey_crl:is_delta_crl(CRL) + end, CRLs). + +combine([], _,_,_,Acc) -> + Acc; +combine([{_, CRL} = Entry | CRLs], DeltaCRLs, DP, FreshCB, Acc) -> + DeltaCRL = combine(CRL, DeltaCRLs), + case pubkey_crl:fresh_crl(DP, DeltaCRL, FreshCB) of + no_fresh_crl -> + combine(CRLs, DeltaCRLs, DP, FreshCB, [{DP, Entry, {undefined, undefined}} | Acc]); + {fresh, NewDeltaCRL} -> + combine(CRLs, DeltaCRLs, DP, FreshCB, [{DP, Entry, NewDeltaCRL} | Acc]) + end. + +combine(CRL, DeltaCRLs) -> + Deltas = lists:filter(fun({_,DeltaCRL}) -> + pubkey_crl:combines(CRL, DeltaCRL) + end, DeltaCRLs), + case Deltas of + [] -> + {undefined, undefined}; + [Delta] -> + Delta; + [_,_|_] -> + Fun = + fun({_, #'CertificateList'{tbsCertList = FirstTBSCRL}} = CRL1, + {_, #'CertificateList'{tbsCertList = SecondTBSCRL}} = CRL2) -> + Time1 = pubkey_cert:time_str_2_gregorian_sec( + FirstTBSCRL#'TBSCertList'.thisUpdate), + Time2 = pubkey_cert:time_str_2_gregorian_sec( + SecondTBSCRL#'TBSCertList'.thisUpdate), + case Time1 > Time2 of + true -> + CRL1; + false -> + CRL2 + end + end, + lists:foldl(Fun, hd(Deltas), tl(Deltas)) + end. |