diff options
Diffstat (limited to 'security/nss/lib/pkcs7/p7decode.c')
-rw-r--r-- | security/nss/lib/pkcs7/p7decode.c | 2064 |
1 files changed, 0 insertions, 2064 deletions
diff --git a/security/nss/lib/pkcs7/p7decode.c b/security/nss/lib/pkcs7/p7decode.c deleted file mode 100644 index df3919975..000000000 --- a/security/nss/lib/pkcs7/p7decode.c +++ /dev/null @@ -1,2064 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape security libraries. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1994-2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * PKCS7 decoding, verification. - * - * $Id$ - */ - -#include "nssrenam.h" - -#include "p7local.h" - -#include "cert.h" - /* XXX do not want to have to include */ -#include "certdb.h" /* certdb.h -- the trust stuff needed by */ - /* the add certificate code needs to get */ - /* rewritten/abstracted and then this */ - /* include should be removed! */ -/*#include "cdbhdl.h" */ -#include "cryptohi.h" -#include "key.h" -#include "secasn1.h" -#include "secitem.h" -#include "secoid.h" -#include "pk11func.h" -#include "prtime.h" -#include "secerr.h" -#include "sechash.h" /* for HASH_GetHashObject() */ -#include "secder.h" -#include "secpkcs5.h" - -struct sec_pkcs7_decoder_worker { - int depth; - int digcnt; - void **digcxs; - const SECHashObject **digobjs; - sec_PKCS7CipherObject *decryptobj; - PRBool saw_contents; -}; - -struct SEC_PKCS7DecoderContextStr { - SEC_ASN1DecoderContext *dcx; - SEC_PKCS7ContentInfo *cinfo; - SEC_PKCS7DecoderContentCallback cb; - void *cb_arg; - SECKEYGetPasswordKey pwfn; - void *pwfn_arg; - struct sec_pkcs7_decoder_worker worker; - PRArenaPool *tmp_poolp; - int error; - SEC_PKCS7GetDecryptKeyCallback dkcb; - void *dkcb_arg; - SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb; -}; - -/* - * Handle one worker, decrypting and digesting the data as necessary. - * - * XXX If/when we support nested contents, this probably needs to be - * revised somewhat to get passed the content-info (which unfortunately - * can be two different types depending on whether it is encrypted or not) - * corresponding to the given worker. - */ -static void -sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx, - struct sec_pkcs7_decoder_worker *worker, - const unsigned char *data, unsigned long len, - PRBool final) -{ - unsigned char *buf = NULL; - SECStatus rv; - int i; - - /* - * 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); - - /* - * Decrypt this chunk. - * - * XXX If we get an error, we do not want to do the digest or callback, - * but we want to keep decoding. Or maybe we want to stop decoding - * altogether if there is a callback, because obviously we are not - * sending the data back and they want to know that. - */ - if (worker->decryptobj != NULL) { - /* XXX the following lengths should all be longs? */ - unsigned int inlen; /* length of data being decrypted */ - unsigned int outlen; /* length of decrypted data */ - unsigned int buflen; /* length available for decrypted data */ - SECItem *plain; - - inlen = len; - buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final); - if (buflen == 0) { - if (inlen == 0) /* no input and no output */ - return; - /* - * No output is expected, but the input data may be buffered - * so we still have to call Decrypt. - */ - rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0, - data, inlen, final); - if (rv != SECSuccess) { - p7dcx->error = PORT_GetError(); - return; /* XXX indicate error? */ - } - return; - } - - if (p7dcx->cb != NULL) { - buf = (unsigned char *) PORT_Alloc (buflen); - plain = NULL; - } else { - unsigned long oldlen; - - /* - * XXX This assumes one level of content only. - * See comment above about nested content types. - * XXX Also, it should work for signedAndEnvelopedData, too! - */ - plain = &(p7dcx->cinfo-> - content.envelopedData->encContentInfo.plainContent); - - oldlen = plain->len; - if (oldlen == 0) { - buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp, - buflen); - } else { - buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp, - plain->data, - oldlen, oldlen + buflen); - if (buf != NULL) - buf += oldlen; - } - plain->data = buf; - } - if (buf == NULL) { - p7dcx->error = SEC_ERROR_NO_MEMORY; - return; /* XXX indicate error? */ - } - rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen, - data, inlen, final); - if (rv != SECSuccess) { - p7dcx->error = PORT_GetError(); - return; /* XXX indicate error? */ - } - if (plain != NULL) { - PORT_Assert (final || outlen == buflen); - plain->len += outlen; - } - data = buf; - len = outlen; - } - - /* - * Update the running digests. - */ - if (len) { - for (i = 0; i < worker->digcnt; i++) { - (* worker->digobjs[i]->update) (worker->digcxs[i], data, len); - } - } - - /* - * Pass back the contents bytes, and free the temporary buffer. - */ - if (p7dcx->cb != NULL) { - if (len) - (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len); - if (worker->decryptobj != NULL) { - PORT_Assert (buf != NULL); - PORT_Free (buf); - } - } -} - -static void -sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len, - int depth, SEC_ASN1EncodingPart data_kind) -{ - SEC_PKCS7DecoderContext *p7dcx; - struct sec_pkcs7_decoder_worker *worker; - - /* - * Since we do not handle any nested contents, the only bytes we - * are really interested in are the actual contents bytes (not - * the identifier, length, or end-of-contents bytes). If we were - * handling nested types we would probably need to do something - * smarter based on depth and data_kind. - */ - if (data_kind != SEC_ASN1_Contents) - return; - - /* - * The ASN.1 decoder should not even call us with a length of 0. - * Just being paranoid. - */ - PORT_Assert (len); - if (len == 0) - return; - - p7dcx = (SEC_PKCS7DecoderContext*)arg; - - /* - * Handling nested contents would mean that there is a chain - * of workers -- one per each level of content. The following - * would start with the first worker and loop over them. - */ - worker = &(p7dcx->worker); - - worker->saw_contents = PR_TRUE; - - sec_pkcs7_decoder_work_data (p7dcx, worker, - (const unsigned char *) data, len, PR_FALSE); -} - - -/* - * Create digest contexts for each algorithm in "digestalgs". - * No algorithms is not an error, we just do not do anything. - * An error (like trouble allocating memory), marks the error - * in "p7dcx" and returns SECFailure, which means that our caller - * should just give up altogether. - */ -static SECStatus -sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth, - SECAlgorithmID **digestalgs) -{ - int i, digcnt; - - if (digestalgs == NULL) - return SECSuccess; - - /* - * Count the algorithms. - */ - digcnt = 0; - while (digestalgs[digcnt] != NULL) - digcnt++; - - /* - * No algorithms means no work to do. - * Just act as if there were no algorithms specified. - */ - if (digcnt == 0) - return SECSuccess; - - p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp, - digcnt * sizeof (void *)); - p7dcx->worker.digobjs = (const SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_poolp, - digcnt * sizeof (SECHashObject *)); - if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) { - p7dcx->error = SEC_ERROR_NO_MEMORY; - return SECFailure; - } - - p7dcx->worker.depth = depth; - p7dcx->worker.digcnt = 0; - - /* - * Create a digest context for each algorithm. - */ - for (i = 0; i < digcnt; i++) { - SECAlgorithmID * algid = digestalgs[i]; - SECOidTag oidTag = SECOID_FindOIDTag(&(algid->algorithm)); - const SECHashObject *digobj = HASH_GetHashObjectByOidTag(oidTag); - void *digcx; - - /* - * Skip any algorithm we do not even recognize; obviously, - * this could be a problem, but if it is critical then the - * result will just be that the signature does not verify. - * We do not necessarily want to error out here, because - * the particular algorithm may not actually be important, - * but we cannot know that until later. - */ - if (digobj == NULL) { - p7dcx->worker.digcnt--; - continue; - } - - digcx = (* digobj->create)(); - if (digcx != NULL) { - (* digobj->begin) (digcx); - p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj; - p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx; - p7dcx->worker.digcnt++; - } - } - - if (p7dcx->worker.digcnt != 0) - SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, - sec_pkcs7_decoder_filter, - p7dcx, - (PRBool)(p7dcx->cb != NULL)); - return SECSuccess; -} - - -/* - * Close out all of the digest contexts, storing the results in "digestsp". - */ -static SECStatus -sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx, - PRArenaPool *poolp, - SECItem ***digestsp) -{ - struct sec_pkcs7_decoder_worker *worker; - const SECHashObject *digobj; - void *digcx; - SECItem **digests, *digest; - int i; - void *mark; - - /* - * XXX Handling nested contents would mean that there is a chain - * of workers -- one per each level of content. The following - * would want to find the last worker in the chain. - */ - worker = &(p7dcx->worker); - - /* - * If no digests, then we have nothing to do. - */ - if (worker->digcnt == 0) - return SECSuccess; - - /* - * No matter what happens after this, we want to stop filtering. - * XXX If we handle nested contents, we only want to stop filtering - * if we are finishing off the *last* worker. - */ - SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); - - /* - * If we ended up with no contents, just destroy each - * digest context -- they are meaningless and potentially - * confusing, because their presence would imply some content - * was digested. - */ - if (! worker->saw_contents) { - for (i = 0; i < worker->digcnt; i++) { - digcx = worker->digcxs[i]; - digobj = worker->digobjs[i]; - (* digobj->destroy) (digcx, PR_TRUE); - } - return SECSuccess; - } - - mark = PORT_ArenaMark (poolp); - - /* - * Close out each digest context, saving digest away. - */ - digests = - (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *)); - digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem)); - if (digests == NULL || digest == NULL) { - p7dcx->error = PORT_GetError(); - PORT_ArenaRelease (poolp, mark); - return SECFailure; - } - - for (i = 0; i < worker->digcnt; i++, digest++) { - digcx = worker->digcxs[i]; - digobj = worker->digobjs[i]; - - digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length); - if (digest->data == NULL) { - p7dcx->error = PORT_GetError(); - PORT_ArenaRelease (poolp, mark); - return SECFailure; - } - - digest->len = digobj->length; - (* digobj->end) (digcx, digest->data, &(digest->len), digest->len); - (* digobj->destroy) (digcx, PR_TRUE); - - digests[i] = digest; - } - digests[i] = NULL; - *digestsp = digests; - - PORT_ArenaUnmark (poolp, mark); - return SECSuccess; -} - -/* - * XXX Need comment explaining following helper function (which is used - * by sec_pkcs7_decoder_start_decrypt). - */ -extern const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[]; - -static PK11SymKey * -sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx, - SEC_PKCS7RecipientInfo **recipientinfos, - SEC_PKCS7EncryptedContentInfo *enccinfo) -{ - SEC_PKCS7RecipientInfo *ri; - CERTCertificate *cert = NULL; - SECKEYPrivateKey *privkey = NULL; - PK11SymKey *bulkkey; - SECOidTag keyalgtag, bulkalgtag, encalgtag; - PK11SlotInfo *slot; - int bulkLength = 0; - - if (recipientinfos == NULL || recipientinfos[0] == NULL) { - p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; - goto no_key_found; - } - - cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri, - &privkey, p7dcx->pwfn_arg); - if (cert == NULL) { - p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT; - goto no_key_found; - } - - ri->cert = cert; /* so we can find it later */ - PORT_Assert(privkey != NULL); - - keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); - encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg)); - if ((encalgtag != SEC_OID_NETSCAPE_SMIME_KEA) && (keyalgtag != encalgtag)) { - p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH; - goto no_key_found; - } - bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg)); - - switch (encalgtag) { - case SEC_OID_PKCS1_RSA_ENCRYPTION: - bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey, - PK11_AlgtagToMechanism (bulkalgtag), - CKA_DECRYPT, 0); - if (bulkkey == NULL) { - p7dcx->error = PORT_GetError(); - PORT_SetError(0); - goto no_key_found; - } - break; - /* ### mwelch -- KEA */ - case SEC_OID_NETSCAPE_SMIME_KEA: - { - SECStatus err; - CK_MECHANISM_TYPE bulkType; - PK11SymKey *tek; - SECKEYPublicKey *senderPubKey; - SEC_PKCS7SMIMEKEAParameters keaParams; - - (void) memset(&keaParams, 0, sizeof(keaParams)); - - /* Decode the KEA algorithm parameters. */ - err = SEC_ASN1DecodeItem(NULL, - &keaParams, - SEC_SMIMEKEAParamTemplateAllParams, - &(ri->keyEncAlg.parameters)); - if (err != SECSuccess) - { - p7dcx->error = err; - PORT_SetError(0); - goto no_key_found; - } - - - /* We just got key data, no key structure. So, we - create one. */ - senderPubKey = - PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data, - keaParams.originatorKEAKey.len); - if (senderPubKey == NULL) - { - p7dcx->error = PORT_GetError(); - PORT_SetError(0); - goto no_key_found; - } - - /* Generate the TEK (token exchange key) which we use - to unwrap the bulk encryption key. */ - tek = PK11_PubDerive(privkey, senderPubKey, - PR_FALSE, - &keaParams.originatorRA, - NULL, - CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, - CKA_WRAP, 0, p7dcx->pwfn_arg); - SECKEY_DestroyPublicKey(senderPubKey); - - if (tek == NULL) - { - p7dcx->error = PORT_GetError(); - PORT_SetError(0); - goto no_key_found; - } - - /* Now that we have the TEK, unwrap the bulk key - with which to decrypt the message. We have to - do one of two different things depending on - whether Skipjack was used for bulk encryption - of the message. */ - bulkType = PK11_AlgtagToMechanism (bulkalgtag); - switch(bulkType) - { - case CKM_SKIPJACK_CBC64: - case CKM_SKIPJACK_ECB64: - case CKM_SKIPJACK_OFB64: - case CKM_SKIPJACK_CFB64: - case CKM_SKIPJACK_CFB32: - case CKM_SKIPJACK_CFB16: - case CKM_SKIPJACK_CFB8: - /* Skipjack is being used as the bulk encryption algorithm.*/ - /* Unwrap the bulk key. */ - bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, - NULL, &ri->encKey, - CKM_SKIPJACK_CBC64, - CKA_DECRYPT, 0); - break; - default: - /* Skipjack was not used for bulk encryption of this - message. Use Skipjack CBC64, with the nonSkipjackIV - part of the KEA key parameters, to decrypt - the bulk key. If we got a parameter indicating that the - bulk key size is different than the encrypted key size, - pass in the real key size. */ - - /* Check for specified bulk key length (unspecified implies - that the bulk key length is the same as encrypted length) */ - if (keaParams.bulkKeySize.len > 0) - { - p7dcx->error = SEC_ASN1DecodeItem(NULL, &bulkLength, - SEC_ASN1_GET(SEC_IntegerTemplate), - &keaParams.bulkKeySize); - } - - if (p7dcx->error != SECSuccess) - goto no_key_found; - - bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, - &keaParams.nonSkipjackIV, - &ri->encKey, - bulkType, - CKA_DECRYPT, bulkLength); - } - - - if (bulkkey == NULL) - { - p7dcx->error = PORT_GetError(); - PORT_SetError(0); - goto no_key_found; - } - break; - } - default: - p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG; - goto no_key_found; - } - - return bulkkey; - -no_key_found: - if (privkey != NULL) - SECKEY_DestroyPrivateKey (privkey); - - return NULL; -} - -/* - * XXX The following comment is old -- the function used to only handle - * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData - * as well (and it had all of the code of the helper function above - * built into it), though the comment was left as is. Fix it... - * - * We are just about to decode the content of an EnvelopedData. - * Set up a decryption context so we can decrypt as we go. - * Presumably we are one of the recipients listed in "recipientinfos". - * (XXX And if we are not, or if we have trouble, what should we do? - * It would be nice to let the decoding still work. Maybe it should - * be an error if there is a content callback, but not an error otherwise?) - * The encryption key and related information can be found in "enccinfo". - */ -static SECStatus -sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth, - SEC_PKCS7RecipientInfo **recipientinfos, - SEC_PKCS7EncryptedContentInfo *enccinfo, - PK11SymKey **copy_key_for_signature) -{ - PK11SymKey *bulkkey = NULL; - sec_PKCS7CipherObject *decryptobj; - - /* - * If a callback is supplied to retrieve the encryption key, - * for instance, for Encrypted Content infos, then retrieve - * the bulkkey from the callback. Otherwise, assume that - * we are processing Enveloped or SignedAndEnveloped data - * content infos. - * - * XXX Put an assert here? - */ - if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) { - if (p7dcx->dkcb != NULL) { - bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg, - &(enccinfo->contentEncAlg)); - } - enccinfo->keysize = 0; - } else { - bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos, - enccinfo); - if (bulkkey == NULL) goto no_decryption; - enccinfo->keysize = PK11_GetKeyStrength(bulkkey, - &(enccinfo->contentEncAlg)); - - } - - /* - * XXX I think following should set error in p7dcx and clear set error - * (as used to be done here, or as is done in get_receipient_key above. - */ - if(bulkkey == NULL) { - goto no_decryption; - } - - /* - * We want to make sure decryption is allowed. This is done via - * a callback specified in SEC_PKCS7DecoderStart(). - */ - if (p7dcx->decrypt_allowed_cb) { - if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg), - bulkkey) == PR_FALSE) { - p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; - goto no_decryption; - } - } else { - p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED; - goto no_decryption; - } - - /* - * When decrypting a signedAndEnvelopedData, the signature also has - * to be decrypted with the bulk encryption key; to avoid having to - * get it all over again later (and do another potentially expensive - * RSA operation), copy it for later signature verification to use. - */ - if (copy_key_for_signature != NULL) - *copy_key_for_signature = PK11_ReferenceSymKey (bulkkey); - - /* - * Now we have the bulk encryption key (in bulkkey) and the - * the algorithm (in enccinfo->contentEncAlg). Using those, - * create a decryption context. - */ - decryptobj = sec_PKCS7CreateDecryptObject (bulkkey, - &(enccinfo->contentEncAlg)); - - /* - * We are done with (this) bulkkey now. - */ - PK11_FreeSymKey (bulkkey); - - if (decryptobj == NULL) { - p7dcx->error = PORT_GetError(); - PORT_SetError(0); - goto no_decryption; - } - - SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, - sec_pkcs7_decoder_filter, - p7dcx, - (PRBool)(p7dcx->cb != NULL)); - - p7dcx->worker.depth = depth; - p7dcx->worker.decryptobj = decryptobj; - - return SECSuccess; - -no_decryption: - /* - * For some reason (error set already, if appropriate), we cannot - * decrypt the content. I am not sure what exactly is the right - * thing to do here; in some cases we want to just stop, and in - * others we want to let the decoding finish even though we cannot - * decrypt the content. My current thinking is that if the caller - * set up a content callback, then they are really interested in - * getting (decrypted) content, and if they cannot they will want - * to know about it. However, if no callback was specified, then - * maybe it is not important that the decryption failed. - */ - if (p7dcx->cb != NULL) - return SECFailure; - else - return SECSuccess; /* Let the decoding continue. */ -} - - -static SECStatus -sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx, - PRArenaPool *poolp, - SEC_PKCS7EncryptedContentInfo *enccinfo) -{ - struct sec_pkcs7_decoder_worker *worker; - - /* - * XXX Handling nested contents would mean that there is a chain - * of workers -- one per each level of content. The following - * would want to find the last worker in the chain. - */ - worker = &(p7dcx->worker); - - /* - * If no decryption context, then we have nothing to do. - */ - if (worker->decryptobj == NULL) - return SECSuccess; - - /* - * No matter what happens after this, we want to stop filtering. - * XXX If we handle nested contents, we only want to stop filtering - * if we are finishing off the *last* worker. - */ - SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); - - /* - * Handle the last block. - */ - sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE); - - /* - * All done, destroy it. - */ - sec_PKCS7DestroyDecryptObject (worker->decryptobj); - worker->decryptobj = NULL; - - return SECSuccess; -} - - -static void -sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth) -{ - SEC_PKCS7DecoderContext *p7dcx; - SEC_PKCS7ContentInfo *cinfo; - SEC_PKCS7SignedData *sigd; - SEC_PKCS7EnvelopedData *envd; - SEC_PKCS7SignedAndEnvelopedData *saed; - SEC_PKCS7EncryptedData *encd; - SEC_PKCS7DigestedData *digd; - PRBool after; - SECStatus rv; - - /* - * Just to make the code easier to read, create an "after" variable - * that is equivalent to "not before". - * (This used to be just the statement "after = !before", but that - * causes a warning on the mac; to avoid that, we do it the long way.) - */ - if (before) - after = PR_FALSE; - else - after = PR_TRUE; - - p7dcx = (SEC_PKCS7DecoderContext*)arg; - cinfo = p7dcx->cinfo; - - if (cinfo->contentTypeTag == NULL) { - if (after && dest == &(cinfo->contentType)) - cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType)); - return; - } - - switch (cinfo->contentTypeTag->offset) { - case SEC_OID_PKCS7_SIGNED_DATA: - sigd = cinfo->content.signedData; - if (sigd == NULL) - break; - - if (sigd->contentInfo.contentTypeTag == NULL) { - if (after && dest == &(sigd->contentInfo.contentType)) - sigd->contentInfo.contentTypeTag = - SECOID_FindOID(&(sigd->contentInfo.contentType)); - break; - } - - /* - * We only set up a filtering digest if the content is - * plain DATA; anything else needs more work because a - * second pass is required to produce a DER encoding from - * an input that can be BER encoded. (This is a requirement - * of PKCS7 that is unfortunate, but there you have it.) - * - * XXX Also, since we stop here if this is not DATA, the - * inner content is not getting processed at all. Someday - * we may want to fix that. - */ - if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) { - /* XXX Set an error in p7dcx->error */ - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - break; - } - - /* - * Just before the content, we want to set up a digest context - * for each digest algorithm listed, and start a filter which - * will run all of the contents bytes through that digest. - */ - if (before && dest == &(sigd->contentInfo.content)) { - rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, - sigd->digestAlgorithms); - if (rv != SECSuccess) - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - - break; - } - - /* - * XXX To handle nested types, here is where we would want - * to check for inner boundaries that need handling. - */ - - /* - * Are we done? - */ - if (after && dest == &(sigd->contentInfo.content)) { - /* - * Close out the digest contexts. We ignore any error - * because we are stopping anyway; the error status left - * behind in p7dcx will be seen by outer functions. - */ - (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, - &(sigd->digests)); - - /* - * XXX To handle nested contents, we would need to remove - * the worker from the chain (and free it). - */ - - /* - * Stop notify. - */ - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - } - break; - - case SEC_OID_PKCS7_ENVELOPED_DATA: - envd = cinfo->content.envelopedData; - if (envd == NULL) - break; - - if (envd->encContentInfo.contentTypeTag == NULL) { - if (after && dest == &(envd->encContentInfo.contentType)) - envd->encContentInfo.contentTypeTag = - SECOID_FindOID(&(envd->encContentInfo.contentType)); - break; - } - - /* - * Just before the content, we want to set up a decryption - * context, and start a filter which will run all of the - * contents bytes through it to determine the plain content. - */ - if (before && dest == &(envd->encContentInfo.encContent)) { - rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, - envd->recipientInfos, - &(envd->encContentInfo), - NULL); - if (rv != SECSuccess) - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - - break; - } - - /* - * Are we done? - */ - if (after && dest == &(envd->encContentInfo.encContent)) { - /* - * Close out the decryption context. We ignore any error - * because we are stopping anyway; the error status left - * behind in p7dcx will be seen by outer functions. - */ - (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, - &(envd->encContentInfo)); - - /* - * XXX To handle nested contents, we would need to remove - * the worker from the chain (and free it). - */ - - /* - * Stop notify. - */ - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - } - break; - - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - saed = cinfo->content.signedAndEnvelopedData; - if (saed == NULL) - break; - - if (saed->encContentInfo.contentTypeTag == NULL) { - if (after && dest == &(saed->encContentInfo.contentType)) - saed->encContentInfo.contentTypeTag = - SECOID_FindOID(&(saed->encContentInfo.contentType)); - break; - } - - /* - * Just before the content, we want to set up a decryption - * context *and* digest contexts, and start a filter which - * will run all of the contents bytes through both. - */ - if (before && dest == &(saed->encContentInfo.encContent)) { - rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, - saed->recipientInfos, - &(saed->encContentInfo), - &(saed->sigKey)); - if (rv == SECSuccess) - rv = sec_pkcs7_decoder_start_digests (p7dcx, depth, - saed->digestAlgorithms); - if (rv != SECSuccess) - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - - break; - } - - /* - * Are we done? - */ - if (after && dest == &(saed->encContentInfo.encContent)) { - /* - * Close out the decryption and digests contexts. - * We ignore any errors because we are stopping anyway; - * the error status left behind in p7dcx will be seen by - * outer functions. - * - * Note that the decrypt stuff must be called first; - * it may have a last buffer to do which in turn has - * to be added to the digest. - */ - (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, - &(saed->encContentInfo)); - (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp, - &(saed->digests)); - - /* - * XXX To handle nested contents, we would need to remove - * the worker from the chain (and free it). - */ - - /* - * Stop notify. - */ - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - } - break; - - case SEC_OID_PKCS7_DIGESTED_DATA: - digd = cinfo->content.digestedData; - - /* - * XXX Want to do the digest or not? Maybe future enhancement... - */ - if (before && dest == &(digd->contentInfo.content.data)) { - SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter, - p7dcx, - (PRBool)(p7dcx->cb != NULL)); - break; - } - - /* - * Are we done? - */ - if (after && dest == &(digd->contentInfo.content.data)) { - SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); - } - break; - - case SEC_OID_PKCS7_ENCRYPTED_DATA: - encd = cinfo->content.encryptedData; - - /* - * XXX If the decryption key callback is set, we want to start - * the decryption. If the callback is not set, we will treat the - * content as plain data, since we do not have the key. - * - * Is this the proper thing to do? - */ - if (before && dest == &(encd->encContentInfo.encContent)) { - /* - * Start the encryption process if the decryption key callback - * is present. Otherwise, treat the content like plain data. - */ - rv = SECSuccess; - if (p7dcx->dkcb != NULL) { - rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL, - &(encd->encContentInfo), - NULL); - } - - if (rv != SECSuccess) - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - - break; - } - - /* - * Are we done? - */ - if (after && dest == &(encd->encContentInfo.encContent)) { - /* - * Close out the decryption context. We ignore any error - * because we are stopping anyway; the error status left - * behind in p7dcx will be seen by outer functions. - */ - (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp, - &(encd->encContentInfo)); - - /* - * Stop notify. - */ - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - } - break; - - case SEC_OID_PKCS7_DATA: - /* - * If a output callback has been specified, we want to set the filter - * to call the callback. This is taken care of in - * sec_pkcs7_decoder_start_decrypt() or - * sec_pkcs7_decoder_start_digests() for the other content types. - */ - - if (before && dest == &(cinfo->content.data)) { - - /* - * Set the filter proc up. - */ - SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, - sec_pkcs7_decoder_filter, - p7dcx, - (PRBool)(p7dcx->cb != NULL)); - break; - } - - if (after && dest == &(cinfo->content.data)) { - /* - * Time to clean up after ourself, stop the Notify and Filter - * procedures. - */ - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - SEC_ASN1DecoderClearFilterProc (p7dcx->dcx); - } - break; - - default: - SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx); - break; - } -} - - -SEC_PKCS7DecoderContext * -SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg, - SECKEYGetPasswordKey pwfn, void *pwfn_arg, - SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, - void *decrypt_key_cb_arg, - SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) -{ - SEC_PKCS7DecoderContext *p7dcx; - SEC_ASN1DecoderContext *dcx; - SEC_PKCS7ContentInfo *cinfo; - PRArenaPool *poolp; - - poolp = PORT_NewArena (1024); /* XXX what is right value? */ - if (poolp == NULL) - return NULL; - - cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo)); - if (cinfo == NULL) { - PORT_FreeArena (poolp, PR_FALSE); - return NULL; - } - - cinfo->poolp = poolp; - cinfo->pwfn = pwfn; - cinfo->pwfn_arg = pwfn_arg; - cinfo->created = PR_FALSE; - cinfo->refCount = 1; - - p7dcx = - (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext)); - if (p7dcx == NULL) { - PORT_FreeArena (poolp, PR_FALSE); - return NULL; - } - - p7dcx->tmp_poolp = PORT_NewArena (1024); /* XXX what is right value? */ - if (p7dcx->tmp_poolp == NULL) { - PORT_Free (p7dcx); - PORT_FreeArena (poolp, PR_FALSE); - return NULL; - } - - dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate); - if (dcx == NULL) { - PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); - PORT_Free (p7dcx); - PORT_FreeArena (poolp, PR_FALSE); - return NULL; - } - - SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx); - - p7dcx->dcx = dcx; - p7dcx->cinfo = cinfo; - p7dcx->cb = cb; - p7dcx->cb_arg = cb_arg; - p7dcx->pwfn = pwfn; - p7dcx->pwfn_arg = pwfn_arg; - p7dcx->dkcb = decrypt_key_cb; - p7dcx->dkcb_arg = decrypt_key_cb_arg; - p7dcx->decrypt_allowed_cb = decrypt_allowed_cb; - - return p7dcx; -} - - -/* - * Do the next chunk of PKCS7 decoding. If there is a problem, set - * an error and return a failure status. Note that in the case of - * an error, this routine is still prepared to be called again and - * again in case that is the easiest route for our caller to take. - * We simply detect it and do not do anything except keep setting - * that error in case our caller has not noticed it yet... - */ -SECStatus -SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx, - const char *buf, unsigned long len) -{ - if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) { - PORT_Assert (p7dcx->error == 0); - if (p7dcx->error == 0) { - if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) { - p7dcx->error = PORT_GetError(); - PORT_Assert (p7dcx->error); - if (p7dcx->error == 0) - p7dcx->error = -1; - } - } - } - - if (p7dcx->error) { - if (p7dcx->dcx != NULL) { - (void) SEC_ASN1DecoderFinish (p7dcx->dcx); - p7dcx->dcx = NULL; - } - if (p7dcx->cinfo != NULL) { - SEC_PKCS7DestroyContentInfo (p7dcx->cinfo); - p7dcx->cinfo = NULL; - } - PORT_SetError (p7dcx->error); - return SECFailure; - } - - return SECSuccess; -} - - -SEC_PKCS7ContentInfo * -SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx) -{ - SEC_PKCS7ContentInfo *cinfo; - - cinfo = p7dcx->cinfo; - if (p7dcx->dcx != NULL) { - if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) { - SEC_PKCS7DestroyContentInfo (cinfo); - cinfo = NULL; - } - } - /* free any NSS data structures */ - if (p7dcx->worker.decryptobj) { - sec_PKCS7DestroyDecryptObject (p7dcx->worker.decryptobj); - } - PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE); - PORT_Free (p7dcx); - return cinfo; -} - - -SEC_PKCS7ContentInfo * -SEC_PKCS7DecodeItem(SECItem *p7item, - SEC_PKCS7DecoderContentCallback cb, void *cb_arg, - SECKEYGetPasswordKey pwfn, void *pwfn_arg, - SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb, - void *decrypt_key_cb_arg, - SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb) -{ - SEC_PKCS7DecoderContext *p7dcx; - - p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, - decrypt_key_cb_arg, decrypt_allowed_cb); - (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len); - return SEC_PKCS7DecoderFinish(p7dcx); -} - -/* - * Abort the ASN.1 stream. Used by pkcs 12 - */ -void -SEC_PKCS7DecoderAbort(SEC_PKCS7DecoderContext *p7dcx, int error) -{ - PORT_Assert(p7dcx); - SEC_ASN1DecoderAbort(p7dcx->dcx, error); -} - - -/* - * If the thing contains any certs or crls return true; false otherwise. - */ -PRBool -SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo) -{ - SECOidTag kind; - SECItem **certs; - CERTSignedCrl **crls; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - default: - case SEC_OID_PKCS7_DATA: - case SEC_OID_PKCS7_DIGESTED_DATA: - case SEC_OID_PKCS7_ENVELOPED_DATA: - case SEC_OID_PKCS7_ENCRYPTED_DATA: - return PR_FALSE; - case SEC_OID_PKCS7_SIGNED_DATA: - certs = cinfo->content.signedData->rawCerts; - crls = cinfo->content.signedData->crls; - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - certs = cinfo->content.signedAndEnvelopedData->rawCerts; - crls = cinfo->content.signedAndEnvelopedData->crls; - break; - } - - /* - * I know this could be collapsed, but I was in a mood to be explicit. - */ - if (certs != NULL && certs[0] != NULL) - return PR_TRUE; - else if (crls != NULL && crls[0] != NULL) - return PR_TRUE; - else - return PR_FALSE; -} - -/* return the content length...could use GetContent, however we - * need the encrypted content length - */ -PRBool -SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen) -{ - SECItem *item = NULL; - - if(cinfo == NULL) { - return PR_TRUE; - } - - switch(SEC_PKCS7ContentType(cinfo)) - { - case SEC_OID_PKCS7_DATA: - item = cinfo->content.data; - break; - case SEC_OID_PKCS7_ENCRYPTED_DATA: - item = &cinfo->content.encryptedData->encContentInfo.encContent; - break; - default: - /* add other types */ - return PR_FALSE; - } - - if(!item) { - return PR_TRUE; - } else if(item->len <= minLen) { - return PR_TRUE; - } - - return PR_FALSE; -} - - -PRBool -SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo) -{ - SECOidTag kind; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - default: - case SEC_OID_PKCS7_DATA: - case SEC_OID_PKCS7_DIGESTED_DATA: - case SEC_OID_PKCS7_SIGNED_DATA: - return PR_FALSE; - case SEC_OID_PKCS7_ENCRYPTED_DATA: - case SEC_OID_PKCS7_ENVELOPED_DATA: - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - return PR_TRUE; - } -} - - -/* - * If the PKCS7 content has a signature (not just *could* have a signature) - * return true; false otherwise. This can/should be called before calling - * VerifySignature, which will always indicate failure if no signature is - * present, but that does not mean there even was a signature! - * Note that the content itself can be empty (detached content was sent - * another way); it is the presence of the signature that matters. - */ -PRBool -SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo) -{ - SECOidTag kind; - SEC_PKCS7SignerInfo **signerinfos; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - default: - case SEC_OID_PKCS7_DATA: - case SEC_OID_PKCS7_DIGESTED_DATA: - case SEC_OID_PKCS7_ENVELOPED_DATA: - case SEC_OID_PKCS7_ENCRYPTED_DATA: - return PR_FALSE; - case SEC_OID_PKCS7_SIGNED_DATA: - signerinfos = cinfo->content.signedData->signerInfos; - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos; - break; - } - - /* - * I know this could be collapsed; but I kind of think it will get - * more complicated before I am finished, so... - */ - if (signerinfos != NULL && signerinfos[0] != NULL) - return PR_TRUE; - else - return PR_FALSE; -} - - -/* - * SEC_PKCS7ContentVerifySignature - * Look at a PKCS7 contentInfo and check if the signature is good. - * The digest was either calculated earlier (and is stored in the - * contentInfo itself) or is passed in via "detached_digest". - * - * The verification checks that the signing cert is valid and trusted - * for the purpose specified by "certusage". - * - * In addition, if "keepcerts" is true, add any new certificates found - * into our local database. - * - * XXX Each place which returns PR_FALSE should be sure to have a good - * error set for inspection by the caller. Alternatively, we could create - * an enumeration of success and each type of failure and return that - * instead of a boolean. For now, the default in a bad situation is to - * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE. But this should be - * reviewed; better (more specific) errors should be possible (to distinguish - * a signature failure from a badly-formed pkcs7 signedData, for example). - * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE, - * but that has a less helpful error string associated with it right now; - * if/when that changes, review and change these as needed. - * - * XXX This is broken wrt signedAndEnvelopedData. In that case, the - * message digest is doubly encrypted -- first encrypted with the signer - * private key but then again encrypted with the bulk encryption key used - * to encrypt the content. So before we can pass the digest to VerifyDigest, - * we need to decrypt it with the bulk encryption key. Also, in this case, - * there should be NO authenticatedAttributes (signerinfo->authAttr should - * be NULL). - */ -static PRBool -sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo, - SECCertUsage certusage, - SECItem *detached_digest, - HASH_HashType digest_type, - PRBool keepcerts) -{ - SECAlgorithmID **digestalgs, *bulkid; - SECItem *digest; - SECItem **digests; - SECItem **rawcerts; - CERTSignedCrl **crls; - SEC_PKCS7SignerInfo **signerinfos, *signerinfo; - CERTCertificate *cert, **certs; - PRBool goodsig; - CERTCertDBHandle *certdb, *defaultdb; - SECOidData *algiddata; - int i, certcount; - SECKEYPublicKey *publickey; - SECItem *content_type; - PK11SymKey *sigkey; - SECItem *encoded_stime; - int64 stime; - SECStatus rv; - - /* - * Everything needed in order to "goto done" safely. - */ - goodsig = PR_FALSE; - certcount = 0; - cert = NULL; - certs = NULL; - certdb = NULL; - defaultdb = CERT_GetDefaultCertDB(); - publickey = NULL; - - if (! SEC_PKCS7ContentIsSigned(cinfo)) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - PORT_Assert (cinfo->contentTypeTag != NULL); - - switch (cinfo->contentTypeTag->offset) { - default: - case SEC_OID_PKCS7_DATA: - case SEC_OID_PKCS7_DIGESTED_DATA: - case SEC_OID_PKCS7_ENVELOPED_DATA: - case SEC_OID_PKCS7_ENCRYPTED_DATA: - /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */ - PORT_Assert (0); - case SEC_OID_PKCS7_SIGNED_DATA: - { - SEC_PKCS7SignedData *sdp; - - sdp = cinfo->content.signedData; - digestalgs = sdp->digestAlgorithms; - digests = sdp->digests; - rawcerts = sdp->rawCerts; - crls = sdp->crls; - signerinfos = sdp->signerInfos; - content_type = &(sdp->contentInfo.contentType); - sigkey = NULL; - bulkid = NULL; - } - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - { - SEC_PKCS7SignedAndEnvelopedData *saedp; - - saedp = cinfo->content.signedAndEnvelopedData; - digestalgs = saedp->digestAlgorithms; - digests = saedp->digests; - rawcerts = saedp->rawCerts; - crls = saedp->crls; - signerinfos = saedp->signerInfos; - content_type = &(saedp->encContentInfo.contentType); - sigkey = saedp->sigKey; - bulkid = &(saedp->encContentInfo.contentEncAlg); - } - break; - } - - if ((signerinfos == NULL) || (signerinfos[0] == NULL)) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - /* - * XXX Need to handle multiple signatures; checking them is easy, - * but what should be the semantics here (like, return value)? - */ - if (signerinfos[1] != NULL) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - signerinfo = signerinfos[0]; - - /* - * XXX I would like to just pass the issuerAndSN, along with the rawcerts - * and crls, to some function that did all of this certificate stuff - * (open/close the database if necessary, verifying the certs, etc.) - * and gave me back a cert pointer if all was good. - */ - certdb = defaultdb; - if (certdb == NULL) { - goto done; - } - - certcount = 0; - if (rawcerts != NULL) { - for (; rawcerts[certcount] != NULL; certcount++) { - /* just counting */ - } - } - - /* - * Note that the result of this is that each cert in "certs" - * needs to be destroyed. - */ - rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs, - keepcerts, PR_FALSE, NULL); - if ( rv != SECSuccess ) { - goto done; - } - - /* - * This cert will also need to be freed, but since we save it - * in signerinfo for later, we do not want to destroy it when - * we leave this function -- we let the clean-up of the entire - * cinfo structure later do the destroy of this cert. - */ - cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN); - if (cert == NULL) { - goto done; - } - - signerinfo->cert = cert; - - /* - * Get and convert the signing time; if available, it will be used - * both on the cert verification and for importing the sender - * email profile. - */ - encoded_stime = SEC_PKCS7GetSigningTime (cinfo); - if (encoded_stime != NULL) { - if (DER_DecodeTimeChoice (&stime, encoded_stime) != SECSuccess) - encoded_stime = NULL; /* conversion failed, so pretend none */ - } - - /* - * XXX This uses the signing time, if available. Additionally, we - * might want to, if there is no signing time, get the message time - * from the mail header itself, and use that. That would require - * a change to our interface though, and for S/MIME callers to pass - * in a time (and for non-S/MIME callers to pass in nothing, or - * maybe make them pass in the current time, always?). - */ - if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, - encoded_stime != NULL ? stime : PR_Now(), - cinfo->pwfn_arg, NULL) != SECSuccess) - { - /* - * XXX Give the user an option to check the signature anyway? - * If we want to do this, need to give a way to leave and display - * some dialog and get the answer and come back through (or do - * the rest of what we do below elsewhere, maybe by putting it - * in a function that we call below and could call from a dialog - * finish handler). - */ - goto savecert; - } - - publickey = CERT_ExtractPublicKey (cert); - if (publickey == NULL) - goto done; - - /* - * XXX No! If digests is empty, see if we can create it now by - * digesting the contents. This is necessary if we want to allow - * somebody to do a simple decode (without filtering, etc.) and - * then later call us here to do the verification. - * OR, we can just specify that the interface to this routine - * *requires* that the digest(s) be done before calling and either - * stashed in the struct itself or passed in explicitly (as would - * be done for detached contents). - */ - if ((digests == NULL || digests[0] == NULL) - && (detached_digest == NULL || detached_digest->data == NULL)) - goto done; - - /* - * Find and confirm digest algorithm. - */ - algiddata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm)); - - if (detached_digest != NULL) { - HASH_HashType found_type = HASH_GetHashTypeByOidTag(algiddata->offset); - unsigned int hashLen = HASH_ResultLen(digest_type); - - if (digest_type != found_type || - digest_type == HASH_AlgNULL || - detached_digest->len != hashLen) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - digest = detached_digest; - } else { - PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL); - if (digestalgs == NULL || digestalgs[0] == NULL) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - /* - * pick digest matching signerinfo->digestAlg from digests - */ - if (algiddata == NULL) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - for (i = 0; digestalgs[i] != NULL; i++) { - if (SECOID_FindOID (&(digestalgs[i]->algorithm)) == algiddata) - break; - } - if (digestalgs[i] == NULL) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - digest = digests[i]; - } - - /* - * XXX This may not be the right set of algorithms to check. - * I'd prefer to trust that just calling VFY_Verify{Data,Digest} - * would do the right thing (and set an error if it could not); - * then additional algorithms could be handled by that code - * and we would Just Work. So this check should just be removed, - * but not until the VFY code is better at setting errors. - */ - algiddata = SECOID_FindOID (&(signerinfo->digestEncAlg.algorithm)); - if (algiddata == NULL || - ((algiddata->offset != SEC_OID_PKCS1_RSA_ENCRYPTION) && -#ifdef NSS_ECC_MORE_THAN_SUITE_B - (algiddata->offset != SEC_OID_ANSIX962_EC_PUBLIC_KEY) && -#endif - (algiddata->offset != SEC_OID_ANSIX9_DSA_SIGNATURE))) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - if (signerinfo->authAttr != NULL) { - SEC_PKCS7Attribute *attr; - SECItem *value; - SECItem encoded_attrs; - - /* - * We have a sigkey only for signedAndEnvelopedData, which is - * not supposed to have any authenticated attributes. - */ - if (sigkey != NULL) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - /* - * PKCS #7 says that if there are any authenticated attributes, - * then there must be one for content type which matches the - * content type of the content being signed, and there must - * be one for message digest which matches our message digest. - * So check these things first. - * XXX Might be nice to have a compare-attribute-value function - * which could collapse the following nicely. - */ - attr = sec_PKCS7FindAttribute (signerinfo->authAttr, - SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE); - value = sec_PKCS7AttributeValue (attr); - if (value == NULL || value->len != content_type->len) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - attr = sec_PKCS7FindAttribute (signerinfo->authAttr, - SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE); - value = sec_PKCS7AttributeValue (attr); - if (value == NULL || value->len != digest->len) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - if (PORT_Memcmp (value->data, digest->data, value->len) != 0) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - /* - * Okay, we met the constraints of the basic attributes. - * Now check the signature, which is based on a digest of - * the DER-encoded authenticated attributes. So, first we - * encode and then we digest/verify. - */ - encoded_attrs.data = NULL; - encoded_attrs.len = 0; - if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs, - &(signerinfo->authAttr)) == NULL) - goto done; - - if (encoded_attrs.data == NULL || encoded_attrs.len == 0) { - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - /* - * XXX the 5th (algid) argument should be the signature algorithm. - * See sec_pkcs7_pick_sign_alg in p7encode.c. - */ - goodsig = (PRBool)(VFY_VerifyData (encoded_attrs.data, - encoded_attrs.len, - publickey, &(signerinfo->encDigest), - SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)), - cinfo->pwfn_arg) == SECSuccess); - PORT_Free (encoded_attrs.data); - } else { - SECItem *sig; - SECItem holder; - SECStatus rv; - - /* - * No authenticated attributes. - * The signature is based on the plain message digest. - */ - - sig = &(signerinfo->encDigest); - if (sig->len == 0) { /* bad signature */ - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - goto done; - } - - if (sigkey != NULL) { - sec_PKCS7CipherObject *decryptobj; - unsigned int buflen; - - /* - * For signedAndEnvelopedData, we first must decrypt the encrypted - * digest with the bulk encryption key. The result is the normal - * encrypted digest (aka the signature). - */ - decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid); - if (decryptobj == NULL) - goto done; - - buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE); - PORT_Assert (buflen); - if (buflen == 0) { /* something is wrong */ - sec_PKCS7DestroyDecryptObject (decryptobj); - goto done; - } - - holder.data = (unsigned char*)PORT_Alloc (buflen); - if (holder.data == NULL) { - sec_PKCS7DestroyDecryptObject (decryptobj); - goto done; - } - - rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen, - sig->data, sig->len, PR_TRUE); - sec_PKCS7DestroyDecryptObject (decryptobj); - if (rv != SECSuccess) { - goto done; - } - - sig = &holder; - } - - /* - * XXX the 4th (algid) argument should be the signature algorithm. - * See sec_pkcs7_pick_sign_alg in p7encode.c. - */ - goodsig = (PRBool)(VFY_VerifyDigest (digest, publickey, sig, - SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)), - cinfo->pwfn_arg) - == SECSuccess); - - if (sigkey != NULL) { - PORT_Assert (sig == &holder); - PORT_ZFree (holder.data, holder.len); - } - } - - if (! goodsig) { - /* - * XXX Change the generic error into our specific one, because - * in that case we get a better explanation out of the Security - * Advisor. This is really a bug in our error strings (the - * "generic" error has a lousy/wrong message associated with it - * which assumes the signature verification was done for the - * purposes of checking the issuer signature on a certificate) - * but this is at least an easy workaround and/or in the - * Security Advisor, which specifically checks for the error - * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation - * in that case but does not similarly check for - * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would - * probably say the wrong thing in the case that it *was* the - * certificate signature check that failed during the cert - * verification done above. Our error handling is really a mess. - */ - if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE) - PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE); - } - -savecert: - /* - * Only save the smime profile if we are checking an email message and - * the cert has an email address in it. - */ - if ( cert->emailAddr && cert->emailAddr[0] && - ( ( certusage == certUsageEmailSigner ) || - ( certusage == certUsageEmailRecipient ) ) ) { - SECItem *profile = NULL; - int save_error; - - /* - * Remember the current error set because we do not care about - * anything set by the functions we are about to call. - */ - save_error = PORT_GetError(); - - if (goodsig && (signerinfo->authAttr != NULL)) { - /* - * If the signature is good, then we can save the S/MIME profile, - * if we have one. - */ - SEC_PKCS7Attribute *attr; - - attr = sec_PKCS7FindAttribute (signerinfo->authAttr, - SEC_OID_PKCS9_SMIME_CAPABILITIES, - PR_TRUE); - profile = sec_PKCS7AttributeValue (attr); - } - - rv = CERT_SaveSMimeProfile (cert, profile, encoded_stime); - - /* - * Restore the saved error in case the calls above set a new - * one that we do not actually care about. - */ - PORT_SetError (save_error); - - /* - * XXX Failure is not indicated anywhere -- the signature - * verification itself is unaffected by whether or not the - * profile was successfully saved. - */ - } - - -done: - - /* - * See comment above about why we do not want to destroy cert - * itself here. - */ - - if (certs != NULL) - CERT_DestroyCertArray (certs, certcount); - - if (publickey != NULL) - SECKEY_DestroyPublicKey (publickey); - - return goodsig; -} - -/* - * SEC_PKCS7VerifySignature - * Look at a PKCS7 contentInfo and check if the signature is good. - * The verification checks that the signing cert is valid and trusted - * for the purpose specified by "certusage". - * - * In addition, if "keepcerts" is true, add any new certificates found - * into our local database. - */ -PRBool -SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo, - SECCertUsage certusage, - PRBool keepcerts) -{ - return sec_pkcs7_verify_signature (cinfo, certusage, - NULL, HASH_AlgNULL, keepcerts); -} - -/* - * SEC_PKCS7VerifyDetachedSignature - * Look at a PKCS7 contentInfo and check if the signature matches - * a passed-in digest (calculated, supposedly, from detached contents). - * The verification checks that the signing cert is valid and trusted - * for the purpose specified by "certusage". - * - * In addition, if "keepcerts" is true, add any new certificates found - * into our local database. - */ -PRBool -SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo, - SECCertUsage certusage, - SECItem *detached_digest, - HASH_HashType digest_type, - PRBool keepcerts) -{ - return sec_pkcs7_verify_signature (cinfo, certusage, - detached_digest, digest_type, - keepcerts); -} - - -/* - * Return the asked-for portion of the name of the signer of a PKCS7 - * signed object. - * - * Returns a pointer to allocated memory, which must be freed. - * A NULL return value is an error. - */ - -#define sec_common_name 1 -#define sec_email_address 2 - -static char * -sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector) -{ - SECOidTag kind; - SEC_PKCS7SignerInfo **signerinfos; - CERTCertificate *signercert; - char *container; - - kind = SEC_PKCS7ContentType (cinfo); - switch (kind) { - default: - case SEC_OID_PKCS7_DATA: - case SEC_OID_PKCS7_DIGESTED_DATA: - case SEC_OID_PKCS7_ENVELOPED_DATA: - case SEC_OID_PKCS7_ENCRYPTED_DATA: - PORT_Assert (0); - return NULL; - case SEC_OID_PKCS7_SIGNED_DATA: - { - SEC_PKCS7SignedData *sdp; - - sdp = cinfo->content.signedData; - signerinfos = sdp->signerInfos; - } - break; - case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA: - { - SEC_PKCS7SignedAndEnvelopedData *saedp; - - saedp = cinfo->content.signedAndEnvelopedData; - signerinfos = saedp->signerInfos; - } - break; - } - - if (signerinfos == NULL || signerinfos[0] == NULL) - return NULL; - - signercert = signerinfos[0]->cert; - - /* - * No cert there; see if we can find one by calling verify ourselves. - */ - if (signercert == NULL) { - /* - * The cert usage does not matter in this case, because we do not - * actually care about the verification itself, but we have to pick - * some valid usage to pass in. - */ - (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner, - NULL, HASH_AlgNULL, PR_FALSE); - signercert = signerinfos[0]->cert; - if (signercert == NULL) - return NULL; - } - - switch (selector) { - case sec_common_name: - container = CERT_GetCommonName (&signercert->subject); - break; - case sec_email_address: - if(signercert->emailAddr && signercert->emailAddr[0]) { - container = PORT_Strdup(signercert->emailAddr); - } else { - container = NULL; - } - break; - default: - PORT_Assert (0); - container = NULL; - break; - } - - return container; -} - -char * -SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo) -{ - return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name); -} - -char * -SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo) -{ - return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address); -} - - -/* - * Return the signing time, in UTCTime format, of a PKCS7 contentInfo. - */ -SECItem * -SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo) -{ - SEC_PKCS7SignerInfo **signerinfos; - SEC_PKCS7Attribute *attr; - - if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA) - return NULL; - - signerinfos = cinfo->content.signedData->signerInfos; - - /* - * No signature, or more than one, means no deal. - */ - if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL) - return NULL; - - attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr, - SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE); - return sec_PKCS7AttributeValue (attr); -} |