Back to index

lightning-sunbird  0.9+nobinonly
cmsenvdata.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * CMS envelopedData methods.
00039  *
00040  * $Id: cmsenvdata.c,v 1.11 2005/10/03 22:01:57 relyea%netscape.com Exp $
00041  */
00042 
00043 #include "cmslocal.h"
00044 
00045 #include "cert.h"
00046 #include "key.h"
00047 #include "secasn1.h"
00048 #include "secitem.h"
00049 #include "secoid.h"
00050 #include "pk11func.h"
00051 #include "secerr.h"
00052 #include "secpkcs5.h"
00053 
00054 /*
00055  * NSS_CMSEnvelopedData_Create - create an enveloped data message
00056  */
00057 NSSCMSEnvelopedData *
00058 NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
00059 {
00060     void *mark;
00061     NSSCMSEnvelopedData *envd;
00062     PLArenaPool *poolp;
00063     SECStatus rv;
00064 
00065     poolp = cmsg->poolp;
00066 
00067     mark = PORT_ArenaMark(poolp);
00068 
00069     envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData));
00070     if (envd == NULL)
00071        goto loser;
00072 
00073     envd->cmsg = cmsg;
00074 
00075     /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */
00076 
00077     rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), algorithm, NULL, keysize);
00078     if (rv != SECSuccess)
00079        goto loser;
00080 
00081     PORT_ArenaUnmark(poolp, mark);
00082     return envd;
00083 
00084 loser:
00085     PORT_ArenaRelease(poolp, mark);
00086     return NULL;
00087 }
00088 
00089 /*
00090  * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
00091  */
00092 void
00093 NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp)
00094 {
00095     NSSCMSRecipientInfo **recipientinfos;
00096     NSSCMSRecipientInfo *ri;
00097 
00098     if (edp == NULL)
00099        return;
00100 
00101     recipientinfos = edp->recipientInfos;
00102     if (recipientinfos == NULL)
00103        return;
00104 
00105     while ((ri = *recipientinfos++) != NULL)
00106        NSS_CMSRecipientInfo_Destroy(ri);
00107 
00108    NSS_CMSContentInfo_Destroy(&(edp->contentInfo));
00109 
00110 }
00111 
00112 /*
00113  * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
00114  */
00115 NSSCMSContentInfo *
00116 NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd)
00117 {
00118     return &(envd->contentInfo);
00119 }
00120 
00121 /*
00122  * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg
00123  *
00124  * rip must be created on the same pool as edp - this is not enforced, though.
00125  */
00126 SECStatus
00127 NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip)
00128 {
00129     void *mark;
00130     SECStatus rv;
00131 
00132     /* XXX compare pools, if not same, copy rip into edp's pool */
00133 
00134     PR_ASSERT(edp != NULL);
00135     PR_ASSERT(rip != NULL);
00136 
00137     mark = PORT_ArenaMark(edp->cmsg->poolp);
00138 
00139     rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
00140     if (rv != SECSuccess) {
00141        PORT_ArenaRelease(edp->cmsg->poolp, mark);
00142        return SECFailure;
00143     }
00144 
00145     PORT_ArenaUnmark (edp->cmsg->poolp, mark);
00146     return SECSuccess;
00147 }
00148 
00149 /*
00150  * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding
00151  *
00152  * at this point, we need
00153  * - recipientinfos set up with recipient's certificates
00154  * - a content encryption algorithm (if none, 3DES will be used)
00155  *
00156  * this function will generate a random content encryption key (aka bulk key),
00157  * initialize the recipientinfos with certificate identification and wrap the bulk key
00158  * using the proper algorithm for every certificiate.
00159  * it will finally set the bulk algorithm and key so that the encode step can find it.
00160  */
00161 SECStatus
00162 NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd)
00163 {
00164     int version;
00165     NSSCMSRecipientInfo **recipientinfos;
00166     NSSCMSContentInfo *cinfo;
00167     PK11SymKey *bulkkey = NULL;
00168     SECOidTag bulkalgtag;
00169     CK_MECHANISM_TYPE type;
00170     PK11SlotInfo *slot;
00171     SECStatus rv;
00172     SECItem *dummy;
00173     PLArenaPool *poolp;
00174     extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
00175     void *mark = NULL;
00176     int i;
00177 
00178     poolp = envd->cmsg->poolp;
00179     cinfo = &(envd->contentInfo);
00180 
00181     recipientinfos = envd->recipientInfos;
00182     if (recipientinfos == NULL) {
00183        PORT_SetError(SEC_ERROR_BAD_DATA);
00184 #if 0
00185        PORT_SetErrorString("Cannot find recipientinfos to encode.");
00186 #endif
00187        goto loser;
00188     }
00189 
00190     version = NSS_CMS_ENVELOPED_DATA_VERSION_REG;
00191     if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
00192        version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
00193     } else {
00194        for (i = 0; recipientinfos[i] != NULL; i++) {
00195            if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) {
00196               version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
00197               break;
00198            }
00199        }
00200     }
00201     dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
00202     if (dummy == NULL)
00203        goto loser;
00204 
00205     /* now we need to have a proper content encryption algorithm
00206      * on the SMIME level, we would figure one out by looking at SMIME capabilities
00207      * we cannot do that on our level, so if none is set already, we'll just go
00208      * with one of the mandatory algorithms (3DES) */
00209     if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
00210        rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168);
00211        if (rv != SECSuccess)
00212            goto loser;
00213        bulkalgtag = SEC_OID_DES_EDE3_CBC;
00214     } 
00215 
00216     /* generate a random bulk key suitable for content encryption alg */
00217     type = PK11_AlgtagToMechanism(bulkalgtag);
00218     slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg);
00219     if (slot == NULL)
00220        goto loser;   /* error has been set by PK11_GetBestSlot */
00221 
00222     /* this is expensive... */
00223     bulkkey = PK11_KeyGen(slot, type, NULL, NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, envd->cmsg->pwfn_arg);
00224     PK11_FreeSlot(slot);
00225     if (bulkkey == NULL)
00226        goto loser;   /* error has been set by PK11_KeyGen */
00227 
00228     mark = PORT_ArenaMark(poolp);
00229 
00230     /* Encrypt the bulk key with the public key of each recipient.  */
00231     for (i = 0; recipientinfos[i] != NULL; i++) {
00232        rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
00233        if (rv != SECSuccess)
00234            goto loser;      /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */
00235                      /* could be: alg not supported etc. */
00236     }
00237 
00238     /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */
00239     rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, NSSCMSRecipientInfoTemplate, NULL);
00240     if (rv != SECSuccess)
00241        goto loser;   /* error has been set by NSS_CMSArray_SortByDER */
00242 
00243     /* store the bulk key in the contentInfo so that the encoder can find it */
00244     NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
00245 
00246     PORT_ArenaUnmark(poolp, mark);
00247 
00248     PK11_FreeSymKey(bulkkey);
00249 
00250     return SECSuccess;
00251 
00252 loser:
00253     if (mark != NULL)
00254        PORT_ArenaRelease (poolp, mark);
00255     if (bulkkey)
00256        PK11_FreeSymKey(bulkkey);
00257 
00258     return SECFailure;
00259 }
00260 
00261 /*
00262  * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
00263  *
00264  * it is essential that this is called before the contentEncAlg is encoded, because
00265  * setting up the encryption may generate IVs and thus change it!
00266  */
00267 SECStatus
00268 NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd)
00269 {
00270     NSSCMSContentInfo *cinfo;
00271     PK11SymKey *bulkkey;
00272     SECAlgorithmID *algid;
00273 
00274     cinfo = &(envd->contentInfo);
00275 
00276     /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */
00277     bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
00278     if (bulkkey == NULL)
00279        return SECFailure;
00280     algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
00281     if (algid == NULL)
00282        return SECFailure;
00283 
00284     /* this may modify algid (with IVs generated in a token).
00285      * it is essential that algid is a pointer to the contentEncAlg data, not a
00286      * pointer to a copy! */
00287     cinfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid);
00288     PK11_FreeSymKey(bulkkey);
00289     if (cinfo->ciphcx == NULL)
00290        return SECFailure;
00291 
00292     return SECSuccess;
00293 }
00294 
00295 /*
00296  * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
00297  */
00298 SECStatus
00299 NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd)
00300 {
00301     if (envd->contentInfo.ciphcx) {
00302        NSS_CMSCipherContext_Destroy(envd->contentInfo.ciphcx);
00303        envd->contentInfo.ciphcx = NULL;
00304     }
00305 
00306     /* nothing else to do after data */
00307     return SECSuccess;
00308 }
00309 
00310 /*
00311  * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, 
00312  * derive bulk key & set up our contentinfo
00313  */
00314 SECStatus
00315 NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd)
00316 {
00317     NSSCMSRecipientInfo *ri;
00318     PK11SymKey *bulkkey = NULL;
00319     SECOidTag bulkalgtag;
00320     SECAlgorithmID *bulkalg;
00321     SECStatus rv = SECFailure;
00322     NSSCMSContentInfo *cinfo;
00323     NSSCMSRecipient **recipient_list = NULL;
00324     NSSCMSRecipient *recipient;
00325     int rlIndex;
00326 
00327     if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) {
00328        PORT_SetError(SEC_ERROR_BAD_DATA);
00329 #if 0
00330        PORT_SetErrorString("No recipient data in envelope.");
00331 #endif
00332        goto loser;
00333     }
00334 
00335     /* look if one of OUR cert's issuerSN is on the list of recipients, and if so,  */
00336     /* get the cert and private key for it right away */
00337     recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
00338     if (recipient_list == NULL)
00339        goto loser;
00340 
00341     /* what about multiple recipientInfos that match?
00342      * especially if, for some reason, we could not produce a bulk key with the first match?!
00343      * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
00344      * maybe later... */
00345     rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg);
00346 
00347     /* if that fails, then we're not an intended recipient and cannot decrypt */
00348     if (rlIndex < 0) {
00349        PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
00350 #if 0
00351        PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
00352 #endif
00353        goto loser;
00354     }
00355 
00356     recipient = recipient_list[rlIndex];
00357     if (!recipient->cert || !recipient->privkey) {
00358        /* XXX should set an error code ?!? */
00359        goto loser;
00360     }
00361     /* get a pointer to "our" recipientinfo */
00362     ri = envd->recipientInfos[recipient->riIndex];
00363 
00364     cinfo = &(envd->contentInfo);
00365     bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
00366     if (bulkalgtag == SEC_OID_UNKNOWN) {
00367        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
00368     } else 
00369        bulkkey = 
00370            NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex,
00371                                               recipient->cert,
00372                                               recipient->privkey,
00373                                               bulkalgtag);
00374     if (bulkkey == NULL) {
00375        /* no success finding a bulk key */
00376        goto loser;
00377     }
00378 
00379     NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
00380 
00381     bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
00382 
00383     cinfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
00384     if (cinfo->ciphcx == NULL)
00385        goto loser;          /* error has been set by NSS_CMSCipherContext_StartDecrypt */
00386 
00387 
00388     rv = SECSuccess;
00389 
00390 loser:
00391     if (bulkkey)
00392        PK11_FreeSymKey(bulkkey);
00393     if (recipient_list != NULL)
00394        nss_cms_recipient_list_destroy(recipient_list);
00395     return rv;
00396 }
00397 
00398 /*
00399  * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
00400  */
00401 SECStatus
00402 NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd)
00403 {
00404     if (envd && envd->contentInfo.ciphcx) {
00405        NSS_CMSCipherContext_Destroy(envd->contentInfo.ciphcx);
00406        envd->contentInfo.ciphcx = NULL;
00407     }
00408 
00409     return SECSuccess;
00410 }
00411 
00412 /*
00413  * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
00414  */
00415 SECStatus
00416 NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd)
00417 {
00418     /* apply final touches */
00419     return SECSuccess;
00420 }
00421