Merge smimetk_branch to tip...

git-svn-id: svn://10.0.0.236/trunk@72136 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
chrisk%netscape.com 2000-06-13 21:56:37 +00:00
parent aee8ba11d6
commit c97a690adc
44 changed files with 13104 additions and 35 deletions

View File

@ -60,6 +60,7 @@ DIRS = lib \
strsclnt \
swfort \
tstclnt \
smimetools \
$(NULL)
TEMPORARILY_DONT_BUILD = \

View File

@ -43,6 +43,7 @@ ifdef MOZILLA_BSAFE_BUILD
endif
EXTRA_LIBS += \
$(DIST)/lib/smime.lib \
$(DIST)/lib/ssl.lib \
$(DIST)/lib/jar.lib \
$(DIST)/lib/zlib.lib \
@ -81,6 +82,7 @@ ifdef MOZILLA_BSAFE_BUILD
CRYPTOLIB=$(DIST)/lib/libbsafe.a
endif
EXTRA_LIBS += \
$(DIST)/lib/libsmime.a \
$(DIST)/lib/libssl.a \
$(DIST)/lib/libjar.a \
$(DIST)/lib/libzlib.a \

View File

@ -0,0 +1,73 @@
#! gmake
#
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
#######################################################################
# (1) Include initial platform-independent assignments (MANDATORY). #
#######################################################################
include manifest.mn
#######################################################################
# (2) Include "global" configuration information. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/config.mk
#######################################################################
# (3) Include "component" configuration information. (OPTIONAL) #
#######################################################################
#######################################################################
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
#######################################################################
include ../platlibs.mk
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
# (6) Execute "component" rules. (OPTIONAL) #
#######################################################################
#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################
include rules.mk
include ../platrules.mk

View File

@ -0,0 +1,841 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* cmsutil -- A command to work with CMS data
*
* $Id: cmsutil.c,v 1.2 2000-06-13 21:56:15 chrisk%netscape.com Exp $
*/
#include "nspr.h"
#include "secutil.h"
#include "plgetopt.h"
#include "secpkcs7.h"
#include "cert.h"
#include "certdb.h"
#include "cdbhdl.h"
#include "secoid.h"
#include "cms.h"
#include "smime.h"
#if defined(XP_UNIX)
#include <unistd.h>
#endif
#include <stdio.h>
#include <string.h>
extern void SEC_Init(void); /* XXX */
static SECStatus
DigestFile(PLArenaPool *poolp, SECItem ***digests, FILE *inFile, SECAlgorithmID **algids)
{
NSSCMSDigestContext *digcx;
int nb;
char ibuf[4096];
SECStatus rv;
digcx = NSS_CMSDigestContext_StartMultiple(algids);
if (digcx == NULL)
return SECFailure;
for (;;) {
if (feof(inFile)) break;
nb = fread(ibuf, 1, sizeof(ibuf), inFile);
if (nb == 0) {
if (ferror(inFile)) {
PORT_SetError(SEC_ERROR_IO);
NSS_CMSDigestContext_Cancel(digcx);
return SECFailure;
}
/* eof */
break;
}
NSS_CMSDigestContext_Update(digcx, (const unsigned char *)ibuf, nb);
}
rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
return rv;
}
static void
Usage(char *progName)
{
fprintf(stderr, "Usage: %s [-D|-S|-E] [<options>] [-d dbdir] [-u certusage]\n", progName);
fprintf(stderr, " -i infile use infile as source of data (default: stdin)\n");
fprintf(stderr, " -o outfile use outfile as destination of data (default: stdout)\n");
fprintf(stderr, " -d dbdir key/cert database directory (default: ~/.netscape)\n");
fprintf(stderr, " -p password use password as key db password (default: prompt)\n");
fprintf(stderr, " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n");
fprintf(stderr, "\n");
fprintf(stderr, " -D decode a CMS message\n");
fprintf(stderr, " -c content use this detached content\n");
fprintf(stderr, " -n suppress output of content\n");
fprintf(stderr, " -h num generate email headers with info about CMS message\n");
fprintf(stderr, " -S create a CMS signed message\n");
fprintf(stderr, " -N nick use certificate named \"nick\" for signing\n");
fprintf(stderr, " -T do not include content in CMS message\n");
fprintf(stderr, " -G include a signing time attribute\n");
fprintf(stderr, " -P include a SMIMECapabilities attribute\n");
fprintf(stderr, " -Y nick include a EncryptionKeyPreference attribute with cert\n");
fprintf(stderr, " -E create a CMS enveloped message (NYI)\n");
fprintf(stderr, " -r id,... create envelope for these recipients,\n");
fprintf(stderr, " where id can be a certificate nickname or email address\n");
fprintf(stderr, "\nCert usage codes:\n");
fprintf(stderr, "%-25s 0 - certUsageSSLClient\n", " ");
fprintf(stderr, "%-25s 1 - certUsageSSLServer\n", " ");
fprintf(stderr, "%-25s 2 - certUsageSSLServerWithStepUp\n", " ");
fprintf(stderr, "%-25s 3 - certUsageSSLCA\n", " ");
fprintf(stderr, "%-25s 4 - certUsageEmailSigner\n", " ");
fprintf(stderr, "%-25s 5 - certUsageEmailRecipient\n", " ");
fprintf(stderr, "%-25s 6 - certUsageObjectSigner\n", " ");
fprintf(stderr, "%-25s 7 - certUsageUserCertImport\n", " ");
fprintf(stderr, "%-25s 8 - certUsageVerifyCA\n", " ");
fprintf(stderr, "%-25s 9 - certUsageProtectedObjectSigner\n", " ");
fprintf(stderr, "%-25s 10 - certUsageStatusResponder\n", " ");
fprintf(stderr, "%-25s 11 - certUsageAnyCA\n", " ");
exit(-1);
}
static CERTCertDBHandle certHandleStatic; /* avoid having to allocate */
static CERTCertDBHandle *
OpenCertDB(char *progName)
{
CERTCertDBHandle *certHandle;
SECStatus rv;
certHandle = &certHandleStatic;
rv = CERT_OpenCertDB(certHandle, PR_FALSE, SECU_CertDBNameCallback, NULL);
if (rv != SECSuccess) {
SECU_PrintError(progName, "could not open cert database");
return NULL;
}
return certHandle;
}
char *
ownpw(PK11SlotInfo *info, PRBool retry, void *arg)
{
char * passwd = NULL;
if ( (!retry) && arg ) {
passwd = PL_strdup((char *)arg);
}
return passwd;
}
struct optionsStr {
char *password;
SECCertUsage certUsage;
CERTCertDBHandle *certHandle;
};
struct decodeOptionsStr {
FILE *contentFile;
int headerLevel;
PRBool suppressContent;
};
struct signOptionsStr {
char *nickname;
char *encryptionKeyPreferenceNick;
PRBool signingTime;
PRBool smimeProfile;
PRBool detached;
};
struct envelopeOptionsStr {
char **recipients;
};
static int
decode(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct decodeOptionsStr decodeOptions)
{
NSSCMSDecoderContext *dcx;
NSSCMSMessage *cmsg;
NSSCMSContentInfo *cinfo;
NSSCMSSignedData *sigd = NULL;
NSSCMSEnvelopedData *envd;
SECAlgorithmID **digestalgs;
unsigned char buffer[32];
int nlevels, i, nsigners, j;
char *signercn;
NSSCMSSignerInfo *si;
SECOidTag typetag;
SECItem **digests;
PLArenaPool *poolp;
int nb;
char ibuf[4096];
PK11PasswordFunc pwcb;
void *pwcb_arg;
SECItem *item;
pwcb = (options.password != NULL) ? ownpw : NULL;
pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
dcx = NSS_CMSDecoder_Start(NULL,
NULL, NULL, /* content callback */
pwcb, pwcb_arg, /* password callback */
NULL, NULL); /* decrypt key callback */
for (;;) {
if (feof(infile)) break;
nb = fread(ibuf, 1, sizeof(ibuf), infile);
if (nb == 0) {
if (ferror(infile)) {
fprintf(stderr, "ERROR: file i/o error.\n");
NSS_CMSDecoder_Cancel(dcx);
return SECFailure;
}
/* eof */
break;
}
(void)NSS_CMSDecoder_Update(dcx, (const char *)ibuf, nb);
}
cmsg = NSS_CMSDecoder_Finish(dcx);
if (cmsg == NULL)
return -1;
if (decodeOptions.headerLevel >= 0) {
fprintf(out, "SMIME: ", decodeOptions.headerLevel, i);
}
nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
for (i = 0; i < nlevels; i++) {
cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
if (decodeOptions.headerLevel >= 0)
fprintf(out, "\tlevel=%d.%d; ", decodeOptions.headerLevel, nlevels - i);
switch (typetag) {
case SEC_OID_PKCS7_SIGNED_DATA:
if (decodeOptions.headerLevel >= 0)
fprintf(out, "type=signedData; ");
sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
if (sigd == NULL) {
SECU_PrintError(progName, "problem finding signedData component");
return -1;
}
/* if we have a content file, but no digests for this signedData */
if (decodeOptions.contentFile != NULL && !NSS_CMSSignedData_HasDigests(sigd)) {
if ((poolp = PORT_NewArena(1024)) == NULL) {
fprintf(stderr, "Out of memory.\n");
return -1;
}
digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
if (DigestFile (poolp, &digests, decodeOptions.contentFile, digestalgs) != SECSuccess) {
SECU_PrintError(progName, "problem computing message digest");
return -1;
}
if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests) != SECSuccess) {
SECU_PrintError(progName, "problem setting message digests");
return -1;
}
PORT_FreeArena(poolp, PR_FALSE);
}
/* still no digests? */
if (!NSS_CMSSignedData_HasDigests(sigd)) {
SECU_PrintError(progName, "no message digests");
return -1;
}
/* find out about signers */
nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
if (decodeOptions.headerLevel >= 0)
fprintf(out, "nsigners=%d; ", nsigners);
if (nsigners == 0) {
/* must be a cert transport message */
} else {
/* import the certificates */
if (NSS_CMSSignedData_ImportCerts(sigd, options.certHandle, options.certUsage, PR_FALSE) != SECSuccess) {
SECU_PrintError(progName, "cert import failed");
return -1;
}
for (j = 0; j < nsigners; j++) {
si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
if (signercn == NULL)
signercn = "";
if (decodeOptions.headerLevel >= 0)
fprintf(out, "\n\t\tsigner%d.id=\"%s\"; ", j, signercn);
(void)NSS_CMSSignedData_VerifySignerInfo(sigd, j, options.certHandle, options.certUsage);
if (decodeOptions.headerLevel >= 0)
fprintf(out, "signer%d.status=%s; ", j, NSS_CMSUtil_VerificationStatusToString(NSS_CMSSignerInfo_GetVerificationStatus(si)));
/* XXX what do we do if we don't print headers? */
}
}
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
if (decodeOptions.headerLevel >= 0)
fprintf(out, "type=envelopedData; ");
envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
break;
case SEC_OID_PKCS7_DATA:
if (decodeOptions.headerLevel >= 0)
fprintf(out, "type=data; ");
break;
default:
break;
}
if (decodeOptions.headerLevel >= 0)
fprintf(out, "\n");
}
if (!decodeOptions.suppressContent) {
/* XXX only if we do not have detached content... */
if ((item = NSS_CMSMessage_GetContent(cmsg)) != NULL) {
fwrite(item->data, item->len, 1, out);
}
}
NSS_CMSMessage_Destroy(cmsg);
return 0;
}
static void
writeout(void *arg, const char *buf, unsigned long len)
{
FILE *f = (FILE *)arg;
if (f != NULL && buf != NULL)
(void)fwrite(buf, len, 1, f);
}
static int
sign(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct signOptionsStr signOptions)
{
NSSCMSEncoderContext *ecx;
NSSCMSMessage *cmsg;
NSSCMSContentInfo *cinfo;
NSSCMSSignedData *sigd;
NSSCMSSignerInfo *signerinfo;
int nb;
char ibuf[4096];
PK11PasswordFunc pwcb;
void *pwcb_arg;
CERTCertificate *cert;
if (signOptions.nickname == NULL) {
fprintf(stderr, "ERROR: please indicate the nickname of a certificate to sign with.\n");
return SECFailure;
}
if ((cert = CERT_FindCertByNickname(options.certHandle, signOptions.nickname)) == NULL) {
SECU_PrintError(progName, "the corresponding cert for key \"%s\" does not exist",
signOptions.nickname);
return SECFailure;
}
/*
* create the message object
*/
cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
if (cmsg == NULL) {
fprintf(stderr, "ERROR: cannot create CMS message.\n");
return SECFailure;
}
/*
* build chain of objects: message->signedData->data
*/
if ((sigd = NSS_CMSSignedData_Create(cmsg)) == NULL) {
fprintf(stderr, "ERROR: cannot create CMS signedData object.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) != SECSuccess) {
fprintf(stderr, "ERROR: cannot attach CMS signedData object.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
cinfo = NSS_CMSSignedData_GetContentInfo(sigd);
/* we're always passing data in and detaching optionally */
if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, signOptions.detached) != SECSuccess) {
fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
/*
* create & attach signer information
*/
if ((signerinfo = NSS_CMSSignerInfo_Create(cmsg, cert, SEC_OID_SHA1)) == NULL) {
fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
/* we want the cert chain included for this one */
if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, options.certUsage) != SECSuccess) {
fprintf(stderr, "ERROR: cannot find cert chain.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
if (signOptions.signingTime) {
if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) {
fprintf(stderr, "ERROR: cannot create CMS signerInfo object.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
}
if (signOptions.smimeProfile) {
/* TBD */
}
if (signOptions.encryptionKeyPreferenceNick) {
/* TBD */
/* get the cert, add it to the message */
}
if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) {
fprintf(stderr, "ERROR: cannot add CMS signerInfo object.\n");
NSS_CMSMessage_Destroy(cmsg);
return SECFailure;
}
/*
* do not add signer independent certificates
*/
pwcb = (options.password != NULL) ? ownpw : NULL;
pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
ecx = NSS_CMSEncoder_Start(cmsg,
writeout, out, /* DER output callback */
NULL, NULL, /* destination storage */
pwcb, pwcb_arg, /* password callback */
NULL, NULL, /* decrypt key callback */
NULL, NULL); /* detached digests (not used, we feed) */
for (;;) {
if (feof(infile)) break;
nb = fread(ibuf, 1, sizeof(ibuf), infile);
if (nb == 0) {
if (ferror(infile)) {
fprintf(stderr, "ERROR: file i/o error.\n");
NSS_CMSEncoder_Cancel(ecx);
return SECFailure;
}
/* eof */
break;
}
(void)NSS_CMSEncoder_Update(ecx, (const char *)ibuf, nb);
}
if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
fprintf(stderr, "ERROR: DER encoding problem.\n");
return SECFailure;
}
NSS_CMSMessage_Destroy(cmsg);
return SECSuccess;
}
static int
envelope(FILE *out, FILE *infile, char *progName, struct optionsStr options, struct envelopeOptionsStr envelopeOptions)
{
SECStatus retval = SECFailure;
NSSCMSEncoderContext *ecx;
NSSCMSMessage *cmsg = NULL;
NSSCMSContentInfo *cinfo;
NSSCMSEnvelopedData *envd;
NSSCMSRecipientInfo *recipientinfo;
int cnt;
int nb;
char ibuf[4096];
PK11PasswordFunc pwcb;
void *pwcb_arg;
CERTCertificate **recipientcerts;
PLArenaPool *tmppoolp = NULL;
SECOidTag bulkalgtag;
int keysize, i;
if ((cnt = NSS_CMSArray_Count(envelopeOptions.recipients)) == 0) {
fprintf(stderr, "ERROR: please indicate the nickname of a certificate to sign with.\n");
goto loser;
}
if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
fprintf(stderr, "ERROR: out of memory.\n");
goto loser;
}
/* XXX find the recipient's certs by email address or nickname */
if ((recipientcerts = (CERTCertificate **)NSS_CMSArray_Alloc(tmppoolp, cnt)) == NULL) {
fprintf(stderr, "ERROR: out of memory.\n");
goto loser;
}
for (i=0; envelopeOptions.recipients[i] != NULL; i++) {
if ((recipientcerts[i] = CERT_FindCertByNicknameOrEmailAddr(options.certHandle, envelopeOptions.recipients[i])) == NULL) {
SECU_PrintError(progName, "cannot find certificate for \"%s\"", envelopeOptions.recipients[i]);
goto loser;
}
}
recipientcerts[i] = NULL;
/* find a nice bulk algorithm */
if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientcerts, &bulkalgtag, &keysize) != SECSuccess) {
fprintf(stderr, "ERROR: cannot find common bulk algorithm.\n");
goto loser;
}
/*
* create the message object
*/
cmsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */
if (cmsg == NULL) {
fprintf(stderr, "ERROR: cannot create CMS message.\n");
goto loser;
}
/*
* build chain of objects: message->envelopedData->data
*/
if ((envd = NSS_CMSEnvelopedData_Create(cmsg, bulkalgtag, keysize)) == NULL) {
fprintf(stderr, "ERROR: cannot create CMS envelopedData object.\n");
goto loser;
}
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
if (NSS_CMSContentInfo_SetContent_EnvelopedData(cmsg, cinfo, envd) != SECSuccess) {
fprintf(stderr, "ERROR: cannot attach CMS envelopedData object.\n");
goto loser;
}
cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd);
/* we're always passing data in, so the content is NULL */
if (NSS_CMSContentInfo_SetContent_Data(cmsg, cinfo, NULL, PR_FALSE) != SECSuccess) {
fprintf(stderr, "ERROR: cannot attach CMS data object.\n");
goto loser;
}
/*
* create & attach recipient information
*/
for (i = 0; recipientcerts[i] != NULL; i++) {
if ((recipientinfo = NSS_CMSRecipientInfo_Create(cmsg, recipientcerts[i])) == NULL) {
fprintf(stderr, "ERROR: cannot create CMS recipientInfo object.\n");
goto loser;
}
if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientinfo) != SECSuccess) {
fprintf(stderr, "ERROR: cannot add CMS recipientInfo object.\n");
goto loser;
}
}
/* we might need a password for diffie hellman key agreement, so set it up... */
pwcb = (options.password != NULL) ? ownpw : NULL;
pwcb_arg = (options.password != NULL) ? (void *)options.password : NULL;
ecx = NSS_CMSEncoder_Start(cmsg,
writeout, out, /* DER output callback */
NULL, NULL, /* destination storage */
pwcb, pwcb_arg, /* password callback */
NULL, NULL, /* decrypt key callback (not used) */
NULL, NULL); /* detached digests (not used) */
for (;;) {
if (feof(infile)) break;
nb = fread(ibuf, 1, sizeof(ibuf), infile);
if (nb == 0) {
if (ferror(infile)) {
fprintf(stderr, "ERROR: file i/o error.\n");
NSS_CMSEncoder_Cancel(ecx);
goto loser;
}
/* eof */
break;
}
(void)NSS_CMSEncoder_Update(ecx, (const char *)ibuf, nb);
}
if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
fprintf(stderr, "ERROR: DER encoding problem.\n");
goto loser;
}
retval = SECSuccess;
loser:
if (cmsg)
NSS_CMSMessage_Destroy(cmsg);
if (tmppoolp)
PORT_FreeArena(tmppoolp, PR_FALSE);
return retval;
}
typedef enum { UNKNOWN, DECODE, SIGN, ENCRYPT } Mode;
int
main(int argc, char **argv)
{
char *progName;
FILE *outFile, *inFile;
PLOptState *optstate;
PLOptStatus status;
Mode mode = UNKNOWN;
struct decodeOptionsStr decodeOptions = { 0 };
struct signOptionsStr signOptions = { 0 };
struct envelopeOptionsStr envelopeOptions = { 0 };
struct optionsStr options = { 0 };
int exitstatus;
static char *ptrarray[128] = { 0 };
int nrecipients = 0;
progName = strrchr(argv[0], '/');
progName = progName ? progName+1 : argv[0];
inFile = stdin;
outFile = stdout;
mode = UNKNOWN;
decodeOptions.contentFile = NULL;
decodeOptions.suppressContent = PR_FALSE;
decodeOptions.headerLevel = -1;
options.certUsage = certUsageEmailSigner;
options.password = NULL;
signOptions.nickname = NULL;
signOptions.detached = PR_FALSE;
signOptions.signingTime = PR_FALSE;
signOptions.smimeProfile = PR_FALSE;
signOptions.encryptionKeyPreferenceNick = NULL;
envelopeOptions.recipients = NULL;
/*
* Parse command line arguments
*/
optstate = PL_CreateOptState(argc, argv, "DSEnN:TGPY:h:p:i:c:d:o:s:u:r:");
while ((status = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case '?':
Usage(progName);
break;
case 'D':
mode = DECODE;
break;
case 'S':
mode = SIGN;
break;
case 'E':
mode = ENCRYPT;
break;
case 'n':
if (mode != DECODE) {
fprintf(stderr, "%s: option -n only supported with option -D.\n", progName);
Usage(progName);
exit(1);
}
decodeOptions.suppressContent = PR_TRUE;
break;
case 'N':
if (mode != SIGN) {
fprintf(stderr, "%s: option -N only supported with option -S.\n", progName);
Usage(progName);
exit(1);
}
signOptions.nickname = strdup(optstate->value);
break;
case 'Y':
if (mode != SIGN) {
fprintf(stderr, "%s: option -Y only supported with option -S.\n", progName);
Usage(progName);
exit(1);
}
signOptions.encryptionKeyPreferenceNick = strdup(optstate->value);
break;
case 'T':
if (mode != SIGN) {
fprintf(stderr, "%s: option -T only supported with option -S.\n", progName);
Usage(progName);
exit(1);
}
signOptions.detached = PR_TRUE;
break;
case 'G':
if (mode != SIGN) {
fprintf(stderr, "%s: option -G only supported with option -S.\n", progName);
Usage(progName);
exit(1);
}
signOptions.signingTime = PR_TRUE;
break;
case 'P':
if (mode != SIGN) {
fprintf(stderr, "%s: option -P only supported with option -S.\n", progName);
Usage(progName);
exit(1);
}
signOptions.smimeProfile = PR_TRUE;
break;
case 'h':
if (mode != DECODE) {
fprintf(stderr, "%s: option -h only supported with option -D.\n", progName);
Usage(progName);
exit(1);
}
decodeOptions.headerLevel = atoi(optstate->value);
if (decodeOptions.headerLevel < 0) {
fprintf(stderr, "option -h cannot have a negative value.\n");
exit(1);
}
break;
case 'p':
if (!optstate->value) {
fprintf(stderr, "%s: option -p must have a value.\n", progName);
Usage(progName);
exit(1);
}
options.password = strdup(optstate->value);
break;
case 'i':
if ((inFile = fopen(optstate->value, "r")) == NULL) {
fprintf(stderr, "%s: unable to open \"%s\" for reading\n",
progName, optstate->value);
exit(1);
}
break;
case 'c':
if (mode != DECODE) {
fprintf(stderr, "%s: option -c only supported with option -D.\n", progName);
Usage(progName);
exit(1);
}
if ((decodeOptions.contentFile = fopen(optstate->value, "r")) == NULL) {
fprintf(stderr, "%s: unable to open \"%s\" for reading.\n",
progName, optstate->value);
exit(1);
}
break;
case 'o':
if ((outFile = fopen(optstate->value, "w")) == NULL) {
fprintf(stderr, "%s: unable to open \"%s\" for writing\n",
progName, optstate->value);
exit(1);
}
break;
case 'r':
if (!optstate->value) {
fprintf(stderr, "%s: option -r must have a value.\n", progName);
Usage(progName);
exit(1);
}
#if 0
fprintf(stderr, "recipient = %s\n", optstate->value);
#endif
envelopeOptions.recipients = ptrarray;
envelopeOptions.recipients[nrecipients++] = strdup(optstate->value);
envelopeOptions.recipients[nrecipients] = NULL;
break;
case 'd':
SECU_ConfigDirectory(optstate->value);
break;
case 'u': {
int usageType;
usageType = atoi (strdup(optstate->value));
if (usageType < certUsageSSLClient || usageType > certUsageAnyCA)
return -1;
options.certUsage = (SECCertUsage)usageType;
break;
}
}
}
/* Call the libsec initialization routines */
PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
SECU_PKCS11Init(PR_FALSE);
SEC_Init();
/* open cert database */
options.certHandle = OpenCertDB(progName);
if (options.certHandle == NULL) {
return -1;
}
CERT_SetDefaultCertDB(options.certHandle);
exitstatus = 0;
switch (mode) {
case DECODE:
if (decode(outFile, inFile, progName, options, decodeOptions)) {
SECU_PrintError(progName, "problem decoding");
exitstatus = 1;
}
break;
case SIGN:
if (sign(outFile, inFile, progName, options, signOptions)) {
SECU_PrintError(progName, "problem signing");
exitstatus = 1;
}
break;
case ENCRYPT:
if (envelope(outFile, inFile, progName, options, envelopeOptions)) {
SECU_PrintError(progName, "problem encrypting");
exitstatus = 1;
}
break;
default:
fprintf(stderr, "One of options -D, -S or -E must be set.\n");
Usage(progName);
exitstatus = 1;
}
if (outFile != stdout)
fclose(outFile);
exit(exitstatus);
}

View File

@ -0,0 +1,45 @@
#
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
CORE_DEPTH = ../../..
MODULE = security
CSRCS = cmsutil.c
MYLIB = $(DIST)/lib/libsmime.a
REQUIRES = seccmd dbm
PROGRAM = cmsutil
SCRIPTS = smime

View File

@ -0,0 +1,37 @@
#
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
# $Id: rules.mk,v 1.2 2000-06-13 21:56:15 chrisk%netscape.com Exp $
#
install::
$(INSTALL) -m 755 $(SCRIPTS) $(SOURCE_BIN_DIR)

View File

