mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 16:46:26 +00:00
422 lines
12 KiB
C
422 lines
12 KiB
C
/* ***** 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):
|
|
*
|
|
* 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 ***** */
|
|
|
|
/*
|
|
* CMS envelopedData methods.
|
|
*
|
|
* $Id: cmsenvdata.c,v 1.11 2005/10/03 22:01:57 relyea%netscape.com Exp $
|
|
*/
|
|
|
|
#include "cmslocal.h"
|
|
|
|
#include "cert.h"
|
|
#include "key.h"
|
|
#include "secasn1.h"
|
|
#include "secitem.h"
|
|
#include "secoid.h"
|
|
#include "pk11func.h"
|
|
#include "secerr.h"
|
|
#include "secpkcs5.h"
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Create - create an enveloped data message
|
|
*/
|
|
NSSCMSEnvelopedData *
|
|
NSS_CMSEnvelopedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
|
|
{
|
|
void *mark;
|
|
NSSCMSEnvelopedData *envd;
|
|
PLArenaPool *poolp;
|
|
SECStatus rv;
|
|
|
|
poolp = cmsg->poolp;
|
|
|
|
mark = PORT_ArenaMark(poolp);
|
|
|
|
envd = (NSSCMSEnvelopedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEnvelopedData));
|
|
if (envd == NULL)
|
|
goto loser;
|
|
|
|
envd->cmsg = cmsg;
|
|
|
|
/* version is set in NSS_CMSEnvelopedData_Encode_BeforeStart() */
|
|
|
|
rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(envd->contentInfo), algorithm, NULL, keysize);
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
|
|
PORT_ArenaUnmark(poolp, mark);
|
|
return envd;
|
|
|
|
loser:
|
|
PORT_ArenaRelease(poolp, mark);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Destroy - destroy an enveloped data message
|
|
*/
|
|
void
|
|
NSS_CMSEnvelopedData_Destroy(NSSCMSEnvelopedData *edp)
|
|
{
|
|
NSSCMSRecipientInfo **recipientinfos;
|
|
NSSCMSRecipientInfo *ri;
|
|
|
|
if (edp == NULL)
|
|
return;
|
|
|
|
recipientinfos = edp->recipientInfos;
|
|
if (recipientinfos == NULL)
|
|
return;
|
|
|
|
while ((ri = *recipientinfos++) != NULL)
|
|
NSS_CMSRecipientInfo_Destroy(ri);
|
|
|
|
NSS_CMSContentInfo_Destroy(&(edp->contentInfo));
|
|
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_GetContentInfo - return pointer to this envelopedData's contentinfo
|
|
*/
|
|
NSSCMSContentInfo *
|
|
NSS_CMSEnvelopedData_GetContentInfo(NSSCMSEnvelopedData *envd)
|
|
{
|
|
return &(envd->contentInfo);
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_AddRecipient - add a recipientinfo to the enveloped data msg
|
|
*
|
|
* rip must be created on the same pool as edp - this is not enforced, though.
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_AddRecipient(NSSCMSEnvelopedData *edp, NSSCMSRecipientInfo *rip)
|
|
{
|
|
void *mark;
|
|
SECStatus rv;
|
|
|
|
/* XXX compare pools, if not same, copy rip into edp's pool */
|
|
|
|
PR_ASSERT(edp != NULL);
|
|
PR_ASSERT(rip != NULL);
|
|
|
|
mark = PORT_ArenaMark(edp->cmsg->poolp);
|
|
|
|
rv = NSS_CMSArray_Add(edp->cmsg->poolp, (void ***)&(edp->recipientInfos), (void *)rip);
|
|
if (rv != SECSuccess) {
|
|
PORT_ArenaRelease(edp->cmsg->poolp, mark);
|
|
return SECFailure;
|
|
}
|
|
|
|
PORT_ArenaUnmark (edp->cmsg->poolp, mark);
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Encode_BeforeStart - prepare this envelopedData for encoding
|
|
*
|
|
* at this point, we need
|
|
* - recipientinfos set up with recipient's certificates
|
|
* - a content encryption algorithm (if none, 3DES will be used)
|
|
*
|
|
* this function will generate a random content encryption key (aka bulk key),
|
|
* initialize the recipientinfos with certificate identification and wrap the bulk key
|
|
* using the proper algorithm for every certificiate.
|
|
* it will finally set the bulk algorithm and key so that the encode step can find it.
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_Encode_BeforeStart(NSSCMSEnvelopedData *envd)
|
|
{
|
|
int version;
|
|
NSSCMSRecipientInfo **recipientinfos;
|
|
NSSCMSContentInfo *cinfo;
|
|
PK11SymKey *bulkkey = NULL;
|
|
SECOidTag bulkalgtag;
|
|
CK_MECHANISM_TYPE type;
|
|
PK11SlotInfo *slot;
|
|
SECStatus rv;
|
|
SECItem *dummy;
|
|
PLArenaPool *poolp;
|
|
extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
|
|
void *mark = NULL;
|
|
int i;
|
|
|
|
poolp = envd->cmsg->poolp;
|
|
cinfo = &(envd->contentInfo);
|
|
|
|
recipientinfos = envd->recipientInfos;
|
|
if (recipientinfos == NULL) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATA);
|
|
#if 0
|
|
PORT_SetErrorString("Cannot find recipientinfos to encode.");
|
|
#endif
|
|
goto loser;
|
|
}
|
|
|
|
version = NSS_CMS_ENVELOPED_DATA_VERSION_REG;
|
|
if (envd->originatorInfo != NULL || envd->unprotectedAttr != NULL) {
|
|
version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
|
|
} else {
|
|
for (i = 0; recipientinfos[i] != NULL; i++) {
|
|
if (NSS_CMSRecipientInfo_GetVersion(recipientinfos[i]) != 0) {
|
|
version = NSS_CMS_ENVELOPED_DATA_VERSION_ADV;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
dummy = SEC_ASN1EncodeInteger(poolp, &(envd->version), version);
|
|
if (dummy == NULL)
|
|
goto loser;
|
|
|
|
/* now we need to have a proper content encryption algorithm
|
|
* on the SMIME level, we would figure one out by looking at SMIME capabilities
|
|
* we cannot do that on our level, so if none is set already, we'll just go
|
|
* with one of the mandatory algorithms (3DES) */
|
|
if ((bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo)) == SEC_OID_UNKNOWN) {
|
|
rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, cinfo, SEC_OID_DES_EDE3_CBC, NULL, 168);
|
|
if (rv != SECSuccess)
|
|
goto loser;
|
|
bulkalgtag = SEC_OID_DES_EDE3_CBC;
|
|
}
|
|
|
|
/* generate a random bulk key suitable for content encryption alg */
|
|
type = PK11_AlgtagToMechanism(bulkalgtag);
|
|
slot = PK11_GetBestSlot(type, envd->cmsg->pwfn_arg);
|
|
if (slot == NULL)
|
|
goto loser; /* error has been set by PK11_GetBestSlot */
|
|
|
|
/* this is expensive... */
|
|
bulkkey = PK11_KeyGen(slot, type, NULL, NSS_CMSContentInfo_GetBulkKeySize(cinfo) / 8, envd->cmsg->pwfn_arg);
|
|
PK11_FreeSlot(slot);
|
|
if (bulkkey == NULL)
|
|
goto loser; /* error has been set by PK11_KeyGen */
|
|
|
|
mark = PORT_ArenaMark(poolp);
|
|
|
|
/* Encrypt the bulk key with the public key of each recipient. */
|
|
for (i = 0; recipientinfos[i] != NULL; i++) {
|
|
rv = NSS_CMSRecipientInfo_WrapBulkKey(recipientinfos[i], bulkkey, bulkalgtag);
|
|
if (rv != SECSuccess)
|
|
goto loser; /* error has been set by NSS_CMSRecipientInfo_EncryptBulkKey */
|
|
/* could be: alg not supported etc. */
|
|
}
|
|
|
|
/* the recipientinfos are all finished. now sort them by DER for SET OF encoding */
|
|
rv = NSS_CMSArray_SortByDER((void **)envd->recipientInfos, NSSCMSRecipientInfoTemplate, NULL);
|
|
if (rv != SECSuccess)
|
|
goto loser; /* error has been set by NSS_CMSArray_SortByDER */
|
|
|
|
/* store the bulk key in the contentInfo so that the encoder can find it */
|
|
NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
|
|
|
|
PORT_ArenaUnmark(poolp, mark);
|
|
|
|
PK11_FreeSymKey(bulkkey);
|
|
|
|
return SECSuccess;
|
|
|
|
loser:
|
|
if (mark != NULL)
|
|
PORT_ArenaRelease (poolp, mark);
|
|
if (bulkkey)
|
|
PK11_FreeSymKey(bulkkey);
|
|
|
|
return SECFailure;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Encode_BeforeData - set up encryption
|
|
*
|
|
* it is essential that this is called before the contentEncAlg is encoded, because
|
|
* setting up the encryption may generate IVs and thus change it!
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_Encode_BeforeData(NSSCMSEnvelopedData *envd)
|
|
{
|
|
NSSCMSContentInfo *cinfo;
|
|
PK11SymKey *bulkkey;
|
|
SECAlgorithmID *algid;
|
|
|
|
cinfo = &(envd->contentInfo);
|
|
|
|
/* find bulkkey and algorithm - must have been set by NSS_CMSEnvelopedData_Encode_BeforeStart */
|
|
bulkkey = NSS_CMSContentInfo_GetBulkKey(cinfo);
|
|
if (bulkkey == NULL)
|
|
return SECFailure;
|
|
algid = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
|
|
if (algid == NULL)
|
|
return SECFailure;
|
|
|
|
/* this may modify algid (with IVs generated in a token).
|
|
* it is essential that algid is a pointer to the contentEncAlg data, not a
|
|
* pointer to a copy! */
|
|
cinfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(envd->cmsg->poolp, bulkkey, algid);
|
|
PK11_FreeSymKey(bulkkey);
|
|
if (cinfo->ciphcx == NULL)
|
|
return SECFailure;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Encode_AfterData - finalize this envelopedData for encoding
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_Encode_AfterData(NSSCMSEnvelopedData *envd)
|
|
{
|
|
if (envd->contentInfo.ciphcx) {
|
|
NSS_CMSCipherContext_Destroy(envd->contentInfo.ciphcx);
|
|
envd->contentInfo.ciphcx = NULL;
|
|
}
|
|
|
|
/* nothing else to do after data */
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Decode_BeforeData - find our recipientinfo,
|
|
* derive bulk key & set up our contentinfo
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_Decode_BeforeData(NSSCMSEnvelopedData *envd)
|
|
{
|
|
NSSCMSRecipientInfo *ri;
|
|
PK11SymKey *bulkkey = NULL;
|
|
SECOidTag bulkalgtag;
|
|
SECAlgorithmID *bulkalg;
|
|
SECStatus rv = SECFailure;
|
|
NSSCMSContentInfo *cinfo;
|
|
NSSCMSRecipient **recipient_list = NULL;
|
|
NSSCMSRecipient *recipient;
|
|
int rlIndex;
|
|
|
|
if (NSS_CMSArray_Count((void **)envd->recipientInfos) == 0) {
|
|
PORT_SetError(SEC_ERROR_BAD_DATA);
|
|
#if 0
|
|
PORT_SetErrorString("No recipient data in envelope.");
|
|
#endif
|
|
goto loser;
|
|
}
|
|
|
|
/* look if one of OUR cert's issuerSN is on the list of recipients, and if so, */
|
|
/* get the cert and private key for it right away */
|
|
recipient_list = nss_cms_recipient_list_create(envd->recipientInfos);
|
|
if (recipient_list == NULL)
|
|
goto loser;
|
|
|
|
/* what about multiple recipientInfos that match?
|
|
* especially if, for some reason, we could not produce a bulk key with the first match?!
|
|
* we could loop & feed partial recipient_list to PK11_FindCertAndKeyByRecipientList...
|
|
* maybe later... */
|
|
rlIndex = PK11_FindCertAndKeyByRecipientListNew(recipient_list, envd->cmsg->pwfn_arg);
|
|
|
|
/* if that fails, then we're not an intended recipient and cannot decrypt */
|
|
if (rlIndex < 0) {
|
|
PORT_SetError(SEC_ERROR_NOT_A_RECIPIENT);
|
|
#if 0
|
|
PORT_SetErrorString("Cannot decrypt data because proper key cannot be found.");
|
|
#endif
|
|
goto loser;
|
|
}
|
|
|
|
recipient = recipient_list[rlIndex];
|
|
if (!recipient->cert || !recipient->privkey) {
|
|
/* XXX should set an error code ?!? */
|
|
goto loser;
|
|
}
|
|
/* get a pointer to "our" recipientinfo */
|
|
ri = envd->recipientInfos[recipient->riIndex];
|
|
|
|
cinfo = &(envd->contentInfo);
|
|
bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
|
|
if (bulkalgtag == SEC_OID_UNKNOWN) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
|
|
} else
|
|
bulkkey =
|
|
NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient->subIndex,
|
|
recipient->cert,
|
|
recipient->privkey,
|
|
bulkalgtag);
|
|
if (bulkkey == NULL) {
|
|
/* no success finding a bulk key */
|
|
goto loser;
|
|
}
|
|
|
|
NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
|
|
|
|
bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
|
|
|
|
cinfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
|
|
if (cinfo->ciphcx == NULL)
|
|
goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */
|
|
|
|
|
|
rv = SECSuccess;
|
|
|
|
loser:
|
|
if (bulkkey)
|
|
PK11_FreeSymKey(bulkkey);
|
|
if (recipient_list != NULL)
|
|
nss_cms_recipient_list_destroy(recipient_list);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Decode_AfterData - finish decrypting this envelopedData's content
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_Decode_AfterData(NSSCMSEnvelopedData *envd)
|
|
{
|
|
if (envd && envd->contentInfo.ciphcx) {
|
|
NSS_CMSCipherContext_Destroy(envd->contentInfo.ciphcx);
|
|
envd->contentInfo.ciphcx = NULL;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* NSS_CMSEnvelopedData_Decode_AfterEnd - finish decoding this envelopedData
|
|
*/
|
|
SECStatus
|
|
NSS_CMSEnvelopedData_Decode_AfterEnd(NSSCMSEnvelopedData *envd)
|
|
{
|
|
/* apply final touches */
|
|
return SECSuccess;
|
|
}
|
|
|