#include "ocspResponse.h"
#include "ocspdUtils.h"
#include <Security/cssmapple.h>
#include <Security/oidscrl.h>
#include <Security/oidsalg.h>
#include "ocspdDebug.h"
#include <CommonCrypto/CommonDigest.h>
#include <security_cdsa_utilities/cssmerrors.h>
#include <Security/SecAsn1Templates.h>
static void allocCopyData(
const CSSM_DATA &src,
CSSM_DATA &dst)
{
if(src.Length == 0) {
dst.Data = NULL;
dst.Length = 0;
return;
}
dst.Data = (uint8 *)malloc(src.Length);
memmove(dst.Data, src.Data, src.Length);
dst.Length = src.Length;
}
static void freeData(
CSSM_DATA &d)
{
if(d.Data) {
free(d.Data);
d.Data = NULL;
}
d.Length = 0;
}
#pragma mark ---- OCSPClientCertID ----
OCSPClientCertID::OCSPClientCertID(
const CSSM_DATA &issuerName,
const CSSM_DATA &issuerPubKey,
const CSSM_DATA &subjectSerial)
{
mEncoded.Data = NULL;
mEncoded.Length = 0;
allocCopyData(issuerName, mIssuerName);
allocCopyData(issuerPubKey, mIssuerPubKey);
allocCopyData(subjectSerial, mSubjectSerial);
}
OCSPClientCertID::~OCSPClientCertID()
{
freeData(mIssuerName);
freeData(mIssuerPubKey);
freeData(mSubjectSerial);
freeData(mEncoded);
}
static uint8 nullParam[2] = {5, 0};
const CSSM_DATA *OCSPClientCertID::encode()
{
if(mEncoded.Data != NULL) {
return &mEncoded;
}
SecAsn1OCSPCertID certID;
uint8 issuerNameHash[CC_SHA1_DIGEST_LENGTH];
uint8 pubKeyHash[CC_SHA1_DIGEST_LENGTH];
certID.algId.algorithm = CSSMOID_SHA1;
certID.algId.parameters.Data = nullParam;
certID.algId.parameters.Length = sizeof(nullParam);
ocspdSha1(mIssuerName.Data, (CC_LONG)mIssuerName.Length, issuerNameHash);
ocspdSha1(mIssuerPubKey.Data, (CC_LONG)mIssuerPubKey.Length, pubKeyHash);
certID.issuerNameHash.Data = issuerNameHash;
certID.issuerNameHash.Length = CC_SHA1_DIGEST_LENGTH;
certID.issuerPubKeyHash.Data = pubKeyHash;
certID.issuerPubKeyHash.Length = CC_SHA1_DIGEST_LENGTH;
certID.serialNumber = mSubjectSerial;
SecAsn1CoderRef coder;
SecAsn1CoderCreate(&coder);
CSSM_DATA tmp = {0, NULL};
SecAsn1EncodeItem(coder, &certID, kSecAsn1OCSPCertIDTemplate, &tmp);
allocCopyData(tmp, mEncoded);
SecAsn1CoderRelease(coder);
return &mEncoded;
}
typedef void (*hashFcn)(const void *data, CC_LONG len, unsigned char *md);
bool OCSPClientCertID::compareToExist(
const SecAsn1OCSPCertID &exist)
{
if(!ocspdCompareCssmData(&mSubjectSerial, &exist.serialNumber)) {
return false;
}
hashFcn hf = NULL;
const CSSM_OID *alg = &exist.algId.algorithm;
uint8 digest[OCSPD_MAX_DIGEST_LEN];
CSSM_DATA digestData = {0, digest};
if(ocspdCompareCssmData(alg, &CSSMOID_SHA1)) {
hf = ocspdSha1;
digestData.Length = CC_SHA1_DIGEST_LENGTH;
}
else if(ocspdCompareCssmData(alg, &CSSMOID_MD5)) {
hf = ocspdMD5;
digestData.Length = CC_MD5_DIGEST_LENGTH;
}
else if(ocspdCompareCssmData(alg, &CSSMOID_MD4)) {
hf = ocspdMD4;
digestData.Length = CC_MD4_DIGEST_LENGTH;
}
else {
return false;
}
hf(mIssuerName.Data, (CC_LONG)mIssuerName.Length, digest);
if(!ocspdCompareCssmData(&digestData, &exist.issuerNameHash)) {
return false;
}
hf(mIssuerPubKey.Data, (CC_LONG)mIssuerPubKey.Length, digest);
if(!ocspdCompareCssmData(&digestData, &exist.issuerPubKeyHash)) {
return false;
}
return true;
}
bool OCSPClientCertID::compareToExist(
const CSSM_DATA &exist)
{
SecAsn1CoderRef coder;
SecAsn1OCSPCertID certID;
bool brtn = false;
SecAsn1CoderCreate(&coder);
memset(&certID, 0, sizeof(certID));
if(SecAsn1DecodeData(coder, &exist, kSecAsn1OCSPCertIDTemplate, &certID)) {
goto errOut;
}
brtn = compareToExist(certID);
errOut:
SecAsn1CoderRelease(coder);
return brtn;
}
#pragma mark ---- OCSPSingleResponse ----
OCSPSingleResponse::OCSPSingleResponse(
SecAsn1OCSPSingleResponse *resp)
: mCertStatus(CS_NotParsed),
mThisUpdate(NULL_TIME),
mNextUpdate(NULL_TIME),
mRevokedTime(NULL_TIME),
mCrlReason(CrlReason_NONE),
mExtensions(NULL)
{
assert(resp != NULL);
SecAsn1CoderCreate(&mCoder);
if((resp->certStatus.Data == NULL) || (resp->certStatus.Length == 0)) {
ocspdErrorLog("OCSPSingleResponse: bad certStatus\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
mCertStatus = (SecAsn1OCSPCertStatusTag)(resp->certStatus.Data[0] & SEC_ASN1_TAGNUM_MASK);
if(mCertStatus == CS_Revoked) {
SecAsn1OCSPCertStatus certStatus;
memset(&certStatus, 0, sizeof(certStatus));
if(SecAsn1DecodeData(mCoder, &resp->certStatus,
kSecAsn1OCSPCertStatusRevokedTemplate, &certStatus)) {
ocspdErrorLog("OCSPSingleResponse: err decoding certStatus\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
SecAsn1OCSPRevokedInfo *revokedInfo = certStatus.revokedInfo;
if(revokedInfo != NULL) {
mRevokedTime = genTimeToCFAbsTime(&revokedInfo->revocationTime);
const CSSM_DATA *revReason = revokedInfo->revocationReason;
if((revReason != NULL) &&
(revReason->Data != NULL) &&
(revReason->Length != 0)) {
mCrlReason = revReason->Data[0];
}
}
}
mThisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
if(resp->nextUpdate != NULL) {
mNextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
}
mExtensions = new OCSPExtensions(resp->singleExtensions);
ocspdDebug("OCSPSingleResponse: status %d reason %d", (int)mCertStatus,
(int)mCrlReason);
}
OCSPSingleResponse::~OCSPSingleResponse()
{
delete mExtensions;
SecAsn1CoderRelease(mCoder);
}
#if 0
const CSSM_DATA *OCSPSingleResponse::*crlUrl()
{
return NULL;
}
#endif
const CSSM_DATA *OCSPSingleResponse::crlNum()
{
return NULL;
}
CFAbsoluteTime OCSPSingleResponse::crlTime()
{
return NULL_TIME;
}
CFAbsoluteTime OCSPSingleResponse::archiveCutoff()
{
return NULL_TIME;
}
#pragma mark ---- OCSPResponse ----
OCSPResponse::OCSPResponse(
const CSSM_DATA &resp,
CFTimeInterval defaultTTL) : mLatestNextUpdate(NULL_TIME),
mExpireTime(NULL_TIME),
mExtensions(NULL)
{
SecAsn1CoderCreate(&mCoder);
memset(&mTopResp, 0, sizeof(mTopResp));
memset(&mBasicResponse, 0, sizeof(mBasicResponse));
memset(&mResponseData, 0, sizeof(mResponseData));
memset(&mResponderId, 0, sizeof(mResponderId));
mResponderIdTag = (SecAsn1OCSPResponderIDTag)0; mEncResponderName.Data = NULL;
mEncResponderName.Length = 0;
if(SecAsn1DecodeData(mCoder, &resp, kSecAsn1OCSPResponseTemplate, &mTopResp)) {
ocspdErrorLog("OCSPResponse: decode failure at top level\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if((mTopResp.responseStatus.Data == NULL) ||
(mTopResp.responseStatus.Length == 0)) {
ocspdErrorLog("OCSPResponse: no responseStatus\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(mTopResp.responseStatus.Data[0] != RS_Success) {
return;
}
if(mTopResp.responseBytes == NULL) {
ocspdErrorLog("OCSPResponse: empty responseBytes\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(!ocspdCompareCssmData(&mTopResp.responseBytes->responseType,
&CSSMOID_PKIX_OCSP_BASIC)) {
ocspdErrorLog("OCSPResponse: unknown responseType\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(SecAsn1DecodeData(mCoder, &mTopResp.responseBytes->response,
kSecAsn1OCSPBasicResponseTemplate, &mBasicResponse)) {
ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPBasicResponse\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(SecAsn1DecodeData(mCoder, &mBasicResponse.tbsResponseData,
kSecAsn1OCSPResponseDataTemplate, &mResponseData)) {
ocspdErrorLog("OCSPResponse: decode failure at SecAsn1OCSPResponseData\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(mResponseData.responderID.Data == NULL) {
ocspdErrorLog("OCSPResponse: bad responderID\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
mResponderIdTag = (SecAsn1OCSPResponderIDTag)
(mResponseData.responderID.Data[0] & SEC_ASN1_TAGNUM_MASK);
const SecAsn1Template *templ;
switch(mResponderIdTag) {
case RIT_Name:
templ = kSecAsn1OCSPResponderIDAsNameTemplate;
break;
case RIT_Key:
templ = kSecAsn1OCSPResponderIDAsKeyTemplate;
break;
default:
ocspdErrorLog("OCSPResponse: bad responderID tag\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(SecAsn1DecodeData(mCoder, &mResponseData.responderID, templ, &mResponderId)) {
ocspdErrorLog("OCSPResponse: decode failure at responderID\n");
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
if(!calculateValidity(defaultTTL)) {
CssmError::throwMe(CSSMERR_APPLETP_OCSP_BAD_RESPONSE);
}
mExtensions = new OCSPExtensions(mResponseData.responseExtensions);
}
OCSPResponse::~OCSPResponse()
{
delete mExtensions;
SecAsn1CoderRelease(mCoder);
}
SecAsn1OCSPResponseStatus OCSPResponse::responseStatus()
{
assert(mTopResp.responseStatus.Data != NULL);
return (SecAsn1OCSPResponseStatus)(mTopResp.responseStatus.Data[0]);
}
const CSSM_DATA *OCSPResponse::nonce()
{
OCSPExtension *ext = mExtensions->findExtension(CSSMOID_PKIX_OCSP_NONCE);
if(ext == NULL) {
return NULL;
}
OCSPNonce *nonceExt = dynamic_cast<OCSPNonce *>(ext);
return &(nonceExt->nonce());
}
CFAbsoluteTime OCSPResponse::producedAt()
{
return genTimeToCFAbsTime(&mResponseData.producedAt);
}
uint32 OCSPResponse::numSignerCerts()
{
return ocspdArraySize((const void **)mBasicResponse.certs);
}
const CSSM_DATA *OCSPResponse::signerCert(uint32 dex)
{
uint32 numCerts = numSignerCerts();
if(dex >= numCerts) {
ocspdErrorLog("OCSPResponse::signerCert: numCerts overflow\n");
CssmError::throwMe(CSSMERR_TP_INTERNAL_ERROR);
}
return mBasicResponse.certs[dex];
}
OCSPSingleResponse *OCSPResponse::singleResponseFor(OCSPClientCertID &matchCertID)
{
unsigned numResponses = ocspdArraySize((const void **)mResponseData.responses);
for(unsigned dex=0; dex<numResponses; dex++) {
SecAsn1OCSPSingleResponse *resp = mResponseData.responses[dex];
SecAsn1OCSPCertID &certID = resp->certID;
if(matchCertID.compareToExist(certID)) {
try {
OCSPSingleResponse *singleResp = new OCSPSingleResponse(resp);
return singleResp;
}
catch(...) {
continue;
}
}
}
ocspdDebug("OCSPResponse::singleResponse: certID not found");
return NULL;
}
const CSSM_DATA *OCSPResponse::encResponderName()
{
if(mResponderIdTag != RIT_Name) {
assert(0);
return NULL;
}
if(mEncResponderName.Data != NULL) {
return &mEncResponderName;
}
if(SecAsn1EncodeItem(mCoder, &mResponderId.byName, kSecAsn1AnyTemplate,
&mEncResponderName)) {
ocspdDebug("OCSPResponse::encResponderName: error encoding ResponderId!");
return NULL;
}
return &mEncResponderName;
}
OCSPSingleResponse *OCSPResponse::singleResponseFor(const CSSM_DATA &matchCertID)
{
unsigned numResponses = ocspdArraySize((const void **)mResponseData.responses);
for(unsigned dex=0; dex<numResponses; dex++) {
SecAsn1OCSPSingleResponse *resp = mResponseData.responses[dex];
CSSM_DATA certID = {0, NULL};
if(SecAsn1EncodeItem(mCoder, &resp->certID, kSecAsn1OCSPCertIDTemplate,
&certID)) {
ocspdDebug("OCSPResponse::singleResponse: error encoding certID!");
return NULL;
}
if(!ocspdCompareCssmData(&matchCertID, &certID)) {
continue;
}
try {
OCSPSingleResponse *singleResp = new OCSPSingleResponse(resp);
return singleResp;
}
catch(...) {
continue;
}
}
ocspdDebug("OCSPResponse::singleResponse: certID not found");
return NULL;
}
bool OCSPResponse::calculateValidity(CFTimeInterval defaultTTL)
{
mLatestNextUpdate = NULL_TIME;
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
unsigned numResponses = ocspdArraySize((const void **)mResponseData.responses);
for(unsigned dex=0; dex<numResponses; dex++) {
SecAsn1OCSPSingleResponse *resp = mResponseData.responses[dex];
CFAbsoluteTime thisUpdate = genTimeToCFAbsTime(&resp->thisUpdate);
if(thisUpdate > now) {
ocspdErrorLog("OCSPResponse::calculateValidity: thisUpdate not passed\n");
return false;
}
if(resp->nextUpdate != NULL) {
CFAbsoluteTime nextUpdate = genTimeToCFAbsTime(resp->nextUpdate);
if(nextUpdate > mLatestNextUpdate) {
mLatestNextUpdate = nextUpdate;
}
}
}
CFAbsoluteTime defaultExpire = now + defaultTTL;
if(mLatestNextUpdate == NULL_TIME) {
mExpireTime = defaultExpire;
}
else if(defaultExpire < mLatestNextUpdate) {
mExpireTime = defaultExpire;
}
else {
if(mLatestNextUpdate < now) {
ocspdErrorLog("OCSPResponse::calculateValidity: now > mLatestNextUpdate\n");
return false;
}
mExpireTime = mLatestNextUpdate;
}
return true;
}