X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_apple_x509_cl/lib/clNssUtils.cpp diff --git a/Security/libsecurity_apple_x509_cl/lib/clNssUtils.cpp b/Security/libsecurity_apple_x509_cl/lib/clNssUtils.cpp new file mode 100644 index 00000000..bad0ce39 --- /dev/null +++ b/Security/libsecurity_apple_x509_cl/lib/clNssUtils.cpp @@ -0,0 +1,1386 @@ +/* + * Copyright (c) 2003,2011-2012,2014 Apple 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. + */ + +/* + * clNssUtils.cpp - support for libnssasn1-based ASN1 encode/decode + */ + +#include "clNssUtils.h" +#include "clNameUtils.h" +#include "CSPAttacher.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma mark ----- ArenaAllocator ----- + +/* + * Avoid inlining this for debuggability + */ +void *ArenaAllocator::malloc(size_t len) throw(std::bad_alloc) +{ + try { + return mCoder.malloc(len); + } + catch (...) { + throw std::bad_alloc(); + } +} + +/* intentionally not implemented, should never be called */ +void ArenaAllocator::free(void *p) throw() +{ + throw std::bad_alloc(); +} + +void *ArenaAllocator::realloc(void *p, size_t len) throw(std::bad_alloc) +{ + throw std::bad_alloc(); +} + +#pragma mark ----- Malloc/Copy/Compare CSSM_DATA ----- + +/* + * Misc. alloc/copy with arbitrary Allocator + */ +/* malloc d.Data, set d.Length */ +void clAllocData( + Allocator &alloc, + CSSM_DATA &dst, + size_t len) +{ + if(len == 0) { + dst.Data = NULL; + } + else { + dst.Data = (uint8 *)alloc.malloc(len); + } + dst.Length = len; +} + +/* malloc and copy */ +void clAllocCopyData( + Allocator &alloc, + const CSSM_DATA &src, + CSSM_DATA &dst) +{ + clAllocData(alloc, dst, src.Length); + if(dst.Length != 0) { + memmove(dst.Data, src.Data, src.Length); + } +} + +/* + * Compare two CSSM_DATAs (or two CSSM_OIDs), return true if identical. + */ +bool clCompareCssmData( + const CSSM_DATA *data1, + const CSSM_DATA *data2) +{ + if((data1 == NULL) || (data1->Data == NULL) || + (data2 == NULL) || (data2->Data == NULL) || + (data1->Length != data2->Length)) { + return false; + } + if(data1->Length != data2->Length) { + return false; + } + if(memcmp(data1->Data, data2->Data, data1->Length) == 0) { + return true; + } + else { + return false; + } +} + +#pragma mark ----- CSSM_DATA <--> uint32 ----- + +uint32 clDataToInt( + const CSSM_DATA &cdata, + CSSM_RETURN toThrow) /* = CSSMERR_CL_INVALID_CERT_POINTER */ +{ + if((cdata.Length == 0) || (cdata.Data == NULL)) { + return 0; + } + size_t len = cdata.Length; + if(len > sizeof(uint32)) { + if(toThrow == 0) { + /* tolerate this */ + len = sizeof(uint32); + } + else { + CssmError::throwMe(toThrow); + } + } + + uint32 rtn = 0; + uint8 *cp = cdata.Data; + for(size_t i=0; i>= 8; + } +} + +#pragma mark ----- CSSM_BOOL <--> CSSM_DATA ----- +/* + * A Bool is encoded as one byte of either 0 or 0xff + * Default of NSS boolean not present is false + */ +CSSM_BOOL clNssBoolToCssm( + const CSSM_DATA &nssBool) +{ + if((nssBool.Data != NULL) && (nssBool.Data[0] == 0xff)) { + return CSSM_TRUE; + } + else { + return CSSM_FALSE; + } +} + +void clCssmBoolToNss( + CSSM_BOOL cBool, + CSSM_DATA &nssBool, + Allocator &alloc) +{ + uint32 num = cBool ? 0xff : 0; + clIntToData(num, nssBool, alloc); +} + +#pragma mark ----- Bit String manipulation ----- + +/* + * Adjust the length of a CSSM_DATA representing a pre-encoded + * bit string. On entry the length field is the number of bytes + * of data; en exit, the number if bits. Trailing zero bits + * are counted as unused (which is how KeyUsage and NetscapeCertType + * extensions are encoded). + */ +void clCssmBitStringToNss( + CSSM_DATA &b) +{ + size_t numBits = b.Length * 8; + + /* start at end of bit array, scanning backwards looking + * for the first set bit */ + bool foundSet = false; + for(ptrdiff_t dex=b.Length-1; dex>=0; dex--) { + unsigned bitMask = 0x01; + uint8 byte = b.Data[dex]; + for(unsigned bdex=0; bdex<8; bdex++) { + if(byte & bitMask) { + foundSet = true; + break; + } + else { + bitMask <<= 1; + numBits--; + } + } + if(foundSet) { + break; + } + } + /* !foundSet --> numBits = 0 */ + assert(((numBits > 0) & foundSet) || ((numBits == 0) && !foundSet)); + b.Length = numBits; +} + +/* + * On entry, Length is bit count; on exit, a byte count. + * The job here is to ensure that bits marked as "unused" in the + * BER encoding are cleared. Encoding rules say they are undefined in + * the actual encoding. + */ +void clNssBitStringToCssm( + CSSM_DATA &b) +{ + CSSM_SIZE byteCount = (b.Length + 7) / 8; + unsigned partialBits = b.Length & 0x7; + b.Length = byteCount; + if(partialBits == 0) { + return; + } + + /* mask off unused bits */ + unsigned unusedBits = 8 - partialBits; + uint8 *bp = b.Data + b.Length - 1; + /* mask = (2 ** unusedBits) - 1 */ + unsigned mask = (1 << unusedBits) - 1; + *bp &= ~mask; +} + +#pragma mark ----- NSS array manipulation ----- +/* + * How many items in a NULL-terminated array of pointers? + */ +unsigned clNssArraySize( + const void **array) +{ + unsigned count = 0; + if (array) { + while (*array++) { + count++; + } + } + return count; +} + +/* malloc a NULL-ed array of pointers of size num+1 */ +void **clNssNullArray( + uint32 num, + SecNssCoder &coder) +{ + unsigned len = (num + 1) * sizeof(void *); + void **p = (void **)coder.malloc(len); + memset(p, 0, len); + return p; +} + +/* + * GIven a CSSM_DATA containing a decoded BIT_STRING, + * convert to a KeyUsage. + */ +CE_KeyUsage clBitStringToKeyUsage( + const CSSM_DATA &cdata) +{ + size_t toCopy = (cdata.Length + 7) / 8; + if(toCopy > 2) { + /* I hope I never see this... */ + clErrorLog("clBitStringToKeyUsage: KeyUsage larger than 2 bytes!"); + toCopy = 2; + } + unsigned char bits[2] = {0, 0}; + memmove(bits, cdata.Data, toCopy); + CE_KeyUsage usage = (((unsigned)bits[0]) << 8) | bits[1]; + return usage; +} + +CSSM_ALGORITHMS CL_oidToAlg( + const CSSM_OID &oid) +{ + CSSM_ALGORITHMS alg; + bool found = cssmOidToAlg(&oid, &alg); + if(!found) { + clErrorLog("CL_oidToAlg: unknown alg\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + return alg; +} + +#pragma mark ----- copy CSSM_X509_ALGORITHM_IDENTIFIER ----- + +/* + * Copy CSSM_X509_ALGORITHM_IDENTIFIER, same format (NSS and CSSM). + */ +void CL_copyAlgId( + const CSSM_X509_ALGORITHM_IDENTIFIER &srcAlgId, + CSSM_X509_ALGORITHM_IDENTIFIER &dstAlgId, + Allocator &alloc) +{ + clAllocCopyData(alloc, srcAlgId.algorithm, dstAlgId.algorithm); + clAllocCopyData(alloc, srcAlgId.parameters, dstAlgId.parameters); +} + +void CL_freeCssmAlgId( + CSSM_X509_ALGORITHM_IDENTIFIER *cdsaObj, // optional + Allocator &alloc) +{ + if(cdsaObj == NULL) { + return; + } + alloc.free(cdsaObj->algorithm.Data); + alloc.free(cdsaObj->parameters.Data); + memset(cdsaObj, 0, sizeof(CSSM_X509_ALGORITHM_IDENTIFIER)); +} + + +#pragma mark ----- CSSM_X509_TIME <--> NSS format ----- + +/* + * Map the tag associated with a choice of DirectoryString elements to + * a template array for encoding/decoding that string type. + * Contrary to RFC2459, we allow the IA5String type, which is actually + * used in the real world (cf. the email address in Thawte's serverbasic + * cert). + */ + +/* The template chooser does the work here */ + +bool CL_nssTimeToCssm( + const NSS_TaggedItem &nssTime, + CSSM_X509_TIME &cssmObj, + Allocator &alloc) +{ + cssmObj.timeType = nssTime.tag; + clAllocCopyData(alloc, nssTime.item, cssmObj.time); + return true; +} + +/* + * CSSM time to NSS time. + */ +void CL_cssmTimeToNss( + const CSSM_X509_TIME &cssmTime, + NSS_TaggedItem &nssTime, + SecNssCoder &coder) +{ + nssTime.tag = cssmTime.timeType; + coder.allocCopyItem(cssmTime.time, nssTime.item); +} + +void CL_freeCssmTime( + CSSM_X509_TIME *cssmTime, + Allocator &alloc) +{ + if(cssmTime == NULL) { + return; + } + if(cssmTime->time.Data) { + alloc.free(cssmTime->time.Data); + } + memset(cssmTime, 0, sizeof(CSSM_X509_TIME)); +} + + +#pragma mark ----- CSSM_X509_SUBJECT_PUBLIC_KEY_INFO <--> CSSM_KEY ----- + +/* + * Copy a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO. + * + * Same format (NSS and CSSM), EXCEPT: + * + * Objects which have just been NSS decoded or are about to be + * NSS encoded have the subjectPublicKey.Length field in BITS + * since this field is wrapped in a BIT STRING upon encoding. + * + * Caller tells us which format (bits or bytes) + * to use for each of {src, dst}. + */ +void CL_copySubjPubKeyInfo( + const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &srcInfo, + bool srcInBits, + CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &dstInfo, + bool dstInBits, + Allocator &alloc) +{ + CL_copyAlgId(srcInfo.algorithm, dstInfo.algorithm, alloc); + + CSSM_DATA srcKey = srcInfo.subjectPublicKey; + if(srcInBits) { + srcKey.Length = (srcKey.Length + 7) / 8; + } + clAllocCopyData(alloc, srcKey, dstInfo.subjectPublicKey); + if(dstInBits) { + dstInfo.subjectPublicKey.Length *= 8; + } +} + +/* + * Obtain a CSSM_KEY from a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO, + * inferring as much as we can from required fields + * (CSSM_X509_SUBJECT_PUBLIC_KEY_INFO) and extensions (for + * KeyUse, obtained from the optional DecodedCert). + */ +CSSM_KEY_PTR CL_extractCSSMKeyNSS( + const CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &keyInfo, + Allocator &alloc, + const DecodedCert *decodedCert) // optional +{ + CSSM_KEY_PTR cssmKey = (CSSM_KEY_PTR) alloc.malloc(sizeof(CSSM_KEY)); + memset(cssmKey, 0, sizeof(CSSM_KEY)); + CSSM_KEYHEADER &hdr = cssmKey->KeyHeader; + CssmRemoteData keyData(alloc, cssmKey->KeyData); + + hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; + /* CspId blank */ + hdr.BlobType = CSSM_KEYBLOB_RAW; + hdr.AlgorithmId = CL_oidToAlg(keyInfo.algorithm.algorithm); + hdr.KeyAttr = CSSM_KEYATTR_MODIFIABLE | CSSM_KEYATTR_EXTRACTABLE; + + /* + * Format inferred from AlgorithmId. I have never seen these defined + * anywhere, e.g., what's the format of an RSA public key in a cert? + * X509 certainly doesn't say. However. the following two cases are + * known to be correct. + */ + switch(hdr.AlgorithmId) { + case CSSM_ALGID_RSA: + hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; + break; + case CSSM_ALGID_DSA: + case CSSM_ALGID_ECDSA: + case CSSM_ALGID_DH: + case CSSM_ALGMODE_PKCS1_EME_OAEP: + hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_X509; + break; + case CSSM_ALGID_FEE: + /* CSSM_KEYBLOB_RAW_FORMAT_NONE --> DER encoded */ + hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; + break; + default: + /* punt */ + hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_NONE; + } + hdr.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + + /* KeyUsage inferred from extensions */ + if(decodedCert) { + hdr.KeyUsage = decodedCert->inferKeyUsage(); + } + else { + hdr.KeyUsage = CSSM_KEYUSE_ANY; + } + + /* start/end date unknown, leave zero */ + hdr.WrapAlgorithmId = CSSM_ALGID_NONE; + hdr.WrapMode = CSSM_ALGMODE_NONE; + + switch(hdr.AlgorithmId) { + case CSSM_ALGID_DSA: + case CSSM_ALGID_ECDSA: + case CSSM_ALGID_DH: + case CSSM_ALGMODE_PKCS1_EME_OAEP: + { + /* + * Just encode the whole subject public key info blob. + * NOTE we're assuming that the keyInfo.subjectPublicKey + * field is in the NSS_native BITSTRING format, i.e., + * its Length field is in bits and we don't have to adjust. + */ + PRErrorCode prtn = SecNssEncodeItemOdata(&keyInfo, + kSecAsn1SubjectPublicKeyInfoTemplate, keyData); + if(prtn) { + clErrorLog("extractCSSMKey: error on reencode\n"); + CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); + } + break; + } + default: + /* + * RSA, FEE for now. + * keyInfo.subjectPublicKey (in BITS) ==> KeyData + */ + keyData.copy(keyInfo.subjectPublicKey.Data, + (keyInfo.subjectPublicKey.Length + 7) / 8); + } + + /* + * LogicalKeySizeInBits - ask the CSP + */ + CSSM_CSP_HANDLE cspHand = getGlobalCspHand(true); + CSSM_KEY_SIZE keySize; + CSSM_RETURN crtn; + crtn = CSSM_QueryKeySizeInBits(cspHand, CSSM_INVALID_HANDLE, cssmKey, + &keySize); + switch(crtn) { + default: + CssmError::throwMe(crtn); + case CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE: + /* + * This is how the CSP indicates a "partial" public key, + * with a valid public key value but no alg-specific + * parameters (currently, DSA only). + */ + hdr.KeyAttr |= CSSM_KEYATTR_PARTIAL; + /* and drop thru */ + case CSSM_OK: + cssmKey->KeyHeader.LogicalKeySizeInBits = + keySize.LogicalKeySizeInBits; + break; + } + + keyData.release(); + return cssmKey; +} + +/* + * Set up a encoded NULL for CSSM_X509_ALGORITHM_IDENTIFIER.parameters. + */ +void CL_nullAlgParams( + CSSM_X509_ALGORITHM_IDENTIFIER &algId) +{ + static const uint8 encNull[2] = { SEC_ASN1_NULL, 0 }; + CSSM_DATA encNullData; + encNullData.Data = (uint8 *)encNull; + encNullData.Length = 2; + + algId.parameters = encNullData; +} + +/* + * Convert a CSSM_KEY to a CSSM_X509_SUBJECT_PUBLIC_KEY_INFO. The + * CSSM key must be in raw format and with a specific blob format. + * -- RSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_PKCS1 + * -- DSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_X509 + * -- ECDSA keys have to be CSSM_KEYBLOB_RAW_FORMAT_X509 + */ +void CL_CSSMKeyToSubjPubKeyInfoNSS( + const CSSM_KEY &cssmKey, + CSSM_X509_SUBJECT_PUBLIC_KEY_INFO &nssKeyInfo, + SecNssCoder &coder) +{ + const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader; + if(hdr.BlobType != CSSM_KEYBLOB_RAW) { + clErrorLog("CL SetField: must specify RAW key blob\n"); + CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); + } + memset(&nssKeyInfo, 0, sizeof(nssKeyInfo)); + + /* algorithm and format dependent from here... */ + switch(hdr.AlgorithmId) { + case CSSM_ALGID_RSA: + if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_PKCS1) { + clErrorLog("CL SetField: RSA key must be in PKCS1 format\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); + } + /* and fall thru */ + default: + { + /* Key header's algorithm --> OID */ + const CSSM_OID *oid = cssmAlgToOid(hdr.AlgorithmId); + if(oid == NULL) { + clErrorLog("CL SetField: Unknown key algorithm\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + CSSM_X509_ALGORITHM_IDENTIFIER &algId = nssKeyInfo.algorithm; + coder.allocCopyItem(*oid, algId.algorithm); + + /* NULL algorithm parameters, always in this case */ + CL_nullAlgParams(algId); + + /* Copy key bits, destination is a BIT STRING */ + coder.allocCopyItem(cssmKey.KeyData, nssKeyInfo.subjectPublicKey); + nssKeyInfo.subjectPublicKey.Length *= 8; + break; + } + case CSSM_ALGID_DSA: + case CSSM_ALGID_ECDSA: + if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_X509) { + clErrorLog("CL SetField: DSA/ECDSA key must be in X509 format\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); + } + + /* + * All we do is decode the whole key blob into the + * SubjectPublicKeyInfo. + */ + if(coder.decodeItem(cssmKey.KeyData, + kSecAsn1SubjectPublicKeyInfoTemplate, + &nssKeyInfo)) { + clErrorLog("CL SetField: Error decoding DSA public key\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); + } + break; + } +} + +void CL_freeCSSMKey( + CSSM_KEY_PTR cssmKey, + Allocator &alloc, + bool freeTop) +{ + if(cssmKey == NULL) { + return; + } + alloc.free(cssmKey->KeyData.Data); + memset(cssmKey, 0, sizeof(CSSM_KEY)); + if(freeTop) { + alloc.free(cssmKey); + } +} + +#pragma mark ----- CE_AuthorityKeyID <--> NSS_AuthorityKeyId ----- + +void CL_cssmAuthorityKeyIdToNss( + const CE_AuthorityKeyID &cdsaObj, + NSS_AuthorityKeyId &nssObj, + SecNssCoder &coder) +{ + memset(&nssObj, 0, sizeof(nssObj)); + if(cdsaObj.keyIdentifierPresent) { + nssObj.keyIdentifier = (CSSM_DATA_PTR)coder.malloc(sizeof(CSSM_DATA)); + coder.allocCopyItem(cdsaObj.keyIdentifier, *nssObj.keyIdentifier); + } + if(cdsaObj.generalNamesPresent ) { + /* GeneralNames, the hard one */ + CL_cssmGeneralNamesToNss(*cdsaObj.generalNames, + nssObj.genNames, coder); + } + if(cdsaObj.serialNumberPresent) { + coder.allocCopyItem(cdsaObj.serialNumber,nssObj.serialNumber); + } +} + +void CL_nssAuthorityKeyIdToCssm( + const NSS_AuthorityKeyId &nssObj, + CE_AuthorityKeyID &cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + if(nssObj.keyIdentifier != NULL) { + cdsaObj.keyIdentifierPresent = CSSM_TRUE; + clAllocCopyData(alloc, *nssObj.keyIdentifier, cdsaObj.keyIdentifier); + } + if(nssObj.genNames.names != NULL) { + /* GeneralNames, the hard one */ + cdsaObj.generalNamesPresent = CSSM_TRUE; + cdsaObj.generalNames = + (CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames)); + CL_nssGeneralNamesToCssm(nssObj.genNames, + *cdsaObj.generalNames, + coder, + alloc); + } + if(nssObj.serialNumber.Data != NULL) { + cdsaObj.serialNumberPresent = CSSM_TRUE; + clAllocCopyData(alloc, nssObj.serialNumber, cdsaObj.serialNumber); + } +} + +#pragma mark ----- CE_AuthorityInfoAccess <--> NSS_AuthorityInfoAccess ----- + +void CL_cssmInfoAccessToNss( + const CE_AuthorityInfoAccess &cdsaObj, + NSS_AuthorityInfoAccess &nssObj, + SecNssCoder &coder) +{ + memset(&nssObj, 0, sizeof(nssObj)); + uint32 numDescs = cdsaObj.numAccessDescriptions; + nssObj.accessDescriptions = (NSS_AccessDescription **)clNssNullArray(numDescs, coder); + + for(unsigned dex=0; dex(); + CE_AccessDescription *src = &cdsaObj.accessDescriptions[dex]; + NSS_AccessDescription *dst = nssObj.accessDescriptions[dex]; + coder.allocCopyItem(src->accessMethod, dst->accessMethod); + + /* Convert general name, then encode it into destination */ + NSS_GeneralName nssGenName; + CL_cssmGeneralNameToNss(src->accessLocation, nssGenName, coder); + PRErrorCode prtn = coder.encodeItem(&nssGenName, kSecAsn1GeneralNameTemplate, + dst->encodedAccessLocation); + if(prtn) { + clErrorLog("CL_cssmInfoAccessToNss: encode error\n"); + CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); + } + } +} + +void CL_infoAccessToCssm( + const NSS_AuthorityInfoAccess &nssObj, + CE_AuthorityInfoAccess &cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + memset(&cdsaObj, 0, sizeof(cdsaObj)); + unsigned numDescs = clNssArraySize((const void **)nssObj.accessDescriptions); + if(numDescs == 0) { + return; + } + cdsaObj.accessDescriptions = (CE_AccessDescription *)alloc.malloc( + numDescs * sizeof(CE_AccessDescription)); + cdsaObj.numAccessDescriptions = numDescs; + for(unsigned dex=0; dexaccessMethod, dst->accessMethod); + + /* decode the general name */ + NSS_GeneralName nssGenName; + memset(&nssGenName, 0, sizeof(nssGenName)); + PRErrorCode prtn = coder.decodeItem(src->encodedAccessLocation, + kSecAsn1GeneralNameTemplate, &nssGenName); + if(prtn) { + clErrorLog("***Error decoding NSS_AuthorityInfoAccess.accessLocation\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + /* then convert the result to CSSM */ + CL_nssGeneralNameToCssm(nssGenName, dst->accessLocation, coder, alloc); + } +} + +void CL_freeInfoAccess( + CE_AuthorityInfoAccess &cssmInfo, + Allocator &alloc) +{ + uint32 numDescs = cssmInfo.numAccessDescriptions; + for(unsigned dex=0; dexaccessMethod.Data); + CL_freeCssmGeneralName(dst->accessLocation, alloc); + } + alloc.free(cssmInfo.accessDescriptions); +} + + +#pragma mark ----- CE_QC_Statements <--> NSS_QC_Statements ----- + +void CL_cssmQualCertStatementsToNss( + const CE_QC_Statements &cdsaObj, + NSS_QC_Statements &nssObj, + SecNssCoder &coder) +{ + memset(&nssObj, 0, sizeof(nssObj)); + uint32 numQcs = cdsaObj.numQCStatements; + nssObj.qcStatements = + (NSS_QC_Statement **)clNssNullArray(numQcs, coder); + for(uint32 dex=0; dexstatementId, dst->statementId); + if(src->semanticsInfo) { + if(src->otherInfo) { + /* this is either/or, not both */ + CssmError::throwMe(CSSMERR_CL_INVALID_FIELD_POINTER); + } + + /* encode this CE_SemanticsInformation */ + CE_SemanticsInformation *srcSI = src->semanticsInfo; + NSS_SemanticsInformation dstSI; + memset(&dstSI, 0, sizeof(dstSI)); + if(srcSI->semanticsIdentifier) { + dstSI.semanticsIdentifier = (CSSM_DATA_PTR)coder.malloc(sizeof(CSSM_DATA)); + coder.allocCopyItem(*srcSI->semanticsIdentifier, + *dstSI.semanticsIdentifier); + } + if(srcSI->nameRegistrationAuthorities) { + dstSI.nameRegistrationAuthorities = + (NSS_GeneralNames *)coder.malloc(sizeof(NSS_GeneralNames)); + CL_cssmGeneralNamesToNss(*srcSI->nameRegistrationAuthorities, + *dstSI.nameRegistrationAuthorities, coder); + } + PRErrorCode prtn = coder.encodeItem(&dstSI, kSecAsn1SemanticsInformationTemplate, + dst->info); + if(prtn) { + clErrorLog("CL_cssmQualCertStatementsToNss: encode error\n"); + CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); + } + + } + if(src->otherInfo) { + /* drop in as ASN_ANY */ + coder.allocCopyItem(*src->otherInfo, dst->info); + } + } +} + +void CL_qualCertStatementsToCssm( + const NSS_QC_Statements &nssObj, + CE_QC_Statements &cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + memset(&cdsaObj, 0, sizeof(cdsaObj)); + unsigned numQcs = clNssArraySize((const void **)nssObj.qcStatements); + if(numQcs == 0) { + return; + } + cdsaObj.qcStatements = (CE_QC_Statement *)alloc.malloc( + numQcs * sizeof(CE_AccessDescription)); + cdsaObj.numQCStatements = numQcs; + for(unsigned dex=0; dexstatementId, dst->statementId); + + /* + * Whether the optional info is a SemanticsInformation or is uninterpreted + * DER data depends on statementId. + */ + if(src->info.Data) { + if(clCompareCssmData(&src->statementId, &CSSMOID_OID_QCS_SYNTAX_V2)) { + NSS_SemanticsInformation srcSI; + memset(&srcSI, 0, sizeof(srcSI)); + + /* decode info as a NSS_SemanticsInformation */ + PRErrorCode prtn = coder.decodeItem(src->info, + kSecAsn1SemanticsInformationTemplate, &srcSI); + if(prtn) { + clErrorLog("***Error decoding CE_SemanticsInformation\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + /* NSS_SemanticsInformation --> CE_SemanticsInformation */ + dst->semanticsInfo = + (CE_SemanticsInformation *)alloc.malloc(sizeof(CE_SemanticsInformation)); + CE_SemanticsInformation *dstSI = dst->semanticsInfo; + memset(dstSI, 0, sizeof(*dstSI)); + if(srcSI.semanticsIdentifier) { + dstSI->semanticsIdentifier = (CSSM_OID *)alloc.malloc(sizeof(CSSM_OID)); + clAllocCopyData(alloc, *srcSI.semanticsIdentifier, *dstSI->semanticsIdentifier); + } + if(srcSI.nameRegistrationAuthorities) { + dstSI->nameRegistrationAuthorities = + (CE_NameRegistrationAuthorities *)alloc.malloc( + sizeof(CE_NameRegistrationAuthorities)); + CL_nssGeneralNamesToCssm(*srcSI.nameRegistrationAuthorities, + *dstSI->nameRegistrationAuthorities, + coder, + alloc); + } + } + else { + dst->otherInfo = (CSSM_DATA_PTR)alloc.malloc(sizeof(CSSM_DATA)); + clAllocCopyData(alloc, src->info, *dst->otherInfo); + } + } + } +} + +void CL_freeQualCertStatements( + CE_QC_Statements &cssmQCs, + Allocator &alloc) +{ + uint32 numQCs = cssmQCs.numQCStatements; + for(unsigned dex=0; dexstatementId.Data); + if(dst->semanticsInfo) { + CE_SemanticsInformation *si = dst->semanticsInfo; + if(si->semanticsIdentifier) { + alloc.free(si->semanticsIdentifier->Data); + alloc.free(si->semanticsIdentifier); + } + if(si->nameRegistrationAuthorities) { + CL_freeCssmGeneralNames(si->nameRegistrationAuthorities, alloc); + alloc.free(si->nameRegistrationAuthorities); + } + alloc.free(si); + } + if(dst->otherInfo) { + alloc.free(dst->otherInfo->Data); + alloc.free(dst->otherInfo); + } + } + alloc.free(cssmQCs.qcStatements); +} + +#pragma mark ----- decode/encode CE_DistributionPointName ----- + +/* This is always a DER-encoded blob at the NSS level */ +void CL_decodeDistributionPointName( + const CSSM_DATA &nssBlob, + CE_DistributionPointName &cssmDpn, + SecNssCoder &coder, + Allocator &alloc) +{ + memset(&cssmDpn, 0, sizeof(CE_DistributionPointName)); + if(nssBlob.Length == 0) { + clErrorLog("***CL_decodeDistributionPointName: bad PointName\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + unsigned char tag = nssBlob.Data[0] & SEC_ASN1_TAGNUM_MASK; + switch(tag) { + case NSS_DIST_POINT_FULL_NAME_TAG: + { + /* decode to temp coder memory */ + NSS_GeneralNames gnames; + gnames.names = NULL; + if(coder.decodeItem(nssBlob, kSecAsn1DistPointFullNameTemplate, + &gnames)) { + clErrorLog("***Error decoding DistPointFullName\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + cssmDpn.nameType = CE_CDNT_FullName; + cssmDpn.dpn.fullName = (CE_GeneralNames *)alloc.malloc( + sizeof(CE_GeneralNames)); + + /* copy out to caller */ + CL_nssGeneralNamesToCssm(gnames, + *cssmDpn.dpn.fullName, coder, alloc); + break; + } + case NSS_DIST_POINT_RDN_TAG: + { + /* decode to temp coder memory */ + NSS_RDN rdn; + memset(&rdn, 0, sizeof(rdn)); + if(coder.decodeItem(nssBlob, kSecAsn1DistPointRDNTemplate, + &rdn)) { + clErrorLog("***Error decoding DistPointRDN\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + cssmDpn.nameType = CE_CDNT_NameRelativeToCrlIssuer; + cssmDpn.dpn.rdn = (CSSM_X509_RDN_PTR)alloc.malloc( + sizeof(CSSM_X509_RDN)); + + /* copy out to caller */ + CL_nssRdnToCssm(rdn, *cssmDpn.dpn.rdn, alloc, coder); + break; + } + default: + clErrorLog("***Bad CE_DistributionPointName tag\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } +} + +void CL_encodeDistributionPointName( + CE_DistributionPointName &cpoint, + CSSM_DATA &npoint, + SecNssCoder &coder) +{ + const SecAsn1Template *templ = NULL; + NSS_GeneralNames gnames; + NSS_RDN rdn; + void *encodeSrc = NULL; + + /* + * Our job is to convert one of two incoming aggregate types + * into NSS format, then encode the result into npoint. + */ + switch(cpoint.nameType) { + case CE_CDNT_FullName: + CL_cssmGeneralNamesToNss(*cpoint.dpn.fullName, + gnames, coder); + encodeSrc = &gnames; + templ = kSecAsn1DistPointFullNameTemplate; + break; + + case CE_CDNT_NameRelativeToCrlIssuer: + CL_cssmRdnToNss(*cpoint.dpn.rdn, rdn, coder); + encodeSrc = &rdn; + templ = kSecAsn1DistPointRDNTemplate; + break; + default: + clErrorLog("CL_encodeDistributionPointName: bad nameType\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_TAG); + } + if(coder.encodeItem(encodeSrc, templ, npoint)) { + clErrorLog("CL_encodeDistributionPointName: encode error\n"); + CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); + } +} + + +#pragma mark --- CE_CRLDistPointsSyntax <--> NSS_CRLDistributionPoints --- + +void CL_cssmDistPointsToNss( + const CE_CRLDistPointsSyntax &cdsaObj, + NSS_CRLDistributionPoints &nssObj, + SecNssCoder &coder) +{ + memset(&nssObj, 0, sizeof(nssObj)); + unsigned numPoints = cdsaObj.numDistPoints; + if(numPoints == 0) { + return; + } + nssObj.distPoints = + (NSS_DistributionPoint **)clNssNullArray(numPoints, coder); + for(unsigned dex=0; dexdistPointName) { + /* encode and drop into ASN_ANY slot */ + npoint->distPointName = (CSSM_DATA *) + coder.malloc(sizeof(CSSM_DATA)); + CL_encodeDistributionPointName(*cpoint->distPointName, + *npoint->distPointName, coder); + + } + + if(cpoint->reasonsPresent) { + /* bit string, presumed max length 8 bits */ + coder.allocItem(npoint->reasons, 1); + npoint->reasons.Data[0] = cpoint->reasons; + /* adjust for bit string length */ + npoint->reasons.Length = 8; + } + + if(cpoint->crlIssuer) { + CL_cssmGeneralNamesToNss(*cpoint->crlIssuer, + npoint->crlIssuer, coder); + } + } +} + +void CL_nssDistPointsToCssm( + const NSS_CRLDistributionPoints &nssObj, + CE_CRLDistPointsSyntax &cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + memset(&cdsaObj, 0, sizeof(cdsaObj)); + unsigned numPoints = clNssArraySize((const void **)nssObj.distPoints); + if(numPoints == 0) { + return; + } + + unsigned len = sizeof(CE_CRLDistributionPoint) * numPoints; + cdsaObj.distPoints = (CE_CRLDistributionPoint *)alloc.malloc(len); + memset(cdsaObj.distPoints, 0, len); + cdsaObj.numDistPoints = numPoints; + + for(unsigned dex=0; dex 8) { + clErrorLog("***CL_nssDistPointsToCssm: Malformed reasons\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + cpoint.reasonsPresent = CSSM_TRUE; + if(npoint.reasons.Length != 0) { + cpoint.reasons = npoint.reasons.Data[0]; + } + } + + if(npoint.crlIssuer.names != NULL) { + /* Cook up a new CE_GeneralNames */ + cpoint.crlIssuer = + (CE_GeneralNames *)alloc.malloc(sizeof(CE_GeneralNames)); + CL_nssGeneralNamesToCssm(npoint.crlIssuer, *cpoint.crlIssuer, + coder, alloc); + } + } +} + +#pragma mark ----- IssuingDistributionPoint ----- + +void CL_nssIssuingDistPointToCssm( + NSS_IssuingDistributionPoint *nssIdp, + CE_IssuingDistributionPoint *cssmIdp, + SecNssCoder &coder, + Allocator &alloc) +{ + /* All fields optional */ + memset(cssmIdp, 0, sizeof(*cssmIdp)); + if(nssIdp->distPointName) { + CE_DistributionPointName *cssmDp = (CE_DistributionPointName *) + alloc.malloc(sizeof(CE_DistributionPointName)); + + /* + * This one is currently still encoded; we have to peek + * at its tag and decode accordingly. + */ + CL_decodeDistributionPointName(*nssIdp->distPointName, + *cssmDp, coder, alloc); + cssmIdp->distPointName = cssmDp; + } + if(nssIdp->onlyUserCerts) { + cssmIdp->onlyUserCertsPresent = CSSM_TRUE; + cssmIdp->onlyUserCerts = clNssBoolToCssm(*nssIdp->onlyUserCerts); + } + if(nssIdp->onlyCACerts) { + cssmIdp->onlyCACertsPresent = CSSM_TRUE; + cssmIdp->onlyCACerts = clNssBoolToCssm(*nssIdp->onlyCACerts); + } + if(nssIdp->onlySomeReasons) { + cssmIdp->onlySomeReasonsPresent = CSSM_TRUE; + if(nssIdp->onlySomeReasons->Length > 0) { + cssmIdp->onlySomeReasons = *nssIdp->onlySomeReasons->Data; + } + else { + cssmIdp->onlySomeReasons = 0; + } + } + if(nssIdp->indirectCRL) { + cssmIdp->indirectCrlPresent = CSSM_TRUE; + cssmIdp->indirectCrl = clNssBoolToCssm(*nssIdp->indirectCRL); + } +} + +#pragma mark --- CE_NameConstraints <--> NSS_NameConstraints --- + +void CL_cssmNameConstraintsToNss( + const CE_NameConstraints &cdsaObj, + NSS_NameConstraints &nssObj, + SecNssCoder &coder) +{ + //%%%FIXME tba +} + +void CL_nssNameConstraintsToCssm( + const NSS_NameConstraints &nssObj, + CE_NameConstraints &cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + //%%%FIXME tba +} + +void CL_freeCssmNameConstraints( + CE_NameConstraints *cssmNcs, + Allocator &alloc) +{ + if(cssmNcs == NULL) { + return; + } +//%%%FIXME need to add a CL_freeCssmGeneralSubtrees function to clNameUtils{.h,.cpp} +#if 0 + switch(cssmDpn->nameType) { + case CE_CDNT_FullName: + CL_freeCssmGeneralNames(cssmDpn->dpn.fullName, alloc); + alloc.free(cssmDpn->dpn.fullName); + break; + case CE_CDNT_NameRelativeToCrlIssuer: + CL_freeX509Rdn(cssmDpn->dpn.rdn, alloc); + alloc.free(cssmDpn->dpn.rdn); + break; + } +#endif + memset(cssmNcs, 0, sizeof(*cssmNcs)); +} + +#pragma mark --- CE_PolicyMappings <--> NSS_PolicyMappings --- + +void CL_cssmPolicyMappingsToNss( + const CE_PolicyMappings &cdsaObj, + NSS_PolicyMappings &nssObj, + SecNssCoder &coder) +{ + //%%%FIXME tba +} + +void CL_nssPolicyMappingsToCssm( + const NSS_PolicyMappings &nssObj, + CE_PolicyMappings &cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + //%%%FIXME tba +} + +void CL_freeCssmPolicyMappings( + CE_PolicyMappings *cssmPms, + Allocator &alloc) +{ + if(cssmPms == NULL) { + return; + } + //%%%FIXME tba + + memset(cssmPms, 0, sizeof(*cssmPms)); +} + +#pragma mark --- CE_PolicyConstraints <--> NSS_PolicyConstraints --- + +void CL_cssmPolicyConstraintsToNss( + const CE_PolicyConstraints *cdsaObj, + NSS_PolicyConstraints *nssObj, + SecNssCoder &coder) +{ + //%%%FIXME tba +} + +void CL_nssPolicyConstraintsToCssm( + const NSS_PolicyConstraints *nssObj, + CE_PolicyConstraints *cdsaObj, + SecNssCoder &coder, // for temp decoding + Allocator &alloc) +{ + memset(cdsaObj, 0, sizeof(*cdsaObj)); + if(nssObj->requireExplicitPolicy.Data) { + cdsaObj->requireExplicitPolicyPresent = CSSM_TRUE; + cdsaObj->inhibitPolicyMapping = clDataToInt( + nssObj->requireExplicitPolicy, 0); + } + if(nssObj->inhibitPolicyMapping.Data) { + cdsaObj->inhibitPolicyMappingPresent = CSSM_TRUE; + cdsaObj->inhibitPolicyMapping = clDataToInt( + nssObj->inhibitPolicyMapping, 0); + } +} + +void CL_freeCssmPolicyConstraints( + CE_PolicyConstraints *cssmPcs, + Allocator &alloc) +{ + if(cssmPcs == NULL) { + return; + } + + memset(cssmPcs, 0, sizeof(*cssmPcs)); +} + + +#pragma mark ----- ECDSA_SigAlgParams support ----- + +/* + * Some implementations use a two-OID mechanism to specify ECDSA signature + * algorithm with a digest of other than SHA1. This is really not necessary; + * we use the single-OID method (e.g. CSSMOID_ECDSA_WithSHA512) when + * encoding, but we have to accomodate externally generated items with + * the two-OID method. This routine decodes the digest OID and infers a + * CSSM_ALGORITHMS from it. + * Throws CSSMERR_CL_UNKNOWN_FORMAT on any error. + */ +CSSM_ALGORITHMS CL_nssDecodeECDSASigAlgParams( + const CSSM_DATA &encParams, + SecNssCoder &coder) +{ + CSSM_X509_ALGORITHM_IDENTIFIER algParams; + memset(&algParams, 0, sizeof(algParams)); + PRErrorCode prtn = coder.decodeItem(encParams, kSecAsn1AlgorithmIDTemplate, &algParams); + if(prtn) { + clErrorLog("CL_nssDecodeECDSASigAlgParams: error decoding CSSM_X509_ALGORITHM_IDENTIFIER\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + /* get the digest algorithm, convert to ECDSA w/digest OID */ + CSSM_ALGORITHMS digestAlg = CL_oidToAlg(algParams.algorithm); + switch(digestAlg) { + case CSSM_ALGID_SHA1: + return CSSM_ALGID_SHA1WithECDSA; + case CSSM_ALGID_SHA224: + return CSSM_ALGID_SHA224WithECDSA; + case CSSM_ALGID_SHA256: + return CSSM_ALGID_SHA256WithECDSA; + case CSSM_ALGID_SHA384: + return CSSM_ALGID_SHA384WithECDSA; + case CSSM_ALGID_SHA512: + return CSSM_ALGID_SHA512WithECDSA; + default: + clErrorLog("CL_nssDecodeECDSASigAlgParams: unknown digest algorithm\n"); + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } +} + +#pragma mark ----- Top-level Cert/CRL encode and decode ----- + +/* + * To ensure a secure means of signing and verifying TBSCert blobs, we + * provide these functions to encode and decode just the top-level + * elements of a certificate. Unfortunately there is no guarantee + * that when you decode and re-encode a TBSCert blob, you get the + * same thing you started with (although with DER rules, as opposed + * to BER rules, you should). Thus when signing, we sign the TBSCert + * and encode the signed cert here without ever decoding the TBSCert (or, + * at least, without using the decoded version to get the encoded TBS blob). + */ + +void CL_certCrlDecodeComponents( + const CssmData &signedItem, // DER-encoded cert or CRL + CssmOwnedData &tbsBlob, // still DER-encoded + CssmOwnedData &algId, // ditto + CssmOwnedData &rawSig) // raw bits (not an encoded AsnBits) +{ + /* BER-decode into temp memory */ + NSS_SignedCertOrCRL nssObj; + SecNssCoder coder; + PRErrorCode prtn; + + memset(&nssObj, 0, sizeof(nssObj)); + prtn = coder.decode(signedItem.data(), signedItem.length(), + kSecAsn1SignedCertOrCRLTemplate, &nssObj); + if(prtn) { + CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); + } + + /* tbsBlob and algId are raw ASN_ANY including tags, which we pass + * back to caller intact */ + tbsBlob.copy(nssObj.tbsBlob.Data, nssObj.tbsBlob.Length); + algId.copy(nssObj.signatureAlgorithm.Data, + nssObj.signatureAlgorithm.Length); + + /* signature is a bit string which we do in fact decode */ + rawSig.copy(nssObj.signature.Data, + (nssObj.signature.Length + 7) / 8); +} + + +/* + * Given pre-DER-encoded blobs, do the final encode step for a signed cert. + */ +void +CL_certEncodeComponents( + const CssmData &TBSCert, // DER-encoded + const CssmData &algId, // ditto + const CssmData &rawSig, // raw bits, not encoded + CssmOwnedData &signedCert) // DER-encoded +{ + NSS_SignedCertOrCRL nssObj; + nssObj.tbsBlob.Data = TBSCert.Data; + nssObj.tbsBlob.Length = TBSCert.Length; + nssObj.signatureAlgorithm.Data = algId.Data; + nssObj.signatureAlgorithm.Length = algId.Length; + nssObj.signature.Data = rawSig.Data; + nssObj.signature.Length = rawSig.Length * 8; // BIT STRING + + PRErrorCode prtn; + + prtn = SecNssEncodeItemOdata(&nssObj, + kSecAsn1SignedCertOrCRLTemplate,signedCert); + if(prtn) { + CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); + } + +}