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