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