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