1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2010-2020. 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 %% Assume the cRLIssuer SEQUENCE is of length exactly 1 344 [{directoryName, Issuer}] = pubkey_cert_records:transform(DPCRLIssuer, decode), 345 Issuer. 346 347is_indirect_crl(#'IssuingDistributionPoint'{indirectCRL = Value})-> 348 Value; 349is_indirect_crl(_) -> 350 false. 351 352verify_scope(_,_, undefined) -> 353 ok; 354verify_scope(#'OTPCertificate'{tbsCertificate = TBSCert}, #'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP) -> 355 CertIssuer = TBSCert#'OTPTBSCertificate'.issuer, 356 Names = case gen_names(DPIssuer) of 357 [{directoryName, TNames}] -> 358 TNames; 359 Other -> 360 Other 361 end, 362 DPName = dp_names(DP#'DistributionPoint'.distributionPoint, Names, CertIssuer), 363 IDPName = dp_names(IDP#'IssuingDistributionPoint'.distributionPoint, Names, CertIssuer), 364 verify_scope(DPName, IDPName, Names, TBSCert, IDP). 365 366verify_scope(asn1_NOVALUE, _, asn1_NOVALUE, _, _) -> 367 throw({bad_crl, scope_error}); 368verify_scope(asn1_NOVALUE, IDPName, DPIssuerNames, TBSCert, IDP) -> 369 verify_dp_name(IDPName, DPIssuerNames), 370 verify_dp_bools(TBSCert, IDP); 371 372verify_scope(DPName, IDPName, _, TBSCert, IDP) -> 373 verify_dp_name(IDPName, DPName), 374 verify_dp_bools(TBSCert, IDP). 375 376dp_names(asn1_NOVALUE, _, _) -> 377 asn1_NOVALUE; 378dp_names({fullName, Name}, _, _) -> 379 gen_names(Name); 380dp_names({nameRelativeToCRLIssuer, Fragment}, asn1_NOVALUE, {rdnSequence, RelativeDestinguistNames}) -> 381 [{directoryName, {rdnSequence, RelativeDestinguistNames ++ 382 [lists:map(fun(AttrAndValue) -> 383 pubkey_cert_records:transform(AttrAndValue, decode) 384 end, Fragment)]}}]; 385dp_names({nameRelativeToCRLIssuer, Fragment},{rdnSequence, RelativeDestinguistNames}, _) -> 386 [{directoryName, {rdnSequence, RelativeDestinguistNames ++ 387 [lists:map(fun(AttrAndValue) -> 388 pubkey_cert_records:transform(AttrAndValue, decode) 389 end, Fragment)]}}]; 390dp_names([{rdnSequence, _}] = Name0, _,_) -> 391 [Name] = pubkey_cert_records:transform(Name0, decode), 392 [{directoryName, Name}]. 393 394gen_names(asn1_NOVALUE) -> 395 asn1_NOVALUE; 396gen_names([]) -> 397 []; 398gen_names([{NameType, Name} | Rest]) -> 399 [ {NameType, pubkey_cert_records:transform(Name, decode)} | gen_names(Rest)]. 400 401verify_dp_name(asn1_NOVALUE, _) -> 402 ok; 403 404verify_dp_name(IDPNames, DPorIssuerNames) -> 405 case match_one(DPorIssuerNames, IDPNames) of 406 true -> 407 ok; 408 false -> 409 throw({bad_crl, scope_error}) 410 end. 411 412match_one([], _) -> 413 false; 414match_one([{Type, Name} | Names], CandidateNames) -> 415 Candidates = [NameName || {NameType, NameName} <- CandidateNames, 416 NameType == Type], 417 case Candidates of 418 [] -> 419 false; 420 [_|_] -> 421 case pubkey_cert:match_name(Type, Name, Candidates) of 422 true -> 423 true; 424 false -> 425 match_one(Names, CandidateNames) 426 end 427 end. 428 429verify_dp_bools(TBSCert, IDP) -> 430 BasicConstraints = 431 pubkey_cert:select_extension(?'id-ce-basicConstraints', 432 TBSCert#'OTPTBSCertificate'.extensions), 433 434 case verify_onlyContainsUserCerts(BasicConstraints, IDP) andalso 435 verify_onlyContainsCACerts(BasicConstraints, IDP) andalso 436 verify_onlyContainsAttributeCerts(IDP) of 437 true -> 438 ok; 439 _ -> 440 throw({bad_crl, scope_error}) 441 end. 442 443verify_onlyContainsUserCerts( 444 #'Extension'{extnValue = #'BasicConstraints'{cA = true}}, 445 #'IssuingDistributionPoint'{onlyContainsUserCerts = true}) -> 446 false; 447verify_onlyContainsUserCerts(_,_) -> 448 true. 449 450verify_onlyContainsCACerts( 451 #'Extension'{extnValue = #'BasicConstraints'{cA = true}}, 452 #'IssuingDistributionPoint'{onlyContainsCACerts = true}) -> 453 true; 454verify_onlyContainsCACerts(_,#'IssuingDistributionPoint'{onlyContainsCACerts = true}) -> 455 false; 456verify_onlyContainsCACerts(_,_) -> 457 true. 458 459verify_onlyContainsAttributeCerts( 460 #'IssuingDistributionPoint'{onlyContainsAttributeCerts = Bool}) -> 461 not Bool. 462 463check_crl_num(#'CertificateList'{tbsCertList = TBSCRL}, 464 #'CertificateList'{tbsCertList = TBSDeltaCRL})-> 465 Extensions = TBSCRL#'TBSCertList'.crlExtensions, 466 DeltaExtensions = TBSDeltaCRL#'TBSCertList'.crlExtensions, 467 468 try 469 CRLNum = assert_extension_value(?'id-ce-cRLNumber', 'CRLNumber', Extensions), 470 DeltaBaseNum = assert_extension_value(?'id-ce-deltaCRLIndicator', 471 'CRLNumber', DeltaExtensions), 472 DeltaCRLNum = assert_extension_value(?'id-ce-cRLNumber', 'CRLNumber', DeltaExtensions), 473 (CRLNum >= DeltaBaseNum) andalso (CRLNum < DeltaCRLNum) 474 catch 475 throw:no_extension_present -> 476 false 477 end; 478check_crl_num(_,_) -> 479 false. 480 481 482extension_value(Extension, ExtType, Extensions) -> 483 case pubkey_cert:select_extension(Extension, Extensions) of 484 #'Extension'{extnValue = Value} -> 485 public_key:der_decode(ExtType, iolist_to_binary(Value)); 486 _ -> 487 undefined 488 end. 489 490 491assert_extension_value(Extension, ExtType, Extensions) -> 492 case extension_value(Extension, ExtType, Extensions) of 493 undefined -> 494 throw(no_extension_present); 495 Value -> 496 Value 497 end. 498 499check_delta_issuer_and_scope(_, undefined) -> 500 true; 501check_delta_issuer_and_scope(#'CertificateList'{tbsCertList = TBSCRL}, 502 #'CertificateList'{tbsCertList = TBSDeltaCRL}) -> 503 case pubkey_cert:is_issuer(TBSCRL#'TBSCertList'.issuer, 504 TBSDeltaCRL#'TBSCertList'.issuer) of 505 true -> 506 check_delta_scope(TBSCRL, TBSDeltaCRL); 507 false -> 508 false 509 end. 510 511check_delta_scope(#'TBSCertList'{crlExtensions = Extensions}, 512 #'TBSCertList'{crlExtensions = DeltaExtensions})-> 513 IDP = issuing_distribution_point(Extensions), 514 DeltaIDP = issuing_distribution_point(DeltaExtensions), 515 516 AuthKey = authority_key_identifier(Extensions), 517 DeltaAuthKey = authority_key_identifier(DeltaExtensions), 518 is_match(IDP, DeltaIDP) andalso is_match(AuthKey, DeltaAuthKey). 519 520is_match(X, X) -> 521 true; 522is_match(_,_) -> 523 false. 524 525compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE}, 526 #'IssuingDistributionPoint'{onlySomeReasons = 527 asn1_NOVALUE}) -> 528 all_reasons(); 529compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE}, 530 undefined) -> 531 all_reasons(); 532 533compute_interim_reasons_mask(#'DistributionPoint'{reasons = asn1_NOVALUE}, 534 #'IssuingDistributionPoint'{onlySomeReasons = 535 IDPReasons}) -> 536 sets:from_list(IDPReasons); 537compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons}, 538 #'IssuingDistributionPoint'{onlySomeReasons = 539 asn1_NOVALUE}) -> 540 sets:from_list(DPReasons); 541compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons}, 542 undefined) -> 543 sets:from_list(DPReasons); 544compute_interim_reasons_mask(#'DistributionPoint'{reasons = DPReasons}, 545 #'IssuingDistributionPoint'{onlySomeReasons = 546 IDPReasons}) -> 547 sets:intersection(sets:from_list(DPReasons), sets:from_list(IDPReasons)). 548 549verify_interim_reasons_mask(#revoke_state{reasons_mask = Mask, 550 interim_reasons_mask = IntMask}) -> 551 case sets:fold(fun(Element, Acc) -> 552 case sets:is_element(Element, Mask) of 553 true -> 554 Acc; 555 false -> 556 true 557 end 558 end, false, IntMask) of 559 true -> 560 ok; 561 false -> 562 throw({bad_crl, mask_error}) 563 end. 564 565verify_crl_signature(undefined, undefined, _,_) -> 566 true; 567verify_crl_signature(CRL, DerCRL, Key, KeyParams) -> 568 {DigestType, PlainText, Signature} = extract_crl_verify_data(CRL, DerCRL), 569 case Key of 570 #'RSAPublicKey'{} -> 571 public_key:verify(PlainText, DigestType, Signature, Key); 572 _ -> 573 public_key:verify(PlainText, DigestType, Signature, 574 {Key, KeyParams}) 575 end. 576extract_crl_verify_data(CRL, DerCRL) -> 577 Signature = CRL#'CertificateList'.signature, 578 #'AlgorithmIdentifier'{algorithm = SigAlg} = 579 CRL#'CertificateList'.signatureAlgorithm, 580 PlainText = encoded_tbs_crl(DerCRL), 581 {DigestType, _} = public_key:pkix_sign_types(SigAlg), 582 {DigestType, PlainText, Signature}. 583 584encoded_tbs_crl(CRL) -> 585 {ok, PKIXCRL} = 586 'OTP-PUB-KEY':decode_TBSCertList_exclusive(CRL), 587 {'CertificateList', 588 {'CertificateList_tbsCertList', EncodedTBSCertList}, _, _} = PKIXCRL, 589 EncodedTBSCertList. 590 591check_revoked(_,_,_,_,_,[], State) -> 592 State; 593check_revoked(#'DistributionPoint'{cRLIssuer = DPIssuer} = DP, IDP, DefaultIssuer0, Names, SerialNr, 594 [#'TBSCertList_revokedCertificates_SEQOF'{userCertificate = 595 SerialNr, 596 crlEntryExtensions = 597 Extensions}| Rest], 598 State) -> 599 Reason = revoked_reason(Extensions), 600 case (DPIssuer =/= asn1_NOVALUE) and is_indirect_crl(IDP) of 601 true -> 602 handle_indirect_crl_check(DP, IDP, DefaultIssuer0, Names, SerialNr, Extensions, Reason, Rest, State); 603 false -> 604 State#revoke_state{cert_status = Reason} 605 end; 606 607check_revoked(DP, IDP, DefaultIssuer0, Names, SerialNr, 608 [#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = 609 Extensions}| Rest], State) -> 610 DefaultIssuer = case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of 611 undefined -> 612 DefaultIssuer0; 613 GeneralNames -> 614 gen_names(GeneralNames) 615 end, 616 check_revoked(DP, IDP, DefaultIssuer, Names, SerialNr, Rest, State). 617 618handle_indirect_crl_check(DP, IDP, DefaultIssuer0, Names, SerialNr, Extensions, Reason, Rest, State) -> 619 case check_crl_issuer_extension(Names, Extensions, DefaultIssuer0) of 620 {true, _} -> 621 State#revoke_state{cert_status = Reason}; 622 {false, DefaultIssuer} -> 623 check_revoked(DP, IDP, DefaultIssuer, Names, SerialNr, Rest, State) 624 end. 625 626check_crl_issuer_extension(Names, Extensions, Default0) -> 627 case extension_value(?'id-ce-certificateIssuer', 'GeneralNames', Extensions) of 628 undefined -> 629 {match_one(Default0, Names), Default0}; 630 GeneralNames -> 631 Default = gen_names(GeneralNames), 632 {match_one(Default, Names), Default} 633 end. 634 635revoked_reason(Extensions) -> 636 case extension_value(?'id-ce-cRLReasons', 'CRLReason', Extensions) of 637 undefined -> 638 unspecified; 639 Value -> 640 Value 641 end. 642 643verify_crl_keybit(#'OTPCertificate'{tbsCertificate = TBS}, Bit) -> 644 case pubkey_cert:select_extension( ?'id-ce-keyUsage', 645 TBS#'OTPTBSCertificate'.extensions) of 646 #'Extension'{extnID = ?'id-ce-keyUsage', 647 extnValue = KeyUse} -> 648 lists:member(Bit, KeyUse); 649 _ -> 650 true 651 end. 652 653issuer_id(Cert, #'CertificateList'{tbsCertList = TBSCRL}) -> 654 Extensions = 655 pubkey_cert:extensions_list(TBSCRL#'TBSCertList'.crlExtensions), 656 case authority_key_identifier(Extensions) of 657 undefined -> 658 issuer_id(Cert); 659 #'AuthorityKeyIdentifier'{authorityCertIssuer = asn1_NOVALUE, 660 authorityCertSerialNumber = asn1_NOVALUE} -> 661 issuer_id(Cert); 662 #'AuthorityKeyIdentifier'{authorityCertIssuer = Issuer, 663 authorityCertSerialNumber = Nr} -> 664 {ok, {Nr, Issuer}} 665 end. 666 667issuer_id(#'OTPCertificate'{} = Cert) -> 668 case public_key:pkix_is_self_signed(Cert) of 669 true -> 670 public_key:pkix_issuer_id(Cert, self); 671 false -> 672 public_key:pkix_issuer_id(Cert, other) 673 end. 674 675status(unrevoked) -> 676 unrevoked; 677status(Reason) -> 678 {revoked, Reason}. 679 680verify_extensions([#'TBSCertList_revokedCertificates_SEQOF'{crlEntryExtensions = Ext} | Rest]) -> 681 verify_extensions(pubkey_cert:extensions_list(Ext)) and verify_extensions(Rest); 682verify_extensions([]) -> 683 true; 684verify_extensions(asn1_NOVALUE) -> 685 true; 686verify_extensions([#'Extension'{critical = true, extnID = Id} | Rest]) -> 687 case lists:member(Id, [?'id-ce-authorityKeyIdentifier', 688 ?'id-ce-issuerAltName', 689 ?'id-ce-cRLNumber', 690 ?'id-ce-certificateIssuer', 691 ?'id-ce-deltaCRLIndicator', 692 ?'id-ce-issuingDistributionPoint', 693 ?'id-ce-freshestCRL']) of 694 true -> 695 verify_extensions(Rest); 696 false -> 697 false 698 end; 699verify_extensions([_Ext | Rest]) -> 700 verify_extensions(Rest). 701 702issuing_distribution_point(Extensions) -> 703 Enc = extension_value(?'id-ce-issuingDistributionPoint', 704 'IssuingDistributionPoint', Extensions), 705 pubkey_cert_records:transform(Enc, decode). 706 707authority_key_identifier(Extensions) -> 708 Enc = extension_value(?'id-ce-authorityKeyIdentifier', 709 'AuthorityKeyIdentifier', Extensions), 710 pubkey_cert_records:transform(Enc, decode). 711