summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pkcs12
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/pkcs12
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--security/nss/lib/pkcs12/Makefile47
-rw-r--r--security/nss/lib/pkcs12/exports.gyp29
-rw-r--r--security/nss/lib/pkcs12/manifest.mn32
-rw-r--r--security/nss/lib/pkcs12/p12.h236
-rw-r--r--security/nss/lib/pkcs12/p12creat.c218
-rw-r--r--security/nss/lib/pkcs12/p12d.c3626
-rw-r--r--security/nss/lib/pkcs12/p12dec.c670
-rw-r--r--security/nss/lib/pkcs12/p12e.c2087
-rw-r--r--security/nss/lib/pkcs12/p12exp.c1374
-rw-r--r--security/nss/lib/pkcs12/p12local.c1417
-rw-r--r--security/nss/lib/pkcs12/p12local.h70
-rw-r--r--security/nss/lib/pkcs12/p12plcy.c106
-rw-r--r--security/nss/lib/pkcs12/p12plcy.h25
-rw-r--r--security/nss/lib/pkcs12/p12t.h156
-rw-r--r--security/nss/lib/pkcs12/p12tmpl.c290
-rw-r--r--security/nss/lib/pkcs12/pkcs12.gyp29
-rw-r--r--security/nss/lib/pkcs12/pkcs12.h41
-rw-r--r--security/nss/lib/pkcs12/pkcs12t.h341
18 files changed, 10794 insertions, 0 deletions
diff --git a/security/nss/lib/pkcs12/Makefile b/security/nss/lib/pkcs12/Makefile
new file mode 100644
index 0000000000..3fd2f50b12
--- /dev/null
+++ b/security/nss/lib/pkcs12/Makefile
@@ -0,0 +1,47 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+#######################################################################
+# (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). #
+#######################################################################
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
+
diff --git a/security/nss/lib/pkcs12/exports.gyp b/security/nss/lib/pkcs12/exports.gyp
new file mode 100644
index 0000000000..274ef68d67
--- /dev/null
+++ b/security/nss/lib/pkcs12/exports.gyp
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'lib_pkcs12_exports',
+ 'type': 'none',
+ 'copies': [
+ {
+ 'files': [
+ 'p12.h',
+ 'p12plcy.h',
+ 'p12t.h',
+ 'pkcs12.h',
+ 'pkcs12t.h'
+ ],
+ 'destination': '<(nss_public_dist_dir)/<(module)'
+ }
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+}
diff --git a/security/nss/lib/pkcs12/manifest.mn b/security/nss/lib/pkcs12/manifest.mn
new file mode 100644
index 0000000000..ab54bec43a
--- /dev/null
+++ b/security/nss/lib/pkcs12/manifest.mn
@@ -0,0 +1,32 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+CORE_DEPTH = ../..
+
+EXPORTS = \
+ pkcs12t.h \
+ pkcs12.h \
+ p12plcy.h \
+ p12.h \
+ p12t.h \
+ $(NULL)
+
+MODULE = nss
+
+CSRCS = \
+ p12local.c \
+ p12creat.c \
+ p12dec.c \
+ p12plcy.c \
+ p12tmpl.c \
+ p12e.c \
+ p12d.c \
+ $(NULL)
+
+LIBRARY_NAME = pkcs12
+SHARED_LIBRARY = $(NULL)
+
+# This part of the code, including all sub-dirs, can be optimized for size
+export ALLOW_OPT_CODE_SIZE = 1
diff --git a/security/nss/lib/pkcs12/p12.h b/security/nss/lib/pkcs12/p12.h
new file mode 100644
index 0000000000..495bbf6c49
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12.h
@@ -0,0 +1,236 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _P12_H_
+#define _P12_H_
+
+#include "secoid.h"
+#include "keyhi.h"
+#include "secpkcs7.h"
+#include "p12t.h"
+
+typedef int(PR_CALLBACK *PKCS12OpenFunction)(void *arg);
+typedef int(PR_CALLBACK *PKCS12ReadFunction)(void *arg,
+ unsigned char *buffer,
+ unsigned int *lenRead,
+ unsigned int maxLen);
+typedef int(PR_CALLBACK *PKCS12WriteFunction)(void *arg,
+ unsigned char *buffer,
+ unsigned int *bufLen,
+ unsigned int *lenWritten);
+typedef int(PR_CALLBACK *PKCS12CloseFunction)(void *arg);
+typedef SECStatus(PR_CALLBACK *PKCS12UnicodeConvertFunction)(
+ PLArenaPool *arena,
+ SECItem *dest, SECItem *src,
+ PRBool toUnicode,
+ PRBool swapBytes);
+typedef void(PR_CALLBACK *SEC_PKCS12EncoderOutputCallback)(
+ void *arg, const char *buf,
+ unsigned long len);
+typedef void(PR_CALLBACK *SEC_PKCS12DecoderOutputCallback)(
+ void *arg, const char *buf,
+ unsigned long len);
+/*
+ * In NSS 3.12 or later, 'arg' actually points to a CERTCertificate,
+ * the 'leafCert' variable in sec_pkcs12_validate_cert in p12d.c.
+ * See r1.35 of p12d.c ("Patch 2" in bug 321584).
+ *
+ * This callback might be called by SEC_PKCS12DecoderValidateBags each time
+ * a nickname collission is detected. The callback must return a new
+ * nickname. The returned SECItem should be of type siAsciiString,
+ * it should be allocated using:
+ * SECITEM_AllocItem(NULL, NULL, LENGTH_OF_NEW_NICKNAME + 1)
+ * and data must contain the new nickname as a zero terminated string.
+ */
+typedef SECItem *(PR_CALLBACK *SEC_PKCS12NicknameCollisionCallback)(
+ SECItem *old_nickname,
+ PRBool *cancel,
+ void *arg);
+/*
+ * This callback is called by SEC_PKCS12DecoderRenameCertNicknames for each
+ * certificate found in the p12 source data.
+ *
+ * cert: A decoded certificate.
+ * default_nickname: The nickname as found in the source data.
+ * Will be NULL if source data doesn't have nickname.
+ * new_nickname: Output parameter that may contain the renamed nickname.
+ * arg: The user data that was passed to SEC_PKCS12DecoderRenameCertNicknames.
+ *
+ * If the callback accept that NSS will use a nickname based on the
+ * default_nickname (potentially resolving conflicts), then the callback
+ * must set *new_nickname to NULL.
+ *
+ * If the callback wishes to override the nickname, it must set *new_nickname
+ * to a new SECItem which should be allocated using
+ * SECITEM_AllocItem(NULL, NULL, LENGTH_OF_NEW_NICKNAME + 1)
+ * new_nickname->type should be set to siAsciiString, and new_nickname->data
+ * must contain the new nickname as a zero terminated string.
+ *
+ * A return value of SECFailure indicates that the renaming operation failed,
+ * and callback should release new_nickname before returning if it's already
+ * being allocated.
+ * Otherwise, the callback function must return SECSuccess, including use
+ * default nickname as mentioned above.
+ */
+typedef SECStatus(PR_CALLBACK *SEC_PKCS12NicknameRenameCallback)(
+ const CERTCertificate *cert,
+ const SECItem *default_nickname,
+ SECItem **new_nickname,
+ void *arg);
+
+typedef SECStatus(PR_CALLBACK *digestOpenFn)(void *arg, PRBool readData);
+typedef SECStatus(PR_CALLBACK *digestCloseFn)(void *arg, PRBool removeFile);
+typedef int(PR_CALLBACK *digestIOFn)(void *arg, unsigned char *buf,
+ unsigned long len);
+
+typedef struct SEC_PKCS12ExportContextStr SEC_PKCS12ExportContext;
+typedef struct SEC_PKCS12SafeInfoStr SEC_PKCS12SafeInfo;
+typedef struct SEC_PKCS12DecoderContextStr SEC_PKCS12DecoderContext;
+typedef struct SEC_PKCS12DecoderItemStr SEC_PKCS12DecoderItem;
+
+struct sec_PKCS12PasswordModeInfo {
+ SECItem *password;
+ SECOidTag algorithm;
+};
+
+struct sec_PKCS12PublicKeyModeInfo {
+ CERTCertificate *cert;
+ CERTCertDBHandle *certDb;
+ SECOidTag algorithm;
+ int keySize;
+};
+
+struct SEC_PKCS12DecoderItemStr {
+ SECItem *der;
+ SECOidTag type;
+ PRBool hasKey;
+ SECItem *friendlyName; /* UTF-8 string */
+ SECAlgorithmID *shroudAlg;
+};
+
+SEC_BEGIN_PROTOS
+
+SEC_PKCS12SafeInfo *
+SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt,
+ CERTCertDBHandle *certDb,
+ CERTCertificate *signer,
+ CERTCertificate **recipients,
+ SECOidTag algorithm, int keysize);
+
+extern SEC_PKCS12SafeInfo *
+SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt,
+ SECItem *pwitem, SECOidTag privAlg);
+
+extern SEC_PKCS12SafeInfo *
+SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt);
+
+extern SECStatus
+SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt,
+ SECItem *pwitem, SECOidTag integAlg);
+extern SECStatus
+SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ SECOidTag algorithm, int keySize);
+
+extern SEC_PKCS12ExportContext *
+SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg,
+ PK11SlotInfo *slot, void *wincx);
+
+extern SECStatus
+SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt,
+ SEC_PKCS12SafeInfo *safe, void *nestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ SECItem *keyId, PRBool includeCertChain);
+
+extern SECStatus
+SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt,
+ SEC_PKCS12SafeInfo *safe,
+ void *nestedDest, CERTCertificate *cert,
+ PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem,
+ SECItem *keyId, SECItem *nickName);
+
+extern SECStatus
+SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ void *keySafe, void *keyNestedDest, PRBool shroudKey,
+ SECItem *pwitem, SECOidTag algorithm,
+ PRBool includeCertChain);
+
+extern SECStatus
+SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ void *keySafe, void *keyNestedDest,
+ PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm);
+
+extern void *
+SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt,
+ void *baseSafe, void *nestedDest);
+
+extern SECStatus
+SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp,
+ SEC_PKCS12EncoderOutputCallback output, void *outputarg);
+
+extern void
+SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12exp);
+
+extern SEC_PKCS12DecoderContext *
+SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx,
+ digestOpenFn dOpen, digestCloseFn dClose,
+ digestIOFn dRead, digestIOFn dWrite, void *dArg);
+
+extern SECStatus
+SEC_PKCS12DecoderSetTargetTokenCAs(SEC_PKCS12DecoderContext *p12dcx,
+ SECPKCS12TargetTokenCAs tokenCAs);
+
+extern SECStatus
+SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx, unsigned char *data,
+ unsigned long len);
+
+extern void
+SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx);
+
+extern SECStatus
+SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx);
+
+extern SECStatus
+SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb);
+
+/*
+ * SEC_PKCS12DecoderRenameCertNicknames() can be used to change
+ * certificate nicknames in SEC_PKCS12DecoderContext, prior to calling
+ * SEC_PKCS12DecoderImportBags.
+ *
+ * arg: User-defined data that will be passed to nicknameCb.
+ *
+ * If SEC_PKCS12DecoderRenameCertNicknames() is called after calling
+ * SEC_PKCS12DecoderValidateBags(), then only the certificate nickname
+ * will be changed.
+ * If SEC_PKCS12DecoderRenameCertNicknames() is called prior to calling
+ * SEC_PKCS12DecoderValidateBags(), then SEC_PKCS12DecoderValidateBags()
+ * will change the nickname of the corresponding private key, too.
+ */
+extern SECStatus
+SEC_PKCS12DecoderRenameCertNicknames(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12NicknameRenameCallback nicknameCb,
+ void *arg);
+
+extern SECStatus
+SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx);
+
+CERTCertList *
+SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx);
+
+SECStatus
+SEC_PKCS12DecoderIterateInit(SEC_PKCS12DecoderContext *p12dcx);
+
+SECStatus
+SEC_PKCS12DecoderIterateNext(SEC_PKCS12DecoderContext *p12dcx,
+ const SEC_PKCS12DecoderItem **ipp);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pkcs12/p12creat.c b/security/nss/lib/pkcs12/p12creat.c
new file mode 100644
index 0000000000..7cc72cf159
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12creat.c
@@ -0,0 +1,218 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "pkcs12.h"
+#include "secitem.h"
+#include "secport.h"
+#include "secder.h"
+#include "secoid.h"
+#include "p12local.h"
+#include "secerr.h"
+
+/* allocate space for a PFX structure and set up initial
+ * arena pool. pfx structure is cleared and a pointer to
+ * the new structure is returned.
+ */
+SEC_PKCS12PFXItem *
+sec_pkcs12_new_pfx(void)
+{
+ SEC_PKCS12PFXItem *pfx = NULL;
+ PLArenaPool *poolp = NULL;
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); /* XXX Different size? */
+ if (poolp == NULL)
+ goto loser;
+
+ pfx = (SEC_PKCS12PFXItem *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12PFXItem));
+ if (pfx == NULL)
+ goto loser;
+ pfx->poolp = poolp;
+
+ return pfx;
+
+loser:
+ PORT_FreeArena(poolp, PR_TRUE);
+ return NULL;
+}
+
+/* allocate space for a PFX structure and set up initial
+ * arena pool. pfx structure is cleared and a pointer to
+ * the new structure is returned.
+ */
+SEC_PKCS12AuthenticatedSafe *
+sec_pkcs12_new_asafe(PLArenaPool *poolp)
+{
+ SEC_PKCS12AuthenticatedSafe *asafe = NULL;
+ void *mark;
+
+ mark = PORT_ArenaMark(poolp);
+ asafe = (SEC_PKCS12AuthenticatedSafe *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12AuthenticatedSafe));
+ if (asafe == NULL)
+ goto loser;
+ asafe->poolp = poolp;
+ PORT_Memset(&asafe->old_baggage, 0, sizeof(SEC_PKCS12Baggage_OLD));
+
+ PORT_ArenaUnmark(poolp, mark);
+ return asafe;
+
+loser:
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+}
+
+/* create a safe contents structure with a list of
+ * length 0 with the first element being NULL
+ */
+SEC_PKCS12SafeContents *
+sec_pkcs12_create_safe_contents(PLArenaPool *poolp)
+{
+ SEC_PKCS12SafeContents *safe;
+ void *mark;
+
+ if (poolp == NULL)
+ return NULL;
+
+ /* allocate structure */
+ mark = PORT_ArenaMark(poolp);
+ safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12SafeContents));
+ if (safe == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+
+ /* init list */
+ safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12SafeBag *));
+ if (safe->contents == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+ safe->contents[0] = NULL;
+ safe->poolp = poolp;
+ safe->safe_size = 0;
+ PORT_ArenaUnmark(poolp, mark);
+ return safe;
+}
+
+/* create a new external bag which is appended onto the list
+ * of bags in baggage. the bag is created in the same arena
+ * as baggage
+ */
+SEC_PKCS12BaggageItem *
+sec_pkcs12_create_external_bag(SEC_PKCS12Baggage *luggage)
+{
+ void *dummy, *mark;
+ SEC_PKCS12BaggageItem *bag;
+
+ if (luggage == NULL) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(luggage->poolp);
+
+ /* allocate space for null terminated bag list */
+ if (luggage->bags == NULL) {
+ luggage->bags = (SEC_PKCS12BaggageItem **)PORT_ArenaZAlloc(luggage->poolp,
+ sizeof(SEC_PKCS12BaggageItem *));
+ if (luggage->bags == NULL) {
+ goto loser;
+ }
+ luggage->luggage_size = 0;
+ }
+
+ /* grow the list */
+ dummy = PORT_ArenaGrow(luggage->poolp, luggage->bags,
+ sizeof(SEC_PKCS12BaggageItem *) * (luggage->luggage_size + 1),
+ sizeof(SEC_PKCS12BaggageItem *) * (luggage->luggage_size + 2));
+ if (dummy == NULL) {
+ goto loser;
+ }
+ luggage->bags = (SEC_PKCS12BaggageItem **)dummy;
+
+ luggage->bags[luggage->luggage_size] =
+ (SEC_PKCS12BaggageItem *)PORT_ArenaZAlloc(luggage->poolp,
+ sizeof(SEC_PKCS12BaggageItem));
+ if (luggage->bags[luggage->luggage_size] == NULL) {
+ goto loser;
+ }
+
+ /* create new bag and append it to the end */
+ bag = luggage->bags[luggage->luggage_size];
+ bag->espvks = (SEC_PKCS12ESPVKItem **)PORT_ArenaZAlloc(
+ luggage->poolp,
+ sizeof(SEC_PKCS12ESPVKItem *));
+ bag->unencSecrets = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(
+ luggage->poolp,
+ sizeof(SEC_PKCS12SafeBag *));
+ if ((bag->espvks == NULL) || (bag->unencSecrets == NULL)) {
+ goto loser;
+ }
+
+ bag->poolp = luggage->poolp;
+ luggage->luggage_size++;
+ luggage->bags[luggage->luggage_size] = NULL;
+ bag->espvks[0] = NULL;
+ bag->unencSecrets[0] = NULL;
+ bag->nEspvks = bag->nSecrets = 0;
+
+ PORT_ArenaUnmark(luggage->poolp, mark);
+ return bag;
+
+loser:
+ PORT_ArenaRelease(luggage->poolp, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+}
+
+/* creates a baggage witha NULL terminated 0 length list */
+SEC_PKCS12Baggage *
+sec_pkcs12_create_baggage(PLArenaPool *poolp)
+{
+ SEC_PKCS12Baggage *luggage;
+ void *mark;
+
+ if (poolp == NULL)
+ return NULL;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* allocate bag */
+ luggage = (SEC_PKCS12Baggage *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12Baggage));
+ if (luggage == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+
+ /* init list */
+ luggage->bags = (SEC_PKCS12BaggageItem **)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12BaggageItem *));
+ if (luggage->bags == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+
+ luggage->bags[0] = NULL;
+ luggage->luggage_size = 0;
+ luggage->poolp = poolp;
+
+ PORT_ArenaUnmark(poolp, mark);
+ return luggage;
+}
+
+/* free pfx structure and associated items in the arena */
+void
+SEC_PKCS12DestroyPFX(SEC_PKCS12PFXItem *pfx)
+{
+ if (pfx != NULL && pfx->poolp != NULL) {
+ PORT_FreeArena(pfx->poolp, PR_TRUE);
+ }
+}
diff --git a/security/nss/lib/pkcs12/p12d.c b/security/nss/lib/pkcs12/p12d.c
new file mode 100644
index 0000000000..615b123db0
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12d.c
@@ -0,0 +1,3626 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nssrenam.h"
+#include "nss.h"
+#include "p12t.h"
+#include "p12.h"
+#include "plarena.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "seccomon.h"
+#include "secport.h"
+#include "cert.h"
+#include "secpkcs7.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "pk11func.h"
+#include "p12plcy.h"
+#include "p12local.h"
+#include "secder.h"
+#include "secport.h"
+
+#include "certdb.h"
+
+#include "prcpucfg.h"
+
+/* This belongs in secport.h */
+#define PORT_ArenaGrowArray(poolp, oldptr, type, oldnum, newnum) \
+ (type *)PORT_ArenaGrow((poolp), (oldptr), \
+ (oldnum) * sizeof(type), (newnum) * sizeof(type))
+
+typedef struct sec_PKCS12SafeContentsContextStr sec_PKCS12SafeContentsContext;
+
+/* Opaque structure for decoding SafeContents. These are used
+ * for each authenticated safe as well as any nested safe contents.
+ */
+struct sec_PKCS12SafeContentsContextStr {
+ /* the parent decoder context */
+ SEC_PKCS12DecoderContext *p12dcx;
+
+ /* memory arena to allocate space from */
+ PLArenaPool *arena;
+
+ /* decoder context and destination for decoding safe contents */
+ SEC_ASN1DecoderContext *safeContentsA1Dcx;
+ sec_PKCS12SafeContents safeContents;
+
+ /* information for decoding safe bags within the safe contents.
+ * these variables are updated for each safe bag decoded.
+ */
+ SEC_ASN1DecoderContext *currentSafeBagA1Dcx;
+ sec_PKCS12SafeBag *currentSafeBag;
+ PRBool skipCurrentSafeBag;
+
+ /* if the safe contents is nested, the parent is pointed to here. */
+ sec_PKCS12SafeContentsContext *nestedSafeContentsCtx;
+};
+
+/* opaque decoder context structure. information for decoding a pkcs 12
+ * PDU are stored here as well as decoding pointers for intermediary
+ * structures which are part of the PKCS 12 PDU. Upon a successful
+ * decode, the safe bags containing certificates and keys encountered.
+ */
+struct SEC_PKCS12DecoderContextStr {
+ PLArenaPool *arena;
+ PK11SlotInfo *slot;
+ void *wincx;
+ PRBool error;
+ int errorValue;
+
+ /* password */
+ SECItem *pwitem;
+
+ /* used for decoding the PFX structure */
+ SEC_ASN1DecoderContext *pfxA1Dcx;
+ sec_PKCS12PFXItem pfx;
+
+ /* safe bags found during decoding */
+ sec_PKCS12SafeBag **safeBags;
+ unsigned int safeBagCount;
+
+ /* state variables for decoding authenticated safes. */
+ SEC_PKCS7DecoderContext *currentASafeP7Dcx;
+ SEC_ASN1DecoderContext *aSafeA1Dcx;
+ SEC_PKCS7DecoderContext *aSafeP7Dcx;
+ SEC_PKCS7ContentInfo *aSafeCinfo;
+ sec_PKCS12AuthenticatedSafe authSafe;
+ sec_PKCS12SafeContents safeContents;
+
+ /* safe contents info */
+ unsigned int safeContentsCnt;
+ sec_PKCS12SafeContentsContext **safeContentsList;
+
+ /* HMAC info */
+ sec_PKCS12MacData macData;
+
+ /* routines for reading back the data to be hmac'd */
+ /* They are called as follows.
+ *
+ * Stage 1: decode the aSafes cinfo into a buffer in dArg,
+ * which p12d.c sometimes refers to as the "temp file".
+ * This occurs during SEC_PKCS12DecoderUpdate calls.
+ *
+ * dOpen(dArg, PR_FALSE)
+ * dWrite(dArg, buf, len)
+ * ...
+ * dWrite(dArg, buf, len)
+ * dClose(dArg, PR_FALSE)
+ *
+ * Stage 2: verify MAC
+ * This occurs SEC_PKCS12DecoderVerify.
+ *
+ * dOpen(dArg, PR_TRUE)
+ * dRead(dArg, buf, IN_BUF_LEN)
+ * ...
+ * dRead(dArg, buf, IN_BUF_LEN)
+ * dClose(dArg, PR_TRUE)
+ */
+ digestOpenFn dOpen;
+ digestCloseFn dClose;
+ digestIOFn dRead, dWrite;
+ void *dArg;
+ PRBool dIsOpen; /* is the temp file created? */
+
+ /* helper functions */
+ SECKEYGetPasswordKey pwfn;
+ void *pwfnarg;
+ PRBool swapUnicodeBytes;
+ PRBool forceUnicode;
+
+ /* import information */
+ PRBool bagsVerified;
+
+ /* buffer management for the default callbacks implementation */
+ void *buffer; /* storage area */
+ PRInt32 filesize; /* actual data size */
+ PRInt32 allocated; /* total buffer size allocated */
+ PRInt32 currentpos; /* position counter */
+ SECPKCS12TargetTokenCAs tokenCAs;
+ sec_PKCS12SafeBag **keyList; /* used by ...IterateNext() */
+ unsigned int iteration;
+ SEC_PKCS12DecoderItem decitem;
+};
+
+/* forward declarations of functions that are used when decoding
+ * safeContents bags which are nested and when decoding the
+ * authenticatedSafes.
+ */
+static SECStatus
+sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
+ *safeContentsCtx);
+static SECStatus
+sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
+ *safeContentsCtx);
+
+/* make sure that the PFX version being decoded is a version
+ * which we support.
+ */
+static PRBool
+sec_pkcs12_proper_version(sec_PKCS12PFXItem *pfx)
+{
+ /* if no version, assume it is not supported */
+ if (pfx->version.len == 0) {
+ return PR_FALSE;
+ }
+
+ if (DER_GetInteger(&pfx->version) > SEC_PKCS12_VERSION) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* retrieve the key for decrypting the safe contents */
+static PK11SymKey *
+sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid)
+{
+ SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
+ PK11SlotInfo *slot;
+ PK11SymKey *bulkKey;
+ SECItem pwitem = { 0 };
+ SECOidTag algorithm;
+
+ if (!p12dcx) {
+ return NULL;
+ }
+
+ /* if no slot specified, use the internal key slot */
+ if (p12dcx->slot) {
+ slot = PK11_ReferenceSlot(p12dcx->slot);
+ } else {
+ slot = PK11_GetInternalKeySlot();
+ }
+
+ algorithm = SECOID_GetAlgorithmTag(algid);
+
+ if (p12dcx->forceUnicode) {
+ if (SECITEM_CopyItem(NULL, &pwitem, p12dcx->pwitem) != SECSuccess) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ } else {
+ if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm, p12dcx->pwitem)) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ }
+
+ bulkKey = PK11_PBEKeyGen(slot, algid, &pwitem, PR_FALSE, p12dcx->wincx);
+ /* some tokens can't generate PBE keys on their own, generate the
+ * key in the internal slot, and let the Import code deal with it,
+ * (if the slot can't generate PBEs, then we need to use the internal
+ * slot anyway to unwrap). */
+ if (!bulkKey && !PK11_IsInternal(slot)) {
+ PK11_FreeSlot(slot);
+ slot = PK11_GetInternalKeySlot();
+ bulkKey = PK11_PBEKeyGen(slot, algid, &pwitem, PR_FALSE, p12dcx->wincx);
+ }
+ PK11_FreeSlot(slot);
+
+ /* set the password data on the key */
+ if (bulkKey) {
+ PK11_SetSymKeyUserData(bulkKey, p12dcx->pwitem, NULL);
+ }
+
+ if (pwitem.data) {
+ SECITEM_ZfreeItem(&pwitem, PR_FALSE);
+ }
+
+ return bulkKey;
+}
+
+/* XXX this needs to be modified to handle enveloped data. most
+ * likely, it should mirror the routines for SMIME in that regard.
+ */
+static PRBool
+sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid,
+ PK11SymKey *bulkkey)
+{
+ PRBool decryptionAllowed = SEC_PKCS12DecryptionAllowed(algid);
+
+ if (!decryptionAllowed) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* when we encounter a new safe bag during the decoding, we need
+ * to allocate space for the bag to be decoded to and set the
+ * state variables appropriately. all of the safe bags are allocated
+ * in a buffer in the outer SEC_PKCS12DecoderContext, however,
+ * a pointer to the safeBag is also used in the sec_PKCS12SafeContentsContext
+ * for the current bag.
+ */
+static SECStatus
+sec_pkcs12_decoder_init_new_safe_bag(sec_PKCS12SafeContentsContext
+ *safeContentsCtx)
+{
+ void *mark = NULL;
+ SEC_PKCS12DecoderContext *p12dcx;
+
+ /* make sure that the structures are defined, and there has
+ * not been an error in the decoding
+ */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx || safeContentsCtx->p12dcx->error) {
+ return SECFailure;
+ }
+
+ p12dcx = safeContentsCtx->p12dcx;
+ mark = PORT_ArenaMark(p12dcx->arena);
+
+ /* allocate a new safe bag, if bags already exist, grow the
+ * list of bags, otherwise allocate a new list. the list is
+ * NULL terminated.
+ */
+ p12dcx->safeBags = (!p12dcx->safeBagCount)
+ ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
+ : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
+ sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
+ p12dcx->safeBagCount + 2);
+
+ if (!p12dcx->safeBags) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ /* append the bag to the end of the list and update the reference
+ * in the safeContentsCtx.
+ */
+ p12dcx->safeBags[p12dcx->safeBagCount] =
+ safeContentsCtx->currentSafeBag =
+ PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
+ if (!safeContentsCtx->currentSafeBag) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ p12dcx->safeBags[++p12dcx->safeBagCount] = NULL;
+
+ safeContentsCtx->currentSafeBag->slot = safeContentsCtx->p12dcx->slot;
+ safeContentsCtx->currentSafeBag->pwitem = safeContentsCtx->p12dcx->pwitem;
+ safeContentsCtx->currentSafeBag->swapUnicodeBytes =
+ safeContentsCtx->p12dcx->swapUnicodeBytes;
+ safeContentsCtx->currentSafeBag->arena = safeContentsCtx->p12dcx->arena;
+ safeContentsCtx->currentSafeBag->tokenCAs =
+ safeContentsCtx->p12dcx->tokenCAs;
+
+ PORT_ArenaUnmark(p12dcx->arena, mark);
+ return SECSuccess;
+
+loser:
+
+ /* if an error occurred, release the memory and set the error flag
+ * the only possible errors triggered by this function are memory
+ * related.
+ */
+ if (mark) {
+ PORT_ArenaRelease(p12dcx->arena, mark);
+ }
+
+ p12dcx->error = PR_TRUE;
+ return SECFailure;
+}
+
+/* A wrapper for updating the ASN1 context in which a safeBag is
+ * being decoded. This function is called as a callback from
+ * secasn1d when decoding SafeContents structures.
+ */
+static void
+sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ sec_PKCS12SafeContentsContext *safeContentsCtx =
+ (sec_PKCS12SafeContentsContext *)arg;
+ SEC_PKCS12DecoderContext *p12dcx;
+ SECStatus rv;
+
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx || !safeContentsCtx->currentSafeBagA1Dcx) {
+ return;
+ }
+ p12dcx = safeContentsCtx->p12dcx;
+
+ /* make sure that there are no errors and we are not skipping the current safeBag */
+ if (p12dcx->error || safeContentsCtx->skipCurrentSafeBag) {
+ goto loser;
+ }
+
+ rv = SEC_ASN1DecoderUpdate(safeContentsCtx->currentSafeBagA1Dcx, data, len);
+ if (rv != SECSuccess) {
+ p12dcx->errorValue = PORT_GetError();
+ p12dcx->error = PR_TRUE;
+ goto loser;
+ }
+
+ /* The update may have set safeContentsCtx->skipCurrentSafeBag, and we
+ * may not get another opportunity to clean up the decoder context.
+ */
+ if (safeContentsCtx->skipCurrentSafeBag) {
+ goto loser;
+ }
+
+ return;
+
+loser:
+ /* Finish the decoder context. Because there
+ * is not a way of returning an error message, it may be worth
+ * while to do a check higher up and finish any decoding contexts
+ * that are still open.
+ */
+ SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
+ safeContentsCtx->currentSafeBagA1Dcx = NULL;
+ return;
+}
+
+/* notify function for decoding safeBags. This function is
+ * used to filter safeBag types which are not supported,
+ * initiate the decoding of nested safe contents, and decode
+ * safeBags in general. this function is set when the decoder
+ * context for the safeBag is first created.
+ */
+static void
+sec_pkcs12_decoder_safe_bag_notify(void *arg, PRBool before,
+ void *dest, int real_depth)
+{
+ sec_PKCS12SafeContentsContext *safeContentsCtx =
+ (sec_PKCS12SafeContentsContext *)arg;
+ SEC_PKCS12DecoderContext *p12dcx;
+ sec_PKCS12SafeBag *bag;
+ PRBool after;
+
+ /* if an error is encountered, return */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
+ safeContentsCtx->p12dcx->error) {
+ return;
+ }
+ p12dcx = safeContentsCtx->p12dcx;
+
+ /* to make things more readable */
+ if (before)
+ after = PR_FALSE;
+ else
+ after = PR_TRUE;
+
+ /* have we determined the safeBagType yet? */
+ bag = safeContentsCtx->currentSafeBag;
+ if (bag->bagTypeTag == NULL) {
+ if (after && (dest == &(bag->safeBagType))) {
+ bag->bagTypeTag = SECOID_FindOID(&(bag->safeBagType));
+ if (bag->bagTypeTag == NULL) {
+ p12dcx->error = PR_TRUE;
+ p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
+ }
+ }
+ return;
+ }
+
+ /* process the safeBag depending on it's type. those
+ * which we do not support, are ignored. we start a decoding
+ * context for a nested safeContents.
+ */
+ switch (bag->bagTypeTag->offset) {
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ break;
+ case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
+ /* if we are just starting to decode the safeContents, initialize
+ * a new safeContentsCtx to process it.
+ */
+ if (before && (dest == &(bag->safeBagContent))) {
+ sec_pkcs12_decoder_begin_nested_safe_contents(safeContentsCtx);
+ } else if (after && (dest == &(bag->safeBagContent))) {
+ /* clean up the nested decoding */
+ sec_pkcs12_decoder_finish_nested_safe_contents(safeContentsCtx);
+ }
+ break;
+ case SEC_OID_PKCS12_V1_CRL_BAG_ID:
+ case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
+ default:
+ /* skip any safe bag types we don't understand or handle */
+ safeContentsCtx->skipCurrentSafeBag = PR_TRUE;
+ break;
+ }
+
+ return;
+}
+
+/* notify function for decoding safe contents. each entry in the
+ * safe contents is a safeBag which needs to be allocated and
+ * the decoding context initialized at the beginning and then
+ * the context needs to be closed and finished at the end.
+ *
+ * this function is set when the safeContents decode context is
+ * initialized.
+ */
+static void
+sec_pkcs12_decoder_safe_contents_notify(void *arg, PRBool before,
+ void *dest, int real_depth)
+{
+ sec_PKCS12SafeContentsContext *safeContentsCtx =
+ (sec_PKCS12SafeContentsContext *)arg;
+ SEC_PKCS12DecoderContext *p12dcx;
+ SECStatus rv;
+
+ /* if there is an error we don't want to continue processing,
+ * just return and keep going.
+ */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
+ safeContentsCtx->p12dcx->error) {
+ return;
+ }
+ p12dcx = safeContentsCtx->p12dcx;
+
+ /* if we are done with the current safeBag, then we need to
+ * finish the context and set the state variables appropriately.
+ */
+ if (!before) {
+ SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
+ SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
+ safeContentsCtx->currentSafeBagA1Dcx = NULL;
+ safeContentsCtx->skipCurrentSafeBag = PR_FALSE;
+ } else {
+ /* we are starting a new safe bag. we need to allocate space
+ * for the bag and initialize the decoding context.
+ */
+ rv = sec_pkcs12_decoder_init_new_safe_bag(safeContentsCtx);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* set up the decoder context */
+ safeContentsCtx->currentSafeBagA1Dcx =
+ SEC_ASN1DecoderStart(p12dcx->arena,
+ safeContentsCtx->currentSafeBag,
+ sec_PKCS12SafeBagTemplate);
+ if (!safeContentsCtx->currentSafeBagA1Dcx) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ /* set the notify and filter procs so that the safe bag
+ * data gets sent to the proper location when decoding.
+ */
+ SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->currentSafeBagA1Dcx,
+ sec_pkcs12_decoder_safe_bag_notify,
+ safeContentsCtx);
+ SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsA1Dcx,
+ sec_pkcs12_decoder_safe_bag_update,
+ safeContentsCtx, PR_TRUE);
+ }
+
+ return;
+
+loser:
+ /* in the event of an error, we want to close the decoding
+ * context and clear the filter and notify procedures.
+ */
+ p12dcx->error = PR_TRUE;
+
+ if (safeContentsCtx->currentSafeBagA1Dcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagA1Dcx);
+ safeContentsCtx->currentSafeBagA1Dcx = NULL;
+ }
+
+ SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->safeContentsA1Dcx);
+ SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsA1Dcx);
+
+ return;
+}
+
+/* initialize the safeContents for decoding. this routine
+ * is used for authenticatedSafes as well as nested safeContents.
+ */
+static sec_PKCS12SafeContentsContext *
+sec_pkcs12_decoder_safe_contents_init_decode(SEC_PKCS12DecoderContext *p12dcx,
+ PRBool nestedSafe)
+{
+ sec_PKCS12SafeContentsContext *safeContentsCtx = NULL;
+ const SEC_ASN1Template *theTemplate;
+
+ if (!p12dcx || p12dcx->error) {
+ return NULL;
+ }
+
+ /* allocate a new safeContents list or grow the existing list and
+ * append the new safeContents onto the end.
+ */
+ p12dcx->safeContentsList = (!p12dcx->safeContentsCnt)
+ ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeContentsContext *, 2)
+ : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeContentsList,
+ sec_PKCS12SafeContentsContext *,
+ 1 + p12dcx->safeContentsCnt,
+ 2 + p12dcx->safeContentsCnt);
+
+ if (!p12dcx->safeContentsList) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ p12dcx->safeContentsList[p12dcx->safeContentsCnt] = safeContentsCtx =
+ PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeContentsContext);
+ if (!p12dcx->safeContentsList[p12dcx->safeContentsCnt]) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ p12dcx->safeContentsList[++p12dcx->safeContentsCnt] = NULL;
+
+ /* set up the state variables */
+ safeContentsCtx->p12dcx = p12dcx;
+ safeContentsCtx->arena = p12dcx->arena;
+
+ /* begin the decoding -- the template is based on whether we are
+ * decoding a nested safeContents or not.
+ */
+ if (nestedSafe == PR_TRUE) {
+ theTemplate = sec_PKCS12NestedSafeContentsDecodeTemplate;
+ } else {
+ theTemplate = sec_PKCS12SafeContentsDecodeTemplate;
+ }
+
+ /* start the decoder context */
+ safeContentsCtx->safeContentsA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
+ &safeContentsCtx->safeContents,
+ theTemplate);
+
+ if (!safeContentsCtx->safeContentsA1Dcx) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ /* set the safeContents notify procedure to look for
+ * and start the decode of safeBags.
+ */
+ SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsA1Dcx,
+ sec_pkcs12_decoder_safe_contents_notify,
+ safeContentsCtx);
+
+ return safeContentsCtx;
+
+loser:
+ /* in the case of an error, we want to finish the decoder
+ * context and set the error flag.
+ */
+ if (safeContentsCtx && safeContentsCtx->safeContentsA1Dcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
+ safeContentsCtx->safeContentsA1Dcx = NULL;
+ }
+
+ p12dcx->error = PR_TRUE;
+
+ return NULL;
+}
+
+/* wrapper for updating safeContents. this is set as the filter of
+ * safeBag when there is a nested safeContents.
+ */
+static void
+sec_pkcs12_decoder_nested_safe_contents_update(void *arg, const char *buf,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ sec_PKCS12SafeContentsContext *safeContentsCtx =
+ (sec_PKCS12SafeContentsContext *)arg;
+ SEC_PKCS12DecoderContext *p12dcx;
+ SECStatus rv;
+
+ /* check for an error */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
+ safeContentsCtx->p12dcx->error || !safeContentsCtx->safeContentsA1Dcx) {
+ return;
+ }
+
+ /* no need to update if no data sent in */
+ if (!len || !buf) {
+ return;
+ }
+
+ /* update the decoding context */
+ p12dcx = safeContentsCtx->p12dcx;
+ rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len);
+ if (rv != SECSuccess) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ return;
+
+loser:
+ /* handle any errors. If a decoding context is open, close it. */
+ p12dcx->error = PR_TRUE;
+ if (safeContentsCtx->safeContentsA1Dcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
+ safeContentsCtx->safeContentsA1Dcx = NULL;
+ }
+}
+
+/* whenever a new safeContentsSafeBag is encountered, we need
+ * to init a safeContentsContext.
+ */
+static SECStatus
+sec_pkcs12_decoder_begin_nested_safe_contents(sec_PKCS12SafeContentsContext
+ *safeContentsCtx)
+{
+ /* check for an error */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
+ safeContentsCtx->p12dcx->error) {
+ return SECFailure;
+ }
+
+ safeContentsCtx->nestedSafeContentsCtx =
+ sec_pkcs12_decoder_safe_contents_init_decode(safeContentsCtx->p12dcx,
+ PR_TRUE);
+ if (!safeContentsCtx->nestedSafeContentsCtx) {
+ return SECFailure;
+ }
+
+ /* set up new filter proc */
+ SEC_ASN1DecoderSetNotifyProc(
+ safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx,
+ sec_pkcs12_decoder_safe_contents_notify,
+ safeContentsCtx->nestedSafeContentsCtx);
+
+ SEC_ASN1DecoderSetFilterProc(safeContentsCtx->currentSafeBagA1Dcx,
+ sec_pkcs12_decoder_nested_safe_contents_update,
+ safeContentsCtx->nestedSafeContentsCtx,
+ PR_TRUE);
+
+ return SECSuccess;
+}
+
+/* when the safeContents is done decoding, we need to reset the
+ * proper filter and notify procs and close the decoding context
+ */
+static SECStatus
+sec_pkcs12_decoder_finish_nested_safe_contents(sec_PKCS12SafeContentsContext
+ *safeContentsCtx)
+{
+ /* check for error */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
+ safeContentsCtx->p12dcx->error) {
+ return SECFailure;
+ }
+
+ /* clean up */
+ SEC_ASN1DecoderClearFilterProc(safeContentsCtx->currentSafeBagA1Dcx);
+ SEC_ASN1DecoderClearNotifyProc(
+ safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx);
+ SEC_ASN1DecoderFinish(
+ safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx);
+ safeContentsCtx->nestedSafeContentsCtx->safeContentsA1Dcx = NULL;
+ safeContentsCtx->nestedSafeContentsCtx = NULL;
+
+ return SECSuccess;
+}
+
+/* wrapper for updating safeContents. This is used when decoding
+ * the nested safeContents and any authenticatedSafes.
+ */
+static void
+sec_pkcs12_decoder_safe_contents_callback(void *arg, const char *buf,
+ unsigned long len)
+{
+ SECStatus rv;
+ sec_PKCS12SafeContentsContext *safeContentsCtx =
+ (sec_PKCS12SafeContentsContext *)arg;
+ SEC_PKCS12DecoderContext *p12dcx;
+
+ /* check for error */
+ if (!safeContentsCtx || !safeContentsCtx->p12dcx ||
+ safeContentsCtx->p12dcx->error || !safeContentsCtx->safeContentsA1Dcx) {
+ return;
+ }
+ p12dcx = safeContentsCtx->p12dcx;
+
+ /* update the decoder */
+ rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsA1Dcx, buf, len);
+ if (rv != SECSuccess) {
+ /* if we fail while trying to decode a 'safe', it's probably because
+ * we didn't have the correct password. */
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
+ SEC_PKCS7DecoderAbort(p12dcx->currentASafeP7Dcx, SEC_ERROR_BAD_PASSWORD);
+ goto loser;
+ }
+
+ return;
+
+loser:
+ /* set the error and finish the context */
+ p12dcx->error = PR_TRUE;
+ if (safeContentsCtx->safeContentsA1Dcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
+ safeContentsCtx->safeContentsA1Dcx = NULL;
+ }
+
+ return;
+}
+
+/* this is a wrapper for the ASN1 decoder to call SEC_PKCS7DecoderUpdate
+ */
+static void
+sec_pkcs12_decoder_wrap_p7_update(void *arg, const char *data,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ SEC_PKCS7DecoderContext *p7dcx = (SEC_PKCS7DecoderContext *)arg;
+
+ SEC_PKCS7DecoderUpdate(p7dcx, data, len);
+}
+
+/* notify function for decoding aSafes. at the beginning,
+ * of an authenticatedSafe, we start a decode of a safeContents.
+ * at the end, we clean up the safeContents decoder context and
+ * reset state variables
+ */
+static void
+sec_pkcs12_decoder_asafes_notify(void *arg, PRBool before, void *dest,
+ int real_depth)
+{
+ SEC_PKCS12DecoderContext *p12dcx;
+ sec_PKCS12SafeContentsContext *safeContentsCtx;
+
+ /* make sure no error occurred. */
+ p12dcx = (SEC_PKCS12DecoderContext *)arg;
+ if (!p12dcx || p12dcx->error) {
+ return;
+ }
+
+ if (before) {
+
+ /* init a new safeContentsContext */
+ safeContentsCtx = sec_pkcs12_decoder_safe_contents_init_decode(p12dcx,
+ PR_FALSE);
+ if (!safeContentsCtx) {
+ goto loser;
+ }
+
+ /* initiate the PKCS7ContentInfo decode */
+ p12dcx->currentASafeP7Dcx = SEC_PKCS7DecoderStart(
+ sec_pkcs12_decoder_safe_contents_callback,
+ safeContentsCtx,
+ p12dcx->pwfn, p12dcx->pwfnarg,
+ sec_pkcs12_decoder_get_decrypt_key, p12dcx,
+ sec_pkcs12_decoder_decryption_allowed);
+ if (!p12dcx->currentASafeP7Dcx) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ SEC_ASN1DecoderSetFilterProc(p12dcx->aSafeA1Dcx,
+ sec_pkcs12_decoder_wrap_p7_update,
+ p12dcx->currentASafeP7Dcx, PR_TRUE);
+ }
+
+ if (!before) {
+ /* if one is being decoded, finish the decode */
+ if (p12dcx->currentASafeP7Dcx != NULL) {
+ SEC_PKCS7ContentInfo *cinfo;
+ unsigned int cnt = p12dcx->safeContentsCnt - 1;
+ safeContentsCtx = p12dcx->safeContentsList[cnt];
+ if (safeContentsCtx->safeContentsA1Dcx) {
+ SEC_ASN1DecoderClearFilterProc(p12dcx->aSafeA1Dcx);
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
+ safeContentsCtx->safeContentsA1Dcx = NULL;
+ }
+ cinfo = SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
+ p12dcx->currentASafeP7Dcx = NULL;
+ if (!cinfo) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ SEC_PKCS7DestroyContentInfo(cinfo); /* don't leak it */
+ }
+ }
+
+ return;
+
+loser:
+ /* set the error flag */
+ p12dcx->error = PR_TRUE;
+ return;
+}
+
+/* wrapper for updating asafes decoding context. this function
+ * writes data being decoded to disk, so that a mac can be computed
+ * later.
+ */
+static void
+sec_pkcs12_decoder_asafes_callback(void *arg, const char *buf,
+ unsigned long len)
+{
+ SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
+ SECStatus rv;
+
+ if (!p12dcx || p12dcx->error) {
+ return;
+ }
+
+ /* update the context */
+ rv = SEC_ASN1DecoderUpdate(p12dcx->aSafeA1Dcx, buf, len);
+ if (rv != SECSuccess) {
+ p12dcx->errorValue = PORT_GetError();
+ p12dcx->error = PR_TRUE;
+ goto loser;
+ }
+
+ /* if we are writing to a file, write out the new information */
+ if (p12dcx->dWrite) {
+ unsigned long writeLen = (*p12dcx->dWrite)(p12dcx->dArg,
+ (unsigned char *)buf, len);
+ if (writeLen != len) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ }
+
+ return;
+
+loser:
+ /* set the error flag */
+ p12dcx->error = PR_TRUE;
+ SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
+ p12dcx->aSafeA1Dcx = NULL;
+
+ return;
+}
+
+/* start the decode of an authenticatedSafe contentInfo.
+ */
+static SECStatus
+sec_pkcs12_decode_start_asafes_cinfo(SEC_PKCS12DecoderContext *p12dcx)
+{
+ if (!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ /* start the decode context */
+ p12dcx->aSafeA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena,
+ &p12dcx->authSafe,
+ sec_PKCS12AuthenticatedSafeTemplate);
+ if (!p12dcx->aSafeA1Dcx) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ /* set the notify function */
+ SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeA1Dcx,
+ sec_pkcs12_decoder_asafes_notify, p12dcx);
+
+ /* begin the authSafe decoder context */
+ p12dcx->aSafeP7Dcx = SEC_PKCS7DecoderStart(
+ sec_pkcs12_decoder_asafes_callback, p12dcx,
+ p12dcx->pwfn, p12dcx->pwfnarg, NULL, NULL, NULL);
+ if (!p12dcx->aSafeP7Dcx) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ /* open the temp file for writing, if the digest functions were set */
+ if (p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE) != SECSuccess) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ /* dOpen(dArg, PR_FALSE) creates the temp file */
+ p12dcx->dIsOpen = PR_TRUE;
+
+ return SECSuccess;
+
+loser:
+ p12dcx->error = PR_TRUE;
+
+ if (p12dcx->aSafeA1Dcx) {
+ SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
+ p12dcx->aSafeA1Dcx = NULL;
+ }
+
+ if (p12dcx->aSafeP7Dcx) {
+ SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
+ p12dcx->aSafeP7Dcx = NULL;
+ }
+
+ return SECFailure;
+}
+
+/* wrapper for updating the safeContents. this function is used as
+ * a filter for the pfx when decoding the authenticated safes
+ */
+static void
+sec_pkcs12_decode_asafes_cinfo_update(void *arg, const char *buf,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ SEC_PKCS12DecoderContext *p12dcx;
+ SECStatus rv;
+
+ p12dcx = (SEC_PKCS12DecoderContext *)arg;
+ if (!p12dcx || p12dcx->error) {
+ return;
+ }
+
+ /* update the safeContents decoder */
+ rv = SEC_PKCS7DecoderUpdate(p12dcx->aSafeP7Dcx, buf, len);
+ if (rv != SECSuccess) {
+ p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
+ goto loser;
+ }
+
+ return;
+
+loser:
+
+ /* did we find an error? if so, close the context and set the
+ * error flag.
+ */
+ SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
+ p12dcx->aSafeP7Dcx = NULL;
+ p12dcx->error = PR_TRUE;
+}
+
+/* notify procedure used while decoding the pfx. When we encounter
+ * the authSafes, we want to trigger the decoding of authSafes as well
+ * as when we encounter the macData, trigger the decoding of it. we do
+ * this because we we are streaming the decoder and not decoding in place.
+ * the pfx which is the destination, only has the version decoded into it.
+ */
+static void
+sec_pkcs12_decoder_pfx_notify_proc(void *arg, PRBool before, void *dest,
+ int real_depth)
+{
+ SECStatus rv;
+ SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
+
+ /* if an error occurs, clear the notifyProc and the filterProc
+ * and continue.
+ */
+ if (p12dcx->error) {
+ SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxA1Dcx);
+ SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx);
+ return;
+ }
+
+ if (before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
+
+ /* we want to make sure this is a version we support */
+ if (!sec_pkcs12_proper_version(&p12dcx->pfx)) {
+ p12dcx->errorValue = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
+ goto loser;
+ }
+
+ /* start the decode of the aSafes cinfo... */
+ rv = sec_pkcs12_decode_start_asafes_cinfo(p12dcx);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* set the filter proc to update the authenticated safes. */
+ SEC_ASN1DecoderSetFilterProc(p12dcx->pfxA1Dcx,
+ sec_pkcs12_decode_asafes_cinfo_update,
+ p12dcx, PR_TRUE);
+ }
+
+ if (!before && (dest == &p12dcx->pfx.encodedAuthSafe)) {
+
+ /* we are done decoding the authenticatedSafes, so we need to
+ * finish the decoderContext and clear the filter proc
+ * and close the hmac callback, if present
+ */
+ p12dcx->aSafeCinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
+ p12dcx->aSafeP7Dcx = NULL;
+ if (!p12dcx->aSafeCinfo) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ SEC_ASN1DecoderClearFilterProc(p12dcx->pfxA1Dcx);
+ if (p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE) != SECSuccess)) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ }
+
+ return;
+
+loser:
+ p12dcx->error = PR_TRUE;
+}
+
+/* default implementations of the open/close/read/write functions for
+ SEC_PKCS12DecoderStart
+*/
+
+#define DEFAULT_TEMP_SIZE 4096
+
+static SECStatus
+p12u_DigestOpen(void *arg, PRBool readData)
+{
+ SEC_PKCS12DecoderContext *p12cxt = arg;
+
+ p12cxt->currentpos = 0;
+
+ if (PR_FALSE == readData) {
+ /* allocate an initial buffer */
+ p12cxt->filesize = 0;
+ p12cxt->allocated = DEFAULT_TEMP_SIZE;
+ p12cxt->buffer = PORT_Alloc(DEFAULT_TEMP_SIZE);
+ PR_ASSERT(p12cxt->buffer);
+ } else {
+ PR_ASSERT(p12cxt->buffer);
+ if (!p12cxt->buffer) {
+ return SECFailure; /* no data to read */
+ }
+ }
+
+ return SECSuccess;
+}
+
+static SECStatus
+p12u_DigestClose(void *arg, PRBool removeFile)
+{
+ SEC_PKCS12DecoderContext *p12cxt = arg;
+
+ PR_ASSERT(p12cxt);
+ if (!p12cxt) {
+ return SECFailure;
+ }
+ p12cxt->currentpos = 0;
+
+ if (PR_TRUE == removeFile) {
+ PR_ASSERT(p12cxt->buffer);
+ if (!p12cxt->buffer) {
+ return SECFailure;
+ }
+ if (p12cxt->buffer) {
+ PORT_Free(p12cxt->buffer);
+ p12cxt->buffer = NULL;
+ p12cxt->allocated = 0;
+ p12cxt->filesize = 0;
+ }
+ }
+
+ return SECSuccess;
+}
+
+static int
+p12u_DigestRead(void *arg, unsigned char *buf, unsigned long len)
+{
+ int toread = len;
+ SEC_PKCS12DecoderContext *p12cxt = arg;
+
+ if (!buf || len == 0 || !p12cxt->buffer) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return -1;
+ }
+
+ if ((p12cxt->filesize - p12cxt->currentpos) < (long)len) {
+ /* trying to read past the end of the buffer */
+ toread = p12cxt->filesize - p12cxt->currentpos;
+ }
+ memcpy(buf, (char *)p12cxt->buffer + p12cxt->currentpos, toread);
+ p12cxt->currentpos += toread;
+ return toread;
+}
+
+static int
+p12u_DigestWrite(void *arg, unsigned char *buf, unsigned long len)
+{
+ SEC_PKCS12DecoderContext *p12cxt = arg;
+
+ if (!buf || len == 0) {
+ return -1;
+ }
+
+ if (p12cxt->currentpos + (long)len > p12cxt->filesize) {
+ p12cxt->filesize = p12cxt->currentpos + len;
+ } else {
+ p12cxt->filesize += len;
+ }
+ if (p12cxt->filesize > p12cxt->allocated) {
+ void *newbuffer;
+ size_t newsize = p12cxt->filesize + DEFAULT_TEMP_SIZE;
+ newbuffer = PORT_Realloc(p12cxt->buffer, newsize);
+ if (NULL == newbuffer) {
+ return -1; /* can't extend the buffer */
+ }
+ p12cxt->buffer = newbuffer;
+ p12cxt->allocated = newsize;
+ }
+ PR_ASSERT(p12cxt->buffer);
+ memcpy((char *)p12cxt->buffer + p12cxt->currentpos, buf, len);
+ p12cxt->currentpos += len;
+ return len;
+}
+
+/* SEC_PKCS12DecoderStart
+ * Creates a decoder context for decoding a PKCS 12 PDU objct.
+ * This function sets up the initial decoding context for the
+ * PFX and sets the needed state variables.
+ *
+ * pwitem - the password for the hMac and any encoded safes.
+ * this should be changed to take a callback which retrieves
+ * the password. it may be possible for different safes to
+ * have different passwords. also, the password is already
+ * in unicode. it should probably be converted down below via
+ * a unicode conversion callback.
+ * slot - the slot to import the dataa into should multiple slots
+ * be supported based on key type and cert type?
+ * dOpen, dClose, dRead, dWrite - digest routines for writing data
+ * to a file so it could be read back and the hmac recomputed
+ * and verified. doesn't seem to be a way for both encoding
+ * and decoding to be single pass, thus the need for these
+ * routines.
+ * dArg - the argument for dOpen, etc.
+ *
+ * if NULL == dOpen == dClose == dRead == dWrite == dArg, then default
+ * implementations using a memory buffer are used
+ *
+ * This function returns the decoder context, if it was successful.
+ * Otherwise, null is returned.
+ */
+SEC_PKCS12DecoderContext *
+SEC_PKCS12DecoderStart(SECItem *pwitem, PK11SlotInfo *slot, void *wincx,
+ digestOpenFn dOpen, digestCloseFn dClose,
+ digestIOFn dRead, digestIOFn dWrite, void *dArg)
+{
+ SEC_PKCS12DecoderContext *p12dcx;
+ PLArenaPool *arena;
+ PRInt32 forceUnicode = PR_FALSE;
+ SECStatus rv;
+
+ arena = PORT_NewArena(2048); /* different size? */
+ if (!arena) {
+ return NULL; /* error is already set */
+ }
+
+ /* allocate the decoder context and set the state variables */
+ p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext);
+ if (!p12dcx) {
+ goto loser; /* error is already set */
+ }
+
+ if (!dOpen && !dClose && !dRead && !dWrite && !dArg) {
+ /* use default implementations */
+ dOpen = p12u_DigestOpen;
+ dClose = p12u_DigestClose;
+ dRead = p12u_DigestRead;
+ dWrite = p12u_DigestWrite;
+ dArg = (void *)p12dcx;
+ }
+
+ p12dcx->arena = arena;
+ p12dcx->pwitem = pwitem;
+ p12dcx->slot = (slot ? PK11_ReferenceSlot(slot)
+ : PK11_GetInternalKeySlot());
+ p12dcx->wincx = wincx;
+ p12dcx->tokenCAs = SECPKCS12TargetTokenNoCAs;
+#ifdef IS_LITTLE_ENDIAN
+ p12dcx->swapUnicodeBytes = PR_TRUE;
+#else
+ p12dcx->swapUnicodeBytes = PR_FALSE;
+#endif
+ rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ p12dcx->forceUnicode = forceUnicode;
+ p12dcx->errorValue = 0;
+ p12dcx->error = PR_FALSE;
+
+ /* start the decoding of the PFX and set the notify proc
+ * for the PFX item.
+ */
+ p12dcx->pfxA1Dcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx,
+ sec_PKCS12PFXItemTemplate);
+ if (!p12dcx->pfxA1Dcx) {
+ PK11_FreeSlot(p12dcx->slot);
+ goto loser;
+ }
+
+ SEC_ASN1DecoderSetNotifyProc(p12dcx->pfxA1Dcx,
+ sec_pkcs12_decoder_pfx_notify_proc,
+ p12dcx);
+
+ /* set up digest functions */
+ p12dcx->dOpen = dOpen;
+ p12dcx->dWrite = dWrite;
+ p12dcx->dClose = dClose;
+ p12dcx->dRead = dRead;
+ p12dcx->dArg = dArg;
+ p12dcx->dIsOpen = PR_FALSE;
+
+ p12dcx->keyList = NULL;
+ p12dcx->decitem.type = 0;
+ p12dcx->decitem.der = NULL;
+ p12dcx->decitem.hasKey = PR_FALSE;
+ p12dcx->decitem.friendlyName = NULL;
+ p12dcx->iteration = 0;
+
+ return p12dcx;
+
+loser:
+ PORT_FreeArena(arena, PR_TRUE);
+ return NULL;
+}
+
+SECStatus
+SEC_PKCS12DecoderSetTargetTokenCAs(SEC_PKCS12DecoderContext *p12dcx,
+ SECPKCS12TargetTokenCAs tokenCAs)
+{
+ if (!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+ p12dcx->tokenCAs = tokenCAs;
+ return SECSuccess;
+}
+
+/* SEC_PKCS12DecoderUpdate
+ * Streaming update sending more data to the decoder. If
+ * an error occurs, SECFailure is returned.
+ *
+ * p12dcx - the decoder context
+ * data, len - the data buffer and length of data to send to
+ * the update functions.
+ */
+SECStatus
+SEC_PKCS12DecoderUpdate(SEC_PKCS12DecoderContext *p12dcx,
+ unsigned char *data, unsigned long len)
+{
+ SECStatus rv;
+
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* update the PFX decoder context */
+ rv = SEC_ASN1DecoderUpdate(p12dcx->pfxA1Dcx, (const char *)data, len);
+ if (rv != SECSuccess) {
+ p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+
+ p12dcx->error = PR_TRUE;
+ return SECFailure;
+}
+
+/* This should be a nice sized buffer for reading in data (potentially large
+** amounts) to be MACed. It should be MUCH larger than HASH_LENGTH_MAX.
+*/
+#define IN_BUF_LEN 1024
+#ifdef DEBUG
+static const char bufferEnd[] = { "BufferEnd" };
+#endif
+#define FUDGE 128 /* must be as large as bufferEnd or more. */
+
+/* verify the hmac by reading the data from the temporary file
+ * using the routines specified when the decodingContext was
+ * created and return SECSuccess if the hmac matches.
+ */
+static SECStatus
+sec_pkcs12_decoder_verify_mac(SEC_PKCS12DecoderContext *p12dcx)
+{
+ PK11Context *pk11cx = NULL;
+ PK11SymKey *symKey = NULL;
+ SECItem *params = NULL;
+ unsigned char *buf;
+ SECStatus rv = SECFailure;
+ SECStatus lrv;
+ unsigned int bufLen;
+ int iteration;
+ int bytesRead;
+ SECOidTag algtag;
+ SECItem hmacRes;
+ SECItem ignore = { 0 };
+ CK_MECHANISM_TYPE integrityMech;
+
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ buf = (unsigned char *)PORT_Alloc(IN_BUF_LEN + FUDGE);
+ if (!buf)
+ return SECFailure; /* error code has been set. */
+
+#ifdef DEBUG
+ memcpy(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd);
+#endif
+
+ /* generate hmac key */
+ if (p12dcx->macData.iter.data) {
+ iteration = (int)DER_GetInteger(&p12dcx->macData.iter);
+ } else {
+ iteration = 1;
+ }
+
+ params = PK11_CreatePBEParams(&p12dcx->macData.macSalt, p12dcx->pwitem,
+ iteration);
+
+ algtag = SECOID_GetAlgorithmTag(&p12dcx->macData.safeMac.digestAlgorithm);
+ integrityMech = sec_pkcs12_algtag_to_keygen_mech(algtag);
+ if (integrityMech == CKM_INVALID_MECHANISM) {
+ goto loser;
+ }
+ symKey = PK11_KeyGen(NULL, integrityMech, params, 0, NULL);
+ PK11_DestroyPBEParams(params);
+ params = NULL;
+ if (!symKey)
+ goto loser;
+ /* init hmac */
+ pk11cx = PK11_CreateContextBySymKey(sec_pkcs12_algtag_to_mech(algtag),
+ CKA_SIGN, symKey, &ignore);
+ if (!pk11cx) {
+ goto loser;
+ }
+ lrv = PK11_DigestBegin(pk11cx);
+ if (lrv == SECFailure) {
+ goto loser;
+ }
+
+ /* try to open the data for readback */
+ if (p12dcx->dOpen && ((*p12dcx->dOpen)(p12dcx->dArg, PR_TRUE) != SECSuccess)) {
+ goto loser;
+ }
+
+ /* read the data back IN_BUF_LEN bytes at a time and recompute
+ * the hmac. if fewer bytes are read than are requested, it is
+ * assumed that the end of file has been reached. if bytesRead
+ * is returned as -1, then an error occurred reading from the
+ * file.
+ */
+ do {
+ bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN);
+ if (bytesRead < 0) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_READ);
+ goto loser;
+ }
+ PORT_Assert(bytesRead <= IN_BUF_LEN);
+ PORT_Assert(!memcmp(buf + IN_BUF_LEN, bufferEnd, sizeof bufferEnd));
+
+ if (bytesRead > IN_BUF_LEN) {
+ /* dRead callback overflowed buffer. */
+ PORT_SetError(SEC_ERROR_INPUT_LEN);
+ goto loser;
+ }
+
+ if (bytesRead) {
+ lrv = PK11_DigestOp(pk11cx, buf, bytesRead);
+ if (lrv == SECFailure) {
+ goto loser;
+ }
+ }
+ } while (bytesRead == IN_BUF_LEN);
+
+ /* finish the hmac context */
+ lrv = PK11_DigestFinal(pk11cx, buf, &bufLen, IN_BUF_LEN);
+ if (lrv == SECFailure) {
+ goto loser;
+ }
+
+ hmacRes.data = buf;
+ hmacRes.len = bufLen;
+
+ /* is the hmac computed the same as the hmac which was decoded? */
+ rv = SECSuccess;
+ if (SECITEM_CompareItem(&hmacRes, &p12dcx->macData.safeMac.digest) != SECEqual) {
+ PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
+ rv = SECFailure;
+ }
+
+loser:
+ /* close the file and remove it */
+ if (p12dcx->dClose) {
+ (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
+ p12dcx->dIsOpen = PR_FALSE;
+ }
+
+ if (pk11cx) {
+ PK11_DestroyContext(pk11cx, PR_TRUE);
+ }
+ if (params) {
+ PK11_DestroyPBEParams(params);
+ }
+ if (symKey) {
+ PK11_FreeSymKey(symKey);
+ }
+ PORT_ZFree(buf, IN_BUF_LEN + FUDGE);
+
+ return rv;
+}
+
+/* SEC_PKCS12DecoderVerify
+ * Verify the macData or the signature of the decoded PKCS 12 PDU.
+ * If the signature or the macData do not match, SECFailure is
+ * returned.
+ *
+ * p12dcx - the decoder context
+ */
+SECStatus
+SEC_PKCS12DecoderVerify(SEC_PKCS12DecoderContext *p12dcx)
+{
+ SECStatus rv = SECSuccess;
+
+ /* make sure that no errors have occurred... */
+ if (!p12dcx) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ if (p12dcx->error) {
+ /* error code is already set! PORT_SetError(p12dcx->errorValue); */
+ return SECFailure;
+ }
+
+ rv = SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
+ p12dcx->pfxA1Dcx = NULL;
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ /* check the signature or the mac depending on the type of
+ * integrity used.
+ */
+ if (p12dcx->pfx.encodedMacData.len) {
+ rv = SEC_ASN1DecodeItem(p12dcx->arena, &p12dcx->macData,
+ sec_PKCS12MacDataTemplate,
+ &p12dcx->pfx.encodedMacData);
+ if (rv == SECSuccess) {
+ return sec_pkcs12_decoder_verify_mac(p12dcx);
+ }
+ return rv;
+ }
+ if (SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner,
+ PR_FALSE)) {
+ return SECSuccess;
+ }
+ PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
+ return SECFailure;
+}
+
+/* SEC_PKCS12DecoderFinish
+ * Free any open ASN1 or PKCS7 decoder contexts and then
+ * free the arena pool which everything should be allocated
+ * from. This function should be called upon completion of
+ * decoding and installing of a pfx pdu. This should be
+ * called even if an error occurs.
+ *
+ * p12dcx - the decoder context
+ */
+void
+SEC_PKCS12DecoderFinish(SEC_PKCS12DecoderContext *p12dcx)
+{
+ unsigned int i;
+
+ if (!p12dcx) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ if (p12dcx->pfxA1Dcx) {
+ SEC_ASN1DecoderFinish(p12dcx->pfxA1Dcx);
+ p12dcx->pfxA1Dcx = NULL;
+ }
+
+ if (p12dcx->aSafeA1Dcx) {
+ SEC_ASN1DecoderFinish(p12dcx->aSafeA1Dcx);
+ p12dcx->aSafeA1Dcx = NULL;
+ }
+
+ /* cleanup any old ASN1 decoder contexts */
+ for (i = 0; i < p12dcx->safeContentsCnt; ++i) {
+ sec_PKCS12SafeContentsContext *safeContentsCtx, *nested;
+ safeContentsCtx = p12dcx->safeContentsList[i];
+ if (safeContentsCtx) {
+ nested = safeContentsCtx->nestedSafeContentsCtx;
+ while (nested) {
+ if (nested->safeContentsA1Dcx) {
+ SEC_ASN1DecoderFinish(nested->safeContentsA1Dcx);
+ nested->safeContentsA1Dcx = NULL;
+ }
+ nested = nested->nestedSafeContentsCtx;
+ }
+ if (safeContentsCtx->safeContentsA1Dcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsA1Dcx);
+ safeContentsCtx->safeContentsA1Dcx = NULL;
+ }
+ }
+ }
+
+ if (p12dcx->currentASafeP7Dcx &&
+ p12dcx->currentASafeP7Dcx != p12dcx->aSafeP7Dcx) {
+ SEC_PKCS7ContentInfo *cinfo;
+ cinfo = SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
+ if (cinfo) {
+ SEC_PKCS7DestroyContentInfo(cinfo); /* don't leak it */
+ }
+ }
+ p12dcx->currentASafeP7Dcx = NULL;
+
+ if (p12dcx->aSafeP7Dcx) {
+ SEC_PKCS7ContentInfo *cinfo;
+ cinfo = SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
+ if (cinfo) {
+ SEC_PKCS7DestroyContentInfo(cinfo);
+ }
+ p12dcx->aSafeP7Dcx = NULL;
+ }
+
+ if (p12dcx->aSafeCinfo) {
+ SEC_PKCS7DestroyContentInfo(p12dcx->aSafeCinfo);
+ p12dcx->aSafeCinfo = NULL;
+ }
+
+ if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) {
+ SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE);
+ }
+ if (p12dcx->decitem.friendlyName != NULL) {
+ SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE);
+ }
+
+ if (p12dcx->slot) {
+ PK11_FreeSlot(p12dcx->slot);
+ p12dcx->slot = NULL;
+ }
+
+ if (p12dcx->dIsOpen && p12dcx->dClose) {
+ (*p12dcx->dClose)(p12dcx->dArg, PR_TRUE);
+ p12dcx->dIsOpen = PR_FALSE;
+ }
+
+ if (p12dcx->arena) {
+ PORT_FreeArena(p12dcx->arena, PR_TRUE);
+ }
+}
+
+static SECStatus
+sec_pkcs12_decoder_set_attribute_value(sec_PKCS12SafeBag *bag,
+ SECOidTag attributeType,
+ SECItem *attrValue)
+{
+ int i = 0;
+ SECOidData *oid;
+
+ if (!bag || !attrValue) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ oid = SECOID_FindOIDByTag(attributeType);
+ if (!oid) {
+ return SECFailure;
+ }
+
+ if (!bag->attribs) {
+ bag->attribs =
+ PORT_ArenaZNewArray(bag->arena, sec_PKCS12Attribute *, 2);
+ } else {
+ while (bag->attribs[i])
+ i++;
+ bag->attribs = PORT_ArenaGrowArray(bag->arena, bag->attribs,
+ sec_PKCS12Attribute *, i + 1, i + 2);
+ }
+
+ if (!bag->attribs) {
+ return SECFailure;
+ }
+
+ bag->attribs[i] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
+ if (!bag->attribs[i]) {
+ return SECFailure;
+ }
+
+ bag->attribs[i]->attrValue = PORT_ArenaZNewArray(bag->arena, SECItem *, 2);
+ if (!bag->attribs[i]->attrValue) {
+ return SECFailure;
+ }
+
+ bag->attribs[i + 1] = NULL;
+ bag->attribs[i]->attrValue[0] = attrValue;
+ bag->attribs[i]->attrValue[1] = NULL;
+
+ return SECITEM_CopyItem(bag->arena, &bag->attribs[i]->attrType, &oid->oid);
+}
+
+static SECItem *
+sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag,
+ SECOidTag attributeType)
+{
+ int i;
+
+ if (!bag->attribs) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ for (i = 0; bag->attribs[i] != NULL; i++) {
+ if (SECOID_FindOIDTag(&bag->attribs[i]->attrType) == attributeType) {
+ return bag->attribs[i]->attrValue[0];
+ }
+ }
+ return NULL;
+}
+
+/* For now, this function will merely remove any ":"
+ * in the nickname which the PK11 functions may have
+ * placed there. This will keep dual certs from appearing
+ * twice under "Your" certificates when imported onto smart
+ * cards. Once with the name "Slot:Cert" and another with
+ * the nickname "Slot:Slot:Cert"
+ */
+static void
+sec_pkcs12_sanitize_nickname(PK11SlotInfo *slot, SECItem *nick)
+{
+ char *nickname;
+ char *delimit;
+ int delimitlen;
+
+ nickname = (char *)nick->data;
+ if ((delimit = PORT_Strchr(nickname, ':')) != NULL) {
+ char *slotName;
+ int slotNameLen;
+
+ slotNameLen = delimit - nickname;
+ slotName = PORT_NewArray(char, (slotNameLen + 1));
+ PORT_Assert(slotName);
+ if (slotName == NULL) {
+ /* What else can we do?*/
+ return;
+ }
+ PORT_Memcpy(slotName, nickname, slotNameLen);
+ slotName[slotNameLen] = '\0';
+ if (PORT_Strcmp(PK11_GetTokenName(slot), slotName) == 0) {
+ delimitlen = PORT_Strlen(delimit + 1);
+ PORT_Memmove(nickname, delimit + 1, delimitlen + 1);
+ nick->len = delimitlen;
+ }
+ PORT_Free(slotName);
+ }
+}
+
+static SECItem *
+sec_pkcs12_get_nickname(sec_PKCS12SafeBag *bag)
+{
+ SECItem *src, *dest;
+
+ if (!bag) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ src = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
+
+ /* The return value src is 16-bit Unicode characters, in big-endian format.
+ * Check if it is NULL or empty name.
+ */
+ if (!src || !src->data || src->len < 2 || (!src->data[0] && !src->data[1])) {
+ return NULL;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (!dest) {
+ goto loser;
+ }
+ if (!sec_pkcs12_convert_item_to_unicode(NULL, dest, src, PR_FALSE,
+ PR_FALSE, PR_FALSE)) {
+ goto loser;
+ }
+
+ sec_pkcs12_sanitize_nickname(bag->slot, dest);
+
+ return dest;
+
+loser:
+ if (dest) {
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+ }
+
+ bag->problem = PR_TRUE;
+ bag->error = PORT_GetError();
+ return NULL;
+}
+
+static SECStatus
+sec_pkcs12_set_nickname(sec_PKCS12SafeBag *bag, SECItem *name)
+{
+ sec_PKCS12Attribute *attr = NULL;
+ SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_FRIENDLY_NAME);
+
+ if (!bag || !bag->arena || !name) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!bag->attribs) {
+ if (!oid) {
+ goto loser;
+ }
+
+ bag->attribs =
+ PORT_ArenaZNewArray(bag->arena, sec_PKCS12Attribute *, 2);
+ if (!bag->attribs) {
+ goto loser;
+ }
+ bag->attribs[0] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
+ if (!bag->attribs[0]) {
+ goto loser;
+ }
+ bag->attribs[1] = NULL;
+
+ attr = bag->attribs[0];
+ if (SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid) != SECSuccess) {
+ goto loser;
+ }
+ } else {
+ int i;
+ for (i = 0; bag->attribs[i]; i++) {
+ if (SECOID_FindOIDTag(&bag->attribs[i]->attrType) == SEC_OID_PKCS9_FRIENDLY_NAME) {
+ attr = bag->attribs[i];
+ break;
+ }
+ }
+ if (!attr) {
+ if (!oid) {
+ goto loser;
+ }
+ bag->attribs = PORT_ArenaGrowArray(bag->arena, bag->attribs,
+ sec_PKCS12Attribute *, i + 1, i + 2);
+ if (!bag->attribs) {
+ goto loser;
+ }
+ bag->attribs[i] = PORT_ArenaZNew(bag->arena, sec_PKCS12Attribute);
+ if (!bag->attribs[i]) {
+ goto loser;
+ }
+ bag->attribs[i + 1] = NULL;
+ attr = bag->attribs[i];
+ if (SECITEM_CopyItem(bag->arena, &attr->attrType, &oid->oid) != SECSuccess) {
+ goto loser;
+ }
+ }
+ }
+
+ PORT_Assert(attr);
+ if (!attr->attrValue) {
+ attr->attrValue = PORT_ArenaZNewArray(bag->arena, SECItem *, 2);
+ if (!attr->attrValue) {
+ goto loser;
+ }
+ attr->attrValue[0] = PORT_ArenaZNew(bag->arena, SECItem);
+ if (!attr->attrValue[0]) {
+ goto loser;
+ }
+ attr->attrValue[1] = NULL;
+ }
+
+ name->len = PORT_Strlen((char *)name->data);
+ if (!sec_pkcs12_convert_item_to_unicode(bag->arena, attr->attrValue[0],
+ name, PR_FALSE, PR_FALSE, PR_TRUE)) {
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ bag->problem = PR_TRUE;
+ bag->error = PORT_GetError();
+ return SECFailure;
+}
+
+static SECStatus
+sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key)
+{
+ int i = 0;
+ SECKEYPrivateKeyInfo *pki = NULL;
+
+ if (!key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* if the bag does *not* contain an unencrypted PrivateKeyInfo
+ * then we cannot convert the attributes. We are propagating
+ * attributes within the PrivateKeyInfo to the SafeBag level.
+ */
+ if (SECOID_FindOIDTag(&(key->safeBagType)) !=
+ SEC_OID_PKCS12_V1_KEY_BAG_ID) {
+ return SECSuccess;
+ }
+
+ pki = key->safeBagContent.pkcs8KeyBag;
+
+ if (!pki || !pki->attributes) {
+ return SECSuccess;
+ }
+
+ while (pki->attributes[i]) {
+ SECOidTag tag = SECOID_FindOIDTag(&pki->attributes[i]->attrType);
+
+ if (tag == SEC_OID_PKCS9_LOCAL_KEY_ID ||
+ tag == SEC_OID_PKCS9_FRIENDLY_NAME) {
+ SECItem *attrValue = sec_pkcs12_get_attribute_value(key, tag);
+ if (!attrValue) {
+ if (sec_pkcs12_decoder_set_attribute_value(key, tag,
+ pki->attributes[i]->attrValue[0]) != SECSuccess) {
+ key->problem = PR_TRUE;
+ key->error = PORT_GetError();
+ return SECFailure;
+ }
+ }
+ }
+ i++;
+ }
+
+ return SECSuccess;
+}
+
+/* retrieve the nickname for the certificate bag. first look
+ * in the cert bag, otherwise get it from the key.
+ */
+static SECItem *
+sec_pkcs12_get_nickname_for_cert(sec_PKCS12SafeBag *cert,
+ sec_PKCS12SafeBag *key)
+{
+ SECItem *nickname;
+
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ nickname = sec_pkcs12_get_nickname(cert);
+ if (nickname) {
+ return nickname;
+ }
+
+ if (key) {
+ nickname = sec_pkcs12_get_nickname(key);
+
+ if (nickname && sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) {
+ SECITEM_ZfreeItem(nickname, PR_TRUE);
+ return NULL;
+ }
+ }
+
+ return nickname;
+}
+
+/* set the nickname for the certificate */
+static SECStatus
+sec_pkcs12_set_nickname_for_cert(sec_PKCS12SafeBag *cert,
+ sec_PKCS12SafeBag *key,
+ SECItem *nickname)
+{
+ if (!nickname || !cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (key) {
+ if (sec_pkcs12_set_nickname(key, nickname) != SECSuccess) {
+ cert->problem = PR_TRUE;
+ cert->error = key->error;
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+/* retrieve the DER cert from the cert bag */
+static SECItem *
+sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert)
+{
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (SECOID_FindOIDTag(&cert->safeBagType) != SEC_OID_PKCS12_V1_CERT_BAG_ID) {
+ return NULL;
+ }
+
+ /* only support X509 certs not SDSI */
+ if (SECOID_FindOIDTag(&cert->safeBagContent.certBag->bagID) != SEC_OID_PKCS9_X509_CERT) {
+ return NULL;
+ }
+
+ return SECITEM_DupItem(&(cert->safeBagContent.certBag->value.x509Cert));
+}
+
+struct certNickInfo {
+ PLArenaPool *arena;
+ unsigned int nNicks;
+ SECItem **nickList;
+ unsigned int error;
+};
+
+/* callback for traversing certificates to gather the nicknames
+ * used in a particular traversal. for instance, when using
+ * CERT_TraversePermCertsForSubject, gather the nicknames and
+ * store them in the certNickInfo for a particular DN.
+ *
+ * this handles the case where multiple nicknames are allowed
+ * for the same dn, which is not currently allowed, but may be
+ * in the future.
+ */
+static SECStatus
+gatherNicknames(CERTCertificate *cert, void *arg)
+{
+ struct certNickInfo *nickArg = (struct certNickInfo *)arg;
+ SECItem tempNick;
+ unsigned int i;
+
+ if (!cert || !nickArg || nickArg->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!cert->nickname) {
+ return SECSuccess;
+ }
+
+ tempNick.data = (unsigned char *)cert->nickname;
+ tempNick.len = PORT_Strlen(cert->nickname) + 1;
+ tempNick.type = siAsciiString;
+
+ /* do we already have the nickname in the list? */
+ if (nickArg->nNicks > 0) {
+
+ /* nicknames have been encountered, but there is no list -- bad */
+ if (!nickArg->nickList) {
+ nickArg->error = SEC_ERROR_INVALID_ARGS;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ for (i = 0; i < nickArg->nNicks; i++) {
+ if (SECITEM_CompareItem(nickArg->nickList[i], &tempNick) == SECEqual) {
+ return SECSuccess;
+ }
+ }
+ }
+
+ /* add the nickname to the list */
+ nickArg->nickList = (nickArg->nNicks == 0)
+ ? PORT_ArenaZNewArray(nickArg->arena, SECItem *, 2)
+ : PORT_ArenaGrowArray(nickArg->arena, nickArg->nickList, SECItem *,
+ nickArg->nNicks + 1, nickArg->nNicks + 2);
+
+ if (!nickArg->nickList) {
+ nickArg->error = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+ }
+
+ nickArg->nickList[nickArg->nNicks] =
+ PORT_ArenaZNew(nickArg->arena, SECItem);
+ if (!nickArg->nickList[nickArg->nNicks]) {
+ nickArg->error = PORT_GetError();
+ return SECFailure;
+ }
+
+ if (SECITEM_CopyItem(nickArg->arena, nickArg->nickList[nickArg->nNicks],
+ &tempNick) != SECSuccess) {
+ nickArg->error = PORT_GetError();
+ return SECFailure;
+ }
+
+ nickArg->nNicks++;
+
+ return SECSuccess;
+}
+
+/* traverses the certs in the data base or in the token for the
+ * DN to see if any certs currently have a nickname set.
+ * If so, return it.
+ */
+static SECItem *
+sec_pkcs12_get_existing_nick_for_dn(sec_PKCS12SafeBag *cert)
+{
+ struct certNickInfo *nickArg = NULL;
+ SECItem *derCert, *returnDn = NULL;
+ PLArenaPool *arena = NULL;
+ CERTCertificate *tempCert;
+
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ derCert = sec_pkcs12_get_der_cert(cert);
+ if (!derCert) {
+ return NULL;
+ }
+
+ tempCert = CERT_DecodeDERCertificate(derCert, PR_FALSE, NULL);
+ if (!tempCert) {
+ returnDn = NULL;
+ goto loser;
+ }
+
+ arena = PORT_NewArena(1024);
+ if (!arena) {
+ returnDn = NULL;
+ goto loser;
+ }
+ nickArg = PORT_ArenaZNew(arena, struct certNickInfo);
+ if (!nickArg) {
+ returnDn = NULL;
+ goto loser;
+ }
+ nickArg->error = 0;
+ nickArg->nNicks = 0;
+ nickArg->nickList = NULL;
+ nickArg->arena = arena;
+
+ /* if the token is local, first traverse the cert database
+ * then traverse the token.
+ */
+ if (PK11_TraverseCertsForSubjectInSlot(tempCert, cert->slot, gatherNicknames,
+ (void *)nickArg) != SECSuccess) {
+ returnDn = NULL;
+ goto loser;
+ }
+
+ if (nickArg->error) {
+ /* XXX do we want to set the error? */
+ returnDn = NULL;
+ goto loser;
+ }
+
+ if (nickArg->nNicks == 0) {
+ returnDn = NULL;
+ goto loser;
+ }
+
+ /* set it to the first name, for now. handle multiple names? */
+ returnDn = SECITEM_DupItem(nickArg->nickList[0]);
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+
+ if (tempCert) {
+ CERT_DestroyCertificate(tempCert);
+ }
+
+ if (derCert) {
+ SECITEM_FreeItem(derCert, PR_TRUE);
+ }
+
+ return (returnDn);
+}
+
+/* counts certificates found for a given traversal function */
+static SECStatus
+countCertificate(CERTCertificate *cert, void *arg)
+{
+ unsigned int *nCerts = (unsigned int *)arg;
+
+ if (!cert || !arg) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ (*nCerts)++;
+ return SECSuccess;
+}
+
+static PRBool
+sec_pkcs12_certs_for_nickname_exist(SECItem *nickname, PK11SlotInfo *slot)
+{
+ unsigned int nCerts = 0;
+
+ if (!nickname || !slot) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return PR_TRUE;
+ }
+
+ /* we want to check the local database first if we are importing to it */
+ PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate,
+ (void *)&nCerts);
+ return (PRBool)(nCerts != 0);
+}
+
+/* validate cert nickname such that there is a one-to-one relation
+ * between nicknames and dn's. we want to enforce the case that the
+ * nickname is non-NULL and that there is only one nickname per DN.
+ *
+ * if there is a problem with a nickname or the nickname is not present,
+ * the user will be prompted for it.
+ */
+static void
+sec_pkcs12_validate_cert_nickname(sec_PKCS12SafeBag *cert,
+ sec_PKCS12SafeBag *key,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb,
+ CERTCertificate *leafCert)
+{
+ SECItem *certNickname, *existingDNNick;
+ PRBool setNickname = PR_FALSE, cancel = PR_FALSE;
+ SECItem *newNickname = NULL;
+
+ if (!cert || !cert->hasKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ if (!nicknameCb) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_INVALID_ARGS;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ if (cert->hasKey && !key) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_INVALID_ARGS;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ certNickname = sec_pkcs12_get_nickname_for_cert(cert, key);
+ existingDNNick = sec_pkcs12_get_existing_nick_for_dn(cert);
+
+ /* nickname is already used w/ this dn, so it is safe to return */
+ if (certNickname && existingDNNick &&
+ SECITEM_CompareItem(certNickname, existingDNNick) == SECEqual) {
+ goto loser;
+ }
+
+ /* nickname not set in pkcs 12 bags, but a nick is already used for
+ * this dn. set the nicks in the p12 bags and finish.
+ */
+ if (existingDNNick) {
+ sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick);
+ goto loser;
+ }
+
+ /* at this point, we have a certificate for which the DN is not located
+ * on the token. the nickname specified may or may not be NULL. if it
+ * is not null, we need to make sure that there are no other certificates
+ * with this nickname in the token for it to be valid. this imposes a
+ * one to one relationship between DN and nickname.
+ *
+ * if the nickname is null, we need the user to enter a nickname for
+ * the certificate.
+ *
+ * once we have a nickname, we make sure that the nickname is unique
+ * for the DN. if it is not, the user is reprompted to enter a new
+ * nickname.
+ *
+ * in order to exit this loop, the nickname entered is either unique
+ * or the user hits cancel and the certificate is not imported.
+ */
+ setNickname = PR_FALSE;
+ while (1) {
+ /* we will use the nickname so long as no other certs have the
+ * same nickname. and the nickname is not NULL.
+ */
+ if (certNickname && certNickname->data &&
+ !sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) {
+ if (setNickname) {
+ sec_pkcs12_set_nickname_for_cert(cert, key, certNickname);
+ }
+ break;
+ }
+
+ setNickname = PR_FALSE;
+ newNickname = (*nicknameCb)(certNickname, &cancel, leafCert);
+ if (cancel) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_USER_CANCELLED;
+ break;
+ }
+
+ if (!newNickname) {
+ cert->problem = PR_TRUE;
+ cert->error = PORT_GetError();
+ break;
+ }
+
+ /* at this point we have a new nickname, if we have an existing
+ * certNickname, we need to free it and assign the new nickname
+ * to it to avoid a memory leak. happy?
+ */
+ if (certNickname) {
+ SECITEM_ZfreeItem(certNickname, PR_TRUE);
+ certNickname = NULL;
+ }
+
+ certNickname = newNickname;
+ setNickname = PR_TRUE;
+ /* go back and recheck the new nickname */
+ }
+
+loser:
+ if (certNickname) {
+ SECITEM_ZfreeItem(certNickname, PR_TRUE);
+ }
+
+ if (existingDNNick) {
+ SECITEM_ZfreeItem(existingDNNick, PR_TRUE);
+ }
+}
+
+static void
+sec_pkcs12_validate_cert(sec_PKCS12SafeBag *cert,
+ sec_PKCS12SafeBag *key,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb)
+{
+ CERTCertificate *leafCert;
+
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ cert->validated = PR_TRUE;
+
+ if (!nicknameCb) {
+ cert->noInstall = PR_TRUE;
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_INVALID_ARGS;
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ if (!cert->safeBagContent.certBag) {
+ cert->noInstall = PR_TRUE;
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
+ return;
+ }
+
+ cert->noInstall = PR_FALSE;
+ cert->unused = PR_FALSE;
+ cert->problem = PR_FALSE;
+ cert->error = 0;
+
+ leafCert = CERT_DecodeDERCertificate(
+ &cert->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL);
+ if (!leafCert) {
+ cert->noInstall = PR_TRUE;
+ cert->problem = PR_TRUE;
+ cert->error = PORT_GetError();
+ return;
+ }
+
+ sec_pkcs12_validate_cert_nickname(cert, key, nicknameCb, leafCert);
+
+ CERT_DestroyCertificate(leafCert);
+}
+
+static void
+sec_pkcs12_validate_key_by_cert(sec_PKCS12SafeBag *cert, sec_PKCS12SafeBag *key,
+ void *wincx)
+{
+ CERTCertificate *leafCert;
+ SECKEYPrivateKey *privk;
+
+ if (!key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return;
+ }
+
+ key->validated = PR_TRUE;
+
+ if (!cert) {
+ key->problem = PR_TRUE;
+ key->noInstall = PR_TRUE;
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ return;
+ }
+
+ leafCert = CERT_DecodeDERCertificate(
+ &(cert->safeBagContent.certBag->value.x509Cert), PR_FALSE, NULL);
+ if (!leafCert) {
+ key->problem = PR_TRUE;
+ key->noInstall = PR_TRUE;
+ key->error = PORT_GetError();
+ return;
+ }
+
+ privk = PK11_FindPrivateKeyFromCert(key->slot, leafCert, wincx);
+ if (!privk) {
+ privk = PK11_FindKeyByDERCert(key->slot, leafCert, wincx);
+ }
+
+ if (privk) {
+ SECKEY_DestroyPrivateKey(privk);
+ key->noInstall = PR_TRUE;
+ }
+
+ CERT_DestroyCertificate(leafCert);
+}
+
+static SECStatus
+sec_pkcs12_add_cert(sec_PKCS12SafeBag *cert, PRBool keyExists, void *wincx)
+{
+ SECItem *derCert, *nickName;
+ char *nickData = NULL;
+ PRBool isIntermediateCA;
+ SECStatus rv;
+
+ if (!cert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (cert->problem || cert->noInstall || cert->installed) {
+ return SECSuccess;
+ }
+
+ derCert = &cert->safeBagContent.certBag->value.x509Cert;
+
+ PORT_Assert(!cert->problem && !cert->noInstall);
+
+ nickName = sec_pkcs12_get_nickname(cert);
+ if (nickName) {
+ nickData = (char *)nickName->data;
+ }
+
+ isIntermediateCA = CERT_IsCADERCert(derCert, NULL) &&
+ !CERT_IsRootDERCert(derCert);
+
+ if (keyExists) {
+ CERTCertificate *newCert;
+
+ newCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL, PR_FALSE, PR_FALSE);
+ if (!newCert) {
+ if (nickName)
+ SECITEM_ZfreeItem(nickName, PR_TRUE);
+ cert->error = PORT_GetError();
+ cert->problem = PR_TRUE;
+ return SECFailure;
+ }
+
+ rv = PK11_ImportCertForKeyToSlot(cert->slot, newCert, nickData,
+ PR_TRUE, wincx);
+ CERT_DestroyCertificate(newCert);
+ } else if ((cert->tokenCAs == SECPKCS12TargetTokenNoCAs) ||
+ ((cert->tokenCAs == SECPKCS12TargetTokenIntermediateCAs) &&
+ !isIntermediateCA)) {
+ SECItem *certList[2];
+ certList[0] = derCert;
+ certList[1] = NULL;
+
+ rv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageUserCertImport,
+ 1, certList, NULL, PR_TRUE, PR_FALSE, nickData);
+ } else {
+ rv = PK11_ImportDERCert(cert->slot, derCert, CK_INVALID_HANDLE,
+ nickData, PR_FALSE);
+ }
+ if (rv) {
+ cert->problem = 1;
+ cert->error = PORT_GetError();
+ }
+ cert->installed = PR_TRUE;
+ if (nickName)
+ SECITEM_ZfreeItem(nickName, PR_TRUE);
+ return rv;
+}
+
+static SECItem *
+sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey, KeyType *type);
+
+static SECStatus
+sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECKEYPublicKey *pubKey,
+ unsigned int keyUsage,
+ SECItem *nickName, PRBool forceUnicode, void *wincx)
+{
+ SECStatus rv;
+ SECItem *publicValue = NULL;
+ KeyType keyType;
+
+ /* We should always have values for "key" and "pubKey"
+ so they can be dereferenced later. */
+ if (!key || !pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (key->problem || key->noInstall) {
+ return SECSuccess;
+ }
+
+ /* get the value and type from the public key */
+ publicValue = sec_pkcs12_get_public_value_and_type(pubKey, &keyType);
+ if (!publicValue) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ return SECFailure;
+ }
+
+ switch (SECOID_FindOIDTag(&key->safeBagType)) {
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ rv = PK11_ImportPrivateKeyInfo(key->slot,
+ key->safeBagContent.pkcs8KeyBag,
+ nickName, publicValue, PR_TRUE, PR_TRUE,
+ keyUsage, wincx);
+ break;
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID: {
+ SECItem pwitem = { 0 };
+ SECAlgorithmID *algid =
+ &key->safeBagContent.pkcs8ShroudedKeyBag->algorithm;
+ SECOidTag algorithm = SECOID_GetAlgorithmTag(algid);
+
+ if (forceUnicode) {
+ if (SECITEM_CopyItem(NULL, &pwitem, key->pwitem) != SECSuccess) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ return SECFailure;
+ }
+ } else {
+ if (!sec_pkcs12_decode_password(NULL, &pwitem, algorithm,
+ key->pwitem)) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ return SECFailure;
+ }
+ }
+
+ rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot,
+ key->safeBagContent.pkcs8ShroudedKeyBag,
+ &pwitem, nickName, publicValue,
+ PR_TRUE, PR_TRUE, keyType, keyUsage,
+ wincx);
+ if (pwitem.data) {
+ SECITEM_ZfreeItem(&pwitem, PR_FALSE);
+ }
+ break;
+ }
+ default:
+ key->error = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
+ key->problem = PR_TRUE;
+ if (nickName) {
+ SECITEM_ZfreeItem(nickName, PR_TRUE);
+ }
+ return SECFailure;
+ }
+
+ if (rv != SECSuccess) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ } else {
+ /* try to import the public key. Failure to do so is not fatal,
+ * not all tokens can store the public key */
+ if (pubKey) {
+ PK11_ImportPublicKey(key->slot, pubKey, PR_TRUE);
+ }
+ key->installed = PR_TRUE;
+ }
+
+ return rv;
+}
+
+/*
+ * The correctness of the code in this file ABSOLUTELY REQUIRES
+ * that ALL BAGs share a single common arena.
+ *
+ * This function allocates the bag list from the arena of whatever bag
+ * happens to be passed to it. Each time a new bag is handed to it,
+ * it grows (resizes) the arena of the bag that was handed to it.
+ * If the bags have different arenas, it will grow the wrong arena.
+ *
+ * Worse, if the bags had separate arenas, then while destroying the bags
+ * in a bag list, when the bag whose arena contained the bag list was
+ * destroyed, the baglist itself would be destroyed, making it difficult
+ * or impossible to continue to destroy the bags in the destroyed list.
+ */
+static SECStatus
+sec_pkcs12_add_item_to_bag_list(sec_PKCS12SafeBag ***bagList,
+ sec_PKCS12SafeBag *bag)
+{
+ sec_PKCS12SafeBag **newBagList = NULL;
+ int i = 0;
+
+ if (!bagList || !bag) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!(*bagList)) {
+ newBagList = PORT_ArenaZNewArray(bag->arena, sec_PKCS12SafeBag *, 2);
+ } else {
+ while ((*bagList)[i])
+ i++;
+ newBagList = PORT_ArenaGrowArray(bag->arena, *bagList,
+ sec_PKCS12SafeBag *, i + 1, i + 2);
+ }
+
+ if (!newBagList) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ newBagList[i] = bag;
+ newBagList[i + 1] = NULL;
+ *bagList = newBagList;
+
+ return SECSuccess;
+}
+
+static sec_PKCS12SafeBag **
+sec_pkcs12_find_certs_for_key(sec_PKCS12SafeBag **safeBags,
+ sec_PKCS12SafeBag *key)
+{
+ sec_PKCS12SafeBag **certList = NULL;
+ SECItem *keyId;
+ int i;
+
+ if (!safeBags || !safeBags[0]) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID);
+ if (!keyId) {
+ return NULL;
+ }
+
+ for (i = 0; safeBags[i]; i++) {
+ if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
+ SECItem *certKeyId = sec_pkcs12_get_attribute_value(safeBags[i],
+ SEC_OID_PKCS9_LOCAL_KEY_ID);
+
+ if (certKeyId && (SECITEM_CompareItem(certKeyId, keyId) == SECEqual)) {
+ if (sec_pkcs12_add_item_to_bag_list(&certList, safeBags[i]) != SECSuccess) {
+ /* This would leak the partial list of safeBags,
+ * but that list is allocated from the arena of
+ * one of the safebags, and will be destroyed when
+ * that arena is destroyed. So this is not a real leak.
+ */
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return certList;
+}
+
+CERTCertList *
+SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx)
+{
+ CERTCertList *certList = NULL;
+ sec_PKCS12SafeBag **safeBags;
+ int i;
+
+ if (!p12dcx || !p12dcx->safeBags || !p12dcx->safeBags[0]) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ safeBags = p12dcx->safeBags;
+ certList = CERT_NewCertList();
+
+ if (certList == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; safeBags[i]; i++) {
+ if (SECOID_FindOIDTag(&(safeBags[i]->safeBagType)) == SEC_OID_PKCS12_V1_CERT_BAG_ID) {
+ SECItem *derCert = sec_pkcs12_get_der_cert(safeBags[i]);
+ CERTCertificate *tempCert = NULL;
+
+ if (derCert == NULL)
+ continue;
+ tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL,
+ PR_FALSE, PR_TRUE);
+
+ if (tempCert) {
+ CERT_AddCertToListTail(certList, tempCert);
+ }
+ SECITEM_FreeItem(derCert, PR_TRUE);
+ }
+ /* fixed an infinite loop here, by ensuring that i gets incremented
+ * if derCert is NULL above.
+ */
+ }
+
+ return certList;
+}
+static sec_PKCS12SafeBag **
+sec_pkcs12_get_key_bags(sec_PKCS12SafeBag **safeBags)
+{
+ int i;
+ sec_PKCS12SafeBag **keyList = NULL;
+ SECOidTag bagType;
+
+ if (!safeBags || !safeBags[0]) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ for (i = 0; safeBags[i]; i++) {
+ bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType));
+ switch (bagType) {
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ if (sec_pkcs12_add_item_to_bag_list(&keyList, safeBags[i]) != SECSuccess) {
+ /* This would leak, except that keyList is allocated
+ * from the arena shared by all the safeBags.
+ */
+ return NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return keyList;
+}
+
+/* This function takes two passes over the bags, validating them
+ * The two passes are intended to mirror exactly the two passes in
+ * sec_pkcs12_install_bags. But they don't. :(
+ */
+static SECStatus
+sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb,
+ void *wincx)
+{
+ sec_PKCS12SafeBag **keyList;
+ int i;
+
+ if (!safeBags || !nicknameCb) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!safeBags[0]) {
+ return SECSuccess;
+ }
+
+ /* First pass. Find all the key bags.
+ * Find the matching cert(s) for each key.
+ */
+ keyList = sec_pkcs12_get_key_bags(safeBags);
+ if (keyList) {
+ for (i = 0; keyList[i]; ++i) {
+ sec_PKCS12SafeBag *key = keyList[i];
+ sec_PKCS12SafeBag **certList =
+ sec_pkcs12_find_certs_for_key(safeBags, key);
+
+ if (certList) {
+ int j;
+
+ if (SECOID_FindOIDTag(&(key->safeBagType)) ==
+ SEC_OID_PKCS12_V1_KEY_BAG_ID) {
+ /* if it is an unencrypted private key then make sure
+ * the attributes are propageted to the appropriate
+ * level
+ */
+ if (sec_pkcs12_get_key_info(key) != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ sec_pkcs12_validate_key_by_cert(certList[0], key, wincx);
+ for (j = 0; certList[j]; ++j) {
+ sec_PKCS12SafeBag *cert = certList[j];
+ cert->hasKey = PR_TRUE;
+ if (key->problem) {
+ cert->problem = PR_TRUE;
+ cert->error = key->error;
+ continue;
+ }
+ sec_pkcs12_validate_cert(cert, key, nicknameCb);
+ if (cert->problem) {
+ key->problem = cert->problem;
+ key->error = cert->error;
+ }
+ }
+ }
+ }
+ }
+
+ /* Now take a second pass over the safebags and mark for installation any
+ * certs that were neither installed nor disqualified by the first pass.
+ */
+ for (i = 0; safeBags[i]; ++i) {
+ sec_PKCS12SafeBag *bag = safeBags[i];
+
+ if (!bag->validated) {
+ SECOidTag bagType = SECOID_FindOIDTag(&bag->safeBagType);
+
+ switch (bagType) {
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ sec_pkcs12_validate_cert(bag, NULL, nicknameCb);
+ break;
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ bag->noInstall = PR_TRUE;
+ bag->problem = PR_TRUE;
+ bag->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ break;
+ default:
+ bag->noInstall = PR_TRUE;
+ }
+ }
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb)
+{
+ SECStatus rv;
+ int i, probCnt, errorVal = 0;
+ if (!p12dcx || p12dcx->error || !p12dcx->safeBags) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ rv = sec_pkcs12_validate_bags(p12dcx->safeBags, nicknameCb, p12dcx->wincx);
+ if (rv == SECSuccess) {
+ p12dcx->bagsVerified = PR_TRUE;
+ }
+
+ probCnt = 0;
+ i = 0;
+ while (p12dcx->safeBags[i]) {
+ if (p12dcx->safeBags[i]->problem) {
+ probCnt++;
+ errorVal = p12dcx->safeBags[i]->error;
+ }
+ i++;
+ }
+
+ if (probCnt) {
+ PORT_SetError(errorVal);
+ return SECFailure;
+ }
+
+ return rv;
+}
+
+SECStatus
+SEC_PKCS12DecoderRenameCertNicknames(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12NicknameRenameCallback nicknameCb,
+ void *arg)
+{
+ int i;
+ sec_PKCS12SafeBag *safeBag;
+ CERTCertificate *cert;
+ SECStatus srv;
+
+ if (!p12dcx || p12dcx->error || !p12dcx->safeBags || !nicknameCb) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ for (i = 0; (safeBag = p12dcx->safeBags[i]); i++) {
+ SECItem *newNickname = NULL;
+ SECItem *defaultNickname = NULL;
+ SECStatus rename_rv;
+
+ if (SECOID_FindOIDTag(&(safeBag->safeBagType)) !=
+ SEC_OID_PKCS12_V1_CERT_BAG_ID) {
+ continue;
+ }
+
+ cert = CERT_DecodeDERCertificate(
+ &safeBag->safeBagContent.certBag->value.x509Cert,
+ PR_FALSE, NULL);
+ if (!cert) {
+ return SECFailure;
+ }
+
+ defaultNickname = sec_pkcs12_get_nickname(safeBag);
+ rename_rv = (*nicknameCb)(cert, defaultNickname, &newNickname, arg);
+
+ CERT_DestroyCertificate(cert);
+
+ if (defaultNickname) {
+ SECITEM_ZfreeItem(defaultNickname, PR_TRUE);
+ defaultNickname = NULL;
+ }
+
+ if (rename_rv != SECSuccess) {
+ return rename_rv;
+ }
+
+ if (newNickname) {
+ srv = sec_pkcs12_set_nickname(safeBag, newNickname);
+ SECITEM_ZfreeItem(newNickname, PR_TRUE);
+ newNickname = NULL;
+ if (srv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+ }
+
+ return SECSuccess;
+}
+
+static SECKEYPublicKey *
+sec_pkcs12_get_public_key_and_usage(sec_PKCS12SafeBag *certBag,
+ unsigned int *usage)
+{
+ SECKEYPublicKey *pubKey = NULL;
+ CERTCertificate *cert = NULL;
+
+ if (!certBag || !usage) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ *usage = 0;
+
+ cert = CERT_DecodeDERCertificate(
+ &certBag->safeBagContent.certBag->value.x509Cert, PR_FALSE, NULL);
+ if (!cert) {
+ return NULL;
+ }
+
+ *usage = cert->keyUsage;
+ pubKey = CERT_ExtractPublicKey(cert);
+ CERT_DestroyCertificate(cert);
+ return pubKey;
+}
+
+static SECItem *
+sec_pkcs12_get_public_value_and_type(SECKEYPublicKey *pubKey,
+ KeyType *type)
+{
+ SECItem *pubValue = NULL;
+
+ if (!type || !pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ *type = pubKey->keyType;
+ switch (pubKey->keyType) {
+ case dsaKey:
+ pubValue = &pubKey->u.dsa.publicValue;
+ break;
+ case dhKey:
+ pubValue = &pubKey->u.dh.publicValue;
+ break;
+ case rsaKey:
+ pubValue = &pubKey->u.rsa.modulus;
+ break;
+ case ecKey:
+ pubValue = &pubKey->u.ec.publicValue;
+ break;
+ default:
+ pubValue = NULL;
+ }
+
+ return pubValue;
+}
+
+/* This function takes two passes over the bags, installing them in the
+ * desired slot. The two passes are intended to mirror exactly the
+ * two passes in sec_pkcs12_validate_bags.
+ */
+static SECStatus
+sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags, PRBool forceUnicode,
+ void *wincx)
+{
+ sec_PKCS12SafeBag **keyList;
+ int i;
+ int failedKeys = 0;
+
+ if (!safeBags) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!safeBags[0]) {
+ return SECSuccess;
+ }
+
+ /* First pass. Find all the key bags.
+ * Try to install them, and any certs associated with them.
+ */
+ keyList = sec_pkcs12_get_key_bags(safeBags);
+ if (keyList) {
+ for (i = 0; keyList[i]; i++) {
+ SECStatus rv;
+ SECKEYPublicKey *pubKey = NULL;
+ SECItem *nickName = NULL;
+ sec_PKCS12SafeBag *key = keyList[i];
+ sec_PKCS12SafeBag **certList;
+ unsigned int keyUsage;
+
+ if (key->problem) {
+ ++failedKeys;
+ continue;
+ }
+
+ certList = sec_pkcs12_find_certs_for_key(safeBags, key);
+ if (certList && certList[0]) {
+ pubKey = sec_pkcs12_get_public_key_and_usage(certList[0],
+ &keyUsage);
+ /* use the cert's nickname, if it has one, else use the
+ * key's nickname, else fail.
+ */
+ nickName = sec_pkcs12_get_nickname_for_cert(certList[0], key);
+ } else {
+ nickName = sec_pkcs12_get_nickname(key);
+ }
+ if (!nickName) {
+ key->error = SEC_ERROR_BAD_NICKNAME;
+ key->problem = PR_TRUE;
+ rv = SECFailure;
+ } else if (!pubKey) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ rv = SECFailure;
+ } else {
+ rv = sec_pkcs12_add_key(key, pubKey, keyUsage, nickName,
+ forceUnicode, wincx);
+ }
+ if (pubKey) {
+ SECKEY_DestroyPublicKey(pubKey);
+ pubKey = NULL;
+ }
+ if (nickName) {
+ SECITEM_FreeItem(nickName, PR_TRUE);
+ nickName = NULL;
+ }
+ if (rv != SECSuccess) {
+ PORT_SetError(key->error);
+ ++failedKeys;
+ }
+
+ if (certList) {
+ int j;
+
+ for (j = 0; certList[j]; j++) {
+ sec_PKCS12SafeBag *cert = certList[j];
+ SECStatus certRv;
+
+ if (!cert)
+ continue;
+ if (rv != SECSuccess) {
+ cert->problem = key->problem;
+ cert->error = key->error;
+ cert->noInstall = PR_TRUE;
+ continue;
+ }
+
+ certRv = sec_pkcs12_add_cert(cert, cert->hasKey, wincx);
+ if (certRv != SECSuccess) {
+ key->problem = cert->problem;
+ key->error = cert->error;
+ PORT_SetError(cert->error);
+ return SECFailure;
+ }
+ }
+ }
+ }
+ }
+ if (failedKeys)
+ return SECFailure;
+
+ /* Now take a second pass over the safebags and install any certs
+ * that were neither installed nor disqualified by the first pass.
+ */
+ for (i = 0; safeBags[i]; i++) {
+ sec_PKCS12SafeBag *bag = safeBags[i];
+
+ if (!bag->installed && !bag->problem && !bag->noInstall) {
+ SECStatus rv;
+ SECOidTag bagType = SECOID_FindOIDTag(&(bag->safeBagType));
+
+ switch (bagType) {
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ rv = sec_pkcs12_add_cert(bag, bag->hasKey, wincx);
+ if (rv != SECSuccess) {
+ PORT_SetError(bag->error);
+ return SECFailure;
+ }
+ break;
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ default:
+ break;
+ }
+ }
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx)
+{
+ PRBool forceUnicode = PR_FALSE;
+ SECStatus rv;
+
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (!p12dcx->bagsVerified) {
+ return SECFailure;
+ }
+
+ /* We need to check the option here as well as in
+ * SEC_PKCS12DecoderStart, because different PBE's could be used
+ * for PKCS #7 and PKCS #8 */
+ rv = NSS_OptionGet(__NSS_PKCS12_DECODE_FORCE_UNICODE, &forceUnicode);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ return sec_pkcs12_install_bags(p12dcx->safeBags, forceUnicode,
+ p12dcx->wincx);
+}
+
+PRBool
+sec_pkcs12_bagHasKey(SEC_PKCS12DecoderContext *p12dcx, sec_PKCS12SafeBag *bag)
+{
+ int i;
+ SECItem *keyId;
+ SECItem *certKeyId;
+
+ certKeyId = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_LOCAL_KEY_ID);
+ if (certKeyId == NULL) {
+ return PR_FALSE;
+ }
+
+ for (i = 0; p12dcx->keyList && p12dcx->keyList[i]; i++) {
+ keyId = sec_pkcs12_get_attribute_value(p12dcx->keyList[i],
+ SEC_OID_PKCS9_LOCAL_KEY_ID);
+ if (!keyId) {
+ continue;
+ }
+ if (SECITEM_CompareItem(certKeyId, keyId) == SECEqual) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+SECItem *
+sec_pkcs12_get_friendlyName(sec_PKCS12SafeBag *bag)
+{
+ SECItem *friendlyName;
+ SECItem *tempnm;
+
+ tempnm = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
+ friendlyName = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (friendlyName) {
+ if (!sec_pkcs12_convert_item_to_unicode(NULL, friendlyName,
+ tempnm, PR_TRUE, PR_FALSE, PR_FALSE)) {
+ SECITEM_FreeItem(friendlyName, PR_TRUE);
+ friendlyName = NULL;
+ }
+ }
+ return friendlyName;
+}
+
+/* Following two functions provide access to selected portions of the safe bags.
+ * Iteration is implemented per decoder context and may be accessed after
+ * SEC_PKCS12DecoderVerify() returns success.
+ * When ...DecoderIterateNext() returns SUCCESS a decoder item has been returned
+ * where item.type is always set; item.friendlyName is set if it is non-null;
+ * item.der, item.hasKey are set only for SEC_OID_PKCS12_V1_CERT_BAG_ID items.
+ * ...DecoderIterateNext() returns FAILURE when the list is exhausted or when
+ * arguments are invalid; PORT_GetError() is 0 at end-of-list.
+ * Caller has read-only access to decoder items. Any SECItems generated are
+ * owned by the decoder context and are freed by ...DecoderFinish().
+ */
+SECStatus
+SEC_PKCS12DecoderIterateInit(SEC_PKCS12DecoderContext *p12dcx)
+{
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ p12dcx->iteration = 0;
+ return SECSuccess;
+}
+
+SECStatus
+SEC_PKCS12DecoderIterateNext(SEC_PKCS12DecoderContext *p12dcx,
+ const SEC_PKCS12DecoderItem **ipp)
+{
+ sec_PKCS12SafeBag *bag;
+
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (p12dcx->decitem.type != 0 && p12dcx->decitem.der != NULL) {
+ SECITEM_FreeItem(p12dcx->decitem.der, PR_TRUE);
+ }
+ if (p12dcx->decitem.shroudAlg != NULL) {
+ SECOID_DestroyAlgorithmID(p12dcx->decitem.shroudAlg, PR_TRUE);
+ }
+ if (p12dcx->decitem.friendlyName != NULL) {
+ SECITEM_FreeItem(p12dcx->decitem.friendlyName, PR_TRUE);
+ }
+ p12dcx->decitem.type = 0;
+ p12dcx->decitem.der = NULL;
+ p12dcx->decitem.shroudAlg = NULL;
+ p12dcx->decitem.friendlyName = NULL;
+ p12dcx->decitem.hasKey = PR_FALSE;
+ *ipp = NULL;
+ if (p12dcx->keyList == NULL) {
+ p12dcx->keyList = sec_pkcs12_get_key_bags(p12dcx->safeBags);
+ }
+
+ for (; p12dcx->iteration < p12dcx->safeBagCount; p12dcx->iteration++) {
+ bag = p12dcx->safeBags[p12dcx->iteration];
+ if (bag == NULL || bag->problem) {
+ continue;
+ }
+ p12dcx->decitem.type = SECOID_FindOIDTag(&(bag->safeBagType));
+ switch (p12dcx->decitem.type) {
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ p12dcx->decitem.der = sec_pkcs12_get_der_cert(bag);
+ p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
+ p12dcx->decitem.hasKey = sec_pkcs12_bagHasKey(p12dcx, bag);
+ break;
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ p12dcx->decitem.shroudAlg = PORT_ZNew(SECAlgorithmID);
+ if (p12dcx->decitem.shroudAlg) {
+ SECOID_CopyAlgorithmID(NULL, p12dcx->decitem.shroudAlg,
+ &bag->safeBagContent.pkcs8ShroudedKeyBag->algorithm);
+ }
+ /* fall through */
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ p12dcx->decitem.friendlyName = sec_pkcs12_get_friendlyName(bag);
+ break;
+ default:
+ /* return these even though we don't expect them */
+ break;
+ case SEC_OID_UNKNOWN:
+ /* ignore these */
+ continue;
+ }
+ *ipp = &p12dcx->decitem;
+ p12dcx->iteration++;
+ break; /* end for() */
+ }
+
+ PORT_SetError(0); /* end-of-list is SECFailure with no PORT error */
+ return ((p12dcx->decitem.type == 0) ? SECFailure : SECSuccess);
+}
+
+static SECStatus
+sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx,
+ sec_PKCS12SafeBag *bag)
+{
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ p12dcx->safeBags = !p12dcx->safeBagCount
+ ? PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, 2)
+ : PORT_ArenaGrowArray(p12dcx->arena, p12dcx->safeBags,
+ sec_PKCS12SafeBag *, p12dcx->safeBagCount + 1,
+ p12dcx->safeBagCount + 2);
+
+ if (!p12dcx->safeBags) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ p12dcx->safeBags[p12dcx->safeBagCount] = bag;
+ p12dcx->safeBags[p12dcx->safeBagCount + 1] = NULL;
+ p12dcx->safeBagCount++;
+
+ return SECSuccess;
+}
+
+static sec_PKCS12SafeBag *
+sec_pkcs12_decoder_convert_old_key(SEC_PKCS12DecoderContext *p12dcx,
+ void *key, PRBool isEspvk)
+{
+ sec_PKCS12SafeBag *keyBag;
+ SECOidData *oid;
+ SECOidTag keyTag;
+ SECItem *keyID, *nickName, *newNickName;
+
+ if (!p12dcx || p12dcx->error || !key) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ newNickName = PORT_ArenaZNew(p12dcx->arena, SECItem);
+ keyBag = PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
+ if (!keyBag || !newNickName) {
+ return NULL;
+ }
+
+ keyBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
+ keyBag->slot = p12dcx->slot;
+ keyBag->arena = p12dcx->arena;
+ keyBag->pwitem = p12dcx->pwitem;
+ keyBag->tokenCAs = p12dcx->tokenCAs;
+ keyBag->oldBagType = PR_TRUE;
+
+ keyTag = (isEspvk) ? SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID : SEC_OID_PKCS12_V1_KEY_BAG_ID;
+ oid = SECOID_FindOIDByTag(keyTag);
+ if (!oid) {
+ return NULL;
+ }
+
+ if (SECITEM_CopyItem(p12dcx->arena, &keyBag->safeBagType, &oid->oid) != SECSuccess) {
+ return NULL;
+ }
+
+ if (isEspvk) {
+ SEC_PKCS12ESPVKItem *espvk = (SEC_PKCS12ESPVKItem *)key;
+ keyBag->safeBagContent.pkcs8ShroudedKeyBag =
+ espvk->espvkCipherText.pkcs8KeyShroud;
+ nickName = &(espvk->espvkData.uniNickName);
+ if (!espvk->espvkData.assocCerts || !espvk->espvkData.assocCerts[0]) {
+ PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
+ return NULL;
+ }
+ keyID = &espvk->espvkData.assocCerts[0]->digest;
+ } else {
+ SEC_PKCS12PrivateKey *pk = (SEC_PKCS12PrivateKey *)key;
+ keyBag->safeBagContent.pkcs8KeyBag = &pk->pkcs8data;
+ nickName = &(pk->pvkData.uniNickName);
+ if (!pk->pvkData.assocCerts || !pk->pvkData.assocCerts[0]) {
+ PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
+ return NULL;
+ }
+ keyID = &pk->pvkData.assocCerts[0]->digest;
+ }
+
+ if (nickName->len) {
+ if (nickName->len >= 2) {
+ if (nickName->data[0] && nickName->data[1]) {
+ if (!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName,
+ nickName, PR_FALSE, PR_FALSE, PR_TRUE)) {
+ return NULL;
+ }
+ nickName = newNickName;
+ } else if (nickName->data[0] && !nickName->data[1]) {
+ unsigned int j = 0;
+ unsigned char t;
+ for (j = 0; j < nickName->len; j += 2) {
+ t = nickName->data[j + 1];
+ nickName->data[j + 1] = nickName->data[j];
+ nickName->data[j] = t;
+ }
+ }
+ } else {
+ if (!sec_pkcs12_convert_item_to_unicode(p12dcx->arena, newNickName,
+ nickName, PR_FALSE, PR_FALSE, PR_TRUE)) {
+ return NULL;
+ }
+ nickName = newNickName;
+ }
+ }
+
+ if (sec_pkcs12_decoder_set_attribute_value(keyBag,
+ SEC_OID_PKCS9_FRIENDLY_NAME,
+ nickName) != SECSuccess) {
+ return NULL;
+ }
+
+ if (sec_pkcs12_decoder_set_attribute_value(keyBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
+ keyID) != SECSuccess) {
+ return NULL;
+ }
+
+ return keyBag;
+}
+
+static sec_PKCS12SafeBag *
+sec_pkcs12_decoder_create_cert(SEC_PKCS12DecoderContext *p12dcx,
+ SECItem *derCert)
+{
+ sec_PKCS12SafeBag *certBag;
+ SECOidData *oid;
+ SGNDigestInfo *digest;
+ SECItem *keyId;
+ SECStatus rv;
+
+ if (!p12dcx || p12dcx->error || !derCert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ keyId = PORT_ArenaZNew(p12dcx->arena, SECItem);
+ if (!keyId) {
+ return NULL;
+ }
+
+ digest = sec_pkcs12_compute_thumbprint(derCert);
+ if (!digest) {
+ return NULL;
+ }
+
+ rv = SECITEM_CopyItem(p12dcx->arena, keyId, &digest->digest);
+ SGN_DestroyDigestInfo(digest);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ oid = SECOID_FindOIDByTag(SEC_OID_PKCS12_V1_CERT_BAG_ID);
+ certBag = PORT_ArenaZNew(p12dcx->arena, sec_PKCS12SafeBag);
+ if (!certBag || !oid || (SECITEM_CopyItem(p12dcx->arena, &certBag->safeBagType, &oid->oid) != SECSuccess)) {
+ return NULL;
+ }
+
+ certBag->slot = p12dcx->slot;
+ certBag->pwitem = p12dcx->pwitem;
+ certBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
+ certBag->arena = p12dcx->arena;
+ certBag->tokenCAs = p12dcx->tokenCAs;
+
+ oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_X509_CERT);
+ certBag->safeBagContent.certBag =
+ PORT_ArenaZNew(p12dcx->arena, sec_PKCS12CertBag);
+ if (!certBag->safeBagContent.certBag || !oid ||
+ (SECITEM_CopyItem(p12dcx->arena,
+ &certBag->safeBagContent.certBag->bagID,
+ &oid->oid) != SECSuccess)) {
+ return NULL;
+ }
+
+ if (SECITEM_CopyItem(p12dcx->arena,
+ &(certBag->safeBagContent.certBag->value.x509Cert),
+ derCert) != SECSuccess) {
+ return NULL;
+ }
+
+ if (sec_pkcs12_decoder_set_attribute_value(certBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
+ keyId) != SECSuccess) {
+ return NULL;
+ }
+
+ return certBag;
+}
+
+static sec_PKCS12SafeBag **
+sec_pkcs12_decoder_convert_old_cert(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12CertAndCRL *oldCert)
+{
+ sec_PKCS12SafeBag **certList;
+ SECItem **derCertList;
+ int i, j;
+
+ if (!p12dcx || p12dcx->error || !oldCert) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ derCertList = SEC_PKCS7GetCertificateList(&oldCert->value.x509->certOrCRL);
+ if (!derCertList) {
+ return NULL;
+ }
+
+ i = 0;
+ while (derCertList[i])
+ i++;
+
+ certList = PORT_ArenaZNewArray(p12dcx->arena, sec_PKCS12SafeBag *, (i + 1));
+ if (!certList) {
+ return NULL;
+ }
+
+ for (j = 0; j < i; j++) {
+ certList[j] = sec_pkcs12_decoder_create_cert(p12dcx, derCertList[j]);
+ if (!certList[j]) {
+ return NULL;
+ }
+ }
+
+ return certList;
+}
+
+static SECStatus
+sec_pkcs12_decoder_convert_old_key_and_certs(SEC_PKCS12DecoderContext *p12dcx,
+ void *oldKey, PRBool isEspvk,
+ SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ sec_PKCS12SafeBag *key, **certList;
+ SEC_PKCS12CertAndCRL *oldCert;
+ SEC_PKCS12PVKSupportingData *pvkData;
+ int i;
+ SECItem *keyName;
+
+ if (!p12dcx || !oldKey) {
+ return SECFailure;
+ }
+
+ if (isEspvk) {
+ pvkData = &((SEC_PKCS12ESPVKItem *)(oldKey))->espvkData;
+ } else {
+ pvkData = &((SEC_PKCS12PrivateKey *)(oldKey))->pvkData;
+ }
+
+ if (!pvkData->assocCerts || !pvkData->assocCerts[0]) {
+ PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
+ return SECFailure;
+ }
+
+ oldCert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
+ SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID, NULL,
+ pvkData->assocCerts[0]);
+ if (!oldCert) {
+ PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
+ return SECFailure;
+ }
+
+ key = sec_pkcs12_decoder_convert_old_key(p12dcx, oldKey, isEspvk);
+ certList = sec_pkcs12_decoder_convert_old_cert(p12dcx, oldCert);
+ if (!key || !certList) {
+ return SECFailure;
+ }
+
+ if (sec_pkcs12_decoder_append_bag_to_context(p12dcx, key) != SECSuccess) {
+ return SECFailure;
+ }
+
+ keyName = sec_pkcs12_get_nickname(key);
+ if (!keyName) {
+ return SECFailure;
+ }
+
+ i = 0;
+ while (certList[i]) {
+ if (sec_pkcs12_decoder_append_bag_to_context(p12dcx, certList[i]) != SECSuccess) {
+ return SECFailure;
+ }
+ i++;
+ }
+
+ certList = sec_pkcs12_find_certs_for_key(p12dcx->safeBags, key);
+ if (!certList) {
+ return SECFailure;
+ }
+
+ i = 0;
+ while (certList[i] != 0) {
+ if (sec_pkcs12_set_nickname(certList[i], keyName) != SECSuccess) {
+ return SECFailure;
+ }
+ i++;
+ }
+
+ return SECSuccess;
+}
+
+static SECStatus
+sec_pkcs12_decoder_convert_old_safe_to_bags(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ SECStatus rv;
+
+ if (!p12dcx || p12dcx->error) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ if (safe && safe->contents) {
+ int i = 0;
+ while (safe->contents[i] != NULL) {
+ if (SECOID_FindOIDTag(&safe->contents[i]->safeBagType) == SEC_OID_PKCS12_KEY_BAG_ID) {
+ int j = 0;
+ SEC_PKCS12PrivateKeyBag *privBag =
+ safe->contents[i]->safeContent.keyBag;
+
+ while (privBag->privateKeys[j] != NULL) {
+ SEC_PKCS12PrivateKey *pk = privBag->privateKeys[j];
+ rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx, pk,
+ PR_FALSE, safe, baggage);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ j++;
+ }
+ }
+ i++;
+ }
+ }
+
+ if (baggage && baggage->bags) {
+ int i = 0;
+ while (baggage->bags[i] != NULL) {
+ SEC_PKCS12BaggageItem *bag = baggage->bags[i];
+ int j = 0;
+
+ if (!bag->espvks) {
+ i++;
+ continue;
+ }
+
+ while (bag->espvks[j] != NULL) {
+ SEC_PKCS12ESPVKItem *espvk = bag->espvks[j];
+ rv = sec_pkcs12_decoder_convert_old_key_and_certs(p12dcx, espvk,
+ PR_TRUE, safe, baggage);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ j++;
+ }
+ i++;
+ }
+ }
+
+ return SECSuccess;
+
+loser:
+ return SECFailure;
+}
+
+SEC_PKCS12DecoderContext *
+sec_PKCS12ConvertOldSafeToNew(PLArenaPool *arena, PK11SlotInfo *slot,
+ PRBool swapUnicode, SECItem *pwitem,
+ void *wincx, SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ SEC_PKCS12DecoderContext *p12dcx;
+
+ if (!arena || !slot || !pwitem) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ if (!safe && !baggage) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+
+ p12dcx = PORT_ArenaZNew(arena, SEC_PKCS12DecoderContext);
+ if (!p12dcx) {
+ return NULL;
+ }
+
+ p12dcx->arena = arena;
+ p12dcx->slot = PK11_ReferenceSlot(slot);
+ p12dcx->wincx = wincx;
+ p12dcx->error = PR_FALSE;
+ p12dcx->swapUnicodeBytes = swapUnicode;
+ p12dcx->pwitem = pwitem;
+ p12dcx->tokenCAs = SECPKCS12TargetTokenNoCAs;
+
+ if (sec_pkcs12_decoder_convert_old_safe_to_bags(p12dcx, safe, baggage) != SECSuccess) {
+ p12dcx->error = PR_TRUE;
+ return NULL;
+ }
+
+ return p12dcx;
+}
diff --git a/security/nss/lib/pkcs12/p12dec.c b/security/nss/lib/pkcs12/p12dec.c
new file mode 100644
index 0000000000..5a94392631
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12dec.c
@@ -0,0 +1,670 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "pkcs12.h"
+#include "plarena.h"
+#include "secpkcs7.h"
+#include "p12local.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "secport.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "secerr.h"
+#include "cert.h"
+#include "certdb.h"
+#include "p12plcy.h"
+#include "p12.h"
+#include "secpkcs5.h"
+
+/* PFX extraction and validation routines */
+
+/* decode the DER encoded PFX item. if unable to decode, check to see if it
+ * is an older PFX item. If that fails, assume the file was not a valid
+ * pfx file.
+ * the returned pfx structure should be destroyed using SEC_PKCS12DestroyPFX
+ */
+static SEC_PKCS12PFXItem *
+sec_pkcs12_decode_pfx(SECItem *der_pfx)
+{
+ SEC_PKCS12PFXItem *pfx;
+ SECStatus rv;
+
+ if (der_pfx == NULL) {
+ return NULL;
+ }
+
+ /* allocate the space for a new PFX item */
+ pfx = sec_pkcs12_new_pfx();
+ if (pfx == NULL) {
+ return NULL;
+ }
+
+ rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate,
+ der_pfx);
+
+ /* if a failure occurred, check for older version...
+ * we also get rid of the old pfx structure, because we don't
+ * know where it failed and what data in may contain
+ */
+ if (rv != SECSuccess) {
+ SEC_PKCS12DestroyPFX(pfx);
+ pfx = sec_pkcs12_new_pfx();
+ if (pfx == NULL) {
+ return NULL;
+ }
+ rv = SEC_ASN1DecodeItem(pfx->poolp, pfx, SEC_PKCS12PFXItemTemplate_OLD,
+ der_pfx);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_PKCS12_DECODING_PFX);
+ PORT_FreeArena(pfx->poolp, PR_TRUE);
+ return NULL;
+ }
+ pfx->old = PR_TRUE;
+ rv = SGN_CopyDigestInfo(pfx->poolp, &pfx->macData.safeMac, &pfx->old_safeMac);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_FreeArena(pfx->poolp, PR_TRUE);
+ return NULL;
+ }
+ rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, &pfx->old_macSalt);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_FreeArena(pfx->poolp, PR_TRUE);
+ return NULL;
+ }
+ } else {
+ pfx->old = PR_FALSE;
+ }
+
+ /* convert bit string from bits to bytes */
+ pfx->macData.macSalt.len /= 8;
+
+ return pfx;
+}
+
+/* validate the integrity MAC used in the PFX. The MAC is generated
+ * per the PKCS 12 document. If the MAC is incorrect, it is most likely
+ * due to an invalid password.
+ * pwitem is the integrity password
+ * pfx is the decoded pfx item
+ */
+static PRBool
+sec_pkcs12_check_pfx_mac(SEC_PKCS12PFXItem *pfx,
+ SECItem *pwitem)
+{
+ SECItem *key = NULL, *mac = NULL, *data = NULL;
+ SECItem *vpwd = NULL;
+ SECOidTag algorithm;
+ PRBool ret = PR_FALSE;
+
+ if (pfx == NULL) {
+ return PR_FALSE;
+ }
+
+ algorithm = SECOID_GetAlgorithmTag(&pfx->macData.safeMac.digestAlgorithm);
+ switch (algorithm) {
+ /* only SHA1 hashing supported as a MACing algorithm */
+ case SEC_OID_SHA1:
+ if (pfx->old == PR_FALSE) {
+ pfx->swapUnicode = PR_FALSE;
+ }
+
+ recheckUnicodePassword:
+ vpwd = sec_pkcs12_create_virtual_password(pwitem,
+ &pfx->macData.macSalt,
+ pfx->swapUnicode);
+ if (vpwd == NULL) {
+ return PR_FALSE;
+ }
+
+ key = sec_pkcs12_generate_key_from_password(algorithm,
+ &pfx->macData.macSalt,
+ (pfx->old ? pwitem : vpwd));
+ /* free vpwd only for newer PFX */
+ if (vpwd) {
+ SECITEM_ZfreeItem(vpwd, PR_TRUE);
+ }
+ if (key == NULL) {
+ return PR_FALSE;
+ }
+
+ data = SEC_PKCS7GetContent(&pfx->authSafe);
+ if (data == NULL) {
+ break;
+ }
+
+ /* check MAC */
+ mac = sec_pkcs12_generate_mac(key, data, pfx->old);
+ ret = PR_TRUE;
+ if (mac) {
+ SECItem *safeMac = &pfx->macData.safeMac.digest;
+ if (SECITEM_CompareItem(mac, safeMac) != SECEqual) {
+
+ /* if we encounter an invalid mac, lets invert the
+ * password in case of unicode changes
+ */
+ if (((!pfx->old) && pfx->swapUnicode) || (pfx->old)) {
+ PORT_SetError(SEC_ERROR_PKCS12_INVALID_MAC);
+ ret = PR_FALSE;
+ } else {
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ pfx->swapUnicode = PR_TRUE;
+ goto recheckUnicodePassword;
+ }
+ }
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ } else {
+ ret = PR_FALSE;
+ }
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM);
+ ret = PR_FALSE;
+ break;
+ }
+
+ /* let success fall through */
+ if (key != NULL)
+ SECITEM_ZfreeItem(key, PR_TRUE);
+
+ return ret;
+}
+
+/* check the validity of the pfx structure. we currently only support
+ * password integrity mode, so we check the MAC.
+ */
+static PRBool
+sec_pkcs12_validate_pfx(SEC_PKCS12PFXItem *pfx,
+ SECItem *pwitem)
+{
+ SECOidTag contentType;
+
+ contentType = SEC_PKCS7ContentType(&pfx->authSafe);
+ switch (contentType) {
+ case SEC_OID_PKCS7_DATA:
+ return sec_pkcs12_check_pfx_mac(pfx, pwitem);
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ default:
+ PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE);
+ break;
+ }
+
+ return PR_FALSE;
+}
+
+/* decode and return the valid PFX. if the PFX item is not valid,
+ * NULL is returned.
+ */
+static SEC_PKCS12PFXItem *
+sec_pkcs12_get_pfx(SECItem *pfx_data,
+ SECItem *pwitem)
+{
+ SEC_PKCS12PFXItem *pfx;
+ PRBool valid_pfx;
+
+ if ((pfx_data == NULL) || (pwitem == NULL)) {
+ return NULL;
+ }
+
+ pfx = sec_pkcs12_decode_pfx(pfx_data);
+ if (pfx == NULL) {
+ return NULL;
+ }
+
+ valid_pfx = sec_pkcs12_validate_pfx(pfx, pwitem);
+ if (valid_pfx != PR_TRUE) {
+ SEC_PKCS12DestroyPFX(pfx);
+ pfx = NULL;
+ }
+
+ return pfx;
+}
+
+/* authenticated safe decoding, validation, and access routines
+ */
+
+/* convert dogbert beta 3 authenticated safe structure to a post
+ * beta three structure, so that we don't have to change more routines.
+ */
+static SECStatus
+sec_pkcs12_convert_old_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe)
+{
+ SEC_PKCS12Baggage *baggage;
+ SEC_PKCS12BaggageItem *bag;
+ SECStatus rv = SECSuccess;
+
+ if (asafe->old_baggage.espvks == NULL) {
+ /* XXX should the ASN1 engine produce a single NULL element list
+ * rather than setting the pointer to NULL?
+ * There is no need to return an error -- assume that the list
+ * was empty.
+ */
+ return SECSuccess;
+ }
+
+ baggage = sec_pkcs12_create_baggage(asafe->poolp);
+ if (!baggage) {
+ return SECFailure;
+ }
+ bag = sec_pkcs12_create_external_bag(baggage);
+ if (!bag) {
+ return SECFailure;
+ }
+
+ PORT_Memcpy(&asafe->baggage, baggage, sizeof(SEC_PKCS12Baggage));
+
+ /* if there are shrouded keys, append them to the bag */
+ rv = SECSuccess;
+ if (asafe->old_baggage.espvks[0] != NULL) {
+ int nEspvk = 0;
+ rv = SECSuccess;
+ while ((asafe->old_baggage.espvks[nEspvk] != NULL) &&
+ (rv == SECSuccess)) {
+ rv = sec_pkcs12_append_shrouded_key(bag,
+ asafe->old_baggage.espvks[nEspvk]);
+ nEspvk++;
+ }
+ }
+
+ return rv;
+}
+
+/* decodes the authenticated safe item. a return of NULL indicates
+ * an error. however, the error will have occurred either in memory
+ * allocation or in decoding the authenticated safe.
+ *
+ * if an old PFX item has been found, we want to convert the
+ * old authenticated safe to the new one.
+ */
+static SEC_PKCS12AuthenticatedSafe *
+sec_pkcs12_decode_authenticated_safe(SEC_PKCS12PFXItem *pfx)
+{
+ SECItem *der_asafe = NULL;
+ SEC_PKCS12AuthenticatedSafe *asafe = NULL;
+ SECStatus rv;
+
+ if (pfx == NULL) {
+ return NULL;
+ }
+
+ der_asafe = SEC_PKCS7GetContent(&pfx->authSafe);
+ if (der_asafe == NULL) {
+ /* XXX set error ? */
+ goto loser;
+ }
+
+ asafe = sec_pkcs12_new_asafe(pfx->poolp);
+ if (asafe == NULL) {
+ goto loser;
+ }
+
+ if (pfx->old == PR_FALSE) {
+ rv = SEC_ASN1DecodeItem(pfx->poolp, asafe,
+ SEC_PKCS12AuthenticatedSafeTemplate,
+ der_asafe);
+ asafe->old = PR_FALSE;
+ asafe->swapUnicode = pfx->swapUnicode;
+ } else {
+ /* handle beta exported files */
+ rv = SEC_ASN1DecodeItem(pfx->poolp, asafe,
+ SEC_PKCS12AuthenticatedSafeTemplate_OLD,
+ der_asafe);
+ asafe->safe = &(asafe->old_safe);
+ rv = sec_pkcs12_convert_old_auth_safe(asafe);
+ asafe->old = PR_TRUE;
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ asafe->poolp = pfx->poolp;
+
+ return asafe;
+
+loser:
+ return NULL;
+}
+
+/* validates the safe within the authenticated safe item.
+ * in order to be valid:
+ * 1. the privacy salt must be present
+ * 2. the encryption algorithm must be supported (including
+ * export policy)
+ * PR_FALSE indicates an error, PR_TRUE indicates a valid safe
+ */
+static PRBool
+sec_pkcs12_validate_encrypted_safe(SEC_PKCS12AuthenticatedSafe *asafe)
+{
+ PRBool valid = PR_FALSE;
+ SECAlgorithmID *algid;
+
+ if (asafe == NULL) {
+ return PR_FALSE;
+ }
+
+ /* if mode is password privacy, then privacySalt is assumed
+ * to be non-zero.
+ */
+ if (asafe->privacySalt.len != 0) {
+ valid = PR_TRUE;
+ asafe->privacySalt.len /= 8;
+ } else {
+ PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
+ return PR_FALSE;
+ }
+
+ /* until spec changes, content will have between 2 and 8 bytes depending
+ * upon the algorithm used if certs are unencrypted...
+ * also want to support case where content is empty -- which we produce
+ */
+ if (SEC_PKCS7IsContentEmpty(asafe->safe, 8) == PR_TRUE) {
+ asafe->emptySafe = PR_TRUE;
+ return PR_TRUE;
+ }
+
+ asafe->emptySafe = PR_FALSE;
+
+ /* make sure that a pbe algorithm is being used */
+ algid = SEC_PKCS7GetEncryptionAlgorithm(asafe->safe);
+ if (algid != NULL) {
+ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+ valid = SEC_PKCS12DecryptionAllowed(algid);
+
+ if (valid == PR_FALSE) {
+ PORT_SetError(SEC_ERROR_BAD_EXPORT_ALGORITHM);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM);
+ valid = PR_FALSE;
+ }
+ } else {
+ valid = PR_FALSE;
+ PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM);
+ }
+
+ return valid;
+}
+
+/* validates authenticates safe:
+ * 1. checks that the version is supported
+ * 2. checks that only password privacy mode is used (currently)
+ * 3. further, makes sure safe has appropriate policies per above function
+ * PR_FALSE indicates failure.
+ */
+static PRBool
+sec_pkcs12_validate_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe)
+{
+ PRBool valid = PR_TRUE;
+ SECOidTag safe_type;
+ int version;
+
+ if (asafe == NULL) {
+ return PR_FALSE;
+ }
+
+ /* check version, since it is default it may not be present.
+ * therefore, assume ok
+ */
+ if ((asafe->version.len > 0) && (asafe->old == PR_FALSE)) {
+ version = DER_GetInteger(&asafe->version);
+ if (version > SEC_PKCS12_PFX_VERSION) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_VERSION);
+ return PR_FALSE;
+ }
+ }
+
+ /* validate password mode is being used */
+ safe_type = SEC_PKCS7ContentType(asafe->safe);
+ switch (safe_type) {
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ valid = sec_pkcs12_validate_encrypted_safe(asafe);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ default:
+ PORT_SetError(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE);
+ valid = PR_FALSE;
+ break;
+ }
+
+ return valid;
+}
+
+/* retrieves the authenticated safe item from the PFX item
+ * before returning the authenticated safe, the validity of the
+ * authenticated safe is checked and if valid, returned.
+ * a return of NULL indicates that an error occurred.
+ */
+static SEC_PKCS12AuthenticatedSafe *
+sec_pkcs12_get_auth_safe(SEC_PKCS12PFXItem *pfx)
+{
+ SEC_PKCS12AuthenticatedSafe *asafe;
+ PRBool valid_safe;
+
+ if (pfx == NULL) {
+ return NULL;
+ }
+
+ asafe = sec_pkcs12_decode_authenticated_safe(pfx);
+ if (asafe == NULL) {
+ return NULL;
+ }
+
+ valid_safe = sec_pkcs12_validate_auth_safe(asafe);
+ if (valid_safe != PR_TRUE) {
+ asafe = NULL;
+ } else if (asafe) {
+ asafe->baggage.poolp = asafe->poolp;
+ }
+
+ return asafe;
+}
+
+/* decrypts the authenticated safe.
+ * a return of anything but SECSuccess indicates an error. the
+ * password is not known to be valid until the call to the
+ * function sec_pkcs12_get_safe_contents. If decoding the safe
+ * fails, it is assumed the password was incorrect and the error
+ * is set then. any failure here is assumed to be due to
+ * internal problems in SEC_PKCS7DecryptContents or below.
+ */
+static SECStatus
+sec_pkcs12_decrypt_auth_safe(SEC_PKCS12AuthenticatedSafe *asafe,
+ SECItem *pwitem,
+ void *wincx)
+{
+ SECStatus rv = SECFailure;
+ SECItem *vpwd = NULL;
+
+ if ((asafe == NULL) || (pwitem == NULL)) {
+ return SECFailure;
+ }
+
+ if (asafe->old == PR_FALSE) {
+ vpwd = sec_pkcs12_create_virtual_password(pwitem, &asafe->privacySalt,
+ asafe->swapUnicode);
+ if (vpwd == NULL) {
+ return SECFailure;
+ }
+ }
+
+ rv = SEC_PKCS7DecryptContents(asafe->poolp, asafe->safe,
+ (asafe->old ? pwitem : vpwd), wincx);
+
+ if (asafe->old == PR_FALSE) {
+ SECITEM_ZfreeItem(vpwd, PR_TRUE);
+ }
+
+ return rv;
+}
+
+/* extract the safe from the authenticated safe.
+ * if we are unable to decode the safe, then it is likely that the
+ * safe has not been decrypted or the password used to decrypt
+ * the safe was invalid. we assume that the password was invalid and
+ * set an error accordingly.
+ * a return of NULL indicates that an error occurred.
+ */
+static SEC_PKCS12SafeContents *
+sec_pkcs12_get_safe_contents(SEC_PKCS12AuthenticatedSafe *asafe)
+{
+ SECItem *src = NULL;
+ SEC_PKCS12SafeContents *safe = NULL;
+ SECStatus rv = SECFailure;
+
+ if (asafe == NULL) {
+ return NULL;
+ }
+
+ safe = (SEC_PKCS12SafeContents *)PORT_ArenaZAlloc(asafe->poolp,
+ sizeof(SEC_PKCS12SafeContents));
+ if (safe == NULL) {
+ return NULL;
+ }
+ safe->poolp = asafe->poolp;
+ safe->old = asafe->old;
+ safe->swapUnicode = asafe->swapUnicode;
+
+ src = SEC_PKCS7GetContent(asafe->safe);
+ if (src != NULL) {
+ const SEC_ASN1Template *theTemplate;
+ if (asafe->old != PR_TRUE) {
+ theTemplate = SEC_PKCS12SafeContentsTemplate;
+ } else {
+ theTemplate = SEC_PKCS12SafeContentsTemplate_OLD;
+ }
+
+ rv = SEC_ASN1DecodeItem(asafe->poolp, safe, theTemplate, src);
+
+ /* if we could not decode the item, password was probably invalid */
+ if (rv != SECSuccess) {
+ safe = NULL;
+ PORT_SetError(SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE);
+ rv = SECFailure;
+ }
+
+ return safe;
+}
+
+/* import PFX item
+ * der_pfx is the der encoded pfx structure
+ * pbef and pbearg are the integrity/encryption password call back
+ * ncCall is the nickname collision calllback
+ * slot is the destination token
+ * wincx window handler
+ *
+ * on error, error code set and SECFailure returned
+ */
+SECStatus
+SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem,
+ SEC_PKCS12NicknameCollisionCallback ncCall,
+ PK11SlotInfo *slot,
+ void *wincx)
+{
+ SEC_PKCS12PFXItem *pfx;
+ SEC_PKCS12AuthenticatedSafe *asafe;
+ SEC_PKCS12SafeContents *safe_contents = NULL;
+ SECStatus rv;
+
+ if (!der_pfx || !pwitem || !slot) {
+ return SECFailure;
+ }
+
+ /* decode and validate each section */
+ rv = SECFailure;
+
+ pfx = sec_pkcs12_get_pfx(der_pfx, pwitem);
+ if (pfx != NULL) {
+ asafe = sec_pkcs12_get_auth_safe(pfx);
+ if (asafe != NULL) {
+
+ /* decrypt safe -- only if not empty */
+ if (asafe->emptySafe != PR_TRUE) {
+ rv = sec_pkcs12_decrypt_auth_safe(asafe, pwitem, wincx);
+ if (rv == SECSuccess) {
+ safe_contents = sec_pkcs12_get_safe_contents(asafe);
+ if (safe_contents == NULL) {
+ rv = SECFailure;
+ }
+ }
+ } else {
+ safe_contents = sec_pkcs12_create_safe_contents(asafe->poolp);
+ if (safe_contents == NULL) {
+ rv = SECFailure;
+ } else {
+ safe_contents->swapUnicode = pfx->swapUnicode;
+ rv = SECSuccess;
+ }
+ }
+
+ /* get safe contents and begin import */
+ if (rv == SECSuccess) {
+ SEC_PKCS12DecoderContext *p12dcx;
+
+ p12dcx = sec_PKCS12ConvertOldSafeToNew(pfx->poolp, slot,
+ pfx->swapUnicode,
+ pwitem, wincx, safe_contents,
+ &asafe->baggage);
+ if (!p12dcx) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ if (SEC_PKCS12DecoderValidateBags(p12dcx, ncCall) != SECSuccess) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SEC_PKCS12DecoderImportBags(p12dcx);
+ }
+ }
+ }
+
+loser:
+
+ if (pfx) {
+ SEC_PKCS12DestroyPFX(pfx);
+ }
+
+ return rv;
+}
+
+PRBool
+SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength)
+{
+ int lengthLength;
+
+ PRBool valid = PR_FALSE;
+
+ if (buf == NULL) {
+ return PR_FALSE;
+ }
+
+ /* check for constructed sequence identifier tag */
+ if (*buf == (SEC_ASN1_CONSTRUCTED | SEC_ASN1_SEQUENCE)) {
+ totalLength--; /* header byte taken care of */
+ buf++;
+
+ lengthLength = (long int)SEC_ASN1LengthLength(totalLength - 1);
+ if (totalLength > 0x7f) {
+ lengthLength--;
+ *buf &= 0x7f; /* remove bit 8 indicator */
+ if ((*buf - (char)lengthLength) == 0) {
+ valid = PR_TRUE;
+ }
+ } else {
+ lengthLength--;
+ if ((*buf - (char)lengthLength) == 0) {
+ valid = PR_TRUE;
+ }
+ }
+ }
+
+ return valid;
+}
diff --git a/security/nss/lib/pkcs12/p12e.c b/security/nss/lib/pkcs12/p12e.c
new file mode 100644
index 0000000000..2b8654698b
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12e.c
@@ -0,0 +1,2087 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "p12t.h"
+#include "p12.h"
+#include "plarena.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "seccomon.h"
+#include "secport.h"
+#include "cert.h"
+#include "secpkcs5.h"
+#include "secpkcs7.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "sechash.h"
+#include "pk11func.h"
+#include "p12plcy.h"
+#include "p12local.h"
+#include "prcpucfg.h"
+
+extern const int NSS_PBE_DEFAULT_ITERATION_COUNT; /* defined in p7create.c */
+
+/*
+** This PKCS12 file encoder uses numerous nested ASN.1 and PKCS7 encoder
+** contexts. It can be difficult to keep straight. Here's a picture:
+**
+** "outer" ASN.1 encoder. The output goes to the library caller's CB.
+** "middle" PKCS7 encoder. Feeds the "outer" ASN.1 encoder.
+** "middle" ASN1 encoder. Encodes the encrypted aSafes.
+** Feeds the "middle" P7 encoder above.
+** "inner" PKCS7 encoder. Encrypts the "authenticated Safes" (aSafes)
+** Feeds the "middle" ASN.1 encoder above.
+** "inner" ASN.1 encoder. Encodes the unencrypted aSafes.
+** Feeds the "inner" P7 enocder above.
+**
+** Buffering has been added at each point where the output of an ASN.1
+** encoder feeds the input of a PKCS7 encoder.
+*/
+
+/*********************************
+ * Output buffer object, used to buffer output from ASN.1 encoder
+ * before passing data on down to the next PKCS7 encoder.
+ *********************************/
+
+#define PK12_OUTPUT_BUFFER_SIZE 8192
+
+struct sec_pkcs12OutputBufferStr {
+ SEC_PKCS7EncoderContext *p7eCx;
+ PK11Context *hmacCx;
+ unsigned int numBytes;
+ unsigned int bufBytes;
+ char buf[PK12_OUTPUT_BUFFER_SIZE];
+};
+typedef struct sec_pkcs12OutputBufferStr sec_pkcs12OutputBuffer;
+
+/*********************************
+ * Structures used in exporting the PKCS 12 blob
+ *********************************/
+
+/* A SafeInfo is used for each ContentInfo which makes up the
+ * sequence of safes in the AuthenticatedSafe portion of the
+ * PFX structure.
+ */
+struct SEC_PKCS12SafeInfoStr {
+ PLArenaPool *arena;
+
+ /* information for setting up password encryption */
+ SECItem pwitem;
+ SECOidTag algorithm;
+ PK11SymKey *encryptionKey;
+
+ /* how many items have been stored in this safe,
+ * we will skip any safe which does not contain any
+ * items
+ */
+ unsigned int itemCount;
+
+ /* the content info for the safe */
+ SEC_PKCS7ContentInfo *cinfo;
+
+ sec_PKCS12SafeContents *safe;
+};
+
+/* An opaque structure which contains information needed for exporting
+ * certificates and keys through PKCS 12.
+ */
+struct SEC_PKCS12ExportContextStr {
+ PLArenaPool *arena;
+ PK11SlotInfo *slot;
+ void *wincx;
+
+ /* integrity information */
+ PRBool integrityEnabled;
+ PRBool pwdIntegrity;
+ union {
+ struct sec_PKCS12PasswordModeInfo pwdInfo;
+ struct sec_PKCS12PublicKeyModeInfo pubkeyInfo;
+ } integrityInfo;
+
+ /* helper functions */
+ /* retrieve the password call back */
+ SECKEYGetPasswordKey pwfn;
+ void *pwfnarg;
+
+ /* safe contents bags */
+ SEC_PKCS12SafeInfo **safeInfos;
+ unsigned int safeInfoCount;
+
+ /* the sequence of safes */
+ sec_PKCS12AuthenticatedSafe authSafe;
+
+ /* information needing deletion */
+ CERTCertificate **certList;
+};
+
+/* structures for passing information to encoder callbacks when processing
+ * data through the ASN1 engine.
+ */
+struct sec_pkcs12_encoder_output {
+ SEC_PKCS12EncoderOutputCallback outputfn;
+ void *outputarg;
+};
+
+struct sec_pkcs12_hmac_and_output_info {
+ void *arg;
+ struct sec_pkcs12_encoder_output output;
+};
+
+/* An encoder context which is used for the actual encoding
+ * portion of PKCS 12.
+ */
+typedef struct sec_PKCS12EncoderContextStr {
+ PLArenaPool *arena;
+ SEC_PKCS12ExportContext *p12exp;
+
+ /* encoder information - this is set up based on whether
+ * password based or public key pased privacy is being used
+ */
+ SEC_ASN1EncoderContext *outerA1ecx;
+ union {
+ struct sec_pkcs12_hmac_and_output_info hmacAndOutputInfo;
+ struct sec_pkcs12_encoder_output encOutput;
+ } output;
+
+ /* structures for encoding of PFX and MAC */
+ sec_PKCS12PFXItem pfx;
+ sec_PKCS12MacData mac;
+
+ /* authenticated safe encoding tracking information */
+ SEC_PKCS7ContentInfo *aSafeCinfo;
+ SEC_PKCS7EncoderContext *middleP7ecx;
+ SEC_ASN1EncoderContext *middleA1ecx;
+ unsigned int currentSafe;
+
+ /* hmac context */
+ PK11Context *hmacCx;
+
+ /* output buffers */
+ sec_pkcs12OutputBuffer middleBuf;
+ sec_pkcs12OutputBuffer innerBuf;
+
+} sec_PKCS12EncoderContext;
+
+/*********************************
+ * Export setup routines
+ *********************************/
+
+/* SEC_PKCS12CreateExportContext
+ * Creates an export context and sets the unicode and password retrieval
+ * callbacks. This is the first call which must be made when exporting
+ * a PKCS 12 blob.
+ *
+ * pwfn, pwfnarg - password retrieval callback and argument. these are
+ * required for password-authentication mode.
+ */
+SEC_PKCS12ExportContext *
+SEC_PKCS12CreateExportContext(SECKEYGetPasswordKey pwfn, void *pwfnarg,
+ PK11SlotInfo *slot, void *wincx)
+{
+ PLArenaPool *arena = NULL;
+ SEC_PKCS12ExportContext *p12ctxt = NULL;
+
+ /* allocate the arena and create the context */
+ arena = PORT_NewArena(4096);
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ p12ctxt = (SEC_PKCS12ExportContext *)PORT_ArenaZAlloc(arena,
+ sizeof(SEC_PKCS12ExportContext));
+ if (!p12ctxt) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* password callback for key retrieval */
+ p12ctxt->pwfn = pwfn;
+ p12ctxt->pwfnarg = pwfnarg;
+
+ p12ctxt->integrityEnabled = PR_FALSE;
+ p12ctxt->arena = arena;
+ p12ctxt->wincx = wincx;
+ p12ctxt->slot = (slot) ? PK11_ReferenceSlot(slot) : PK11_GetInternalSlot();
+
+ return p12ctxt;
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+
+ return NULL;
+}
+
+/*
+ * Adding integrity mode
+ */
+
+/* SEC_PKCS12AddPasswordIntegrity
+ * Add password integrity to the exported data. If an integrity method
+ * has already been set, then return an error.
+ *
+ * p12ctxt - the export context
+ * pwitem - the password for integrity mode
+ * integAlg - the integrity algorithm to use for authentication.
+ */
+SECStatus
+SEC_PKCS12AddPasswordIntegrity(SEC_PKCS12ExportContext *p12ctxt,
+ SECItem *pwitem, SECOidTag integAlg)
+{
+ if (!p12ctxt || p12ctxt->integrityEnabled) {
+ return SECFailure;
+ }
+
+ /* set up integrity information */
+ p12ctxt->pwdIntegrity = PR_TRUE;
+ p12ctxt->integrityInfo.pwdInfo.password =
+ (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
+ if (!p12ctxt->integrityInfo.pwdInfo.password) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ if (SECITEM_CopyItem(p12ctxt->arena,
+ p12ctxt->integrityInfo.pwdInfo.password, pwitem) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ p12ctxt->integrityInfo.pwdInfo.algorithm = integAlg;
+ p12ctxt->integrityEnabled = PR_TRUE;
+
+ return SECSuccess;
+}
+
+/* SEC_PKCS12AddPublicKeyIntegrity
+ * Add public key integrity to the exported data. If an integrity method
+ * has already been set, then return an error. The certificate must be
+ * allowed to be used as a signing cert.
+ *
+ * p12ctxt - the export context
+ * cert - signer certificate
+ * certDb - the certificate database
+ * algorithm - signing algorithm
+ * keySize - size of the signing key (?)
+ */
+SECStatus
+SEC_PKCS12AddPublicKeyIntegrity(SEC_PKCS12ExportContext *p12ctxt,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ SECOidTag algorithm, int keySize)
+{
+ if (!p12ctxt) {
+ return SECFailure;
+ }
+
+ p12ctxt->integrityInfo.pubkeyInfo.cert = cert;
+ p12ctxt->integrityInfo.pubkeyInfo.certDb = certDb;
+ p12ctxt->integrityInfo.pubkeyInfo.algorithm = algorithm;
+ p12ctxt->integrityInfo.pubkeyInfo.keySize = keySize;
+ p12ctxt->integrityEnabled = PR_TRUE;
+
+ return SECSuccess;
+}
+
+/*
+ * Adding safes - encrypted (password/public key) or unencrypted
+ * Each of the safe creation routines return an opaque pointer which
+ * are later passed into the routines for exporting certificates and
+ * keys.
+ */
+
+/* append the newly created safeInfo to list of safeInfos in the export
+ * context.
+ */
+static SECStatus
+sec_pkcs12_append_safe_info(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *info)
+{
+ void *mark = NULL, *dummy1 = NULL, *dummy2 = NULL;
+
+ if (!p12ctxt || !info) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ /* if no safeInfos have been set, create the list, otherwise expand it. */
+ if (!p12ctxt->safeInfoCount) {
+ p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)PORT_ArenaZAlloc(p12ctxt->arena,
+ 2 * sizeof(SEC_PKCS12SafeInfo *));
+ dummy1 = p12ctxt->safeInfos;
+ p12ctxt->authSafe.encodedSafes = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena,
+ 2 * sizeof(SECItem *));
+ dummy2 = p12ctxt->authSafe.encodedSafes;
+ } else {
+ dummy1 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->safeInfos,
+ (p12ctxt->safeInfoCount + 1) * sizeof(SEC_PKCS12SafeInfo *),
+ (p12ctxt->safeInfoCount + 2) * sizeof(SEC_PKCS12SafeInfo *));
+ p12ctxt->safeInfos = (SEC_PKCS12SafeInfo **)dummy1;
+ dummy2 = PORT_ArenaGrow(p12ctxt->arena, p12ctxt->authSafe.encodedSafes,
+ (p12ctxt->authSafe.safeCount + 1) * sizeof(SECItem *),
+ (p12ctxt->authSafe.safeCount + 2) * sizeof(SECItem *));
+ p12ctxt->authSafe.encodedSafes = (SECItem **)dummy2;
+ }
+ if (!dummy1 || !dummy2) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* append the new safeInfo and null terminate the list */
+ p12ctxt->safeInfos[p12ctxt->safeInfoCount] = info;
+ p12ctxt->safeInfos[++p12ctxt->safeInfoCount] = NULL;
+ p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount] =
+ (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECItem));
+ if (!p12ctxt->authSafe.encodedSafes[p12ctxt->authSafe.safeCount]) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ p12ctxt->authSafe.encodedSafes[++p12ctxt->authSafe.safeCount] = NULL;
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return SECFailure;
+}
+
+/* SEC_PKCS12CreatePasswordPrivSafe
+ * Create a password privacy safe to store exported information in.
+ *
+ * p12ctxt - export context
+ * pwitem - password for encryption
+ * privAlg - pbe algorithm through which encryption is done.
+ */
+SEC_PKCS12SafeInfo *
+SEC_PKCS12CreatePasswordPrivSafe(SEC_PKCS12ExportContext *p12ctxt,
+ SECItem *pwitem, SECOidTag privAlg)
+{
+ SEC_PKCS12SafeInfo *safeInfo = NULL;
+ void *mark = NULL;
+ PK11SlotInfo *slot = NULL;
+ SECAlgorithmID *algId;
+ SECItem uniPwitem = { siBuffer, NULL, 0 };
+
+ if (!p12ctxt) {
+ return NULL;
+ }
+
+ /* allocate the safe info */
+ mark = PORT_ArenaMark(p12ctxt->arena);
+ safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(SEC_PKCS12SafeInfo));
+ if (!safeInfo) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return NULL;
+ }
+
+ safeInfo->itemCount = 0;
+
+ /* create the encrypted safe */
+ if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg)) {
+ SECOidTag prfAlg = SEC_OID_UNKNOWN;
+ /* if we have password integrity set, use that to set the integrity
+ * hash algorithm to set our password PRF. If we haven't set it, just
+ * let the low level code pick it */
+ if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
+ prfAlg = HASH_GetHMACOidTagByHashOidTag(
+ p12ctxt->integrityInfo.pwdInfo.algorithm);
+ }
+ safeInfo->cinfo = SEC_PKCS7CreateEncryptedDataWithPBEV2(SEC_OID_PKCS5_PBES2,
+ privAlg,
+ prfAlg,
+ 0,
+ p12ctxt->pwfn,
+ p12ctxt->pwfnarg);
+ } else {
+ safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn,
+ p12ctxt->pwfnarg);
+ }
+ if (!safeInfo->cinfo) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ safeInfo->arena = p12ctxt->arena;
+
+ if (!sec_pkcs12_encode_password(NULL, &uniPwitem, privAlg, pwitem)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if (SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* generate the encryption key */
+ slot = PK11_ReferenceSlot(p12ctxt->slot);
+ if (!slot) {
+ slot = PK11_GetInternalKeySlot();
+ if (!slot) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ }
+
+ algId = SEC_PKCS7GetEncryptionAlgorithm(safeInfo->cinfo);
+ safeInfo->encryptionKey = PK11_PBEKeyGen(slot, algId, &uniPwitem,
+ PR_FALSE, p12ctxt->wincx);
+ if (!safeInfo->encryptionKey) {
+ goto loser;
+ }
+
+ safeInfo->arena = p12ctxt->arena;
+ safeInfo->safe = NULL;
+ if (sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
+ goto loser;
+ }
+
+ if (uniPwitem.data) {
+ SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
+ }
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ return safeInfo;
+
+loser:
+ if (slot) {
+ PK11_FreeSlot(slot);
+ }
+ if (safeInfo->cinfo) {
+ SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
+ }
+
+ if (uniPwitem.data) {
+ SECITEM_ZfreeItem(&uniPwitem, PR_FALSE);
+ }
+
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return NULL;
+}
+
+/* SEC_PKCS12CreateUnencryptedSafe
+ * Creates an unencrypted safe within the export context.
+ *
+ * p12ctxt - the export context
+ */
+SEC_PKCS12SafeInfo *
+SEC_PKCS12CreateUnencryptedSafe(SEC_PKCS12ExportContext *p12ctxt)
+{
+ SEC_PKCS12SafeInfo *safeInfo = NULL;
+ void *mark = NULL;
+
+ if (!p12ctxt) {
+ return NULL;
+ }
+
+ /* create the safe info */
+ mark = PORT_ArenaMark(p12ctxt->arena);
+ safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(SEC_PKCS12SafeInfo));
+ if (!safeInfo) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ safeInfo->itemCount = 0;
+
+ /* create the safe content */
+ safeInfo->cinfo = SEC_PKCS7CreateData();
+ if (!safeInfo->cinfo) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ if (sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return safeInfo;
+
+loser:
+ if (safeInfo->cinfo) {
+ SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
+ }
+
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return NULL;
+}
+
+/* SEC_PKCS12CreatePubKeyEncryptedSafe
+ * Creates a safe which is protected by public key encryption.
+ *
+ * p12ctxt - the export context
+ * certDb - the certificate database
+ * signer - the signer's certificate
+ * recipients - the list of recipient certificates.
+ * algorithm - the encryption algorithm to use
+ * keysize - the algorithms key size (?)
+ */
+SEC_PKCS12SafeInfo *
+SEC_PKCS12CreatePubKeyEncryptedSafe(SEC_PKCS12ExportContext *p12ctxt,
+ CERTCertDBHandle *certDb,
+ CERTCertificate *signer,
+ CERTCertificate **recipients,
+ SECOidTag algorithm, int keysize)
+{
+ SEC_PKCS12SafeInfo *safeInfo = NULL;
+ void *mark = NULL;
+
+ if (!p12ctxt || !signer || !recipients || !(*recipients)) {
+ return NULL;
+ }
+
+ /* allocate the safeInfo */
+ mark = PORT_ArenaMark(p12ctxt->arena);
+ safeInfo = (SEC_PKCS12SafeInfo *)PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(SEC_PKCS12SafeInfo));
+ if (!safeInfo) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ safeInfo->itemCount = 0;
+ safeInfo->arena = p12ctxt->arena;
+
+ /* create the enveloped content info using certUsageEmailSigner currently.
+ * XXX We need to eventually use something other than certUsageEmailSigner
+ */
+ safeInfo->cinfo = SEC_PKCS7CreateEnvelopedData(signer, certUsageEmailSigner,
+ certDb, algorithm, keysize,
+ p12ctxt->pwfn, p12ctxt->pwfnarg);
+ if (!safeInfo->cinfo) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* add recipients */
+ if (recipients) {
+ unsigned int i = 0;
+ while (recipients[i] != NULL) {
+ SECStatus rv = SEC_PKCS7AddRecipient(safeInfo->cinfo, recipients[i],
+ certUsageEmailRecipient, certDb);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ i++;
+ }
+ }
+
+ if (sec_pkcs12_append_safe_info(p12ctxt, safeInfo) != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return safeInfo;
+
+loser:
+ if (safeInfo->cinfo) {
+ SEC_PKCS7DestroyContentInfo(safeInfo->cinfo);
+ safeInfo->cinfo = NULL;
+ }
+
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return NULL;
+}
+
+/*********************************
+ * Routines to handle the exporting of the keys and certificates
+ *********************************/
+
+/* creates a safe contents which safeBags will be appended to */
+sec_PKCS12SafeContents *
+sec_PKCS12CreateSafeContents(PLArenaPool *arena)
+{
+ sec_PKCS12SafeContents *safeContents;
+
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ /* create the safe contents */
+ safeContents = (sec_PKCS12SafeContents *)PORT_ArenaZAlloc(arena,
+ sizeof(sec_PKCS12SafeContents));
+ if (!safeContents) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* set up the internal contents info */
+ safeContents->safeBags = NULL;
+ safeContents->arena = arena;
+ safeContents->bagCount = 0;
+
+ return safeContents;
+
+loser:
+ return NULL;
+}
+
+/* appends a safe bag to a safeContents using the specified arena.
+ */
+SECStatus
+sec_pkcs12_append_bag_to_safe_contents(PLArenaPool *arena,
+ sec_PKCS12SafeContents *safeContents,
+ sec_PKCS12SafeBag *safeBag)
+{
+ void *mark = NULL, *dummy = NULL;
+
+ if (!arena || !safeBag || !safeContents) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(arena);
+ if (!mark) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ /* allocate space for the list, or reallocate to increase space */
+ if (!safeContents->safeBags) {
+ safeContents->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(arena,
+ (2 * sizeof(sec_PKCS12SafeBag *)));
+ dummy = safeContents->safeBags;
+ safeContents->bagCount = 0;
+ } else {
+ dummy = PORT_ArenaGrow(arena, safeContents->safeBags,
+ (safeContents->bagCount + 1) * sizeof(sec_PKCS12SafeBag *),
+ (safeContents->bagCount + 2) * sizeof(sec_PKCS12SafeBag *));
+ safeContents->safeBags = (sec_PKCS12SafeBag **)dummy;
+ }
+
+ if (!dummy) {
+ PORT_ArenaRelease(arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ /* append the bag at the end and null terminate the list */
+ safeContents->safeBags[safeContents->bagCount++] = safeBag;
+ safeContents->safeBags[safeContents->bagCount] = NULL;
+
+ PORT_ArenaUnmark(arena, mark);
+
+ return SECSuccess;
+}
+
+/* appends a safeBag to a specific safeInfo.
+ */
+SECStatus
+sec_pkcs12_append_bag(SEC_PKCS12ExportContext *p12ctxt,
+ SEC_PKCS12SafeInfo *safeInfo, sec_PKCS12SafeBag *safeBag)
+{
+ sec_PKCS12SafeContents *dest;
+ SECStatus rv = SECFailure;
+
+ if (!p12ctxt || !safeBag || !safeInfo) {
+ return SECFailure;
+ }
+
+ if (!safeInfo->safe) {
+ safeInfo->safe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
+ if (!safeInfo->safe) {
+ return SECFailure;
+ }
+ }
+
+ dest = safeInfo->safe;
+ rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena, dest, safeBag);
+ if (rv == SECSuccess) {
+ safeInfo->itemCount++;
+ }
+
+ return rv;
+}
+
+/* Creates a safeBag of the specified type, and if bagData is specified,
+ * the contents are set. The contents could be set later by the calling
+ * routine.
+ */
+sec_PKCS12SafeBag *
+sec_PKCS12CreateSafeBag(SEC_PKCS12ExportContext *p12ctxt, SECOidTag bagType,
+ void *bagData)
+{
+ sec_PKCS12SafeBag *safeBag;
+ void *mark = NULL;
+ SECStatus rv = SECSuccess;
+ SECOidData *oidData = NULL;
+
+ if (!p12ctxt) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+ if (!mark) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ safeBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(sec_PKCS12SafeBag));
+ if (!safeBag) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ /* set the bags content based upon bag type */
+ switch (bagType) {
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ safeBag->safeBagContent.pkcs8KeyBag =
+ (SECKEYPrivateKeyInfo *)bagData;
+ break;
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ safeBag->safeBagContent.certBag = (sec_PKCS12CertBag *)bagData;
+ break;
+ case SEC_OID_PKCS12_V1_CRL_BAG_ID:
+ safeBag->safeBagContent.crlBag = (sec_PKCS12CRLBag *)bagData;
+ break;
+ case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
+ safeBag->safeBagContent.secretBag = (sec_PKCS12SecretBag *)bagData;
+ break;
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ safeBag->safeBagContent.pkcs8ShroudedKeyBag =
+ (SECKEYEncryptedPrivateKeyInfo *)bagData;
+ break;
+ case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
+ safeBag->safeBagContent.safeContents =
+ (sec_PKCS12SafeContents *)bagData;
+ break;
+ default:
+ goto loser;
+ }
+
+ oidData = SECOID_FindOIDByTag(bagType);
+ if (oidData) {
+ rv = SECITEM_CopyItem(p12ctxt->arena, &safeBag->safeBagType, &oidData->oid);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ } else {
+ goto loser;
+ }
+
+ safeBag->arena = p12ctxt->arena;
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+
+ return safeBag;
+
+loser:
+ if (mark) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ }
+
+ return NULL;
+}
+
+/* Creates a new certificate bag and returns a pointer to it. If an error
+ * occurs NULL is returned.
+ */
+sec_PKCS12CertBag *
+sec_PKCS12NewCertBag(PLArenaPool *arena, SECOidTag certType)
+{
+ sec_PKCS12CertBag *certBag = NULL;
+ SECOidData *bagType = NULL;
+ SECStatus rv;
+ void *mark = NULL;
+
+ if (!arena) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(arena);
+ certBag = (sec_PKCS12CertBag *)PORT_ArenaZAlloc(arena,
+ sizeof(sec_PKCS12CertBag));
+ if (!certBag) {
+ PORT_ArenaRelease(arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ bagType = SECOID_FindOIDByTag(certType);
+ if (!bagType) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ rv = SECITEM_CopyItem(arena, &certBag->bagID, &bagType->oid);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(arena, mark);
+ return certBag;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+/* Creates a new CRL bag and returns a pointer to it. If an error
+ * occurs NULL is returned.
+ */
+sec_PKCS12CRLBag *
+sec_PKCS12NewCRLBag(PLArenaPool *arena, SECOidTag crlType)
+{
+ sec_PKCS12CRLBag *crlBag = NULL;
+ SECOidData *bagType = NULL;
+ SECStatus rv;
+ void *mark = NULL;
+
+ if (!arena) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(arena);
+ crlBag = (sec_PKCS12CRLBag *)PORT_ArenaZAlloc(arena,
+ sizeof(sec_PKCS12CRLBag));
+ if (!crlBag) {
+ PORT_ArenaRelease(arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ bagType = SECOID_FindOIDByTag(crlType);
+ if (!bagType) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ rv = SECITEM_CopyItem(arena, &crlBag->bagID, &bagType->oid);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(arena, mark);
+ return crlBag;
+
+loser:
+ PORT_ArenaRelease(arena, mark);
+ return NULL;
+}
+
+/* sec_PKCS12AddAttributeToBag
+ * adds an attribute to a safeBag. currently, the only attributes supported
+ * are those which are specified within PKCS 12.
+ *
+ * p12ctxt - the export context
+ * safeBag - the safeBag to which attributes are appended
+ * attrType - the attribute type
+ * attrData - the attribute data
+ */
+SECStatus
+sec_PKCS12AddAttributeToBag(SEC_PKCS12ExportContext *p12ctxt,
+ sec_PKCS12SafeBag *safeBag, SECOidTag attrType,
+ SECItem *attrData)
+{
+ sec_PKCS12Attribute *attribute;
+ void *mark = NULL, *dummy = NULL;
+ SECOidData *oiddata = NULL;
+ SECItem unicodeName = { siBuffer, NULL, 0 };
+ void *src = NULL;
+ unsigned int nItems = 0;
+ SECStatus rv;
+
+ PORT_Assert(p12ctxt->arena == safeBag->arena);
+ if (!safeBag || !p12ctxt || p12ctxt->arena != safeBag->arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(safeBag->arena);
+
+ /* allocate the attribute */
+ attribute = (sec_PKCS12Attribute *)PORT_ArenaZAlloc(safeBag->arena,
+ sizeof(sec_PKCS12Attribute));
+ if (!attribute) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* set up the attribute */
+ oiddata = SECOID_FindOIDByTag(attrType);
+ if (!oiddata) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if (SECITEM_CopyItem(p12ctxt->arena, &attribute->attrType, &oiddata->oid) !=
+ SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ nItems = 1;
+ switch (attrType) {
+ case SEC_OID_PKCS9_LOCAL_KEY_ID: {
+ src = attrData;
+ break;
+ }
+ case SEC_OID_PKCS9_FRIENDLY_NAME: {
+ if (!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena,
+ &unicodeName, attrData, PR_FALSE,
+ PR_FALSE, PR_TRUE)) {
+ goto loser;
+ }
+ src = &unicodeName;
+ break;
+ }
+ default:
+ goto loser;
+ }
+
+ /* append the attribute to the attribute value list */
+ attribute->attrValue = (SECItem **)PORT_ArenaZAlloc(p12ctxt->arena,
+ ((nItems + 1) * sizeof(SECItem *)));
+ if (!attribute->attrValue) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* XXX this will need to be changed if attributes requiring more than
+ * one element are ever used.
+ */
+ attribute->attrValue[0] = (SECItem *)PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(SECItem));
+ if (!attribute->attrValue[0]) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ attribute->attrValue[1] = NULL;
+
+ rv = SECITEM_CopyItem(p12ctxt->arena, attribute->attrValue[0],
+ (SECItem *)src);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* append the attribute to the safeBag attributes */
+ if (safeBag->nAttribs) {
+ dummy = PORT_ArenaGrow(p12ctxt->arena, safeBag->attribs,
+ ((safeBag->nAttribs + 1) * sizeof(sec_PKCS12Attribute *)),
+ ((safeBag->nAttribs + 2) * sizeof(sec_PKCS12Attribute *)));
+ safeBag->attribs = (sec_PKCS12Attribute **)dummy;
+ } else {
+ safeBag->attribs = (sec_PKCS12Attribute **)PORT_ArenaZAlloc(p12ctxt->arena,
+ 2 * sizeof(sec_PKCS12Attribute *));
+ dummy = safeBag->attribs;
+ }
+ if (!dummy) {
+ goto loser;
+ }
+
+ safeBag->attribs[safeBag->nAttribs] = attribute;
+ safeBag->attribs[++safeBag->nAttribs] = NULL;
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return SECSuccess;
+
+loser:
+ if (mark) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ }
+
+ return SECFailure;
+}
+
+/* SEC_PKCS12AddCert
+ * Adds a certificate to the data being exported.
+ *
+ * p12ctxt - the export context
+ * safe - the safeInfo to which the certificate is placed
+ * nestedDest - if the cert is to be placed within a nested safeContents then,
+ * this value is to be specified with the destination
+ * cert - the cert to export
+ * certDb - the certificate database handle
+ * keyId - a unique identifier to associate a certificate/key pair
+ * includeCertChain - PR_TRUE if the certificate chain is to be included.
+ */
+SECStatus
+SEC_PKCS12AddCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe,
+ void *nestedDest, CERTCertificate *cert,
+ CERTCertDBHandle *certDb, SECItem *keyId,
+ PRBool includeCertChain)
+{
+ sec_PKCS12CertBag *certBag;
+ sec_PKCS12SafeBag *safeBag;
+ void *mark;
+ SECStatus rv;
+ SECItem nick = { siBuffer, NULL, 0 };
+
+ if (!p12ctxt || !cert) {
+ return SECFailure;
+ }
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ /* allocate the cert bag */
+ certBag = sec_PKCS12NewCertBag(p12ctxt->arena,
+ SEC_OID_PKCS9_X509_CERT);
+ if (!certBag) {
+ goto loser;
+ }
+
+ if (SECITEM_CopyItem(p12ctxt->arena, &certBag->value.x509Cert,
+ &cert->derCert) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* if the cert chain is to be included, we should only be exporting
+ * the cert from our internal database.
+ */
+ if (includeCertChain) {
+ CERTCertificateList *certList = CERT_CertChainFromCert(cert,
+ certUsageSSLClient,
+ PR_TRUE);
+ unsigned int count = 0;
+ if (!certList) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* add cert chain */
+ for (count = 0; count < (unsigned int)certList->len; count++) {
+ if (SECITEM_CompareItem(&certList->certs[count], &cert->derCert) != SECEqual) {
+ CERTCertificate *tempCert;
+
+ /* decode the certificate */
+ /* XXX
+ * This was rather silly. The chain is constructed above
+ * by finding all of the CERTCertificate's in the database.
+ * Then the chain is put into a CERTCertificateList, which only
+ * contains the DER. Finally, the DER was decoded, and the
+ * decoded cert was sent recursively back to this function.
+ * Beyond being inefficent, this causes data loss (specifically,
+ * the nickname). Instead, for 3.4, we'll do a lookup by the
+ * DER, which should return the cached entry.
+ */
+ tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
+ &certList->certs[count]);
+ if (!tempCert) {
+ CERT_DestroyCertificateList(certList);
+ goto loser;
+ }
+
+ /* add the certificate */
+ if (SEC_PKCS12AddCert(p12ctxt, safe, nestedDest, tempCert,
+ certDb, NULL, PR_FALSE) != SECSuccess) {
+ CERT_DestroyCertificate(tempCert);
+ CERT_DestroyCertificateList(certList);
+ goto loser;
+ }
+ CERT_DestroyCertificate(tempCert);
+ }
+ }
+ CERT_DestroyCertificateList(certList);
+ }
+
+ /* if the certificate has a nickname, we will set the friendly name
+ * to that.
+ */
+ if (cert->nickname) {
+ if (cert->slot && !PK11_IsInternal(cert->slot)) {
+ /*
+ * The cert is coming off of an external token,
+ * let's strip the token name from the nickname
+ * and only add what comes after the colon as the
+ * nickname. -javi
+ */
+ char *delimit;
+
+ delimit = PORT_Strchr(cert->nickname, ':');
+ if (delimit == NULL) {
+ nick.data = (unsigned char *)cert->nickname;
+ nick.len = PORT_Strlen(cert->nickname);
+ } else {
+ delimit++;
+ nick.data = (unsigned char *)PORT_ArenaStrdup(p12ctxt->arena,
+ delimit);
+ nick.len = PORT_Strlen(delimit);
+ }
+ } else {
+ nick.data = (unsigned char *)cert->nickname;
+ nick.len = PORT_Strlen(cert->nickname);
+ }
+ }
+
+ safeBag = sec_PKCS12CreateSafeBag(p12ctxt, SEC_OID_PKCS12_V1_CERT_BAG_ID,
+ certBag);
+ if (!safeBag) {
+ goto loser;
+ }
+
+ /* add the friendly name and keyId attributes, if necessary */
+ if (nick.data) {
+ if (sec_PKCS12AddAttributeToBag(p12ctxt, safeBag,
+ SEC_OID_PKCS9_FRIENDLY_NAME, &nick) != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (keyId) {
+ if (sec_PKCS12AddAttributeToBag(p12ctxt, safeBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
+ keyId) != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ /* append the cert safeBag */
+ if (nestedDest) {
+ rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
+ (sec_PKCS12SafeContents *)nestedDest,
+ safeBag);
+ } else {
+ rv = sec_pkcs12_append_bag(p12ctxt, safe, safeBag);
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return SECSuccess;
+
+loser:
+ if (mark) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ }
+
+ return SECFailure;
+}
+
+/* SEC_PKCS12AddKeyForCert
+ * Extracts the key associated with a particular certificate and exports
+ * it.
+ *
+ * p12ctxt - the export context
+ * safe - the safeInfo to place the key in
+ * nestedDest - the nested safeContents to place a key
+ * cert - the certificate which the key belongs to
+ * shroudKey - encrypt the private key for export. This value should
+ * always be true. lower level code will not allow the export
+ * of unencrypted private keys.
+ * algorithm - the algorithm with which to encrypt the private key
+ * pwitem - the password to encrypt the private key with
+ * keyId - the keyID attribute
+ * nickName - the nickname attribute
+ */
+SECStatus
+SEC_PKCS12AddKeyForCert(SEC_PKCS12ExportContext *p12ctxt, SEC_PKCS12SafeInfo *safe,
+ void *nestedDest, CERTCertificate *cert,
+ PRBool shroudKey, SECOidTag algorithm, SECItem *pwitem,
+ SECItem *keyId, SECItem *nickName)
+{
+ void *mark;
+ void *keyItem;
+ SECOidTag keyType;
+ SECStatus rv = SECFailure;
+ SECItem nickname = { siBuffer, NULL, 0 }, uniPwitem = { siBuffer, NULL, 0 };
+ sec_PKCS12SafeBag *returnBag;
+
+ if (!p12ctxt || !cert || !safe) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ /* retrieve the key based upon the type that it is and
+ * specify the type of safeBag to store the key in
+ */
+ if (!shroudKey) {
+
+ /* extract the key unencrypted. this will most likely go away */
+ SECKEYPrivateKeyInfo *pki = PK11_ExportPrivateKeyInfo(cert,
+ p12ctxt->wincx);
+ if (!pki) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ return SECFailure;
+ }
+ keyItem = PORT_ArenaZAlloc(p12ctxt->arena, sizeof(SECKEYPrivateKeyInfo));
+ if (!keyItem) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ rv = SECKEY_CopyPrivateKeyInfo(p12ctxt->arena,
+ (SECKEYPrivateKeyInfo *)keyItem, pki);
+ keyType = SEC_OID_PKCS12_V1_KEY_BAG_ID;
+ SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
+ } else {
+
+ /* extract the key encrypted */
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ PK11SlotInfo *slot = NULL;
+ SECOidTag prfAlg = SEC_OID_UNKNOWN;
+
+ if (!sec_pkcs12_encode_password(p12ctxt->arena, &uniPwitem, algorithm,
+ pwitem)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* if we have password integrity set, use that to set the integrity
+ * hash algorithm to set our password PRF. If we haven't set it, just
+ * let the low level code pick it */
+ if (p12ctxt->integrityEnabled && p12ctxt->pwdIntegrity) {
+ prfAlg = HASH_GetHMACOidTagByHashOidTag(
+ p12ctxt->integrityInfo.pwdInfo.algorithm);
+ }
+
+ /* we want to make sure to take the key out of the key slot */
+ if (PK11_IsInternal(p12ctxt->slot)) {
+ slot = PK11_GetInternalKeySlot();
+ } else {
+ slot = PK11_ReferenceSlot(p12ctxt->slot);
+ }
+
+ /* passing algorithm as the pbe will force the PBE code to
+ * automatically handle the selection between using the algorithm
+ * as a the pbe algorithm, or using the algorithm as a cipher
+ * and building a pkcs5 pbe */
+ epki = PK11_ExportEncryptedPrivateKeyInfoV2(slot, algorithm,
+ SEC_OID_UNKNOWN, prfAlg,
+ &uniPwitem, cert,
+ NSS_PBE_DEFAULT_ITERATION_COUNT,
+ p12ctxt->wincx);
+ PK11_FreeSlot(slot);
+ if (!epki) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ goto loser;
+ }
+
+ keyItem = PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ if (!keyItem) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ rv = SECKEY_CopyEncryptedPrivateKeyInfo(p12ctxt->arena,
+ (SECKEYEncryptedPrivateKeyInfo *)keyItem,
+ epki);
+ keyType = SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID;
+ SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
+ }
+
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* if no nickname specified, let's see if the certificate has a
+ * nickname.
+ */
+ if (!nickName) {
+ if (cert->nickname) {
+ nickname.data = (unsigned char *)cert->nickname;
+ nickname.len = PORT_Strlen(cert->nickname);
+ nickName = &nickname;
+ }
+ }
+
+ /* create the safe bag and set any attributes */
+ returnBag = sec_PKCS12CreateSafeBag(p12ctxt, keyType, keyItem);
+ if (!returnBag) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ if (nickName) {
+ if (sec_PKCS12AddAttributeToBag(p12ctxt, returnBag,
+ SEC_OID_PKCS9_FRIENDLY_NAME, nickName) != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (keyId) {
+ if (sec_PKCS12AddAttributeToBag(p12ctxt, returnBag, SEC_OID_PKCS9_LOCAL_KEY_ID,
+ keyId) != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (nestedDest) {
+ rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
+ (sec_PKCS12SafeContents *)nestedDest,
+ returnBag);
+ } else {
+ rv = sec_pkcs12_append_bag(p12ctxt, safe, returnBag);
+ }
+
+loser:
+
+ if (rv != SECSuccess) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ } else {
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ }
+
+ return rv;
+}
+
+/* SEC_PKCS12AddCertOrChainAndKey
+ * Add a certificate and key pair to be exported.
+ *
+ * p12ctxt - the export context
+ * certSafe - the safeInfo where the cert is stored
+ * certNestedDest - the nested safeContents to store the cert
+ * keySafe - the safeInfo where the key is stored
+ * keyNestedDest - the nested safeContents to store the key
+ * shroudKey - extract the private key encrypted?
+ * pwitem - the password with which the key is encrypted
+ * algorithm - the algorithm with which the key is encrypted
+ * includeCertChain - also add certs from chain to bag.
+ */
+SECStatus
+SEC_PKCS12AddCertOrChainAndKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ void *keySafe, void *keyNestedDest,
+ PRBool shroudKey, SECItem *pwitem,
+ SECOidTag algorithm, PRBool includeCertChain)
+{
+ SECStatus rv = SECFailure;
+ SGNDigestInfo *digest = NULL;
+ void *mark = NULL;
+
+ if (!p12ctxt || !certSafe || !keySafe || !cert) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ /* generate the thumbprint of the cert to use as a keyId */
+ digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
+ if (!digest) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return SECFailure;
+ }
+
+ /* add the certificate */
+ rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo *)certSafe,
+ (SEC_PKCS12SafeInfo *)certNestedDest, cert, certDb,
+ &digest->digest, includeCertChain);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* add the key */
+ rv = SEC_PKCS12AddKeyForCert(p12ctxt, (SEC_PKCS12SafeInfo *)keySafe,
+ keyNestedDest, cert,
+ shroudKey, algorithm, pwitem,
+ &digest->digest, NULL);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ SGN_DestroyDigestInfo(digest);
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return SECSuccess;
+
+loser:
+ SGN_DestroyDigestInfo(digest);
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+
+ return SECFailure;
+}
+
+/* like SEC_PKCS12AddCertOrChainAndKey, but always adds cert chain */
+SECStatus
+SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ void *keySafe, void *keyNestedDest,
+ PRBool shroudKey, SECItem *pwItem, SECOidTag algorithm)
+{
+ return SEC_PKCS12AddCertOrChainAndKey(p12ctxt, certSafe, certNestedDest,
+ cert, certDb, keySafe, keyNestedDest, shroudKey, pwItem,
+ algorithm, PR_TRUE);
+}
+
+/* SEC_PKCS12CreateNestedSafeContents
+ * Allows nesting of safe contents to be implemented. No limit imposed on
+ * depth.
+ *
+ * p12ctxt - the export context
+ * baseSafe - the base safeInfo
+ * nestedDest - a parent safeContents (?)
+ */
+void *
+SEC_PKCS12CreateNestedSafeContents(SEC_PKCS12ExportContext *p12ctxt,
+ void *baseSafe, void *nestedDest)
+{
+ sec_PKCS12SafeContents *newSafe;
+ sec_PKCS12SafeBag *safeContentsBag;
+ void *mark;
+ SECStatus rv;
+
+ if (!p12ctxt || !baseSafe) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ newSafe = sec_PKCS12CreateSafeContents(p12ctxt->arena);
+ if (!newSafe) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ /* create the safeContents safeBag */
+ safeContentsBag = sec_PKCS12CreateSafeBag(p12ctxt,
+ SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID,
+ newSafe);
+ if (!safeContentsBag) {
+ goto loser;
+ }
+
+ /* append the safeContents to the appropriate area */
+ if (nestedDest) {
+ rv = sec_pkcs12_append_bag_to_safe_contents(p12ctxt->arena,
+ (sec_PKCS12SafeContents *)nestedDest,
+ safeContentsBag);
+ } else {
+ rv = sec_pkcs12_append_bag(p12ctxt, (SEC_PKCS12SafeInfo *)baseSafe,
+ safeContentsBag);
+ }
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return newSafe;
+
+loser:
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ return NULL;
+}
+
+/*********************************
+ * Encoding routines
+ *********************************/
+
+/* Clean up the resources allocated by a sec_PKCS12EncoderContext. */
+static void
+sec_pkcs12_encoder_destroy_context(sec_PKCS12EncoderContext *p12enc)
+{
+ if (p12enc) {
+ if (p12enc->outerA1ecx) {
+ SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
+ p12enc->outerA1ecx = NULL;
+ }
+ if (p12enc->aSafeCinfo) {
+ SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
+ p12enc->aSafeCinfo = NULL;
+ }
+ if (p12enc->middleP7ecx) {
+ SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12enc->p12exp->pwfn,
+ p12enc->p12exp->pwfnarg);
+ p12enc->middleP7ecx = NULL;
+ }
+ if (p12enc->middleA1ecx) {
+ SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
+ p12enc->middleA1ecx = NULL;
+ }
+ if (p12enc->hmacCx) {
+ PK11_DestroyContext(p12enc->hmacCx, PR_TRUE);
+ p12enc->hmacCx = NULL;
+ }
+ }
+}
+
+/* set up the encoder context based on information in the export context
+ * and return the newly allocated enocoder context. A return of NULL
+ * indicates an error occurred.
+ */
+static sec_PKCS12EncoderContext *
+sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
+{
+ sec_PKCS12EncoderContext *p12enc = NULL;
+ unsigned int i, nonEmptyCnt;
+ SECStatus rv;
+ SECItem ignore = { 0 };
+ void *mark;
+ SECItem *salt = NULL;
+ SECItem *params = NULL;
+
+ if (!p12exp || !p12exp->safeInfos) {
+ return NULL;
+ }
+
+ /* check for any empty safes and skip them */
+ i = nonEmptyCnt = 0;
+ while (p12exp->safeInfos[i]) {
+ if (p12exp->safeInfos[i]->itemCount) {
+ nonEmptyCnt++;
+ }
+ i++;
+ }
+ if (nonEmptyCnt == 0) {
+ return NULL;
+ }
+ p12exp->authSafe.encodedSafes[nonEmptyCnt] = NULL;
+
+ /* allocate the encoder context */
+ mark = PORT_ArenaMark(p12exp->arena);
+ p12enc = PORT_ArenaZNew(p12exp->arena, sec_PKCS12EncoderContext);
+ if (!p12enc) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ p12enc->arena = p12exp->arena;
+ p12enc->p12exp = p12exp;
+
+ /* set up the PFX version and information */
+ PORT_Memset(&p12enc->pfx, 0, sizeof(sec_PKCS12PFXItem));
+ if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->pfx.version),
+ SEC_PKCS12_VERSION)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* set up the authenticated safe content info based on the
+ * type of integrity being used. this should be changed to
+ * enforce integrity mode, but will not be implemented until
+ * it is confirmed that integrity must be in place
+ */
+ if (p12exp->integrityEnabled && !p12exp->pwdIntegrity) {
+ /* create public key integrity mode */
+ p12enc->aSafeCinfo = SEC_PKCS7CreateSignedData(
+ p12exp->integrityInfo.pubkeyInfo.cert,
+ certUsageEmailSigner,
+ p12exp->integrityInfo.pubkeyInfo.certDb,
+ p12exp->integrityInfo.pubkeyInfo.algorithm,
+ NULL,
+ p12exp->pwfn,
+ p12exp->pwfnarg);
+ if (!p12enc->aSafeCinfo) {
+ goto loser;
+ }
+ if (SEC_PKCS7IncludeCertChain(p12enc->aSafeCinfo, NULL) != SECSuccess) {
+ goto loser;
+ }
+ PORT_CheckSuccess(SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo));
+ } else {
+ p12enc->aSafeCinfo = SEC_PKCS7CreateData();
+
+ /* init password pased integrity mode */
+ if (p12exp->integrityEnabled) {
+ SECItem pwd = { siBuffer, NULL, 0 };
+ PK11SymKey *symKey;
+ CK_MECHANISM_TYPE integrityMechType;
+ CK_MECHANISM_TYPE hmacMechType;
+ salt = sec_pkcs12_generate_salt();
+
+ /* zero out macData and set values */
+ PORT_Memset(&p12enc->mac, 0, sizeof(sec_PKCS12MacData));
+
+ if (!salt) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if (SECITEM_CopyItem(p12exp->arena, &(p12enc->mac.macSalt), salt) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if (!SEC_ASN1EncodeInteger(p12exp->arena, &(p12enc->mac.iter),
+ NSS_PBE_DEFAULT_ITERATION_COUNT)) {
+ goto loser;
+ }
+
+ /* generate HMAC key */
+ if (!sec_pkcs12_convert_item_to_unicode(NULL, &pwd,
+ p12exp->integrityInfo.pwdInfo.password, PR_TRUE,
+ PR_TRUE, PR_TRUE)) {
+ goto loser;
+ }
+ /*
+ * This code only works with PKCS #12 Mac using PKCS #5 v1
+ * PBA keygens. PKCS #5 v2 support will require a change to
+ * the PKCS #12 spec.
+ */
+ params = PK11_CreatePBEParams(salt, &pwd,
+ NSS_PBE_DEFAULT_ITERATION_COUNT);
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ salt = NULL;
+ SECITEM_ZfreeItem(&pwd, PR_FALSE);
+
+ /* get the PBA Mechanism to generate the key */
+ integrityMechType = sec_pkcs12_algtag_to_keygen_mech(
+ p12exp->integrityInfo.pwdInfo.algorithm);
+ if (integrityMechType == CKM_INVALID_MECHANISM) {
+ goto loser;
+ }
+
+ /* generate the key */
+ symKey = PK11_KeyGen(NULL, integrityMechType, params, 20, NULL);
+ PK11_DestroyPBEParams(params);
+ if (!symKey) {
+ goto loser;
+ }
+
+ /* initialize HMAC */
+ /* Get the HMAC mechanism from the hash OID */
+ hmacMechType = sec_pkcs12_algtag_to_mech(
+ p12exp->integrityInfo.pwdInfo.algorithm);
+
+ p12enc->hmacCx = PK11_CreateContextBySymKey(hmacMechType,
+ CKA_SIGN, symKey, &ignore);
+
+ PK11_FreeSymKey(symKey);
+ if (!p12enc->hmacCx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ rv = PK11_DigestBegin(p12enc->hmacCx);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+ }
+
+ if (!p12enc->aSafeCinfo) {
+ goto loser;
+ }
+
+ PORT_ArenaUnmark(p12exp->arena, mark);
+
+ return p12enc;
+
+loser:
+ sec_pkcs12_encoder_destroy_context(p12enc);
+ if (p12exp->arena != NULL)
+ PORT_ArenaRelease(p12exp->arena, mark);
+ if (salt) {
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ }
+ if (params) {
+ PK11_DestroyPBEParams(params);
+ }
+
+ return NULL;
+}
+
+/* The outermost ASN.1 encoder calls this function for output.
+** This function calls back to the library caller's output routine,
+** which typically writes to a PKCS12 file.
+ */
+static void
+sec_P12A1OutputCB_Outer(void *arg, const char *buf, unsigned long len,
+ int depth, SEC_ASN1EncodingPart data_kind)
+{
+ struct sec_pkcs12_encoder_output *output;
+
+ output = (struct sec_pkcs12_encoder_output *)arg;
+ (*output->outputfn)(output->outputarg, buf, len);
+}
+
+/* The "middle" and "inner" ASN.1 encoders call this function to output.
+** This function does HMACing, if appropriate, and then buffers the data.
+** The buffered data is eventually passed down to the underlying PKCS7 encoder.
+ */
+static void
+sec_P12A1OutputCB_HmacP7Update(void *arg, const char *buf,
+ unsigned long len,
+ int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ sec_pkcs12OutputBuffer *bufcx = (sec_pkcs12OutputBuffer *)arg;
+
+ if (!buf || !len)
+ return;
+
+ if (bufcx->hmacCx) {
+ PK11_DigestOp(bufcx->hmacCx, (unsigned char *)buf, len);
+ }
+
+ /* buffer */
+ if (bufcx->numBytes > 0) {
+ int toCopy;
+ if (len + bufcx->numBytes <= bufcx->bufBytes) {
+ memcpy(bufcx->buf + bufcx->numBytes, buf, len);
+ bufcx->numBytes += len;
+ if (bufcx->numBytes < bufcx->bufBytes)
+ return;
+ SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
+ bufcx->numBytes = 0;
+ return;
+ }
+ toCopy = bufcx->bufBytes - bufcx->numBytes;
+ memcpy(bufcx->buf + bufcx->numBytes, buf, toCopy);
+ SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->bufBytes);
+ bufcx->numBytes = 0;
+ len -= toCopy;
+ buf += toCopy;
+ }
+ /* buffer is presently empty */
+ if (len >= bufcx->bufBytes) {
+ /* Just pass it through */
+ SEC_PKCS7EncoderUpdate(bufcx->p7eCx, buf, len);
+ } else {
+ /* copy it all into the buffer, and return */
+ memcpy(bufcx->buf, buf, len);
+ bufcx->numBytes = len;
+ }
+}
+
+void
+sec_FlushPkcs12OutputBuffer(sec_pkcs12OutputBuffer *bufcx)
+{
+ if (bufcx->numBytes > 0) {
+ SEC_PKCS7EncoderUpdate(bufcx->p7eCx, bufcx->buf, bufcx->numBytes);
+ bufcx->numBytes = 0;
+ }
+}
+
+/* Feeds the output of a PKCS7 encoder into the next outward ASN.1 encoder.
+** This function is used by both the inner and middle PCS7 encoders.
+*/
+static void
+sec_P12P7OutputCB_CallA1Update(void *arg, const char *buf, unsigned long len)
+{
+ SEC_ASN1EncoderContext *cx = (SEC_ASN1EncoderContext *)arg;
+
+ if (!buf || !len)
+ return;
+
+ SEC_ASN1EncoderUpdate(cx, buf, len);
+}
+
+/* this function encodes content infos which are part of the
+ * sequence of content infos labeled AuthenticatedSafes
+ */
+static SECStatus
+sec_pkcs12_encoder_asafe_process(sec_PKCS12EncoderContext *p12ecx)
+{
+ SEC_PKCS7EncoderContext *innerP7ecx;
+ SEC_PKCS7ContentInfo *cinfo;
+ PK11SymKey *bulkKey = NULL;
+ SEC_ASN1EncoderContext *innerA1ecx = NULL;
+ SECStatus rv = SECSuccess;
+
+ if (p12ecx->currentSafe < p12ecx->p12exp->authSafe.safeCount) {
+ SEC_PKCS12SafeInfo *safeInfo;
+ SECOidTag cinfoType;
+
+ safeInfo = p12ecx->p12exp->safeInfos[p12ecx->currentSafe];
+
+ /* skip empty safes */
+ if (safeInfo->itemCount == 0) {
+ return SECSuccess;
+ }
+
+ cinfo = safeInfo->cinfo;
+ cinfoType = SEC_PKCS7ContentType(cinfo);
+
+ /* determine the safe type and set the appropriate argument */
+ switch (cinfoType) {
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ bulkKey = safeInfo->encryptionKey;
+ PK11_SetSymKeyUserData(bulkKey, &safeInfo->pwitem, NULL);
+ break;
+ default:
+ return SECFailure;
+ }
+
+ /* start the PKCS7 encoder */
+ innerP7ecx = SEC_PKCS7EncoderStart(cinfo,
+ sec_P12P7OutputCB_CallA1Update,
+ p12ecx->middleA1ecx, bulkKey);
+ if (!innerP7ecx) {
+ goto loser;
+ }
+
+ /* encode safe contents */
+ p12ecx->innerBuf.p7eCx = innerP7ecx;
+ p12ecx->innerBuf.hmacCx = NULL;
+ p12ecx->innerBuf.numBytes = 0;
+ p12ecx->innerBuf.bufBytes = sizeof p12ecx->innerBuf.buf;
+
+ innerA1ecx = SEC_ASN1EncoderStart(safeInfo->safe,
+ sec_PKCS12SafeContentsTemplate,
+ sec_P12A1OutputCB_HmacP7Update,
+ &p12ecx->innerBuf);
+ if (!innerA1ecx) {
+ goto loser;
+ }
+ rv = SEC_ASN1EncoderUpdate(innerA1ecx, NULL, 0);
+ SEC_ASN1EncoderFinish(innerA1ecx);
+ sec_FlushPkcs12OutputBuffer(&p12ecx->innerBuf);
+ innerA1ecx = NULL;
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* finish up safe content info */
+ rv = SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn,
+ p12ecx->p12exp->pwfnarg);
+ }
+ memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
+ return SECSuccess;
+
+loser:
+ if (innerP7ecx) {
+ SEC_PKCS7EncoderFinish(innerP7ecx, p12ecx->p12exp->pwfn,
+ p12ecx->p12exp->pwfnarg);
+ }
+
+ if (innerA1ecx) {
+ SEC_ASN1EncoderFinish(innerA1ecx);
+ }
+ memset(&p12ecx->innerBuf, 0, sizeof p12ecx->innerBuf);
+ return SECFailure;
+}
+
+/* finish the HMAC and encode the macData so that it can be
+ * encoded.
+ */
+static SECStatus
+sec_Pkcs12FinishMac(sec_PKCS12EncoderContext *p12ecx)
+{
+ unsigned char hmacData[HASH_LENGTH_MAX];
+ unsigned int hmacLen;
+ SECStatus rv;
+ SGNDigestInfo *di = NULL;
+ void *dummy;
+
+ if (!p12ecx) {
+ return SECFailure;
+ }
+
+ /* make sure we are using password integrity mode */
+ if (!p12ecx->p12exp->integrityEnabled) {
+ return SECSuccess;
+ }
+
+ if (!p12ecx->p12exp->pwdIntegrity) {
+ return SECSuccess;
+ }
+
+ /* finish the hmac */
+
+ rv = PK11_DigestFinal(p12ecx->hmacCx, hmacData, &hmacLen, HASH_LENGTH_MAX);
+
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* create the digest info */
+ di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm,
+ hmacData, hmacLen);
+ if (!di) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SGN_CopyDigestInfo(p12ecx->arena, &p12ecx->mac.safeMac, di);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* encode the mac data */
+ dummy = SEC_ASN1EncodeItem(p12ecx->arena, &p12ecx->pfx.encodedMacData,
+ &p12ecx->mac, sec_PKCS12MacDataTemplate);
+ if (!dummy) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ }
+
+loser:
+ if (di) {
+ SGN_DestroyDigestInfo(di);
+ }
+ PK11_DestroyContext(p12ecx->hmacCx, PR_TRUE);
+ p12ecx->hmacCx = NULL;
+ PORT_Memset(hmacData, 0, hmacLen);
+
+ return rv;
+}
+
+/* pfx notify function for ASN1 encoder.
+ * We want to stop encoding once we reach the authenticated safe.
+ * At that point, the encoder will be updated via streaming
+ * as the authenticated safe is encoded.
+ */
+static void
+sec_pkcs12_encoder_pfx_notify(void *arg, PRBool before, void *dest, int real_depth)
+{
+ sec_PKCS12EncoderContext *p12ecx;
+
+ if (!before) {
+ return;
+ }
+
+ /* look for authenticated safe */
+ p12ecx = (sec_PKCS12EncoderContext *)arg;
+ if (dest != &p12ecx->pfx.encodedAuthSafe) {
+ return;
+ }
+
+ SEC_ASN1EncoderSetTakeFromBuf(p12ecx->outerA1ecx);
+ SEC_ASN1EncoderSetStreaming(p12ecx->outerA1ecx);
+ SEC_ASN1EncoderClearNotifyProc(p12ecx->outerA1ecx);
+}
+
+/* SEC_PKCS12Encode
+ * Encodes the PFX item and returns it to the output function, via
+ * callback. the output function must be capable of multiple updates.
+ *
+ * p12exp - the export context
+ * output - the output function callback, will be called more than once,
+ * must be able to accept streaming data.
+ * outputarg - argument for the output callback.
+ */
+SECStatus
+SEC_PKCS12Encode(SEC_PKCS12ExportContext *p12exp,
+ SEC_PKCS12EncoderOutputCallback output, void *outputarg)
+{
+ sec_PKCS12EncoderContext *p12enc;
+ struct sec_pkcs12_encoder_output outInfo;
+ SECStatus rv;
+
+ if (!p12exp || !output) {
+ return SECFailure;
+ }
+
+ /* get the encoder context */
+ p12enc = sec_pkcs12_encoder_start_context(p12exp);
+ if (!p12enc) {
+ return SECFailure;
+ }
+
+ outInfo.outputfn = output;
+ outInfo.outputarg = outputarg;
+
+ /* set up PFX encoder, the "outer" encoder. Set it for streaming */
+ p12enc->outerA1ecx = SEC_ASN1EncoderStart(&p12enc->pfx,
+ sec_PKCS12PFXItemTemplate,
+ sec_P12A1OutputCB_Outer,
+ &outInfo);
+ if (!p12enc->outerA1ecx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+ SEC_ASN1EncoderSetStreaming(p12enc->outerA1ecx);
+ SEC_ASN1EncoderSetNotifyProc(p12enc->outerA1ecx,
+ sec_pkcs12_encoder_pfx_notify, p12enc);
+ rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
+ if (rv != SECSuccess) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */
+ p12enc->middleP7ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo,
+ sec_P12P7OutputCB_CallA1Update,
+ p12enc->outerA1ecx, NULL);
+ if (!p12enc->middleP7ecx) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* encode asafe */
+ p12enc->middleBuf.p7eCx = p12enc->middleP7ecx;
+ p12enc->middleBuf.hmacCx = NULL;
+ p12enc->middleBuf.numBytes = 0;
+ p12enc->middleBuf.bufBytes = sizeof p12enc->middleBuf.buf;
+
+ /* Setup the "inner ASN.1 encoder for Authenticated Safes. */
+ if (p12enc->p12exp->integrityEnabled &&
+ p12enc->p12exp->pwdIntegrity) {
+ p12enc->middleBuf.hmacCx = p12enc->hmacCx;
+ }
+ p12enc->middleA1ecx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
+ sec_PKCS12AuthenticatedSafeTemplate,
+ sec_P12A1OutputCB_HmacP7Update,
+ &p12enc->middleBuf);
+ if (!p12enc->middleA1ecx) {
+ rv = SECFailure;
+ goto loser;
+ }
+ SEC_ASN1EncoderSetStreaming(p12enc->middleA1ecx);
+ SEC_ASN1EncoderSetTakeFromBuf(p12enc->middleA1ecx);
+
+ /* encode each of the safes */
+ while (p12enc->currentSafe != p12enc->p12exp->safeInfoCount) {
+ sec_pkcs12_encoder_asafe_process(p12enc);
+ p12enc->currentSafe++;
+ }
+ SEC_ASN1EncoderClearTakeFromBuf(p12enc->middleA1ecx);
+ SEC_ASN1EncoderClearStreaming(p12enc->middleA1ecx);
+ SEC_ASN1EncoderUpdate(p12enc->middleA1ecx, NULL, 0);
+ SEC_ASN1EncoderFinish(p12enc->middleA1ecx);
+ p12enc->middleA1ecx = NULL;
+
+ sec_FlushPkcs12OutputBuffer(&p12enc->middleBuf);
+
+ /* finish the encoding of the authenticated safes */
+ rv = SEC_PKCS7EncoderFinish(p12enc->middleP7ecx, p12exp->pwfn,
+ p12exp->pwfnarg);
+ p12enc->middleP7ecx = NULL;
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ SEC_ASN1EncoderClearTakeFromBuf(p12enc->outerA1ecx);
+ SEC_ASN1EncoderClearStreaming(p12enc->outerA1ecx);
+
+ /* update the mac, if necessary */
+ rv = sec_Pkcs12FinishMac(p12enc);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* finish encoding the pfx */
+ rv = SEC_ASN1EncoderUpdate(p12enc->outerA1ecx, NULL, 0);
+
+ SEC_ASN1EncoderFinish(p12enc->outerA1ecx);
+ p12enc->outerA1ecx = NULL;
+
+loser:
+ sec_pkcs12_encoder_destroy_context(p12enc);
+ return rv;
+}
+
+void
+SEC_PKCS12DestroyExportContext(SEC_PKCS12ExportContext *p12ecx)
+{
+ int i = 0;
+
+ if (!p12ecx) {
+ return;
+ }
+
+ if (p12ecx->safeInfos) {
+ i = 0;
+ while (p12ecx->safeInfos[i] != NULL) {
+ if (p12ecx->safeInfos[i]->encryptionKey) {
+ PK11_FreeSymKey(p12ecx->safeInfos[i]->encryptionKey);
+ }
+ if (p12ecx->safeInfos[i]->cinfo) {
+ SEC_PKCS7DestroyContentInfo(p12ecx->safeInfos[i]->cinfo);
+ }
+ i++;
+ }
+ }
+
+ PK11_FreeSlot(p12ecx->slot);
+
+ PORT_FreeArena(p12ecx->arena, PR_TRUE);
+}
diff --git a/security/nss/lib/pkcs12/p12exp.c b/security/nss/lib/pkcs12/p12exp.c
new file mode 100644
index 0000000000..b9cb076566
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12exp.c
@@ -0,0 +1,1374 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "plarena.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "seccomon.h"
+#include "secport.h"
+#include "cert.h"
+#include "pkcs12.h"
+#include "p12local.h"
+#include "secpkcs7.h"
+#include "secasn1.h"
+#include "secerr.h"
+#include "p12plcy.h"
+
+/* release the memory taken up by the list of nicknames */
+static void
+sec_pkcs12_destroy_nickname_list(SECItem **nicknames)
+{
+ int i = 0;
+
+ if (nicknames == NULL) {
+ return;
+ }
+
+ while (nicknames[i] != NULL) {
+ SECITEM_FreeItem(nicknames[i], PR_FALSE);
+ i++;
+ }
+
+ PORT_Free(nicknames);
+}
+
+/* release the memory taken up by the list of certificates */
+static void
+sec_pkcs12_destroy_certificate_list(CERTCertificate **ref_certs)
+{
+ int i = 0;
+
+ if (ref_certs == NULL) {
+ return;
+ }
+
+ while (ref_certs[i] != NULL) {
+ CERT_DestroyCertificate(ref_certs[i]);
+ i++;
+ }
+}
+
+static void
+sec_pkcs12_destroy_cinfos_for_cert_bags(SEC_PKCS12CertAndCRLBag *certBag)
+{
+ int j = 0;
+ j = 0;
+ while (certBag->certAndCRLs[j] != NULL) {
+ SECOidTag certType = SECOID_FindOIDTag(&certBag->certAndCRLs[j]->BagID);
+ if (certType == SEC_OID_PKCS12_X509_CERT_CRL_BAG) {
+ SEC_PKCS12X509CertCRL *x509;
+ x509 = certBag->certAndCRLs[j]->value.x509;
+ SEC_PKCS7DestroyContentInfo(&x509->certOrCRL);
+ }
+ j++;
+ }
+}
+
+/* destroy all content infos since they were not allocated in common
+ * pool
+ */
+static void
+sec_pkcs12_destroy_cert_content_infos(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ int i, j;
+
+ if ((safe != NULL) && (safe->contents != NULL)) {
+ i = 0;
+ while (safe->contents[i] != NULL) {
+ SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
+ if (bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
+ SEC_PKCS12CertAndCRLBag *certBag;
+ certBag = safe->contents[i]->safeContent.certAndCRLBag;
+ sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
+ }
+ i++;
+ }
+ }
+
+ if ((baggage != NULL) && (baggage->bags != NULL)) {
+ i = 0;
+ while (baggage->bags[i] != NULL) {
+ if (baggage->bags[i]->unencSecrets != NULL) {
+ j = 0;
+ while (baggage->bags[i]->unencSecrets[j] != NULL) {
+ SECOidTag bagType;
+ bagType = SECOID_FindOIDTag(&baggage->bags[i]->unencSecrets[j]->safeBagType);
+ if (bagType == SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID) {
+ SEC_PKCS12CertAndCRLBag *certBag;
+ certBag = baggage->bags[i]->unencSecrets[j]->safeContent.certAndCRLBag;
+ sec_pkcs12_destroy_cinfos_for_cert_bags(certBag);
+ }
+ j++;
+ }
+ }
+ i++;
+ }
+ }
+}
+
+/* convert the nickname list from a NULL termincated Char list
+ * to a NULL terminated SECItem list
+ */
+static SECItem **
+sec_pkcs12_convert_nickname_list(char **nicknames)
+{
+ SECItem **nicks;
+ int i, j;
+ PRBool error = PR_FALSE;
+
+ if (nicknames == NULL) {
+ return NULL;
+ }
+
+ i = j = 0;
+ while (nicknames[i] != NULL) {
+ i++;
+ }
+
+ /* allocate the space and copy the data */
+ nicks = (SECItem **)PORT_ZAlloc(sizeof(SECItem *) * (i + 1));
+ if (nicks != NULL) {
+ for (j = 0; ((j < i) && (error == PR_FALSE)); j++) {
+ nicks[j] = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (nicks[j] != NULL) {
+ nicks[j]->data =
+ (unsigned char *)PORT_ZAlloc(PORT_Strlen(nicknames[j]) + 1);
+ if (nicks[j]->data != NULL) {
+ nicks[j]->len = PORT_Strlen(nicknames[j]);
+ PORT_Memcpy(nicks[j]->data, nicknames[j], nicks[j]->len);
+ nicks[j]->data[nicks[j]->len] = 0;
+ } else {
+ error = PR_TRUE;
+ }
+ } else {
+ error = PR_TRUE;
+ }
+ }
+ }
+
+ if (error == PR_TRUE) {
+ for (i = 0; i < j; i++) {
+ SECITEM_FreeItem(nicks[i], PR_TRUE);
+ }
+ PORT_Free(nicks);
+ nicks = NULL;
+ }
+
+ return nicks;
+}
+
+/* package the certificate add_cert into PKCS12 structures,
+ * retrieve the certificate chain for the cert and return
+ * the packaged contents.
+ * poolp -- common memory pool;
+ * add_cert -- certificate to package up
+ * nickname for the certificate
+ * a return of NULL indicates an error
+ */
+static SEC_PKCS12CertAndCRL *
+sec_pkcs12_get_cert(PLArenaPool *poolp,
+ CERTCertificate *add_cert,
+ SECItem *nickname)
+{
+ SEC_PKCS12CertAndCRL *cert;
+ SEC_PKCS7ContentInfo *cinfo;
+ SGNDigestInfo *t_di;
+ void *mark;
+ SECStatus rv;
+
+ if ((poolp == NULL) || (add_cert == NULL) || (nickname == NULL)) {
+ return NULL;
+ }
+ mark = PORT_ArenaMark(poolp);
+
+ cert = sec_pkcs12_new_cert_crl(poolp, SEC_OID_PKCS12_X509_CERT_CRL_BAG);
+ if (cert != NULL) {
+
+ /* copy the nickname */
+ rv = SECITEM_CopyItem(poolp, &cert->nickname, nickname);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ cert = NULL;
+ } else {
+
+ /* package the certificate and cert chain into a NULL signer
+ * PKCS 7 SignedData content Info and prepare it for encoding
+ * since we cannot use DER_ANY_TEMPLATE
+ */
+ cinfo = SEC_PKCS7CreateCertsOnly(add_cert, PR_TRUE, NULL);
+ rv = SEC_PKCS7PrepareForEncode(cinfo, NULL, NULL, NULL);
+
+ /* thumbprint the certificate */
+ if ((cinfo != NULL) && (rv == SECSuccess)) {
+ PORT_Memcpy(&cert->value.x509->certOrCRL, cinfo, sizeof(*cinfo));
+ t_di = sec_pkcs12_compute_thumbprint(&add_cert->derCert);
+ if (t_di != NULL) {
+ /* test */
+ rv = SGN_CopyDigestInfo(poolp, &cert->value.x509->thumbprint,
+ t_di);
+ if (rv != SECSuccess) {
+ cert = NULL;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ SGN_DestroyDigestInfo(t_di);
+ } else
+ cert = NULL;
+ }
+ }
+ }
+
+ if (cert == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ } else {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return cert;
+}
+
+/* package the private key associated with the certificate and
+ * return the appropriate PKCS 12 structure
+ * poolp common memory pool
+ * nickname key nickname
+ * cert -- cert to look up
+ * wincx -- window handle
+ * an error is indicated by a return of NULL
+ */
+static SEC_PKCS12PrivateKey *
+sec_pkcs12_get_private_key(PLArenaPool *poolp,
+ SECItem *nickname,
+ CERTCertificate *cert,
+ void *wincx)
+{
+ SECKEYPrivateKeyInfo *pki;
+ SEC_PKCS12PrivateKey *pk;
+ SECStatus rv;
+ void *mark;
+
+ if ((poolp == NULL) || (nickname == NULL)) {
+ return NULL;
+ }
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* retrieve key from the data base */
+ pki = PK11_ExportPrivateKeyInfo(nickname, cert, wincx);
+ if (pki == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ return NULL;
+ }
+
+ pk = (SEC_PKCS12PrivateKey *)PORT_ArenaZAlloc(poolp,
+ sizeof(SEC_PKCS12PrivateKey));
+ if (pk != NULL) {
+ rv = sec_pkcs12_init_pvk_data(poolp, &pk->pvkData);
+
+ if (rv == SECSuccess) {
+ /* copy the key into poolp memory space */
+ rv = SECKEY_CopyPrivateKeyInfo(poolp, &pk->pkcs8data, pki);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem(poolp, &pk->pvkData.nickname, nickname);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ pk = NULL;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+
+ /* destroy private key, zeroing out data */
+ SECKEY_DestroyPrivateKeyInfo(pki, PR_TRUE);
+ if (pk == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ } else {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return pk;
+}
+
+/* get a shrouded key item associated with a certificate
+ * return the appropriate PKCS 12 structure
+ * poolp common memory pool
+ * nickname key nickname
+ * cert -- cert to look up
+ * wincx -- window handle
+ * an error is indicated by a return of NULL
+ */
+static SEC_PKCS12ESPVKItem *
+sec_pkcs12_get_shrouded_key(PLArenaPool *poolp,
+ SECItem *nickname,
+ CERTCertificate *cert,
+ SECOidTag algorithm,
+ SECItem *pwitem,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ SECKEYEncryptedPrivateKeyInfo *epki;
+ SEC_PKCS12ESPVKItem *pk;
+ void *mark;
+ SECStatus rv;
+ PK11SlotInfo *slot = NULL;
+ PRBool swapUnicodeBytes = PR_FALSE;
+
+#ifdef IS_LITTLE_ENDIAN
+ swapUnicodeBytes = PR_TRUE;
+#endif
+
+ if ((poolp == NULL) || (nickname == NULL))
+ return NULL;
+
+ mark = PORT_ArenaMark(poolp);
+
+ /* use internal key slot */
+ slot = PK11_GetInternalKeySlot();
+
+ /* retrieve encrypted prviate key */
+ epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm, pwitem,
+ nickname, cert, 1, 0, NULL);
+ PK11_FreeSlot(slot);
+ if (epki == NULL) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ PORT_ArenaRelease(poolp, mark);
+ return NULL;
+ }
+
+ /* create a private key and store the data into the poolp memory space */
+ pk = sec_pkcs12_create_espvk(poolp, SEC_OID_PKCS12_PKCS8_KEY_SHROUDING);
+ if (pk != NULL) {
+ rv = sec_pkcs12_init_pvk_data(poolp, &pk->espvkData);
+ rv = SECITEM_CopyItem(poolp, &pk->espvkData.nickname, nickname);
+ pk->espvkCipherText.pkcs8KeyShroud =
+ (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(poolp,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ if ((pk->espvkCipherText.pkcs8KeyShroud != NULL) && (rv == SECSuccess)) {
+ rv = SECKEY_CopyEncryptedPrivateKeyInfo(poolp,
+ pk->espvkCipherText.pkcs8KeyShroud, epki);
+ if (rv == SECSuccess) {
+ rv = (*unicodeFn)(poolp, &pk->espvkData.uniNickName, nickname,
+ PR_TRUE, swapUnicodeBytes);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ pk = NULL;
+ }
+ }
+
+ SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
+ if (pk == NULL) {
+ PORT_ArenaRelease(poolp, mark);
+ } else {
+ PORT_ArenaUnmark(poolp, mark);
+ }
+
+ return pk;
+}
+
+/* add a thumbprint to a private key associated certs list
+ * pvk is the area where the list is stored
+ * thumb is the thumbprint to copy
+ * a return of SECFailure indicates an error
+ */
+static SECStatus
+sec_pkcs12_add_thumbprint(SEC_PKCS12PVKSupportingData *pvk,
+ SGNDigestInfo *thumb)
+{
+ SGNDigestInfo **thumb_list = NULL;
+ int nthumbs, size;
+ void *mark, *dummy;
+ SECStatus rv = SECFailure;
+
+ if ((pvk == NULL) || (thumb == NULL)) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(pvk->poolp);
+
+ thumb_list = pvk->assocCerts;
+ nthumbs = pvk->nThumbs;
+
+ /* allocate list space needed -- either growing or allocating
+ * list must be NULL terminated
+ */
+ size = sizeof(SGNDigestInfo *);
+ dummy = PORT_ArenaGrow(pvk->poolp, thumb_list, (size * (nthumbs + 1)),
+ (size * (nthumbs + 2)));
+ thumb_list = dummy;
+ if (dummy != NULL) {
+ thumb_list[nthumbs] = (SGNDigestInfo *)PORT_ArenaZAlloc(pvk->poolp,
+ sizeof(SGNDigestInfo));
+ if (thumb_list[nthumbs] != NULL) {
+ SGN_CopyDigestInfo(pvk->poolp, thumb_list[nthumbs], thumb);
+ nthumbs += 1;
+ thumb_list[nthumbs] = 0;
+ } else {
+ dummy = NULL;
+ }
+ }
+
+ if (dummy == NULL) {
+ PORT_ArenaRelease(pvk->poolp, mark);
+ return SECFailure;
+ }
+
+ pvk->assocCerts = thumb_list;
+ pvk->nThumbs = nthumbs;
+
+ PORT_ArenaUnmark(pvk->poolp, mark);
+ return SECSuccess;
+}
+
+/* search the list of shrouded keys in the baggage for the desired
+ * name. return a pointer to the item. a return of NULL indicates
+ * that no match was present or that an error occurred.
+ */
+static SEC_PKCS12ESPVKItem *
+sec_pkcs12_get_espvk_by_name(SEC_PKCS12Baggage *luggage,
+ SECItem *name)
+{
+ PRBool found = PR_FALSE;
+ SEC_PKCS12ESPVKItem *espvk = NULL;
+ int i, j;
+ SECComparison rv = SECEqual;
+ SECItem *t_name;
+ SEC_PKCS12BaggageItem *bag;
+
+ if ((luggage == NULL) || (name == NULL)) {
+ return NULL;
+ }
+
+ i = 0;
+ while ((found == PR_FALSE) && (i < luggage->luggage_size)) {
+ j = 0;
+ bag = luggage->bags[i];
+ while ((found == PR_FALSE) && (j < bag->nEspvks)) {
+ espvk = bag->espvks[j];
+ if (espvk->poolp == NULL) {
+ espvk->poolp = luggage->poolp;
+ }
+ t_name = SECITEM_DupItem(&espvk->espvkData.nickname);
+ if (t_name != NULL) {
+ rv = SECITEM_CompareItem(name, t_name);
+ if (rv == SECEqual) {
+ found = PR_TRUE;
+ }
+ SECITEM_FreeItem(t_name, PR_TRUE);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ j++;
+ }
+ i++;
+ }
+
+ if (found != PR_TRUE) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
+ return NULL;
+ }
+
+ return espvk;
+}
+
+/* locates a certificate and copies the thumbprint to the
+ * appropriate private key
+ */
+static SECStatus
+sec_pkcs12_propagate_thumbprints(SECItem **nicknames,
+ CERTCertificate **ref_certs,
+ SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ SEC_PKCS12CertAndCRL *cert;
+ SEC_PKCS12PrivateKey *key;
+ SEC_PKCS12ESPVKItem *espvk;
+ int i;
+ PRBool error = PR_FALSE;
+ SECStatus rv = SECFailure;
+
+ if ((nicknames == NULL) || (safe == NULL)) {
+ return SECFailure;
+ }
+
+ i = 0;
+ while ((nicknames[i] != NULL) && (error == PR_FALSE)) {
+ /* process all certs */
+ cert = (SEC_PKCS12CertAndCRL *)sec_pkcs12_find_object(safe, baggage,
+ SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID,
+ nicknames[i], NULL);
+ if (cert != NULL) {
+ /* locate key and copy thumbprint */
+ key = (SEC_PKCS12PrivateKey *)sec_pkcs12_find_object(safe, baggage,
+ SEC_OID_PKCS12_KEY_BAG_ID,
+ nicknames[i], NULL);
+ if (key != NULL) {
+ key->pvkData.poolp = key->poolp;
+ rv = sec_pkcs12_add_thumbprint(&key->pvkData,
+ &cert->value.x509->thumbprint);
+ if (rv == SECFailure)
+ error = PR_TRUE; /* XXX Set error? */
+ }
+
+ /* look in the baggage as well...*/
+ if ((baggage != NULL) && (error == PR_FALSE)) {
+ espvk = sec_pkcs12_get_espvk_by_name(baggage, nicknames[i]);
+ if (espvk != NULL) {
+ espvk->espvkData.poolp = espvk->poolp;
+ rv = sec_pkcs12_add_thumbprint(&espvk->espvkData,
+ &cert->value.x509->thumbprint);
+ if (rv == SECFailure)
+ error = PR_TRUE; /* XXX Set error? */
+ }
+ }
+ }
+ i++;
+ }
+
+ if (error == PR_TRUE) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* append a safe bag to the end of the safe contents list */
+SECStatus
+sec_pkcs12_append_safe_bag(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12SafeBag *bag)
+{
+ int size;
+ void *mark = NULL, *dummy = NULL;
+
+ if ((bag == NULL) || (safe == NULL))
+ return SECFailure;
+
+ mark = PORT_ArenaMark(safe->poolp);
+
+ size = (safe->safe_size * sizeof(SEC_PKCS12SafeBag *));
+
+ if (safe->safe_size > 0) {
+ dummy = (SEC_PKCS12SafeBag **)PORT_ArenaGrow(safe->poolp,
+ safe->contents,
+ size,
+ (size + sizeof(SEC_PKCS12SafeBag *)));
+ safe->contents = dummy;
+ } else {
+ safe->contents = (SEC_PKCS12SafeBag **)PORT_ArenaZAlloc(safe->poolp,
+ (2 * sizeof(SEC_PKCS12SafeBag *)));
+ dummy = safe->contents;
+ }
+
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ safe->contents[safe->safe_size] = bag;
+ safe->safe_size++;
+ safe->contents[safe->safe_size] = NULL;
+
+ PORT_ArenaUnmark(safe->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(safe->poolp, mark);
+ return SECFailure;
+}
+
+/* append a certificate onto the end of a cert bag */
+static SECStatus
+sec_pkcs12_append_cert_to_bag(PLArenaPool *arena,
+ SEC_PKCS12SafeBag *safebag,
+ CERTCertificate *cert,
+ SECItem *nickname)
+{
+ int size;
+ void *dummy = NULL, *mark = NULL;
+ SEC_PKCS12CertAndCRL *p12cert;
+ SEC_PKCS12CertAndCRLBag *bag;
+
+ if ((arena == NULL) || (safebag == NULL) ||
+ (cert == NULL) || (nickname == NULL)) {
+ return SECFailure;
+ }
+
+ bag = safebag->safeContent.certAndCRLBag;
+ if (bag == NULL) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(arena);
+
+ p12cert = sec_pkcs12_get_cert(arena, cert, nickname);
+ if (p12cert == NULL) {
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+ }
+
+ size = bag->bag_size * sizeof(SEC_PKCS12CertAndCRL *);
+ if (bag->bag_size > 0) {
+ dummy = (SEC_PKCS12CertAndCRL **)PORT_ArenaGrow(bag->poolp,
+ bag->certAndCRLs,
+ size,
+ size + sizeof(SEC_PKCS12CertAndCRL *));
+ bag->certAndCRLs = dummy;
+ } else {
+ bag->certAndCRLs = (SEC_PKCS12CertAndCRL **)PORT_ArenaZAlloc(bag->poolp,
+ (2 * sizeof(SEC_PKCS12CertAndCRL *)));
+ dummy = bag->certAndCRLs;
+ }
+
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->certAndCRLs[bag->bag_size] = p12cert;
+ bag->bag_size++;
+ bag->certAndCRLs[bag->bag_size] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* append a key onto the end of a list of keys in a key bag */
+SECStatus
+sec_pkcs12_append_key_to_bag(SEC_PKCS12SafeBag *safebag,
+ SEC_PKCS12PrivateKey *pk)
+{
+ void *mark, *dummy;
+ SEC_PKCS12PrivateKeyBag *bag;
+ int size;
+
+ if ((safebag == NULL) || (pk == NULL))
+ return SECFailure;
+
+ bag = safebag->safeContent.keyBag;
+ if (bag == NULL) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(bag->poolp);
+
+ size = (bag->bag_size * sizeof(SEC_PKCS12PrivateKey *));
+
+ if (bag->bag_size > 0) {
+ dummy = (SEC_PKCS12PrivateKey **)PORT_ArenaGrow(bag->poolp,
+ bag->privateKeys,
+ size,
+ size + sizeof(SEC_PKCS12PrivateKey *));
+ bag->privateKeys = dummy;
+ } else {
+ bag->privateKeys = (SEC_PKCS12PrivateKey **)PORT_ArenaZAlloc(bag->poolp,
+ (2 * sizeof(SEC_PKCS12PrivateKey *)));
+ dummy = bag->privateKeys;
+ }
+
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->privateKeys[bag->bag_size] = pk;
+ bag->bag_size++;
+ bag->privateKeys[bag->bag_size] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ /* XXX Free memory? */
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* append a safe bag to the baggage area */
+static SECStatus
+sec_pkcs12_append_unshrouded_bag(SEC_PKCS12BaggageItem *bag,
+ SEC_PKCS12SafeBag *u_bag)
+{
+ int size;
+ void *mark = NULL, *dummy = NULL;
+
+ if ((bag == NULL) || (u_bag == NULL))
+ return SECFailure;
+
+ mark = PORT_ArenaMark(bag->poolp);
+
+ /* dump things into the first bag */
+ size = (bag->nSecrets + 1) * sizeof(SEC_PKCS12SafeBag *);
+ dummy = PORT_ArenaGrow(bag->poolp,
+ bag->unencSecrets, size,
+ size + sizeof(SEC_PKCS12SafeBag *));
+ bag->unencSecrets = dummy;
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->unencSecrets[bag->nSecrets] = u_bag;
+ bag->nSecrets++;
+ bag->unencSecrets[bag->nSecrets] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* gather up all certificates and keys and package them up
+ * in the safe, baggage, or both.
+ * nicknames is the list of nicknames and corresponding certs in ref_certs
+ * ref_certs a null terminated list of certificates
+ * rSafe, rBaggage -- return areas for safe and baggage
+ * shroud_keys -- store keys externally
+ * pwitem -- password for computing integrity mac and encrypting contents
+ * wincx -- window handle
+ *
+ * if a failure occurs, an error is set and SECFailure returned.
+ */
+static SECStatus
+sec_pkcs12_package_certs_and_keys(SECItem **nicknames,
+ CERTCertificate **ref_certs,
+ PRBool unencryptedCerts,
+ SEC_PKCS12SafeContents **rSafe,
+ SEC_PKCS12Baggage **rBaggage,
+ PRBool shroud_keys,
+ SECOidTag shroud_alg,
+ SECItem *pwitem,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ PLArenaPool *permArena;
+ SEC_PKCS12SafeContents *safe = NULL;
+ SEC_PKCS12Baggage *baggage = NULL;
+
+ SECStatus rv = SECFailure;
+ PRBool problem = PR_FALSE;
+
+ SEC_PKCS12ESPVKItem *espvk = NULL;
+ SEC_PKCS12PrivateKey *pk = NULL;
+ CERTCertificate *add_cert = NULL;
+ SEC_PKCS12SafeBag *certbag = NULL, *keybag = NULL;
+ SEC_PKCS12BaggageItem *external_bag = NULL;
+ int ncerts = 0, nkeys = 0;
+ int i;
+
+ if ((nicknames == NULL) || (rSafe == NULL) || (rBaggage == NULL)) {
+ return SECFailure;
+ }
+
+ *rBaggage = baggage;
+ *rSafe = safe;
+
+ permArena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (permArena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ /* allocate structures */
+ safe = sec_pkcs12_create_safe_contents(permArena);
+ if (safe == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ certbag = sec_pkcs12_create_safe_bag(permArena,
+ SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID);
+ if (certbag == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ if (shroud_keys != PR_TRUE) {
+ keybag = sec_pkcs12_create_safe_bag(permArena,
+ SEC_OID_PKCS12_KEY_BAG_ID);
+ if (keybag == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+ if ((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
+ baggage = sec_pkcs12_create_baggage(permArena);
+ if (baggage == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ external_bag = sec_pkcs12_create_external_bag(baggage);
+ }
+
+ /* package keys and certs */
+ i = 0;
+ while ((nicknames[i] != NULL) && (problem == PR_FALSE)) {
+ if (ref_certs[i] != NULL) {
+ /* append cert to bag o certs */
+ rv = sec_pkcs12_append_cert_to_bag(permArena, certbag,
+ ref_certs[i],
+ nicknames[i]);
+ if (rv == SECFailure) {
+ problem = PR_FALSE;
+ } else {
+ ncerts++;
+ }
+
+ if (rv == SECSuccess) {
+ /* package up them keys */
+ if (shroud_keys == PR_TRUE) {
+ espvk = sec_pkcs12_get_shrouded_key(permArena,
+ nicknames[i],
+ ref_certs[i],
+ shroud_alg,
+ pwitem, unicodeFn,
+ wincx);
+ if (espvk != NULL) {
+ rv = sec_pkcs12_append_shrouded_key(external_bag, espvk);
+ SECITEM_CopyItem(permArena, &espvk->derCert,
+ &ref_certs[i]->derCert);
+ } else {
+ rv = SECFailure;
+ }
+ } else {
+ pk = sec_pkcs12_get_private_key(permArena, nicknames[i],
+ ref_certs[i], wincx);
+ if (pk != NULL) {
+ rv = sec_pkcs12_append_key_to_bag(keybag, pk);
+ SECITEM_CopyItem(permArena, &espvk->derCert,
+ &ref_certs[i]->derCert);
+ } else {
+ rv = SECFailure;
+ }
+ }
+
+ if (rv == SECFailure) {
+ problem = PR_TRUE;
+ } else {
+ nkeys++;
+ }
+ }
+ } else {
+ /* handle only keys here ? */
+ problem = PR_TRUE;
+ }
+ i++;
+ }
+
+/* let success fall through */
+loser:
+ if (problem == PR_FALSE) {
+ /* if we have certs, we want to append the cert bag to the
+ * appropriate area
+ */
+ if (ncerts > 0) {
+ if (unencryptedCerts != PR_TRUE) {
+ rv = sec_pkcs12_append_safe_bag(safe, certbag);
+ } else {
+ rv = sec_pkcs12_append_unshrouded_bag(external_bag, certbag);
+ }
+ } else {
+ rv = SECSuccess;
+ }
+
+ /* append key bag, if they are stored in safe contents */
+ if ((rv == SECSuccess) && (shroud_keys == PR_FALSE) && (nkeys > 0)) {
+ rv = sec_pkcs12_append_safe_bag(safe, keybag);
+ }
+ } else {
+ rv = SECFailure;
+ }
+
+ /* if baggage not used, NULLify it */
+ if ((shroud_keys == PR_TRUE) || (unencryptedCerts == PR_TRUE)) {
+ if (((unencryptedCerts == PR_TRUE) && (ncerts == 0)) &&
+ ((shroud_keys == PR_TRUE) && (nkeys == 0)))
+ baggage = NULL;
+ } else {
+ baggage = NULL;
+ }
+
+ if ((problem == PR_TRUE) || (rv == SECFailure)) {
+ PORT_FreeArena(permArena, PR_TRUE);
+ rv = SECFailure;
+ baggage = NULL;
+ safe = NULL;
+ }
+
+ *rBaggage = baggage;
+ *rSafe = safe;
+
+ return rv;
+}
+
+/* DER encode the safe contents and return a SECItem. if an error
+ * occurs, NULL is returned.
+ */
+static SECItem *
+sec_pkcs12_encode_safe_contents(SEC_PKCS12SafeContents *safe)
+{
+ SECItem *dsafe = NULL, *tsafe;
+ void *dummy = NULL;
+ PLArenaPool *arena;
+
+ if (safe == NULL) {
+ return NULL;
+ }
+
+ /* rv = sec_pkcs12_prepare_for_der_code_safe(safe, PR_TRUE);
+ if(rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }*/
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ tsafe = (SECItem *)PORT_ArenaZAlloc(arena, sizeof(SECItem));
+ if (tsafe != NULL) {
+ dummy = SEC_ASN1EncodeItem(arena, tsafe, safe,
+ SEC_PKCS12SafeContentsTemplate);
+ if (dummy != NULL) {
+ dsafe = SECITEM_DupItem(tsafe);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+
+ PORT_FreeArena(arena, PR_TRUE);
+
+ return dsafe;
+}
+
+/* prepare the authenicated safe for encoding and encode it.
+ * baggage is copied to the appropriate area, safe is encoded and
+ * encrypted. the version and transport mode are set on the asafe.
+ * the whole ball of wax is then der encoded and packaged up into
+ * data content info
+ * safe -- container of certs and keys, is encrypted.
+ * baggage -- container of certs and keys, keys assumed to be encrypted by
+ * another method, certs are in the clear
+ * algorithm -- algorithm by which to encrypt safe
+ * pwitem -- password for encryption
+ * wincx - window handle
+ *
+ * return of NULL is an error condition.
+ */
+static SEC_PKCS7ContentInfo *
+sec_pkcs12_get_auth_safe(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage,
+ SECOidTag algorithm,
+ SECItem *pwitem,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ SECItem *src = NULL, *dest = NULL, *psalt = NULL;
+ PLArenaPool *poolp;
+ SEC_PKCS12AuthenticatedSafe *asafe;
+ SEC_PKCS7ContentInfo *safe_cinfo = NULL;
+ SEC_PKCS7ContentInfo *asafe_cinfo = NULL;
+ void *dummy;
+ SECStatus rv = SECSuccess;
+ PRBool swapUnicodeBytes = PR_FALSE;
+
+#ifdef IS_LITTLE_ENDIAN
+ swapUnicodeBytes = PR_TRUE;
+#endif
+
+ if (((safe != NULL) && (pwitem == NULL)) && (baggage == NULL))
+ return NULL;
+
+ poolp = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (poolp == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ /* prepare authenticated safe for encode */
+ asafe = sec_pkcs12_new_asafe(poolp);
+ if (asafe != NULL) {
+
+ /* set version */
+ dummy = SEC_ASN1EncodeInteger(asafe->poolp, &asafe->version,
+ SEC_PKCS12_PFX_VERSION);
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* generate the privacy salt used to create virtual pwd */
+ psalt = sec_pkcs12_generate_salt();
+ if (psalt != NULL) {
+ rv = SECITEM_CopyItem(asafe->poolp, &asafe->privacySalt,
+ psalt);
+ if (rv == SECSuccess) {
+ asafe->privacySalt.len *= 8;
+ } else {
+ SECITEM_ZfreeItem(psalt, PR_TRUE);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ }
+
+ if ((psalt == NULL) || (rv == SECFailure)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* package up safe contents */
+ if (safe != NULL) {
+ safe_cinfo = SEC_PKCS7CreateEncryptedData(algorithm, NULL, wincx);
+ if ((safe_cinfo != NULL) && (safe->safe_size > 0)) {
+ /* encode the safe and encrypt the contents of the
+ * content info
+ */
+ src = sec_pkcs12_encode_safe_contents(safe);
+
+ if (src != NULL) {
+ rv = SEC_PKCS7SetContent(safe_cinfo, (char *)src->data, src->len);
+ SECITEM_ZfreeItem(src, PR_TRUE);
+ if (rv == SECSuccess) {
+ SECItem *vpwd;
+ vpwd = sec_pkcs12_create_virtual_password(pwitem, psalt,
+ unicodeFn, swapUnicodeBytes);
+ if (vpwd != NULL) {
+ rv = SEC_PKCS7EncryptContents(NULL, safe_cinfo,
+ vpwd, wincx);
+ SECITEM_ZfreeItem(vpwd, PR_TRUE);
+ } else {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ rv = SECFailure;
+ }
+ } else if (safe->safe_size > 0) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ } else {
+ /* case where there is NULL content in the safe contents */
+ rv = SEC_PKCS7SetContent(safe_cinfo, NULL, 0);
+ if (rv != SECFailure) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ }
+
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo(safe_cinfo);
+ safe_cinfo = NULL;
+ goto loser;
+ }
+
+ asafe->safe = safe_cinfo;
+ /*
+ PORT_Memcpy(&asafe->safe, safe_cinfo, sizeof(*safe_cinfo));
+ */
+ }
+
+ /* copy the baggage to the authenticated safe baggage if present */
+ if (baggage != NULL) {
+ PORT_Memcpy(&asafe->baggage, baggage, sizeof(*baggage));
+ }
+
+ /* encode authenticated safe and store it in a Data content info */
+ dest = (SECItem *)PORT_ArenaZAlloc(poolp, sizeof(SECItem));
+ if (dest != NULL) {
+ dummy = SEC_ASN1EncodeItem(poolp, dest, asafe,
+ SEC_PKCS12AuthenticatedSafeTemplate);
+ if (dummy != NULL) {
+ asafe_cinfo = SEC_PKCS7CreateData();
+ if (asafe_cinfo != NULL) {
+ rv = SEC_PKCS7SetContent(asafe_cinfo,
+ (char *)dest->data,
+ dest->len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ SEC_PKCS7DestroyContentInfo(asafe_cinfo);
+ asafe_cinfo = NULL;
+ }
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ }
+ }
+ }
+
+loser:
+ PORT_FreeArena(poolp, PR_TRUE);
+ if (safe_cinfo != NULL) {
+ SEC_PKCS7DestroyContentInfo(safe_cinfo);
+ }
+ if (psalt != NULL) {
+ SECITEM_ZfreeItem(psalt, PR_TRUE);
+ }
+
+ if (rv == SECFailure) {
+ return NULL;
+ }
+
+ return asafe_cinfo;
+}
+
+/* generates the PFX and computes the mac on the authenticated safe
+ * NULL implies an error
+ */
+static SEC_PKCS12PFXItem *
+sec_pkcs12_get_pfx(SEC_PKCS7ContentInfo *cinfo,
+ PRBool do_mac,
+ SECItem *pwitem, PKCS12UnicodeConvertFunction unicodeFn)
+{
+ SECItem *dest = NULL, *mac = NULL, *salt = NULL, *key = NULL;
+ SEC_PKCS12PFXItem *pfx;
+ SECStatus rv = SECFailure;
+ SGNDigestInfo *di;
+ SECItem *vpwd;
+ PRBool swapUnicodeBytes = PR_FALSE;
+
+#ifdef IS_LITTLE_ENDIAN
+ swapUnicodeBytes = PR_TRUE;
+#endif
+
+ if ((cinfo == NULL) || ((do_mac == PR_TRUE) && (pwitem == NULL))) {
+ return NULL;
+ }
+
+ /* allocate new pfx structure */
+ pfx = sec_pkcs12_new_pfx();
+ if (pfx == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ PORT_Memcpy(&pfx->authSafe, cinfo, sizeof(*cinfo));
+ if (do_mac == PR_TRUE) {
+
+ /* salt for computing mac */
+ salt = sec_pkcs12_generate_salt();
+ if (salt != NULL) {
+ rv = SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, salt);
+ pfx->macData.macSalt.len *= 8;
+
+ vpwd = sec_pkcs12_create_virtual_password(pwitem, salt,
+ unicodeFn, swapUnicodeBytes);
+ if (vpwd == NULL) {
+ rv = SECFailure;
+ key = NULL;
+ } else {
+ key = sec_pkcs12_generate_key_from_password(SEC_OID_SHA1,
+ salt, vpwd);
+ SECITEM_ZfreeItem(vpwd, PR_TRUE);
+ }
+
+ if ((key != NULL) && (rv == SECSuccess)) {
+ dest = SEC_PKCS7GetContent(cinfo);
+ if (dest != NULL) {
+
+ /* compute mac on data -- for password integrity mode */
+ mac = sec_pkcs12_generate_mac(key, dest, PR_FALSE);
+ if (mac != NULL) {
+ di = SGN_CreateDigestInfo(SEC_OID_SHA1,
+ mac->data, mac->len);
+ if (di != NULL) {
+ rv = SGN_CopyDigestInfo(pfx->poolp,
+ &pfx->macData.safeMac, di);
+ SGN_DestroyDigestInfo(di);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ }
+ } else {
+ rv = SECFailure;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ }
+
+ if (key != NULL) {
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ }
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ }
+ }
+
+ if (rv == SECFailure) {
+ SEC_PKCS12DestroyPFX(pfx);
+ pfx = NULL;
+ }
+
+ return pfx;
+}
+
+/* der encode the pfx */
+static SECItem *
+sec_pkcs12_encode_pfx(SEC_PKCS12PFXItem *pfx)
+{
+ SECItem *dest;
+ void *dummy;
+
+ if (pfx == NULL) {
+ return NULL;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (dest == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ dummy = SEC_ASN1EncodeItem(NULL, dest, pfx, SEC_PKCS12PFXItemTemplate);
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+ dest = NULL;
+ }
+
+ return dest;
+}
+
+SECItem *
+SEC_PKCS12GetPFX(char **nicknames,
+ CERTCertificate **ref_certs,
+ PRBool shroud_keys,
+ SEC_PKCS5GetPBEPassword pbef,
+ void *pbearg,
+ PKCS12UnicodeConvertFunction unicodeFn,
+ void *wincx)
+{
+ SECItem **nicks = NULL;
+ SEC_PKCS12PFXItem *pfx = NULL;
+ SEC_PKCS12Baggage *baggage = NULL;
+ SEC_PKCS12SafeContents *safe = NULL;
+ SEC_PKCS7ContentInfo *cinfo = NULL;
+ SECStatus rv = SECFailure;
+ SECItem *dest = NULL, *pwitem = NULL;
+ PRBool problem = PR_FALSE;
+ PRBool unencryptedCerts;
+ SECOidTag shroud_alg, safe_alg;
+
+ /* how should we encrypt certs ? */
+ unencryptedCerts = !SEC_PKCS12IsEncryptionAllowed();
+ if (!unencryptedCerts) {
+ safe_alg = SEC_PKCS12GetPreferredEncryptionAlgorithm();
+ if (safe_alg == SEC_OID_UNKNOWN) {
+ safe_alg = SEC_PKCS12GetStrongestAllowedAlgorithm();
+ }
+ if (safe_alg == SEC_OID_UNKNOWN) {
+ unencryptedCerts = PR_TRUE;
+ /* for export where no encryption is allowed, we still need
+ * to encrypt the NULL contents per the spec. encrypted info
+ * is known plaintext, so it shouldn't be a problem.
+ */
+ safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ }
+ } else {
+ /* for export where no encryption is allowed, we still need
+ * to encrypt the NULL contents per the spec. encrypted info
+ * is known plaintext, so it shouldn't be a problem.
+ */
+ safe_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ }
+
+ /* keys are always stored with triple DES */
+ shroud_alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
+
+ /* check for FIPS, if so, do not encrypt certs */
+ if (PK11_IsFIPS() && !unencryptedCerts) {
+ unencryptedCerts = PR_TRUE;
+ }
+
+ if ((nicknames == NULL) || (pbef == NULL) || (ref_certs == NULL)) {
+ problem = PR_TRUE;
+ goto loser;
+ }
+
+ /* get password */
+ pwitem = (*pbef)(pbearg);
+ if (pwitem == NULL) {
+ problem = PR_TRUE;
+ goto loser;
+ }
+ nicks = sec_pkcs12_convert_nickname_list(nicknames);
+
+ /* get safe and baggage */
+ rv = sec_pkcs12_package_certs_and_keys(nicks, ref_certs, unencryptedCerts,
+ &safe, &baggage, shroud_keys,
+ shroud_alg, pwitem, unicodeFn, wincx);
+ if (rv == SECFailure) {
+ problem = PR_TRUE;
+ }
+
+ if ((safe != NULL) && (problem == PR_FALSE)) {
+ /* copy thumbprints */
+ rv = sec_pkcs12_propagate_thumbprints(nicks, ref_certs, safe, baggage);
+
+ /* package everything up into AuthenticatedSafe */
+ cinfo = sec_pkcs12_get_auth_safe(safe, baggage,
+ safe_alg, pwitem, unicodeFn, wincx);
+
+ sec_pkcs12_destroy_cert_content_infos(safe, baggage);
+
+ /* get the pfx and mac it */
+ if (cinfo != NULL) {
+ pfx = sec_pkcs12_get_pfx(cinfo, PR_TRUE, pwitem, unicodeFn);
+ if (pfx != NULL) {
+ dest = sec_pkcs12_encode_pfx(pfx);
+ SEC_PKCS12DestroyPFX(pfx);
+ }
+ SEC_PKCS7DestroyContentInfo(cinfo);
+ }
+
+ if (safe != NULL) {
+ PORT_FreeArena(safe->poolp, PR_TRUE);
+ }
+ } else {
+ if (safe != NULL) {
+ PORT_FreeArena(safe->poolp, PR_TRUE);
+ }
+ }
+
+loser:
+ if (nicks != NULL) {
+ sec_pkcs12_destroy_nickname_list(nicks);
+ }
+
+ if (ref_certs != NULL) {
+ sec_pkcs12_destroy_certificate_list(ref_certs);
+ }
+
+ if (pwitem != NULL) {
+ SECITEM_ZfreeItem(pwitem, PR_TRUE);
+ }
+
+ if (problem == PR_TRUE) {
+ dest = NULL;
+ }
+
+ return dest;
+}
diff --git a/security/nss/lib/pkcs12/p12local.c b/security/nss/lib/pkcs12/p12local.c
new file mode 100644
index 0000000000..f64448664d
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12local.c
@@ -0,0 +1,1417 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nssrenam.h"
+#include "pkcs12.h"
+#include "secpkcs7.h"
+#include "secasn1.h"
+#include "seccomon.h"
+#include "secoid.h"
+#include "sechash.h"
+#include "secitem.h"
+#include "secerr.h"
+#include "pk11func.h"
+#include "p12local.h"
+#include "p12.h"
+
+#define SALT_LENGTH 16
+
+SEC_ASN1_MKSUB(SECKEY_PrivateKeyInfoTemplate)
+SEC_ASN1_MKSUB(sgn_DigestInfoTemplate)
+
+CK_MECHANISM_TYPE
+sec_pkcs12_algtag_to_mech(SECOidTag algtag)
+{
+ switch (algtag) {
+ case SEC_OID_MD2:
+ return CKM_MD2_HMAC;
+ case SEC_OID_MD5:
+ return CKM_MD5_HMAC;
+ case SEC_OID_SHA1:
+ return CKM_SHA_1_HMAC;
+ case SEC_OID_SHA224:
+ return CKM_SHA224_HMAC;
+ case SEC_OID_SHA256:
+ return CKM_SHA256_HMAC;
+ case SEC_OID_SHA384:
+ return CKM_SHA384_HMAC;
+ case SEC_OID_SHA512:
+ return CKM_SHA512_HMAC;
+ default:
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+CK_MECHANISM_TYPE
+sec_pkcs12_algtag_to_keygen_mech(SECOidTag algtag)
+{
+ switch (algtag) {
+ case SEC_OID_SHA1:
+ return CKM_NSS_PBE_SHA1_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_MD5:
+ return CKM_NSS_PBE_MD5_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_MD2:
+ return CKM_NSS_PBE_MD2_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_SHA224:
+ return CKM_NSS_PKCS12_PBE_SHA224_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_SHA256:
+ return CKM_NSS_PKCS12_PBE_SHA256_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_SHA384:
+ return CKM_NSS_PKCS12_PBE_SHA384_HMAC_KEY_GEN;
+ break;
+ case SEC_OID_SHA512:
+ return CKM_NSS_PKCS12_PBE_SHA512_HMAC_KEY_GEN;
+ break;
+ default:
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+/* helper functions */
+/* returns proper bag type template based upon object type tag */
+const SEC_ASN1Template *
+sec_pkcs12_choose_bag_type_old(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS12SafeBag *safebag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ safebag = (SEC_PKCS12SafeBag *)src_or_dest;
+
+ oiddata = safebag->safeBagTypeTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&safebag->safeBagType);
+ safebag->safeBagTypeTag = oiddata;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ break;
+ case SEC_OID_PKCS12_KEY_BAG_ID:
+ theTemplate = SEC_PointerToPKCS12KeyBagTemplate;
+ break;
+ case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
+ theTemplate = SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD;
+ break;
+ case SEC_OID_PKCS12_SECRET_BAG_ID:
+ theTemplate = SEC_PointerToPKCS12SecretBagTemplate;
+ break;
+ }
+ return theTemplate;
+}
+
+const SEC_ASN1Template *
+sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS12SafeBag *safebag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ safebag = (SEC_PKCS12SafeBag *)src_or_dest;
+
+ oiddata = safebag->safeBagTypeTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&safebag->safeBagType);
+ safebag->safeBagTypeTag = oiddata;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+ break;
+ case SEC_OID_PKCS12_KEY_BAG_ID:
+ theTemplate = SEC_PKCS12PrivateKeyBagTemplate;
+ break;
+ case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
+ theTemplate = SEC_PKCS12CertAndCRLBagTemplate;
+ break;
+ case SEC_OID_PKCS12_SECRET_BAG_ID:
+ theTemplate = SEC_PKCS12SecretBagTemplate;
+ break;
+ }
+ return theTemplate;
+}
+
+/* returns proper cert crl template based upon type tag */
+const SEC_ASN1Template *
+sec_pkcs12_choose_cert_crl_type_old(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS12CertAndCRL *certbag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ certbag = (SEC_PKCS12CertAndCRL *)src_or_dest;
+ oiddata = certbag->BagTypeTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&certbag->BagID);
+ certbag->BagTypeTag = oiddata;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ break;
+ case SEC_OID_PKCS12_X509_CERT_CRL_BAG:
+ theTemplate = SEC_PointerToPKCS12X509CertCRLTemplate_OLD;
+ break;
+ case SEC_OID_PKCS12_SDSI_CERT_BAG:
+ theTemplate = SEC_PointerToPKCS12SDSICertTemplate;
+ break;
+ }
+ return theTemplate;
+}
+
+const SEC_ASN1Template *
+sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS12CertAndCRL *certbag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ certbag = (SEC_PKCS12CertAndCRL *)src_or_dest;
+ oiddata = certbag->BagTypeTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&certbag->BagID);
+ certbag->BagTypeTag = oiddata;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ break;
+ case SEC_OID_PKCS12_X509_CERT_CRL_BAG:
+ theTemplate = SEC_PointerToPKCS12X509CertCRLTemplate;
+ break;
+ case SEC_OID_PKCS12_SDSI_CERT_BAG:
+ theTemplate = SEC_PointerToPKCS12SDSICertTemplate;
+ break;
+ }
+ return theTemplate;
+}
+
+/* returns appropriate shroud template based on object type tag */
+const SEC_ASN1Template *
+sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS12ESPVKItem *espvk;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ espvk = (SEC_PKCS12ESPVKItem *)src_or_dest;
+ oiddata = espvk->espvkTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&espvk->espvkOID);
+ espvk->espvkTag = oiddata;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ break;
+ case SEC_OID_PKCS12_PKCS8_KEY_SHROUDING:
+ theTemplate =
+ SEC_ASN1_GET(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate);
+ break;
+ }
+ return theTemplate;
+}
+
+/* generate SALT placing it into the character array passed in.
+ * it is assumed that salt_dest is an array of appropriate size
+ * XXX We might want to generate our own random context
+ */
+SECItem *
+sec_pkcs12_generate_salt(void)
+{
+ SECItem *salt;
+
+ salt = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (salt == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+ salt->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) *
+ SALT_LENGTH);
+ salt->len = SALT_LENGTH;
+ if (salt->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+ return NULL;
+ }
+
+ PK11_GenerateRandom(salt->data, salt->len);
+
+ return salt;
+}
+
+/* generate KEYS -- as per PKCS12 section 7.
+ * only used for MAC
+ */
+SECItem *
+sec_pkcs12_generate_key_from_password(SECOidTag algorithm,
+ SECItem *salt,
+ SECItem *password)
+{
+ unsigned char *pre_hash = NULL;
+ unsigned char *hash_dest = NULL;
+ SECStatus res;
+ PLArenaPool *poolp;
+ SECItem *key = NULL;
+ int key_len = 0;
+
+ if ((salt == NULL) || (password == NULL)) {
+ return NULL;
+ }
+
+ poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (poolp == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ pre_hash = (unsigned char *)PORT_ArenaZAlloc(poolp, sizeof(char) * (salt->len + password->len));
+ if (pre_hash == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ hash_dest = (unsigned char *)PORT_ArenaZAlloc(poolp,
+ sizeof(unsigned char) * SHA1_LENGTH);
+ if (hash_dest == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ PORT_Memcpy(pre_hash, salt->data, salt->len);
+ /* handle password of 0 length case */
+ if (password->len > 0) {
+ PORT_Memcpy(&(pre_hash[salt->len]), password->data, password->len);
+ }
+
+ res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, pre_hash,
+ (salt->len + password->len));
+ if (res == SECFailure) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ switch (algorithm) {
+ case SEC_OID_SHA1:
+ if (key_len == 0)
+ key_len = 16;
+ key = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (key == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ key->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * key_len);
+ if (key->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ key->len = key_len;
+ PORT_Memcpy(key->data, &hash_dest[SHA1_LENGTH - key->len], key->len);
+ break;
+ default:
+ goto loser;
+ break;
+ }
+
+ PORT_FreeArena(poolp, PR_TRUE);
+ return key;
+
+loser:
+ PORT_FreeArena(poolp, PR_TRUE);
+ if (key != NULL) {
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ }
+ return NULL;
+}
+
+/* MAC is generated per PKCS 12 section 6. It is expected that key, msg
+ * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in
+ * because it is not known how long the message actually is. String
+ * manipulation routines will not necessarily work because msg may have
+ * imbedded NULLs
+ */
+static SECItem *
+sec_pkcs12_generate_old_mac(SECItem *key,
+ SECItem *msg)
+{
+ SECStatus res;
+ PLArenaPool *temparena = NULL;
+ unsigned char *hash_dest = NULL, *hash_src1 = NULL, *hash_src2 = NULL;
+ int i;
+ SECItem *mac = NULL;
+
+ if ((key == NULL) || (msg == NULL))
+ goto loser;
+
+ /* allocate return item */
+ mac = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (mac == NULL)
+ return NULL;
+ mac->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) * SHA1_LENGTH);
+ mac->len = SHA1_LENGTH;
+ if (mac->data == NULL)
+ goto loser;
+
+ /* allocate temporary items */
+ temparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (temparena == NULL)
+ goto loser;
+
+ hash_src1 = (unsigned char *)PORT_ArenaZAlloc(temparena,
+ sizeof(unsigned char) * (16 + msg->len));
+ if (hash_src1 == NULL)
+ goto loser;
+
+ hash_src2 = (unsigned char *)PORT_ArenaZAlloc(temparena,
+ sizeof(unsigned char) * (SHA1_LENGTH + 16));
+ if (hash_src2 == NULL)
+ goto loser;
+
+ hash_dest = (unsigned char *)PORT_ArenaZAlloc(temparena,
+ sizeof(unsigned char) * SHA1_LENGTH);
+ if (hash_dest == NULL)
+ goto loser;
+
+ /* perform mac'ing as per PKCS 12 */
+
+ /* first round of hashing */
+ for (i = 0; i < 16; i++)
+ hash_src1[i] = key->data[i] ^ 0x36;
+ PORT_Memcpy(&(hash_src1[16]), msg->data, msg->len);
+ res = PK11_HashBuf(SEC_OID_SHA1, hash_dest, hash_src1, (16 + msg->len));
+ if (res == SECFailure)
+ goto loser;
+
+ /* second round of hashing */
+ for (i = 0; i < 16; i++)
+ hash_src2[i] = key->data[i] ^ 0x5c;
+ PORT_Memcpy(&(hash_src2[16]), hash_dest, SHA1_LENGTH);
+ res = PK11_HashBuf(SEC_OID_SHA1, mac->data, hash_src2, SHA1_LENGTH + 16);
+ if (res == SECFailure)
+ goto loser;
+
+ PORT_FreeArena(temparena, PR_TRUE);
+ return mac;
+
+loser:
+ if (temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+ if (mac != NULL)
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ return NULL;
+}
+
+/* MAC is generated per PKCS 12 section 6. It is expected that key, msg
+ * and mac_dest are pre allocated, non-NULL arrays. msg_len is passed in
+ * because it is not known how long the message actually is. String
+ * manipulation routines will not necessarily work because msg may have
+ * imbedded NULLs
+ */
+SECItem *
+sec_pkcs12_generate_mac(SECItem *key,
+ SECItem *msg,
+ PRBool old_method)
+{
+ SECStatus res = SECFailure;
+ SECItem *mac = NULL;
+ PK11Context *pk11cx = NULL;
+ SECItem ignore = { 0 };
+
+ if ((key == NULL) || (msg == NULL)) {
+ return NULL;
+ }
+
+ if (old_method == PR_TRUE) {
+ return sec_pkcs12_generate_old_mac(key, msg);
+ }
+
+ /* allocate return item */
+ mac = SECITEM_AllocItem(NULL, NULL, SHA1_LENGTH);
+ if (mac == NULL) {
+ return NULL;
+ }
+
+ pk11cx = PK11_CreateContextByRawKey(NULL, CKM_SHA_1_HMAC, PK11_OriginDerive,
+ CKA_SIGN, key, &ignore, NULL);
+ if (pk11cx == NULL) {
+ goto loser;
+ }
+
+ res = PK11_DigestBegin(pk11cx);
+ if (res == SECFailure) {
+ goto loser;
+ }
+
+ res = PK11_DigestOp(pk11cx, msg->data, msg->len);
+ if (res == SECFailure) {
+ goto loser;
+ }
+
+ res = PK11_DigestFinal(pk11cx, mac->data, &mac->len, SHA1_LENGTH);
+ if (res == SECFailure) {
+ goto loser;
+ }
+
+ PK11_DestroyContext(pk11cx, PR_TRUE);
+ pk11cx = NULL;
+
+loser:
+
+ if (res != SECSuccess) {
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ mac = NULL;
+ if (pk11cx) {
+ PK11_DestroyContext(pk11cx, PR_TRUE);
+ }
+ }
+
+ return mac;
+}
+
+/* compute the thumbprint of the DER cert and create a digest info
+ * to store it in and return the digest info.
+ * a return of NULL indicates an error.
+ */
+SGNDigestInfo *
+sec_pkcs12_compute_thumbprint(SECItem *der_cert)
+{
+ SGNDigestInfo *thumb = NULL;
+ SECItem digest;
+ PLArenaPool *temparena = NULL;
+ SECStatus rv = SECFailure;
+
+ if (der_cert == NULL)
+ return NULL;
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (temparena == NULL) {
+ return NULL;
+ }
+
+ digest.data = (unsigned char *)PORT_ArenaZAlloc(temparena,
+ sizeof(unsigned char) *
+ SHA1_LENGTH);
+ /* digest data and create digest info */
+ if (digest.data != NULL) {
+ digest.len = SHA1_LENGTH;
+ rv = PK11_HashBuf(SEC_OID_SHA1, digest.data, der_cert->data,
+ der_cert->len);
+ if (rv == SECSuccess) {
+ thumb = SGN_CreateDigestInfo(SEC_OID_SHA1,
+ digest.data,
+ digest.len);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+
+ PORT_FreeArena(temparena, PR_TRUE);
+
+ return thumb;
+}
+
+/* create a virtual password per PKCS 12, the password is converted
+ * to unicode, the salt is prepended to it, and then the whole thing
+ * is returned */
+SECItem *
+sec_pkcs12_create_virtual_password(SECItem *password, SECItem *salt,
+ PRBool swap)
+{
+ SECItem uniPwd = { siBuffer, NULL, 0 }, *retPwd = NULL;
+
+ if ((password == NULL) || (salt == NULL)) {
+ return NULL;
+ }
+
+ if (password->len == 0) {
+ uniPwd.data = (unsigned char *)PORT_ZAlloc(2);
+ uniPwd.len = 2;
+ if (!uniPwd.data) {
+ return NULL;
+ }
+ } else {
+ uniPwd.data = (unsigned char *)PORT_ZAlloc(password->len * 3);
+ uniPwd.len = password->len * 3;
+ if (!PORT_UCS2_ASCIIConversion(PR_TRUE, password->data, password->len,
+ uniPwd.data, uniPwd.len, &uniPwd.len, swap)) {
+ SECITEM_ZfreeItem(&uniPwd, PR_FALSE);
+ return NULL;
+ }
+ }
+
+ retPwd = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if (retPwd == NULL) {
+ goto loser;
+ }
+
+ /* allocate space and copy proper data */
+ retPwd->len = uniPwd.len + salt->len;
+ retPwd->data = (unsigned char *)PORT_Alloc(retPwd->len);
+ if (retPwd->data == NULL) {
+ PORT_Free(retPwd);
+ goto loser;
+ }
+
+ PORT_Memcpy(retPwd->data, salt->data, salt->len);
+ PORT_Memcpy((retPwd->data + salt->len), uniPwd.data, uniPwd.len);
+
+ SECITEM_ZfreeItem(&uniPwd, PR_FALSE);
+
+ return retPwd;
+
+loser:
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ SECITEM_ZfreeItem(&uniPwd, PR_FALSE);
+ return NULL;
+}
+
+/* appends a shrouded key to a key bag. this is used for exporting
+ * to store externally wrapped keys. it is used when importing to convert
+ * old items to new
+ */
+SECStatus
+sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag,
+ SEC_PKCS12ESPVKItem *espvk)
+{
+ int size;
+ void *mark = NULL, *dummy = NULL;
+
+ if ((bag == NULL) || (espvk == NULL))
+ return SECFailure;
+
+ mark = PORT_ArenaMark(bag->poolp);
+
+ /* grow the list */
+ size = (bag->nEspvks + 1) * sizeof(SEC_PKCS12ESPVKItem *);
+ dummy = (SEC_PKCS12ESPVKItem **)PORT_ArenaGrow(bag->poolp,
+ bag->espvks, size,
+ size + sizeof(SEC_PKCS12ESPVKItem *));
+ bag->espvks = (SEC_PKCS12ESPVKItem **)dummy;
+ if (dummy == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ bag->espvks[bag->nEspvks] = espvk;
+ bag->nEspvks++;
+ bag->espvks[bag->nEspvks] = NULL;
+
+ PORT_ArenaUnmark(bag->poolp, mark);
+ return SECSuccess;
+
+loser:
+ PORT_ArenaRelease(bag->poolp, mark);
+ return SECFailure;
+}
+
+/* search a certificate list for a nickname, a thumbprint, or both
+ * within a certificate bag. if the certificate could not be
+ * found or an error occurs, NULL is returned;
+ */
+static SEC_PKCS12CertAndCRL *
+sec_pkcs12_find_cert_in_certbag(SEC_PKCS12CertAndCRLBag *certbag,
+ SECItem *nickname, SGNDigestInfo *thumbprint)
+{
+ PRBool search_both = PR_FALSE, search_nickname = PR_FALSE;
+ int i, j;
+
+ if ((certbag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) {
+ return NULL;
+ }
+
+ if (thumbprint && nickname) {
+ search_both = PR_TRUE;
+ }
+
+ if (nickname) {
+ search_nickname = PR_TRUE;
+ }
+
+search_again:
+ i = 0;
+ while (certbag->certAndCRLs[i] != NULL) {
+ SEC_PKCS12CertAndCRL *cert = certbag->certAndCRLs[i];
+
+ if (SECOID_FindOIDTag(&cert->BagID) == SEC_OID_PKCS12_X509_CERT_CRL_BAG) {
+
+ /* check nicknames */
+ if (search_nickname) {
+ if (SECITEM_CompareItem(nickname, &cert->nickname) == SECEqual) {
+ return cert;
+ }
+ } else {
+ /* check thumbprints */
+ SECItem **derCertList;
+
+ /* get pointer to certificate list, does not need to
+ * be freed since it is within the arena which will
+ * be freed later.
+ */
+ derCertList = SEC_PKCS7GetCertificateList(&cert->value.x509->certOrCRL);
+ j = 0;
+ if (derCertList != NULL) {
+ while (derCertList[j] != NULL) {
+ SECComparison eq;
+ SGNDigestInfo *di;
+ di = sec_pkcs12_compute_thumbprint(derCertList[j]);
+ if (di) {
+ eq = SGN_CompareDigestInfo(thumbprint, di);
+ SGN_DestroyDigestInfo(di);
+ if (eq == SECEqual) {
+ /* copy the derCert for later reference */
+ cert->value.x509->derLeafCert = derCertList[j];
+ return cert;
+ }
+ } else {
+ /* an error occurred */
+ return NULL;
+ }
+ j++;
+ }
+ }
+ }
+ }
+
+ i++;
+ }
+
+ if (search_both) {
+ search_both = PR_FALSE;
+ search_nickname = PR_FALSE;
+ goto search_again;
+ }
+
+ return NULL;
+}
+
+/* search a key list for a nickname, a thumbprint, or both
+ * within a key bag. if the key could not be
+ * found or an error occurs, NULL is returned;
+ */
+static SEC_PKCS12PrivateKey *
+sec_pkcs12_find_key_in_keybag(SEC_PKCS12PrivateKeyBag *keybag,
+ SECItem *nickname, SGNDigestInfo *thumbprint)
+{
+ PRBool search_both = PR_FALSE, search_nickname = PR_FALSE;
+ int i, j;
+
+ if ((keybag == NULL) || ((nickname == NULL) && (thumbprint == NULL))) {
+ return NULL;
+ }
+
+ if (keybag->privateKeys == NULL) {
+ return NULL;
+ }
+
+ if (thumbprint && nickname) {
+ search_both = PR_TRUE;
+ }
+
+ if (nickname) {
+ search_nickname = PR_TRUE;
+ }
+
+search_again:
+ i = 0;
+ while (keybag->privateKeys[i] != NULL) {
+ SEC_PKCS12PrivateKey *key = keybag->privateKeys[i];
+
+ /* check nicknames */
+ if (search_nickname) {
+ if (SECITEM_CompareItem(nickname, &key->pvkData.nickname) == SECEqual) {
+ return key;
+ }
+ } else {
+ /* check digests */
+ SGNDigestInfo **assocCerts = key->pvkData.assocCerts;
+ if ((assocCerts == NULL) || (assocCerts[0] == NULL)) {
+ return NULL;
+ }
+
+ j = 0;
+ while (assocCerts[j] != NULL) {
+ SECComparison eq;
+ eq = SGN_CompareDigestInfo(thumbprint, assocCerts[j]);
+ if (eq == SECEqual) {
+ return key;
+ }
+ j++;
+ }
+ }
+ i++;
+ }
+
+ if (search_both) {
+ search_both = PR_FALSE;
+ search_nickname = PR_FALSE;
+ goto search_again;
+ }
+
+ return NULL;
+}
+
+/* seach the safe first then try the baggage bag
+ * safe and bag contain certs and keys to search
+ * objType is the object type to look for
+ * bagType is the type of bag that was found by sec_pkcs12_find_object
+ * index is the entity in safe->safeContents or bag->unencSecrets which
+ * is being searched
+ * nickname and thumbprint are the search criteria
+ *
+ * a return of null indicates no match
+ */
+static void *
+sec_pkcs12_try_find(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12BaggageItem *bag,
+ SECOidTag objType, SECOidTag bagType, int index,
+ SECItem *nickname, SGNDigestInfo *thumbprint)
+{
+ PRBool searchSafe;
+ int i = index;
+
+ if ((safe == NULL) && (bag == NULL)) {
+ return NULL;
+ }
+
+ searchSafe = (safe == NULL ? PR_FALSE : PR_TRUE);
+ switch (objType) {
+ case SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID:
+ if (objType == bagType) {
+ SEC_PKCS12CertAndCRLBag *certBag;
+
+ if (searchSafe) {
+ certBag = safe->contents[i]->safeContent.certAndCRLBag;
+ } else {
+ certBag = bag->unencSecrets[i]->safeContent.certAndCRLBag;
+ }
+ return sec_pkcs12_find_cert_in_certbag(certBag, nickname,
+ thumbprint);
+ }
+ break;
+ case SEC_OID_PKCS12_KEY_BAG_ID:
+ if (objType == bagType) {
+ SEC_PKCS12PrivateKeyBag *keyBag;
+
+ if (searchSafe) {
+ keyBag = safe->contents[i]->safeContent.keyBag;
+ } else {
+ keyBag = bag->unencSecrets[i]->safeContent.keyBag;
+ }
+ return sec_pkcs12_find_key_in_keybag(keyBag, nickname,
+ thumbprint);
+ }
+ break;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+/* searches both the baggage and the safe areas looking for
+ * object of specified type matching either the nickname or the
+ * thumbprint specified.
+ *
+ * safe and baggage store certs and keys
+ * objType is the OID for the bag type to be searched:
+ * SEC_OID_PKCS12_KEY_BAG_ID, or
+ * SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID
+ * nickname and thumbprint are the search criteria
+ *
+ * if no match found, NULL returned and error set
+ */
+void *
+sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage,
+ SECOidTag objType,
+ SECItem *nickname,
+ SGNDigestInfo *thumbprint)
+{
+ int i, j;
+ void *retItem;
+
+ if (((safe == NULL) && (thumbprint == NULL)) ||
+ ((nickname == NULL) && (thumbprint == NULL))) {
+ return NULL;
+ }
+
+ i = 0;
+ if ((safe != NULL) && (safe->contents != NULL)) {
+ while (safe->contents[i] != NULL) {
+ SECOidTag bagType = SECOID_FindOIDTag(&safe->contents[i]->safeBagType);
+ retItem = sec_pkcs12_try_find(safe, NULL, objType, bagType, i,
+ nickname, thumbprint);
+ if (retItem != NULL) {
+ return retItem;
+ }
+ i++;
+ }
+ }
+
+ if ((baggage != NULL) && (baggage->bags != NULL)) {
+ i = 0;
+ while (baggage->bags[i] != NULL) {
+ SEC_PKCS12BaggageItem *xbag = baggage->bags[i];
+ j = 0;
+ if (xbag->unencSecrets != NULL) {
+ while (xbag->unencSecrets[j] != NULL) {
+ SECOidTag bagType;
+ bagType = SECOID_FindOIDTag(&xbag->unencSecrets[j]->safeBagType);
+ retItem = sec_pkcs12_try_find(NULL, xbag, objType, bagType,
+ j, nickname, thumbprint);
+ if (retItem != NULL) {
+ return retItem;
+ }
+ j++;
+ }
+ }
+ i++;
+ }
+ }
+
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME);
+ return NULL;
+}
+
+/* this function converts a password to UCS2 and ensures that the
+ * required double 0 byte be placed at the end of the string (if zeroTerm
+ * is set), or the 0 bytes at the end are dropped (if zeroTerm is not set).
+ * If toUnicode is false, we convert from UCS2 to UTF8/ASCII (latter is a
+ * proper subset of the former) depending on the state of the asciiCovert
+ * flag)
+ */
+PRBool
+sec_pkcs12_convert_item_to_unicode(PLArenaPool *arena, SECItem *dest,
+ SECItem *src, PRBool zeroTerm,
+ PRBool asciiConvert, PRBool toUnicode)
+{
+ PRBool success = PR_FALSE;
+ int bufferSize;
+
+ if (!src || !dest) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return PR_FALSE;
+ }
+
+ bufferSize = src->len * 3 + 2;
+ dest->len = bufferSize;
+ if (arena) {
+ dest->data = (unsigned char *)PORT_ArenaZAlloc(arena, dest->len);
+ } else {
+ dest->data = (unsigned char *)PORT_ZAlloc(dest->len);
+ }
+
+ if (!dest->data) {
+ dest->len = 0;
+ return PR_FALSE;
+ }
+
+ if (!asciiConvert) {
+ success = PORT_UCS2_UTF8Conversion(toUnicode, src->data, src->len, dest->data,
+ dest->len, &dest->len);
+ } else {
+#ifndef IS_LITTLE_ENDIAN
+ PRBool swapUnicode = PR_FALSE;
+#else
+ PRBool swapUnicode = PR_TRUE;
+#endif
+ success = PORT_UCS2_ASCIIConversion(toUnicode, src->data, src->len, dest->data,
+ dest->len, &dest->len, swapUnicode);
+ }
+
+ if (!success) {
+ if (!arena) {
+ PORT_Free(dest->data);
+ dest->data = NULL;
+ dest->len = 0;
+ }
+ return PR_FALSE;
+ }
+
+ /* in some cases we need to add NULL terminations and in others
+ * we need to drop null terminations */
+ if (zeroTerm) {
+ /* unicode adds two nulls at the end */
+ if (toUnicode) {
+ if ((dest->len < 2) || dest->data[dest->len - 1] || dest->data[dest->len - 2]) {
+ /* we've already allocated space for these new NULLs */
+ PORT_Assert(dest->len + 2 <= bufferSize);
+ dest->len += 2;
+ dest->data[dest->len - 1] = dest->data[dest->len - 2] = 0;
+ }
+ /* ascii/utf-8 adds just 1 */
+ } else if (!dest->len || dest->data[dest->len - 1]) {
+ PORT_Assert(dest->len + 1 <= bufferSize);
+ dest->len++;
+ dest->data[dest->len - 1] = 0;
+ }
+ } else {
+ /* handle the drop case, no need to do any allocations here. */
+ if (toUnicode) {
+ while ((dest->len >= 2) && !dest->data[dest->len - 1] &&
+ !dest->data[dest->len - 2]) {
+ dest->len -= 2;
+ }
+ } else {
+ while (dest->len && !dest->data[dest->len - 1]) {
+ dest->len--;
+ }
+ }
+ }
+
+ return PR_TRUE;
+}
+
+PRBool
+sec_pkcs12_is_pkcs12_pbe_algorithm(SECOidTag algorithm)
+{
+ switch (algorithm) {
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ /* those are actually PKCS #5 v1.5 PBEs, but we
+ * historically treat them in the same way as PKCS #12
+ * PBEs */
+ case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+ case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+ return PR_TRUE;
+ default:
+ return PR_FALSE;
+ }
+}
+
+/* this function decodes a password from Unicode if necessary,
+ * according to the PBE algorithm.
+ *
+ * we assume that the pwitem is already encoded in Unicode by the
+ * caller. if the encryption scheme is not the one defined in PKCS
+ * #12, decode the pwitem back into UTF-8. NOTE: UTF-8 strings are
+ * used in the PRF without the trailing NULL */
+PRBool
+sec_pkcs12_decode_password(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag algorithm,
+ const SECItem *pwitem)
+{
+ if (!sec_pkcs12_is_pkcs12_pbe_algorithm(algorithm))
+ return sec_pkcs12_convert_item_to_unicode(arena, result,
+ (SECItem *)pwitem,
+ PR_FALSE, PR_FALSE, PR_FALSE);
+
+ return SECITEM_CopyItem(arena, result, pwitem) == SECSuccess;
+}
+
+/* this function encodes a password into Unicode if necessary,
+ * according to the PBE algorithm.
+ *
+ * we assume that the pwitem holds a raw password. if the encryption
+ * scheme is the one defined in PKCS #12, encode the password into
+ * BMPString. */
+PRBool
+sec_pkcs12_encode_password(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag algorithm,
+ const SECItem *pwitem)
+{
+ if (sec_pkcs12_is_pkcs12_pbe_algorithm(algorithm))
+ return sec_pkcs12_convert_item_to_unicode(arena, result,
+ (SECItem *)pwitem,
+ PR_TRUE, PR_TRUE, PR_TRUE);
+
+ return SECITEM_CopyItem(arena, result, pwitem) == SECSuccess;
+}
+
+/* pkcs 12 templates */
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_shroud_chooser =
+ sec_pkcs12_choose_shroud_type;
+
+const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
+ { SEC_ASN1_ANY, offsetof(SEC_PKCS12SafeBag, derSafeContent) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) },
+ { SEC_ASN1_ANY, offsetof(SEC_PKCS12CertAndCRL, derValue) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[] = {
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs),
+ SEC_PKCS12CodedCertBagTemplate },
+};
+
+const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) },
+ { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData),
+ SEC_PKCS12PVKSupportingDataTemplate_OLD },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_DYNAMIC | 0,
+ offsetof(SEC_PKCS12ESPVKItem, espvkCipherText),
+ &sec_pkcs12_shroud_chooser },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12ESPVKItem) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12ESPVKItem, espvkOID) },
+ { SEC_ASN1_INLINE, offsetof(SEC_PKCS12ESPVKItem, espvkData),
+ SEC_PKCS12PVKSupportingDataTemplate },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_DYNAMIC | 0,
+ offsetof(SEC_PKCS12ESPVKItem, espvkCipherText),
+ &sec_pkcs12_shroud_chooser },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12PVKAdditionalDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKAdditionalData) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalType) },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS12PVKAdditionalData, pvkAdditionalContent) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
+ offsetof(SEC_PKCS12PVKSupportingData, assocCerts),
+ SEC_ASN1_SUB(sgn_DigestInfoTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN,
+ offsetof(SEC_PKCS12PVKSupportingData, regenerable) },
+ { SEC_ASN1_PRINTABLE_STRING,
+ offsetof(SEC_PKCS12PVKSupportingData, nickname) },
+ { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PVKSupportingData) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_XTRN,
+ offsetof(SEC_PKCS12PVKSupportingData, assocCerts),
+ SEC_ASN1_SUB(sgn_DigestInfoTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN,
+ offsetof(SEC_PKCS12PVKSupportingData, regenerable) },
+ { SEC_ASN1_BMP_STRING,
+ offsetof(SEC_PKCS12PVKSupportingData, uniNickName) },
+ { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS12PVKSupportingData, pvkAdditionalDER) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12BaggageItemTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12BaggageItem) },
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, espvks),
+ SEC_PKCS12ESPVKItemTemplate },
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets),
+ SEC_PKCS12SafeBagTemplate },
+ /*{ SEC_ASN1_SET_OF, offsetof(SEC_PKCS12BaggageItem, unencSecrets),
+ SEC_PKCS12CodedSafeBagTemplate }, */
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12BaggageTemplate[] = {
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage, bags),
+ SEC_PKCS12BaggageItemTemplate },
+};
+
+const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[] = {
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12Baggage_OLD, espvks),
+ SEC_PKCS12ESPVKItemTemplate_OLD },
+};
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_bag_chooser =
+ sec_pkcs12_choose_bag_type;
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_bag_chooser_old =
+ sec_pkcs12_choose_bag_type_old;
+
+const SEC_ASN1Template SEC_PKCS12SafeBagTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS12SafeBag, safeContent),
+ &sec_pkcs12_bag_chooser_old },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER,
+ offsetof(SEC_PKCS12SafeBag, safeContent),
+ &sec_pkcs12_bag_chooser },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_BMP_STRING,
+ offsetof(SEC_PKCS12SafeBag, uniSafeBagName) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[] = {
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS12SafeContents, contents),
+ SEC_PKCS12SafeBagTemplate_OLD }
+};
+
+const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[] = {
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS12SafeContents, contents),
+ SEC_PKCS12SafeBagTemplate } /* here */
+};
+
+const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKey) },
+ { SEC_ASN1_INLINE, offsetof(SEC_PKCS12PrivateKey, pvkData),
+ SEC_PKCS12PVKSupportingDataTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(SEC_PKCS12PrivateKey, pkcs8data),
+ SEC_ASN1_SUB(SECKEY_PrivateKeyInfoTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PrivateKeyBag) },
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12PrivateKeyBag, privateKeys),
+ SEC_PKCS12PrivateKeyTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) },
+ { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL),
+ sec_PKCS7ContentInfoTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(SEC_PKCS12X509CertCRL, thumbprint),
+ SEC_ASN1_SUB(sgn_DigestInfoTemplate) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) },
+ { SEC_ASN1_INLINE, offsetof(SEC_PKCS12X509CertCRL, certOrCRL),
+ sec_PKCS7ContentInfoTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12X509CertCRL) },
+ { SEC_ASN1_IA5_STRING, offsetof(SEC_PKCS12SDSICert, value) },
+ { 0 }
+};
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_cert_crl_chooser_old =
+ sec_pkcs12_choose_cert_crl_type_old;
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_cert_crl_chooser =
+ sec_pkcs12_choose_cert_crl_type;
+
+const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) },
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | 0,
+ offsetof(SEC_PKCS12CertAndCRL, value),
+ &sec_pkcs12_cert_crl_chooser_old },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRL) },
+ { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12CertAndCRL, BagID) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS12CertAndCRL, value),
+ &sec_pkcs12_cert_crl_chooser },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[] = {
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs),
+ SEC_PKCS12CertAndCRLTemplate },
+};
+
+const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12CertAndCRLBag) },
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12CertAndCRLBag, certAndCRLs),
+ SEC_PKCS12CertAndCRLTemplate_OLD },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SecretAdditional) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(SEC_PKCS12SecretAdditional, secretAdditionalType) },
+ { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_EXPLICIT,
+ offsetof(SEC_PKCS12SecretAdditional, secretAdditionalContent) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SecretTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) },
+ { SEC_ASN1_BMP_STRING, offsetof(SEC_PKCS12Secret, uniSecretName) },
+ { SEC_ASN1_ANY, offsetof(SEC_PKCS12Secret, value) },
+ { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS12Secret, secretAdditional),
+ SEC_PKCS12SecretAdditionalTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12Secret) },
+ { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS12SecretItem, secret), SEC_PKCS12SecretTemplate },
+ { SEC_ASN1_INLINE | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS12SecretItem, subFolder), SEC_PKCS12SafeBagTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[] = {
+ { SEC_ASN1_SET_OF, offsetof(SEC_PKCS12SecretBag, secrets),
+ SEC_PKCS12SecretItemTemplate },
+};
+
+const SEC_ASN1Template SEC_PKCS12MacDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(SEC_PKCS12MacData, safeMac),
+ SEC_ASN1_SUB(sgn_DigestInfoTemplate) },
+ { SEC_ASN1_BIT_STRING, offsetof(SEC_PKCS12MacData, macSalt) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) },
+ { SEC_ASN1_OPTIONAL |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS12PFXItem, macData), SEC_PKCS12MacDataTemplate },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS12PFXItem, authSafe),
+ sec_PKCS7ContentInfoTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12PFXItem) },
+ { SEC_ASN1_OPTIONAL |
+ SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(SEC_PKCS12PFXItem, old_safeMac),
+ SEC_ASN1_SUB(sgn_DigestInfoTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_BIT_STRING,
+ offsetof(SEC_PKCS12PFXItem, old_macSalt) },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS12PFXItem, authSafe),
+ sec_PKCS7ContentInfoTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS12AuthenticatedSafe, version) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_OBJECT_ID,
+ offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) },
+ { SEC_ASN1_BIT_STRING | SEC_ASN1_OPTIONAL,
+ offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS12AuthenticatedSafe, baggage.bags),
+ SEC_PKCS12BaggageItemTemplate },
+ { SEC_ASN1_POINTER,
+ offsetof(SEC_PKCS12AuthenticatedSafe, safe),
+ sec_PKCS7ContentInfoTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12AuthenticatedSafe) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS12AuthenticatedSafe, version) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS12AuthenticatedSafe, transportMode) },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(SEC_PKCS12AuthenticatedSafe, privacySalt) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS12AuthenticatedSafe, old_baggage),
+ SEC_PKCS12BaggageTemplate_OLD },
+ { SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS12AuthenticatedSafe, old_safe),
+ sec_PKCS7ContentInfoTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12PrivateKeyBagTemplate }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate_OLD }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12CertAndCRLBagTemplate }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12SecretBagTemplate }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate_OLD }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12X509CertCRLTemplate }
+};
+
+const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS12SDSICertTemplate }
+};
diff --git a/security/nss/lib/pkcs12/p12local.h b/security/nss/lib/pkcs12/p12local.h
new file mode 100644
index 0000000000..99068e21ef
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12local.h
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _P12LOCAL_H_
+#define _P12LOCAL_H_
+
+#include "plarena.h"
+#include "secoidt.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "certt.h"
+#include "secpkcs7.h"
+#include "pkcs12.h"
+#include "p12.h"
+
+/* helper functions */
+extern const SEC_ASN1Template *
+sec_pkcs12_choose_bag_type(void *src_or_dest, PRBool encoding);
+extern const SEC_ASN1Template *
+sec_pkcs12_choose_cert_crl_type(void *src_or_dest, PRBool encoding);
+extern const SEC_ASN1Template *
+sec_pkcs12_choose_shroud_type(void *src_or_dest, PRBool encoding);
+extern SECItem *sec_pkcs12_generate_salt(void);
+extern SECItem *sec_pkcs12_generate_key_from_password(SECOidTag algorithm,
+ SECItem *salt, SECItem *password);
+extern SECItem *sec_pkcs12_generate_mac(SECItem *key, SECItem *msg,
+ PRBool old_method);
+extern SGNDigestInfo *sec_pkcs12_compute_thumbprint(SECItem *der_cert);
+extern SECItem *sec_pkcs12_create_virtual_password(SECItem *password,
+ SECItem *salt, PRBool swapUnicodeBytes);
+extern SECStatus sec_pkcs12_append_shrouded_key(SEC_PKCS12BaggageItem *bag,
+ SEC_PKCS12ESPVKItem *espvk);
+extern void *sec_pkcs12_find_object(SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage, SECOidTag objType,
+ SECItem *nickname, SGNDigestInfo *thumbprint);
+extern PRBool sec_pkcs12_convert_item_to_unicode(PLArenaPool *arena, SECItem *dest,
+ SECItem *src, PRBool zeroTerm,
+ PRBool asciiConvert, PRBool toUnicode);
+extern CK_MECHANISM_TYPE sec_pkcs12_algtag_to_mech(SECOidTag algtag);
+extern CK_MECHANISM_TYPE sec_pkcs12_algtag_to_keygen_mech(SECOidTag algtag);
+
+/* create functions */
+extern SEC_PKCS12PFXItem *sec_pkcs12_new_pfx(void);
+extern SEC_PKCS12SafeContents *sec_pkcs12_create_safe_contents(
+ PLArenaPool *poolp);
+extern SEC_PKCS12Baggage *sec_pkcs12_create_baggage(PLArenaPool *poolp);
+extern SEC_PKCS12BaggageItem *sec_pkcs12_create_external_bag(SEC_PKCS12Baggage *luggage);
+extern void SEC_PKCS12DestroyPFX(SEC_PKCS12PFXItem *pfx);
+extern SEC_PKCS12AuthenticatedSafe *sec_pkcs12_new_asafe(PLArenaPool *poolp);
+
+/* conversion from old to new */
+extern SEC_PKCS12DecoderContext *
+sec_PKCS12ConvertOldSafeToNew(PLArenaPool *arena, PK11SlotInfo *slot,
+ PRBool swapUnicode, SECItem *pwitem,
+ void *wincx, SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage);
+
+extern PRBool sec_pkcs12_is_pkcs12_pbe_algorithm(SECOidTag algorithm);
+
+extern PRBool sec_pkcs12_decode_password(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag algorithm,
+ const SECItem *pwitem);
+extern PRBool sec_pkcs12_encode_password(PLArenaPool *arena,
+ SECItem *result,
+ SECOidTag algorithm,
+ const SECItem *pwitem);
+
+#endif
diff --git a/security/nss/lib/pkcs12/p12plcy.c b/security/nss/lib/pkcs12/p12plcy.c
new file mode 100644
index 0000000000..5c1754dce1
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12plcy.c
@@ -0,0 +1,106 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "p12plcy.h"
+#include "secoid.h"
+#include "secport.h"
+#include "secpkcs5.h"
+#include "secerr.h"
+
+#define PKCS12_NULL 0x0000
+
+typedef struct pkcs12SuiteMapStr {
+ SECOidTag algTag;
+ unsigned int keyLengthBits; /* in bits */
+ unsigned long suite;
+ PRBool allowed;
+ PRBool preferred;
+} pkcs12SuiteMap;
+
+static pkcs12SuiteMap pkcs12SuiteMaps[] = {
+ { SEC_OID_RC4, 40, PKCS12_RC4_40, PR_FALSE, PR_FALSE },
+ { SEC_OID_RC4, 128, PKCS12_RC4_128, PR_FALSE, PR_FALSE },
+ { SEC_OID_RC2_CBC, 40, PKCS12_RC2_CBC_40, PR_FALSE, PR_TRUE },
+ { SEC_OID_RC2_CBC, 128, PKCS12_RC2_CBC_128, PR_FALSE, PR_FALSE },
+ { SEC_OID_DES_CBC, 64, PKCS12_DES_56, PR_FALSE, PR_FALSE },
+ { SEC_OID_DES_EDE3_CBC, 192, PKCS12_DES_EDE3_168, PR_FALSE, PR_FALSE },
+ { SEC_OID_AES_128_CBC, 128, PKCS12_AES_CBC_128, PR_FALSE, PR_FALSE },
+ { SEC_OID_AES_192_CBC, 192, PKCS12_AES_CBC_192, PR_FALSE, PR_FALSE },
+ { SEC_OID_AES_256_CBC, 256, PKCS12_AES_CBC_256, PR_FALSE, PR_FALSE },
+ { SEC_OID_UNKNOWN, 0, PKCS12_NULL, PR_FALSE, PR_FALSE },
+ { SEC_OID_UNKNOWN, 0, 0L, PR_FALSE, PR_FALSE }
+};
+
+/* determine if algid is an algorithm which is allowed */
+static PRBool
+sec_PKCS12Allowed(SECOidTag alg)
+{
+ PRUint32 policy;
+ SECStatus rv;
+
+ rv = NSS_GetAlgorithmPolicy(alg, &policy);
+ if (rv != SECSuccess) {
+ return PR_FALSE;
+ }
+ if (policy & NSS_USE_ALG_IN_PKCS12) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRBool
+SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid)
+{
+ SECOidTag algId;
+
+ algId = SEC_PKCS5GetCryptoAlgorithm(algid);
+ if (algId == SEC_OID_UNKNOWN) {
+ return PR_FALSE;
+ }
+ return sec_PKCS12Allowed(algId);
+}
+
+/* is any encryption allowed? */
+PRBool
+SEC_PKCS12IsEncryptionAllowed(void)
+{
+ int i;
+
+ for (i = 0; pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN; i++) {
+ /* we're going to return true here if any of the traditional
+ * algorithms are enabled */
+ if (sec_PKCS12Allowed(pkcs12SuiteMaps[i].algTag)) {
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+/* keep the traditional enable/disable for old ciphers so old applications
+ * continue to work. This only works for the traditional pkcs12 values,
+ * you need to use NSS_SetAlgorithmPolicy directly for other ciphers. */
+SECStatus
+SEC_PKCS12EnableCipher(long which, int on)
+{
+ int i;
+ PRUint32 set = on ? NSS_USE_ALG_IN_PKCS12 : 0;
+ PRUint32 clear = on ? 0 : NSS_USE_ALG_IN_PKCS12;
+
+ for (i = 0; pkcs12SuiteMaps[i].suite != 0L; i++) {
+ if (pkcs12SuiteMaps[i].suite == (unsigned long)which) {
+ return NSS_SetAlgorithmPolicy(pkcs12SuiteMaps[i].algTag, set, clear);
+ }
+ }
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+}
+
+SECStatus
+SEC_PKCS12SetPreferredCipher(long which, int on)
+{
+ /* nothing looked at the preferences in the suite maps, so this function
+ * has always been a noop */
+ return SECSuccess;
+}
diff --git a/security/nss/lib/pkcs12/p12plcy.h b/security/nss/lib/pkcs12/p12plcy.h
new file mode 100644
index 0000000000..d3f818d49a
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12plcy.h
@@ -0,0 +1,25 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _P12PLCY_H_
+#define _P12PLCY_H_
+
+#include "secoid.h"
+#include "ciferfam.h"
+
+SEC_BEGIN_PROTOS
+
+/* for the algid specified, can we decrypt it ? */
+extern PRBool SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid);
+
+/* is encryption allowed? */
+extern PRBool SEC_PKCS12IsEncryptionAllowed(void);
+
+/* enable a cipher for encryption/decryption */
+extern SECStatus SEC_PKCS12EnableCipher(long which, int on);
+
+/* return the preferred cipher for encryption */
+extern SECStatus SEC_PKCS12SetPreferredCipher(long which, int on);
+
+SEC_END_PROTOS
+#endif
diff --git a/security/nss/lib/pkcs12/p12t.h b/security/nss/lib/pkcs12/p12t.h
new file mode 100644
index 0000000000..d449afdd89
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12t.h
@@ -0,0 +1,156 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _P12T_H_
+#define _P12T_H_
+
+#include "secoid.h"
+#include "keythi.h"
+#include "pkcs11.h"
+#include "secpkcs7.h"
+#include "secdig.h" /* for SGNDigestInfo */
+#include "pkcs12t.h"
+
+#define SEC_PKCS12_VERSION 3
+
+/* structure declarations */
+typedef struct sec_PKCS12PFXItemStr sec_PKCS12PFXItem;
+typedef struct sec_PKCS12MacDataStr sec_PKCS12MacData;
+typedef struct sec_PKCS12AuthenticatedSafeStr sec_PKCS12AuthenticatedSafe;
+typedef struct sec_PKCS12SafeContentsStr sec_PKCS12SafeContents;
+typedef struct sec_PKCS12SafeBagStr sec_PKCS12SafeBag;
+typedef struct sec_PKCS12PKCS8ShroudedKeyBagStr sec_PKCS12PKCS8ShroudedKeyBag;
+typedef struct sec_PKCS12CertBagStr sec_PKCS12CertBag;
+typedef struct sec_PKCS12CRLBagStr sec_PKCS12CRLBag;
+typedef struct sec_PKCS12SecretBag sec_PKCS12SecretBag;
+typedef struct sec_PKCS12AttributeStr sec_PKCS12Attribute;
+
+struct sec_PKCS12CertBagStr {
+ /* what type of cert is stored? */
+ SECItem bagID;
+
+ /* certificate information */
+ union {
+ SECItem x509Cert;
+ SECItem SDSICert;
+ } value;
+};
+
+struct sec_PKCS12CRLBagStr {
+ /* what type of cert is stored? */
+ SECItem bagID;
+
+ /* certificate information */
+ union {
+ SECItem x509CRL;
+ } value;
+};
+
+struct sec_PKCS12SecretBag {
+ /* what type of secret? */
+ SECItem secretType;
+
+ /* secret information. ssshhhh be vewy vewy quiet. */
+ SECItem secretContent;
+};
+
+struct sec_PKCS12AttributeStr {
+ SECItem attrType;
+ SECItem **attrValue;
+};
+
+struct sec_PKCS12SafeBagStr {
+
+ /* What type of bag are we using? */
+ SECItem safeBagType;
+
+ /* Dependent upon the type of bag being used. */
+ union {
+ SECKEYPrivateKeyInfo *pkcs8KeyBag;
+ SECKEYEncryptedPrivateKeyInfo *pkcs8ShroudedKeyBag;
+ sec_PKCS12CertBag *certBag;
+ sec_PKCS12CRLBag *crlBag;
+ sec_PKCS12SecretBag *secretBag;
+ sec_PKCS12SafeContents *safeContents;
+ SECItem *unknownBag;
+ } safeBagContent;
+
+ sec_PKCS12Attribute **attribs;
+
+ /* used locally */
+ SECOidData *bagTypeTag;
+ PLArenaPool *arena;
+ unsigned int nAttribs;
+
+ /* used for validation/importing */
+ PRBool problem, noInstall, validated, hasKey, unused, installed;
+ int error;
+
+ PRBool swapUnicodeBytes;
+ PK11SlotInfo *slot;
+ SECItem *pwitem;
+ PRBool oldBagType;
+ SECPKCS12TargetTokenCAs tokenCAs;
+};
+
+struct sec_PKCS12SafeContentsStr {
+ sec_PKCS12SafeBag **safeBags;
+ SECItem **encodedSafeBags;
+
+ /* used locally */
+ PLArenaPool *arena;
+ unsigned int bagCount;
+};
+
+struct sec_PKCS12MacDataStr {
+ SGNDigestInfo safeMac;
+ SECItem macSalt;
+ SECItem iter;
+};
+
+struct sec_PKCS12PFXItemStr {
+
+ SECItem version;
+
+ /* Content type will either be Data (password integrity mode)
+ * or signedData (public-key integrity mode)
+ */
+ SEC_PKCS7ContentInfo *authSafe;
+ SECItem encodedAuthSafe;
+
+ /* Only present in password integrity mode */
+ sec_PKCS12MacData macData;
+ SECItem encodedMacData;
+};
+
+struct sec_PKCS12AuthenticatedSafeStr {
+ /* Content type will either be encryptedData (password privacy mode)
+ * or envelopedData (public-key privacy mode)
+ */
+ SEC_PKCS7ContentInfo **safes;
+ SECItem **encodedSafes;
+
+ /* used locally */
+ unsigned int safeCount;
+ SECItem dummySafe;
+};
+
+extern const SEC_ASN1Template sec_PKCS12PFXItemTemplate[];
+extern const SEC_ASN1Template sec_PKCS12MacDataTemplate[];
+extern const SEC_ASN1Template sec_PKCS12AuthenticatedSafeTemplate[];
+extern const SEC_ASN1Template sec_PKCS12SafeContentsTemplate[];
+extern const SEC_ASN1Template sec_PKCS12SafeContentsDecodeTemplate[];
+extern const SEC_ASN1Template sec_PKCS12NestedSafeContentsDecodeTemplate[];
+extern const SEC_ASN1Template sec_PKCS12CertBagTemplate[];
+extern const SEC_ASN1Template sec_PKCS12CRLBagTemplate[];
+extern const SEC_ASN1Template sec_PKCS12SecretBagTemplate[];
+extern const SEC_ASN1Template sec_PKCS12PointerToCertBagTemplate[];
+extern const SEC_ASN1Template sec_PKCS12PointerToCRLBagTemplate[];
+extern const SEC_ASN1Template sec_PKCS12PointerToSecretBagTemplate[];
+extern const SEC_ASN1Template sec_PKCS12PointerToSafeContentsTemplate[];
+extern const SEC_ASN1Template sec_PKCS12AttributeTemplate[];
+extern const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[];
+extern const SEC_ASN1Template sec_PKCS12SafeBagTemplate[];
+
+#endif
diff --git a/security/nss/lib/pkcs12/p12tmpl.c b/security/nss/lib/pkcs12/p12tmpl.c
new file mode 100644
index 0000000000..b08384f66a
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12tmpl.c
@@ -0,0 +1,290 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "plarena.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "seccomon.h"
+#include "secport.h"
+#include "cert.h"
+#include "secpkcs7.h"
+#include "secasn1.h"
+#include "p12t.h"
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(sgn_DigestInfoTemplate)
+
+static const SEC_ASN1Template *
+sec_pkcs12_choose_safe_bag_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ sec_PKCS12SafeBag *safeBag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ safeBag = (sec_PKCS12SafeBag *)src_or_dest;
+
+ oiddata = SECOID_FindOID(&safeBag->safeBagType);
+ if (oiddata == NULL) {
+ return SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ break;
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ theTemplate = SEC_ASN1_GET(SECKEY_PointerToPrivateKeyInfoTemplate);
+ break;
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ theTemplate = sec_PKCS12PointerToCertBagTemplate;
+ break;
+ case SEC_OID_PKCS12_V1_CRL_BAG_ID:
+ theTemplate = sec_PKCS12PointerToCRLBagTemplate;
+ break;
+ case SEC_OID_PKCS12_V1_SECRET_BAG_ID:
+ theTemplate = sec_PKCS12PointerToSecretBagTemplate;
+ break;
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ theTemplate =
+ SEC_ASN1_GET(SECKEY_PointerToEncryptedPrivateKeyInfoTemplate);
+ break;
+ case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
+ if (encoding) {
+ theTemplate = sec_PKCS12PointerToSafeContentsTemplate;
+ } else {
+ theTemplate = SEC_ASN1_GET(SEC_PointerToAnyTemplate);
+ }
+ break;
+ }
+ return theTemplate;
+}
+
+static const SEC_ASN1Template *
+sec_pkcs12_choose_crl_bag_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ sec_PKCS12CRLBag *crlbag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ crlbag = (sec_PKCS12CRLBag *)src_or_dest;
+
+ oiddata = SECOID_FindOID(&crlbag->bagID);
+ if (oiddata == NULL) {
+ return SEC_ASN1_GET(SEC_AnyTemplate);
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+ break;
+ case SEC_OID_PKCS9_X509_CRL:
+ theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
+ break;
+ }
+ return theTemplate;
+}
+
+static const SEC_ASN1Template *
+sec_pkcs12_choose_cert_bag_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ sec_PKCS12CertBag *certbag;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ certbag = (sec_PKCS12CertBag *)src_or_dest;
+
+ oiddata = SECOID_FindOID(&certbag->bagID);
+ if (oiddata == NULL) {
+ return SEC_ASN1_GET(SEC_AnyTemplate);
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+ break;
+ case SEC_OID_PKCS9_X509_CERT:
+ theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
+ break;
+ case SEC_OID_PKCS9_SDSI_CERT:
+ theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
+ break;
+ }
+ return theTemplate;
+}
+
+static const SEC_ASN1Template *
+sec_pkcs12_choose_attr_type(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ sec_PKCS12Attribute *attr;
+ SECOidData *oiddata;
+
+ if (src_or_dest == NULL) {
+ return NULL;
+ }
+
+ attr = (sec_PKCS12Attribute *)src_or_dest;
+
+ oiddata = SECOID_FindOID(&attr->attrType);
+ if (oiddata == NULL) {
+ return SEC_ASN1_GET(SEC_AnyTemplate);
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
+ break;
+ case SEC_OID_PKCS9_FRIENDLY_NAME:
+ theTemplate = SEC_ASN1_GET(SEC_BMPStringTemplate);
+ break;
+ case SEC_OID_PKCS9_LOCAL_KEY_ID:
+ theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
+ break;
+ case SEC_OID_PKCS12_KEY_USAGE:
+ theTemplate = SEC_ASN1_GET(SEC_BitStringTemplate);
+ break;
+ }
+
+ return theTemplate;
+}
+
+const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_MAY_STREAM, 0, sec_PKCS7ContentInfoTemplate }
+};
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_crl_bag_chooser =
+ sec_pkcs12_choose_crl_bag_type;
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_cert_bag_chooser =
+ sec_pkcs12_choose_cert_bag_type;
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_safe_bag_chooser =
+ sec_pkcs12_choose_safe_bag_type;
+
+static const SEC_ASN1TemplateChooserPtr sec_pkcs12_attr_chooser =
+ sec_pkcs12_choose_attr_type;
+
+const SEC_ASN1Template sec_PKCS12PointerToCertBagTemplate[] = {
+ { SEC_ASN1_POINTER, 0, sec_PKCS12CertBagTemplate }
+};
+
+const SEC_ASN1Template sec_PKCS12PointerToCRLBagTemplate[] = {
+ { SEC_ASN1_POINTER, 0, sec_PKCS12CRLBagTemplate }
+};
+
+const SEC_ASN1Template sec_PKCS12PointerToSecretBagTemplate[] = {
+ { SEC_ASN1_POINTER, 0, sec_PKCS12SecretBagTemplate }
+};
+
+const SEC_ASN1Template sec_PKCS12PointerToSafeContentsTemplate[] = {
+ { SEC_ASN1_POINTER, 0, sec_PKCS12SafeContentsTemplate }
+};
+
+const SEC_ASN1Template sec_PKCS12PFXItemTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, 0, NULL,
+ sizeof(sec_PKCS12PFXItem) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER,
+ offsetof(sec_PKCS12PFXItem, version) },
+ { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM,
+ offsetof(sec_PKCS12PFXItem, encodedAuthSafe) },
+ { SEC_ASN1_ANY | SEC_ASN1_MAY_STREAM,
+ offsetof(sec_PKCS12PFXItem, encodedMacData) },
+ { 0 }
+};
+
+const SEC_ASN1Template sec_PKCS12MacDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12MacData) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(sec_PKCS12MacData, safeMac),
+ SEC_ASN1_SUB(sgn_DigestInfoTemplate) },
+ { SEC_ASN1_OCTET_STRING, offsetof(sec_PKCS12MacData, macSalt) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_INTEGER, offsetof(sec_PKCS12MacData, iter) },
+ { 0 }
+};
+
+const SEC_ASN1Template sec_PKCS12AuthenticatedSafeTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM | SEC_ASN1_XTRN,
+ offsetof(sec_PKCS12AuthenticatedSafe, encodedSafes),
+ SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+const SEC_ASN1Template sec_PKCS12SafeBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM, 0, NULL,
+ sizeof(sec_PKCS12SafeBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12SafeBag, safeBagType) },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_DYNAMIC | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(sec_PKCS12SafeBag, safeBagContent),
+ &sec_pkcs12_safe_bag_chooser },
+ { SEC_ASN1_SET_OF | SEC_ASN1_OPTIONAL, offsetof(sec_PKCS12SafeBag, attribs),
+ sec_PKCS12AttributeTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template sec_PKCS12SafeContentsTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM,
+ offsetof(sec_PKCS12SafeContents, safeBags),
+ sec_PKCS12SafeBagTemplate }
+};
+
+const SEC_ASN1Template sec_PKCS12SequenceOfAnyTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM | SEC_ASN1_XTRN, 0,
+ SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+const SEC_ASN1Template sec_PKCS12NestedSafeContentsDecodeTemplate[] = {
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | 0,
+ offsetof(sec_PKCS12SafeContents, encodedSafeBags),
+ sec_PKCS12SequenceOfAnyTemplate }
+};
+
+const SEC_ASN1Template sec_PKCS12SafeContentsDecodeTemplate[] = {
+ { SEC_ASN1_SEQUENCE_OF | SEC_ASN1_MAY_STREAM | SEC_ASN1_XTRN,
+ offsetof(sec_PKCS12SafeContents, encodedSafeBags),
+ SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+const SEC_ASN1Template sec_PKCS12CRLBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12CRLBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12CRLBag, bagID) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_POINTER,
+ offsetof(sec_PKCS12CRLBag, value), &sec_pkcs12_crl_bag_chooser },
+ { 0 }
+};
+
+const SEC_ASN1Template sec_PKCS12CertBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12CertBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12CertBag, bagID) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(sec_PKCS12CertBag, value), &sec_pkcs12_cert_bag_chooser },
+ { 0 }
+};
+
+const SEC_ASN1Template sec_PKCS12SecretBagTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12SecretBag) },
+ { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12SecretBag, secretType) },
+ { SEC_ASN1_ANY, offsetof(sec_PKCS12SecretBag, secretContent) },
+ { 0 }
+};
+
+const SEC_ASN1Template sec_PKCS12AttributeTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(sec_PKCS12Attribute) },
+ { SEC_ASN1_OBJECT_ID, offsetof(sec_PKCS12Attribute, attrType) },
+ { SEC_ASN1_SET_OF | SEC_ASN1_DYNAMIC,
+ offsetof(sec_PKCS12Attribute, attrValue),
+ &sec_pkcs12_attr_chooser },
+ { 0 }
+};
diff --git a/security/nss/lib/pkcs12/pkcs12.gyp b/security/nss/lib/pkcs12/pkcs12.gyp
new file mode 100644
index 0000000000..5b296cc56c
--- /dev/null
+++ b/security/nss/lib/pkcs12/pkcs12.gyp
@@ -0,0 +1,29 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+{
+ 'includes': [
+ '../../coreconf/config.gypi'
+ ],
+ 'targets': [
+ {
+ 'target_name': 'pkcs12',
+ 'type': 'static_library',
+ 'sources': [
+ 'p12creat.c',
+ 'p12d.c',
+ 'p12dec.c',
+ 'p12e.c',
+ 'p12local.c',
+ 'p12plcy.c',
+ 'p12tmpl.c'
+ ],
+ 'dependencies': [
+ '<(DEPTH)/exports.gyp:nss_exports'
+ ]
+ }
+ ],
+ 'variables': {
+ 'module': 'nss'
+ }
+} \ No newline at end of file
diff --git a/security/nss/lib/pkcs12/pkcs12.h b/security/nss/lib/pkcs12/pkcs12.h
new file mode 100644
index 0000000000..57b3263fb9
--- /dev/null
+++ b/security/nss/lib/pkcs12/pkcs12.h
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PKCS12_H_
+#define _PKCS12_H_
+
+#include "pkcs12t.h"
+#include "p12.h"
+
+SEC_BEGIN_PROTOS
+
+typedef SECItem *(*SEC_PKCS12GetPassword)(void *arg);
+
+/* Decode functions */
+/* Import a PFX item.
+ * der_pfx is the der-encoded pfx item to import.
+ * pbef, and pbefarg are used to retrieve passwords for the HMAC,
+ * and any passwords needed for passing to PKCS5 encryption
+ * routines.
+ * algorithm is the algorithm by which private keys are stored in
+ * the key database. this could be a specific algorithm or could
+ * be based on a global setting.
+ * slot is the slot to where the certificates will be placed. if NULL,
+ * the internal key slot is used.
+ * If the process is successful, a SECSuccess is returned, otherwise
+ * a failure occurred.
+ */
+SECStatus
+SEC_PKCS12PutPFX(SECItem *der_pfx, SECItem *pwitem,
+ SEC_PKCS12NicknameCollisionCallback ncCall,
+ PK11SlotInfo *slot, void *wincx);
+
+/* check the first two bytes of a file to make sure that it matches
+ * the desired header for a PKCS 12 file
+ */
+PRBool SEC_PKCS12ValidData(char *buf, int bufLen, long int totalLength);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pkcs12/pkcs12t.h b/security/nss/lib/pkcs12/pkcs12t.h
new file mode 100644
index 0000000000..db10d28af7
--- /dev/null
+++ b/security/nss/lib/pkcs12/pkcs12t.h
@@ -0,0 +1,341 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _PKCS12T_H_
+#define _PKCS12T_H_
+
+#include "seccomon.h"
+#include "secoid.h"
+#include "cert.h"
+#include "keythi.h"
+#include "plarena.h"
+#include "secpkcs7.h"
+#include "secdig.h" /* for SGNDigestInfo */
+
+typedef enum {
+ SECPKCS12TargetTokenNoCAs, /* CA get loaded intothe fixed token,
+ * User certs go to target token */
+ SECPKCS12TargetTokenIntermediateCAs, /* User certs and intermediates go to
+ * target token, root certs got to
+ * fixed token */
+ SECPKCS12TargetTokenAllCAs /* All certs go to target token */
+} SECPKCS12TargetTokenCAs;
+
+/* PKCS12 Structures */
+typedef struct SEC_PKCS12PFXItemStr SEC_PKCS12PFXItem;
+typedef struct SEC_PKCS12MacDataStr SEC_PKCS12MacData;
+typedef struct SEC_PKCS12AuthenticatedSafeStr SEC_PKCS12AuthenticatedSafe;
+typedef struct SEC_PKCS12BaggageItemStr SEC_PKCS12BaggageItem;
+typedef struct SEC_PKCS12BaggageStr SEC_PKCS12Baggage;
+typedef struct SEC_PKCS12Baggage_OLDStr SEC_PKCS12Baggage_OLD;
+typedef struct SEC_PKCS12ESPVKItemStr SEC_PKCS12ESPVKItem;
+typedef struct SEC_PKCS12PVKSupportingDataStr SEC_PKCS12PVKSupportingData;
+typedef struct SEC_PKCS12PVKAdditionalDataStr SEC_PKCS12PVKAdditionalData;
+typedef struct SEC_PKCS12SafeContentsStr SEC_PKCS12SafeContents;
+typedef struct SEC_PKCS12SafeBagStr SEC_PKCS12SafeBag;
+typedef struct SEC_PKCS12PrivateKeyStr SEC_PKCS12PrivateKey;
+typedef struct SEC_PKCS12PrivateKeyBagStr SEC_PKCS12PrivateKeyBag;
+typedef struct SEC_PKCS12CertAndCRLBagStr SEC_PKCS12CertAndCRLBag;
+typedef struct SEC_PKCS12CertAndCRLStr SEC_PKCS12CertAndCRL;
+typedef struct SEC_PKCS12X509CertCRLStr SEC_PKCS12X509CertCRL;
+typedef struct SEC_PKCS12SDSICertStr SEC_PKCS12SDSICert;
+typedef struct SEC_PKCS12SecretStr SEC_PKCS12Secret;
+typedef struct SEC_PKCS12SecretAdditionalStr SEC_PKCS12SecretAdditional;
+typedef struct SEC_PKCS12SecretItemStr SEC_PKCS12SecretItem;
+typedef struct SEC_PKCS12SecretBagStr SEC_PKCS12SecretBag;
+
+typedef SECItem *(*SEC_PKCS12PasswordFunc)(SECItem *args);
+
+/* PKCS12 types */
+
+/* stores shrouded keys */
+struct SEC_PKCS12BaggageStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12BaggageItem **bags;
+
+ int luggage_size; /* used locally */
+};
+
+/* additional data to be associated with keys. currently there
+ * is nothing defined to be stored here. allows future expansion.
+ */
+struct SEC_PKCS12PVKAdditionalDataStr {
+ PLArenaPool *poolp;
+ SECOidData *pvkAdditionalTypeTag; /* used locally */
+ SECItem pvkAdditionalType;
+ SECItem pvkAdditionalContent;
+};
+
+/* cert and other supporting data for private keys. used
+ * for both shrouded and non-shrouded keys.
+ */
+struct SEC_PKCS12PVKSupportingDataStr {
+ PLArenaPool *poolp;
+ SGNDigestInfo **assocCerts;
+ SECItem regenerable;
+ SECItem nickname;
+ SEC_PKCS12PVKAdditionalData pvkAdditional;
+ SECItem pvkAdditionalDER;
+
+ SECItem uniNickName;
+ /* used locally */
+ int nThumbs;
+};
+
+/* shrouded key structure. supports only pkcs8 shrouding
+ * currently.
+ */
+struct SEC_PKCS12ESPVKItemStr {
+ PLArenaPool *poolp; /* used locally */
+ SECOidData *espvkTag; /* used locally */
+ SECItem espvkOID;
+ SEC_PKCS12PVKSupportingData espvkData;
+ union {
+ SECKEYEncryptedPrivateKeyInfo *pkcs8KeyShroud;
+ } espvkCipherText;
+
+ PRBool duplicate; /* used locally */
+ PRBool problem_cert; /* used locally */
+ PRBool single_cert; /* used locally */
+ int nCerts; /* used locally */
+ SECItem derCert; /* used locally */
+};
+
+/* generic bag store for the safe. safeBagType identifies
+ * the type of bag stored.
+ */
+struct SEC_PKCS12SafeBagStr {
+ PLArenaPool *poolp;
+ SECOidData *safeBagTypeTag; /* used locally */
+ SECItem safeBagType;
+ union {
+ SEC_PKCS12PrivateKeyBag *keyBag;
+ SEC_PKCS12CertAndCRLBag *certAndCRLBag;
+ SEC_PKCS12SecretBag *secretBag;
+ } safeContent;
+
+ SECItem derSafeContent;
+ SECItem safeBagName;
+
+ SECItem uniSafeBagName;
+};
+
+/* stores private keys and certificates in a list. each safebag
+ * has an ID identifying the type of content stored.
+ */
+struct SEC_PKCS12SafeContentsStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12SafeBag **contents;
+
+ /* used for tracking purposes */
+ int safe_size;
+ PRBool old;
+ PRBool swapUnicode;
+ PRBool possibleSwapUnicode;
+};
+
+/* private key structure which holds encrypted private key and
+ * supporting data including nickname and certificate thumbprint.
+ */
+struct SEC_PKCS12PrivateKeyStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12PVKSupportingData pvkData;
+ SECKEYPrivateKeyInfo pkcs8data; /* borrowed from PKCS 8 */
+
+ PRBool duplicate; /* used locally */
+ PRBool problem_cert; /* used locally */
+ PRBool single_cert; /* used locally */
+ int nCerts; /* used locally */
+ SECItem derCert; /* used locally */
+};
+
+/* private key bag, holds a (null terminated) list of private key
+ * structures.
+ */
+struct SEC_PKCS12PrivateKeyBagStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12PrivateKey **privateKeys;
+
+ int bag_size; /* used locally */
+};
+
+/* container to hold certificates. currently supports x509
+ * and sdsi certificates
+ */
+struct SEC_PKCS12CertAndCRLStr {
+ PLArenaPool *poolp;
+ SECOidData *BagTypeTag; /* used locally */
+ SECItem BagID;
+ union {
+ SEC_PKCS12X509CertCRL *x509;
+ SEC_PKCS12SDSICert *sdsi;
+ } value;
+
+ SECItem derValue;
+ SECItem nickname; /* used locally */
+ PRBool duplicate; /* used locally */
+};
+
+/* x509 certificate structure. typically holds the der encoding
+ * of the x509 certificate. thumbprint contains a digest of the
+ * certificate
+ */
+struct SEC_PKCS12X509CertCRLStr {
+ PLArenaPool *poolp;
+ SEC_PKCS7ContentInfo certOrCRL;
+ SGNDigestInfo thumbprint;
+
+ SECItem *derLeafCert; /* used locally */
+};
+
+/* sdsi certificate structure. typically holds the der encoding
+ * of the sdsi certificate. thumbprint contains a digest of the
+ * certificate
+ */
+struct SEC_PKCS12SDSICertStr {
+ PLArenaPool *poolp;
+ SECItem value;
+ SGNDigestInfo thumbprint;
+};
+
+/* contains a null terminated list of certs and crls */
+struct SEC_PKCS12CertAndCRLBagStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12CertAndCRL **certAndCRLs;
+
+ int bag_size; /* used locally */
+};
+
+/* additional secret information. currently no information
+ * stored in this structure.
+ */
+struct SEC_PKCS12SecretAdditionalStr {
+ PLArenaPool *poolp;
+ SECOidData *secretTypeTag; /* used locally */
+ SECItem secretAdditionalType;
+ SECItem secretAdditionalContent;
+};
+
+/* secrets container. this will be used to contain currently
+ * unspecified secrets. (it's a secret)
+ */
+struct SEC_PKCS12SecretStr {
+ PLArenaPool *poolp;
+ SECItem secretName;
+ SECItem value;
+ SEC_PKCS12SecretAdditional secretAdditional;
+
+ SECItem uniSecretName;
+};
+
+struct SEC_PKCS12SecretItemStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12Secret secret;
+ SEC_PKCS12SafeBag subFolder;
+};
+
+/* a bag of secrets. holds a null terminated list of secrets.
+ */
+struct SEC_PKCS12SecretBagStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12SecretItem **secrets;
+
+ int bag_size; /* used locally */
+};
+
+struct SEC_PKCS12MacDataStr {
+ SGNDigestInfo safeMac;
+ SECItem macSalt;
+};
+
+/* outer transfer unit */
+struct SEC_PKCS12PFXItemStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12MacData macData;
+ SEC_PKCS7ContentInfo authSafe;
+
+ /* for compatibility with beta */
+ PRBool old;
+ SGNDigestInfo old_safeMac;
+ SECItem old_macSalt;
+
+ /* compatibility between platforms for unicode swapping */
+ PRBool swapUnicode;
+};
+
+struct SEC_PKCS12BaggageItemStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12ESPVKItem **espvks;
+ SEC_PKCS12SafeBag **unencSecrets;
+
+ int nEspvks;
+ int nSecrets;
+};
+
+/* stores shrouded keys */
+struct SEC_PKCS12Baggage_OLDStr {
+ PLArenaPool *poolp;
+ SEC_PKCS12ESPVKItem **espvks;
+
+ int luggage_size; /* used locally */
+};
+
+/* authenticated safe, stores certs, keys, and shrouded keys */
+struct SEC_PKCS12AuthenticatedSafeStr {
+ PLArenaPool *poolp;
+ SECItem version;
+ SECOidData *transportTypeTag; /* local not part of encoding*/
+ SECItem transportMode;
+ SECItem privacySalt;
+ SEC_PKCS12Baggage baggage;
+ SEC_PKCS7ContentInfo *safe;
+
+ /* used for beta compatibility */
+ PRBool old;
+ PRBool emptySafe;
+ SEC_PKCS12Baggage_OLD old_baggage;
+ SEC_PKCS7ContentInfo old_safe;
+ PRBool swapUnicode;
+};
+#define SEC_PKCS12_PFX_VERSION 1 /* what we create */
+
+/* PKCS 12 Templates */
+extern const SEC_ASN1Template SEC_PKCS12PFXItemTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PKCS12BaggageTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PKCS12PFXItemTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12MacDataTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12AuthenticatedSafeTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12BaggageTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12PVKAdditionalTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PKCS12SafeContentsTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SafeBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12PrivateKeyTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12PrivateKeyBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12CertAndCRLTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12CertAndCRLBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PKCS12X509CertCRLTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SDSICertTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SecretBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SecretTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SecretItemTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12SecretAdditionalTemplate[];
+extern const SEC_ASN1Template SGN_DigestInfoTemplate[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12KeyBagTemplate[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12CertAndCRLBagTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12SecretBagTemplate[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12X509CertCRLTemplate[];
+extern const SEC_ASN1Template SEC_PointerToPKCS12SDSICertTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12CodedCertBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12CodedCertAndCRLBagTemplate[];
+extern const SEC_ASN1Template SEC_PKCS12PVKSupportingDataTemplate_OLD[];
+extern const SEC_ASN1Template SEC_PKCS12ESPVKItemTemplate_OLD[];
+#endif