#include "CMSEncoder.h"
#include "CMSPrivate.h"
#include "CMSUtils.h"
#include <Security/SecBase.h>
#include <Security/SecCmsEncoder.h>
#include <Security/SecCmsEnvelopedData.h>
#include <Security/SecCmsMessage.h>
#include <Security/SecCmsRecipientInfo.h>
#include <Security/SecCmsSignedData.h>
#include <Security/SecCmsSignerInfo.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCertificate.h>
#include <Security/SecIdentity.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecSMIME.h>
#include <Security/oidsattr.h>
#include <Security/SecAsn1Coder.h>
#include <Security/SecAsn1Types.h>
#include <Security/SecAsn1Templates.h>
#include <CoreFoundation/CFRuntime.h>
#include <pthread.h>
#include <security_smime/tsaSupport.h>
#include <security_smime/cmspriv.h>
#pragma mark --- Private types and definitions ---
typedef enum {
ES_Init,
ES_Msg,
ES_Updating,
ES_Final
} CMSEncoderState;
typedef enum {
EO_Sign,
EO_Encrypt,
EO_SignEncrypt
} CMSEncoderOp;
struct _CMSEncoder {
CFRuntimeBase base;
CMSEncoderState encState;
CMSEncoderOp op;
Boolean detachedContent;
CSSM_OID eContentType;
CFMutableArrayRef signers;
CFMutableArrayRef recipients;
CFMutableArrayRef otherCerts;
CMSSignedAttributes signedAttributes;
CFAbsoluteTime signingTime;
SecCmsMessageRef cmsMsg;
SecArenaPoolRef arena;
SecCmsEncoderRef encoder;
CSSM_DATA encoderOut;
bool customCoder;
CMSCertificateChainMode chainMode;
};
static void cmsEncoderInit(CFTypeRef enc);
static void cmsEncoderFinalize(CFTypeRef enc);
static CFRuntimeClass cmsEncoderRuntimeClass =
{
0,
"CMSEncoder",
cmsEncoderInit,
NULL,
cmsEncoderFinalize,
NULL,
NULL,
NULL,
NULL
};
void
CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback);
#pragma mark --- Private routines ---
static int cfStringToNumber(
CFStringRef inStr)
{
int max = 32;
char buf[max];
if (!inStr || !CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII))
return -1;
return atoi(buf);
}
static unsigned encodeNumber(
int num,
unsigned char **encodeArray) {
unsigned char *result;
unsigned dex;
unsigned numDigits = 0;
unsigned scratch;
if(num == 0) {
*encodeArray = (unsigned char *)malloc(1);
**encodeArray = 0;
return 1;
}
scratch = (unsigned)num;
while(scratch != 0) {
numDigits++;
scratch >>= 7;
}
result = (unsigned char *)malloc(numDigits);
scratch = (unsigned)num;
for(dex=0; dex<numDigits; dex++) {
result[numDigits - dex - 1] = scratch & 0x7f;
scratch >>= 7;
}
for(dex=0; dex<(numDigits - 1); dex++) {
result[dex] |= 0x80;
}
*encodeArray = result;
return numDigits;
}
static int encodeOid(
const unsigned char *inStr,
unsigned char **outOid,
unsigned int *outLen)
{
unsigned char **digits = NULL;
unsigned *numDigits = NULL;
CFIndex digit;
unsigned numDigitBytes;
unsigned char firstByte;
unsigned char *outP;
CFIndex numsToProcess;
CFStringRef oidStr = NULL;
CFArrayRef argvRef = NULL;
int num, result = 1;
CFIndex argc;
if (!inStr || !outOid || !outLen) goto cleanExit;
oidStr = CFStringCreateWithCString(NULL, (const char *)inStr, kCFStringEncodingASCII);
if (!oidStr) goto cleanExit;
argvRef = CFStringCreateArrayBySeparatingStrings(NULL, oidStr, CFSTR("."));
if (!argvRef) goto cleanExit;
argc = CFArrayGetCount(argvRef);
if (argc < 3) goto cleanExit;
num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, 0));
if (num < 0) goto cleanExit;
firstByte = (40 * num);
num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, 1));
if (num < 0) goto cleanExit;
firstByte += num;
numDigitBytes = 1;
numsToProcess = argc - 2;
if(numsToProcess > 0) {
digits = (unsigned char **) malloc(numsToProcess * sizeof(unsigned char *));
numDigits = (unsigned *) malloc(numsToProcess * sizeof(unsigned));
for(digit=0; digit<numsToProcess; digit++) {
num = cfStringToNumber((CFStringRef)CFArrayGetValueAtIndex(argvRef, digit+2));
if (num < 0) goto cleanExit;
numDigits[digit] = encodeNumber(num, &digits[digit]);
numDigitBytes += numDigits[digit];
}
}
*outLen = (2 + numDigitBytes);
*outOid = outP = (unsigned char *) malloc(*outLen);
*outP++ = 0x06;
*outP++ = numDigitBytes;
*outP++ = firstByte;
for(digit=0; digit<numsToProcess; digit++) {
unsigned int byteDex;
for(byteDex=0; byteDex<numDigits[digit]; byteDex++) {
*outP++ = digits[digit][byteDex];
}
}
if(digits) {
for(digit=0; digit<numsToProcess; digit++) {
free(digits[digit]);
}
free(digits);
free(numDigits);
}
result = 0;
cleanExit:
if (oidStr) CFRelease(oidStr);
if (argvRef) CFRelease(argvRef);
return result;
}
static int convertOid(
CFTypeRef inRef,
CSSM_OID *outOid)
{
if (!inRef || !outOid)
return errSecParam;
unsigned char *oidData = NULL;
unsigned int oidLen = 0;
if (CFGetTypeID(inRef) == CFStringGetTypeID()) {
CFStringRef inStr = (CFStringRef)inRef;
CFIndex max = CFStringGetLength(inStr) * 3;
char buf[max];
if (!CFStringGetCString(inStr, buf, max-1, kCFStringEncodingASCII))
return errSecParam;
if(encodeOid((unsigned char *)buf, &oidData, &oidLen) != 0)
return errSecParam;
}
else if (CFGetTypeID(inRef) == CFDataGetTypeID()) {
CFDataRef inData = (CFDataRef)inRef;
oidLen = (unsigned int) CFDataGetLength(inData);
oidData = (unsigned char *) malloc(oidLen);
memcpy(oidData, CFDataGetBytePtr(inData), oidLen);
}
else {
return errSecParam;
}
outOid->Length = oidLen;
outOid->Data = (uint8 *)oidData;
return 0;
}
static CFTypeID cmsEncoderTypeID = _kCFRuntimeNotATypeID;
static void cmsEncoderClassInitialize(void)
{
cmsEncoderTypeID =
_CFRuntimeRegisterClass((const CFRuntimeClass * const)&cmsEncoderRuntimeClass);
}
static void cmsEncoderInit(CFTypeRef enc)
{
char *start = ((char *)enc) + sizeof(CFRuntimeBase);
memset(start, 0, sizeof(struct _CMSEncoder) - sizeof(CFRuntimeBase));
}
static void cmsEncoderFinalize(
CFTypeRef enc)
{
CMSEncoderRef cmsEncoder = (CMSEncoderRef)enc;
if(cmsEncoder == NULL) {
return;
}
if(cmsEncoder->eContentType.Data != NULL) {
free(cmsEncoder->eContentType.Data);
}
CFRELEASE(cmsEncoder->signers);
CFRELEASE(cmsEncoder->recipients);
CFRELEASE(cmsEncoder->otherCerts);
if(cmsEncoder->cmsMsg != NULL) {
SecCmsMessageDestroy(cmsEncoder->cmsMsg);
}
if(cmsEncoder->arena != NULL) {
SecArenaPoolFree(cmsEncoder->arena, false);
}
if(cmsEncoder->encoder != NULL) {
SecCmsEncoderDestroy(cmsEncoder->encoder);
}
}
static OSStatus cmsSetupEncoder(
CMSEncoderRef cmsEncoder)
{
OSStatus ortn;
ASSERT(cmsEncoder->arena == NULL);
ASSERT(cmsEncoder->encoder == NULL);
ortn = SecArenaPoolCreate(1024, &cmsEncoder->arena);
if(ortn) {
return cmsRtnToOSStatus(ortn);
}
ortn = SecCmsEncoderCreate(cmsEncoder->cmsMsg,
NULL, NULL, &cmsEncoder->encoderOut, cmsEncoder->arena,
NULL, NULL, NULL, NULL, NULL, NULL, &cmsEncoder->encoder);
if(ortn) {
return cmsRtnToOSStatus(ortn);
}
return errSecSuccess;
}
static OSStatus cmsSetupForSignedData(
CMSEncoderRef cmsEncoder)
{
ASSERT((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL));
SecCmsContentInfoRef contentInfo = NULL;
SecCmsSignedDataRef signedData = NULL;
OSStatus ortn;
if(cmsEncoder->cmsMsg != NULL) {
SecCmsMessageDestroy(cmsEncoder->cmsMsg);
}
cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
if(cmsEncoder->cmsMsg == NULL) {
return errSecInternalComponent;
}
signedData = SecCmsSignedDataCreate(cmsEncoder->cmsMsg);
if(signedData == NULL) {
return errSecInternalComponent;
}
contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
ortn = SecCmsContentInfoSetContentSignedData(cmsEncoder->cmsMsg, contentInfo,
signedData);
if(ortn) {
return cmsRtnToOSStatus(ortn);
}
contentInfo = SecCmsSignedDataGetContentInfo(signedData);
if(cmsEncoder->eContentType.Data != NULL) {
ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
contentInfo,
NULL,
cmsEncoder->detachedContent,
&cmsEncoder->eContentType);
}
else {
ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
contentInfo,
NULL,
cmsEncoder->detachedContent);
}
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsContentInfoSetContent*", ortn);
return ortn;
}
if(cmsEncoder->otherCerts != NULL) {
ortn = SecCmsSignedDataAddCertList(signedData, cmsEncoder->otherCerts);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignedDataAddCertList", ortn);
return ortn;
}
}
CFIndex numSigners = 0;
if(cmsEncoder->signers != NULL) {
numSigners = CFArrayGetCount(cmsEncoder->signers);
}
CFIndex dex;
SecKeychainRef ourKc = NULL;
SecCertificateRef ourCert = NULL;
SecCmsCertChainMode chainMode = SecCmsCMCertChain;
switch(cmsEncoder->chainMode) {
case kCMSCertificateNone:
chainMode = SecCmsCMNone;
break;
case kCMSCertificateSignerOnly:
chainMode = SecCmsCMCertOnly;
break;
case kCMSCertificateChainWithRoot:
chainMode = SecCmsCMCertChainWithRoot;
break;
default:
break;
}
for(dex=0; dex<numSigners; dex++) {
SecCmsSignerInfoRef signerInfo;
SecIdentityRef ourId =
(SecIdentityRef)CFArrayGetValueAtIndex(cmsEncoder->signers, dex);
ortn = SecIdentityCopyCertificate(ourId, &ourCert);
if(ortn) {
CSSM_PERROR("SecIdentityCopyCertificate", ortn);
break;
}
ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc);
if(ortn) {
CSSM_PERROR("SecKeychainItemCopyKeychain", ortn);
break;
}
signerInfo = SecCmsSignerInfoCreate(cmsEncoder->cmsMsg, ourId, SEC_OID_SHA1);
if (signerInfo == NULL) {
ortn = errSecInternalComponent;
break;
}
ortn = SecCmsSignerInfoIncludeCerts(signerInfo, chainMode,
certUsageEmailSigner);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignerInfoIncludeCerts", ortn);
break;
}
if(cmsEncoder->signedAttributes & kCMSAttrSmimeCapabilities) {
ortn = SecCmsSignerInfoAddSMIMECaps(signerInfo);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
break;
}
}
if(cmsEncoder->signedAttributes & kCMSAttrSmimeEncryptionKeyPrefs) {
ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn);
break;
}
}
if(cmsEncoder->signedAttributes & kCMSAttrSmimeMSEncryptionKeyPrefs) {
ortn = SecCmsSignerInfoAddMSSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignerInfoAddMSSMIMEEncKeyPrefs", ortn);
break;
}
}
if(cmsEncoder->signedAttributes & kCMSAttrSigningTime) {
if (cmsEncoder->signingTime == 0)
cmsEncoder->signingTime = CFAbsoluteTimeGetCurrent();
ortn = SecCmsSignerInfoAddSigningTime(signerInfo, cmsEncoder->signingTime);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignerInfoAddSigningTime", ortn);
break;
}
}
ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsSignedDataAddSignerInfo", ortn);
break;
}
CFRELEASE(ourKc);
CFRELEASE(ourCert);
ourKc = NULL;
ourCert = NULL;
}
if(ortn) {
CFRELEASE(ourKc);
CFRELEASE(ourCert);
}
return ortn;
}
static OSStatus cmsSetupForEnvelopedData(
CMSEncoderRef cmsEncoder)
{
ASSERT(cmsEncoder->op == EO_Encrypt);
ASSERT(cmsEncoder->recipients != NULL);
SecCmsContentInfoRef contentInfo = NULL;
SecCmsEnvelopedDataRef envelopedData = NULL;
SECOidTag algorithmTag;
int keySize;
OSStatus ortn;
CFIndex numCerts = CFArrayGetCount(cmsEncoder->recipients);
CFIndex dex;
SecCertificateRef *certArray = (SecCertificateRef *)malloc(
(numCerts+1) * sizeof(SecCertificateRef));
for(dex=0; dex<numCerts; dex++) {
certArray[dex] = (SecCertificateRef)CFArrayGetValueAtIndex(
cmsEncoder->recipients, dex);
}
certArray[numCerts] = NULL;
ortn = SecSMIMEFindBulkAlgForRecipients(certArray, &algorithmTag, &keySize);
free(certArray);
if(ortn) {
CSSM_PERROR("SecSMIMEFindBulkAlgForRecipients", ortn);
return ortn;
}
if(cmsEncoder->cmsMsg != NULL) {
SecCmsMessageDestroy(cmsEncoder->cmsMsg);
}
cmsEncoder->cmsMsg = SecCmsMessageCreate(NULL);
if(cmsEncoder->cmsMsg == NULL) {
return errSecInternalComponent;
}
envelopedData = SecCmsEnvelopedDataCreate(cmsEncoder->cmsMsg,
algorithmTag, keySize);
if(envelopedData == NULL) {
return errSecInternalComponent;
}
contentInfo = SecCmsMessageGetContentInfo(cmsEncoder->cmsMsg);
ortn = SecCmsContentInfoSetContentEnvelopedData(cmsEncoder->cmsMsg,
contentInfo, envelopedData);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsContentInfoSetContentEnvelopedData", ortn);
return ortn;
}
contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData);
if(cmsEncoder->eContentType.Data != NULL) {
ortn = SecCmsContentInfoSetContentOther(cmsEncoder->cmsMsg,
contentInfo,
NULL,
FALSE,
&cmsEncoder->eContentType);
}
else {
ortn = SecCmsContentInfoSetContentData(cmsEncoder->cmsMsg,
contentInfo,
NULL ,
cmsEncoder->detachedContent);
}
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsContentInfoSetContentData*", ortn);
return ortn;
}
for(dex=0; dex<numCerts; dex++) {
SecCmsRecipientInfoRef recipientInfo = NULL;
SecCertificateRef thisRecip = (SecCertificateRef)CFArrayGetValueAtIndex(
cmsEncoder->recipients, dex);
recipientInfo = SecCmsRecipientInfoCreate(cmsEncoder->cmsMsg, thisRecip);
ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsEnvelopedDataAddRecipient", ortn);
return ortn;
}
}
return errSecSuccess;
}
static OSStatus cmsSetupCmsMsg(
CMSEncoderRef cmsEncoder)
{
ASSERT(cmsEncoder != NULL);
ASSERT(cmsEncoder->encState == ES_Init);
if((cmsEncoder->signers != NULL) || (cmsEncoder->otherCerts != NULL)) {
if(cmsEncoder->recipients != NULL) {
cmsEncoder->op = EO_SignEncrypt;
}
else {
cmsEncoder->op = EO_Sign;
}
}
else if(cmsEncoder->recipients != NULL) {
cmsEncoder->op = EO_Encrypt;
}
else {
dprintf("CMSEncoderUpdateContent: nothing to do\n");
return errSecParam;
}
OSStatus ortn = errSecSuccess;
switch(cmsEncoder->op) {
case EO_Sign:
case EO_SignEncrypt:
ortn = cmsSetupForSignedData(cmsEncoder);
break;
case EO_Encrypt:
ortn = cmsSetupForEnvelopedData(cmsEncoder);
break;
}
cmsEncoder->encState = ES_Msg;
return ortn;
}
typedef struct {
CSSM_OID contentType;
CSSM_DATA content;
} SimpleContentInfo;
static const SecAsn1Template cmsSimpleContentInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SimpleContentInfo) },
{ SEC_ASN1_OBJECT_ID, offsetof(SimpleContentInfo, contentType) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(SimpleContentInfo, content),
kSecAsn1AnyTemplate },
{ 0, }
};
static OSStatus cmsContentInfoContent(
SecAsn1CoderRef asn1Coder,
const CSSM_DATA *contentInfo,
CSSM_DATA *content)
{
OSStatus ortn;
SimpleContentInfo decodedInfo;
memset(&decodedInfo, 0, sizeof(decodedInfo));
ortn = SecAsn1DecodeData(asn1Coder, contentInfo,
cmsSimpleContentInfoTemplate, &decodedInfo);
if(ortn) {
return ortn;
}
if(decodedInfo.content.Data == NULL) {
dprintf("***Error decoding contentInfo: no content\n");
return errSecInternalComponent;
}
*content = decodedInfo.content;
return errSecSuccess;
}
#pragma mark --- Start of Public API ---
CFTypeID CMSEncoderGetTypeID(void)
{
static pthread_once_t once = PTHREAD_ONCE_INIT;
if(cmsEncoderTypeID == _kCFRuntimeNotATypeID) {
pthread_once(&once, &cmsEncoderClassInitialize);
}
return cmsEncoderTypeID;
}
OSStatus CMSEncoderCreate(
CMSEncoderRef *cmsEncoderOut)
{
CMSEncoderRef cmsEncoder = NULL;
uint32_t extra = sizeof(*cmsEncoder) - sizeof(cmsEncoder->base);
cmsEncoder = (CMSEncoderRef)_CFRuntimeCreateInstance(NULL, CMSEncoderGetTypeID(),
extra, NULL);
if(cmsEncoder == NULL) {
return errSecAllocate;
}
cmsEncoder->encState = ES_Init;
cmsEncoder->chainMode = kCMSCertificateChain;
*cmsEncoderOut = cmsEncoder;
return errSecSuccess;
}
#pragma mark --- Getters & Setters ---
OSStatus CMSEncoderAddSigners(
CMSEncoderRef cmsEncoder,
CFTypeRef signerOrArray)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
return cmsAppendToArray(signerOrArray, &cmsEncoder->signers, SecIdentityGetTypeID());
}
OSStatus CMSEncoderCopySigners(
CMSEncoderRef cmsEncoder,
CFArrayRef *signers)
{
if((cmsEncoder == NULL) || (signers == NULL)) {
return errSecParam;
}
if(cmsEncoder->signers != NULL) {
CFRetain(cmsEncoder->signers);
}
*signers = cmsEncoder->signers;
return errSecSuccess;
}
OSStatus CMSEncoderAddRecipients(
CMSEncoderRef cmsEncoder,
CFTypeRef recipientOrArray)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
return cmsAppendToArray(recipientOrArray, &cmsEncoder->recipients,
SecCertificateGetTypeID());
}
OSStatus CMSEncoderCopyRecipients(
CMSEncoderRef cmsEncoder,
CFArrayRef *recipients)
{
if((cmsEncoder == NULL) || (recipients == NULL)) {
return errSecParam;
}
if(cmsEncoder->recipients != NULL) {
CFRetain(cmsEncoder->recipients);
}
*recipients = cmsEncoder->recipients;
return errSecSuccess;
}
OSStatus CMSEncoderAddSupportingCerts(
CMSEncoderRef cmsEncoder,
CFTypeRef certOrArray)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
return cmsAppendToArray(certOrArray, &cmsEncoder->otherCerts,
SecCertificateGetTypeID());
}
OSStatus CMSEncoderCopySupportingCerts(
CMSEncoderRef cmsEncoder,
CFArrayRef *certs)
{
if((cmsEncoder == NULL) || (certs == NULL)) {
return errSecParam;
}
if(cmsEncoder->otherCerts != NULL) {
CFRetain(cmsEncoder->otherCerts);
}
*certs = cmsEncoder->otherCerts;
return errSecSuccess;
}
OSStatus CMSEncoderSetHasDetachedContent(
CMSEncoderRef cmsEncoder,
Boolean detachedContent)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
cmsEncoder->detachedContent = detachedContent;
return errSecSuccess;
}
OSStatus CMSEncoderGetHasDetachedContent(
CMSEncoderRef cmsEncoder,
Boolean *detachedContent)
{
if((cmsEncoder == NULL) || (detachedContent == NULL)) {
return errSecParam;
}
*detachedContent = cmsEncoder->detachedContent;
return errSecSuccess;
}
OSStatus CMSEncoderSetEncapsulatedContentType(
CMSEncoderRef cmsEncoder,
const CSSM_OID *eContentType)
{
if((cmsEncoder == NULL) || (eContentType == NULL)) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
CSSM_OID *ecOid = &cmsEncoder->eContentType;
if(ecOid->Data != NULL) {
free(ecOid->Data);
}
cmsCopyCmsData(eContentType, ecOid);
return errSecSuccess;
}
OSStatus CMSEncoderSetEncapsulatedContentTypeOID(
CMSEncoderRef cmsEncoder,
CFTypeRef eContentTypeOID)
{
CSSM_OID contentType = { 0, NULL };
if (!eContentTypeOID || convertOid(eContentTypeOID, &contentType) != 0)
return errSecParam;
OSStatus result = CMSEncoderSetEncapsulatedContentType(cmsEncoder, &contentType);
if (contentType.Data)
free(contentType.Data);
return result;
}
OSStatus CMSEncoderCopyEncapsulatedContentType(
CMSEncoderRef cmsEncoder,
CFDataRef *eContentType)
{
if((cmsEncoder == NULL) || (eContentType == NULL)) {
return errSecParam;
}
CSSM_OID *ecOid = &cmsEncoder->eContentType;
if(ecOid->Data == NULL) {
*eContentType = NULL;
}
else {
*eContentType = CFDataCreate(NULL, ecOid->Data, ecOid->Length);
}
return errSecSuccess;
}
OSStatus CMSEncoderAddSignedAttributes(
CMSEncoderRef cmsEncoder,
CMSSignedAttributes signedAttributes)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
cmsEncoder->signedAttributes = signedAttributes;
return errSecSuccess;
}
OSStatus CMSEncoderSetSigningTime(
CMSEncoderRef cmsEncoder,
CFAbsoluteTime time)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
cmsEncoder->signingTime = time;
return errSecSuccess;
}
OSStatus CMSEncoderSetCertificateChainMode(
CMSEncoderRef cmsEncoder,
CMSCertificateChainMode chainMode)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
if(cmsEncoder->encState != ES_Init) {
return errSecParam;
}
switch(chainMode) {
case kCMSCertificateNone:
case kCMSCertificateSignerOnly:
case kCMSCertificateChain:
case kCMSCertificateChainWithRoot:
break;
default:
return errSecParam;
}
cmsEncoder->chainMode = chainMode;
return errSecSuccess;
}
OSStatus CMSEncoderGetCertificateChainMode(
CMSEncoderRef cmsEncoder,
CMSCertificateChainMode *chainModeOut)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
*chainModeOut = cmsEncoder->chainMode;
return errSecSuccess;
}
void
CmsMessageSetTSACallback(CMSEncoderRef cmsEncoder, SecCmsTSACallback tsaCallback)
{
if (cmsEncoder->cmsMsg)
SecCmsMessageSetTSACallback(cmsEncoder->cmsMsg, tsaCallback);
}
void
CmsMessageSetTSAContext(CMSEncoderRef cmsEncoder, CFTypeRef tsaContext)
{
if (cmsEncoder->cmsMsg)
SecCmsMessageSetTSAContext(cmsEncoder->cmsMsg, tsaContext);
}
#pragma mark --- Action ---
OSStatus CMSEncoderUpdateContent(
CMSEncoderRef cmsEncoder,
const void *content,
size_t contentLen)
{
if(cmsEncoder == NULL) {
return errSecParam;
}
OSStatus ortn = errSecSuccess;
switch(cmsEncoder->encState) {
case ES_Init:
ortn = cmsSetupCmsMsg(cmsEncoder);
if(ortn) {
return ortn;
}
case ES_Msg:
ASSERT(cmsEncoder->cmsMsg != NULL);
ASSERT(cmsEncoder->encoder == NULL);
ortn = cmsSetupEncoder(cmsEncoder);
if(ortn) {
return ortn;
}
cmsEncoder->encState = ES_Updating;
break;
case ES_Updating:
ASSERT(cmsEncoder->encoder != NULL);
break;
case ES_Final:
return errSecParam;
default:
return errSecInternalComponent;
}
ortn = SecCmsEncoderUpdate(cmsEncoder->encoder, content, (CFIndex)contentLen);
if(ortn) {
ortn = cmsRtnToOSStatus(ortn);
CSSM_PERROR("SecCmsEncoderUpdate", ortn);
}
return ortn;
}
OSStatus CMSEncoderCopyEncodedContent(
CMSEncoderRef cmsEncoder,
CFDataRef *encodedContent)
{
if((cmsEncoder == NULL) || (encodedContent == NULL)) {
return errSecParam;
}
OSStatus ortn;
switch(cmsEncoder->encState) {
case ES_Updating:
break;
case ES_Final:
return errSecParam;
case ES_Msg:
case ES_Init:
if((cmsEncoder->signers != NULL) ||
(cmsEncoder->recipients != NULL) ||
(cmsEncoder->otherCerts == NULL)) {
return errSecParam;
}
ortn = cmsSetupForSignedData(cmsEncoder);
if(ortn) {
return ortn;
}
ortn = cmsSetupEncoder(cmsEncoder);
if(ortn) {
return ortn;
}
break;
}
ASSERT(cmsEncoder->encoder != NULL);
ortn = SecCmsEncoderFinish(cmsEncoder->encoder);
cmsEncoder->encoder = NULL;
if(ortn) {
return cmsRtnToOSStatus(ortn);
}
cmsEncoder->encState = ES_Final;
if((cmsEncoder->encoderOut.Data == NULL) && !cmsEncoder->customCoder) {
dprintf("Successful encode, but no data\n");
return errSecInternalComponent;
}
if(cmsEncoder->customCoder) {
*encodedContent = NULL;
return errSecSuccess;
}
switch(cmsEncoder->op) {
case EO_Sign:
case EO_Encrypt:
*encodedContent = CFDataCreate(NULL, (const UInt8 *)cmsEncoder->encoderOut.Data,
cmsEncoder->encoderOut.Length);
return errSecSuccess;
case EO_SignEncrypt:
break;
}
SecAsn1CoderRef asn1Coder = NULL;
CSSM_DATA signedData = {0, NULL};
ortn = SecAsn1CoderCreate(&asn1Coder);
if(ortn) {
return ortn;
}
ortn = cmsContentInfoContent(asn1Coder, &cmsEncoder->encoderOut, &signedData);
if(ortn) {
goto errOut;
}
ortn = CMSEncode(NULL,
cmsEncoder->recipients,
&CSSMOID_PKCS7_SignedData,
FALSE,
kCMSAttrNone,
signedData.Data, signedData.Length,
encodedContent);
errOut:
if(asn1Coder) {
SecAsn1CoderRelease(asn1Coder);
}
return ortn;
}
#pragma mark --- High-level API ---
OSStatus CMSEncode(
CFTypeRef signers,
CFTypeRef recipients,
const CSSM_OID *eContentType,
Boolean detachedContent,
CMSSignedAttributes signedAttributes,
const void *content,
size_t contentLen,
CFDataRef *encodedContent)
{
if((signers == NULL) && (recipients == NULL)) {
return errSecParam;
}
if(encodedContent == NULL) {
return errSecParam;
}
CMSEncoderRef cmsEncoder;
OSStatus ortn;
ortn = CMSEncoderCreate(&cmsEncoder);
if(ortn) {
return ortn;
}
if(signers) {
ortn = CMSEncoderAddSigners(cmsEncoder, signers);
if(ortn) {
goto errOut;
}
}
if(recipients) {
ortn = CMSEncoderAddRecipients(cmsEncoder, recipients);
if(ortn) {
goto errOut;
}
}
if(eContentType) {
ortn = CMSEncoderSetEncapsulatedContentType(cmsEncoder, eContentType);
if(ortn) {
goto errOut;
}
}
if(detachedContent) {
ortn = CMSEncoderSetHasDetachedContent(cmsEncoder, detachedContent);
if(ortn) {
goto errOut;
}
}
if(signedAttributes) {
ortn = CMSEncoderAddSignedAttributes(cmsEncoder, signedAttributes);
if(ortn) {
goto errOut;
}
}
ortn = CMSEncoderUpdateContent(cmsEncoder, content, contentLen);
if(ortn) {
goto errOut;
}
ortn = CMSEncoderCopyEncodedContent(cmsEncoder, encodedContent);
errOut:
CFRelease(cmsEncoder);
return ortn;
}
OSStatus CMSEncodeContent(
CFTypeRef signers,
CFTypeRef recipients,
CFTypeRef eContentTypeOID,
Boolean detachedContent,
CMSSignedAttributes signedAttributes,
const void *content,
size_t contentLen,
CFDataRef *encodedContentOut)
{
CSSM_OID contentType = { 0, NULL };
if (eContentTypeOID && convertOid(eContentTypeOID, &contentType) != 0)
return errSecParam;
const CSSM_OID *contentTypePtr = (eContentTypeOID) ? &contentType : NULL;
OSStatus result = CMSEncode(signers, recipients, contentTypePtr,
detachedContent, signedAttributes,
content, contentLen, encodedContentOut);
if (contentType.Data)
free(contentType.Data);
return result;
}
#pragma mark --- SPI routines declared in CMSPrivate.h ---
OSStatus CMSEncoderGetCmsMessage(
CMSEncoderRef cmsEncoder,
SecCmsMessageRef *cmsMessage)
{
if((cmsEncoder == NULL) || (cmsMessage == NULL)) {
return errSecParam;
}
if(cmsEncoder->cmsMsg != NULL) {
ASSERT(cmsEncoder->encState != ES_Init);
*cmsMessage = cmsEncoder->cmsMsg;
return errSecSuccess;
}
OSStatus ortn = cmsSetupCmsMsg(cmsEncoder);
if(ortn) {
return ortn;
}
*cmsMessage = cmsEncoder->cmsMsg;
cmsEncoder->encState = ES_Msg;
return errSecSuccess;
}
OSStatus CMSEncoderSetEncoder(
CMSEncoderRef cmsEncoder,
SecCmsEncoderRef encoder)
{
if((cmsEncoder == NULL) || (encoder == NULL)) {
return errSecParam;
}
OSStatus ortn;
switch(cmsEncoder->encState) {
case ES_Init:
ASSERT(cmsEncoder->cmsMsg == NULL);
ASSERT(cmsEncoder->encoder == NULL);
ortn = cmsSetupCmsMsg(cmsEncoder);
if(ortn) {
return ortn;
}
case ES_Msg:
ASSERT(cmsEncoder->cmsMsg != NULL);
ASSERT(cmsEncoder->encoder == NULL);
cmsEncoder->encoder = encoder;
cmsEncoder->encState = ES_Updating;
cmsEncoder->customCoder = true;
return errSecSuccess;
default:
return errSecParam;
}
}
OSStatus CMSEncoderGetEncoder(
CMSEncoderRef cmsEncoder,
SecCmsEncoderRef *encoder)
{
if((cmsEncoder == NULL) || (encoder == NULL)) {
return errSecParam;
}
*encoder = cmsEncoder->encoder;
return errSecSuccess;
}
#include <AssertMacros.h>
OSStatus CMSEncoderCopySignerTimestamp(
CMSEncoderRef cmsEncoder,
size_t signerIndex,
CFAbsoluteTime *timestamp)
{
OSStatus status = errSecParam;
SecCmsMessageRef cmsg;
SecCmsSignedDataRef signedData = NULL;
int numContentInfos = 0;
require(cmsEncoder && timestamp, xit);
require_noerr(CMSEncoderGetCmsMessage(cmsEncoder, &cmsg), xit);
numContentInfos = SecCmsMessageContentLevelCount(cmsg);
for (int dex = 0; !signedData && dex < numContentInfos; dex++)
{
SecCmsContentInfoRef ci = SecCmsMessageContentLevel(cmsg, dex);
SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci);
if (tag == SEC_OID_PKCS7_SIGNED_DATA)
if ((signedData = SecCmsSignedDataRef(SecCmsContentInfoGetContent(ci))))
if (SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, (int)signerIndex))
{
status = SecCmsSignerInfoGetTimestampTime(signerInfo, timestamp);
break;
}
}
xit:
return status;
}