/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ /* * DecodedCert.cpp - object representing a decoded cert, in NSS * format, with extensions parsed and decoded (still in NSS format). * * Created 9/1/2000 by Doug Mitchell. * Copyright (c) 2000 by Apple Computer. */ #include "DecodedCert.h" #include "clNssUtils.h" #include "cldebugging.h" #include "AppleX509CLSession.h" #include "CSPAttacher.h" #include #include // ??? #include "clExtensionTemplates.h" DecodedCert::DecodedCert( AppleX509CLSession &session) : DecodedItem(session) { memset(&mCert, 0, sizeof(mCert)); } /* one-shot constructor, decoding from DER-encoded data */ DecodedCert::DecodedCert( AppleX509CLSession &session, const CssmData &encodedCert) : DecodedItem(session) { memset(&mCert, 0, sizeof(mCert)); PRErrorCode prtn = mCoder.decode(encodedCert.data(), encodedCert.length(), NSS_SignedCertTemplate, &mCert); if(prtn) { CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); } mDecodedExtensions.decodeFromNss(mCert.tbs.extensions); mState = IS_DecodedAll; } DecodedCert::~DecodedCert() { } /* decode TBSCert and its extensions */ void DecodedCert::decodeTbs( const CssmData &encodedTbs) { assert(mState == IS_Empty); memset(&mCert, 0, sizeof(mCert)); PRErrorCode prtn = mCoder.decode(encodedTbs.data(), encodedTbs.length(), NSS_TBSCertificateTemplate, &mCert.tbs); if(prtn) { CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); } mDecodedExtensions.decodeFromNss(mCert.tbs.extensions); mState = IS_DecodedTBS; } void DecodedCert::encodeExtensions() { NSS_TBSCertificate &tbs = mCert.tbs; assert(mState == IS_Building); assert(tbs.extensions == NULL); if(mDecodedExtensions.numExtensions() == 0) { /* no extensions, no error */ return; } mDecodedExtensions.encodeToNss(tbs.extensions); } /* * FIXME : how to determine max encoding size at run time!? */ #define MAX_TEMPLATE_SIZE (8 * 1024) /* encode TBS component; only called from CertCreateTemplate */ void DecodedCert::encodeTbs( CssmOwnedData &encodedTbs) { encodeExtensions(); assert(mState == IS_Building); /* enforce required fields - could go deeper, maybe we should */ NSS_TBSCertificate &tbs = mCert.tbs; if((tbs.signature.algorithm.Data == NULL) || (tbs.issuer.rdns == NULL) || (tbs.subject.rdns == NULL) || (tbs.subjectPublicKeyInfo.subjectPublicKey.Data == NULL)) { clErrorLog("DecodedCert::encodeTbs: incomplete TBS"); /* an odd, undocumented error return */ CssmError::throwMe(CSSMERR_CL_NO_FIELD_VALUES); } PRErrorCode prtn; prtn = SecNssEncodeItemOdata(&tbs, NSS_TBSCertificateTemplate, encodedTbs); if(prtn) { CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); } } /* * Cook up CSSM_KEYUSE, gleaning as much as possible from * (optional) extensions. If no applicable extensions available, * we'll just return CSSM_KEYUSE_ANY. * * Note that the standard KeyUsage flags involving 'signing' translate * to verify since we're only dealing with public keys. */ CSSM_KEYUSE DecodedCert::inferKeyUsage() const { CSSM_KEYUSE keyUse = 0; const DecodedExten *decodedExten; uint32 numFields; /* Basic KeyUsage */ decodedExten = DecodedItem::findDecodedExt(CSSMOID_KeyUsage, false, 0, numFields); if(decodedExten) { CSSM_DATA *ku = (CSSM_DATA *)decodedExten->nssObj(); assert(ku != NULL); CE_KeyUsage kuse = clBitStringToKeyUsage(*ku); if(kuse & CE_KU_DigitalSignature) { keyUse |= CSSM_KEYUSE_VERIFY; } if(kuse & CE_KU_NonRepudiation) { keyUse |= CSSM_KEYUSE_VERIFY; } if(kuse & CE_KU_KeyEncipherment) { keyUse |= CSSM_KEYUSE_WRAP; } if(kuse & CE_KU_KeyAgreement) { keyUse |= CSSM_KEYUSE_DERIVE; } if(kuse & CE_KU_KeyCertSign) { keyUse |= CSSM_KEYUSE_VERIFY; } if(kuse & CE_KU_CRLSign) { keyUse |= CSSM_KEYUSE_VERIFY; } if(kuse & CE_KU_DataEncipherment) { keyUse |= CSSM_KEYUSE_ENCRYPT; } } /* Extended key usage */ decodedExten = DecodedItem::findDecodedExt(CSSMOID_ExtendedKeyUsage, false, 0, numFields); if(decodedExten) { NSS_ExtKeyUsage *euse = (NSS_ExtKeyUsage *)decodedExten->nssObj(); assert(euse != NULL); unsigned numUses = clNssArraySize((const void **)euse->purposes); for(unsigned dex=0; dexpurposes[dex]; if(clCompareCssmData(thisUse, &CSSMOID_ExtendedKeyUsageAny)) { /* we're done */ keyUse = CSSM_KEYUSE_ANY; break; } else if(clCompareCssmData(thisUse, &CSSMOID_ServerAuth)) { keyUse |= (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT); } else if(clCompareCssmData(thisUse, &CSSMOID_ClientAuth)) { keyUse |= (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT); } else if(clCompareCssmData(thisUse, &CSSMOID_ExtendedUseCodeSigning)) { keyUse |= CSSM_KEYUSE_VERIFY; } else if(clCompareCssmData(thisUse, &CSSMOID_EmailProtection)) { keyUse |= (CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_DERIVE); } else if(clCompareCssmData(thisUse, &CSSMOID_TimeStamping)) { keyUse |= CSSM_KEYUSE_VERIFY; } else if(clCompareCssmData(thisUse, &CSSMOID_OCSPSigning)) { keyUse |= CSSM_KEYUSE_VERIFY; } } } if(keyUse == 0) { /* Nothing found; take the default. */ keyUse = CSSM_KEYUSE_ANY; } return keyUse; } /* * Obtain a CSSM_KEY from a decoded cert, inferring as much as we can * from required fields (subjectPublicKeyInfo) and extensions (for * KeyUse). */ CSSM_KEY_PTR DecodedCert::extractCSSMKey( CssmAllocator &alloc) const { const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &keyInfo = mCert.tbs.subjectPublicKeyInfo; return CL_extractCSSMKeyNSS(keyInfo, alloc, this); }