@ -0,0 +1,325 @@
#!/usr/local/bin/perl
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
# smime.pl - frontend for S/MIME message generation
#
# $Id: smime,v 1.2 2000-06-13 21:56:15 chrisk%netscape.com Exp $
#
use Getopt::Std;
@boundarychars = ( "0" .. "9", "A" .. "F" );
# path to cmsutil
$cmsutilpath = "cmsutil";
#
# Thanks to Gisle Aas <gisle@aas.no> for the encode_base64 function
# originally taken from MIME-Base64-2.11 at www.cpan.org
#
sub encode_base64($)
{
my $res = "";
pos($_[0]) = 0; # ensure start at the beginning
while ($_[0] =~ /(.{1,45})/gs) {
$res .= substr(pack('u', $1), 1); # get rid of length byte after packing
chop($res);
}
$res =~ tr|` -_|AA-Za-z0-9+/|;
# fix padding at the end
my $padding = (3 - length($_[0]) % 3) % 3;
$res =~ s/.{$padding}$/'=' x $padding/e if $padding;
# break encoded string into lines of no more than 76 characters each
$res =~ s/(.{1,76})/$1\n/g;
$res;
}
#
# encryptentity($entity, $options) - encrypt an S/MIME entity
#
# entity - string containing entire S/MIME entity to encrypt
# options - options for cmsutil
#
# this will generate and return a new multipart/signed entity consisting
# of the canonicalized original content, plus a signature block.
#
sub encryptentity($$)
{
my ($entity, $cmsutiloptions) = @_;
my $out = "";
my $boundary;
$tmpencfile = "/tmp/encryptentity.$$";
#
# generate a random boundary string
#
$boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
#
# tell cmsutil to generate a signed CMS message using the canonicalized data
# The signedData has detached content (-T) and includes a signing time attribute (-G)
#
# if we do not provide a password on the command line, here's where we would be asked for it
#
open(CMS, "|$cmsutilpath -E $cmsutiloptions -o $tmpencfile") or die "ERROR: cannot pipe to cmsutil";
print CMS $entity;
unless (close(CMS)) {
print STDERR "ERROR: encryption failed.\n";
unlink($tmpsigfile);
exit 1;
}
open (ENC, $tmpencfile) or die "ERROR: cannot find newly generated encrypted content";
#
# construct a new multipart/signed MIME entity consisting of the original content and
# the signature
#
# (we assume that cmsutil generates a SHA1 digest)
$out .= "Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m\n";
$out .= "Content-Transfer-Encoding: base64\n";
$out .= "Content-Disposition: attachment; filename=smime.p7m\n";
$out .= "\n"; # end of entity header
local($/) = undef; # slurp whole file
$out .= encode_base64(<ENC>), "\n"; # append base64-encoded signature
close(ENC);
unlink($tmpencfile);
$out;
}
#
# signentity($entity, $options) - sign an S/MIME entity
#
# entity - string containing entire S/MIME entity to sign
# options - options for cmsutil
#
# this will generate and return a new multipart/signed entity consisting
# of the canonicalized original content, plus a signature block.
#
sub signentity($$)
{
my ($entity, $cmsutiloptions) = @_;
my $out = "";
my $boundary;
$tmpsigfile = "/tmp/signentity.$$";
#
# generate a random boundary string
#
$boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
#
# tell cmsutil to generate a signed CMS message using the canonicalized data
# The signedData has detached content (-T) and includes a signing time attribute (-G)
#
# if we do not provide a password on the command line, here's where we would be asked for it
#
open(CMS, "|$cmsutilpath -S -T -G $cmsutiloptions -o $tmpsigfile") or die "ERROR: cannot pipe to cmsutil";
print CMS $entity;
unless (close(CMS)) {
print STDERR "ERROR: signature generation failed.\n";
unlink($tmpsigfile);
exit 1;
}
open (SIG, $tmpsigfile) or die "ERROR: cannot find newly generated signature";
#
# construct a new multipart/signed MIME entity consisting of the original content and
# the signature
#
# (we assume that cmsutil generates a SHA1 digest)
$out .= "Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha1; boundary=\"${boundary}\"\n";
$out .= "\n"; # end of entity header
$out .= "This is a cryptographically signed message in MIME format.\n\n"; # explanatory comment
$out .= "--${boundary}\n";
$out .= "$entity\n"; # the trailing \n seems to be important
$out .= "--${boundary}\n";
$out .= "Content-Type: application/pkcs7-signature; name=smime.p7s\n";
$out .= "Content-Transfer-Encoding: base64\n";
$out .= "Content-Disposition: attachment; filename=smime.p7s\n";
$out .= "Content-Description: S/MIME Cryptographic Signature\n";
$out .= "\n"; # end of signature subentity header
local($/) = undef; # slurp whole file
$out .= encode_base64(<SIG>), "\n"; # append base64-encoded signature
$out .= "--${boundary}--\n";
close(SIG);
unlink($tmpsigfile);
$out;
}
sub usage {
print STDERR "usage: smime [options]\n";
print STDERR " options:\n";
print STDERR " -S nick generate signed message, use certificate named \"nick\"\n";
print STDERR " -p passwd use \"passwd\" as security module password\n";
print STDERR " -E rec1[,rec2...] generate encrypted message for recipients\n";
print STDERR " -C pathname set pathname of \"cmsutil\"\n";
print STDERR "\nWith -S or -E, smime will take a regular RFC822 message or MIME entity\n";
print STDERR "and generate a signed or encrypted S/MIME message with the same headers\n";
print STDERR "and content from it. The output can be used as input to a MTA.\n";
}
#
# start of main procedures
#
#
# process command line options
#
unless (getopts('S:E:p:C:D')) {
usage();
exit 1;
}
unless (defined($opt_S) or defined($opt_E)) {
print STDERR "ERROR: -S and/or -E must be specified.\n";
usage();
exit 1;
}
$signopts = "";
$encryptopts = "";
if (defined($opt_S)) {
$signopts .= "-N \"$opt_S\" ";
}
if (defined($opt_p)) {
$signopts .= "-p \"$opt_p\" ";
}
if (defined($opt_E)) {
@recipients = split(",", $opt_E);
$encryptopts .= "-r ";
$encryptopts .= join (" -r ", @recipients);
}
if (defined($opt_C)) {
$cmsutilpath = $opt_C;
}
#
# split headers into mime entity headers and RFC822 headers
# The RFC822 headers are preserved and stay on the outer layer of the message
#
$rfc822headers = "";
$mimeentity = "";
while (<STDIN>) {
last if (/^$/);
if (/^content-\S+: /i) {
$mimeentity .= $_;
} elsif (/^mime-version: /i) {
; # skip it
} else {
$rfc822headers .= $_;
}
}
#
# if there are no MIME entity headers, generate some default ones
#
if ($mimeentity eq "") {
$mimeentity .= "Content-Type: text/plain; charset=us-ascii\n";
$mimeentity .= "Content-Transfer-Encoding: 7bit\n";
}
#
# generate end of header-LF/LF pair
#
$mimeentity .= "\n";
#
# slurp in the entity body
#
$saveRS = $/;
$/ = undef;
$mimeentity .= <STDIN>;
$/ = $saveRS;
if (defined $opt_D) {
#
# decode
#
} else {
#
# encode
#
#
# canonicalize inner entity (rudimentary yet)
# convert single LFs to CRLF
# if no Content-Transfer-Encoding header present:
# if 8 bit chars present, use Content-Transfer-Encoding: quoted-printable
# otherwise, use Content-Transfer-Encoding: 7bit
#
$mimeentity =~ s/\n/\r\n/mg;
#
# now do the wrapping
# we sign first, then encrypt because that's what Communicator needs
#
if (defined($opt_S)) {
$mimeentity = signentity($mimeentity, $signopts);
}
if (defined($opt_E)) {
$mimeentity = encryptentity($mimeentity, $encryptopts);
}
#
# XXX sign again to do triple wrapping (RFC2634)
#
#
# now write out the RFC822 headers
# followed by the final $mimeentity
#
print $rfc822headers;
print "MIME-Version: 1.0 (NSS SMIME - http://www.mozilla.org/projects/security)\n"; # set up the flag
print $mimeentity;
}
exit 0;

View File

@ -34,7 +34,7 @@
/*
* cert.h - public data structures and prototypes for the certificate library
*
* $Id: cert.h,v 1.1 2000-03-31 19:42:30 relyea%netscape.com Exp $
* $Id: cert.h,v 1.2 2000-06-13 21:56:19 chrisk%netscape.com Exp $
*/
#ifndef _CERT_H_
@ -902,6 +902,9 @@ extern CERTCertificateList *
CERT_CertChainFromCert(CERTCertificate *cert, SECCertUsage usage,
PRBool includeRoot);
extern CERTCertificateList *
CERT_CertListFromCert(CERTCertificate *cert);
extern void CERT_DestroyCertificateList(CERTCertificateList *list);
/* is cert a newer than cert b? */

View File

@ -1013,6 +1013,38 @@ loser:
return NULL;
}
CERTCertificateList *
CERT_CertListFromCert(CERTCertificate *cert)
{
CERTCertificateList *chain = NULL;
int rv;
PRArenaPool *arena;
/* arena for SecCertificateList */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) goto no_memory;
/* build the CERTCertificateList */
chain = (CERTCertificateList *)PORT_ArenaAlloc(arena, sizeof(CERTCertificateList));
if (chain == NULL) goto no_memory;
chain->certs = (SECItem*)PORT_ArenaAlloc(arena, 1 * sizeof(SECItem));
if (chain->certs == NULL) goto no_memory;
rv = SECITEM_CopyItem(arena, chain->certs, &(cert->derCert));
if (rv < 0) goto loser;
chain->len = 1;
chain->arena = arena;
return chain;
no_memory:
PORT_SetError(SEC_ERROR_NO_MEMORY);
loser:
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
}
return NULL;
}
void
CERT_DestroyCertificateList(CERTCertificateList *list)
{

View File

@ -32,7 +32,7 @@
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: secvfy.c,v 1.2 2000-06-12 23:43:39 chrisk%netscape.com Exp $
* $Id: secvfy.c,v 1.3 2000-06-13 21:56:22 chrisk%netscape.com Exp $
*/
#include <stdio.h>

View File

@ -36,7 +36,8 @@ DEPTH = ../..
DIRS = pkcs7 ssl nss crmf jar \
certhigh pk11wrap cryptohi \
softoken certdb crypto \
util freebl pkcs12 fortcrypt
util freebl pkcs12 fortcrypt \
smime
#
# these dirs are not built at the moment
#

View File

@ -46,6 +46,7 @@
#include "hasht.h"
#include "secoid.h"
#include "pkcs7t.h"
#include "cmsreclist.h"
#include "certdb.h"
#include "secerr.h"
@ -1475,6 +1476,105 @@ pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr,
}
/*
* We're looking for a cert which we have the private key for that's on the
* list of recipients. This searches one slot.
* this is the new version for NSS SMIME code
* this stuff should REALLY be in the SMIME code, but some things in here are not public
* (they should be!)
*/
static CK_OBJECT_HANDLE
pk11_FindCertObjectByRecipientNew(PK11SlotInfo *slot, NSSCMSRecipient **recipientlist, int *rlIndex)
{
CK_OBJECT_HANDLE certHandle;
CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
CK_OBJECT_CLASS peerClass ;
CK_ATTRIBUTE searchTemplate[] = {
{ CKA_CLASS, NULL, 0 },
{ CKA_ISSUER, NULL, 0 },
{ CKA_SERIAL_NUMBER, NULL, 0}
};
int count = sizeof(searchTemplate)/sizeof(CK_ATTRIBUTE);
NSSCMSRecipient *ri = NULL;
CK_ATTRIBUTE *attrs;
int i;
peerClass = CKO_PRIVATE_KEY;
if (!PK11_IsLoggedIn(slot,NULL) && PK11_NeedLogin(slot)) {
peerClass = CKO_PUBLIC_KEY;
}
for (i=0; (ri = recipientlist[i]) != NULL; i++) {
/* XXXXX fixme - not yet implemented! */
if (ri->kind == RLSubjKeyID)
continue;
attrs = searchTemplate;
PK11_SETATTRS(attrs, CKA_CLASS, &certClass,sizeof(certClass)); attrs++;
PK11_SETATTRS(attrs, CKA_ISSUER, ri->id.issuerAndSN->derIssuer.data,
ri->id.issuerAndSN->derIssuer.len); attrs++;
PK11_SETATTRS(attrs, CKA_SERIAL_NUMBER,
ri->id.issuerAndSN->serialNumber.data,ri->id.issuerAndSN->serialNumber.len);
certHandle = pk11_FindObjectByTemplate(slot,searchTemplate,count);
if (certHandle != CK_INVALID_KEY) {
CERTCertificate *cert = pk11_fastCert(slot,certHandle,NULL,NULL);
if (PK11_IsUserCert(slot,cert,certHandle)) {
/* we've found a cert handle, now let's see if there is a key
* associated with it... */
ri->slot = PK11_ReferenceSlot(slot);
*rlIndex = i;
CERT_DestroyCertificate(cert);
return certHandle;
}
CERT_DestroyCertificate(cert);
}
}
*rlIndex = -1;
return CK_INVALID_KEY;
}
/*
* This function is the same as above, but it searches all the slots.
* this is the new version for NSS SMIME code
* this stuff should REALLY be in the SMIME code, but some things in here are not public
* (they should be!)
*/
static CK_OBJECT_HANDLE
pk11_AllFindCertObjectByRecipientNew(NSSCMSRecipient **recipientlist, void *wincx, int *rlIndex)
{
PK11SlotList *list;
PK11SlotListElement *le;
CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY;
PK11SlotInfo *slot = NULL;
SECStatus rv;
/* get them all! */
list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx);
if (list == NULL) {
if (list) PK11_FreeSlotList(list);
return CK_INVALID_KEY;
}
/* Look for the slot that holds the Key */
for (le = list->head ; le; le = le->next) {
if ( !PK11_IsFriendly(le->slot)) {
rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
if (rv != SECSuccess) continue;
}
certHandle = pk11_FindCertObjectByRecipientNew(le->slot, recipientlist, rlIndex);
if (certHandle != CK_INVALID_KEY)
break;
}
PK11_FreeSlotList(list);
return (le == NULL) ? CK_INVALID_KEY : certHandle;
}
/*
* We're looking for a cert which we have the private key for that's on the
* list of recipients. This searches one slot.
@ -1632,6 +1732,63 @@ PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr,
return cert;
}
/*
* This is the new version of the above function for NSS SMIME code
* this stuff should REALLY be in the SMIME code, but some things in here are not public
* (they should be!)
*/
int
PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist, void *wincx)
{
CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY;
CK_OBJECT_HANDLE keyHandle = CK_INVALID_KEY;
SECStatus rv;
NSSCMSRecipient *rl;
int rlIndex;
certHandle = pk11_AllFindCertObjectByRecipientNew(recipientlist, wincx, &rlIndex);
if (certHandle == CK_INVALID_KEY) {
return NULL;
}
rl = recipientlist[rlIndex];
/* at this point, rl->slot is set */
/* authenticate to the token */
if (PK11_Authenticate(rl->slot, PR_TRUE, wincx) != SECSuccess) {
PK11_FreeSlot(rl->slot);
rl->slot = NULL;
return -1;
}
/* try to get a private key handle for the cert we found */
keyHandle = PK11_MatchItem(rl->slot, certHandle, CKO_PRIVATE_KEY);
if (keyHandle == CK_INVALID_KEY) {
PK11_FreeSlot(rl->slot);
rl->slot = NULL;
return -1;
}
/* make a private key out of the handle */
rl->privkey = PK11_MakePrivKey(rl->slot, nullKey, PR_TRUE, keyHandle, wincx);
if (rl->privkey == NULL) {
PK11_FreeSlot(rl->slot);
rl->slot = NULL;
return -1;
}
/* make a cert from the cert handle */
rl->cert = PK11_MakeCertFromHandle(rl->slot, certHandle, NULL);
if (rl->cert == NULL) {
PK11_FreeSlot(rl->slot);
SECKEY_DestroyPrivateKey(rl->privkey);
rl->slot = NULL;
rl->privkey = NULL;
return NULL;
}
return rlIndex;
}
CERTCertificate *
PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN,
void *wincx)

View File

@ -46,6 +46,7 @@
#include "secmodt.h"
#include "seccomon.h"
#include "pkcs7t.h"
#include "cmsreclist.h"
SEC_BEGIN_PROTOS
@ -353,6 +354,8 @@ CERTCertificate * PK11_FindCertByIssuerAndSN(PK11SlotInfo **slot,
CERTCertificate * PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slot,
SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip,
SECKEYPrivateKey**privKey, void *wincx);
int PK11_FindCertAndKeyByRecipientListNew(NSSCMSRecipient **recipientlist,
void *wincx);
CK_BBOOL PK11_HasAttributeSet( PK11SlotInfo *slot,
CK_OBJECT_HANDLE id,
CK_ATTRIBUTE_TYPE type );

View File

@ -0,0 +1,76 @@
#! gmake
#
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
#######################################################################
# (1) Include initial platform-independent assignments (MANDATORY). #
#######################################################################
include manifest.mn
#######################################################################
# (2) Include "global" configuration information. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/config.mk
#######################################################################
# (3) Include "component" configuration information. (OPTIONAL) #
#######################################################################
#######################################################################
# (4) Include "local" platform-dependent assignments (OPTIONAL). #
#######################################################################
include config.mk
#######################################################################
# (5) Execute "global" rules. (OPTIONAL) #
#######################################################################
include $(CORE_DEPTH)/coreconf/rules.mk
#######################################################################
# (6) Execute "component" rules. (OPTIONAL) #
#######################################################################
#######################################################################
# (7) Execute "local" rules. (OPTIONAL). #
#######################################################################

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,205 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS array functions.
*
* $Id: cmsarray.c,v 1.2 2000-06-13 21:56:26 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "secerr.h"
/*
* ARRAY FUNCTIONS
*
* In NSS, arrays are rather primitive arrays of pointers.
* Makes it easy to walk the array, but hard to count elements
* and manage the storage.
*
* This is a feeble attempt to encapsulate the functionality
* and get rid of hundreds of lines of similar code
*/
/*
* NSS_CMSArray_Alloc - allocate an array in an arena
*/
void **
NSS_CMSArray_Alloc(PRArenaPool *poolp, int n)
{
return (void **)PORT_ArenaZAlloc(poolp, n * sizeof(void *));
}
/*
* NSS_CMSArray_Add - add an element to the end of an array
*/
SECStatus
NSS_CMSArray_Add(PRArenaPool *poolp, void ***array, void *obj)
{
void **p;
int n;
void **dest;
PORT_Assert(array != NULL);
if (array == NULL)
return SECFailure;
if (*array == NULL) {
dest = (void **)PORT_ArenaAlloc(poolp, 2 * sizeof(void *));
n = 0;
} else {
n = 0; p = *array;
while (*p++)
n++;
dest = (void **)PORT_ArenaGrow (poolp,
*array,
(n + 1) * sizeof(void *),
(n + 2) * sizeof(void *));
}
dest[n] = obj;
dest[n+1] = NULL;
*array = dest;
return SECSuccess;
}
/*
* NSS_CMSArray_IsEmpty - check if array is empty
*/
PRBool
NSS_CMSArray_IsEmpty(void **array)
{
return (array == NULL || array[0] == NULL);
}
/*
* NSS_CMSArray_IsEmpty - count number of elements in array
*/
int
NSS_CMSArray_Count(void **array)
{
int n = 0;
if (array == NULL)
return 0;
while (*array++ != NULL)
n++;
return n;
}
/*
* NSS_CMSArray_Sort - sort an array ascending, in place
*
* If "secondary" is not NULL, the same reordering gets applied to it.
* If "tertiary" is not NULL, the same reordering gets applied to it.
* "compare" is a function that returns
* < 0 when the first element is less than the second
* = 0 when the first element is equal to the second
* > 0 when the first element is greater than the second
*/
void
NSS_CMSArray_Sort(void **primary, int (*compare)(void *,void *), void **secondary, void **tertiary)
{
int n, i, limit, lastxchg;
void *tmp;
if (primary == NULL)
return;
n = NSS_CMSArray_Count(primary);
if (n <= 1) /* ordering is fine */
return;
/* yes, ladies and gentlemen, it's BUBBLE SORT TIME! */
limit = n - 1;
while (1) {
lastxchg = 0;
for (i = 0; i < limit; i++) {
if ((*compare)(primary[i], primary[i+1]) > 0) {
/* exchange the neighbours */
tmp = primary[i+1];
primary[i+1] = primary[i];
primary[i] = tmp;
if (secondary) { /* secondary array? */
tmp = secondary[i+1]; /* exchange there as well */
secondary[i+1] = secondary[i];
secondary[i] = tmp;
}
if (tertiary) { /* tertiary array? */
tmp = tertiary[i+1]; /* exchange there as well */
tertiary[i+1] = tertiary[i];
tertiary[i] = tmp;
}
lastxchg = i+1; /* index of the last element bubbled up */
}
}
if (lastxchg == 0) /* no exchanges, so array is sorted */
break; /* we're done */
limit = lastxchg; /* array is sorted up to [limit] */
}
}
#if 0
/* array iterator stuff... not used */
typedef void **NSSCMSArrayIterator;
/* iterator */
NSSCMSArrayIterator
NSS_CMSArray_First(void **array)
{
if (array == NULL || array[0] == NULL)
return NULL;
return (NSSCMSArrayIterator)&(array[0]);
}
void *
NSS_CMSArray_Obj(NSSCMSArrayIterator iter)
{
void **p = (void **)iter;
return *iter; /* which is NULL if we are at the end of the array */
}
NSSCMSArrayIterator
NSS_CMSArray_Next(NSSCMSArrayIterator iter)
{
void **p = (void **)iter;
return (NSSCMSArrayIterator)(p + 1);
}
#endif

View File

@ -0,0 +1,560 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS ASN.1 templates
*
* $Id: cmsasn1.c,v 1.2 2000-06-13 21:56:26 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "cert.h"
#include "key.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "prtime.h"
#include "secerr.h"
extern const SEC_ASN1Template nss_cms_set_of_attribute_template[];
/* -----------------------------------------------------------------------------
* MESSAGE
* (uses NSSCMSContentInfo)
*/
/* forward declaration */
static const SEC_ASN1Template *
nss_cms_choose_content_template(void *src_or_dest, PRBool encoding);
static SEC_ChooseASN1TemplateFunc nss_cms_chooser
= nss_cms_choose_content_template;
const SEC_ASN1Template NSSCMSMessageTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSMessage) },
{ SEC_ASN1_OBJECT_ID,
offsetof(NSSCMSMessage,contentInfo.contentType) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM
| SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSMessage,contentInfo.content),
&nss_cms_chooser },
{ 0 }
};
static const SEC_ASN1Template NSS_PointerToCMSMessageTemplate[] = {
{ SEC_ASN1_POINTER, 0, NSSCMSMessageTemplate }
};
/* -----------------------------------------------------------------------------
* ENCAPSULATED & ENCRYPTED CONTENTINFO
* (both use a NSSCMSContentInfo)
*/
static const SEC_ASN1Template NSSCMSEncapsulatedContentInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSContentInfo) },
{ SEC_ASN1_OBJECT_ID,
offsetof(NSSCMSContentInfo,contentType) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_MAY_STREAM |
SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSContentInfo,rawContent),
SEC_PointerToOctetStringTemplate },
{ 0 }
};
static const SEC_ASN1Template NSSCMSEncryptedContentInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSContentInfo) },
{ SEC_ASN1_OBJECT_ID,
offsetof(NSSCMSContentInfo,contentType) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSContentInfo,contentEncAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_POINTER | SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSContentInfo,rawContent),
SEC_OctetStringTemplate },
{ 0 }
};
/* -----------------------------------------------------------------------------
* SIGNED DATA
*/
const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
const SEC_ASN1Template NSSCMSSignedDataTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSSignedData) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSSignedData,version) },
{ SEC_ASN1_SET_OF,
offsetof(NSSCMSSignedData,digestAlgorithms),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSSignedData,contentInfo),
NSSCMSEncapsulatedContentInfoTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSSignedData,rawCerts),
SEC_SetOfAnyTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSSignedData,crls),
CERT_SetOfSignedCrlTemplate },
{ SEC_ASN1_SET_OF,
offsetof(NSSCMSSignedData,signerInfos),
NSSCMSSignerInfoTemplate },
{ 0 }
};
const SEC_ASN1Template NSS_PointerToCMSSignedDataTemplate[] = {
{ SEC_ASN1_POINTER, 0, NSSCMSSignedDataTemplate }
};
/* -----------------------------------------------------------------------------
* signeridentifier
*/
static const SEC_ASN1Template NSSCMSSignerIdentifierTemplate[] = {
{ SEC_ASN1_CHOICE,
offsetof(NSSCMSSignerIdentifier,identifierType), NULL,
sizeof(NSSCMSSignerIdentifier) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSSignerIdentifier,id.subjectKeyID),
SEC_PointerToOctetStringTemplate,
NSSCMSRecipientID_SubjectKeyID },
{ SEC_ASN1_POINTER,
offsetof(NSSCMSSignerIdentifier,id.issuerAndSN),
CERT_IssuerAndSNTemplate,
NSSCMSRecipientID_IssuerSN },
{ 0 }
};
/* -----------------------------------------------------------------------------
* signerinfo
*/
const SEC_ASN1Template NSSCMSSignerInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSSignerInfo) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSSignerInfo,version) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSSignerInfo,signerIdentifier),
NSSCMSSignerIdentifierTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSSignerInfo,digestAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSSignerInfo,authAttr),
nss_cms_set_of_attribute_template },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSSignerInfo,digestEncAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSSignerInfo,encDigest) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSSignerInfo,unAuthAttr),
nss_cms_set_of_attribute_template },
{ 0 }
};
/* -----------------------------------------------------------------------------
* ENVELOPED DATA
*/
static const SEC_ASN1Template NSSCMSOriginatorInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSOriginatorInfo) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSOriginatorInfo,rawCerts),
SEC_SetOfAnyTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSOriginatorInfo,crls),
CERT_SetOfSignedCrlTemplate },
{ 0 }
};
const SEC_ASN1Template NSSCMSRecipientInfoTemplate[];
const SEC_ASN1Template NSSCMSEnvelopedDataTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSEnvelopedData) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSEnvelopedData,version) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_POINTER | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSEnvelopedData,originatorInfo),
NSSCMSOriginatorInfoTemplate },
{ SEC_ASN1_SET_OF,
offsetof(NSSCMSEnvelopedData,recipientInfos),
NSSCMSRecipientInfoTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSEnvelopedData,contentInfo),
NSSCMSEncryptedContentInfoTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSEnvelopedData,unprotectedAttr),
nss_cms_set_of_attribute_template },
{ 0 }
};
const SEC_ASN1Template NSS_PointerToCMSEnvelopedDataTemplate[] = {
{ SEC_ASN1_POINTER, 0, NSSCMSEnvelopedDataTemplate }
};
/* here come the 15 gazillion templates for all the v3 varieties of RecipientInfo */
/* -----------------------------------------------------------------------------
* key transport recipient info
*/
static const SEC_ASN1Template NSSCMSRecipientIdentifierTemplate[] = {
{ SEC_ASN1_CHOICE,
offsetof(NSSCMSRecipientIdentifier,identifierType), NULL,
sizeof(NSSCMSRecipientIdentifier) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSRecipientIdentifier,id.subjectKeyID),
SEC_PointerToOctetStringTemplate,
NSSCMSRecipientID_SubjectKeyID },
{ SEC_ASN1_POINTER,
offsetof(NSSCMSRecipientIdentifier,id.issuerAndSN),
CERT_IssuerAndSNTemplate,
NSSCMSRecipientID_IssuerSN },
{ 0 }
};
static const SEC_ASN1Template NSSCMSKeyTransRecipientInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSKeyTransRecipientInfo) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSKeyTransRecipientInfo,version) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSKeyTransRecipientInfo,recipientIdentifier),
NSSCMSRecipientIdentifierTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSKeyTransRecipientInfo,keyEncAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSKeyTransRecipientInfo,encKey) },
{ 0 }
};
/* -----------------------------------------------------------------------------
* key agreement recipient info
*/
static const SEC_ASN1Template NSSCMSOriginatorPublicKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSOriginatorPublicKey) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSOriginatorPublicKey,algorithmIdentifier),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSOriginatorPublicKey,publicKey),
SEC_BitStringTemplate },
{ 0 }
};
static const SEC_ASN1Template NSSCMSOriginatorIdentifierOrKeyTemplate[] = {
{ SEC_ASN1_CHOICE,
offsetof(NSSCMSOriginatorIdentifierOrKey,identifierType), NULL,
sizeof(NSSCMSOriginatorIdentifierOrKey) },
{ SEC_ASN1_POINTER,
offsetof(NSSCMSOriginatorIdentifierOrKey,id.issuerAndSN),
CERT_IssuerAndSNTemplate,
NSSCMSOriginatorIDOrKey_IssuerSN },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSOriginatorIdentifierOrKey,id.subjectKeyID),
SEC_PointerToOctetStringTemplate,
NSSCMSOriginatorIDOrKey_SubjectKeyID },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
offsetof(NSSCMSOriginatorIdentifierOrKey,id.originatorPublicKey),
NSSCMSOriginatorPublicKeyTemplate,
NSSCMSOriginatorIDOrKey_OriginatorPublicKey },
{ 0 }
};
static const SEC_ASN1Template NSSCMSRecipientKeyIdentifierTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSRecipientKeyIdentifier) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSRecipientKeyIdentifier,subjectKeyIdentifier) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSRecipientKeyIdentifier,date) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSRecipientKeyIdentifier,other) },
{ 0 }
};
static const SEC_ASN1Template NSSCMSKeyAgreeRecipientIdentifierTemplate[] = {
{ SEC_ASN1_CHOICE,
offsetof(NSSCMSKeyAgreeRecipientIdentifier,identifierType), NULL,
sizeof(NSSCMSKeyAgreeRecipientIdentifier) },
{ SEC_ASN1_POINTER,
offsetof(NSSCMSKeyAgreeRecipientIdentifier,id.issuerAndSN),
CERT_IssuerAndSNTemplate,
NSSCMSKeyAgreeRecipientID_IssuerSN },
{ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSKeyAgreeRecipientIdentifier,id.recipientKeyIdentifier),
NSSCMSRecipientKeyIdentifierTemplate,
NSSCMSKeyAgreeRecipientID_RKeyID },
{ 0 }
};
static const SEC_ASN1Template NSSCMSRecipientEncryptedKeyTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSRecipientEncryptedKey) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSRecipientEncryptedKey,recipientIdentifier),
NSSCMSKeyAgreeRecipientIdentifierTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSRecipientEncryptedKey,encKey),
SEC_BitStringTemplate },
{ 0 }
};
static const SEC_ASN1Template NSSCMSKeyAgreeRecipientInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSKeyAgreeRecipientInfo) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSKeyAgreeRecipientInfo,version) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
offsetof(NSSCMSKeyAgreeRecipientInfo,originatorIdentifierOrKey),
NSSCMSOriginatorIdentifierOrKeyTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSKeyAgreeRecipientInfo,ukm),
SEC_OctetStringTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSKeyAgreeRecipientInfo,keyEncAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_SEQUENCE_OF,
offsetof(NSSCMSKeyAgreeRecipientInfo,recipientEncryptedKeys),
NSSCMSRecipientEncryptedKeyTemplate },
{ 0 }
};
/* -----------------------------------------------------------------------------
* KEK recipient info
*/
static const SEC_ASN1Template NSSCMSKEKIdentifierTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSKEKIdentifier) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSKEKIdentifier,keyIdentifier) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSKEKIdentifier,date) },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSKEKIdentifier,other) },
{ 0 }
};
static const SEC_ASN1Template NSSCMSKEKRecipientInfoTemplate[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSKEKRecipientInfo) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSKEKRecipientInfo,version) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSKEKRecipientInfo,kekIdentifier),
NSSCMSKEKIdentifierTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSKEKRecipientInfo,keyEncAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSKEKRecipientInfo,encKey) },
{ 0 }
};
/* -----------------------------------------------------------------------------
* recipient info
*/
const SEC_ASN1Template NSSCMSRecipientInfoTemplate[] = {
{ SEC_ASN1_CHOICE,
offsetof(NSSCMSRecipientInfo,recipientInfoType), NULL,
sizeof(NSSCMSRecipientInfo) },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSRecipientInfo,ri.keyAgreeRecipientInfo),
NSSCMSKeyAgreeRecipientInfoTemplate,
NSSCMSRecipientInfoID_KeyAgree },
{ SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
offsetof(NSSCMSRecipientInfo,ri.kekRecipientInfo),
NSSCMSKEKRecipientInfoTemplate,
NSSCMSRecipientInfoID_KEK },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSRecipientInfo,ri.keyTransRecipientInfo),
NSSCMSKeyTransRecipientInfoTemplate,
NSSCMSRecipientInfoID_KeyTrans },
{ 0 }
};
/* -----------------------------------------------------------------------------
*
*/
const SEC_ASN1Template NSSCMSDigestedDataTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSDigestedData) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSDigestedData,version) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSDigestedData,digestAlg),
SECOID_AlgorithmIDTemplate },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSDigestedData,contentInfo),
NSSCMSEncapsulatedContentInfoTemplate },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSDigestedData,digest) },
{ 0 }
};
const SEC_ASN1Template NSS_PointerToCMSDigestedDataTemplate[] = {
{ SEC_ASN1_POINTER, 0, NSSCMSDigestedDataTemplate }
};
const SEC_ASN1Template NSSCMSEncryptedDataTemplate[] = {
{ SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
0, NULL, sizeof(NSSCMSEncryptedData) },
{ SEC_ASN1_INTEGER,
offsetof(NSSCMSEncryptedData,version) },
{ SEC_ASN1_INLINE,
offsetof(NSSCMSEncryptedData,contentInfo),
NSSCMSEncryptedContentInfoTemplate },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
offsetof(NSSCMSEncryptedData,unprotectedAttr),
nss_cms_set_of_attribute_template },
{ 0 }
};
const SEC_ASN1Template NSS_PointerToCMSEncryptedDataTemplate[] = {
{ SEC_ASN1_POINTER, 0, NSSCMSEncryptedDataTemplate }
};
/* -----------------------------------------------------------------------------
* FORTEZZA KEA
*/
const SEC_ASN1Template NSS_SMIMEKEAParamTemplateSkipjack[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSSMIMEKEAParameters) },
{ SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
offsetof(NSSCMSSMIMEKEAParameters,originatorKEAKey) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSSMIMEKEAParameters,originatorRA) },
{ 0 }
};
const SEC_ASN1Template NSS_SMIMEKEAParamTemplateNoSkipjack[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSSMIMEKEAParameters) },
{ SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
offsetof(NSSCMSSMIMEKEAParameters,originatorKEAKey) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSSMIMEKEAParameters,originatorRA) },
{ SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
offsetof(NSSCMSSMIMEKEAParameters,nonSkipjackIV) },
{ 0 }
};
const SEC_ASN1Template NSS_SMIMEKEAParamTemplateAllParams[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSSMIMEKEAParameters) },
{ SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
offsetof(NSSCMSSMIMEKEAParameters,originatorKEAKey) },
{ SEC_ASN1_OCTET_STRING,
offsetof(NSSCMSSMIMEKEAParameters,originatorRA) },
{ SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
offsetof(NSSCMSSMIMEKEAParameters,nonSkipjackIV) },
{ SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
offsetof(NSSCMSSMIMEKEAParameters,bulkKeySize) },
{ 0 }
};
const SEC_ASN1Template *
nss_cms_get_kea_template(NSSCMSKEATemplateSelector whichTemplate)
{
const SEC_ASN1Template *returnVal = NULL;
switch(whichTemplate)
{
case NSSCMSKEAUsesNonSkipjack:
returnVal = NSS_SMIMEKEAParamTemplateNoSkipjack;
break;
case NSSCMSKEAUsesSkipjack:
returnVal = NSS_SMIMEKEAParamTemplateSkipjack;
break;
case NSSCMSKEAUsesNonSkipjackWithPaddedEncKey:
default:
returnVal = NSS_SMIMEKEAParamTemplateAllParams;
break;
}
return returnVal;
}
/* -----------------------------------------------------------------------------
*
*/
static const SEC_ASN1Template *
nss_cms_choose_content_template(void *src_or_dest, PRBool encoding)
{
const SEC_ASN1Template *theTemplate;
NSSCMSContentInfo *cinfo;
PORT_Assert (src_or_dest != NULL);
if (src_or_dest == NULL)
return NULL;
cinfo = (NSSCMSContentInfo *)src_or_dest;
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
default:
theTemplate = SEC_PointerToAnyTemplate;
break;
case SEC_OID_PKCS7_DATA:
theTemplate = SEC_PointerToOctetStringTemplate;
break;
case SEC_OID_PKCS7_SIGNED_DATA:
theTemplate = NSS_PointerToCMSSignedDataTemplate;
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
theTemplate = NSS_PointerToCMSEnvelopedDataTemplate;
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
theTemplate = NSS_PointerToCMSDigestedDataTemplate;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
theTemplate = NSS_PointerToCMSEncryptedDataTemplate;
break;
}
return theTemplate;
}

View File

@ -0,0 +1,450 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS attributes.
*
* $Id: cmsattr.c,v 1.2 2000-06-13 21:56:27 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "prtime.h"
#include "secerr.h"
/*
* -------------------------------------------------------------------
* XXX The following Attribute stuff really belongs elsewhere.
* The Attribute type is *not* part of CMS but rather X.501.
* But for now, since CMS is the only customer of attributes,
* we define them here. Once there is a use outside of CMS,
* then change the attribute types and functions from internal
* to external naming convention, and move them elsewhere!
*/
/*
* NSS_CMSAttribute_Create - create an attribute
*
* if value is NULL, the attribute won't have a value. It can be added later
* with NSS_CMSAttribute_AddValue.
*/
NSSCMSAttribute *
NSS_CMSAttribute_Create(PRArenaPool *poolp, SECOidTag oidtag, SECItem *value, PRBool encoded)
{
NSSCMSAttribute *attr;
SECItem *copiedvalue;
void *mark;
PORT_Assert (poolp != NULL);
mark = PORT_ArenaMark (poolp);
attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute));
if (attr == NULL)
goto loser;
attr->typeTag = SECOID_FindOIDByTag(oidtag);
if (attr->typeTag == NULL)
goto loser;
if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess)
goto loser;
if (value != NULL) {
if ((copiedvalue = SECITEM_AllocItem(poolp, NULL, value->len)) == NULL)
goto loser;
if (SECITEM_CopyItem(poolp, copiedvalue, value) != SECSuccess)
goto loser;
NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue);
}
attr->encoded = encoded;
PORT_ArenaUnmark (poolp, mark);
return attr;
loser:
PORT_Assert (mark != NULL);
PORT_ArenaRelease (poolp, mark);
return NULL;
}
/*
* NSS_CMSAttribute_AddValue - add another value to an attribute
*/
SECStatus
NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value)
{
SECItem copiedvalue;
void *mark;
PORT_Assert (poolp != NULL);
mark = PORT_ArenaMark(poolp);
/* XXX we need an object memory model #$%#$%! */
if (SECITEM_CopyItem(poolp, &copiedvalue, value) != SECSuccess)
goto loser;
if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)&copiedvalue) != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_Assert (mark != NULL);
PORT_ArenaRelease (poolp, mark);
return SECFailure;
}
/*
* NSS_CMSAttribute_GetType - return the OID tag
*/
SECOidTag
NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
{
SECOidData *typetag;
typetag = SECOID_FindOID(&(attr->type));
if (typetag == NULL)
return SEC_OID_UNKNOWN;
return typetag->offset;
}
/*
* NSS_CMSAttribute_GetValue - return the first attribute value
*
* We do some sanity checking first:
* - Multiple values are *not* expected.
* - Empty values are *not* expected.
*/
SECItem *
NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr)
{
SECItem *value;
if (attr == NULL)
return NULL;
value = attr->values[0];
if (value == NULL || value->data == NULL || value->len == 0)
return NULL;
if (attr->values[1] != NULL)
return NULL;
return value;
}
/*
* NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
*/
PRBool
NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av)
{
SECItem *value;
if (attr == NULL)
return PR_FALSE;
value = NSS_CMSAttribute_GetValue(attr);
return (value != NULL && value->len == av->len &&
PORT_Memcmp (value->data, av->data, value->len) == 0);
}
/*
* templates and functions for separate ASN.1 encoding of attributes
*
* used in NSS_CMSAttributeArray_Reorder
*/
/*
* helper function for dynamic template determination of the attribute value
*/
static const SEC_ASN1Template *
cms_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
{
const SEC_ASN1Template *theTemplate;
NSSCMSAttribute *attribute;
SECOidData *oiddata;
PRBool encoded;
PORT_Assert (src_or_dest != NULL);
if (src_or_dest == NULL)
return NULL;
attribute = (NSSCMSAttribute *)src_or_dest;
if (encoding && attribute->encoded)
/* we're encoding, and the attribute value is already encoded. */
return SEC_AnyTemplate;
/* get attribute's typeTag */
oiddata = attribute->typeTag;
if (oiddata == NULL) {
oiddata = SECOID_FindOID(&attribute->type);
attribute->typeTag = oiddata;
}
if (oiddata == NULL) {
/* still no OID tag? OID is unknown then. en/decode value as ANY. */
encoded = PR_TRUE;
theTemplate = SEC_AnyTemplate;
} else {
switch (oiddata->offset) {
default:
/* same goes for OIDs that are not handled here */
encoded = PR_TRUE;
theTemplate = SEC_AnyTemplate;
break;
/* otherwise choose proper template */
case SEC_OID_PKCS9_EMAIL_ADDRESS:
case SEC_OID_RFC1274_MAIL:
case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
encoded = PR_FALSE;
theTemplate = SEC_IA5StringTemplate;
break;
case SEC_OID_PKCS9_CONTENT_TYPE:
encoded = PR_FALSE;
theTemplate = SEC_ObjectIDTemplate;
break;
case SEC_OID_PKCS9_MESSAGE_DIGEST:
encoded = PR_FALSE;
theTemplate = SEC_OctetStringTemplate;
break;
case SEC_OID_PKCS9_SIGNING_TIME:
encoded = PR_FALSE;
theTemplate = SEC_UTCTimeTemplate;
break;
/* XXX Want other types here, too */
}
}
if (encoding) {
/*
* If we are encoding and we think we have an already-encoded value,
* then the code which initialized this attribute should have set
* the "encoded" property to true (and we would have returned early,
* up above). No devastating error, but that code should be fixed.
* (It could indicate that the resulting encoded bytes are wrong.)
*/
PORT_Assert (!encoded);
} else {
/*
* We are decoding; record whether the resulting value is
* still encoded or not.
*/
attribute->encoded = encoded;
}
return theTemplate;
}
static SEC_ChooseASN1TemplateFunc cms_attr_chooser
= cms_attr_choose_attr_value_template;
const SEC_ASN1Template nss_cms_attribute_template[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSCMSAttribute) },
{ SEC_ASN1_OBJECT_ID,
offsetof(NSSCMSAttribute,type) },
{ SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
offsetof(NSSCMSAttribute,values),
&cms_attr_chooser },
{ 0 }
};
const SEC_ASN1Template nss_cms_set_of_attribute_template[] = {
{ SEC_ASN1_SET_OF, 0, nss_cms_attribute_template },
};
/* =============================================================================
* Attribute Array methods
*/
/*
* NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
*
* If you are wondering why this routine does not reorder the attributes
* first, and might be tempted to make it do so, see the comment by the
* call to ReorderAttributes in cmsencode.c. (Or, see who else calls this
* and think long and hard about the implications of making it always
* do the reordering.)
*/
SECItem *
NSS_CMSAttributeArray_Encode(PRArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest)
{
return SEC_ASN1EncodeItem (poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
}
/*
* NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
*
* make sure that the order of the attributes guarantees valid DER (which must be
* in lexigraphically ascending order for a SET OF); if reordering is necessary it
* will be done in place (in attrs).
*/
SECStatus
NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs)
{
return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL);
}
/*
* NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
* find one that matches the specified object ID.
*
* If "only" is true, then make sure that there is not more than one attribute
* of the same type. Otherwise, just return the first one found. (XXX Does
* anybody really want that first-found behavior? It was like that when I found it...)
*/
NSSCMSAttribute *
NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
{
SECOidData *oid;
NSSCMSAttribute *attr1, *attr2;
if (attrs == NULL)
return NULL;
oid = SECOID_FindOIDByTag(oidtag);
if (oid == NULL)
return NULL;
while ((attr1 = *attrs++) != NULL) {
if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
oid->oid.data,
oid->oid.len) == 0)
break;
}
if (attr1 == NULL)
return NULL;
if (!only)
return attr1;
while ((attr2 = *attrs++) != NULL) {
if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
oid->oid.data,
oid->oid.len) == 0)
break;
}
if (attr2 != NULL)
return NULL;
return attr1;
}
/*
* NSS_CMSAttributeArray_AddAttr - add an attribute to an
* array of attributes.
*/
SECStatus
NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
{
NSSCMSAttribute *oattr;
void *mark;
SECOidTag type;
mark = PORT_ArenaMark(poolp);
/* find oidtag of attr */
type = NSS_CMSAttribute_GetType(attr);
/* see if we have one already */
oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
PORT_Assert (oattr == NULL);
if (oattr != NULL)
goto loser; /* XXX or would it be better to replace it? */
/* no, shove it in */
if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
/*
* NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
*/
SECStatus
NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECOidTag type, SECItem *value, PRBool encoded)
{
NSSCMSAttribute *attr;
void *mark;
mark = PORT_ArenaMark(poolp);
/* see if we have one already */
attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
if (attr == NULL) {
/* not found? create one! */
attr = NSS_CMSAttribute_Create(poolp, type, value, encoded);
if (attr == NULL)
goto loser;
/* and add it to the list */
if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
goto loser;
} else {
/* found, shove it in */
/* XXX we need a decent memory model @#$#$!#!!! */
attr->values[0] = value;
attr->encoded = encoded;
}
PORT_ArenaUnmark (poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease (poolp, mark);
return SECFailure;
}

View File

@ -0,0 +1,327 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS contentInfo methods.
*
* $Id: cmscinfo.c,v 1.2 2000-06-13 21:56:27 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "pk11func.h"
#include "secitem.h"
#include "secoid.h"
#include "secerr.h"
/*
* NSS_CMSContentInfo_Create - create a content info
*
* version is set in the _Finalize procedures for each content type
*/
/*
* NSS_CMSContentInfo_Destroy - destroy a CMS contentInfo and all of its sub-pieces.
*/
void
NSS_CMSContentInfo_Destroy(NSSCMSContentInfo *cinfo)
{
SECOidTag kind;
kind = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
switch (kind) {
case SEC_OID_PKCS7_ENVELOPED_DATA:
NSS_CMSEnvelopedData_Destroy(cinfo->content.envelopedData);
break;
case SEC_OID_PKCS7_SIGNED_DATA:
NSS_CMSSignedData_Destroy(cinfo->content.signedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
NSS_CMSEncryptedData_Destroy(cinfo->content.encryptedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
NSS_CMSDigestedData_Destroy(cinfo->content.digestedData);
break;
default:
/* XXX Anything else that needs to be "manually" freed/destroyed? */
break;
}
if (cinfo->bulkkey)
PK11_FreeSymKey(cinfo->bulkkey);
/* we live in a pool, so no need to worry about storage */
}
/*
* NSS_CMSContentInfo_GetChildContentInfo - get content's contentInfo (if it exists)
*/
NSSCMSContentInfo *
NSS_CMSContentInfo_GetChildContentInfo(NSSCMSContentInfo *cinfo)
{
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
case SEC_OID_PKCS7_DATA:
return NULL;
case SEC_OID_PKCS7_SIGNED_DATA:
return &(cinfo->content.signedData->contentInfo);
case SEC_OID_PKCS7_ENVELOPED_DATA:
return &(cinfo->content.envelopedData->contentInfo);
case SEC_OID_PKCS7_DIGESTED_DATA:
return &(cinfo->content.digestedData->contentInfo);
case SEC_OID_PKCS7_ENCRYPTED_DATA:
return &(cinfo->content.encryptedData->contentInfo);
default:
return NULL;
}
}
/*
* NSS_CMSContentInfo_SetContent - set content type & content
*/
SECStatus
NSS_CMSContentInfo_SetContent(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, SECOidTag type, void *ptr)
{
SECStatus rv;
cinfo->contentTypeTag = SECOID_FindOIDByTag(type);
if (cinfo->contentTypeTag == NULL)
return SECFailure;
/* do not copy the oid, just create a reference */
rv = SECITEM_CopyItem (cmsg->poolp, &(cinfo->contentType), &(cinfo->contentTypeTag->oid));
if (rv != SECSuccess)
return SECFailure;
cinfo->content.pointer = ptr;
if (type != SEC_OID_PKCS7_DATA) {
/* as we always have some inner data,
* we need to set it to something, just to fool the encoder enough to work on it
* and get us into nss_cms_encoder_notify at that point */
cinfo->rawContent = SECITEM_AllocItem(cmsg->poolp, NULL, 1);
if (cinfo->rawContent == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
}
return SECSuccess;
}
/*
* NSS_CMSContentInfo_SetContent_XXXX - typesafe wrappers for NSS_CMSContentInfo_SetContent
*/
/*
* data == NULL -> pass in data via NSS_CMSEncoder_Update
* data != NULL -> take this data
*/
SECStatus
NSS_CMSContentInfo_SetContent_Data(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, SECItem *data, PRBool detached)
{
if (NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_DATA, (void *)data) != SECSuccess)
return SECFailure;
cinfo->rawContent = (detached) ?
NULL : (data) ?
data : SECITEM_AllocItem(cmsg->poolp, NULL, 1);
return SECSuccess;
}
SECStatus
NSS_CMSContentInfo_SetContent_SignedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSSignedData *sigd)
{
return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_SIGNED_DATA, (void *)sigd);
}
SECStatus
NSS_CMSContentInfo_SetContent_EnvelopedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSEnvelopedData *envd)
{
return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_ENVELOPED_DATA, (void *)envd);
}
SECStatus
NSS_CMSContentInfo_SetContent_DigestedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSDigestedData *digd)
{
return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_DIGESTED_DATA, (void *)digd);
}
SECStatus
NSS_CMSContentInfo_SetContent_EncryptedData(NSSCMSMessage *cmsg, NSSCMSContentInfo *cinfo, NSSCMSEncryptedData *encd)
{
return NSS_CMSContentInfo_SetContent(cmsg, cinfo, SEC_OID_PKCS7_ENCRYPTED_DATA, (void *)encd);
}
/*
* NSS_CMSContentInfo_GetContent - get pointer to inner content
*
* needs to be casted...
*/
void *
NSS_CMSContentInfo_GetContent(NSSCMSContentInfo *cinfo)
{
switch (cinfo->contentTypeTag->offset) {
case SEC_OID_PKCS7_DATA:
case SEC_OID_PKCS7_SIGNED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
return cinfo->content.pointer;
default:
return NULL;
}
}
/*
* NSS_CMSContentInfo_GetInnerContent - get pointer to innermost content
*
* this is typically only called by NSS_CMSMessage_GetContent()
*/
SECItem *
NSS_CMSContentInfo_GetInnerContent(NSSCMSContentInfo *cinfo)
{
NSSCMSContentInfo *ccinfo;
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
case SEC_OID_PKCS7_DATA:
return cinfo->content.data; /* end of recursion - every message has to have a data cinfo */
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_SIGNED_DATA:
if ((ccinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) == NULL)
break;
return NSS_CMSContentInfo_GetContent(ccinfo);
default:
PORT_Assert(0);
break;
}
return NULL;
}
/*
* NSS_CMSContentInfo_GetContentType{Tag,OID} - find out (saving pointer to lookup result
* for future reference) and return the inner content type.
*/
SECOidTag
NSS_CMSContentInfo_GetContentTypeTag(NSSCMSContentInfo *cinfo)
{
if (cinfo->contentTypeTag == NULL)
cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
if (cinfo->contentTypeTag == NULL)
return SEC_OID_UNKNOWN;
return cinfo->contentTypeTag->offset;
}
SECItem *
NSS_CMSContentInfo_GetContentTypeOID(NSSCMSContentInfo *cinfo)
{
if (cinfo->contentTypeTag == NULL)
cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
if (cinfo->contentTypeTag == NULL)
return NULL;
return &(cinfo->contentTypeTag->oid);
}
/*
* NSS_CMSContentInfo_GetContentEncAlgTag - find out (saving pointer to lookup result
* for future reference) and return the content encryption algorithm tag.
*/
SECOidTag
NSS_CMSContentInfo_GetContentEncAlgTag(NSSCMSContentInfo *cinfo)
{
if (cinfo->contentEncAlgTag == SEC_OID_UNKNOWN)
cinfo->contentEncAlgTag = SECOID_GetAlgorithmTag(&(cinfo->contentEncAlg));
return cinfo->contentEncAlgTag;
}
/*
* NSS_CMSContentInfo_GetContentEncAlg - find out and return the content encryption algorithm tag.
*/
SECAlgorithmID *
NSS_CMSContentInfo_GetContentEncAlg(NSSCMSContentInfo *cinfo)
{
return &(cinfo->contentEncAlg);
}
SECStatus
NSS_CMSContentInfo_SetContentEncAlg(PLArenaPool *poolp, NSSCMSContentInfo *cinfo,
SECOidTag bulkalgtag, SECItem *parameters, int keysize)
{
SECStatus rv;
rv = SECOID_SetAlgorithmID(poolp, &(cinfo->contentEncAlg), bulkalgtag, parameters);
if (rv != SECSuccess)
return SECFailure;
cinfo->keysize = keysize;
return SECSuccess;
}
SECStatus
NSS_CMSContentInfo_SetContentEncAlgID(PLArenaPool *poolp, NSSCMSContentInfo *cinfo,
SECAlgorithmID *algid, int keysize)
{
SECStatus rv;
rv = SECOID_CopyAlgorithmID(poolp, &(cinfo->contentEncAlg), algid);
if (rv != SECSuccess)
return SECFailure;
if (keysize >= 0)
cinfo->keysize = keysize;
return SECSuccess;
}
void
NSS_CMSContentInfo_SetBulkKey(NSSCMSContentInfo *cinfo, PK11SymKey *bulkkey)
{
cinfo->bulkkey = PK11_ReferenceSymKey(bulkkey);
cinfo->keysize = PK11_GetKeyStrength(cinfo->bulkkey, &(cinfo->contentEncAlg));
}
PK11SymKey *
NSS_CMSContentInfo_GetBulkKey(NSSCMSContentInfo *cinfo)
{
if (cinfo->bulkkey == NULL)
return NULL;
return PK11_ReferenceSymKey(cinfo->bulkkey);
}
int
NSS_CMSContentInfo_GetBulkKeySize(NSSCMSContentInfo *cinfo)
{
return cinfo->keysize;
}

View File

@ -0,0 +1,792 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* Encryption/decryption routines for CMS implementation, none of which are exported.
*
* $Id: cmscipher.c,v 1.2 2000-06-13 21:56:27 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "secoid.h"
#include "secitem.h"
#include "pk11func.h"
#include "secerr.h"
#include "secpkcs5.h"
/*
* -------------------------------------------------------------------
* Cipher stuff.
*/
typedef SECStatus (*nss_cms_cipher_function) (void *, unsigned char *, unsigned int *,
unsigned int, const unsigned char *, unsigned int);
typedef SECStatus (*nss_cms_cipher_destroy) (void *, PRBool);
#define BLOCK_SIZE 4096
struct NSSCMSCipherContextStr {
void * cx; /* PK11 cipher context */
nss_cms_cipher_function doit;
nss_cms_cipher_destroy destroy;
PRBool encrypt; /* encrypt / decrypt switch */
int block_size; /* block & pad sizes for cipher */
int pad_size;
int pending_count; /* pending data (not yet en/decrypted */
unsigned char pending_buf[BLOCK_SIZE];/* because of blocking */
};
/*
* NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
* based on the given bulk * encryption key and algorithm identifier (which may include an iv).
*
* XXX Once both are working, it might be nice to combine this and the
* function below (for starting up encryption) into one routine, and just
* have two simple cover functions which call it.
*/
NSSCMSCipherContext *
NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid)
{
NSSCMSCipherContext *cc;
void *ciphercx;
CK_MECHANISM_TYPE mechanism;
SECItem *param;
PK11SlotInfo *slot;
SECOidTag algtag;
algtag = SECOID_GetAlgorithmTag(algid);
/* set param and mechanism */
if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
CK_MECHANISM pbeMech, cryptoMech;
SECItem *pbeParams;
SEC_PKCS5KeyAndPassword *keyPwd;
PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));
PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));
/* HACK ALERT!
* in this case, key is not actually a PK11SymKey *, but a SEC_PKCS5KeyAndPassword *
*/
keyPwd = (SEC_PKCS5KeyAndPassword *)key;
key = keyPwd->key;
/* find correct PK11 mechanism and parameters to initialize pbeMech */
pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
pbeParams = PK11_ParamFromAlgid(algid);
if (!pbeParams)
return NULL;
pbeMech.pParameter = pbeParams->data;
pbeMech.ulParameterLen = pbeParams->len;
/* now map pbeMech to cryptoMech */
if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, keyPwd->pwitem,
PR_FALSE) != CKR_OK) {
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
return NULL;
}
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
/* and use it to initialize param & mechanism */
if ((param = (SECItem *)PORT_ZAlloc(sizeof(SECItem))) == NULL)
return NULL;
param->data = (unsigned char *)cryptoMech.pParameter;
param->len = cryptoMech.ulParameterLen;
mechanism = cryptoMech.mechanism;
} else {
mechanism = PK11_AlgtagToMechanism(algtag);
if ((param = PK11_ParamFromAlgid(algid)) == NULL)
return NULL;
}
cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
if (cc == NULL) {
SECITEM_FreeItem(param,PR_TRUE);
return NULL;
}
/* figure out pad and block sizes */
cc->pad_size = PK11_GetBlockSize(mechanism, param);
slot = PK11_GetSlotFromKey(key);
cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
PK11_FreeSlot(slot);
/* create PK11 cipher context */
ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_DECRYPT, key, param);
SECITEM_FreeItem(param, PR_TRUE);
if (ciphercx == NULL) {
PORT_Free (cc);
return NULL;
}
cc->cx = ciphercx;
cc->doit = (nss_cms_cipher_function) PK11_CipherOp;
cc->destroy = (nss_cms_cipher_destroy) PK11_DestroyContext;
cc->encrypt = PR_FALSE;
cc->pending_count = 0;
return cc;
}
/*
* NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
* based on the given bulk encryption key and algorithm tag. Fill in the algorithm
* identifier (which may include an iv) appropriately.
*
* XXX Once both are working, it might be nice to combine this and the
* function above (for starting up decryption) into one routine, and just
* have two simple cover functions which call it.
*/
NSSCMSCipherContext *
NSS_CMSCipherContext_StartEncrypt(PRArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid)
{
NSSCMSCipherContext *cc;
void *ciphercx;
SECItem *param;
SECStatus rv;
CK_MECHANISM_TYPE mechanism;
PK11SlotInfo *slot;
PRBool needToEncodeAlgid = PR_FALSE;
SECOidTag algtag = SECOID_GetAlgorithmTag(algid);
/* set param and mechanism */
if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
CK_MECHANISM pbeMech, cryptoMech;
SECItem *pbeParams;
SEC_PKCS5KeyAndPassword *keyPwd;
PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));
PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));
/* HACK ALERT!
* in this case, key is not actually a PK11SymKey *, but a SEC_PKCS5KeyAndPassword *
*/
keyPwd = (SEC_PKCS5KeyAndPassword *)key;
key = keyPwd->key;
/* find correct PK11 mechanism and parameters to initialize pbeMech */
pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
pbeParams = PK11_ParamFromAlgid(algid);
if (!pbeParams)
return NULL;
pbeMech.pParameter = pbeParams->data;
pbeMech.ulParameterLen = pbeParams->len;
/* now map pbeMech to cryptoMech */
if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, keyPwd->pwitem,
PR_FALSE) != CKR_OK) {
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
return NULL;
}
SECITEM_ZfreeItem(pbeParams, PR_TRUE);
/* and use it to initialize param & mechanism */
if ((param = (SECItem *)PORT_ZAlloc(sizeof(SECItem))) == NULL)
return NULL;
param->data = (unsigned char *)cryptoMech.pParameter;
param->len = cryptoMech.ulParameterLen;
mechanism = cryptoMech.mechanism;
} else {
mechanism = PK11_AlgtagToMechanism(algtag);
if ((param = PK11_GenerateNewParam(mechanism, key)) == NULL)
return NULL;
needToEncodeAlgid = PR_TRUE;
}
cc = (NSSCMSCipherContext *)PORT_ZAlloc(sizeof(NSSCMSCipherContext));
if (cc == NULL)
return NULL;
/* now find pad and block sizes for our mechanism */
cc->pad_size = PK11_GetBlockSize(mechanism,param);
slot = PK11_GetSlotFromKey(key);
cc->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : cc->pad_size;
PK11_FreeSlot(slot);
/* and here we go, creating a PK11 cipher context */
ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT, key, param);
if (ciphercx == NULL) {
PORT_Free(cc);
cc = NULL;
goto loser;
}
/*
* These are placed after the CreateContextBySymKey() because some
* mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
* Don't move it from here.
* XXX is that right? the purpose of this is to get the correct algid
* containing the IVs etc. for encoding. this means we need to set this up
* BEFORE encoding the algid in the contentInfo, right?
*/
if (needToEncodeAlgid) {
rv = PK11_ParamToAlgid(algtag, param, poolp, algid);
if(rv != SECSuccess) {
PORT_Free(cc);
cc = NULL;
goto loser;
}
}
cc->cx = ciphercx;
cc->doit = (nss_cms_cipher_function)PK11_CipherOp;
cc->destroy = (nss_cms_cipher_destroy)PK11_DestroyContext;
cc->encrypt = PR_TRUE;
cc->pending_count = 0;
loser:
SECITEM_FreeItem(param, PR_TRUE);
return cc;
}
void
NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc)
{
PORT_Assert(cc != NULL);
if (cc == NULL)
return;
(*cc->destroy)(cc->cx, PR_TRUE);
PORT_Free(cc);
}
/*
* NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
*
* cc - the cipher context
* input_len - number of bytes used as input
* final - true if this is the final chunk of data
*
* Result can be used to perform memory allocations. Note that the amount
* is exactly accurate only when not doing a block cipher or when final
* is false, otherwise it is an upper bound on the amount because until
* we see the data we do not know how many padding bytes there are
* (always between 1 and bsize).
*
* Note that this can return zero, which does not mean that the decrypt
* operation can be skipped! (It simply means that there are not enough
* bytes to make up an entire block; the bytes will be reserved until
* there are enough to encrypt/decrypt at least one block.) However,
* if zero is returned it *does* mean that no output buffer need be
* passed in to the subsequent decrypt operation, as no output bytes
* will be stored.
*/
unsigned int
NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
{
int blocks, block_size;
PORT_Assert (! cc->encrypt);
block_size = cc->block_size;
/*
* If this is not a block cipher, then we always have the same
* number of output bytes as we had input bytes.
*/
if (block_size == 0)
return input_len;
/*
* On the final call, we will always use up all of the pending
* bytes plus all of the input bytes, *but*, there will be padding
* at the end and we cannot predict how many bytes of padding we
* will end up removing. The amount given here is actually known
* to be at least 1 byte too long (because we know we will have
* at least 1 byte of padding), but seemed clearer/better to me.
*/
if (final)
return cc->pending_count + input_len;
/*
* Okay, this amount is exactly what we will output on the
* next cipher operation. We will always hang onto the last
* 1 - block_size bytes for non-final operations. That is,
* we will do as many complete blocks as we can *except* the
* last block (complete or partial). (This is because until
* we know we are at the end, we cannot know when to interpret
* and removing the padding byte(s), which are guaranteed to
* be there.)
*/
blocks = (cc->pending_count + input_len - 1) / block_size;
return blocks * block_size;
}
/*
* NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
*
* cc - the cipher context
* input_len - number of bytes used as input
* final - true if this is the final chunk of data
*
* Result can be used to perform memory allocations.
*
* Note that this can return zero, which does not mean that the encrypt
* operation can be skipped! (It simply means that there are not enough
* bytes to make up an entire block; the bytes will be reserved until
* there are enough to encrypt/decrypt at least one block.) However,
* if zero is returned it *does* mean that no output buffer need be
* passed in to the subsequent encrypt operation, as no output bytes
* will be stored.
*/
unsigned int
NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final)
{
int blocks, block_size;
int pad_size;
PORT_Assert (cc->encrypt);
block_size = cc->block_size;
pad_size = cc->pad_size;
/*
* If this is not a block cipher, then we always have the same
* number of output bytes as we had input bytes.
*/
if (block_size == 0)
return input_len;
/*
* On the final call, we only send out what we need for
* remaining bytes plus the padding. (There is always padding,
* so even if we have an exact number of blocks as input, we
* will add another full block that is just padding.)
*/
if (final) {
if (pad_size == 0) {
return cc->pending_count + input_len;
} else {
blocks = (cc->pending_count + input_len) / pad_size;
blocks++;
return blocks*pad_size;
}
}
/*
* Now, count the number of complete blocks of data we have.
*/
blocks = (cc->pending_count + input_len) / block_size;
return blocks * block_size;
}
/*
* NSS_CMSCipherContext_Decrypt - do the decryption
*
* cc - the cipher context
* output - buffer for decrypted result bytes
* output_len_p - number of bytes in output
* max_output_len - upper bound on bytes to put into output
* input - pointer to input bytes
* input_len - number of input bytes
* final - true if this is the final chunk of data
*
* Decrypts a given length of input buffer (starting at "input" and
* containing "input_len" bytes), placing the decrypted bytes in
* "output" and storing the output length in "*output_len_p".
* "cc" is the return value from NSS_CMSCipher_StartDecrypt.
* When "final" is true, this is the last of the data to be decrypted.
*
* This is much more complicated than it sounds when the cipher is
* a block-type, meaning that the decryption function will only
* operate on whole blocks. But our caller is operating stream-wise,
* and can pass in any number of bytes. So we need to keep track
* of block boundaries. We save excess bytes between calls in "cc".
* We also need to determine which bytes are padding, and remove
* them from the output. We can only do this step when we know we
* have the final block of data. PKCS #7 specifies that the padding
* used for a block cipher is a string of bytes, each of whose value is
* the same as the length of the padding, and that all data is padded.
* (Even data that starts out with an exact multiple of blocks gets
* added to it another block, all of which is padding.)
*/
SECStatus
NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
unsigned int *output_len_p, unsigned int max_output_len,
const unsigned char *input, unsigned int input_len,
PRBool final)
{
int blocks, bsize, pcount, padsize;
unsigned int max_needed, ifraglen, ofraglen, output_len;
unsigned char *pbuf;
SECStatus rv;
PORT_Assert (! cc->encrypt);
/*
* Check that we have enough room for the output. Our caller should
* already handle this; failure is really an internal error (i.e. bug).
*/
max_needed = NSS_CMSCipherContext_DecryptLength(cc, input_len, final);
PORT_Assert (max_output_len >= max_needed);
if (max_output_len < max_needed) {
/* PORT_SetError (XXX); */
return SECFailure;
}
/*
* hardware encryption does not like small decryption sizes here, so we
* allow both blocking and padding.
*/
bsize = cc->block_size;
padsize = cc->pad_size;
/*
* When no blocking or padding work to do, we can simply call the
* cipher function and we are done.
*/
if (bsize == 0) {
return (* cc->doit) (cc->cx, output, output_len_p, max_output_len,
input, input_len);
}
pcount = cc->pending_count;
pbuf = cc->pending_buf;
output_len = 0;
if (pcount) {
/*
* Try to fill in an entire block, starting with the bytes
* we already have saved away.
*/
while (input_len && pcount < bsize) {
pbuf[pcount++] = *input++;
input_len--;
}
/*
* If we have at most a whole block and this is not our last call,
* then we are done for now. (We do not try to decrypt a lone
* single block because we cannot interpret the padding bytes
* until we know we are handling the very last block of all input.)
*/
if (input_len == 0 && !final) {
cc->pending_count = pcount;
if (output_len_p)
*output_len_p = 0;
return SECSuccess;
}
/*
* Given the logic above, we expect to have a full block by now.
* If we do not, there is something wrong, either with our own
* logic or with (length of) the data given to us.
*/
PORT_Assert ((padsize == 0) || (pcount % padsize) == 0);
if ((padsize != 0) && (pcount % padsize) != 0) {
PORT_Assert (final);
PORT_SetError (SEC_ERROR_BAD_DATA);
return SECFailure;
}
/*
* Decrypt the block.
*/
rv = (*cc->doit)(cc->cx, output, &ofraglen, max_output_len,
pbuf, pcount);
if (rv != SECSuccess)
return rv;
/*
* For now anyway, all of our ciphers have the same number of
* bytes of output as they do input. If this ever becomes untrue,
* then NSS_CMSCipherContext_DecryptLength needs to be made smarter!
*/
PORT_Assert(ofraglen == pcount);
/*
* Account for the bytes now in output.
*/
max_output_len -= ofraglen;
output_len += ofraglen;
output += ofraglen;
}
/*
* If this is our last call, we expect to have an exact number of
* blocks left to be decrypted; we will decrypt them all.
*
* If not our last call, we always save between 1 and bsize bytes
* until next time. (We must do this because we cannot be sure
* that none of the decrypted bytes are padding bytes until we
* have at least another whole block of data. You cannot tell by
* looking -- the data could be anything -- you can only tell by
* context, knowing you are looking at the last block.) We could
* decrypt a whole block now but it is easier if we just treat it
* the same way we treat partial block bytes.
*/
if (final) {
if (padsize) {
blocks = input_len / padsize;
ifraglen = blocks * padsize;
} else ifraglen = input_len;
PORT_Assert (ifraglen == input_len);
if (ifraglen != input_len) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
} else {
blocks = (input_len - 1) / bsize;
ifraglen = blocks * bsize;
PORT_Assert (ifraglen < input_len);
pcount = input_len - ifraglen;
PORT_Memcpy (pbuf, input + ifraglen, pcount);
cc->pending_count = pcount;
}
if (ifraglen) {
rv = (* cc->doit)(cc->cx, output, &ofraglen, max_output_len,
input, ifraglen);
if (rv != SECSuccess)
return rv;
/*
* For now anyway, all of our ciphers have the same number of
* bytes of output as they do input. If this ever becomes untrue,
* then sec_PKCS7DecryptLength needs to be made smarter!
*/
PORT_Assert (ifraglen == ofraglen);
if (ifraglen != ofraglen) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
output_len += ofraglen;
} else {
ofraglen = 0;
}
/*
* If we just did our very last block, "remove" the padding by
* adjusting the output length.
*/
if (final && (padsize != 0)) {
unsigned int padlen = *(output + ofraglen - 1);
PORT_Assert (padlen > 0 && padlen <= padsize);
if (padlen == 0 || padlen > padsize) {
PORT_SetError(SEC_ERROR_BAD_DATA);
return SECFailure;
}
output_len -= padlen;
}
PORT_Assert (output_len_p != NULL || output_len == 0);
if (output_len_p != NULL)
*output_len_p = output_len;
return SECSuccess;
}
/*
* NSS_CMSCipherContext_Encrypt - do the encryption
*
* cc - the cipher context
* output - buffer for decrypted result bytes
* output_len_p - number of bytes in output
* max_output_len - upper bound on bytes to put into output
* input - pointer to input bytes
* input_len - number of input bytes
* final - true if this is the final chunk of data
*
* Encrypts a given length of input buffer (starting at "input" and
* containing "input_len" bytes), placing the encrypted bytes in
* "output" and storing the output length in "*output_len_p".
* "cc" is the return value from NSS_CMSCipher_StartEncrypt.
* When "final" is true, this is the last of the data to be encrypted.
*
* This is much more complicated than it sounds when the cipher is
* a block-type, meaning that the encryption function will only
* operate on whole blocks. But our caller is operating stream-wise,
* and can pass in any number of bytes. So we need to keep track
* of block boundaries. We save excess bytes between calls in "cc".
* We also need to add padding bytes at the end. PKCS #7 specifies
* that the padding used for a block cipher is a string of bytes,
* each of whose value is the same as the length of the padding,
* and that all data is padded. (Even data that starts out with
* an exact multiple of blocks gets added to it another block,
* all of which is padding.)
*
* XXX I would kind of like to combine this with the function above
* which does decryption, since they have a lot in common. But the
* tricky parts about padding and filling blocks would be much
* harder to read that way, so I left them separate. At least for
* now until it is clear that they are right.
*/
SECStatus
NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
unsigned int *output_len_p, unsigned int max_output_len,
const unsigned char *input, unsigned int input_len,
PRBool final)
{
int blocks, bsize, padlen, pcount, padsize;
unsigned int max_needed, ifraglen, ofraglen, output_len;
unsigned char *pbuf;
SECStatus rv;
PORT_Assert (cc->encrypt);
/*
* Check that we have enough room for the output. Our caller should
* already handle this; failure is really an internal error (i.e. bug).
*/
max_needed = NSS_CMSCipherContext_EncryptLength (cc, input_len, final);
PORT_Assert (max_output_len >= max_needed);
if (max_output_len < max_needed) {
/* PORT_SetError (XXX); */
return SECFailure;
}
bsize = cc->block_size;
padsize = cc->pad_size;
/*
* When no blocking and padding work to do, we can simply call the
* cipher function and we are done.
*/
if (bsize == 0) {
return (*cc->doit)(cc->cx, output, output_len_p, max_output_len,
input, input_len);
}
pcount = cc->pending_count;
pbuf = cc->pending_buf;
output_len = 0;
if (pcount) {
/*
* Try to fill in an entire block, starting with the bytes
* we already have saved away.
*/
while (input_len && pcount < bsize) {
pbuf[pcount++] = *input++;
input_len--;
}
/*
* If we do not have a full block and we know we will be
* called again, then we are done for now.
*/
if (pcount < bsize && !final) {
cc->pending_count = pcount;
if (output_len_p != NULL)
*output_len_p = 0;
return SECSuccess;
}
/*
* If we have a whole block available, encrypt it.
*/
if ((padsize == 0) || (pcount % padsize) == 0) {
rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
pbuf, pcount);
if (rv != SECSuccess)
return rv;
/*
* For now anyway, all of our ciphers have the same number of
* bytes of output as they do input. If this ever becomes untrue,
* then sec_PKCS7EncryptLength needs to be made smarter!
*/
PORT_Assert (ofraglen == pcount);
/*
* Account for the bytes now in output.
*/
max_output_len -= ofraglen;
output_len += ofraglen;
output += ofraglen;
pcount = 0;
}
}
if (input_len) {
PORT_Assert (pcount == 0);
blocks = input_len / bsize;
ifraglen = blocks * bsize;
if (ifraglen) {
rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
input, ifraglen);
if (rv != SECSuccess)
return rv;
/*
* For now anyway, all of our ciphers have the same number of
* bytes of output as they do input. If this ever becomes untrue,
* then sec_PKCS7EncryptLength needs to be made smarter!
*/
PORT_Assert (ifraglen == ofraglen);
max_output_len -= ofraglen;
output_len += ofraglen;
output += ofraglen;
}
pcount = input_len - ifraglen;
PORT_Assert (pcount < bsize);
if (pcount)
PORT_Memcpy (pbuf, input + ifraglen, pcount);
}
if (final) {
padlen = padsize - (pcount % padsize);
PORT_Memset (pbuf + pcount, padlen, padlen);
rv = (* cc->doit) (cc->cx, output, &ofraglen, max_output_len,
pbuf, pcount+padlen);
if (rv != SECSuccess)
return rv;
/*
* For now anyway, all of our ciphers have the same number of
* bytes of output as they do input. If this ever becomes untrue,
* then sec_PKCS7EncryptLength needs to be made smarter!
*/
PORT_Assert (ofraglen == (pcount+padlen));
output_len += ofraglen;
} else {
cc->pending_count = pcount;
}
PORT_Assert (output_len_p != NULL || output_len == 0);
if (output_len_p != NULL)
*output_len_p = output_len;
return SECSuccess;
}

View File

@ -0,0 +1,688 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS decoding.
*
* $Id: cmsdecode.c,v 1.2 2000-06-13 21:56:28 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "cert.h"
#include "key.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "prtime.h"
#include "secerr.h"
struct NSSCMSDecoderContextStr {
SEC_ASN1DecoderContext * dcx; /* ASN.1 decoder context */
NSSCMSMessage * cmsg; /* backpointer to the root message */
SECOidTag type; /* type of message */
NSSCMSContent content; /* pointer to message */
NSSCMSDecoderContext * childp7dcx; /* inner CMS decoder context */
PRBool saw_contents;
int error;
NSSCMSContentCallback cb;
void * cb_arg;
};
static void nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind);
static SECStatus nss_cms_before_data(NSSCMSDecoderContext *p7dcx);
static SECStatus nss_cms_after_data(NSSCMSDecoderContext *p7dcx);
static SECStatus nss_cms_after_end(NSSCMSDecoderContext *p7dcx);
static void nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx,
const unsigned char *data, unsigned long len, PRBool final);
extern const SEC_ASN1Template NSSCMSMessageTemplate[];
/*
* nss_cms_decoder_notify -
* this is the driver of the decoding process. It gets called by the ASN.1
* decoder before and after an object is decoded.
* at various points in the decoding process, we intercept to set up and do
* further processing.
*/
static void
nss_cms_decoder_notify(void *arg, PRBool before, void *dest, int depth)
{
NSSCMSDecoderContext *p7dcx;
NSSCMSContentInfo *rootcinfo, *cinfo;
PRBool after = !before;
p7dcx = (NSSCMSDecoderContext *)arg;
rootcinfo = &(p7dcx->cmsg->contentInfo);
/* XXX error handling: need to set p7dcx->error */
#ifdef CMSDEBUG
fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
#endif
/* so what are we working on right now? */
switch (p7dcx->type) {
case SEC_OID_UNKNOWN:
/*
* right now, we are still decoding the OUTER (root) cinfo
* As soon as we know the inner content type, set up the info,
* but NO inner decoder or filter. The root decoder handles the first
* level children by itself - only for encapsulated contents (which
* are encoded as DER inside of an OCTET STRING) we need to set up a
* child decoder...
*/
if (after && dest == &(rootcinfo->contentType)) {
p7dcx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
p7dcx->content = rootcinfo->content; /* is this ready already ? need to alloc? */
/* XXX yes we need to alloc -- continue here */
}
break;
case SEC_OID_PKCS7_DATA:
/* this can only happen if the outermost cinfo has DATA in it */
/* otherwise, we handle this type implicitely in the inner decoders */
if (before && dest == &(rootcinfo->content)) {
/* fake it to cause the filter to put the data in the right place... */
/* we want the ASN.1 decoder to deliver the decoded bytes to us from now on */
SEC_ASN1DecoderSetFilterProc(p7dcx->dcx,
nss_cms_decoder_update_filter,
p7dcx,
(PRBool)(p7dcx->cb != NULL));
break;
}
if (after && dest == &(rootcinfo->content.data)) {
/* remove the filter */
SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
}
break;
case SEC_OID_PKCS7_SIGNED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
if (before && dest == &(rootcinfo->content))
break; /* we're not there yet */
if (p7dcx->content.pointer == NULL)
p7dcx->content = rootcinfo->content;
/* get this data type's inner contentInfo */
cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
if (before && dest == &(cinfo->contentType)) {
/* at this point, set up the &%$&$ back pointer */
/* we cannot do it later, because the content itself is optional! */
/* please give me C++ */
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
p7dcx->content.signedData->cmsg = p7dcx->cmsg;
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
p7dcx->content.digestedData->cmsg = p7dcx->cmsg;
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
p7dcx->content.envelopedData->cmsg = p7dcx->cmsg;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
p7dcx->content.encryptedData->cmsg = p7dcx->cmsg;
break;
}
}
if (before && dest == &(cinfo->rawContent)) {
/* we want the ASN.1 decoder to deliver the decoded bytes to us from now on */
SEC_ASN1DecoderSetFilterProc(p7dcx->dcx, nss_cms_decoder_update_filter,
p7dcx, (PRBool)(p7dcx->cb != NULL));
/* we're right in front of the data */
if (nss_cms_before_data(p7dcx) != SECSuccess) {
SEC_ASN1DecoderClearFilterProc(p7dcx->dcx); /* stop all processing */
p7dcx->error = PORT_GetError();
}
}
if (after && dest == &(cinfo->rawContent)) {
/* we're right after of the data */
if (nss_cms_after_data(p7dcx) != SECSuccess)
p7dcx->error = PORT_GetError();
/* we don't need to see the contents anymore */
SEC_ASN1DecoderClearFilterProc(p7dcx->dcx);
}
break;
#if 0 /* NIH */
case SEC_OID_PKCS7_AUTHENTICATED_DATA:
#endif
default:
/* unsupported or unknown message type - fail (more or less) gracefully */
p7dcx->error = SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE;
break;
}
}
/*
* nss_cms_before_data - set up the current encoder to receive data
*/
static SECStatus
nss_cms_before_data(NSSCMSDecoderContext *p7dcx)
{
SECStatus rv;
SECOidTag childtype;
PLArenaPool *poolp;
NSSCMSDecoderContext *childp7dcx;
NSSCMSContentInfo *cinfo;
const SEC_ASN1Template *template;
void *mark = NULL;
size_t size;
poolp = p7dcx->cmsg->poolp;
/* call _Decode_BeforeData handlers */
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
/* we're decoding a signedData, so set up the digests */
rv = NSS_CMSSignedData_Decode_BeforeData(p7dcx->content.signedData);
if (rv != SECSuccess)
return SECFailure;
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
/* we're encoding a digestedData, so set up the digest */
rv = NSS_CMSDigestedData_Decode_BeforeData(p7dcx->content.digestedData);
if (rv != SECSuccess)
return SECFailure;
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Decode_BeforeData(p7dcx->content.envelopedData);
if (rv != SECSuccess)
return SECFailure;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Decode_BeforeData(p7dcx->content.encryptedData);
if (rv != SECSuccess)
return SECFailure;
break;
default:
return SECFailure;
}
/* ok, now we have a pointer to cinfo */
/* find out what kind of data is encapsulated */
cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
if (childtype == SEC_OID_PKCS7_DATA) {
cinfo->content.data = SECITEM_AllocItem(poolp, NULL, 0);
if (cinfo->content.data == NULL)
/* set memory error */
return SECFailure;
p7dcx->childp7dcx = NULL;
return SECSuccess;
}
/* set up inner decoder */
if ((template = NSS_CMSUtil_GetTemplateByTypeTag(childtype)) == NULL)
return SECFailure;
childp7dcx = (NSSCMSDecoderContext *)PORT_ZAlloc(sizeof(NSSCMSDecoderContext));
if (childp7dcx == NULL)
return SECFailure;
mark = PORT_ArenaMark(poolp);
/* allocate space for the stuff we're creating */
size = NSS_CMSUtil_GetSizeByTypeTag(childtype);
childp7dcx->content.pointer = (void *)PORT_ArenaZAlloc(poolp, size);
if (childp7dcx->content.pointer == NULL)
goto loser;
/* start the child decoder */
childp7dcx->dcx = SEC_ASN1DecoderStart(poolp, childp7dcx->content.pointer, template);
if (childp7dcx->dcx == NULL)
goto loser;
/* the new decoder needs to notify, too */
SEC_ASN1DecoderSetNotifyProc(childp7dcx->dcx, nss_cms_decoder_notify, childp7dcx);
/* tell the parent decoder that it needs to feed us the content data */
p7dcx->childp7dcx = childp7dcx;
childp7dcx->type = childtype; /* our type */
childp7dcx->cmsg = p7dcx->cmsg; /* backpointer to root message */
/* should the child decoder encounter real data, it needs to give it to the caller */
childp7dcx->cb = p7dcx->cb;
childp7dcx->cb_arg = p7dcx->cb_arg;
/* now set up the parent to hand decoded data to the next level decoder */
p7dcx->cb = (NSSCMSContentCallback)NSS_CMSDecoder_Update;
p7dcx->cb_arg = childp7dcx;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
if (mark)
PORT_ArenaRelease(poolp, mark);
if (childp7dcx)
PORT_Free(childp7dcx);
p7dcx->childp7dcx = NULL;
return SECFailure;
}
static SECStatus
nss_cms_after_data(NSSCMSDecoderContext *p7dcx)
{
PLArenaPool *poolp;
NSSCMSDecoderContext *childp7dcx;
SECStatus rv;
poolp = p7dcx->cmsg->poolp;
/* Handle last block. This is necessary to flush out the last bytes
* of a possibly incomplete block */
nss_cms_decoder_work_data(p7dcx, NULL, 0, PR_TRUE);
/* finish any "inner" decoders - there's no more data coming... */
if (p7dcx->childp7dcx != NULL) {
childp7dcx = p7dcx->childp7dcx;
if (childp7dcx->dcx != NULL) {
if (SEC_ASN1DecoderFinish(childp7dcx->dcx) != SECSuccess) {
/* do what? free content? */
rv = SECFailure;
} else {
rv = nss_cms_after_end(childp7dcx);
}
if (rv != SECSuccess)
goto done;
}
PORT_Free(p7dcx->childp7dcx);
p7dcx->childp7dcx = NULL;
}
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
/* this will finish the digests and verify */
rv = NSS_CMSSignedData_Decode_AfterData(p7dcx->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Decode_AfterData(p7dcx->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = NSS_CMSDigestedData_Decode_AfterData(p7dcx->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Decode_AfterData(p7dcx->content.encryptedData);
break;
case SEC_OID_PKCS7_DATA:
/* do nothing */
break;
default:
rv = SECFailure;
break;
}
done:
return rv;
}
static SECStatus
nss_cms_after_end(NSSCMSDecoderContext *p7dcx)
{
SECStatus rv;
PLArenaPool *poolp;
poolp = p7dcx->cmsg->poolp;
switch (p7dcx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
rv = NSS_CMSSignedData_Decode_AfterEnd(p7dcx->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Decode_AfterEnd(p7dcx->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = NSS_CMSDigestedData_Decode_AfterEnd(p7dcx->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Decode_AfterEnd(p7dcx->content.encryptedData);
break;
case SEC_OID_PKCS7_DATA:
rv = SECSuccess;
break;
default:
rv = SECFailure; /* we should not have got that far... */
break;
}
return rv;
}
/*
* nss_cms_decoder_work_data - handle decoded data bytes.
*
* This function either decrypts the data if needed, and/or calculates digests
* on it, then either stores it or passes it on to the next level decoder.
*/
static void
nss_cms_decoder_work_data(NSSCMSDecoderContext *p7dcx,
const unsigned char *data, unsigned long len,
PRBool final)
{
NSSCMSContentInfo *cinfo;
unsigned char *buf = NULL;
unsigned char *dest;
unsigned int offset;
SECStatus rv;
SECItem *storage;
/*
* 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);
cinfo = NSS_CMSContent_GetContentInfo(p7dcx->content.pointer, p7dcx->type);
if (cinfo->ciphcx != NULL) {
/*
* we are decrypting.
*
* 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.
*/
unsigned int outlen = 0; /* length of decrypted data */
unsigned int buflen; /* length available for decrypted data */
/* find out about the length of decrypted data */
buflen = NSS_CMSCipherContext_DecryptLength(cinfo->ciphcx, len, final);
/*
* it might happen that we did not provide enough data for a full
* block (decryption unit), and that there is no output available
*/
/* no output available, AND no input? */
if (buflen == 0 && len == 0)
goto loser; /* bail out */
/*
* have inner decoder: pass the data on (means inner content type is NOT data)
* no inner decoder: we have DATA in here: either call callback or store
*/
if (buflen != 0) {
/* there will be some output - need to make room for it */
/* allocate buffer from the heap */
buf = (unsigned char *)PORT_Alloc(buflen);
if (buf == NULL) {
p7dcx->error = SEC_ERROR_NO_MEMORY;
goto loser;
}
}
/*
* decrypt incoming data
* buf can still be NULL here (and buflen == 0) here if we don't expect
* any output (see above), but we still need to call NSS_CMSCipherContext_Decrypt to
* keep track of incoming data
*/
rv = NSS_CMSCipherContext_Decrypt(cinfo->ciphcx, buf, &outlen, buflen,
data, len, final);
if (rv != SECSuccess) {
p7dcx->error = PORT_GetError();
goto loser;
}
PORT_Assert (final || outlen == buflen);
/* swap decrypted data in */
data = buf;
len = outlen;
}
if (len == 0)
return; /* nothing more to do */
/*
* Update the running digests with plaintext bytes (if we need to).
*/
if (cinfo->digcx)
NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
/* at this point, we have the plain decoded & decrypted data */
/* which is either more encoded DER which we need to hand to the child decoder */
/* or data we need to hand back to our caller */
/* pass the content back to our caller or */
/* feed our freshly decrypted and decoded data into child decoder */
if (p7dcx->cb != NULL) {
(*p7dcx->cb)(p7dcx->cb_arg, (const char *)data, len);
}
#if 1
else
#endif
if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) == SEC_OID_PKCS7_DATA) {
/* store it in "inner" data item as well */
/* find the DATA item in the encapsulated cinfo and store it there */
storage = cinfo->content.data;
offset = storage->len;
if (storage->len == 0) {
dest = (unsigned char *)PORT_ArenaAlloc(p7dcx->cmsg->poolp, len);
} else {
dest = (unsigned char *)PORT_ArenaGrow(p7dcx->cmsg->poolp,
storage->data,
storage->len,
storage->len + len);
}
if (dest == NULL) {
p7dcx->error = SEC_ERROR_NO_MEMORY;
goto loser;
}
storage->data = dest;
storage->len += len;
/* copy it in */
PORT_Memcpy(storage->data + offset, data, len);
}
loser:
if (buf)
PORT_Free (buf);
}
/*
* nss_cms_decoder_update_filter - process ASN.1 data
*
* once we have set up a filter in nss_cms_decoder_notify(),
* all data processed by the ASN.1 decoder is also passed through here.
* we pass the content bytes (as opposed to length and tag bytes) on to
* nss_cms_decoder_work_data().
*/
static void
nss_cms_decoder_update_filter (void *arg, const char *data, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind)
{
NSSCMSDecoderContext *p7dcx;
PORT_Assert (len); /* paranoia */
if (len == 0)
return;
p7dcx = (NSSCMSDecoderContext*)arg;
p7dcx->saw_contents = PR_TRUE;
/* pass on the content bytes only */
if (data_kind == SEC_ASN1_Contents)
nss_cms_decoder_work_data(p7dcx, (const unsigned char *) data, len, PR_FALSE);
}
/*
* NSS_CMSDecoder_Start - set up decoding of a DER-encoded CMS message
*
* "poolp" - pointer to arena for message, or NULL if new pool should be created
* "cb", "cb_arg" - callback function and argument for delivery of inner content
* "pwfn", pwfn_arg" - callback function for getting token password
* "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
*/
NSSCMSDecoderContext *
NSS_CMSDecoder_Start(PRArenaPool *poolp,
NSSCMSContentCallback cb, void *cb_arg,
PK11PasswordFunc pwfn, void *pwfn_arg,
NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg)
{
NSSCMSDecoderContext *p7dcx;
NSSCMSMessage *cmsg;
cmsg = NSS_CMSMessage_Create(poolp);
if (cmsg == NULL)
return NULL;
NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
NULL, NULL);
p7dcx = (NSSCMSDecoderContext*)PORT_ZAlloc(sizeof(NSSCMSDecoderContext));
if (p7dcx == NULL) {
NSS_CMSMessage_Destroy(cmsg);
return NULL;
}
p7dcx->dcx = SEC_ASN1DecoderStart(cmsg->poolp, cmsg, NSSCMSMessageTemplate);
if (p7dcx->dcx == NULL) {
PORT_Free (p7dcx);
NSS_CMSMessage_Destroy(cmsg);
return NULL;
}
SEC_ASN1DecoderSetNotifyProc (p7dcx->dcx, nss_cms_decoder_notify, p7dcx);
p7dcx->cmsg = cmsg;
p7dcx->type = SEC_OID_UNKNOWN;
p7dcx->cb = cb;
p7dcx->cb_arg = cb_arg;
return p7dcx;
}
/*
* NSS_CMSDecoder_Update - feed DER-encoded data to decoder
*/
SECStatus
NSS_CMSDecoder_Update(NSSCMSDecoderContext *p7dcx, const char *buf, unsigned long len)
{
if (p7dcx->dcx != NULL && p7dcx->error == 0) { /* if error is set already, don't bother */
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 == 0)
return SECSuccess;
/* there has been a problem, let's finish the decoder */
if (p7dcx->dcx != NULL) {
(void) SEC_ASN1DecoderFinish (p7dcx->dcx);
p7dcx->dcx = NULL;
}
PORT_SetError (p7dcx->error);
return SECFailure;
}
/*
* NSS_CMSDecoder_Cancel - stop decoding in case of error
*/
void
NSS_CMSDecoder_Cancel(NSSCMSDecoderContext *p7dcx)
{
/* XXXX what about inner decoders? running digests? decryption? */
/* XXXX there's a leak here! */
NSS_CMSMessage_Destroy(p7dcx->cmsg);
(void)SEC_ASN1DecoderFinish(p7dcx->dcx);
PORT_Free(p7dcx);
}
/*
* NSS_CMSDecoder_Finish - mark the end of inner content and finish decoding
*/
NSSCMSMessage *
NSS_CMSDecoder_Finish(NSSCMSDecoderContext *p7dcx)
{
NSSCMSMessage *cmsg;
cmsg = p7dcx->cmsg;
if (p7dcx->dcx == NULL || SEC_ASN1DecoderFinish(p7dcx->dcx) != SECSuccess ||
nss_cms_after_end(p7dcx) != SECSuccess)
{
NSS_CMSMessage_Destroy(cmsg); /* needs to get rid of pool if it's ours */
cmsg = NULL;
}
PORT_Free(p7dcx);
return cmsg;
}
NSSCMSMessage *
NSS_CMSMessage_CreateFromDER(SECItem *DERmessage,
NSSCMSContentCallback cb, void *cb_arg,
PK11PasswordFunc pwfn, void *pwfn_arg,
NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg)
{
NSSCMSDecoderContext *p7dcx;
/* first arg(poolp) == NULL => create our own pool */
p7dcx = NSS_CMSDecoder_Start(NULL, cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg);
(void) NSS_CMSDecoder_Update(p7dcx, (char *)DERmessage->data, DERmessage->len);
return NSS_CMSDecoder_Finish(p7dcx);
}

View File

@ -0,0 +1,223 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS digestedData methods.
*
* $Id: cmsdigdata.c,v 1.2 2000-06-13 21:56:28 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "secitem.h"
#include "secasn1.h"
#include "secoid.h"
#include "secerr.h"
/*
* NSS_CMSDigestedData_Create - create a digestedData object (presumably for encoding)
*
* version will be set by NSS_CMSDigestedData_Encode_BeforeStart
* digestAlg is passed as parameter
* contentInfo must be filled by the user
* digest will be calculated while encoding
*/
NSSCMSDigestedData *
NSS_CMSDigestedData_Create(NSSCMSMessage *cmsg, SECAlgorithmID *digestalg)
{
void *mark;
NSSCMSDigestedData *digd;
PLArenaPool *poolp;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
digd = (NSSCMSDigestedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSDigestedData));
if (digd == NULL)
goto loser;
digd->cmsg = cmsg;
if (SECOID_CopyAlgorithmID (poolp, &(digd->digestAlg), digestalg) != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return digd;
loser:
PORT_ArenaRelease(poolp, mark);
return NULL;
}
/*
* NSS_CMSDigestedData_Destroy - destroy a digestedData object
*/
void
NSS_CMSDigestedData_Destroy(NSSCMSDigestedData *digd)
{
/* everything's in a pool, so don't worry about the storage */
return;
}
/*
* NSS_CMSDigestedData_GetContentInfo - return pointer to digestedData object's contentInfo
*/
NSSCMSContentInfo *
NSS_CMSDigestedData_GetContentInfo(NSSCMSDigestedData *digd)
{
return &(digd->contentInfo);
}
/*
* NSS_CMSDigestedData_Encode_BeforeStart - do all the necessary things to a DigestedData
* before encoding begins.
*
* In particular:
* - set the right version number. The contentInfo's content type must be set up already.
*/
SECStatus
NSS_CMSDigestedData_Encode_BeforeStart(NSSCMSDigestedData *digd)
{
unsigned long version;
SECItem *dummy;
version = NSS_CMS_DIGESTED_DATA_VERSION_DATA;
if (NSS_CMSContentInfo_GetContentTypeTag(&(digd->contentInfo)) != SEC_OID_PKCS7_DATA)
version = NSS_CMS_DIGESTED_DATA_VERSION_ENCAP;
dummy = SEC_ASN1EncodeInteger(digd->cmsg->poolp, &(digd->version), version);
return (dummy == NULL) ? SECFailure : SECSuccess;
}
/*
* NSS_CMSDigestedData_Encode_BeforeData - do all the necessary things to a DigestedData
* before the encapsulated data is passed through the encoder.
*
* In detail:
* - set up the digests if necessary
*/
SECStatus
NSS_CMSDigestedData_Encode_BeforeData(NSSCMSDigestedData *digd)
{
/* set up the digests */
if (digd->digestAlg.algorithm.len != 0 && digd->digest.len == 0) {
/* if digest is already there, do nothing */
digd->contentInfo.digcx = NSS_CMSDigestContext_StartSingle(&(digd->digestAlg));
if (digd->contentInfo.digcx == NULL)
return SECFailure;
}
return SECSuccess;
}
/*
* NSS_CMSDigestedData_Encode_AfterData - do all the necessary things to a DigestedData
* after all the encapsulated data was passed through the encoder.
*
* In detail:
* - finish the digests
*/
SECStatus
NSS_CMSDigestedData_Encode_AfterData(NSSCMSDigestedData *digd)
{
/* did we have digest calculation going on? */
if (digd->contentInfo.digcx) {
if (NSS_CMSDigestContext_FinishSingle(digd->contentInfo.digcx,
digd->cmsg->poolp, &(digd->digest)) != SECSuccess)
return SECFailure; /* error has been set by NSS_CMSDigestContext_FinishSingle */
digd->contentInfo.digcx = NULL;
}
return SECSuccess;
}
/*
* NSS_CMSDigestedData_Decode_BeforeData - do all the necessary things to a DigestedData
* before the encapsulated data is passed through the encoder.
*
* In detail:
* - set up the digests if necessary
*/
SECStatus
NSS_CMSDigestedData_Decode_BeforeData(NSSCMSDigestedData *digd)
{
/* is there a digest algorithm yet? */
if (digd->digestAlg.algorithm.len == 0)
return SECFailure;
digd->contentInfo.digcx = NSS_CMSDigestContext_StartSingle(&(digd->digestAlg));
if (digd->contentInfo.digcx == NULL)
return SECFailure;
return SECSuccess;
}
/*
* NSS_CMSDigestedData_Decode_AfterData - do all the necessary things to a DigestedData
* after all the encapsulated data was passed through the encoder.
*
* In detail:
* - finish the digests
*/
SECStatus
NSS_CMSDigestedData_Decode_AfterData(NSSCMSDigestedData *digd)
{
/* did we have digest calculation going on? */
if (digd->contentInfo.digcx) {
if (NSS_CMSDigestContext_FinishSingle(digd->contentInfo.digcx,
digd->cmsg->poolp, &(digd->cdigest)) != SECSuccess)
return SECFailure; /* error has been set by NSS_CMSDigestContext_FinishSingle */
digd->contentInfo.digcx = NULL;
}
return SECSuccess;
}
/*
* NSS_CMSDigestedData_Decode_AfterEnd - finalize a digestedData.
*
* In detail:
* - check the digests for equality
*/
SECStatus
NSS_CMSDigestedData_Decode_AfterEnd(NSSCMSDigestedData *digd)
{
/* did we have digest calculation going on? */
if (digd->cdigest.len != 0) {
/* XXX comparision btw digest & cdigest */
/* XXX set status */
/* TODO!!!! */
}
return SECSuccess;
}

View File

@ -0,0 +1,259 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS digesting.
*
* $Id: cmsdigest.c,v 1.2 2000-06-13 21:56:28 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "cert.h"
#include "key.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "prtime.h"
#include "secerr.h"
struct NSSCMSDigestContextStr {
PRBool saw_contents;
int digcnt;
void ** digcxs;
SECHashObject ** digobjs;
};
/*
* NSS_CMSDigestContext_StartMultiple - start digest calculation using all the
* digest algorithms in "digestalgs" in parallel.
*/
NSSCMSDigestContext *
NSS_CMSDigestContext_StartMultiple(SECAlgorithmID **digestalgs)
{
NSSCMSDigestContext *cmsdigcx;
SECHashObject *digobj;
void *digcx;
int digcnt;
int i;
digcnt = (digestalgs == NULL) ? 0 : NSS_CMSArray_Count((void **)digestalgs);
cmsdigcx = (NSSCMSDigestContext *)PORT_Alloc(sizeof(NSSCMSDigestContext));
if (cmsdigcx == NULL)
return NULL;
if (digcnt > 0) {
cmsdigcx->digcxs = (void **)PORT_Alloc(digcnt * sizeof (void *));
cmsdigcx->digobjs = (SECHashObject **)PORT_Alloc(digcnt * sizeof(SECHashObject *));
if (cmsdigcx->digcxs == NULL || cmsdigcx->digobjs == NULL)
goto loser;
}
cmsdigcx->digcnt = 0;
/*
* Create a digest object context for each algorithm.
*/
for (i = 0; i < digcnt; i++) {
digobj = NSS_CMSUtil_GetHashObjByAlgID(digestalgs[i]);
/*
* 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)
continue;
digcx = (*digobj->create)();
if (digcx != NULL) {
(*digobj->begin) (digcx);
cmsdigcx->digobjs[cmsdigcx->digcnt] = digobj;
cmsdigcx->digcxs[cmsdigcx->digcnt] = digcx;
cmsdigcx->digcnt++;
}
}
cmsdigcx->saw_contents = PR_FALSE;
return cmsdigcx;
loser:
if (cmsdigcx) {
if (cmsdigcx->digobjs)
PORT_Free(cmsdigcx->digobjs);
if (cmsdigcx->digcxs)
PORT_Free(cmsdigcx->digcxs);
}
return NULL;
}
/*
* NSS_CMSDigestContext_StartSingle - same as NSS_CMSDigestContext_StartMultiple, but
* only one algorithm.
*/
NSSCMSDigestContext *
NSS_CMSDigestContext_StartSingle(SECAlgorithmID *digestalg)
{
SECAlgorithmID *digestalgs[] = { NULL, NULL }; /* fake array */
digestalgs[0] = digestalg;
return NSS_CMSDigestContext_StartMultiple(digestalgs);
}
/*
* NSS_CMSDigestContext_Update - feed more data into the digest machine
*/
void
NSS_CMSDigestContext_Update(NSSCMSDigestContext *cmsdigcx, const unsigned char *data, int len)
{
int i;
cmsdigcx->saw_contents = PR_TRUE;
for (i = 0; i < cmsdigcx->digcnt; i++)
(*cmsdigcx->digobjs[i]->update)(cmsdigcx->digcxs[i], data, len);
}
/*
* NSS_CMSDigestContext_Cancel - cancel digesting operation
*/
void
NSS_CMSDigestContext_Cancel(NSSCMSDigestContext *cmsdigcx)
{
int i;
for (i = 0; i < cmsdigcx->digcnt; i++)
(*cmsdigcx->digobjs[i]->destroy)(cmsdigcx->digcxs[i], PR_TRUE);
}
/*
* NSS_CMSDigestContext_FinishMultiple - finish the digests and put them
* into an array of SECItems (allocated on poolp)
*/
SECStatus
NSS_CMSDigestContext_FinishMultiple(NSSCMSDigestContext *cmsdigcx, PLArenaPool *poolp,
SECItem ***digestsp)
{
SECHashObject *digobj;
void *digcx;
SECItem **digests, *digest;
int i;
void *mark;
SECStatus rv = SECFailure;
/* no contents? do not update digests */
if (digestsp == NULL || !cmsdigcx->saw_contents) {
for (i = 0; i < cmsdigcx->digcnt; i++)
(*cmsdigcx->digobjs[i]->destroy)(cmsdigcx->digcxs[i], PR_TRUE);
rv = SECSuccess;
goto cleanup;
}
mark = PORT_ArenaMark (poolp);
/* allocate digest array & SECItems on arena */
digests = (SECItem **)PORT_ArenaAlloc(poolp, (cmsdigcx->digcnt+1) * sizeof(SECItem *));
digest = (SECItem *)PORT_ArenaZAlloc(poolp, cmsdigcx->digcnt * sizeof(SECItem));
if (digests == NULL || digest == NULL) {
goto loser;
}
for (i = 0; i < cmsdigcx->digcnt; i++, digest++) {
digcx = cmsdigcx->digcxs[i];
digobj = cmsdigcx->digobjs[i];
digest->data = (unsigned char*)PORT_ArenaAlloc(poolp, digobj->length);
if (digest->data == NULL)
goto loser;
digest->len = digobj->length;
(* digobj->end)(digcx, digest->data, &(digest->len), digest->len);
digests[i] = digest;
(* digobj->destroy)(digcx, PR_TRUE);
}
digests[i] = NULL;
*digestsp = digests;
rv = SECSuccess;
loser:
if (rv == SECSuccess)
PORT_ArenaUnmark(poolp, mark);
else
PORT_ArenaRelease(poolp, mark);
cleanup:
if (cmsdigcx->digcnt > 0) {
PORT_Free(cmsdigcx->digcxs);
PORT_Free(cmsdigcx->digobjs);
}
PORT_Free(cmsdigcx);
return rv;
}
/*
* NSS_CMSDigestContext_FinishSingle - same as NSS_CMSDigestContext_FinishMultiple,
* but for one digest.
*/
SECStatus
NSS_CMSDigestContext_FinishSingle(NSSCMSDigestContext *cmsdigcx, PLArenaPool *poolp,
SECItem *digest)
{
SECStatus rv = SECFailure;
SECItem **dp;
PLArenaPool *arena = NULL;
if ((arena = PORT_NewArena(1024)) == NULL)
goto loser;
/* get the digests into arena, then copy the first digest into poolp */
if (NSS_CMSDigestContext_FinishMultiple(cmsdigcx, arena, &dp) != SECSuccess)
goto loser;
/* now copy it into poolp */
if (SECITEM_CopyItem(poolp, digest, dp[0]) != SECSuccess)
goto loser;
rv = SECSuccess;
loser:
if (arena)
PORT_FreeArena(arena, PR_FALSE);
return rv;
}

View File

@ -0,0 +1,279 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS encryptedData methods.
*
* $Id: cmsencdata.c,v 1.2 2000-06-13 21:56:28 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "key.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "prtime.h"
#include "secerr.h"
/*
* NSS_CMSEncryptedData_Create - create an empty encryptedData object.
*
* "algorithm" specifies the bulk encryption algorithm to use.
* "keysize" is the key size.
*
* An error results in a return value of NULL and an error set.
* (Retrieve specific errors via PORT_GetError()/XP_GetError().)
*/
NSSCMSEncryptedData *
NSS_CMSEncryptedData_Create(NSSCMSMessage *cmsg, SECOidTag algorithm, int keysize)
{
void *mark;
NSSCMSEncryptedData *encd;
PLArenaPool *poolp;
SECAlgorithmID *pbe_algid;
SECStatus rv;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
encd = (NSSCMSEncryptedData *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSEncryptedData));
if (encd == NULL)
goto loser;
encd->cmsg = cmsg;
/* version is set in NSS_CMSEncryptedData_Encode_BeforeStart() */
switch (algorithm) {
/* XXX hmmm... hardcoded algorithms? */
case SEC_OID_RC2_CBC:
case SEC_OID_DES_EDE3_CBC:
case SEC_OID_DES_CBC:
rv = NSS_CMSContentInfo_SetContentEncAlg(poolp, &(encd->contentInfo), algorithm, NULL, keysize);
break;
default:
/* Assume password-based-encryption. At least, try that. */
pbe_algid = PK11_CreatePBEAlgorithmID(algorithm, 1, NULL);
if (pbe_algid == NULL) {
rv = SECFailure;
break;
}
rv = NSS_CMSContentInfo_SetContentEncAlgID(poolp, &(encd->contentInfo), pbe_algid, keysize);
SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
break;
}
if (rv != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return encd;
loser:
PORT_ArenaRelease(poolp, mark);
return NULL;
}
/*
* NSS_CMSEncryptedData_Destroy - destroy an encryptedData object
*/
void
NSS_CMSEncryptedData_Destroy(NSSCMSEncryptedData *encd)
{
/* everything's in a pool, so don't worry about the storage */
return;
}
/*
* NSS_CMSEncryptedData_GetContentInfo - return pointer to encryptedData object's contentInfo
*/
NSSCMSContentInfo *
NSS_CMSEncryptedData_GetContentInfo(NSSCMSEncryptedData *encd)
{
return &(encd->contentInfo);
}
/*
* NSS_CMSEncryptedData_Encode_BeforeStart - do all the necessary things to a EncryptedData
* before encoding begins.
*
* In particular:
* - set the correct version value.
* - get the encryption key
*/
SECStatus
NSS_CMSEncryptedData_Encode_BeforeStart(NSSCMSEncryptedData *encd)
{
int version;
PK11SymKey *bulkkey = NULL;
SECItem *dummy;
NSSCMSContentInfo *cinfo = &(encd->contentInfo);
if (NSS_CMSArray_IsEmpty((void **)encd->unprotectedAttr))
version = NSS_CMS_ENCRYPTED_DATA_VERSION;
else
version = NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR;
dummy = SEC_ASN1EncodeInteger (encd->cmsg->poolp, &(encd->version), version);
if (dummy == NULL)
return SECFailure;
/* now get content encryption key (bulk key) by using our cmsg callback */
if (encd->cmsg->decrypt_key_cb)
bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg,
NSS_CMSContentInfo_GetContentEncAlg(cinfo));
if (bulkkey == NULL)
return SECFailure;
/* store the bulk key in the contentInfo so that the encoder can find it */
NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
return SECSuccess;
}
/*
* NSS_CMSEncryptedData_Encode_BeforeData - set up encryption
*/
SECStatus
NSS_CMSEncryptedData_Encode_BeforeData(NSSCMSEncryptedData *encd)
{
NSSCMSContentInfo *cinfo;
PK11SymKey *bulkkey;
SECAlgorithmID *algid;
cinfo = &(encd->contentInfo);
/* find bulkkey and algorithm - must have been set by NSS_CMSEncryptedData_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 therefore essential that algid is a pointer to the "real" contentEncAlg,
* not just to a copy */
cinfo->ciphcx = NSS_CMSCipherContext_StartEncrypt(encd->cmsg->poolp, bulkkey, algid);
PK11_FreeSymKey(bulkkey);
if (cinfo->ciphcx == NULL)
return SECFailure;
return SECSuccess;
}
/*
* NSS_CMSEncryptedData_Encode_AfterData - finalize this encryptedData for encoding
*/
SECStatus
NSS_CMSEncryptedData_Encode_AfterData(NSSCMSEncryptedData *encd)
{
if (encd->contentInfo.ciphcx)
NSS_CMSCipherContext_Destroy(encd->contentInfo.ciphcx);
/* nothing to do after data */
return SECSuccess;
}
/*
* NSS_CMSEncryptedData_Decode_BeforeData - find bulk key & set up decryption
*/
SECStatus
NSS_CMSEncryptedData_Decode_BeforeData(NSSCMSEncryptedData *encd)
{
PK11SymKey *bulkkey = NULL;
NSSCMSContentInfo *cinfo;
SECAlgorithmID *bulkalg;
SECStatus rv = SECFailure;
cinfo = &(encd->contentInfo);
bulkalg = NSS_CMSContentInfo_GetContentEncAlg(cinfo);
if (encd->cmsg->decrypt_key_cb == NULL) /* no callback? no key../ */
goto loser;
bulkkey = (*encd->cmsg->decrypt_key_cb)(encd->cmsg->decrypt_key_cb_arg, bulkalg);
if (bulkkey == NULL)
/* no success finding a bulk key */
goto loser;
NSS_CMSContentInfo_SetBulkKey(cinfo, bulkkey);
cinfo->ciphcx = NSS_CMSCipherContext_StartDecrypt(bulkkey, bulkalg);
if (cinfo->ciphcx == NULL)
goto loser; /* error has been set by NSS_CMSCipherContext_StartDecrypt */
/*
* HACK ALERT!!
* For PKCS5 Encryption Algorithms, the bulkkey is actually a different
* structure. Therefore, we need to set the bulkkey to the actual key
* prior to freeing it.
*/
if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) {
SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
bulkkey = keyPwd->key;
}
/* we are done with (this) bulkkey now. */
PK11_FreeSymKey(bulkkey);
rv = SECSuccess;
loser:
return rv;
}
/*
* NSS_CMSEncryptedData_Decode_AfterData - finish decrypting this encryptedData's content
*/
SECStatus
NSS_CMSEncryptedData_Decode_AfterData(NSSCMSEncryptedData *encd)
{
NSS_CMSCipherContext_Destroy(encd->contentInfo.ciphcx);
return SECSuccess;
}
/*
* NSS_CMSEncryptedData_Decode_AfterEnd - finish decoding this encryptedData
*/
SECStatus
NSS_CMSEncryptedData_Decode_AfterEnd(NSSCMSEncryptedData *encd)
{
/* apply final touches */
return SECSuccess;
}

View File

@ -0,0 +1,740 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS encoding.
*
* $Id: cmsencode.c,v 1.2 2000-06-13 21:56:29 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "cert.h"
#include "key.h"
#include "secasn1.h"
#include "secoid.h"
#include "secrng.h"
#include "secitem.h"
#include "pk11func.h"
#include "secerr.h"
struct nss_cms_encoder_output {
NSSCMSContentCallback outputfn;
void *outputarg;
PLArenaPool *destpoolp;
SECItem *dest;
};
struct NSSCMSEncoderContextStr {
SEC_ASN1EncoderContext * ecx; /* ASN.1 encoder context */
PRBool ecxupdated; /* true if data was handed in */
NSSCMSMessage * cmsg; /* pointer to the root message */
SECOidTag type; /* type tag of the current content */
NSSCMSContent content; /* pointer to current content */
struct nss_cms_encoder_output output; /* output function */
int error; /* error code */
NSSCMSEncoderContext * childp7ecx; /* link to child encoder context */
};
static SECStatus nss_cms_before_data(NSSCMSEncoderContext *p7ecx);
static SECStatus nss_cms_after_data(NSSCMSEncoderContext *p7ecx);
static SECStatus nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len);
static SECStatus nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
const unsigned char *data, unsigned long len,
PRBool final, PRBool innermost);
extern const SEC_ASN1Template NSSCMSMessageTemplate[];
/*
* The little output function that the ASN.1 encoder calls to hand
* us bytes which we in turn hand back to our caller (via the callback
* they gave us).
*/
static void
nss_cms_encoder_out(void *arg, const char *buf, unsigned long len,
int depth, SEC_ASN1EncodingPart data_kind)
{
struct nss_cms_encoder_output *output = (struct nss_cms_encoder_output *)arg;
unsigned char *dest;
unsigned long offset;
#ifdef CMSDEBUG
int i;
fprintf(stderr, "kind = %d, depth = %d, len = %d\n", data_kind, depth, len);
for (i=0; i < len; i++) {
fprintf(stderr, " %02x%s", (unsigned int)buf[i] & 0xff, ((i % 16) == 15) ? "\n" : "");
}
if ((i % 16) != 0)
fprintf(stderr, "\n");
#endif
if (output->outputfn != NULL)
/* call output callback with DER data */
output->outputfn(output->outputarg, buf, len);
if (output->dest != NULL) {
/* store DER data in SECItem */
offset = output->dest->len;
if (offset == 0) {
dest = (unsigned char *)PORT_ArenaAlloc(output->destpoolp, len);
} else {
dest = (unsigned char *)PORT_ArenaGrow(output->destpoolp,
output->dest->data,
output->dest->len,
output->dest->len + len);
}
if (dest == NULL)
/* oops */
return;
output->dest->data = dest;
output->dest->len += len;
/* copy it in */
PORT_Memcpy(output->dest->data + offset, buf, len);
}
}
/*
* nss_cms_encoder_notify - ASN.1 encoder callback
*
* this function is called by the ASN.1 encoder before and after the encoding of
* every object. here, it is used to keep track of data structures, set up
* encryption and/or digesting and possibly set up child encoders.
*/
static void
nss_cms_encoder_notify(void *arg, PRBool before, void *dest, int depth)
{
NSSCMSEncoderContext *p7ecx;
NSSCMSContentInfo *rootcinfo, *cinfo;
PRBool after = !before;
PLArenaPool *poolp;
SECOidTag childtype;
SECItem *item;
p7ecx = (NSSCMSEncoderContext *)arg;
PORT_Assert(p7ecx != NULL);
rootcinfo = &(p7ecx->cmsg->contentInfo);
poolp = p7ecx->cmsg->poolp;
#ifdef CMSDEBUG
fprintf(stderr, "%6.6s, dest = 0x%08x, depth = %d\n", before ? "before" : "after", dest, depth);
#endif
/*
* Watch for the content field, at which point we want to instruct
* the ASN.1 encoder to start taking bytes from the buffer.
*/
switch (p7ecx->type) {
default:
case SEC_OID_UNKNOWN:
/* we're still in the root message */
if (after && dest == &(rootcinfo->contentType)) {
/* got the content type OID now - so find out the type tag */
p7ecx->type = NSS_CMSContentInfo_GetContentTypeTag(rootcinfo);
/* set up a pointer to our current content */
p7ecx->content = rootcinfo->content;
}
break;
case SEC_OID_PKCS7_DATA:
if (before && dest == &(rootcinfo->rawContent)) {
/* just set up encoder to grab from user - no encryption or digesting */
if ((item = rootcinfo->content.data) != NULL)
(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
else
SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
}
break;
case SEC_OID_PKCS7_SIGNED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
/* when we know what the content is, we encode happily until we reach the inner content */
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
if (after && dest == &(cinfo->contentType)) {
/* we're right before encoding the data (if we have some or not) */
/* (for encrypted data, we're right before the contentEncAlg which may change */
/* in nss_cms_before_data because of IV calculation when setting up encryption) */
if (nss_cms_before_data(p7ecx) != SECSuccess)
p7ecx->error = PORT_GetError();
}
if (before && dest == &(cinfo->rawContent)) {
if (childtype == SEC_OID_PKCS7_DATA && (item = cinfo->content.data) != NULL)
/* we have data - feed it in */
(void)nss_cms_encoder_work_data(p7ecx, NULL, item->data, item->len, PR_TRUE, PR_TRUE);
else
/* else try to get it from user */
SEC_ASN1EncoderSetTakeFromBuf(p7ecx->ecx);
}
if (after && dest == &(cinfo->rawContent)) {
if (nss_cms_after_data(p7ecx) != SECSuccess)
p7ecx->error = PORT_GetError();
SEC_ASN1EncoderClearNotifyProc(p7ecx->ecx); /* no need to get notified anymore */
}
break;
}
}
/*
* nss_cms_before_data - setup the current encoder to receive data
*/
static SECStatus
nss_cms_before_data(NSSCMSEncoderContext *p7ecx)
{
SECStatus rv;
SECOidTag childtype;
NSSCMSContentInfo *cinfo;
PLArenaPool *poolp;
NSSCMSEncoderContext *childp7ecx;
const SEC_ASN1Template *template;
poolp = p7ecx->cmsg->poolp;
/* call _Encode_BeforeData handlers */
switch (p7ecx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
/* we're encoding a signedData, so set up the digests */
rv = NSS_CMSSignedData_Encode_BeforeData(p7ecx->content.signedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
/* we're encoding a digestedData, so set up the digest */
rv = NSS_CMSDigestedData_Encode_BeforeData(p7ecx->content.digestedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Encode_BeforeData(p7ecx->content.envelopedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Encode_BeforeData(p7ecx->content.encryptedData);
break;
default:
rv = SECFailure;
}
if (rv != SECSuccess)
return SECFailure;
/* ok, now we have a pointer to cinfo */
/* find out what kind of data is encapsulated */
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
switch (childtype) {
case SEC_OID_PKCS7_SIGNED_DATA:
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
case SEC_OID_PKCS7_DIGESTED_DATA:
#if 0
case SEC_OID_PKCS7_DATA: /* XXX here also??? maybe yes! */
#endif
/* in these cases, we need to set up a child encoder! */
/* create new encoder context */
childp7ecx = PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
if (childp7ecx == NULL)
return SECFailure;
/* the CHILD encoder needs to hand its encoded data to the CURRENT encoder
* (which will encrypt and/or digest it)
* this needs to route back into our update function
* which finds the lowest encoding context & encrypts and computes digests */
childp7ecx->type = childtype;
childp7ecx->content = cinfo->content;
/* use the non-recursive update function here, of course */
childp7ecx->output.outputfn = (NSSCMSContentCallback)nss_cms_encoder_update;
childp7ecx->output.outputarg = p7ecx;
childp7ecx->output.destpoolp = NULL;
childp7ecx->output.dest = NULL;
childp7ecx->cmsg = p7ecx->cmsg;
template = NSS_CMSUtil_GetTemplateByTypeTag(childtype);
if (template == NULL)
goto loser; /* cannot happen */
/* now initialize the data for encoding the first third */
switch (childp7ecx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
break;
case SEC_OID_PKCS7_DATA:
rv = SECSuccess;
break;
}
if (rv != SECSuccess)
goto loser;
/*
* Initialize the BER encoder.
*/
childp7ecx->ecx = SEC_ASN1EncoderStart(cinfo->content.pointer, template,
nss_cms_encoder_out, &(childp7ecx->output));
if (childp7ecx->ecx == NULL)
goto loser;
childp7ecx->ecxupdated = PR_FALSE;
/*
* Indicate that we are streaming. We will be streaming until we
* get past the contents bytes.
*/
SEC_ASN1EncoderSetStreaming(childp7ecx->ecx);
/*
* The notify function will watch for the contents field.
*/
SEC_ASN1EncoderSetNotifyProc(childp7ecx->ecx, nss_cms_encoder_notify, childp7ecx);
/* please note that we are NOT calling SEC_ASN1EncoderUpdate here to kick off the */
/* encoding process - we'll do that from the update function instead */
/* otherwise we'd be encoding data from a call of the notify function of the */
/* parent encoder (which would not work) */
/* this will kick off the encoding process & encode everything up to the content bytes,
* at which point the notify function sets streaming mode (and possibly creates
* another child encoder). */
if (SEC_ASN1EncoderUpdate(childp7ecx->ecx, NULL, 0) != SECSuccess)
goto loser;
p7ecx->childp7ecx = childp7ecx;
break;
case SEC_OID_PKCS7_DATA:
p7ecx->childp7ecx = NULL;
break;
default:
/* we do not know this type */
p7ecx->error = SEC_ERROR_BAD_DER;
break;
}
return SECSuccess;
loser:
if (childp7ecx) {
if (childp7ecx->ecx)
SEC_ASN1EncoderFinish(childp7ecx->ecx);
PORT_Free(childp7ecx);
}
return SECFailure;
}
static SECStatus
nss_cms_after_data(NSSCMSEncoderContext *p7ecx)
{
SECStatus rv;
switch (p7ecx->type) {
case SEC_OID_PKCS7_SIGNED_DATA:
/* this will finish the digests and sign */
rv = NSS_CMSSignedData_Encode_AfterData(p7ecx->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Encode_AfterData(p7ecx->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = NSS_CMSDigestedData_Encode_AfterData(p7ecx->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Encode_AfterData(p7ecx->content.encryptedData);
break;
case SEC_OID_PKCS7_DATA:
/* do nothing */
break;
default:
rv = SECFailure;
break;
}
return rv;
}
/*
* nss_cms_encoder_work_data - process incoming data
*
* (from the user or the next encoding layer)
* Here, we need to digest and/or encrypt, then pass it on
*/
static SECStatus
nss_cms_encoder_work_data(NSSCMSEncoderContext *p7ecx, SECItem *dest,
const unsigned char *data, unsigned long len,
PRBool final, PRBool innermost)
{
unsigned char *buf = NULL;
SECStatus rv;
NSSCMSContentInfo *cinfo;
rv = SECSuccess; /* may as well be optimistic */
/*
* 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);
/* we got data (either from the caller, or from a lower level encoder) */
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
/* Update the running digest. */
if (len && cinfo->digcx != NULL)
NSS_CMSDigestContext_Update(cinfo->digcx, data, len);
/* Encrypt this chunk. */
if (cinfo->ciphcx != NULL) {
unsigned int inlen; /* length of data being encrypted */
unsigned int outlen; /* length of encrypted data */
unsigned int buflen; /* length available for encrypted data */
inlen = len;
buflen = NSS_CMSCipherContext_EncryptLength(cinfo->ciphcx, inlen, final);
if (buflen == 0) {
/*
* No output is expected, but the input data may be buffered
* so we still have to call Encrypt.
*/
rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, NULL, NULL, 0,
data, inlen, final);
if (final) {
len = 0;
goto done;
}
return rv;
}
if (dest != NULL)
buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cmsg->poolp, buflen);
else
buf = (unsigned char*)PORT_Alloc(buflen);
if (buf == NULL) {
rv = SECFailure;
} else {
rv = NSS_CMSCipherContext_Encrypt(cinfo->ciphcx, buf, &outlen, buflen,
data, inlen, final);
data = buf;
len = outlen;
}
if (rv != SECSuccess)
/* encryption or malloc failed? */
return rv;
}
/*
* at this point (data,len) has everything we'd like to give to the CURRENT encoder
* (which will encode it, then hand it back to the user or the parent encoder)
* We don't encode the data if we're innermost and we're told not to include the data
*/
if (p7ecx->ecx != NULL && len && (!innermost || cinfo->rawContent != NULL))
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, (const char *)data, len);
done:
if (cinfo->ciphcx != NULL) {
if (dest != NULL) {
dest->data = buf;
dest->len = len;
} else if (buf != NULL) {
PORT_Free (buf);
}
}
return rv;
}
/*
* nss_cms_encoder_update - deliver encoded data to the next higher level
*
* no recursion here because we REALLY want to end up at the next higher encoder!
*/
static SECStatus
nss_cms_encoder_update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
{
/* XXX Error handling needs help. Return what? Do "Finish" on failure? */
return nss_cms_encoder_work_data (p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_FALSE);
}
/*
* NSS_CMSEncoder_Start - set up encoding of a CMS message
*
* "cmsg" - message to encode
* "outputfn", "outputarg" - callback function for delivery of DER-encoded output
* will not be called if NULL.
* "dest" - if non-NULL, pointer to SECItem that will hold the DER-encoded output
* "destpoolp" - pool to allocate DER-encoded output in
* "pwfn", pwfn_arg" - callback function for getting token password
* "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
* "detached_digestalgs", "detached_digests" - digests from detached content
*/
NSSCMSEncoderContext *
NSS_CMSEncoder_Start(NSSCMSMessage *cmsg,
NSSCMSContentCallback outputfn, void *outputarg,
SECItem *dest, PLArenaPool *destpoolp,
PK11PasswordFunc pwfn, void *pwfn_arg,
NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
{
NSSCMSEncoderContext *p7ecx;
SECStatus rv;
NSSCMSContentInfo *cinfo;
NSS_CMSMessage_SetEncodingParams(cmsg, pwfn, pwfn_arg, decrypt_key_cb, decrypt_key_cb_arg,
detached_digestalgs, detached_digests);
p7ecx = (NSSCMSEncoderContext *)PORT_ZAlloc(sizeof(NSSCMSEncoderContext));
if (p7ecx == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return NULL;
}
p7ecx->cmsg = cmsg;
p7ecx->output.outputfn = outputfn;
p7ecx->output.outputarg = outputarg;
p7ecx->output.dest = dest;
p7ecx->output.destpoolp = destpoolp;
p7ecx->type = SEC_OID_UNKNOWN;
cinfo = NSS_CMSMessage_GetContentInfo(cmsg);
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
case SEC_OID_PKCS7_SIGNED_DATA:
rv = NSS_CMSSignedData_Encode_BeforeStart(cinfo->content.signedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
rv = NSS_CMSEnvelopedData_Encode_BeforeStart(cinfo->content.envelopedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
rv = NSS_CMSDigestedData_Encode_BeforeStart(cinfo->content.digestedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
rv = NSS_CMSEncryptedData_Encode_BeforeStart(cinfo->content.encryptedData);
break;
default:
rv = SECFailure;
break;
}
if (rv != SECSuccess)
return NULL;
/* Initialize the BER encoder.
* Note that this will not encode anything until the first call to SEC_ASN1EncoderUpdate */
p7ecx->ecx = SEC_ASN1EncoderStart(cmsg, NSSCMSMessageTemplate,
nss_cms_encoder_out, &(p7ecx->output));
if (p7ecx->ecx == NULL) {
PORT_Free (p7ecx);
return NULL;
}
p7ecx->ecxupdated = PR_FALSE;
/*
* Indicate that we are streaming. We will be streaming until we
* get past the contents bytes.
*/
SEC_ASN1EncoderSetStreaming(p7ecx->ecx);
/*
* The notify function will watch for the contents field.
*/
SEC_ASN1EncoderSetNotifyProc(p7ecx->ecx, nss_cms_encoder_notify, p7ecx);
/* this will kick off the encoding process & encode everything up to the content bytes,
* at which point the notify function sets streaming mode (and possibly creates
* a child encoder). */
if (SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0) != SECSuccess) {
PORT_Free (p7ecx);
return NULL;
}
return p7ecx;
}
/*
* NSS_CMSEncoder_Update - take content data delivery from the user
*
* "p7ecx" - encoder context
* "data" - content data
* "len" - length of content data
*
* need to find the lowest level (and call SEC_ASN1EncoderUpdate on the way down),
* then hand the data to the work_data fn
*/
SECStatus
NSS_CMSEncoder_Update(NSSCMSEncoderContext *p7ecx, const char *data, unsigned long len)
{
SECStatus rv;
NSSCMSContentInfo *cinfo;
SECOidTag childtype;
if (p7ecx->error)
return SECFailure;
/* hand data to the innermost decoder */
if (p7ecx->childp7ecx) {
/* recursion here */
rv = NSS_CMSEncoder_Update(p7ecx->childp7ecx, data, len);
} else {
/* we are at innermost decoder */
/* find out about our inner content type - must be data */
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
if (childtype != SEC_OID_PKCS7_DATA)
return SECFailure;
/* and we must not have preset data */
if (cinfo->content.data != NULL)
return SECFailure;
/* hand it the data so it can encode it (let DER trickle up the chain) */
rv = nss_cms_encoder_work_data(p7ecx, NULL, (const unsigned char *)data, len, PR_FALSE, PR_TRUE);
}
return rv;
}
/*
* NSS_CMSEncoder_Cancel - stop all encoding
*
* we need to walk down the chain of encoders and the finish them from the innermost out
*/
SECStatus
NSS_CMSEncoder_Cancel(NSSCMSEncoderContext *p7ecx)
{
SECStatus rv = SECFailure;
/* XXX do this right! */
/*
* Finish any inner decoders before us so that all the encoded data is flushed
* This basically finishes all the decoders from the innermost to the outermost.
* Finishing an inner decoder may result in data being updated to the outer decoder
* while we are already in NSS_CMSEncoder_Finish, but that's allright.
*/
if (p7ecx->childp7ecx) {
rv = NSS_CMSEncoder_Cancel(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
/* remember rv for now */
}
/*
* On the way back up, there will be no more data (if we had an
* inner encoder, it is done now!)
* Flush out any remaining data and/or finish digests.
*/
rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
if (rv != SECSuccess)
goto loser;
p7ecx->childp7ecx = NULL;
/* kick the encoder back into working mode again.
* We turn off streaming stuff (which will cause the encoder to continue
* encoding happily, now that we have all the data (like digests) ready for it).
*/
SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
/* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
loser:
SEC_ASN1EncoderFinish(p7ecx->ecx);
PORT_Free (p7ecx);
return rv;
}
/*
* NSS_CMSEncoder_Finish - signal the end of data
*
* we need to walk down the chain of encoders and the finish them from the innermost out
*/
SECStatus
NSS_CMSEncoder_Finish(NSSCMSEncoderContext *p7ecx)
{
SECStatus rv = SECFailure;
NSSCMSContentInfo *cinfo;
SECOidTag childtype;
/*
* Finish any inner decoders before us so that all the encoded data is flushed
* This basically finishes all the decoders from the innermost to the outermost.
* Finishing an inner decoder may result in data being updated to the outer decoder
* while we are already in NSS_CMSEncoder_Finish, but that's allright.
*/
if (p7ecx->childp7ecx) {
rv = NSS_CMSEncoder_Finish(p7ecx->childp7ecx); /* frees p7ecx->childp7ecx */
if (rv != SECSuccess)
goto loser;
}
/*
* On the way back up, there will be no more data (if we had an
* inner encoder, it is done now!)
* Flush out any remaining data and/or finish digests.
*/
rv = nss_cms_encoder_work_data(p7ecx, NULL, NULL, 0, PR_TRUE, (p7ecx->childp7ecx == NULL));
if (rv != SECSuccess)
goto loser;
p7ecx->childp7ecx = NULL;
/* find out about our inner content type - must be data */
cinfo = NSS_CMSContent_GetContentInfo(p7ecx->content.pointer, p7ecx->type);
childtype = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
if (childtype == SEC_OID_PKCS7_DATA && cinfo->content.data == NULL) {
SEC_ASN1EncoderClearTakeFromBuf(p7ecx->ecx);
/* now that TakeFromBuf is off, this will kick this encoder to finish encoding */
rv = SEC_ASN1EncoderUpdate(p7ecx->ecx, NULL, 0);
}
SEC_ASN1EncoderClearStreaming(p7ecx->ecx);
if (p7ecx->error)
rv = SECFailure;
loser:
SEC_ASN1EncoderFinish(p7ecx->ecx);
PORT_Free (p7ecx);
return rv;
}

View File

@ -0,0 +1,416 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS envelopedData methods.
*
* $Id: cmsenvdata.c,v 1.2 2000-06-13 21:56:29 chrisk%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"
/*
* 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);
/* XXX storage ??? */
}
/*
* 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;
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;
}
/* get a pointer to "our" recipientinfo */
ri = envd->recipientInfos[recipient_list[rlIndex]->riIndex];
cinfo = &(envd->contentInfo);
bulkalgtag = NSS_CMSContentInfo_GetContentEncAlgTag(cinfo);
bulkkey = NSS_CMSRecipientInfo_UnwrapBulkKey(ri,recipient_list[rlIndex]->subIndex,
recipient_list[rlIndex]->cert,
recipient_list[rlIndex]->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 */
/*
* HACK ALERT!!
* For PKCS5 Encryption Algorithms, the bulkkey is actually a different
* structure. Therefore, we need to set the bulkkey to the actual key
* prior to freeing it.
*/
if (SEC_PKCS5IsAlgorithmPBEAlg(bulkalg)) {
SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
bulkkey = keyPwd->key;
}
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->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;
}

View File

@ -0,0 +1,330 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* Support routines for CMS implementation, none of which are exported.
*
* Do not export this file! If something in here is really needed outside
* of smime code, first try to add a CMS interface which will do it for
* you. If that has a problem, then just move out what you need, changing
* its name as appropriate!
*
* $Id: cmslocal.h,v 1.2 2000-06-13 21:56:29 chrisk%netscape.com Exp $
*/
#ifndef _CMSLOCAL_H_
#define _CMSLOCAL_H_
#include "cms.h"
#include "cmsreclist.h"
#include "secasn1t.h"
extern const SEC_ASN1Template NSSCMSContentInfoTemplate[];
/************************************************************************/
SEC_BEGIN_PROTOS
/***********************************************************************
* cmscipher.c - en/decryption routines
***********************************************************************/
/*
* NSS_CMSCipherContext_StartDecrypt - create a cipher context to do decryption
* based on the given bulk * encryption key and algorithm identifier (which may include an iv).
*/
extern NSSCMSCipherContext *
NSS_CMSCipherContext_StartDecrypt(PK11SymKey *key, SECAlgorithmID *algid);
/*
* NSS_CMSCipherContext_StartEncrypt - create a cipher object to do encryption,
* based on the given bulk encryption key and algorithm tag. Fill in the algorithm
* identifier (which may include an iv) appropriately.
*/
extern NSSCMSCipherContext *
NSS_CMSCipherContext_StartEncrypt(PRArenaPool *poolp, PK11SymKey *key, SECAlgorithmID *algid);
extern void
NSS_CMSCipherContext_Destroy(NSSCMSCipherContext *cc);
/*
* NSS_CMSCipherContext_DecryptLength - find the output length of the next call to decrypt.
*
* cc - the cipher context
* input_len - number of bytes used as input
* final - true if this is the final chunk of data
*
* Result can be used to perform memory allocations. Note that the amount
* is exactly accurate only when not doing a block cipher or when final
* is false, otherwise it is an upper bound on the amount because until
* we see the data we do not know how many padding bytes there are
* (always between 1 and bsize).
*/
extern unsigned int
NSS_CMSCipherContext_DecryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final);
/*
* NSS_CMSCipherContext_EncryptLength - find the output length of the next call to encrypt.
*
* cc - the cipher context
* input_len - number of bytes used as input
* final - true if this is the final chunk of data
*
* Result can be used to perform memory allocations.
*/
extern unsigned int
NSS_CMSCipherContext_EncryptLength(NSSCMSCipherContext *cc, unsigned int input_len, PRBool final);
/*
* NSS_CMSCipherContext_Decrypt - do the decryption
*
* cc - the cipher context
* output - buffer for decrypted result bytes
* output_len_p - number of bytes in output
* max_output_len - upper bound on bytes to put into output
* input - pointer to input bytes
* input_len - number of input bytes
* final - true if this is the final chunk of data
*
* Decrypts a given length of input buffer (starting at "input" and
* containing "input_len" bytes), placing the decrypted bytes in
* "output" and storing the output length in "*output_len_p".
* "cc" is the return value from NSS_CMSCipher_StartDecrypt.
* When "final" is true, this is the last of the data to be decrypted.
*/
extern SECStatus
NSS_CMSCipherContext_Decrypt(NSSCMSCipherContext *cc, unsigned char *output,
unsigned int *output_len_p, unsigned int max_output_len,
const unsigned char *input, unsigned int input_len,
PRBool final);
/*
* NSS_CMSCipherContext_Encrypt - do the encryption
*
* cc - the cipher context
* output - buffer for decrypted result bytes
* output_len_p - number of bytes in output
* max_output_len - upper bound on bytes to put into output
* input - pointer to input bytes
* input_len - number of input bytes
* final - true if this is the final chunk of data
*
* Encrypts a given length of input buffer (starting at "input" and
* containing "input_len" bytes), placing the encrypted bytes in
* "output" and storing the output length in "*output_len_p".
* "cc" is the return value from NSS_CMSCipher_StartEncrypt.
* When "final" is true, this is the last of the data to be encrypted.
*/
extern SECStatus
NSS_CMSCipherContext_Encrypt(NSSCMSCipherContext *cc, unsigned char *output,
unsigned int *output_len_p, unsigned int max_output_len,
const unsigned char *input, unsigned int input_len,
PRBool final);
/************************************************************************
* cmspubkey.c - public key operations
************************************************************************/
/*
* NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
*
* this function takes a symmetric key and encrypts it using an RSA public key
* according to PKCS#1 and RFC2633 (S/MIME)
*/
extern SECStatus
NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
SECItem *encKey);
/*
* NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
*
* this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
* key handle. Please note that the actual unwrapped key data may not be allowed to leave
* a hardware token...
*/
extern PK11SymKey *
NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag);
extern SECStatus
NSS_CMSUtil_EncryptSymKey_MISSI(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
SECOidTag symalgtag, SECItem *encKey, SECItem **pparams, void *pwfn_arg);
extern PK11SymKey *
NSS_CMSUtil_DecryptSymKey_MISSI(SECKEYPrivateKey *privkey, SECItem *encKey,
SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg);
extern SECStatus
NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
SECItem *originatorPubKey);
extern PK11SymKey *
NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey,
SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg);
/************************************************************************
* cmsreclist.c - recipient list stuff
************************************************************************/
extern NSSCMSRecipient **nss_cms_recipient_list_create(NSSCMSRecipientInfo **recipientinfos);
extern void nss_cms_recipient_list_destroy(NSSCMSRecipient **recipient_list);
extern NSSCMSRecipientEncryptedKey *NSS_CMSRecipientEncryptedKey_Create(PLArenaPool *poolp);
/************************************************************************
* cmsarray.c - misc array functions
************************************************************************/
/*
* NSS_CMSArray_Alloc - allocate an array in an arena
*/
extern void **
NSS_CMSArray_Alloc(PRArenaPool *poolp, int n);
/*
* NSS_CMSArray_Add - add an element to the end of an array
*/
extern SECStatus
NSS_CMSArray_Add(PRArenaPool *poolp, void ***array, void *obj);
/*
* NSS_CMSArray_IsEmpty - check if array is empty
*/
extern PRBool
NSS_CMSArray_IsEmpty(void **array);
/*
* NSS_CMSArray_Count - count number of elements in array
*/
extern int
NSS_CMSArray_Count(void **array);
/*
* NSS_CMSArray_Sort - sort an array ascending, in place
*
* If "secondary" is not NULL, the same reordering gets applied to it.
* If "tertiary" is not NULL, the same reordering gets applied to it.
* "compare" is a function that returns
* < 0 when the first element is less than the second
* = 0 when the first element is equal to the second
* > 0 when the first element is greater than the second
*/
extern void
NSS_CMSArray_Sort(void **primary, int (*compare)(void *,void *), void **secondary, void **tertiary);
/************************************************************************
* cmsattr.c - misc attribute functions
************************************************************************/
/*
* NSS_CMSAttribute_Create - create an attribute
*
* if value is NULL, the attribute won't have a value. It can be added later
* with NSS_CMSAttribute_AddValue.
*/
extern NSSCMSAttribute *
NSS_CMSAttribute_Create(PRArenaPool *poolp, SECOidTag oidtag, SECItem *value, PRBool encoded);
/*
* NSS_CMSAttribute_AddValue - add another value to an attribute
*/
extern SECStatus
NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value);
/*
* NSS_CMSAttribute_GetType - return the OID tag
*/
extern SECOidTag
NSS_CMSAttribute_GetType(NSSCMSAttribute *attr);
/*
* NSS_CMSAttribute_GetValue - return the first attribute value
*
* We do some sanity checking first:
* - Multiple values are *not* expected.
* - Empty values are *not* expected.
*/
extern SECItem *
NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr);
/*
* NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
*/
extern PRBool
NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av);
/*
* NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
*
* If you are wondering why this routine does not reorder the attributes
* first, and might be tempted to make it do so, see the comment by the
* call to ReorderAttributes in cmsencode.c. (Or, see who else calls this
* and think long and hard about the implications of making it always
* do the reordering.)
*/
extern SECItem *
NSS_CMSAttributeArray_Encode(PRArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest);
/*
* NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
*
* make sure that the order of the attributes guarantees valid DER (which must be
* in lexigraphically ascending order for a SET OF); if reordering is necessary it
* will be done in place (in attrs).
*/
extern SECStatus
NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs);
/*
* NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
* find one that matches the specified object ID.
*
* If "only" is true, then make sure that there is not more than one attribute
* of the same type. Otherwise, just return the first one found. (XXX Does
* anybody really want that first-found behavior? It was like that when I found it...)
*/
extern NSSCMSAttribute *
NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only);
/*
* NSS_CMSAttributeArray_AddAttr - add an attribute to an
* array of attributes.
*/
extern SECStatus
NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr);
/*
* NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
*/
extern SECStatus
NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECOidTag type, SECItem *value, PRBool encoded);
/************************************************************************/
SEC_END_PROTOS
#endif /* _CMSLOCAL_H_ */

View File

@ -0,0 +1,313 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS message methods.
*
* $Id: cmsmessage.c,v 1.2 2000-06-13 21:56:30 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "cert.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "secerr.h"
/*
* NSS_CMSMessage_Create - create a CMS message object
*
* "poolp" - arena to allocate memory from, or NULL if new arena should be created
*/
NSSCMSMessage *
NSS_CMSMessage_Create(PLArenaPool *poolp)
{
void *mark;
NSSCMSMessage *cmsg;
PRBool poolp_is_ours = PR_FALSE;
if (poolp == NULL) {
poolp = PORT_NewArena (1024); /* XXX what is right value? */
if (poolp == NULL)
return NULL;
poolp_is_ours = PR_TRUE;
}
if (!poolp_is_ours)
mark = PORT_ArenaMark(poolp);
cmsg = (NSSCMSMessage *)PORT_ArenaZAlloc (poolp, sizeof(NSSCMSMessage));
if (cmsg == NULL) {
if (!poolp_is_ours)
PORT_ArenaRelease(poolp, mark);
else
PORT_FreeArena(poolp, PR_FALSE);
return NULL;
}
cmsg->poolp = poolp;
cmsg->poolp_is_ours = poolp_is_ours;
cmsg->refCount = 1;
if (!poolp_is_ours)
PORT_ArenaUnmark(poolp, mark);
return cmsg;
}
/*
* NSS_CMSMessage_SetEncodingParams - set up a CMS message object for encoding or decoding
*
* "cmsg" - message object
* "pwfn", pwfn_arg" - callback function for getting token password
* "decrypt_key_cb", "decrypt_key_cb_arg" - callback function for getting bulk key for encryptedData
* "detached_digestalgs", "detached_digests" - digests from detached content
*/
void
NSS_CMSMessage_SetEncodingParams(NSSCMSMessage *cmsg,
PK11PasswordFunc pwfn, void *pwfn_arg,
NSSCMSGetDecryptKeyCallback decrypt_key_cb, void *decrypt_key_cb_arg,
SECAlgorithmID **detached_digestalgs, SECItem **detached_digests)
{
if (pwfn)
PK11_SetPasswordFunc(pwfn);
cmsg->pwfn_arg = pwfn_arg;
cmsg->decrypt_key_cb = decrypt_key_cb;
cmsg->decrypt_key_cb_arg = decrypt_key_cb_arg;
cmsg->detached_digestalgs = detached_digestalgs;
cmsg->detached_digests = detached_digests;
}
/*
* NSS_CMSMessage_Destroy - destroy a CMS message and all of its sub-pieces.
*/
void
NSS_CMSMessage_Destroy(NSSCMSMessage *cmsg)
{
PORT_Assert (cmsg->refCount > 0);
if (cmsg->refCount <= 0) /* oops */
return;
cmsg->refCount--; /* thread safety? */
if (cmsg->refCount > 0)
return;
NSS_CMSContentInfo_Destroy(&(cmsg->contentInfo));
/* if poolp is not NULL, cmsg is the owner of its arena */
if (cmsg->poolp_is_ours)
PORT_FreeArena (cmsg->poolp, PR_FALSE); /* XXX clear it? */
}
/*
* NSS_CMSMessage_Copy - return a copy of the given message.
*
* The copy may be virtual or may be real -- either way, the result needs
* to be passed to NSS_CMSMessage_Destroy later (as does the original).
*/
NSSCMSMessage *
NSS_CMSMessage_Copy(NSSCMSMessage *cmsg)
{
if (cmsg == NULL)
return NULL;
PORT_Assert (cmsg->refCount > 0);
cmsg->refCount++; /* XXX chrisk thread safety? */
return cmsg;
}
/*
* NSS_CMSMessage_GetArena - return a pointer to the message's arena pool
*/
PLArenaPool *
NSS_CMSMessage_GetArena(NSSCMSMessage *cmsg)
{
return cmsg->poolp;
}
/*
* NSS_CMSMessage_GetContentInfo - return a pointer to the top level contentInfo
*/
NSSCMSContentInfo *
NSS_CMSMessage_GetContentInfo(NSSCMSMessage *cmsg)
{
return &(cmsg->contentInfo);
}
/*
* Return a pointer to the actual content.
* In the case of those types which are encrypted, this returns the *plain* content.
* In case of nested contentInfos, this descends and retrieves the innermost content.
*/
SECItem *
NSS_CMSMessage_GetContent(NSSCMSMessage *cmsg)
{
/* this is a shortcut */
return NSS_CMSContentInfo_GetInnerContent(NSS_CMSMessage_GetContentInfo(cmsg));
}
/*
* NSS_CMSMessage_ContentLevelCount - count number of levels of CMS content objects in this message
*
* CMS data content objects do not count.
*/
int
NSS_CMSMessage_ContentLevelCount(NSSCMSMessage *cmsg)
{
int count = 0;
NSSCMSContentInfo *cinfo;
/* walk down the chain of contentinfos */
for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
count++;
}
return count;
}
/*
* NSS_CMSMessage_ContentLevel - find content level #n
*
* CMS data content objects do not count.
*/
NSSCMSContentInfo *
NSS_CMSMessage_ContentLevel(NSSCMSMessage *cmsg, int n)
{
int count = 0;
NSSCMSContentInfo *cinfo;
/* walk down the chain of contentinfos */
for (cinfo = &(cmsg->contentInfo); cinfo != NULL && count < n; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
count++;
}
return cinfo;
}
/*
* NSS_CMSMessage_ContainsCertsOrCrls - see if message contains certs along the way
*/
PRBool
NSS_CMSMessage_ContainsCertsOrCrls(NSSCMSMessage *cmsg)
{
NSSCMSContentInfo *cinfo;
/* descend into CMS message */
for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo)) {
if (NSS_CMSContentInfo_GetContentTypeTag(cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
continue; /* next level */
if (NSS_CMSSignedData_ContainsCertsOrCrls(cinfo->content.signedData))
return PR_TRUE;
}
return PR_FALSE;
}
/*
* NSS_CMSMessage_IsEncrypted - see if message contains a encrypted submessage
*/
PRBool
NSS_CMSMessage_IsEncrypted(NSSCMSMessage *cmsg)
{
NSSCMSContentInfo *cinfo;
/* walk down the chain of contentinfos */
for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo))
{
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
case SEC_OID_PKCS7_ENVELOPED_DATA:
case SEC_OID_PKCS7_ENCRYPTED_DATA:
return PR_TRUE;
default:
break;
}
}
return PR_FALSE;
}
/*
* NSS_CMSMessage_IsSigned - see if message contains a signed submessage
*
* If the CMS message has a SignedData with a signature (not just a SignedData)
* 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
NSS_CMSMessage_IsSigned(NSSCMSMessage *cmsg)
{
NSSCMSContentInfo *cinfo;
/* walk down the chain of contentinfos */
for (cinfo = &(cmsg->contentInfo); cinfo != NULL; cinfo = NSS_CMSContentInfo_GetChildContentInfo(cinfo))
{
switch (NSS_CMSContentInfo_GetContentTypeTag(cinfo)) {
case SEC_OID_PKCS7_SIGNED_DATA:
if (!NSS_CMSArray_IsEmpty((void **)cinfo->content.signedData->signerInfos))
return PR_TRUE;
break;
default:
break;
}
}
return PR_FALSE;
}
/*
* NSS_CMSMessage_IsContentEmpty - see if content is empty
*
* returns PR_TRUE is innermost content length is < minLen
* XXX need the encrypted content length (why?)
*/
PRBool
NSS_CMSMessage_IsContentEmpty(NSSCMSMessage *cmsg, unsigned int minLen)
{
SECItem *item = NULL;
if (cmsg == NULL)
return PR_TRUE;
item = NSS_CMSContentInfo_GetContent(NSS_CMSMessage_GetContentInfo(cmsg));
if (!item) {
return PR_TRUE;
} else if(item->len <= minLen) {
return PR_TRUE;
}
return PR_FALSE;
}

View File

@ -0,0 +1,531 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS public key crypto
*
* $Id: cmspubkey.c,v 1.2 2000-06-13 21:56:30 chrisk%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"
/* ====== RSA ======================================================================= */
/*
* NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
*
* this function takes a symmetric key and encrypts it using an RSA public key
* according to PKCS#1 and RFC2633 (S/MIME)
*/
SECStatus
NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *bulkkey,
SECItem *encKey)
{
SECOidTag certalgtag; /* the certificate's encryption algorithm */
SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
SECStatus rv;
SECKEYPublicKey *publickey;
int data_len;
void *mark;
/* sanity check */
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
PORT_Assert(certalgtag == SEC_OID_PKCS1_RSA_ENCRYPTION);
encalgtag = SEC_OID_PKCS1_RSA_ENCRYPTION;
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL)
goto loser;
mark = PORT_ArenaMark(poolp);
/* allocate memory for the encrypted key */
data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */
encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
encKey->len = data_len;
if (encKey->data == NULL)
goto loser;
/* encrypt the key now */
rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
publickey, bulkkey, encKey);
SECKEY_DestroyPublicKey(publickey);
if (rv != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
/*
* NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
*
* this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
* key handle. Please note that the actual unwrapped key data may not be allowed to leave
* a hardware token...
*/
PK11SymKey *
NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag)
{
/* that's easy */
return PK11_PubUnwrapSymKey(privkey, encKey, PK11_AlgtagToMechanism(bulkalgtag), CKA_DECRYPT, 0);
}
/* ====== MISSI (Fortezza) ========================================================== */
extern const SEC_ASN1Template NSS_SMIMEKEAParamTemplateAllParams[];
SECStatus
NSS_CMSUtil_EncryptSymKey_MISSI(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *bulkkey,
SECOidTag symalgtag, SECItem *encKey, SECItem **pparams, void *pwfn_arg)
{
SECOidTag certalgtag; /* the certificate's encryption algorithm */
SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
SECStatus rv = SECFailure;
SECItem *params = NULL;
SECStatus err;
PK11SymKey *tek;
CERTCertificate *ourCert;
SECKEYPublicKey *ourPubKey, *publickey = NULL;
SECKEYPrivateKey *ourPrivKey = NULL;
NSSCMSKEATemplateSelector whichKEA;
NSSCMSSMIMEKEAParameters keaParams;
PLArenaPool *arena;
extern const SEC_ASN1Template *nss_cms_get_kea_template(NSSCMSKEATemplateSelector whichTemplate);
/* Clear keaParams, since cleanup code checks the lengths */
(void) memset(&keaParams, 0, sizeof(keaParams));
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD ||
certalgtag == SEC_OID_MISSI_KEA_DSS ||
certalgtag == SEC_OID_MISSI_KEA);
#define SMIME_FORTEZZA_RA_LENGTH 128
#define SMIME_FORTEZZA_IV_LENGTH 24
#define SMIME_FORTEZZA_MAX_KEY_SIZE 256
/* We really want to show our KEA tag as the key exchange algorithm tag. */
encalgtag = SEC_OID_NETSCAPE_SMIME_KEA;
/* Get the public key of the recipient. */
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL) goto loser;
/* Find our own cert, and extract its keys. */
ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg);
if (ourCert == NULL) goto loser;
arena = PORT_NewArena(1024);
if (arena == NULL)
goto loser;
ourPubKey = CERT_ExtractPublicKey(ourCert);
if (ourPubKey == NULL) {
CERT_DestroyCertificate(ourCert);
goto loser;
}
/* While we're here, copy the public key into the outgoing
* KEA parameters. */
SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey));
SECKEY_DestroyPublicKey(ourPubKey);
ourPubKey = NULL;
/* Extract our private key in order to derive the KEA key. */
ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg);
CERT_DestroyCertificate(ourCert); /* we're done with this */
if (!ourPrivKey)
goto loser;
/* Prepare raItem with 128 bytes (filled with zeros). */
keaParams.originatorRA.data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH);
keaParams.originatorRA.len = SMIME_FORTEZZA_RA_LENGTH;
/* Generate the TEK (token exchange key) which we use
* to wrap the bulk encryption key. (keaparams.originatorRA) will be
* filled with a random seed which we need to send to
* the recipient. (user keying material in RFC2630/DSA speak) */
tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
&keaParams.originatorRA, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, pwfn_arg);
SECKEY_DestroyPublicKey(publickey);
SECKEY_DestroyPrivateKey(ourPrivKey);
publickey = NULL;
ourPrivKey = NULL;
if (!tek)
goto loser;
/* allocate space for the wrapped key data */
encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE;
if (encKey->data == NULL) {
PK11_FreeSymKey(tek);
goto loser;
}
/* Wrap the bulk key. What we do with the resulting data
depends on whether we're using Skipjack to wrap the key. */
switch (PK11_AlgtagToMechanism(symalgtag)) {
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, we use the wrap mechanism because we can do it on the hardware */
err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey);
whichKEA = NSSCMSKEAUsesSkipjack;
break;
default:
/* Not SKIPJACK, we encrypt the raw key data */
keaParams.nonSkipjackIV.data =
(unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH);
keaParams.nonSkipjackIV.len = SMIME_FORTEZZA_IV_LENGTH;
err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey);
if (err != SECSuccess)
goto loser;
if (encKey->len != PK11_GetKeyLength(bulkkey)) {
/* The size of the encrypted key is not the same as
that of the original bulk key, presumably due to
padding. Encode and store the real size of the
bulk key. */
if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL)
err = (SECStatus)PORT_GetError();
else
/* use full template for encoding */
whichKEA = NSSCMSKEAUsesNonSkipjackWithPaddedEncKey;
}
else
/* enc key length == bulk key length */
whichKEA = NSSCMSKEAUsesNonSkipjack;
break;
}
PK11_FreeSymKey(tek);
if (err != SECSuccess)
goto loser;
/* Encode the KEA parameters into the recipient info. */
params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA));
if (params == NULL)
goto loser;
/* pass back the algorithm params */
*pparams = params;
rv = SECSuccess;
loser:
if (arena)
PORT_FreeArena(arena, PR_FALSE);
if (publickey)
SECKEY_DestroyPublicKey(publickey);
if (ourPrivKey)
SECKEY_DestroyPrivateKey(ourPrivKey);
return rv;
}
PK11SymKey *
NSS_CMSUtil_DecryptSymKey_MISSI(SECKEYPrivateKey *privkey, SECItem *encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
{
/* fortezza: do a key exchange */
SECStatus err;
CK_MECHANISM_TYPE bulkType;
PK11SymKey *tek;
SECKEYPublicKey *originatorPubKey;
NSSCMSSMIMEKEAParameters keaParams;
PK11SymKey *bulkkey;
int bulkLength;
(void) memset(&keaParams, 0, sizeof(keaParams));
/* NOTE: this uses the SMIME v2 recipientinfo for compatibility.
All additional KEA parameters are DER-encoded in the encryption algorithm parameters */
/* Decode the KEA algorithm parameters. */
err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams,
&(keyEncAlg->parameters));
if (err != SECSuccess)
goto loser;
/* get originator's public key */
originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
keaParams.originatorKEAKey.len);
if (originatorPubKey == NULL)
goto loser;
/* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
The Derive function generates a shared secret and combines it with the originatorRA
data to come up with an unique session key */
tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
&keaParams.originatorRA, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, pwfn_arg);
SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
if (tek == NULL)
goto loser;
/* 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,
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 the optional parameter bulkKeySize is present,
bulk key size is different than the encrypted key size */
if (keaParams.bulkKeySize.len > 0) {
err = SEC_ASN1DecodeItem(NULL, &bulkLength,
SEC_IntegerTemplate,
&keaParams.bulkKeySize);
if (err != SECSuccess)
goto loser;
}
bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV,
encKey, bulkType, CKA_DECRYPT, bulkLength);
break;
}
return bulkkey;
loser:
return NULL;
}
/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
SECStatus
NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
SECItem *pubKey)
{
#if 0 /* not yet done */
SECOidTag certalgtag; /* the certificate's encryption algorithm */
SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
SECStatus rv;
SECItem *params = NULL;
int data_len;
SECStatus err;
PK11SymKey *tek;
CERTCertificate *ourCert;
SECKEYPublicKey *ourPubKey;
NSSCMSKEATemplateSelector whichKEA;
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
/* We really want to show our KEA tag as the key exchange algorithm tag. */
encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
/* Get the public key of the recipient. */
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL) goto loser;
/* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
/* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
if (ourCert == NULL) goto loser;
arena = PORT_NewArena(1024);
if (arena == NULL) goto loser;
/* While we're here, extract the key pair's public key data and copy it into */
/* the outgoing parameters. */
/* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
if (ourPubKey == NULL)
{
goto loser;
}
SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
ourPubKey = NULL;
/* Extract our private key in order to derive the KEA key. */
ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
CERT_DestroyCertificate(ourCert); /* we're done with this */
if (!ourPrivKey) goto loser;
/* If ukm desired, prepare it - allocate enough space (filled with zeros). */
if (ukm) {
ukm->data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
ukm->len = /* XXXX */;
}
/* Generate the KEK (key exchange key) according to RFC2631 which we use
* to wrap the bulk encryption key. */
kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
ukm, NULL,
/* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, wincx);
SECKEY_DestroyPublicKey(publickey);
SECKEY_DestroyPrivateKey(ourPrivKey);
publickey = NULL;
ourPrivKey = NULL;
if (!kek)
goto loser;
/* allocate space for the encrypted CEK (bulk key) */
encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE;
if (encKey->data == NULL)
{
PK11_FreeSymKey(kek);
goto loser;
}
/* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
/* bulk encryption algorithm */
switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
{
case /* XXXX */CKM_SKIPJACK_CFB8:
err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
whichKEA = NSSCMSKEAUsesSkipjack;
break;
case /* XXXX */CKM_SKIPJACK_CFB8:
err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
whichKEA = NSSCMSKEAUsesSkipjack;
break;
default:
/* XXXX what do we do here? Neither RC2 nor 3DES... */
break;
}
PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
if (err != SECSuccess)
goto loser;
/* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
/* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
if (params == NULL)
goto loser;
/* now set keyEncAlg */
rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
if (rv != SECSuccess)
goto loser;
/* XXXXXXX this is not right yet */
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
if (publickey) {
SECKEY_DestroyPublicKey(publickey);
}
if (ourPrivKey) {
SECKEY_DestroyPrivateKey(ourPrivKey);
}
#endif
return SECFailure;
}
PK11SymKey *
NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
{
#if 0 /* not yet done */
SECStatus err;
CK_MECHANISM_TYPE bulkType;
PK11SymKey *tek;
SECKEYPublicKey *originatorPubKey;
NSSCMSSMIMEKEAParameters keaParams;
/* XXXX get originator's public key */
originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
keaParams.originatorKEAKey.len);
if (originatorPubKey == NULL)
goto loser;
/* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
The Derive function generates a shared secret and combines it with the originatorRA
data to come up with an unique session key */
tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
&keaParams.originatorRA, NULL,
CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
CKA_WRAP, 0, pwfn_arg);
SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
if (tek == NULL)
goto loser;
/* Now that we have the TEK, unwrap the bulk key
with which to decrypt the message. */
/* Skipjack is being used as the bulk encryption algorithm.*/
/* Unwrap the bulk key. */
bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
return bulkkey;
loser:
#endif
return NULL;
}

View File

@ -0,0 +1,421 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS recipientInfo methods.
*
* $Id: cmsrecinfo.c,v 1.2 2000-06-13 21:56:30 chrisk%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"
/*
* NSS_CMSRecipientInfo_Create - create a recipientinfo
*
* we currently do not create KeyAgreement recipientinfos with multiple recipientEncryptedKeys
* the certificate is supposed to have been verified by the caller
*/
NSSCMSRecipientInfo *
NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert)
{
NSSCMSRecipientInfo *ri;
void *mark;
SECOidTag certalgtag;
SECStatus rv;
NSSCMSRecipientEncryptedKey *rek;
NSSCMSOriginatorIdentifierOrKey *oiok;
unsigned long version;
SECItem *dummy;
PLArenaPool *poolp;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
ri = (NSSCMSRecipientInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSRecipientInfo));
if (ri == NULL)
goto loser;
ri->cmsg = cmsg;
ri->cert = CERT_DupCertificate(cert);
if (ri->cert == NULL)
goto loser;
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
switch (certalgtag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans;
/* hardcoded issuerSN choice for now */
ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = NSSCMSRecipientID_IssuerSN;
ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
rv = SECFailure;
break;
}
break;
case SEC_OID_MISSI_KEA_DSS_OLD:
case SEC_OID_MISSI_KEA_DSS:
case SEC_OID_MISSI_KEA:
/* backward compatibility - this is not really a keytrans operation */
ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans;
/* hardcoded issuerSN choice for now */
ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = NSSCMSRecipientID_IssuerSN;
ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
rv = SECFailure;
break;
}
break;
case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
/* a key agreement op */
ri->recipientInfoType = NSSCMSRecipientInfoID_KeyAgree;
if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
rv = SECFailure;
break;
}
/* we do not support the case where multiple recipients
* share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
* in this case, we would need to walk all the recipientInfos, take the
* ones that do KeyAgreement algorithms and join them, algorithm by algorithm
* Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */
/* only epheremal-static Diffie-Hellman is supported for now
* this is the only form of key agreement that provides potential anonymity
* of the sender, plus we do not have to include certs in the message */
/* force single recipientEncryptedKey for now */
if ((rek = NSS_CMSRecipientEncryptedKey_Create(poolp)) == NULL) {
rv = SECFailure;
break;
}
/* hardcoded IssuerSN choice for now */
rek->recipientIdentifier.identifierType = NSSCMSKeyAgreeRecipientID_IssuerSN;
if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
rv = SECFailure;
break;
}
oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
/* see RFC2630 12.3.1.1 */
oiok->identifierType = NSSCMSOriginatorIDOrKey_OriginatorPublicKey;
rv = NSS_CMSArray_Add(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
(void *)rek);
break;
default:
/* other algorithms not supported yet */
/* NOTE that we do not support any KEK algorithm */
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
rv = SECFailure;
break;
}
if (rv == SECFailure)
goto loser;
/* set version */
switch (ri->recipientInfoType) {
case NSSCMSRecipientInfoID_KeyTrans:
if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == NSSCMSRecipientID_IssuerSN)
version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN;
else
version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY;
dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version);
if (dummy == NULL)
goto loser;
break;
case NSSCMSRecipientInfoID_KeyAgree:
dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version),
NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION);
if (dummy == NULL)
goto loser;
break;
case NSSCMSRecipientInfoID_KEK:
/* NOTE: this cannot happen as long as we do not support any KEK algorithm */
dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version),
NSS_CMS_KEK_RECIPIENT_INFO_VERSION);
if (dummy == NULL)
goto loser;
break;
}
PORT_ArenaUnmark (poolp, mark);
return ri;
loser:
PORT_ArenaRelease (poolp, mark);
return NULL;
}
void
NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri)
{
/* version was allocated on the pool, so no need to destroy it */
/* issuerAndSN was allocated on the pool, so no need to destroy it */
if (ri->cert != NULL)
CERT_DestroyCertificate(ri->cert);
/* recipientInfo structure itself was allocated on the pool, so no need to destroy it */
/* we're done. */
}
int
NSS_CMSRecipientInfo_GetVersion(NSSCMSRecipientInfo *ri)
{
unsigned long version;
SECItem *versionitem;
switch (ri->recipientInfoType) {
case NSSCMSRecipientInfoID_KeyTrans:
/* ignore subIndex */
versionitem = &(ri->ri.keyTransRecipientInfo.version);
break;
case NSSCMSRecipientInfoID_KEK:
/* ignore subIndex */
versionitem = &(ri->ri.kekRecipientInfo.version);
break;
case NSSCMSRecipientInfoID_KeyAgree:
versionitem = &(ri->ri.keyAgreeRecipientInfo.version);
break;
}
/* always take apart the SECItem */
if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess)
return 0;
else
return (int)version;
}
SECItem *
NSS_CMSRecipientInfo_GetEncryptedKey(NSSCMSRecipientInfo *ri, int subIndex)
{
SECItem *enckey;
switch (ri->recipientInfoType) {
case NSSCMSRecipientInfoID_KeyTrans:
/* ignore subIndex */
enckey = &(ri->ri.keyTransRecipientInfo.encKey);
break;
case NSSCMSRecipientInfoID_KEK:
/* ignore subIndex */
enckey = &(ri->ri.kekRecipientInfo.encKey);
break;
case NSSCMSRecipientInfoID_KeyAgree:
enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
break;
}
return enckey;
}
SECOidTag
NSS_CMSRecipientInfo_GetKeyEncryptionAlgorithmTag(NSSCMSRecipientInfo *ri)
{
SECOidTag encalgtag;
switch (ri->recipientInfoType) {
case NSSCMSRecipientInfoID_KeyTrans:
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
break;
case NSSCMSRecipientInfoID_KeyAgree:
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
break;
case NSSCMSRecipientInfoID_KEK:
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
break;
}
return encalgtag;
}
SECStatus
NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, SECOidTag bulkalgtag)
{
CERTCertificate *cert;
SECOidTag certalgtag;
SECStatus rv;
SECItem *params = NULL;
NSSCMSRecipientEncryptedKey *rek;
NSSCMSOriginatorIdentifierOrKey *oiok;
PLArenaPool *poolp;
poolp = ri->cmsg->poolp;
cert = ri->cert;
PORT_Assert (cert != NULL);
if (cert == NULL)
return SECFailure;
/* XXX set ri->recipientInfoType to the proper value here */
/* or should we look if it's been set already ? */
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
switch (certalgtag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
/* wrap the symkey */
if (NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey, &ri->ri.keyTransRecipientInfo.encKey) != SECSuccess) {
rv = SECFailure;
break;
}
rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL);
break;
case SEC_OID_MISSI_KEA_DSS_OLD:
case SEC_OID_MISSI_KEA_DSS:
case SEC_OID_MISSI_KEA:
rv = NSS_CMSUtil_EncryptSymKey_MISSI(poolp, cert, bulkkey,
bulkalgtag,
&ri->ri.keyTransRecipientInfo.encKey,
&params, ri->cmsg->pwfn_arg);
if (rv != SECSuccess)
break;
/* here, we DO need to pass the params to the wrap function because, with
* RSA, there is no funny stuff going on with generation of IV vectors or so */
rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, params);
break;
case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[0];
if (rek == NULL) {
rv = SECFailure;
break;
}
oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
PORT_Assert(oiok->identifierType == NSSCMSOriginatorIDOrKey_OriginatorPublicKey);
/* see RFC2630 12.3.1.1 */
if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
SEC_OID_X942_DIFFIE_HELMAN_KEY, NULL) != SECSuccess) {
rv = SECFailure;
break;
}
/* this will generate a key pair, compute the shared secret, */
/* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
/* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */
rv = NSS_CMSUtil_EncryptSymKey_ESDH(poolp, cert, bulkkey,
&rek->encKey,
&ri->ri.keyAgreeRecipientInfo.ukm,
&ri->ri.keyAgreeRecipientInfo.keyEncAlg,
&oiok->id.originatorPublicKey.publicKey);
break;
default:
/* other algorithms not supported yet */
/* NOTE that we do not support any KEK algorithm */
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
rv = SECFailure;
break;
}
return rv;
}
PK11SymKey *
NSS_CMSRecipientInfo_UnwrapBulkKey(NSSCMSRecipientInfo *ri, int subIndex, CERTCertificate *cert, SECKEYPrivateKey *privkey, SECOidTag bulkalgtag)
{
PK11SymKey *bulkkey = NULL;
SECAlgorithmID *encalg;
SECOidTag encalgtag;
SECItem *enckey;
int error;
ri->cert = cert; /* mark the recipientInfo so we can find it later */
switch (ri->recipientInfoType) {
case NSSCMSRecipientInfoID_KeyTrans:
encalg = &(ri->ri.keyTransRecipientInfo.keyEncAlg);
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
enckey = &(ri->ri.keyTransRecipientInfo.encKey); /* ignore subIndex */
switch (encalgtag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
/* RSA encryption algorithm: */
/* get the symmetric (bulk) key by unwrapping it using our private key */
bulkkey = NSS_CMSUtil_DecryptSymKey_RSA(privkey, enckey, bulkalgtag);
break;
case SEC_OID_NETSCAPE_SMIME_KEA:
/* FORTEZZA key exchange algorithm */
/* the supplemental data is in the parameters of encalg */
bulkkey = NSS_CMSUtil_DecryptSymKey_MISSI(privkey, enckey, encalg, bulkalgtag, ri->cmsg->pwfn_arg);
break;
default:
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
}
break;
case NSSCMSRecipientInfoID_KeyAgree:
encalg = &(ri->ri.keyAgreeRecipientInfo.keyEncAlg);
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
switch (encalgtag) {
case SEC_OID_X942_DIFFIE_HELMAN_KEY:
/* Diffie-Helman key exchange */
/* XXX not yet implemented */
/* XXX problem: SEC_OID_X942_DIFFIE_HELMAN_KEY points to a PKCS3 mechanism! */
/* we support ephemeral-static DH only, so if the recipientinfo */
/* has originator stuff in it, we punt (or do we? shouldn't be that hard...) */
/* first, we derive the KEK (a symkey!) using a Derive operation, then we get the */
/* content encryption key using a Unwrap op */
/* the derive operation has to generate the key using the algorithm in RFC2631 */
error = SEC_ERROR_UNSUPPORTED_KEYALG;
break;
default:
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
}
break;
case NSSCMSRecipientInfoID_KEK:
encalg = &(ri->ri.kekRecipientInfo.keyEncAlg);
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
enckey = &(ri->ri.kekRecipientInfo.encKey);
/* not supported yet */
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
break;
}
/* XXXX continue here */
return bulkkey;
loser:
return NULL;
}

View File

@ -0,0 +1,187 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS recipient list functions
*
* $Id: cmsreclist.c,v 1.2 2000-06-13 21:56:31 chrisk%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 "prtime.h"
#include "secerr.h"
static int
nss_cms_recipients_traverse(NSSCMSRecipientInfo **recipientinfos, NSSCMSRecipient **recipient_list)
{
int count = 0;
int rlindex = 0;
int i, j;
NSSCMSRecipient *rle;
NSSCMSRecipientInfo *ri;
NSSCMSRecipientEncryptedKey *rek;
for (i = 0; recipientinfos[i] != NULL; i++) {
ri = recipientinfos[i];
switch (ri->recipientInfoType) {
case NSSCMSRecipientInfoID_KeyTrans:
if (recipient_list) {
/* alloc one & fill it out */
rle = (NSSCMSRecipient *)PORT_ZAlloc(sizeof(NSSCMSRecipient));
if (rle == NULL)
return -1;
rle->riIndex = i;
rle->subIndex = -1;
switch (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType) {
case NSSCMSRecipientID_IssuerSN:
rle->kind = RLIssuerSN;
rle->id.issuerAndSN = ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN;
break;
case NSSCMSRecipientID_SubjectKeyID:
rle->kind = RLSubjKeyID;
rle->id.subjectKeyID = ri->ri.keyTransRecipientInfo.recipientIdentifier.id.subjectKeyID;
break;
}
recipient_list[rlindex++] = rle;
} else {
count++;
}
break;
case NSSCMSRecipientInfoID_KeyAgree:
if (ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys == NULL)
break;
for (j=0; ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[j] != NULL; j++) {
if (recipient_list) {
rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[j];
/* alloc one & fill it out */
rle = (NSSCMSRecipient *)PORT_ZAlloc(sizeof(NSSCMSRecipient));
if (rle == NULL)
return -1;
rle->riIndex = i;
rle->subIndex = j;
switch (rek->recipientIdentifier.identifierType) {
case NSSCMSKeyAgreeRecipientID_IssuerSN:
rle->kind = RLIssuerSN;
rle->id.issuerAndSN = rek->recipientIdentifier.id.issuerAndSN;
break;
case NSSCMSKeyAgreeRecipientID_RKeyID:
rle->kind = RLSubjKeyID;
rle->id.subjectKeyID = rek->recipientIdentifier.id.recipientKeyIdentifier.subjectKeyIdentifier;
break;
}
recipient_list[rlindex++] = rle;
} else {
count++;
}
}
break;
case NSSCMSRecipientInfoID_KEK:
/* KEK is not implemented */
break;
}
}
/* if we have a recipient list, we return on success (-1, above, on failure) */
/* otherwise, we return the count. */
if (recipient_list) {
recipient_list[rlindex] = NULL;
return 0;
} else {
return count;
}
}
NSSCMSRecipient **
nss_cms_recipient_list_create(NSSCMSRecipientInfo **recipientinfos)
{
int count, rv;
NSSCMSRecipient **recipient_list;
/* count the number of recipient identifiers */
count = nss_cms_recipients_traverse(recipientinfos, NULL);
if (count <= 0) {
/* no recipients? */
PORT_SetError(SEC_ERROR_BAD_DATA);
#if 0
PORT_SetErrorString("Cannot find recipient data in envelope.");
#endif
return NULL;
}
/* allocate an array of pointers */
recipient_list = (NSSCMSRecipient **)
PORT_ZAlloc((count + 1) * sizeof(NSSCMSRecipient *));
if (recipient_list == NULL)
return NULL;
/* now fill in the recipient_list */
rv = nss_cms_recipients_traverse(recipientinfos, recipient_list);
if (rv < 0) {
nss_cms_recipient_list_destroy(recipient_list);
return NULL;
}
return recipient_list;
}
void
nss_cms_recipient_list_destroy(NSSCMSRecipient **recipient_list)
{
int i;
NSSCMSRecipient *recipient;
for (i=0; recipient_list[i] != NULL; i++) {
recipient = recipient_list[i];
if (recipient->cert)
CERT_DestroyCertificate(recipient->cert);
if (recipient->privkey)
SECKEY_DestroyPrivateKey(recipient->privkey);
if (recipient->slot)
PK11_FreeSlot(recipient->slot);
PORT_Free(recipient);
}
PORT_Free(recipient_list);
}
NSSCMSRecipientEncryptedKey *
NSS_CMSRecipientEncryptedKey_Create(PLArenaPool *poolp)
{
return (NSSCMSRecipientEncryptedKey *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSRecipientEncryptedKey));
}

View File

@ -0,0 +1,59 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* $Id: cmsreclist.h,v 1.2 2000-06-13 21:56:31 chrisk%netscape.com Exp $
*/
#ifndef _CMSRECLIST_H
#define _CMSRECLIST_H
struct NSSCMSRecipientStr {
int riIndex; /* this recipient's index in recipientInfo array */
int subIndex; /* index into recipientEncryptedKeys */
/* (only in NSSCMSKeyAgreeRecipientInfoStr) */
enum {RLIssuerSN, RLSubjKeyID} kind; /* for conversion recipientinfos -> recipientlist */
union {
CERTIssuerAndSN * issuerAndSN;
SECItem * subjectKeyID;
} id;
/* result data (filled out for each recipient that's us) */
CERTCertificate * cert;
SECKEYPrivateKey * privkey;
PK11SlotInfo * slot;
};
typedef struct NSSCMSRecipientStr NSSCMSRecipient;
#endif /* _CMSRECLIST_H */

View File

@ -0,0 +1,850 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS signedData methods.
*
* $Id: cmssigdata.c,v 1.2 2000-06-13 21:56:31 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "cert.h"
#include "cdbhdl.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "secerr.h"
NSSCMSSignedData *
NSS_CMSSignedData_Create(NSSCMSMessage *cmsg)
{
void *mark;
NSSCMSSignedData *sigd;
PLArenaPool *poolp;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
sigd = (NSSCMSSignedData *)PORT_ArenaZAlloc (poolp, sizeof(NSSCMSSignedData));
if (sigd == NULL)
goto loser;
sigd->cmsg = cmsg;
/* signerInfos, certs, certlists, crls are all empty */
/* version is set in NSS_CMSSignedData_Finalize() */
PORT_ArenaUnmark(poolp, mark);
return sigd;
loser:
PORT_ArenaRelease(poolp, mark);
return NULL;
}
void
NSS_CMSSignedData_Destroy(NSSCMSSignedData *sigd)
{
CERTCertificate **certs, *cert;
CERTCertificateList **certlists, *certlist;
NSSCMSSignerInfo **signerinfos, *si;
if (sigd == NULL)
return;
certs = sigd->certs;
certlists = sigd->certLists;
signerinfos = sigd->signerInfos;
if (certs != NULL) {
while ((cert = *certs++) != NULL)
CERT_DestroyCertificate (cert);
}
if (certlists != NULL) {
while ((certlist = *certlists++) != NULL)
CERT_DestroyCertificateList (certlist);
}
if (signerinfos != NULL) {
while ((si = *signerinfos++) != NULL)
NSS_CMSSignerInfo_Destroy(si);
}
/* everything's in a pool, so don't worry about the storage */
}
/*
* NSS_CMSSignedData_Encode_BeforeStart - do all the necessary things to a SignedData
* before start of encoding.
*
* In detail:
* - find out about the right value to put into sigd->version
* - come up with a list of digestAlgorithms (which should be the union of the algorithms
* in the signerinfos).
* If we happen to have a pre-set list of algorithms (and digest values!), we
* check if we have all the signerinfos' algorithms. If not, this is an error.
*/
SECStatus
NSS_CMSSignedData_Encode_BeforeStart(NSSCMSSignedData *sigd)
{
NSSCMSSignerInfo **signerinfos, *signerinfo;
SECOidTag digestalgtag;
SECItem *dummy;
int version;
SECStatus rv;
PRBool haveDigests = PR_FALSE;
int n, i;
PLArenaPool *poolp;
poolp = sigd->cmsg->poolp;
/* we assume that we have precomputed digests if there is a list of algorithms, and */
/* a chunk of data for each of those algorithms */
if (sigd->digestAlgorithms != NULL && sigd->digests != NULL) {
for (i=0; sigd->digestAlgorithms[i] != NULL; i++) {
if (sigd->digests[i] == NULL)
break;
}
if (sigd->digestAlgorithms[i] == NULL) /* reached the end of the array? */
haveDigests = PR_TRUE; /* yes: we must have all the digests */
}
version = NSS_CMS_SIGNED_DATA_VERSION_BASIC;
/* RFC2630 5.1 "version is the syntax version number..." */
if (NSS_CMSContentInfo_GetContentTypeTag(&(sigd->contentInfo)) != SEC_OID_PKCS7_DATA)
version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
signerinfos = sigd->signerInfos;
/* prepare all the SignerInfos */
for (i = 0; signerinfos[i] != NULL; i++) {
signerinfo = signerinfos[i];
/* RFC2630 5.1 "version is the syntax version number..." */
if (NSS_CMSSignerInfo_GetVersion(signerinfo) != NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN)
version = NSS_CMS_SIGNED_DATA_VERSION_EXT;
/* collect digestAlgorithms from SignerInfos */
/* (we need to know which algorithms we have when the content comes in) */
/* do not overwrite any existing digestAlgorithms (and digest) */
digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
if (n < 0 && haveDigests) {
/* oops, there is a digestalg we do not have a digest for */
/* but we were supposed to have all the digests already... */
goto loser;
} else if (n < 0) {
/* add the digestAlgorithm & a NULL digest */
rv = NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, NULL);
if (rv != SECSuccess)
goto loser;
} else {
/* found it, nothing to do */
}
}
dummy = SEC_ASN1EncodeInteger(poolp, &(sigd->version), (long)version);
if (dummy == NULL)
return SECFailure;
/* this is a SET OF, so we need to sort them guys */
rv = NSS_CMSArray_SortByDER((void **)sigd->digestAlgorithms, SECOID_AlgorithmIDTemplate,
(void **)sigd->digests);
if (rv != SECSuccess)
return SECFailure;
return SECSuccess;
loser:
return SECFailure;
}
SECStatus
NSS_CMSSignedData_Encode_BeforeData(NSSCMSSignedData *sigd)
{
/* set up the digests */
if (sigd->digestAlgorithms != NULL) {
sigd->contentInfo.digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
if (sigd->contentInfo.digcx == NULL)
return SECFailure;
}
return SECSuccess;
}
/*
* NSS_CMSSignedData_Encode_AfterData - do all the necessary things to a SignedData
* after all the encapsulated data was passed through the encoder.
*
* In detail:
* - create the signatures in all the SignerInfos
*
* Please note that nothing is done to the Certificates and CRLs in the message - this
* is entirely the responsibility of our callers.
*/
SECStatus
NSS_CMSSignedData_Encode_AfterData(NSSCMSSignedData *sigd)
{
NSSCMSSignerInfo **signerinfos, *signerinfo;
NSSCMSContentInfo *cinfo;
SECOidTag digestalgtag;
SECStatus ret = SECFailure;
SECStatus rv;
SECItem *contentType;
int certcount;
int i, ci, cli, n, rci, si;
PLArenaPool *poolp;
CERTCertificateList *certlist;
extern const SEC_ASN1Template NSSCMSSignerInfoTemplate[];
poolp = sigd->cmsg->poolp;
cinfo = &(sigd->contentInfo);
/* did we have digest calculation going on? */
if (cinfo->digcx) {
rv = NSS_CMSDigestContext_FinishMultiple(cinfo->digcx, poolp, &(sigd->digests));
if (rv != SECSuccess)
goto loser; /* error has been set by NSS_CMSDigestContext_FinishMultiple */
cinfo->digcx = NULL;
}
signerinfos = sigd->signerInfos;
certcount = 0;
/* prepare all the SignerInfos */
for (i = 0; signerinfos[i] != NULL; i++) {
signerinfo = signerinfos[i];
/* find correct digest for this signerinfo */
digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
if (n < 0 || sigd->digests == NULL || sigd->digests[n] == NULL) {
/* oops - digest not found */
PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
goto loser;
}
/* XXX if our content is anything else but data, we need to force the
* presence of signed attributes (RFC2630 5.3 "signedAttributes is a
* collection...") */
/* pass contentType here as we want a contentType attribute */
if ((contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo)) == NULL)
goto loser;
/* sign the thing */
rv = NSS_CMSSignerInfo_Sign(signerinfo, sigd->digests[n], contentType);
if (rv != SECSuccess)
goto loser;
/* while we're at it, count number of certs in certLists */
certlist = NSS_CMSSignerInfo_GetCertList(signerinfo);
if (certlist)
certcount += certlist->len;
}
/* this is a SET OF, so we need to sort them guys */
rv = NSS_CMSArray_SortByDER((void **)signerinfos, NSSCMSSignerInfoTemplate, NULL);
if (rv != SECSuccess)
goto loser;
/*
* now prepare certs & crls
*/
/* count the rest of the certs */
if (sigd->certs != NULL) {
for (ci = 0; sigd->certs[ci] != NULL; ci++)
certcount++;
}
if (sigd->certLists != NULL) {
for (cli = 0; sigd->certLists[cli] != NULL; cli++)
certcount += sigd->certLists[cli]->len;
}
if (certcount == 0) {
sigd->rawCerts = NULL;
} else {
/*
* Combine all of the certs and cert chains into rawcerts.
* Note: certcount is an upper bound; we may not need that many slots
* but we will allocate anyway to avoid having to do another pass.
* (The temporary space saving is not worth it.)
*
* XXX ARGH - this NEEDS to be fixed. need to come up with a decent
* SetOfDERcertficates implementation
*/
sigd->rawCerts = (SECItem **)PORT_ArenaAlloc(poolp, (certcount + 1) * sizeof(SECItem *));
if (sigd->rawCerts == NULL)
return SECFailure;
/*
* XXX Want to check for duplicates and not add *any* cert that is
* already in the set. This will be more important when we start
* dealing with larger sets of certs, dual-key certs (signing and
* encryption), etc. For the time being we can slide by...
*
* XXX ARGH - this NEEDS to be fixed. need to come up with a decent
* SetOfDERcertficates implementation
*/
rci = 0;
if (signerinfos != NULL) {
for (si = 0; signerinfos[si] != NULL; si++) {
signerinfo = signerinfos[si];
for (ci = 0; ci < signerinfo->certList->len; ci++)
sigd->rawCerts[rci++] = &(signerinfo->certList->certs[ci]);
}
}
if (sigd->certs != NULL) {
for (ci = 0; sigd->certs[ci] != NULL; ci++)
sigd->rawCerts[rci++] = &(sigd->certs[ci]->derCert);
}
if (sigd->certLists != NULL) {
for (cli = 0; sigd->certLists[cli] != NULL; cli++) {
for (ci = 0; ci < sigd->certLists[cli]->len; ci++)
sigd->rawCerts[rci++] = &(sigd->certLists[cli]->certs[ci]);
}
}
sigd->rawCerts[rci] = NULL;
/* this is a SET OF, so we need to sort them guys - we have the DER already, though */
NSS_CMSArray_Sort((void **)sigd->rawCerts, NSS_CMSUtil_DERCompare, NULL, NULL);
}
ret = SECSuccess;
loser:
return ret;
}
SECStatus
NSS_CMSSignedData_Decode_BeforeData(NSSCMSSignedData *sigd)
{
/* set up the digests */
if (sigd->digestAlgorithms != NULL && sigd->digests == NULL) {
/* if digests are already there, do nothing */
sigd->contentInfo.digcx = NSS_CMSDigestContext_StartMultiple(sigd->digestAlgorithms);
if (sigd->contentInfo.digcx == NULL)
return SECFailure;
}
return SECSuccess;
}
/*
* NSS_CMSSignedData_Decode_AfterData - do all the necessary things to a SignedData
* after all the encapsulated data was passed through the decoder.
*/
SECStatus
NSS_CMSSignedData_Decode_AfterData(NSSCMSSignedData *sigd)
{
/* did we have digest calculation going on? */
if (sigd->contentInfo.digcx) {
if (NSS_CMSDigestContext_FinishMultiple(sigd->contentInfo.digcx, sigd->cmsg->poolp, &(sigd->digests)) != SECSuccess)
return SECFailure; /* error has been set by NSS_CMSDigestContext_FinishMultiple */
sigd->contentInfo.digcx = NULL;
}
return SECSuccess;
}
/*
* NSS_CMSSignedData_Decode_AfterEnd - do all the necessary things to a SignedData
* after all decoding is finished.
*/
SECStatus
NSS_CMSSignedData_Decode_AfterEnd(NSSCMSSignedData *sigd)
{
NSSCMSSignerInfo **signerinfos;
int i;
/* set cmsg for all the signerinfos */
signerinfos = sigd->signerInfos;
/* set cmsg for all the signerinfos */
for (i = 0; signerinfos[i] != NULL; i++)
signerinfos[i]->cmsg = sigd->cmsg;
return SECSuccess;
}
/*
* NSS_CMSSignedData_GetSignerInfos - retrieve the SignedData's signer list
*/
NSSCMSSignerInfo **
NSS_CMSSignedData_GetSignerInfos(NSSCMSSignedData *sigd)
{
return sigd->signerInfos;
}
int
NSS_CMSSignedData_SignerInfoCount(NSSCMSSignedData *sigd)
{
return NSS_CMSArray_Count((void **)sigd->signerInfos);
}
NSSCMSSignerInfo *
NSS_CMSSignedData_GetSignerInfo(NSSCMSSignedData *sigd, int i)
{
return sigd->signerInfos[i];
}
/*
* NSS_CMSSignedData_GetDigestAlgs - retrieve the SignedData's digest algorithm list
*/
SECAlgorithmID **
NSS_CMSSignedData_GetDigestAlgs(NSSCMSSignedData *sigd)
{
return sigd->digestAlgorithms;
}
/*
* NSS_CMSSignedData_GetContentInfo - return pointer to this signedData's contentinfo
*/
NSSCMSContentInfo *
NSS_CMSSignedData_GetContentInfo(NSSCMSSignedData *sigd)
{
return &(sigd->contentInfo);
}
/*
* NSS_CMSSignedData_GetCertificateList - retrieve the SignedData's certificate list
*/
SECItem **
NSS_CMSSignedData_GetCertificateList(NSSCMSSignedData *sigd)
{
return sigd->rawCerts;
}
SECStatus
NSS_CMSSignedData_ImportCerts(NSSCMSSignedData *sigd, CERTCertDBHandle *certdb,
SECCertUsage certusage, PRBool keepcerts)
{
int certcount;
SECStatus rv;
int i;
certcount = NSS_CMSArray_Count((void **)sigd->rawCerts);
rv = CERT_ImportCerts(certdb, certusage, certcount, sigd->rawCerts, NULL,
keepcerts, PR_FALSE, NULL);
/* XXX CRL handling */
if (sigd->signerInfos != NULL) {
/* fill in all signerinfo's certs */
for (i = 0; sigd->signerInfos[i] != NULL; i++)
(void)NSS_CMSSignerInfo_GetSigningCertificate(sigd->signerInfos[i], certdb);
}
return rv;
}
/*
* XXX the digests need to be passed in BETWEEN the decoding and the verification in case
* of external signatures!
*/
/*
* NSS_CMSSignedData_VerifySignerInfo - check the signatures.
*
* The digests were either calculated during decoding (and are stored in the
* signedData itself) or set after decoding using NSS_CMSSignedData_SetDigests.
*
* The verification checks if the signing cert is valid and has a trusted chain
* for the purpose specified by "certusage".
*/
SECStatus
NSS_CMSSignedData_VerifySignerInfo(NSSCMSSignedData *sigd, int i,
CERTCertDBHandle *certdb, SECCertUsage certusage)
{
NSSCMSSignerInfo *signerinfo;
NSSCMSContentInfo *cinfo;
SECOidData *algiddata;
SECItem *contentType, *digest;
cinfo = &(sigd->contentInfo);
signerinfo = sigd->signerInfos[i];
/* verify certificate */
if (NSS_CMSSignerInfo_VerifyCertificate(signerinfo, certdb, certusage) != SECSuccess)
return SECFailure; /* error is set by NSS_CMSSignerInfo_VerifyCertificate */
/* find digest and contentType for signerinfo */
algiddata = NSS_CMSSignerInfo_GetDigestAlg(signerinfo);
digest = NSS_CMSSignedData_GetDigestByAlgTag(sigd, algiddata->offset);
contentType = NSS_CMSContentInfo_GetContentTypeOID(cinfo);
/* now verify signature */
return NSS_CMSSignerInfo_Verify(signerinfo, digest, contentType);
}
/*
* NSS_CMSSignedData_HasDigests - see if we have digests in place
*/
PRBool
NSS_CMSSignedData_HasDigests(NSSCMSSignedData *sigd)
{
return (sigd->digests != NULL);
}
SECStatus
NSS_CMSSignedData_AddCertList(NSSCMSSignedData *sigd, CERTCertificateList *certlist)
{
SECStatus rv;
PORT_Assert(certlist != NULL);
if (certlist == NULL)
return SECFailure;
/* XXX memory?? a certlist has an arena of its own and is not refcounted!?!? */
rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certLists), (void *)certlist);
return rv;
}
/*
* NSS_CMSSignedData_AddCertChain - add cert and its entire chain to the set of certs
*/
SECStatus
NSS_CMSSignedData_AddCertChain(NSSCMSSignedData *sigd, CERTCertificate *cert)
{
CERTCertificateList *certlist;
SECCertUsage usage;
SECStatus rv;
usage = certUsageEmailSigner;
/* do not include root */
certlist = CERT_CertChainFromCert(cert, usage, PR_FALSE);
if (certlist == NULL)
return SECFailure;
rv = NSS_CMSSignedData_AddCertList(sigd, certlist);
CERT_DestroyCertificateList(certlist);
return rv;
}
SECStatus
NSS_CMSSignedData_AddCertificate(NSSCMSSignedData *sigd, CERTCertificate *cert)
{
CERTCertificate *c;
SECStatus rv;
PORT_Assert(cert != NULL);
if (cert == NULL)
return SECFailure;
c = CERT_DupCertificate(cert);
rv = NSS_CMSArray_Add(sigd->cmsg->poolp, (void ***)&(sigd->certs), (void *)c);
return rv;
}
PRBool
NSS_CMSSignedData_ContainsCertsOrCrls(NSSCMSSignedData *sigd)
{
if (sigd->rawCerts != NULL && sigd->rawCerts[0] != NULL)
return PR_TRUE;
else if (sigd->crls != NULL && sigd->crls[0] != NULL)
return PR_TRUE;
else
return PR_FALSE;
}
SECStatus
NSS_CMSSignedData_AddSignerInfo(NSSCMSSignedData *sigd,
NSSCMSSignerInfo *signerinfo)
{
void *mark;
SECStatus rv;
SECOidTag digestalgtag;
PLArenaPool *poolp;
poolp = sigd->cmsg->poolp;
mark = PORT_ArenaMark(poolp);
/* add signerinfo */
rv = NSS_CMSArray_Add(poolp, (void ***)&(sigd->signerInfos), (void *)signerinfo);
if (rv != SECSuccess)
goto loser;
/*
* add empty digest
* Empty because we don't have it yet. Either it gets created during encoding
* (if the data is present) or has to be set externally.
* XXX maybe pass it in optionally?
*/
digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
rv = NSS_CMSSignedData_SetDigestValue(sigd, digestalgtag, NULL);
if (rv != SECSuccess)
goto loser;
/*
* The last thing to get consistency would be adding the digest.
*/
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease (poolp, mark);
return SECFailure;
}
SECItem *
NSS_CMSSignedData_GetDigestByAlgTag(NSSCMSSignedData *sigd, SECOidTag algtag)
{
int idx;
idx = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, algtag);
return sigd->digests[idx];
}
/*
* NSS_CMSSignedData_SetDigests - set a signedData's digests member
*
* "digestalgs" - array of digest algorithm IDs
* "digests" - array of digests corresponding to the digest algorithms
*/
SECStatus
NSS_CMSSignedData_SetDigests(NSSCMSSignedData *sigd,
SECAlgorithmID **digestalgs,
SECItem **digests)
{
int cnt, i, idx;
if (sigd->digestAlgorithms == NULL) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
/* we assume that the digests array is just not there yet */
PORT_Assert(sigd->digests == NULL);
if (sigd->digests != NULL) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
return SECFailure;
}
/* now allocate one (same size as digestAlgorithms) */
cnt = NSS_CMSArray_Count((void **)sigd->digestAlgorithms);
sigd->digests = PORT_ArenaZAlloc(sigd->cmsg->poolp, (cnt + 1) * sizeof(SECItem *));
if (sigd->digests == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
for (i = 0; sigd->digestAlgorithms[i] != NULL; i++) {
/* try to find the sigd's i'th digest algorithm in the array we passed in */
idx = NSS_CMSAlgArray_GetIndexByAlgID(digestalgs, sigd->digestAlgorithms[i]);
if (idx < 0) {
PORT_SetError(SEC_ERROR_DIGEST_NOT_FOUND);
return SECFailure;
}
/* found it - now set it */
if ((sigd->digests[i] = SECITEM_AllocItem(sigd->cmsg->poolp, NULL, 0)) == NULL ||
SECITEM_CopyItem(sigd->cmsg->poolp, sigd->digests[i], digests[idx]) != SECSuccess)
{
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
}
return SECSuccess;
}
SECStatus
NSS_CMSSignedData_SetDigestValue(NSSCMSSignedData *sigd,
SECOidTag digestalgtag,
SECItem *digestdata)
{
SECItem *digest = NULL;
PLArenaPool *poolp;
void *mark;
int n;
poolp = sigd->cmsg->poolp;
mark = PORT_ArenaMark(poolp);
if (digestdata) {
/* copy digestdata item to arena (in case we have it and are not only making room) */
if (SECITEM_CopyItem(poolp, digest, digestdata) != SECSuccess)
goto loser;
}
n = -1;
if (sigd->digestAlgorithms != NULL)
n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
/* if not found, add a digest */
if (n < 0) {
if (NSS_CMSSignedData_AddDigest(poolp, sigd, digestalgtag, digest) != SECSuccess)
goto loser;
} else {
/* replace NULL pointer with digest item (and leak previous value) */
sigd->digests[n] = digest;
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
SECStatus
NSS_CMSSignedData_AddDigest(PRArenaPool *poolp,
NSSCMSSignedData *sigd,
SECOidTag digestalgtag,
SECItem *digest)
{
SECAlgorithmID *digestalg;
void *mark;
mark = PORT_ArenaMark(poolp);
digestalg = PORT_ArenaZAlloc(poolp, sizeof(SECAlgorithmID));
if (digestalg == NULL)
goto loser;
if (SECOID_SetAlgorithmID (poolp, digestalg, digestalgtag, NULL) != SECSuccess) /* no params */
goto loser;
if (NSS_CMSArray_Add(poolp, (void ***)&(sigd->digestAlgorithms), (void *)digestalg) != SECSuccess ||
/* even if digest is NULL, add dummy to have same-size array */
NSS_CMSArray_Add(poolp, (void ***)&(sigd->digests), (void *)digest) != SECSuccess)
{
goto loser;
}
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease(poolp, mark);
return SECFailure;
}
SECItem *
NSS_CMSSignedData_GetDigestValue(NSSCMSSignedData *sigd, SECOidTag digestalgtag)
{
int n;
if (sigd->digestAlgorithms == NULL)
return NULL;
n = NSS_CMSAlgArray_GetIndexByAlgTag(sigd->digestAlgorithms, digestalgtag);
return (n < 0) ? NULL : sigd->digests[n];
}
/* =============================================================================
* Misc. utility functions
*/
/*
* NSS_CMSSignedData_CreateCertsOnly - create a certs-only SignedData.
*
* cert - base certificates that will be included
* include_chain - if true, include the complete cert chain for cert
*
* More certs and chains can be added via AddCertificate and AddCertChain.
*
* An error results in a return value of NULL and an error set.
*
* XXXX CRLs
*/
NSSCMSSignedData *
NSS_CMSSignedData_CreateCertsOnly(NSSCMSMessage *cmsg, CERTCertificate *cert, PRBool include_chain)
{
NSSCMSSignedData *sigd;
void *mark;
PLArenaPool *poolp;
SECStatus rv;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
sigd = NSS_CMSSignedData_Create(cmsg);
if (sigd == NULL)
goto loser;
/* no signerinfos, thus no digestAlgorithms */
/* but certs */
if (include_chain) {
rv = NSS_CMSSignedData_AddCertChain(sigd, cert);
} else {
rv = NSS_CMSSignedData_AddCertificate(sigd, cert);
}
if (rv != SECSuccess)
goto loser;
/* RFC2630 5.2 sez:
* In the degenerate case where there are no signers, the
* EncapsulatedContentInfo value being "signed" is irrelevant. In this
* case, the content type within the EncapsulatedContentInfo value being
* "signed" should be id-data (as defined in section 4), and the content
* field of the EncapsulatedContentInfo value should be omitted.
*/
rv = NSS_CMSContentInfo_SetContent_Data(cmsg, &(sigd->contentInfo), NULL, PR_TRUE);
if (rv != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return sigd;
loser:
if (sigd)
NSS_CMSSignedData_Destroy(sigd);
PORT_ArenaRelease(poolp, mark);
return NULL;
}
/* TODO:
* NSS_CMSSignerInfo_GetReceiptRequest()
* NSS_CMSSignedData_HasReceiptRequest()
* easy way to iterate over signers
*/

View File

@ -0,0 +1,752 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS signerInfo methods.
*
* $Id: cmssiginfo.c,v 1.2 2000-06-13 21:56:32 chrisk%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 "prtime.h"
#include "secerr.h"
#include "cryptohi.h"
/* =============================================================================
* SIGNERINFO
*/
NSSCMSSignerInfo *
NSS_CMSSignerInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert, SECOidTag digestalgtag)
{
void *mark;
NSSCMSSignerInfo *signerinfo;
int version;
PLArenaPool *poolp;
poolp = cmsg->poolp;
mark = PORT_ArenaMark(poolp);
signerinfo = (NSSCMSSignerInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSSignerInfo));
if (signerinfo == NULL) {
PORT_ArenaRelease(poolp, mark);
return NULL;
}
if ((signerinfo->cert = CERT_DupCertificate(cert)) == NULL)
goto loser;
signerinfo->cmsg = cmsg;
signerinfo->signerIdentifier.identifierType = NSSCMSSignerID_IssuerSN; /* hardcoded for now */
if ((signerinfo->signerIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL)
goto loser;
/* set version right now */
version = NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN;
/* RFC2630 5.3 "version is the syntax version number. If the .... " */
if (signerinfo->signerIdentifier.identifierType == NSSCMSSignerID_SubjectKeyID)
version = NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY;
(void)SEC_ASN1EncodeInteger(poolp, &(signerinfo->version), (long)version);
if (SECOID_SetAlgorithmID(poolp, &signerinfo->digestAlg, digestalgtag, NULL) != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return signerinfo;
loser:
PORT_ArenaRelease(poolp, mark);
return NULL;
}
/*
* NSS_CMSSignerInfo_Destroy - destroy a SignerInfo data structure
*/
void
NSS_CMSSignerInfo_Destroy(NSSCMSSignerInfo *si)
{
if (si->cert != NULL)
CERT_DestroyCertificate(si->cert);
/* XXX storage ??? */
}
/*
* NSS_CMSSignerInfo_Sign - sign something
*
*/
SECStatus
NSS_CMSSignerInfo_Sign(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType)
{
CERTCertificate *cert;
SECKEYPrivateKey *privkey = NULL;
SECOidTag digestalgtag;
SECOidTag signalgtag;
SECItem signature = { 0 };
SECStatus rv;
PLArenaPool *poolp, *tmppoolp;
PORT_Assert (digest != NULL);
poolp = signerinfo->cmsg->poolp;
cert = signerinfo->cert;
if ((privkey = PK11_FindKeyByAnyCert(cert, signerinfo->cmsg->pwfn_arg)) == NULL)
goto loser;
digestalgtag = NSS_CMSSignerInfo_GetDigestAlgTag(signerinfo);
/*
* XXX I think there should be a cert-level interface for this,
* so that I do not have to know about subjectPublicKeyInfo...
*/
signalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
/* Fortezza MISSI have weird signature formats. Map them to standard DSA formats */
signalgtag = PK11_FortezzaMapSig(signalgtag);
if (signerinfo->authAttr != NULL) {
SECItem encoded_attrs;
/* find and fill in the message digest attribute. */
rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_MESSAGE_DIGEST, digest, PR_FALSE);
if (rv != SECSuccess)
goto loser;
if (contentType != NULL) {
/* if the caller wants us to, find and fill in the content type attribute. */
rv = NSS_CMSAttributeArray_SetAttr(poolp, &(signerinfo->authAttr), SEC_OID_PKCS9_CONTENT_TYPE, contentType, PR_FALSE);
if (rv != SECSuccess)
goto loser;
}
if ((tmppoolp = PORT_NewArena (1024)) == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/*
* Before encoding, reorder the attributes so that when they
* are encoded, they will be conforming DER, which is required
* to have a specific order and that is what must be used for
* the hash/signature. We do this here, rather than building
* it into EncodeAttributes, because we do not want to do
* such reordering on incoming messages (which also uses
* EncodeAttributes) or our old signatures (and other "broken"
* implementations) will not verify. So, we want to guarantee
* that we send out good DER encodings of attributes, but not
* to expect to receive them.
*/
if (NSS_CMSAttributeArray_Reorder(signerinfo->authAttr) != SECSuccess)
goto loser;
encoded_attrs.data = NULL;
encoded_attrs.len = 0;
if (NSS_CMSAttributeArray_Encode(tmppoolp, &(signerinfo->authAttr), &encoded_attrs) == NULL)
goto loser;
rv = SEC_SignData(&signature, encoded_attrs.data, encoded_attrs.len, privkey,
NSS_CMSUtil_MakeSignatureAlgorithm(digestalgtag, signalgtag));
PORT_FreeArena(tmppoolp, PR_FALSE); /* awkward memory management :-( */
} else {
rv = SGN_Digest(privkey, digestalgtag, &signature, digest);
}
SECKEY_DestroyPrivateKey(privkey);
privkey = NULL;
if (rv != SECSuccess)
goto loser;
if (SECITEM_CopyItem(poolp, &(signerinfo->encDigest), &signature) != SECSuccess)
goto loser;
SECITEM_FreeItem(&signature, PR_FALSE);
if (SECOID_SetAlgorithmID(poolp, &(signerinfo->digestEncAlg), signalgtag, NULL) != SECSuccess)
goto loser;
return SECSuccess;
loser:
if (signature.len != 0)
SECITEM_FreeItem (&signature, PR_FALSE);
if (privkey)
SECKEY_DestroyPrivateKey(privkey);
return SECFailure;
}
SECStatus
NSS_CMSSignerInfo_VerifyCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb,
SECCertUsage certusage)
{
CERTCertificate *cert;
int64 stime;
if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, certdb)) == NULL) {
signerinfo->verificationStatus = NSSCMSVS_SigningCertNotFound;
return SECFailure;
}
/*
* Get and convert the signing time; if available, it will be used
* both on the cert verification and for importing the sender
* email profile.
*/
if (NSS_CMSSignerInfo_GetSigningTime (signerinfo, &stime) != SECSuccess)
stime = PR_Now(); /* not found or conversion failed, so check against now */
/*
* 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, stime, signerinfo->cmsg->pwfn_arg, NULL) != SECSuccess) {
signerinfo->verificationStatus = NSSCMSVS_SigningCertNotTrusted;
return SECFailure;
}
return SECSuccess;
}
/*
* NSS_CMSSignerInfo_Verify - verify the signature of a single SignerInfo
*
* Just verifies the signature. The assumption is that verification of the certificate
* is done already.
*/
SECStatus
NSS_CMSSignerInfo_Verify(NSSCMSSignerInfo *signerinfo, SECItem *digest, SECItem *contentType)
{
SECKEYPublicKey *publickey = NULL;
NSSCMSAttribute *attr;
SECItem encoded_attrs;
CERTCertificate *cert;
NSSCMSVerificationStatus vs = NSSCMSVS_Unverified;
PLArenaPool *poolp;
if (signerinfo == NULL)
return SECFailure;
/* NSS_CMSSignerInfo_GetSigningCertificate will fail if 2nd parm is NULL and */
/* cert has not been verified */
if ((cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL)) == NULL) {
vs = NSSCMSVS_SigningCertNotFound;
goto loser;
}
if ((publickey = CERT_ExtractPublicKey(cert)) == NULL) {
vs = NSSCMSVS_ProcessingError;
goto loser;
}
/*
* 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.
*/
switch (SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg))) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
case SEC_OID_ANSIX9_DSA_SIGNATURE:
case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
/* ok */
break;
case SEC_OID_UNKNOWN:
vs = NSSCMSVS_SignatureAlgorithmUnknown;
goto loser;
default:
vs = NSSCMSVS_SignatureAlgorithmUnsupported;
goto loser;
}
if (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
if (contentType) {
/*
* Check content type
*
* RFC2630 sez 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.
*/
if ((attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE)) == NULL)
{
vs = NSSCMSVS_MalformedSignature;
goto loser;
}
if (NSS_CMSAttribute_CompareValue(attr, contentType) == PR_FALSE) {
vs = NSSCMSVS_MalformedSignature;
goto loser;
}
}
/*
* Check digest
*/
if ((attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr, SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE)) == NULL)
{
vs = NSSCMSVS_MalformedSignature;
goto loser;
}
if (NSS_CMSAttribute_CompareValue(attr, digest) == PR_FALSE) {
vs = NSSCMSVS_DigestMismatch;
goto loser;
}
if ((poolp = PORT_NewArena (1024)) == NULL) {
vs = NSSCMSVS_ProcessingError;
goto loser;
}
/*
* Check signature
*
* The signature is based on a digest of the DER-encoded authenticated
* attributes. So, first we encode and then we digest/verify.
* we trust the decoder to have the attributes in the right (sorted) order
*/
encoded_attrs.data = NULL;
encoded_attrs.len = 0;
if (NSS_CMSAttributeArray_Encode(poolp, &(signerinfo->authAttr), &encoded_attrs) == NULL ||
encoded_attrs.data == NULL || encoded_attrs.len == 0)
{
vs = NSSCMSVS_ProcessingError;
goto loser;
}
vs = (VFY_VerifyData (encoded_attrs.data, encoded_attrs.len,
publickey, &(signerinfo->encDigest),
SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
signerinfo->cmsg->pwfn_arg) != SECSuccess) ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
PORT_FreeArena(poolp, PR_FALSE); /* awkward memory management :-( */
} else {
SECItem *sig;
/* No authenticated attributes. The signature is based on the plain message digest. */
sig = &(signerinfo->encDigest);
if (sig->len == 0)
goto loser;
vs = (VFY_VerifyDigest(digest, publickey, sig,
SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
signerinfo->cmsg->pwfn_arg) != SECSuccess) ? NSSCMSVS_BadSignature : NSSCMSVS_GoodSignature;
}
if (vs == NSSCMSVS_BadSignature) {
/*
* 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);
}
if (publickey != NULL)
SECKEY_DestroyPublicKey (publickey);
signerinfo->verificationStatus = vs;
return (vs == NSSCMSVS_GoodSignature) ? SECSuccess : SECFailure;
loser:
if (publickey != NULL)
SECKEY_DestroyPublicKey (publickey);
signerinfo->verificationStatus = vs;
PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
return SECFailure;
}
NSSCMSVerificationStatus
NSS_CMSSignerInfo_GetVerificationStatus(NSSCMSSignerInfo *signerinfo)
{
return signerinfo->verificationStatus;
}
SECOidData *
NSS_CMSSignerInfo_GetDigestAlg(NSSCMSSignerInfo *signerinfo)
{
return SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
}
SECOidTag
NSS_CMSSignerInfo_GetDigestAlgTag(NSSCMSSignerInfo *signerinfo)
{
SECOidData *algdata;
algdata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
if (algdata != NULL)
return algdata->offset;
else
return SEC_OID_UNKNOWN;
}
CERTCertificateList *
NSS_CMSSignerInfo_GetCertList(NSSCMSSignerInfo *signerinfo)
{
return signerinfo->certList;
}
int
NSS_CMSSignerInfo_GetVersion(NSSCMSSignerInfo *signerinfo)
{
unsigned long version;
/* always take apart the SECItem */
if (SEC_ASN1DecodeInteger(&(signerinfo->version), &version) != SECSuccess)
return 0;
else
return (int)version;
}
/*
* NSS_CMSSignerInfo_GetSigningTime - return the signing time,
* in UTCTime format, of a CMS signerInfo.
*
* sinfo - signerInfo data for this signer
*
* Returns a pointer to XXXX (what?)
* A return value of NULL is an error.
*/
SECStatus
NSS_CMSSignerInfo_GetSigningTime(NSSCMSSignerInfo *sinfo, PRTime *stime)
{
NSSCMSAttribute *attr;
SECItem *value;
if (sinfo == NULL)
return SECFailure;
if (sinfo->signingTime != 0) {
*stime = sinfo->signingTime; /* cached copy */
return SECSuccess;
}
attr = NSS_CMSAttributeArray_FindAttrByOidTag(sinfo->authAttr, SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
/* XXXX multi-valued attributes NIH */
if (attr == NULL || (value = NSS_CMSAttribute_GetValue(attr)) == NULL)
return SECFailure;
if (DER_UTCTimeToTime(stime, value) != SECSuccess)
return SECFailure;
sinfo->signingTime = *stime; /* make cached copy */
return SECSuccess;
}
/*
* Return the signing cert of a CMS signerInfo.
*
* the certs in the enclosing SignedData must have been imported already
*/
CERTCertificate *
NSS_CMSSignerInfo_GetSigningCertificate(NSSCMSSignerInfo *signerinfo, CERTCertDBHandle *certdb)
{
CERTCertificate *cert;
if (signerinfo->cert != NULL)
return signerinfo->cert;
/* no certdb, and cert hasn't been set yet? */
if (certdb == NULL)
return NULL;
/*
* 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.
*/
switch (signerinfo->signerIdentifier.identifierType) {
case NSSCMSSignerID_IssuerSN:
cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->signerIdentifier.id.issuerAndSN);
break;
case NSSCMSSignerID_SubjectKeyID:
#if 0 /* not yet implemented */
cert = CERT_FindCertBySubjectKeyID(certdb, signerinfo->signerIdentifier.id.subjectKeyID);
#else
cert = NULL;
#endif
break;
default:
cert = NULL;
break;
}
/* cert can be NULL at that point */
signerinfo->cert = cert; /* earmark it */
return cert;
}
/*
* NSS_CMSSignerInfo_GetSignerCommonName - return the common name of the signer
*
* sinfo - signerInfo data for this signer
*
* Returns a pointer to allocated memory, which must be freed.
* A return value of NULL is an error.
*/
char *
NSS_CMSSignerInfo_GetSignerCommonName(NSSCMSSignerInfo *sinfo)
{
CERTCertificate *signercert;
/* will fail if cert is not verified */
if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
return NULL;
return (CERT_GetCommonName(&signercert->subject));
}
/*
* NSS_CMSSignerInfo_GetSignerEmailAddress - return the common name of the signer
*
* sinfo - signerInfo data for this signer
*
* Returns a pointer to allocated memory, which must be freed.
* A return value of NULL is an error.
*/
char *
NSS_CMSSignerInfo_GetSignerEmailAddress(NSSCMSSignerInfo *sinfo)
{
CERTCertificate *signercert;
if ((signercert = NSS_CMSSignerInfo_GetSigningCertificate(sinfo, NULL)) == NULL)
return NULL;
if (signercert->emailAddr == NULL)
return NULL;
return (PORT_Strdup(signercert->emailAddr));
}
/*
* NSS_CMSSignerInfo_AddAuthAttr - add an attribute to the
* authenticated (i.e. signed) attributes of "signerinfo".
*/
SECStatus
NSS_CMSSignerInfo_AddAuthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
{
return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->authAttr), attr);
}
/*
* NSS_CMSSignerInfo_AddUnauthAttr - add an attribute to the
* unauthenticated attributes of "signerinfo".
*/
SECStatus
NSS_CMSSignerInfo_AddUnauthAttr(NSSCMSSignerInfo *signerinfo, NSSCMSAttribute *attr)
{
return NSS_CMSAttributeArray_AddAttr(signerinfo->cmsg->poolp, &(signerinfo->unAuthAttr), attr);
}
/*
* NSS_CMSSignerInfo_AddSigningTime - add the signing time to the
* authenticated (i.e. signed) attributes of "signerinfo".
*
* This is expected to be included in outgoing signed
* messages for email (S/MIME) but is likely useful in other situations.
*
* This should only be added once; a second call will do nothing.
*
* XXX This will probably just shove the current time into "signerinfo"
* but it will not actually get signed until the entire item is
* processed for encoding. Is this (expected to be small) delay okay?
*/
SECStatus
NSS_CMSSignerInfo_AddSigningTime(NSSCMSSignerInfo *signerinfo, PRTime t)
{
NSSCMSAttribute *attr;
SECItem stime;
void *mark;
PLArenaPool *poolp;
poolp = signerinfo->cmsg->poolp;
mark = PORT_ArenaMark(poolp);
/* create new signing time attribute */
if (DER_TimeToUTCTime(&stime, t) != SECSuccess)
goto loser;
if ((attr = NSS_CMSAttribute_Create(poolp, SEC_OID_PKCS9_SIGNING_TIME, &stime, PR_FALSE)) == NULL) {
SECITEM_FreeItem (&stime, PR_FALSE);
goto loser;
}
SECITEM_FreeItem (&stime, PR_FALSE);
if (NSS_CMSSignerInfo_AddAuthAttr(signerinfo, attr) != SECSuccess)
goto loser;
PORT_ArenaUnmark (poolp, mark);
return SECSuccess;
loser:
PORT_ArenaRelease (poolp, mark);
return SECFailure;
}
/*
* NSS_CMSSignerInfo_AddCounterSignature - countersign a signerinfo
*
* 1. digest the DER-encoded signature value of the original signerinfo
* 2. create new signerinfo with correct version, sid, digestAlg
* 3. add message-digest authAttr, but NO content-type
* 4. sign the authAttrs
* 5. add the whole thing to original signerInfo's unAuthAttrs
*
* XXXX give back the new signerinfo?
*/
SECStatus
NSS_CMSSignerInfo_AddCounterSignature(NSSCMSSignerInfo *signerinfo,
SECOidTag digestalg, CERTCertificate signingcert)
{
/* XXXX TBD XXXX */
return SECFailure;
}
/*
* XXXX the following needs to be done in the S/MIME layer code
* after signature of a signerinfo is verified
*/
SECStatus
NSS_SMIMESignerInfo_SaveSMIMEProfile(NSSCMSSignerInfo *signerinfo)
{
CERTCertificate *cert;
SECItem *profile = NULL;
NSSCMSAttribute *attr;
SECItem *utc_stime;
int save_error;
SECStatus rv;
/* XXX sanity check - see if verification status is ok */
/* XXX 0 = unverified */
/* XXX find preferred encyption cert */
/* find the cert the signerinfo is signed with */
cert = NSS_CMSSignerInfo_GetSigningCertificate(signerinfo, NULL);
if (cert == NULL || cert->emailAddr == NULL)
return SECFailure;
/*
* 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 (!NSS_CMSArray_IsEmpty((void **)signerinfo->authAttr)) {
attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
SEC_OID_PKCS9_SMIME_CAPABILITIES,
PR_TRUE);
profile = NSS_CMSAttribute_GetValue(attr);
attr = NSS_CMSAttributeArray_FindAttrByOidTag(signerinfo->authAttr,
SEC_OID_PKCS9_SIGNING_TIME,
PR_TRUE);
utc_stime = NSS_CMSAttribute_GetValue(attr);
}
rv = CERT_SaveSMimeProfile (cert, profile, utc_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);
return rv;
}
/*
* NSS_CMSSignerInfo_IncludeCerts - set cert chain inclusion mode for this signer
*/
SECStatus
NSS_CMSSignerInfo_IncludeCerts(NSSCMSSignerInfo *signerinfo, NSSCMSCertChainMode cm, SECCertUsage usage)
{
if (signerinfo->cert == NULL)
return SECFailure;
switch (cm) {
case NSSCMSCM_None:
signerinfo->certList = NULL;
break;
case NSSCMSCM_CertOnly:
signerinfo->certList = CERT_CertListFromCert(signerinfo->cert);
break;
case NSSCMSCM_CertChain:
signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_FALSE);
break;
case NSSCMSCM_CertChainWithRoot:
signerinfo->certList = CERT_CertChainFromCert(signerinfo->cert, usage, PR_TRUE);
break;
}
if (cm != NSSCMSCM_None && signerinfo->certList == NULL)
return SECFailure;
return SECSuccess;
}

View File

@ -0,0 +1,489 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* Header for CMS types.
*
* $Id: cmst.h,v 1.2 2000-06-13 21:56:32 chrisk%netscape.com Exp $
*/
#ifndef _CMST_H_
#define _CMST_H_
#include "seccomon.h"
#include "secoidt.h"
#include "certt.h"
#include "secmodt.h"
#include "secmodt.h"
#include "plarena.h"
/* Non-opaque objects. NOTE, though: I want them to be treated as
* opaque as much as possible. If I could hide them completely,
* I would. (I tried, but ran into trouble that was taking me too
* much time to get out of.) I still intend to try to do so.
* In fact, the only type that "outsiders" should even *name* is
* NSSCMSMessage, and they should not reference its fields.
*/
/* rjr: PKCS #11 cert handling (pk11cert.c) does use NSSCMSRecipientInfo's.
* This is because when we search the recipient list for the cert and key we
* want, we need to invert the order of the loops we used to have. The old
* loops were:
*
* For each recipient {
* find_cert = PK11_Find_AllCert(recipient->issuerSN);
* [which unrolls to... ]
* For each slot {
* Log into slot;
* search slot for cert;
* }
* }
*
* the new loop searchs all the recipients at once on a slot. this allows
* PKCS #11 to order slots in such a way that logout slots don't get checked
* if we can find the cert on a logged in slot. This eliminates lots of
* spurious password prompts when smart cards are installed... so why this
* comment? If you make NSSCMSRecipientInfo completely opaque, you need
* to provide a non-opaque list of issuerSN's (the only field PKCS#11 needs
* and fix up pk11cert.c first. NOTE: Only S/MIME calls this special PKCS #11
* function.
*/
typedef struct NSSCMSMessageStr NSSCMSMessage;
typedef union NSSCMSContentUnion NSSCMSContent;
typedef struct NSSCMSContentInfoStr NSSCMSContentInfo;
typedef struct NSSCMSSignedDataStr NSSCMSSignedData;
typedef struct NSSCMSSignerInfoStr NSSCMSSignerInfo;
typedef struct NSSCMSSignerIdentifierStr NSSCMSSignerIdentifier;
typedef struct NSSCMSEnvelopedDataStr NSSCMSEnvelopedData;
typedef struct NSSCMSOriginatorInfoStr NSSCMSOriginatorInfo;
typedef struct NSSCMSRecipientInfoStr NSSCMSRecipientInfo;
typedef struct NSSCMSDigestedDataStr NSSCMSDigestedData;
typedef struct NSSCMSEncryptedDataStr NSSCMSEncryptedData;
typedef struct NSSCMSSMIMEKEAParametersStr NSSCMSSMIMEKEAParameters;
typedef struct NSSCMSAttributeStr NSSCMSAttribute;
typedef struct NSSCMSDecoderContextStr NSSCMSDecoderContext;
typedef struct NSSCMSEncoderContextStr NSSCMSEncoderContext;
typedef struct NSSCMSCipherContextStr NSSCMSCipherContext;
typedef struct NSSCMSDigestContextStr NSSCMSDigestContext;
/*
* Type of function passed to NSSCMSDecode or NSSCMSDecoderStart.
* If specified, this is where the content bytes (only) will be "sent"
* as they are recovered during the decoding.
* And:
* Type of function passed to NSSCMSEncode or NSSCMSEncoderStart.
* This is where the DER-encoded bytes will be "sent".
*
* XXX Should just combine this with NSSCMSEncoderContentCallback type
* and use a simpler, common name.
*/
typedef void (*NSSCMSContentCallback)(void *arg, const char *buf, unsigned long len);
/*
* Type of function passed to NSSCMSDecode or NSSCMSDecoderStart
* to retrieve the decryption key. This function is intended to be
* used for EncryptedData content info's which do not have a key available
* in a certificate, etc.
*/
typedef PK11SymKey *(*NSSCMSGetDecryptKeyCallback)(void *arg, SECAlgorithmID *algid);
/* =============================================================================
* ENCAPSULATED CONTENTINFO & CONTENTINFO
*/
union NSSCMSContentUnion {
/* either unstructured */
SECItem * data;
/* or structured data */
NSSCMSDigestedData * digestedData;
NSSCMSEncryptedData * encryptedData;
NSSCMSEnvelopedData * envelopedData;
NSSCMSSignedData * signedData;
/* or anonymous pointer to something */
void * pointer;
};
struct NSSCMSContentInfoStr {
SECItem contentType;
NSSCMSContent content;
/* --------- local; not part of encoding --------- */
SECOidData * contentTypeTag;
/* additional info for encryptedData and envelopedData */
/* we waste this space for signedData and digestedData. sue me. */
SECAlgorithmID contentEncAlg;
SECItem * rawContent; /* encrypted DER, optional */
/* XXXX bytes not encrypted, but encoded? */
/* --------- local; not part of encoding --------- */
PK11SymKey * bulkkey; /* bulk encryption key */
int keysize; /* size of bulk encryption key
* (only used by creation code) */
SECOidTag contentEncAlgTag; /* oid tag of encryption algorithm
* (only used by creation code) */
NSSCMSCipherContext *ciphcx; /* context for en/decryption going on */
NSSCMSDigestContext *digcx; /* context for digesting going on */
};
/* =============================================================================
* MESSAGE
*/
struct NSSCMSMessageStr {
NSSCMSContentInfo contentInfo; /* "outer" cinfo */
/* --------- local; not part of encoding --------- */
PLArenaPool * poolp;
PRBool poolp_is_ours;
int refCount;
/* properties of the "inner" data */
SECAlgorithmID ** detached_digestalgs;
SECItem ** detached_digests;
void * pwfn_arg;
NSSCMSGetDecryptKeyCallback decrypt_key_cb;
void * decrypt_key_cb_arg;
};
/* =============================================================================
* SIGNEDDATA
*/
struct NSSCMSSignedDataStr {
SECItem version;
SECAlgorithmID ** digestAlgorithms;
NSSCMSContentInfo contentInfo;
SECItem ** rawCerts;
CERTSignedCrl ** crls;
NSSCMSSignerInfo ** signerInfos;
/* --------- local; not part of encoding --------- */
NSSCMSMessage * cmsg; /* back pointer to message */
SECItem ** digests;
CERTCertificate ** certs;
CERTCertificateList ** certLists;
};
#define NSS_CMS_SIGNED_DATA_VERSION_BASIC 1 /* what we *create* */
#define NSS_CMS_SIGNED_DATA_VERSION_EXT 3 /* what we *create* */
typedef enum {
NSSCMSVS_Unverified = 0,
NSSCMSVS_GoodSignature,
NSSCMSVS_BadSignature,
NSSCMSVS_DigestMismatch,
NSSCMSVS_SigningCertNotFound,
NSSCMSVS_SigningCertNotTrusted,
NSSCMSVS_SignatureAlgorithmUnknown,
NSSCMSVS_SignatureAlgorithmUnsupported,
NSSCMSVS_MalformedSignature,
NSSCMSVS_ProcessingError
} NSSCMSVerificationStatus;
typedef enum {
NSSCMSSignerID_IssuerSN,
NSSCMSSignerID_SubjectKeyID
} NSSCMSSignerIDSelector;
struct NSSCMSSignerIdentifierStr {
NSSCMSSignerIDSelector identifierType;
union {
CERTIssuerAndSN *issuerAndSN;
SECItem *subjectKeyID;
} id;
};
struct NSSCMSSignerInfoStr {
SECItem version;
NSSCMSSignerIdentifier signerIdentifier;
SECAlgorithmID digestAlg;
NSSCMSAttribute ** authAttr;
SECAlgorithmID digestEncAlg;
SECItem encDigest;
NSSCMSAttribute ** unAuthAttr;
/* --------- local; not part of encoding --------- */
NSSCMSMessage * cmsg; /* back pointer to message */
CERTCertificate * cert;
CERTCertificateList * certList;
PRTime signingTime;
NSSCMSVerificationStatus verificationStatus;
};
#define NSS_CMS_SIGNER_INFO_VERSION_ISSUERSN 1 /* what we *create* */
#define NSS_CMS_SIGNER_INFO_VERSION_SUBJKEY 3 /* what we *create* */
typedef enum {
NSSCMSCM_None = 0,
NSSCMSCM_CertOnly,
NSSCMSCM_CertChain,
NSSCMSCM_CertChainWithRoot
} NSSCMSCertChainMode;
/* =============================================================================
* ENVELOPED DATA
*/
struct NSSCMSEnvelopedDataStr {
SECItem version;
NSSCMSOriginatorInfo * originatorInfo; /* optional */
NSSCMSRecipientInfo ** recipientInfos;
NSSCMSContentInfo contentInfo;
NSSCMSAttribute ** unprotectedAttr;
/* --------- local; not part of encoding --------- */
NSSCMSMessage * cmsg; /* back pointer to message */
};
#define NSS_CMS_ENVELOPED_DATA_VERSION_REG 0 /* what we *create* */
#define NSS_CMS_ENVELOPED_DATA_VERSION_ADV 2 /* what we *create* */
struct NSSCMSOriginatorInfoStr {
SECItem ** rawCerts;
CERTSignedCrl ** crls;
/* --------- local; not part of encoding --------- */
CERTCertificate ** certs;
};
/* -----------------------------------------------------------------------------
* key transport recipient info
*/
typedef enum {
NSSCMSRecipientID_IssuerSN,
NSSCMSRecipientID_SubjectKeyID
} NSSCMSRecipientIDSelector;
struct NSSCMSRecipientIdentifierStr {
NSSCMSRecipientIDSelector identifierType;
union {
CERTIssuerAndSN *issuerAndSN;
SECItem *subjectKeyID;
} id;
};
typedef struct NSSCMSRecipientIdentifierStr NSSCMSRecipientIdentifier;
struct NSSCMSKeyTransRecipientInfoStr {
SECItem version;
NSSCMSRecipientIdentifier recipientIdentifier;
SECAlgorithmID keyEncAlg;
SECItem encKey;
};
typedef struct NSSCMSKeyTransRecipientInfoStr NSSCMSKeyTransRecipientInfo;
#define NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN 0 /* what we *create* */
#define NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY 2 /* what we *create* */
/* -----------------------------------------------------------------------------
* key agreement recipient info
*/
struct NSSCMSOriginatorPublicKeyStr {
SECAlgorithmID algorithmIdentifier;
SECItem publicKey; /* bit string! */
};
typedef struct NSSCMSOriginatorPublicKeyStr NSSCMSOriginatorPublicKey;
typedef enum {
NSSCMSOriginatorIDOrKey_IssuerSN,
NSSCMSOriginatorIDOrKey_SubjectKeyID,
NSSCMSOriginatorIDOrKey_OriginatorPublicKey
} NSSCMSOriginatorIDOrKeySelector;
struct NSSCMSOriginatorIdentifierOrKeyStr {
NSSCMSOriginatorIDOrKeySelector identifierType;
union {
CERTIssuerAndSN *issuerAndSN; /* static-static */
SECItem *subjectKeyID; /* static-static */
NSSCMSOriginatorPublicKey originatorPublicKey; /* ephemeral-static */
} id;
};
typedef struct NSSCMSOriginatorIdentifierOrKeyStr NSSCMSOriginatorIdentifierOrKey;
struct NSSCMSRecipientKeyIdentifierStr {
SECItem * subjectKeyIdentifier;
SECItem * date; /* optional */
SECItem * other; /* optional */
};
typedef struct NSSCMSRecipientKeyIdentifierStr NSSCMSRecipientKeyIdentifier;
typedef enum {
NSSCMSKeyAgreeRecipientID_IssuerSN,
NSSCMSKeyAgreeRecipientID_RKeyID
} NSSCMSKeyAgreeRecipientIDSelector;
struct NSSCMSKeyAgreeRecipientIdentifierStr {
NSSCMSKeyAgreeRecipientIDSelector identifierType;
union {
CERTIssuerAndSN *issuerAndSN;
NSSCMSRecipientKeyIdentifier recipientKeyIdentifier;
} id;
};
typedef struct NSSCMSKeyAgreeRecipientIdentifierStr NSSCMSKeyAgreeRecipientIdentifier;
struct NSSCMSRecipientEncryptedKeyStr {
NSSCMSKeyAgreeRecipientIdentifier recipientIdentifier;
SECItem encKey;
};
typedef struct NSSCMSRecipientEncryptedKeyStr NSSCMSRecipientEncryptedKey;
struct NSSCMSKeyAgreeRecipientInfoStr {
SECItem version;
NSSCMSOriginatorIdentifierOrKey originatorIdentifierOrKey;
SECItem * ukm; /* optional */
SECAlgorithmID keyEncAlg;
NSSCMSRecipientEncryptedKey ** recipientEncryptedKeys;
};
typedef struct NSSCMSKeyAgreeRecipientInfoStr NSSCMSKeyAgreeRecipientInfo;
#define NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION 3 /* what we *create* */
/* -----------------------------------------------------------------------------
* KEK recipient info
*/
struct NSSCMSKEKIdentifierStr {
SECItem keyIdentifier;
SECItem * date; /* optional */
SECItem * other; /* optional */
};
typedef struct NSSCMSKEKIdentifierStr NSSCMSKEKIdentifier;
struct NSSCMSKEKRecipientInfoStr {
SECItem version;
NSSCMSKEKIdentifier kekIdentifier;
SECAlgorithmID keyEncAlg;
SECItem encKey;
};
typedef struct NSSCMSKEKRecipientInfoStr NSSCMSKEKRecipientInfo;
#define NSS_CMS_KEK_RECIPIENT_INFO_VERSION 4 /* what we *create* */
/* -----------------------------------------------------------------------------
* recipient info
*/
typedef enum {
NSSCMSRecipientInfoID_KeyTrans,
NSSCMSRecipientInfoID_KeyAgree,
NSSCMSRecipientInfoID_KEK
} NSSCMSRecipientInfoIDSelector;
struct NSSCMSRecipientInfoStr {
NSSCMSRecipientInfoIDSelector recipientInfoType;
union {
NSSCMSKeyTransRecipientInfo keyTransRecipientInfo;
NSSCMSKeyAgreeRecipientInfo keyAgreeRecipientInfo;
NSSCMSKEKRecipientInfo kekRecipientInfo;
} ri;
/* --------- local; not part of encoding --------- */
NSSCMSMessage * cmsg; /* back pointer to message */
CERTCertificate * cert; /* recipient's certificate */
};
/* =============================================================================
* DIGESTED DATA
*/
struct NSSCMSDigestedDataStr {
SECItem version;
SECAlgorithmID digestAlg;
NSSCMSContentInfo contentInfo;
SECItem digest;
/* --------- local; not part of encoding --------- */
NSSCMSMessage * cmsg; /* back pointer */
SECItem cdigest; /* calculated digest */
};
#define NSS_CMS_DIGESTED_DATA_VERSION_DATA 0 /* what we *create* */
#define NSS_CMS_DIGESTED_DATA_VERSION_ENCAP 2 /* what we *create* */
/* =============================================================================
* ENCRYPTED DATA
*/
struct NSSCMSEncryptedDataStr {
SECItem version;
NSSCMSContentInfo contentInfo;
NSSCMSAttribute ** unprotectedAttr; /* optional */
/* --------- local; not part of encoding --------- */
NSSCMSMessage * cmsg; /* back pointer */
};
#define NSS_CMS_ENCRYPTED_DATA_VERSION 0 /* what we *create* */
#define NSS_CMS_ENCRYPTED_DATA_VERSION_UPATTR 2 /* what we *create* */
/* =============================================================================
* FORTEZZA KEA
*/
/* An enumerated type used to select templates based on the encryption
scenario and data specifics. */
typedef enum {
NSSCMSKEAUsesSkipjack,
NSSCMSKEAUsesNonSkipjack,
NSSCMSKEAUsesNonSkipjackWithPaddedEncKey
} NSSCMSKEATemplateSelector;
/* ### mwelch - S/MIME KEA parameters. These don't really fit here,
but I cannot think of a more appropriate place at this time. */
struct NSSCMSSMIMEKEAParametersStr {
SECItem originatorKEAKey; /* sender KEA key (encrypted?) */
SECItem originatorRA; /* random number generated by sender */
SECItem nonSkipjackIV; /* init'n vector for SkipjackCBC64
decryption of KEA key if Skipjack
is not the bulk algorithm used on
the message */
SECItem bulkKeySize; /* if Skipjack is not the bulk
algorithm used on the message,
and the size of the bulk encryption
key is not the same as that of
originatorKEAKey (due to padding
perhaps), this field will contain
the real size of the bulk encryption
key. */
};
/*
* *****************************************************************************
* *****************************************************************************
* *****************************************************************************
*/
/*
* See comment above about this type not really belonging to CMS.
*/
struct NSSCMSAttributeStr {
/* The following fields make up an encoded Attribute: */
SECItem type;
SECItem ** values; /* data may or may not be encoded */
/* The following fields are not part of an encoded Attribute: */
SECOidData * typeTag;
PRBool encoded; /* when true, values are encoded */
};
#endif /* _CMST_H_ */

View File

@ -0,0 +1,362 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* CMS miscellaneous utility functions.
*
* $Id: cmsutil.c,v 1.2 2000-06-13 21:56:33 chrisk%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"
/*
* NSS_CMSArray_SortByDER - sort array of objects by objects' DER encoding
*
* make sure that the order of the objects guarantees valid DER (which must be
* in lexigraphically ascending order for a SET OF); if reordering is necessary it
* will be done in place (in objs).
*/
SECStatus
NSS_CMSArray_SortByDER(void **objs, const SEC_ASN1Template *objtemplate, void **objs2)
{
PRArenaPool *poolp;
int num_objs;
SECItem **enc_objs;
SECStatus rv = SECFailure;
int i;
if (objs == NULL) /* already sorted */
return SECSuccess;
num_objs = NSS_CMSArray_Count((void **)objs);
if (num_objs == 0 || num_objs == 1) /* already sorted. */
return SECSuccess;
poolp = PORT_NewArena (1024); /* arena for temporaries */
if (poolp == NULL)
return SECFailure; /* no memory; nothing we can do... */
/*
* Allocate arrays to hold the individual encodings which we will use
* for comparisons and the reordered attributes as they are sorted.
*/
enc_objs = (SECItem **)PORT_ArenaZAlloc(poolp, (num_objs + 1) * sizeof(SECItem *));
if (enc_objs == NULL)
goto loser;
/* DER encode each individual object. */
for (i = 0; i < num_objs; i++) {
enc_objs[i] = SEC_ASN1EncodeItem(poolp, NULL, objs[i], objtemplate);
if (enc_objs[i] == NULL)
goto loser;
}
enc_objs[num_objs] = NULL;
/* now compare and sort objs by the order of enc_objs */
NSS_CMSArray_Sort((void **)enc_objs, NSS_CMSUtil_DERCompare, objs, objs2);
rv = SECSuccess;
loser:
PORT_FreeArena (poolp, PR_FALSE);
return rv;
}
/*
* NSS_CMSUtil_DERCompare - for use with NSS_CMSArray_Sort to
* sort arrays of SECItems containing DER
*/
int
NSS_CMSUtil_DERCompare(void *a, void *b)
{
SECItem *der1 = (SECItem *)a;
SECItem *der2 = (SECItem *)b;
int j;
/*
* Find the lowest (lexigraphically) encoding. One that is
* shorter than all the rest is known to be "less" because each
* attribute is of the same type (a SEQUENCE) and so thus the
* first octet of each is the same, and the second octet is
* the length (or the length of the length with the high bit
* set, followed by the length, which also works out to always
* order the shorter first). Two (or more) that have the
* same length need to be compared byte by byte until a mismatch
* is found.
*/
if (der1->len != der2->len)
return (der1->len < der2->len) ? -1 : 1;
for (j = 0; j < der1->len; j++) {
if (der1->data[j] == der2->data[j])
continue;
return (der1->data[j] < der2->data[j]) ? -1 : 1;
}
return 0;
}
/*
* NSS_CMSAlgArray_GetIndexByAlgID - find a specific algorithm in an array of
* algorithms.
*
* algorithmArray - array of algorithm IDs
* algid - algorithmid of algorithm to pick
*
* Returns:
* An integer containing the index of the algorithm in the array or -1 if
* algorithm was not found.
*/
int
NSS_CMSAlgArray_GetIndexByAlgID(SECAlgorithmID **algorithmArray, SECAlgorithmID *algid)
{
int i;
if (algorithmArray == NULL || algorithmArray[0] == NULL)
return -1;
for (i = 0; algorithmArray[i] != NULL; i++) {
if (SECOID_CompareAlgorithmID(algorithmArray[i], algid) == SECEqual)
break; /* bingo */
}
if (algorithmArray[i] == NULL)
return -1; /* not found */
return i;
}
/*
* NSS_CMSAlgArray_GetIndexByAlgTag - find a specific algorithm in an array of
* algorithms.
*
* algorithmArray - array of algorithm IDs
* algtag - algorithm tag of algorithm to pick
*
* Returns:
* An integer containing the index of the algorithm in the array or -1 if
* algorithm was not found.
*/
int
NSS_CMSAlgArray_GetIndexByAlgTag(SECAlgorithmID **algorithmArray, SECOidTag algtag)
{
SECOidData *algid;
int i;
if (algorithmArray == NULL || algorithmArray[0] == NULL)
return -1;
for (i = 0; algorithmArray[i] != NULL; i++) {
algid = SECOID_FindOID(&(algorithmArray[i]->algorithm));
if (algid->offset == algtag)
break; /* bingo */
}
if (algorithmArray[i] == NULL)
return -1; /* not found */
return i;
}
SECHashObject *
NSS_CMSUtil_GetHashObjByAlgID(SECAlgorithmID *algid)
{
SECOidData *oiddata;
SECHashObject *digobj;
/* here are the algorithms we know */
oiddata = SECOID_FindOID(&(algid->algorithm));
if (oiddata == NULL) {
digobj = NULL;
} else {
switch (oiddata->offset) {
case SEC_OID_MD2:
digobj = &SECHashObjects[HASH_AlgMD2];
break;
case SEC_OID_MD5:
digobj = &SECHashObjects[HASH_AlgMD5];
break;
case SEC_OID_SHA1:
digobj = &SECHashObjects[HASH_AlgSHA1];
break;
default:
digobj = NULL;
break;
}
}
return digobj;
}
/*
* XXX I would *really* like to not have to do this, but the current
* signing interface gives me little choice.
*/
SECOidTag
NSS_CMSUtil_MakeSignatureAlgorithm(SECOidTag hashalg, SECOidTag encalg)
{
switch (encalg) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
switch (hashalg) {
case SEC_OID_MD2:
return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
case SEC_OID_MD5:
return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
case SEC_OID_SHA1:
return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
default:
return SEC_OID_UNKNOWN;
}
case SEC_OID_ANSIX9_DSA_SIGNATURE:
case SEC_OID_MISSI_KEA_DSS:
case SEC_OID_MISSI_DSS:
switch (hashalg) {
case SEC_OID_SHA1:
return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
default:
return SEC_OID_UNKNOWN;
}
default:
break;
}
return encalg; /* maybe it is already the right algid */
}
const SEC_ASN1Template *
NSS_CMSUtil_GetTemplateByTypeTag(SECOidTag type)
{
const SEC_ASN1Template *template;
extern const SEC_ASN1Template NSSCMSSignedDataTemplate[];
extern const SEC_ASN1Template NSSCMSEnvelopedDataTemplate[];
extern const SEC_ASN1Template NSSCMSEncryptedDataTemplate[];
extern const SEC_ASN1Template NSSCMSDigestedDataTemplate[];
switch (type) {
case SEC_OID_PKCS7_SIGNED_DATA:
template = NSSCMSSignedDataTemplate;
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
template = NSSCMSEnvelopedDataTemplate;
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
template = NSSCMSEncryptedDataTemplate;
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
template = NSSCMSDigestedDataTemplate;
break;
default:
case SEC_OID_PKCS7_DATA:
template = NULL;
break;
}
return template;
}
size_t
NSS_CMSUtil_GetSizeByTypeTag(SECOidTag type)
{
size_t size;
switch (type) {
case SEC_OID_PKCS7_SIGNED_DATA:
size = sizeof(NSSCMSSignedData);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
size = sizeof(NSSCMSEnvelopedData);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
size = sizeof(NSSCMSEncryptedData);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
size = sizeof(NSSCMSDigestedData);
break;
default:
case SEC_OID_PKCS7_DATA:
size = 0;
break;
}
return size;
}
NSSCMSContentInfo *
NSS_CMSContent_GetContentInfo(void *msg, SECOidTag type)
{
NSSCMSContent c;
NSSCMSContentInfo *cinfo;
PORT_Assert(msg != NULL);
c.pointer = msg;
switch (type) {
case SEC_OID_PKCS7_SIGNED_DATA:
cinfo = &(c.signedData->contentInfo);
break;
case SEC_OID_PKCS7_ENVELOPED_DATA:
cinfo = &(c.envelopedData->contentInfo);
break;
case SEC_OID_PKCS7_ENCRYPTED_DATA:
cinfo = &(c.encryptedData->contentInfo);
break;
case SEC_OID_PKCS7_DIGESTED_DATA:
cinfo = &(c.digestedData->contentInfo);
break;
default:
cinfo = NULL;
}
return cinfo;
}
const char *
NSS_CMSUtil_VerificationStatusToString(NSSCMSVerificationStatus vs)
{
switch (vs) {
case NSSCMSVS_Unverified: return "Unverified";
case NSSCMSVS_GoodSignature: return "GoodSignature";
case NSSCMSVS_BadSignature: return "BadSignature";
case NSSCMSVS_DigestMismatch: return "DigestMismatch";
case NSSCMSVS_SigningCertNotFound: return "SigningCertNotFound";
case NSSCMSVS_SigningCertNotTrusted: return "SigningCertNotTrusted";
case NSSCMSVS_SignatureAlgorithmUnknown: return "SignatureAlgorithmUnknown";
case NSSCMSVS_SignatureAlgorithmUnsupported: return "SignatureAlgorithmUnsupported";
case NSSCMSVS_MalformedSignature: return "MalformedSignature";
case NSSCMSVS_ProcessingError: return "ProcessingError";
default: return "Unknown";
}
}

View File

@ -0,0 +1,44 @@
#
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
#
# Override TARGETS variable so that only static libraries
# are specifed as dependencies within rules.mk.
#
TARGETS = $(LIBRARY)
SHARED_LIBRARY =
IMPORT_LIBRARY =
PURE_LIBRARY =
PROGRAM =

View File

@ -0,0 +1,74 @@
#
# 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 Netscape are
# Copyright (C) 1994-2000 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the
# terms of the GNU General Public License Version 2 or later (the
# "GPL"), in which case the provisions of the GPL are applicable
# instead of those above. If you wish to allow use of your
# version of this file only under the terms of the GPL and not to
# allow others to use your version of this file under the MPL,
# indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by
# the GPL. If you do not delete the provisions above, a recipient
# may use your version of this file under either the MPL or the
# GPL.
#
CORE_DEPTH = ../../..
EXPORTS = \
cms.h \
cmst.h \
smime.h \
cmsreclist.h \
$(NULL)
PRIVATE_EXPORTS = \
cmslocal.h \
$(NULL)
MODULE = security
CSRCS = \
cmsarray.c \
cmsasn1.c \
cmsattr.c \
cmscinfo.c \
cmscipher.c \
cmsdecode.c \
cmsdigdata.c \
cmsdigest.c \
cmsencdata.c \
cmsencode.c \
cmsenvdata.c \
cmsmessage.c \
cmspubkey.c \
cmsrecinfo.c \
cmsreclist.c \
cmssigdata.c \
cmssiginfo.c \
cmsutil.c \
smimemessage.c \
smimeutil.c \
$(NULL)
REQUIRES = security dbm
LIBRARY_NAME = smime

View File

@ -0,0 +1,139 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* Header file for routines specific to S/MIME. Keep things that are pure
* pkcs7 out of here; this is for S/MIME policy, S/MIME interoperability, etc.
*
* $Id: smime.h,v 1.2 2000-06-13 21:56:33 chrisk%netscape.com Exp $
*/
#ifndef _SECMIME_H_
#define _SECMIME_H_ 1
#include "cms.h"
/************************************************************************/
SEC_BEGIN_PROTOS
/*
* Initialize the local recording of the user S/MIME cipher preferences.
* This function is called once for each cipher, the order being
* important (first call records greatest preference, and so on).
* When finished, it is called with a "which" of CIPHER_FAMILID_MASK.
* If the function is called again after that, it is assumed that
* the preferences are being reset, and the old preferences are
* discarded.
*
* XXX This is for a particular user, and right now the storage is
* XXX local, static. The preference should be stored elsewhere to allow
* XXX for multiple uses of one library? How does SSL handle this;
* XXX it has something similar?
*
* - The "which" values are defined in ciferfam.h (the SMIME_* values,
* for example SMIME_DES_CBC_56).
* - If "on" is non-zero then the named cipher is enabled, otherwise
* it is disabled. (It is not necessary to call the function for
* ciphers that are disabled, however, as that is the default.)
*
* If the cipher preference is successfully recorded, SECSuccess
* is returned. Otherwise SECFailure is returned. The only errors
* are due to failure allocating memory or bad parameters/calls:
* SEC_ERROR_XXX ("which" is not in the S/MIME cipher family)
* SEC_ERROR_XXX (function is being called more times than there
* are known/expected ciphers)
*/
extern SECStatus NSS_SMIMEUtil_EnableCipher(long which, int on);
/*
* Initialize the local recording of the S/MIME policy.
* This function is called to allow/disallow a particular cipher.
*
* XXX This is for a the current module, I think, so local, static storage
* XXX is okay. Is that correct, or could multiple uses of the same
* XXX library expect to operate under different policies?
*
* - The "which" values are defined in ciferfam.h (the SMIME_* values,
* for example SMIME_DES_CBC_56).
* - If "on" is non-zero then the named cipher is enabled, otherwise
* it is disabled.
*/
extern SECStatus NSS_SMIMEUtils_AllowCipher(long which, int on);
/*
* Does the current policy allow S/MIME decryption of this particular
* algorithm and keysize?
*/
extern PRBool NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key);
/*
* Does the current policy allow *any* S/MIME encryption (or decryption)?
*
* This tells whether or not *any* S/MIME encryption can be done,
* according to policy. Callers may use this to do nicer user interface
* (say, greying out a checkbox so a user does not even try to encrypt
* a message when they are not allowed to) or for any reason they want
* to check whether S/MIME encryption (or decryption, for that matter)
* may be done.
*
* It takes no arguments. The return value is a simple boolean:
* PR_TRUE means encryption (or decryption) is *possible*
* (but may still fail due to other reasons, like because we cannot
* find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
* PR_FALSE means encryption (or decryption) is not permitted
*
* There are no errors from this routine.
*/
extern PRBool NSS_SMIMEUtil_EncryptionPossible(void);
/*
* NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
*
* scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
* S/MIME capabilities attribute value.
*
* "cert" - sender's certificate
*/
extern SECItem *NSS_SMIMEUtil_GetSMIMECapabilities(CERTCertificate *cert);
/*
* NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
*/
extern SECStatus
NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize);
/************************************************************************/
SEC_END_PROTOS
#endif /* _SECMIME_H_ */

View File

@ -0,0 +1,233 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* SMIME message methods
*
* $Id: smimemessage.c,v 1.2 2000-06-13 21:56:34 chrisk%netscape.com Exp $
*/
#include "cmslocal.h"
#include "smime.h"
#include "cert.h"
#include "key.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "prtime.h"
#include "secerr.h"
SECStatus
NSS_SMIMESignerInfo_AddSMIMEProfile(NSSCMSSignerInfo *signerinfo, CERTCertificate *cert)
{
SECItem *smimecapsdata;
NSSCMSAttribute *smimecapsattr;
if ((smimecapsdata = NSS_SMIMEUtil_GetSMIMECapabilities(cert)) == NULL)
return SECFailure;
smimecapsattr = NSS_CMSAttribute_Create(signerinfo->cmsg->poolp,
SEC_OID_PKCS9_SMIME_CAPABILITIES,
smimecapsdata, PR_TRUE);
if (smimecapsattr == NULL)
return SECFailure;
return NSS_CMSSignerInfo_AddAuthAttr(signerinfo, smimecapsattr);
}
#if 0
/*
* NSS_SMIMEMessage_CreateEncrypted - start an S/MIME encrypting context.
*
* "scert" is the cert for the sender. It will be checked for validity.
* "rcerts" are the certs for the recipients. They will also be checked.
*
* "certdb" is the cert database to use for verifying the certs.
* It can be NULL if a default database is available (like in the client).
*
* This function already does all of the stuff specific to S/MIME protocol
* and local policy; the return value just needs to be passed to
* SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
* and finally to SEC_PKCS7DestroyContentInfo().
*
* An error results in a return value of NULL and an error set.
* (Retrieve specific errors via PORT_GetError()/XP_GetError().)
*/
NSSCMSMessage *
NSS_SMIMEMessage_CreateEncrypted(CERTCertificate *scert,
CERTCertificate **rcerts,
CERTCertDBHandle *certdb,
PK11PasswordFunc pwfn,
void *pwfn_arg)
{
NSSCMSMessage *cmsg;
long cipher;
SECOidTag encalg;
int keysize;
int mapi, rci;
cipher = smime_choose_cipher (scert, rcerts);
if (cipher < 0)
return NULL;
mapi = smime_mapi_by_cipher (cipher);
if (mapi < 0)
return NULL;
/*
* XXX This is stretching it -- CreateEnvelopedData should probably
* take a cipher itself of some sort, because we cannot know what the
* future will bring in terms of parameters for each type of algorithm.
* For example, just an algorithm and keysize is *not* sufficient to
* fully specify the usage of RC5 (which also needs to know rounds and
* block size). Work this out into a better API!
*/
encalg = smime_cipher_map[mapi].algtag;
keysize = smime_keysize_by_cipher (cipher);
if (keysize < 0)
return NULL;
cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient,
certdb, encalg, keysize,
pwfn, pwfn_arg);
if (cinfo == NULL)
return NULL;
for (rci = 0; rcerts[rci] != NULL; rci++) {
if (rcerts[rci] == scert)
continue;
if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient,
NULL) != SECSuccess) {
SEC_PKCS7DestroyContentInfo (cinfo);
return NULL;
}
}
return cinfo;
}
#endif
/*
* Start an S/MIME signing context.
*
* "scert" is the cert that will be used to sign the data. It will be
* checked for validity.
*
* "ecert" is the signer's encryption cert. If it is different from
* scert, then it will be included in the signed message so that the
* recipient can save it for future encryptions.
*
* "certdb" is the cert database to use for verifying the cert.
* It can be NULL if a default database is available (like in the client).
*
* "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
* XXX There should be SECMIME functions for hashing, or the hashing should
* be built into this interface, which we would like because we would
* support more smartcards that way, and then this argument should go away.)
*
* "digest" is the actual digest of the data. It must be provided in
* the case of detached data or NULL if the content will be included.
*
* This function already does all of the stuff specific to S/MIME protocol
* and local policy; the return value just needs to be passed to
* SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
* and finally to SEC_PKCS7DestroyContentInfo().
*
* An error results in a return value of NULL and an error set.
* (Retrieve specific errors via PORT_GetError()/XP_GetError().)
*/
NSSCMSMessage *
NSS_SMIMEMessage_CreateSigned(CERTCertificate *scert,
CERTCertificate *ecert,
CERTCertDBHandle *certdb,
SECOidTag digestalgtag,
SECItem *digest,
PK11PasswordFunc pwfn,
void *pwfn_arg)
{
NSSCMSMessage *cmsg;
NSSCMSSignedData *sigd;
NSSCMSSignerInfo *signerinfo;
/* See note in header comment above about digestalg. */
PORT_Assert (digestalgtag == SEC_OID_SHA1);
cmsg = NSS_CMSMessage_Create(NULL);
if (cmsg == NULL)
return NULL;
sigd = NSS_CMSSignedData_Create(cmsg);
if (sigd == NULL)
goto loser;
/* create just one signerinfo */
signerinfo = NSS_CMSSignerInfo_Create(cmsg, scert, digestalgtag);
if (signerinfo == NULL)
goto loser;
/* Add the signing time to the signerinfo. */
if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess)
goto loser;
/* and add the SMIME profile */
if (NSS_SMIMESignerInfo_AddSMIMEProfile(signerinfo, scert) != SECSuccess)
goto loser;
/* now add the signerinfo to the signeddata */
if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess)
goto loser;
/* include the signing cert and its entire chain */
/* note that there are no checks for duplicate certs in place, as all the */
/* essential data structures (like set of certificate) are not there */
if (NSS_CMSSignedData_AddCertChain(sigd, scert) != SECSuccess)
goto loser;
/* If the encryption cert and the signing cert differ, then include
* the encryption cert too. */
if ( ( ecert != NULL ) && ( ecert != scert ) ) {
if (NSS_CMSSignedData_AddCertificate(sigd, ecert) != SECSuccess)
goto loser;
}
return cmsg;
loser:
if (cmsg)
NSS_CMSMessage_Destroy(cmsg);
return NULL;
}

View File

@ -0,0 +1,675 @@
/*
* 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 Netscape are
* Copyright (C) 1994-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License Version 2 or later (the
* "GPL"), in which case the provisions of the GPL are applicable
* instead of those above. If you wish to allow use of your
* version of this file only under the terms of the GPL and not to
* allow others to use your version of this file under the MPL,
* indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by
* the GPL. If you do not delete the provisions above, a recipient
* may use your version of this file under either the MPL or the
* GPL.
*/
/*
* Stuff specific to S/MIME policy and interoperability.
*
* $Id: smimeutil.c,v 1.2 2000-06-13 21:56:34 chrisk%netscape.com Exp $
*/
#include "secmime.h"
#include "secoid.h"
#include "pk11func.h"
#include "ciferfam.h" /* for CIPHER_FAMILY symbols */
#include "secasn1.h"
#include "secitem.h"
#include "cert.h"
#include "key.h"
#include "secerr.h"
/* various integer's ASN.1 encoding */
static unsigned char asn1_int40[] = { SEC_ASN1_INTEGER, 0x01, 0x28 };
static unsigned char asn1_int64[] = { SEC_ASN1_INTEGER, 0x01, 0x40 };
static unsigned char asn1_int128[] = { SEC_ASN1_INTEGER, 0x02, 0x00, 0x80 };
/* RC2 algorithm parameters (used in smime_cipher_map) */
static SECItem param_int40 = { siBuffer, asn1_int40, sizeof(asn1_int40) };
static SECItem param_int64 = { siBuffer, asn1_int64, sizeof(asn1_int64) };
static SECItem param_int128 = { siBuffer, asn1_int128, sizeof(asn1_int128) };
/*
* XXX Would like the "parameters" field to be a SECItem *, but the
* encoder is having trouble with optional pointers to an ANY. Maybe
* once that is fixed, can change this back...
*/
typedef struct {
SECItem capabilityID;
SECItem parameters;
long cipher; /* optimization */
} NSSSMIMECapability;
static const SEC_ASN1Template smime_capability_template[] = {
{ SEC_ASN1_SEQUENCE,
0, NULL, sizeof(NSSSMIMECapability) },
{ SEC_ASN1_OBJECT_ID,
offsetof(NSSSMIMECapability,capabilityID), },
{ SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
offsetof(NSSSMIMECapability,parameters), },
{ 0, }
};
static const SEC_ASN1Template smime_capabilities_template[] = {
{ SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
};
/* smime_cipher_map - map of SMIME symmetric "ciphers" to algtag & parameters */
typedef struct {
unsigned long cipher;
SECOidTag algtag;
SECItem *parms;
PRBool enabled; /* in the user's preferences */
PRBool allowed; /* per export policy */
} smime_cipher_map_entry;
/* global: list of supported SMIME symmetric ciphers, ordered roughly by increasing strength */
static smime_cipher_map_entry smime_cipher_map[] = {
/* cipher algtag parms enabled allowed */
/* ---------------------------------------------------------------------------------- */
{ SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &param_int40, PR_TRUE, PR_TRUE },
{ SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL, PR_TRUE, PR_TRUE },
{ SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &param_int64, PR_TRUE, PR_TRUE },
{ SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &param_int128, PR_TRUE, PR_TRUE },
{ SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL, PR_TRUE, PR_TRUE },
{ SMIME_FORTEZZA, SEC_OID_FORTEZZA_SKIPJACK, NULL, PR_TRUE, PR_TRUE }
};
static const int smime_cipher_map_count = sizeof(smime_cipher_map) / sizeof(smime_cipher_map_entry);
/* the other global variables */
static PRBool smime_prefs_changed = PR_TRUE;
static NSSSMIMECapability **smime_capabilities;
static SECItem *smime_encoded_caps;
static PRBool lastUsedFortezza;
/*
* smime_mapi_by_cipher - find index into smime_cipher_map by cipher
*/
static int
smime_mapi_by_cipher(unsigned long cipher)
{
int i;
for (i = 0; i < smime_cipher_map_count; i++) {
if (smime_cipher_map[i].cipher == cipher)
return i; /* bingo */
}
return -1; /* should not happen if we're consistent, right? */
}
/*
* NSS_SMIME_EnableCipher - this function locally records the user's preference
*/
SECStatus
NSS_SMIMEUtil_EnableCipher(unsigned long which, PRBool on)
{
unsigned long mask;
int mapi;
mask = which & CIPHER_FAMILYID_MASK;
PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
if (mask != CIPHER_FAMILYID_SMIME)
/* XXX set an error! */
return SECFailure;
mapi = smime_mapi_by_cipher(which);
if (mapi < 0)
/* XXX set an error */
return SECFailure;
/* do we try to turn on a forbidden cipher? */
if (!smime_cipher_map[mapi].allowed && on) {
PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
return SECFailure;
}
if (smime_cipher_map[mapi].enabled != on) {
smime_cipher_map[mapi].enabled = on;
smime_prefs_changed = PR_TRUE;
}
return SECSuccess;
}
/*
* this function locally records the export policy
*/
SECStatus
NSS_SMIMEUtil_AllowCipher(unsigned long which, PRBool on)
{
unsigned long mask;
int mapi;
mask = which & CIPHER_FAMILYID_MASK;
PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
if (mask != CIPHER_FAMILYID_SMIME)
/* XXX set an error! */
return SECFailure;
mapi = smime_mapi_by_cipher(which);
if (mapi < 0)
/* XXX set an error */
return SECFailure;
if (smime_cipher_map[mapi].allowed != on) {
smime_cipher_map[mapi].allowed = on;
smime_prefs_changed = PR_TRUE;
}
return SECSuccess;
}
/*
* Based on the given algorithm (including its parameters, in some cases!)
* and the given key (may or may not be inspected, depending on the
* algorithm), find the appropriate policy algorithm specification
* and return it. If no match can be made, -1 is returned.
*/
static SECStatus
nss_smime_get_cipher_for_alg_and_key(SECAlgorithmID *algid, PK11SymKey *key, unsigned long *cipher)
{
SECOidTag algtag;
unsigned int keylen_bits;
SECStatus rv = SECSuccess;
unsigned long c;
algtag = SECOID_GetAlgorithmTag(algid);
switch (algtag) {
case SEC_OID_RC2_CBC:
keylen_bits = PK11_GetKeyStrength(key, algid);
switch (keylen_bits) {
case 40:
c = SMIME_RC2_CBC_40;
case 64:
c = SMIME_RC2_CBC_64;
case 128:
c = SMIME_RC2_CBC_128;
default:
rv = SECFailure;
break;
}
break;
case SEC_OID_DES_CBC:
c = SMIME_DES_CBC_56;
case SEC_OID_DES_EDE3_CBC:
c = SMIME_DES_EDE3_168;
case SEC_OID_FORTEZZA_SKIPJACK:
c = SMIME_FORTEZZA;
default:
rv = SECFailure;
}
if (rv == SECSuccess)
*cipher = c;
return rv;
}
static PRBool
nss_smime_cipher_allowed(unsigned long which)
{
int mapi;
mapi = smime_mapi_by_cipher(which);
if (mapi < 0)
return PR_FALSE;
return smime_cipher_map[mapi].allowed;
}
PRBool
NSS_SMIMEUtil_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
{
unsigned long which;
if (nss_smime_get_cipher_for_alg_and_key(algid, key, &which) != SECSuccess)
return PR_FALSE;
return nss_smime_cipher_allowed(which);
}
/*
* NSS_SMIME_EncryptionPossible - check if any encryption is allowed
*
* This tells whether or not *any* S/MIME encryption can be done,
* according to policy. Callers may use this to do nicer user interface
* (say, greying out a checkbox so a user does not even try to encrypt
* a message when they are not allowed to) or for any reason they want
* to check whether S/MIME encryption (or decryption, for that matter)
* may be done.
*
* It takes no arguments. The return value is a simple boolean:
* PR_TRUE means encryption (or decryption) is *possible*
* (but may still fail due to other reasons, like because we cannot
* find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
* PR_FALSE means encryption (or decryption) is not permitted
*
* There are no errors from this routine.
*/
PRBool
NSS_SMIMEUtil_EncryptionPossible(void)
{
int i;
for (i = 0; i < smime_cipher_map_count; i++) {
if (smime_cipher_map[i].allowed)
return PR_TRUE;
}
return PR_FALSE;
}
static int
nss_SMIME_FindCipherForSMIMECap(NSSSMIMECapability *cap)
{
int i;
SECOidTag capIDTag;
/* we need the OIDTag here */
capIDTag = SECOID_FindOIDTag(&(cap->capabilityID));
/* go over all the SMIME ciphers we know and see if we find a match */
for (i = 0; i < smime_cipher_map_count; i++) {
if (smime_cipher_map[i].algtag != capIDTag)
continue;
/*
* XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
* 2 NULLs as equal and NULL and non-NULL as not equal), we could
* use that here instead of all of the following comparison code.
*/
if (cap->parameters.data == NULL && smime_cipher_map[i].parms == NULL)
break; /* both empty: bingo */
if (cap->parameters.data != NULL && smime_cipher_map[i].parms != NULL &&
cap->parameters.len == smime_cipher_map[i].parms->len &&
PORT_Memcmp (cap->parameters.data, smime_cipher_map[i].parms->data,
cap->parameters.len) == 0)
{
break; /* both not empty, same length & equal content: bingo */
}
}
if (i == smime_cipher_map_count)
return 0; /* no match found */
else
return smime_cipher_map[i].cipher; /* match found, point to cipher */
}
/*
* smime_choose_cipher - choose a cipher that works for all the recipients
*
* "scert" - sender's certificate
* "rcerts" - recipient's certificates
*/
static long
smime_choose_cipher(CERTCertificate *scert, CERTCertificate **rcerts)
{
PRArenaPool *poolp;
long cipher;
long chosen_cipher;
int *cipher_abilities;
int *cipher_votes;
int weak_mapi;
int strong_mapi;
int rcount, mapi, max, i;
PRBool scert_is_fortezza = (scert == NULL) ? PR_FALSE : PK11_FortezzaHasKEA(scert);
chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
weak_mapi = smime_mapi_by_cipher(chosen_cipher);
poolp = PORT_NewArena (1024); /* XXX what is right value? */
if (poolp == NULL)
goto done;
cipher_abilities = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
cipher_votes = (int *)PORT_ArenaZAlloc(poolp, smime_cipher_map_count * sizeof(int));
if (cipher_votes == NULL || cipher_abilities == NULL)
goto done;
/* If the user has the Fortezza preference turned on, make
* that the strong cipher. Otherwise, use triple-DES. */
strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
if (scert_is_fortezza) {
mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
if (mapi >= 0 && smime_cipher_map[mapi].enabled)
strong_mapi = mapi;
}
/* walk all the recipient's certs */
for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
SECItem *profile;
NSSSMIMECapability **caps;
int pref;
/* the first cipher that matches in the user's SMIME profile gets
* "smime_cipher_map_count" votes; the next one gets "smime_cipher_map_count" - 1
* and so on. If every cipher matches, the last one gets 1 (one) vote */
pref = smime_cipher_map_count;
/* find recipient's SMIME profile */
profile = CERT_FindSMimeProfile(rcerts[rcount]);
if (profile != NULL && profile->data != NULL && profile->len > 0) {
/* we have a profile */
caps = NULL;
/* decode it */
if (SEC_ASN1DecodeItem(poolp, &caps, smime_capabilities_template, profile) == SECSuccess &&
caps != NULL)
{
/* walk the SMIME capabilities for this recipient */
for (i = 0; caps[i] != NULL; i++) {
cipher = nss_SMIME_FindCipherForSMIMECap(caps[i]);
mapi = smime_mapi_by_cipher(cipher);
if (mapi >= 0) {
/* found the cipher */
cipher_abilities[mapi]++;
cipher_votes[mapi] += pref;
--pref;
}
}
}
} else {
/* no profile found - so we can only assume that the user can do
* the mandatory algorithms which is RC2-40 (weak crypto) and 3DES (strong crypto) */
SECKEYPublicKey *key;
unsigned int pklen_bits;
/*
* if recipient's public key length is > 512, vote for a strong cipher
* please not that the side effect of this is that if only one recipient
* has an export-level public key, the strong cipher is disabled.
*
* XXX This is probably only good for RSA keys. What I would
* really like is a function to just say; Is the public key in
* this cert an export-length key? Then I would not have to
* know things like the value 512, or the kind of key, or what
* a subjectPublicKeyInfo is, etc.
*/
key = CERT_ExtractPublicKey(rcerts[rcount]);
pklen_bits = 0;
if (key != NULL) {
pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
SECKEY_DestroyPublicKey (key);
}
if (pklen_bits > 512) {
/* cast votes for the strong algorithm */
cipher_abilities[strong_mapi]++;
cipher_votes[strong_mapi] += pref;
pref--;
}
/* always cast (possibly less) votes for the weak algorithm */
cipher_abilities[weak_mapi]++;
cipher_votes[weak_mapi] += pref;
}
if (profile != NULL)
SECITEM_FreeItem(profile, PR_TRUE);
}
/* find cipher that is agreeable by all recipients and that has the most votes */
max = 0;
for (mapi = 0; mapi < smime_cipher_map_count; mapi++) {
/* if not all of the recipients can do this, forget it */
if (cipher_abilities[mapi] != rcount)
continue;
/* if cipher is not enabled or not allowed by policy, forget it */
if (!smime_cipher_map[mapi].enabled || !smime_cipher_map[mapi].allowed)
continue;
/* if we're not doing fortezza, but the cipher is fortezza, forget it */
if (!scert_is_fortezza && (smime_cipher_map[mapi].cipher == SMIME_FORTEZZA))
continue;
/* now see if this one has more votes than the last best one */
if (cipher_votes[mapi] >= max) {
/* if equal number of votes, prefer the ones further down in the list */
/* with the expectation that these are higher rated ciphers */
chosen_cipher = smime_cipher_map[mapi].cipher;
max = cipher_votes[mapi];
}
}
/* if no common cipher was found, chosen_cipher stays at the default */
done:
if (poolp != NULL)
PORT_FreeArena (poolp, PR_FALSE);
return chosen_cipher;
}
/*
* XXX This is a hack for now to satisfy our current interface.
* Eventually, with more parameters needing to be specified, just
* looking up the keysize is not going to be sufficient.
*/
static int
smime_keysize_by_cipher (unsigned long which)
{
int keysize;
switch (which) {
case SMIME_RC2_CBC_40:
keysize = 40;
break;
case SMIME_RC2_CBC_64:
keysize = 64;
break;
case SMIME_RC2_CBC_128:
keysize = 128;
break;
case SMIME_DES_CBC_56:
case SMIME_DES_EDE3_168:
case SMIME_FORTEZZA:
/*
* These are special; since the key size is fixed, we actually
* want to *avoid* specifying a key size.
*/
keysize = 0;
break;
default:
keysize = -1;
break;
}
return keysize;
}
/*
* NSS_SMIMEUtil_FindBulkAlgForRecipients - find bulk algorithm suitable for all recipients
*
* it would be great for UI purposes if there would be a way to find out which recipients
* prevented a strong cipher from being used...
*/
SECStatus
NSS_SMIMEUtil_FindBulkAlgForRecipients(CERTCertificate **rcerts, SECOidTag *bulkalgtag, int *keysize)
{
unsigned long cipher;
int mapi;
cipher = smime_choose_cipher(NULL, rcerts);
mapi = smime_mapi_by_cipher(cipher);
*bulkalgtag = smime_cipher_map[mapi].algtag;
*keysize = smime_keysize_by_cipher(smime_cipher_map[mapi].algtag);
return SECSuccess;
}
static SECStatus
smime_init_caps(PRBool isFortezza)
{
NSSSMIMECapability *cap;
smime_cipher_map_entry *map;
SECOidData *oiddata;
SECStatus rv;
int i, capIndex;
/* if we have caps, and the prefs did not change, and we are using fortezza as last time */
/* we're done */
if (smime_encoded_caps != NULL && (!smime_prefs_changed) && lastUsedFortezza == isFortezza)
return SECSuccess;
/* ok, we need to cook up new caps. So throw the old ones away */
if (smime_encoded_caps != NULL) {
SECITEM_FreeItem (smime_encoded_caps, PR_TRUE);
smime_encoded_caps = NULL;
}
/* if we have an old NSSSMIMECapability array, we'll reuse it (has the right size) */
if (smime_capabilities == NULL) {
smime_capabilities = (NSSSMIMECapability **)PORT_ZAlloc((smime_cipher_map_count + 1)
* sizeof(NSSSMIMECapability *));
if (smime_capabilities == NULL)
return SECFailure;
}
rv = SECFailure;
/*
The process of creating the encoded CMS cipher capability list
involves two basic steps:
(a) Convert our internal representation of cipher preferences
(smime_prefs) into an array containing cipher OIDs and
parameter data (smime_capabilities). This step is
performed here.
(b) Encode, using ASN.1, the cipher information in
smime_capabilities, leaving the encoded result in
smime_encoded_caps.
(In the process of performing (a), Lisa put in some optimizations
which allow us to avoid needlessly re-populating elements in
smime_capabilities as we walk through smime_prefs.)
We want to use separate loop variables for smime_prefs and
smime_capabilities because in the case where the Skipjack cipher
is turned on in the prefs, but where we don't want to include
Skipjack in the encoded capabilities (presumably due to using a
non-fortezza cert when sending a message), we want to avoid creating
an empty element in smime_capabilities. This would otherwise cause
the encoding step to produce an empty set, since Skipjack happens
to be the first cipher in smime_prefs, if it is turned on.
*/
capIndex = 0;
for (i = 0; i < smime_cipher_map_count; i++) {
/* Find the corresponding entry in the cipher map. */
map = &(smime_cipher_map[i]);
if (!map->enabled)
continue;
/* If we're using a non-Fortezza cert, only advertise non-Fortezza
capabilities. (We advertise all capabilities if we have a
Fortezza cert.) */
if ((!isFortezza) && (map->cipher == SMIME_FORTEZZA))
continue;
/* get next SMIME capability */
cap = smime_capabilities[capIndex];
if (cap == NULL) {
cap = (NSSSMIMECapability *)PORT_ZAlloc(sizeof(NSSSMIMECapability));
if (cap == NULL)
break;
smime_capabilities[capIndex] = cap;
}
capIndex++;
if (cap->cipher == smime_cipher_map[i].cipher)
continue; /* no change to this one */
oiddata = SECOID_FindOIDByTag(map->algtag);
if (oiddata == NULL)
break;
if (cap->capabilityID.data != NULL) {
SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
cap->capabilityID.data = NULL;
cap->capabilityID.len = 0;
}
rv = SECITEM_CopyItem(NULL, &(cap->capabilityID), &(oiddata->oid));
if (rv != SECSuccess)
break;
if (map->parms == NULL) {
cap->parameters.data = NULL;
cap->parameters.len = 0;
} else {
cap->parameters.data = map->parms->data;
cap->parameters.len = map->parms->len;
}
cap->cipher = smime_cipher_map[i].cipher;
}
while (capIndex < smime_cipher_map_count) {
cap = smime_capabilities[capIndex];
if (cap != NULL) {
SECITEM_FreeItem(&(cap->capabilityID), PR_FALSE);
PORT_Free(cap);
}
smime_capabilities[capIndex] = NULL;
capIndex++;
}
smime_capabilities[capIndex] = NULL; /* last one */
smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities,
smime_capabilities_template);
if (smime_encoded_caps == NULL)
return SECFailure;
lastUsedFortezza = isFortezza;
return SECSuccess;
}
/*
* NSS_SMIMEUtil_GetSMIMECapabilities - get S/MIME capabilities for this instance of NSS
*
* scans the list of allowed and enabled ciphers and construct a PKCS9-compliant
* S/MIME capabilities attribute value.
*
* "cert" - sender's certificate
*/
SECItem *
NSS_SMIMEUtil_GetSMIMECapabilities(CERTCertificate *cert)
{
PRBool isFortezza = PR_FALSE;
/* See if the sender's cert specifies Fortezza key exchange. */
if (cert != NULL)
isFortezza = PK11_FortezzaHasKEA(cert);
if (smime_init_caps(isFortezza) != SECSuccess)
return NULL;
return smime_encoded_caps;
}

View File

@ -35,7 +35,7 @@
* Support for DEcoding ASN.1 data based on BER/DER (Basic/Distinguished
* Encoding Rules).
*
* $Id: secasn1d.c,v 1.2 2000-05-22 15:24:20 chrisk%netscape.com Exp $
* $Id: secasn1d.c,v 1.3 2000-06-13 21:56:36 chrisk%netscape.com Exp $
*/
#include "secasn1.h"
@ -2121,6 +2121,9 @@ sec_asn1d_during_choice
PORT_Assert((sec_asn1d_state *)NULL != child);
if( child->missing ) {
unsigned char child_found_tag_modifiers = 0;
unsigned long child_found_tag_number = 0;
child->theTemplate++;
if( 0 == child->theTemplate->kind ) {
@ -2149,14 +2152,24 @@ sec_asn1d_during_choice
child->consumed = 0;
sec_asn1d_scrub_state(child);
/* move it on top again */
state->top->current = child;
child_found_tag_modifiers = child->found_tag_modifiers;
child_found_tag_number = child->found_tag_number;
child = sec_asn1d_init_state_based_on_template(child);
if( (sec_asn1d_state *)NULL == child ) {
return (sec_asn1d_state *)NULL;
}
/* copy our findings to the new top */
child->found_tag_modifiers = child_found_tag_modifiers;
child->found_tag_number = child_found_tag_number;
child->optional = PR_TRUE;
child->place = afterIdentifier;
state->top->current = child;
return child;
} else {

View File

@ -35,7 +35,7 @@
* Support for ENcoding ASN.1 data based on BER/DER (Basic/Distinguished
* Encoding Rules).
*
* $Id: secasn1e.c,v 1.1 2000-03-31 19:38:58 relyea%netscape.com Exp $
* $Id: secasn1e.c,v 1.2 2000-06-13 21:56:37 chrisk%netscape.com Exp $
*/
#include "secasn1.h"
@ -476,7 +476,7 @@ sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,
PRBool *noheaderp)
{
unsigned long encode_kind, underlying_kind;
PRBool explicit, optional, universal;
PRBool explicit, optional, universal, may_stream;
unsigned long len;
encode_kind = theTemplate->kind;
@ -492,7 +492,7 @@ sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,
PORT_Assert (!(explicit && universal)); /* bad templates */
/* We have already decided we are not streaming. */
may_stream = (encode_kind & SEC_ASN1_MAY_STREAM) ? PR_TRUE : PR_FALSE;
encode_kind &= ~SEC_ASN1_MAY_STREAM;
/* Just clear this to get it out of the way; we do not need it here */
@ -548,14 +548,12 @@ sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,
if (len == 0 && optional) {
*noheaderp = PR_TRUE;
} else if (*noheaderp) {
/*
* Okay, *we* do not want to add in a header, but our
* caller still does.
*/
/* Okay, *we* do not want to add in a header, but our caller still does. */
*noheaderp = PR_FALSE;
} else {
/*
* XXX The 1 below is the presumed length of the identifier;
/* if the inner content exists, our length is
* len(identifier) + len(length) + len(innercontent)
* XXX we currently assume len(identifier) == 1;
* to support a high-tag-number this would need to be smarter.
*/
len += 1 + SEC_ASN1LengthLength (len);
@ -661,6 +659,8 @@ sec_asn1e_contents_length (const SEC_ASN1Template *theTemplate, void *src,
default:
len = ((SECItem *)src)->len;
if (may_stream && len == 0)
len = 1; /* if we're streaming, we may have a secitem w/len 0 as placeholder */
break;
}
@ -678,6 +678,7 @@ sec_asn1e_write_header (sec_asn1e_state *state)
{
unsigned long contents_length;
unsigned char tag_number, tag_modifiers;
PRBool noheader;
PORT_Assert (state->place == beforeHeader);
@ -713,6 +714,29 @@ sec_asn1e_write_header (sec_asn1e_state *state)
return;
}
/*
* We are doing a definite-length encoding. First we have to
* walk the data structure to calculate the entire contents length.
*/
contents_length = sec_asn1e_contents_length (state->theTemplate,
state->src, &noheader);
/*
* We might be told explicitly not to put out a header.
* But it can also be the case, via a pushed subtemplate, that
* sec_asn1e_contents_length could not know that this field is
* really optional. So check for that explicitly, too.
*/
if (noheader || (contents_length == 0 && state->optional)) {
state->place = afterContents;
if (state->top->streaming && state->may_stream && state->top->from_buf)
/* we did not find an optional indefinite string, so we don't encode it.
* However, if TakeFromBuf is on, we stop here anyway to give our caller
* a chance to intercept at the same point where we would stop if the
* field were present. */
state->top->status = needBytes;
return;
}
if (state->top->streaming && state->may_stream
&& (state->top->from_buf || !state->is_string)) {
/*
@ -730,26 +754,7 @@ sec_asn1e_write_header (sec_asn1e_state *state)
|| state->is_string);
tag_modifiers |= SEC_ASN1_CONSTRUCTED;
contents_length = 0;
} else {
PRBool noheader;
/*
* We are doing a definite-length encoding. First we have to
* walk the data structure to calculate the entire contents length.
*/
contents_length = sec_asn1e_contents_length (state->theTemplate,
state->src, &noheader);
/*
* We might be told explicitly not to put out a header.
* But it can also be the case, via a pushed subtemplate, that
* sec_asn1e_contents_length could not know that this field is
* really optional. So check for that explicitly, too.
*/
if (noheader || (contents_length == 0 && state->optional)) {
state->place = afterContents;
return;
}
}
}
sec_asn1e_write_identifier_bytes (state, tag_number | tag_modifiers);
sec_asn1e_write_length_bytes (state, contents_length, state->indefinite);

View File

@ -176,7 +176,10 @@ SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER = (SEC_ERROR_BASE + 128),
SEC_ERROR_OCSP_MALFORMED_RESPONSE = (SEC_ERROR_BASE + 129),
SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE = (SEC_ERROR_BASE + 130),
SEC_ERROR_OCSP_FUTURE_RESPONSE = (SEC_ERROR_BASE + 131),
SEC_ERROR_OCSP_OLD_RESPONSE = (SEC_ERROR_BASE + 132)
SEC_ERROR_OCSP_OLD_RESPONSE = (SEC_ERROR_BASE + 132),
/* smime stuff */
SEC_ERROR_DIGEST_NOT_FOUND = (SEC_ERROR_BASE + 133),
SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE = (SEC_ERROR_BASE + 134)
} SECErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */