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 * ocspResponse.cpp - OCSP Response class
27 #include "ocspResponse.h"
28 #include "ocspdUtils.h"
29 #include <Security/cssmapple.h>
30 #include <Security/oidscrl.h>
31 #include <Security/oidsalg.h>
32 #include "ocspdDebug.h"
33 #include <CommonCrypto/CommonDigest.h>
34 #include <security_cdsa_utilities/cssmerrors.h>
35 #include <Security/SecAsn1Templates.h>
37 /* malloc & copy CSSM_DATA using std malloc */
38 static void allocCopyData(
47 dst
.Data
= (uint8
*)malloc(src
.Length
);
48 memmove(dst
.Data
, src
.Data
, src
.Length
);
49 dst
.Length
= src
.Length
;
52 /* std free() of a CSSM_DATA */
63 #pragma mark ---- OCSPClientCertID ----
66 * Basic constructor given issuer's public key and name, and subject's
69 OCSPClientCertID::OCSPClientCertID(
70 const CSSM_DATA
&issuerName
,
71 const CSSM_DATA
&issuerPubKey
,
72 const CSSM_DATA
&subjectSerial
)
76 allocCopyData(issuerName
, mIssuerName
);
77 allocCopyData(issuerPubKey
, mIssuerPubKey
);
78 allocCopyData(subjectSerial
, mSubjectSerial
);
81 OCSPClientCertID::~OCSPClientCertID()
83 freeData(mIssuerName
);
84 freeData(mIssuerPubKey
);
85 freeData(mSubjectSerial
);
89 /* preencoded DER NULL */
90 static uint8 nullParam
[2] = {5, 0};
93 * DER encode in specified coder's memory.
95 const CSSM_DATA
*OCSPClientCertID::encode()
97 if(mEncoded
.Data
!= NULL
) {
101 SecAsn1OCSPCertID certID
;
102 uint8 issuerNameHash
[CC_SHA1_DIGEST_LENGTH
];
103 uint8 pubKeyHash
[CC_SHA1_DIGEST_LENGTH
];
105 /* algId refers to the hash we'll perform in issuer name and key */
106 certID
.algId
.algorithm
= CSSMOID_SHA1
;
107 certID
.algId
.parameters
.Data
= nullParam
;
108 certID
.algId
.parameters
.Length
= sizeof(nullParam
);
110 /* SHA1(issuerName) */
111 ocspdSha1(mIssuerName
.Data
, (CC_LONG
)mIssuerName
.Length
, issuerNameHash
);
113 /* SHA1(issuer public key bytes) */
114 ocspdSha1(mIssuerPubKey
.Data
, (CC_LONG
)mIssuerPubKey
.Length
, pubKeyHash
);
116 /* build the CertID from those components */
117 certID
.issuerNameHash
.Data
= issuerNameHash
;
118 certID
.issuerNameHash
.Length
= CC_SHA1_DIGEST_LENGTH
;
119 certID
.issuerPubKeyHash
.Data
= pubKeyHash
;
120 certID
.issuerPubKeyHash
.Length
= CC_SHA1_DIGEST_LENGTH
;
121 certID
.serialNumber
= mSubjectSerial
;
124 SecAsn1CoderRef coder
;
125 SecAsn1CoderCreate(&coder
);
127 CSSM_DATA tmp
= {0, NULL
};
128 SecAsn1EncodeItem(coder
, &certID
, kSecAsn1OCSPCertIDTemplate
, &tmp
);
129 allocCopyData(tmp
, mEncoded
);
130 SecAsn1CoderRelease(coder
);
135 * Does this object refer to the same cert as specified SecAsn1OCSPCertID?
136 * This is the main purpose of this class's existence; this function works
137 * even if specified SecAsn1OCSPCertID uses a different hash algorithm
138 * than we do, since we keep copies of our basic components.
140 * Returns true if compare successful.
142 typedef void (*hashFcn
)(const void *data
, CC_LONG len
, unsigned char *md
);
144 bool OCSPClientCertID::compareToExist(
145 const SecAsn1OCSPCertID
&exist
)
148 if(!ocspdCompareCssmData(&mSubjectSerial
, &exist
.serialNumber
)) {
153 const CSSM_OID
*alg
= &exist
.algId
.algorithm
;
154 uint8 digest
[OCSPD_MAX_DIGEST_LEN
];
155 CSSM_DATA digestData
= {0, digest
};
157 if(ocspdCompareCssmData(alg
, &CSSMOID_SHA1
)) {
159 digestData
.Length
= CC_SHA1_DIGEST_LENGTH
;
161 else if(ocspdCompareCssmData(alg
, &CSSMOID_MD5
)) {
163 digestData
.Length
= CC_MD5_DIGEST_LENGTH
;
165 else if(ocspdCompareCssmData(alg
, &CSSMOID_MD4
)) {
167 digestData
.Length
= CC_MD4_DIGEST_LENGTH
;
169 /* an OID for SHA256? */
174 /* generate digests using exist's hash algorithm */
175 hf(mIssuerName
.Data
, (CC_LONG
)mIssuerName
.Length
, digest
);
176 if(!ocspdCompareCssmData(&digestData
, &exist
.issuerNameHash
)) {
179 hf(mIssuerPubKey
.Data
, (CC_LONG
)mIssuerPubKey
.Length
, digest
);
180 if(!ocspdCompareCssmData(&digestData
, &exist
.issuerPubKeyHash
)) {
187 bool OCSPClientCertID::compareToExist(
188 const CSSM_DATA
&exist
)
190 SecAsn1CoderRef coder
;
191 SecAsn1OCSPCertID certID
;
194 SecAsn1CoderCreate(&coder
);
195 memset(&certID
, 0, sizeof(certID
));
196 if(SecAsn1DecodeData(coder
, &exist
, kSecAsn1OCSPCertIDTemplate
, &certID
)) {
199 brtn
= compareToExist(certID
);
201 SecAsn1CoderRelease(coder
);
205 #pragma mark ---- OCSPSingleResponse ----
208 * Constructor, called by OCSPResponse.
210 OCSPSingleResponse::OCSPSingleResponse(
211 SecAsn1OCSPSingleResponse
*resp
)
212 : mCertStatus(CS_NotParsed
),
213 mThisUpdate(NULL_TIME
),
214 mNextUpdate(NULL_TIME
),
215 mRevokedTime(NULL_TIME
),
216 mCrlReason(CrlReason_NONE
),
219 assert(resp
!= NULL
);
221 SecAsn1CoderCreate(&mCoder
);
222 if((resp
->certStatus
.Data
== NULL
) || (resp
->certStatus
.Length
== 0)) {
223 ocspdErrorLog("OCSPSingleResponse: bad certStatus\n");
224 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
226 mCertStatus
= (SecAsn1OCSPCertStatusTag
)(resp
->certStatus
.Data
[0] & SEC_ASN1_TAGNUM_MASK
);
227 if(mCertStatus
== CS_Revoked
) {
228 /* decode further to get SecAsn1OCSPRevokedInfo */
229 SecAsn1OCSPCertStatus certStatus
;
230 memset(&certStatus
, 0, sizeof(certStatus
));
231 if(SecAsn1DecodeData(mCoder
, &resp
->certStatus
,
232 kSecAsn1OCSPCertStatusRevokedTemplate
, &certStatus
)) {
233 ocspdErrorLog("OCSPSingleResponse: err decoding certStatus\n");
234 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
236 SecAsn1OCSPRevokedInfo
*revokedInfo
= certStatus
.revokedInfo
;
237 if(revokedInfo
!= NULL
) {
238 /* Treat this as optional even for CS_Revoked */
239 mRevokedTime
= genTimeToCFAbsTime(&revokedInfo
->revocationTime
);
240 const CSSM_DATA
*revReason
= revokedInfo
->revocationReason
;
241 if((revReason
!= NULL
) &&
242 (revReason
->Data
!= NULL
) &&
243 (revReason
->Length
!= 0)) {
244 mCrlReason
= revReason
->Data
[0];
248 mThisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
249 if(resp
->nextUpdate
!= NULL
) {
250 mNextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
252 mExtensions
= new OCSPExtensions(resp
->singleExtensions
);
253 ocspdDebug("OCSPSingleResponse: status %d reason %d", (int)mCertStatus
,
257 OCSPSingleResponse::~OCSPSingleResponse()
260 SecAsn1CoderRelease(mCoder
);
263 /*** Extensions-specific accessors ***/
265 const CSSM_DATA
*OCSPSingleResponse::*crlUrl()
272 const CSSM_DATA
*OCSPSingleResponse::crlNum()
279 CFAbsoluteTime
OCSPSingleResponse::crlTime() /* may be NULL_TIME */
286 CFAbsoluteTime
OCSPSingleResponse::archiveCutoff()
292 #pragma mark ---- OCSPResponse ----
294 OCSPResponse::OCSPResponse(
295 const CSSM_DATA
&resp
,
296 CFTimeInterval defaultTTL
) // default time-to-live in seconds
297 : mLatestNextUpdate(NULL_TIME
),
298 mExpireTime(NULL_TIME
),
301 SecAsn1CoderCreate(&mCoder
);
302 memset(&mTopResp
, 0, sizeof(mTopResp
));
303 memset(&mBasicResponse
, 0, sizeof(mBasicResponse
));
304 memset(&mResponseData
, 0, sizeof(mResponseData
));
305 memset(&mResponderId
, 0, sizeof(mResponderId
));
306 mResponderIdTag
= (SecAsn1OCSPResponderIDTag
)0; // invalid
307 mEncResponderName
.Data
= NULL
;
308 mEncResponderName
.Length
= 0;
310 if(SecAsn1DecodeData(mCoder
, &resp
, kSecAsn1OCSPResponseTemplate
, &mTopResp
)) {
311 ocspdErrorLog("OCSPResponse: decode failure at top level\n");
312 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
315 /* remainder is valid only on RS_Success */
316 if((mTopResp
.responseStatus
.Data
== NULL
) ||
317 (mTopResp
.responseStatus
.Length
== 0)) {
318 ocspdErrorLog("OCSPResponse: no responseStatus\n");
319 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
321 if(mTopResp
.responseStatus
.Data
[0] != RS_Success
) {
322 /* not a failure of our constructor; this object is now useful, but
323 * only for this one byte of status info */
326 if(mTopResp
.responseBytes
== NULL
) {
327 /* I don't see how this can be legal on RS_Success */
328 ocspdErrorLog("OCSPResponse: empty responseBytes\n");
329 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
331 if(!ocspdCompareCssmData(&mTopResp
.responseBytes
->responseType
,
332 &CSSMOID_PKIX_OCSP_BASIC
)) {
333 ocspdErrorLog("OCSPResponse: unknown responseType\n");
334 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
337 /* decode the SecAsn1OCSPBasicResponse */
338 if(SecAsn1DecodeData(mCoder
, &mTopResp
.responseBytes
->response
,
339 kSecAsn1OCSPBasicResponseTemplate
, &mBasicResponse
)) {
340 ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse\n");
341 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
344 /* signature and cert evaluation done externally */
346 /* decode the SecAsn1OCSPResponseData */
347 if(SecAsn1DecodeData(mCoder
, &mBasicResponse
.tbsResponseData
,
348 kSecAsn1OCSPResponseDataTemplate
, &mResponseData
)) {
349 ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData\n");
350 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
352 if(mResponseData
.responderID
.Data
== NULL
) {
353 ocspdErrorLog("OCSPResponse: bad responderID\n");
354 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
357 /* choice processing for ResponderID */
358 mResponderIdTag
= (SecAsn1OCSPResponderIDTag
)
359 (mResponseData
.responderID
.Data
[0] & SEC_ASN1_TAGNUM_MASK
);
360 const SecAsn1Template
*templ
;
361 switch(mResponderIdTag
) {
363 templ
= kSecAsn1OCSPResponderIDAsNameTemplate
;
366 templ
= kSecAsn1OCSPResponderIDAsKeyTemplate
;
369 ocspdErrorLog("OCSPResponse: bad responderID tag\n");
370 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
372 if(SecAsn1DecodeData(mCoder
, &mResponseData
.responderID
, templ
, &mResponderId
)) {
373 ocspdErrorLog("OCSPResponse: decode failure at responderID\n");
374 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
377 /* check temporal validity */
378 if(!calculateValidity(defaultTTL
)) {
380 CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE
);
384 * Individual responses looked into when we're asked for a specific one
385 * via singleResponse()
387 mExtensions
= new OCSPExtensions(mResponseData
.responseExtensions
);
390 OCSPResponse::~OCSPResponse()
393 SecAsn1CoderRelease(mCoder
);
396 SecAsn1OCSPResponseStatus
OCSPResponse::responseStatus()
398 assert(mTopResp
.responseStatus
.Data
!= NULL
); /* else constructor should have failed */
399 return (SecAsn1OCSPResponseStatus
)(mTopResp
.responseStatus
.Data
[0]);
402 const CSSM_DATA
*OCSPResponse::nonce() /* NULL means not present */
404 OCSPExtension
*ext
= mExtensions
->findExtension(CSSMOID_PKIX_OCSP_NONCE
);
408 OCSPNonce
*nonceExt
= dynamic_cast<OCSPNonce
*>(ext
);
409 return &(nonceExt
->nonce());
412 CFAbsoluteTime
OCSPResponse::producedAt()
414 return genTimeToCFAbsTime(&mResponseData
.producedAt
);
417 uint32
OCSPResponse::numSignerCerts()
419 return ocspdArraySize((const void **)mBasicResponse
.certs
);
422 const CSSM_DATA
*OCSPResponse::signerCert(uint32 dex
)
424 uint32 numCerts
= numSignerCerts();
425 if(dex
>= numCerts
) {
426 ocspdErrorLog("OCSPResponse::signerCert: numCerts overflow\n");
427 CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR
);
429 return mBasicResponse
.certs
[dex
];
433 * Obtain a OCSPSingleResponse for a given "smart" CertID.
435 OCSPSingleResponse
*OCSPResponse::singleResponseFor(OCSPClientCertID
&matchCertID
)
437 unsigned numResponses
= ocspdArraySize((const void **)mResponseData
.responses
);
438 for(unsigned dex
=0; dex
<numResponses
; dex
++) {
439 SecAsn1OCSPSingleResponse
*resp
= mResponseData
.responses
[dex
];
440 SecAsn1OCSPCertID
&certID
= resp
->certID
;
441 if(matchCertID
.compareToExist(certID
)) {
443 OCSPSingleResponse
*singleResp
= new OCSPSingleResponse(resp
);
447 /* try to find another... */
452 ocspdDebug("OCSPResponse::singleResponse: certID not found");
457 * If responderID is of form RIT_Name, return the encoded version of the
458 * NSS_Name (for comparison with issuer's subjectName). Evaluated lazily,
459 * once, in mCoder space.
461 const CSSM_DATA
*OCSPResponse::encResponderName()
463 if(mResponderIdTag
!= RIT_Name
) {
467 if(mEncResponderName
.Data
!= NULL
) {
468 return &mEncResponderName
;
470 if(SecAsn1EncodeItem(mCoder
, &mResponderId
.byName
, kSecAsn1AnyTemplate
,
471 &mEncResponderName
)) {
472 ocspdDebug("OCSPResponse::encResponderName: error encoding ResponderId!");
475 return &mEncResponderName
;
479 * Obtain a OCSPSingleResponse for a given raw encoded CertID.
481 OCSPSingleResponse
*OCSPResponse::singleResponseFor(const CSSM_DATA
&matchCertID
)
483 unsigned numResponses
= ocspdArraySize((const void **)mResponseData
.responses
);
484 for(unsigned dex
=0; dex
<numResponses
; dex
++) {
485 SecAsn1OCSPSingleResponse
*resp
= mResponseData
.responses
[dex
];
486 CSSM_DATA certID
= {0, NULL
};
487 if(SecAsn1EncodeItem(mCoder
, &resp
->certID
, kSecAsn1OCSPCertIDTemplate
,
489 ocspdDebug("OCSPResponse::singleResponse: error encoding certID!");
492 if(!ocspdCompareCssmData(&matchCertID
, &certID
)) {
493 /* try to find another */
497 OCSPSingleResponse
*singleResp
= new OCSPSingleResponse(resp
);
501 /* try to find another... */
505 ocspdDebug("OCSPResponse::singleResponse: certID not found");
511 * Calculate temporal validity; set mLatestNextUpdate and mExpireTime. Only
512 * called from constructor. Returns true if valid, else returns false.
514 bool OCSPResponse::calculateValidity(CFTimeInterval defaultTTL
)
516 mLatestNextUpdate
= NULL_TIME
;
517 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
519 unsigned numResponses
= ocspdArraySize((const void **)mResponseData
.responses
);
520 for(unsigned dex
=0; dex
<numResponses
; dex
++) {
521 SecAsn1OCSPSingleResponse
*resp
= mResponseData
.responses
[dex
];
524 * First off, a thisUpdate later than 'now' invalidates the whole response.
526 CFAbsoluteTime thisUpdate
= genTimeToCFAbsTime(&resp
->thisUpdate
);
527 if(thisUpdate
> now
) {
528 ocspdErrorLog("OCSPResponse::calculateValidity: thisUpdate not passed\n");
533 * Accumulate latest nextUpdate
535 if(resp
->nextUpdate
!= NULL
) {
536 CFAbsoluteTime nextUpdate
= genTimeToCFAbsTime(resp
->nextUpdate
);
537 if(nextUpdate
> mLatestNextUpdate
) {
538 mLatestNextUpdate
= nextUpdate
;
543 CFAbsoluteTime defaultExpire
= now
+ defaultTTL
;
544 if(mLatestNextUpdate
== NULL_TIME
) {
545 /* absolute expire time = current time plus default TTL */
546 mExpireTime
= defaultExpire
;
548 else if(defaultExpire
< mLatestNextUpdate
) {
549 /* caller more stringent than response */
550 mExpireTime
= defaultExpire
;
553 /* response more stringent than caller */
554 if(mLatestNextUpdate
< now
) {
555 ocspdErrorLog("OCSPResponse::calculateValidity: now > mLatestNextUpdate\n");
558 mExpireTime
= mLatestNextUpdate
;