security/nss/lib/smime/cmsenvdata.c

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /*
     6  * CMS envelopedData methods.
     7  */
     9 #include "cmslocal.h"
    11 #include "cert.h"
    12 #include "key.h"
    13 #include "secasn1.h"
    14 #include "secitem.h"
    15 #include "secoid.h"
    16 #include "pk11func.h"
    17 #include "secerr.h"
    18 #include "secpkcs5.h"
    20 /*
    21  * NSS_CMSEnvelopedData_Create - create an enveloped data message
    22  */
    23 NSSCMSEnvelopedData *
    24 NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
    25 {
    26     void *mark;
    27     NSSCMSEnvelopedData *envd;
    28     PLArenaPool *poolp;
    29     SECStatus rv;
    31     poolp = cmsg->poolp;
    33     mark = PORT_ArenaMark(poolp);
    35     envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData));
    36     if (envd == NULL)
    37 	goto loser;
    39     envd->cmsg = cmsg;
    41     /* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */
    43     rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), algorithm, NULL, keysize);
    44     if (rv != SECSuccess)
    45 	goto loser;
    47     PORT_ArenaUnmark(poolp, mark);
    48     return envd;
    50 loser:
    51     PORT_ArenaRelease(poolp, mark);
    52     return NULL;
    53 }
    55 /*
    56  * NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
    57  */
    58 void
    59 NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp)
    60 {
    61     NSSCMSRecipientInfo **recipientinfos;
    62     NSSCMSRecipientInfo *ri;
    64     if (edp == NULL)
    65 	return;
    67     recipientinfos = edp->recipientInfos;
    68     if (recipientinfos == NULL)
    69 	return;
    71     while ((ri = *recipientinfos++) != NULL)
    72 	NSS_CMSRecipientInfo_Destroy(ri);
    74    NSS_CMSContentInfo_Destroy(&(edp->contentInfo));
    76 }
    78 /*
    79  * NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
    80  */
    81 NSSCMSContentInfo *
    82 NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd)
    83 {
    84     return &(envd->contentInfo);
    85 }
    87 /*
    88  * NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg
    89  *
    90  * rip must be created on the same pool as edp - this is not enforced, though.
    91  */
    92 SECStatus
    93 NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip)
    94 {
    95     void *mark;
    96     SECStatus rv;
    98     /* XXX compare pools, if not same, copy rip into edp's pool */
   100     PR_ASSERT(edp != NULL);
   101     PR_ASSERT(rip != NULL);
   103     mark = PORT_ArenaMark(edp->cmsg->poolp);
   105     rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
   106     if (rv != SECSuccess) {
   107 	PORT_ArenaRelease(edp->cmsg->poolp, mark);
   108 	return SECFailure;
   109     }
   111     PORT_ArenaUnmark (edp->cmsg->poolp, mark);
   112     return SECSuccess;
   113 }
   115 /*
   116  * NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding
   117  *
   118  * at this point, we need
   119  * - recipientinfos set up with recipient's certificates
   120  * - a content encryption algorithm (if none, 3DES will be used)
   121  *
   122  * this function will generate a random content encryption key (aka bulk key),
   123  * initialize the recipientinfos with certificate identification and wrap the bulk key
   124  * using the proper algorithm for every certificiate.
   125  * it will finally set the bulk algorithm and key so that the encode step can find it.
   126  */
   127 SECStatus
   128 NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd)
   129 {
   130     int version;
   131     NSSCMSRecipientInfo **recipientinfos;
   132     NSSCMSContentInfo *cinfo;
   133     PK11SymKey *bulkkey = NULL;
   134     SECOidTag bulkalgtag;
   135     CK_MECHANISM_TYPE type;
   136     PK11SlotInfo *slot;
   137     SECStatus rv;
   138     SECItem *dummy;
   139     PLArenaPool *poolp;
   140     extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
   141     void *mark = NULL;
   142     int i;
   144     poolp = envd->cmsg->poolp;
   145     cinfo = &(envd->contentInfo);
   147     recipientinfos = envd->recipientInfos;
   148     if (recipientinfos == NULL) {
   149 	PORT_SetError(SEC_ERROR_BAD_DATA);
   150 #if 0
   151 	PORT_SetErrorString("Cannot find recipientinfos to encode.");
   152 #endif
   153 	goto loser;
   154     }
   156     version = NSS_CMS_ENVELOPED_DATA_VERSION_REG;
   157     if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
   158 	version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
   159     } else {
   160 	for (i = 0; recipientinfos[i] != NULL; i++) {
   161 	    if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) {
   162 		version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
   163 		break;
   164 	    }
   165 	}
   166     }
   167     dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
   168     if (dummy == NULL)
   169 	goto loser;
   171     /* now we need to have a proper content encryption algorithm
   172      * on the SMIME level, we would figure one out by looking at SMIME capabilities
   173      * we cannot do that on our level, so if none is set already, we'll just go
   174      * with one of the mandatory algorithms (3DES) */
   175     if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
   176 	rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168);
   177 	if (rv != SECSuccess)
   178 	    goto loser;
   179 	bulkalgtag = SEC_OID_DES_EDE3_CBC;
   180     } 
   182     /* generate a random bulk key suitable for content encryption alg */
   183     type = PK11_AlgtagToMechanism(bulkalgtag);
   184     slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg);
   185     if (slot == NULL)
   186 	goto loser;	/* error has been set by PK11_GetBestSlot */
   188     /* this is expensive... */
   189     bulkkey = PK11_KeyGen(slot, type, NULL, NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, envd->cmsg->pwfn_arg);
   190     PK11_FreeSlot(slot);
   191     if (bulkkey == NULL)
   192 	goto loser;	/* error has been set by PK11_KeyGen */
   194     mark = PORT_ArenaMark(poolp);
   196     /* Encrypt the bulk key with the public key of each recipient.  */
   197     for (i = 0; recipientinfos[i] != NULL; i++) {
   198 	rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
   199 	if (rv != SECSuccess)
   200 	    goto loser;	/* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */
   201 	    		/* could be: alg not supported etc. */
   202     }
   204     /* the recipientinfos are all finished. now sort them by DER for SET OF encoding */
   205     rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, NSSCMSRecipientInfoTemplate, NULL);
   206     if (rv != SECSuccess)
   207 	goto loser;	/* error has been set by NSS_CMSArray_SortByDER */
   209     /* store the bulk key in the contentInfo so that the encoder can find it */
   210     NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
   212     PORT_ArenaUnmark(poolp, mark);
   214     PK11_FreeSymKey(bulkkey);
   216     return SECSuccess;
   218 loser:
   219     if (mark != NULL)
   220 	PORT_ArenaRelease (poolp, mark);
   221     if (bulkkey)
   222 	PK11_FreeSymKey(bulkkey);
   224     return SECFailure;
   225 }
   227 /*
   228  * NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
   229  *
   230  * it is essential that this is called before the contentEncAlg is encoded, because
   231  * setting up the encryption may generate IVs and thus change it!
   232  */
   233 SECStatus
   234 NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd)
   235 {
   236     NSSCMSContentInfo *cinfo;
   237     PK11SymKey *bulkkey;
   238     SECAlgorithmID *algid;
   239     SECStatus rv;
   241     cinfo = &(envd->contentInfo);
   243     /* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */
   244     bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
   245     if (bulkkey == NULL)
   246 	return SECFailure;
   247     algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
   248     if (algid == NULL)
   249 	return SECFailure;
   251     rv = NSS_CMSContentInfo_Private_Init(cinfo);
   252     if (rv != SECSuccess) {
   253 	return SECFailure;
   254     }
   255     /* this may modify algid (with IVs generated in a token).
   256      * it is essential that algid is a pointer to the contentEncAlg data, not a
   257      * pointer to a copy! */
   258     cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid);
   259     PK11_FreeSymKey(bulkkey);
   260     if (cinfo->privateInfo->ciphcx == NULL)
   261 	return SECFailure;
   263     return SECSuccess;
   264 }
   266 /*
   267  * NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
   268  */
   269 SECStatus
   270 NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd)
   271 {
   272     if (envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) {
   273 	NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx);
   274 	envd->contentInfo.privateInfo->ciphcx = NULL;
   275     }
   277     /* nothing else to do after data */
   278     return SECSuccess;
   279 }
   281 /*
   282  * NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo, 
   283  * derive bulk key & set up our contentinfo
   284  */
   285 SECStatus
   286 NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd)
   287 {
   288     NSSCMSRecipientInfo *ri;
   289     PK11SymKey *bulkkey = NULL;
   290     SECOidTag bulkalgtag;
   291     SECAlgorithmID *bulkalg;
   292     SECStatus rv = SECFailure;
   293     NSSCMSContentInfo *cinfo;
   294     NSSCMSRecipient **recipient_list = NULL;
   295     NSSCMSRecipient *recipient;
   296     int rlIndex;
   298     if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) {
   299 	PORT_SetError(SEC_ERROR_BAD_DATA);
   300 #if 0
   301 	PORT_SetErrorString("No recipient data in envelope.");
   302 #endif
   303 	goto loser;
   304     }
   306     /* look if one of OUR cert's issuerSN is on the list of recipients, and if so,  */
   307     /* get the cert and private key for it right away */
   308     recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
   309     if (recipient_list == NULL)
   310 	goto loser;
   312     /* what about multiple recipientInfos that match?
   313      * especially if, for some reason, we could not produce a bulk key with the first match?!
   314      * we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
   315      * maybe later... */
   316     rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg);
   318     /* if that fails, then we're not an intended recipient and cannot decrypt */
   319     if (rlIndex < 0) {
   320 	PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
   321 #if 0
   322 	PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
   323 #endif
   324 	goto loser;
   325     }
   327     recipient = recipient_list[rlIndex];
   328     if (!recipient->cert || !recipient->privkey) {
   329 	/* XXX should set an error code ?!? */
   330 	goto loser;
   331     }
   332     /* get a pointer to "our" recipientinfo */
   333     ri = envd->recipientInfos[recipient->riIndex];
   335     cinfo = &(envd->contentInfo);
   336     bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
   337     if (bulkalgtag == SEC_OID_UNKNOWN) {
   338 	PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   339     } else 
   340 	bulkkey = 
   341 	    NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex,
   342 						    recipient->cert,
   343 						    recipient->privkey,
   344 						    bulkalgtag);
   345     if (bulkkey == NULL) {
   346 	/* no success finding a bulk key */
   347 	goto loser;
   348     }
   350     NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
   352     bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
   354     rv = NSS_CMSContentInfo_Private_Init(cinfo);
   355     if (rv != SECSuccess) {
   356 	goto loser;
   357     }
   358     rv = SECFailure;
   359     cinfo->privateInfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
   360     if (cinfo->privateInfo->ciphcx == NULL)
   361 	goto loser;		/* error has been set by NSS_CMSCipherContext_StartDecrypt */
   364     rv = SECSuccess;
   366 loser:
   367     if (bulkkey)
   368 	PK11_FreeSymKey(bulkkey);
   369     if (recipient_list != NULL)
   370 	nss_cms_recipient_list_destroy(recipient_list);
   371     return rv;
   372 }
   374 /*
   375  * NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
   376  */
   377 SECStatus
   378 NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd)
   379 {
   380     if (envd && envd->contentInfo.privateInfo && envd->contentInfo.privateInfo->ciphcx) {
   381 	NSS_CMSCipherContext_Destroy(envd->contentInfo.privateInfo->ciphcx);
   382 	envd->contentInfo.privateInfo->ciphcx = NULL;
   383     }
   385     return SECSuccess;
   386 }
   388 /*
   389  * NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
   390  */
   391 SECStatus
   392 NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd)
   393 {
   394     /* apply final touches */
   395     return SECSuccess;
   396 }

mercurial