2 * Copyright (c) 2004,2011-2012,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * tpOcspCertVfy.cpp - OCSP cert verification routines
28 #include "tpOcspCertVfy.h"
29 #include "tpdebugging.h"
30 #include "certGroupUtils.h"
31 #include <Security/oidscert.h>
32 #include <CommonCrypto/CommonDigest.h>
33 #include <security_ocspd/ocspdUtils.h>
36 * Is signerCert authorized to sign OCSP responses by issuerCert? IssuerCert is
37 * assumed to be (i.e., must, but we don't check that here) the signer of the
38 * cert being verified, which is not in the loop for this op. Just a bool returned;
39 * it's autoritized or it's not.
41 static bool tpIsAuthorizedOcspSigner(
42 TPCertInfo
&issuerCert
, // issuer of cert being verified
43 TPCertInfo
&signerCert
) // potential signer of OCSP response
45 CSSM_DATA_PTR fieldValue
= NULL
; // mallocd by CL
48 CE_ExtendedKeyUsage
*eku
= NULL
;
49 bool foundEku
= false;
52 * First see if issuerCert issued signerCert (No signature vfy yet, just
53 * subject/issuer check).
55 if(!issuerCert
.isIssuerOf(signerCert
)) {
59 /* Fetch ExtendedKeyUse field from signerCert */
60 crtn
= signerCert
.fetchField(&CSSMOID_ExtendedKeyUsage
, &fieldValue
);
62 tpOcspDebug("tpIsAuthorizedOcspSigner: signer is issued by issuer, no EKU");
65 CSSM_X509_EXTENSION
*cssmExt
= (CSSM_X509_EXTENSION
*)fieldValue
->Data
;
66 if(cssmExt
->format
!= CSSM_X509_DATAFORMAT_PARSED
) {
67 tpOcspDebug("tpIsAuthorizedOcspSigner: bad extension format");
70 eku
= (CE_ExtendedKeyUsage
*)cssmExt
->value
.parsedValue
;
72 /* Look for OID_KP_OCSPSigning */
73 for(unsigned dex
=0; dex
<eku
->numPurposes
; dex
++) {
74 if(tpCompareCssmData(&eku
->purposes
[dex
], &CSSMOID_OCSPSigning
)) {
80 tpOcspDebug("tpIsAuthorizedOcspSigner: signer is issued by issuer, no OCSP "
86 * OK, signerCert is authorized by *someone* to sign OCSP requests, and
87 * it claims to be issued by issuer. Sig verify to be sure.
88 * FIXME this is not handling partial public keys, which would be a colossal
89 * mess to handle in this module...so we don't.
91 crtn
= signerCert
.verifyWithIssuer(&issuerCert
, NULL
);
93 tpOcspDebug("tpIsAuthorizedOcspSigner: FOUND authorized signer");
97 /* This is a highly irregular situation... */
98 tpOcspDebug("tpIsAuthorizedOcspSigner: signer sig verify FAIL");
101 if(fieldValue
!= NULL
) {
102 signerCert
.freeField(&CSSMOID_ExtendedKeyUsage
, fieldValue
);
108 * Check ResponderID linkage between an OCSPResponse and a cert we believe to
109 * be the issuer of both that response and the cert being verified. Returns
113 bool tpOcspResponderIDCheck(
114 OCSPResponse
&ocspResp
,
117 bool shouldBeSigner
= false;
118 if(ocspResp
.responderIDTag() == RIT_Name
) {
120 * Name inside response must == signer's SubjectName.
121 * Note we can't use signer.subjectName(); that's normalized.
124 const CSSM_DATA
*respIdName
= ocspResp
.encResponderName();
125 CSSM_DATA
*subjectName
= NULL
;
126 CSSM_RETURN crtn
= signer
.fetchField(&CSSMOID_X509V1SubjectNameStd
,
130 tpOcspDebug("tpOcspResponderIDCheck: error on fetchField(subjectName");
133 if(tpCompareCssmData(respIdName
, subjectName
)) {
134 tpOcspDebug("tpOcspResponderIDCheck: good ResponderID.byName");
135 shouldBeSigner
= true;
138 tpOcspDebug("tpOcspResponderIDCheck: BAD ResponderID.byName");
140 signer
.freeField(&CSSMOID_X509V1SubjectNameStd
, subjectName
);
143 /* ResponderID.byKey must == SHA1(signer's public key) */
144 const CSSM_KEY
*pubKey
= signer
.pubKey();
145 assert(pubKey
!= NULL
);
146 uint8 digest
[CC_SHA1_DIGEST_LENGTH
];
147 CSSM_DATA keyHash
= {CC_SHA1_DIGEST_LENGTH
, digest
};
148 ocspdSha1(pubKey
->KeyData
.Data
, (CC_LONG
)pubKey
->KeyData
.Length
, digest
);
149 const CSSM_DATA
*respKeyHash
= &ocspResp
.responderID().byKey
;
150 if(tpCompareCssmData(&keyHash
, respKeyHash
)) {
151 tpOcspDebug("tpOcspResponderIDCheck: good ResponderID.byKey");
152 shouldBeSigner
= true;
155 tpOcspDebug("tpOcspResponderIDCheck: BAD ResponderID.byKey");
158 return shouldBeSigner
;
162 * Verify the signature of an OCSP response. Caller is responsible for all other
163 * verification of the response, this is just the crypto.
164 * Returns true on success.
166 static bool tpOcspResponseSigVerify(
167 TPVerifyContext
&vfyCtx
,
168 OCSPResponse
&ocspResp
, // parsed response
171 /* get signature algorithm in CSSM form from the response */
172 const SecAsn1OCSPBasicResponse
&basicResp
= ocspResp
.basicResponse();
173 const CSSM_OID
*algOid
= &basicResp
.algId
.algorithm
;
174 CSSM_ALGORITHMS sigAlg
;
176 if(!cssmOidToAlg(algOid
, &sigAlg
)) {
177 tpOcspDebug("tpOcspResponseSigVerify: unknown signature algorithm");
180 /* signer's public key from the cert */
181 const CSSM_KEY
*pubKey
= signer
.pubKey();
183 /* signature: on decode, length is in BITS */
184 CSSM_DATA sig
= basicResp
.sig
;
188 CSSM_CC_HANDLE sigHand
;
190 crtn
= CSSM_CSP_CreateSignatureContext(vfyCtx
.cspHand
, sigAlg
, NULL
,
194 cssmPerror("tpOcspResponseSigVerify, CSSM_CSP_CreateSignatureContext", crtn
);
198 crtn
= CSSM_VerifyData(sigHand
, &basicResp
.tbsResponseData
, 1,
199 CSSM_ALGID_NONE
, &sig
);
202 cssmPerror("tpOcspResponseSigVerify, CSSM_VerifyData", crtn
);
208 CSSM_DeleteContext(sigHand
);
212 /* possible return from tpIsOcspIssuer() */
214 OIS_No
, // not the issuer
215 OIS_Good
, // is the issuer and signature matches
216 OIS_BadSig
, // appears to be issuer, but signature doesn't match
219 /* type of rawCert passed to tpIsOcspIssuer */
221 OCT_Local
, // LocalResponder - no checking other than signature
222 OCT_Issuer
, // it's the issuer of the cert being verified
223 OCT_Provided
, // came with response, provenance unknown
227 * Did specified cert issue the OCSP response?
229 * This implements the algorithm described in RFC2560, section 4.2.2.2,
230 * "Authorized Responders". It sees if the cert could be the issuer of the
231 * OCSP response per that algorithm; then if it could, it performs signature
234 static OcspIssuerStatus
tpIsOcspIssuer(
235 TPVerifyContext
&vfyCtx
,
236 OCSPResponse
&ocspResp
, // parsed response
237 /* on input specify at least one of the following two */
238 const CSSM_DATA
*signerData
,
240 OcspCertType certType
, // where rawCert came from
241 TPCertInfo
*issuer
, // OPTIONAL, if known
242 TPCertInfo
**signerRtn
) // optionally RETURNED if at all possible
244 assert((signerData
!= NULL
) || (signer
!= NULL
));
246 /* get signer as TPCertInfo if caller hasn't provided */
247 TPCertInfo
*tmpSigner
= NULL
;
250 tmpSigner
= new TPCertInfo(vfyCtx
.clHand
, vfyCtx
.cspHand
, signerData
,
251 TIC_CopyData
, vfyCtx
.verifyTime
);
254 tpOcspDebug("tpIsOcspIssuer: bad cert");
262 if(signerRtn
!= NULL
) {
267 * Qualification of "this can be the signer" depends on where the
270 bool shouldBeSigner
= false;
271 OcspIssuerStatus ourRtn
= OIS_No
;
274 case OCT_Local
: // caller trusts this and thinks it's the signer
275 shouldBeSigner
= true;
277 case OCT_Issuer
: // last resort, the actual issuer
278 /* check ResponderID linkage */
279 shouldBeSigner
= tpOcspResponderIDCheck(ocspResp
, *signer
);
284 * This cert came with the response.
288 * careful, might not know the issuer...how would this path ever
289 * work then? I don't think it needs to because you can NOT
290 * do OCSP on a cert without its issuer in hand.
295 /* check EKU linkage */
296 shouldBeSigner
= tpIsAuthorizedOcspSigner(*issuer
, *signer
);
300 if(!shouldBeSigner
) {
304 /* verify the signature */
305 if(tpOcspResponseSigVerify(vfyCtx
, ocspResp
, *signer
)) {
310 if((signerRtn
== NULL
) && (tmpSigner
!= NULL
)) {
317 OcspRespStatus
tpVerifyOcspResp(
318 TPVerifyContext
&vfyCtx
,
320 TPCertInfo
*issuer
, // issuer of the related cert, may be issuer of
321 // reply, may not be known
322 OCSPResponse
&ocspResp
,
323 CSSM_RETURN
&cssmErr
) // possible per-cert error
325 OcspRespStatus ourRtn
= ORS_Unknown
;
328 tpOcspDebug("tpVerifyOcspResp top");
330 switch(ocspResp
.responseStatus()) {
334 case RS_MalformedRequest
:
335 crtn
= CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ
;
337 case RS_InternalError
:
338 crtn
= CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR
;
341 crtn
= CSSMERR_APPLETP_OCSP_RESP_TRY_LATER
;
344 crtn
= CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED
;
346 case RS_Unauthorized
:
347 crtn
= CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED
;
350 crtn
= CSSMERR_APPLETP_OCSP_BAD_RESPONSE
;
354 tpOcspDebug("tpVerifyOcspResp aborting due to response status %d",
355 (int)(ocspResp
.responseStatus()));
361 /* one of our main jobs is to locate the signer of the response, here */
362 TPCertInfo
*signerInfo
= NULL
;
363 TPCertInfo
*signerInfoTBD
= NULL
; // if non NULL at end, we delete
364 /* we'll be verifying into this cert group */
365 TPCertGroup
ocspCerts(vfyCtx
.alloc
, TGO_Caller
);
366 CSSM_BOOL verifiedToRoot
;
367 CSSM_BOOL verifiedToAnchor
;
368 CSSM_BOOL verifiedViaTrustSetting
;
370 const CSSM_APPLE_TP_OCSP_OPTIONS
*ocspOpts
= vfyCtx
.ocspOpts
;
371 OcspIssuerStatus issuerStat
;
374 * Set true if we ever find an apparent issuer which does not correctly
375 * pass signature verify. If true and we never success, that's a XXX error.
377 bool foundBadIssuer
= false;
378 bool foundLocalResponder
= false;
379 uint32 numSignerCerts
= ocspResp
.numSignerCerts();
382 * This cert group, allocated by AppleTPSession::CertGroupVerify(),
383 * serves two functions here:
385 * -- it accumulates certs we get from the net (as parts of OCSP responses)
386 * for user in verifying OCSPResponse-related certs.
387 * TPCertGroup::buildCertGroup() uses this group as one of the many
388 * sources of certs when building a cert chain.
390 * -- it provides a container into which to stash TPCertInfos which
391 * persist at least as long as the TPVerifyContext; it's of type TGO_Group,
392 * so all of the certs added to it get freed when the group does.
394 assert(vfyCtx
.signerCerts
!= NULL
);
396 TPCertGroup
&gatheredCerts
= vfyCtx
.gatheredCerts
;
398 /* set up for disposal of TPCertInfos created by TPCertGroup::buildCertGroup() */
399 TPCertGroup
certsToBeFreed(vfyCtx
.alloc
, TGO_Group
);
402 * First job is to find the cert which signed this response.
403 * Give priority to caller's LocalResponderCert.
405 if((ocspOpts
!= NULL
) && (ocspOpts
->LocalResponderCert
!= NULL
)) {
406 TPCertInfo
*responderInfo
= NULL
;
407 issuerStat
= tpIsOcspIssuer(vfyCtx
, ocspResp
,
408 ocspOpts
->LocalResponderCert
, NULL
,
409 OCT_Local
, issuer
, &responderInfo
);
412 foundBadIssuer
= true;
415 if(responderInfo
!= NULL
) {
416 /* can't use it - should this be an immediate error? */
417 delete responderInfo
;
421 assert(responderInfo
!= NULL
);
422 signerInfo
= signerInfoTBD
= responderInfo
;
423 foundLocalResponder
= true;
424 tpOcspDebug("tpVerifyOcspResp: signer := LocalResponderCert");
429 if((signerInfo
== NULL
) && (numSignerCerts
!= 0)) {
431 * App did not specify a local responder (or provided a bad one)
432 * and the response came with some certs. Try those.
434 TPCertInfo
*respCert
= NULL
;
435 for(unsigned dex
=0; dex
<numSignerCerts
; dex
++) {
436 const CSSM_DATA
*certData
= ocspResp
.signerCert(dex
);
437 if(signerInfo
== NULL
) {
438 /* stop trying this after we succeed... */
439 issuerStat
= tpIsOcspIssuer(vfyCtx
, ocspResp
,
441 OCT_Provided
, issuer
, &respCert
);
446 assert(respCert
!= NULL
);
447 signerInfo
= signerInfoTBD
= respCert
;
448 tpOcspDebug("tpVerifyOcspResp: signer := signerCert[%u]", dex
);
451 foundBadIssuer
= true;
457 * At least add this cert to certGroup for verification.
458 * OcspCert will own the TPCertInfo.
461 respCert
= new TPCertInfo(vfyCtx
.clHand
, vfyCtx
.cspHand
, certData
,
462 TIC_CopyData
, vfyCtx
.verifyTime
);
465 tpOcspDebug("tpVerifyOcspResp: BAD signerCert[%u]", dex
);
468 /* if we got a TPCertInfo, and it's not the signer, add it to certGroup */
469 if((respCert
!= NULL
) && (respCert
!= signerInfo
)) {
470 gatheredCerts
.appendCert(respCert
);
475 if((signerInfo
== NULL
) && (issuer
!= NULL
)) {
477 * Haven't found it yet, try the actual issuer
479 issuerStat
= tpIsOcspIssuer(vfyCtx
, ocspResp
,
481 OCT_Issuer
, issuer
, NULL
);
484 ourRtn
= ORS_Unknown
;
485 cssmErr
= CSSMERR_APPLETP_OCSP_SIG_ERROR
;
491 tpOcspDebug("tpVerifyOcspResp: signer := issuer");
496 if(signerInfo
== NULL
) {
497 if((issuer
!= NULL
) && !issuer
->isStatusFatal(CSSMERR_APPLETP_OCSP_NO_SIGNER
)) {
498 /* user wants to proceed without verifying! */
499 tpOcspDebug("tpVerifyOcspResp: no signer found, user allows!");
503 tpOcspDebug("tpVerifyOcspResp: no signer found");
504 ourRtn
= ORS_Unknown
;
505 /* caller adds to per-cert status */
506 cssmErr
= CSSMERR_APPLETP_OCSP_NO_SIGNER
;
511 if(signerInfo
!= NULL
&& !foundLocalResponder
) {
513 * tpIsOcspIssuer has verified that signerInfo is the signer of the
514 * OCSP response, and that it is either the issuer of the cert being
515 * checked or is a valid authorized responder for that issuer based on
516 * key id linkage and EKU. There is no stipulation in RFC2560 to also
517 * build the chain back to a trusted anchor; however, we'll continue to
518 * enforce this for the local responder case. (10742723)
520 tpOcspDebug("tpVerifyOcspResp SUCCESS");
526 * Last remaining task is to verify the signer, and all the certs back to
530 /* start from scratch with both of these groups */
531 gatheredCerts
.setAllUnused();
532 vfyCtx
.signerCerts
->setAllUnused();
533 crtn
= ocspCerts
.buildCertGroup(
534 *signerInfo
, // subject item
535 vfyCtx
.signerCerts
, // inCertGroup the original group-to-be-verified
536 vfyCtx
.dbList
, // optional
540 vfyCtx
.numAnchorCerts
,
542 certsToBeFreed
, // local to-be-freed right now
543 &gatheredCerts
, // accumulate gathered certs here
544 CSSM_FALSE
, // subjectIsInGroup
549 kSecTrustSettingsKeyUseSignRevocation
,
552 verifiedViaTrustSetting
);
554 tpOcspDebug("tpVerifyOcspResp buildCertGroup failure");
556 ourRtn
= ORS_Unknown
;
560 if(!verifiedToAnchor
&& !verifiedViaTrustSetting
) {
562 ourRtn
= ORS_Unknown
;
564 /* verified to root which is not an anchor */
565 tpOcspDebug("tpVerifyOcspResp root, no anchor");
566 cssmErr
= CSSMERR_APPLETP_OCSP_INVALID_ANCHOR_CERT
;
569 /* partial chain, no root, not verifiable by anchor */
570 tpOcspDebug("tpVerifyOcspResp no root, no anchor");
571 cssmErr
= CSSMERR_APPLETP_OCSP_NOT_TRUSTED
;
573 if((issuer
!= NULL
) && !issuer
->isStatusFatal(cssmErr
)) {
574 tpOcspDebug("...ignoring last error per trust setting");
578 ourRtn
= ORS_Unknown
;
582 tpOcspDebug("tpVerifyOcspResp SUCCESS; chain verified");
586 /* FIXME policy verify? */
589 delete signerInfoTBD
;
590 /* any other cleanup? */