1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2018. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(pubkey_crl). 22 23-include("public_key.hrl"). 24 25-export([validate/7, init_revokation_state/0, fresh_crl/3, verify_crl_signature/4, 26 is_delta_crl/1, combines/2, match_one/2]). 27 28-record(userstate, {dpcrls, 29 idp 30 }). 31 32validate(OtpCert, OtherDPCRLs, DP, {DerCRL, CRL}, {DerDeltaCRL, DeltaCRL}, 33 Options, RevokedState0) -> 34 RevokedState = 35 case verify_crl(OtpCert, DP, CRL, DerCRL, DeltaCRL, 36 DerDeltaCRL, OtherDPCRLs, Options, RevokedState0) of 37 {valid, Revoked, DeltaRevoked, RevokedState1, IDP} -> 38 TBSCert = OtpCert#'OTPCertificate'.tbsCertificate, 39 SerialNumber = TBSCert#'OTPTBSCertificate'.serialNumber, 40 CertIssuer = TBSCert#'OTPTBSCertificate'.issuer, 41 TBSCRL = CRL#'CertificateList'.tbsCertList, 42 CRLIssuer = TBSCRL#'TBSCertList'.issuer, 43 AltNames = case pubkey_cert:select_extension(?'id-ce-subjectAltName', 44 TBSCert#'OTPTBSCertificate'.extensions) of 45 #'Extension'{extnValue = Value} -> 46 Value; 47 _ -> 48 [] 49 end, 50 revoked_status(DP, IDP, {directoryName, CRLIssuer}, 51 [ {directoryName, CertIssuer} | AltNames], SerialNumber, Revoked, 52 DeltaRevoked, RevokedState1); 53 {invalid, Revoked} -> 54 Revoked 55 end, 56 crl_status(RevokedState). 57 58init_revokation_state() -> 59 #revoke_state{reasons_mask = sets:new(), 60 interim_reasons_mask = sets:new(), 61 cert_status = unrevoked, 62 details = []}. 63 64fresh_crl(_, {undefined, undefined}, _) -> 65 %% Typically happens when there is no delta CRL that covers a CRL 66 no_fresh_crl; 67 68fresh_crl(DP, {_, #'CertificateList'{tbsCertList = TBSCRL}} = CRL, CallBack) -> 69 Now = calendar:datetime_to_gregorian_seconds(calendar:universal_time()), 70 UpdateTime = 71 pubkey_cert:time_str_2_gregorian_sec(TBSCRL#'TBSCertList'.nextUpdate), 72 case Now >= UpdateTime of 73 true -> 74 case CallBack(DP, CRL) of 75 CRL -> 76 no_fresh_crl; 77 NewCRL -> 78 fresh_crl(DP, NewCRL, CallBack) 79 end; 80 false -> 81 {fresh, CRL} 82 end. 83 84is_delta_crl(#'CertificateList'{tbsCertList = TBSCRL}) -> 85 Extensions = TBSCRL#'TBSCertList'.crlExtensions, 86 case pubkey_cert:select_extension(?'id-ce-deltaCRLIndicator', 87 Extensions) of 88 undefined -> 89 false; 90 _ -> 91 true 92 end. 93 94combines(CRL, DeltaCRL) -> 95 check_crl_num(CRL, DeltaCRL) andalso 96 check_delta_issuer_and_scope(CRL, DeltaCRL). 97 98crl_status(State)-> 99 %% Fun argument is to enable future implementation of CRL checking 100 %% that does not care about all possible reasons. 101 crl_status(State, fun all_reasons/0). 102 103crl_status({skip, #revoke_state{cert_status = Status} = RevokedState}, _) -> 104 {undetermined, status(Status), RevokedState}; 105 106crl_status(#revoke_state{cert_status = unrevoked = Status, 107 valid_ext = false} = RevokedState, _) -> 108 {undetermined, Status, RevokedState}; 109 110crl_status(#revoke_state{cert_status = Status, 111 valid_ext = false}, _) -> 112 {finished, status(Status)}; 113 114crl_status(#revoke_state{reasons_mask = Mask, 115 cert_status = Status, 116 valid_ext = true} = RevokedState, Fun) -> 117 case is_all_reasons(Mask, Fun) of 118 true -> 119 {finished, status(Status)}; 120 false when (Status == unrevoked) -> 121 {undetermined, Status, RevokedState}; 122 _ -> 123 {finished ,status(Status)} 124 end. 125 126verify_crl(OtpCert, DP, CRL, DerCRL, DeltaCRL, DerDeltaCRL, OtherDPCRLs, 127 Options, State0) -> 128 #'CertificateList'{tbsCertList = 129 #'TBSCertList'{crlExtensions = Extensions, 130 revokedCertificates = TmpRevoked} 131 } = CRL, 132 Revoked = revoked(TmpRevoked), 133 IDP = issuing_distribution_point(Extensions), 134 135 DeltaRevoked = delta_revoked(DeltaCRL), 136 137 ValidExt = verify_extensions(Extensions) and 138 verify_extensions(Revoked), 139 140 IntMask = compute_interim_reasons_mask(DP, IDP), 141 142 RevokedState = 143 State0#revoke_state{interim_reasons_mask = IntMask, 144 valid_ext = ValidExt}, 145 146 {Fun, AdditionalArgs} = IssuerFun = proplists:get_value(issuer_fun, Options), 147 148 try verify_issuer_and_scope(OtpCert, DP, IDP, CRL) of 149 {ok, Issuer} -> 150 case Fun(DP, CRL, Issuer, AdditionalArgs) of 151 {ok, TrustedOtpCert, Path} -> 152 verify_mask_and_signatures(Revoked, DeltaRevoked, 153 RevokedState, 154 CRL, DerCRL, DeltaCRL, DerDeltaCRL, 155 IssuerFun, TrustedOtpCert, Path, OtherDPCRLs, IDP); 156 _ -> 157 Details = RevokedState#revoke_state.details, 158 {invalid, RevokedState#revoke_state{valid_ext = ValidExt, details = [{{bad_crl, no_issuer_cert_chain}, CRL} | Details]}} 159 end; 160 {error, issuer_not_found} -> 161 case Fun(DP, CRL, issuer_not_found, AdditionalArgs) of 162 {ok, TrustedOtpCert, Path} -> 163 verify_mask_and_signatures(Revoked, DeltaRevoked, 164 RevokedState, CRL, DerCRL, DeltaCRL, 165 DerDeltaCRL, IssuerFun, 166 TrustedOtpCert, Path, OtherDPCRLs, IDP); 167 _ -> 168 Details = State0#revoke_state.details, 169 {invalid, {skip, State0#revoke_state{details = [{{bad_crl, no_issuer_cert_chain}, CRL} | Details] }}} 170 end 171 catch 172 throw:{bad_crl, invalid_issuer} = Reason -> 173 Details = RevokedState#revoke_state.details, 174 {invalid, {skip, RevokedState#revoke_state{details = [{Reason, CRL} | Details]}}}; 175 throw:Reason -> 176 Details = RevokedState#revoke_state.details, 177 {invalid, RevokedState#revoke_state{details = [{Reason, CRL} | Details]}} 178 end. 179 180verify_mask_and_signatures(Revoked, DeltaRevoked, RevokedState, CRL, DerCRL, DeltaCRL, DerDeltaCRL, 181 IssuerFun, TrustedOtpCert, Path, OtherDPCRLs, IDP) -> 182 183 ReasonsMask = sets:union(RevokedState#revoke_state.reasons_mask, 184 RevokedState#revoke_state.interim_reasons_mask), 185 try 186 verify_interim_reasons_mask(RevokedState), 187 true = verify_crl_signatures(CRL, DerCRL, DeltaCRL, DerDeltaCRL, 188 TrustedOtpCert, Path, IssuerFun, OtherDPCRLs, IDP), 189 {valid, Revoked, DeltaRevoked, RevokedState#revoke_state{reasons_mask = ReasonsMask}, IDP} 190 catch 191 throw:Reason -> 192 Details = RevokedState#revoke_state.details, 193 {invalid, RevokedState#revoke_state{details = [{Reason, CRL} | Details]}}; 194 error:{badmatch, _} -> 195 Details = RevokedState#revoke_state.details, 196 {invalid, RevokedState#revoke_state{details = [{{bad_crl, invalid_signature}, CRL} | Details]}} 197 end. 198 199 200verify_crl_signatures(CRL, DerCRL, DeltaCRL, DerDeltaCRL, TrustedOtpCert, Path, 201 IssuerFun, OtherDPCRLs, IDP) -> 202 try 203 VerifyFunAndState = 204 {fun(_, {bad_cert, _} = Reason, _UserState) -> 205 {fail, Reason}; 206 (_,{extension, _}, UserState) -> 207 {unknown, UserState}; 208 (_Cert, valid, UserState) -> 209 {valid, UserState}; 210 (Cert, valid_peer, UserState) -> 211 case verify_crl_keybit(Cert, cRLSign) of 212 true -> 213 handle_crlsigner(Cert, IssuerFun, UserState); 214 false -> 215 {fail, crl_sign_bit_not_set} 216 end 217 end, #userstate{dpcrls = OtherDPCRLs, idp = IDP}}, 218 219 {ok, {{_,Key, KeyParams},_}} = 220 public_key:pkix_path_validation(TrustedOtpCert, Path, 221 [{verify_fun, VerifyFunAndState}]), 222 true = verify_crl_signature(CRL, DerCRL, Key, KeyParams), 223 true = verify_crl_signature(DeltaCRL, DerDeltaCRL, Key, KeyParams) 224 catch 225 error:{badmatch, _} -> 226 false 227 end. 228 229handle_crlsigner(OtpCert, IssuerFun, #userstate{idp = IDP} = UserState) -> 230 case verify_crl_keybit(OtpCert, keyCertSign) of 231 true -> 232 {valid, UserState}; 233 false -> 234 case not is_indirect_crl(IDP) andalso not public_key:pkix_is_self_signed(OtpCert) of 235 true -> 236 validate_crl_signing_cert(OtpCert, IssuerFun, UserState); 237 false -> 238 {valid, UserState} 239 end 240 end. 241 242validate_crl_signing_cert(_, _,#userstate{dpcrls = []} = UserState) -> 243 {valid, UserState}; 244validate_crl_signing_cert(OtpCert, IssuerFun, #userstate{dpcrls = CRLInfo} = UserState) -> 245 case public_key:pkix_crls_validate(OtpCert, CRLInfo, [{issuer_fun, IssuerFun}]) of 246 valid -> 247 {valid, UserState}; 248 Reason -> 249 {fail, Reason} 250 end. 251 252delta_revoked(undefined)-> 253 []; 254delta_revoked(#'CertificateList'{tbsCertList = 255 #'TBSCertList'{revokedCertificates 256 = DeltaRevoked}}) -> 257 revoked(DeltaRevoked). 258 259revoked(asn1_NOVALUE) -> 260 []; 261revoked(Revoked) -> 262 Revoked. 263 264revoked_status(DP, IDP, CRLIssuer, Names, SerialNumber, Revoked, DeltaRevoked, RevokedState0) -> 265 DefaultIssuer0 = default_issuer(CRLIssuer, DeltaRevoked), 266 RevokedState1 = check_revoked(DP, IDP, DefaultIssuer0, Names, SerialNumber, DeltaRevoked, RevokedState0), 267 RevokedState = case RevokedState1#revoke_state.cert_status of 268 unrevoked when RevokedState1#revoke_state.cert_status =/= removeFromCRL -> 269 DefaultIssuer = default_issuer(CRLIssuer, Revoked), 270 check_revoked(DP, IDP, DefaultIssuer, Names, SerialNumber, 271 Revoked, RevokedState1); 272 _ -> 273 RevokedState1 274 end, 275 case RevokedState#revoke_state.cert_status of 276 removeFromCRL -> 277 RevokedState#revoke_state{cert_status = unrevoked}; 278 _ -> 279 RevokedState 280 end. 281 282default_issuer(_, []) -> 283 undefined; 284default_issuer(Default, [#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = Extensions}| _]) -> 285 case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of 286 undefined -> 287 [pubkey_cert_records:transform(Default, decode)]; 288 GeneralNames -> 289 gen_names(GeneralNames) 290 end. 291 292is_all_reasons(Mask, AllReasonsFun) -> 293 AllReasons = AllReasonsFun(), 294 case sets:is_subset(AllReasons, Mask) of 295 true -> 296 true; 297 false -> 298 %% As the "uspecified" reason should not 299 %% be explicitly used according to RFC 3280 300 %% and the conformance tests have test cases 301 %% that should succed, and that does not specify 302 %% "unspecified", we tolorate that it is not included. 303 sets:is_subset(sets:del_element(unspecified, AllReasons), Mask) 304 end. 305 306all_reasons() -> 307 sets:from_list([unspecified, keyCompromise, 308 cACompromise, affiliationChanged, superseded, 309 cessationOfOperation, certificateHold, 310 privilegeWithdrawn, aACompromise]). 311 312verify_issuer_and_scope(#'OTPCertificate'{tbsCertificate = TBSCert} = Cert, 313 #'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP, 314 #'CertificateList'{tbsCertList = TBSCRL} = CRL) 315 when DPIssuer =/= asn1_NOVALUE -> 316 CRLIssuer = pubkey_cert_records:transform(TBSCRL#'TBSCertList'.issuer, decode), 317 Issuer = dp_crlissuer_to_issuer(DPIssuer), 318 case pubkey_cert:is_issuer(Issuer, CRLIssuer) and is_indirect_crl(IDP) of 319 true -> 320 verify_scope(Cert, DP, IDP), 321 issuer_id(Cert, CRL); 322 false -> 323 %% otherwise verify that the CRL issuer matches the certificate issuer 324 verify_issuer_and_scope(Cert, DP#'DistributionPoint'{ 325 distributionPoint = [TBSCert#'OTPTBSCertificate'.issuer], 326 cRLIssuer = asn1_NOVALUE}, 327 IDP, CRL) 328 end; 329verify_issuer_and_scope(#'OTPCertificate'{tbsCertificate = TBSCert}= Cert, 330 DP, IDP, 331 #'CertificateList'{tbsCertList = TBSCRL}) -> 332 CRLIssuer = pubkey_cert_records:transform(TBSCRL#'TBSCertList'.issuer, decode), 333 CertIssuer = TBSCert#'OTPTBSCertificate'.issuer, 334 case pubkey_cert:is_issuer(CertIssuer, CRLIssuer) of 335 true -> 336 verify_scope(Cert, DP, IDP), 337 issuer_id(Cert); 338 false -> 339 throw({bad_crl, invalid_issuer}) 340 end. 341 342dp_crlissuer_to_issuer(DPCRLIssuer) -> 343 [{directoryName, Issuer}] = pubkey_cert_records:transform(DPCRLIssuer, decode), 344 Issuer. 345 346is_indirect_crl(#'IssuingDistributionPoint'{indirectCRL = Value})-> 347 Value; 348is_indirect_crl(_) -> 349 false. 350 351verify_scope(_,_, undefined) -> 352 ok; 353verify_scope(#'OTPCertificate'{tbsCertificate = TBSCert}, #'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP) -> 354 CertIssuer = TBSCert#'OTPTBSCertificate'.issuer, 355 Names = case gen_names(DPIssuer) of 356 [{directoryName, TNames}] -> 357 TNames; 358 Other -> 359 Other 360 end, 361 DPName = dp_names(DP#'DistributionPoint'.distributionPoint, Names, CertIssuer), 362 IDPName = dp_names(IDP#'IssuingDistributionPoint'.distributionPoint, Names, CertIssuer), 363 verify_scope(DPName, IDPName, Names, TBSCert, IDP). 364 365verify_scope(asn1_NOVALUE, _, asn1_NOVALUE, _, _) -> 366 throw({bad_crl, scope_error}); 367verify_scope(asn1_NOVALUE, IDPName, DPIssuerNames, TBSCert, IDP) -> 368 verify_dp_name(IDPName, DPIssuerNames), 369 verify_dp_bools(TBSCert, IDP); 370 371verify_scope(DPName, IDPName, _, TBSCert, IDP) -> 372 verify_dp_name(IDPName, DPName), 373 verify_dp_bools(TBSCert, IDP). 374 375dp_names(asn1_NOVALUE, _, _) -> 376 asn1_NOVALUE; 377dp_names({fullName, Name}, _, _) -> 378 gen_names(Name); 379dp_names({nameRelativeToCRLIssuer, Fragment}, asn1_NOVALUE, {rdnSequence, RelativeDestinguistNames}) -> 380 [{directoryName, {rdnSequence, RelativeDestinguistNames ++ 381 [lists:map(fun(AttrAndValue) -> 382 pubkey_cert_records:transform(AttrAndValue, decode) 383 end, Fragment)]}}]; 384dp_names({nameRelativeToCRLIssuer, Fragment},{rdnSequence, RelativeDestinguistNames}, _) -> 385 [{directoryName, {rdnSequence, RelativeDestinguistNames ++ 386 [lists:map(fun(AttrAndValue) -> 387 pubkey_cert_records:transform(AttrAndValue, decode) 388 end, Fragment)]}}]; 389dp_names([{rdnSequence, _}] = Name0, _,_) -> 390 [Name] = pubkey_cert_records:transform(Name0, decode), 391 [{directoryName, Name}]. 392 393gen_names(asn1_NOVALUE) -> 394 asn1_NOVALUE; 395gen_names([]) -> 396 []; 397gen_names([{NameType, Name} | Rest]) -> 398 [ {NameType, pubkey_cert_records:transform(Name, decode)} | gen_names(Rest)]. 399 400verify_dp_name(asn1_NOVALUE, _) -> 401 ok; 402 403verify_dp_name(IDPNames, DPorIssuerNames) -> 404 case match_one(DPorIssuerNames, IDPNames) of 405 true -> 406 ok; 407 false -> 408 throw({bad_crl, scope_error}) 409 end. 410 411match_one([], _) -> 412 false; 413match_one([{Type, Name} | Names], CandidateNames) -> 414 Candidates = [NameName || {NameType, NameName} <- CandidateNames, 415 NameType == Type], 416 case Candidates of 417 [] -> 418 false; 419 [_|_] -> 420 case pubkey_cert:match_name(Type, Name, Candidates) of 421 true -> 422 true; 423 false -> 424 match_one(Names, CandidateNames) 425 end 426 end. 427 428verify_dp_bools(TBSCert, IDP) -> 429 BasicConstraints = 430 pubkey_cert:select_extension(?'id-ce-basicConstraints', 431 TBSCert#'OTPTBSCertificate'.extensions), 432 433 case verify_onlyContainsUserCerts(BasicConstraints, IDP) andalso 434 verify_onlyContainsCACerts(BasicConstraints, IDP) andalso 435 verify_onlyContainsAttributeCerts(IDP) of 436 true -> 437 ok; 438 _ -> 439 throw({bad_crl, scope_error}) 440 end. 441 442verify_onlyContainsUserCerts( 443 #'Extension'{extnValue = #'BasicConstraints'{cA = true}}, 444 #'IssuingDistributionPoint'{onlyContainsUserCerts = true}) -> 445 false; 446verify_onlyContainsUserCerts(_,_) -> 447 true. 448 449verify_onlyContainsCACerts( 450 #'Extension'{extnValue = #'BasicConstraints'{cA = true}}, 451 #'IssuingDistributionPoint'{onlyContainsCACerts = true}) -> 452 true; 453verify_onlyContainsCACerts(_,#'IssuingDistributionPoint'{onlyContainsCACerts = true}) -> 454 false; 455verify_onlyContainsCACerts(_,_) -> 456 true. 457 458verify_onlyContainsAttributeCerts( 459 #'IssuingDistributionPoint'{onlyContainsAttributeCerts = Bool}) -> 460 not Bool. 461 462check_crl_num(#'CertificateList'{tbsCertList = TBSCRL}, 463 #'CertificateList'{tbsCertList = TBSDeltaCRL})-> 464 Extensions = TBSCRL#'TBSCertList'.crlExtensions, 465 DeltaExtensions = TBSDeltaCRL#'TBSCertList'.crlExtensions, 466 467 try 468 CRLNum = assert_extension_value(?'id-ce-cRLNumber', 'CRLNumber', Extensions), 469 DeltaBaseNum = assert_extension_value(?'id-ce-deltaCRLIndicator', 470 'CRLNumber', DeltaExtensions), 471 DeltaCRLNum = assert_extension_value(?'id-ce-cRLNumber', 'CRLNumber', DeltaExtensions), 472 (CRLNum >= DeltaBaseNum) andalso (CRLNum < DeltaCRLNum) 473 catch 474 throw:no_extension_present -> 475 false 476 end; 477check_crl_num(_,_) -> 478 false. 479 480 481extension_value(Extension, ExtType, Extensions) -> 482 case pubkey_cert:select_extension(Extension, Extensions) of 483 #'Extension'{extnValue = Value} -> 484 public_key:der_decode(ExtType, iolist_to_binary(Value)); 485 _ -> 486 undefined 487 end. 488 489 490assert_extension_value(Extension, ExtType, Extensions) -> 491 case extension_value(Extension, ExtType, Extensions) of 492 undefined -> 493 throw(no_extension_present); 494 Value -> 495 Value 496 end. 497 498check_delta_issuer_and_scope(_, undefined) -> 499 true; 500check_delta_issuer_and_scope(#'CertificateList'{tbsCertList = TBSCRL}, 501 #'CertificateList'{tbsCertList = TBSDeltaCRL}) -> 502 case pubkey_cert:is_issuer(TBSCRL#'TBSCertList'.issuer, 503 TBSDeltaCRL#'TBSCertList'.issuer) of 504 true -> 505 check_delta_scope(TBSCRL, TBSDeltaCRL); 506 false -> 507 false 508 end. 509 510check_delta_scope(#'TBSCertList'{crlExtensions = Extensions}, 511 #'TBSCertList'{crlExtensions = DeltaExtensions})-> 512 IDP = issuing_distribution_point(Extensions), 513 DeltaIDP = issuing_distribution_point(DeltaExtensions), 514 515 AuthKey = authority_key_identifier(Extensions), 516 DeltaAuthKey = authority_key_identifier(DeltaExtensions), 517 is_match(IDP, DeltaIDP) andalso is_match(AuthKey, DeltaAuthKey). 518 519is_match(X, X) -> 520 true; 521is_match(_,_) -> 522 false. 523 524compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE}, 525 #'IssuingDistributionPoint'{onlySomeReasons = 526 asn1_NOVALUE}) -> 527 all_reasons(); 528compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE}, 529 undefined) -> 530 all_reasons(); 531 532compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE}, 533 #'IssuingDistributionPoint'{onlySomeReasons = 534 IDPReasons}) -> 535 sets:from_list(IDPReasons); 536compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons}, 537 #'IssuingDistributionPoint'{onlySomeReasons = 538 asn1_NOVALUE}) -> 539 sets:from_list(DPReasons); 540compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons}, 541 undefined) -> 542 sets:from_list(DPReasons); 543compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons}, 544 #'IssuingDistributionPoint'{onlySomeReasons = 545 IDPReasons}) -> 546 sets:intersection(sets:from_list(DPReasons), sets:from_list(IDPReasons)). 547 548verify_interim_reasons_mask(#revoke_state{reasons_mask = Mask, 549 interim_reasons_mask = IntMask}) -> 550 case sets:fold(fun(Element, Acc) -> 551 case sets:is_element(Element, Mask) of 552 true -> 553 Acc; 554 false -> 555 true 556 end 557 end, false, IntMask) of 558 true -> 559 ok; 560 false -> 561 throw({bad_crl, mask_error}) 562 end. 563 564verify_crl_signature(undefined, undefined, _,_) -> 565 true; 566verify_crl_signature(CRL, DerCRL, Key, KeyParams) -> 567 {DigestType, PlainText, Signature} = extract_crl_verify_data(CRL, DerCRL), 568 case Key of 569 #'RSAPublicKey'{} -> 570 public_key:verify(PlainText, DigestType, Signature, Key); 571 _ -> 572 public_key:verify(PlainText, DigestType, Signature, 573 {Key, KeyParams}) 574 end. 575extract_crl_verify_data(CRL, DerCRL) -> 576 Signature = CRL#'CertificateList'.signature, 577 #'AlgorithmIdentifier'{algorithm = SigAlg} = 578 CRL#'CertificateList'.signatureAlgorithm, 579 PlainText = encoded_tbs_crl(DerCRL), 580 {DigestType, _} = public_key:pkix_sign_types(SigAlg), 581 {DigestType, PlainText, Signature}. 582 583encoded_tbs_crl(CRL) -> 584 {ok, PKIXCRL} = 585 'OTP-PUB-KEY':decode_TBSCertList_exclusive(CRL), 586 {'CertificateList', 587 {'CertificateList_tbsCertList', EncodedTBSCertList}, _, _} = PKIXCRL, 588 EncodedTBSCertList. 589 590check_revoked(_,_,_,_,_,[], State) -> 591 State; 592check_revoked(#'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP, DefaultIssuer0, Names, SerialNr, 593 [#'TBSCertList_revokedCertificates_SEQOF'{userCertificate = 594 SerialNr, 595 crlEntryExtensions = 596 Extensions}| Rest], 597 State) -> 598 Reason = revoked_reason(Extensions), 599 case (DPIssuer =/= asn1_NOVALUE) and is_indirect_crl(IDP) of 600 true -> 601 handle_indirect_crl_check(DP, IDP, DefaultIssuer0, Names, SerialNr, Extensions, Reason, Rest, State); 602 false -> 603 State#revoke_state{cert_status = Reason} 604 end; 605 606check_revoked(DP, IDP, DefaultIssuer0, Names, SerialNr, 607 [#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = 608 Extensions}| Rest], State) -> 609 DefaultIssuer = case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of 610 undefined -> 611 DefaultIssuer0; 612 GeneralNames -> 613 gen_names(GeneralNames) 614 end, 615 check_revoked(DP, IDP, DefaultIssuer, Names, SerialNr, Rest, State). 616 617handle_indirect_crl_check(DP, IDP, DefaultIssuer0, Names, SerialNr, Extensions, Reason, Rest, State) -> 618 case check_crl_issuer_extension(Names, Extensions, DefaultIssuer0) of 619 {true, _} -> 620 State#revoke_state{cert_status = Reason}; 621 {false, DefaultIssuer} -> 622 check_revoked(DP, IDP, DefaultIssuer, Names, SerialNr, Rest, State) 623 end. 624 625check_crl_issuer_extension(Names, Extensions, Default0) -> 626 case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of 627 undefined -> 628 {match_one(Default0, Names), Default0}; 629 GeneralNames -> 630 Default = gen_names(GeneralNames), 631 {match_one(Default, Names), Default} 632 end. 633 634revoked_reason(Extensions) -> 635 case extension_value(?'id-ce-cRLReasons', 'CRLReason', Extensions) of 636 undefined -> 637 unspecified; 638 Value -> 639 Value 640 end. 641 642verify_crl_keybit(#'OTPCertificate'{tbsCertificate = TBS}, Bit) -> 643 case pubkey_cert:select_extension( ?'id-ce-keyUsage', 644 TBS#'OTPTBSCertificate'.extensions) of 645 #'Extension'{extnID = ?'id-ce-keyUsage', 646 extnValue = KeyUse} -> 647 lists:member(Bit, KeyUse); 648 _ -> 649 true 650 end. 651 652issuer_id(Cert, #'CertificateList'{tbsCertList = TBSCRL}) -> 653 Extensions = 654 pubkey_cert:extensions_list(TBSCRL#'TBSCertList'.crlExtensions), 655 case authority_key_identifier(Extensions) of 656 undefined -> 657 issuer_id(Cert); 658 #'AuthorityKeyIdentifier'{authorityCertIssuer = asn1_NOVALUE, 659 authorityCertSerialNumber = asn1_NOVALUE} -> 660 issuer_id(Cert); 661 #'AuthorityKeyIdentifier'{authorityCertIssuer = Issuer, 662 authorityCertSerialNumber = Nr} -> 663 {ok, {Nr, Issuer}} 664 end. 665 666issuer_id(#'OTPCertificate'{} = Cert) -> 667 case public_key:pkix_is_self_signed(Cert) of 668 true -> 669 public_key:pkix_issuer_id(Cert, self); 670 false -> 671 public_key:pkix_issuer_id(Cert, other) 672 end. 673 674status(unrevoked) -> 675 unrevoked; 676status(Reason) -> 677 {revoked, Reason}. 678 679verify_extensions([#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = Ext} | Rest]) -> 680 verify_extensions(pubkey_cert:extensions_list(Ext)) and verify_extensions(Rest); 681verify_extensions([]) -> 682 true; 683verify_extensions(asn1_NOVALUE) -> 684 true; 685verify_extensions([#'Extension'{critical = true, extnID = Id} | Rest]) -> 686 case lists:member(Id, [?'id-ce-authorityKeyIdentifier', 687 ?'id-ce-issuerAltName', 688 ?'id-ce-cRLNumber', 689 ?'id-ce-certificateIssuer', 690 ?'id-ce-deltaCRLIndicator', 691 ?'id-ce-issuingDistributionPoint', 692 ?'id-ce-freshestCRL']) of 693 true -> 694 verify_extensions(Rest); 695 false -> 696 false 697 end; 698verify_extensions([_Ext | Rest]) -> 699 verify_extensions(Rest). 700 701issuing_distribution_point(Extensions) -> 702 Enc = extension_value(?'id-ce-issuingDistributionPoint', 703 'IssuingDistributionPoint', Extensions), 704 pubkey_cert_records:transform(Enc, decode). 705 706authority_key_identifier(Extensions) -> 707 Enc = extension_value(?'id-ce-authorityKeyIdentifier', 708 'AuthorityKeyIdentifier', Extensions), 709 pubkey_cert_records:transform(Enc, decode). 710