Index: mozilla/security/nss/lib/pkcs7/p7encode.c |
=================================================================== |
--- mozilla/security/nss/lib/pkcs7/p7encode.c (revision 191424) |
+++ mozilla/security/nss/lib/pkcs7/p7encode.c (working copy) |
@@ -1,1112 +0,0 @@ |
-/* This Source Code Form is subject to the terms of the Mozilla Public |
- * License, v. 2.0. If a copy of the MPL was not distributed with this |
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
- |
-/* |
- * PKCS7 encoding. |
- * |
- * $Id: p7encode.c,v 1.15 2012/04/25 14:50:06 gerv%gerv.net Exp $ |
- */ |
- |
-#include "p7local.h" |
- |
-#include "cert.h" |
-#include "cryptohi.h" |
-#include "keyhi.h" |
-#include "secasn1.h" |
-#include "secoid.h" |
-#include "secitem.h" |
-#include "pk11func.h" |
-#include "secerr.h" |
-#include "sechash.h" /* for HASH_GetHashObject() */ |
- |
-struct sec_pkcs7_encoder_output { |
- SEC_PKCS7EncoderOutputCallback outputfn; |
- void *outputarg; |
-}; |
- |
-struct SEC_PKCS7EncoderContextStr { |
- SEC_ASN1EncoderContext *ecx; |
- SEC_PKCS7ContentInfo *cinfo; |
- struct sec_pkcs7_encoder_output output; |
- sec_PKCS7CipherObject *encryptobj; |
- const SECHashObject *digestobj; |
- void *digestcx; |
-}; |
- |
- |
-/* |
- * The little output function that the ASN.1 encoder calls to hand |
- * us bytes which we in turn hand back to our caller (via the callback |
- * they gave us). |
- */ |
-static void |
-sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len, |
- int depth, SEC_ASN1EncodingPart data_kind) |
-{ |
- struct sec_pkcs7_encoder_output *output; |
- |
- output = (struct sec_pkcs7_encoder_output*)arg; |
- output->outputfn (output->outputarg, buf, len); |
-} |
- |
-static sec_PKCS7CipherObject * |
-sec_pkcs7_encoder_start_encrypt (SEC_PKCS7ContentInfo *cinfo, |
- PK11SymKey *orig_bulkkey) |
-{ |
- SECOidTag kind; |
- sec_PKCS7CipherObject *encryptobj; |
- SEC_PKCS7RecipientInfo **recipientinfos, *ri; |
- SEC_PKCS7EncryptedContentInfo *enccinfo; |
- SECKEYPublicKey *publickey = NULL; |
- SECKEYPrivateKey *ourPrivKey = NULL; |
- PK11SymKey *bulkkey; |
- void *mark, *wincx; |
- int i; |
- PRArenaPool *arena = NULL; |
- |
- /* Get the context in case we need it below. */ |
- wincx = cinfo->pwfn_arg; |
- |
- kind = SEC_PKCS7ContentType (cinfo); |
- switch (kind) { |
- default: |
- case SEC_OID_PKCS7_DATA: |
- case SEC_OID_PKCS7_DIGESTED_DATA: |
- case SEC_OID_PKCS7_SIGNED_DATA: |
- recipientinfos = NULL; |
- enccinfo = NULL; |
- break; |
- case SEC_OID_PKCS7_ENCRYPTED_DATA: |
- { |
- SEC_PKCS7EncryptedData *encdp; |
- |
- /* To do EncryptedData we *must* be given a bulk key. */ |
- PORT_Assert (orig_bulkkey != NULL); |
- if (orig_bulkkey == NULL) { |
- /* XXX error? */ |
- return NULL; |
- } |
- |
- encdp = cinfo->content.encryptedData; |
- recipientinfos = NULL; |
- enccinfo = &(encdp->encContentInfo); |
- } |
- break; |
- case SEC_OID_PKCS7_ENVELOPED_DATA: |
- { |
- SEC_PKCS7EnvelopedData *envdp; |
- |
- envdp = cinfo->content.envelopedData; |
- recipientinfos = envdp->recipientInfos; |
- enccinfo = &(envdp->encContentInfo); |
- } |
- break; |
- case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
- { |
- SEC_PKCS7SignedAndEnvelopedData *saedp; |
- |
- saedp = cinfo->content.signedAndEnvelopedData; |
- recipientinfos = saedp->recipientInfos; |
- enccinfo = &(saedp->encContentInfo); |
- } |
- break; |
- } |
- |
- if (enccinfo == NULL) |
- return NULL; |
- |
- bulkkey = orig_bulkkey; |
- if (bulkkey == NULL) { |
- CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg); |
- PK11SlotInfo *slot; |
- |
- |
- slot = PK11_GetBestSlot(type,cinfo->pwfn_arg); |
- if (slot == NULL) { |
- return NULL; |
- } |
- bulkkey = PK11_KeyGen(slot,type,NULL, enccinfo->keysize/8, |
- cinfo->pwfn_arg); |
- PK11_FreeSlot(slot); |
- if (bulkkey == NULL) { |
- return NULL; |
- } |
- } |
- |
- encryptobj = NULL; |
- mark = PORT_ArenaMark (cinfo->poolp); |
- |
- /* |
- * Encrypt the bulk key with the public key of each recipient. |
- */ |
- for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) { |
- CERTCertificate *cert; |
- SECOidTag certalgtag, encalgtag; |
- SECStatus rv; |
- int data_len; |
- SECItem *params = NULL; |
- |
- cert = ri->cert; |
- PORT_Assert (cert != NULL); |
- if (cert == NULL) |
- continue; |
- |
- /* |
- * XXX Want an interface that takes a cert and some data and |
- * fills in an algorithmID and encrypts the data with the public |
- * key from the cert. Or, give me two interfaces -- one which |
- * gets the algorithm tag from a cert (I should not have to go |
- * down into the subjectPublicKeyInfo myself) and another which |
- * takes a public key and algorithm tag and data and encrypts |
- * the data. Or something like that. The point is that all |
- * of the following hardwired RSA stuff should be done elsewhere. |
- */ |
- |
- certalgtag=SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); |
- |
- switch (certalgtag) { |
- case SEC_OID_PKCS1_RSA_ENCRYPTION: |
- encalgtag = certalgtag; |
- publickey = CERT_ExtractPublicKey (cert); |
- if (publickey == NULL) goto loser; |
- |
- data_len = SECKEY_PublicKeyStrength(publickey); |
- ri->encKey.data = |
- (unsigned char*)PORT_ArenaAlloc(cinfo->poolp ,data_len); |
- ri->encKey.len = data_len; |
- if (ri->encKey.data == NULL) goto loser; |
- |
- rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag),publickey, |
- bulkkey,&ri->encKey); |
- |
- SECKEY_DestroyPublicKey(publickey); |
- publickey = NULL; |
- if (rv != SECSuccess) goto loser; |
- params = NULL; /* paranoia */ |
- break; |
- default: |
- PORT_SetError (SEC_ERROR_INVALID_ALGORITHM); |
- goto loser; |
- } |
- |
- rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag, |
- params); |
- if (rv != SECSuccess) |
- goto loser; |
- if (arena) PORT_FreeArena(arena,PR_FALSE); |
- arena = NULL; |
- } |
- |
- encryptobj = sec_PKCS7CreateEncryptObject (cinfo->poolp, bulkkey, |
- enccinfo->encalg, |
- &(enccinfo->contentEncAlg)); |
- if (encryptobj != NULL) { |
- PORT_ArenaUnmark (cinfo->poolp, mark); |
- mark = NULL; /* good one; do not want to release */ |
- } |
- /* fallthru */ |
- |
-loser: |
- if (arena) { |
- PORT_FreeArena(arena, PR_FALSE); |
- } |
- if (publickey) { |
- SECKEY_DestroyPublicKey(publickey); |
- } |
- if (ourPrivKey) { |
- SECKEY_DestroyPrivateKey(ourPrivKey); |
- } |
- if (mark != NULL) { |
- PORT_ArenaRelease (cinfo->poolp, mark); |
- } |
- if (orig_bulkkey == NULL) { |
- if (bulkkey) PK11_FreeSymKey(bulkkey); |
- } |
- |
- return encryptobj; |
-} |
- |
- |
-static void |
-sec_pkcs7_encoder_notify (void *arg, PRBool before, void *dest, int depth) |
-{ |
- SEC_PKCS7EncoderContext *p7ecx; |
- SEC_PKCS7ContentInfo *cinfo; |
- SECOidTag kind; |
- PRBool before_content; |
- |
- /* |
- * We want to notice just before the content field. After fields are |
- * not interesting to us. |
- */ |
- if (!before) |
- return; |
- |
- p7ecx = (SEC_PKCS7EncoderContext*)arg; |
- cinfo = p7ecx->cinfo; |
- |
- before_content = PR_FALSE; |
- |
- /* |
- * Watch for the content field, at which point we want to instruct |
- * the ASN.1 encoder to start taking bytes from the buffer. |
- * |
- * XXX The following assumes the inner content type is data; |
- * if/when we want to handle fully nested types, this will have |
- * to recurse until reaching the innermost data content. |
- */ |
- kind = SEC_PKCS7ContentType (cinfo); |
- switch (kind) { |
- default: |
- case SEC_OID_PKCS7_DATA: |
- if (dest == &(cinfo->content.data)) |
- before_content = PR_TRUE; |
- break; |
- |
- case SEC_OID_PKCS7_DIGESTED_DATA: |
- { |
- SEC_PKCS7DigestedData *digd; |
- |
- digd = cinfo->content.digestedData; |
- if (digd == NULL) |
- break; |
- |
- if (dest == &(digd->contentInfo.content)) |
- before_content = PR_TRUE; |
- } |
- break; |
- |
- case SEC_OID_PKCS7_ENCRYPTED_DATA: |
- { |
- SEC_PKCS7EncryptedData *encd; |
- |
- encd = cinfo->content.encryptedData; |
- if (encd == NULL) |
- break; |
- |
- if (dest == &(encd->encContentInfo.encContent)) |
- before_content = PR_TRUE; |
- } |
- break; |
- |
- case SEC_OID_PKCS7_ENVELOPED_DATA: |
- { |
- SEC_PKCS7EnvelopedData *envd; |
- |
- envd = cinfo->content.envelopedData; |
- if (envd == NULL) |
- break; |
- |
- if (dest == &(envd->encContentInfo.encContent)) |
- before_content = PR_TRUE; |
- } |
- break; |
- |
- case SEC_OID_PKCS7_SIGNED_DATA: |
- { |
- SEC_PKCS7SignedData *sigd; |
- |
- sigd = cinfo->content.signedData; |
- if (sigd == NULL) |
- break; |
- |
- if (dest == &(sigd->contentInfo.content)) |
- before_content = PR_TRUE; |
- } |
- break; |
- |
- case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
- { |
- SEC_PKCS7SignedAndEnvelopedData *saed; |
- |
- saed = cinfo->content.signedAndEnvelopedData; |
- if (saed == NULL) |
- break; |
- |
- if (dest == &(saed->encContentInfo.encContent)) |
- before_content = PR_TRUE; |
- } |
- break; |
- } |
- |
- if (before_content) { |
- /* |
- * This will cause the next SEC_ASN1EncoderUpdate to take the |
- * contents bytes from the passed-in buffer. |
- */ |
- SEC_ASN1EncoderSetTakeFromBuf (p7ecx->ecx); |
- /* |
- * And that is all we needed this notify function for. |
- */ |
- SEC_ASN1EncoderClearNotifyProc (p7ecx->ecx); |
- } |
-} |
- |
- |
-static SEC_PKCS7EncoderContext * |
-sec_pkcs7_encoder_start_contexts (SEC_PKCS7ContentInfo *cinfo, |
- PK11SymKey *bulkkey) |
-{ |
- SEC_PKCS7EncoderContext *p7ecx; |
- SECOidTag kind; |
- PRBool encrypt; |
- SECItem **digests; |
- SECAlgorithmID *digestalg, **digestalgs; |
- |
- p7ecx = |
- (SEC_PKCS7EncoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7EncoderContext)); |
- if (p7ecx == NULL) |
- return NULL; |
- |
- digests = NULL; |
- digestalg = NULL; |
- digestalgs = NULL; |
- encrypt = PR_FALSE; |
- |
- kind = SEC_PKCS7ContentType (cinfo); |
- switch (kind) { |
- default: |
- case SEC_OID_PKCS7_DATA: |
- break; |
- case SEC_OID_PKCS7_DIGESTED_DATA: |
- digestalg = &(cinfo->content.digestedData->digestAlg); |
- break; |
- case SEC_OID_PKCS7_SIGNED_DATA: |
- digests = cinfo->content.signedData->digests; |
- digestalgs = cinfo->content.signedData->digestAlgorithms; |
- break; |
- case SEC_OID_PKCS7_ENCRYPTED_DATA: |
- case SEC_OID_PKCS7_ENVELOPED_DATA: |
- encrypt = PR_TRUE; |
- break; |
- case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
- digests = cinfo->content.signedAndEnvelopedData->digests; |
- digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms; |
- encrypt = PR_TRUE; |
- break; |
- } |
- |
- if (encrypt) { |
- p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt (cinfo, bulkkey); |
- if (p7ecx->encryptobj == NULL) { |
- PORT_Free (p7ecx); |
- return NULL; |
- } |
- } |
- |
- if (digestalgs != NULL) { |
- if (digests != NULL) { |
- /* digests already created (probably for detached data) */ |
- digestalg = NULL; |
- } else { |
- /* |
- * XXX Some day we should handle multiple digests; for now, |
- * assume only one will be done. |
- */ |
- PORT_Assert (digestalgs[0] != NULL && digestalgs[1] == NULL); |
- digestalg = digestalgs[0]; |
- } |
- } |
- |
- if (digestalg != NULL) { |
- SECOidTag oidTag = SECOID_FindOIDTag(&(digestalg->algorithm)); |
- |
- p7ecx->digestobj = HASH_GetHashObjectByOidTag(oidTag); |
- if (p7ecx->digestobj != NULL) { |
- p7ecx->digestcx = (* p7ecx->digestobj->create) (); |
- if (p7ecx->digestcx == NULL) |
- p7ecx->digestobj = NULL; |
- else |
- (* p7ecx->digestobj->begin) (p7ecx->digestcx); |
- } |
- if (p7ecx->digestobj == NULL) { |
- if (p7ecx->encryptobj != NULL) |
- sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj); |
- PORT_Free (p7ecx); |
- return NULL; |
- } |
- } |
- |
- p7ecx->cinfo = cinfo; |
- return p7ecx; |
-} |
- |
- |
-SEC_PKCS7EncoderContext * |
-SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo, |
- SEC_PKCS7EncoderOutputCallback outputfn, |
- void *outputarg, |
- PK11SymKey *bulkkey) |
-{ |
- SEC_PKCS7EncoderContext *p7ecx; |
- SECStatus rv; |
- |
- p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); |
- if (p7ecx == NULL) |
- return NULL; |
- |
- p7ecx->output.outputfn = outputfn; |
- p7ecx->output.outputarg = outputarg; |
- |
- /* |
- * Initialize the BER encoder. |
- */ |
- p7ecx->ecx = SEC_ASN1EncoderStart (cinfo, sec_PKCS7ContentInfoTemplate, |
- sec_pkcs7_encoder_out, &(p7ecx->output)); |
- if (p7ecx->ecx == NULL) { |
- PORT_Free (p7ecx); |
- return NULL; |
- } |
- |
- /* |
- * Indicate that we are streaming. We will be streaming until we |
- * get past the contents bytes. |
- */ |
- SEC_ASN1EncoderSetStreaming (p7ecx->ecx); |
- |
- /* |
- * The notify function will watch for the contents field. |
- */ |
- SEC_ASN1EncoderSetNotifyProc (p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx); |
- |
- /* |
- * This will encode everything up to the content bytes. (The notify |
- * function will then cause the encoding to stop there.) Then our |
- * caller can start passing contents bytes to our Update, which we |
- * will pass along. |
- */ |
- rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0); |
- if (rv != SECSuccess) { |
- PORT_Free (p7ecx); |
- return NULL; |
- } |
- |
- return p7ecx; |
-} |
- |
- |
-/* |
- * XXX If/when we support nested contents, this needs to be revised. |
- */ |
-static SECStatus |
-sec_pkcs7_encoder_work_data (SEC_PKCS7EncoderContext *p7ecx, SECItem *dest, |
- const unsigned char *data, unsigned long len, |
- PRBool final) |
-{ |
- unsigned char *buf = NULL; |
- SECStatus rv; |
- |
- |
- rv = SECSuccess; /* may as well be optimistic */ |
- |
- /* |
- * We should really have data to process, or we should be trying |
- * to finish/flush the last block. (This is an overly paranoid |
- * check since all callers are in this file and simple inspection |
- * proves they do it right. But it could find a bug in future |
- * modifications/development, that is why it is here.) |
- */ |
- PORT_Assert ((data != NULL && len) || final); |
- |
- /* |
- * Update the running digest. |
- * XXX This needs modification if/when we handle multiple digests. |
- */ |
- if (len && p7ecx->digestobj != NULL) { |
- (* p7ecx->digestobj->update) (p7ecx->digestcx, data, len); |
- } |
- |
- /* |
- * Encrypt this chunk. |
- */ |
- if (p7ecx->encryptobj != NULL) { |
- /* XXX the following lengths should all be longs? */ |
- unsigned int inlen; /* length of data being encrypted */ |
- unsigned int outlen; /* length of encrypted data */ |
- unsigned int buflen; /* length available for encrypted data */ |
- |
- inlen = len; |
- buflen = sec_PKCS7EncryptLength (p7ecx->encryptobj, inlen, final); |
- if (buflen == 0) { |
- /* |
- * No output is expected, but the input data may be buffered |
- * so we still have to call Encrypt. |
- */ |
- rv = sec_PKCS7Encrypt (p7ecx->encryptobj, NULL, NULL, 0, |
- data, inlen, final); |
- if (final) { |
- len = 0; |
- goto done; |
- } |
- return rv; |
- } |
- |
- if (dest != NULL) |
- buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen); |
- else |
- buf = (unsigned char*)PORT_Alloc (buflen); |
- |
- if (buf == NULL) { |
- rv = SECFailure; |
- } else { |
- rv = sec_PKCS7Encrypt (p7ecx->encryptobj, buf, &outlen, buflen, |
- data, inlen, final); |
- data = buf; |
- len = outlen; |
- } |
- if (rv != SECSuccess) { |
- if (final) |
- goto done; |
- return rv; |
- } |
- } |
- |
- if (p7ecx->ecx != NULL) { |
- /* |
- * Encode the contents bytes. |
- */ |
- if(len) { |
- rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, (const char *)data, len); |
- } |
- } |
- |
-done: |
- if (p7ecx->encryptobj != NULL) { |
- if (final) |
- sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj); |
- if (dest != NULL) { |
- dest->data = buf; |
- dest->len = len; |
- } else if (buf != NULL) { |
- PORT_Free (buf); |
- } |
- } |
- |
- if (final && p7ecx->digestobj != NULL) { |
- SECItem *digest, **digests, ***digestsp; |
- unsigned char *digdata; |
- SECOidTag kind; |
- |
- kind = SEC_PKCS7ContentType (p7ecx->cinfo); |
- switch (kind) { |
- default: |
- PORT_Assert (0); |
- return SECFailure; |
- case SEC_OID_PKCS7_DIGESTED_DATA: |
- digest = &(p7ecx->cinfo->content.digestedData->digest); |
- digestsp = NULL; |
- break; |
- case SEC_OID_PKCS7_SIGNED_DATA: |
- digest = NULL; |
- digestsp = &(p7ecx->cinfo->content.signedData->digests); |
- break; |
- case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
- digest = NULL; |
- digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests); |
- break; |
- } |
- |
- digdata = (unsigned char*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, |
- p7ecx->digestobj->length); |
- if (digdata == NULL) |
- return SECFailure; |
- |
- if (digestsp != NULL) { |
- PORT_Assert (digest == NULL); |
- |
- digest = (SECItem*)PORT_ArenaAlloc (p7ecx->cinfo->poolp, |
- sizeof(SECItem)); |
- digests = (SECItem**)PORT_ArenaAlloc (p7ecx->cinfo->poolp, |
- 2 * sizeof(SECItem *)); |
- if (digests == NULL || digest == NULL) |
- return SECFailure; |
- |
- digests[0] = digest; |
- digests[1] = NULL; |
- |
- *digestsp = digests; |
- } |
- |
- PORT_Assert (digest != NULL); |
- |
- digest->data = digdata; |
- digest->len = p7ecx->digestobj->length; |
- |
- (* p7ecx->digestobj->end) (p7ecx->digestcx, digest->data, |
- &(digest->len), digest->len); |
- (* p7ecx->digestobj->destroy) (p7ecx->digestcx, PR_TRUE); |
- } |
- |
- return rv; |
-} |
- |
- |
-SECStatus |
-SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx, |
- const char *data, unsigned long len) |
-{ |
- /* XXX Error handling needs help. Return what? Do "Finish" on failure? */ |
- return sec_pkcs7_encoder_work_data (p7ecx, NULL, |
- (const unsigned char *)data, len, |
- PR_FALSE); |
-} |
- |
-static SECStatus |
-sec_pkcs7_encoder_sig_and_certs (SEC_PKCS7ContentInfo *cinfo, |
- SECKEYGetPasswordKey pwfn, void *pwfnarg) |
-{ |
- SECOidTag kind; |
- CERTCertificate **certs; |
- CERTCertificateList **certlists; |
- SECAlgorithmID **digestalgs; |
- SECItem **digests; |
- SEC_PKCS7SignerInfo *signerinfo, **signerinfos; |
- SECItem **rawcerts, ***rawcertsp; |
- PRArenaPool *poolp; |
- int certcount; |
- int ci, cli, rci, si; |
- |
- kind = SEC_PKCS7ContentType (cinfo); |
- switch (kind) { |
- default: |
- case SEC_OID_PKCS7_DATA: |
- case SEC_OID_PKCS7_DIGESTED_DATA: |
- case SEC_OID_PKCS7_ENCRYPTED_DATA: |
- case SEC_OID_PKCS7_ENVELOPED_DATA: |
- certs = NULL; |
- certlists = NULL; |
- digestalgs = NULL; |
- digests = NULL; |
- signerinfos = NULL; |
- rawcertsp = NULL; |
- break; |
- case SEC_OID_PKCS7_SIGNED_DATA: |
- { |
- SEC_PKCS7SignedData *sdp; |
- |
- sdp = cinfo->content.signedData; |
- certs = sdp->certs; |
- certlists = sdp->certLists; |
- digestalgs = sdp->digestAlgorithms; |
- digests = sdp->digests; |
- signerinfos = sdp->signerInfos; |
- rawcertsp = &(sdp->rawCerts); |
- } |
- break; |
- case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
- { |
- SEC_PKCS7SignedAndEnvelopedData *saedp; |
- |
- saedp = cinfo->content.signedAndEnvelopedData; |
- certs = saedp->certs; |
- certlists = saedp->certLists; |
- digestalgs = saedp->digestAlgorithms; |
- digests = saedp->digests; |
- signerinfos = saedp->signerInfos; |
- rawcertsp = &(saedp->rawCerts); |
- } |
- break; |
- } |
- |
- if (certs == NULL && certlists == NULL && signerinfos == NULL) |
- return SECSuccess; /* nothing for us to do! */ |
- |
- poolp = cinfo->poolp; |
- certcount = 0; |
- |
- if (signerinfos != NULL) { |
- SECOidTag digestalgtag; |
- int di; |
- SECStatus rv; |
- CERTCertificate *cert; |
- SECKEYPrivateKey *privkey; |
- SECItem signature; |
- SECOidTag signalgtag; |
- |
- PORT_Assert (digestalgs != NULL && digests != NULL); |
- |
- /* |
- * If one fails, we bail right then. If we want to continue and |
- * try to do subsequent signatures, this loop, and the departures |
- * from it, will need to be reworked. |
- */ |
- for (si = 0; signerinfos[si] != NULL; si++) { |
- |
- signerinfo = signerinfos[si]; |
- |
- /* find right digest */ |
- digestalgtag = SECOID_GetAlgorithmTag (&(signerinfo->digestAlg)); |
- for (di = 0; digestalgs[di] != NULL; di++) { |
- /* XXX Should I be comparing more than the tag? */ |
- if (digestalgtag == SECOID_GetAlgorithmTag (digestalgs[di])) |
- break; |
- } |
- if (digestalgs[di] == NULL) { |
- /* XXX oops; do what? set an error? */ |
- return SECFailure; |
- } |
- PORT_Assert (digests[di] != NULL); |
- |
- cert = signerinfo->cert; |
- privkey = PK11_FindKeyByAnyCert (cert, pwfnarg); |
- if (privkey == NULL) |
- return SECFailure; |
- |
- /* |
- * XXX I think there should be a cert-level interface for this, |
- * so that I do not have to know about subjectPublicKeyInfo... |
- */ |
- signalgtag = SECOID_GetAlgorithmTag (&(cert->subjectPublicKeyInfo.algorithm)); |
- |
- if (signerinfo->authAttr != NULL) { |
- SEC_PKCS7Attribute *attr; |
- SECItem encoded_attrs; |
- SECItem *dummy; |
- SECOidTag algid; |
- |
- /* |
- * First, find and fill in the message digest attribute. |
- */ |
- attr = sec_PKCS7FindAttribute (signerinfo->authAttr, |
- SEC_OID_PKCS9_MESSAGE_DIGEST, |
- PR_TRUE); |
- PORT_Assert (attr != NULL); |
- if (attr == NULL) { |
- SECKEY_DestroyPrivateKey (privkey); |
- return SECFailure; |
- } |
- |
- /* |
- * XXX The second half of the following assertion prevents |
- * the encoder from being called twice on the same content. |
- * Either just remove the second half the assertion, or |
- * change the code to check if the value already there is |
- * the same as digests[di], whichever seems more right. |
- */ |
- PORT_Assert (attr->values != NULL && attr->values[0] == NULL); |
- attr->values[0] = digests[di]; |
- |
- /* |
- * Before encoding, reorder the attributes so that when they |
- * are encoded, they will be conforming DER, which is required |
- * to have a specific order and that is what must be used for |
- * the hash/signature. We do this here, rather than building |
- * it into EncodeAttributes, because we do not want to do |
- * such reordering on incoming messages (which also uses |
- * EncodeAttributes) or our old signatures (and other "broken" |
- * implementations) will not verify. So, we want to guarantee |
- * that we send out good DER encodings of attributes, but not |
- * to expect to receive them. |
- */ |
- rv = sec_PKCS7ReorderAttributes (signerinfo->authAttr); |
- if (rv != SECSuccess) { |
- SECKEY_DestroyPrivateKey (privkey); |
- return SECFailure; |
- } |
- |
- encoded_attrs.data = NULL; |
- encoded_attrs.len = 0; |
- dummy = sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, |
- &(signerinfo->authAttr)); |
- if (dummy == NULL) { |
- SECKEY_DestroyPrivateKey (privkey); |
- return SECFailure; |
- } |
- |
- algid = SEC_GetSignatureAlgorithmOidTag(privkey->keyType, |
- digestalgtag); |
- if (algid == SEC_OID_UNKNOWN) { |
- PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); |
- SECKEY_DestroyPrivateKey (privkey); |
- return SECFailure; |
- } |
- rv = SEC_SignData (&signature, |
- encoded_attrs.data, encoded_attrs.len, |
- privkey, |
- algid); |
- SECITEM_FreeItem (&encoded_attrs, PR_FALSE); |
- } else { |
- rv = SGN_Digest (privkey, digestalgtag, &signature, |
- digests[di]); |
- } |
- |
- SECKEY_DestroyPrivateKey (privkey); |
- |
- if (rv != SECSuccess) |
- return rv; |
- |
- rv = SECITEM_CopyItem (poolp, &(signerinfo->encDigest), &signature); |
- if (rv != SECSuccess) |
- return rv; |
- |
- SECITEM_FreeItem (&signature, PR_FALSE); |
- |
- rv = SECOID_SetAlgorithmID (poolp, &(signerinfo->digestEncAlg), |
- signalgtag, NULL); |
- if (rv != SECSuccess) |
- return SECFailure; |
- |
- /* |
- * Count the cert chain for this signer. |
- */ |
- if (signerinfo->certList != NULL) |
- certcount += signerinfo->certList->len; |
- } |
- } |
- |
- if (certs != NULL) { |
- for (ci = 0; certs[ci] != NULL; ci++) |
- certcount++; |
- } |
- |
- if (certlists != NULL) { |
- for (cli = 0; certlists[cli] != NULL; cli++) |
- certcount += certlists[cli]->len; |
- } |
- |
- if (certcount == 0) |
- return SECSuccess; /* signing done; no certs */ |
- |
- /* |
- * Combine all of the certs and cert chains into rawcerts. |
- * Note: certcount is an upper bound; we may not need that many slots |
- * but we will allocate anyway to avoid having to do another pass. |
- * (The temporary space saving is not worth it.) |
- */ |
- rawcerts = (SECItem**)PORT_ArenaAlloc (poolp, |
- (certcount + 1) * sizeof(SECItem *)); |
- if (rawcerts == NULL) |
- return SECFailure; |
- |
- /* |
- * XXX Want to check for duplicates and not add *any* cert that is |
- * already in the set. This will be more important when we start |
- * dealing with larger sets of certs, dual-key certs (signing and |
- * encryption), etc. For the time being we can slide by... |
- */ |
- rci = 0; |
- if (signerinfos != NULL) { |
- for (si = 0; signerinfos[si] != NULL; si++) { |
- signerinfo = signerinfos[si]; |
- for (ci = 0; ci < signerinfo->certList->len; ci++) |
- rawcerts[rci++] = &(signerinfo->certList->certs[ci]); |
- } |
- |
- } |
- |
- if (certs != NULL) { |
- for (ci = 0; certs[ci] != NULL; ci++) |
- rawcerts[rci++] = &(certs[ci]->derCert); |
- } |
- |
- if (certlists != NULL) { |
- for (cli = 0; certlists[cli] != NULL; cli++) { |
- for (ci = 0; ci < certlists[cli]->len; ci++) |
- rawcerts[rci++] = &(certlists[cli]->certs[ci]); |
- } |
- } |
- |
- rawcerts[rci] = NULL; |
- *rawcertsp = rawcerts; |
- |
- return SECSuccess; |
-} |
- |
- |
-SECStatus |
-SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx, |
- SECKEYGetPasswordKey pwfn, void *pwfnarg) |
-{ |
- SECStatus rv; |
- |
- /* |
- * Flush out any remaining data. |
- */ |
- rv = sec_pkcs7_encoder_work_data (p7ecx, NULL, NULL, 0, PR_TRUE); |
- |
- /* |
- * Turn off streaming stuff. |
- */ |
- SEC_ASN1EncoderClearTakeFromBuf (p7ecx->ecx); |
- SEC_ASN1EncoderClearStreaming (p7ecx->ecx); |
- |
- if (rv != SECSuccess) |
- goto loser; |
- |
- rv = sec_pkcs7_encoder_sig_and_certs (p7ecx->cinfo, pwfn, pwfnarg); |
- if (rv != SECSuccess) |
- goto loser; |
- |
- rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0); |
- |
-loser: |
- SEC_ASN1EncoderFinish (p7ecx->ecx); |
- PORT_Free (p7ecx); |
- return rv; |
-} |
- |
-/* |
- * Abort the ASN.1 stream. Used by pkcs 12 |
- */ |
-void |
-SEC_PKCS7EncoderAbort(SEC_PKCS7EncoderContext *p7ecx, int error) |
-{ |
- PORT_Assert(p7ecx); |
- SEC_ASN1EncoderAbort(p7ecx->ecx, error); |
-} |
- |
-/* |
- * After this routine is called, the entire PKCS7 contentInfo is ready |
- * to be encoded. This is used internally, but can also be called from |
- * elsewhere for those who want to be able to just have pointers to |
- * the ASN1 template for pkcs7 contentInfo built into their own encodings. |
- */ |
-SECStatus |
-SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo, |
- PK11SymKey *bulkkey, |
- SECKEYGetPasswordKey pwfn, |
- void *pwfnarg) |
-{ |
- SEC_PKCS7EncoderContext *p7ecx; |
- SECItem *content, *enc_content; |
- SECStatus rv; |
- |
- p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey); |
- if (p7ecx == NULL) |
- return SECFailure; |
- |
- content = SEC_PKCS7GetContent (cinfo); |
- |
- if (p7ecx->encryptobj != NULL) { |
- SECOidTag kind; |
- SEC_PKCS7EncryptedContentInfo *enccinfo; |
- |
- kind = SEC_PKCS7ContentType (p7ecx->cinfo); |
- switch (kind) { |
- default: |
- PORT_Assert (0); |
- rv = SECFailure; |
- goto loser; |
- case SEC_OID_PKCS7_ENCRYPTED_DATA: |
- enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo); |
- break; |
- case SEC_OID_PKCS7_ENVELOPED_DATA: |
- enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo); |
- break; |
- case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: |
- enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo); |
- break; |
- } |
- enc_content = &(enccinfo->encContent); |
- } else { |
- enc_content = NULL; |
- } |
- |
- if (content != NULL && content->data != NULL && content->len) { |
- rv = sec_pkcs7_encoder_work_data (p7ecx, enc_content, |
- content->data, content->len, PR_TRUE); |
- if (rv != SECSuccess) |
- goto loser; |
- } |
- |
- rv = sec_pkcs7_encoder_sig_and_certs (cinfo, pwfn, pwfnarg); |
- |
-loser: |
- PORT_Free (p7ecx); |
- return rv; |
-} |
- |
- |
-/* |
- * Encode a PKCS7 object, in one shot. All necessary components |
- * of the object must already be specified. Either the data has |
- * already been included (via SetContent), or the data is detached, |
- * or there is no data at all (certs-only). |
- * |
- * "cinfo" specifies the object to be encoded. |
- * |
- * "outputfn" is where the encoded bytes will be passed. |
- * |
- * "outputarg" is an opaque argument to the above callback. |
- * |
- * "bulkkey" specifies the bulk encryption key to use. This argument |
- * can be NULL if no encryption is being done, or if the bulk key should |
- * be generated internally (usually the case for EnvelopedData but never |
- * for EncryptedData, which *must* provide a bulk encryption key). |
- * |
- * "pwfn" is a callback for getting the password which protects the |
- * private key of the signer. This argument can be NULL if it is known |
- * that no signing is going to be done. |
- * |
- * "pwfnarg" is an opaque argument to the above callback. |
- */ |
-SECStatus |
-SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo, |
- SEC_PKCS7EncoderOutputCallback outputfn, |
- void *outputarg, |
- PK11SymKey *bulkkey, |
- SECKEYGetPasswordKey pwfn, |
- void *pwfnarg) |
-{ |
- SECStatus rv; |
- |
- rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); |
- if (rv == SECSuccess) { |
- struct sec_pkcs7_encoder_output outputcx; |
- |
- outputcx.outputfn = outputfn; |
- outputcx.outputarg = outputarg; |
- |
- rv = SEC_ASN1Encode (cinfo, sec_PKCS7ContentInfoTemplate, |
- sec_pkcs7_encoder_out, &outputcx); |
- } |
- |
- return rv; |
-} |
- |
- |
-/* |
- * Encode a PKCS7 object, in one shot. All necessary components |
- * of the object must already be specified. Either the data has |
- * already been included (via SetContent), or the data is detached, |
- * or there is no data at all (certs-only). The output, rather than |
- * being passed to an output function as is done above, is all put |
- * into a SECItem. |
- * |
- * "pool" specifies a pool from which to allocate the result. |
- * It can be NULL, in which case memory is allocated generically. |
- * |
- * "dest" specifies a SECItem in which to put the result data. |
- * It can be NULL, in which case the entire item is allocated, too. |
- * |
- * "cinfo" specifies the object to be encoded. |
- * |
- * "bulkkey" specifies the bulk encryption key to use. This argument |
- * can be NULL if no encryption is being done, or if the bulk key should |
- * be generated internally (usually the case for EnvelopedData but never |
- * for EncryptedData, which *must* provide a bulk encryption key). |
- * |
- * "pwfn" is a callback for getting the password which protects the |
- * private key of the signer. This argument can be NULL if it is known |
- * that no signing is going to be done. |
- * |
- * "pwfnarg" is an opaque argument to the above callback. |
- */ |
-SECItem * |
-SEC_PKCS7EncodeItem (PRArenaPool *pool, |
- SECItem *dest, |
- SEC_PKCS7ContentInfo *cinfo, |
- PK11SymKey *bulkkey, |
- SECKEYGetPasswordKey pwfn, |
- void *pwfnarg) |
-{ |
- SECStatus rv; |
- |
- rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg); |
- if (rv != SECSuccess) |
- return NULL; |
- |
- return SEC_ASN1EncodeItem (pool, dest, cinfo, sec_PKCS7ContentInfoTemplate); |
-} |
- |