/* * Copyright (c) 2003 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. */ /* * 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 #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 CssmAllocator */ /* malloc d.Data, set d.Length */ void clAllocData( CssmAllocator &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( CssmAllocator &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; } uint32 len = cdata.Length; if(len > sizeof(uint32)) { CssmError::throwMe(toThrow); } uint32 rtn = 0; uint8 *cp = cdata.Data; for(uint32 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, CssmAllocator &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) { int numBits = b.Length * 8; /* start at end of bit array, scanning backwards looking * for the first set bit */ bool foundSet = false; for(int 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 = (uint32)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) { uint32 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) { unsigned 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, CssmAllocator &alloc) { clAllocCopyData(alloc, srcAlgId.algorithm, dstAlgId.algorithm); clAllocCopyData(alloc, srcAlgId.parameters, dstAlgId.parameters); } void CL_freeCssmAlgId( CSSM_X509_ALGORITHM_IDENTIFIER *cdsaObj, // optional CssmAllocator &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, CssmAllocator &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, CssmAllocator &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, CssmAllocator &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, CssmAllocator &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); try { 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_DH: 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_DH: { /* * 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, NSS_SubjectPublicKeyInfoTemplate, 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); } keyData.release(); /* * 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; } } catch (...) { alloc.free(cssmKey); throw; } 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 */ 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: if(hdr.Format != CSSM_KEYBLOB_RAW_FORMAT_X509) { clErrorLog("CL SetField: DSA 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, NSS_SubjectPublicKeyInfoTemplate, &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, CssmAllocator &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 CssmAllocator &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 ----- 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, CssmAllocator &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, NSS_DistPointFullNameTemplate, &gnames)) { clErrorLog("***Error decoding DistPointFullName\n"); CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); } cssmDpn.nameType = CE_CDNT_FullName; cssmDpn.fullName = (CE_GeneralNames *)alloc.malloc( sizeof(CE_GeneralNames)); /* copy out to caller */ CL_nssGeneralNamesToCssm(gnames, *cssmDpn.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, NSS_DistPointRDNTemplate, &rdn)) { clErrorLog("***Error decoding DistPointRDN\n"); CssmError::throwMe(CSSMERR_CL_UNKNOWN_FORMAT); } cssmDpn.nameType = CE_CDNT_NameRelativeToCrlIssuer; cssmDpn.rdn = (CSSM_X509_RDN_PTR)alloc.malloc( sizeof(CSSM_X509_RDN)); /* copy out to caller */ CL_nssRdnToCssm(rdn, *cssmDpn.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 SEC_ASN1Template *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.fullName, gnames, coder); encodeSrc = &gnames; templ = NSS_DistPointFullNameTemplate; break; case CE_CDNT_NameRelativeToCrlIssuer: CL_cssmRdnToNss(*cpoint.rdn, rdn, coder); encodeSrc = &rdn; templ = NSS_DistPointRDNTemplate; 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 CssmAllocator &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, CssmAllocator &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 ----- 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(), NSS_SignedCertOrCRLTemplate, &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, NSS_SignedCertOrCRLTemplate,signedCert); if(prtn) { CssmError::throwMe(CSSMERR_CL_MEMORY_ERROR); } }