static int sm_encrypt(CamelCipherContext *context, const char *userid, GPtrArray *recipients, CamelMimePart *ipart, CamelMimePart *opart, CamelException *ex) { struct _CamelSMIMEContextPrivate *p = ((CamelSMIMEContext *)context)->priv; /*NSSCMSRecipientInfo **recipient_infos;*/ CERTCertificate **recipient_certs = NULL; NSSCMSContentInfo *cinfo; PK11SymKey *bulkkey = NULL; SECOidTag bulkalgtag; int bulkkeysize, i; CK_MECHANISM_TYPE type; PK11SlotInfo *slot; PLArenaPool *poolp; NSSCMSMessage *cmsg = NULL; NSSCMSEnvelopedData *envd; NSSCMSEncoderContext *enc = NULL; CamelStreamMem *mem; CamelStream *ostream = NULL; CamelDataWrapper *dw; CamelContentType *ct; poolp = PORT_NewArena(1024); if (poolp == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM)); return -1; } /* Lookup all recipients certs, for later working */ recipient_certs = (CERTCertificate **)PORT_ArenaZAlloc(poolp, sizeof(*recipient_certs[0])*(recipients->len + 1)); if (recipient_certs == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, g_strerror (ENOMEM)); goto fail; } for (i=0;i<recipients->len;i++) { recipient_certs[i] = CERT_FindCertByNicknameOrEmailAddr(p->certdb, recipients->pdata[i]); if (recipient_certs[i] == NULL) { camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find certificate for `%s'"), recipients->pdata[i]); goto fail; } } /* Find a common algorithm, probably 3DES anyway ... */ if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipient_certs, &bulkalgtag, &bulkkeysize) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot find common bulk encryption algorithm")); goto fail; } /* Generate a new bulk key based on the common algorithm - expensive */ type = PK11_AlgtagToMechanism(bulkalgtag); slot = PK11_GetBestSlot(type, context); if (slot == NULL) { /* PORT_GetError(); ?? */ camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot allocate slot for encryption bulk key")); goto fail; } bulkkey = PK11_KeyGen(slot, type, NULL, bulkkeysize/8, context); PK11_FreeSlot(slot); /* Now we can start building the message */ /* msg->envelopedData->data */ cmsg = NSS_CMSMessage_Create(NULL); if (cmsg == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Message")); goto fail; } envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, bulkkeysize); if (envd == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Enveloped data")); goto fail; } cinfo = NSS_CMSMessage_GetContentInfo(cmsg); if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS Enveloped data")); goto fail; } cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot attach CMS data object")); goto fail; } /* add recipient certs */ for (i=0;recipient_certs[i];i++) { NSSCMSRecipientInfo *ri = NSS_CMSRecipientInfo_Create(cmsg, recipient_certs[i]); if (ri == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create CMS Recipient information")); goto fail; } if (NSS_CMSEnvelopedData_AddRecipient(envd, ri) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot add CMS Recipient information")); goto fail; } } /* dump it out */ ostream = camel_stream_mem_new(); enc = NSS_CMSEncoder_Start(cmsg, sm_write_stream, ostream, NULL, NULL, NULL, NULL, sm_decrypt_key, bulkkey, NULL, NULL); if (enc == NULL) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Cannot create encoder context")); goto fail; } /* FIXME: Stream the input */ /* FIXME: Canonicalise the input? */ mem = (CamelStreamMem *)camel_stream_mem_new(); camel_data_wrapper_write_to_stream((CamelDataWrapper *)ipart, (CamelStream *)mem); if (NSS_CMSEncoder_Update(enc, (char *) mem->buffer->data, mem->buffer->len) != SECSuccess) { NSS_CMSEncoder_Cancel(enc); camel_object_unref(mem); camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to add data to encoder")); goto fail; } camel_object_unref(mem); if (NSS_CMSEncoder_Finish(enc) != SECSuccess) { camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, _("Failed to encode data")); goto fail; } PK11_FreeSymKey(bulkkey); NSS_CMSMessage_Destroy(cmsg); for (i=0;recipient_certs[i];i++) CERT_DestroyCertificate(recipient_certs[i]); PORT_FreeArena(poolp, PR_FALSE); dw = camel_data_wrapper_new(); camel_data_wrapper_construct_from_stream(dw, ostream); camel_object_unref(ostream); dw->encoding = CAMEL_TRANSFER_ENCODING_BINARY; ct = camel_content_type_new("application", "x-pkcs7-mime"); camel_content_type_set_param(ct, "name", "smime.p7m"); camel_content_type_set_param(ct, "smime-type", "enveloped-data"); camel_data_wrapper_set_mime_type_field(dw, ct); camel_content_type_unref(ct); camel_medium_set_content_object((CamelMedium *)opart, dw); camel_object_unref(dw); camel_mime_part_set_disposition(opart, "attachment"); camel_mime_part_set_filename(opart, "smime.p7m"); camel_mime_part_set_description(opart, "S/MIME Encrypted Message"); camel_mime_part_set_encoding(opart, CAMEL_TRANSFER_ENCODING_BASE64); return 0; fail: if (ostream) camel_object_unref(ostream); if (cmsg) NSS_CMSMessage_Destroy(cmsg); if (bulkkey) PK11_FreeSymKey(bulkkey); if (recipient_certs) { for (i=0;recipient_certs[i];i++) CERT_DestroyCertificate(recipient_certs[i]); } PORT_FreeArena(poolp, PR_FALSE); return -1; }
NS_IMETHODIMP nsCMSMessage::CreateEncrypted(nsIArray * aRecipientCerts) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted\n")); NSSCMSContentInfo *cinfo; NSSCMSEnvelopedData *envd; NSSCMSRecipientInfo *recipientInfo; nsZeroTerminatedCertArray recipientCerts; SECOidTag bulkAlgTag; int keySize; PRUint32 i; nsCOMPtr<nsIX509Cert2> nssRecipientCert; nsresult rv = NS_ERROR_FAILURE; // Check the recipient certificates // PRUint32 recipientCertCount; aRecipientCerts->GetLength(&recipientCertCount); PR_ASSERT(recipientCertCount > 0); if (!recipientCerts.allocate(recipientCertCount)) { goto loser; } for (i=0; i<recipientCertCount; i++) { nsCOMPtr<nsIX509Cert> x509cert = do_QueryElementAt(aRecipientCerts, i); nssRecipientCert = do_QueryInterface(x509cert); if (!nssRecipientCert) return NS_ERROR_FAILURE; CERTCertificate *c = nssRecipientCert->GetCert(); CERTCertificateCleaner rcCleaner(c); recipientCerts.set(i, c); } // Find a bulk key algorithm // if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientCerts.getRawArray(), &bulkAlgTag, &keySize) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't find bulk alg for recipients\n")); rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG; goto loser; } m_cmsMsg = NSS_CMSMessage_Create(NULL); if (m_cmsMsg == nsnull) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create new cms message\n")); rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) == nsnull) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create enveloped data\n")); goto loser; } cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create content enveloped data\n")); goto loser; } cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nsnull, PR_FALSE) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't set content data\n")); goto loser; } // Create and attach recipient information // for (i=0; i < recipientCertCount; i++) { CERTCertificate *rc = recipientCerts.get(i); CERTCertificateCleaner rcCleaner(rc); if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc)) == nsnull) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create recipient info\n")); goto loser; } if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't add recipient info\n")); goto loser; } } return NS_OK; loser: if (m_cmsMsg) { NSS_CMSMessage_Destroy(m_cmsMsg); m_cmsMsg = nsnull; } return rv; }