X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/SecurityTests/clxutils/cmstool/cmstool.cpp diff --git a/SecurityTests/clxutils/cmstool/cmstool.cpp b/SecurityTests/clxutils/cmstool/cmstool.cpp new file mode 100644 index 00000000..ac24ef92 --- /dev/null +++ b/SecurityTests/clxutils/cmstool/cmstool.cpp @@ -0,0 +1,1220 @@ +/* + * cmstool.cpp - manipluate CMS messages, intended to be an alternate for the + * currently useless cms command in /usr/bin/security + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(char **argv) +{ + printf("Usage: %s cmd [option ...]\n", argv[0]); + printf("cmd values:\n"); + printf(" sign -- create signedData\n"); + printf(" envel -- create envelopedData\n"); + printf(" signEnv -- create nested EnvelopedData(signedData(data))\n"); + printf(" parse -- parse a CMS message file\n"); + printf("Options:\n"); + printf(" -i infile\n"); + printf(" -o outfile\n"); + printf(" -k keychain -- Keychain to search for certs\n"); + printf(" -p -- Use identity picker\n"); + printf(" -r recipient -- specify recipient of enveloped data\n"); + printf(" -c -- parse signer cert\n"); + printf(" -v sign|encr -- verify message is signed/encrypted\n"); + printf(" -e eContentType -- a(uthData)|r(keyData)\n"); + printf(" -d detached -- infile contains detached content (sign only)\n"); + printf(" -D detachedContent -- detached content (parse only)\n"); + printf(" -q -- quiet\n"); + exit(1); +} + +/* high level op */ +typedef enum { + CTO_Sign, + CTO_Envelop, + CTO_SignEnvelop, + CTO_Parse +} CT_Op; + +/* to verify */ +typedef enum { + CTV_None, + CTV_Sign, + CTV_Envelop, + CTV_SignEnvelop +} CT_Vfy; + +/* additional OIDS to specify as eContentType */ +#define OID_PKINIT 0x2B, 6, 1, 5, 2, 3 +#define OID_PKINIT_LEN 6 + +static const uint8 OID_PKINIT_AUTH_DATA[] = {OID_PKINIT, 1}; +static const uint8 OID_PKINIT_DH_KEY_DATA[] = {OID_PKINIT, 2}; +static const uint8 OID_PKINIT_RKEY_DATA[] = {OID_PKINIT, 3}; +static const uint8 OID_PKINIT_KP_CLIENTAUTH[] = {OID_PKINIT, 3}; +static const uint8 OID_PKINIT_KPKDC[] = {OID_PKINIT, 5}; + +static const CSSM_OID CSSMOID_PKINIT_AUTH_DATA = + {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_AUTH_DATA}; +static const CSSM_OID CSSMOID_PKINIT_DH_KEY_DATA = + {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_DH_KEY_DATA}; +static const CSSM_OID CSSMOID_PKINIT_RKEY_DATA = + {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_RKEY_DATA}; +static const CSSM_OID CSSMOID_PKINIT_KP_CLIENTAUTH = + {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KP_CLIENTAUTH}; +static const CSSM_OID CSSMOID_PKINIT_KPKDC = + {OID_PKINIT_LEN+1, (uint8 *)OID_PKINIT_KPKDC}; + +typedef struct { + CSSM_OID contentType; + CSSM_DATA content; +} SimpleContentInfo; + +const SecAsn1Template SimpleContentInfoTemplate[] = { + { 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, } +}; + +/* + * Obtain the content of a contentInfo, This basically strips off the contentType OID + * and returns a mallocd copy of the ASN_ANY content. + */ +static OSStatus ContentInfoContent( + const unsigned char *contentInfo, + unsigned contentInfoLen, + unsigned char **content, /* mallocd and RETURNED */ + unsigned *contentLen) /* RETURNED */ +{ + SecAsn1CoderRef coder = NULL; + OSStatus ortn; + SimpleContentInfo decodedInfo; + + ortn = SecAsn1CoderCreate(&coder); + if(ortn) { + return ortn; + } + memset(&decodedInfo, 0, sizeof(decodedInfo)); + ortn = SecAsn1Decode(coder, contentInfo, contentInfoLen, + SimpleContentInfoTemplate, &decodedInfo); + if(ortn) { + goto errOut; + } + if(decodedInfo.content.Data == NULL) { + printf("***Error decoding contentInfo: no content\n"); + ortn = internalComponentErr; + goto errOut; + } + *content = (unsigned char *)malloc(decodedInfo.content.Length); + memmove(*content, decodedInfo.content.Data, decodedInfo.content.Length); + *contentLen = decodedInfo.content.Length; +errOut: + SecAsn1CoderRelease(coder); + return ortn; +} + +/* + * Find a cert in specified keychain or keychain list matching specified + * email address. We happen to knopw that the email address is stored with the + * kSecKeyAlias attribute. + */ +static OSStatus findCert( + const char *emailAddress, + CFTypeRef kcArArray, // kc, array, or even NULL + SecCertificateRef *cert) +{ + OSStatus ortn; + SecKeychainSearchRef srch; + SecKeychainAttributeList attrList; + SecKeychainAttribute attr; + + attr.tag = kSecKeyAlias; + attr.length = strlen(emailAddress); + attr.data = (void *)emailAddress; + attrList.count = 1; + attrList.attr = &attr; + + ortn = SecKeychainSearchCreateFromAttributes(kcArArray, + kSecCertificateItemClass, + &attrList, + &srch); + if(ortn) { + cssmPerror("SecKeychainSearchCreateFromAttributes", ortn); + return ortn; + } + + ortn = SecKeychainSearchCopyNext(srch, (SecKeychainItemRef *)cert); + if(ortn) { + printf("***No certs founmd matching recipient %s. Aborting.\n", + emailAddress); + return ortn; + } + CFRelease(srch); + return noErr; +} + +static void evalSecTrust( + SecTrustRef secTrust, + bool quiet) +{ + OSStatus ortn; + SecTrustResultType secTrustResult; + + ortn = SecTrustEvaluate(secTrust, &secTrustResult); + if(ortn) { + /* should never happen */ + cssmPerror("SecTrustEvaluate", ortn); + return; + } + switch(secTrustResult) { + case kSecTrustResultUnspecified: + /* cert chain valid, no special UserTrust assignments */ + case kSecTrustResultProceed: + /* cert chain valid AND user explicitly trusts this */ + if(!quiet) { + fprintf(stderr, "Successful\n"); + } + return; + case kSecTrustResultDeny: + case kSecTrustResultConfirm: + /* + * Cert chain may well have verified OK, but user has flagged + * one of these certs as untrustable. + */ + printf("Not trusted per user-specified Trust level\n"); + return; + default: + { + /* get low-level TP error */ + OSStatus tpStatus; + ortn = SecTrustGetCssmResultCode(secTrust, &tpStatus); + if(ortn) { + cssmPerror("SecTrustGetCssmResultCode", ortn); + return; + } + switch(tpStatus) { + case CSSMERR_TP_INVALID_ANCHOR_CERT: + fprintf(stderr, "Untrusted root\n"); + return; + case CSSMERR_TP_NOT_TRUSTED: + /* no root, not even in implicit SSL roots */ + fprintf(stderr, "No root cert found\n"); + return; + case CSSMERR_TP_CERT_EXPIRED: + fprintf(stderr, "Expired cert\n"); + return; + case CSSMERR_TP_CERT_NOT_VALID_YET: + fprintf(stderr, "Cert not valid yet\n"); + break; + default: + printf("Other cert failure: "); + cssmPerror("", tpStatus); + return; + } + } + } /* SecTrustEvaluate error */ + +} +static OSStatus parseSignedData( + SecCmsSignedDataRef signedData, + SecArenaPoolRef arena, /* used for detached content only */ + const unsigned char *detachedData, + unsigned detachedDataLen, + CT_Vfy vfyOp, + bool quiet, + bool parseSignerCert) +{ + Boolean b; + b = SecCmsSignedDataHasDigests(signedData); + if(!quiet) { + printf(" has digests : %s\n", b ? "true" : "false"); + } + + SecTrustRef secTrust = NULL; + OSStatus ortn; + SecPolicyRef policy = NULL; + SecPolicySearchRef policySearch = NULL; + + ortn = SecPolicySearchCreate(CSSM_CERT_X_509v3, + &CSSMOID_APPLE_X509_BASIC, + NULL, + &policySearch); + if(ortn) { + cssmPerror("SecPolicySearchCreate", ortn); + return ortn; + } + ortn = SecPolicySearchCopyNext(policySearch, &policy); + if(ortn) { + cssmPerror("SecPolicySearchCopyNext", ortn); + return ortn; + } + + int numSigners = SecCmsSignedDataSignerInfoCount(signedData); + if(!quiet) { + printf(" num signers : %d\n", numSigners); + } + for(int dex=0; dex "); + /* FIXME - does this make sense? Error? */ + } + } + else if(detachedData != NULL) { + /* digest the detached content */ + SECAlgorithmID **digestAlgorithms = SecCmsSignedDataGetDigestAlgs(signedData); + SecCmsDigestContextRef digcx = SecCmsDigestContextStartMultiple(digestAlgorithms); + CSSM_DATA **digests = NULL; + + SecCmsDigestContextUpdate(digcx, detachedData, detachedDataLen); + ortn = SecCmsDigestContextFinishMultiple(digcx, arena, &digests); + if(ortn) { + fprintf(stderr, "SecCmsDigestContextFinishMultiple() returned %d\n", (int)ortn); + } + else { + SecCmsSignedDataSetDigests(signedData, digestAlgorithms, digests); + } + } + else { + fprintf(stderr, " "); + } + ortn = SecCmsSignedDataVerifySignerInfo(signedData, dex, NULL, + policy, &secTrust); + if(ortn) { + fprintf(stderr, "vfSignerInfo() returned %d\n", (int)ortn); + fprintf(stderr, " vfy status : "); + } + if(secTrust == NULL) { + fprintf(stderr, "***NO SecTrust available!\n"); + } + else { + evalSecTrust(secTrust, quiet); + } + + SecCmsSignerInfoRef signerInfo = SecCmsSignedDataGetSignerInfo(signedData, dex); + CFStringRef emailAddrs = SecCmsSignerInfoGetSignerCommonName(signerInfo); + char emailStr[1000]; + if(!quiet) { + fprintf(stderr, " signer : "); + } + if(emailAddrs == NULL) { + fprintf(stderr, "<>\n"); + } + else { + if(!CFStringGetCString(emailAddrs, emailStr, 1000, kCFStringEncodingASCII)) { + fprintf(stderr, "*** Error converting email address to C string\n"); + } + else if(!quiet) { + + fprintf(stderr, "%s\n", emailStr); + } + } + if(parseSignerCert) { + SecCertificateRef signer; + signer = SecCmsSignerInfoGetSigningCertificate(signerInfo, NULL); + if(signer) { + CSSM_DATA certData; + ortn = SecCertificateGetData(signer, &certData); + if(ortn) { + fprintf(stderr, "***Error getting signing cert data***\n"); + cssmPerror("SecCertificateGetData", ortn); + } + else { + printf("========== Signer Cert==========\n\n"); + printCert(certData.Data, certData.Length, CSSM_FALSE); + printf("========== End Signer Cert==========\n\n"); + } + } + else { + fprintf(stderr, "***Error getting signing cert ***\n"); + } + } + } + return ortn; +} + +static OSStatus doParse( + const unsigned char *data, + unsigned dataLen, + const unsigned char *detachedData, + unsigned detachedDataLen, + CT_Vfy vfyOp, + bool parseSignerCert, + bool quiet, + unsigned char **outData, // mallocd and RETURNED + unsigned *outDataLen) // RETURNED +{ + if((data == NULL) || (dataLen == 0)) { + fprintf(stderr, "***Parse requires input file. Aborting.\n"); + return paramErr; + } + + SecArenaPoolRef arena = NULL; + SecArenaPoolCreate(1024, &arena); + SecCmsMessageRef cmsMsg = NULL; + SecCmsDecoderRef decoder; + OSStatus ortn; + OSStatus ourRtn = noErr; + bool foundOneSigned = false; + bool foundOneEnveloped = false; + + ortn = SecCmsDecoderCreate(arena, NULL, NULL, NULL, NULL, NULL, NULL, &decoder); + if(ortn) { + cssmPerror("SecCmsDecoderCreate", ortn); + return ortn; + } + ortn = SecCmsDecoderUpdate(decoder, data, dataLen); + if(ortn) { + cssmPerror("SecCmsDecoderUpdate", ortn); + return ortn; + } + ortn = SecCmsDecoderFinish(decoder, &cmsMsg); + if(ortn) { + cssmPerror("SecCmsDecoderFinish", ortn); + return ortn; + } + + Boolean b = SecCmsMessageIsSigned(cmsMsg); + switch(vfyOp) { + case CTV_None: + break; + case CTV_Sign: + if(!b) { + fprintf(stderr, "***Expected SignedData, but !SecCmsMessageIsSigned()\n"); + ourRtn = -1; + } + break; + case CTV_SignEnvelop: + if(!b) { + fprintf(stderr, "***Expected Signed&Enveloped, but !SecCmsMessageIsSigned()\n"); + ourRtn = -1; + } + break; + case CTV_Envelop: + if(b) { + fprintf(stderr, "***Expected EnvelopedData, but SecCmsMessageIsSigned() " + "TRUE\n"); + ourRtn = -1; + } + break; + } + int numContentInfos = SecCmsMessageContentLevelCount(cmsMsg); + if(!quiet) { + fprintf(stderr, "=== CMS message info ===\n"); + fprintf(stderr, " Signed : %s\n", b ? "true" : "false"); + b = SecCmsMessageIsEncrypted(cmsMsg); + fprintf(stderr, " Encrypted : %s\n", b ? "true" : "false"); + b = SecCmsMessageContainsCertsOrCrls(cmsMsg); + fprintf(stderr, " certs/crls : %s\n", b ? "present" : "not present"); + fprintf(stderr, " Num ContentInfos : %d\n", numContentInfos); + } + + /* FIXME needs work for CTV_SignEnvelop */ + OidParser oidParser; + for(int dex=0; dexLength == 0) { + printf("***EMPTY***\n"); + } + else { + char str[OID_PARSER_STRING_SIZE]; + oidParser.oidParse(typeOid->Data, typeOid->Length, str); + printf("%s\n", str); + } + } + SECOidTag tag = SecCmsContentInfoGetContentTypeTag(ci); + switch(tag) { + case SEC_OID_PKCS7_SIGNED_DATA: + { + switch(vfyOp) { + case CTV_None: // caller doesn't care + case CTV_Sign: // got what we wanted + break; + case CTV_Envelop: + fprintf(stderr, "***Expected EnvelopedData, got SignedData\n"); + ourRtn = -1; + break; + case CTV_SignEnvelop: + printf("CTV_SignEnvelop code on demand\n"); + break; + } + foundOneSigned = true; + SecCmsSignedDataRef sd = + (SecCmsSignedDataRef) SecCmsContentInfoGetContent(ci); + parseSignedData(sd, arena, + detachedData, detachedDataLen, + vfyOp, quiet, parseSignerCert); + break; + } + case SEC_OID_PKCS7_DATA: + case SEC_OID_OTHER: + break; + case SEC_OID_PKCS7_ENVELOPED_DATA: + foundOneEnveloped = true; + if(vfyOp == CTV_Sign) { + fprintf(stderr, "***Expected SignedData, EnvelopedData\n"); + ourRtn = -1; + break; + } + case SEC_OID_PKCS7_ENCRYPTED_DATA: + switch(vfyOp) { + case CTV_None: + break; + case CTV_Sign: + fprintf(stderr, "***Expected SignedData, got EncryptedData\n"); + ourRtn = -1; + break; + case CTV_Envelop: + fprintf(stderr, "***Expected EnvelopedData, got EncryptedData\n"); + ourRtn = -1; + break; + case CTV_SignEnvelop: + printf("CTV_SignEnvelop code on demand\n"); + break; + } + break; + default: + fprintf(stderr, " other content type TBD\n"); + } + } + if(outData) { + CSSM_DATA_PTR odata = SecCmsMessageGetContent(cmsMsg); + if(odata == NULL) { + fprintf(stderr, "***No inner content available\n"); + } + else { + *outData = (unsigned char *)malloc(odata->Length); + memmove(*outData, odata->Data, odata->Length); + *outDataLen = odata->Length; + } + } + if(arena) { + SecArenaPoolFree(arena, false); + } + switch(vfyOp) { + case CTV_None: + break; + case CTV_Sign: + if(!foundOneSigned) { + fprintf(stderr, "Expected signed, never saw a SignedData\n"); + ourRtn = -1; + } + break; + case CTV_Envelop: + if(!foundOneEnveloped) { + fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n"); + ourRtn = -1; + } + break; + case CTV_SignEnvelop: + if(!foundOneSigned) { + fprintf(stderr, "Expected signed, never saw a SignedData\n"); + ourRtn = -1; + } + if(!foundOneEnveloped) { + fprintf(stderr, "Expected enveloped, never saw an EnvelopedData\n"); + ourRtn = -1; + } + break; + } + /* free decoder? cmsMsg? */ + return ourRtn; +} + +/* + * Common encode routine. + */ +#if 1 +/* the simple way, when 3655861 is fixed */ +static OSStatus encodeCms( + SecCmsMessageRef cmsMsg, + const unsigned char *inData, // add in this + unsigned inDataLen, + unsigned char **outData, // mallocd and RETURNED + unsigned *outDataLen) // RETURNED +{ + SecArenaPoolRef arena = NULL; + SecArenaPoolCreate(1024, &arena); + CSSM_DATA cdataIn = {inDataLen, (uint8 *)inData}; + CSSM_DATA cdataOut = {0, NULL}; + + OSStatus ortn = SecCmsMessageEncode(cmsMsg, &cdataIn, arena, &cdataOut); + if((ortn == noErr) && (cdataOut.Length != 0)) { + *outData = (unsigned char *)malloc(cdataOut.Length); + memmove(*outData, cdataOut.Data, cdataOut.Length); + *outDataLen = cdataOut.Length; + } + else { + cssmPerror("SecCmsMessageEncode", ortn); + *outData = NULL; + *outDataLen = 0; + } + SecArenaPoolFree(arena, false); + return ortn; +} + +#else + +/* the hard way back when SecCmsMessageEncode() didn't work */ +static OSStatus encodeCms( + SecCmsMessageRef cmsMsg, + const unsigned char *inData, // add in this + unsigned inDataLen, + unsigned char **outData, // mallocd and RETURNED + unsigned *outDataLen) // RETURNED +{ + SecArenaPoolRef arena = NULL; + SecArenaPoolCreate(1024, &arena); + SecCmsEncoderRef cmsEnc = NULL; + CSSM_DATA output = { 0, NULL }; + OSStatus ortn; + + ortn = SecCmsEncoderCreate(cmsMsg, + NULL, NULL, // no callback + &output, arena, // data goes here + NULL, NULL, // no password callback (right?) + NULL, NULL, // decrypt key callback + NULL, NULL, // detached digests + &cmsEnc); + if(ortn) { + cssmPerror("SecKeychainItemCopyKeychain", ortn); + goto errOut; + } + ortn = SecCmsEncoderUpdate(cmsEnc, (char *)inData, inDataLen); + if(ortn) { + cssmPerror("SecCmsEncoderUpdate", ortn); + goto errOut; + } + ortn = SecCmsEncoderFinish(cmsEnc); + if(ortn) { + cssmPerror("SecCMsEncoderFinish", ortn); + goto errOut; + } + + /* Did we get any data? */ + if(output.Length) { + *outData = (unsigned char *)malloc(output.Length); + memmove(*outData, output.Data, output.Length); + *outDataLen = output.Length; + } + else { + *outData = NULL; + *outDataLen = 0; + } +errOut: + if(arena) { + SecArenaPoolFree(arena, false); + } + return ortn; +} + +#endif + +static OSStatus doSign( + SecIdentityRef signerId, + const unsigned char *inData, + unsigned inDataLen, + bool detachedContent, + const CSSM_OID *eContentType, // OPTIONAL + unsigned char **outData, // mallocd and RETURNED + unsigned *outDataLen) // RETURNED +{ + if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { + fprintf(stderr, "***Sign requires input file. Aborting.\n"); + return paramErr; + } + if(signerId == NULL) { + fprintf(stderr, "***Sign requires a signing identity. Aborting.\n"); + return paramErr; + } + + SecCmsMessageRef cmsMsg = NULL; + SecCmsContentInfoRef contentInfo = NULL; + SecCmsSignedDataRef signedData = NULL; + SecCertificateRef ourCert = NULL; + SecCmsSignerInfoRef signerInfo; + OSStatus ortn; + SecKeychainRef ourKc = NULL; + + ortn = SecIdentityCopyCertificate(signerId, &ourCert); + if(ortn) { + cssmPerror("SecIdentityCopyCertificate", ortn); + return ortn; + } + ortn = SecKeychainItemCopyKeychain((SecKeychainItemRef)ourCert, &ourKc); + if(ortn) { + cssmPerror("SecKeychainItemCopyKeychain", ortn); + goto errOut; + } + + // build chain of objects: message->signedData->data + cmsMsg = SecCmsMessageCreate(NULL); + if(cmsMsg == NULL) { + fprintf(stderr, "***Error creating SecCmsMessageRef\n"); + ortn = -1; + goto errOut; + } + signedData = SecCmsSignedDataCreate(cmsMsg); + if(signedData == NULL) { + printf("***Error creating SecCmsSignedDataRef\n"); + ortn = -1; + goto errOut; + } + contentInfo = SecCmsMessageGetContentInfo(cmsMsg); + ortn = SecCmsContentInfoSetContentSignedData(cmsMsg, contentInfo, signedData); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentSignedData", ortn); + goto errOut; + } + contentInfo = SecCmsSignedDataGetContentInfo(signedData); + if(eContentType != NULL) { + ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo, + NULL /* data */, + detachedContent, + eContentType); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentData", ortn); + goto errOut; + } + } + else { + ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, + detachedContent); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentData", ortn); + goto errOut; + } + } + + /* + * create & attach signer information + */ + signerInfo = SecCmsSignerInfoCreate(cmsMsg, signerId, SEC_OID_SHA1); + if (signerInfo == NULL) { + fprintf(stderr, "***Error on SecCmsSignerInfoCreate\n"); + ortn = -1; + goto errOut; + } + /* we want the cert chain included for this one */ + /* FIXME - what's the significance of the usage? */ + ortn = SecCmsSignerInfoIncludeCerts(signerInfo, SecCmsCMCertChain, certUsageEmailSigner); + if(ortn) { + cssmPerror("SecCmsSignerInfoIncludeCerts", ortn); + goto errOut; + } + + /* other options go here - signing time, etc. */ + + ortn = SecCmsSignerInfoAddSMIMEEncKeyPrefs(signerInfo, ourCert, ourKc); + if(ortn) { + cssmPerror("SecCmsSignerInfoAddSMIMEEncKeyPrefs", ortn); + goto errOut; + } + ortn = SecCmsSignedDataAddCertificate(signedData, ourCert); + if(ortn) { + cssmPerror("SecCmsSignedDataAddCertificate", ortn); + goto errOut; + } + + ortn = SecCmsSignedDataAddSignerInfo(signedData, signerInfo); + if(ortn) { + cssmPerror("SecCmsSignedDataAddSignerInfo", ortn); + goto errOut; + } + + /* go */ + ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen); +errOut: + /* free resources */ + if(cmsMsg) { + SecCmsMessageDestroy(cmsMsg); + } + if(ourCert) { + CFRelease(ourCert); + } + if(ourKc) { + CFRelease(ourKc); + } + return ortn; +} + +static OSStatus doEncrypt( + SecCertificateRef recipCert, // eventually more than one + const unsigned char *inData, + unsigned inDataLen, + unsigned char **outData, // mallocd and RETURNED + unsigned *outDataLen) // RETURNED +{ + if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { + fprintf(stderr, "***Encrypt requires input file. Aborting.\n"); + return paramErr; + } + if(recipCert == NULL) { + fprintf(stderr, "***Encrypt requires a recipient certificate. Aborting.\n"); + return paramErr; + } + + SecCmsMessageRef cmsMsg = NULL; + SecCmsContentInfoRef contentInfo = NULL; + SecCmsEnvelopedDataRef envelopedData = NULL; + SecCmsRecipientInfoRef recipientInfo = NULL; + OSStatus ortn; + SecCertificateRef allCerts[2] = { recipCert, NULL}; + + SECOidTag algorithmTag; + int keySize; + + ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize); + if(ortn) { + cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn); + return ortn; + } + + // build chain of objects: message->envelopedData->data + cmsMsg = SecCmsMessageCreate(NULL); + if(cmsMsg == NULL) { + fprintf(stderr, "***Error creating SecCmsMessageRef\n"); + ortn = -1; + goto errOut; + } + envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize); + if(envelopedData == NULL) { + fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n"); + ortn = -1; + goto errOut; + } + contentInfo = SecCmsMessageGetContentInfo(cmsMsg); + ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn); + goto errOut; + } + contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData); + ortn = SecCmsContentInfoSetContentData(cmsMsg, contentInfo, NULL /* data */, false); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentData", ortn); + goto errOut; + } + + /* + * create & attach recipient information + */ + recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert); + ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo); + if(ortn) { + cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn); + goto errOut; + } + + + /* go */ + ortn = encodeCms(cmsMsg, inData, inDataLen, outData, outDataLen); +errOut: + /* free resources */ + if(cmsMsg) { + SecCmsMessageDestroy(cmsMsg); + } + return ortn; +} + +/* create nested message: msg = EnvelopedData(SignedData(inData)) */ +static OSStatus doSignEncrypt( + SecCertificateRef recipCert, // encryption recipient + SecIdentityRef signerId, // signer + const CSSM_OID *eContentType, // OPTIONAL - for signedData + const unsigned char *inData, + unsigned inDataLen, + unsigned char **outData, // mallocd and RETURNED + unsigned *outDataLen) // RETURNED +{ + if((inData == NULL) || (inDataLen == 0) || (outData == NULL)) { + fprintf(stderr, "***Sign/Encrypt requires input file. Aborting.\n"); + return paramErr; + } + if(recipCert == NULL) { + fprintf(stderr, "***Sign/Encrypt requires a recipient certificate. Aborting.\n"); + return paramErr; + } + if(signerId == NULL) { + fprintf(stderr, "***Sign/Encrypt requires a signer Identity. Aborting.\n"); + return paramErr; + } + + OSStatus ortn; + unsigned char *signedData = NULL; + unsigned signedDataLen = 0; + SecCmsMessageRef cmsMsg = NULL; + SecCmsContentInfoRef contentInfo = NULL; + SecCmsEnvelopedDataRef envelopedData = NULL; + SecCmsRecipientInfoRef recipientInfo = NULL; + SecCertificateRef allCerts[2] = { recipCert, NULL}; + SECOidTag algorithmTag; + int keySize; + + /* first get a SignedData */ + ortn = doSign(signerId, inData, inDataLen, + false, /* can't do detached content here */ + eContentType, + &signedData, &signedDataLen); + if(ortn) { + printf("***Error generating inner signedData. Aborting.\n"); + return ortn; + } + + /* extract just the content - don't need the whole ContentINfo */ + unsigned char *signedDataContent = NULL; + unsigned signedDataContentLen = 0; + ortn = ContentInfoContent(signedData, signedDataLen, &signedDataContent, &signedDataContentLen); + if(ortn) { + goto errOut; + } + + /* now wrap that in an EnvelopedData */ + ortn = SecSMIMEFindBulkAlgForRecipients(allCerts, &algorithmTag, &keySize); + if(ortn) { + cssmPerror("SecSMIMEFindBulkAlgForRecipients", ortn); + return ortn; + } + + // build chain of objects: message->envelopedData->data + cmsMsg = SecCmsMessageCreate(NULL); + if(cmsMsg == NULL) { + fprintf(stderr, "***Error creating SecCmsMessageRef\n"); + ortn = -1; + goto errOut; + } + envelopedData = SecCmsEnvelopedDataCreate(cmsMsg, algorithmTag, keySize); + if(envelopedData == NULL) { + fprintf(stderr, "***Error creating SecCmsEnvelopedDataRef\n"); + ortn = -1; + goto errOut; + } + contentInfo = SecCmsMessageGetContentInfo(cmsMsg); + ortn = SecCmsContentInfoSetContentEnvelopedData(cmsMsg, contentInfo, envelopedData); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentEnvelopedData", ortn); + goto errOut; + } + contentInfo = SecCmsEnvelopedDataGetContentInfo(envelopedData); + + /* here's the difference: we override the 'data' content with a SignedData type, + * but we fool the smime lib into thinking it's a plain old data so it doesn't try + * to encode the SignedData */ + ortn = SecCmsContentInfoSetContentOther(cmsMsg, contentInfo, + NULL /* data */, + false, + &CSSMOID_PKCS7_SignedData); + if(ortn) { + cssmPerror("SecCmsContentInfoSetContentData", ortn); + goto errOut; + } + + /* + * create & attach recipient information + */ + recipientInfo = SecCmsRecipientInfoCreate(cmsMsg, recipCert); + ortn = SecCmsEnvelopedDataAddRecipient(envelopedData, recipientInfo); + if(ortn) { + cssmPerror("SecCmsEnvelopedDataAddRecipient", ortn); + goto errOut; + } + + + /* go */ + ortn = encodeCms(cmsMsg, signedDataContent, signedDataContentLen, outData, outDataLen); +errOut: + /* free resources */ + if(cmsMsg) { + SecCmsMessageDestroy(cmsMsg); + } + if(signedData) { + free(signedData); + } + if(signedDataContent) { + free(signedDataContent); + } + return ortn; +} + +int main(int argc, char **argv) +{ + if(argc < 2) { + usage(argv); + } + + CT_Op op; + bool needId = false; + if(!strcmp(argv[1], "sign")) { + op = CTO_Sign; + needId = true; + } + else if(!strcmp(argv[1], "envel")) { + op = CTO_Envelop; + } + else if(!strcmp(argv[1], "signEnv")) { + op = CTO_SignEnvelop; + needId = true; + } + else if(!strcmp(argv[1], "parse")) { + op = CTO_Parse; + } + else { + fprintf(stderr, "***Unrecognized cmd.\n"); + usage(argv); + } + + extern int optind; + extern char *optarg; + int arg; + + /* optional args */ + const char *keychainName = NULL; + char *inFileName = NULL; + char *outFileName = NULL; + bool detachedContent = false; + char *detachedFile = NULL; + bool useIdPicker = false; + char *recipient = NULL; + bool quiet = false; + bool parseSignerCert = false; + CT_Vfy vfyOp = CTV_None; + const CSSM_OID *eContentType = NULL; + + optind = 2; + while ((arg = getopt(argc, argv, "i:o:k:pr:e:dD:qcv:")) != -1) { + switch (arg) { + case 'i': + inFileName = optarg; + break; + case 'o': + outFileName = optarg; + break; + case 'k': + keychainName = optarg; + break; + case 'p': + useIdPicker = true; + break; + case 'r': + recipient = optarg; + break; + case 'c': + parseSignerCert = true; + break; + case 'v': + if(!strcmp(optarg, "sign")) { + vfyOp = CTV_Sign; + } + else if(!strcmp(optarg, "encr")) { + vfyOp = CTV_Envelop; + } + else if(!strcmp(optarg, "signEnv")) { + vfyOp = CTV_SignEnvelop; + } + else { + usage(argv); + } + break; + case 'e': + switch(optarg[0]) { + case 'a': + eContentType = &CSSMOID_PKINIT_AUTH_DATA; + break; + case 'r': + eContentType = &CSSMOID_PKINIT_RKEY_DATA; + break; + default: + usage(argv); + } + break; + case 'd': + if(op != CTO_Sign) { + printf("-d only valid for op sign\n"); + exit(1); + } + detachedContent = true; + break; + case 'D': + if(op != CTO_Parse) { + printf("-D only valid for op sign\n"); + exit(1); + } + detachedFile = optarg; + break; + case 'q': + quiet = true; + break; + default: + case '?': + usage(argv); + } + } + if(optind != argc) { + /* getopt does not return '?' */ + usage(argv); + } + + SecIdentityRef idRef = NULL; + SecKeychainRef kcRef = NULL; + SecCertificateRef recipientCert = NULL; + unsigned char *inData = NULL; + unsigned inDataLen = 0; + unsigned char *outData = NULL; + unsigned outDataLen = 0; + unsigned char *detachedData = NULL; + unsigned detachedDataLen = 0; + OSStatus ortn; + + if(inFileName) { + if(readFile(inFileName, &inData, &inDataLen)) { + fprintf(stderr, "***Error reading infile %s. Aborting.\n", inFileName); + exit(1); + } + } + if(detachedFile) { + if(readFile(detachedFile, &detachedData, &detachedDataLen)) { + fprintf(stderr, "***Error reading detachedFile %s. Aborting.\n", detachedFile); + exit(1); + } + } + if(keychainName) { + ortn = SecKeychainOpen(keychainName, &kcRef); + if(ortn) { + cssmPerror("SecKeychainOpen", ortn); + exit(1); + } + } + if(useIdPicker) { + ortn = sslSimpleIdentPicker(kcRef, &idRef); + if(ortn) { + fprintf(stderr, "***Error obtaining identity via picker. Aborting.\n"); + exit(1); + } + } + else if(needId) { + /* use first identity in specified keychain */ + CFArrayRef array = sslKcRefToCertArray(kcRef, CSSM_FALSE, CSSM_FALSE, + NULL, // no verify policy + NULL); + if(array == NULL) { + fprintf(stderr, "***Error finding a signing cert. Aborting.\n"); + exit(1); + } + idRef = (SecIdentityRef)CFArrayGetValueAtIndex(array, 0); + if(idRef == NULL) { + fprintf(stderr, "***No identities found. Aborting.\n"); + exit(1); + } + CFRetain(idRef); + CFRelease(array); + } + if(recipient) { + ortn = findCert(recipient, kcRef, &recipientCert); + if(ortn) { + exit(1); + } + } + + switch(op) { + case CTO_Sign: + ortn = doSign(idRef, inData, inDataLen, + detachedContent, eContentType, + &outData, &outDataLen); + break; + case CTO_Envelop: + if(recipientCert == NULL) { + if(idRef == NULL) { + printf("***Need a recipient or an identity to encrypt\n"); + exit(1); + } + ortn = SecIdentityCopyCertificate(idRef, &recipientCert); + if(ortn) { + cssmPerror("SecIdentityCopyCertificate", ortn); + exit(1); + } + } + ortn = doEncrypt(recipientCert, inData, inDataLen, &outData, &outDataLen); + break; + case CTO_SignEnvelop: + ortn = doSignEncrypt(recipientCert, idRef, eContentType, + inData, inDataLen, &outData, &outDataLen); + break; + case CTO_Parse: + ortn = doParse(inData, inDataLen, + detachedData, detachedDataLen, + vfyOp, parseSignerCert, quiet, + &outData, &outDataLen); + break; + } + if(ortn) { + goto errOut; + } + if(outData && outFileName) { + if(writeFile(outFileName, outData, outDataLen)) { + fprintf(stderr, "***Error writing to %s.\n", outFileName); + ortn = -1; + } + else { + if(!quiet) { + fprintf(stderr, "...wrote %u bytes to %s.\n", outDataLen, outFileName); + } + } + } + else if(outData) { + fprintf(stderr, "...generated %u bytes but no place to write it.\n", outDataLen); + } + else if(outFileName) { + fprintf(stderr, "...nothing to write to file %s.\n", outFileName); + /* assume this is an error, caller wanted something */ + ortn = -1; + } +errOut: + return ortn; +}