/* * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The 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. * * @APPLE_LICENSE_HEADER_END@ */ /* * CMSDecoder.cpp - Interface for decoding CMS messages. * Created 1/12/06 by Doug Mitchell. */ #include "CMSDecoder.h" #include "CMSPrivate.h" #include "CMSUtils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #pragma mark --- Private types and definitions --- /* * Decoder state. */ typedef enum { DS_Init, /* between CMSDecoderCreate and CMSDecoderUpdateMessage */ DS_Updating, /* between first CMSDecoderUpdateMessage and CMSDecoderFinalizeMessage */ DS_Final /* CMSDecoderFinalizeMessage has been called */ } CMSDecoderState; /* * Caller's CMSDecoderRef points to one of these. */ struct _CMSDecoder { CFRuntimeBase base; CMSDecoderState decState; SecArenaPoolRef arena; /* the decoder's arena */ SecCmsDecoderRef decoder; CFDataRef detachedContent; CFTypeRef keychainOrArray; /* from CMSDecoderSetSearchKeychain() */ /* * The following are valid (and quiescent) after CMSDecoderFinalizeMessage(). */ SecCmsMessageRef cmsMsg; Boolean wasEncrypted; /* valid after CMSDecoderFinalizeMessage() */ SecCmsSignedDataRef signedData; /* if there is one... */ /* only non-NULL if we found a signedData */ size_t numSigners; CSSM_OID *eContentType; /* etc. */ }; static void cmsDecoderInit(CFTypeRef dec); static void cmsDecoderFinalize(CFTypeRef dec); static CFRuntimeClass cmsDecoderRuntimeClass = { 0, /* version */ "CMSDecoder", cmsDecoderInit, NULL, /* copy */ cmsDecoderFinalize, NULL, /* equal - just use pointer equality */ NULL, /* hash, ditto */ NULL, /* copyFormattingDesc */ NULL /* copyDebugDesc */ }; #pragma mark --- Private Routines --- static CFTypeID cmsDecoderTypeID = _kCFRuntimeNotATypeID; /* one time only class init, called via pthread_once() in CMSDecoderGetTypeID() */ static void cmsDecoderClassInitialize(void) { cmsDecoderTypeID = _CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsDecoderRuntimeClass); } /* init called out from _CFRuntimeCreateInstance() */ static void cmsDecoderInit(CFTypeRef dec) { char *start = ((char *)dec) + sizeof(CFRuntimeBase); memset(start, 0, sizeof(struct _CMSDecoder) - sizeof(CFRuntimeBase)); } /* * Dispose of a CMSDecoder. Called out from CFRelease(). */ static void cmsDecoderFinalize( CFTypeRef dec) { CMSDecoderRef cmsDecoder = (CMSDecoderRef)dec; if(cmsDecoder == NULL) { return; } if(cmsDecoder->decoder != NULL) { /* * Normally this gets freed in SecCmsDecoderFinish - this is * an error case. * FIXME: SecCmsDecoderDestroy() appears to destroy the * cmsMsg too! Plus there's a comment there re: a leak... */ SecCmsDecoderDestroy(cmsDecoder->decoder); } CFRELEASE(cmsDecoder->detachedContent); CFRELEASE(cmsDecoder->keychainOrArray); if(cmsDecoder->cmsMsg != NULL) { SecCmsMessageDestroy(cmsDecoder->cmsMsg); } if(cmsDecoder->arena != NULL) { SecArenaPoolFree(cmsDecoder->arena, false); } } /* * Given detached content and a valid (decoded) SignedData, digest the detached * content. This occurs at the later of {CMSDecoderFinalizeMessage() finding a * SignedData when already have detachedContent, or CMSDecoderSetDetachedContent() * when we already have a SignedData). */ static OSStatus cmsDigestDetachedContent( CMSDecoderRef cmsDecoder) { ASSERT((cmsDecoder->signedData != NULL) && (cmsDecoder->detachedContent != NULL)); SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(cmsDecoder->signedData); if(digestAlgorithms == NULL) { return errSecUnknownFormat; } SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms); if(digcx == NULL) { return memFullErr; } CSSM_DATA **digests = NULL; SecCmsDigestContextUpdate(digcx, CFDataGetBytePtr(cmsDecoder->detachedContent), CFDataGetLength(cmsDecoder->detachedContent)); /* note this frees the digest content regardless */ OSStatus ortn = SecCmsDigestContextFinishMultiple(digcx, cmsDecoder->arena, &digests); if(ortn) { ortn = cmsRtnToOSStatus(ortn); CSSM_PERROR("SecCmsDigestContextFinishMultiple", ortn); return ortn; } ortn = SecCmsSignedDataSetDigests(cmsDecoder->signedData, digestAlgorithms, digests); if(ortn) { ortn = cmsRtnToOSStatus(ortn); CSSM_PERROR("SecCmsSignedDataSetDigests", ortn); } return ortn; } #pragma mark --- Start of Public API --- CFTypeID CMSDecoderGetTypeID(void) { static pthread_once_t once = PTHREAD_ONCE_INIT; if(cmsDecoderTypeID == _kCFRuntimeNotATypeID) { pthread_once(&once, &cmsDecoderClassInitialize); } return cmsDecoderTypeID; } /* * Create a CMSDecoder. Result must eventually be freed via CFRelease(). */ OSStatus CMSDecoderCreate( CMSDecoderRef *cmsDecoderOut) /* RETURNED */ { CMSDecoderRef cmsDecoder = NULL; uint32_t extra = sizeof(*cmsDecoder) - sizeof(cmsDecoder->base); cmsDecoder = (CMSDecoderRef)_CFRuntimeCreateInstance(NULL, CMSDecoderGetTypeID(), extra, NULL); if(cmsDecoder == NULL) { return memFullErr; } cmsDecoder->decState = DS_Init; *cmsDecoderOut = cmsDecoder; return noErr; } /* * Feed raw bytes of the message to be decoded into the decoder. Can be called * multiple times. */ OSStatus CMSDecoderUpdateMessage( CMSDecoderRef cmsDecoder, const void *msgBytes, size_t msgBytesLen) { if(cmsDecoder == NULL) { return paramErr; } OSStatus ortn; switch(cmsDecoder->decState) { case DS_Init: /* First time through; set up */ ASSERT(cmsDecoder->decoder == NULL); ASSERT(cmsDecoder->arena == NULL); ortn = SecArenaPoolCreate(1024, &cmsDecoder->arena); if(ortn) { return cmsRtnToOSStatus(ortn); } ortn = SecCmsDecoderCreate(cmsDecoder->arena, NULL, NULL, NULL, NULL, NULL, NULL, &cmsDecoder->decoder); if(ortn) { ortn = cmsRtnToOSStatus(ortn); CSSM_PERROR("SecCmsDecoderCreate", ortn); return ortn; } cmsDecoder->decState = DS_Updating; break; case DS_Updating: ASSERT(cmsDecoder->decoder != NULL); break; case DS_Final: /* Too late for another update */ return paramErr; default: dprintf("CMSDecoderUpdateMessage: bad decState\n"); return internalComponentErr; } /* FIXME - CFIndex same size as size_t on 64bit? */ ortn = SecCmsDecoderUpdate(cmsDecoder->decoder, msgBytes, (CFIndex)msgBytesLen); if(ortn) { ortn = cmsRtnToOSStatus(ortn, errSecUnknownFormat); CSSM_PERROR("SecCmsDecoderUpdate", ortn); } return ortn; } /* * Indicate that no more CMSDecoderUpdateMessage() calls are forthcoming; * finish decoding the message. We parse the message as best we can, up to * but not including verifying individual signerInfos. */ OSStatus CMSDecoderFinalizeMessage( CMSDecoderRef cmsDecoder) { if(cmsDecoder == NULL) { return paramErr; } if(cmsDecoder->decState != DS_Updating) { return paramErr; } ASSERT(cmsDecoder->decoder != NULL); OSStatus ortn = SecCmsDecoderFinish(cmsDecoder->decoder, &cmsDecoder->cmsMsg); cmsDecoder->decState = DS_Final; /* SecCmsDecoderFinish destroyed the decoder even on failure */ cmsDecoder->decoder = NULL; if(ortn) { ortn = cmsRtnToOSStatus(ortn, errSecUnknownFormat); CSSM_PERROR("SecCmsDecoderFinish", ortn); return ortn; } ASSERT(cmsDecoder->cmsMsg != NULL); cmsDecoder->wasEncrypted = SecCmsMessageIsEncrypted(cmsDecoder->cmsMsg); /* Look for a SignedData */ int numContentInfos = SecCmsMessageContentLevelCount(cmsDecoder->cmsMsg); int dex; for(dex=0; dexcmsMsg, dex); SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); switch(tag) { case SEC_OID_PKCS7_SIGNED_DATA: cmsDecoder->signedData = (SecCmsSignedDataRef)SecCmsContentInfoGetContent(ci); /* dig down one more layer for eContentType */ ci = SecCmsSignedDataGetContentInfo(cmsDecoder->signedData); cmsDecoder->eContentType = SecCmsContentInfoGetContentTypeOID(ci); break; default: break; } if(cmsDecoder->signedData != NULL) { break; } } /* minimal processing of optional signedData... */ if(cmsDecoder->signedData != NULL) { cmsDecoder->numSigners = (size_t) SecCmsSignedDataSignerInfoCount(cmsDecoder->signedData); if(cmsDecoder->detachedContent != NULL) { /* time to calculate digests from detached content */ ortn = cmsDigestDetachedContent(cmsDecoder); } } return ortn; } /* * A signed CMS message optionally includes the data which was signed. If the * message does not include the signed data, caller specifies the signed data * (the "detached content") here. * * This can be called either before or after the actual decoding of the message * (via CMSDecoderUpdateMessage() and CMSDecoderFinalizeMessage()); the only * restriction is that, if detached content is required, this function must * be called befoere successfully ascertaining the signature status via * CMSDecoderCopySignerStatus(). */ OSStatus CMSDecoderSetDetachedContent( CMSDecoderRef cmsDecoder, CFDataRef detachedContent) { if((cmsDecoder == NULL) || (detachedContent == NULL)) { return paramErr; } cmsDecoder->detachedContent = detachedContent; CFRetain(detachedContent); if(cmsDecoder->signedData != NULL) { /* time to calculate digests from detached content */ ASSERT(cmsDecoder->decState == DS_Final); return cmsDigestDetachedContent(cmsDecoder); } return noErr; } /* * Obtain the detached content specified in CMSDecoderSetDetachedContent(). * Returns a NULL detachedContent if no detached content has been specified. * Caller must CFRelease() the result. */ OSStatus CMSDecoderCopyDetachedContent( CMSDecoderRef cmsDecoder, CFDataRef *detachedContent) /* RETURNED */ { if((cmsDecoder == NULL) || (detachedContent == NULL)) { return paramErr; } if(cmsDecoder->detachedContent != NULL) { CFRetain(cmsDecoder->detachedContent); } *detachedContent = cmsDecoder->detachedContent; return noErr; } /* * Optionally specify a SecKeycainRef, or an array of them, containing * intermediate certs to be used in verifying a signed message's signer * certs. By default, the default keychain search list is used for this. * SPecify an empty CFArrayRef to search *no* keychains for intermediate * certs. * IF this is called, it must be called before CMSDecoderCopySignerStatus(). */ OSStatus CMSDecoderSetSearchKeychain( CMSDecoderRef cmsDecoder, CFTypeRef keychainOrArray) { if(cmsDecoder == NULL) { return paramErr; } cmsDecoder->keychainOrArray = keychainOrArray; if(keychainOrArray) { CFRetain(keychainOrArray); } return noErr; } /* * Obtain the number of signers of a message. A result of zero indicates that * the message was not signed. */ OSStatus CMSDecoderGetNumSigners( CMSDecoderRef cmsDecoder, size_t *numSigners) /* RETURNED */ { if((cmsDecoder == NULL) || (numSigners == NULL)) { return paramErr; } if(cmsDecoder->decState != DS_Final) { return paramErr; } *numSigners = cmsDecoder->numSigners; return noErr; } /* * Obtain the status of a CMS message's signature. A CMS message can * be signed my multiple signers; this function returns the status * associated with signer 'n' as indicated by the signerIndex parameter. */ OSStatus CMSDecoderCopySignerStatus( CMSDecoderRef cmsDecoder, size_t signerIndex, CFTypeRef policyOrArray, Boolean evaluateSecTrust, CMSSignerStatus *signerStatus, /* optional; RETURNED */ SecTrustRef *secTrust, /* optional; RETURNED */ OSStatus *certVerifyResultCode) /* optional; RETURNED */ { if((cmsDecoder == NULL) || (cmsDecoder->decState != DS_Final)) { return paramErr; } /* initialize return values */ if(signerStatus) { *signerStatus = kCMSSignerUnsigned; } if(secTrust) { *secTrust = NULL; } if(certVerifyResultCode) { *certVerifyResultCode = 0; } if(cmsDecoder->signedData == NULL) { *signerStatus = kCMSSignerUnsigned; /* redundant, I know, but explicit */ return noErr; } ASSERT(cmsDecoder->numSigners > 0); if(signerIndex >= cmsDecoder->numSigners) { *signerStatus = kCMSSignerInvalidIndex; return noErr; } if(!SecCmsSignedDataHasDigests(cmsDecoder->signedData)) { *signerStatus = kCMSSignerNeedsDetachedContent; return noErr; } /* * OK, we should be able to verify this signerInfo. * I think we have to do the SecCmsSignedDataVerifySignerInfo first * in order get all the cert pieces into place before returning them * to the caller. */ SecTrustRef theTrust = NULL; OSStatus vfyRtn = SecCmsSignedDataVerifySignerInfo(cmsDecoder->signedData, signerIndex, /* * FIXME this cast should not be necessary, but libsecurity_smime * declares this argument as a SecKeychainRef */ (SecKeychainRef)cmsDecoder->keychainOrArray, policyOrArray, &theTrust); /* Subsequent errors to errOut: */ /* * NOTE the smime lib did NOT evaluate that SecTrust - it only does * SecTrustEvaluate() if we don't ask for a copy. * * FIXME deal with multitudes of status returns here...for now, proceed with * obtaining components the caller wants and assume that a nonzero vfyRtn * means "bad signature". */ OSStatus ortn = noErr; SecTrustResultType secTrustResult; CSSM_RETURN tpVfyStatus = CSSM_OK; OSStatus evalRtn; if(secTrust != NULL) { *secTrust = theTrust; /* we'll release our reference at the end */ if (theTrust) CFRetain(theTrust); } SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, signerIndex); if(signerInfo == NULL) { /* should never happen */ ASSERT(0); dprintf("CMSDecoderCopySignerStatus: no signerInfo\n"); ortn = internalComponentErr; goto errOut; } /* now do the actual cert verify */ if(evaluateSecTrust) { evalRtn = SecTrustEvaluate(theTrust, &secTrustResult); if(evalRtn) { /* should never happen */ CSSM_PERROR("SecTrustEvaluate", evalRtn); dprintf("CMSDecoderCopySignerStatus: SecTrustEvaluate error\n"); ortn = internalComponentErr; goto errOut; } switch(secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ break; case kSecTrustResultDeny: tpVfyStatus = CSSMERR_APPLETP_TRUST_SETTING_DENY; break; case kSecTrustResultConfirm: dprintf("SecTrustEvaluate reported confirm\n"); tpVfyStatus = CSSMERR_TP_NOT_TRUSTED; break; default: { /* get low-level TP error */ OSStatus tpStatus; ortn = SecTrustGetCssmResultCode(theTrust, &tpStatus); if(ortn) { CSSM_PERROR("SecTrustGetCssmResultCode", ortn); } else { tpVfyStatus = tpStatus; } CSSM_PERROR("TP status after SecTrustEvaluate", tpVfyStatus); break; } } /* switch(secTrustResult) */ } /* evaluateSecTrust true */ if(certVerifyResultCode != NULL) { *certVerifyResultCode = tpVfyStatus; } /* cook up global status based on vfyRtn and tpVfyStatus */ if(signerStatus != NULL) { if((vfyRtn == noErr) && (tpVfyStatus == CSSM_OK)) { *signerStatus = kCMSSignerValid; } else if(vfyRtn != noErr) { /* this could mean other things, but for now... */ *signerStatus = kCMSSignerInvalidSignature; } else { *signerStatus = kCMSSignerInvalidCert; } } errOut: CFRELEASE(theTrust); return ortn; } /* * Obtain the email address of signer 'signerIndex' of a CMS message, if * present. * * This cannot be called until after CMSDecoderFinalizeMessage() is called. */ OSStatus CMSDecoderCopySignerEmailAddress( CMSDecoderRef cmsDecoder, size_t signerIndex, CFStringRef *signerEmailAddress) /* RETURNED */ { if((cmsDecoder == NULL) || (signerEmailAddress == NULL) || (cmsDecoder->signedData == NULL) || /* not signed */ (signerIndex >= cmsDecoder->numSigners) || /* index out of range */ (cmsDecoder->decState != DS_Final)) { return paramErr; } SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, signerIndex); if(signerInfo == NULL) { /* should never happen */ ASSERT(0); dprintf("CMSDecoderCopySignerEmailAddress: no signerInfo\n"); return internalComponentErr; } /* * This is leaking memory in libsecurityKeychain per Radar 4412699. */ *signerEmailAddress = SecCmsSignerInfoGetSignerEmailAddress(signerInfo); return noErr; } /* * Obtain the certificate of signer 'signerIndex' of a CMS message, if * present. * * This cannot be called until after CMSDecoderFinalizeMessage() is called. */ OSStatus CMSDecoderCopySignerCert( CMSDecoderRef cmsDecoder, size_t signerIndex, SecCertificateRef *signerCert) /* RETURNED */ { if((cmsDecoder == NULL) || (signerCert == NULL) || (cmsDecoder->signedData == NULL) || /* not signed */ (signerIndex >= cmsDecoder->numSigners) || /* index out of range */ (cmsDecoder->decState != DS_Final)) { return paramErr; } SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(cmsDecoder->signedData, signerIndex); if(signerInfo == NULL) { /* should never happen */ ASSERT(0); dprintf("CMSDecoderCopySignerCertificate: no signerInfo\n"); return internalComponentErr; } *signerCert = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL); /* libsecurity_smime does NOT retain that */ if(*signerCert == NULL) { /* should never happen */ ASSERT(0); dprintf("CMSDecoderCopySignerCertificate: no signerCert\n"); return internalComponentErr; } CFRetain(*signerCert); return noErr; } /* * Determine whether a CMS message was encrypted, and if so, whether we were * able to decrypt it. */ OSStatus CMSDecoderIsContentEncrypted( CMSDecoderRef cmsDecoder, Boolean *wasEncrypted) { if((cmsDecoder == NULL) || (wasEncrypted == NULL)) { return paramErr; } if(cmsDecoder->decState != DS_Final) { return paramErr; } *wasEncrypted = cmsDecoder->wasEncrypted; return noErr; } /* * Obtain the eContentType OID for a SignedData's EncapsulatedContentType, if * present. */ OSStatus CMSDecoderCopyEncapsulatedContentType( CMSDecoderRef cmsDecoder, CFDataRef *eContentType) /* RETURNED */ { if((cmsDecoder == NULL) || (eContentType == NULL)) { return paramErr; } if(cmsDecoder->decState != DS_Final) { return paramErr; } if(cmsDecoder->signedData == NULL) { *eContentType = NULL; } else { CSSM_OID *ecOid = cmsDecoder->eContentType; *eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length); } return noErr; } /* * Obtain an array of all of the certificates in a message. Elements of the * returned array are SecCertificateRefs. The caller must CFRelease the returned * array. * This cannot be called until after CMSDecoderFinalizeMessage() is called. */ OSStatus CMSDecoderCopyAllCerts( CMSDecoderRef cmsDecoder, CFArrayRef *certs) /* RETURNED */ { if((cmsDecoder == NULL) || (certs == NULL)) { return paramErr; } if(cmsDecoder->decState != DS_Final) { return paramErr; } if(cmsDecoder->signedData == NULL) { /* message wasn't signed */ *certs = NULL; return noErr; } /* NULL_terminated array of CSSM_DATA ptrs */ CSSM_DATA_PTR *cssmCerts = SecCmsSignedDataGetCertificateList(cmsDecoder->signedData); if((cssmCerts == NULL) || (*cssmCerts == NULL)) { *certs = NULL; return noErr; } CFMutableArrayRef allCerts = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); CSSM_DATA_PTR *cssmCert; for(cssmCert=cssmCerts; *cssmCert!=NULL; cssmCert++) { OSStatus ortn; SecCertificateRef cfCert; ortn = SecCertificateCreateFromData(*cssmCert, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &cfCert); if(ortn) { CFRelease(allCerts); return ortn; } CFArrayAppendValue(allCerts, cfCert); /* the array holds the only needed refcount */ CFRelease(cfCert); } *certs = allCerts; return noErr; } /* * Obtain the actual message content (payload), if any. If the message was * signed with detached content this will return NULL. * Caller must CFRelease the result. */ OSStatus CMSDecoderCopyContent( CMSDecoderRef cmsDecoder, CFDataRef *content) /* RETURNED */ { if((cmsDecoder == NULL) || (content == NULL)) { return paramErr; } if(cmsDecoder->decState != DS_Final) { return paramErr; } if(cmsDecoder->cmsMsg == NULL) { /* Hmmm....looks like the finalize call failed */ return paramErr; } CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsDecoder->cmsMsg); if((odata == NULL) || (odata->Length == 0)) { /* i.e., detached content */ *content = NULL; return noErr; } *content = CFDataCreate(NULL, (const UInt8 *)odata->Data, odata->Length); return noErr; } #pragma mark --- SPI declared in CMSPrivate.h --- /* * Obtain the SecCmsMessageRef associated with a CMSDecoderRef. Intended * to be called after decoding the message (i.e., after * CMSDecoderFinalizeMessage() to gain finer access to the contents of the * SecCmsMessageRef than is otherwise available via the CMSDecoder interface. * Returns a NULL SecCmsMessageRef if CMSDecoderFinalizeMessage() has not been * called. * * The CMSDecoder retains ownership of the returned SecCmsMessageRef. */ OSStatus CMSDecoderGetCmsMessage( CMSDecoderRef cmsDecoder, SecCmsMessageRef *cmsMessage) /* RETURNED */ { if((cmsDecoder == NULL) || (cmsMessage == NULL)) { return paramErr; } /* any state, whether we have a msg or not is OK */ *cmsMessage = cmsDecoder->cmsMsg; return noErr; } /* * Optionally specify a SecCmsDecoderRef to use with a CMSDecoderRef. * If this is called, it must be called before the first call to * CMSDecoderUpdateMessage(). The CMSDecoderRef takes ownership of the * incoming SecCmsDecoderRef. */ OSStatus CMSDecoderSetDecoder( CMSDecoderRef cmsDecoder, SecCmsDecoderRef decoder) { if((cmsDecoder == NULL) || (decoder == NULL)) { return paramErr; } switch(cmsDecoder->decState) { case DS_Init: ASSERT(cmsDecoder->decoder == NULL); cmsDecoder->decoder = decoder; cmsDecoder->decState = DS_Updating; return noErr; case DS_Updating: case DS_Final: return paramErr; } return noErr; } /* * Obtain the SecCmsDecoderRef associated with a CMSDecoderRef. * Returns a NULL SecCmsDecoderRef if neither CMSDecoderSetDecoder() nor * CMSDecoderUpdateMessage() has been called. * The CMSDecoderRef retains ownership of the SecCmsDecoderRef. */ OSStatus CMSDecoderGetDecoder( CMSDecoderRef cmsDecoder, SecCmsDecoderRef *decoder) /* RETURNED */ { if((cmsDecoder == NULL) || (decoder == NULL)) { return paramErr; } /* any state, whether we have a decoder or not is OK */ *decoder = cmsDecoder->decoder; return noErr; }