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:
parent
aee8ba11d6
commit
c97a690adc
@ -60,6 +60,7 @@ DIRS = lib \
|
||||
strsclnt \
|
||||
swfort \
|
||||
tstclnt \
|
||||
smimetools \
|
||||
$(NULL)
|
||||
|
||||
TEMPORARILY_DONT_BUILD = \
|
||||
|
||||
@ -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 \
|
||||
|
||||
73
mozilla/security/nss/cmd/smimetools/Makefile
Normal file
73
mozilla/security/nss/cmd/smimetools/Makefile
Normal 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
|
||||
841
mozilla/security/nss/cmd/smimetools/cmsutil.c
Normal file
841
mozilla/security/nss/cmd/smimetools/cmsutil.c
Normal 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);
|
||||
}
|
||||
45
mozilla/security/nss/cmd/smimetools/manifest.mn
Normal file
45
mozilla/security/nss/cmd/smimetools/manifest.mn
Normal 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
|
||||
37
mozilla/security/nss/cmd/smimetools/rules.mk
Normal file
37
mozilla/security/nss/cmd/smimetools/rules.mk
Normal 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)
|
||||
325
mozilla/security/nss/cmd/smimetools/smime
Executable file
325
mozilla/security/nss/cmd/smimetools/smime
Executable 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;
|
||||
@ -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? */
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
#
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 );
|
||||
|
||||
76
mozilla/security/nss/lib/smime/Makefile
Normal file
76
mozilla/security/nss/lib/smime/Makefile
Normal 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). #
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
1054
mozilla/security/nss/lib/smime/cms.h
Normal file
1054
mozilla/security/nss/lib/smime/cms.h
Normal file
File diff suppressed because it is too large
Load Diff
205
mozilla/security/nss/lib/smime/cmsarray.c
Normal file
205
mozilla/security/nss/lib/smime/cmsarray.c
Normal 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
|
||||
560
mozilla/security/nss/lib/smime/cmsasn1.c
Normal file
560
mozilla/security/nss/lib/smime/cmsasn1.c
Normal 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;
|
||||
}
|
||||
450
mozilla/security/nss/lib/smime/cmsattr.c
Normal file
450
mozilla/security/nss/lib/smime/cmsattr.c
Normal 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;
|
||||
}
|
||||
|
||||
327
mozilla/security/nss/lib/smime/cmscinfo.c
Normal file
327
mozilla/security/nss/lib/smime/cmscinfo.c
Normal 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;
|
||||
}
|
||||
792
mozilla/security/nss/lib/smime/cmscipher.c
Normal file
792
mozilla/security/nss/lib/smime/cmscipher.c
Normal 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;
|
||||
}
|
||||
688
mozilla/security/nss/lib/smime/cmsdecode.c
Normal file
688
mozilla/security/nss/lib/smime/cmsdecode.c
Normal 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);
|
||||
}
|
||||
|
||||
223
mozilla/security/nss/lib/smime/cmsdigdata.c
Normal file
223
mozilla/security/nss/lib/smime/cmsdigdata.c
Normal 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;
|
||||
}
|
||||
259
mozilla/security/nss/lib/smime/cmsdigest.c
Normal file
259
mozilla/security/nss/lib/smime/cmsdigest.c
Normal 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;
|
||||
}
|
||||
279
mozilla/security/nss/lib/smime/cmsencdata.c
Normal file
279
mozilla/security/nss/lib/smime/cmsencdata.c
Normal 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;
|
||||
}
|
||||
740
mozilla/security/nss/lib/smime/cmsencode.c
Normal file
740
mozilla/security/nss/lib/smime/cmsencode.c
Normal 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;
|
||||
}
|
||||
416
mozilla/security/nss/lib/smime/cmsenvdata.c
Normal file
416
mozilla/security/nss/lib/smime/cmsenvdata.c
Normal 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;
|
||||
}
|
||||
|
||||
330
mozilla/security/nss/lib/smime/cmslocal.h
Normal file
330
mozilla/security/nss/lib/smime/cmslocal.h
Normal 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_ */
|
||||
313
mozilla/security/nss/lib/smime/cmsmessage.c
Normal file
313
mozilla/security/nss/lib/smime/cmsmessage.c
Normal 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;
|
||||
}
|
||||
531
mozilla/security/nss/lib/smime/cmspubkey.c
Normal file
531
mozilla/security/nss/lib/smime/cmspubkey.c
Normal 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;
|
||||
}
|
||||
|
||||
421
mozilla/security/nss/lib/smime/cmsrecinfo.c
Normal file
421
mozilla/security/nss/lib/smime/cmsrecinfo.c
Normal 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,
|
||||
¶ms, 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;
|
||||
}
|
||||
187
mozilla/security/nss/lib/smime/cmsreclist.c
Normal file
187
mozilla/security/nss/lib/smime/cmsreclist.c
Normal 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));
|
||||
}
|
||||
59
mozilla/security/nss/lib/smime/cmsreclist.h
Normal file
59
mozilla/security/nss/lib/smime/cmsreclist.h
Normal 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 */
|
||||
850
mozilla/security/nss/lib/smime/cmssigdata.c
Normal file
850
mozilla/security/nss/lib/smime/cmssigdata.c
Normal 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
|
||||
*/
|
||||
|
||||
752
mozilla/security/nss/lib/smime/cmssiginfo.c
Normal file
752
mozilla/security/nss/lib/smime/cmssiginfo.c
Normal 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;
|
||||
}
|
||||
489
mozilla/security/nss/lib/smime/cmst.h
Normal file
489
mozilla/security/nss/lib/smime/cmst.h
Normal 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_ */
|
||||
362
mozilla/security/nss/lib/smime/cmsutil.c
Normal file
362
mozilla/security/nss/lib/smime/cmsutil.c
Normal 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";
|
||||
}
|
||||
}
|
||||
44
mozilla/security/nss/lib/smime/config.mk
Normal file
44
mozilla/security/nss/lib/smime/config.mk
Normal 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 =
|
||||
|
||||
74
mozilla/security/nss/lib/smime/manifest.mn
Normal file
74
mozilla/security/nss/lib/smime/manifest.mn
Normal 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
|
||||
139
mozilla/security/nss/lib/smime/smime.h
Normal file
139
mozilla/security/nss/lib/smime/smime.h
Normal 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_ */
|
||||
233
mozilla/security/nss/lib/smime/smimemessage.c
Normal file
233
mozilla/security/nss/lib/smime/smimemessage.c
Normal 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;
|
||||
}
|
||||
675
mozilla/security/nss/lib/smime/smimeutil.c
Normal file
675
mozilla/security/nss/lib/smime/smimeutil.c
Normal 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, ¶m_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, ¶m_int64, PR_TRUE, PR_TRUE },
|
||||
{ SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, ¶m_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;
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user