summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrelyea%netscape.com <devnull@localhost>2000-03-31 19:16:26 +0000
committerrelyea%netscape.com <devnull@localhost>2000-03-31 19:16:26 +0000
commit222a52dab759085f56dcb6588b69a6a937d82aa2 (patch)
tree8cc2b9251b3777cc1b8a9fe5b615d02b0aadece9
parentdc6d4e64be376d12f325301e7e3c5cd048338e9a (diff)
downloadnss-hg-222a52dab759085f56dcb6588b69a6a937d82aa2.tar.gz
Initial NSS Open Source Checkin
-rw-r--r--security/nss/lib/pk11wrap/Makefile77
-rw-r--r--security/nss/lib/pk11wrap/config.mk44
-rw-r--r--security/nss/lib/pk11wrap/manifest.mn62
-rw-r--r--security/nss/lib/pk11wrap/pk11cert.c2256
-rw-r--r--security/nss/lib/pk11wrap/pk11db.c643
-rw-r--r--security/nss/lib/pk11wrap/pk11err.c147
-rw-r--r--security/nss/lib/pk11wrap/pk11func.h440
-rw-r--r--security/nss/lib/pk11wrap/pk11kea.c224
-rw-r--r--security/nss/lib/pk11wrap/pk11list.c165
-rw-r--r--security/nss/lib/pk11wrap/pk11load.c239
-rw-r--r--security/nss/lib/pk11wrap/pk11skey.c4676
-rw-r--r--security/nss/lib/pk11wrap/pk11slot.c4244
-rw-r--r--security/nss/lib/pk11wrap/pk11util.c518
-rw-r--r--security/nss/lib/pk11wrap/secmod.h132
-rw-r--r--security/nss/lib/pk11wrap/secmodi.h81
-rw-r--r--security/nss/lib/pk11wrap/secmodt.h173
-rw-r--r--security/nss/lib/pk11wrap/secmodti.h179
-rw-r--r--security/nss/lib/pkcs12/Makefile77
-rw-r--r--security/nss/lib/pkcs12/config.mk45
-rw-r--r--security/nss/lib/pkcs12/manifest.mn58
-rw-r--r--security/nss/lib/pkcs12/p12.h173
-rw-r--r--security/nss/lib/pkcs12/p12creat.c251
-rw-r--r--security/nss/lib/pkcs12/p12d.c3224
-rw-r--r--security/nss/lib/pkcs12/p12dec.c692
-rw-r--r--security/nss/lib/pkcs12/p12e.c2254
-rw-r--r--security/nss/lib/pkcs12/p12exp.c1407
-rw-r--r--security/nss/lib/pkcs12/p12local.c1325
-rw-r--r--security/nss/lib/pkcs12/p12local.h87
-rw-r--r--security/nss/lib/pkcs12/p12plcy.c198
-rw-r--r--security/nss/lib/pkcs12/p12plcy.h57
-rw-r--r--security/nss/lib/pkcs12/p12t.h182
-rw-r--r--security/nss/lib/pkcs12/p12tmpl.c315
-rw-r--r--security/nss/lib/pkcs12/pkcs12.h67
-rw-r--r--security/nss/lib/pkcs12/pkcs12t.h386
-rw-r--r--security/nss/lib/pkcs7/Makefile76
-rw-r--r--security/nss/lib/pkcs7/config.mk44
-rw-r--r--security/nss/lib/pkcs7/manifest.mn59
-rw-r--r--security/nss/lib/pkcs7/p7common.c738
-rw-r--r--security/nss/lib/pkcs7/p7create.c1320
-rw-r--r--security/nss/lib/pkcs7/p7decode.c2087
-rw-r--r--security/nss/lib/pkcs7/p7encode.c1329
-rw-r--r--security/nss/lib/pkcs7/p7local.c1431
-rw-r--r--security/nss/lib/pkcs7/p7local.h176
-rw-r--r--security/nss/lib/pkcs7/pkcs7t.h292
-rw-r--r--security/nss/lib/pkcs7/secmime.c901
-rw-r--r--security/nss/lib/pkcs7/secmime.h192
-rw-r--r--security/nss/lib/pkcs7/secpkcs7.h618
-rw-r--r--security/nss/lib/pki/nsspki.h3161
-rw-r--r--security/nss/lib/pki/nsspkit.h261
-rw-r--r--security/nss/lib/pki1/.cvsignore3
-rw-r--r--security/nss/lib/pki1/Makefile38
-rw-r--r--security/nss/lib/pki1/atav.c1803
-rw-r--r--security/nss/lib/pki1/config.mk37
-rw-r--r--security/nss/lib/pki1/genname.c94
-rw-r--r--security/nss/lib/pki1/gnseq.c71
-rw-r--r--security/nss/lib/pki1/manifest.mn63
-rw-r--r--security/nss/lib/pki1/name.c77
-rw-r--r--security/nss/lib/pki1/nsspki1.h2869
-rw-r--r--security/nss/lib/pki1/nsspki1t.h202
-rw-r--r--security/nss/lib/pki1/oid.c1615
-rwxr-xr-xsecurity/nss/lib/pki1/oidgen.perl311
-rw-r--r--security/nss/lib/pki1/oids.txt2115
-rw-r--r--security/nss/lib/pki1/pki1.h3032
-rw-r--r--security/nss/lib/pki1/pki1t.h104
-rw-r--r--security/nss/lib/pki1/rdn.c73
-rw-r--r--security/nss/lib/pki1/rdnseq.c71
66 files changed, 50361 insertions, 0 deletions
diff --git a/security/nss/lib/pk11wrap/Makefile b/security/nss/lib/pk11wrap/Makefile
new file mode 100644
index 000000000..774e68762
--- /dev/null
+++ b/security/nss/lib/pk11wrap/Makefile
@@ -0,0 +1,77 @@
+#! gmake
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+-include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+export:: private_export
+
+
diff --git a/security/nss/lib/pk11wrap/config.mk b/security/nss/lib/pk11wrap/config.mk
new file mode 100644
index 000000000..a73a1086e
--- /dev/null
+++ b/security/nss/lib/pk11wrap/config.mk
@@ -0,0 +1,44 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#
+# Override TARGETS variable so that only static libraries
+# are specifed as dependencies within rules.mk.
+#
+
+TARGETS = $(LIBRARY)
+SHARED_LIBRARY =
+IMPORT_LIBRARY =
+PURE_LIBRARY =
+PROGRAM =
+
diff --git a/security/nss/lib/pk11wrap/manifest.mn b/security/nss/lib/pk11wrap/manifest.mn
new file mode 100644
index 000000000..b558c6f0d
--- /dev/null
+++ b/security/nss/lib/pk11wrap/manifest.mn
@@ -0,0 +1,62 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+CORE_DEPTH = ../../..
+
+EXPORTS = \
+ secmod.h \
+ secmodt.h \
+ pk11func.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ secmodi.h \
+ secmodti.h \
+ $(NULL)
+
+MODULE = security
+
+CSRCS = \
+ pk11cert.c \
+ pk11err.c \
+ pk11load.c \
+ pk11slot.c \
+ pk11db.c \
+ pk11list.c \
+ pk11skey.c \
+ pk11kea.c \
+ pk11util.c \
+ $(NULL)
+
+REQUIRES = security dbm
+
+LIBRARY_NAME = pk11wrap
diff --git a/security/nss/lib/pk11wrap/pk11cert.c b/security/nss/lib/pk11wrap/pk11cert.c
new file mode 100644
index 000000000..da77fc09b
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11cert.c
@@ -0,0 +1,2256 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * This file implements the Symkey wrapper and the PKCS context
+ * Interfaces.
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "prlock.h"
+#include "secmodi.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "cert.h"
+#include "secitem.h"
+#include "key.h"
+#include "hasht.h"
+#include "secoid.h"
+#include "pkcs7t.h"
+
+#include "certdb.h"
+#include "secerr.h"
+#include "sslerr.h"
+
+#define PK11_SEARCH_CHUNKSIZE 10
+
+CK_OBJECT_HANDLE
+pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx);
+
+/*
+ * build a cert nickname based on the token name and the label of the
+ * certificate If the label in NULL, build a label based on the ID.
+ */
+static int toHex(int x) { return (x < 10) ? (x+'0') : (x+'a'-10); }
+#define MAX_CERT_ID 4
+#define DEFAULT_STRING "Cert ID "
+static char *
+pk11_buildNickname(PK11SlotInfo *slot,CK_ATTRIBUTE *cert_label,
+ CK_ATTRIBUTE *key_label, CK_ATTRIBUTE *cert_id)
+{
+ int prefixLen = PORT_Strlen(slot->token_name);
+ int suffixLen = 0;
+ char *suffix = NULL;
+ char buildNew[sizeof(DEFAULT_STRING)+MAX_CERT_ID*2];
+ char *next,*nickname;
+
+ if (slot->isInternal) {
+ return NULL;
+ }
+
+ if ((cert_label) && (cert_label->pValue)) {
+ suffixLen = cert_label->ulValueLen;
+ suffix = (char*)cert_label->pValue;
+ } else if (key_label && (key_label->pValue)) {
+ suffixLen = key_label->ulValueLen;
+ suffix = (char*)key_label->pValue;
+ } else if ((cert_id) && cert_id->pValue) {
+ int i,first = cert_id->ulValueLen - MAX_CERT_ID;
+ int offset = sizeof(DEFAULT_STRING);
+ char *idValue = (char *)cert_id->pValue;
+
+ PORT_Memcpy(buildNew,DEFAULT_STRING,sizeof(DEFAULT_STRING)-1);
+ next = buildNew + offset;
+ if (first < 0) first = 0;
+ for (i=first; i < (int) cert_id->ulValueLen; i++) {
+ *next++ = toHex((idValue[i] >> 4) & 0xf);
+ *next++ = toHex(idValue[i] & 0xf);
+ }
+ *next++ = 0;
+ suffix = buildNew;
+ suffixLen = PORT_Strlen(buildNew);
+ } else {
+ PORT_SetError( SEC_ERROR_LIBRARY_FAILURE );
+ return NULL;
+ }
+
+ next = nickname = (char *)PORT_Alloc(prefixLen+1+suffixLen+1);
+ if (nickname == NULL) return NULL;
+
+ PORT_Memcpy(next,slot->token_name,prefixLen);
+ next += prefixLen;
+ *next++ = ':';
+ PORT_Memcpy(next,suffix,suffixLen);
+ next += suffixLen;
+ *next++ = 0;
+ return nickname;
+}
+
+/*
+ * return the object handle that matches the template
+ */
+CK_OBJECT_HANDLE
+pk11_FindObjectByTemplate(PK11SlotInfo *slot,CK_ATTRIBUTE *theTemplate,int tsize)
+{
+ CK_OBJECT_HANDLE object;
+ CK_RV crv;
+ CK_ULONG objectCount;
+
+ /*
+ * issue the find
+ */
+ PK11_EnterSlotMonitor(slot);
+ crv=PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, theTemplate, tsize);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError( PK11_MapError(crv) );
+ return CK_INVALID_KEY;
+ }
+
+ crv=PK11_GETTAB(slot)->C_FindObjects(slot->session,&object,1,&objectCount);
+ PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ if ((crv != CKR_OK) || (objectCount < 1)) {
+ /* shouldn't use SSL_ERROR... here */
+ PORT_SetError( crv != CKR_OK ? PK11_MapError(crv) :
+ SSL_ERROR_NO_CERTIFICATE);
+ return CK_INVALID_KEY;
+ }
+
+ /* blow up if the PKCS #11 module returns us and invalid object handle */
+ PORT_Assert(object != CK_INVALID_KEY);
+ return object;
+}
+
+/*
+ * return all the object handles that matches the template
+ */
+CK_OBJECT_HANDLE *
+pk11_FindObjectsByTemplate(PK11SlotInfo *slot,
+ CK_ATTRIBUTE *findTemplate,int findCount,int *object_count) {
+ CK_OBJECT_HANDLE *objID = NULL;
+ CK_ULONG returned_count = 0;
+ CK_RV crv;
+
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session, findTemplate,
+ findCount);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError( PK11_MapError(crv) );
+ *object_count = -1;
+ return NULL;
+ }
+
+
+ /*
+ * collect all the Matching Objects
+ */
+ do {
+ CK_OBJECT_HANDLE *oldObjID = objID;
+
+ if (objID == NULL) {
+ objID = (CK_OBJECT_HANDLE *) PORT_Alloc(sizeof(CK_OBJECT_HANDLE)*
+ (*object_count+ PK11_SEARCH_CHUNKSIZE));
+ } else {
+ objID = (CK_OBJECT_HANDLE *) PORT_Realloc(objID,
+ sizeof(CK_OBJECT_HANDLE)*(*object_count+PK11_SEARCH_CHUNKSIZE));
+ }
+
+ if (objID == NULL) {
+ if (oldObjID) PORT_Free(oldObjID);
+ break;
+ }
+ crv = PK11_GETTAB(slot)->C_FindObjects(slot->session,
+ &objID[*object_count],PK11_SEARCH_CHUNKSIZE,&returned_count);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ PORT_Free(objID);
+ objID = NULL;
+ break;
+ }
+ *object_count += returned_count;
+ } while (returned_count == PK11_SEARCH_CHUNKSIZE);
+
+ PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
+ PK11_ExitSlotMonitor(slot);
+
+ if (objID && (*object_count == 0)) {
+ PORT_Free(objID);
+ return NULL;
+ }
+ if (objID == NULL) *object_count = -1;
+ return objID;
+}
+/*
+ * given a PKCS #11 object, match it's peer based on the KeyID. searchID
+ * is typically a privateKey or a certificate while the peer is the opposite
+ */
+CK_OBJECT_HANDLE
+PK11_MatchItem(PK11SlotInfo *slot, CK_OBJECT_HANDLE searchID,
+ CK_OBJECT_CLASS matchclass)
+{
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ CK_ATTRIBUTE *keyclass = &theTemplate[1];
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ /* if you change the array, change the variable below as well */
+ CK_OBJECT_HANDLE peerID;
+ CK_OBJECT_HANDLE parent;
+ PRArenaPool *arena;
+ CK_RV crv;
+
+ /* now we need to create space for the public key */
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) return CK_INVALID_KEY;
+
+ crv = PK11_GetAttributes(arena,slot,searchID,theTemplate,tsize);
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ PORT_SetError( PK11_MapError(crv) );
+ return CK_INVALID_KEY;
+ }
+
+ /*
+ * issue the find
+ */
+ parent = *(CK_OBJECT_CLASS *)(keyclass->pValue);
+ *(CK_OBJECT_CLASS *)(keyclass->pValue) = matchclass;
+
+ peerID = pk11_FindObjectByTemplate(slot,theTemplate,tsize);
+ PORT_FreeArena(arena,PR_FALSE);
+
+ return peerID;
+}
+
+PRBool
+PK11_IsUserCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ CK_OBJECT_HANDLE certID)
+{
+ CK_OBJECT_CLASS theClass;
+
+ if (slot == NULL) return PR_FALSE;
+ if (cert == NULL) return PR_FALSE;
+
+ theClass = CKO_PRIVATE_KEY;
+ if (!PK11_IsLoggedIn(slot,NULL) && PK11_NeedLogin(slot)) {
+ theClass = CKO_PUBLIC_KEY;
+ }
+ if (PK11_MatchItem(slot, certID , theClass) != CK_INVALID_KEY) {
+ return PR_TRUE;
+ }
+
+ if (theClass == CKO_PUBLIC_KEY) {
+ SECKEYPublicKey *pubKey= CERT_ExtractPublicKey(cert);
+ CK_ATTRIBUTE theTemplate;
+
+ if (pubKey == NULL) {
+ return PR_FALSE;
+ }
+
+ PK11_SETATTRS(&theTemplate,0,NULL,0);
+ switch (pubKey->keyType) {
+ case rsaKey:
+ PK11_SETATTRS(&theTemplate,CKA_MODULUS, pubKey->u.rsa.modulus.data,
+ pubKey->u.rsa.modulus.len);
+ break;
+ case dsaKey:
+ PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dsa.publicValue.data,
+ pubKey->u.dsa.publicValue.len);
+ case dhKey:
+ PK11_SETATTRS(&theTemplate,CKA_VALUE, pubKey->u.dh.publicValue.data,
+ pubKey->u.dh.publicValue.len);
+ break;
+ }
+
+ if (theTemplate.ulValueLen == 0) {
+ SECKEY_DestroyPublicKey(pubKey);
+ return PR_FALSE;
+ }
+ pk11_SignedToUnsigned(&theTemplate);
+ if (pk11_FindObjectByTemplate(slot,&theTemplate,1) != CK_INVALID_KEY) {
+ SECKEY_DestroyPublicKey(pubKey);
+ return PR_TRUE;
+ }
+ SECKEY_DestroyPublicKey(pubKey);
+ }
+ return PR_FALSE;
+}
+
+/*
+ * Check out if a cert has ID of zero. This is a magic ID that tells
+ * NSS that this cert may be an automagically trusted cert.
+ * The Cert has to be self signed as well. That check is done elsewhere.
+ *
+ */
+PRBool
+pk11_isID0(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID)
+{
+ CK_ATTRIBUTE keyID = {CKA_ID, NULL, 0};
+ PRBool isZero = PR_FALSE;
+ int i;
+ CK_RV crv;
+
+
+ crv = PK11_GetAttributes(NULL,slot,certID,&keyID,1);
+ if (crv != CKR_OK) {
+ return isZero;
+ }
+
+ if (keyID.ulValueLen != 0) {
+ char *value = (char *)keyID.pValue;
+ isZero = PR_TRUE; /* ID exists, may be zero */
+ for (i=0; i < (int) keyID.ulValueLen; i++) {
+ if (value[i] != 0) {
+ isZero = PR_FALSE; /* nope */
+ break;
+ }
+ }
+ }
+ PORT_Free(keyID.pValue);
+ return isZero;
+
+}
+
+CERTCertificate
+*pk11_fastCert(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID,
+ CK_ATTRIBUTE *privateLabel, char **nickptr)
+{
+ CK_ATTRIBUTE certTemp[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_VALUE, NULL, 0 },
+ { CKA_LABEL, NULL, 0 }
+ };
+ CK_ATTRIBUTE *id = &certTemp[0];
+ CK_ATTRIBUTE *certDER = &certTemp[1];
+ CK_ATTRIBUTE *label = &certTemp[2];
+ SECItem derCert;
+ int csize = sizeof(certTemp)/sizeof(certTemp[0]);
+ PRArenaPool *arena;
+ char *nickname;
+ CERTCertificate *cert;
+ CK_RV crv;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) return NULL;
+ /*
+ * grab the der encoding
+ */
+ crv = PK11_GetAttributes(arena,slot,certID,certTemp,csize);
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+
+ /*
+ * build a certificate out of it
+ */
+ derCert.data = (unsigned char*)certDER->pValue;
+ derCert.len = certDER->ulValueLen;
+
+ /* figure out the nickname.... */
+ nickname = pk11_buildNickname(slot,label,privateLabel,id);
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, nickname,
+ PR_FALSE, PR_TRUE);
+ if (nickptr) {
+ *nickptr = nickname;
+ } else {
+ if (nickname) PORT_Free(nickname);
+ }
+ PORT_FreeArena(arena,PR_FALSE);
+ return cert;
+}
+
+/*
+ * Build an CERTCertificate structure from a PKCS#11 object ID.... certID
+ * Must be a CertObject. This code does not explicitly checks that.
+ */
+CERTCertificate *
+PK11_MakeCertFromHandle(PK11SlotInfo *slot,CK_OBJECT_HANDLE certID,
+ CK_ATTRIBUTE *privateLabel)
+{
+ char * nickname = NULL;
+ CERTCertificate *cert = NULL;
+ CERTCertTrust *trust;
+ PRBool isFortezzaRootCA = PR_FALSE;
+ PRBool swapNickname = PR_FALSE;
+
+ cert = pk11_fastCert(slot,certID,privateLabel, &nickname);
+ if (cert == NULL) goto loser;
+
+ if (nickname) {
+ if (cert->nickname != NULL) {
+ cert->dbnickname = cert->nickname;
+ }
+ cert->nickname = PORT_ArenaStrdup(cert->arena,nickname);
+ PORT_Free(nickname);
+ nickname = NULL;
+ swapNickname = PR_TRUE;
+ }
+
+ /* remember where this cert came from.... If we have just looked
+ * it up from the database and it already has a slot, don't add a new
+ * one. */
+ if (cert->slot == NULL) {
+ cert->slot = PK11_ReferenceSlot(slot);
+ cert->pkcs11ID = certID;
+ cert->ownSlot = PR_TRUE;
+ }
+
+ if (cert->trust == NULL) {
+ unsigned int type;
+
+ trust =
+ (CERTCertTrust*)PORT_ArenaAlloc(cert->arena, sizeof(CERTCertTrust));
+ if (trust == NULL) goto loser;
+
+ PORT_Memset(trust,0, sizeof(CERTCertTrust));
+ cert->trust = trust;
+ /* build some cert trust flags */
+ if (CERT_IsCACert(cert, &type)) {
+ unsigned int trustflags = CERTDB_VALID_CA;
+
+ /* Allow PKCS #11 modules to give us trusted CA's. We only accept
+ * valid CA's which are self-signed here. They must have an object
+ * ID of '0'. */
+ if (pk11_isID0(slot,certID) &&
+ SECITEM_CompareItem(&cert->derSubject,&cert->derIssuer)
+ == SECEqual) {
+ trustflags |= CERTDB_TRUSTED_CA;
+ /* is the slot a fortezza card? allow the user or
+ * admin to turn on objectSigning, but don't turn
+ * full trust on explicitly */
+ if (PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) {
+ trust->objectSigningFlags |= CERTDB_VALID_CA;
+ isFortezzaRootCA = PR_TRUE;
+ }
+ }
+ if ((type & NS_CERT_TYPE_SSL_CA) == NS_CERT_TYPE_SSL_CA) {
+ trust->sslFlags |= trustflags;
+ }
+ if ((type & NS_CERT_TYPE_EMAIL_CA) == NS_CERT_TYPE_EMAIL_CA) {
+ trust->emailFlags |= trustflags;
+ }
+ if ((type & NS_CERT_TYPE_OBJECT_SIGNING_CA)
+ == NS_CERT_TYPE_OBJECT_SIGNING_CA) {
+ trust->objectSigningFlags |= trustflags;
+ }
+ }
+ } else {
+ trust = cert->trust;
+ }
+
+ if (PK11_IsUserCert(slot,cert,certID)) {
+ trust->sslFlags |= CERTDB_USER;
+ trust->emailFlags |= CERTDB_USER;
+ /* trust->objectSigningFlags |= CERTDB_USER; */
+ }
+
+
+ /* if fortezza, write the root cert to the DB */
+ if ((isFortezzaRootCA) && (!cert->isperm)) {
+ char *name = NULL;
+ if (swapNickname) {
+ nickname = cert->nickname;
+ cert->nickname = cert->dbnickname;
+ }
+ if (cert->nickname) {
+ name = PORT_Strdup(cert->nickname);
+ }
+ if (name == NULL) name = CERT_MakeCANickname(cert);
+ CERT_AddTempCertToPerm(cert,name,cert->trust);
+ if (name) PORT_Free(name);
+ if (swapNickname) {
+ if (cert->nickname != NULL) {
+ cert->dbnickname = cert->nickname;
+ }
+ cert->nickname = PORT_ArenaStrdup(cert->arena,nickname);
+ }
+
+ }
+
+ return cert;
+
+loser:
+ if (nickname) PORT_Free(nickname);
+ if (cert) CERT_DestroyCertificate(cert);
+ return NULL;
+}
+
+
+/*
+ * Build get a certificate from a private key
+ */
+CERTCertificate *
+PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey)
+{
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ CK_OBJECT_HANDLE certID =
+ PK11_MatchItem(slot,privKey->pkcs11ID,CKO_CERTIFICATE);
+ SECStatus rv;
+ CERTCertificate *cert;
+
+ if (certID == CK_INVALID_KEY) {
+ /* couldn't find it on the card, look in our data base */
+ SECItem derSubject;
+
+ rv = PK11_ReadAttribute(slot, privKey->pkcs11ID, CKA_SUBJECT, NULL,
+ &derSubject);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_NO_CERTIFICATE);
+ return NULL;
+ }
+
+ cert = CERT_FindCertByName(CERT_GetDefaultCertDB(),&derSubject);
+ PORT_Free(derSubject.data);
+ return cert;
+ }
+ cert = PK11_MakeCertFromHandle(slot,certID,NULL);
+ return (cert);
+
+}
+
+/*
+ * destroy a private key if there are no matching certs.
+ * this function also frees the privKey structure.
+ */
+SECStatus
+PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey)
+{
+ CERTCertificate *cert=PK11_GetCertFromPrivateKey(privKey);
+
+ /* found a cert matching the private key?. */
+ if (cert != NULL) {
+ /* yes, don't delete the key */
+ CERT_DestroyCertificate(cert);
+ SECKEY_DestroyPrivateKey(privKey);
+ return SECWouldBlock;
+ }
+ /* now, then it's safe for the key to go away */
+ PK11_DestroyTokenObject(privKey->pkcs11Slot,privKey->pkcs11ID);
+ SECKEY_DestroyPrivateKey(privKey);
+ return SECSuccess;
+}
+
+
+/*
+ * delete a cert and it's private key (if no other certs are pointing to the
+ * private key.
+ */
+SECStatus
+PK11_DeleteTokenCertAndKey(CERTCertificate *cert,void *wincx)
+{
+ SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert,wincx);
+ CK_OBJECT_HANDLE pubKey;
+ PK11SlotInfo *slot = NULL;
+
+ pubKey = pk11_FindPubKeyByAnyCert(cert, &slot, wincx);
+ if (privKey) {
+ PK11_DestroyTokenObject(cert->slot,cert->pkcs11ID);
+ PK11_DeleteTokenPrivateKey(privKey);
+ }
+ if ((pubKey != CK_INVALID_KEY) && (slot != NULL)) {
+ PK11_DestroyTokenObject(slot,pubKey);
+ PK11_FreeSlot(slot);
+ }
+ return SECSuccess;
+}
+
+/*
+ * count the number of objects that match the template.
+ */
+int
+PK11_NumberObjectsFor(PK11SlotInfo *slot, CK_ATTRIBUTE *findTemplate,
+ int templateCount)
+{
+ CK_OBJECT_HANDLE objID[PK11_SEARCH_CHUNKSIZE];
+ int object_count = 0;
+ CK_ULONG returned_count = 0;
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_FindObjectsInit(slot->session,
+ findTemplate, templateCount);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError( PK11_MapError(crv) );
+ return 0;
+ }
+
+ /*
+ * collect all the Matching Objects
+ */
+ do {
+ crv = PK11_GETTAB(slot)->C_FindObjects(slot->session,
+ objID,PK11_SEARCH_CHUNKSIZE,&returned_count);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ break;
+ }
+ object_count += returned_count;
+ } while (returned_count == PK11_SEARCH_CHUNKSIZE);
+
+ PK11_GETTAB(slot)->C_FindObjectsFinal(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ return object_count;
+}
+
+/*
+ * cert callback structure
+ */
+typedef struct pk11DoCertCallbackStr {
+ SECStatus(* callback)(PK11SlotInfo *slot, CERTCertificate*, void *);
+ SECStatus(* noslotcallback)(CERTCertificate*, void *);
+ void *callbackArg;
+} pk11DoCertCallback;
+
+/*
+ * callback to map object handles to certificate structures.
+ */
+SECStatus
+pk11_DoCerts(PK11SlotInfo *slot, CK_OBJECT_HANDLE certID, void *arg)
+{
+ CERTCertificate *cert;
+ pk11DoCertCallback *certcb = (pk11DoCertCallback *) arg;
+
+ cert = PK11_MakeCertFromHandle(slot, certID, NULL);
+
+ if (cert == NULL) {
+ return SECFailure;
+ }
+
+ if (certcb ) {
+ if (certcb->callback) {
+ (*certcb->callback)(slot, cert, certcb->callbackArg);
+ }
+ if (certcb->noslotcallback) {
+ (*certcb->noslotcallback)(cert, certcb->callbackArg);
+ }
+ }
+
+ CERT_DestroyCertificate(cert);
+
+ return SECSuccess;
+}
+
+
+/*
+ * key call back structure.
+ */
+typedef struct pk11KeyCallbackStr {
+ SECStatus (* callback)(SECKEYPrivateKey *,void *);
+ void *callbackArg;
+ void *wincx;
+} pk11KeyCallback;
+
+/*
+ * callback to map Object Handles to Private Keys;
+ */
+SECStatus
+pk11_DoKeys(PK11SlotInfo *slot, CK_OBJECT_HANDLE keyHandle, void *arg)
+{
+ SECStatus rv = SECSuccess;
+ SECKEYPrivateKey *privKey;
+ pk11KeyCallback *keycb = (pk11KeyCallback *) arg;
+
+ privKey = PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,keycb->wincx);
+
+ if (privKey == NULL) {
+ return SECFailure;
+ }
+
+ if (keycb && (keycb->callback)) {
+ rv = (*keycb->callback)(privKey,keycb->callbackArg);
+ }
+
+ SECKEY_DestroyPrivateKey(privKey);
+ return rv;
+}
+
+
+/* Traverse slots callback */
+typedef struct pk11TraverseSlotStr {
+ SECStatus (*callback)(PK11SlotInfo *,CK_OBJECT_HANDLE, void *);
+ void *callbackArg;
+ CK_ATTRIBUTE *findTemplate;
+ int templateCount;
+} pk11TraverseSlotCert;
+
+/*
+ * Extract all the certs on a card from a slot.
+ */
+SECStatus
+PK11_TraverseSlot(PK11SlotInfo *slot, void *arg)
+{
+ int i;
+ CK_OBJECT_HANDLE *objID = NULL;
+ int object_count = 0;
+ CK_ULONG returned_count = 0;
+ pk11TraverseSlotCert *slotcb = (pk11TraverseSlotCert *) arg;
+
+ objID = pk11_FindObjectsByTemplate(slot,slotcb->findTemplate,
+ slotcb->templateCount,&object_count);
+
+ /*Actually this isn't a failure... there just were no objs to be found*/
+ if (object_count == 0) {
+ return SECSuccess;
+ }
+
+ if (objID == NULL) {
+ return SECFailure;
+ }
+
+ for (i=0; i < object_count; i++) {
+ (*slotcb->callback)(slot,objID[i],slotcb->callbackArg);
+ }
+ PORT_Free(objID);
+ return SECSuccess;
+}
+
+typedef struct pk11CertCallbackStr {
+ SECStatus(* callback)(CERTCertificate*,SECItem *,void *);
+ void *callbackArg;
+} pk11CertCallback;
+
+static SECStatus
+pk11_SaveCert(PK11SlotInfo *slot, CERTCertificate *cert, void *arg)
+{
+ pk11CertCallback *certcb = (pk11CertCallback *)arg;
+ SECStatus rv = SECSuccess;
+
+ if (slot->cert_count == slot->array_size) return CKR_OK;
+
+ slot->cert_array[slot->cert_count] = CERT_DupCertificate(cert);
+ if (slot->cert_array[slot->cert_count] == NULL) {
+ return SECFailure;
+ }
+ /* now the slot has a hold of the cert, free the slot's element in the
+ * cert.. */
+ if (cert->ownSlot && (slot == cert->slot)) {
+ PK11_FreeSlot(cert->slot);
+ cert->ownSlot = PR_FALSE;
+ }
+ slot->cert_count++;
+
+ if (certcb->callback) {
+ rv = (*certcb->callback)(cert, NULL, certcb->callbackArg);
+ }
+ return rv;
+}
+
+
+/* free the slots */
+void
+PK11_FreeSlotCerts(PK11SlotInfo *slot)
+{
+ int i;
+
+ if (slot->cert_array) {
+ for (i=0; i < slot->cert_count; i++) {
+ /* if we point the cert on our array, the cert doesn't have a
+ * reference to use (otherwise you would never be able to free
+ * a slot :) */
+ if ((slot->cert_array[i]->slot == slot) &&
+ (!slot->cert_array[i]->ownSlot)) {
+ slot->cert_array[i]->slot = NULL;
+ }
+ CERT_DestroyCertificate(slot->cert_array[i]);
+ }
+ PORT_Free(slot->cert_array);
+ slot->cert_array = NULL;
+ slot->cert_count = 0;
+ }
+ return;
+}
+
+/*
+ * Update PQG parameters for all the certs on a slot.
+ */
+static SECStatus
+pk11_UpdateSlotPQG(PK11SlotInfo *slot)
+{
+ int i, tag;
+ CERTCertificate * cert;
+ SECOidData *oid;
+ SECStatus rv1 = SECSuccess;
+ SECStatus rv2 = SECSuccess;
+
+ if (slot->cert_array) {
+ for (i=0; i < slot->cert_count; i++) {
+
+ cert = slot->cert_array[i];
+
+ oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm);
+
+ if (oid != NULL) {
+ tag = oid->offset;
+
+ /* Check if cert has a DSA or Fortezza public key */
+ if ( (tag == SEC_OID_MISSI_KEA_DSS_OLD) ||
+ (tag == SEC_OID_MISSI_DSS_OLD) ||
+ (tag == SEC_OID_MISSI_KEA_DSS) ||
+ (tag == SEC_OID_MISSI_DSS) ||
+ (tag == SEC_OID_ANSIX9_DSA_SIGNATURE) ||
+ (tag == SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST) ||
+ (tag == SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST) ) {
+
+ /* update PQG parameters */
+
+ rv1 = SECKEY_UpdateCertPQG(cert);
+ if (rv1 == SECFailure) {
+ rv2 = rv1;
+ }
+ }
+ } /* end of if oid != NULL */
+ } /* end of for loop */
+ }
+ return rv2;
+}
+
+
+/*
+ * Extract all the certs on a card from a slot.
+ */
+static SECStatus
+pk11_ExtractCertsFromSlot(PK11SlotInfo *slot, void *arg)
+{
+ pk11TraverseSlotCert *slotcb = (pk11TraverseSlotCert *) arg;
+ int object_count;
+ SECStatus rv;
+
+ rv = SECSuccess;
+
+ PK11_FreeSlotCerts(slot);
+
+ object_count = PK11_NumberObjectsFor(slot,slotcb->findTemplate,
+ slotcb->templateCount);
+
+ /*Actually this isn't a failure... there just were no certs to be found*/
+ if (object_count == 0) {
+ return SECSuccess;
+ }
+
+ slot->cert_array = (CERTCertificate **)
+ PORT_Alloc(sizeof(CERTCertificate *)*object_count);
+ if (slot->cert_array == NULL) {
+ return SECFailure;
+ }
+ slot->cert_count = 0;
+ slot->array_size = object_count;
+ PK11_TraverseSlot(slot,arg);
+
+ /* Update the PQG parameters for the extracted certs. */
+ rv = pk11_UpdateSlotPQG(slot);
+
+ return rv;
+}
+
+/*
+ * read all the certs from a slot
+ */
+SECStatus
+PK11_ReadSlotCerts(PK11SlotInfo *slot)
+{
+
+ /* build slot list */
+ pk11CertCallback caller;
+ pk11DoCertCallback saver;
+ pk11TraverseSlotCert creater;
+ CK_ATTRIBUTE theTemplate;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+
+ PK11_SETATTRS(&theTemplate, CKA_CLASS, &certClass, sizeof(certClass));
+
+ caller.callback = NULL;
+ caller.callbackArg = NULL;
+ saver.callback = pk11_SaveCert;
+ saver.noslotcallback = NULL;
+ saver.callbackArg = (void *) & caller;
+ creater.callback = pk11_DoCerts;
+ creater.callbackArg = (void *) & saver;
+ creater.findTemplate = &theTemplate;
+ creater.templateCount = 1;
+
+ return pk11_ExtractCertsFromSlot(slot, &creater);
+}
+
+/*
+ * Extract all the certs on a card from a slot.
+ */
+static SECStatus
+pk11_TraverseAllSlots(PRBool loadCerts,
+ SECStatus (*callback)(PK11SlotInfo *,void *),void *arg,void *wincx) {
+
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ SECStatus rv;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,loadCerts,wincx);
+ if (list == NULL) return SECFailure;
+
+ /* look at each slot and authenticate as necessary */
+ for (le = list->head ; le; le = le->next) {
+ /* don't nab internal slots */
+ if ((!loadCerts) && le->slot->isInternal == PR_TRUE) {
+ continue;
+ }
+ if (loadCerts || !PK11_IsFriendly(le->slot)) {
+ rv = PK11_Authenticate(le->slot, loadCerts, wincx);
+ if (rv != SECSuccess) continue;
+ }
+ (*callback)(le->slot,arg);
+ }
+
+ PK11_FreeSlotList(list);
+
+ return SECSuccess;
+}
+
+/*
+ * Extract all the certs on a card from a slot.
+ */
+SECStatus
+PK11_TraverseSlotCerts(SECStatus(* callback)(CERTCertificate*,SECItem *,void *),
+ void *arg, void *wincx) {
+ pk11CertCallback caller;
+ pk11DoCertCallback saver;
+ pk11TraverseSlotCert creater;
+ CK_ATTRIBUTE theTemplate;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+
+ PK11_SETATTRS(&theTemplate, CKA_CLASS, &certClass, sizeof(certClass));
+
+ caller.callback = callback;
+ caller.callbackArg = arg;
+ saver.callback = pk11_SaveCert;
+ saver.noslotcallback = NULL;
+ saver.callbackArg = (void *) & caller;
+ creater.callback = pk11_DoCerts;
+ creater.callbackArg = (void *) & saver;
+ creater.findTemplate = &theTemplate;
+ creater.templateCount = 1;
+
+ return pk11_TraverseAllSlots(PR_FALSE, pk11_ExtractCertsFromSlot,
+ &creater, wincx);
+}
+
+CK_OBJECT_HANDLE *
+PK11_FindObjectsFromNickname(char *nickname,PK11SlotInfo **slotptr,
+ CK_OBJECT_CLASS objclass, int *returnCount, void *wincx) {
+ char *tokenName;
+ char *delimit;
+ PK11SlotInfo *slot;
+ CK_OBJECT_HANDLE *objID;
+ CK_ATTRIBUTE findTemplate[] = {
+ { CKA_LABEL, NULL, 0},
+ { CKA_CLASS, NULL, 0},
+ };
+ int findCount = sizeof(findTemplate)/sizeof(findTemplate[0]);
+ SECStatus rv;
+ PK11_SETATTRS(&findTemplate[1], CKA_CLASS, &objclass, sizeof(objclass));
+
+ *slotptr = slot = NULL;
+ *returnCount = 0;
+ /* first find the slot associated with this nickname */
+ if ((delimit = PORT_Strchr(nickname,':')) != NULL) {
+ int len = delimit - nickname;
+ tokenName = (char*)PORT_Alloc(len+1);
+ PORT_Memcpy(tokenName,nickname,len);
+ tokenName[len] = 0;
+
+ slot = *slotptr = PK11_FindSlotByName(tokenName);
+ PORT_Free(tokenName);
+ /* if we couldn't find a slot, assume the nickname is an internal cert
+ * with no proceding slot name */
+ if (slot == NULL) {
+ slot = *slotptr = PK11_GetInternalKeySlot();
+ } else {
+ nickname = delimit+1;
+ }
+ } else {
+ *slotptr = slot = PK11_GetInternalKeySlot();
+ }
+ if (slot == NULL) {
+ return CK_INVALID_KEY;
+ }
+
+ if (!PK11_IsFriendly(slot)) {
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ PK11_FreeSlot(slot);
+ *slotptr = NULL;
+ return CK_INVALID_KEY;
+ }
+ }
+
+ findTemplate[0].pValue = nickname;
+ findTemplate[0].ulValueLen = PORT_Strlen(nickname);
+ objID = pk11_FindObjectsByTemplate(slot,findTemplate,findCount,returnCount);
+ if (objID == NULL) {
+ /* PKCS #11 isn't clear on whether or not the NULL is
+ * stored in the template.... try the find again with the
+ * full null terminated string. */
+ findTemplate[0].ulValueLen += 1;
+ objID = pk11_FindObjectsByTemplate(slot,findTemplate,findCount,
+ returnCount);
+ if (objID == NULL) {
+ /* Well that's the best we can do. It's just not here */
+ /* what about faked nicknames? */
+ PK11_FreeSlot(slot);
+ *slotptr = NULL;
+ *returnCount = 0;
+ }
+ }
+
+ return objID;
+}
+
+
+CERTCertificate *
+PK11_FindCertFromNickname(char *nickname, void *wincx) {
+ PK11SlotInfo *slot;
+ int count=0;
+ CK_OBJECT_HANDLE *certID = PK11_FindObjectsFromNickname(nickname,&slot,
+ CKO_CERTIFICATE, &count, wincx);
+ CERTCertificate *cert;
+
+ if (certID == CK_INVALID_KEY) return NULL;
+ cert = PK11_MakeCertFromHandle(slot,certID[0],NULL);
+ PK11_FreeSlot(slot);
+ PORT_Free(certID);
+ return cert;
+}
+
+CERTCertList *
+PK11_FindCertsFromNickname(char *nickname, void *wincx) {
+ PK11SlotInfo *slot;
+ int i,count = 0;
+ CK_OBJECT_HANDLE *certID = PK11_FindObjectsFromNickname(nickname,&slot,
+ CKO_CERTIFICATE, &count, wincx);
+ CERTCertList *certList = NULL;
+
+ if (certID == NULL) return NULL;
+
+ certList= CERT_NewCertList();
+
+ for (i=0; i < count; i++) {
+ CERTCertificate *cert = PK11_MakeCertFromHandle(slot,certID[i],NULL);
+
+ if (cert) CERT_AddCertToListTail(certList,cert);
+ }
+
+ if (CERT_LIST_HEAD(certList) == NULL) {
+ CERT_DestroyCertList(certList);
+ certList = NULL;
+ }
+ PK11_FreeSlot(slot);
+ PORT_Free(certID);
+ return certList;
+}
+
+/*
+ * extract a key ID for a certificate...
+ * NOTE: We call this function from PKCS11.c If we ever use
+ * pkcs11 to extract the public key (we currently do not), this will break.
+ */
+SECItem *
+PK11_GetPubIndexKeyID(CERTCertificate *cert) {
+ SECKEYPublicKey *pubk;
+ SECItem *newItem = NULL;
+
+ pubk = CERT_ExtractPublicKey(cert);
+ if (pubk == NULL) return NULL;
+
+ switch (pubk->keyType) {
+ case rsaKey:
+ newItem = SECITEM_DupItem(&pubk->u.rsa.modulus);
+ break;
+ case dsaKey:
+ newItem = SECITEM_DupItem(&pubk->u.dsa.publicValue);
+ break;
+ case dhKey:
+ newItem = SECITEM_DupItem(&pubk->u.dh.publicValue);
+ case fortezzaKey:
+ default:
+ newItem = NULL; /* Fortezza Fix later... */
+ }
+ SECKEY_DestroyPublicKey(pubk);
+ /* make hash of it */
+ return newItem;
+}
+
+/*
+ * generate a CKA_ID from a certificate.
+ */
+SECItem *
+pk11_mkcertKeyID(CERTCertificate *cert) {
+ SECItem *pubKeyData = PK11_GetPubIndexKeyID(cert) ;
+ SECItem *certCKA_ID;
+
+ if (pubKeyData == NULL) return NULL;
+
+ certCKA_ID = PK11_MakeIDFromPubKey(pubKeyData);
+ SECITEM_FreeItem(pubKeyData,PR_TRUE);
+ return certCKA_ID;
+}
+
+
+/*
+ * Generate a CKA_ID from the relevant public key data. The CKA_ID is generated
+ * from the pubKeyData by SHA1_Hashing it to produce a smaller CKA_ID (to make
+ * smart cards happy.
+ */
+SECItem *
+PK11_MakeIDFromPubKey(SECItem *pubKeyData) {
+ PK11Context *context;
+ SECItem *certCKA_ID;
+ SECStatus rv;
+
+ context = PK11_CreateDigestContext(SEC_OID_SHA1);
+ if (context == NULL) {
+ return NULL;
+ }
+
+ rv = PK11_DigestBegin(context);
+ if (rv == SECSuccess) {
+ rv = PK11_DigestOp(context,pubKeyData->data,pubKeyData->len);
+ }
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context,PR_TRUE);
+ return NULL;
+ }
+
+ certCKA_ID = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (certCKA_ID == NULL) {
+ PK11_DestroyContext(context,PR_TRUE);
+ return NULL;
+ }
+
+ certCKA_ID->len = SHA1_LENGTH;
+ certCKA_ID->data = (unsigned char*)PORT_Alloc(certCKA_ID->len);
+ if (certCKA_ID->data == NULL) {
+ PORT_Free(certCKA_ID);
+ PK11_DestroyContext(context,PR_TRUE);
+ return NULL;
+ }
+
+ rv = PK11_DigestFinal(context,certCKA_ID->data,&certCKA_ID->len,
+ SHA1_LENGTH);
+ PK11_DestroyContext(context,PR_TRUE);
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(certCKA_ID,PR_TRUE);
+ return NULL;
+ }
+
+ return certCKA_ID;
+}
+
+/*
+ * Write the cert into the token.
+ */
+SECStatus
+PK11_ImportCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ CK_OBJECT_HANDLE key, char *nickname, PRBool includeTrust) {
+ int len = 0;
+ SECItem *keyID = pk11_mkcertKeyID(cert);
+ CK_ATTRIBUTE keyAttrs[] = {
+ { CKA_LABEL, NULL, 0},
+ { CKA_SUBJECT, NULL, 0},
+ };
+ CK_OBJECT_CLASS certc = CKO_CERTIFICATE;
+ CK_CERTIFICATE_TYPE certType = CKC_X_509;
+ CK_OBJECT_HANDLE certID;
+ CK_SESSION_HANDLE rwsession;
+ CK_BBOOL cktrue = CK_TRUE;
+ SECStatus rv = SECFailure;
+ CK_ATTRIBUTE certAttrs[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_LABEL, NULL, 0},
+ { CKA_CLASS, NULL, 0},
+ { CKA_TOKEN, NULL, 0},
+ { CKA_CERTIFICATE_TYPE, NULL, 0},
+ { CKA_SUBJECT, NULL, 0},
+ { CKA_ISSUER, NULL, 0},
+ { CKA_SERIAL_NUMBER, NULL, 0},
+ { CKA_VALUE, NULL, 0},
+ { CKA_NETSCAPE_TRUST, NULL, 0},
+ };
+ int certCount = sizeof(certAttrs)/sizeof(certAttrs[0]), keyCount = 2;
+ CK_ATTRIBUTE *attrs;
+ CK_RV crv;
+ SECCertUsage *certUsage = NULL;
+
+ if (keyID == NULL) {
+ PORT_SetError(SEC_ERROR_ADDING_CERT);
+ return rv;
+ }
+
+ len = ((nickname) ? PORT_Strlen(nickname) : 0);
+
+ attrs = certAttrs;
+ PK11_SETATTRS(attrs,CKA_ID, keyID->data, keyID->len); attrs++;
+ if(nickname) {
+ PK11_SETATTRS(attrs,CKA_LABEL, nickname, len ); attrs++;
+ }
+ PK11_SETATTRS(attrs,CKA_CLASS, &certc, sizeof(certc) ); attrs++;
+ PK11_SETATTRS(attrs,CKA_TOKEN, &cktrue, sizeof(cktrue) ); attrs++;
+ PK11_SETATTRS(attrs,CKA_CERTIFICATE_TYPE, &certType,
+ sizeof(certType)); attrs++;
+ PK11_SETATTRS(attrs,CKA_SUBJECT, cert->derSubject.data,
+ cert->derSubject.len ); attrs++;
+ PK11_SETATTRS(attrs,CKA_ISSUER, cert->derIssuer.data,
+ cert->derIssuer.len ); attrs++;
+ PK11_SETATTRS(attrs,CKA_SERIAL_NUMBER, cert->serialNumber.data,
+ cert->serialNumber.len); attrs++;
+ PK11_SETATTRS(attrs,CKA_VALUE, cert->derCert.data, cert->derCert.len);
+ if(includeTrust && PK11_IsInternal(slot)) {
+ attrs++;
+ certUsage = (SECCertUsage*)PORT_Alloc(sizeof(SECCertUsage));
+ if(!certUsage) {
+ SECITEM_FreeItem(keyID,PR_TRUE);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return rv;
+ }
+ *certUsage = certUsageUserCertImport;
+ PK11_SETATTRS(attrs,CKA_NETSCAPE_TRUST, certUsage, sizeof(SECCertUsage));
+ } else {
+ certCount--;
+ }
+
+ attrs = keyAttrs;
+ if(nickname) {
+ PK11_SETATTRS(attrs,CKA_LABEL, nickname, len ); attrs++;
+ }
+ PK11_SETATTRS(attrs,CKA_SUBJECT, cert->derSubject.data,
+ cert->derSubject.len );
+
+ if(!nickname) {
+ certCount--;
+ keyCount--;
+ }
+
+ rwsession = PK11_GetRWSession(slot);
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(rwsession,key,keyAttrs,
+ keyCount);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ goto done;
+ }
+
+ crv = PK11_GETTAB(slot)->
+ C_CreateObject(rwsession,certAttrs,certCount,&certID);
+ if (crv == CKR_OK) {
+ rv = SECSuccess;
+ } else {
+ PORT_SetError( PK11_MapError(crv) );
+ }
+
+done:
+ SECITEM_FreeItem(keyID,PR_TRUE);
+ PK11_RestoreROSession(slot,rwsession);
+ if(certUsage) {
+ PORT_Free(certUsage);
+ }
+ return rv;
+
+}
+
+/*
+ * get a certificate handle, look at the cached handle first..
+ */
+CK_OBJECT_HANDLE
+pk11_getcerthandle(PK11SlotInfo *slot, CERTCertificate *cert,
+ CK_ATTRIBUTE *theTemplate,int tsize)
+{
+ CK_OBJECT_HANDLE certh;
+
+ if (cert->slot == slot) {
+ certh = cert->pkcs11ID;
+ if (certh == CK_INVALID_KEY) {
+ certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize);
+ cert->pkcs11ID = certh;
+ }
+ } else {
+ certh = pk11_FindObjectByTemplate(slot,theTemplate,tsize);
+ }
+ return certh;
+}
+
+/*
+ * return the private key From a given Cert
+ */
+SECKEYPrivateKey *
+PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx) {
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_VALUE, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_OBJECT_HANDLE certh;
+ CK_OBJECT_HANDLE keyh;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ SECStatus rv;
+
+ PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data,
+ cert->derCert.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass));
+
+ /*
+ * issue the find
+ */
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ certh = pk11_getcerthandle(slot,cert,theTemplate,tsize);
+ if (certh == CK_INVALID_KEY) {
+ return NULL;
+ }
+ keyh = PK11_MatchItem(slot,certh,CKO_PRIVATE_KEY);
+ if (keyh == CK_INVALID_KEY) { return NULL; }
+ return PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyh, wincx);
+}
+
+
+/*
+ * return the private key with the given ID
+ */
+static CK_OBJECT_HANDLE
+pk11_FindPrivateKeyFromCertID(PK11SlotInfo *slot, SECItem *keyID) {
+ CK_OBJECT_CLASS privKey = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ { CKA_CLASS, NULL, 0 },
+ };
+ /* if you change the array, change the variable below as well */
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+
+ PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len ); attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &privKey, sizeof(privKey));
+
+ return pk11_FindObjectByTemplate(slot,theTemplate,tsize);
+}
+
+/*
+ * import a cert for a private key we have already generated. Set the label
+ * on both to be the nickname. This is for the Key Gen, orphaned key case.
+ */
+PK11SlotInfo *
+PK11_KeyForCertExists(CERTCertificate *cert, CK_OBJECT_HANDLE *keyPtr,
+ void *wincx) {
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ SECItem *keyID;
+ CK_OBJECT_HANDLE key;
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+
+ keyID = pk11_mkcertKeyID(cert);
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx);
+ if ((keyID == NULL) || (list == NULL)) {
+ if (keyID) SECITEM_FreeItem(keyID,PR_TRUE);
+ if (list) PK11_FreeSlotList(list);
+ return NULL;
+ }
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head ; le; le = le->next) {
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) continue;
+
+ key = pk11_FindPrivateKeyFromCertID(le->slot,keyID);
+ if (key != CK_INVALID_KEY) {
+ slot = PK11_ReferenceSlot(le->slot);
+ if (keyPtr) *keyPtr = key;
+ break;
+ }
+ }
+
+ SECITEM_FreeItem(keyID,PR_TRUE);
+ PK11_FreeSlotList(list);
+ return slot;
+
+}
+
+PK11SlotInfo *
+PK11_ImportCertForKey(CERTCertificate *cert, char *nickname,void *wincx) {
+ PK11SlotInfo *slot = NULL;
+ CK_OBJECT_HANDLE key;
+
+ slot = PK11_KeyForCertExists(cert,&key,wincx);
+
+ if (slot) {
+ if (PK11_ImportCert(slot,cert,key,nickname,PR_FALSE) != SECSuccess) {
+ PK11_FreeSlot(slot);
+ slot = NULL;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_ADDING_CERT);
+ }
+
+ return slot;
+}
+
+static CK_OBJECT_HANDLE
+pk11_FindCertObjectByTemplate(PK11SlotInfo **slotPtr,
+ CK_ATTRIBUTE *searchTemplate, int count, void *wincx) {
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY;
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+
+ *slotPtr = NULL;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx);
+ if (list == NULL) {
+ if (list) PK11_FreeSlotList(list);
+ return CK_INVALID_KEY;
+ }
+
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head ; le; le = le->next) {
+ if (!PK11_IsFriendly(le->slot)) {
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) continue;
+ }
+
+ certHandle = pk11_FindObjectByTemplate(le->slot,searchTemplate,count);
+ if (certHandle != CK_INVALID_KEY) {
+ slot = PK11_ReferenceSlot(le->slot);
+ break;
+ }
+ }
+
+ PK11_FreeSlotList(list);
+
+ if (slot == NULL) {
+ return CK_INVALID_KEY;
+ }
+ *slotPtr = slot;
+ return certHandle;
+}
+
+
+/*
+ * We're looking for a cert which we have the private key for that's on the
+ * list of recipients. This searches one slot.
+ */
+static CK_OBJECT_HANDLE
+pk11_FindCertObjectByRecipient(PK11SlotInfo *slot,
+ SEC_PKCS7RecipientInfo **recipientArray,SEC_PKCS7RecipientInfo **rip)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_OBJECT_CLASS peerClass ;
+ CK_ATTRIBUTE searchTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_ISSUER, NULL, 0 },
+ { CKA_SERIAL_NUMBER, NULL, 0}
+ };
+ int count = sizeof(searchTemplate)/sizeof(CK_ATTRIBUTE);
+ SEC_PKCS7RecipientInfo *ri = NULL;
+ CK_ATTRIBUTE *attrs;
+ int i;
+
+
+ peerClass = CKO_PRIVATE_KEY;
+ if (!PK11_IsLoggedIn(slot,NULL) && PK11_NeedLogin(slot)) {
+ peerClass = CKO_PUBLIC_KEY;
+ }
+
+ for (i=0; (ri = recipientArray[i]) != NULL; i++) {
+ attrs = searchTemplate;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass,sizeof(certClass)); attrs++;
+ PK11_SETATTRS(attrs, CKA_ISSUER, ri->issuerAndSN->derIssuer.data,
+ ri->issuerAndSN->derIssuer.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_SERIAL_NUMBER,
+ ri->issuerAndSN->serialNumber.data,ri->issuerAndSN->serialNumber.len);
+
+ certHandle = pk11_FindObjectByTemplate(slot,searchTemplate,count);
+ if (certHandle != CK_INVALID_KEY) {
+ CERTCertificate *cert = pk11_fastCert(slot,certHandle,NULL,NULL);
+ if (PK11_IsUserCert(slot,cert,certHandle)) {
+ /* we've found a cert handle, now let's see if there is a key
+ * associated with it... */
+ *rip = ri;
+
+ CERT_DestroyCertificate(cert);
+ return certHandle;
+ }
+ CERT_DestroyCertificate(cert);
+ }
+ }
+ *rip = NULL;
+ return CK_INVALID_KEY;
+}
+
+/*
+ * This function is the same as above, but it searches all the slots.
+ */
+static CK_OBJECT_HANDLE
+pk11_AllFindCertObjectByRecipient(PK11SlotInfo **slotPtr,
+ SEC_PKCS7RecipientInfo **recipientArray,SEC_PKCS7RecipientInfo **rip,
+ void *wincx) {
+ PK11SlotList *list;
+ PK11SlotListElement *le;
+ CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY;
+ PK11SlotInfo *slot = NULL;
+ SECStatus rv;
+
+ *slotPtr = NULL;
+
+ /* get them all! */
+ list = PK11_GetAllTokens(CKM_INVALID_MECHANISM,PR_FALSE,PR_TRUE,wincx);
+ if (list == NULL) {
+ if (list) PK11_FreeSlotList(list);
+ return CK_INVALID_KEY;
+ }
+
+ *rip = NULL;
+
+ /* Look for the slot that holds the Key */
+ for (le = list->head ; le; le = le->next) {
+ if ( !PK11_IsFriendly(le->slot)) {
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) continue;
+ }
+
+ certHandle = pk11_FindCertObjectByRecipient(le->slot,
+ recipientArray,rip);
+ if (certHandle != CK_INVALID_KEY) {
+ slot = PK11_ReferenceSlot(le->slot);
+ break;
+ }
+ }
+
+ PK11_FreeSlotList(list);
+
+ if (slot == NULL) {
+ return CK_INVALID_KEY;
+ }
+ *slotPtr = slot;
+ return certHandle;
+}
+
+/*
+ * We need to invert the search logic for PKCS 7 because if we search for
+ * each cert on the list over all the slots, we wind up with lots of spurious
+ * password prompts. This way we get only one password prompt per slot, at
+ * the max, and most of the time we can find the cert, and only prompt for
+ * the key...
+ */
+CERTCertificate *
+PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slotPtr,
+ SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip,
+ SECKEYPrivateKey**privKey, void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle = CK_INVALID_KEY;
+ CK_OBJECT_HANDLE keyHandle = CK_INVALID_KEY;
+ PK11SlotInfo *slot = NULL;
+ CERTCertificate *cert = NULL;
+ SECStatus rv;
+
+ *privKey = NULL;
+ certHandle = pk11_AllFindCertObjectByRecipient(slotPtr,array,rip,wincx);
+ if (certHandle == CK_INVALID_KEY) {
+ return NULL;
+ }
+
+ rv = PK11_Authenticate(*slotPtr,PR_TRUE,wincx);
+ if (rv != SECSuccess) {
+ PK11_FreeSlot(*slotPtr);
+ *slotPtr = NULL;
+ return NULL;
+ }
+
+ keyHandle = PK11_MatchItem(*slotPtr,certHandle,CKO_PRIVATE_KEY);
+ if (keyHandle == CK_INVALID_KEY) {
+ PK11_FreeSlot(*slotPtr);
+ *slotPtr = NULL;
+ return NULL;
+ }
+
+ *privKey = PK11_MakePrivKey(*slotPtr, nullKey, PR_TRUE, keyHandle, wincx);
+ if (*privKey == NULL) {
+ PK11_FreeSlot(*slotPtr);
+ *slotPtr = NULL;
+ return NULL;
+ }
+ cert = PK11_MakeCertFromHandle(*slotPtr,certHandle,NULL);
+ if (cert == NULL) {
+ PK11_FreeSlot(*slotPtr);
+ SECKEY_DestroyPrivateKey(*privKey);
+ *slotPtr = NULL;
+ *privKey = NULL;
+ return NULL;
+ }
+ return cert;
+}
+
+CERTCertificate *
+PK11_FindCertByIssuerAndSN(PK11SlotInfo **slotPtr, CERTIssuerAndSN *issuerSN,
+ void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CERTCertificate *cert = NULL;
+ CK_ATTRIBUTE searchTemplate[] = {
+ { CKA_ISSUER, NULL, 0 },
+ { CKA_SERIAL_NUMBER, NULL, 0}
+ };
+ int count = sizeof(searchTemplate)/sizeof(CK_ATTRIBUTE);
+ CK_ATTRIBUTE *attrs = searchTemplate;
+
+ PK11_SETATTRS(attrs, CKA_ISSUER, issuerSN->derIssuer.data,
+ issuerSN->derIssuer.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_SERIAL_NUMBER, issuerSN->serialNumber.data,
+ issuerSN->serialNumber.len);
+
+ certHandle = pk11_FindCertObjectByTemplate
+ (slotPtr,searchTemplate,count,wincx);
+ if (certHandle == CK_INVALID_KEY) {
+ return NULL;
+ }
+ cert = PK11_MakeCertFromHandle(*slotPtr,certHandle,NULL);
+ if (cert == NULL) {
+ PK11_FreeSlot(*slotPtr);
+ return NULL;
+ }
+ return cert;
+}
+
+CK_OBJECT_HANDLE
+PK11_FindObjectForCert(CERTCertificate *cert, void *wincx, PK11SlotInfo **pSlot)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_ATTRIBUTE searchTemplate = { CKA_VALUE, NULL, 0 };
+
+ PK11_SETATTRS(&searchTemplate, CKA_VALUE, cert->derCert.data,
+ cert->derCert.len);
+
+ if (cert->slot) {
+ certHandle = pk11_getcerthandle(cert->slot,cert,&searchTemplate,1);
+ if (certHandle != CK_INVALID_KEY) {
+ *pSlot = PK11_ReferenceSlot(cert->slot);
+ return certHandle;
+ }
+ }
+
+ certHandle = pk11_FindCertObjectByTemplate(pSlot,&searchTemplate,1,wincx);
+ if (certHandle != CK_INVALID_KEY) {
+ if (cert->slot == NULL) {
+ cert->slot = PK11_ReferenceSlot(*pSlot);
+ cert->pkcs11ID = certHandle;
+ cert->ownSlot = PR_FALSE;
+ }
+ }
+
+ return(certHandle);
+}
+
+SECKEYPrivateKey *
+PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_OBJECT_HANDLE keyHandle;
+ PK11SlotInfo *slot = NULL;
+ SECKEYPrivateKey *privKey;
+ SECStatus rv;
+
+ certHandle = PK11_FindObjectForCert(cert, wincx, &slot);
+ if (certHandle == CK_INVALID_KEY) {
+ return NULL;
+ }
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ keyHandle = PK11_MatchItem(slot,certHandle,CKO_PRIVATE_KEY);
+ if (keyHandle == CK_INVALID_KEY) {
+ PK11_FreeSlot(slot);
+ return NULL;
+ }
+ privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx);
+ PK11_FreeSlot(slot);
+ return privKey;
+}
+
+CK_OBJECT_HANDLE
+pk11_FindPubKeyByAnyCert(CERTCertificate *cert, PK11SlotInfo **slot, void *wincx)
+{
+ CK_OBJECT_HANDLE certHandle;
+ CK_OBJECT_HANDLE keyHandle;
+
+ certHandle = PK11_FindObjectForCert(cert, wincx, slot);
+ if (certHandle == CK_INVALID_KEY) {
+ return CK_INVALID_KEY;
+ }
+ keyHandle = PK11_MatchItem(*slot,certHandle,CKO_PUBLIC_KEY);
+ if (keyHandle == CK_INVALID_KEY) {
+ PK11_FreeSlot(*slot);
+ return CK_INVALID_KEY;
+ }
+ return keyHandle;
+}
+
+SECKEYPrivateKey *
+PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID, void *wincx)
+{
+ CK_OBJECT_HANDLE keyHandle;
+ SECKEYPrivateKey *privKey;
+
+ keyHandle = pk11_FindPrivateKeyFromCertID(slot, keyID);
+ if (keyHandle == CK_INVALID_KEY) {
+ return NULL;
+ }
+ privKey = PK11_MakePrivKey(slot, nullKey, PR_TRUE, keyHandle, wincx);
+ return privKey;
+}
+
+/*
+ * find the number of certs in the slot with the same subject name
+ */
+int
+PK11_NumberCertsForCertSubject(CERTCertificate *cert)
+{
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr = theTemplate;
+ int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+
+ PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++;
+ PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len);
+
+ if ((cert->slot == NULL) || (cert->slot->isInternal)) {
+ return 0;
+ }
+
+ return PK11_NumberObjectsFor(cert->slot,theTemplate,templateSize);
+}
+
+/*
+ * Walk all the certs with the same subject
+ */
+SECStatus
+PK11_TraverseCertsForSubject(CERTCertificate *cert,
+ SECStatus(* callback)(CERTCertificate*, void *), void *arg)
+{
+ if(!cert) {
+ return SECFailure;
+ }
+
+ return PK11_TraverseCertsForSubjectInSlot(cert, cert->slot, callback, arg);
+}
+
+SECStatus
+PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert, PK11SlotInfo *slot,
+ SECStatus(* callback)(CERTCertificate*, void *), void *arg)
+{
+ pk11DoCertCallback caller;
+ pk11TraverseSlotCert callarg;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr = theTemplate;
+ int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+
+ PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++;
+ PK11_SETATTRS(attr,CKA_SUBJECT,cert->derSubject.data,cert->derSubject.len);
+
+ if ((slot == NULL) || (slot->isInternal)) {
+ return SECSuccess;
+ }
+ caller.noslotcallback = callback;
+ caller.callback = NULL;
+ caller.callbackArg = arg;
+ callarg.callback = pk11_DoCerts;
+ callarg.callbackArg = (void *) & caller;
+ callarg.findTemplate = theTemplate;
+ callarg.templateCount = templateSize;
+
+ return PK11_TraverseSlot(slot, &callarg);
+}
+
+SECStatus
+PK11_TraverseCertsForNicknameInSlot(SECItem *nickname, PK11SlotInfo *slot,
+ SECStatus(* callback)(CERTCertificate*, void *), void *arg)
+{
+ pk11DoCertCallback caller;
+ pk11TraverseSlotCert callarg;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ { CKA_LABEL, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr = theTemplate;
+ int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+
+ if(!nickname) {
+ return SECSuccess;
+ }
+
+ PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++;
+ PK11_SETATTRS(attr,CKA_LABEL,nickname->data,nickname->len);
+
+ if ((slot == NULL) || (slot->isInternal)) {
+ return SECSuccess;
+ }
+
+ caller.noslotcallback = callback;
+ caller.callback = NULL;
+ caller.callbackArg = arg;
+ callarg.callback = pk11_DoCerts;
+ callarg.callbackArg = (void *) & caller;
+ callarg.findTemplate = theTemplate;
+ callarg.templateCount = templateSize;
+
+ return PK11_TraverseSlot(slot, &callarg);
+}
+
+SECStatus
+PK11_TraverseCertsInSlot(PK11SlotInfo *slot,
+ SECStatus(* callback)(CERTCertificate*, void *), void *arg)
+{
+ pk11DoCertCallback caller;
+ pk11TraverseSlotCert callarg;
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_CLASS, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attr = theTemplate;
+ int templateSize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+
+ PK11_SETATTRS(attr,CKA_CLASS, &certClass, sizeof(certClass)); attr++;
+
+ if (slot == NULL) {
+ return SECSuccess;
+ }
+
+ caller.noslotcallback = callback;
+ caller.callback = NULL;
+ caller.callbackArg = arg;
+ callarg.callback = pk11_DoCerts;
+ callarg.callbackArg = (void *) & caller;
+ callarg.findTemplate = theTemplate;
+ callarg.templateCount = templateSize;
+
+ return PK11_TraverseSlot(slot, &callarg);
+}
+
+/*
+ * return the certificate associated with a derCert
+ */
+CERTCertificate *
+PK11_FindCertFromDERCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_VALUE, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_OBJECT_HANDLE certh;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ SECStatus rv;
+
+ PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data,
+ cert->derCert.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass));
+
+ /*
+ * issue the find
+ */
+ if ( !PK11_IsFriendly(slot)) {
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) return NULL;
+ }
+
+ certh = pk11_getcerthandle(slot,cert,theTemplate,tsize);
+ if (certh == CK_INVALID_KEY) {
+ return NULL;
+ }
+ return PK11_MakeCertFromHandle(slot, certh, NULL);
+}
+
+/*
+ * return the certificate associated with a derCert
+ */
+CERTCertificate *
+PK11_FindCertFromDERSubjectAndNickname(PK11SlotInfo *slot,
+ CERTCertificate *cert,
+ char *nickname, void *wincx)
+{
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_SUBJECT, NULL, 0 },
+ { CKA_LABEL, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_OBJECT_HANDLE certh;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ SECStatus rv;
+
+ PK11_SETATTRS(attrs, CKA_SUBJECT, cert->derSubject.data,
+ cert->derSubject.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname, PORT_Strlen(nickname));
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass));
+
+ /*
+ * issue the find
+ */
+ if ( !PK11_IsFriendly(slot)) {
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) return NULL;
+ }
+
+ certh = pk11_getcerthandle(slot,cert,theTemplate,tsize);
+ if (certh == CK_INVALID_KEY) {
+ return NULL;
+ }
+
+ return PK11_MakeCertFromHandle(slot, certh, NULL);
+}
+
+/*
+ * import a cert for a private key we have already generated. Set the label
+ * on both to be the nickname.
+ */
+static CK_OBJECT_HANDLE
+pk11_findKeyObjectByDERCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ SECItem *keyID;
+ CK_OBJECT_HANDLE key;
+ SECStatus rv;
+
+ if((slot == NULL) || (cert == NULL)) {
+ return CK_INVALID_KEY;
+ }
+
+ keyID = pk11_mkcertKeyID(cert);
+ if(keyID == NULL) {
+ return CK_INVALID_KEY;
+ }
+
+ key = CK_INVALID_KEY;
+
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) goto loser;
+
+ key = pk11_FindPrivateKeyFromCertID(slot, keyID);
+
+loser:
+ SECITEM_ZfreeItem(keyID, PR_TRUE);
+ return key;
+}
+
+SECKEYPrivateKey *
+PK11_FindKeyByDERCert(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx)
+{
+ CK_OBJECT_HANDLE keyHandle;
+
+ if((slot == NULL) || (cert == NULL)) {
+ return NULL;
+ }
+
+ keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx);
+ if (keyHandle == CK_INVALID_KEY) {
+ return NULL;
+ }
+
+ return PK11_MakePrivKey(slot,nullKey,PR_TRUE,keyHandle,wincx);
+}
+
+SECStatus
+PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert,
+ char *nickname,
+ PRBool addCertUsage,void *wincx)
+{
+ CK_OBJECT_HANDLE keyHandle;
+
+ if((slot == NULL) || (cert == NULL) || (nickname == NULL)) {
+ return SECFailure;
+ }
+
+ keyHandle = pk11_findKeyObjectByDERCert(slot, cert, wincx);
+ if (keyHandle == CK_INVALID_KEY) {
+ return SECFailure;
+ }
+
+ return PK11_ImportCert(slot, cert, keyHandle, nickname, addCertUsage);
+}
+
+
+/* remove when the real version comes out */
+#define SEC_OID_MISSI_KEA 300 /* until we have v3 stuff merged */
+PRBool
+KEAPQGCompare(CERTCertificate *server,CERTCertificate *cert) {
+
+ if ( SECKEY_KEAParamCompare(server,cert) == SECEqual ) {
+ return PR_TRUE;
+ } else {
+ return PR_FALSE;
+ }
+}
+
+PRBool
+PK11_FortezzaHasKEA(CERTCertificate *cert) {
+ /* look at the subject and see if it is a KEA for MISSI key */
+ SECOidData *oid;
+
+ if ((cert->trust == NULL) ||
+ ((cert->trust->sslFlags & CERTDB_USER) != CERTDB_USER)) {
+ return PR_FALSE;
+ }
+
+ oid = SECOID_FindOID(&cert->subjectPublicKeyInfo.algorithm.algorithm);
+
+
+ return (PRBool)((oid->offset == SEC_OID_MISSI_KEA_DSS_OLD) ||
+ (oid->offset == SEC_OID_MISSI_KEA_DSS) ||
+ (oid->offset == SEC_OID_MISSI_KEA)) ;
+}
+
+/*
+ * Find a kea cert on this slot that matches the domain of it's peer
+ */
+static CERTCertificate
+*pk11_GetKEAMate(PK11SlotInfo *slot,CERTCertificate *peer)
+{
+ int i;
+ CERTCertificate *returnedCert = NULL;
+
+ for (i=0; i < slot->cert_count; i++) {
+ CERTCertificate *cert = slot->cert_array[i];
+
+ if (PK11_FortezzaHasKEA(cert) && KEAPQGCompare(peer,cert)) {
+ returnedCert = CERT_DupCertificate(cert);
+ break;
+ }
+ }
+ return returnedCert;
+}
+
+/*
+ * The following is a FORTEZZA only Certificate request. We call this when we
+ * are doing a non-client auth SSL connection. We are only interested in the
+ * fortezza slots, and we are only interested in certs that share the same root
+ * key as the server.
+ */
+CERTCertificate *
+PK11_FindBestKEAMatch(CERTCertificate *server, void *wincx)
+{
+ PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE,
+ PR_FALSE,PR_TRUE,wincx);
+ PK11SlotListElement *le;
+ CERTCertificate *returnedCert = NULL;
+ SECStatus rv;
+
+ /* loop through all the fortezza tokens */
+ for (le = keaList->head; le; le = le->next) {
+ rv = PK11_Authenticate(le->slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) continue;
+ if (le->slot->session == CK_INVALID_SESSION) {
+ continue;
+ }
+ returnedCert = pk11_GetKEAMate(le->slot,server);
+ if (returnedCert) break;
+ }
+ PK11_FreeSlotList(keaList);
+
+ return returnedCert;
+}
+
+/*
+ * find a matched pair of kea certs to key exchange parameters from one
+ * fortezza card to another as necessary.
+ */
+SECStatus
+PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1, PK11SlotInfo *slot2,
+ CERTCertificate **cert1, CERTCertificate **cert2)
+{
+ PK11SlotList *keaList = PK11_GetAllTokens(CKM_KEA_KEY_DERIVE,
+ PR_FALSE,PR_TRUE,NULL);
+ CERTCertificate *returnedCert = NULL;
+ int i;
+
+ for (i=0; i < slot1->cert_count; i++) {
+ CERTCertificate *cert = slot1->cert_array[i];
+
+ if (PK11_FortezzaHasKEA(cert)) {
+ returnedCert = pk11_GetKEAMate(slot2,cert);
+ if (returnedCert != NULL) {
+ *cert2 = returnedCert;
+ *cert1 = CERT_DupCertificate(cert);
+ return SECSuccess;
+ }
+ }
+ }
+ return SECFailure;
+}
+
+SECOidTag
+PK11_FortezzaMapSig(SECOidTag algTag)
+{
+ switch (algTag) {
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_DSS:
+ case SEC_OID_MISSI_DSS_OLD:
+ case SEC_OID_MISSI_KEA_DSS_OLD:
+ case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
+ return SEC_OID_ANSIX9_DSA_SIGNATURE;
+ default:
+ break;
+ }
+ return algTag;
+}
+
+/*
+ * return the private key From a given Cert
+ */
+CK_OBJECT_HANDLE
+PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert, void *wincx)
+{
+ CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_VALUE, NULL, 0 },
+ { CKA_CLASS, NULL, 0 }
+ };
+ /* if you change the array, change the variable below as well */
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+ SECStatus rv;
+
+ PK11_SETATTRS(attrs, CKA_VALUE, cert->derCert.data,
+ cert->derCert.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &certClass, sizeof(certClass));
+
+ /*
+ * issue the find
+ */
+ rv = PK11_Authenticate(slot, PR_TRUE, wincx);
+ if (rv != SECSuccess) {
+ return CK_INVALID_KEY;
+ }
+
+ return pk11_getcerthandle(slot,cert,theTemplate,tsize);
+}
+
+SECItem *
+PK11_GetKeyIDFromCert(CERTCertificate *cert, void *wincx)
+{
+ CK_OBJECT_HANDLE handle;
+ PK11SlotInfo *slot = NULL;
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ };
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ SECItem *item = NULL;
+ CK_RV crv;
+
+ handle = PK11_FindObjectForCert(cert,wincx,&slot);
+ if (handle == CK_INVALID_KEY) {
+ goto loser;
+ }
+
+
+ crv = PK11_GetAttributes(NULL,slot,handle,theTemplate,tsize);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ goto loser;
+ }
+
+ item = PORT_ZNew(SECItem);
+ if (item) {
+ item->data = theTemplate[0].pValue;
+ item->len = theTemplate[0].ulValueLen;
+ }
+
+
+loser:
+ PK11_FreeSlot(slot);
+ return item;
+}
+
+SECItem *
+PK11_GetKeyIDFromPrivateKey(SECKEYPrivateKey *key, void *wincx)
+{
+ CK_ATTRIBUTE theTemplate[] = {
+ { CKA_ID, NULL, 0 },
+ };
+ int tsize = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ SECItem *item = NULL;
+ CK_RV crv;
+
+ crv = PK11_GetAttributes(NULL,key->pkcs11Slot,key->pkcs11ID,
+ theTemplate,tsize);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ goto loser;
+ }
+
+ item = PORT_ZNew(SECItem);
+ if (item) {
+ item->data = theTemplate[0].pValue;
+ item->len = theTemplate[0].ulValueLen;
+ }
+
+
+loser:
+ return item;
+}
diff --git a/security/nss/lib/pk11wrap/pk11db.c b/security/nss/lib/pk11wrap/pk11db.c
new file mode 100644
index 000000000..799f894d1
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11db.c
@@ -0,0 +1,643 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. This file is written to abstract away how the modules are
+ * stored so we can deside that later.
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "prlock.h"
+#include "pkcs11.h"
+#include "secmodi.h"
+#include "pk11func.h"
+#include "mcom_db.h"
+
+/* create a new module */
+SECMODModule *SECMOD_NewModule(void) {
+ SECMODModule *newMod;
+ PRArenaPool *arena;
+
+
+ /* create an arena in which dllName and commonName can be
+ * allocated.
+ */
+ arena = PORT_NewArena(512);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ newMod = (SECMODModule *)PORT_ArenaAlloc(arena,sizeof (SECMODModule));
+ if (newMod == NULL) {
+ PORT_FreeArena(arena,PR_FALSE);
+ return NULL;
+ }
+
+ /*
+ * initialize of the fields of the module
+ */
+ newMod->arena = arena;
+ newMod->internal = PR_FALSE;
+ newMod->loaded = PR_FALSE;
+ newMod->isFIPS = PR_FALSE;
+ newMod->dllName = NULL;
+ newMod->commonName = NULL;
+ newMod->library = NULL;
+ newMod->functionList = NULL;
+ newMod->slotCount = 0;
+ newMod->slots = NULL;
+ newMod->slotInfo = NULL;
+ newMod->slotInfoCount = 0;
+ newMod->refCount = 1;
+ newMod->ssl[0] = 0;
+ newMod->ssl[1] = 0;
+#ifdef PKCS11_USE_THREADS
+ newMod->refLock = (void *)PR_NewLock();
+ if (newMod->refLock == NULL) {
+ PORT_FreeArena(arena,PR_FALSE);
+ return NULL;
+ }
+#else
+ newMod->refLock = NULL;
+#endif
+ return newMod;
+
+}
+
+/* create a new ModuleListElement */
+SECMODModuleList *SECMOD_NewModuleListElement(void) {
+ SECMODModuleList *newModList;
+
+ newModList= (SECMODModuleList *) PORT_Alloc(sizeof(SECMODModuleList));
+ if (newModList) {
+ newModList->next = NULL;
+ newModList->module = NULL;
+ }
+ return newModList;
+}
+
+static unsigned long internalFlags = SECMOD_RSA_FLAG|SECMOD_DSA_FLAG|
+ SECMOD_RC2_FLAG| SECMOD_RC4_FLAG|SECMOD_DES_FLAG|SECMOD_RANDOM_FLAG|
+ SECMOD_SHA1_FLAG|SECMOD_MD5_FLAG|SECMOD_MD2_FLAG;
+
+/* create a Internal module */
+SECMODModule *SECMOD_NewInternal(void) {
+ SECMODModule *intern;
+ static PK11PreSlotInfo internSlotInfo =
+ { 1, SECMOD_RSA_FLAG|SECMOD_DSA_FLAG|SECMOD_RC2_FLAG|
+ SECMOD_RC4_FLAG|SECMOD_DES_FLAG|SECMOD_RANDOM_FLAG|
+ SECMOD_SHA1_FLAG|SECMOD_MD5_FLAG|SECMOD_MD2_FLAG, -1, 30 };
+
+ intern = SECMOD_NewModule();
+ if (intern == NULL) {
+ return NULL;
+ }
+
+ /*
+ * make this module an internal module
+ */
+ intern->commonName = "Netscape Internal PKCS #11 Module";
+ intern->internal = PR_TRUE;
+ intern->slotInfoCount = 1;
+ intern->slotInfo = &internSlotInfo;
+
+ return (intern);
+}
+
+/* create a FIPS Internal module */
+SECMODModule *SECMOD_GetFIPSInternal(void) {
+ SECMODModule *intern;
+
+ intern = SECMOD_NewInternal();
+ if (intern == NULL) {
+ return NULL;
+ }
+
+ /*
+ * make this module a FIPS internal module
+ */
+ intern->slotInfo[0].slotID = 3; /* FIPS slot */
+ intern->commonName = "Netscape Internal FIPS PKCS #11 Module";
+ intern->isFIPS = PR_TRUE;
+
+ return (intern);
+}
+
+SECMODModule *SECMOD_DupModule(SECMODModule *old) {
+ SECMODModule *newMod;
+
+ newMod = SECMOD_NewModule();
+ if (newMod == NULL) {
+ return NULL;
+ }
+
+ /*
+ * initialize of the fields of the module
+ */
+ newMod->dllName = PORT_ArenaStrdup(newMod->arena,old->dllName);
+ newMod->commonName = PORT_ArenaStrdup(newMod->arena,old->commonName);;
+
+ return newMod;
+
+}
+
+/*
+ * make a new reference to a module so It doesn't go away on us
+ */
+SECMODModule *
+SECMOD_ReferenceModule(SECMODModule *module) {
+ PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);)
+ PORT_Assert(module->refCount > 0);
+
+ module->refCount++;
+ PK11_USE_THREADS(PR_Unlock((PRLock*)module->refLock);)
+ return module;
+}
+
+
+/* destroy an existing module */
+void
+SECMOD_DestroyModule(SECMODModule *module) {
+ PRBool willfree = PR_FALSE;
+ int slotCount;
+ int i;
+
+ PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);)
+ if (module->refCount-- == 1) {
+ willfree = PR_TRUE;
+ }
+ PORT_Assert(willfree || (module->refCount > 0));
+ PK11_USE_THREADS(PR_Unlock((PRLock *)module->refLock);)
+
+ if (!willfree) {
+ return;
+ }
+
+ /* slots can't really disappear until our module starts freeing them,
+ * so this check is safe */
+ slotCount = module->slotCount;
+ if (slotCount == 0) {
+ SECMOD_SlotDestroyModule(module,PR_FALSE);
+ return;
+ }
+
+ /* now free all out slots, when they are done, they will cause the
+ * module to disappear altogether */
+ for (i=0 ; i < slotCount; i++) {
+ if (!module->slots[i]->disabled) {
+ PK11_ClearSlotList(module->slots[i]);
+ }
+ PK11_FreeSlot(module->slots[i]);
+ }
+ /* WARNING: once the last slot has been freed is it possible (even likely)
+ * that module is no more... touching it now is a good way to go south */
+}
+
+
+/* we can only get here if we've destroyed the module, or some one has
+ * erroneously freed a slot that wasn't referenced. */
+void
+SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot) {
+ PRBool willfree = PR_FALSE;
+ if (fromSlot) {
+ PORT_Assert(module->refCount == 0);
+ PK11_USE_THREADS(PR_Lock((PRLock *)module->refLock);)
+ if (module->slotCount-- == 1) {
+ willfree = PR_TRUE;
+ }
+ PORT_Assert(willfree || (module->slotCount > 0));
+ PK11_USE_THREADS(PR_Unlock((PRLock *)module->refLock);)
+ if (!willfree) return;
+ }
+ if (module->loaded) {
+ SECMOD_UnloadModule(module);
+ }
+ PK11_USE_THREADS(PR_DestroyLock((PRLock *)module->refLock);)
+ PORT_FreeArena(module->arena,PR_FALSE);
+}
+
+/* destroy a list element
+ * this destroys a single element, and returns the next element
+ * on the chain. It makes it easy to implement for loops to delete
+ * the chain. It also make deleting a single element easy */
+SECMODModuleList *
+SECMOD_DestroyModuleListElement(SECMODModuleList *element) {
+ SECMODModuleList *next = element->next;
+
+ if (element->module) {
+ SECMOD_DestroyModule(element->module);
+ element->module = NULL;
+ }
+ PORT_Free(element);
+ return next;
+}
+
+
+/*
+ * Destroy an entire module list
+ */
+void
+SECMOD_DestroyModuleList(SECMODModuleList *list) {
+ SECMODModuleList *lp;
+
+ for ( lp = list; lp != NULL; lp = SECMOD_DestroyModuleListElement(lp)) ;
+}
+
+
+/* Construct a database key for a given module */
+static SECStatus secmod_MakeKey(DBT *key, SECMODModule * module) {
+ int len = 0;
+
+ len = PORT_Strlen(module->commonName);
+ key->data = module->commonName;
+ key->size = len;
+ return SECSuccess;
+}
+
+/* free out constructed database key */
+static void secmod_FreeKey(DBT *key) {
+ key->data = NULL;
+ key->size = 0;
+}
+
+typedef struct secmodDataStr secmodData;
+typedef struct secmodSlotDataStr secmodSlotData;
+struct secmodDataStr {
+ unsigned char major;
+ unsigned char minor;
+ unsigned char nameStart[2];
+ unsigned char slotOffset[2];
+ unsigned char internal;
+ unsigned char fips;
+ unsigned char ssl[8];
+ unsigned char names[4]; /* enough space for the length fields */
+};
+
+struct secmodSlotDataStr {
+ unsigned char slotID[4];
+ unsigned char defaultFlags[4];
+ unsigned char timeout[4];
+ unsigned char askpw;
+ unsigned char reserved[19]; /* this makes it a round 32 bytes */
+};
+
+#define SECMOD_DB_VERSION_MAJOR 0
+#define SECMOD_DB_VERSION_MINOR 4
+#define SECMOD_DB_NOUI_VERSION_MAJOR 0
+#define SECMOD_DB_NOUI_VERSION_MINOR 3
+
+#define SECMOD_PUTSHORT(dest,src) \
+ (dest)[1] = (unsigned char) ((src)&0xff); \
+ (dest)[0] = (unsigned char) (((src) >> 8) & 0xff);
+#define SECMOD_PUTLONG(dest,src) \
+ (dest)[3] = (unsigned char) ((src)&0xff); \
+ (dest)[2] = (unsigned char) (((src) >> 8) & 0xff); \
+ (dest)[1] = (unsigned char) (((src) >> 16) & 0xff); \
+ (dest)[0] = (unsigned char) (((src) >> 24) & 0xff);
+#define SECMOD_GETSHORT(src) \
+ ((unsigned short) (((src)[0] << 8) | (src)[1]))
+#define SECMOD_GETLONG(src) \
+ ((unsigned long) (( (unsigned long) (src)[0] << 24) | \
+ ( (unsigned long) (src)[1] << 16) | \
+ ( (unsigned long) (src)[2] << 8) | \
+ (unsigned long) (src)[3]))
+/*
+ * build a data base entry from a module
+ */
+static SECStatus secmod_EncodeData(DBT *data, SECMODModule * module) {
+ secmodData *encoded;
+ secmodSlotData *slot;
+ unsigned char *dataPtr;
+ unsigned short len, len2 = 0,count = 0;
+ unsigned short offset;
+ int dataLen, i, si;
+
+ len = PORT_Strlen(module->commonName);
+ if (module->dllName) {
+ len2 = PORT_Strlen(module->dllName);
+ }
+ if (module->slotCount != 0) {
+ for (i=0; i < module->slotCount; i++) {
+ if (module->slots[i]->defaultFlags != 0) {
+ count++;
+ }
+ }
+ } else {
+ count = module->slotInfoCount;
+ }
+ dataLen = sizeof(secmodData) + len + len2 + 2 +
+ count*sizeof(secmodSlotData);
+
+ data->data = (unsigned char *)
+ PORT_Alloc(dataLen);
+ encoded = (secmodData *)data->data;
+ dataPtr = (unsigned char *) data->data;
+ data->size = dataLen;
+
+ if (encoded == NULL) return SECFailure;
+
+ encoded->major = SECMOD_DB_VERSION_MAJOR;
+ encoded->minor = SECMOD_DB_VERSION_MINOR;
+ encoded->internal = (unsigned char) (module->internal ? 1 : 0);
+ encoded->fips = (unsigned char) (module->isFIPS ? 1 : 0);
+ SECMOD_PUTLONG(encoded->ssl,module->ssl[0]);
+ SECMOD_PUTLONG(&encoded->ssl[4],module->ssl[1]);
+
+ offset = (unsigned long) &(((secmodData *)0)->names[0]);
+ SECMOD_PUTSHORT(encoded->nameStart,offset);
+ offset = offset +len + len2 + 4;
+ SECMOD_PUTSHORT(encoded->slotOffset,offset);
+
+
+ SECMOD_PUTSHORT(&dataPtr[offset],count);
+ slot = (secmodSlotData *)(dataPtr+offset+2);
+
+ SECMOD_PUTSHORT(encoded->names,len);
+ PORT_Memcpy(&encoded->names[2],module->commonName,len);
+
+
+ SECMOD_PUTSHORT(&encoded->names[len+2],len2);
+ if (len2) PORT_Memcpy(&encoded->names[len+4],module->dllName,len2);
+
+ if (module->slotCount) {
+ for (i=0,si=0; i < module->slotCount; i++) {
+ if (module->slots[i]->defaultFlags) {
+ SECMOD_PUTLONG(slot[si].slotID, module->slots[i]->slotID);
+ SECMOD_PUTLONG(slot[si].defaultFlags,
+ module->slots[i]->defaultFlags);
+ SECMOD_PUTLONG(slot[si].timeout,module->slots[i]->timeout);
+ slot[si].askpw = module->slots[i]->askpw;
+ PORT_Memset(slot[si].reserved, 0, sizeof(slot[si].reserved));
+ si++;
+ }
+ }
+ } else {
+ for (i=0; i < module->slotInfoCount; i++) {
+ SECMOD_PUTLONG(slot[i].slotID, module->slotInfo[i].slotID);
+ SECMOD_PUTLONG(slot[i].defaultFlags,
+ module->slotInfo[i].defaultFlags);
+ SECMOD_PUTLONG(slot[i].timeout,module->slotInfo[i].timeout);
+ slot[i].askpw = module->slotInfo[i].askpw;
+ PORT_Memset(slot[i].reserved, 0, sizeof(slot[i].reserved));
+ }
+ }
+
+ return SECSuccess;
+
+}
+
+static void secmod_FreeData(DBT *data) {
+ if (data->data) {
+ PORT_Free(data->data);
+ }
+}
+
+
+/*
+ * build a module from the data base entry.
+ */
+static SECMODModule *secmod_DecodeData(DBT *data) {
+ SECMODModule * module;
+ secmodData *encoded;
+ secmodSlotData *slots;
+ unsigned char *names;
+ unsigned short len,len1;
+ unsigned long slotCount;
+ unsigned short offset;
+ PRBool isOldVersion = PR_FALSE;
+ int i;
+
+ encoded = (secmodData *)data->data;
+ names = (unsigned char *)data->data;
+ offset = SECMOD_GETSHORT(encoded->slotOffset);
+ slots = (secmodSlotData *) (names + offset + 2);
+ slotCount = SECMOD_GETSHORT(names + offset);
+ names += SECMOD_GETSHORT(encoded->nameStart);
+
+ module = SECMOD_NewModule();
+ if (module == NULL) return NULL;
+
+ module->internal = (encoded->internal != 0) ? PR_TRUE: PR_FALSE;
+ module->isFIPS = (encoded->fips != 0) ? PR_TRUE: PR_FALSE;
+ len = SECMOD_GETSHORT(names);
+
+ if (module->internal && (encoded->major == SECMOD_DB_NOUI_VERSION_MAJOR) &&
+ (encoded->minor <= SECMOD_DB_NOUI_VERSION_MINOR)) {
+ isOldVersion = PR_TRUE;
+ }
+
+ /* decode the common name */
+ module->commonName = (char*)PORT_ArenaAlloc(module->arena,len+1);
+ if (module->commonName == NULL) {
+ SECMOD_DestroyModule(module);
+ return NULL;
+ }
+ PORT_Memcpy(module->commonName,&names[2],len);
+ module->commonName[len] = 0;
+
+ /* decode the DLL name */
+ len1 = (names[len+2] << 8) | names[len+3];
+ if (len1) {
+ module->dllName = (char*)PORT_ArenaAlloc(module->arena,len1 + 1);
+ if (module->dllName == NULL) {
+ SECMOD_DestroyModule(module);
+ return NULL;
+ }
+ PORT_Memcpy(module->dllName,&names[len+4],len1);
+ module->dllName[len1] = 0;
+ }
+
+ module->slotInfoCount = slotCount;
+ module->slotInfo = (PK11PreSlotInfo *) PORT_ArenaAlloc(module->arena,
+ slotCount * sizeof(PK11PreSlotInfo));
+ for (i=0; i < (int) slotCount; i++) {
+ module->slotInfo[i].slotID = SECMOD_GETLONG(slots[i].slotID);
+ module->slotInfo[i].defaultFlags =
+ SECMOD_GETLONG(slots[i].defaultFlags);
+ if (isOldVersion && module->internal &&
+ (module->slotInfo[i].slotID != 2)) {
+ module->slotInfo[i].defaultFlags |= internalFlags;
+ }
+ module->slotInfo[i].timeout = SECMOD_GETLONG(slots[i].timeout);
+ module->slotInfo[i].askpw = slots[i].askpw;
+ if (module->slotInfo[i].askpw == 0xff) {
+ module->slotInfo[i].askpw = -1;
+ }
+ }
+
+ /* decode SSL cipher enable flags */
+ module->ssl[0] = SECMOD_GETLONG(encoded->ssl);
+ module->ssl[1] = SECMOD_GETLONG(&encoded->ssl[4]);
+
+ return (module);
+}
+
+/*
+ * open the PKCS #11 data base.
+ */
+static char *pkcs11dbName = NULL;
+void SECMOD_InitDB(char *dbname) {
+ pkcs11dbName = PORT_Strdup(dbname);
+}
+
+
+static DB *secmod_OpenDB(PRBool readOnly) {
+ DB *pkcs11db = NULL;
+ char *dbname;
+
+ if (pkcs11dbName == NULL) return NULL;
+ dbname = pkcs11dbName;
+
+ /* I'm sure we should do more checks here sometime... */
+ pkcs11db = dbopen(dbname, readOnly ? O_RDONLY : O_RDWR, 0600, DB_HASH, 0);
+
+ /* didn't exist? create it */
+ if (pkcs11db == NULL) {
+ if (readOnly) return NULL;
+
+ pkcs11db = dbopen( dbname,
+ O_RDWR | O_CREAT | O_TRUNC, 0600, DB_HASH, 0 );
+ if (pkcs11db) (* pkcs11db->sync)(pkcs11db, 0);
+ }
+ return pkcs11db;
+}
+
+static void secmod_CloseDB(DB *pkcs11db) {
+ (*pkcs11db->close)(pkcs11db);
+}
+
+/*
+ * Read all the existing modules in
+ */
+SECMODModuleList *
+SECMOD_ReadPermDB(void) {
+ DBT key,data;
+ int ret;
+ DB *pkcs11db = NULL;
+ SECMODModuleList *newmod = NULL,*mod = NULL;
+
+ pkcs11db = secmod_OpenDB(PR_TRUE);
+ if (pkcs11db == NULL) {
+ return NULL;
+ }
+
+ /* read and parse the file or data base */
+ ret = (*pkcs11db->seq)(pkcs11db, &key, &data, R_FIRST);
+ if (ret) goto done;
+
+ do {
+ /* allocate space for modules */
+ newmod = SECMOD_NewModuleListElement();
+ if (newmod == NULL) break;
+ newmod->module = secmod_DecodeData(&data);
+ if (newmod->module == NULL) {
+ SECMOD_DestroyModuleListElement(newmod);
+ break;
+ }
+ newmod->next = mod;
+ mod = newmod;
+ } while ( (*pkcs11db->seq)(pkcs11db, &key, &data, R_NEXT) == 0);
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return mod;
+}
+
+/*
+ * Delete a module from the Data Base
+ */
+SECStatus
+SECMOD_DeletePermDB(SECMODModule * module) {
+ DBT key;
+ SECStatus rv = SECFailure;
+ DB *pkcs11db = NULL;
+ int ret;
+
+ /* make sure we have a db handle */
+ pkcs11db = secmod_OpenDB(PR_FALSE);
+ if (pkcs11db == NULL) {
+ return SECFailure;
+ }
+
+ rv = secmod_MakeKey(&key,module);
+ if (rv != SECSuccess) goto done;
+ rv = SECFailure;
+ ret = (*pkcs11db->del)(pkcs11db, &key, 0);
+ secmod_FreeKey(&key);
+ if (ret != 0) goto done;
+
+
+ ret = (*pkcs11db->sync)(pkcs11db, 0);
+ if (ret == 0) rv = SECSuccess;
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return rv;
+}
+
+/*
+ * Add a module to the Data base
+ */
+SECStatus
+SECMOD_AddPermDB(SECMODModule *module) {
+ DBT key,data;
+ SECStatus rv = SECFailure;
+ DB *pkcs11db = NULL;
+ int ret;
+
+ /* make sure we have a db handle */
+ pkcs11db = secmod_OpenDB(PR_FALSE);
+ if (pkcs11db == NULL) {
+ return SECFailure;
+ }
+
+ rv = secmod_MakeKey(&key,module);
+ if (rv != SECSuccess) goto done;
+ rv = secmod_EncodeData(&data,module);
+ if (rv != SECSuccess) {
+ secmod_FreeKey(&key);
+ goto done;
+ }
+ rv = SECFailure;
+ ret = (*pkcs11db->put)(pkcs11db, &key, &data, 0);
+ secmod_FreeKey(&key);
+ secmod_FreeData(&data);
+ if (ret != 0) goto done;
+
+ ret = (*pkcs11db->sync)(pkcs11db, 0);
+ if (ret == 0) rv = SECSuccess;
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return rv;
+}
diff --git a/security/nss/lib/pk11wrap/pk11err.c b/security/nss/lib/pk11wrap/pk11err.c
new file mode 100644
index 000000000..b6846af84
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11err.c
@@ -0,0 +1,147 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * this file maps PKCS11 Errors into SECErrors
+ * This is an information reducing process, since most errors are reflected
+ * back to the user (the user doesn't care about invalid flags, or active
+ * operations). If any of these errors need more detail in the upper layers
+ * which call PK11 library functions, we can add more SEC_ERROR_XXX functions
+ * and change there mappings here.
+ */
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+#ifdef PK11_ERROR_USE_ARRAY
+
+/*
+ * build a static array of entries...
+ */
+static struct {
+ CK_RV pk11_error;
+ int sec_error;
+} pk11_error_map = {
+#define MAPERROR(x,y) {x, y},
+
+#else
+
+/* the default is to use a big switch statement */
+int
+PK11_MapError(CK_RV rv) {
+
+ switch (rv) {
+#define MAPERROR(x,y) case x: return y;
+
+#endif
+
+/* the guts mapping */
+ MAPERROR(CKR_OK, 0)
+ MAPERROR(CKR_CANCEL, SEC_ERROR_IO)
+ MAPERROR(CKR_HOST_MEMORY, SEC_ERROR_NO_MEMORY)
+ MAPERROR(CKR_SLOT_ID_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_ATTRIBUTE_READ_ONLY, SEC_ERROR_READ_ONLY)
+ MAPERROR(CKR_ATTRIBUTE_SENSITIVE, SEC_ERROR_IO) /* XX SENSITIVE */
+ MAPERROR(CKR_ATTRIBUTE_TYPE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_ATTRIBUTE_VALUE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_DATA_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_DATA_LEN_RANGE, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_DEVICE_ERROR, SEC_ERROR_IO)
+ MAPERROR(CKR_DEVICE_MEMORY, SEC_ERROR_NO_MEMORY)
+ MAPERROR(CKR_DEVICE_REMOVED, SEC_ERROR_NO_TOKEN)
+ MAPERROR(CKR_ENCRYPTED_DATA_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_ENCRYPTED_DATA_LEN_RANGE, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_FUNCTION_CANCELED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_FUNCTION_NOT_PARALLEL, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_MECHANISM_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_MECHANISM_PARAM_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_OBJECT_HANDLE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_OPERATION_ACTIVE, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_OPERATION_NOT_INITIALIZED,SEC_ERROR_LIBRARY_FAILURE )
+ MAPERROR(CKR_PIN_INCORRECT, SEC_ERROR_BAD_PASSWORD)
+ MAPERROR(CKR_PIN_INVALID, SEC_ERROR_BAD_PASSWORD)
+ MAPERROR(CKR_PIN_LEN_RANGE, SEC_ERROR_BAD_PASSWORD)
+ MAPERROR(CKR_SESSION_CLOSED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_SESSION_COUNT, SEC_ERROR_NO_MEMORY) /* XXXX? */
+ MAPERROR(CKR_SESSION_HANDLE_INVALID, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_SESSION_PARALLEL_NOT_SUPPORTED, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_SESSION_READ_ONLY, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_SIGNATURE_INVALID, SEC_ERROR_BAD_SIGNATURE)
+ MAPERROR(CKR_SIGNATURE_LEN_RANGE, SEC_ERROR_BAD_SIGNATURE)
+ MAPERROR(CKR_TEMPLATE_INCOMPLETE, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_TEMPLATE_INCONSISTENT, SEC_ERROR_BAD_DATA)
+ MAPERROR(CKR_TOKEN_NOT_PRESENT, SEC_ERROR_NO_TOKEN)
+ MAPERROR(CKR_TOKEN_NOT_RECOGNIZED, SEC_ERROR_IO)
+ MAPERROR(CKR_TOKEN_WRITE_PROTECTED, SEC_ERROR_READ_ONLY)
+ MAPERROR(CKR_UNWRAPPING_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_UNWRAPPING_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_USER_ALREADY_LOGGED_IN, 0)
+ MAPERROR(CKR_USER_NOT_LOGGED_IN, SEC_ERROR_LIBRARY_FAILURE) /* XXXX */
+ MAPERROR(CKR_USER_PIN_NOT_INITIALIZED, SEC_ERROR_NO_TOKEN)
+ MAPERROR(CKR_USER_TYPE_INVALID, SEC_ERROR_LIBRARY_FAILURE)
+ MAPERROR(CKR_WRAPPED_KEY_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPED_KEY_LEN_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPING_KEY_HANDLE_INVALID, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPING_KEY_SIZE_RANGE, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_WRAPPING_KEY_TYPE_INCONSISTENT, SEC_ERROR_INVALID_KEY)
+ MAPERROR(CKR_VENDOR_DEFINED, SEC_ERROR_LIBRARY_FAILURE)
+
+
+#ifdef PK11_ERROR_USE_ARRAY
+
+int
+PK11_MapError(CK_RV rv) {
+ int size = sizeof(pk11_error_map)/sizeof(pk11_error_map[0]);
+
+ for (i=0; i < size; i++) {
+ if (pk11_error_map[i].pk11_error == rv) {
+ return pk11_error_map[i].sec_error;
+ }
+ }
+ return SEC_ERROR_IO;
+ }
+
+
+#else
+
+ default:
+ break;
+ }
+ return SEC_ERROR_IO;
+}
+
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11func.h b/security/nss/lib/pk11wrap/pk11func.h
new file mode 100644
index 000000000..a6a6b5a0f
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11func.h
@@ -0,0 +1,440 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ *
+ * PKCS #11 Wrapper functions which handles authenticating to the card's
+ * choosing the best cards, etc.
+ */
+#ifndef _PK11FUNC_H_
+#define _PK11FUNC_H_
+#include "plarena.h"
+#include "mcom_db.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "secdert.h"
+#include "keyt.h"
+#include "certt.h"
+#include "pkcs11t.h"
+#include "secmodt.h"
+#include "seccomon.h"
+#include "pkcs7t.h"
+
+SEC_BEGIN_PROTOS
+
+/************************************************************
+ * Generic Slot Lists Management
+ ************************************************************/
+PK11SlotList * PK11_NewSlotList(void);
+void PK11_FreeSlotList(PK11SlotList *list);
+SECStatus PK11_AddSlotToList(PK11SlotList *list,PK11SlotInfo *slot);
+SECStatus PK11_DeleteSlotFromList(PK11SlotList *list,PK11SlotListElement *le);
+PK11SlotListElement * PK11_GetFirstSafe(PK11SlotList *list);
+PK11SlotListElement *PK11_GetNextSafe(PK11SlotList *list,
+ PK11SlotListElement *le, PRBool restart);
+PK11SlotListElement *PK11_FindSlotElement(PK11SlotList *list,
+ PK11SlotInfo *slot);
+
+/************************************************************
+ * Generic Slot Management
+ ************************************************************/
+PK11SlotInfo *PK11_ReferenceSlot(PK11SlotInfo *slot);
+PK11SlotInfo *PK11_FindSlotByID(SECMODModuleID modID,CK_SLOT_ID slotID);
+void PK11_FreeSlot(PK11SlotInfo *slot);
+SECStatus PK11_DestroyObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object);
+SECStatus PK11_DestroyTokenObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object);
+CK_OBJECT_HANDLE PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject);
+SECStatus PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PRArenaPool *arena, SECItem *result);
+CK_ULONG PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type);
+PK11SlotInfo *PK11_GetInternalKeySlot(void);
+PK11SlotInfo *PK11_GetInternalSlot(void);
+char * PK11_MakeString(PRArenaPool *arena,char *space,char *staticSring,
+ int stringLen);
+int PK11_MapError(CK_RV error);
+CK_SESSION_HANDLE PK11_GetRWSession(PK11SlotInfo *slot);
+void PK11_RestoreROSession(PK11SlotInfo *slot,CK_SESSION_HANDLE rwsession);
+PRBool PK11_RWSessionHasLock(PK11SlotInfo *slot,
+ CK_SESSION_HANDLE session_handle);
+PK11SlotInfo *PK11_NewSlotInfo(void);
+SECStatus PK11_Logout(PK11SlotInfo *slot);
+void PK11_LogoutAll(void);
+void PK11_EnterSlotMonitor(PK11SlotInfo *);
+void PK11_ExitSlotMonitor(PK11SlotInfo *);
+
+
+/************************************************************
+ * Slot Password Management
+ ************************************************************/
+void PK11_SetSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout);
+void PK11_GetSlotPWValues(PK11SlotInfo *slot,int *askpw, int *timeout);
+SECStatus PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw);
+SECStatus PK11_CheckUserPassword(PK11SlotInfo *slot,char *pw);
+SECStatus PK11_DoPassword(PK11SlotInfo *slot, PRBool loadCerts, void *wincx);
+PRBool PK11_IsLoggedIn(PK11SlotInfo *slot, void *wincx);
+SECStatus PK11_VerifyPW(PK11SlotInfo *slot,char *pw);
+SECStatus PK11_InitPin(PK11SlotInfo *slot,char *ssopw, char *pk11_userpwd);
+SECStatus PK11_ChangePW(PK11SlotInfo *slot,char *oldpw, char *newpw);
+void PK11_HandlePasswordCheck(PK11SlotInfo *slot,void *wincx);
+void PK11_SetPasswordFunc(PK11PasswordFunc func);
+void PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func);
+void PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func);
+int PK11_GetMinimumPwdLength(PK11SlotInfo *slot);
+SECStatus PK11_ResetToken(PK11SlotInfo *slot, char *sso_pwd);
+
+/************************************************************
+ * Manage the built-In Slot Lists
+ ************************************************************/
+SECStatus PK11_InitSlotLists(void);
+PK11SlotList *PK11_GetSlotList(CK_MECHANISM_TYPE type);
+void PK11_LoadSlotList(PK11SlotInfo *slot, PK11PreSlotInfo *psi, int count);
+void PK11_ClearSlotList(PK11SlotInfo *slot);
+
+
+/******************************************************************
+ * Slot initialization
+ ******************************************************************/
+PRBool PK11_VerifyMechanism(PK11SlotInfo *slot,PK11SlotInfo *intern,
+ CK_MECHANISM_TYPE mech, SECItem *data, SECItem *iv);
+PRBool PK11_VerifySlotMechanisms(PK11SlotInfo *slot);
+SECStatus pk11_CheckVerifyTest(PK11SlotInfo *slot);
+SECStatus PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts);
+SECStatus PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx);
+void PK11_InitSlot(SECMODModule *mod,CK_SLOT_ID slotID,PK11SlotInfo *slot);
+
+
+/******************************************************************
+ * Slot info functions
+ ******************************************************************/
+PK11SlotInfo *PK11_FindSlotByName(char *name);
+PK11SlotInfo *PK11_FindSlotBySerial(char *serial);
+PRBool PK11_IsReadOnly(PK11SlotInfo *slot);
+PRBool PK11_IsInternal(PK11SlotInfo *slot);
+char * PK11_GetTokenName(PK11SlotInfo *slot);
+char * PK11_GetSlotName(PK11SlotInfo *slot);
+PRBool PK11_NeedLogin(PK11SlotInfo *slot);
+PRBool PK11_IsFriendly(PK11SlotInfo *slot);
+PRBool PK11_IsHW(PK11SlotInfo *slot);
+PRBool PK11_NeedUserInit(PK11SlotInfo *slot);
+int PK11_GetSlotSeries(PK11SlotInfo *slot);
+int PK11_GetCurrentWrapIndex(PK11SlotInfo *slot);
+unsigned long PK11_GetDefaultFlags(PK11SlotInfo *slot);
+CK_SLOT_ID PK11_GetSlotID(PK11SlotInfo *slot);
+SECMODModuleID PK11_GetModuleID(PK11SlotInfo *slot);
+SECStatus PK11_GetSlotInfo(PK11SlotInfo *slot, CK_SLOT_INFO *info);
+SECStatus PK11_GetTokenInfo(PK11SlotInfo *slot, CK_TOKEN_INFO *info);
+PRBool PK11_IsDisabled(PK11SlotInfo *slot);
+PK11DisableReasons PK11_GetDisabledReason(PK11SlotInfo *slot);
+/* Prevents the slot from being used, and set disable reason to user-disable */
+/* NOTE: Mechanisms that were ON continue to stay ON */
+/* Therefore, when the slot is enabled, it will remember */
+/* what mechanisms needs to be turned on */
+PRBool PK11_UserDisableSlot(PK11SlotInfo *slot);
+/* Allow all mechanisms that are ON before UserDisableSlot() */
+/* was called to be available again */
+PRBool PK11_UserEnableSlot(PK11SlotInfo *slot);
+
+PRBool PK11_NeedPWInit(void);
+PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot);
+PRBool PK11_TokenExists(CK_MECHANISM_TYPE);
+SECStatus PK11_GetModInfo(SECMODModule *mod, CK_INFO *info);
+PRBool PK11_IsFIPS(void);
+SECMODModule *PK11_GetModule(PK11SlotInfo *slot);
+
+/*********************************************************************
+ * Slot mapping utility functions.
+ *********************************************************************/
+PRBool PK11_IsPresent(PK11SlotInfo *slot);
+PRBool PK11_DoesMechanism(PK11SlotInfo *slot, CK_MECHANISM_TYPE type);
+PK11SlotList * PK11_GetAllTokens(CK_MECHANISM_TYPE type,PRBool needRW,
+ PRBool loadCerts, void *wincx);
+PK11SlotList * PK11_GetPrivateKeyTokens(CK_MECHANISM_TYPE type,
+ PRBool needRW,void *wincx);
+PK11SlotInfo *PK11_GetBestSlotMultiple(CK_MECHANISM_TYPE *type, int count,
+ void *wincx);
+PK11SlotInfo *PK11_GetBestSlot(CK_MECHANISM_TYPE type, void *wincx);
+CK_MECHANISM_TYPE PK11_GetBestWrapMechanism(PK11SlotInfo *slot);
+int PK11_GetBestKeyLength(PK11SlotInfo *slot, CK_MECHANISM_TYPE type);
+
+/*********************************************************************
+ * Mechanism Mapping functions
+ *********************************************************************/
+void PK11_AddMechanismEntry(CK_MECHANISM_TYPE type, CK_KEY_TYPE key,
+ CK_MECHANISM_TYPE keygen, int ivLen, int blocksize);
+CK_MECHANISM_TYPE PK11_GetKeyType(CK_MECHANISM_TYPE type,unsigned long len);
+CK_MECHANISM_TYPE PK11_GetKeyGen(CK_MECHANISM_TYPE type);
+int PK11_GetBlockSize(CK_MECHANISM_TYPE type,SECItem *params);
+int PK11_GetIVLength(CK_MECHANISM_TYPE type);
+SECItem *PK11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv);
+unsigned char *PK11_IVFromParam(CK_MECHANISM_TYPE type,SECItem *param,int *len);
+SECItem * PK11_BlockData(SECItem *data,unsigned long size);
+
+/* PKCS #11 to DER mapping functions */
+SECItem *PK11_ParamFromAlgid(SECAlgorithmID *algid);
+SECItem *PK11_GenerateNewParam(CK_MECHANISM_TYPE, PK11SymKey *);
+CK_MECHANISM_TYPE PK11_AlgtagToMechanism(SECOidTag algTag);
+SECOidTag PK11_MechanismToAlgtag(CK_MECHANISM_TYPE type);
+SECOidTag PK11_FortezzaMapSig(SECOidTag algTag);
+SECStatus PK11_ParamToAlgid(SECOidTag algtag, SECItem *param,
+ PRArenaPool *arena, SECAlgorithmID *algid);
+SECStatus PK11_SeedRandom(PK11SlotInfo *,unsigned char *data,int len);
+SECStatus PK11_RandomUpdate(void *data, size_t bytes);
+SECStatus PK11_GenerateRandom(unsigned char *data,int len);
+CK_RV PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism,
+ CK_MECHANISM_PTR pCryptoMechanism,
+ SECItem *pbe_pwd, PRBool bad3DES);
+CK_MECHANISM_TYPE PK11_GetPadMechanism(CK_MECHANISM_TYPE);
+
+/**********************************************************************
+ * Symetric, Public, and Private Keys
+ **********************************************************************/
+PK11SymKey *PK11_CreateSymKey(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, void *wincx);
+void PK11_FreeSymKey(PK11SymKey *key);
+PK11SymKey *PK11_ReferenceSymKey(PK11SymKey *symKey);
+PK11SymKey *PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,void *wincx);
+PK11SymKey *PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent,
+ PK11Origin origin, CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID,
+ PRBool owner, void *wincx);
+PK11SymKey *PK11_GetWrapKey(PK11SlotInfo *slot, int wrap,
+ CK_MECHANISM_TYPE type,int series, void *wincx);
+void PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey);
+CK_MECHANISM_TYPE PK11_GetMechanism(PK11SymKey *symKey);
+CK_OBJECT_HANDLE PK11_ImportPublicKey(PK11SlotInfo *slot,
+ SECKEYPublicKey *pubKey, PRBool isToken);
+PK11SymKey *PK11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ SECItem *param, int keySize,void *wincx);
+SECStatus PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
+ PK11SymKey *symKey, SECItem *wrappedKey);
+SECStatus PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *params,
+ PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey);
+PK11SymKey *PK11_Derive(PK11SymKey *baseKey, CK_MECHANISM_TYPE mechanism,
+ SECItem *param, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_DeriveWithFlags( PK11SymKey *baseKey,
+ CK_MECHANISM_TYPE derive, SECItem *param, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize, CK_FLAGS flags);
+PK11SymKey *PK11_PubDerive( SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,void *wincx) ;
+PK11SymKey *PK11_UnwrapSymKey(PK11SymKey *key,
+ CK_MECHANISM_TYPE wraptype, SECItem *param, SECItem *wrapppedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey,
+ CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize,
+ CK_FLAGS flags);
+PK11SymKey *PK11_PubUnwrapSymKey(SECKEYPrivateKey *key, SECItem *wrapppedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize);
+PK11SymKey *PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ SECItem *keyID, void *wincx);
+SECStatus PK11_DeleteTokenPrivateKey(SECKEYPrivateKey *privKey);
+SECStatus PK11_DeleteTokenCertAndKey(CERTCertificate *cert,void *wincx);
+
+/* size to hold key in bytes */
+unsigned int PK11_GetKeyLength(PK11SymKey *key);
+/* size of actual secret parts of key in bits */
+/* algid is because RC4 strength is determined by the effective bits as well
+ * as the key bits */
+unsigned int PK11_GetKeyStrength(PK11SymKey *key,SECAlgorithmID *algid);
+SECStatus PK11_ExtractKeyValue(PK11SymKey *symKey);
+SECItem * PK11_GetKeyData(PK11SymKey *symKey);
+PK11SlotInfo * PK11_GetSlotFromKey(PK11SymKey *symKey);
+void *PK11_GetWindow(PK11SymKey *symKey);
+SECKEYPrivateKey *PK11_GenerateKeyPair(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, void *param, SECKEYPublicKey **pubk,
+ PRBool isPerm, PRBool isSensitive, void *wincx);
+SECKEYPrivateKey *PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType,
+ PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx);
+SECKEYPrivateKey * PK11_FindPrivateKeyFromCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx);
+SECKEYPrivateKey * PK11_FindKeyByAnyCert(CERTCertificate *cert, void *wincx);
+SECKEYPrivateKey * PK11_FindKeyByKeyID(PK11SlotInfo *slot, SECItem *keyID,
+ void *wincx);
+CK_OBJECT_HANDLE PK11_FindObjectForCert(CERTCertificate *cert,
+ void *wincx, PK11SlotInfo **pSlot);
+int PK11_GetPrivateModulusLen(SECKEYPrivateKey *key);
+SECStatus PK11_PubDecryptRaw(SECKEYPrivateKey *key, unsigned char *data,
+ unsigned *outLen, unsigned int maxLen, unsigned char *enc, unsigned encLen);
+/* The encrypt version of the above function */
+SECStatus PK11_PubEncryptRaw(SECKEYPublicKey *key, unsigned char *enc,
+ unsigned char *data, unsigned dataLen, void *wincx);
+SECStatus PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot,
+ SECKEYPrivateKeyInfo *pki, SECItem *nickname,
+ SECItem *publicValue, PRBool isPerm, PRBool isPrivate,
+ unsigned int usage, void *wincx);
+SECStatus PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
+ SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, KeyType type,
+ unsigned int usage, void *wincx);
+SECKEYPrivateKeyInfo *PK11_ExportPrivateKeyInfo(
+ CERTCertificate *cert, void *wincx);
+SECKEYEncryptedPrivateKeyInfo *PK11_ExportEncryptedPrivateKeyInfo(
+ PK11SlotInfo *slot, SECOidTag algTag, SECItem *pwitem,
+ CERTCertificate *cert, int iteration, void *wincx);
+SECKEYPrivateKey *PK11_FindKeyByDERCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx);
+SECKEYPublicKey *PK11_MakeKEAPubKey(unsigned char *data, int length);
+SECStatus PK11_DigestKey(PK11Context *context, PK11SymKey *key);
+PRBool PK11_VerifyKeyOK(PK11SymKey *key);
+SECKEYPrivateKey *PK11_UnwrapPrivKey(PK11SlotInfo *slot,
+ PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey, SECItem *label,
+ SECItem *publicValue, PRBool token, PRBool sensitive,
+ CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage, int usageCount,
+ void *wincx);
+SECStatus PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
+ SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey, void *wincx);
+PK11SymKey * pk11_CopyToSlot(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey);
+SECItem *PK11_GetKeyIDFromCert(CERTCertificate *cert, void *wincx);
+SECItem * PK11_GetKeyIDFromPrivateKey(SECKEYPrivateKey *key, void *wincx);
+
+/**********************************************************************
+ * Certs
+ **********************************************************************/
+SECItem *PK11_MakeIDFromPubKey(SECItem *pubKeyData);
+CERTCertificate *PK11_GetCertFromPrivateKey(SECKEYPrivateKey *privKey);
+SECStatus PK11_TraverseSlotCerts(
+ SECStatus(* callback)(CERTCertificate*,SECItem *,void *),
+ void *arg, void *wincx);
+CERTCertificate * PK11_FindCertFromNickname(char *nickname, void *wincx);
+CERTCertList * PK11_FindCertsFromNickname(char *nickname, void *wincx);
+SECKEYPrivateKey * PK11_FindPrivateKeyFromNickname(char *nickname, void *wincx);
+PK11SlotInfo *PK11_ImportCertForKey(CERTCertificate *cert, char *nickname,
+ void *wincx);
+CK_OBJECT_HANDLE * PK11_FindObjectsFromNickname(char *nickname,
+ PK11SlotInfo **slotptr, CK_OBJECT_CLASS objclass, int *returnCount,
+ void *wincx);
+PK11SlotInfo *PK11_KeyForCertExists(CERTCertificate *cert,
+ CK_OBJECT_HANDLE *keyPtr, void *wincx);
+CK_OBJECT_HANDLE PK11_MatchItem(PK11SlotInfo *slot,CK_OBJECT_HANDLE peer,
+ CK_OBJECT_CLASS o_class);
+CERTCertificate * PK11_FindCertByIssuerAndSN(PK11SlotInfo **slot,
+ CERTIssuerAndSN *sn, void *wincx);
+CERTCertificate * PK11_FindCertAndKeyByRecipientList(PK11SlotInfo **slot,
+ SEC_PKCS7RecipientInfo **array, SEC_PKCS7RecipientInfo **rip,
+ SECKEYPrivateKey**privKey, void *wincx);
+CK_BBOOL PK11_HasAttributeSet( PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type );
+CK_RV PK11_GetAttributes(PRArenaPool *arena,PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE obj,CK_ATTRIBUTE *attr, int count);
+int PK11_NumberCertsForCertSubject(CERTCertificate *cert);
+SECStatus PK11_TraverseCertsForSubject(CERTCertificate *cert,
+ SECStatus(*callback)(CERTCertificate *, void *), void *arg);
+SECStatus PK11_TraverseCertsForSubjectInSlot(CERTCertificate *cert,
+ PK11SlotInfo *slot, SECStatus(*callback)(CERTCertificate *, void *),
+ void *arg);
+CERTCertificate *PK11_FindCertFromDERCert(PK11SlotInfo *slot,
+ CERTCertificate *cert, void *wincx);
+CERTCertificate *PK11_FindCertFromDERSubjectAndNickname(
+ PK11SlotInfo *slot,
+ CERTCertificate *cert, char *nickname,
+ void *wincx);
+SECStatus PK11_ImportCertForKeyToSlot(PK11SlotInfo *slot, CERTCertificate *cert,
+ char *nickname, PRBool addUsage,
+ void *wincx);
+CERTCertificate *PK11_FindBestKEAMatch(CERTCertificate *serverCert,void *wincx);
+SECStatus PK11_GetKEAMatchedCerts(PK11SlotInfo *slot1,
+ PK11SlotInfo *slot2, CERTCertificate **cert1, CERTCertificate **cert2);
+PRBool PK11_FortezzaHasKEA(CERTCertificate *cert);
+CK_OBJECT_HANDLE PK11_FindCertInSlot(PK11SlotInfo *slot, CERTCertificate *cert,
+ void *wincx);
+SECStatus PK11_TraverseCertsForNicknameInSlot(SECItem *nickname,
+ PK11SlotInfo *slot, SECStatus(*callback)(CERTCertificate *, void *),
+ void *arg);
+SECStatus PK11_TraverseCertsInSlot(PK11SlotInfo *slot,
+ SECStatus(* callback)(CERTCertificate*, void *), void *arg);
+
+
+/**********************************************************************
+ * Sign/Verify
+ **********************************************************************/
+int PK11_SignatureLen(SECKEYPrivateKey *key);
+PK11SlotInfo * PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key);
+SECStatus PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, SECItem *hash);
+SECStatus PK11_VerifyRecover(SECKEYPublicKey *key, SECItem *sig,
+ SECItem *dsig, void * wincx);
+SECStatus PK11_Verify(SECKEYPublicKey *key, SECItem *sig,
+ SECItem *hash, void *wincx);
+
+
+
+/**********************************************************************
+ * Crypto Contexts
+ **********************************************************************/
+void PK11_DestroyContext(PK11Context *context, PRBool freeit);
+PK11Context * PK11_CreateContextByRawKey(PK11SlotInfo *slot,
+ CK_MECHANISM_TYPE type, PK11Origin origin, CK_ATTRIBUTE_TYPE operation,
+ SECItem *key, SECItem *param, void *wincx);
+PK11Context *PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey, SECItem *param);
+PK11Context *PK11_CreateDigestContext(SECOidTag hashAlg);
+PK11Context *PK11_CloneContext(PK11Context *old);
+SECStatus PK11_DigestBegin(PK11Context *cx);
+SECStatus PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in,
+ int32 len);
+SECStatus PK11_DigestOp(PK11Context *context, const unsigned char *in,
+ unsigned len);
+SECStatus PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen,
+ int maxout, unsigned char *in, int inlen);
+SECStatus PK11_Finalize(PK11Context *context);
+SECStatus PK11_DigestFinal(PK11Context *context, unsigned char *data,
+ unsigned int *outLen, unsigned int length);
+PRBool PK11_HashOK(SECOidTag hashAlg);
+SECStatus PK11_SaveContext(PK11Context *cx,unsigned char *save,
+ int *len, int saveLength);
+SECStatus PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len);
+SECStatus PK11_GenerateFortezzaIV(PK11SymKey *symKey,unsigned char *iv,int len);
+SECStatus PK11_ReadSlotCerts(PK11SlotInfo *slot);
+void PK11_FreeSlotCerts(PK11SlotInfo *slot);
+void PK11_SetFortezzaHack(PK11SymKey *symKey) ;
+
+
+/**********************************************************************
+ * PBE functions
+ **********************************************************************/
+SECAlgorithmID *
+PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt);
+PK11SymKey *
+PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx);
+SECItem *
+PK11_GetPBEIV(SECAlgorithmID *algid, SECItem *pwitem);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/pk11kea.c b/security/nss/lib/pk11wrap/pk11kea.c
new file mode 100644
index 000000000..ef9fe2aef
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11kea.c
@@ -0,0 +1,224 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * This file implements the Symkey wrapper and the PKCS context
+ * Interfaces.
+ */
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "prlock.h"
+#include "secmodi.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "key.h"
+#include "secasn1.h"
+#include "sechash.h"
+#include "cert.h"
+#include "secerr.h"
+
+/*
+ * find an RSA public key on a card
+ */
+static CK_OBJECT_HANDLE
+pk11_FindRSAPubKey(PK11SlotInfo *slot)
+{
+ CK_KEY_TYPE key_type = CKK_RSA;
+ CK_OBJECT_CLASS class_type = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE theTemplate[2];
+ int template_count = sizeof(theTemplate)/sizeof(theTemplate[0]);
+ CK_ATTRIBUTE *attrs = theTemplate;
+
+ PK11_SETATTRS(attrs,CKA_CLASS,&class_type,sizeof(class_type)); attrs++;
+ PK11_SETATTRS(attrs,CKA_KEY_TYPE,&key_type,sizeof(key_type)); attrs++;
+ template_count = attrs - theTemplate;
+ PR_ASSERT(template_count <= sizeof(theTemplate)/sizeof(CK_ATTRIBUTE));
+
+ return pk11_FindObjectByTemplate(slot,theTemplate,template_count);
+}
+
+SECKEYPublicKey *PK11_ExtractPublicKey(PK11SlotInfo *slot, KeyType keyType,
+ CK_OBJECT_HANDLE id);
+
+PK11SymKey *
+pk11_KeyExchange(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey)
+{
+ PK11SymKey *newSymKey = NULL;
+ SECStatus rv;
+ /* performance improvement can go here --- use a generated key to as a
+ * per startup wrapping key. If it exists, use it, otherwise do a full
+ * key exchange. */
+
+ /* find a common Key Exchange algorithm */
+ /* RSA */
+ if (PK11_DoesMechanism(symKey->slot, CKM_RSA_PKCS) &&
+ PK11_DoesMechanism(slot,CKM_RSA_PKCS)) {
+ CK_OBJECT_HANDLE pubKeyHandle = CK_INVALID_KEY;
+ CK_OBJECT_HANDLE privKeyHandle = CK_INVALID_KEY;
+ SECKEYPublicKey *pubKey = NULL;
+ SECKEYPrivateKey *privKey = NULL;
+ SECItem wrapData;
+
+ wrapData.data = NULL;
+
+ /* find RSA Public Key on target */
+ pubKeyHandle = pk11_FindRSAPubKey(slot);
+ if (pubKeyHandle != CK_INVALID_KEY) {
+ privKeyHandle = PK11_MatchItem(slot,pubKeyHandle,CKO_PRIVATE_KEY);
+ }
+
+ /* if no key exits, generate a key pair */
+ if (privKeyHandle == CK_INVALID_KEY) {
+ unsigned int keyLength = PK11_GetKeyLength(symKey);
+ PK11RSAGenParams rsaParams;
+
+ rsaParams.keySizeInBits =
+ ((keyLength == 0) || (keyLength > 16)) ? 512 : 256;
+ rsaParams.pe = 0x10001;
+ privKey = PK11_GenerateKeyPair(slot,CKM_RSA_PKCS_KEY_PAIR_GEN,
+ &rsaParams, &pubKey,PR_FALSE,PR_TRUE,symKey->cx);
+ } else {
+ /* if key's exist, build SECKEY data structures for them */
+ privKey = PK11_MakePrivKey(slot,nullKey, PR_TRUE, privKeyHandle,
+ symKey->cx);
+ if (privKey != NULL) {
+ pubKey = PK11_ExtractPublicKey(slot, rsaKey, pubKeyHandle);
+ if (pubKey && pubKey->pkcs11Slot) {
+ PK11_FreeSlot(pubKey->pkcs11Slot);
+ pubKey->pkcs11Slot = NULL;
+ pubKey->pkcs11ID = CK_INVALID_KEY;
+ }
+ }
+ }
+ if (privKey == NULL) goto rsa_failed;
+ if (pubKey == NULL) goto rsa_failed;
+
+ wrapData.len = SECKEY_PublicKeyStrength(pubKey);
+ if (!wrapData.len) goto rsa_failed;
+ wrapData.data = PORT_Alloc(wrapData.len);
+ if (wrapData.data == NULL) goto rsa_failed;
+
+ /* now wrap the keys in and out */
+ rv = PK11_PubWrapSymKey(CKM_RSA_PKCS, pubKey, symKey, &wrapData);
+ if (rv == SECSuccess) {
+ newSymKey = PK11_PubUnwrapSymKey(privKey,&wrapData,type,operation,
+ symKey->size);
+ }
+rsa_failed:
+ if (wrapData.data != NULL) PORT_Free(wrapData.data);
+ if (privKey != NULL) SECKEY_DestroyPrivateKey(privKey);
+ if (pubKey != NULL) SECKEY_DestroyPublicKey(pubKey);
+
+ return newSymKey;
+ }
+ /* KEA */
+ if (PK11_DoesMechanism(symKey->slot, CKM_KEA_KEY_DERIVE) &&
+ PK11_DoesMechanism(slot,CKM_KEA_KEY_DERIVE)) {
+ CERTCertificate *certSource = NULL;
+ CERTCertificate *certTarget = NULL;
+ SECKEYPublicKey *pubKeySource = NULL;
+ SECKEYPublicKey *pubKeyTarget = NULL;
+ SECKEYPrivateKey *privKeySource = NULL;
+ SECKEYPrivateKey *privKeyTarget = NULL;
+ PK11SymKey *tekSource = NULL;
+ PK11SymKey *tekTarget = NULL;
+ SECItem Ra,wrap;
+
+ /* can only exchange skipjack keys */
+ if (type != CKM_SKIPJACK_CBC64) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ goto kea_failed;
+ }
+
+ /* find a pair of certs we can use */
+ rv = PK11_GetKEAMatchedCerts(symKey->slot,slot,&certSource,&certTarget);
+ if (rv != SECSuccess) goto kea_failed;
+
+ /* get all the key pairs */
+ pubKeyTarget = CERT_ExtractPublicKey(certSource);
+ pubKeySource = CERT_ExtractPublicKey(certTarget);
+ privKeySource =
+ PK11_FindKeyByDERCert(symKey->slot,certSource,symKey->cx);
+ privKeyTarget =
+ PK11_FindKeyByDERCert(slot,certTarget,symKey->cx);
+
+ if ((pubKeySource == NULL) || (pubKeyTarget == NULL) ||
+ (privKeySource == NULL) || (privKeyTarget == NULL)) goto kea_failed;
+
+ /* generate the wrapping TEK's */
+ Ra.data = (unsigned char*)PORT_Alloc(128 /* FORTEZZA RA MAGIC */);
+ Ra.len = 128;
+ if (Ra.data == NULL) goto kea_failed;
+
+ tekSource = PK11_PubDerive(privKeySource,pubKeyTarget,PR_TRUE,&Ra,NULL,
+ CKM_SKIPJACK_WRAP, CKM_KEA_KEY_DERIVE,CKA_WRAP,0,symKey->cx);
+ tekTarget = PK11_PubDerive(privKeyTarget,pubKeySource,PR_FALSE,&Ra,NULL,
+ CKM_SKIPJACK_WRAP, CKM_KEA_KEY_DERIVE,CKA_WRAP,0,symKey->cx);
+ PORT_Free(Ra.data);
+
+ if ((tekSource == NULL) || (tekTarget == NULL)) { goto kea_failed; }
+
+ /* wrap the key out of Source into target */
+ wrap.data = (unsigned char*)PORT_Alloc(12); /* MAGIC SKIPJACK LEN */
+ wrap.len = 12;
+
+ /* paranoia to prevent infinite recursion on bugs */
+ PORT_Assert(tekSource->slot == symKey->slot);
+ if (tekSource->slot != symKey->slot) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ goto kea_failed;
+ }
+
+ rv = PK11_WrapSymKey(CKM_SKIPJACK_WRAP,NULL,tekSource,symKey,&wrap);
+ if (rv == SECSuccess) {
+ newSymKey = PK11_UnwrapSymKey(tekTarget, CKM_SKIPJACK_WRAP, NULL,
+ &wrap, type, operation, symKey->size);
+ }
+ PORT_Free(wrap.data);
+kea_failed:
+ if (certSource == NULL) CERT_DestroyCertificate(certSource);
+ if (certTarget == NULL) CERT_DestroyCertificate(certTarget);
+ if (pubKeySource == NULL) SECKEY_DestroyPublicKey(pubKeySource);
+ if (pubKeyTarget == NULL) SECKEY_DestroyPublicKey(pubKeyTarget);
+ if (privKeySource == NULL) SECKEY_DestroyPrivateKey(privKeySource);
+ if (privKeyTarget == NULL) SECKEY_DestroyPrivateKey(privKeyTarget);
+ if (tekSource == NULL) PK11_FreeSymKey(tekSource);
+ if (tekTarget == NULL) PK11_FreeSymKey(tekTarget);
+ return newSymKey;
+ }
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+}
+
diff --git a/security/nss/lib/pk11wrap/pk11list.c b/security/nss/lib/pk11wrap/pk11list.c
new file mode 100644
index 000000000..35a68d8a2
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11list.c
@@ -0,0 +1,165 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * Locking and queue management primatives
+ *
+ */
+
+#include "seccomon.h"
+#include "prlock.h"
+#include "prmon.h"
+#include "secmod.h"
+#include "secmodi.h"
+#include "prlong.h"
+
+#define ISREADING 1
+#define ISWRITING 2
+#define WANTWRITE 4
+#define ISLOCKED 3
+
+/*
+ * create a new lock for a Module List
+ */
+SECMODListLock *SECMOD_NewListLock() {
+ SECMODListLock *modLock;
+
+ modLock = (SECMODListLock*)PORT_Alloc(sizeof(SECMODListLock));
+#ifdef PKCS11_USE_THREADS
+ modLock->mutex = NULL;
+ modLock->monitor = PR_NewMonitor();
+#else
+ modLock->mutex = NULL;
+ modLock->monitor = NULL;
+#endif
+ modLock->state = 0;
+ modLock->count = 0;
+ return modLock;
+}
+
+/*
+ * destroy the lock
+ */
+void SECMOD_DestroyListLock(SECMODListLock *lock) {
+ PK11_USE_THREADS(PR_DestroyMonitor(lock->monitor);)
+ PORT_Free(lock);
+}
+
+
+/*
+ * Lock the List for Read: NOTE: this assumes the reading isn't so common
+ * the writing will be starved.
+ */
+void SECMOD_GetReadLock(SECMODListLock *modLock) {
+#ifdef PKCS11_USE_THREADS
+ PR_EnterMonitor(modLock->monitor);
+ while (modLock->state & ISWRITING) {
+ PR_Wait(modLock->monitor,PR_INTERVAL_NO_TIMEOUT); /* wait until woken up */
+ }
+ modLock->state |= ISREADING;
+ modLock->count++;
+ PR_ExitMonitor(modLock->monitor);
+#endif
+}
+
+/*
+ * Release the Read lock
+ */
+void SECMOD_ReleaseReadLock(SECMODListLock *modLock) {
+#ifdef PKCS11_USE_THREADS
+ PR_EnterMonitor(modLock->monitor);
+ modLock->count--;
+ if (modLock->count == 0) {
+ modLock->state &= ~ISREADING;
+ if (modLock->state & WANTWRITE) {
+ PR_Notify(modLock->monitor); /* only one writer at a time */
+ }
+ }
+ PR_ExitMonitor(modLock->monitor);
+#endif
+}
+
+
+/*
+ * lock the list for Write
+ */
+void SECMOD_GetWriteLock(SECMODListLock *modLock) {
+#ifdef PKCS11_USE_THREADS
+ PR_EnterMonitor(modLock->monitor);
+ while (modLock->state & ISLOCKED) {
+ modLock->state |= WANTWRITE;
+ PR_Wait(modLock->monitor,PR_INTERVAL_NO_TIMEOUT); /* wait until woken up */
+ }
+ modLock->state = ISWRITING;
+ PR_ExitMonitor(modLock->monitor);
+#endif
+}
+
+
+/*
+ * Release the Write Lock: NOTE, this code is pretty inefficient if you have
+ * lots of write collisions.
+ */
+void SECMOD_ReleaseWriteLock(SECMODListLock *modLock) {
+#ifdef PKCS11_USE_THREADS
+ PR_EnterMonitor(modLock->monitor);
+ modLock->state = 0;
+ PR_NotifyAll(modLock->monitor); /* enable all the readers */
+ PR_ExitMonitor(modLock->monitor);
+#endif
+}
+
+
+/*
+ * must Hold the Write lock
+ */
+void
+SECMOD_RemoveList(SECMODModuleList **parent, SECMODModuleList *child) {
+ *parent = child->next;
+ child->next = NULL;
+}
+
+/*
+ * if lock is not specified, it must already be held
+ */
+void
+SECMOD_AddList(SECMODModuleList *parent, SECMODModuleList *child,
+ SECMODListLock *lock) {
+ if (lock) { SECMOD_GetWriteLock(lock); }
+
+ child->next = parent->next;
+ parent->next = child;
+
+ if (lock) { SECMOD_ReleaseWriteLock(lock); }
+}
+
+
diff --git a/security/nss/lib/pk11wrap/pk11load.c b/security/nss/lib/pk11wrap/pk11load.c
new file mode 100644
index 000000000..65d898e6e
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11load.c
@@ -0,0 +1,239 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * The following handles the loading, unloading and management of
+ * various PCKS #11 modules
+ */
+#include "seccomon.h"
+#include "pkcs11.h"
+#include "secmod.h"
+#include "prlink.h"
+#include "pk11func.h"
+#include "secmodi.h"
+#include "prlock.h"
+
+extern void FC_GetFunctionList(void);
+extern void NSC_GetFunctionList(void);
+
+
+/* build the PKCS #11 2.01 lock files */
+CK_RV PR_CALLBACK secmodCreateMutext(CK_VOID_PTR_PTR pmutex) {
+ *pmutex = (CK_VOID_PTR) PR_NewLock();
+ if ( *pmutex ) return CKR_OK;
+ return CKR_HOST_MEMORY;
+}
+
+CK_RV PR_CALLBACK secmodDestroyMutext(CK_VOID_PTR mutext) {
+ PR_DestroyLock((PRLock *)mutext);
+ return CKR_OK;
+}
+
+CK_RV PR_CALLBACK secmodLockMutext(CK_VOID_PTR mutext) {
+ PR_Lock((PRLock *)mutext);
+ return CKR_OK;
+}
+
+CK_RV PR_CALLBACK secmodUnlockMutext(CK_VOID_PTR mutext) {
+ PR_Unlock((PRLock *)mutext);
+ return CKR_OK;
+}
+
+static SECMODModuleID nextModuleID = 1;
+static CK_C_INITIALIZE_ARGS secmodLockFunctions = {
+ secmodCreateMutext, secmodDestroyMutext, secmodLockMutext,
+ secmodUnlockMutext, CKF_LIBRARY_CANT_CREATE_OS_THREADS|
+ CKF_OS_LOCKING_OK
+ ,NULL
+};
+
+/*
+ * load a new module into our address space and initialize it.
+ */
+SECStatus
+SECMOD_LoadModule(SECMODModule *mod) {
+ PRLibrary *library = NULL;
+ CK_C_GetFunctionList entry;
+ char * full_name;
+ CK_INFO info;
+ CK_ULONG slotCount = 0;
+
+
+ if (mod->loaded) return SECSuccess;
+
+ /* intenal modules get loaded from their internal list */
+ if (mod->internal) {
+ /* internal, statically get the C_GetFunctionList function */
+ if (mod->isFIPS) {
+ entry = (CK_C_GetFunctionList) FC_GetFunctionList;
+ } else {
+ entry = (CK_C_GetFunctionList) NSC_GetFunctionList;
+ }
+ } else {
+ /* Not internal, load the DLL and look up C_GetFunctionList */
+ if (mod->dllName == NULL) {
+ return SECFailure;
+ }
+
+#ifdef notdef
+ /* look up the library name */
+ full_name = PR_GetLibraryName(PR_GetLibraryPath(),mod->dllName);
+ if (full_name == NULL) {
+ return SECFailure;
+ }
+#else
+ full_name = PORT_Strdup(mod->dllName);
+#endif
+
+ /* load the library. If this succeeds, then we have to remember to
+ * unload the library if anything goes wrong from here on out...
+ */
+ library = PR_LoadLibrary(full_name);
+ mod->library = (void *)library;
+ PORT_Free(full_name);
+ if (library == NULL) {
+ return SECFailure;
+ }
+
+ /*
+ * now we need to get the entry point to find the function pointers
+ */
+ entry = (CK_C_GetFunctionList)
+ PR_FindSymbol(library, "C_GetFunctionList");
+ if (entry == NULL) {
+ PR_UnloadLibrary(library);
+ return SECFailure;
+ }
+ }
+
+ /*
+ * We need to get the function list
+ */
+ if ((*entry)((CK_FUNCTION_LIST_PTR *)&mod->functionList) != CKR_OK)
+ goto fail;
+
+ mod->isThreadSafe = PR_TRUE;
+ /* Now we initialize the module */
+ if (PK11_GETTAB(mod)->C_Initialize(&secmodLockFunctions) != CKR_OK) {
+ mod->isThreadSafe = PR_FALSE;
+ if (PK11_GETTAB(mod)->C_Initialize(NULL) != CKR_OK) goto fail;
+ }
+
+ /* check the version number */
+ if (PK11_GETTAB(mod)->C_GetInfo(&info) != CKR_OK) goto fail2;
+ if (info.cryptokiVersion.major != 2) goto fail2;
+ /* all 2.0 are a priori *not* thread safe */
+ if (info.cryptokiVersion.minor < 1) mod->isThreadSafe = PR_FALSE;
+
+
+ /* If we don't have a common name, get it from the PKCS 11 module */
+ if ((mod->commonName == NULL) || (mod->commonName[0] == 0)) {
+ mod->commonName = PK11_MakeString(mod->arena,NULL,
+ (char *)info.libraryDescription, sizeof(info.libraryDescription));
+ if (mod->commonName == NULL) goto fail2;
+ }
+
+
+ /* initialize the Slots */
+ if (PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, NULL, &slotCount) == CKR_OK) {
+ CK_SLOT_ID *slotIDs;
+ int i;
+ CK_RV rv;
+
+ mod->slots = (PK11SlotInfo **)PORT_ArenaAlloc(mod->arena,
+ sizeof(PK11SlotInfo *) * slotCount);
+ if (mod->slots == NULL) goto fail2;
+
+ slotIDs = (CK_SLOT_ID *) PORT_Alloc(sizeof(CK_SLOT_ID)*slotCount);
+ if (slotIDs == NULL) {
+ goto fail2;
+ }
+ rv = PK11_GETTAB(mod)->C_GetSlotList(CK_FALSE, slotIDs, &slotCount);
+ if (rv != CKR_OK) {
+ PORT_Free(slotIDs);
+ goto fail2;
+ }
+
+ /* Initialize each slot */
+ for (i=0; i < (int)slotCount; i++) {
+ mod->slots[i] = PK11_NewSlotInfo();
+ PK11_InitSlot(mod,slotIDs[i],mod->slots[i]);
+ /* look down the slot info table */
+ PK11_LoadSlotList(mod->slots[i],mod->slotInfo,mod->slotInfoCount);
+ }
+ mod->slotCount = slotCount;
+ mod->slotInfoCount = 0;
+ PORT_Free(slotIDs);
+ }
+
+
+
+
+ mod->loaded = PR_TRUE;
+ mod->moduleID = nextModuleID++;
+ return SECSuccess;
+fail2:
+ PK11_GETTAB(mod)->C_Finalize(NULL);
+fail:
+ mod->functionList = NULL;
+ if (library) PR_UnloadLibrary(library);
+ return SECFailure;
+}
+
+SECStatus
+SECMOD_UnloadModule(SECMODModule *mod) {
+ PRLibrary *library;
+
+ if (!mod->loaded) {
+ return SECFailure;
+ }
+
+ PK11_GETTAB(mod)->C_Finalize(NULL);
+ mod->moduleID = 0;
+ mod->loaded = PR_FALSE;
+
+ /* do we want the semantics to allow unloading the internal library?
+ * if not, we should change this to SECFailure and move it above the
+ * mod->loaded = PR_FALSE; */
+ if (mod->internal) {
+ return SECSuccess;
+ }
+
+ library = (PRLibrary *)mod->library;
+ /* paranoia */
+ if (library == NULL) {
+ return SECFailure;
+ }
+
+ PR_UnloadLibrary(library);
+ return SECSuccess;
+}
diff --git a/security/nss/lib/pk11wrap/pk11skey.c b/security/nss/lib/pk11wrap/pk11skey.c
new file mode 100644
index 000000000..dd31f6769
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11skey.c
@@ -0,0 +1,4676 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * This file implements the Symkey wrapper and the PKCS context
+ * Interfaces.
+ */
+
+#include "seccomon.h"
+#include "secmod.h"
+#include "prlock.h"
+#include "secmodi.h"
+#include "pkcs11.h"
+#include "pk11func.h"
+#include "secitem.h"
+#include "key.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "sechash.h"
+#include "cert.h"
+#include "secerr.h"
+
+#define PAIRWISE_SECITEM_TYPE siBuffer
+#define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */
+#define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */
+
+/* forward static declarations. */
+static PK11SymKey *pk11_DeriveWithTemplate(PK11SymKey *baseKey,
+ CK_MECHANISM_TYPE derive, SECItem *param, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize, CK_ATTRIBUTE *userAttr,
+ unsigned int numAttrs);
+
+
+/*
+ * strip leading zero's from key material
+ */
+void
+pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib) {
+ char *ptr = (char *)attrib->pValue;
+ unsigned long len = attrib->ulValueLen;
+
+ while (len && (*ptr == 0)) {
+ len--;
+ ptr++;
+ }
+ attrib->pValue = ptr;
+ attrib->ulValueLen = len;
+}
+
+/*
+ * get a new session on a slot. If we run out of session, use the slot's
+ * 'exclusive' session. In this case owner becomes false.
+ */
+static CK_SESSION_HANDLE
+pk11_GetNewSession(PK11SlotInfo *slot,PRBool *owner)
+{
+ CK_SESSION_HANDLE session;
+ *owner = PR_TRUE;
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ if ( PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION,
+ slot,pk11_notify,&session) != CKR_OK) {
+ *owner = PR_FALSE;
+ session = slot->session;
+ }
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+
+ return session;
+}
+
+static void
+pk11_CloseSession(PK11SlotInfo *slot,CK_SESSION_HANDLE session,PRBool owner)
+{
+ if (!owner) return;
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ (void) PK11_GETTAB(slot)->C_CloseSession(session);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+}
+
+
+SECStatus
+PK11_CreateNewObject(PK11SlotInfo *slot,CK_ATTRIBUTE *theTemplate, int count,
+ PRBool token, CK_OBJECT_HANDLE *objectID)
+{
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+
+ if (token) {
+ rwsession = PK11_GetRWSession(slot);
+ } else {
+ rwsession = slot->session;
+ PK11_EnterSlotMonitor(slot);
+ }
+ crv = PK11_GETTAB(slot)->C_CreateObject(rwsession, theTemplate,
+ count,objectID);
+ if(crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ rv = SECFailure;
+ }
+ if (token) {
+ PK11_RestoreROSession(slot, rwsession);
+ } else {
+ PK11_ExitSlotMonitor(slot);
+ }
+ return rv;
+}
+
+static void
+pk11_EnterKeyMonitor(PK11SymKey *symKey) {
+ if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe))
+ PK11_EnterSlotMonitor(symKey->slot);
+}
+
+static void
+pk11_ExitKeyMonitor(PK11SymKey *symKey) {
+ if (!symKey->sessionOwner || !(symKey->slot->isThreadSafe))
+ PK11_ExitSlotMonitor(symKey->slot);
+}
+
+/*
+ * create a symetric key:
+ * Slot is the slot to create the key in.
+ * type is the mechainism type
+ */
+PK11SymKey *
+PK11_CreateSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, void *wincx)
+{
+
+ PK11SymKey *symKey = (PK11SymKey *)PORT_Alloc(sizeof(PK11SymKey));
+ if (symKey == NULL) {
+ return NULL;
+ }
+ symKey->refLock = PR_NewLock();
+ if (symKey->refLock == NULL) {
+ PORT_Free(symKey);
+ return NULL;
+ }
+ symKey->type = type;
+ symKey->data.data = NULL;
+ symKey->data.len = 0;
+ symKey->owner = PR_TRUE;
+ symKey->objectID = CK_INVALID_KEY;
+ symKey->slot = slot;
+ symKey->series = slot->series;
+ symKey->cx = wincx;
+ symKey->size = 0;
+ symKey->refCount = 1;
+ symKey->origin = PK11_OriginNULL;
+ symKey->session = pk11_GetNewSession(slot,&symKey->sessionOwner);
+ symKey->origin = PK11_OriginNULL;
+ PK11_ReferenceSlot(slot);
+ return symKey;
+}
+
+/*
+ * destroy a symetric key
+ */
+void
+PK11_FreeSymKey(PK11SymKey *symKey)
+{
+ PRBool destroy = PR_FALSE;
+
+ PK11_USE_THREADS(PR_Lock(symKey->refLock);)
+ if (symKey->refCount-- == 1) {
+ destroy= PR_TRUE;
+ }
+ PK11_USE_THREADS(PR_Unlock(symKey->refLock);)
+ if (destroy) {
+ if ((symKey->owner) && symKey->objectID != CK_INVALID_KEY) {
+ pk11_EnterKeyMonitor(symKey);
+ (void) PK11_GETTAB(symKey->slot)->
+ C_DestroyObject(symKey->session, symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ }
+ pk11_CloseSession(symKey->slot, symKey->session,symKey->sessionOwner);
+ if (symKey->data.data) {
+ PORT_Memset(symKey->data.data, 0, symKey->data.len);
+ PORT_Free(symKey->data.data);
+ }
+ PK11_USE_THREADS(PR_DestroyLock(symKey->refLock);)
+ PK11_FreeSlot(symKey->slot);
+ PORT_Free(symKey);
+ }
+}
+
+PK11SymKey *
+PK11_ReferenceSymKey(PK11SymKey *symKey)
+{
+ PK11_USE_THREADS(PR_Lock(symKey->refLock);)
+ symKey->refCount++;
+ PK11_USE_THREADS(PR_Unlock(symKey->refLock);)
+ return symKey;
+}
+
+/*
+ * turn key handle into an appropriate key object
+ */
+PK11SymKey *
+PK11_SymKeyFromHandle(PK11SlotInfo *slot, PK11SymKey *parent, PK11Origin origin,
+ CK_MECHANISM_TYPE type, CK_OBJECT_HANDLE keyID, PRBool owner, void *wincx)
+{
+ PK11SymKey *symKey;
+
+ if (keyID == CK_INVALID_KEY) {
+ return NULL;
+ }
+
+ symKey = PK11_CreateSymKey(slot,type,wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->objectID = keyID;
+ symKey->origin = origin;
+ symKey->owner = owner;
+
+ /* adopt the parent's session */
+ /* This is only used by SSL. What we really want here is a session
+ * structure with a ref count so the session goes away only after all the
+ * keys do. */
+ if (owner && parent) {
+ pk11_CloseSession(symKey->slot, symKey->session,symKey->sessionOwner);
+ symKey->sessionOwner = parent->sessionOwner;
+ symKey->session = parent->session;
+ parent->sessionOwner = PR_FALSE;
+ }
+
+ return symKey;
+}
+
+/*
+ * turn key handle into an appropriate key object
+ */
+PK11SymKey *
+PK11_GetWrapKey(PK11SlotInfo *slot, int wrap, CK_MECHANISM_TYPE type,
+ int series, void *wincx)
+{
+ PK11SymKey *symKey = NULL;
+
+ if (slot->series != series) return NULL;
+ if (slot->refKeys[wrap] == CK_INVALID_KEY) return NULL;
+ if (type == CKM_INVALID_MECHANISM) type = slot->wrapMechanism;
+
+ symKey = PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive,
+ slot->wrapMechanism, slot->refKeys[wrap], PR_FALSE, wincx);
+ return symKey;
+}
+
+void
+PK11_SetWrapKey(PK11SlotInfo *slot, int wrap, PK11SymKey *wrapKey)
+{
+ /* save the handle and mechanism for the wrapping key */
+ /* mark the key and session as not owned by us to they don't get freed
+ * when the key goes way... that lets us reuse the key later */
+ slot->refKeys[wrap] = wrapKey->objectID;
+ wrapKey->owner = PR_FALSE;
+ wrapKey->sessionOwner = PR_FALSE;
+ slot->wrapMechanism = wrapKey->type;
+}
+
+CK_MECHANISM_TYPE
+PK11_GetMechanism(PK11SymKey *symKey)
+{
+ return symKey->type;
+}
+
+/*
+ * figure out if a key is still valid or if it is stale.
+ */
+PRBool
+PK11_VerifyKeyOK(PK11SymKey *key) {
+ if (!PK11_IsPresent(key->slot)) {
+ return PR_FALSE;
+ }
+ return (PRBool)(key->series == key->slot->series);
+}
+
+static PK11SymKey *
+pk11_ImportSymKeyWithTempl(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE *keyTemplate,
+ unsigned int templateCount, SECItem *key, void *wincx)
+{
+ PK11SymKey * symKey;
+ CK_RV crv;
+
+ symKey = PK11_CreateSymKey(slot,type,wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->size = key->len;
+
+ if (SECITEM_CopyItem(NULL,&symKey->data,key) != SECSuccess) {
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+
+ symKey->origin = origin;
+
+ /* import the keys */
+ crv = PK11_CreateNewObject(slot, keyTemplate, templateCount, PR_FALSE,
+ &symKey->objectID);
+ if ( crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError( PK11_MapError(crv));
+ return NULL;
+ }
+
+ return symKey;
+}
+
+/*
+ * turn key bits into an appropriate key object
+ */
+PK11SymKey *
+PK11_ImportSymKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,void *wincx)
+{
+ PK11SymKey * symKey;
+ unsigned int templateCount = 0;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_BBOOL cktrue = CK_TRUE; /* sigh */
+ CK_ATTRIBUTE keyTemplate[5];
+ CK_ATTRIBUTE * attrs = keyTemplate;
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++;
+ PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, key->data, key->len); attrs++;
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(type,key->len);
+ symKey = pk11_ImportSymKeyWithTempl(slot, type, origin, keyTemplate,
+ templateCount, key, wincx);
+ return symKey;
+}
+
+/*
+ * import a public key into the desired slot
+ */
+CK_OBJECT_HANDLE
+PK11_ImportPublicKey(PK11SlotInfo *slot, SECKEYPublicKey *pubKey,
+ PRBool isToken)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_OBJECT_HANDLE objectID;
+ CK_ATTRIBUTE theTemplate[10];
+ CK_ATTRIBUTE *signedattr = NULL;
+ CK_ATTRIBUTE *attrs = theTemplate;
+ int signedcount = 0;
+ int templateCount = 0;
+ CK_RV crv;
+
+ /* if we already have an object in the desired slot, use it */
+ if (!isToken && pubKey->pkcs11Slot == slot) {
+ return pubKey->pkcs11ID;
+ }
+
+ /* free the existing key */
+ if (pubKey->pkcs11Slot != NULL) {
+ PK11SlotInfo *oSlot = pubKey->pkcs11Slot;
+ PK11_EnterSlotMonitor(oSlot);
+ (void) PK11_GETTAB(oSlot)->C_DestroyObject(oSlot->session,
+ pubKey->pkcs11ID);
+ PK11_ExitSlotMonitor(oSlot);
+ PK11_FreeSlot(oSlot);
+ pubKey->pkcs11Slot = NULL;
+ }
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, isToken ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL) ); attrs++;
+
+ /* now import the key */
+ {
+ switch (pubKey->keyType) {
+ case rsaKey:
+ keyType = CKK_RSA;
+ PK11_SETATTRS(attrs, CKA_WRAP, &cktrue, sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_ENCRYPT, &cktrue,
+ sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_MODULUS, pubKey->u.rsa.modulus.data,
+ pubKey->u.rsa.modulus.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT,
+ pubKey->u.rsa.publicExponent.data,
+ pubKey->u.rsa.publicExponent.len); attrs++;
+ break;
+ case dsaKey:
+ keyType = CKK_DSA;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dsa.params.prime.data,
+ pubKey->u.dsa.params.prime.len); attrs++;
+ PK11_SETATTRS(attrs,CKA_SUBPRIME,pubKey->u.dsa.params.subPrime.data,
+ pubKey->u.dsa.params.subPrime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dsa.params.base.data,
+ pubKey->u.dsa.params.base.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dsa.publicValue.data,
+ pubKey->u.dsa.publicValue.len); attrs++;
+ break;
+ case fortezzaKey:
+ keyType = CKK_DSA;
+ PK11_SETATTRS(attrs, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL));attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME,pubKey->u.fortezza.params.prime.data,
+ pubKey->u.fortezza.params.prime.len); attrs++;
+ PK11_SETATTRS(attrs,CKA_SUBPRIME,
+ pubKey->u.fortezza.params.subPrime.data,
+ pubKey->u.fortezza.params.subPrime.len);attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.fortezza.params.base.data,
+ pubKey->u.fortezza.params.base.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.fortezza.DSSKey.data,
+ pubKey->u.fortezza.DSSKey.len); attrs++;
+ break;
+ case dhKey:
+ keyType = CKK_DH;
+ PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL));attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, pubKey->u.dh.prime.data,
+ pubKey->u.dh.prime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, pubKey->u.dh.base.data,
+ pubKey->u.dh.base.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, pubKey->u.dh.publicValue.data,
+ pubKey->u.dh.publicValue.len); attrs++;
+ break;
+ /* what about fortezza??? */
+ default:
+ PORT_SetError( SEC_ERROR_BAD_KEY );
+ return CK_INVALID_KEY;
+ }
+
+ templateCount = attrs - theTemplate;
+ signedcount = attrs - signedattr;
+ PORT_Assert(templateCount <= (sizeof(theTemplate)/sizeof(CK_ATTRIBUTE)));
+ for (attrs=signedattr; signedcount; attrs++, signedcount--) {
+ pk11_SignedToUnsigned(attrs);
+ }
+ crv = PK11_CreateNewObject(slot, theTemplate, templateCount, isToken,
+ &objectID);
+ if ( crv != CKR_OK) {
+ PORT_SetError (PK11_MapError(crv));
+ return CK_INVALID_KEY;
+ }
+ }
+
+ pubKey->pkcs11ID = objectID;
+ pubKey->pkcs11Slot = PK11_ReferenceSlot(slot);
+
+ return objectID;
+}
+
+
+/*
+ * return the slot associated with a symetric key
+ */
+PK11SlotInfo *
+PK11_GetSlotFromKey(PK11SymKey *symKey)
+{
+ return PK11_ReferenceSlot(symKey->slot);
+}
+
+PK11SymKey *
+PK11_FindFixedKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *keyID,
+ void *wincx)
+{
+ CK_ATTRIBUTE findTemp[4];
+ CK_ATTRIBUTE *attrs;
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_OBJECT_CLASS keyclass = CKO_SECRET_KEY;
+ int tsize = 0;
+ CK_OBJECT_HANDLE key_id;
+
+ attrs = findTemp;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyclass, sizeof(keyclass)); attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, &ckTrue, sizeof(ckTrue)); attrs++;
+ if (keyID) {
+ PK11_SETATTRS(attrs, CKA_ID, keyID->data, keyID->len); attrs++;
+ }
+ tsize = attrs - findTemp;
+ PORT_Assert(tsize <= sizeof(findTemp)/sizeof(CK_ATTRIBUTE));
+
+ key_id = pk11_FindObjectByTemplate(slot,findTemp,tsize);
+ if (key_id == CK_INVALID_KEY) {
+ return NULL;
+ }
+ return PK11_SymKeyFromHandle(slot, NULL, PK11_OriginDerive, type, key_id,
+ PR_FALSE, wincx);
+}
+
+void *
+PK11_GetWindow(PK11SymKey *key)
+{
+ return key->cx;
+}
+
+
+/*
+ * extract a symetric key value. NOTE: if the key is sensitive, we will
+ * not be able to do this operation. This function is used to move
+ * keys from one token to another */
+SECStatus
+PK11_ExtractKeyValue(PK11SymKey *symKey)
+{
+
+ if (symKey->data.data != NULL) return SECSuccess;
+
+ if (symKey->slot == NULL) {
+ PORT_SetError( SEC_ERROR_INVALID_KEY );
+ return SECFailure;
+ }
+
+ return PK11_ReadAttribute(symKey->slot,symKey->objectID,CKA_VALUE,NULL,
+ &symKey->data);
+}
+
+SECItem *
+PK11_GetKeyData(PK11SymKey *symKey)
+{
+ return &symKey->data;
+}
+
+/*
+ * take an attribute and copy it into a secitem, converting unsigned to signed.
+ */
+static CK_RV
+pk11_Attr2SecItem(PRArenaPool *arena, CK_ATTRIBUTE *attr, SECItem *item) {
+ unsigned char *dataPtr;
+
+ item->len = attr->ulValueLen;
+ dataPtr = (unsigned char*) PORT_ArenaAlloc(arena, item->len+1);
+ if ( dataPtr == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ *dataPtr = 0;
+ item->data = dataPtr+1;
+ PORT_Memcpy(item->data,attr->pValue,item->len);
+ if (item->data[0] & 0x80) {
+ item->data = item->data-1;
+ item->len++;
+ }
+ return CKR_OK;
+}
+/*
+ * extract a public key from a slot and id
+ */
+SECKEYPublicKey *
+PK11_ExtractPublicKey(PK11SlotInfo *slot,KeyType keyType,CK_OBJECT_HANDLE id)
+{
+ CK_OBJECT_CLASS keyClass = CKO_PUBLIC_KEY;
+ PRArenaPool *arena;
+ PRArenaPool *tmp_arena;
+ SECKEYPublicKey *pubKey;
+ int templateCount = 0;
+ CK_KEY_TYPE pk11KeyType;
+ CK_RV crv;
+ CK_ATTRIBUTE template[8];
+ CK_ATTRIBUTE *attrs= template;
+ CK_ATTRIBUTE *modulus,*exponent,*base,*prime,*subprime,*value;
+
+ /* if we didn't know the key type, get it */
+ if (keyType== nullKey) {
+
+ pk11KeyType = PK11_ReadULongAttribute(slot,id,CKA_KEY_TYPE);
+ if (pk11KeyType == CK_UNAVAILABLE_INFORMATION) {
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+ switch (pk11KeyType) {
+ case CKK_RSA:
+ keyType = rsaKey;
+ break;
+ case CKK_DSA:
+ keyType = dsaKey;
+ break;
+ case CKK_DH:
+ keyType = dhKey;
+ break;
+ default:
+ PORT_SetError( SEC_ERROR_BAD_KEY );
+ return NULL;
+ }
+ }
+
+
+ /* now we need to create space for the public key */
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) return NULL;
+ tmp_arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
+ if (tmp_arena == NULL) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+
+ pubKey = (SECKEYPublicKey *)
+ PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
+ if (pubKey == NULL) {
+ PORT_FreeArena (arena, PR_FALSE);
+ PORT_FreeArena (tmp_arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubKey->arena = arena;
+ pubKey->keyType = keyType;
+ pubKey->pkcs11Slot = PK11_ReferenceSlot(slot);
+ pubKey->pkcs11ID = id;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass,
+ sizeof(keyClass)); attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &pk11KeyType,
+ sizeof(pk11KeyType) ); attrs++;
+ switch (pubKey->keyType) {
+ case rsaKey:
+ modulus = attrs;
+ PK11_SETATTRS(attrs, CKA_MODULUS, NULL, 0); attrs++;
+ exponent = attrs;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT, NULL, 0); attrs++;
+
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount);
+ if (crv != CKR_OK) break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_RSA)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ crv = pk11_Attr2SecItem(arena,modulus,&pubKey->u.rsa.modulus);
+ if (crv != CKR_OK) break;
+ crv = pk11_Attr2SecItem(arena,exponent,&pubKey->u.rsa.publicExponent);
+ if (crv != CKR_OK) break;
+ break;
+ case dsaKey:
+ prime = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0); attrs++;
+ subprime = attrs;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, NULL, 0); attrs++;
+ base = attrs;
+ PK11_SETATTRS(attrs, CKA_BASE, NULL, 0); attrs++;
+ value = attrs;
+ PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++;
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount);
+ if (crv != CKR_OK) break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ crv = pk11_Attr2SecItem(arena,prime,&pubKey->u.dsa.params.prime);
+ if (crv != CKR_OK) break;
+ crv = pk11_Attr2SecItem(arena,subprime,&pubKey->u.dsa.params.subPrime);
+ if (crv != CKR_OK) break;
+ crv = pk11_Attr2SecItem(arena,base,&pubKey->u.dsa.params.base);
+ if (crv != CKR_OK) break;
+ crv = pk11_Attr2SecItem(arena,value,&pubKey->u.dsa.publicValue);
+ if (crv != CKR_OK) break;
+ break;
+ case dhKey:
+ prime = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, NULL, 0); attrs++;
+ base = attrs;
+ PK11_SETATTRS(attrs, CKA_BASE, NULL, 0); attrs++;
+ value =attrs;
+ PK11_SETATTRS(attrs, CKA_VALUE, NULL, 0); attrs++;
+ templateCount = attrs - template;
+ PR_ASSERT(templateCount <= sizeof(template)/sizeof(CK_ATTRIBUTE));
+ crv = PK11_GetAttributes(tmp_arena,slot,id,template,templateCount);
+ if (crv != CKR_OK) break;
+
+ if ((keyClass != CKO_PUBLIC_KEY) || (pk11KeyType != CKK_DSA)) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+ crv = pk11_Attr2SecItem(arena,prime,&pubKey->u.dh.prime);
+ if (crv != CKR_OK) break;
+ crv = pk11_Attr2SecItem(arena,base,&pubKey->u.dh.base);
+ if (crv != CKR_OK) break;
+ crv = pk11_Attr2SecItem(arena,value,&pubKey->u.dh.publicValue);
+ if (crv != CKR_OK) break;
+ break;
+ case fortezzaKey:
+ case nullKey:
+ default:
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ break;
+ }
+
+ PORT_FreeArena(tmp_arena,PR_FALSE);
+
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ PK11_FreeSlot(slot);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+
+ return pubKey;
+}
+
+/*
+ * Build a Private Key structure from raw PKCS #11 information.
+ */
+SECKEYPrivateKey *
+PK11_MakePrivKey(PK11SlotInfo *slot, KeyType keyType,
+ PRBool isTemp, CK_OBJECT_HANDLE privID, void *wincx)
+{
+ PRArenaPool *arena;
+ SECKEYPrivateKey *privKey;
+
+ /* don't know? look it up */
+ if (keyType == nullKey) {
+ CK_KEY_TYPE pk11Type = CKK_RSA;
+
+ pk11Type = PK11_ReadULongAttribute(slot,privID,CKA_KEY_TYPE);
+ isTemp = (PRBool)!PK11_HasAttributeSet(slot,privID,CKA_TOKEN);
+ switch (pk11Type) {
+ case CKK_RSA: keyType = rsaKey; break;
+ case CKK_DSA: keyType = dsaKey; break;
+ case CKK_DH: keyType = dhKey; break;
+ case CKK_KEA: keyType = fortezzaKey; break;
+ default:
+ break;
+ }
+ }
+
+ /* now we need to create space for the private key */
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) return NULL;
+
+ privKey = (SECKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena, sizeof(SECKEYPrivateKey));
+ if (privKey == NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+ }
+
+ privKey->arena = arena;
+ privKey->keyType = keyType;
+ privKey->pkcs11Slot = PK11_ReferenceSlot(slot);
+ privKey->pkcs11ID = privID;
+ privKey->pkcs11IsTemp = isTemp;
+ privKey->wincx = wincx;
+
+ return privKey;
+}
+
+/* return the keylength if possible. '0' if not */
+unsigned int
+PK11_GetKeyLength(PK11SymKey *key)
+{
+ if (key->size != 0) return key->size ;
+ if (key->data.data == NULL) {
+ PK11_ExtractKeyValue(key);
+ }
+ /* key is probably secret. Look up it's type and length */
+ /* this is new PKCS #11 version 2.0 functionality. */
+ if (key->size == 0) {
+ CK_ULONG keyLength;
+
+ keyLength = PK11_ReadULongAttribute(key->slot,key->objectID,CKA_VALUE_LEN);
+ /* doesn't have a length field, check the known PKCS #11 key types,
+ * which don't have this field */
+ if (keyLength == CK_UNAVAILABLE_INFORMATION) {
+ CK_KEY_TYPE keyType;
+ keyType = PK11_ReadULongAttribute(key->slot,key->objectID,CKA_KEY_TYPE);
+ switch (keyType) {
+ case CKK_DES: key->size = 8; break;
+ case CKK_DES2: key->size = 16; break;
+ case CKK_DES3: key->size = 24; break;
+ case CKK_SKIPJACK: key->size = 10; break;
+ case CKK_BATON: key->size = 20; break;
+ case CKK_JUNIPER: key->size = 20; break;
+ case CKK_GENERIC_SECRET:
+ if (key->type == CKM_SSL3_PRE_MASTER_KEY_GEN) {
+ key->size=48;
+ }
+ break;
+ default: break;
+ }
+ } else {
+ key->size = (unsigned int)keyLength;
+ }
+ }
+
+ return key->size;
+}
+
+/* return the strength of a key. This is different from length in that
+ * 1) it returns the size in bits, and 2) it returns only the secret portions
+ * of the key minus any checksums or parity.
+ */
+unsigned int
+PK11_GetKeyStrength(PK11SymKey *key, SECAlgorithmID *algid)
+{
+ int size=0;
+ CK_MECHANISM_TYPE mechanism= CKM_INVALID_MECHANISM; /* RC2 only */
+ SECItem *param = NULL; /* RC2 only */
+ CK_RC2_CBC_PARAMS *rc2_params = NULL; /* RC2 ONLY */
+ unsigned int effectiveBits = 0; /* RC2 ONLY */
+
+ switch (PK11_GetKeyType(key->type,0)) {
+ case CKK_CDMF:
+ return 40;
+ case CKK_DES:
+ return 56;
+ case CKK_DES3:
+ case CKK_DES2:
+ size = PK11_GetKeyLength(key);
+ if (size == 16) {
+ /* double des */
+ return 112; /* 16*7 */
+ }
+ return 168;
+ /*
+ * RC2 has is different than other ciphers in that it allows the user
+ * to deprecating keysize while still requiring all the bits for the
+ * original key. The info
+ * on what the effective key strength is in the parameter for the key.
+ * In S/MIME this parameter is stored in the DER encoded algid. In Our
+ * other uses of RC2, effectiveBits == keyBits, so this code functions
+ * correctly without an algid.
+ */
+ case CKK_RC2:
+ /* if no algid was provided, fall through to default */
+ if (!algid) {
+ break;
+ }
+ /* verify that the algid is for RC2 */
+ mechanism = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(algid));
+ if ((mechanism != CKM_RC2_CBC) && (mechanism != CKM_RC2_ECB)) {
+ break;
+ }
+
+ /* now get effective bits from the algorithm ID. */
+ param = PK11_ParamFromAlgid(algid);
+ /* if we couldn't get memory just use key length */
+ if (param == NULL) {
+ break;
+ }
+
+ rc2_params = (CK_RC2_CBC_PARAMS *) param->data;
+ /* paranoia... shouldn't happen */
+ PORT_Assert(param->data != NULL);
+ if (param->data == NULL) {
+ SECITEM_FreeItem(param,PR_TRUE);
+ break;
+ }
+ effectiveBits = (unsigned int)rc2_params->ulEffectiveBits;
+ SECITEM_FreeItem(param,PR_TRUE);
+ param = NULL; rc2_params=NULL; /* paranoia */
+
+ /* we have effective bits, is and allocated memory is free, now
+ * we need to return the smaller of effective bits and keysize */
+ size = PK11_GetKeyLength(key);
+ if ((unsigned int)size*8 > effectiveBits) {
+ return effectiveBits;
+ }
+
+ return size*8; /* the actual key is smaller, the strength can't be
+ * greater than the actual key size */
+
+ default:
+ break;
+ }
+ return PK11_GetKeyLength(key) * 8;
+}
+
+
+/*
+ * get the length of a signature object based on the key
+ */
+int
+PK11_SignatureLen(SECKEYPrivateKey *key)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ int val;
+
+ switch (key->keyType) {
+ case rsaKey:
+ val = PK11_GetPrivateModulusLen(key);
+ if (val == -1) {
+ break; /* failed */
+ }
+ return (unsigned long) val;
+
+ case fortezzaKey:
+ case dsaKey:
+ return 40;
+
+ default:
+ break;
+ }
+ PORT_SetError( SEC_ERROR_INVALID_KEY );
+ return 0;
+}
+
+PK11SlotInfo *
+PK11_GetSlotFromPrivateKey(SECKEYPrivateKey *key)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ slot = PK11_ReferenceSlot(slot);
+ return slot;
+}
+
+/*
+ * Get the modulus length for raw parsing
+ */
+int
+PK11_GetPrivateModulusLen(SECKEYPrivateKey *key)
+{
+ CK_ATTRIBUTE theTemplate = { CKA_MODULUS, NULL, 0 };
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_RV crv;
+ int length;
+
+ switch (key->keyType) {
+ case rsaKey:
+ crv = PK11_GetAttributes(NULL, slot, key->pkcs11ID, &theTemplate, 1);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return -1;
+ }
+ length = theTemplate.ulValueLen;
+ if ( *(unsigned char *)theTemplate.pValue == 0) {
+ length--;
+ }
+ if (theTemplate.pValue != NULL)
+ PORT_Free(theTemplate.pValue);
+ return (int) length;
+
+ case fortezzaKey:
+ case dsaKey:
+ case dhKey:
+ default:
+ break;
+ }
+ if (theTemplate.pValue != NULL)
+ PORT_Free(theTemplate.pValue);
+ PORT_SetError( SEC_ERROR_INVALID_KEY );
+ return -1;
+}
+
+
+/* Make a Key type to an appropriate signing/verification mechanism */
+static CK_MECHANISM_TYPE
+pk11_mapSignKeyType(KeyType keyType)
+{
+ switch (keyType) {
+ case rsaKey:
+ return CKM_RSA_PKCS;
+ case fortezzaKey:
+ case dsaKey:
+ return CKM_DSA;
+ case dhKey:
+ default:
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+static CK_MECHANISM_TYPE
+pk11_mapWrapKeyType(KeyType keyType)
+{
+ switch (keyType) {
+ case rsaKey:
+ return CKM_RSA_PKCS;
+ /* Add fortezza?? */
+ default:
+ break;
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+/*
+ * copy a key (or any other object) on a token
+ */
+CK_OBJECT_HANDLE
+PK11_CopyKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE srcObject)
+{
+ CK_OBJECT_HANDLE destObject;
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_CopyObject(slot->session,srcObject,NULL,0,
+ &destObject);
+ PK11_ExitSlotMonitor(slot);
+ if (crv == CKR_OK) return destObject;
+ PORT_SetError( PK11_MapError(crv) );
+ return CK_INVALID_KEY;
+}
+
+
+PK11SymKey *
+pk11_KeyExchange(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey);
+
+/*
+ * The next two utilities are to deal with the fact that a given operation
+ * may be a multi-slot affair. This creates a new key object that is copied
+ * into the new slot.
+ */
+PK11SymKey *
+pk11_CopyToSlot(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey)
+{
+ SECStatus rv;
+ PK11SymKey *newKey = NULL;
+
+ /* Extract the raw key data if possible */
+ if (symKey->data.data == NULL) {
+ rv = PK11_ExtractKeyValue(symKey);
+ /* KEY is sensitive, we're try key exchanging it. */
+ if (rv != SECSuccess) {
+ return pk11_KeyExchange(slot, type, operation, symKey);
+ }
+ }
+ newKey = PK11_ImportSymKey(slot, type, symKey->origin, operation,
+ &symKey->data, symKey->cx);
+ if (newKey == NULL) newKey = pk11_KeyExchange(slot,type,operation,symKey);
+ return newKey;
+}
+
+/*
+ * Make sure the slot we are in the correct slot for the operation
+ */
+static PK11SymKey *
+pk11_ForceSlot(PK11SymKey *symKey,CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation)
+{
+ PK11SlotInfo *slot = symKey->slot;
+ PK11SymKey *newKey = NULL;
+
+ if ((slot== NULL) || !PK11_DoesMechanism(slot,type)) {
+ slot = PK11_GetBestSlot(type,symKey->cx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+ }
+ newKey = pk11_CopyToSlot(slot, type, operation, symKey);
+ PK11_FreeSlot(slot);
+ }
+ return newKey;
+}
+
+/*
+ * Use the token to Generate a key. keySize must be 'zero' for fixed key
+ * length algorithms. NOTE: this means we can never generate a DES2 key
+ * from this interface!
+ */
+PK11SymKey *
+PK11_KeyGen(PK11SlotInfo *slot, CK_MECHANISM_TYPE type, SECItem *param,
+ int keySize, void *wincx)
+{
+ CK_ULONG key_size = 0;
+ /* we have to use these native types because when we call PKCS 11 modules
+ * we have to make sure that we are using the correct sizes for all the
+ * parameters. */
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_ATTRIBUTE genTemplate[2];
+ int count = sizeof(genTemplate)/sizeof(genTemplate[0]);
+ CK_MECHANISM mechanism;
+ CK_MECHANISM_TYPE key_gen_mechanism;
+ PK11SymKey *symKey;
+ CK_RV crv;
+ CK_ATTRIBUTE *attrs = genTemplate;
+ PRBool weird = PR_FALSE; /* hack for fortezza */
+
+ if ((keySize == -1) && (type == CKM_SKIPJACK_CBC64)) {
+ weird = PR_TRUE;
+ keySize = 0;
+ }
+
+ PK11_SETATTRS(attrs, (!weird)
+ ? CKA_ENCRYPT : CKA_DECRYPT, &cktrue, sizeof(CK_BBOOL)); attrs++;
+ key_size = keySize;
+ if (key_size != 0) {
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size));
+ attrs++;
+ }
+ count = attrs - genTemplate;
+ PR_ASSERT(count <= sizeof(genTemplate)/sizeof(CK_ATTRIBUTE));
+
+ /* find a slot to generate the key into */
+ if ((slot == NULL) || (!PK11_DoesMechanism(slot,type))) {
+ slot = PK11_GetBestSlot(type,wincx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+ }
+ } else {
+ PK11_ReferenceSlot(slot);
+ }
+
+ /* Initialize the Key Gen Mechanism */
+ key_gen_mechanism = PK11_GetKeyGen(type);
+ if (key_gen_mechanism == CKM_FAKE_RANDOM) {
+ PK11_FreeSlot(slot);
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+ }
+ mechanism.mechanism = key_gen_mechanism;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ }
+
+ /* get our key Structure */
+ symKey = PK11_CreateSymKey(slot,type,wincx);
+ PK11_FreeSlot(slot);
+ if (symKey == NULL) {
+ return NULL;
+ }
+ symKey->size = keySize;
+ symKey->origin = (!weird) ? PK11_OriginGenerated : PK11_OriginFortezzaHack;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(symKey->slot)->C_GenerateKey(symKey->session,
+ &mechanism, genTemplate, count, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ if (crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+ return symKey;
+}
+
+/*
+ * PKCS #11 pairwise consistency check utilized to validate key pair.
+ */
+static SECStatus
+pk11_PairwiseConsistencyCheck(SECKEYPublicKey *pubKey,
+ SECKEYPrivateKey *privKey, CK_MECHANISM *mech, void* wincx )
+{
+ /* Variables used for Encrypt/Decrypt functions. */
+ unsigned char *known_message = (unsigned char *)"Known Crypto Message";
+ CK_BBOOL isEncryptable = CK_FALSE;
+ CK_BBOOL canSignVerify = CK_FALSE;
+ CK_BBOOL isDerivable = CK_FALSE;
+ unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH];
+ CK_ULONG bytes_decrypted;
+ PK11SlotInfo *slot;
+ CK_OBJECT_HANDLE id;
+ unsigned char *ciphertext;
+ unsigned char *text_compared;
+ CK_ULONG max_bytes_encrypted;
+ CK_ULONG bytes_encrypted;
+ CK_ULONG bytes_compared;
+ CK_RV crv;
+
+ /* Variables used for Signature/Verification functions. */
+ unsigned char *known_digest = (unsigned char *)"Mozilla Rules World!";
+ SECItem signature;
+ SECItem digest; /* always uses SHA-1 digest */
+ int signature_length;
+ SECStatus rv;
+
+ /**************************************************/
+ /* Pairwise Consistency Check of Encrypt/Decrypt. */
+ /**************************************************/
+
+ isEncryptable = PK11_HasAttributeSet( privKey->pkcs11Slot,
+ privKey->pkcs11ID, CKA_DECRYPT );
+
+ /* If the encryption attribute is set; attempt to encrypt */
+ /* with the public key and decrypt with the private key. */
+ if( isEncryptable ) {
+ /* Find a module to encrypt against */
+ slot = PK11_GetBestSlot(pk11_mapWrapKeyType(privKey->keyType),wincx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return SECFailure;
+ }
+
+ id = PK11_ImportPublicKey(slot,pubKey,PR_FALSE);
+ if (id == CK_INVALID_KEY) {
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ /* Compute max bytes encrypted from modulus length of private key. */
+ max_bytes_encrypted = PK11_GetPrivateModulusLen( privKey );
+
+
+ /* Prepare for encryption using the public key. */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB( slot )->C_EncryptInit( slot->session,
+ mech, id );
+ if( crv != CKR_OK ) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError( PK11_MapError( crv ) );
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ /* Allocate space for ciphertext. */
+ ciphertext = (unsigned char *) PORT_Alloc( max_bytes_encrypted );
+ if( ciphertext == NULL ) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError( SEC_ERROR_NO_MEMORY );
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ /* Initialize bytes encrypted to max bytes encrypted. */
+ bytes_encrypted = max_bytes_encrypted;
+
+ /* Encrypt using the public key. */
+ crv = PK11_GETTAB( slot )->C_Encrypt( slot->session,
+ known_message,
+ PAIRWISE_MESSAGE_LENGTH,
+ ciphertext,
+ &bytes_encrypted );
+ PK11_ExitSlotMonitor(slot);
+ PK11_FreeSlot(slot);
+ if( crv != CKR_OK ) {
+ PORT_SetError( PK11_MapError( crv ) );
+ PORT_Free( ciphertext );
+ return SECFailure;
+ }
+
+ /* Always use the smaller of these two values . . . */
+ bytes_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH )
+ ? PAIRWISE_MESSAGE_LENGTH
+ : bytes_encrypted;
+
+ /* If there was a failure, the plaintext */
+ /* goes at the end, therefore . . . */
+ text_compared = ( bytes_encrypted > PAIRWISE_MESSAGE_LENGTH )
+ ? (ciphertext + bytes_encrypted -
+ PAIRWISE_MESSAGE_LENGTH )
+ : ciphertext;
+
+ /* Check to ensure that ciphertext does */
+ /* NOT EQUAL known input message text */
+ /* per FIPS PUB 140-1 directive. */
+ if( ( bytes_encrypted != max_bytes_encrypted ) ||
+ ( PORT_Memcmp( text_compared, known_message,
+ bytes_compared ) == 0 ) ) {
+ /* Set error to Invalid PRIVATE Key. */
+ PORT_SetError( SEC_ERROR_INVALID_KEY );
+ PORT_Free( ciphertext );
+ return SECFailure;
+ }
+
+ slot = privKey->pkcs11Slot;
+ /* Prepare for decryption using the private key. */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB( slot )->C_DecryptInit( slot->session,
+ mech,
+ privKey->pkcs11ID );
+ if( crv != CKR_OK ) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError( PK11_MapError(crv) );
+ PORT_Free( ciphertext );
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ /* Initialize bytes decrypted to be the */
+ /* expected PAIRWISE_MESSAGE_LENGTH. */
+ bytes_decrypted = PAIRWISE_MESSAGE_LENGTH;
+
+ /* Decrypt using the private key. */
+ /* NOTE: No need to reset the */
+ /* value of bytes_encrypted. */
+ crv = PK11_GETTAB( slot )->C_Decrypt( slot->session,
+ ciphertext,
+ bytes_encrypted,
+ plaintext,
+ &bytes_decrypted );
+ PK11_ExitSlotMonitor(slot);
+
+ /* Finished with ciphertext; free it. */
+ PORT_Free( ciphertext );
+
+ if( crv != CKR_OK ) {
+ PORT_SetError( PK11_MapError(crv) );
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+
+ /* Check to ensure that the output plaintext */
+ /* does EQUAL known input message text. */
+ if( ( bytes_decrypted != PAIRWISE_MESSAGE_LENGTH ) ||
+ ( PORT_Memcmp( plaintext, known_message,
+ PAIRWISE_MESSAGE_LENGTH ) != 0 ) ) {
+ /* Set error to Bad PUBLIC Key. */
+ PORT_SetError( SEC_ERROR_BAD_KEY );
+ PK11_FreeSlot(slot);
+ return SECFailure;
+ }
+ }
+
+ /**********************************************/
+ /* Pairwise Consistency Check of Sign/Verify. */
+ /**********************************************/
+
+ canSignVerify = PK11_HasAttributeSet ( privKey->pkcs11Slot,
+ privKey->pkcs11ID, CKA_VERIFY);
+
+ if (canSignVerify)
+ {
+ /* Initialize signature and digest data. */
+ signature.data = NULL;
+ digest.data = NULL;
+
+ /* Determine length of signature. */
+ signature_length = PK11_SignatureLen( privKey );
+ if( signature_length == 0 )
+ goto failure;
+
+ /* Allocate space for signature data. */
+ signature.data = (unsigned char *) PORT_Alloc( signature_length );
+ if( signature.data == NULL ) {
+ PORT_SetError( SEC_ERROR_NO_MEMORY );
+ goto failure;
+ }
+
+ /* Allocate space for known digest data. */
+ digest.data = (unsigned char *) PORT_Alloc( PAIRWISE_DIGEST_LENGTH );
+ if( digest.data == NULL ) {
+ PORT_SetError( SEC_ERROR_NO_MEMORY );
+ goto failure;
+ }
+
+ /* "Fill" signature type and length. */
+ signature.type = PAIRWISE_SECITEM_TYPE;
+ signature.len = signature_length;
+
+ /* "Fill" digest with known SHA-1 digest parameters. */
+ digest.type = PAIRWISE_SECITEM_TYPE;
+ PORT_Memcpy( digest.data, known_digest, PAIRWISE_DIGEST_LENGTH );
+ digest.len = PAIRWISE_DIGEST_LENGTH;
+
+ /* Sign the known hash using the private key. */
+ rv = PK11_Sign( privKey, &signature, &digest );
+ if( rv != SECSuccess )
+ goto failure;
+
+ /* Verify the known hash using the public key. */
+ rv = PK11_Verify( pubKey, &signature, &digest, wincx );
+ if( rv != SECSuccess )
+ goto failure;
+
+ /* Free signature and digest data. */
+ PORT_Free( signature.data );
+ PORT_Free( digest.data );
+ }
+
+
+
+ /**********************************************/
+ /* Pairwise Consistency Check for Derivation */
+ /**********************************************/
+
+ isDerivable = PK11_HasAttributeSet ( privKey->pkcs11Slot,
+ privKey->pkcs11ID, CKA_DERIVE);
+
+ if (isDerivable)
+ {
+ /*
+ * We are not doing consistency check for Diffie-Hellman Key -
+ * otherwise it would be here
+ */
+
+ }
+
+ return SECSuccess;
+
+failure:
+ if( signature.data != NULL )
+ PORT_Free( signature.data );
+ if( digest.data != NULL )
+ PORT_Free( digest.data );
+
+ return SECFailure;
+}
+
+
+
+/*
+ * take a private key in one pkcs11 module and load it into another:
+ * NOTE: the source private key is a rare animal... it can't be sensitive.
+ * This is used to do a key gen using one pkcs11 module and storing the
+ * result into another.
+ */
+SECKEYPrivateKey *
+pk11_loadPrivKey(PK11SlotInfo *slot,SECKEYPrivateKey *privKey,
+ SECKEYPublicKey *pubKey, PRBool token, PRBool sensitive)
+{
+ CK_ATTRIBUTE privTemplate[] = {
+ /* class must be first */
+ { CKA_CLASS, NULL, 0 },
+ { CKA_KEY_TYPE, NULL, 0 },
+ /* these three must be next */
+ { CKA_TOKEN, NULL, 0 },
+ { CKA_PRIVATE, NULL, 0 },
+ { CKA_SENSITIVE, NULL, 0 },
+ { CKA_ID, NULL, 0 },
+#ifdef notdef
+ { CKA_LABEL, NULL, 0 },
+ { CKA_SUBJECT, NULL, 0 },
+#endif
+ /* RSA */
+ { CKA_MODULUS, NULL, 0 },
+ { CKA_PRIVATE_EXPONENT, NULL, 0 },
+ { CKA_PUBLIC_EXPONENT, NULL, 0 },
+ { CKA_PRIME_1, NULL, 0 },
+ { CKA_PRIME_2, NULL, 0 },
+ { CKA_EXPONENT_1, NULL, 0 },
+ { CKA_EXPONENT_2, NULL, 0 },
+ { CKA_COEFFICIENT, NULL, 0 },
+ };
+ CK_ATTRIBUTE *attrs = NULL, *ap;
+ int templateSize = sizeof(privTemplate)/sizeof(privTemplate[0]);
+ PRArenaPool *arena;
+ CK_OBJECT_HANDLE objectID;
+ int i, count = 0;
+ int extra_count = 0;
+ CK_RV crv;
+ SECStatus rv;
+
+ for (i=0; i < templateSize; i++) {
+ if (privTemplate[i].type == CKA_MODULUS) {
+ attrs= &privTemplate[i];
+ count = i;
+ break;
+ }
+ }
+ PORT_Assert(attrs != NULL);
+ if (attrs == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ ap = attrs;
+
+ switch (privKey->keyType) {
+ case rsaKey:
+ count = templateSize;
+ extra_count = templateSize - (attrs - privTemplate);
+ break;
+ case dsaKey:
+ ap->type = CKA_PRIME; ap++; count++; extra_count++;
+ ap->type = CKA_SUBPRIME; ap++; count++; extra_count++;
+ ap->type = CKA_BASE; ap++; count++; extra_count++;
+ ap->type = CKA_VALUE; ap++; count++; extra_count++;
+ break;
+ case dhKey:
+ ap->type = CKA_PRIME; ap++; count++; extra_count++;
+ ap->type = CKA_BASE; ap++; count++; extra_count++;
+ ap->type = CKA_VALUE; ap++; count++; extra_count++;
+ break;
+ default:
+ count = 0;
+ extra_count = 0;
+ break;
+ }
+
+ if (count == 0) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) return NULL;
+ /*
+ * read out the old attributes.
+ */
+ crv = PK11_GetAttributes(arena, privKey->pkcs11Slot, privKey->pkcs11ID,
+ privTemplate,count);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ PORT_FreeArena(arena, PR_TRUE);
+ return NULL;
+ }
+
+ /* Reset sensitive, token, and private */
+ *(CK_BBOOL *)(privTemplate[2].pValue) = token ? CK_TRUE : CK_FALSE;
+ *(CK_BBOOL *)(privTemplate[3].pValue) = token ? CK_TRUE : CK_FALSE;
+ *(CK_BBOOL *)(privTemplate[4].pValue) = sensitive ? CK_TRUE : CK_FALSE;
+
+ /* Not everyone can handle zero padded key values, give
+ * them the raw data as unsigned */
+ for (ap=attrs; extra_count; ap++, extra_count--) {
+ pk11_SignedToUnsigned(ap);
+ }
+
+ /* now Store the puppies */
+ rv = PK11_CreateNewObject(slot,privTemplate, count, token, &objectID);
+ PORT_FreeArena(arena, PR_TRUE);
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+
+ /* try loading the public key as a token object */
+ if (pubKey) {
+ PK11_ImportPublicKey(slot, pubKey, PR_TRUE);
+ if (pubKey->pkcs11Slot) {
+ PK11_FreeSlot(pubKey->pkcs11Slot);
+ pubKey->pkcs11Slot = NULL;
+ pubKey->pkcs11ID = CK_INVALID_KEY;
+ }
+ }
+
+ /* build new key structure */
+ return PK11_MakePrivKey(slot, privKey->keyType, (PRBool)!token,
+ objectID, privKey->wincx);
+}
+
+
+/*
+ * Use the token to Generate a key. keySize must be 'zero' for fixed key
+ * length algorithms. NOTE: this means we can never generate a DES2 key
+ * from this interface!
+ */
+SECKEYPrivateKey *
+PK11_GenerateKeyPair(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,
+ void *param, SECKEYPublicKey **pubKey, PRBool token,
+ PRBool sensitive, void *wincx)
+{
+ /* we have to use these native types because when we call PKCS 11 modules
+ * we have to make sure that we are using the correct sizes for all the
+ * parameters. */
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_ULONG modulusBits;
+ CK_BYTE publicExponent[4];
+ CK_ATTRIBUTE privTemplate[] = {
+ { CKA_SENSITIVE, NULL, 0},
+ { CKA_TOKEN, NULL, 0},
+ { CKA_PRIVATE, NULL, 0},
+ { CKA_DERIVE, NULL, 0},
+ { CKA_UNWRAP, NULL, 0},
+ { CKA_SIGN, NULL, 0},
+ { CKA_DECRYPT, NULL, 0},
+ };
+ CK_ATTRIBUTE rsaPubTemplate[] = {
+ { CKA_MODULUS_BITS, NULL, 0},
+ { CKA_PUBLIC_EXPONENT, NULL, 0},
+ { CKA_TOKEN, NULL, 0},
+ { CKA_DERIVE, NULL, 0},
+ { CKA_WRAP, NULL, 0},
+ { CKA_VERIFY, NULL, 0},
+ { CKA_VERIFY_RECOVER, NULL, 0},
+ { CKA_ENCRYPT, NULL, 0},
+ };
+ CK_ATTRIBUTE dsaPubTemplate[] = {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_SUBPRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_TOKEN, NULL, 0},
+ { CKA_DERIVE, NULL, 0},
+ { CKA_WRAP, NULL, 0},
+ { CKA_VERIFY, NULL, 0},
+ { CKA_VERIFY_RECOVER, NULL, 0},
+ { CKA_ENCRYPT, NULL, 0},
+ };
+ CK_ATTRIBUTE dhPubTemplate[] = {
+ { CKA_PRIME, NULL, 0 },
+ { CKA_BASE, NULL, 0 },
+ { CKA_TOKEN, NULL, 0},
+ { CKA_DERIVE, NULL, 0},
+ { CKA_WRAP, NULL, 0},
+ { CKA_VERIFY, NULL, 0},
+ { CKA_VERIFY_RECOVER, NULL, 0},
+ { CKA_ENCRYPT, NULL, 0},
+ };
+
+ int dsaPubCount = sizeof(dsaPubTemplate)/sizeof(dsaPubTemplate[0]);
+ /*CK_ULONG key_size = 0;*/
+ CK_ATTRIBUTE *pubTemplate;
+ int privCount = sizeof(privTemplate)/sizeof(privTemplate[0]);
+ int rsaPubCount = sizeof(rsaPubTemplate)/sizeof(rsaPubTemplate[0]);
+ int dhPubCount = sizeof(dhPubTemplate)/sizeof(dhPubTemplate[0]);
+ int pubCount = 0;
+ PK11RSAGenParams *rsaParams;
+ PQGParams *dsaParams;
+ DHParams * dhParams;
+ CK_MECHANISM mechanism;
+ CK_MECHANISM test_mech;
+ CK_SESSION_HANDLE session_handle;
+ CK_RV crv;
+ CK_OBJECT_HANDLE privID,pubID;
+ SECKEYPrivateKey *privKey;
+ KeyType keyType;
+ PRBool restore;
+ int peCount,i;
+ CK_ATTRIBUTE *attrs;
+ CK_ATTRIBUTE *privattrs;
+ SECItem *pubKeyIndex;
+ CK_ATTRIBUTE setTemplate;
+ SECStatus rv;
+ CK_MECHANISM_INFO mechanism_info;
+ CK_OBJECT_CLASS keyClass;
+ SECItem *cka_id;
+ PRBool haslock = PR_FALSE;
+ PRBool pubIsToken = PR_FALSE;
+
+ PORT_Assert(slot != NULL);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE);
+ return NULL;
+ }
+
+ /* if our slot really doesn't do this mechanism, Generate the key
+ * in our internal token and write it out */
+ if (!PK11_DoesMechanism(slot,type)) {
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ /* don't loop forever looking for a slot */
+ if (slot == int_slot) {
+ PK11_FreeSlot(int_slot);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+
+ /* if there isn't a suitable slot, then we can't do the keygen */
+ if (int_slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+ }
+
+ /* generate the temporary key to load */
+ privKey = PK11_GenerateKeyPair(int_slot,type, param, pubKey, PR_FALSE,
+ PR_FALSE, wincx);
+ PK11_FreeSlot(int_slot);
+
+ /* if successful, load the temp key into the new token */
+ if (privKey != NULL) {
+ SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey,
+ *pubKey,token,sensitive);
+ SECKEY_DestroyPrivateKey(privKey);
+ if (newPrivKey == NULL) {
+ SECKEY_DestroyPublicKey(*pubKey);
+ *pubKey = NULL;
+ }
+ return newPrivKey;
+ }
+ return NULL;
+ }
+
+
+ mechanism.mechanism = type;
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ test_mech.pParameter = NULL;
+ test_mech.ulParameterLen = 0;
+
+ /* set up the private key template */
+ privattrs = privTemplate;
+ PK11_SETATTRS(privattrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+ PK11_SETATTRS(privattrs, CKA_TOKEN, token ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+ PK11_SETATTRS(privattrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+
+ /* set up the mechanism specific info */
+ switch (type) {
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ rsaParams = (PK11RSAGenParams *)param;
+ modulusBits = rsaParams->keySizeInBits;
+ peCount = 0;
+
+ /* convert pe to a PKCS #11 string */
+ for (i=0; i < 4; i++) {
+ if (peCount || (rsaParams->pe &
+ ((unsigned long)0xff000000L >> (i*8)))) {
+ publicExponent[peCount] =
+ (CK_BYTE)((rsaParams->pe >> (3-i)*8) & 0xff);
+ peCount++;
+ }
+ }
+ PORT_Assert(peCount != 0);
+ attrs = rsaPubTemplate;
+ PK11_SETATTRS(attrs, CKA_MODULUS_BITS,
+ &modulusBits, sizeof(modulusBits)); attrs++;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT,
+ publicExponent, peCount);attrs++;
+ pubTemplate = rsaPubTemplate;
+ pubCount = rsaPubCount;
+ keyType = rsaKey;
+ test_mech.mechanism = CKM_RSA_PKCS;
+ break;
+ case CKM_DSA_KEY_PAIR_GEN:
+ dsaParams = (PQGParams *)param;
+ attrs = dsaPubTemplate;
+ PK11_SETATTRS(attrs, CKA_PRIME, dsaParams->prime.data,
+ dsaParams->prime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_SUBPRIME, dsaParams->subPrime.data,
+ dsaParams->subPrime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, dsaParams->base.data,
+ dsaParams->base.len); attrs++;
+ pubTemplate = dsaPubTemplate;
+ pubCount = dsaPubCount;
+ keyType = dsaKey;
+ test_mech.mechanism = CKM_DSA;
+ break;
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ dhParams = (DHParams *)param;
+ attrs = dhPubTemplate;
+ PK11_SETATTRS(attrs, CKA_PRIME, dhParams->prime.data,
+ dhParams->prime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, dhParams->base.data,
+ dhParams->base.len); attrs++;
+ pubTemplate = dhPubTemplate;
+ pubCount = dhPubCount;
+ keyType = dhKey;
+ test_mech.mechanism = CKM_DH_PKCS_DERIVE;
+ break;
+ default:
+ PORT_SetError( SEC_ERROR_BAD_KEY );
+ return NULL;
+ }
+
+ /* now query the slot to find out how "good" a key we can generate */
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ test_mech.mechanism,&mechanism_info);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if ((crv != CKR_OK) || (mechanism_info.flags == 0)) {
+ /* must be old module... guess what it should be... */
+ switch (test_mech.mechanism) {
+ case CKM_RSA_PKCS:
+ mechanism_info.flags = (CKF_SIGN | CKF_DECRYPT |
+ CKF_WRAP | CKF_VERIFY_RECOVER | CKF_ENCRYPT | CKF_WRAP);;
+ break;
+ case CKM_DSA:
+ mechanism_info.flags = CKF_SIGN | CKF_VERIFY;
+ break;
+ case CKM_DH_PKCS_DERIVE:
+ mechanism_info.flags = CKF_DERIVE;
+ break;
+ default:
+ break;
+ }
+ }
+ /* set the public key objects */
+ PK11_SETATTRS(attrs, CKA_TOKEN, token ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(attrs, CKA_DERIVE,
+ mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(attrs, CKA_WRAP,
+ mechanism_info.flags & CKF_WRAP ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(attrs, CKA_VERIFY,
+ mechanism_info.flags & CKF_VERIFY ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(attrs, CKA_VERIFY_RECOVER,
+ mechanism_info.flags & CKF_VERIFY_RECOVER ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(attrs, CKA_ENCRYPT,
+ mechanism_info.flags & CKF_ENCRYPT? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(privattrs, CKA_DERIVE,
+ mechanism_info.flags & CKF_DERIVE ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+ PK11_SETATTRS(privattrs, CKA_UNWRAP,
+ mechanism_info.flags & CKF_UNWRAP ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+ PK11_SETATTRS(privattrs, CKA_SIGN,
+ mechanism_info.flags & CKF_SIGN ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+ PK11_SETATTRS(privattrs, CKA_DECRYPT,
+ mechanism_info.flags & CKF_DECRYPT ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL)); privattrs++;
+
+ if (token) {
+ session_handle = PK11_GetRWSession(slot);
+ haslock = PK11_RWSessionHasLock(slot,session_handle);
+ restore = PR_TRUE;
+ } else {
+ PK11_EnterSlotMonitor(slot); /* gross!! */
+ session_handle = slot->session;
+ restore = PR_FALSE;
+ haslock = PR_TRUE;
+ }
+
+ crv = PK11_GETTAB(slot)->C_GenerateKeyPair(session_handle, &mechanism,
+ pubTemplate,pubCount,privTemplate,privCount,&pubID,&privID);
+
+
+ if (crv != CKR_OK) {
+ if (restore) {
+ PK11_RestoreROSession(slot,session_handle);
+ } else PK11_ExitSlotMonitor(slot);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+ /* This locking code is dangerous and needs to be more thought
+ * out... the real problem is that we're holding the mutex open this long
+ */
+ if (haslock) { PK11_ExitSlotMonitor(slot); }
+
+ /* swap around the ID's for older PKCS #11 modules */
+ keyClass = PK11_ReadULongAttribute(slot,pubID,CKA_CLASS);
+ if (keyClass != CKO_PUBLIC_KEY) {
+ CK_OBJECT_HANDLE tmp = pubID;
+ pubID = privID;
+ privID = tmp;
+ }
+
+ *pubKey = PK11_ExtractPublicKey(slot, keyType, pubID);
+ if (*pubKey == NULL) {
+ if (restore) {
+ /* we may have to restore the mutex so it get's exited properly
+ * in RestoreROSession */
+ if (haslock) PK11_EnterSlotMonitor(slot);
+ PK11_RestoreROSession(slot,session_handle);
+ }
+ PK11_DestroyObject(slot,pubID);
+ PK11_DestroyObject(slot,privID);
+ return NULL;
+ }
+
+ /* set the ID to the public key so we can find it again */
+ pubKeyIndex = NULL;
+ switch (type) {
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ pubKeyIndex = &(*pubKey)->u.rsa.modulus;
+ break;
+ case CKM_DSA_KEY_PAIR_GEN:
+ pubKeyIndex = &(*pubKey)->u.dsa.publicValue;
+ break;
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ pubKeyIndex = &(*pubKey)->u.dh.publicValue;
+ break;
+ }
+ PORT_Assert(pubKeyIndex != NULL);
+
+ cka_id = PK11_MakeIDFromPubKey(pubKeyIndex);
+ pubIsToken = (PRBool)PK11_HasAttributeSet(slot,pubID, CKA_TOKEN);
+
+ PK11_SETATTRS(&setTemplate, CKA_ID, cka_id->data, cka_id->len);
+
+ if (haslock) { PK11_EnterSlotMonitor(slot); }
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, privID,
+ &setTemplate, 1);
+
+ if (crv == CKR_OK && pubIsToken) {
+ crv = PK11_GETTAB(slot)->C_SetAttributeValue(session_handle, pubID,
+ &setTemplate, 1);
+ }
+
+
+ if (restore) {
+ PK11_RestoreROSession(slot,session_handle);
+ } else {
+ PK11_ExitSlotMonitor(slot);
+ }
+ SECITEM_FreeItem(cka_id,PR_TRUE);
+
+
+ if (crv != CKR_OK) {
+ PK11_DestroyObject(slot,pubID);
+ PK11_DestroyObject(slot,privID);
+ PORT_SetError( PK11_MapError(crv) );
+ *pubKey = NULL;
+ return NULL;
+ }
+
+ privKey = PK11_MakePrivKey(slot,keyType,(PRBool)!token,privID,wincx);
+ if (privKey == NULL) {
+ SECKEY_DestroyPublicKey(*pubKey);
+ PK11_DestroyObject(slot,privID);
+ *pubKey = NULL;
+ return NULL; /* due to pairwise consistency check */
+ }
+
+ /* Perform PKCS #11 pairwise consistency check. */
+ rv = pk11_PairwiseConsistencyCheck( *pubKey, privKey, &test_mech, wincx );
+ if( rv != SECSuccess ) {
+ SECKEY_DestroyPublicKey( *pubKey );
+ SECKEY_DestroyPrivateKey( privKey );
+ *pubKey = NULL;
+ privKey = NULL;
+ return NULL;
+ }
+
+ return privKey;
+}
+
+/*
+ * This function does a straight public key wrap (which only RSA can do).
+ * Use PK11_PubGenKey and PK11_WrapSymKey to implement the FORTEZZA and
+ * Diffie-Hellman Ciphers. */
+SECStatus
+PK11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,
+ PK11SymKey *symKey, SECItem *wrappedKey)
+{
+ PK11SlotInfo *slot;
+ CK_ULONG len = wrappedKey->len;
+ PK11SymKey *newKey = NULL;
+ CK_OBJECT_HANDLE id;
+ CK_MECHANISM mechanism;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ newKey = pk11_ForceSlot(symKey,type,CKA_ENCRYPT);
+ if (newKey != NULL) {
+ symKey = newKey;
+ }
+
+ if ((symKey == NULL) || (symKey->slot == NULL)) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return SECFailure;
+ }
+
+ slot = symKey->slot;
+ mechanism.mechanism = pk11_mapWrapKeyType(pubKey->keyType);
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+
+ id = PK11_ImportPublicKey(slot,pubKey,PR_FALSE);
+
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_WrapKey(session,&mechanism,
+ id,symKey->objectID,wrappedKey->data,&len);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ if (newKey) {
+ PK11_FreeSymKey(newKey);
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ wrappedKey->len = len;
+ return SECSuccess;
+}
+
+/*
+ * this little function uses the Encrypt function to wrap a key, just in
+ * case we have problems with the wrap implementation for a token.
+ */
+static SECStatus
+pk11_HandWrap(PK11SymKey *wrappingKey, SECItem *param, CK_MECHANISM_TYPE type,
+ SECItem *inKey, SECItem *outKey)
+{
+ PK11SlotInfo *slot;
+ CK_ULONG len;
+ SECItem *data;
+ CK_MECHANISM mech;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ slot = wrappingKey->slot;
+ /* use NULL IV's for wrapping */
+ mech.mechanism = type;
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ } else {
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+ }
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_EncryptInit(session,&mech,
+ wrappingKey->objectID);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+
+ /* keys are almost always aligned, but if we get this far,
+ * we've gone above and beyond anyway... */
+ data = PK11_BlockData(inKey,PK11_GetBlockSize(type,param));
+ if (data == NULL) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ len = outKey->len;
+ crv = PK11_GETTAB(slot)->C_Encrypt(session,data->data,data->len,
+ outKey->data, &len);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ SECITEM_FreeItem(data,PR_TRUE);
+ outKey->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * This function does a symetric based wrap.
+ */
+SECStatus
+PK11_WrapSymKey(CK_MECHANISM_TYPE type, SECItem *param,
+ PK11SymKey *wrappingKey, PK11SymKey *symKey, SECItem *wrappedKey)
+{
+ PK11SlotInfo *slot;
+ CK_ULONG len = wrappedKey->len;
+ PK11SymKey *newKey = NULL;
+ SECItem *param_save = NULL;
+ CK_MECHANISM mechanism;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+ SECStatus rv;
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ /* Force symKey and wrappingKey into the same slot */
+ if ((wrappingKey->slot == NULL) || (symKey->slot != wrappingKey->slot)) {
+ /* first try copying the wrapping Key to the symKey slot */
+ if (symKey->slot && PK11_DoesMechanism(symKey->slot,type)) {
+ newKey = pk11_CopyToSlot(symKey->slot,type,CKA_WRAP,wrappingKey);
+ }
+ /* Nope, try it the other way */
+ if (newKey == NULL) {
+ if (wrappingKey->slot) {
+ newKey = pk11_CopyToSlot(wrappingKey->slot,
+ symKey->type, CKA_ENCRYPT, symKey);
+ }
+ /* just not playing... one last thing, can we get symKey's data?
+ * If it's possible, we it should already be in the
+ * symKey->data.data pointer because pk11_CopyToSlot would have
+ * tried to put it there. */
+ if (newKey == NULL) {
+ /* Can't get symKey's data: Game Over */
+ if (symKey->data.data == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return SECFailure;
+ }
+ if (param == NULL) {
+ param_save = param = PK11_ParamFromIV(type,NULL);
+ }
+ rv = pk11_HandWrap(wrappingKey, param, type,
+ &symKey->data,wrappedKey);
+ if (param_save) SECITEM_FreeItem(param_save,PR_TRUE);
+ return rv;
+ }
+ /* we successfully moved the sym Key */
+ symKey = newKey;
+ } else {
+ /* we successfully moved the wrapping Key */
+ wrappingKey = newKey;
+ }
+ }
+
+ /* at this point both keys are in the same token */
+ slot = wrappingKey->slot;
+ mechanism.mechanism = type;
+ /* use NULL IV's for wrapping */
+ if (param == NULL) {
+ param_save = param = PK11_ParamFromIV(type,NULL);
+ }
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+
+ len = wrappedKey->len;
+
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_WrapKey(session, &mechanism,
+ wrappingKey->objectID, symKey->objectID,
+ wrappedKey->data, &len);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ rv = SECSuccess;
+ if (crv != CKR_OK) {
+ /* can't wrap it? try hand wrapping it... */
+ do {
+ if (symKey->data.data == NULL) {
+ rv = PK11_ExtractKeyValue(symKey);
+ if (rv != SECSuccess) break;
+ }
+ rv = pk11_HandWrap(wrappingKey, param, type, &symKey->data,
+ wrappedKey);
+ } while (PR_FALSE);
+ } else {
+ wrappedKey->len = len;
+ }
+ if (newKey) PK11_FreeSymKey(newKey);
+ if (param_save) SECITEM_FreeItem(param_save,PR_TRUE);
+ return rv;
+}
+
+/*
+ * This Generates a new key based on a symetricKey
+ */
+PK11SymKey *
+PK11_Derive( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive, SECItem *param,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize)
+{
+ return pk11_DeriveWithTemplate(baseKey, derive, param, target, operation,
+ keySize, NULL, 0);
+}
+
+#define MAX_TEMPL_ATTRS 16 /* maximum attributes in template */
+
+/* This mask includes all CK_FLAGs with an equivalent CKA_ attribute. */
+#define CKF_KEY_OPERATION_FLAGS 0x000e7b00UL
+
+static unsigned int
+pk11_FlagsToAttributes(CK_FLAGS flags, CK_ATTRIBUTE *attrs, CK_BBOOL *ckTrue)
+{
+
+ const static CK_ATTRIBUTE_TYPE attrTypes[12] = {
+ CKA_ENCRYPT, CKA_DECRYPT, 0 /* DIGEST */, CKA_SIGN,
+ CKA_SIGN_RECOVER, CKA_VERIFY, CKA_VERIFY_RECOVER, 0 /* GEN */,
+ 0 /* GEN PAIR */, CKA_WRAP, CKA_UNWRAP, CKA_DERIVE
+ };
+
+ const CK_ATTRIBUTE_TYPE *pType = attrTypes;
+ CK_ATTRIBUTE *attr = attrs;
+ CK_FLAGS test = CKF_ENCRYPT;
+
+
+ PR_ASSERT(!(flags & ~CKF_KEY_OPERATION_FLAGS));
+ flags &= CKF_KEY_OPERATION_FLAGS;
+
+ for (; flags && test <= CKF_DERIVE; test <<= 1, ++pType) {
+ if (test & flags) {
+ flags ^= test;
+ PK11_SETATTRS(attr, *pType, ckTrue, sizeof *ckTrue);
+ ++attr;
+ }
+ }
+ return (attr - attrs);
+}
+
+PK11SymKey *
+PK11_DeriveWithFlags( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags)
+{
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ unsigned int templateCount;
+
+ templateCount = pk11_FlagsToAttributes(flags, keyTemplate, &ckTrue);
+ return pk11_DeriveWithTemplate(baseKey, derive, param, target, operation,
+ keySize, keyTemplate, templateCount);
+}
+
+static PRBool
+pk11_FindAttrInTemplate(CK_ATTRIBUTE * attr,
+ unsigned int numAttrs,
+ CK_ATTRIBUTE_TYPE target)
+{
+ for (; numAttrs > 0; ++attr, --numAttrs) {
+ if (attr->type == target)
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+static PK11SymKey *
+pk11_DeriveWithTemplate( PK11SymKey *baseKey, CK_MECHANISM_TYPE derive,
+ SECItem *param, CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_ATTRIBUTE *userAttr, unsigned int numAttrs)
+{
+ PK11SlotInfo * slot = baseKey->slot;
+ PK11SymKey * symKey;
+ PK11SymKey * newBaseKey = NULL;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG valueLen = 0;
+ CK_MECHANISM mechanism;
+ CK_RV crv;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE * attrs = keyTemplate;
+ unsigned int templateCount;
+
+ if (numAttrs > MAX_TEMPL_ATTRS) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* first copy caller attributes in. */
+ for (templateCount = 0; templateCount < numAttrs; ++templateCount) {
+ *attrs++ = *userAttr++;
+ }
+
+ /* We only add the following attributes to the template if the caller
+ ** didn't already supply them.
+ */
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) {
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass);
+ attrs++;
+ }
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) {
+ keyType = PK11_GetKeyType(target, keySize);
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType );
+ attrs++;
+ }
+ if (keySize > 0 &&
+ !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) {
+ valueLen = (CK_ULONG)keySize;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen);
+ attrs++;
+ }
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) {
+ PK11_SETATTRS(attrs, operation, &cktrue, sizeof cktrue); attrs++;
+ }
+
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= MAX_TEMPL_ATTRS);
+
+ /* move the key to a slot that can do the function */
+ if (!PK11_DoesMechanism(slot,derive)) {
+ /* get a new base key & slot */
+ PK11SlotInfo *newSlot = PK11_GetBestSlot(derive, baseKey->cx);
+
+ if (newSlot == NULL) return NULL;
+
+ newBaseKey = pk11_CopyToSlot (newSlot, derive, CKA_DERIVE,
+ baseKey);
+ PK11_FreeSlot(newSlot);
+ if (newBaseKey == NULL) return NULL;
+ baseKey = newBaseKey;
+ slot = baseKey->slot;
+ }
+
+
+ /* get our key Structure */
+ symKey = PK11_CreateSymKey(slot,target,baseKey->cx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->size = keySize;
+
+ mechanism.mechanism = derive;
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+ symKey->origin=PK11_OriginDerive;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism,
+ baseKey->objectID, keyTemplate, templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+
+ if (newBaseKey) PK11_FreeSymKey(newBaseKey);
+ if (crv != CKR_OK) {
+ PK11_FreeSymKey(symKey);
+ return NULL;
+ }
+ return symKey;
+}
+
+/* build a public KEA key from the public value */
+SECKEYPublicKey *
+PK11_MakeKEAPubKey(unsigned char *keyData,int length)
+{
+ SECKEYPublicKey *pubk;
+ SECItem pkData;
+ SECStatus rv;
+ PRArenaPool *arena;
+
+ pkData.data = keyData;
+ pkData.len = length;
+
+ arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey));
+ if (pubk == NULL) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubk->arena = arena;
+ pubk->pkcs11Slot = 0;
+ pubk->pkcs11ID = CK_INVALID_KEY;
+ pubk->keyType = fortezzaKey;
+ rv = SECITEM_CopyItem(arena, &pubk->u.fortezza.KEAKey, &pkData);
+ if (rv != SECSuccess) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+ return pubk;
+}
+
+
+/*
+ * This Generates a wrapping key based on a privateKey, publicKey, and two
+ * random numbers. For Mail usage RandomB should be NULL. In the Sender's
+ * case RandomA is generate, outherwize it is passed.
+ */
+static unsigned char *rb_email = NULL;
+
+PK11SymKey *
+PK11_PubDerive(SECKEYPrivateKey *privKey, SECKEYPublicKey *pubKey,
+ PRBool isSender, SECItem *randomA, SECItem *randomB,
+ CK_MECHANISM_TYPE derive, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE_TYPE operation, int keySize,void *wincx)
+{
+ PK11SlotInfo *slot = privKey->pkcs11Slot;
+ CK_MECHANISM mechanism;
+ PK11SymKey *symKey;
+ CK_RV crv;
+
+
+ if (rb_email == NULL) {
+ rb_email = PORT_ZAlloc(128);
+ if (rb_email == NULL) {
+ return NULL;
+ }
+ rb_email[127] = 1;
+ }
+
+ /* get our key Structure */
+ symKey = PK11_CreateSymKey(slot,target,wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->origin = PK11_OriginDerive;
+
+ switch (privKey->keyType) {
+ case rsaKey:
+ case nullKey:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ /* case keaKey: */
+ case dsaKey:
+ case fortezzaKey:
+ {
+ CK_KEA_DERIVE_PARAMS param;
+ param.isSender = (CK_BBOOL) isSender;
+ param.ulRandomLen = randomA->len;
+ param.pRandomA = randomA->data;
+ param.pRandomB = rb_email;
+ if (randomB)
+ param.pRandomB = randomB->data;
+ if (pubKey->keyType == fortezzaKey) {
+ param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len;
+ param.pPublicData = pubKey->u.fortezza.KEAKey.data;
+ } else {
+ /* assert type == keaKey */
+ /* XXX change to match key key types */
+ param.ulPublicDataLen = pubKey->u.fortezza.KEAKey.len;
+ param.pPublicData = pubKey->u.fortezza.KEAKey.data;
+ }
+
+ mechanism.mechanism = derive;
+ mechanism.pParameter = &param;
+ mechanism.ulParameterLen = sizeof(param);
+
+ /* get a new symKey structure */
+ pk11_EnterKeyMonitor(symKey);
+ crv=PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism,
+ privKey->pkcs11ID, NULL, 0, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ if (crv == CKR_OK) return symKey;
+ PORT_SetError( PK11_MapError(crv) );
+ }
+ break;
+ case dhKey:
+ {
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG key_size = 0;
+ CK_ATTRIBUTE keyTemplate[4];
+ int templateCount;
+ CK_ATTRIBUTE *attrs = keyTemplate;
+
+ if (pubKey->keyType != dhKey) {
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ break;
+ }
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass));
+ attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType));
+ attrs++;
+ PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &key_size, sizeof(key_size));
+ attrs++;
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE));
+
+ keyType = PK11_GetKeyType(target,keySize);
+ key_size = keySize;
+ symKey->size = keySize;
+ if (key_size == 0) templateCount--;
+
+ mechanism.mechanism = derive;
+
+ /* we can undefine these when we define diffie-helman keys */
+ mechanism.pParameter = pubKey->u.dh.publicValue.data;
+ mechanism.ulParameterLen = pubKey->u.dh.publicValue.len;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_DeriveKey(symKey->session, &mechanism,
+ privKey->pkcs11ID, keyTemplate, templateCount, &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ if (crv == CKR_OK) return symKey;
+ PORT_SetError( PK11_MapError(crv) );
+ }
+ break;
+ }
+
+ PK11_FreeSymKey(symKey);
+ return NULL;
+}
+
+/*
+ * this little function uses the Decrypt function to unwrap a key, just in
+ * case we are having problem with unwrap. NOTE: The key size may
+ * not be preserved properly for some algorithms!
+ */
+static PK11SymKey *
+pk11_HandUnwrap(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey,
+ CK_MECHANISM *mech, SECItem *inKey, CK_MECHANISM_TYPE target,
+ CK_ATTRIBUTE *keyTemplate, unsigned int templateCount,
+ int key_size, void * wincx)
+{
+ CK_ULONG len;
+ SECItem outKey;
+ PK11SymKey *symKey;
+ CK_RV crv;
+
+ /* keys are almost always aligned, but if we get this far,
+ * we've gone above and beyond anyway... */
+ outKey.data = (unsigned char*)PORT_Alloc(inKey->len);
+ if (outKey.data == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MEMORY );
+ return NULL;
+ }
+ len = inKey->len;
+
+
+ /* use NULL IV's for wrapping */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DecryptInit(slot->session,mech,wrappingKey);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_Free(outKey.data);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+
+ crv = PK11_GETTAB(slot)->C_Decrypt(slot->session,inKey->data,inKey->len,
+ outKey.data, &len);
+ PK11_ExitSlotMonitor(slot);
+ outKey.len = (key_size == 0) ? len : key_size;
+ if (crv != CKR_OK) {
+ PORT_Free(outKey.data);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+
+ if (PK11_DoesMechanism(slot,target)) {
+ symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap,
+ keyTemplate, templateCount,
+ &outKey, wincx);
+ } else {
+ slot = PK11_GetBestSlot(target,wincx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ PORT_Free(outKey.data);
+ return NULL;
+ }
+ symKey = pk11_ImportSymKeyWithTempl(slot, target, PK11_OriginUnwrap,
+ keyTemplate, templateCount,
+ &outKey, wincx);
+ PK11_FreeSlot(slot);
+ }
+ PORT_Free(outKey.data);
+ return symKey;
+}
+
+/*
+ * The wrap/unwrap function is pretty much the same for private and
+ * public keys. It's just getting the Object ID and slot right. This is
+ * the combined unwrap function.
+ */
+static PK11SymKey *
+pk11_AnyUnwrapKey(PK11SlotInfo *slot, CK_OBJECT_HANDLE wrappingKey,
+ CK_MECHANISM_TYPE wrapType, SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize,
+ void *wincx, CK_ATTRIBUTE *userAttr, unsigned int numAttrs)
+{
+ PK11SymKey * symKey;
+ SECItem * param_free = NULL;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_ULONG valueLen = 0;
+ CK_MECHANISM mechanism;
+ CK_RV crv;
+ CK_MECHANISM_INFO mechanism_info;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ CK_ATTRIBUTE * attrs = keyTemplate;
+ unsigned int templateCount;
+
+ if (numAttrs > MAX_TEMPL_ATTRS) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return NULL;
+ }
+ /* first copy caller attributes in. */
+ for (templateCount = 0; templateCount < numAttrs; ++templateCount) {
+ *attrs++ = *userAttr++;
+ }
+
+ /* We only add the following attributes to the template if the caller
+ ** didn't already supply them.
+ */
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_CLASS)) {
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof keyClass);
+ attrs++;
+ }
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_KEY_TYPE)) {
+ keyType = PK11_GetKeyType(target, keySize);
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof keyType );
+ attrs++;
+ }
+ if (keySize > 0 &&
+ !pk11_FindAttrInTemplate(keyTemplate, numAttrs, CKA_VALUE_LEN)) {
+ valueLen = (CK_ULONG)keySize;
+ PK11_SETATTRS(attrs, CKA_VALUE_LEN, &valueLen, sizeof valueLen);
+ attrs++;
+ }
+ if (!pk11_FindAttrInTemplate(keyTemplate, numAttrs, operation)) {
+ PK11_SETATTRS(attrs, operation, &cktrue, 1); attrs++;
+ }
+
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= sizeof(keyTemplate)/sizeof(CK_ATTRIBUTE));
+
+
+ /* find out if we can do wrap directly. Because the RSA case if *very*
+ * common, cache the results for it. */
+ if ((wrapType == CKM_RSA_PKCS) && (slot->hasRSAInfo)) {
+ mechanism_info.flags = slot->RSAInfoFlags;
+ } else {
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,wrapType,
+ &mechanism_info);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ mechanism_info.flags = 0;
+ }
+ if (wrapType == CKM_RSA_PKCS) {
+ slot->RSAInfoFlags = mechanism_info.flags;
+ slot->hasRSAInfo = PR_TRUE;
+ }
+ }
+
+ /* initialize the mechanism structure */
+ mechanism.mechanism = wrapType;
+ /* use NULL IV's for wrapping */
+ if (param == NULL) param = param_free = PK11_ParamFromIV(wrapType,NULL);
+ if (param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+
+ if ((mechanism_info.flags & CKF_UNWRAP) == 0) {
+ symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey,
+ target, keyTemplate, templateCount, keySize,
+ wincx);
+ if (symKey) return symKey;
+ /* fall through, maybe they incorrectly set CKF_UNWRAP */
+ }
+
+ /* get our key Structure */
+ symKey = PK11_CreateSymKey(slot,target,wincx);
+ if (symKey == NULL) {
+ return NULL;
+ }
+
+ symKey->size = keySize;
+ symKey->origin = PK11_OriginUnwrap;
+
+ pk11_EnterKeyMonitor(symKey);
+ crv = PK11_GETTAB(slot)->C_UnwrapKey(symKey->session,&mechanism,wrappingKey,
+ wrappedKey->data, wrappedKey->len, keyTemplate, templateCount,
+ &symKey->objectID);
+ pk11_ExitKeyMonitor(symKey);
+ if (param_free) SECITEM_FreeItem(param_free,PR_TRUE);
+ if (crv != CKR_OK) {
+ /* try hand Unwrapping */
+ PK11_FreeSymKey(symKey);
+ symKey = pk11_HandUnwrap(slot, wrappingKey, &mechanism, wrappedKey,
+ target, keyTemplate, templateCount, keySize,
+ wincx);
+ }
+
+ return symKey;
+}
+
+/* use a symetric key to unwrap another symetric key */
+PK11SymKey *
+PK11_UnwrapSymKey( PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize)
+{
+ return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
+ wrapType, param, wrappedKey, target, operation, keySize,
+ wrappingKey->cx, NULL, 0);
+}
+
+/* use a symetric key to unwrap another symetric key */
+PK11SymKey *
+PK11_UnwrapSymKeyWithFlags(PK11SymKey *wrappingKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation,
+ int keySize, CK_FLAGS flags)
+{
+ CK_BBOOL ckTrue = CK_TRUE;
+ CK_ATTRIBUTE keyTemplate[MAX_TEMPL_ATTRS];
+ unsigned int templateCount;
+
+ templateCount = pk11_FlagsToAttributes(flags, keyTemplate, &ckTrue);
+ return pk11_AnyUnwrapKey(wrappingKey->slot, wrappingKey->objectID,
+ wrapType, param, wrappedKey, target, operation, keySize,
+ wrappingKey->cx, keyTemplate, templateCount);
+}
+
+
+/* unwrap a symetric key with a private key. */
+PK11SymKey *
+PK11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey,
+ CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize)
+{
+ CK_MECHANISM_TYPE wrapType = pk11_mapWrapKeyType(wrappingKey->keyType);
+
+ PK11_HandlePasswordCheck(wrappingKey->pkcs11Slot,wrappingKey->wincx);
+
+ return pk11_AnyUnwrapKey(wrappingKey->pkcs11Slot, wrappingKey->pkcs11ID,
+ wrapType, NULL, wrappedKey, target, operation, keySize,
+ wrappingKey->wincx, NULL, 0);
+}
+
+/*
+ * Recover the Signed data. We need this because our old verify can't
+ * figure out which hash algorithm to use until we decryptted this.
+ */
+SECStatus
+PK11_VerifyRecover(SECKEYPublicKey *key,
+ SECItem *sig, SECItem *dsig, void *wincx)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_OBJECT_HANDLE id = key->pkcs11ID;
+ CK_MECHANISM mech = {0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_ULONG len;
+ CK_RV crv;
+
+ mech.mechanism = pk11_mapSignKeyType(key->keyType);
+
+ if (slot == NULL) {
+ slot = PK11_GetBestSlot(mech.mechanism,wincx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return SECFailure;
+ }
+ id = PK11_ImportPublicKey(slot,key,PR_FALSE);
+ }
+
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_VerifyRecoverInit(session,&mech,id);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ len = dsig->len;
+ crv = PK11_GETTAB(slot)->C_VerifyRecover(session,sig->data,
+ sig->len, dsig->data, &len);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ dsig->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * verify a signature from its hash.
+ */
+SECStatus
+PK11_Verify(SECKEYPublicKey *key, SECItem *sig, SECItem *hash, void *wincx)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ PK11SlotInfo *tmpslot = key->pkcs11Slot;
+ CK_OBJECT_HANDLE id = key->pkcs11ID;
+ CK_MECHANISM mech = {0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ mech.mechanism = pk11_mapSignKeyType(key->keyType);
+
+ if (slot == NULL) {
+ if (mech.mechanism == CKM_DSA) {
+ slot = PK11_GetInternalSlot(); /* use internal slot for
+ DSA verify */
+ } else {
+ slot = PK11_GetBestSlot(mech.mechanism,wincx);
+ };
+
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return SECFailure;
+ }
+ id = PK11_ImportPublicKey(slot,key,PR_FALSE);
+
+ }
+
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_VerifyInit(session,&mech,id);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_Verify(session,hash->data,
+ hash->len, sig->data, sig->len);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * sign a hash. The algorithm is determined by the key.
+ */
+SECStatus
+PK11_Sign(SECKEYPrivateKey *key, SECItem *sig, SECItem *hash)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_MECHANISM mech = {0, NULL, 0 };
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_ULONG len;
+ CK_RV crv;
+
+ mech.mechanism = pk11_mapSignKeyType(key->keyType);
+
+ PK11_HandlePasswordCheck(slot, key->wincx);
+
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SignInit(session,&mech,key->pkcs11ID);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ len = sig->len;
+ crv = PK11_GETTAB(slot)->C_Sign(session,hash->data,
+ hash->len, sig->data, &len);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ sig->len = len;
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * Now SSL 2.0 uses raw RSA stuff. These next to functions *must* use
+ * RSA keys, or they'll fail. We do the checks up front. If anyone comes
+ * up with a meaning for rawdecrypt for any other public key operation,
+ * then we need to move this check into some of PK11_PubDecrypt callers,
+ * (namely SSL 2.0).
+ */
+SECStatus
+PK11_PubDecryptRaw(SECKEYPrivateKey *key, unsigned char *data,
+ unsigned *outLen, unsigned int maxLen, unsigned char *enc,
+ unsigned encLen)
+{
+ PK11SlotInfo *slot = key->pkcs11Slot;
+ CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 };
+ CK_ULONG out = maxLen;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ if (key->keyType != rsaKey) {
+ PORT_SetError( SEC_ERROR_INVALID_KEY );
+ return SECFailure;
+ }
+
+ /* Why do we do a PK11_handle check here? for simple
+ * decryption? .. because the user may have asked for 'ask always'
+ * and this is a private key operation. In practice, thought, it's mute
+ * since only servers wind up using this function */
+ PK11_HandlePasswordCheck(slot, key->wincx);
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DecryptInit(session,&mech,key->pkcs11ID);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_Decrypt(session,enc, encLen,
+ data, &out);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ *outLen = out;
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* The encrypt version of the above function */
+SECStatus
+PK11_PubEncryptRaw(SECKEYPublicKey *key, unsigned char *enc,
+ unsigned char *data, unsigned dataLen, void *wincx)
+{
+ PK11SlotInfo *slot;
+ CK_MECHANISM mech = {CKM_RSA_X_509, NULL, 0 };
+ CK_OBJECT_HANDLE id;
+ CK_ULONG out = dataLen;
+ PRBool owner = PR_TRUE;
+ CK_SESSION_HANDLE session;
+ CK_RV crv;
+
+ if (key->keyType != rsaKey) {
+ PORT_SetError( SEC_ERROR_BAD_KEY );
+ return SECFailure;
+ }
+
+ slot = PK11_GetBestSlot(mech.mechanism, wincx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return SECFailure;
+ }
+
+ id = PK11_ImportPublicKey(slot,key,PR_FALSE);
+
+ session = pk11_GetNewSession(slot,&owner);
+ if (!owner || !(slot->isThreadSafe)) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_EncryptInit(session,&mech,id);
+ if (crv != CKR_OK) {
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_Encrypt(session,data,dataLen,enc,&out);
+ if (!owner || !(slot->isThreadSafe)) PK11_ExitSlotMonitor(slot);
+ pk11_CloseSession(slot,session,owner);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+
+/**********************************************************************
+ *
+ * Now Deal with Crypto Contexts
+ *
+ **********************************************************************/
+
+/*
+ * the monitors...
+ */
+void
+PK11_EnterContextMonitor(PK11Context *cx) {
+ /* if we own the session and our slot is ThreadSafe, only monitor
+ * the Context */
+ if ((cx->ownSession) && (cx->slot->isThreadSafe)) {
+ /* Should this use monitors instead? */
+ PR_Lock(cx->sessionLock);
+ } else {
+ PK11_EnterSlotMonitor(cx->slot);
+ }
+}
+
+void
+PK11_ExitContextMonitor(PK11Context *cx) {
+ /* if we own the session and our slot is ThreadSafe, only monitor
+ * the Context */
+ if ((cx->ownSession) && (cx->slot->isThreadSafe)) {
+ /* Should this use monitors instead? */
+ PR_Unlock(cx->sessionLock);
+ } else {
+ PK11_ExitSlotMonitor(cx->slot);
+ }
+}
+
+/*
+ * Free up a Cipher Context
+ */
+void
+PK11_DestroyContext(PK11Context *context, PRBool freeit)
+{
+ pk11_CloseSession(context->slot,context->session,context->ownSession);
+ /* initialize the critical fields of the context */
+ if (context->savedData != NULL ) PORT_Free(context->savedData);
+ if (context->key) PK11_FreeSymKey(context->key);
+ if (context->param) SECITEM_FreeItem(context->param, PR_TRUE);
+ if (context->sessionLock) PR_DestroyLock(context->sessionLock);
+ PK11_FreeSlot(context->slot);
+ if (freeit) PORT_Free(context);
+}
+
+/*
+ * save the current context. Allocate Space if necessary.
+ */
+void *
+pk11_saveContext(PK11Context *context,void *space, unsigned long *savedLength)
+{
+ CK_ULONG length;
+ CK_RV crv;
+
+ if (space == NULL) {
+ crv =PK11_GETTAB(context->slot)->C_GetOperationState(context->session,
+ NULL,&length);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+ space = PORT_Alloc(length);
+ if (space == NULL) return NULL;
+ *savedLength = length;
+ }
+ crv = PK11_GETTAB(context->slot)->C_GetOperationState(context->session,
+ (CK_BYTE_PTR)space,savedLength);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+ return space;
+}
+
+/*
+ * restore the current context
+ */
+SECStatus
+pk11_restoreContext(PK11Context *context,void *space, unsigned long savedLength)
+{
+ CK_RV crv;
+ CK_OBJECT_HANDLE objectID = (context->key) ? context->key->objectID:
+ CK_INVALID_KEY;
+
+ PORT_Assert(space != NULL);
+ if (space == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(context->slot)->C_SetOperationState(context->session,
+ (CK_BYTE_PTR)space, savedLength, objectID, 0);
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus pk11_Finalize(PK11Context *context);
+
+/*
+ * Context initialization. Used by all flavors of CreateContext
+ */
+static SECStatus
+pk11_context_init(PK11Context *context, CK_MECHANISM *mech_info)
+{
+ CK_RV crv;
+ PK11SymKey *symKey = context->key;
+ SECStatus rv = SECSuccess;
+
+ switch (context->operation) {
+ case CKA_ENCRYPT:
+ crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session,
+ mech_info, symKey->objectID);
+ break;
+ case CKA_DECRYPT:
+ if (context->fortezzaHack) {
+ CK_ULONG count = 0;;
+ /* generate the IV for fortezza */
+ crv=PK11_GETTAB(context->slot)->C_EncryptInit(context->session,
+ mech_info, symKey->objectID);
+ if (crv != CKR_OK) break;
+ PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
+ NULL, &count);
+ }
+ crv=PK11_GETTAB(context->slot)->C_DecryptInit(context->session,
+ mech_info, symKey->objectID);
+ break;
+ case CKA_SIGN:
+ crv=PK11_GETTAB(context->slot)->C_SignInit(context->session,
+ mech_info, symKey->objectID);
+ break;
+ case CKA_VERIFY:
+ crv=PK11_GETTAB(context->slot)->C_SignInit(context->session,
+ mech_info, symKey->objectID);
+ break;
+ case CKA_DIGEST:
+ crv=PK11_GETTAB(context->slot)->C_DigestInit(context->session,
+ mech_info);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context,context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL) rv = SECFailure;
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ return rv;
+}
+
+
+/*
+ * Common Helper Function do come up with a new context.
+ */
+static PK11Context *pk11_CreateNewContextInSlot(CK_MECHANISM_TYPE type,
+ PK11SlotInfo *slot, CK_ATTRIBUTE_TYPE operation, PK11SymKey *symKey,
+ SECItem *param)
+{
+ CK_MECHANISM mech_info;
+ PK11Context *context;
+ SECStatus rv;
+
+ context = (PK11Context *) PORT_Alloc(sizeof(PK11Context));
+ if (context == NULL) {
+ return NULL;
+ }
+
+ /* now deal with the fortezza hack... the fortezza hack is an attempt
+ * to get around the issue of the card not allowing you to do a FORTEZZA
+ * LoadIV/Encrypt, which was added because such a combination could be
+ * use to circumvent the key escrow system. Unfortunately SSL needs to
+ * do this kind of operation, so in SSL we do a loadIV (to verify it),
+ * Then GenerateIV, and through away the first 8 bytes on either side
+ * of the connection.*/
+ context->fortezzaHack = PR_FALSE;
+ if (type == CKM_SKIPJACK_CBC64) {
+ if (symKey->origin == PK11_OriginFortezzaHack) {
+ context->fortezzaHack = PR_TRUE;
+ }
+ }
+
+ /* initialize the critical fields of the context */
+ context->operation = operation;
+ context->key = symKey ? PK11_ReferenceSymKey(symKey) : NULL;
+ context->slot = PK11_ReferenceSlot(slot);
+ context->session = pk11_GetNewSession(slot,&context->ownSession);
+ context->cx = symKey ? symKey->cx : NULL;
+ /* get our session */
+ context->savedData = NULL;
+
+ /* save the parameters so that some digesting stuff can do multiple
+ * begins on a single context */
+ context->type = type;
+ context->param = SECITEM_DupItem(param);
+ context->init = PR_FALSE;
+ context->sessionLock = PR_NewLock();
+ if ((context->param == NULL) || (context->sessionLock == NULL)) {
+ PK11_DestroyContext(context,PR_TRUE);
+ return NULL;
+ }
+
+ mech_info.mechanism = type;
+ mech_info.pParameter = param->data;
+ mech_info.ulParameterLen = param->len;
+ PK11_EnterContextMonitor(context);
+ rv = pk11_context_init(context,&mech_info);
+ PK11_ExitContextMonitor(context);
+
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context,PR_TRUE);
+ return NULL;
+ }
+ context->init = PR_TRUE;
+ return context;
+}
+
+
+/*
+ * put together the various PK11_Create_Context calls used by different
+ * parts of libsec.
+ */
+PK11Context *
+PK11_CreateContextByRawKey(PK11SlotInfo *slot, CK_MECHANISM_TYPE type,
+ PK11Origin origin, CK_ATTRIBUTE_TYPE operation, SECItem *key,
+ SECItem *param, void *wincx)
+{
+ PK11SymKey *symKey;
+ PK11Context *context;
+
+ /* first get a slot */
+ if (slot == NULL) {
+ slot = PK11_GetBestSlot(type,wincx);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+ }
+ } else {
+ PK11_ReferenceSlot(slot);
+ }
+
+ /* now import the key */
+ symKey = PK11_ImportSymKey(slot, type, origin, operation, key, wincx);
+ if (symKey == NULL) return NULL;
+
+ context = PK11_CreateContextBySymKey(type, operation, symKey, param);
+
+ PK11_FreeSymKey(symKey);
+ PK11_FreeSlot(slot);
+
+ return context;
+}
+
+
+/*
+ * Create a context from a key. We really should make sure we aren't using
+ * the same key in multiple session!
+ */
+PK11Context *
+PK11_CreateContextBySymKey(CK_MECHANISM_TYPE type,CK_ATTRIBUTE_TYPE operation,
+ PK11SymKey *symKey, SECItem *param)
+{
+ PK11SymKey *newKey;
+ PK11Context *context;
+
+ /* if this slot doesn't support the mechanism, go to a slot that does */
+ newKey = pk11_ForceSlot(symKey,type,operation);
+ if (newKey == NULL) {
+ PK11_ReferenceSymKey(symKey);
+ } else {
+ symKey = newKey;
+ }
+
+
+ /* Context Adopts the symKey.... */
+ context = pk11_CreateNewContextInSlot(type, symKey->slot, operation, symKey,
+ param);
+ PK11_FreeSymKey(symKey);
+ return context;
+}
+
+/*
+ * Digest contexts don't need keys, but the do need to find a slot.
+ * Macing should use PK11_CreateContextBySymKey.
+ */
+PK11Context *
+PK11_CreateDigestContext(SECOidTag hashAlg)
+{
+ /* digesting has to work without authentication to the slot */
+ CK_MECHANISM_TYPE type;
+ PK11SlotInfo *slot;
+ PK11Context *context;
+ SECItem param;
+
+ type = PK11_AlgtagToMechanism(hashAlg);
+ slot = PK11_GetBestSlot(type, NULL);
+ if (slot == NULL) {
+ PORT_SetError( SEC_ERROR_NO_MODULE );
+ return NULL;
+ }
+
+ /* maybe should really be PK11_GenerateNewParam?? */
+ param.data = NULL;
+ param.len = 0;
+
+ context = pk11_CreateNewContextInSlot(type, slot, CKA_DIGEST, NULL, &param);
+ PK11_FreeSlot(slot);
+ return context;
+}
+
+/*
+ * create a new context which is the clone of the state of old context.
+ */
+PK11Context * PK11_CloneContext(PK11Context *old)
+{
+ PK11Context *newcx;
+ PRBool needFree = PR_FALSE;
+ SECStatus rv = SECSuccess;
+ void *data;
+ unsigned long len;
+
+ newcx = pk11_CreateNewContextInSlot(old->type, old->slot, old->operation,
+ old->key, old->param);
+ if (newcx == NULL) return NULL;
+
+ /* now clone the save state. First we need to find the save state
+ * of the old session. If the old context owns it's session,
+ * the state needs to be saved, otherwise the state is in saveData. */
+ if (old->ownSession) {
+ PK11_EnterContextMonitor(old);
+ data=pk11_saveContext(old,NULL,&len);
+ PK11_ExitContextMonitor(old);
+ needFree = PR_TRUE;
+ } else {
+ data = old->savedData;
+ len = old->savedLength;
+ }
+
+ if (data == NULL) {
+ PK11_DestroyContext(newcx,PR_TRUE);
+ return NULL;
+ }
+
+ /* now copy that state into our new context. Again we have different
+ * work if the new context owns it's own session. If it does, we
+ * restore the state gathered above. If it doesn't, we copy the
+ * saveData pointer... */
+ if (newcx->ownSession) {
+ PK11_EnterContextMonitor(newcx);
+ rv = pk11_restoreContext(newcx,data,len);
+ PK11_ExitContextMonitor(newcx);
+ } else {
+ PORT_Assert(newcx->savedData != NULL);
+ if ((newcx->savedData == NULL) || (newcx->savedLength < len)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ } else {
+ PORT_Memcpy(newcx->savedData,data,len);
+ newcx->savedLength = len;
+ }
+ }
+
+ if (needFree) PORT_Free(data);
+
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(newcx,PR_TRUE);
+ return NULL;
+ }
+ return newcx;
+}
+
+/*
+ * save the current context state into a variable. Required to make FORTEZZA
+ * work.
+ */
+SECStatus
+PK11_SaveContext(PK11Context *cx,unsigned char *save,int *len, int saveLength)
+{
+ unsigned char * data;
+ CK_ULONG length = saveLength;
+
+ if (cx->ownSession) {
+ PK11_EnterContextMonitor(cx);
+ data = (unsigned char*)pk11_saveContext(cx,save,&length);
+ PK11_ExitContextMonitor(cx);
+ if (data) *len = length;
+ } else {
+ data = (unsigned char*)cx->savedData;
+ if (cx->savedData) {
+ PORT_Memcpy(save,cx->savedData,cx->savedLength);
+ }
+ *len = cx->savedLength;
+ }
+ return (data != NULL) ? SECSuccess : SECFailure;
+}
+
+/*
+ * restore the context state into a new running context. Also required for
+ * FORTEZZA .
+ */
+SECStatus
+PK11_RestoreContext(PK11Context *cx,unsigned char *save,int len)
+{
+ SECStatus rv = SECSuccess;
+ if (cx->ownSession) {
+ PK11_EnterContextMonitor(cx);
+ pk11_Finalize(cx);
+ rv = pk11_restoreContext(cx,save,len);
+ PK11_ExitContextMonitor(cx);
+ } else {
+ PORT_Assert(cx->savedData != NULL);
+ if ((cx->savedData == NULL) || (cx->savedLength < (unsigned) len)) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ rv = SECFailure;
+ } else {
+ PORT_Memcpy(cx->savedData,save,len);
+ cx->savedLength = len;
+ }
+ }
+ return rv;
+}
+
+/*
+ * This is to get FIPS compliance until we can convert
+ * libjar to use PK11_ hashing functions. It returns PR_FALSE
+ * if we can't get a PK11 Context.
+ */
+PRBool
+PK11_HashOK(SECOidTag algID) {
+ PK11Context *cx;
+
+ cx = PK11_CreateDigestContext(algID);
+ if (cx == NULL) return PR_FALSE;
+ PK11_DestroyContext(cx, PR_TRUE);
+ return PR_TRUE;
+}
+
+
+
+/*
+ * start a new digesting or Mac'ing operation on this context
+ */
+SECStatus PK11_DigestBegin(PK11Context *cx)
+{
+ CK_MECHANISM mech_info;
+ SECStatus rv;
+
+ if (cx->init == PR_TRUE) {
+ return SECSuccess;
+ }
+
+ /*
+ * make sure the old context is clear first
+ */
+ PK11_EnterContextMonitor(cx);
+ pk11_Finalize(cx);
+
+ mech_info.mechanism = cx->type;
+ mech_info.pParameter = cx->param->data;
+ mech_info.ulParameterLen = cx->param->len;
+ rv = pk11_context_init(cx,&mech_info);
+ PK11_ExitContextMonitor(cx);
+
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ cx->init = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+PK11_HashBuf(SECOidTag hashAlg, unsigned char *out, unsigned char *in,
+ int32 len) {
+ PK11Context *context;
+ unsigned int max_length;
+ unsigned int out_length;
+ SECStatus rv;
+
+ context = PK11_CreateDigestContext(hashAlg);
+ if (context == NULL) return SECFailure;
+
+ rv = PK11_DigestBegin(context);
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return rv;
+ }
+
+ rv = PK11_DigestOp(context, in, len);
+ if (rv != SECSuccess) {
+ PK11_DestroyContext(context, PR_TRUE);
+ return rv;
+ }
+
+ /* we need the output length ... maybe this should be table driven...*/
+ switch (hashAlg) {
+ case SEC_OID_SHA1: max_length = SHA1_LENGTH; break;
+ case SEC_OID_MD2: max_length = MD2_LENGTH; break;
+ case SEC_OID_MD5: max_length = MD5_LENGTH; break;
+ default: max_length = 16; break;
+ }
+
+ rv = PK11_DigestFinal(context,out,&out_length,max_length);
+ PK11_DestroyContext(context, PR_TRUE);
+ return rv;
+}
+
+
+/*
+ * execute a bulk encryption operation
+ */
+SECStatus
+PK11_CipherOp(PK11Context *context, unsigned char * out, int *outlen,
+ int maxout, unsigned char *in, int inlen)
+{
+ CK_RV crv = CKR_OK;
+ CK_ULONG length = maxout;
+ CK_ULONG offset =0;
+ PK11SymKey *symKey = context->key;
+ SECStatus rv = SECSuccess;
+ unsigned char *saveOut = out;
+ unsigned char *allocOut = NULL;
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context,context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ /*
+ * The fortezza hack is to send 8 extra bytes on the first encrypted and
+ * loose them on the first decrypt.
+ */
+ if (context->fortezzaHack) {
+ unsigned char random[8];
+ if (context->operation == CKA_ENCRYPT) {
+ PK11_ExitContextMonitor(context);
+ rv = PK11_GenerateRandom(random,sizeof(random));
+ PK11_EnterContextMonitor(context);
+
+ /* since we are offseting the output, we can't encrypt back into
+ * the same buffer... allocate a temporary buffer just for this
+ * call. */
+ allocOut = out = (unsigned char*)PORT_Alloc(maxout);
+ if (out == NULL) {
+ PK11_ExitContextMonitor(context);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session,
+ random,sizeof(random),out,&length);
+
+ out += length;
+ maxout -= length;
+ offset = length;
+ } else if (context->operation == CKA_DECRYPT) {
+ length = sizeof(random);
+ crv = PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session,
+ in,sizeof(random),random,&length);
+ inlen -= length;
+ in += length;
+ context->fortezzaHack = PR_FALSE;
+ }
+ }
+
+ switch (context->operation) {
+ case CKA_ENCRYPT:
+ length = maxout;
+ crv=PK11_GETTAB(context->slot)->C_EncryptUpdate(context->session,
+ in, inlen, out, &length);
+ length += offset;
+ break;
+ case CKA_DECRYPT:
+ length = maxout;
+ crv=PK11_GETTAB(context->slot)->C_DecryptUpdate(context->session,
+ in, inlen, out, &length);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ *outlen = 0;
+ rv = SECFailure;
+ } else {
+ *outlen = length;
+ }
+
+ if (context->fortezzaHack) {
+ if (context->operation == CKA_ENCRYPT) {
+ PORT_Assert(allocOut);
+ PORT_Memcpy(saveOut, allocOut, length);
+ PORT_Free(allocOut);
+ }
+ context->fortezzaHack = PR_FALSE;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context,context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL) rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * execute a digest/signature operation
+ */
+SECStatus
+PK11_DigestOp(PK11Context *context, const unsigned char * in, unsigned inLen)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv = SECSuccess;
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ context->init = PR_FALSE;
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context,context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ switch (context->operation) {
+ /* also for MAC'ing */
+ case CKA_SIGN:
+ crv=PK11_GETTAB(context->slot)->C_SignUpdate(context->session,
+ (unsigned char *)in,
+ inLen);
+ break;
+ case CKA_VERIFY:
+ crv=PK11_GETTAB(context->slot)->C_VerifyUpdate(context->session,
+ (unsigned char *)in,
+ inLen);
+ break;
+ case CKA_DIGEST:
+ crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session,
+ (unsigned char *)in,
+ inLen);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ rv = SECFailure;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context,context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL) rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * Digest a key if possible./
+ */
+SECStatus
+PK11_DigestKey(PK11Context *context, PK11SymKey *key)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv = SECSuccess;
+ PK11SymKey *newKey = NULL;
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ if (context->slot != key->slot) {
+ newKey = pk11_CopyToSlot(context->slot,CKM_SSL3_SHA1_MAC,CKA_SIGN,key);
+ } else {
+ newKey = PK11_ReferenceSymKey(key);
+ }
+
+ context->init = PR_FALSE;
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context,context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ PK11_FreeSymKey(newKey);
+ return rv;
+ }
+ }
+
+
+ if (newKey == NULL) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ if (key->data.data) {
+ crv=PK11_GETTAB(context->slot)->C_DigestUpdate(context->session,
+ key->data.data,key->data.len);
+ }
+ } else {
+ crv=PK11_GETTAB(context->slot)->C_DigestKey(context->session,
+ newKey->objectID);
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ rv = SECFailure;
+ }
+
+ /*
+ * handle session starvation case.. use our last session to multiplex
+ */
+ if (!context->ownSession) {
+ context->savedData = pk11_saveContext(context,context->savedData,
+ &context->savedLength);
+ if (context->savedData == NULL) rv = SECFailure;
+
+ /* clear out out session for others to use */
+ pk11_Finalize(context);
+ }
+ PK11_ExitContextMonitor(context);
+ if (newKey) PK11_FreeSymKey(newKey);
+ return rv;
+}
+
+/*
+ * externally callable version of the lowercase pk11_finalize().
+ */
+SECStatus
+PK11_Finalize(PK11Context *context) {
+ SECStatus rv;
+
+ PK11_EnterContextMonitor(context);
+ rv = pk11_Finalize(context);
+ PK11_ExitContextMonitor(context);
+ return rv;
+}
+
+/*
+ * clean up a cipher operation, so the session can be used by
+ * someone new.
+ */
+SECStatus
+pk11_Finalize(PK11Context *context)
+{
+ CK_ULONG count = 0;
+ CK_RV crv;
+
+ if (!context->ownSession) {
+ return SECSuccess;
+ }
+
+ switch (context->operation) {
+ case CKA_ENCRYPT:
+ crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
+ NULL,&count);
+ break;
+ case CKA_DECRYPT:
+ crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session,
+ NULL,&count);
+ break;
+ case CKA_SIGN:
+ crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session,
+ NULL,&count);
+ break;
+ case CKA_VERIFY:
+ crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session,
+ NULL,count);
+ break;
+ case CKA_DIGEST:
+ crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session,
+ NULL,&count);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * Return the final digested or signed data...
+ * this routine can either take pre initialized data, or allocate data
+ * either out of an arena or out of the standard heap.
+ */
+SECStatus
+PK11_DigestFinal(PK11Context *context,unsigned char *data,
+ unsigned int *outLen, unsigned int length)
+{
+ CK_ULONG len;
+ CK_RV crv;
+ SECStatus rv;
+
+
+ /* if we ran out of session, we need to restore our previously stored
+ * state.
+ */
+ PK11_EnterContextMonitor(context);
+ if (!context->ownSession) {
+ rv = pk11_restoreContext(context,context->savedData,
+ context->savedLength);
+ if (rv != SECSuccess) {
+ PK11_ExitContextMonitor(context);
+ return rv;
+ }
+ }
+
+ len = length;
+ switch (context->operation) {
+ case CKA_SIGN:
+ crv=PK11_GETTAB(context->slot)->C_SignFinal(context->session,
+ data,&len);
+ break;
+ case CKA_VERIFY:
+ crv=PK11_GETTAB(context->slot)->C_VerifyFinal(context->session,
+ data,len);
+ break;
+ case CKA_DIGEST:
+ crv=PK11_GETTAB(context->slot)->C_DigestFinal(context->session,
+ data,&len);
+ break;
+ case CKA_ENCRYPT:
+ crv=PK11_GETTAB(context->slot)->C_EncryptFinal(context->session,
+ data, &len);
+ break;
+ case CKA_DECRYPT:
+ crv = PK11_GETTAB(context->slot)->C_DecryptFinal(context->session,
+ data, &len);
+ break;
+ default:
+ crv = CKR_OPERATION_NOT_INITIALIZED;
+ break;
+ }
+ PK11_ExitContextMonitor(context);
+
+ *outLen = (unsigned int) len;
+ context->init = PR_FALSE; /* allow Begin to start up again */
+
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/****************************************************************************
+ *
+ * Now Do The PBE Functions Here...
+ *
+ ****************************************************************************/
+
+SECAlgorithmID *
+PK11_CreatePBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt)
+{
+ SECAlgorithmID *algid;
+
+ algid = SEC_PKCS5CreateAlgorithmID(algorithm, salt, iteration);
+ return algid;
+}
+
+PK11SymKey *
+PK11_PBEKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx)
+{
+ /* pbe stuff */
+ CK_PBE_PARAMS *pbe_params;
+ CK_MECHANISM_TYPE type;
+ SECItem *mech;
+ PK11SymKey *symKey;
+
+ mech = PK11_ParamFromAlgid(algid);
+ type = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm));
+ if(faulty3DES && (type == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC)) {
+ type = CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC;
+ }
+ if(mech == NULL) {
+ return NULL;
+ }
+
+ pbe_params = (CK_PBE_PARAMS *)mech->data;
+ pbe_params->pPassword = (CK_CHAR_PTR)PORT_ZAlloc(pwitem->len);
+ if(pbe_params->pPassword != NULL) {
+ PORT_Memcpy(pbe_params->pPassword, pwitem->data, pwitem->len);
+ pbe_params->ulPasswordLen = pwitem->len;
+ } else {
+ SECITEM_ZfreeItem(mech, PR_TRUE);
+ return NULL;
+ }
+
+ symKey = PK11_KeyGen(slot, type, mech, 0, wincx);
+
+ PORT_ZFree(pbe_params->pPassword, pwitem->len);
+ SECITEM_ZfreeItem(mech, PR_TRUE);
+ return symKey;
+}
+
+
+SECStatus
+PK11_ImportEncryptedPrivateKeyInfo(PK11SlotInfo *slot,
+ SECKEYEncryptedPrivateKeyInfo *epki, SECItem *pwitem,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, KeyType keyType, unsigned int keyUsage,
+ void *wincx)
+{
+ CK_MECHANISM_TYPE mechanism;
+ SECItem *pbe_param, crypto_param;
+ PK11SymKey *key = NULL;
+ SECStatus rv = SECSuccess;
+ CK_MECHANISM cryptoMech, pbeMech;
+ CK_RV crv;
+ SECKEYPrivateKey *privKey = NULL;
+ PRBool faulty3DES = PR_FALSE;
+ int usageCount;
+ CK_KEY_TYPE key_type;
+ CK_ATTRIBUTE_TYPE *usage;
+ CK_ATTRIBUTE_TYPE rsaUsage[] = {
+ CKA_UNWRAP, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER };
+ CK_ATTRIBUTE_TYPE dsaUsage[] = { CKA_SIGN };
+ CK_ATTRIBUTE_TYPE dhUsage[] = { CKA_DERIVE };
+
+ if((epki == NULL) || (pwitem == NULL))
+ return SECFailure;
+
+ crypto_param.data = NULL;
+
+ mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag(
+ &epki->algorithm.algorithm));
+
+ switch (keyType) {
+ default:
+ case rsaKey:
+ key_type = CKK_RSA;
+ switch (keyUsage & (KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE)) {
+ case KU_KEY_ENCIPHERMENT:
+ usage = rsaUsage;
+ usageCount = 2;
+ break;
+ case KU_DIGITAL_SIGNATURE:
+ usage = &rsaUsage[2];
+ usageCount = 2;
+ break;
+ case KU_KEY_ENCIPHERMENT|KU_DIGITAL_SIGNATURE:
+ case 0: /* default to everything */
+ usage = rsaUsage;
+ usageCount = 4;
+ break;
+ }
+ break;
+ case dhKey:
+ key_type = CKK_DH;
+ usage = dhUsage;
+ usageCount = sizeof(dhUsage)/sizeof(dhUsage[0]);
+ break;
+ case dsaKey:
+ key_type = CKK_DSA;
+ usage = dsaUsage;
+ usageCount = sizeof(dsaUsage)/sizeof(dsaUsage[0]);
+ break;
+ }
+
+try_faulty_3des:
+ pbe_param = PK11_ParamFromAlgid(&epki->algorithm);
+
+ key = PK11_PBEKeyGen(slot, &epki->algorithm, pwitem, faulty3DES, wincx);
+ if((key == NULL) || (pbe_param == NULL)) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ pbeMech.mechanism = mechanism;
+ pbeMech.pParameter = pbe_param->data;
+ pbeMech.ulParameterLen = pbe_param->len;
+
+ crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech,
+ pwitem, faulty3DES);
+ if(crv != CKR_OK) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMech.mechanism);
+ crypto_param.data = (unsigned char*)cryptoMech.pParameter;
+ crypto_param.len = cryptoMech.ulParameterLen;
+
+ privKey = PK11_UnwrapPrivKey(slot, key, cryptoMech.mechanism,
+ &crypto_param, &epki->encryptedData,
+ nickname, publicValue, isPerm, isPrivate,
+ key_type, usage, usageCount, wincx);
+ if(privKey) {
+ SECKEY_DestroyPrivateKey(privKey);
+ privKey = NULL;
+ rv = SECSuccess;
+ goto done;
+ }
+
+ /* if we are unable to import the key and the mechanism is
+ * CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC, then it is possible that
+ * the encrypted blob was created with a buggy key generation method
+ * which is described in the PKCS 12 implementation notes. So we
+ * need to try importing via that method.
+ */
+ if((mechanism == CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC) && (!faulty3DES)) {
+ /* clean up after ourselves before redoing the key generation. */
+
+ PK11_FreeSymKey(key);
+ key = NULL;
+
+ if(pbe_param) {
+ SECITEM_ZfreeItem(pbe_param, PR_TRUE);
+ pbe_param = NULL;
+ }
+
+ if(crypto_param.data) {
+ SECITEM_ZfreeItem(&crypto_param, PR_FALSE);
+ crypto_param.data = NULL;
+ cryptoMech.pParameter = NULL;
+ crypto_param.len = cryptoMech.ulParameterLen = 0;
+ }
+
+ faulty3DES = PR_TRUE;
+ goto try_faulty_3des;
+ }
+
+ /* key import really did fail */
+ rv = SECFailure;
+
+done:
+ if(pbe_param != NULL) {
+ SECITEM_ZfreeItem(pbe_param, PR_TRUE);
+ pbe_param = NULL;
+ }
+
+ if(crypto_param.data != NULL) {
+ SECITEM_ZfreeItem(&crypto_param, PR_FALSE);
+ }
+
+ if(key != NULL) {
+ PK11_FreeSymKey(key);
+ }
+
+ return rv;
+}
+
+/*
+ * import a private key info into the desired slot
+ */
+SECStatus
+PK11_ImportPrivateKeyInfo(PK11SlotInfo *slot, SECKEYPrivateKeyInfo *pki,
+ SECItem *nickname, SECItem *publicValue, PRBool isPerm,
+ PRBool isPrivate, unsigned int keyUsage, void *wincx)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ CK_OBJECT_HANDLE objectID;
+ CK_ATTRIBUTE theTemplate[20];
+ int templateCount = 0;
+ SECStatus rv = SECFailure;
+ SECKEYLowPrivateKey *lpk = NULL;
+ const SEC_ASN1Template *keyTemplate, *paramTemplate;
+ void *paramDest = NULL;
+ PRArenaPool *arena;
+ CK_ATTRIBUTE *attrs;
+ CK_ATTRIBUTE *signedattr = NULL;
+ int signedcount = 0;
+ CK_ATTRIBUTE *ap;
+ SECItem *ck_id = NULL;
+
+ arena = PORT_NewArena(2048);
+ if(!arena) {
+ return SECFailure;
+ }
+
+ /* need to change this to use RSA/DSA keys */
+ lpk = (SECKEYLowPrivateKey *)PORT_ArenaZAlloc(arena,
+ sizeof(SECKEYLowPrivateKey));
+ if(lpk == NULL) {
+ goto loser;
+ }
+ lpk->arena = arena;
+
+ attrs = theTemplate;
+ switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ keyTemplate = SECKEY_RSAPrivateKeyTemplate;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = rsaKey;
+ keyType = CKK_RSA;
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ if(!publicValue) {
+ goto loser;
+ }
+ keyTemplate = SECKEY_DSAPrivateKeyExportTemplate;
+ paramTemplate = SECKEY_PQGParamsTemplate;
+ paramDest = &(lpk->u.dsa.params);
+ lpk->keyType = dsaKey;
+ keyType = CKK_DSA;
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ if(!publicValue) {
+ goto loser;
+ }
+ keyTemplate = SECKEY_DHPrivateKeyExportTemplate;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = dhKey;
+ keyType = CKK_DH;
+ break;
+
+ default:
+ keyTemplate = NULL;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ break;
+ }
+
+ if(!keyTemplate) {
+ goto loser;
+ }
+
+ /* decode the private key and any algorithm parameters */
+ rv = SEC_ASN1DecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+ if(paramDest && paramTemplate) {
+ rv = SEC_ASN1DecodeItem(arena, paramDest, paramTemplate,
+ &(pki->algorithm.parameters));
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_TOKEN, isPerm ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_SENSITIVE, isPrivate ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIVATE, isPrivate ? &cktrue : &ckfalse,
+ sizeof(CK_BBOOL) ); attrs++;
+
+ switch (lpk->keyType) {
+ case rsaKey:
+ PK11_SETATTRS(attrs, CKA_UNWRAP, (keyUsage & KU_KEY_ENCIPHERMENT) ?
+ &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_DECRYPT, (keyUsage & KU_DATA_ENCIPHERMENT) ?
+ &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN, (keyUsage & KU_DIGITAL_SIGNATURE) ?
+ &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER,
+ (keyUsage & KU_DIGITAL_SIGNATURE) ?
+ &cktrue : &ckfalse, sizeof(CK_BBOOL) ); attrs++;
+ ck_id = PK11_MakeIDFromPubKey(&lpk->u.rsa.modulus);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++;
+ if (nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len); attrs++;
+ }
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_MODULUS, lpk->u.rsa.modulus.data,
+ lpk->u.rsa.modulus.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_PUBLIC_EXPONENT,
+ lpk->u.rsa.publicExponent.data,
+ lpk->u.rsa.publicExponent.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIVATE_EXPONENT,
+ lpk->u.rsa.privateExponent.data,
+ lpk->u.rsa.privateExponent.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIME_1,
+ lpk->u.rsa.prime1.data,
+ lpk->u.rsa.prime1.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIME_2,
+ lpk->u.rsa.prime2.data,
+ lpk->u.rsa.prime2.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_EXPONENT_1,
+ lpk->u.rsa.exponent1.data,
+ lpk->u.rsa.exponent1.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_EXPONENT_2,
+ lpk->u.rsa.exponent2.data,
+ lpk->u.rsa.exponent2.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_COEFFICIENT,
+ lpk->u.rsa.coefficient.data,
+ lpk->u.rsa.coefficient.len); attrs++;
+ break;
+ case dsaKey:
+ /* To make our intenal PKCS #11 module work correctly with
+ * our database, we need to pass in the public key value for
+ * this dsa key. We have a netscape only CKA_ value to do this.
+ * Only send it to internal slots */
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NETSCAPE_DB,
+ publicValue->data, publicValue->len); attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_SIGN, &cktrue, sizeof(CK_BBOOL)); attrs++;
+ PK11_SETATTRS(attrs, CKA_SIGN_RECOVER, &cktrue, sizeof(CK_BBOOL)); attrs++;
+ if(nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ ck_id = PK11_MakeIDFromPubKey(publicValue);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dsa.params.prime.data,
+ lpk->u.dsa.params.prime.len); attrs++;
+ PK11_SETATTRS(attrs,CKA_SUBPRIME,lpk->u.dsa.params.subPrime.data,
+ lpk->u.dsa.params.subPrime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dsa.params.base.data,
+ lpk->u.dsa.params.base.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dsa.privateValue.data,
+ lpk->u.dsa.privateValue.len); attrs++;
+ break;
+ case dhKey:
+ /* To make our intenal PKCS #11 module work correctly with
+ * our database, we need to pass in the public key value for
+ * this dh key. We have a netscape only CKA_ value to do this.
+ * Only send it to internal slots */
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NETSCAPE_DB,
+ publicValue->data, publicValue->len); attrs++;
+ }
+ PK11_SETATTRS(attrs, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); attrs++;
+ if(nickname) {
+ PK11_SETATTRS(attrs, CKA_LABEL, nickname->data, nickname->len);
+ attrs++;
+ }
+ ck_id = PK11_MakeIDFromPubKey(publicValue);
+ if (ck_id == NULL) {
+ goto loser;
+ }
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data,ck_id->len); attrs++;
+ signedattr = attrs;
+ PK11_SETATTRS(attrs, CKA_PRIME, lpk->u.dh.prime.data,
+ lpk->u.dh.prime.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_BASE, lpk->u.dh.base.data,
+ lpk->u.dh.base.len); attrs++;
+ PK11_SETATTRS(attrs, CKA_VALUE, lpk->u.dh.privateValue.data,
+ lpk->u.dh.privateValue.len); attrs++;
+ break;
+ /* what about fortezza??? */
+ default:
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ goto loser;
+ }
+ templateCount = attrs - theTemplate;
+ PR_ASSERT(templateCount <= sizeof(theTemplate)/sizeof(CK_ATTRIBUTE));
+ signedcount = attrs - signedattr;
+
+ for (ap=signedattr; signedcount; ap++, signedcount--) {
+ pk11_SignedToUnsigned(ap);
+ }
+
+ rv = PK11_CreateNewObject(slot,theTemplate,templateCount, isPerm, &objectID);
+
+ if (ck_id) {
+ SECITEM_ZfreeItem(ck_id, PR_TRUE);
+ }
+
+loser:
+ if (lpk!= NULL) {
+ SECKEY_LowDestroyPrivateKey(lpk);
+ }
+
+ return rv;
+}
+
+SECKEYPrivateKeyInfo *
+PK11_ExportPrivateKeyInfo(CERTCertificate *cert, void *wincx)
+{
+ return NULL;
+}
+
+static int
+pk11_private_key_encrypt_buffer_length(SECKEYPrivateKey *key)
+
+{
+ CK_ATTRIBUTE rsaTemplate = { CKA_MODULUS, NULL, 0 };
+ CK_ATTRIBUTE dsaTemplate = { CKA_PRIME, NULL, 0 };
+ CK_ATTRIBUTE_PTR pTemplate;
+ CK_RV crv;
+ int length;
+
+ if(!key) {
+ return -1;
+ }
+
+ switch (key->keyType) {
+ case rsaKey:
+ pTemplate = &rsaTemplate;
+ break;
+ case dsaKey:
+ case dhKey:
+ pTemplate = &dsaTemplate;
+ break;
+ case fortezzaKey:
+ default:
+ pTemplate = NULL;
+ }
+
+ if(!pTemplate) {
+ return -1;
+ }
+
+ crv = PK11_GetAttributes(NULL, key->pkcs11Slot, key->pkcs11ID,
+ pTemplate, 1);
+ if(crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return -1;
+ }
+
+ length = pTemplate->ulValueLen;
+ length *= 10;
+
+ if(pTemplate->pValue != NULL) {
+ PORT_Free(pTemplate->pValue);
+ }
+
+ return length;
+}
+
+SECKEYEncryptedPrivateKeyInfo *
+PK11_ExportEncryptedPrivateKeyInfo(PK11SlotInfo *slot, SECOidTag algTag,
+ SECItem *pwitem, CERTCertificate *cert, int iteration, void *wincx)
+{
+ SECKEYEncryptedPrivateKeyInfo *epki = NULL;
+ SECKEYPrivateKey *pk;
+ PRArenaPool *arena = NULL;
+ SECAlgorithmID *algid;
+ CK_MECHANISM_TYPE mechanism;
+ SECItem *pbe_param = NULL, crypto_param;
+ PK11SymKey *key = NULL;
+ SECStatus rv = SECSuccess;
+ CK_MECHANISM pbeMech, cryptoMech;
+ CK_RV crv;
+ SECItem encryptedKey = {siBuffer,NULL,0};
+ int encryptBufLen;
+
+ if(!pwitem)
+ return NULL;
+
+ crypto_param.data = NULL;
+
+ arena = PORT_NewArena(2048);
+ epki = (SECKEYEncryptedPrivateKeyInfo *)PORT_ArenaZAlloc(arena,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ if(epki == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ epki->arena = arena;
+ algid = SEC_PKCS5CreateAlgorithmID(algTag, NULL, iteration);
+ if(algid == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ mechanism = PK11_AlgtagToMechanism(SECOID_FindOIDTag(&algid->algorithm));
+ pbe_param = PK11_ParamFromAlgid(algid);
+ pbeMech.mechanism = mechanism;
+ pbeMech.pParameter = pbe_param->data;
+ pbeMech.ulParameterLen = pbe_param->len;
+ key = PK11_PBEKeyGen(slot, algid, pwitem, PR_FALSE, wincx);
+
+ if((key == NULL) || (pbe_param == NULL)) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ crv = PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech,
+ pwitem, PR_FALSE);
+ if(crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ cryptoMech.mechanism = PK11_GetPadMechanism(cryptoMech.mechanism);
+ crypto_param.data = (unsigned char *)cryptoMech.pParameter;
+ crypto_param.len = cryptoMech.ulParameterLen;
+
+ pk = PK11_FindKeyByAnyCert(cert, wincx);
+ if(pk == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ encryptBufLen = pk11_private_key_encrypt_buffer_length(pk);
+ if(encryptBufLen == -1) {
+ rv = SECFailure;
+ goto loser;
+ }
+ encryptedKey.len = (unsigned int)encryptBufLen;
+ encryptedKey.data = (unsigned char *)PORT_ZAlloc(encryptedKey.len);
+ if(!encryptedKey.data) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* we are extracting an encrypted privateKey structure.
+ * which needs to be freed along with the buffer into which it is
+ * returned. eventually, we should retrieve an encrypted key using
+ * pkcs8/pkcs5.
+ */
+ PK11_EnterSlotMonitor(pk->pkcs11Slot);
+ crv = PK11_GETTAB(pk->pkcs11Slot)->C_WrapKey(pk->pkcs11Slot->session,
+ &cryptoMech, key->objectID, pk->pkcs11ID, encryptedKey.data,
+ (CK_ULONG_PTR)(&encryptedKey.len));
+ PK11_ExitSlotMonitor(pk->pkcs11Slot);
+ if(crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ if(!encryptedKey.len) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECITEM_CopyItem(arena, &epki->encryptedData, &encryptedKey);
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SECOID_CopyAlgorithmID(arena, &epki->algorithm, algid);
+
+loser:
+ if(pbe_param != NULL) {
+ SECITEM_ZfreeItem(pbe_param, PR_TRUE);
+ pbe_param = NULL;
+ }
+
+ if(crypto_param.data != NULL) {
+ SECITEM_ZfreeItem(&crypto_param, PR_FALSE);
+ crypto_param.data = NULL;
+ }
+
+ if(key != NULL) {
+ PK11_FreeSymKey(key);
+ }
+
+ if(rv == SECFailure) {
+ if(arena != NULL) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ epki = NULL;
+ }
+
+ return epki;
+}
+
+
+/*
+ * This is required to allow FORTEZZA_NULL and FORTEZZA_RC4
+ * working. This function simply gets a valid IV for the keys.
+ */
+SECStatus
+PK11_GenerateFortezzaIV(PK11SymKey *symKey,unsigned char *iv,int len)
+{
+ CK_MECHANISM mech_info;
+ CK_ULONG count = 0;
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+
+ mech_info.mechanism = CKM_SKIPJACK_CBC64;
+ mech_info.pParameter = iv;
+ mech_info.ulParameterLen = len;
+
+ /* generate the IV for fortezza */
+ PK11_EnterSlotMonitor(symKey->slot);
+ crv=PK11_GETTAB(symKey->slot)->C_EncryptInit(symKey->slot->session,
+ &mech_info, symKey->objectID);
+ if (crv == CKR_OK) {
+ PK11_GETTAB(symKey->slot)->C_EncryptFinal(symKey->slot->session,
+ NULL, &count);
+ rv = SECSuccess;
+ }
+ PK11_ExitSlotMonitor(symKey->slot);
+ return rv;
+}
+
+SECKEYPrivateKey *
+PK11_UnwrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
+ CK_MECHANISM_TYPE wrapType, SECItem *param,
+ SECItem *wrappedKey, SECItem *label,
+ SECItem *idValue, PRBool perm, PRBool sensitive,
+ CK_KEY_TYPE keyType, CK_ATTRIBUTE_TYPE *usage, int usageCount,
+ void *wincx)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_OBJECT_CLASS keyClass = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE keyTemplate[15] ;
+ int templateCount = 0;
+ CK_OBJECT_HANDLE privKeyID;
+ CK_MECHANISM mechanism;
+ CK_ATTRIBUTE *attrs = keyTemplate;
+ SECItem *param_free = NULL, *ck_id;
+ CK_RV crv;
+ CK_SESSION_HANDLE rwsession;
+ PK11SymKey *newKey = NULL;
+ int i;
+
+ if(!slot || !wrappedKey || !idValue) {
+ /* SET AN ERROR!!! */
+ return NULL;
+ }
+
+ ck_id = PK11_MakeIDFromPubKey(idValue);
+ if(!ck_id) {
+ return NULL;
+ }
+
+ PK11_SETATTRS(attrs, CKA_TOKEN, perm ? &cktrue : &ckfalse,
+ sizeof(cktrue)); attrs++;
+ PK11_SETATTRS(attrs, CKA_CLASS, &keyClass, sizeof(keyClass)); attrs++;
+ PK11_SETATTRS(attrs, CKA_KEY_TYPE, &keyType, sizeof(keyType)); attrs++;
+ PK11_SETATTRS(attrs, CKA_PRIVATE, sensitive ? &cktrue : &ckfalse,
+ sizeof(cktrue)); attrs++;
+ PK11_SETATTRS(attrs, CKA_SENSITIVE, sensitive ? &cktrue : &ckfalse,
+ sizeof(cktrue)); attrs++;
+ PK11_SETATTRS(attrs, CKA_LABEL, label->data, label->len); attrs++;
+ PK11_SETATTRS(attrs, CKA_ID, ck_id->data, ck_id->len); attrs++;
+ for (i=0; i < usageCount; i++) {
+ PK11_SETATTRS(attrs, usage[i], &cktrue, sizeof(cktrue)); attrs++;
+ }
+
+ if (PK11_IsInternal(slot)) {
+ PK11_SETATTRS(attrs, CKA_NETSCAPE_DB, idValue->data,
+ idValue->len); attrs++;
+ }
+
+ templateCount = attrs - keyTemplate;
+ PR_ASSERT(templateCount <= (sizeof(keyTemplate) / sizeof(CK_ATTRIBUTE)) );
+
+ mechanism.mechanism = wrapType;
+ if(!param) param = param_free= PK11_ParamFromIV(wrapType, NULL);
+ if(param) {
+ mechanism.pParameter = param->data;
+ mechanism.ulParameterLen = param->len;
+ } else {
+ mechanism.pParameter = NULL;
+ mechanism.ulParameterLen = 0;
+ }
+
+ if (wrappingKey->slot != slot) {
+ newKey = pk11_CopyToSlot(slot,wrapType,CKA_WRAP,wrappingKey);
+ } else {
+ newKey = PK11_ReferenceSymKey(wrappingKey);
+ }
+
+ if (newKey) {
+ if (perm) {
+ rwsession = PK11_GetRWSession(slot);
+ } else {
+ rwsession = slot->session;
+ }
+ crv = PK11_GETTAB(slot)->C_UnwrapKey(rwsession, &mechanism,
+ newKey->objectID,
+ wrappedKey->data,
+ wrappedKey->len, keyTemplate,
+ templateCount, &privKeyID);
+
+ if (perm) PK11_RestoreROSession(slot, rwsession);
+ PK11_FreeSymKey(newKey);
+ } else {
+ crv = CKR_FUNCTION_NOT_SUPPORTED;
+ }
+
+ if(ck_id) {
+ SECITEM_FreeItem(ck_id, PR_TRUE);
+ ck_id = NULL;
+ }
+
+ if (crv != CKR_OK) {
+ /* we couldn't unwrap the key, use the internal module to do the
+ * unwrap, then load the new key into the token */
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ if (int_slot && (slot != int_slot)) {
+ SECKEYPrivateKey *privKey = PK11_UnwrapPrivKey(int_slot,
+ wrappingKey, wrapType, param, wrappedKey, label,
+ idValue, PR_FALSE, PR_FALSE,
+ keyType, usage, usageCount, wincx);
+ if (privKey) {
+ SECKEYPrivateKey *newPrivKey = pk11_loadPrivKey(slot,privKey,
+ NULL,perm,sensitive);
+ SECKEY_DestroyPrivateKey(privKey);
+ PK11_FreeSlot(int_slot);
+ return newPrivKey;
+ }
+ }
+ if (int_slot) PK11_FreeSlot(int_slot);
+ PORT_SetError( PK11_MapError(crv) );
+ return NULL;
+ }
+ return PK11_MakePrivKey(slot, nullKey, PR_FALSE, privKeyID, wincx);
+}
+
+#define ALLOC_BLOCK 10
+
+/*
+ * Now we're going to wrap a SECKEYPrivateKey with a PK11SymKey
+ * The strategy is to get both keys to reside in the same slot,
+ * one that can perform the desired crypto mechanism and then
+ * call C_WrapKey after all the setup has taken place.
+ */
+SECStatus
+PK11_WrapPrivKey(PK11SlotInfo *slot, PK11SymKey *wrappingKey,
+ SECKEYPrivateKey *privKey, CK_MECHANISM_TYPE wrapType,
+ SECItem *param, SECItem *wrappedKey, void *wincx)
+{
+ PK11SlotInfo *privSlot = privKey->pkcs11Slot; /* The slot where
+ * the private key
+ * we are going to
+ * wrap lives.
+ */
+ PK11SymKey *newSymKey = NULL;
+ SECKEYPrivateKey *newPrivKey = NULL;
+ SECItem *param_free = NULL;
+ CK_ULONG len = wrappedKey->len;
+ CK_MECHANISM mech;
+ CK_RV crv;
+
+ if (!privSlot || !PK11_DoesMechanism(privSlot, wrapType)) {
+ /* Figure out a slot that does the mechanism and try to import
+ * the private key onto that slot.
+ */
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ privSlot = int_slot; /* The private key has a new home */
+ newPrivKey = pk11_loadPrivKey(privSlot,privKey,NULL,PR_FALSE,PR_FALSE);
+ if (newPrivKey == NULL) {
+ PK11_FreeSlot (int_slot);
+ return SECFailure;
+ }
+ privKey = newPrivKey;
+ }
+
+ if (privSlot != wrappingKey->slot) {
+ newSymKey = pk11_CopyToSlot (privSlot, wrapType, CKA_WRAP,
+ wrappingKey);
+ wrappingKey = newSymKey;
+ }
+
+ if (wrappingKey == NULL) {
+ if (newPrivKey) {
+ SECKEY_DestroyPrivateKey(newPrivKey);
+ }
+ return SECFailure;
+ }
+ mech.mechanism = wrapType;
+ if (!param) {
+ param = param_free = PK11_ParamFromIV(wrapType, NULL);
+ }
+ if (param) {
+ mech.pParameter = param->data;
+ mech.ulParameterLen = param->len;
+ } else {
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+ }
+
+ PK11_EnterSlotMonitor(privSlot);
+ crv = PK11_GETTAB(privSlot)->C_WrapKey(privSlot->session, &mech,
+ wrappingKey->objectID,
+ privKey->pkcs11ID,
+ wrappedKey->data, &len);
+ PK11_ExitSlotMonitor(privSlot);
+
+ if (newSymKey) {
+ PK11_FreeSymKey(newSymKey);
+ }
+ if (newPrivKey) {
+ SECKEY_DestroyPrivateKey(newPrivKey);
+ }
+
+ if (crv != CKR_OK) {
+ PORT_SetError( PK11_MapError(crv) );
+ return SECFailure;
+ }
+
+ wrappedKey->len = len;
+ return SECSuccess;
+}
+
+void
+PK11_SetFortezzaHack(PK11SymKey *symKey) {
+ symKey->origin = PK11_OriginFortezzaHack;
+}
+
diff --git a/security/nss/lib/pk11wrap/pk11slot.c b/security/nss/lib/pk11wrap/pk11slot.c
new file mode 100644
index 000000000..0cfff2344
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11slot.c
@@ -0,0 +1,4244 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * Deal with PKCS #11 Slots.
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "prlock.h"
+#include "secmodi.h"
+#include "pkcs11t.h"
+#include "pk11func.h"
+#include "cert.h"
+#include "key.h"
+#include "secitem.h"
+#include "secder.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "prtime.h"
+#include "prlong.h"
+#include "secerr.h"
+
+/*************************************************************
+ * local static and global data
+ *************************************************************/
+
+/*
+ * This array helps parsing between names, mechanisms, and flags.
+ * to make the config files understand more entries, add them
+ * to this table. (NOTE: we need function to export this table and it's size)
+ */
+PK11DefaultArrayEntry PK11_DefaultArray[] = {
+ { "RSA", SECMOD_RSA_FLAG, CKM_RSA_PKCS },
+ { "DSA", SECMOD_DSA_FLAG, CKM_DSA },
+ { "DH", SECMOD_DH_FLAG, CKM_DH_PKCS_DERIVE },
+ { "RC2", SECMOD_RC2_FLAG, CKM_RC2_CBC },
+ { "RC4", SECMOD_RC4_FLAG, CKM_RC4 },
+ { "DES", SECMOD_DES_FLAG, CKM_DES_CBC },
+ { "RC5", SECMOD_RC5_FLAG, CKM_RC5_CBC },
+ { "SHA-1", SECMOD_SHA1_FLAG, CKM_SHA_1 },
+ { "MD5", SECMOD_MD5_FLAG, CKM_MD5 },
+ { "MD2", SECMOD_MD2_FLAG, CKM_MD2 },
+ { "SKIPJACK", SECMOD_FORTEZZA_FLAG, CKM_SKIPJACK_CBC64 },
+ { "Publicly-readable certs", SECMOD_FRIENDLY_FLAG, CKM_INVALID_MECHANISM },
+ { "Random Num Generator", SECMOD_RANDOM_FLAG, CKM_FAKE_RANDOM },
+};
+int num_pk11_default_mechanisms = sizeof(PK11_DefaultArray) / sizeof(PK11_DefaultArray[0]);
+
+/*
+ * These slotlists are lists of modules which provide default support for
+ * a given algorithm or mechanism.
+ */
+static PK11SlotList pk11_desSlotList,
+ pk11_rc4SlotList,
+ pk11_rc2SlotList,
+ pk11_rc5SlotList,
+ pk11_sha1SlotList,
+ pk11_md5SlotList,
+ pk11_md2SlotList,
+ pk11_rsaSlotList,
+ pk11_dsaSlotList,
+ pk11_dhSlotList,
+ pk11_idea,
+ pk11_random;
+
+/*
+ * Tables used for Extended mechanism mapping (currently not used)
+ */
+typedef struct {
+ CK_MECHANISM_TYPE keyGen;
+ CK_KEY_TYPE keyType;
+ CK_MECHANISM_TYPE type;
+ int blockSize;
+ int iv;
+} pk11MechanismData;
+
+static pk11MechanismData pk11_default =
+ { CKM_GENERIC_SECRET_KEY_GEN, CKK_GENERIC_SECRET, CKM_FAKE_RANDOM, 8, 8 };
+static pk11MechanismData *pk11_MechanismTable = NULL;
+static int pk11_MechTableSize = 0;
+static int pk11_MechEntrySize = 0;
+
+/*
+ * list of mechanisms we're willing to wrap secret keys with.
+ * This list is ordered by preference.
+ */
+CK_MECHANISM_TYPE wrapMechanismList[] = {
+ CKM_DES3_ECB,
+ CKM_CAST5_ECB,
+ CKM_DES_ECB,
+ CKM_KEY_WRAP_LYNKS,
+ CKM_IDEA_ECB,
+ CKM_CAST3_ECB,
+ CKM_CAST_ECB,
+ CKM_RC5_ECB,
+ CKM_RC2_ECB,
+ CKM_CDMF_ECB,
+ CKM_SKIPJACK_WRAP,
+};
+
+int wrapMechanismCount = sizeof(wrapMechanismList)/sizeof(wrapMechanismList[0]);
+
+/*
+ * This structure keeps track of status that spans all the Slots.
+ * NOTE: This is a global data structure. It semantics expect thread crosstalk
+ * be very careful when you see it used.
+ * It's major purpose in life is to allow the user to log in one PER
+ * Tranaction, even if a transaction spans threads. The problem is the user
+ * may have to enter a password one just to be able to look at the
+ * personalities/certificates (s)he can use. Then if Auth every is one, they
+ * may have to enter the password again to use the card. See PK11_StartTransac
+ * and PK11_EndTransaction.
+ */
+static struct PK11GlobalStruct {
+ int transaction;
+ PRBool inTransaction;
+ char *(*getPass)(PK11SlotInfo *,PRBool,void *);
+ PRBool (*verifyPass)(PK11SlotInfo *,void *);
+ PRBool (*isLoggedIn)(PK11SlotInfo *,void *);
+} PK11_Global = { 1, PR_FALSE, NULL, NULL, NULL };
+
+/************************************************************
+ * Generic Slot List and Slot List element manipulations
+ ************************************************************/
+
+/*
+ * allocate a new list
+ */
+PK11SlotList *
+PK11_NewSlotList(void)
+{
+ PK11SlotList *list;
+
+ list = (PK11SlotList *)PORT_Alloc(sizeof(PK11SlotList));
+ if (list == NULL) return NULL;
+ list->head = NULL;
+ list->tail = NULL;
+#ifdef PKCS11_USE_THREADS
+ list->lock = PR_NewLock();
+ if (list->lock == NULL) {
+ PORT_Free(list);
+ return NULL;
+ }
+#else
+ list->lock = NULL;
+#endif
+
+ return list;
+}
+
+/*
+ * free a list element when all the references go away.
+ */
+static void
+pk11_FreeListElement(PK11SlotList *list, PK11SlotListElement *le)
+{
+ PRBool freeit = PR_FALSE;
+
+ PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));)
+ if (le->refCount-- == 1) {
+ freeit = PR_TRUE;
+ }
+ PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));)
+ if (freeit) {
+ PK11_FreeSlot(le->slot);
+ PORT_Free(le);
+ }
+}
+
+/*
+ * if we are freeing the list, we must be the only ones with a pointer
+ * to the list.
+ */
+void
+PK11_FreeSlotList(PK11SlotList *list)
+{
+ PK11SlotListElement *le, *next ;
+ if (list == NULL) return;
+
+ for (le = list->head ; le; le = next) {
+ next = le->next;
+ pk11_FreeListElement(list,le);
+ }
+ PK11_USE_THREADS(PR_DestroyLock((PRLock *)(list->lock));)
+ PORT_Free(list);
+}
+
+/*
+ * add a slot to a list
+ */
+SECStatus
+PK11_AddSlotToList(PK11SlotList *list,PK11SlotInfo *slot)
+{
+ PK11SlotListElement *le;
+
+ le = (PK11SlotListElement *) PORT_Alloc(sizeof(PK11SlotListElement));
+ if (le == NULL) return SECFailure;
+
+ le->slot = PK11_ReferenceSlot(slot);
+ le->prev = NULL;
+ le->refCount = 1;
+ PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));)
+ if (list->head) list->head->prev = le; else list->tail = le;
+ le->next = list->head;
+ list->head = le;
+ PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));)
+
+ return SECSuccess;
+}
+
+/*
+ * remove a slot entry from the list
+ */
+SECStatus
+PK11_DeleteSlotFromList(PK11SlotList *list,PK11SlotListElement *le)
+{
+ PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));)
+ if (le->prev) le->prev->next = le->next; else list->head = le->next;
+ if (le->next) le->next->prev = le->prev; else list->tail = le->prev;
+ le->next = le->prev = NULL;
+ PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));)
+ pk11_FreeListElement(list,le);
+ return SECSuccess;
+}
+
+/*
+ * Move a list to the end of the target list. NOTE: There is no locking
+ * here... This assumes BOTH lists are private copy lists.
+ */
+SECStatus
+PK11_MoveListToList(PK11SlotList *target,PK11SlotList *src)
+{
+ if (src->head == NULL) return SECSuccess;
+
+ if (target->tail == NULL) {
+ target->head = src->head;
+ } else {
+ target->tail->next = src->head;
+ }
+ src->head->prev = target->tail;
+ target->tail = src->tail;
+ src->head = src->tail = NULL;
+ return SECSuccess;
+}
+
+/*
+ * get an element safely from the list. This just makes sure that if
+ * this element is not deleted while we deal with it.
+ */
+PK11SlotListElement *
+PK11_GetFirstSafe(PK11SlotList *list)
+{
+ PK11SlotListElement *le;
+
+ PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));)
+ le = list->head;
+ if (le != NULL) (le)->refCount++;
+ PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));)
+ return le;
+}
+
+/*
+ * NOTE: if this element gets deleted, we can no longer safely traverse using
+ * it's pointers. We can either terminate the loop, or restart from the
+ * beginning. This is controlled by the restart option.
+ */
+PK11SlotListElement *
+PK11_GetNextSafe(PK11SlotList *list, PK11SlotListElement *le, PRBool restart)
+{
+ PK11SlotListElement *new_le;
+ PK11_USE_THREADS(PR_Lock((PRLock *)(list->lock));)
+ new_le = le->next;
+ if (le->next == NULL) {
+ /* if the prev and next fields are NULL then either this element
+ * has been removed and we need to walk the list again (if restart
+ * is true) or this was the only element on the list */
+ if ((le->prev == NULL) && restart && (list->head != le)) {
+ new_le = list->head;
+ }
+ }
+ if (new_le) new_le->refCount++;
+ PK11_USE_THREADS(PR_Unlock((PRLock *)(list->lock));)
+ pk11_FreeListElement(list,le);
+ return new_le;
+}
+
+
+/*
+ * Find the element that holds this slot
+ */
+PK11SlotListElement *
+PK11_FindSlotElement(PK11SlotList *list,PK11SlotInfo *slot)
+{
+ PK11SlotListElement *le;
+
+ for (le = PK11_GetFirstSafe(list); le;
+ le = PK11_GetNextSafe(list,le,PR_TRUE)) {
+ if (le->slot == slot) return le;
+ }
+ return NULL;
+}
+
+/************************************************************
+ * Generic Slot Utilities
+ ************************************************************/
+/*
+ * Create a new slot structure
+ */
+PK11SlotInfo *
+PK11_NewSlotInfo(void)
+{
+ PK11SlotInfo *slot;
+
+ slot = (PK11SlotInfo *)PORT_Alloc(sizeof(PK11SlotInfo));
+ if (slot == NULL) return slot;
+
+#ifdef PKCS11_USE_THREADS
+ slot->refLock = PR_NewLock();
+ if (slot->refLock == NULL) {
+ PORT_Free(slot);
+ return slot;
+ }
+ slot->sessionLock = PR_NewLock();
+ if (slot->sessionLock == NULL) {
+ PR_DestroyLock(slot->refLock);
+ PORT_Free(slot);
+ return slot;
+ }
+#else
+ slot->sessionLock = NULL;
+ slot->refLock = NULL;
+#endif
+ slot->functionList = NULL;
+ slot->needTest = PR_TRUE;
+ slot->isPerm = PR_FALSE;
+ slot->isHW = PR_FALSE;
+ slot->isInternal = PR_FALSE;
+ slot->isThreadSafe = PR_FALSE;
+ slot->disabled = PR_FALSE;
+ slot->series = 0;
+ slot->wrapKey = 0;
+ slot->wrapMechanism = CKM_INVALID_MECHANISM;
+ slot->refKeys[0] = CK_INVALID_KEY;
+ slot->reason = PK11_DIS_NONE;
+ slot->readOnly = PR_TRUE;
+ slot->needLogin = PR_FALSE;
+ slot->hasRandom = PR_FALSE;
+ slot->defRWSession = PR_FALSE;
+ slot->flags = 0;
+ slot->session = CK_INVALID_SESSION;
+ slot->slotID = 0;
+ slot->defaultFlags = 0;
+ slot->refCount = 1;
+ slot->askpw = 0;
+ slot->timeout = 0;
+ slot->mechanismList = NULL;
+ slot->mechanismCount = 0;
+ slot->cert_array = NULL;
+ slot->cert_count = 0;
+ slot->slot_name[0] = 0;
+ slot->token_name[0] = 0;
+ PORT_Memset(slot->serial,' ',sizeof(slot->serial));
+ slot->module = NULL;
+ slot->authTransact = 0;
+ slot->authTime = LL_ZERO;
+ slot->minPassword = 0;
+ slot->maxPassword = 0;
+ return slot;
+}
+
+/* create a new reference to a slot so it doesn't go away */
+PK11SlotInfo *
+PK11_ReferenceSlot(PK11SlotInfo *slot)
+{
+ PK11_USE_THREADS(PR_Lock(slot->refLock);)
+ slot->refCount++;
+ PK11_USE_THREADS(PR_Unlock(slot->refLock);)
+ return slot;
+}
+
+/* Destroy all info on a slot we have built up */
+void
+PK11_DestroySlot(PK11SlotInfo *slot)
+{
+ /* first free up all the sessions on this slot */
+ if (slot->functionList) {
+ PK11_GETTAB(slot)->C_CloseAllSessions(slot->slotID);
+ }
+
+ /* now free up all the certificates we grabbed on this slot */
+ PK11_FreeSlotCerts(slot);
+
+ /* finally Tell our parent module that we've gone away so it can unload */
+ if (slot->module) {
+ SECMOD_SlotDestroyModule(slot->module,PR_TRUE);
+ }
+#ifdef PKCS11_USE_THREADS
+ if (slot->refLock) {
+ PR_DestroyLock(slot->refLock);
+ slot->refLock = NULL;
+ }
+ if (slot->sessionLock) {
+ PR_DestroyLock(slot->sessionLock);
+ slot->sessionLock = NULL;
+ }
+#endif
+
+ /* ok, well not quit finally... now we free the memory */
+ PORT_Free(slot);
+}
+
+
+/* We're all done with the slot, free it */
+void
+PK11_FreeSlot(PK11SlotInfo *slot)
+{
+ PRBool freeit = PR_FALSE;
+
+ PK11_USE_THREADS(PR_Lock(slot->refLock);)
+ if (slot->refCount-- == 1) freeit = PR_TRUE;
+ PK11_USE_THREADS(PR_Unlock(slot->refLock);)
+
+ if (freeit) PK11_DestroySlot(slot);
+}
+
+void
+PK11_EnterSlotMonitor(PK11SlotInfo *slot) {
+ PR_Lock(slot->sessionLock);
+}
+
+void
+PK11_ExitSlotMonitor(PK11SlotInfo *slot) {
+ PR_Unlock(slot->sessionLock);
+}
+
+/***********************************************************
+ * Functions to find specific slots.
+ ***********************************************************/
+PK11SlotInfo *
+PK11_FindSlotByName(char *name)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules = SECMOD_GetDefaultModuleList();
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ int i;
+ PK11SlotInfo *slot = NULL;
+
+ if ((name == NULL) || (*name == 0)) {
+ return PK11_GetInternalKeySlot();
+ }
+
+ /* work through all the slots */
+ SECMOD_GetReadLock(moduleLock);
+ for(mlp = modules; mlp != NULL; mlp = mlp->next) {
+ for (i=0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *tmpSlot = mlp->module->slots[i];
+ if (PK11_IsPresent(tmpSlot)) {
+ if (PORT_Strcmp(tmpSlot->token_name,name) == 0) {
+ slot = PK11_ReferenceSlot(tmpSlot);
+ break;
+ }
+ }
+ }
+ if (slot != NULL) break;
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+
+ return slot;
+}
+
+
+PK11SlotInfo *
+PK11_FindSlotBySerial(char *serial)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules = SECMOD_GetDefaultModuleList();
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ int i;
+ PK11SlotInfo *slot = NULL;
+
+ /* work through all the slots */
+ SECMOD_GetReadLock(moduleLock);
+ for(mlp = modules; mlp != NULL; mlp = mlp->next) {
+ for (i=0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *tmpSlot = mlp->module->slots[i];
+ if (PK11_IsPresent(tmpSlot)) {
+ if (PORT_Memcmp(tmpSlot->serial,serial,
+ sizeof(tmpSlot->serial)) == 0) {
+ slot = PK11_ReferenceSlot(tmpSlot);
+ break;
+ }
+ }
+ }
+ if (slot != NULL) break;
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ if (slot == NULL) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+
+ return slot;
+}
+
+
+
+
+/***********************************************************
+ * Password Utilities
+ ***********************************************************/
+/*
+ * Check the user's password. Log into the card if it's correct.
+ * succeed if the user is already logged in.
+ */
+SECStatus
+pk11_CheckPassword(PK11SlotInfo *slot,char *pw)
+{
+ int len = PORT_Strlen(pw);
+ CK_RV crv;
+ SECStatus rv;
+ int64 currtime = PR_Now();
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER,
+ (unsigned char *)pw,len);
+ PK11_ExitSlotMonitor(slot);
+ switch (crv) {
+ /* if we're already logged in, we're good to go */
+ case CKR_OK:
+ slot->authTransact = PK11_Global.transaction;
+ case CKR_USER_ALREADY_LOGGED_IN:
+ slot->authTime = currtime;
+ rv = SECSuccess;
+ break;
+ case CKR_PIN_INCORRECT:
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
+ break;
+ default:
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure; /* some failure we can't fix by retrying */
+ }
+ return rv;
+}
+
+/*
+ * Check the user's password. Logout before hand to make sure that
+ * we are really checking the password.
+ */
+SECStatus
+PK11_CheckUserPassword(PK11SlotInfo *slot,char *pw)
+{
+ int len = PORT_Strlen(pw);
+ CK_RV crv;
+ SECStatus rv;
+ int64 currtime = PR_Now();
+
+ /* force a logout */
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Logout(slot->session);
+
+ crv = PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER,
+ (unsigned char *)pw,len);
+ PK11_ExitSlotMonitor(slot);
+ switch (crv) {
+ /* if we're already logged in, we're good to go */
+ case CKR_OK:
+ slot->authTransact = PK11_Global.transaction;
+ slot->authTime = currtime;
+ rv = SECSuccess;
+ break;
+ case CKR_PIN_INCORRECT:
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
+ break;
+ default:
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure; /* some failure we can't fix by retrying */
+ }
+ return rv;
+}
+
+SECStatus
+PK11_Logout(PK11SlotInfo *slot)
+{
+ CK_RV crv;
+
+ /* force a logout */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_Logout(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * transaction stuff is for when we test for the need to do every
+ * time auth to see if we already did it for this slot/transaction
+ */
+void PK11_StartAuthTransaction(void)
+{
+PK11_Global.transaction++;
+PK11_Global.inTransaction = PR_TRUE;
+}
+
+void PK11_EndAuthTransaction(void)
+{
+PK11_Global.transaction++;
+PK11_Global.inTransaction = PR_FALSE;
+}
+
+/*
+ * before we do a private key op, we check to see if we
+ * need to reauthenticate.
+ */
+void
+PK11_HandlePasswordCheck(PK11SlotInfo *slot,void *wincx)
+{
+ int askpw = slot->askpw;
+ PRBool NeedAuth = PR_FALSE;
+
+ if (!slot->needLogin) return;
+
+ if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
+ PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
+
+ if (def_slot) {
+ askpw = def_slot->askpw;
+ PK11_FreeSlot(def_slot);
+ }
+ }
+
+ /* timeouts are handled by isLoggedIn */
+ if (!PK11_IsLoggedIn(slot,wincx)) {
+ NeedAuth = PR_TRUE;
+ } else if (slot->askpw == -1) {
+ if (!PK11_Global.inTransaction ||
+ (PK11_Global.transaction != slot->authTransact)) {
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Logout(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ NeedAuth = PR_TRUE;
+ }
+ }
+ if (NeedAuth) PK11_DoPassword(slot,PR_TRUE,wincx);
+}
+
+void
+PK11_SlotDBUpdate(PK11SlotInfo *slot)
+{
+ SECMOD_AddPermDB(slot->module);
+}
+
+/*
+ * set new askpw and timeout values
+ */
+void
+PK11_SetSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout)
+{
+ slot->askpw = askpw;
+ slot->timeout = timeout;
+ slot->defaultFlags |= PK11_OWN_PW_DEFAULTS;
+ PK11_SlotDBUpdate(slot);
+}
+
+/*
+ * Get the askpw and timeout values for this slot
+ */
+void
+PK11_GetSlotPWValues(PK11SlotInfo *slot,int *askpw, int *timeout)
+{
+ *askpw = slot->askpw;
+ *timeout = slot->timeout;
+
+ if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
+ PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
+
+ if (def_slot) {
+ *askpw = def_slot->askpw;
+ *timeout = def_slot->timeout;
+ PK11_FreeSlot(def_slot);
+ }
+ }
+}
+
+/*
+ * make sure a slot is authenticated...
+ */
+SECStatus
+PK11_Authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx) {
+ if (slot->needLogin && !PK11_IsLoggedIn(slot,wincx)) {
+ return PK11_DoPassword(slot,loadCerts,wincx);
+ }
+ return SECSuccess;
+}
+
+/*
+ * notification stub. If we ever get interested in any events that
+ * the pkcs11 functions may pass back to use, we can catch them here...
+ * currently pdata is a slotinfo structure.
+ */
+CK_RV pk11_notify(CK_SESSION_HANDLE session, CK_NOTIFICATION event,
+ CK_VOID_PTR pdata)
+{
+ return CKR_OK;
+}
+
+
+/*
+ * grab a new RW session
+ * !!! has a side effect of grabbing the Monitor if either the slot's default
+ * session is RW or the slot is not thread safe. Monitor is release in function
+ * below
+ */
+CK_SESSION_HANDLE PK11_GetRWSession(PK11SlotInfo *slot)
+{
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+
+ if (!slot->isThreadSafe || slot->defRWSession) PK11_EnterSlotMonitor(slot);
+ if (slot->defRWSession) return slot->session;
+
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ CKF_RW_SESSION|CKF_SERIAL_SESSION,
+ slot, pk11_notify,&rwsession);
+ if (crv == CKR_SESSION_COUNT) {
+ PK11_GETTAB(slot)->C_CloseSession(slot->session);
+ slot->session = CK_INVALID_SESSION;
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ CKF_RW_SESSION|CKF_SERIAL_SESSION,
+ slot,pk11_notify,&rwsession);
+ }
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ if (slot->session == CK_INVALID_SESSION) {
+ PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION,
+ slot,pk11_notify,&slot->session);
+ }
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ return CK_INVALID_SESSION;
+ }
+
+ return rwsession;
+}
+
+PRBool
+PK11_RWSessionHasLock(PK11SlotInfo *slot,CK_SESSION_HANDLE session_handle) {
+ return (PRBool)(!slot->isThreadSafe || slot->defRWSession);
+}
+
+/*
+ * close the rwsession and restore our readonly session
+ * !!! has a side effect of releasing the Monitor if either the slot's default
+ * session is RW or the slot is not thread safe.
+ */
+void
+PK11_RestoreROSession(PK11SlotInfo *slot,CK_SESSION_HANDLE rwsession)
+{
+ if (slot->defRWSession) {
+ PK11_ExitSlotMonitor(slot);
+ return;
+ }
+ PK11_GETTAB(slot)->C_CloseSession(rwsession);
+ if (slot->session == CK_INVALID_SESSION) {
+ PK11_GETTAB(slot)->C_OpenSession(slot->slotID,CKF_SERIAL_SESSION,
+ slot,pk11_notify,&slot->session);
+ }
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+}
+
+/*
+ * NOTE: this assumes that we are logged out of the card before hand
+ */
+SECStatus
+PK11_CheckSSOPassword(PK11SlotInfo *slot, char *ssopw)
+{
+ CK_SESSION_HANDLE rwsession;
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ int len = PORT_Strlen(ssopw);
+
+ /* get a rwsession */
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_SESSION) return rv;
+
+ /* check the password */
+ crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO,
+ (unsigned char *)ssopw,len);
+ switch (crv) {
+ /* if we're already logged in, we're good to go */
+ case CKR_OK:
+ rv = SECSuccess;
+ break;
+ case CKR_PIN_INCORRECT:
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECWouldBlock; /* everything else is ok, only the pin is bad */
+ break;
+ default:
+ PORT_SetError(PK11_MapError(crv));
+ rv = SECFailure; /* some failure we can't fix by retrying */
+ }
+ PK11_GETTAB(slot)->C_Logout(rwsession);
+ /* release rwsession */
+ PK11_RestoreROSession(slot,rwsession);
+ return rv;
+}
+
+/*
+ * make sure the password conforms to your token's requirements.
+ */
+SECStatus
+PK11_VerifyPW(PK11SlotInfo *slot,char *pw)
+{
+ int len = PORT_Strlen(pw);
+
+ if ((slot->minPassword > len) || (slot->maxPassword < len)) {
+ PORT_SetError(SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * initialize a user PIN Value
+ */
+SECStatus
+PK11_InitPin(PK11SlotInfo *slot,char *ssopw, char *userpw)
+{
+ CK_SESSION_HANDLE rwsession = CK_INVALID_SESSION;
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ int len;
+ int ssolen;
+
+ if (userpw == NULL) userpw = "";
+ if (ssopw == NULL) ssopw = "";
+
+ len = PORT_Strlen(userpw);
+ ssolen = PORT_Strlen(ssopw);
+
+ /* get a rwsession */
+ rwsession = PK11_GetRWSession(slot);
+ if (rwsession == CK_INVALID_SESSION) goto done;
+
+ /* check the password */
+ crv = PK11_GETTAB(slot)->C_Login(rwsession,CKU_SO,
+ (unsigned char *)ssopw,ssolen);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ goto done;
+ }
+
+ crv = PK11_GETTAB(slot)->C_InitPIN(rwsession,(unsigned char *)userpw,len);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ } else {
+ rv = SECSuccess;
+ }
+
+done:
+ PK11_GETTAB(slot)->C_Logout(rwsession);
+ PK11_RestoreROSession(slot,rwsession);
+ if (rv == SECSuccess) {
+ /* update our view of the world */
+ PK11_InitToken(slot,PR_TRUE);
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Login(slot->session,CKU_USER,
+ (unsigned char *)userpw,len);
+ PK11_ExitSlotMonitor(slot);
+ }
+ return rv;
+}
+
+/*
+ * Change an existing user password
+ */
+SECStatus
+PK11_ChangePW(PK11SlotInfo *slot,char *oldpw, char *newpw)
+{
+ CK_RV crv;
+ SECStatus rv = SECFailure;
+ int newLen;
+ int oldLen;
+ CK_SESSION_HANDLE rwsession;
+
+ if (newpw == NULL) newpw = "";
+ if (oldpw == NULL) oldpw = "";
+ newLen = PORT_Strlen(newpw);
+ oldLen = PORT_Strlen(oldpw);
+
+ /* get a rwsession */
+ rwsession = PK11_GetRWSession(slot);
+
+ crv = PK11_GETTAB(slot)->C_SetPIN(rwsession,
+ (unsigned char *)oldpw,oldLen,(unsigned char *)newpw,newLen);
+ if (crv == CKR_OK) {
+ rv = SECSuccess;
+ } else {
+ PORT_SetError(PK11_MapError(crv));
+ }
+
+ PK11_RestoreROSession(slot,rwsession);
+
+ /* update our view of the world */
+ PK11_InitToken(slot,PR_TRUE);
+ return rv;
+}
+
+static char *
+pk11_GetPassword(PK11SlotInfo *slot, PRBool retry, void * wincx)
+{
+ if (PK11_Global.getPass == NULL) return NULL;
+ return (*PK11_Global.getPass)(slot, retry, wincx);
+}
+
+void
+PK11_SetPasswordFunc(PK11PasswordFunc func)
+{
+ PK11_Global.getPass = func;
+}
+
+void
+PK11_SetVerifyPasswordFunc(PK11VerifyPasswordFunc func)
+{
+ PK11_Global.verifyPass = func;
+}
+
+void
+PK11_SetIsLoggedInFunc(PK11IsLoggedInFunc func)
+{
+ PK11_Global.isLoggedIn = func;
+}
+
+
+/*
+ * authenticate to a slot. This loops until we can't recover, the user
+ * gives up, or we succeed. If we're already logged in and this function
+ * is called we will still prompt for a password, but we will probably
+ * succeed no matter what the password was (depending on the implementation
+ * of the PKCS 11 module.
+ */
+SECStatus
+PK11_DoPassword(PK11SlotInfo *slot, PRBool loadCerts, void *wincx)
+{
+ SECStatus rv = SECFailure;
+ char * password;
+ PRBool attempt = PR_FALSE;
+
+ if (PK11_NeedUserInit(slot)) {
+ PORT_SetError(SEC_ERROR_IO);
+ return SECFailure;
+ }
+
+
+ /*
+ * Central server type applications which control access to multiple
+ * slave applications to single crypto devices need to virtuallize the
+ * login state. This is done by a callback out of PK11_IsLoggedIn and
+ * here. If we are actually logged in, then we got here because the
+ * higher level code told us that the particular client application may
+ * still need to be logged in. If that is the case, we simply tell the
+ * server code that it should now verify the clients password and tell us
+ * the results.
+ */
+ if (PK11_IsLoggedIn(slot,NULL) &&
+ (PK11_Global.verifyPass != NULL)) {
+ if (!PK11_Global.verifyPass(slot,wincx)) {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ return SECFailure;
+ }
+ return SECSuccess;
+ }
+
+ /* get the password. This can drop out of the while loop
+ * for the following reasons:
+ * (1) the user refused to enter a password.
+ * (return error to caller)
+ * (2) the token user password is disabled [usually due to
+ * too many failed authentication attempts].
+ * (return error to caller)
+ * (3) the password was successful.
+ */
+ while ((password = pk11_GetPassword(slot, attempt, wincx)) != NULL) {
+ attempt = PR_TRUE;
+ rv = pk11_CheckPassword(slot,password);
+ PORT_Memset(password, 0, PORT_Strlen(password));
+ PORT_Free(password);
+ if (rv != SECWouldBlock) break;
+ }
+ if (rv == SECSuccess) {
+ if ((loadCerts) && (!slot->isInternal) && (slot->cert_count == 0)) {
+ PK11_ReadSlotCerts(slot);
+ }
+ rv = pk11_CheckVerifyTest(slot);
+ } else if (!attempt) PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ return rv;
+}
+
+void PK11_LogoutAll(void)
+{
+ SECMODListLock *lock = SECMOD_GetDefaultModuleListLock();
+ SECMODModuleList *modList = SECMOD_GetDefaultModuleList();
+ SECMODModuleList *mlp = NULL;
+ int i;
+
+ SECMOD_GetReadLock(lock);
+ /* find the number of entries */
+ for (mlp = modList; mlp != NULL; mlp = mlp->next) {
+ for (i=0; i < mlp->module->slotCount; i++) {
+ PK11_Logout(mlp->module->slots[i]);
+ }
+ }
+
+ SECMOD_ReleaseReadLock(lock);
+}
+
+int
+PK11_GetMinimumPwdLength(PK11SlotInfo *slot)
+{
+ return ((int)slot->minPassword);
+}
+
+/************************************************************
+ * Manage the built-In Slot Lists
+ ************************************************************/
+
+/* Init the static built int slot list (should actually integrate
+ * with PK11_NewSlotList */
+static void
+pk11_initSlotList(PK11SlotList *list)
+{
+#ifdef PKCS11_USE_THREADS
+ list->lock = PR_NewLock();
+#else
+ list->lock = NULL;
+#endif
+ list->head = NULL;
+}
+
+/* initialize the system slotlists */
+SECStatus
+PK11_InitSlotLists(void)
+{
+ pk11_initSlotList(&pk11_desSlotList);
+ pk11_initSlotList(&pk11_rc4SlotList);
+ pk11_initSlotList(&pk11_rc2SlotList);
+ pk11_initSlotList(&pk11_rc5SlotList);
+ pk11_initSlotList(&pk11_md5SlotList);
+ pk11_initSlotList(&pk11_md2SlotList);
+ pk11_initSlotList(&pk11_sha1SlotList);
+ pk11_initSlotList(&pk11_rsaSlotList);
+ pk11_initSlotList(&pk11_dsaSlotList);
+ pk11_initSlotList(&pk11_dhSlotList);
+ pk11_initSlotList(&pk11_idea);
+ pk11_initSlotList(&pk11_random);
+ return SECSuccess;
+}
+
+/* return a system slot list based on mechanism */
+PK11SlotList *
+PK11_GetSlotList(CK_MECHANISM_TYPE type)
+{
+ switch (type) {
+ case CKM_DES_CBC:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_DES3_CBC:
+ return &pk11_desSlotList;
+ case CKM_RC4:
+ return &pk11_rc4SlotList;
+ case CKM_RC5_CBC:
+ return &pk11_rc5SlotList;
+ case CKM_SHA_1:
+ return &pk11_sha1SlotList;
+ case CKM_MD5:
+ return &pk11_md5SlotList;
+ case CKM_MD2:
+ return &pk11_md2SlotList;
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ return &pk11_rc2SlotList;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ case CKM_RSA_X_509:
+ return &pk11_rsaSlotList;
+ case CKM_DSA:
+ return &pk11_dsaSlotList;
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ case CKM_DH_PKCS_DERIVE:
+ return &pk11_dhSlotList;
+ case CKM_IDEA_CBC:
+ case CKM_IDEA_ECB:
+ return &pk11_idea;
+ case CKM_FAKE_RANDOM:
+ return &pk11_random;
+ }
+ return NULL;
+}
+
+/*
+ * load the static SlotInfo structures used to select a PKCS11 slot.
+ * preSlotInfo has a list of all the default flags for the slots on this
+ * module.
+ */
+void
+PK11_LoadSlotList(PK11SlotInfo *slot, PK11PreSlotInfo *psi, int count)
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ if (psi[i].slotID == slot->slotID)
+ break;
+ }
+
+ if (i == count) return;
+
+ slot->defaultFlags = psi[i].defaultFlags;
+ slot->askpw = psi[i].askpw;
+ slot->timeout = psi[i].timeout;
+
+ /* if the slot is already disabled, don't load them into the
+ * default slot lists. We get here so we can save the default
+ * list value. */
+ if (slot->disabled) return;
+
+ /* if the user has disabled us, don't load us in */
+ if (slot->defaultFlags & PK11_DISABLE_FLAG) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_USER_SELECTED;
+ /* free up sessions and things?? */
+ return;
+ }
+
+ for (i=0; i < sizeof(PK11_DefaultArray)/sizeof(PK11_DefaultArray[0]);
+ i++) {
+ if (slot->defaultFlags & PK11_DefaultArray[i].flag) {
+ CK_MECHANISM_TYPE mechanism = PK11_DefaultArray[i].mechanism;
+ PK11SlotList *slotList = PK11_GetSlotList(mechanism);
+
+ if (slotList) PK11_AddSlotToList(slotList,slot);
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * update a slot to its new attribute according to the slot list
+ * returns: SECSuccess if nothing to do or add/delete is successful
+ */
+SECStatus
+PK11_UpdateSlotAttribute(PK11SlotInfo *slot, PK11DefaultArrayEntry *entry,
+ PRBool add)
+ /* add: PR_TRUE if want to turn on */
+{
+ SECStatus result = SECSuccess;
+ PK11SlotList *slotList = PK11_GetSlotList(entry->mechanism);
+
+ if (add) { /* trying to turn on a mechanism */
+
+ /* turn on the default flag in the slot */
+ slot->defaultFlags |= entry->flag;
+
+ /* add this slot to the list */
+ if (slotList!=NULL)
+ result = PK11_AddSlotToList(slotList, slot);
+
+ } else { /* trying to turn off */
+
+ /* turn OFF the flag in the slot */
+ slot->defaultFlags &= ~entry->flag;
+
+ if (slotList) {
+ /* find the element in the list & delete it */
+ PK11SlotListElement *le = PK11_FindSlotElement(slotList, slot);
+
+ /* remove the slot from the list */
+ if (le)
+ result = PK11_DeleteSlotFromList(slotList, le);
+ }
+ }
+ return result;
+}
+
+/*
+ * clear a slot off of all of it's default list
+ */
+void
+PK11_ClearSlotList(PK11SlotInfo *slot)
+{
+ int i;
+
+ if (slot->disabled) return;
+ if (slot->defaultFlags == 0) return;
+
+ for (i=0; i < sizeof(PK11_DefaultArray)/sizeof(PK11_DefaultArray[0]);
+ i++) {
+ if (slot->defaultFlags & PK11_DefaultArray[i].flag) {
+ CK_MECHANISM_TYPE mechanism = PK11_DefaultArray[i].mechanism;
+ PK11SlotList *slotList = PK11_GetSlotList(mechanism);
+ PK11SlotListElement *le = NULL;
+
+ if (slotList) le = PK11_FindSlotElement(slotList,slot);
+
+ if (le) {
+ PK11_DeleteSlotFromList(slotList,le);
+ pk11_FreeListElement(slotList,le);
+ }
+ }
+ }
+}
+
+
+/******************************************************************
+ * Slot initialization
+ ******************************************************************/
+/*
+ * turn a PKCS11 Static Label into a string
+ */
+char *
+PK11_MakeString(PRArenaPool *arena,char *space,
+ char *staticString,int stringLen)
+{
+ int i;
+ char *newString;
+ for(i=(stringLen-1); i >= 0; i--) {
+ if (staticString[i] != ' ') break;
+ }
+ /* move i to point to the last space */
+ i++;
+ if (arena) {
+ newString = (char*)PORT_ArenaAlloc(arena,i+1 /* space for NULL */);
+ } else if (space) {
+ newString = space;
+ } else {
+ newString = (char*)PORT_Alloc(i+1 /* space for NULL */);
+ }
+ if (newString == NULL) return NULL;
+
+ if (i) PORT_Memcpy(newString,staticString, i);
+ newString[i] = 0;
+
+ return newString;
+}
+
+/*
+ * verify that slot implements Mechanism mech properly by checking against
+ * our internal implementation
+ */
+PRBool
+PK11_VerifyMechanism(PK11SlotInfo *slot,PK11SlotInfo *intern,
+ CK_MECHANISM_TYPE mech, SECItem *data, SECItem *iv)
+{
+ PK11Context *test = NULL, *reference = NULL;
+ PK11SymKey *symKey = NULL, *testKey = NULL;
+ SECItem *param = NULL;
+ unsigned char encTest[8];
+ unsigned char encRef[8];
+ int outLenTest,outLenRef;
+ int key_size = 0;
+ SECStatus rv;
+
+ if ((mech == CKM_RC2_CBC) || (mech == CKM_RC2_ECB) || (mech == CKM_RC4)) {
+ key_size = 16;
+ }
+
+ /* initialize the mechanism parameter */
+ param = PK11_ParamFromIV(mech,iv);
+ if (param == NULL) goto loser;
+
+ /* load the keys and contexts */
+ symKey = PK11_KeyGen(intern,mech,NULL, key_size, NULL);
+ if (symKey == NULL) goto loser;
+
+ reference = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, symKey, param);
+ if (reference == NULL) goto loser;
+
+ testKey = pk11_CopyToSlot(slot, mech, CKA_ENCRYPT, symKey);
+ if (testKey == NULL) goto loser;
+
+ test = PK11_CreateContextBySymKey(mech, CKA_ENCRYPT, testKey, param);
+ if (test == NULL) goto loser;
+ SECITEM_FreeItem(param,PR_TRUE); param = NULL;
+
+ /* encrypt the test data */
+ rv = PK11_CipherOp(test,encTest,&outLenTest,sizeof(encTest),
+ data->data,data->len);
+ if (rv != SECSuccess) goto loser;
+ rv = PK11_CipherOp(reference,encRef,&outLenRef,sizeof(encRef),
+ data->data,data->len);
+ if (rv != SECSuccess) goto loser;
+
+ PK11_DestroyContext(reference,PR_TRUE); reference = NULL;
+ PK11_DestroyContext(test,PR_TRUE); test = NULL;
+
+ if (outLenTest != outLenRef) goto loser;
+ if (PORT_Memcmp(encTest, encRef, outLenTest) != 0) goto loser;
+
+ return PR_TRUE;
+
+loser:
+ if (test) PK11_DestroyContext(test,PR_TRUE);
+ if (symKey) PK11_FreeSymKey(symKey);
+ if (testKey) PK11_FreeSymKey(testKey);
+ if (reference) PK11_DestroyContext(reference,PR_TRUE);
+ if (param) SECITEM_FreeItem(param,PR_TRUE);
+
+ return PR_FALSE;
+}
+
+/*
+ * this code verifies that the advertised mechanisms are what they
+ * seem to be.
+ */
+#define MAX_MECH_LIST_SIZE 30 /* we only know of about 30 odd mechanisms */
+PRBool
+PK11_VerifySlotMechanisms(PK11SlotInfo *slot)
+{
+ CK_MECHANISM_TYPE mechListArray[MAX_MECH_LIST_SIZE];
+ CK_MECHANISM_TYPE *mechList = mechListArray;
+ static SECItem data;
+ static SECItem iv;
+ static SECItem key;
+ static unsigned char dataV[8];
+ static unsigned char ivV[8];
+ static unsigned char keyV[8];
+ static PRBool generated = PR_FALSE;
+ CK_ULONG count;
+ int i;
+ CK_RV crv;
+
+ PRBool alloced = PR_FALSE;
+ PK11SlotInfo *intern = PK11_GetInternalSlot();
+
+ /* if we couldn't initialize an internal module,
+ * we can't check external ones */
+ if (intern == NULL) return PR_FALSE;
+
+ /* first get the count of mechanisms */
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,NULL,&count);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PK11_FreeSlot(intern);
+ return PR_FALSE;
+ }
+
+
+ /* don't blow up just because the card supports more mechanisms than
+ * we know about, just alloc space for them */
+ if (count > MAX_MECH_LIST_SIZE) {
+ mechList = (CK_MECHANISM_TYPE *)
+ PORT_Alloc(count *sizeof(CK_MECHANISM_TYPE));
+ alloced = PR_TRUE;
+ if (mechList == NULL) return PR_FALSE;
+ }
+ /* get the list */
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv =PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID, mechList, &count);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ if (alloced) PORT_Free(mechList);
+ PK11_FreeSlot(intern);
+ return PR_FALSE;
+ }
+
+ if (!generated) {
+ data.data = dataV;
+ data.len = sizeof(dataV);
+ iv.data = ivV;
+ iv.len = sizeof(ivV);
+ /* ok, this is a cheat, we know our internal random number generater
+ * is thread safe */
+ PK11_GETTAB(intern)->C_GenerateRandom(intern->session,
+ data.data, data.len);
+ PK11_GETTAB(intern)->C_GenerateRandom(intern->session,
+ iv.data, iv.len);
+ }
+ for (i=0; i < (int) count; i++) {
+ switch (mechList[i]) {
+ case CKM_DES_CBC:
+ case CKM_DES_ECB:
+ case CKM_RC4:
+ case CKM_RC2_CBC:
+ case CKM_RC2_ECB:
+ if (!PK11_VerifyMechanism(slot,intern,mechList[i],&data,&iv)){
+ if (alloced) PORT_Free(mechList);
+ PK11_FreeSlot(intern);
+ return PR_FALSE;
+ }
+ }
+ }
+ if (alloced) PORT_Free(mechList);
+ PK11_FreeSlot(intern);
+ return PR_TRUE;
+}
+
+/*
+ * See if we need to run the verify test, do so if necessary. If we fail,
+ * disable the slot.
+ */
+SECStatus
+pk11_CheckVerifyTest(PK11SlotInfo *slot)
+{
+ PK11_EnterSlotMonitor(slot);
+ if (slot->needTest) {
+ slot->needTest = PR_FALSE;
+ PK11_ExitSlotMonitor(slot);
+ if (!PK11_VerifySlotMechanisms(slot)) {
+ (void)PK11_GETTAB(slot)->C_CloseSession(slot->session);
+ slot->session = CK_INVALID_SESSION;
+ PK11_ClearSlotList(slot);
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_TOKEN_VERIFY_FAILED;
+ slot->needTest = PR_TRUE;
+ PORT_SetError(SEC_ERROR_IO);
+ return SECFailure;
+ }
+ } else {
+ PK11_ExitSlotMonitor(slot);
+ }
+ return SECSuccess;
+}
+
+/*
+ * Reads in the slots mechanism list for later use
+ */
+SECStatus
+PK11_ReadMechanismList(PK11SlotInfo *slot)
+{
+ CK_ULONG count;
+ CK_RV crv;
+
+ if (slot->mechanismList) {
+ PORT_Free(slot->mechanismList);
+ slot->mechanismList = NULL;
+ }
+ slot->mechanismCount = 0;
+
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,NULL,&count);
+ if (crv != CKR_OK) {
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ slot->mechanismList = (CK_MECHANISM_TYPE *)
+ PORT_Alloc(count *sizeof(CK_MECHANISM_TYPE));
+ if (slot->mechanismList == NULL) {
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ return SECFailure;
+ }
+ crv = PK11_GETTAB(slot)->C_GetMechanismList(slot->slotID,
+ slot->mechanismList, &count);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_Free(slot->mechanismList);
+ slot->mechanismList = NULL;
+ PORT_SetError(PK11_MapError(crv));
+ return SECSuccess;
+ }
+ slot->mechanismCount = count;
+ return SECSuccess;
+}
+
+/*
+ * initialize a new token
+ * unlike initialize slot, this can be called multiple times in the lifetime
+ * of NSS. It reads the information associated with a card or token,
+ * that is not going to change unless the card or token changes.
+ */
+SECStatus
+PK11_InitToken(PK11SlotInfo *slot, PRBool loadCerts)
+{
+ CK_TOKEN_INFO tokenInfo;
+ CK_RV crv;
+ char *tmp;
+ SECStatus rv;
+
+ /* set the slot flags to the current token values */
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID,&tokenInfo);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+
+ /* set the slot flags to the current token values */
+ slot->series++; /* allow other objects to detect that the
+ * slot is different */
+ slot->flags = tokenInfo.flags;
+ slot->needLogin = ((tokenInfo.flags & CKF_LOGIN_REQUIRED) ?
+ PR_TRUE : PR_FALSE);
+ slot->readOnly = ((tokenInfo.flags & CKF_WRITE_PROTECTED) ?
+ PR_TRUE : PR_FALSE);
+ slot->hasRandom = ((tokenInfo.flags & CKF_RNG) ? PR_TRUE : PR_FALSE);
+ tmp = PK11_MakeString(NULL,slot->token_name,
+ (char *)tokenInfo.label, sizeof(tokenInfo.label));
+ slot->minPassword = tokenInfo.ulMinPinLen;
+ slot->maxPassword = tokenInfo.ulMaxPinLen;
+ PORT_Memcpy(slot->serial,tokenInfo.serialNumber,sizeof(slot->serial));
+
+ slot->defRWSession = (PRBool)((!slot->readOnly) &&
+ (tokenInfo.ulMaxSessionCount == 1));
+ rv = PK11_ReadMechanismList(slot);
+ if (rv != SECSuccess) return rv;
+
+ slot->hasRSAInfo = PR_FALSE;
+ slot->RSAInfoFlags = 0;
+
+ /* Make sure our session handle is valid */
+ if (slot->session == CK_INVALID_SESSION) {
+ /* we know we don't have a valid session, go get one */
+ CK_SESSION_HANDLE session;
+
+ /* session should be Readonly, serial */
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION,
+ slot,pk11_notify,&session);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ slot->session = session;
+ } else {
+ /* The session we have may be defunct (the token associated with it)
+ * has been removed */
+ CK_SESSION_INFO sessionInfo;
+
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session,&sessionInfo);
+ if (crv == CKR_DEVICE_ERROR) {
+ PK11_GETTAB(slot)->C_CloseSession(slot->session);
+ crv = CKR_SESSION_CLOSED;
+ }
+ if ((crv==CKR_SESSION_CLOSED) || (crv==CKR_SESSION_HANDLE_INVALID)) {
+ crv =PK11_GETTAB(slot)->C_OpenSession(slot->slotID,
+ (slot->defRWSession ? CKF_RW_SESSION : 0) | CKF_SERIAL_SESSION,
+ slot,pk11_notify,&slot->session);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ slot->session = CK_INVALID_SESSION;
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ return SECFailure;
+ }
+ }
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ }
+
+ /*if we have cached slotcerts, free them they are almost certainly stale*/
+ PK11_FreeSlotCerts(slot);
+
+ if (loadCerts && (!slot->isInternal) &&
+ ((!slot->needLogin) || (slot->defaultFlags & SECMOD_FRIENDLY_FLAG))) {
+ PK11_ReadSlotCerts(slot);
+ }
+
+ if (!(slot->needLogin)) {
+ return pk11_CheckVerifyTest(slot);
+ }
+
+
+ if (!(slot->isInternal) && (slot->hasRandom)) {
+ /* if this slot has a random number generater, use it to add entropy
+ * to the internal slot. */
+ PK11SlotInfo *int_slot = PK11_GetInternalSlot();
+
+ if (int_slot) {
+ unsigned char random_bytes[32];
+
+ /* if this slot can issue random numbers, get some entropy from
+ * that random number generater and give it to our internal token.
+ */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GenerateRandom
+ (slot->session,random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(slot);
+ if (crv == CKR_OK) {
+ PK11_EnterSlotMonitor(int_slot);
+ PK11_GETTAB(int_slot)->C_SeedRandom(int_slot->session,
+ random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(int_slot);
+ }
+
+ /* Now return the favor and send entropy to the token's random
+ * number generater */
+ PK11_EnterSlotMonitor(int_slot);
+ crv = PK11_GETTAB(int_slot)->C_GenerateRandom(int_slot->session,
+ random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(int_slot);
+ if (crv == CKR_OK) {
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_SeedRandom(slot->session,
+ random_bytes, sizeof(random_bytes));
+ PK11_ExitSlotMonitor(slot);
+ }
+ }
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * Initialize the slot :
+ * This initialization code is called on each slot a module supports when
+ * it is loaded. It does the bringup initialization. The difference between
+ * this and InitToken is Init slot does those one time initialization stuff,
+ * usually associated with the reader, while InitToken may get called multiple
+ * times as tokens are removed and re-inserted.
+ */
+void
+PK11_InitSlot(SECMODModule *mod,CK_SLOT_ID slotID,PK11SlotInfo *slot)
+{
+ SECStatus rv;
+ char *tmp;
+ CK_SLOT_INFO slotInfo;
+
+ slot->functionList = mod->functionList;
+ slot->isInternal = mod->internal;
+ slot->slotID = slotID;
+ slot->isThreadSafe = mod->isThreadSafe;
+ slot->hasRSAInfo = PR_FALSE;
+
+ if (PK11_GETTAB(slot)->C_GetSlotInfo(slotID,&slotInfo) != CKR_OK) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ return;
+ }
+
+ /* test to make sure claimed mechanism work */
+ slot->needTest = mod->internal ? PR_FALSE : PR_TRUE;
+ slot->module = mod; /* NOTE: we don't make a reference here because
+ * modules have references to their slots. This
+ * works because modules keep implicit references
+ * from their slots, and won't unload and disappear
+ * until all their slots have been freed */
+ tmp = PK11_MakeString(NULL,slot->slot_name,
+ (char *)slotInfo.slotDescription, sizeof(slotInfo.slotDescription));
+ slot->isHW = (PRBool)((slotInfo.flags & CKF_HW_SLOT) == CKF_HW_SLOT);
+ if ((slotInfo.flags & CKF_REMOVABLE_DEVICE) == 0) {
+ slot->isPerm = PR_TRUE;
+ /* permanment slots must have the token present always */
+ if ((slotInfo.flags & CKF_TOKEN_PRESENT) == 0) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_TOKEN_NOT_PRESENT;
+ return; /* nothing else to do */
+ }
+ }
+ /* if the token is present, initialize it */
+ if ((slotInfo.flags & CKF_TOKEN_PRESENT) != 0) {
+ rv = PK11_InitToken(slot,PR_TRUE);
+ /* the only hard failures are on permanent devices, or function
+ * verify failures... function verify failures are already handled
+ * by tokenInit */
+ if ((rv != SECSuccess) && (slot->isPerm) && (!slot->disabled)) {
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_COULD_NOT_INIT_TOKEN;
+ }
+ }
+}
+
+
+
+/*********************************************************************
+ * Slot mapping utility functions.
+ *********************************************************************/
+
+/*
+ * determine if the token is present. If the token is present, make sure
+ * we have a valid session handle. Also set the value of needLogin
+ * appropriately.
+ */
+static PRBool
+pk11_IsPresentCertLoad(PK11SlotInfo *slot, PRBool loadCerts)
+{
+ CK_SLOT_INFO slotInfo;
+ CK_SESSION_INFO sessionInfo;
+ CK_RV crv;
+
+ /* disabled slots are never present */
+ if (slot->disabled) {
+ return PR_FALSE;
+ }
+
+ /* permanent slots are always present */
+ if (slot->isPerm && (slot->session != CK_INVALID_SESSION)) {
+ return PR_TRUE;
+ }
+
+ /* removable slots have a flag that says they are present */
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ if (PK11_GETTAB(slot)->C_GetSlotInfo(slot->slotID,&slotInfo) != CKR_OK) {
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ return PR_FALSE;
+ }
+ if ((slotInfo.flags & CKF_TOKEN_PRESENT) == 0) {
+ /* if the slot is no longer present, close the session */
+ if (slot->session != CK_INVALID_SESSION) {
+ PK11_GETTAB(slot)->C_CloseSession(slot->session);
+ slot->session = CK_INVALID_SESSION;
+ /* force certs to be freed */
+ PK11_FreeSlotCerts(slot);
+ }
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ return PR_FALSE;
+ }
+
+ /* use the session Info to determine if the card has been removed and then
+ * re-inserted */
+ if (slot->session != CK_INVALID_SESSION) {
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session, &sessionInfo);
+ if (crv != CKR_OK) {
+ PK11_GETTAB(slot)->C_CloseSession(slot->session);
+ slot->session = CK_INVALID_SESSION;
+ PK11_FreeSlotCerts(slot);
+ }
+ }
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+
+ /* card has not been removed, current token info is correct */
+ if (slot->session != CK_INVALID_SESSION) return PR_TRUE;
+
+ /* initialize the token info state */
+ if (PK11_InitToken(slot,loadCerts) != SECSuccess) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/*
+ * old version of the routine
+ */
+PRBool
+PK11_IsPresent(PK11SlotInfo *slot) {
+ return pk11_IsPresentCertLoad(slot,PR_TRUE);
+}
+
+/* is the slot disabled? */
+PRBool
+PK11_IsDisabled(PK11SlotInfo *slot)
+{
+ return slot->disabled;
+}
+
+/* and why? */
+PK11DisableReasons
+PK11_GetDisabledReason(PK11SlotInfo *slot)
+{
+ return slot->reason;
+}
+
+/* returns PR_TRUE if successfully disable the slot */
+/* returns PR_FALSE otherwise */
+PRBool PK11_UserDisableSlot(PK11SlotInfo *slot) {
+
+ slot->defaultFlags |= PK11_DISABLE_FLAG;
+ slot->disabled = PR_TRUE;
+ slot->reason = PK11_DIS_USER_SELECTED;
+
+ return PR_TRUE;
+}
+
+PRBool PK11_UserEnableSlot(PK11SlotInfo *slot) {
+
+ slot->defaultFlags &= ~PK11_DISABLE_FLAG;
+ slot->disabled = PR_FALSE;
+ slot->reason = PK11_DIS_NONE;
+ return PR_TRUE;
+}
+
+/* Get the module this slot is attatched to */
+SECMODModule *
+PK11_GetModule(PK11SlotInfo *slot)
+{
+ return slot->module;
+}
+
+/* returnt the default flags of a slot */
+unsigned long
+PK11_GetDefaultFlags(PK11SlotInfo *slot)
+{
+ return slot->defaultFlags;
+}
+
+/*
+ * we can initialize the password if 1) The toke is not inited
+ * (need login == true and see need UserInit) or 2) the token has
+ * a NULL password. (slot->needLogin = false & need user Init = false).
+ */
+PRBool PK11_NeedPWInitForSlot(PK11SlotInfo *slot)
+{
+ if (slot->needLogin && PK11_NeedUserInit(slot)) {
+ return PR_TRUE;
+ }
+ if (!slot->needLogin && !PK11_NeedUserInit(slot)) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+PRBool PK11_NeedPWInit()
+{
+ PK11SlotInfo *slot = PK11_GetInternalKeySlot();
+ PRBool ret = PK11_NeedPWInitForSlot(slot);
+
+ PK11_FreeSlot(slot);
+ return ret;
+}
+
+/*
+ * The following wrapper functions allow us to export an opaque slot
+ * function to the rest of libsec and the world... */
+PRBool
+PK11_IsReadOnly(PK11SlotInfo *slot)
+{
+ return slot->readOnly;
+}
+
+PRBool
+PK11_IsHW(PK11SlotInfo *slot)
+{
+ return slot->isHW;
+}
+
+PRBool
+PK11_IsInternal(PK11SlotInfo *slot)
+{
+ return slot->isInternal;
+}
+
+PRBool
+PK11_NeedLogin(PK11SlotInfo *slot)
+{
+ return slot->needLogin;
+}
+
+PRBool
+PK11_IsFriendly(PK11SlotInfo *slot)
+{
+ /* internal slot always has public readable certs */
+ return (PRBool)(slot->isInternal ||
+ ((slot->defaultFlags & SECMOD_FRIENDLY_FLAG) ==
+ SECMOD_FRIENDLY_FLAG));
+}
+
+char *
+PK11_GetTokenName(PK11SlotInfo *slot)
+{
+ return slot->token_name;
+}
+
+char *
+PK11_GetSlotName(PK11SlotInfo *slot)
+{
+ return slot->slot_name;
+}
+
+int
+PK11_GetSlotSeries(PK11SlotInfo *slot)
+{
+ return slot->series;
+}
+
+int
+PK11_GetCurrentWrapIndex(PK11SlotInfo *slot)
+{
+ return slot->wrapKey;
+}
+
+CK_SLOT_ID
+PK11_GetSlotID(PK11SlotInfo *slot)
+{
+ return slot->slotID;
+}
+
+SECMODModuleID
+PK11_GetModuleID(PK11SlotInfo *slot)
+{
+ return slot->module->moduleID;
+}
+
+
+/* return the slot info structure */
+SECStatus
+PK11_GetSlotInfo(PK11SlotInfo *slot, CK_SLOT_INFO *info)
+{
+ CK_RV crv;
+
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetSlotInfo(slot->slotID,info);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* return the token info structure */
+SECStatus
+PK11_GetTokenInfo(PK11SlotInfo *slot, CK_TOKEN_INFO *info)
+{
+ CK_RV crv;
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetTokenInfo(slot->slotID,info);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* Find out if we need to initialize the user's pin */
+PRBool
+PK11_NeedUserInit(PK11SlotInfo *slot)
+{
+ return (PRBool)((slot->flags & CKF_USER_PIN_INITIALIZED) == 0);
+}
+
+/* get the internal key slot. FIPS has only one slot for both key slots and
+ * default slots */
+PK11SlotInfo *
+PK11_GetInternalKeySlot(void)
+{
+ SECMODModule *mod = SECMOD_GetInternalModule();
+ return PK11_ReferenceSlot(mod->isFIPS ? mod->slots[0] : mod->slots[1]);
+}
+
+/* get the internal default slot */
+PK11SlotInfo *
+PK11_GetInternalSlot(void)
+{
+ return PK11_ReferenceSlot(SECMOD_GetInternalModule()->slots[0]);
+}
+
+/*
+ * Determine if the token is logged in. We have to actually query the token,
+ * because it's state can change without intervention from us.
+ */
+PRBool
+PK11_IsLoggedIn(PK11SlotInfo *slot,void *wincx)
+{
+ CK_SESSION_INFO sessionInfo;
+ int askpw = slot->askpw;
+ int timeout = slot->timeout;
+ CK_RV crv;
+
+ /* If we don't have our own password default values, use the system
+ * ones */
+ if ((slot->defaultFlags & PK11_OWN_PW_DEFAULTS) == 0) {
+ PK11SlotInfo *def_slot = PK11_GetInternalKeySlot();
+
+ if (def_slot) {
+ askpw = def_slot->askpw;
+ timeout = def_slot->timeout;
+ PK11_FreeSlot(def_slot);
+ }
+ }
+
+ if ((wincx != NULL) && (PK11_Global.isLoggedIn != NULL) &&
+ (*PK11_Global.isLoggedIn)(slot, wincx) == PR_FALSE) { return PR_FALSE; }
+
+
+ /* forget the password if we've been inactive too long */
+ if (askpw == 1) {
+ int64 currtime = PR_Now();
+ int64 result;
+ int64 mult;
+
+ LL_I2L(result, timeout);
+ LL_I2L(mult, 60*1000*1000);
+ LL_MUL(result,result,mult);
+ LL_ADD(result, result, slot->authTime);
+ if (LL_CMP(result, <, currtime) ) {
+ PK11_EnterSlotMonitor(slot);
+ PK11_GETTAB(slot)->C_Logout(slot->session);
+ PK11_ExitSlotMonitor(slot);
+ } else {
+ slot->authTime = currtime;
+ }
+ }
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetSessionInfo(slot->session,&sessionInfo);
+ PK11_ExitSlotMonitor(slot);
+ /* if we can't get session info, something is really wrong */
+ if (crv != CKR_OK) {
+ slot->session = CK_INVALID_SESSION;
+ return PR_FALSE;
+ }
+
+ switch (sessionInfo.state) {
+ case CKS_RW_PUBLIC_SESSION:
+ case CKS_RO_PUBLIC_SESSION:
+ default:
+ break; /* fail */
+ case CKS_RW_USER_FUNCTIONS:
+ case CKS_RW_SO_FUNCTIONS:
+ case CKS_RO_USER_FUNCTIONS:
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+
+/*
+ * check if a given slot supports the requested mechanism
+ */
+PRBool
+PK11_DoesMechanism(PK11SlotInfo *slot, CK_MECHANISM_TYPE type)
+{
+ int i;
+
+ /* CKM_FAKE_RANDOM is not a real PKCS mechanism. It's a marker to
+ * tell us we're looking form someone that has implemented get
+ * random bits */
+ if (type == CKM_FAKE_RANDOM) {
+ return slot->hasRandom;
+ }
+
+ for (i=0; i < (int) slot->mechanismCount; i++) {
+ if (slot->mechanismList[i] == type) return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * Return true if a token that can do the desired mechanism exists.
+ * This allows us to have hardware tokens that can do function XYZ magically
+ * allow SSL Ciphers to appear if they are plugged in.
+ */
+PRBool
+PK11_TokenExists(CK_MECHANISM_TYPE type)
+{
+ SECMODModuleList *mlp;
+ SECMODModuleList *modules = SECMOD_GetDefaultModuleList();
+ SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock();
+ PK11SlotInfo *slot;
+ PRBool found = PR_FALSE;
+ int i;
+
+ /* we only need to know if there is a token that does this mechanism.
+ * check the internal module first because it's fast, and supports
+ * almost everything. */
+ slot = PK11_GetInternalSlot();
+ if (slot) {
+ found = PK11_DoesMechanism(slot,type);
+ PK11_FreeSlot(slot);
+ }
+ if (found) return PR_TRUE; /* bypass getting module locks */
+
+ SECMOD_GetReadLock(moduleLock);
+ for(mlp = modules; mlp != NULL && (!found); mlp = mlp->next) {
+ for (i=0; i < mlp->module->slotCount; i++) {
+ slot = mlp->module->slots[i];
+ if (PK11_IsPresent(slot)) {
+ if (PK11_DoesMechanism(slot,type)) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+ return found;
+}
+
+/*
+ * get all the currently available tokens in a list.
+ * that can perform the given mechanism. If mechanism is CKM_INVALID_MECHANISM,
+ * get all the tokens. Make sure tokens that need authentication are put at
+ * the end of this list.
+ */
+PK11SlotList *
+PK11_GetAllTokens(CK_MECHANISM_TYPE type, PRBool needRW, PRBool loadCerts,
+ void *wincx)
+{
+ PK11SlotList * list = PK11_NewSlotList();
+ PK11SlotList * loginList = PK11_NewSlotList();
+ PK11SlotList * friendlyList = PK11_NewSlotList();
+ SECMODModuleList * mlp;
+ SECMODModuleList * modules = SECMOD_GetDefaultModuleList();
+ SECMODListLock * moduleLock = SECMOD_GetDefaultModuleListLock();
+ int i;
+#if defined( XP_WIN32 )
+ int j = 0;
+ PRInt32 waste[16];
+#endif
+
+ if ((list == NULL) || (loginList == NULL) || (friendlyList == NULL)) {
+ if (list) PK11_FreeSlotList(list);
+ if (loginList) PK11_FreeSlotList(loginList);
+ if (friendlyList) PK11_FreeSlotList(friendlyList);
+ return NULL;
+ }
+
+ SECMOD_GetReadLock(moduleLock);
+ for(mlp = modules; mlp != NULL; mlp = mlp->next) {
+
+#if defined( XP_WIN32 )
+ /* This is works around some horrible cache/page thrashing problems
+ ** on Win32. Without this, this loop can take up to 6 seconds at
+ ** 100% CPU on a Pentium-Pro 200. The thing this changes is to
+ ** increase the size of the stack frame and modify it.
+ ** Moving the loop code itself seems to have no effect.
+ ** Dunno why this combination makes a difference, but it does.
+ */
+ waste[ j & 0xf] = j++;
+#endif
+
+ for (i = 0; i < mlp->module->slotCount; i++) {
+ PK11SlotInfo *slot = mlp->module->slots[i];
+
+ if (pk11_IsPresentCertLoad(slot, loadCerts)) {
+ if (needRW && slot->readOnly) continue;
+ if ((type == CKM_INVALID_MECHANISM)
+ || PK11_DoesMechanism(slot, type)) {
+ if (slot->needLogin && !PK11_IsLoggedIn(slot, wincx)) {
+ if (PK11_IsFriendly(slot)) {
+ PK11_AddSlotToList(friendlyList, slot);
+ } else {
+ PK11_AddSlotToList(loginList, slot);
+ }
+ } else {
+ PK11_AddSlotToList(list, slot);
+ }
+ }
+ }
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ PK11_MoveListToList(list,friendlyList);
+ PK11_FreeSlotList(friendlyList);
+ PK11_MoveListToList(list,loginList);
+ PK11_FreeSlotList(loginList);
+
+ return list;
+}
+
+/*
+ * NOTE: This routine is working from a private List generated by
+ * PK11_GetAllTokens. That is why it does not need to lock.
+ */
+PK11SlotList *
+PK11_GetPrivateKeyTokens(CK_MECHANISM_TYPE type,PRBool needRW,void *wincx)
+{
+ PK11SlotList *list = PK11_GetAllTokens(type,needRW,PR_TRUE,wincx);
+ PK11SlotListElement *le, *next ;
+ SECStatus rv;
+
+ if (list == NULL) return list;
+
+ for (le = list->head ; le; le = next) {
+ next = le->next; /* save the pointer here in case we have to
+ * free the element later */
+ rv = PK11_Authenticate(le->slot,PR_TRUE,wincx);
+ if (rv != SECSuccess) {
+ PK11_DeleteSlotFromList(list,le);
+ continue;
+ }
+ }
+ return list;
+}
+
+
+/*
+ * find the best slot which supports the given
+ * Mechanism. In normal cases this should grab the first slot on the list
+ * with no fuss.
+ */
+PK11SlotInfo *
+PK11_GetBestSlotMultiple(CK_MECHANISM_TYPE *type, int mech_count, void *wincx)
+{
+ PK11SlotList *list = NULL;
+ PK11SlotListElement *le ;
+ PK11SlotInfo *slot = NULL;
+ PRBool freeit = PR_FALSE;
+ PRBool listNeedLogin = PR_FALSE;
+ int i;
+ SECStatus rv;
+
+ list = PK11_GetSlotList(type[0]);
+
+ if (list == NULL) {
+ /* We need to look up all the tokens for the mechanism */
+ list = PK11_GetAllTokens(type[0],PR_FALSE,PR_TRUE,wincx);
+ freeit = PR_TRUE;
+ }
+
+ /* no one can do it! */
+ if (list == NULL) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ return NULL;
+ }
+
+ PORT_SetError(0);
+
+
+ listNeedLogin = PR_FALSE;
+ for (i=0; i < mech_count; i++) {
+ if ((type[i] != CKM_FAKE_RANDOM) && (type[i] != CKM_SHA_1) &&
+ (type[i] != CKM_MD5) && (type[i] != CKM_MD2)) {
+ listNeedLogin = PR_TRUE;
+ break;
+ }
+ }
+
+ for (le = PK11_GetFirstSafe(list); le;
+ le = PK11_GetNextSafe(list,le,PR_TRUE)) {
+ if (PK11_IsPresent(le->slot)) {
+ PRBool doExit = PR_FALSE;
+ for (i=0; i < mech_count; i++) {
+ if (!PK11_DoesMechanism(le->slot,type[i])) {
+ doExit = PR_TRUE;
+ break;
+ }
+ }
+ if (doExit) continue;
+
+ if (listNeedLogin && le->slot->needLogin) {
+ rv = PK11_Authenticate(le->slot,PR_TRUE,wincx);
+ if (rv != SECSuccess) continue;
+ }
+ slot = le->slot;
+ PK11_ReferenceSlot(slot);
+ pk11_FreeListElement(list,le);
+ if (freeit) { PK11_FreeSlotList(list); }
+ return slot;
+ }
+ }
+ if (freeit) { PK11_FreeSlotList(list); }
+ if (PORT_GetError() == 0) {
+ PORT_SetError(SEC_ERROR_NO_TOKEN);
+ }
+ return NULL;
+}
+
+/* original get best slot now calls the multiple version with only one type */
+PK11SlotInfo *
+PK11_GetBestSlot(CK_MECHANISM_TYPE type, void *wincx)
+{
+ return PK11_GetBestSlotMultiple(&type, 1, wincx);
+}
+
+/*
+ * find the best key wrap mechanism for this slot.
+ */
+CK_MECHANISM_TYPE
+PK11_GetBestWrapMechanism(PK11SlotInfo *slot)
+{
+ int i;
+ for (i=0; i < wrapMechanismCount; i++) {
+ if (PK11_DoesMechanism(slot,wrapMechanismList[i])) {
+ return wrapMechanismList[i];
+ }
+ }
+ return CKM_INVALID_MECHANISM;
+}
+
+int
+PK11_GetBestKeyLength(PK11SlotInfo *slot,CK_MECHANISM_TYPE mechanism)
+{
+ CK_MECHANISM_INFO mechanism_info;
+ CK_RV crv;
+
+ if (!slot->isThreadSafe) PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetMechanismInfo(slot->slotID,
+ mechanism,&mechanism_info);
+ if (!slot->isThreadSafe) PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) return 0;
+
+ if (mechanism_info.ulMinKeySize == mechanism_info.ulMaxKeySize)
+ return 0;
+ return mechanism_info.ulMaxKeySize;
+}
+
+
+/*********************************************************************
+ * Mechanism Mapping functions
+ *********************************************************************/
+
+/*
+ * lookup an entry in the mechanism table. If none found, return the
+ * default structure.
+ */
+static pk11MechanismData *
+pk11_lookup(CK_MECHANISM_TYPE type)
+{
+ int i;
+ for (i=0; i < pk11_MechEntrySize; i++) {
+ if (pk11_MechanismTable[i].type == type) {
+ return (&pk11_MechanismTable[i]);
+ }
+ }
+ return &pk11_default;
+}
+
+/*
+ * NOTE: This is not thread safe. Called at init time, and when loading
+ * a new Entry. It is reasonably safe as long as it is not re-entered
+ * (readers will always see a consistant table)
+ *
+ * This routine is called to add entries to the mechanism table, once there,
+ * they can not be removed.
+ */
+void
+PK11_AddMechanismEntry(CK_MECHANISM_TYPE type, CK_KEY_TYPE key,
+ CK_MECHANISM_TYPE keyGen, int ivLen, int blockSize)
+{
+ int tableSize = pk11_MechTableSize;
+ int size = pk11_MechEntrySize;
+ int entry = size++;
+ pk11MechanismData *old = pk11_MechanismTable;
+ pk11MechanismData *newt = pk11_MechanismTable;
+
+
+ if (size > tableSize) {
+ int oldTableSize = tableSize;
+ tableSize += 10;
+ newt = (pk11MechanismData *)
+ PORT_Alloc(tableSize*sizeof(pk11MechanismData));
+ if (newt == NULL) return;
+
+ if (old) PORT_Memcpy(newt,old,oldTableSize*sizeof(pk11MechanismData));
+ } else old = NULL;
+
+ newt[entry].type = type;
+ newt[entry].keyType = key;
+ newt[entry].keyGen = keyGen;
+ newt[entry].iv = ivLen;
+ newt[entry].blockSize = blockSize;
+
+ pk11_MechanismTable = newt;
+ pk11_MechTableSize = tableSize;
+ pk11_MechEntrySize = size;
+ if (old) PORT_Free(old);
+}
+
+/*
+ * Get the key type needed for the given mechanism
+ */
+CK_MECHANISM_TYPE
+PK11_GetKeyType(CK_MECHANISM_TYPE type,unsigned long len)
+{
+ switch (type) {
+ case CKM_DES_ECB:
+ case CKM_DES_CBC:
+ case CKM_DES_MAC:
+ case CKM_DES_MAC_GENERAL:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES_KEY_GEN:
+ case CKM_KEY_WRAP_LYNKS:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ return CKK_DES;
+ case CKM_DES3_ECB:
+ case CKM_DES3_CBC:
+ case CKM_DES3_MAC:
+ case CKM_DES3_MAC_GENERAL:
+ case CKM_DES3_CBC_PAD:
+ return (len == 128) ? CKK_DES2 : CKK_DES3;
+ case CKM_DES2_KEY_GEN:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ return CKK_DES2;
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_DES3_KEY_GEN:
+ return CKK_DES3;
+ case CKM_CDMF_ECB:
+ case CKM_CDMF_CBC:
+ case CKM_CDMF_MAC:
+ case CKM_CDMF_MAC_GENERAL:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CDMF_KEY_GEN:
+ return CKK_CDMF;
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ case CKM_RC2_MAC:
+ case CKM_RC2_MAC_GENERAL:
+ case CKM_RC2_CBC_PAD:
+ case CKM_RC2_KEY_GEN:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ return CKK_RC2;
+ case CKM_RC4:
+ case CKM_RC4_KEY_GEN:
+ return CKK_RC4;
+ case CKM_RC5_ECB:
+ case CKM_RC5_CBC:
+ case CKM_RC5_MAC:
+ case CKM_RC5_MAC_GENERAL:
+ case CKM_RC5_CBC_PAD:
+ case CKM_RC5_KEY_GEN:
+ return CKK_RC5;
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_SKIPJACK_KEY_GEN:
+ case CKM_SKIPJACK_WRAP:
+ case CKM_SKIPJACK_PRIVATE_WRAP:
+ return CKK_SKIPJACK;
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_BATON_WRAP:
+ case CKM_BATON_KEY_GEN:
+ return CKK_BATON;
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ case CKM_JUNIPER_WRAP:
+ case CKM_JUNIPER_KEY_GEN:
+ return CKK_JUNIPER;
+ case CKM_IDEA_CBC:
+ case CKM_IDEA_ECB:
+ case CKM_IDEA_MAC:
+ case CKM_IDEA_MAC_GENERAL:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_IDEA_KEY_GEN:
+ return CKK_IDEA;
+ case CKM_CAST_ECB:
+ case CKM_CAST_CBC:
+ case CKM_CAST_MAC:
+ case CKM_CAST_MAC_GENERAL:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST_KEY_GEN:
+ case CKM_PBE_MD5_CAST_CBC:
+ return CKK_CAST;
+ case CKM_CAST3_ECB:
+ case CKM_CAST3_CBC:
+ case CKM_CAST3_MAC:
+ case CKM_CAST3_MAC_GENERAL:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST3_KEY_GEN:
+ case CKM_PBE_MD5_CAST3_CBC:
+ return CKK_CAST3;
+ case CKM_CAST5_ECB:
+ case CKM_CAST5_CBC:
+ case CKM_CAST5_MAC:
+ case CKM_CAST5_MAC_GENERAL:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_CAST5_KEY_GEN:
+ case CKM_PBE_MD5_CAST5_CBC:
+ return CKK_CAST5;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ case CKM_MD2_RSA_PKCS:
+ case CKM_MD5_RSA_PKCS:
+ case CKM_SHA1_RSA_PKCS:
+ case CKM_KEY_WRAP_SET_OAEP:
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ return CKK_RSA;
+ case CKM_DSA:
+ case CKM_DSA_SHA1:
+ case CKM_DSA_KEY_PAIR_GEN:
+ return CKK_DSA;
+ case CKM_DH_PKCS_DERIVE:
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ return CKK_DH;
+ case CKM_KEA_KEY_DERIVE:
+ case CKM_KEA_KEY_PAIR_GEN:
+ return CKK_KEA;
+ case CKM_ECDSA_KEY_PAIR_GEN:
+ case CKM_ECDSA:
+ case CKM_ECDSA_SHA1:
+ return CKK_ECDSA;
+ case CKM_SSL3_PRE_MASTER_KEY_GEN:
+ case CKM_GENERIC_SECRET_KEY_GEN:
+ case CKM_SSL3_MASTER_KEY_DERIVE:
+ case CKM_SSL3_KEY_AND_MAC_DERIVE:
+ case CKM_SSL3_SHA1_MAC:
+ case CKM_SSL3_MD5_MAC:
+ case CKM_TLS_MASTER_KEY_DERIVE:
+ case CKM_TLS_KEY_AND_MAC_DERIVE:
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA_1_HMAC_GENERAL:
+ case CKM_MD2_HMAC:
+ case CKM_MD2_HMAC_GENERAL:
+ case CKM_MD5_HMAC:
+ case CKM_MD5_HMAC_GENERAL:
+ case CKM_TLS_PRF_GENERAL:
+ return CKK_GENERIC_SECRET;
+ default:
+ return pk11_lookup(type)->keyType;
+ }
+}
+
+/*
+ * Get the Key Gen Mechanism needed for the given
+ * crypto mechanism
+ */
+CK_MECHANISM_TYPE
+PK11_GetKeyGen(CK_MECHANISM_TYPE type)
+{
+ switch (type) {
+ case CKM_DES_ECB:
+ case CKM_DES_CBC:
+ case CKM_DES_MAC:
+ case CKM_DES_MAC_GENERAL:
+ case CKM_KEY_WRAP_LYNKS:
+ case CKM_DES_CBC_PAD:
+ return CKM_DES_KEY_GEN;
+ case CKM_DES3_ECB:
+ case CKM_DES3_CBC:
+ case CKM_DES3_MAC:
+ case CKM_DES3_MAC_GENERAL:
+ case CKM_DES3_CBC_PAD:
+ return CKM_DES3_KEY_GEN;
+ case CKM_CDMF_ECB:
+ case CKM_CDMF_CBC:
+ case CKM_CDMF_MAC:
+ case CKM_CDMF_MAC_GENERAL:
+ case CKM_CDMF_CBC_PAD:
+ return CKM_CDMF_KEY_GEN;
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ case CKM_RC2_MAC:
+ case CKM_RC2_MAC_GENERAL:
+ case CKM_RC2_CBC_PAD:
+ return CKM_RC2_KEY_GEN;
+ case CKM_RC4:
+ return CKM_RC4_KEY_GEN;
+ case CKM_RC5_ECB:
+ case CKM_RC5_CBC:
+ case CKM_RC5_MAC:
+ case CKM_RC5_MAC_GENERAL:
+ case CKM_RC5_CBC_PAD:
+ return CKM_RC5_KEY_GEN;
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_SKIPJACK_WRAP:
+ return CKM_SKIPJACK_KEY_GEN;
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_BATON_WRAP:
+ return CKM_BATON_KEY_GEN;
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ case CKM_JUNIPER_WRAP:
+ return CKM_JUNIPER_KEY_GEN;
+ case CKM_IDEA_CBC:
+ case CKM_IDEA_ECB:
+ case CKM_IDEA_MAC:
+ case CKM_IDEA_MAC_GENERAL:
+ case CKM_IDEA_CBC_PAD:
+ return CKM_IDEA_KEY_GEN;
+ case CKM_CAST_ECB:
+ case CKM_CAST_CBC:
+ case CKM_CAST_MAC:
+ case CKM_CAST_MAC_GENERAL:
+ case CKM_CAST_CBC_PAD:
+ return CKM_CAST_KEY_GEN;
+ case CKM_CAST3_ECB:
+ case CKM_CAST3_CBC:
+ case CKM_CAST3_MAC:
+ case CKM_CAST3_MAC_GENERAL:
+ case CKM_CAST3_CBC_PAD:
+ return CKM_CAST3_KEY_GEN;
+ case CKM_CAST5_ECB:
+ case CKM_CAST5_CBC:
+ case CKM_CAST5_MAC:
+ case CKM_CAST5_MAC_GENERAL:
+ case CKM_CAST5_CBC_PAD:
+ return CKM_CAST5_KEY_GEN;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ case CKM_MD2_RSA_PKCS:
+ case CKM_MD5_RSA_PKCS:
+ case CKM_SHA1_RSA_PKCS:
+ case CKM_KEY_WRAP_SET_OAEP:
+ return CKM_RSA_PKCS_KEY_PAIR_GEN;
+ case CKM_DSA:
+ case CKM_DSA_SHA1:
+ return CKM_DSA_KEY_PAIR_GEN;
+ case CKM_DH_PKCS_DERIVE:
+ return CKM_DH_PKCS_KEY_PAIR_GEN;
+ case CKM_KEA_KEY_DERIVE:
+ return CKM_KEA_KEY_PAIR_GEN;
+ case CKM_ECDSA:
+ return CKM_ECDSA_KEY_PAIR_GEN;
+ case CKM_SSL3_PRE_MASTER_KEY_GEN:
+ case CKM_SSL3_MASTER_KEY_DERIVE:
+ case CKM_SSL3_KEY_AND_MAC_DERIVE:
+ case CKM_SSL3_SHA1_MAC:
+ case CKM_SSL3_MD5_MAC:
+ case CKM_TLS_MASTER_KEY_DERIVE:
+ case CKM_TLS_KEY_AND_MAC_DERIVE:
+ return CKM_SSL3_PRE_MASTER_KEY_GEN;
+ case CKM_SHA_1_HMAC:
+ case CKM_SHA_1_HMAC_GENERAL:
+ case CKM_MD2_HMAC:
+ case CKM_MD2_HMAC_GENERAL:
+ case CKM_MD5_HMAC:
+ case CKM_MD5_HMAC_GENERAL:
+ case CKM_TLS_PRF_GENERAL:
+ return CKM_GENERIC_SECRET_KEY_GEN;
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ return type;
+ default:
+ return pk11_lookup(type)->keyGen;
+ }
+}
+
+/*
+ * get the mechanism block size
+ */
+int
+PK11_GetBlockSize(CK_MECHANISM_TYPE type,SECItem *params)
+{
+ CK_RC5_PARAMS *rc5_params;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params;
+ switch (type) {
+ case CKM_RC5_ECB:
+ if ((params) && (params->data)) {
+ rc5_params = (CK_RC5_PARAMS *) params->data;
+ return (rc5_params->ulWordsize)*2;
+ }
+ return 8;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ if ((params) && (params->data)) {
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *) params->data;
+ return (rc5_cbc_params->ulWordsize)*2;
+ }
+ return 8;
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RC2_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ case CKM_RC2_CBC:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_RC2_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ return 8;
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ return 4;
+ case CKM_BATON_ECB128:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ return 16;
+ case CKM_BATON_ECB96:
+ return 12;
+ case CKM_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ return 0;
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ /*actually it's the modulus length of the key!*/
+ return -1; /* failure */
+ default:
+ return pk11_lookup(type)->blockSize;
+ }
+}
+
+/*
+ * get the iv length
+ */
+int
+PK11_GetIVLength(CK_MECHANISM_TYPE type)
+{
+ switch (type) {
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RC2_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_SKIPJACK_WRAP:
+ case CKM_BATON_WRAP:
+ case CKM_RC5_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ return 0;
+ case CKM_RC2_CBC:
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_RC5_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_RC2_CBC_PAD:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_RC5_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ return 8;
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ return 24;
+ case CKM_RC4:
+ case CKM_RSA_PKCS:
+ case CKM_RSA_9796:
+ case CKM_RSA_X_509:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ return 0;
+ default:
+ return pk11_lookup(type)->iv;
+ }
+}
+
+
+/* These next two utilities are here to help facilitate future
+ * Dynamic Encrypt/Decrypt symetric key mechanisms, and to allow functions
+ * like SSL and S-MIME to automatically add them.
+ */
+SECItem *
+PK11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv)
+{
+ CK_RC2_CBC_PARAMS *rc2_params = NULL;
+ CK_RC2_PARAMS *rc2_ecb_params = NULL;
+ CK_RC5_PARAMS *rc5_params = NULL;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params = NULL;
+ SECItem *param;
+
+ param = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (param == NULL) return NULL;
+ param->data = NULL;
+ param->len = 0;
+ switch (type) {
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ case CKM_RSA_9796:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ case CKM_RC4:
+ break;
+ case CKM_RC2_ECB:
+ rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS));
+ if (rc2_ecb_params == NULL) break;
+ /* Maybe we should pass the key size in too to get this value? */
+ *rc2_ecb_params = 128;
+ param->data = (unsigned char *) rc2_ecb_params;
+ param->len = sizeof(CK_RC2_PARAMS);
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS));
+ if (rc2_params == NULL) break;
+ /* Maybe we should pass the key size in too to get this value? */
+ rc2_params->ulEffectiveBits = 128;
+ if (iv && iv->data)
+ PORT_Memcpy(rc2_params->iv,iv->data,sizeof(rc2_params->iv));
+ param->data = (unsigned char *) rc2_params;
+ param->len = sizeof(CK_RC2_CBC_PARAMS);
+ break;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *)
+ PORT_Alloc(sizeof(CK_RC5_CBC_PARAMS) + ((iv) ? iv->len : 0));
+ if (rc5_cbc_params == NULL) break;
+ if (iv && iv->data) {
+ rc5_cbc_params->pIv = ((CK_BYTE_PTR) rc5_cbc_params)
+ + sizeof(CK_RC5_CBC_PARAMS);
+ PORT_Memcpy(rc5_cbc_params->pIv,iv->data,iv->len);
+ rc5_cbc_params->ulIvLen = iv->len;
+ rc5_cbc_params->ulWordsize = iv->len/2;
+ } else {
+ rc5_cbc_params->ulWordsize = 4;
+ rc5_cbc_params->pIv = NULL;
+ rc5_cbc_params->ulIvLen = iv->len;
+ }
+ rc5_cbc_params->ulRounds = 16;
+ param->data = (unsigned char *) rc5_cbc_params;
+ param->len = sizeof(CK_RC5_CBC_PARAMS);
+ break;
+ case CKM_RC5_ECB:
+ rc5_params = (CK_RC5_PARAMS *)PORT_Alloc(sizeof(CK_RC5_PARAMS));
+ if (rc5_params == NULL) break;
+ if (iv && iv->data && iv->len) {
+ rc5_params->ulWordsize = iv->len/2;
+ } else {
+ rc5_params->ulWordsize = 4;
+ }
+ rc5_params->ulRounds = 16;
+ param->data = (unsigned char *) rc5_params;
+ param->len = sizeof(CK_RC5_PARAMS);
+ break;
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ if ((iv == NULL) || (iv->data == NULL)) break;
+ param->data = (unsigned char*)PORT_Alloc(iv->len);
+ if (param->data != NULL) {
+ PORT_Memcpy(param->data,iv->data,iv->len);
+ param->len = iv->len;
+ }
+ break;
+ /* unknown mechanism, pass IV in if it's there */
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ break;
+ }
+ if ((iv == NULL) || (iv->data == NULL)) {
+ break;
+ }
+ param->data = (unsigned char*)PORT_Alloc(iv->len);
+ if (param->data != NULL) {
+ PORT_Memcpy(param->data,iv->data,iv->len);
+ param->len = iv->len;
+ }
+ break;
+ }
+ return param;
+}
+
+unsigned char *
+PK11_IVFromParam(CK_MECHANISM_TYPE type,SECItem *param,int *len)
+{
+ CK_RC2_CBC_PARAMS *rc2_params;
+ CK_RC5_CBC_PARAMS *rc5_cbc_params;
+
+ *len = 0;
+ switch (type) {
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ case CKM_RSA_9796:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ case CKM_RC4:
+ return NULL;
+ case CKM_RC2_ECB:
+ return NULL;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rc2_params = (CK_RC2_CBC_PARAMS *)param->data;
+ *len = sizeof(rc2_params->iv);
+ return &rc2_params->iv[0];
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rc5_cbc_params = (CK_RC5_CBC_PARAMS *) param->data;
+ *len = rc5_cbc_params->ulIvLen;
+ return rc5_cbc_params->pIv;
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ break;
+ /* unknown mechanism, pass IV in if it's there */
+ default:
+ break;
+ }
+ if (param->data) {
+ *len = param->len;
+ }
+ return param->data;
+}
+
+typedef struct sec_rc5cbcParameterStr {
+ SECItem version;
+ SECItem rounds;
+ SECItem blockSizeInBits;
+ SECItem iv;
+} sec_rc5cbcParameter;
+
+static const SEC_ASN1Template sec_rc5ecb_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc5cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter,version) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter,rounds) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter,blockSizeInBits) },
+ { 0 }
+};
+
+static const SEC_ASN1Template sec_rc5cbc_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc5cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter,version) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter,rounds) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc5cbcParameter,blockSizeInBits) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(sec_rc5cbcParameter,iv) },
+ { 0 }
+};
+
+typedef struct sec_rc2cbcParameterStr {
+ SECItem rc2ParameterVersion;
+ SECItem iv;
+} sec_rc2cbcParameter;
+
+static const SEC_ASN1Template sec_rc2cbc_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc2cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc2cbcParameter,rc2ParameterVersion) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(sec_rc2cbcParameter,iv) },
+ { 0 }
+};
+
+static const SEC_ASN1Template sec_rc2ecb_parameter_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(sec_rc2cbcParameter) },
+ { SEC_ASN1_INTEGER,
+ offsetof(sec_rc2cbcParameter,rc2ParameterVersion) },
+ { 0 }
+};
+
+/* S/MIME picked id values to represent differnt keysizes */
+/* I do have a formula, but it ain't pretty, and it only works because you
+ * can always match three points to a parabola:) */
+static unsigned char rc2_map(SECItem *version)
+{
+ long x;
+
+ x = DER_GetInteger(version);
+
+ switch (x) {
+ case 58: return 128;
+ case 120: return 64;
+ case 160: return 40;
+ }
+ return 128;
+}
+
+static unsigned long rc2_unmap(unsigned long x)
+{
+ switch (x) {
+ case 128: return 58;
+ case 64: return 120;
+ case 40: return 160;
+ }
+ return 58;
+}
+
+
+/*
+ * Helper function to decode a PKCS5 DER encode paramter block into a PKCS #11
+ * PBE_Parameter structure.
+ */
+SECStatus
+pk11_pbe_decode(SECAlgorithmID *algid, SECItem *mech)
+{
+ CK_PBE_PARAMS *pbe_params = NULL;
+ SEC_PKCS5PBEParameter *p5_param;
+ SECItem *p5_misc = NULL;
+ int paramSize = 0;
+
+ p5_param = SEC_PKCS5GetPBEParameter(algid);
+ if(p5_param == NULL) {
+ return SECFailure;
+ }
+
+
+ p5_misc = &p5_param->salt;
+ paramSize = sizeof(CK_PBE_PARAMS);
+
+ pbe_params = (CK_PBE_PARAMS *)PORT_ZAlloc(paramSize);
+ if (pbe_params == NULL) {
+ SEC_PKCS5DestroyPBEParameter(p5_param);
+ return SECFailure;
+ }
+
+ /* get salt */
+ pbe_params->pSalt = (CK_CHAR_PTR)PORT_ZAlloc(p5_misc->len);
+ if (pbe_params->pSalt == CK_NULL_PTR) {
+ goto loser;
+ }
+ PORT_Memcpy(pbe_params->pSalt, p5_misc->data, p5_misc->len);
+ pbe_params->ulSaltLen = (CK_ULONG) p5_misc->len;
+
+ /* get iteration count */
+ p5_misc = &p5_param->iteration;
+ pbe_params->ulIteration = (CK_ULONG) DER_GetInteger(p5_misc);
+
+ /* copy into the mechanism sec item */
+ mech->data = (unsigned char *)pbe_params;
+ mech->len = paramSize;
+ SEC_PKCS5DestroyPBEParameter(p5_param);
+ return SECSuccess;
+
+loser:
+ if (pbe_params->pSalt != CK_NULL_PTR) {
+ PORT_Free(pbe_params->pSalt);
+ }
+ PORT_Free(pbe_params);
+ SEC_PKCS5DestroyPBEParameter(p5_param);
+ return SECFailure;
+}
+
+/* Generate a mechaism param from a type, and iv. */
+SECItem *
+PK11_ParamFromAlgid(SECAlgorithmID *algid) {
+ CK_RC2_CBC_PARAMS *rc2_params = NULL;
+ CK_RC2_PARAMS *rc2_ecb_params = NULL;
+ CK_RC5_CBC_PARAMS *rc5_params_cbc;
+ CK_RC5_PARAMS *rc5_params_ecb;
+ SECItem iv;
+ sec_rc2cbcParameter rc2;
+ sec_rc5cbcParameter rc5;
+ SECItem *mech;
+ CK_MECHANISM_TYPE type;
+ SECOidTag algtag;
+ SECStatus rv;
+
+ algtag = SECOID_GetAlgorithmTag(algid);
+ type = PK11_AlgtagToMechanism(algtag);
+
+ mech = (SECItem *) PORT_Alloc(sizeof(SECItem));
+ if (mech == NULL) return NULL;
+
+
+ /* handle the complicated cases */
+ switch (type) {
+ case CKM_RC2_ECB:
+ rv = SEC_ASN1DecodeItem(NULL, &rc2 ,sec_rc2ecb_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS));
+ if (rc2_ecb_params == NULL) {
+ PORT_Free(rc2.rc2ParameterVersion.data);
+ PORT_Free(mech);
+ return NULL;
+ }
+ *rc2_ecb_params = rc2_map(&rc2.rc2ParameterVersion);
+ PORT_Free(rc2.rc2ParameterVersion.data);
+ mech->data = (unsigned char *) rc2_ecb_params;
+ mech->len = sizeof(CK_RC2_PARAMS);
+ return mech;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rv = SEC_ASN1DecodeItem(NULL, &rc2 ,sec_rc2cbc_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS));
+ if (rc2_params == NULL) {
+ PORT_Free(rc2.iv.data);
+ PORT_Free(rc2.rc2ParameterVersion.data);
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc2_params->ulEffectiveBits = rc2_map(&rc2.rc2ParameterVersion);
+ PORT_Free(rc2.rc2ParameterVersion.data);
+ PORT_Memcpy(rc2_params->iv,rc2.iv.data,sizeof(rc2_params->iv));
+ PORT_Free(rc2.iv.data);
+ mech->data = (unsigned char *) rc2_params;
+ mech->len = sizeof(CK_RC2_CBC_PARAMS);
+ return mech;
+ case CKM_RC5_ECB:
+ rv = SEC_ASN1DecodeItem(NULL, &rc5 ,sec_rc5ecb_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc5_params_ecb=(CK_RC5_PARAMS *)PORT_Alloc(sizeof(CK_RC5_PARAMS));
+ PORT_Free(rc5.version.data);
+ if (rc5_params_ecb == NULL) {
+ PORT_Free(rc5.rounds.data);
+ PORT_Free(rc5.blockSizeInBits.data);
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc5_params_ecb->ulRounds = DER_GetInteger(&rc5.rounds);
+ rc5_params_ecb->ulWordsize = DER_GetInteger(&rc5.blockSizeInBits)/8;
+ PORT_Free(rc5.rounds.data);
+ PORT_Free(rc5.blockSizeInBits.data);
+ mech->data = (unsigned char *) rc5_params_ecb;
+ mech->len = sizeof(CK_RC5_PARAMS);
+ return mech;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rv = SEC_ASN1DecodeItem(NULL, &rc5 ,sec_rc5cbc_parameter_template,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc5_params_cbc = (CK_RC5_CBC_PARAMS *)
+ PORT_Alloc(sizeof(CK_RC5_CBC_PARAMS) + rc5.iv.len);
+ PORT_Free(rc5.version.data);
+ if (rc2_params == NULL) {
+ PORT_Free(rc5.iv.data);
+ PORT_Free(rc5.rounds.data);
+ PORT_Free(rc5.blockSizeInBits.data);
+ PORT_Free(mech);
+ return NULL;
+ }
+ rc5_params_cbc->ulRounds = DER_GetInteger(&rc5.rounds);
+ rc5_params_cbc->ulWordsize = DER_GetInteger(&rc5.blockSizeInBits)/8;
+ PORT_Free(rc5.rounds.data);
+ PORT_Free(rc5.blockSizeInBits.data);
+ rc5_params_cbc->pIv = ((CK_BYTE_PTR)rc5_params_cbc)
+ + sizeof(CK_RC5_CBC_PARAMS);
+ PORT_Memcpy(rc5_params_cbc->pIv,rc5.iv.data,rc5.iv.len);
+ rc5_params_cbc->ulIvLen = rc5.iv.len;
+ PORT_Free(rc5.iv.data);
+ mech->data = (unsigned char *) rc5_params_cbc;
+ mech->len = sizeof(CK_RC5_CBC_PARAMS);
+ return mech;
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ rv = pk11_pbe_decode(algid,mech);
+ if (rv != SECSuccess) {
+ PORT_Free(mech);
+ return NULL;
+ }
+ return mech;
+ default:
+ /* must be a simple case */
+ break;
+ }
+
+ /* simple cases are simpley Octect encoded IV's */
+ rv = SEC_ASN1DecodeItem(NULL, &iv, SEC_OctetStringTemplate,
+ &(algid->parameters));
+ if (rv != SECSuccess) {
+ iv.data = NULL;
+ iv.len = 0;
+ }
+
+ rv = SECSuccess;
+ switch (type) {
+ case CKM_RC4:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ mech->data = NULL;
+ mech->len = 0;
+ break;
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ mech->data = NULL;
+ mech->len = 0;
+ break;
+ }
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ if (iv.data == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ mech->data = (unsigned char*)PORT_Alloc(iv.len);
+ if (mech->data == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ PORT_Memcpy(mech->data,iv.data,iv.len);
+ mech->len = iv.len;
+ break;
+ }
+ if (iv.data) PORT_Free(iv.data);
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(mech,PR_TRUE);
+ return NULL;
+ }
+ return mech;
+}
+
+SECStatus
+PK11_SeedRandom(PK11SlotInfo *slot, unsigned char *data, int len) {
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_SeedRandom(slot->session,data, (CK_ULONG)len);
+ PK11_ExitSlotMonitor(slot);
+ return (crv != CKR_OK) ? SECFailure : SECSuccess;
+}
+
+/* Attempts to update the Best Slot for "FAKE RANDOM" generation.
+** If that's not the internal slot, then it also attempts to update the
+** internal slot.
+** The return value indicates if the INTERNAL slot was updated OK.
+*/
+SECStatus
+PK11_RandomUpdate(void *data, size_t bytes)
+{
+ PK11SlotInfo *slot;
+ PRBool bestIsInternal;
+ SECStatus status;
+
+ slot = PK11_GetBestSlot(CKM_FAKE_RANDOM, NULL);
+ if (slot == NULL) {
+ slot = PK11_GetInternalSlot();
+ if (!slot)
+ return SECFailure;
+ }
+
+ bestIsInternal = PK11_IsInternal(slot);
+ status = PK11_SeedRandom(slot, data, bytes);
+ PK11_FreeSlot(slot);
+
+ if (!bestIsInternal) {
+ /* do internal slot, too. */
+ slot = PK11_GetInternalSlot(); /* can't fail */
+ status = PK11_SeedRandom(slot, data, bytes);
+ PK11_FreeSlot(slot);
+ }
+ return status;
+}
+
+
+SECStatus
+PK11_GenerateRandom(unsigned char *data,int len) {
+ PK11SlotInfo *slot;
+ CK_RV crv;
+
+ slot = PK11_GetBestSlot(CKM_FAKE_RANDOM,NULL);
+ if (slot == NULL) return SECFailure;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GenerateRandom(slot->session,data,
+ (CK_ULONG)len);
+ PK11_ExitSlotMonitor(slot);
+ PK11_FreeSlot(slot);
+ return (crv != CKR_OK) ? SECFailure : SECSuccess;
+}
+
+/*
+ * Generate an IV for the given mechanism
+ */
+static SECStatus
+pk11_GenIV(CK_MECHANISM_TYPE type, SECItem *iv) {
+ int iv_size = PK11_GetIVLength(type);
+ SECStatus rv;
+
+ iv->len = iv_size;
+ if (iv_size == 0) {
+ iv->data = NULL;
+ return SECSuccess;
+ }
+
+ iv->data = (unsigned char *) PORT_Alloc(iv_size);
+ if (iv->data == NULL) {
+ iv->len = 0;
+ return SECFailure;
+ }
+
+ rv = PK11_GenerateRandom(iv->data,iv->len);
+ if (rv != SECSuccess) {
+ PORT_Free(iv->data);
+ iv->data = NULL; iv->len = 0;
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+
+/*
+ * create a new paramter block from the passed in MECHANISM and the
+ * key. Use Netscape's S/MIME Rules for the New param block.
+ */
+SECItem *
+PK11_GenerateNewParam(CK_MECHANISM_TYPE type, PK11SymKey *key) {
+ CK_RC2_CBC_PARAMS *rc2_params;
+ CK_RC2_PARAMS *rc2_ecb_params;
+ SECItem *mech;
+ SECItem iv;
+ SECStatus rv;
+
+
+ mech = (SECItem *) PORT_Alloc(sizeof(SECItem));
+ if (mech == NULL) return NULL;
+
+ rv = SECSuccess;
+ switch (type) {
+ case CKM_RC4:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ mech->data = NULL;
+ mech->len = 0;
+ break;
+ case CKM_RC2_ECB:
+ rc2_ecb_params = (CK_RC2_PARAMS *)PORT_Alloc(sizeof(CK_RC2_PARAMS));
+ if (rc2_ecb_params == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ /* NOTE PK11_GetKeyLength can return -1 if the key isn't and RC2, RC5,
+ * or RC4 key. Of course that wouldn't happen here doing RC2:).*/
+ *rc2_ecb_params = PK11_GetKeyLength(key)*8;
+ mech->data = (unsigned char *) rc2_ecb_params;
+ mech->len = sizeof(CK_RC2_PARAMS);
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rv = pk11_GenIV(type,&iv);
+ if (rv != SECSuccess) {
+ break;
+ }
+ rc2_params = (CK_RC2_CBC_PARAMS *)PORT_Alloc(sizeof(CK_RC2_CBC_PARAMS));
+ if (rc2_params == NULL) {
+ PORT_Free(iv.data);
+ rv = SECFailure;
+ break;
+ }
+ /* NOTE PK11_GetKeyLength can return -1 if the key isn't and RC2, RC5,
+ * or RC4 key. Of course that wouldn't happen here doing RC2:).*/
+ rc2_params->ulEffectiveBits = PK11_GetKeyLength(key)*8;
+ if (iv.data)
+ PORT_Memcpy(rc2_params->iv,iv.data,sizeof(rc2_params->iv));
+ mech->data = (unsigned char *) rc2_params;
+ mech->len = sizeof(CK_RC2_CBC_PARAMS);
+ PORT_Free(iv.data);
+ break;
+ case CKM_RC5_ECB:
+ PORT_Free(mech);
+ return PK11_ParamFromIV(type,NULL);
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rv = pk11_GenIV(type,&iv);
+ if (rv != SECSuccess) {
+ break;
+ }
+ PORT_Free(mech);
+ return PK11_ParamFromIV(type,&iv);
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ mech->data = NULL;
+ mech->len = 0;
+ break;
+ }
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ rv = pk11_GenIV(type,&iv);
+ if (rv != SECSuccess) {
+ break;
+ }
+ mech->data = (unsigned char*)PORT_Alloc(iv.len);
+ if (mech->data == NULL) {
+ PORT_Free(iv.data);
+ rv = SECFailure;
+ break;
+ }
+ PORT_Memcpy(mech->data,iv.data,iv.len);
+ mech->len = iv.len;
+ PORT_Free(iv.data);
+ break;
+ }
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(mech,PR_TRUE);
+ return NULL;
+ }
+ return mech;
+
+}
+
+#define RC5_V10 0x10
+
+/* turn a PKCS #11 parameter into a DER Encoded Algorithm ID */
+SECStatus
+PK11_ParamToAlgid(SECOidTag algTag, SECItem *param,
+ PRArenaPool *arena, SECAlgorithmID *algid) {
+ CK_RC2_CBC_PARAMS *rc2_params;
+ sec_rc2cbcParameter rc2;
+ CK_RC5_CBC_PARAMS *rc5_params;
+ sec_rc5cbcParameter rc5;
+ CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(algTag);
+ SECItem *newParams = NULL;
+ SECStatus rv = SECFailure;
+ unsigned long rc2version;
+
+ rv = SECSuccess;
+ switch (type) {
+ case CKM_RC4:
+ case CKM_DES_ECB:
+ case CKM_DES3_ECB:
+ case CKM_IDEA_ECB:
+ case CKM_CDMF_ECB:
+ case CKM_CAST_ECB:
+ case CKM_CAST3_ECB:
+ case CKM_CAST5_ECB:
+ newParams = NULL;
+ rv = SECSuccess;
+ break;
+ case CKM_RC2_ECB:
+ break;
+ case CKM_RC2_CBC:
+ case CKM_RC2_CBC_PAD:
+ rc2_params = (CK_RC2_CBC_PARAMS *)param->data;
+ rc2version = rc2_unmap(rc2_params->ulEffectiveBits);
+ if (SEC_ASN1EncodeUnsignedInteger (NULL, &(rc2.rc2ParameterVersion),
+ rc2version) == NULL)
+ break;
+ rc2.iv.data = rc2_params->iv;
+ rc2.iv.len = sizeof(rc2_params->iv);
+ newParams = SEC_ASN1EncodeItem (NULL, NULL, &rc2,
+ sec_rc2cbc_parameter_template);
+ PORT_Free(rc2.rc2ParameterVersion.data);
+ if (newParams == NULL)
+ break;
+ rv = SECSuccess;
+ break;
+
+ case CKM_RC5_ECB: /* well not really... */
+ break;
+ case CKM_RC5_CBC:
+ case CKM_RC5_CBC_PAD:
+ rc5_params = (CK_RC5_CBC_PARAMS *)param->data;
+ if (SEC_ASN1EncodeUnsignedInteger (NULL, &rc5.version, RC5_V10) == NULL)
+ break;
+ if (SEC_ASN1EncodeUnsignedInteger (NULL, &rc5.blockSizeInBits,
+ rc5_params->ulWordsize*8) == NULL) {
+ PORT_Free(rc5.version.data);
+ break;
+ }
+ if (SEC_ASN1EncodeUnsignedInteger (NULL, &rc5.rounds,
+ rc5_params->ulWordsize*8) == NULL) {
+ PORT_Free(rc5.blockSizeInBits.data);
+ PORT_Free(rc5.version.data);
+ break;
+ }
+ rc5.iv.data = rc5_params->pIv;
+ rc5.iv.len = rc5_params->ulIvLen;
+ newParams = SEC_ASN1EncodeItem (NULL, NULL, &rc5,
+ sec_rc5cbc_parameter_template);
+ PORT_Free(rc5.version.data);
+ PORT_Free(rc5.blockSizeInBits.data);
+ PORT_Free(rc5.rounds.data);
+ if (newParams == NULL)
+ break;
+ rv = SECSuccess;
+ break;
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ return PBE_PK11ParamToAlgid(algTag, param, arena, algid);
+ default:
+ if (pk11_lookup(type)->iv == 0) {
+ rv = SECSuccess;
+ newParams = NULL;
+ break;
+ }
+ case CKM_DES_CBC:
+ case CKM_DES3_CBC:
+ case CKM_IDEA_CBC:
+ case CKM_CDMF_CBC:
+ case CKM_CAST_CBC:
+ case CKM_CAST3_CBC:
+ case CKM_CAST5_CBC:
+ case CKM_DES_CBC_PAD:
+ case CKM_DES3_CBC_PAD:
+ case CKM_IDEA_CBC_PAD:
+ case CKM_CDMF_CBC_PAD:
+ case CKM_CAST_CBC_PAD:
+ case CKM_CAST3_CBC_PAD:
+ case CKM_CAST5_CBC_PAD:
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ case CKM_BATON_ECB128:
+ case CKM_BATON_ECB96:
+ case CKM_BATON_CBC128:
+ case CKM_BATON_COUNTER:
+ case CKM_BATON_SHUFFLE:
+ case CKM_JUNIPER_ECB128:
+ case CKM_JUNIPER_CBC128:
+ case CKM_JUNIPER_COUNTER:
+ case CKM_JUNIPER_SHUFFLE:
+ newParams = SEC_ASN1EncodeItem(NULL,NULL,param,
+ SEC_OctetStringTemplate);
+ rv = SECSuccess;
+ break;
+ }
+
+ if (rv != SECSuccess) {
+ if (newParams) SECITEM_FreeItem(newParams,PR_TRUE);
+ return rv;
+ }
+
+ rv = SECOID_SetAlgorithmID(arena, algid, algTag, newParams);
+ SECITEM_FreeItem(newParams,PR_TRUE);
+ return rv;
+}
+
+/* turn an OID algorithm tag into a PKCS #11 mechanism. This allows us to
+ * map OID's directly into the PKCS #11 mechanism we want to call. We find
+ * this mapping in our standard OID table */
+CK_MECHANISM_TYPE
+PK11_AlgtagToMechanism(SECOidTag algTag) {
+ SECOidData *oid = SECOID_FindOIDByTag(algTag);
+
+ if (oid) return (CK_MECHANISM_TYPE) oid->mechanism;
+ return CKM_INVALID_MECHANISM;
+}
+
+/* turn a mechanism into an oid. */
+SECOidTag
+PK11_MechanismToAlgtag(CK_MECHANISM_TYPE type) {
+ SECOidData *oid = SECOID_FindOIDByMechanism((unsigned long)type);
+
+ if (oid) return oid->offset;
+ return SEC_OID_UNKNOWN;
+}
+
+/* Determine appropriate blocking mechanism, used when wrapping private keys
+ * which require PKCS padding. If the mechanism does not map to a padding
+ * mechanism, we simply return the mechanism.
+ */
+CK_MECHANISM_TYPE
+PK11_GetPadMechanism(CK_MECHANISM_TYPE type) {
+ switch(type) {
+ case CKM_DES_CBC:
+ return CKM_DES_CBC_PAD;
+ case CKM_DES3_CBC:
+ return CKM_DES3_CBC_PAD;
+ case CKM_RC2_CBC:
+ return CKM_RC2_CBC_PAD;
+ case CKM_CDMF_CBC:
+ return CKM_CDMF_CBC_PAD;
+ case CKM_CAST_CBC:
+ return CKM_CAST_CBC_PAD;
+ case CKM_CAST3_CBC:
+ return CKM_CAST3_CBC_PAD;
+ case CKM_CAST5_CBC:
+ return CKM_CAST5_CBC_PAD;
+ case CKM_RC5_CBC:
+ return CKM_RC5_CBC_PAD;
+ case CKM_IDEA_CBC:
+ return CKM_IDEA_CBC_PAD;
+ default:
+ break;
+ }
+
+ return type;
+}
+
+/*
+ * Build a block big enough to hold the data
+ */
+SECItem *
+PK11_BlockData(SECItem *data,unsigned long size) {
+ SECItem *newData;
+
+ newData = (SECItem *)PORT_Alloc(sizeof(SECItem));
+ if (newData == NULL) return NULL;
+
+ newData->len = (data->len + (size-1))/size;
+ newData->len *= size;
+
+ newData->data = (unsigned char *) PORT_ZAlloc(newData->len);
+ if (newData->data == NULL) {
+ PORT_Free(newData);
+ return NULL;
+ }
+ PORT_Memset(newData->data,newData->len-data->len,newData->len);
+ PORT_Memcpy(newData->data,data->data,data->len);
+ return newData;
+}
+
+
+SECStatus
+PK11_DestroyObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object) {
+ CK_RV crv;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_DestroyObject(slot->session,object);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+PK11_DestroyTokenObject(PK11SlotInfo *slot,CK_OBJECT_HANDLE object) {
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+ CK_SESSION_HANDLE rwsession;
+
+
+ rwsession = PK11_GetRWSession(slot);
+
+ crv = PK11_GETTAB(slot)->C_DestroyObject(rwsession,object);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ PORT_SetError(PK11_MapError(crv));
+ }
+ PK11_RestoreROSession(slot,rwsession);
+ return rv;
+}
+
+/*
+ * Read in a single attribute into a SECItem. Allocate space for it with
+ * PORT_Alloc unless an arena is supplied. In the latter case use the arena
+ * to allocate the space.
+ */
+SECStatus
+PK11_ReadAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type, PRArenaPool *arena, SECItem *result) {
+ CK_ATTRIBUTE attr = { 0, NULL, 0 };
+ CK_RV crv;
+
+ attr.type = type;
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,id,&attr,1);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ if (arena) {
+ attr.pValue = PORT_ArenaAlloc(arena,attr.ulValueLen);
+ } else {
+ attr.pValue = PORT_Alloc(attr.ulValueLen);
+ }
+ if (attr.pValue == NULL) return SECFailure;
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,id,&attr,1);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ if (!arena) PORT_Free(attr.pValue);
+ return SECFailure;
+ }
+
+ result->data = (unsigned char*)attr.pValue;
+ result->len = attr.ulValueLen;
+
+ return SECSuccess;
+}
+
+/*
+ * Read in a single attribute into As a Ulong.
+ */
+CK_ULONG
+PK11_ReadULongAttribute(PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type) {
+ CK_ATTRIBUTE attr;
+ CK_ULONG value = CK_UNAVAILABLE_INFORMATION;
+ CK_RV crv;
+
+ PK11_SETATTRS(&attr,type,&value,sizeof(value));
+
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,id,&attr,1);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ }
+ return value;
+}
+
+/*
+ * check to see if a bool has been set.
+ */
+CK_BBOOL
+PK11_HasAttributeSet( PK11SlotInfo *slot, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE_TYPE type )
+{
+ CK_BBOOL ckvalue = CK_FALSE;
+ CK_ATTRIBUTE theTemplate;
+ CK_RV crv;
+
+ /* Prepare to retrieve the attribute. */
+ PK11_SETATTRS( &theTemplate, type, &ckvalue, sizeof( CK_BBOOL ) );
+
+ /* Retrieve attribute value. */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB( slot )->C_GetAttributeValue( slot->session, id,
+ &theTemplate, 1 );
+ PK11_ExitSlotMonitor(slot);
+ if( crv != CKR_OK ) {
+ PORT_SetError( PK11_MapError( crv ) );
+ return CK_FALSE;
+ }
+
+ return ckvalue;
+}
+
+/*
+ * returns a full list of attributes. Allocate space for them. If an arena is
+ * provided, allocate space out of the arena.
+ */
+CK_RV
+PK11_GetAttributes(PRArenaPool *arena,PK11SlotInfo *slot,
+ CK_OBJECT_HANDLE obj,CK_ATTRIBUTE *attr, int count)
+{
+ int i;
+ /* make pedantic happy... note that it's only used arena != NULL */
+ void *mark = NULL;
+ CK_RV crv;
+
+ /*
+ * first get all the lengths of the parameters.
+ */
+ PK11_EnterSlotMonitor(slot);
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,obj,attr,count);
+ if (crv != CKR_OK) {
+ PK11_ExitSlotMonitor(slot);
+ return crv;
+ }
+
+ if (arena) {
+ mark = PORT_ArenaMark(arena);
+ if (mark == NULL) return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * now allocate space to store the results.
+ */
+ for (i=0; i < count; i++) {
+ if (arena) {
+ attr[i].pValue = PORT_ArenaAlloc(arena,attr[i].ulValueLen);
+ if (attr[i].pValue == NULL) {
+ /* arena failures, just release the mark */
+ PORT_ArenaRelease(arena,mark);
+ PK11_ExitSlotMonitor(slot);
+ return CKR_HOST_MEMORY;
+ }
+ } else {
+ attr[i].pValue = PORT_Alloc(attr[i].ulValueLen);
+ if (attr[i].pValue == NULL) {
+ /* Separate malloc failures, loop to release what we have
+ * so far */
+ int j;
+ for (j= 0; j < i; j++) {
+ PORT_Free(attr[j].pValue);
+ /* don't give the caller pointers to freed memory */
+ attr[j].pValue = NULL;
+ }
+ PK11_ExitSlotMonitor(slot);
+ return CKR_HOST_MEMORY;
+ }
+ }
+ }
+
+ /*
+ * finally get the results.
+ */
+ crv = PK11_GETTAB(slot)->C_GetAttributeValue(slot->session,obj,attr,count);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ if (arena) {
+ PORT_ArenaRelease(arena,mark);
+ } else {
+ for (i= 0; i < count; i++) {
+ PORT_Free(attr[i].pValue);
+ /* don't give the caller pointers to freed memory */
+ attr[i].pValue = NULL;
+ }
+ }
+ } else if (arena && mark) {
+ PORT_ArenaUnmark(arena,mark);
+ }
+ return crv;
+}
+
+/*
+ * Reset the token to it's initial state. For the internal module, this will
+ * Purge your keydb, and reset your cert db certs to USER_INIT.
+ */
+SECStatus
+PK11_ResetToken(PK11SlotInfo *slot, char *sso_pwd)
+{
+ unsigned char tokenName[32];
+ int tokenNameLen;
+ CK_RV crv;
+
+ /* reconstruct the token name */
+ tokenNameLen = PORT_Strlen(slot->token_name);
+ if (tokenNameLen > sizeof(tokenName)) {
+ tokenNameLen = sizeof(tokenName);
+ }
+
+ PORT_Memcpy(tokenName,slot->token_name,tokenNameLen);
+ if (tokenNameLen < sizeof(tokenName)) {
+ PORT_Memset(&tokenName[tokenNameLen],' ',
+ sizeof(tokenName)-tokenNameLen);
+ }
+
+ /* initialize the token */
+ PK11_EnterSlotMonitor(slot);
+
+ /* first shutdown the token. Existing sessions will get closed here */
+ PK11_GETTAB(slot)->C_CloseAllSessions(slot->slotID);
+ slot->session = CK_INVALID_SESSION;
+ PK11_FreeSlotCerts(slot);
+
+ /* now re-init the token */
+ crv = PK11_GETTAB(slot)->C_InitToken(slot->slotID,
+ (unsigned char *)sso_pwd, sso_pwd ? PORT_Strlen(sso_pwd): 0, tokenName);
+
+ /* finally bring the token back up */
+ PK11_InitToken(slot,PR_TRUE);
+ PK11_ExitSlotMonitor(slot);
+ if (crv != CKR_OK) {
+ PORT_SetError(PK11_MapError(crv));
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+
+
+
+static SECOidTag
+pk11_MapPBEMechanismTypeToAlgtag(CK_MECHANISM_TYPE mech)
+{
+ switch(mech) {
+ case CKM_PBE_MD2_DES_CBC:
+ return SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC;
+ case CKM_PBE_MD5_DES_CBC:
+ return SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC;
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ return SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC;
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4;
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4;
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ return SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC;
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC;
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC;
+ case CKM_PBE_SHA1_RC4_40:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4;
+ case CKM_PBE_SHA1_RC4_128:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4;
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC;
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ return SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC;
+ default:
+ break;
+ }
+ return SEC_OID_UNKNOWN;
+}
+
+CK_RV
+PK11_MapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism,
+ CK_MECHANISM_PTR pCryptoMechanism,
+ SECItem *pbe_pwd, PRBool faulty3DES)
+{
+ int iv_len = 0;
+ CK_PBE_PARAMS_PTR pPBEparams;
+ CK_RC2_CBC_PARAMS_PTR rc2_params;
+ CK_ULONG rc2_key_len;
+ SECStatus rv = SECFailure;
+ SECAlgorithmID temp_algid;
+ SECItem param, *iv;
+
+ if((pPBEMechanism == CK_NULL_PTR) || (pCryptoMechanism == CK_NULL_PTR)) {
+ return CKR_HOST_MEMORY;
+ }
+
+ pPBEparams = (CK_PBE_PARAMS_PTR)pPBEMechanism->pParameter;
+ iv_len = PK11_GetIVLength(pPBEMechanism->mechanism);
+
+ if(pPBEparams->pInitVector == CK_NULL_PTR) {
+ pPBEparams->pInitVector = (CK_CHAR_PTR)PORT_ZAlloc(iv_len);
+ if(pPBEparams->pInitVector == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ param.data = (unsigned char*)pPBEMechanism->pParameter;
+ param.len = pPBEMechanism->ulParameterLen;
+ rv = PK11_ParamToAlgid(pk11_MapPBEMechanismTypeToAlgtag(
+ pPBEMechanism->mechanism),
+ &param, NULL, &temp_algid);
+ if(rv != SECSuccess) {
+ SECOID_DestroyAlgorithmID(&temp_algid, PR_FALSE);
+ return CKR_HOST_MEMORY;
+ } else {
+ iv = SEC_PKCS5GetIV(&temp_algid, pbe_pwd, faulty3DES);
+ if((iv == NULL) && (iv_len != 0)) {
+ SECOID_DestroyAlgorithmID(&temp_algid, PR_FALSE);
+ return CKR_HOST_MEMORY;
+ }
+ SECOID_DestroyAlgorithmID(&temp_algid, PR_FALSE);
+ if(iv != NULL) {
+ PORT_Memcpy((char *)pPBEparams->pInitVector,
+ (char *)iv->data,
+ iv->len);
+ SECITEM_ZfreeItem(iv, PR_TRUE);
+ }
+ }
+ }
+
+ switch(pPBEMechanism->mechanism) {
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ pCryptoMechanism->mechanism = CKM_DES_CBC;
+ goto have_crypto_mechanism;
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ pCryptoMechanism->mechanism = CKM_DES3_CBC;
+have_crypto_mechanism:
+ pCryptoMechanism->pParameter = PORT_Alloc(iv_len);
+ pCryptoMechanism->ulParameterLen = (CK_ULONG)iv_len;
+ if(pCryptoMechanism->pParameter == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy((unsigned char *)(pCryptoMechanism->pParameter),
+ (unsigned char *)(pPBEparams->pInitVector),
+ iv_len);
+ break;
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_SHA1_RC4_128:
+ pCryptoMechanism->mechanism = CKM_RC4;
+ pCryptoMechanism->ulParameterLen = 0;
+ pCryptoMechanism->pParameter = CK_NULL_PTR;
+ break;
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ rc2_key_len = 40;
+ goto have_key_len;
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ rc2_key_len = 128;
+have_key_len:
+ pCryptoMechanism->mechanism = CKM_RC2_CBC;
+ pCryptoMechanism->ulParameterLen = (CK_ULONG)sizeof(CK_RC2_CBC_PARAMS);
+ pCryptoMechanism->pParameter =
+ (CK_RC2_CBC_PARAMS_PTR)PORT_ZAlloc(sizeof(CK_RC2_CBC_PARAMS));
+ if(pCryptoMechanism->pParameter == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ rc2_params = (CK_RC2_CBC_PARAMS_PTR)pCryptoMechanism->pParameter;
+ PORT_Memcpy((unsigned char *)rc2_params->iv,
+ (unsigned char *)pPBEparams->pInitVector,
+ iv_len);
+ rc2_params->ulEffectiveBits = rc2_key_len;
+ break;
+ default:
+ return CKR_MECHANISM_INVALID;
+ }
+
+ return CKR_OK;
+}
diff --git a/security/nss/lib/pk11wrap/pk11util.c b/security/nss/lib/pk11wrap/pk11util.c
new file mode 100644
index 000000000..709bf32c8
--- /dev/null
+++ b/security/nss/lib/pk11wrap/pk11util.c
@@ -0,0 +1,518 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * Initialize the PCKS 11 subsystem
+ */
+#include "seccomon.h"
+#include "secmod.h"
+#include "prlock.h"
+#include "secmodi.h"
+#include "pk11func.h"
+
+/* these are for displaying error messages */
+
+static SECMODModuleList *modules = NULL;
+static SECMODModule *internalModule = NULL;
+static SECMODListLock *moduleLock = NULL;
+
+extern SECStatus
+PK11_UpdateSlotAttribute(PK11SlotInfo *slot, PK11DefaultArrayEntry *entry,
+ PRBool add);
+
+
+extern int XP_SEC_MODULE_NO_LIB;
+
+extern PK11DefaultArrayEntry PK11_DefaultArray[];
+extern int num_pk11_default_mechanisms;
+
+void SECMOD_init(char *dbname) {
+ SECMODModuleList *thisModule;
+ int found=0;
+ SECStatus rv = SECFailure;
+
+
+ /* don't initialize twice */
+ if (modules) return;
+
+ PK11_InitSlotLists();
+
+ SECMOD_InitDB(dbname);
+
+ /*
+ * read in the current modules from the database
+ */
+ modules = SECMOD_ReadPermDB();
+
+ /* make sure that the internal module is loaded */
+ for (thisModule = modules; thisModule ; thisModule = thisModule->next) {
+ if (thisModule->module->internal) {
+ found++;
+ internalModule = SECMOD_ReferenceModule(thisModule->module);
+ break;
+ }
+ }
+
+ if (!found) {
+ thisModule = modules;
+ modules = SECMOD_NewModuleListElement();
+ modules->module = SECMOD_NewInternal();
+ PORT_Assert(modules->module != NULL);
+ modules->next = thisModule;
+ SECMOD_AddPermDB(modules->module);
+ internalModule = SECMOD_ReferenceModule(modules->module);
+ }
+
+ /* load it first... we need it to verify the external modules
+ * which we are loading.... */
+ rv = SECMOD_LoadModule(internalModule);
+ if( rv != SECSuccess )
+ internalModule = NULL;
+
+ /* Load each new module */
+ for (thisModule = modules; thisModule ; thisModule = thisModule->next) {
+ if( !( thisModule->module->internal ) )
+ SECMOD_LoadModule(thisModule->module);
+ }
+
+ moduleLock = SECMOD_NewListLock();
+}
+
+/*
+ * retrieve the internal module
+ */
+SECMODModule *
+SECMOD_GetInternalModule(void) {
+ return internalModule;
+}
+
+/* called from security/cmd/swfort/instinit, which doesn't need a full
+ * security LIBRARY (it used the swfortezza code, but it does have to verify
+ * cert chains against it's own list of certs. We need to initialize the
+ * security code without any database.
+ */
+void
+secmod_GetInternalModule( SECMODModule *mod) {
+ internalModule = SECMOD_ReferenceModule(mod);
+}
+
+/*
+ * get the list of PKCS11 modules that are available.
+ */
+SECMODModuleList *SECMOD_GetDefaultModuleList() { return modules; }
+SECMODListLock *SECMOD_GetDefaultModuleListLock() { return moduleLock; }
+
+
+
+/*
+ * find a module by name, and add a reference to it.
+ * return that module.
+ */
+SECMODModule *SECMOD_FindModule(char *name) {
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ SECMOD_GetReadLock(moduleLock);
+ for(mlp = modules; mlp != NULL; mlp = mlp->next) {
+ if (PORT_Strcmp(name,mlp->module->commonName) == 0) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ return module;
+}
+
+/*
+ * find a module by ID, and add a reference to it.
+ * return that module.
+ */
+SECMODModule *SECMOD_FindModuleByID(SECMODModuleID id) {
+ SECMODModuleList *mlp;
+ SECMODModule *module = NULL;
+
+ SECMOD_GetReadLock(moduleLock);
+ for(mlp = modules; mlp != NULL; mlp = mlp->next) {
+ if (id == mlp->module->moduleID) {
+ module = mlp->module;
+ SECMOD_ReferenceModule(module);
+ break;
+ }
+ }
+ SECMOD_ReleaseReadLock(moduleLock);
+
+ return module;
+}
+
+/*
+ * lookup the Slot module based on it's module ID and slot ID.
+ */
+PK11SlotInfo *SECMOD_LookupSlot(SECMODModuleID moduleID,CK_SLOT_ID slotID) {
+ int i;
+ SECMODModule *module;
+
+ module = SECMOD_FindModuleByID(moduleID);
+ if (module == NULL) return NULL;
+
+ for (i=0; i < module->slotCount; i++) {
+ PK11SlotInfo *slot = module->slots[i];
+
+ if (slot->slotID == slotID) {
+ SECMOD_DestroyModule(module);
+ return PK11_ReferenceSlot(slot);
+ }
+ }
+ SECMOD_DestroyModule(module);
+ return NULL;
+}
+
+
+/*
+ * find a module by name and delete it of the module list
+ */
+SECStatus
+SECMOD_DeleteModule(char *name, int *type) {
+ SECMODModuleList *mlp;
+ SECMODModuleList **mlpp;
+ SECStatus rv = SECFailure;
+
+
+ *type = SECMOD_EXTERNAL;
+
+ SECMOD_GetWriteLock(moduleLock);
+ for(mlpp = &modules,mlp = modules;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if (PORT_Strcmp(name,mlp->module->commonName) == 0) {
+ /* don't delete the internal module */
+ if (!mlp->module->internal) {
+ SECMOD_RemoveList(mlpp,mlp);
+ /* delete it after we release the lock */
+ rv = SECSuccess;
+ } else if (mlp->module->isFIPS) {
+ *type = SECMOD_FIPS;
+ } else {
+ *type = SECMOD_INTERNAL;
+ }
+ break;
+ }
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+
+
+ if (rv == SECSuccess) {
+ SECMOD_DeletePermDB(mlp->module);
+ SECMOD_DestroyModuleListElement(mlp);
+ }
+ return rv;
+}
+
+/*
+ * find a module by name and delete it of the module list
+ */
+SECStatus
+SECMOD_DeleteInternalModule(char *name) {
+ SECMODModuleList *mlp;
+ SECMODModuleList **mlpp;
+ SECStatus rv = SECFailure;
+
+ SECMOD_GetWriteLock(moduleLock);
+ for(mlpp = &modules,mlp = modules;
+ mlp != NULL; mlpp = &mlp->next, mlp = *mlpp) {
+ if (PORT_Strcmp(name,mlp->module->commonName) == 0) {
+ /* don't delete the internal module */
+ if (mlp->module->internal) {
+ rv = SECSuccess;
+ SECMOD_RemoveList(mlpp,mlp);
+ }
+ break;
+ }
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+
+ if (rv == SECSuccess) {
+ SECMODModule *newModule,*oldModule;
+
+ if (mlp->module->isFIPS) {
+ newModule = SECMOD_NewInternal();
+ } else {
+ newModule = SECMOD_GetFIPSInternal();
+ }
+ if (newModule == NULL) {
+ SECMODModuleList *last,*mlp2;
+ /* we're in pretty deep trouble if this happens...Security
+ * not going to work well... try to put the old module back on
+ * the list */
+ SECMOD_GetWriteLock(moduleLock);
+ for(mlp2 = modules; mlp2 != NULL; mlp2 = mlp->next) {
+ last = mlp2;
+ }
+
+ if (last == NULL) {
+ modules = mlp;
+ } else {
+ SECMOD_AddList(last,mlp,NULL);
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+ return SECFailure;
+ }
+ oldModule = internalModule;
+ internalModule = SECMOD_ReferenceModule(newModule);
+ SECMOD_AddModule(internalModule);
+ SECMOD_DestroyModule(oldModule);
+ SECMOD_DeletePermDB(mlp->module);
+ SECMOD_DestroyModuleListElement(mlp);
+ }
+ return rv;
+}
+
+SECStatus
+SECMOD_AddModule(SECMODModule *newModule) {
+ SECStatus rv;
+ SECMODModuleList *mlp, *newListElement, *last = NULL;
+
+ /* Test if a module w/ the same name already exists */
+ /* and return SECWouldBlock if so. */
+ /* We should probably add a new return value such as */
+ /* SECDublicateModule, but to minimize ripples, I'll */
+ /* give SECWouldBlock a new meaning */
+ if (SECMOD_FindModule(newModule->commonName)) {
+ return SECWouldBlock;
+ /* module already exists. */
+ }
+
+ rv = SECMOD_LoadModule(newModule);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ newListElement = SECMOD_NewModuleListElement();
+ if (newListElement == NULL) {
+ return SECFailure;
+ }
+
+ SECMOD_AddPermDB(newModule);
+
+ newListElement->module = newModule;
+
+ SECMOD_GetWriteLock(moduleLock);
+ /* Added it to the end (This is very inefficient, but Adding a module
+ * on the fly should happen maybe 2-3 times through the life this program
+ * on a given computer, and this list should be *SHORT*. */
+ for(mlp = modules; mlp != NULL; mlp = mlp->next) {
+ last = mlp;
+ }
+
+ if (last == NULL) {
+ modules = newListElement;
+ } else {
+ SECMOD_AddList(last,newListElement,NULL);
+ }
+ SECMOD_ReleaseWriteLock(moduleLock);
+ return SECSuccess;
+}
+
+PK11SlotInfo *SECMOD_FindSlot(SECMODModule *module,char *name) {
+ int i;
+ char *string;
+
+ for (i=0; i < module->slotCount; i++) {
+ PK11SlotInfo *slot = module->slots[i];
+
+ if (PK11_IsPresent(slot)) {
+ string = PK11_GetTokenName(slot);
+ } else {
+ string = PK11_GetSlotName(slot);
+ }
+ if (PORT_Strcmp(name,string) == 0) {
+ return PK11_ReferenceSlot(slot);
+ }
+ }
+ return NULL;
+}
+
+SECStatus
+PK11_GetModInfo(SECMODModule *mod,CK_INFO *info) {
+ CK_RV crv;
+
+ if (mod->functionList == NULL) return SECFailure;
+ crv = PK11_GETTAB(mod)->C_GetInfo(info);
+ return (crv == CKR_OK) ? SECSuccess : SECFailure;
+}
+
+/* Determine if we have the FIP's module loaded as the default
+ * module to trigger other bogus FIPS requirements in PKCS #12 and
+ * SSL
+ */
+PRBool
+PK11_IsFIPS(void)
+{
+ SECMODModule *mod = SECMOD_GetInternalModule();
+
+ if (mod && mod->internal) {
+ return mod->isFIPS;
+ }
+
+ return PR_FALSE;
+}
+
+/* combines NewModule() & AddModule */
+/* give a string for the module name & the full-path for the dll, */
+/* installs the PKCS11 module & update registry */
+SECStatus SECMOD_AddNewModule(char* moduleName, char* dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags) {
+ SECMODModule *module;
+ SECStatus result;
+ int s,i;
+ PK11SlotInfo* slot;
+
+ module = SECMOD_NewModule();
+
+ if (moduleName) {
+ module->commonName=PORT_ArenaStrdup(module->arena,moduleName);
+ } else {
+ module->commonName=NULL;
+ }
+
+ if (dllPath) {
+ module->dllName=PORT_ArenaStrdup(module->arena,dllPath);
+ } else {
+ module->dllName=NULL;
+ }
+
+ if (module->dllName != NULL) {
+ if (module->dllName[0] != 0) {
+ SECStatus rv = SECMOD_AddModule(module);
+ if (rv != SECSuccess) {
+ /* SECFailure: failed to add module, corrupt or missing module etc. */
+ /* SECBlock: a module with the same name already exists */
+ return rv;
+ } else { /* successfully added module */
+ /* turn on SSL cipher enable flags */
+ module->ssl[0] = cipherEnableFlags;
+
+ /* check each slot to turn on appropriate mechanisms */
+ for (s = 0; s < module->slotCount; s++) {
+ slot = (module->slots)[s];
+ /* for each possible mechanism */
+ for (i=0; i < num_pk11_default_mechanisms; i++) {
+ /* we are told to turn it on by default ? */
+ if (PK11_DefaultArray[i].flag & defaultMechanismFlags) {
+ /* it ignores if slot attribute update failes */
+ result = PK11_UpdateSlotAttribute(slot, &(PK11_DefaultArray[i]), PR_TRUE);
+ } else { /* turn this mechanism of the slot off by default */
+ result = PK11_UpdateSlotAttribute(slot, &(PK11_DefaultArray[i]), PR_FALSE);
+ }
+ } /* for each mechanism */
+ /* disable each slot if the defaultFlags say so */
+ if (defaultMechanismFlags & PK11_DISABLE_FLAG) {
+ PK11_UserDisableSlot(slot);
+ }
+ } /* for each slot of this module */
+
+ /* delete and re-add module in order to save changes to the module */
+ result = SECMOD_DeletePermDB(module);
+
+ if (result == SECSuccess) {
+ result = SECMOD_AddPermDB(module);
+ if (result == SECSuccess) {
+ return SECSuccess;
+ }
+ }
+
+ }
+ }
+ }
+ SECMOD_DestroyModule(module);
+ return SECFailure;
+}
+
+/* Public & Internal(Security Library) representation of
+ * encryption mechanism flags conversion */
+
+/* Currently, the only difference is that internal representation
+ * puts RANDOM_FLAG at bit 31 (Most-significant bit), but
+ * public representation puts this bit at bit 28
+ */
+unsigned long SECMOD_PubMechFlagstoInternal(unsigned long publicFlags) {
+ unsigned long internalFlags = publicFlags;
+
+ if (publicFlags & PUBLIC_MECH_RANDOM_FLAG) {
+ internalFlags &= ~PUBLIC_MECH_RANDOM_FLAG;
+ internalFlags |= SECMOD_RANDOM_FLAG;
+ }
+ return internalFlags;
+}
+
+unsigned long SECMOD_InternaltoPubMechFlags(unsigned long internalFlags) {
+ unsigned long publicFlags = internalFlags;
+
+ if (internalFlags & SECMOD_RANDOM_FLAG) {
+ publicFlags &= ~SECMOD_RANDOM_FLAG;
+ publicFlags |= PUBLIC_MECH_RANDOM_FLAG;
+ }
+ return publicFlags;
+}
+
+
+/* Public & Internal(Security Library) representation of */
+/* cipher flags conversion */
+/* Note: currently they are just stubs */
+unsigned long SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags) {
+ return publicFlags;
+}
+
+unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags) {
+ return internalFlags;
+}
+
+/* Funtion reports true if module of modType is installed/configured */
+PRBool
+SECMOD_IsModulePresent( unsigned long int pubCipherEnableFlags )
+{
+ PRBool result = PR_FALSE;
+ SECMODModuleList *mods = SECMOD_GetDefaultModuleList();
+ SECMODListLock *modsLock = SECMOD_GetDefaultModuleListLock();
+ SECMOD_GetReadLock(moduleLock);
+
+
+ for ( ; mods != NULL; mods = mods->next) {
+ if (mods->module->ssl[0] & SECMOD_PubCipherFlagstoInternal(pubCipherEnableFlags)) {
+ result = PR_TRUE;
+ }
+ }
+
+ SECMOD_ReleaseReadLock(moduleLock);
+ return result;
+}
diff --git a/security/nss/lib/pk11wrap/secmod.h b/security/nss/lib/pk11wrap/secmod.h
new file mode 100644
index 000000000..9c0b9100a
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmod.h
@@ -0,0 +1,132 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ *
+ * Definition of Security Module Data Structure. There is a separate data
+ * structure for each loaded PKCS #11 module.
+ */
+#ifndef _SECMOD_H_
+#define _SEDMOD_H_
+#include "seccomon.h"
+#include "secmodt.h"
+
+#define PKCS11_USE_THREADS
+
+/* These mechanisms flags are visible to all other libraries. */
+/* They must be converted to internal SECMOD_*_FLAG */
+/* if used inside the functions of the security library */
+#define PUBLIC_MECH_RSA_FLAG 0x00000001ul
+#define PUBLIC_MECH_DSA_FLAG 0x00000002ul
+#define PUBLIC_MECH_RC2_FLAG 0x00000004ul
+#define PUBLIC_MECH_RC4_FLAG 0x00000008ul
+#define PUBLIC_MECH_DES_FLAG 0x00000010ul
+#define PUBLIC_MECH_DH_FLAG 0x00000020ul
+#define PUBLIC_MECH_FORTEZZA_FLAG 0x00000040ul
+#define PUBLIC_MECH_RC5_FLAG 0x00000080ul
+#define PUBLIC_MECH_SHA1_FLAG 0x00000100ul
+#define PUBLIC_MECH_MD5_FLAG 0x00000200ul
+#define PUBLIC_MECH_MD2_FLAG 0x00000400ul
+
+#define PUBLIC_MECH_RANDOM_FLAG 0x08000000ul
+#define PUBLIC_MECH_FRIENDLY_FLAG 0x10000000ul
+#define PUBLIC_OWN_PW_DEFAULTS 0X20000000ul
+#define PUBLIC_DISABLE_FLAG 0x40000000ul
+
+/* warning: reserved means reserved */
+#define PUBLIC_MECH_RESERVED_FLAGS 0x87FFF800ul
+
+/* These cipher flags are visible to all other libraries, */
+/* But they must be converted before used in functions */
+/* withing the security module */
+#define PUBLIC_CIPHER_FORTEZZA_FLAG 0x00000001ul
+
+/* warning: reserved means reserved */
+#define PUBLIC_CIPHER_RESERVED_FLAGS 0xFFFFFFFEul
+
+SEC_BEGIN_PROTOS
+
+/* protoypes */
+extern void SECMOD_init(char *dbname);
+extern SECMODModuleList *SECMOD_GetDefaultModuleList(void);
+extern SECMODListLock *SECMOD_GetDefaultModuleListLock(void);
+
+/* lock management */
+extern SECMODListLock *SECMOD_NewListLock(void);
+extern void SECMOD_DestroyListLock(SECMODListLock *);
+extern void SECMOD_GetReadLock(SECMODListLock *);
+extern void SECMOD_ReleaseReadLock(SECMODListLock *);
+extern void SECMOD_GetWriteLock(SECMODListLock *);
+extern void SECMOD_ReleaseWriteLock(SECMODListLock *);
+
+/* list managment */
+extern void SECMOD_RemoveList(SECMODModuleList **,SECMODModuleList *);
+extern void SECMOD_AddList(SECMODModuleList *,SECMODModuleList *,SECMODListLock *);
+
+/* Operate on modules by name */
+extern SECMODModule *SECMOD_FindModule(char *name);
+extern SECMODModule *SECMOD_FindModuleByID(SECMODModuleID);
+extern SECStatus SECMOD_DeleteModule(char *name, int *type);
+extern SECStatus SECMOD_DeleteInternalModule(char *name);
+extern SECStatus SECMOD_AddNewModule(char* moduleName, char* dllPath,
+ unsigned long defaultMechanismFlags,
+ unsigned long cipherEnableFlags);
+/* database/memory management */
+extern SECMODModule *SECMOD_NewModule(void);
+extern SECMODModuleList *SECMOD_NewModuleListElement(void);
+extern SECMODModule *SECMOD_GetInternalModule(void);
+extern SECMODModule *SECMOD_GetFIPSInternal(void);
+extern SECMODModule *SECMOD_ReferenceModule(SECMODModule *module);
+extern void SECMOD_DestroyModule(SECMODModule *module);
+extern SECMODModuleList *SECMOD_DestroyModuleListElement(SECMODModuleList *);
+extern void SECMOD_DestroyModuleList(SECMODModuleList *);
+extern SECMODModule *SECMOD_DupModule(SECMODModule *old);
+extern SECStatus SECMOD_AddModule(SECMODModule *newModule);
+extern PK11SlotInfo *SECMOD_FindSlot(SECMODModule *module,char *name);
+extern PK11SlotInfo *SECMOD_LookupSlot(SECMODModuleID module,
+ unsigned long slotID);
+SECStatus SECMOD_DeletePermDB(SECMODModule *);
+SECStatus SECMOD_AddPermDB(SECMODModule *);
+
+/* Funtion reports true if at least one of the modules */
+/* of modType has been installed */
+PRBool SECMOD_IsModulePresent( unsigned long int pubCipherEnableFlags );
+
+/* Functions used to convert between internal & public representation
+ * of Mechanism Flags and Cipher Enable Flags */
+extern unsigned long SECMOD_PubMechFlagstoInternal(unsigned long publicFlags);
+extern unsigned long SECMOD_InternaltoPubMechFlags(unsigned long internalFlags);
+
+extern unsigned long SECMOD_PubCipherFlagstoInternal(unsigned long publicFlags);
+extern unsigned long SECMOD_InternaltoPubCipherFlags(unsigned long internalFlags);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/pk11wrap/secmodi.h b/security/nss/lib/pk11wrap/secmodi.h
new file mode 100644
index 000000000..e2ea081d2
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmodi.h
@@ -0,0 +1,81 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * Internal header file included only by files in pkcs11 dir, or in
+ * pkcs11 specific client and server files.
+ */
+#ifndef _SECMODI_H_
+#define _SECMODI_H_ 1
+#include "pkcs11.h"
+#include "prlock.h"
+#include "mcom_db.h"
+#include "secoidt.h"
+#include "secdert.h"
+#include "certt.h"
+#include "secmodti.h"
+
+#ifdef PKCS11_USE_THREADS
+#define PK11_USE_THREADS(x) x
+#else
+#define PK11_USE_THREADS(x)
+#endif
+
+SEC_BEGIN_PROTOS
+
+/* proto-types */
+SECMODModule * SECMOD_NewModule(void); /* create a new module */
+SECMODModule * SECMOD_NewInternal(void); /* create an internal module */
+
+/* Data base functions */
+void SECMOD_InitDB(char *);
+SECMODModuleList * SECMOD_ReadPermDB(void);
+
+/*void SECMOD_ReferenceModule(SECMODModule *); */
+
+/* Library functions */
+SECStatus SECMOD_LoadModule(SECMODModule *);
+SECStatus SECMOD_UnloadModule(SECMODModule *);
+
+void SECMOD_SlotDestroyModule(SECMODModule *module, PRBool fromSlot);
+CK_RV pk11_notify(CK_SESSION_HANDLE session, CK_NOTIFICATION event,
+ CK_VOID_PTR pdata);
+void pk11_SignedToUnsigned(CK_ATTRIBUTE *attrib);
+CK_OBJECT_HANDLE pk11_FindObjectByTemplate(PK11SlotInfo *slot,
+ CK_ATTRIBUTE *inTemplate,int tsize);
+SEC_END_PROTOS
+
+#define PK11_GETTAB(x) ((CK_FUNCTION_LIST_PTR)((x)->functionList))
+#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \
+ (x)->pValue=(v); (x)->ulValueLen = (l);
+#endif
+
diff --git a/security/nss/lib/pk11wrap/secmodt.h b/security/nss/lib/pk11wrap/secmodt.h
new file mode 100644
index 000000000..8d7ff18a8
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmodt.h
@@ -0,0 +1,173 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ *
+ * Definition of Security Module Data Structure. There is a separate data
+ * structure for each loaded PKCS #11 module.
+ */
+#ifndef _SECMODT_H_
+#define _SECMODT_H_ 1
+
+/* PKCS11 needs to be included */
+typedef struct SECMODModuleStr SECMODModule;
+typedef struct SECMODModuleListStr SECMODModuleList;
+typedef struct SECMODListLockStr SECMODListLock; /* defined in secmodi.h */
+typedef struct PK11SlotInfoStr PK11SlotInfo; /* defined in secmodti.h */
+typedef struct PK11PreSlotInfoStr PK11PreSlotInfo; /* defined in secmodti.h */
+typedef struct PK11SymKeyStr PK11SymKey; /* defined in secmodti.h */
+typedef struct PK11ContextStr PK11Context; /* defined in secmodti.h */
+typedef struct PK11SlotListStr PK11SlotList;
+typedef struct PK11SlotListElementStr PK11SlotListElement;
+typedef struct PK11RSAGenParamsStr PK11RSAGenParams;
+typedef unsigned long SECMODModuleID;
+typedef struct PK11DefaultArrayEntryStr PK11DefaultArrayEntry;
+
+struct SECMODModuleStr {
+ PRArenaPool *arena;
+ PRBool internal; /* true of internally linked modules, false
+ * for the loaded modules */
+ PRBool loaded; /* Set to true if module has been loaded */
+ PRBool isFIPS; /* Set to true if module is finst internal */
+ char *dllName; /* name of the shared library which implements
+ * this module */
+ char *commonName; /* name of the module to display to the user */
+ void *library; /* pointer to the library. opaque. used only by
+ * pk11load.c */
+ void *functionList; /* The PKCS #11 function table */
+ void *refLock; /* only used pk11db.c */
+ int refCount; /* Module reference count */
+ PK11SlotInfo **slots; /* array of slot points attatched to this mod*/
+ int slotCount; /* count of slot in above array */
+ PK11PreSlotInfo *slotInfo; /* special info about slots default settings */
+ int slotInfoCount; /* count */
+ SECMODModuleID moduleID; /* ID so we can find this module again */
+ PRBool isThreadSafe;
+ unsigned long ssl[2]; /* SSL cipher enable flags */
+};
+
+struct SECMODModuleListStr {
+ SECMODModuleList *next;
+ SECMODModule *module;
+};
+
+struct PK11SlotListStr {
+ PK11SlotListElement *head;
+ PK11SlotListElement *tail;
+ void *lock;
+};
+
+struct PK11SlotListElementStr {
+ PK11SlotListElement *next;
+ PK11SlotListElement *prev;
+ PK11SlotInfo *slot;
+ int refCount;
+};
+
+struct PK11RSAGenParamsStr {
+ int keySizeInBits;
+ unsigned long pe;
+};
+
+/*
+ * Entry into the Array which lists all the legal bits for the default flags
+ * in the slot, their definition, and the PKCS #11 mechanism the represent
+ * Always Statically allocated.
+ */
+struct PK11DefaultArrayEntryStr {
+ char *name;
+ unsigned long flag;
+ unsigned long mechanism; /* this is a long so we don't include the
+ * whole pkcs 11 world to use this header */
+};
+
+
+#define SECMOD_RSA_FLAG 0x00000001L
+#define SECMOD_DSA_FLAG 0x00000002L
+#define SECMOD_RC2_FLAG 0x00000004L
+#define SECMOD_RC4_FLAG 0x00000008L
+#define SECMOD_DES_FLAG 0x00000010L
+#define SECMOD_DH_FLAG 0x00000020L
+#define SECMOD_FORTEZZA_FLAG 0x00000040L
+#define SECMOD_RC5_FLAG 0x00000080L
+#define SECMOD_SHA1_FLAG 0x00000100L
+#define SECMOD_MD5_FLAG 0x00000200L
+#define SECMOD_MD2_FLAG 0x00000400L
+/* reserved bit for future, do not use */
+#define SECMOD_RESERVED_FLAG 0X08000000L
+#define SECMOD_FRIENDLY_FLAG 0x10000000L
+#define SECMOD_RANDOM_FLAG 0x80000000L
+
+/* need to make SECMOD and PK11 prefixes consistant. */
+#define PK11_OWN_PW_DEFAULTS 0x20000000L
+#define PK11_DISABLE_FLAG 0x40000000L
+
+/* FAKE PKCS #11 defines */
+#define CKM_FAKE_RANDOM 0x80000efeL
+#define CKM_INVALID_MECHANISM 0xffffffffL
+#define CKA_DIGEST 0x81000000L
+#define CK_INVALID_KEY 0
+#define CK_INVALID_SESSION 0
+
+/* Cryptographic module types */
+#define SECMOD_EXTERNAL 0 /* external module */
+#define SECMOD_INTERNAL 1 /* internal default module */
+#define SECMOD_FIPS 2 /* internal fips module */
+
+/*
+ * What is the origin of a given Key. Normally this doesn't matter, but
+ * the fortezza code needs to know if it needs to invoke the SSL3 fortezza
+ * hack.
+ */
+typedef enum {
+ PK11_OriginNULL, /* There is not key, it's a null SymKey */
+ PK11_OriginDerive, /* Key was derived from some other key */
+ PK11_OriginGenerated, /* Key was generated (also PBE keys) */
+ PK11_OriginFortezzaHack,/* Key was marked for fortezza hack */
+ PK11_OriginUnwrap /* Key was unwrapped or decrypted */
+} PK11Origin;
+
+/* PKCS #11 disable reasons */
+typedef enum {
+ PK11_DIS_NONE = 0,
+ PK11_DIS_USER_SELECTED,
+ PK11_DIS_COULD_NOT_INIT_TOKEN,
+ PK11_DIS_TOKEN_VERIFY_FAILED,
+ PK11_DIS_TOKEN_NOT_PRESENT
+} PK11DisableReasons;
+
+/* function pointer type for password callback function.
+ * This type is passed in to PK11_SetPasswordFunc()
+ */
+typedef char *(*PK11PasswordFunc)(PK11SlotInfo *slot, PRBool retry, void *arg);
+typedef PRBool (*PK11VerifyPasswordFunc)(PK11SlotInfo *slot, void *arg);
+typedef PRBool (*PK11IsLoggedInFunc)(PK11SlotInfo *slot, void *arg);
+
+#endif /*_SECMODT_H_ */
diff --git a/security/nss/lib/pk11wrap/secmodti.h b/security/nss/lib/pk11wrap/secmodti.h
new file mode 100644
index 000000000..c7028641c
--- /dev/null
+++ b/security/nss/lib/pk11wrap/secmodti.h
@@ -0,0 +1,179 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+/*
+ * Internal header file included only by files in pkcs11 dir, or in
+ * pkcs11 specific client and server files.
+ */
+
+#include "prmon.h"
+#include "prtypes.h"
+
+/* internal data structures */
+
+/* structure to allow us to implement the read/write locks for our
+ * module lists */
+struct SECMODListLockStr {
+ PRLock *mutex; /*general mutex to protect this data structure*/
+ PRMonitor *monitor; /* monitor to allow us to signal */
+ int state; /* read/write/waiting state */
+ int count; /* how many waiters on this lock */
+};
+
+/* represent a pkcs#11 slot reference counted. */
+struct PK11SlotInfoStr {
+ /* the PKCS11 function list for this slot */
+ void *functionList;
+ SECMODModule *module; /* our parent module */
+ /* Boolean to indicate the current state of this slot */
+ PRBool needTest; /* Has this slot been tested for Export complience */
+ PRBool isPerm; /* is this slot a permanment device */
+ PRBool isHW; /* is this slot a hardware device */
+ PRBool isInternal; /* is this slot one of our internal PKCS #11 devices */
+ PRBool disabled; /* is this slot disabled... */
+ PK11DisableReasons reason; /* Why this slot is disabled */
+ PRBool readOnly; /* is the token in this slot read-only */
+ PRBool needLogin; /* does the token of the type that needs
+ * authentication (still true even if token is logged
+ * in) */
+ PRBool hasRandom; /* can this token generated random numbers */
+ PRBool defRWSession; /* is the default session RW (we open our default
+ * session rw if the token can only handle one session
+ * at a time. */
+ PRBool isThreadSafe; /* copied from the module */
+ /* The actual flags (many of which are distilled into the above PRBools) */
+ CK_FLAGS flags; /* flags from PKCS #11 token Info */
+ /* a default session handle to do quick and dirty functions */
+ CK_SESSION_HANDLE session;
+ PRLock *sessionLock; /* lock for this session */
+ /* our ID */
+ CK_SLOT_ID slotID;
+ /* persistant flags saved from startup to startup */
+ unsigned long defaultFlags;
+ /* keep track of who is using us so we don't accidently get freed while
+ * still in use */
+ int refCount;
+ PRLock *refLock;
+ /* Password control functions for this slot. many of these are only
+ * active if the appropriate flag is on in defaultFlags */
+ int askpw; /* what our password options are */
+ int timeout; /* If we're ask_timeout, what is our timeout time is
+ * seconds */
+ int authTransact; /* allow multiple authentications off one password if
+ * they are all part of the same transaction */
+ int64 authTime; /* when were we last authenticated */
+ int minPassword; /* smallest legal password */
+ int maxPassword; /* largest legal password */
+ uint16 series; /* break up the slot info into various groups of
+ * inserted tokens so that keys and certs can be
+ * invalidated */
+ uint16 wrapKey; /* current wrapping key for SSL master secrets */
+ CK_MECHANISM_TYPE wrapMechanism;
+ /* current wrapping mechanism for current wrapKey */
+ CK_OBJECT_HANDLE refKeys[1]; /* array of existing wrapping keys for */
+ CK_MECHANISM_TYPE *mechanismList; /* list of mechanism supported by this
+ * token */
+ int mechanismCount;
+ /* cache the certificates stored on the token of this slot */
+ CERTCertificate **cert_array;
+ int array_size;
+ int cert_count;
+ char serial[16];
+ /* since these are odd sizes, keep them last. They are odd sizes to
+ * allow them to become null terminated strings */
+ char slot_name[65];
+ char token_name[33];
+ PRBool hasRSAInfo;
+ CK_FLAGS RSAInfoFlags;
+};
+
+/* hold slot default flags until we initialize a slot. This structure is only
+ * useful between the time we define a module (either by hand or from the
+ * database) and the time the module is loaded. Not reference counted */
+struct PK11PreSlotInfoStr {
+ CK_SLOT_ID slotID; /* slot these flags are for */
+ unsigned long defaultFlags; /* bit mask of default implementation this slot
+ * provides */
+ int askpw; /* slot specific password bits */
+ long timeout; /* slot specific timeout value */
+};
+
+/* Symetric Key structure. Reference Counted */
+struct PK11SymKeyStr {
+ CK_MECHANISM_TYPE type; /* type of operation this key was created for*/
+ CK_OBJECT_HANDLE objectID; /* object id of this key in the slot */
+ PK11SlotInfo *slot; /* Slot this key is loaded into */
+ void *cx; /* window context in case we need to loggin */
+ PRBool owner;
+ SECItem data; /* raw key data if available */
+ CK_SESSION_HANDLE session;
+ PRBool sessionOwner;
+ int refCount; /* number of references to this key */
+ PRLock *refLock;
+ int size; /* key size in bytes */
+ PK11Origin origin; /* where this key came from
+ (see def in secmodt.h) */
+ uint16 series; /* break up the slot info into various groups of
+ * inserted tokens so that keys and certs can be
+ * invalidated */
+};
+
+
+/*
+ * hold a hash, encryption or signing context for multi-part operations.
+ * hold enough information so that multiple contexts can be interleaved
+ * if necessary. ... Not RefCounted.
+ */
+struct PK11ContextStr {
+ CK_ATTRIBUTE_TYPE operation; /* type of operation this context is doing
+ * (CKA_ENCRYPT, CKA_SIGN, CKA_HASH, etc. */
+ PK11SymKey *key; /* symetric key used in this context */
+ PK11SlotInfo *slot; /* slot this context is operationing on */
+ CK_SESSION_HANDLE session; /* session this context is using */
+ PRLock *sessionLock; /* lock before accessing a PKCS #11
+ * session */
+ PRBool ownSession;/* do we own the session? */
+ void *cx; /* window context in case we need to loggin*/
+ void *savedData;/* save data when we are multiplexing on a
+ * single context */
+ unsigned long savedLength; /* length of the saved context */
+ SECItem *param; /* mechanism parameters used to build this
+ context */
+ PRBool init; /* has this contexted been initialized */
+ CK_MECHANISM_TYPE type; /* what is the PKCS #11 this context is
+ * representing (usually what algorithm is
+ * being used (CKM_RSA_PKCS, CKM_DES,
+ * CKM_SHA, etc.*/
+ PRBool fortezzaHack; /*Fortezza SSL has some special
+ * non-standard semantics*/
+};
+
diff --git a/security/nss/lib/pkcs12/Makefile b/security/nss/lib/pkcs12/Makefile
new file mode 100644
index 000000000..5742208b5
--- /dev/null
+++ b/security/nss/lib/pkcs12/Makefile
@@ -0,0 +1,77 @@
+#! gmake
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
+
diff --git a/security/nss/lib/pkcs12/config.mk b/security/nss/lib/pkcs12/config.mk
new file mode 100644
index 000000000..1684cf3fa
--- /dev/null
+++ b/security/nss/lib/pkcs12/config.mk
@@ -0,0 +1,45 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+
+#
+# Override TARGETS variable so that only static libraries
+# are specifed as dependencies within rules.mk.
+#
+
+TARGETS = $(LIBRARY)
+SHARED_LIBRARY =
+IMPORT_LIBRARY =
+PURE_LIBRARY =
+PROGRAM =
+
diff --git a/security/nss/lib/pkcs12/manifest.mn b/security/nss/lib/pkcs12/manifest.mn
new file mode 100644
index 000000000..c8b4981d3
--- /dev/null
+++ b/security/nss/lib/pkcs12/manifest.mn
@@ -0,0 +1,58 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+CORE_DEPTH = ../../..
+
+EXPORTS = \
+ pkcs12t.h \
+ pkcs12.h \
+ p12plcy.h \
+ p12.h \
+ p12t.h \
+ $(NULL)
+
+MODULE = security
+
+CSRCS = \
+ p12local.c \
+ p12creat.c \
+ p12dec.c \
+ p12plcy.c \
+ p12tmpl.c \
+ p12e.c \
+ p12d.c \
+ $(NULL)
+
+REQUIRES = security dbm
+
+LIBRARY_NAME = pkcs12
diff --git a/security/nss/lib/pkcs12/p12.h b/security/nss/lib/pkcs12/p12.h
new file mode 100644
index 000000000..eac067c93
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12.h
@@ -0,0 +1,173 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+
+#ifndef _P12_H_
+#define _P12_H_
+
+#include "secoid.h"
+#include "key.h"
+#include "secpkcs7.h"
+#include "p12t.h"
+
+typedef int (* PKCS12OpenFunction)(void *arg);
+typedef int (* PKCS12ReadFunction)(void *arg, unsigned char *buffer,
+ unsigned int *lenRead, unsigned int maxLen);
+typedef int (* PKCS12WriteFunction)(void *arg, unsigned char *buffer,
+ unsigned int *bufLen, unsigned int *lenWritten);
+typedef int (* PKCS12CloseFunction)(void *arg);
+typedef SECStatus (* PKCS12UnicodeConvertFunction)(PRArenaPool *arena,
+ SECItem *dest, SECItem *src,
+ PRBool toUnicode,
+ PRBool swapBytes);
+typedef void (* SEC_PKCS12EncoderOutputCallback)(void *arg, const char *buf,
+ unsigned long len);
+typedef void (* SEC_PKCS12DecoderOutputCallback)(void *arg, const char *buf,
+ unsigned long len);
+typedef SECItem * (* SEC_PKCS12NicknameCollisionCallback)(SECItem *old_nickname,
+ PRBool *cancel,
+ void *arg);
+
+
+
+
+typedef SECStatus (*digestOpenFn)(void *arg, PRBool readData);
+typedef SECStatus (*digestCloseFn)(void *arg, PRBool removeFile);
+typedef int (*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;
+
+struct sec_PKCS12PasswordModeInfo {
+ SECItem *password;
+ SECOidTag algorithm;
+};
+
+struct sec_PKCS12PublicKeyModeInfo {
+ CERTCertificate *cert;
+ CERTCertDBHandle *certDb;
+ SECOidTag algorithm;
+ int keySize;
+};
+
+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_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ void *keySafe, void *keyNestedDest,
+ PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm);
+
+extern SECStatus
+SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest, SECItem *derCert,
+ void *keySafe, void *keyNestedDest,
+ SECKEYEncryptedPrivateKeyInfo *epki, char *nickname);
+
+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_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);
+
+extern SECStatus
+SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx);
+
+CERTCertList *
+SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx);
+
+#endif
diff --git a/security/nss/lib/pkcs12/p12creat.c b/security/nss/lib/pkcs12/p12creat.c
new file mode 100644
index 000000000..65678b299
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12creat.c
@@ -0,0 +1,251 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#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;
+ PRArenaPool *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(PRArenaPool *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_PKCS7ContentInfo));
+
+ 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(PRArenaPool *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(PRArenaPool *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 000000000..aea26a9e9
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12d.c
@@ -0,0 +1,3224 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+
+#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 "alghmac.h"
+#include "secder.h"
+#include "secport.h"
+
+#include "certdb.h"
+
+#include "prcpucfg.h"
+
+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 */
+ PRArenaPool *arena;
+
+ /* decoder context and destination for decoding safe contents */
+ SEC_ASN1DecoderContext *safeContentsDcx;
+ sec_PKCS12SafeContents safeContents;
+
+ /* information for decoding safe bags within the safe contents.
+ * these variables are updated for each safe bag decoded.
+ */
+ SEC_ASN1DecoderContext *currentSafeBagDcx;
+ sec_PKCS12SafeBag *currentSafeBag;
+ PRBool skipCurrentSafeBag;
+
+ /* if the safe contents is nested, the parent is pointed to here. */
+ sec_PKCS12SafeContentsContext *nestedCtx;
+};
+
+/* 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 {
+ PRArenaPool *arena;
+ PK11SlotInfo *slot;
+ void *wincx;
+ PRBool error;
+ int errorValue;
+
+ /* password */
+ SECItem *pwitem;
+
+ /* used for decoding the PFX structure */
+ SEC_ASN1DecoderContext *pfxDcx;
+ sec_PKCS12PFXItem pfx;
+
+ /* safe bags found during decoding */
+ sec_PKCS12SafeBag **safeBags;
+ unsigned int safeBagCount;
+
+ /* state variables for decoding authenticated safes. */
+ SEC_PKCS7DecoderContext *currentASafeP7Dcx;
+ SEC_PKCS5KeyAndPassword *currentASafeKeyPwd;
+ SEC_ASN1DecoderContext *aSafeDcx;
+ SEC_PKCS7DecoderContext *aSafeP7Dcx;
+ sec_PKCS12AuthenticatedSafe authSafe;
+ SEC_PKCS7ContentInfo *aSafeCinfo;
+ sec_PKCS12SafeContents safeContents;
+
+ /* safe contents info */
+ unsigned int safeContentsCnt;
+ sec_PKCS12SafeContentsContext **safeContentsList;
+
+ /* HMAC info */
+ sec_PKCS12MacData macData;
+ SEC_ASN1DecoderContext *hmacDcx;
+
+ /* routines for reading back the data to be hmac'd */
+ digestOpenFn dOpen;
+ digestCloseFn dClose;
+ digestIOFn dRead, dWrite;
+ void *dArg;
+
+ /* helper functions */
+ SECKEYGetPasswordKey pwfn;
+ void *pwfnarg;
+ PRBool swapUnicodeBytes;
+
+ /* import information */
+ PRBool bagsVerified;
+};
+
+
+/* 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_PKCS5KeyAndPassword *keyPwd =
+ (SEC_PKCS5KeyAndPassword *)arg;
+
+ if(!keyPwd) {
+ return NULL;
+ }
+
+ /* if no slot specified, use the internal key slot */
+ if(!keyPwd->slot) {
+ keyPwd->slot = PK11_GetInternalKeySlot();
+ }
+
+ /* retrieve the key */
+ if(!keyPwd->key) {
+ keyPwd->key = PK11_PBEKeyGen(keyPwd->slot, algid,
+ keyPwd->pwitem, PR_FALSE, keyPwd->wincx);
+ }
+
+ return (PK11SymKey *)keyPwd;
+}
+
+/* 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.
+ */
+ if(p12dcx->safeBagCount) {
+ p12dcx->safeBags =
+ (sec_PKCS12SafeBag**)PORT_ArenaGrow(p12dcx->arena,p12dcx->safeBags,
+ (p12dcx->safeBagCount + 1) * sizeof(sec_PKCS12SafeBag *),
+ (p12dcx->safeBagCount + 2) * sizeof(sec_PKCS12SafeBag *));
+ } else {
+ p12dcx->safeBags = (sec_PKCS12SafeBag**)PORT_ArenaZAlloc(p12dcx->arena,
+ 2 * sizeof(sec_PKCS12SafeBag *));
+ }
+ if(!p12dcx->safeBags) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ /* append the bag to the end of the list and update the reference
+ * in the safeContentsCtx.
+ */
+ p12dcx->safeBags[p12dcx->safeBagCount] =
+ (sec_PKCS12SafeBag*)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(sec_PKCS12SafeBag));
+ safeContentsCtx->currentSafeBag = p12dcx->safeBags[p12dcx->safeBagCount];
+ p12dcx->safeBags[++p12dcx->safeBagCount] = NULL;
+ if(!safeContentsCtx->currentSafeBag) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ safeContentsCtx->currentSafeBag->slot = safeContentsCtx->p12dcx->slot;
+ safeContentsCtx->currentSafeBag->pwitem = safeContentsCtx->p12dcx->pwitem;
+ safeContentsCtx->currentSafeBag->swapUnicodeBytes =
+ safeContentsCtx->p12dcx->swapUnicodeBytes;
+ safeContentsCtx->currentSafeBag->arena = safeContentsCtx->p12dcx->arena;
+
+ 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;
+
+ /* make sure that we are not skipping the current safeBag,
+ * and that there are no errors. If so, just return rather
+ * than continuing to process.
+ */
+ if(!safeContentsCtx || !safeContentsCtx->p12dcx
+ || safeContentsCtx->p12dcx->error
+ || safeContentsCtx->skipCurrentSafeBag) {
+ return;
+ }
+ p12dcx = safeContentsCtx->p12dcx;
+
+ rv = SEC_ASN1DecoderUpdate(safeContentsCtx->currentSafeBagDcx, data, len);
+ if(rv != SECSuccess) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ return;
+
+loser:
+ /* set the error, and 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.
+ */
+ p12dcx->error = PR_TRUE;
+ SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx);
+ safeContentsCtx->currentSafeBagDcx = NULL;
+ return;
+}
+
+/* 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);
+static void
+sec_pkcs12_decoder_safe_bag_update(void *arg, const char *data,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind);
+
+/* 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->safeContentsDcx);
+ SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx);
+ safeContentsCtx->currentSafeBagDcx = 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->currentSafeBagDcx = SEC_ASN1DecoderStart(p12dcx->arena,
+ safeContentsCtx->currentSafeBag,
+ sec_PKCS12SafeBagTemplate);
+ if(!safeContentsCtx->currentSafeBagDcx) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ 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->currentSafeBagDcx,
+ sec_pkcs12_decoder_safe_bag_notify,
+ safeContentsCtx);
+ SEC_ASN1DecoderSetFilterProc(safeContentsCtx->safeContentsDcx,
+ 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->currentSafeBagDcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->currentSafeBagDcx);
+ safeContentsCtx->currentSafeBagDcx = NULL;
+ }
+
+ SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->safeContentsDcx);
+ SEC_ASN1DecoderClearFilterProc(safeContentsCtx->safeContentsDcx);
+
+ 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.
+ */
+ if(!p12dcx->safeContentsCnt) {
+ p12dcx->safeContentsList =
+ (sec_PKCS12SafeContentsContext**)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(sec_PKCS12SafeContentsContext *));
+ } else {
+ p12dcx->safeContentsList =
+ (sec_PKCS12SafeContentsContext **) PORT_ArenaGrow(p12dcx->arena,
+ p12dcx->safeContentsList,
+ (p12dcx->safeContentsCnt *
+ sizeof(sec_PKCS12SafeContentsContext *)),
+ (1 + p12dcx->safeContentsCnt *
+ sizeof(sec_PKCS12SafeContentsContext *)));
+ }
+ if(!p12dcx->safeContentsList) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ p12dcx->safeContentsList[p12dcx->safeContentsCnt] =
+ (sec_PKCS12SafeContentsContext*)PORT_ArenaZAlloc(
+ p12dcx->arena,
+ sizeof(sec_PKCS12SafeContentsContext));
+ p12dcx->safeContentsList[p12dcx->safeContentsCnt+1] = NULL;
+ if(!p12dcx->safeContentsList[p12dcx->safeContentsCnt]) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ /* set up the state variables */
+ safeContentsCtx = p12dcx->safeContentsList[p12dcx->safeContentsCnt];
+ p12dcx->safeContentsCnt++;
+ 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->safeContentsDcx = SEC_ASN1DecoderStart(p12dcx->arena,
+ &safeContentsCtx->safeContents,
+ theTemplate);
+
+ if(!safeContentsCtx->safeContentsDcx) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ /* set the safeContents notify procedure to look for
+ * and start the decode of safeBags.
+ */
+ SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->safeContentsDcx,
+ 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->safeContentsDcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx);
+ safeContentsCtx->safeContentsDcx = 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) {
+ 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->safeContentsDcx, buf, len);
+ if(rv != SECSuccess) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ return;
+
+loser:
+ /* handle any errors. If a decoding context is open, close it. */
+ p12dcx->error = PR_TRUE;
+ if(safeContentsCtx->safeContentsDcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx);
+ safeContentsCtx->safeContentsDcx = 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->nestedCtx = sec_pkcs12_decoder_safe_contents_init_decode(
+ safeContentsCtx->p12dcx,
+ PR_TRUE);
+ if(!safeContentsCtx->nestedCtx) {
+ return SECFailure;
+ }
+
+ /* set up new filter proc */
+ SEC_ASN1DecoderSetNotifyProc(safeContentsCtx->nestedCtx->safeContentsDcx,
+ sec_pkcs12_decoder_safe_contents_notify,
+ safeContentsCtx->nestedCtx);
+ SEC_ASN1DecoderSetFilterProc(safeContentsCtx->currentSafeBagDcx,
+ sec_pkcs12_decoder_nested_safe_contents_update,
+ safeContentsCtx->nestedCtx, 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->currentSafeBagDcx);
+ SEC_ASN1DecoderClearNotifyProc(safeContentsCtx->nestedCtx->safeContentsDcx);
+ SEC_ASN1DecoderFinish(safeContentsCtx->nestedCtx->safeContentsDcx);
+ safeContentsCtx->nestedCtx->safeContentsDcx = NULL;
+ safeContentsCtx->nestedCtx = 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) {
+ return;
+ }
+ p12dcx = safeContentsCtx->p12dcx;
+
+ /* update the decoder */
+ rv = SEC_ASN1DecoderUpdate(safeContentsCtx->safeContentsDcx, buf, len);
+ if(rv != SECSuccess) {
+ p12dcx->errorValue = SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE;
+ goto loser;
+ }
+
+ return;
+
+loser:
+ /* set the error and finish the context */
+ p12dcx->error = PR_TRUE;
+ if(safeContentsCtx->safeContentsDcx) {
+ SEC_ASN1DecoderFinish(safeContentsCtx->safeContentsDcx);
+ safeContentsCtx->safeContentsDcx = 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;
+ }
+
+ /* set up password and encryption key information */
+ p12dcx->currentASafeKeyPwd =
+ (SEC_PKCS5KeyAndPassword*)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(SEC_PKCS5KeyAndPassword));
+ if(!p12dcx->currentASafeKeyPwd) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+ p12dcx->currentASafeKeyPwd->pwitem = p12dcx->pwitem;
+ p12dcx->currentASafeKeyPwd->slot = p12dcx->slot;
+ p12dcx->currentASafeKeyPwd->wincx = p12dcx->wincx;
+
+ /* 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->currentASafeKeyPwd,
+ sec_pkcs12_decoder_decryption_allowed);
+ if(!p12dcx->currentASafeP7Dcx) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ SEC_ASN1DecoderSetFilterProc(p12dcx->aSafeDcx,
+ sec_pkcs12_decoder_wrap_p7_update,
+ p12dcx->currentASafeP7Dcx, PR_TRUE);
+ }
+
+ if(!before) {
+ /* if one is being decoded, finish the decode */
+ if(p12dcx->currentASafeP7Dcx != NULL) {
+ if(!SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx)) {
+ p12dcx->currentASafeP7Dcx = NULL;
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+ p12dcx->currentASafeP7Dcx = NULL;
+ }
+ p12dcx->currentASafeP7Dcx = NULL;
+ if(p12dcx->currentASafeKeyPwd->key != NULL) {
+ p12dcx->currentASafeKeyPwd->key = NULL;
+ }
+ }
+
+
+ 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->aSafeDcx, buf, len);
+ if(rv != SECSuccess) {
+ p12dcx->error = (PRBool)SEC_ERROR_NO_MEMORY;
+ 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->aSafeDcx);
+ p12dcx->aSafeDcx = 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->aSafeDcx = SEC_ASN1DecoderStart(p12dcx->arena,
+ &p12dcx->authSafe,
+ sec_PKCS12AuthenticatedSafeTemplate);
+ if(!p12dcx->aSafeDcx) {
+ p12dcx->errorValue = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ /* set the notify function */
+ SEC_ASN1DecoderSetNotifyProc(p12dcx->aSafeDcx,
+ 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 = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ /* open the temp file for writing, if the filter functions were set */
+ if(p12dcx->dOpen && (*p12dcx->dOpen)(p12dcx->dArg, PR_FALSE)
+ != SECSuccess) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ p12dcx->error = PR_TRUE;
+
+ if(p12dcx->aSafeDcx) {
+ SEC_ASN1DecoderFinish(p12dcx->aSafeDcx);
+ p12dcx->aSafeDcx = 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 occurrs, clear the notifyProc and the filterProc
+ * and continue.
+ */
+ if(p12dcx->error) {
+ SEC_ASN1DecoderClearNotifyProc(p12dcx->pfxDcx);
+ SEC_ASN1DecoderClearFilterProc(p12dcx->pfxDcx);
+ 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->pfxDcx,
+ 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->pfxDcx);
+ if(p12dcx->dClose && ((*p12dcx->dClose)(p12dcx->dArg, PR_FALSE)
+ != SECSuccess)) {
+ p12dcx->errorValue = PORT_GetError();
+ goto loser;
+ }
+
+ }
+
+ return;
+
+loser:
+ p12dcx->error = PR_TRUE;
+}
+
+/* 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 hmack recomputed
+ * and verified. doesn't seem to be away for both encoding
+ * and decoding to be single pass, thus the need for these
+ * routines.
+ * dArg - the argument for dOpen, etc.
+ *
+ * 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;
+ PRArenaPool *arena;
+
+ arena = PORT_NewArena(2048); /* different size? */
+ if(!arena) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ /* allocate the decoder context and set the state variables */
+ p12dcx = (SEC_PKCS12DecoderContext*)PORT_ArenaZAlloc(arena, sizeof(SEC_PKCS12DecoderContext));
+ if(!p12dcx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ p12dcx->arena = arena;
+ p12dcx->pwitem = pwitem;
+ p12dcx->slot = (slot ? slot : PK11_GetInternalKeySlot());
+ p12dcx->wincx = wincx;
+#ifdef IS_LITTLE_ENDIAN
+ p12dcx->swapUnicodeBytes = PR_TRUE;
+#else
+ p12dcx->swapUnicodeBytes = PR_FALSE;
+#endif
+ p12dcx->errorValue = 0;
+ p12dcx->error = PR_FALSE;
+
+ /* a slot is *required */
+ if(!slot) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* start the decoding of the PFX and set the notify proc
+ * for the PFX item.
+ */
+ p12dcx->pfxDcx = SEC_ASN1DecoderStart(p12dcx->arena, &p12dcx->pfx,
+ sec_PKCS12PFXItemTemplate);
+ if(!p12dcx->pfxDcx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ SEC_ASN1DecoderSetNotifyProc(p12dcx->pfxDcx,
+ 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;
+
+ return p12dcx;
+
+loser:
+ PORT_FreeArena(arena, PR_TRUE);
+ return NULL;
+}
+
+/* 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) {
+ return SECFailure;
+ }
+
+ /* update the PFX decoder context */
+ rv = SEC_ASN1DecoderUpdate(p12dcx->pfxDcx, (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;
+}
+
+/* IN_BUF_LEN should be larger than SHA1_LENGTH */
+#define IN_BUF_LEN 80
+
+/* 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)
+{
+ SECStatus rv = SECFailure;
+ PBEBitGenContext *pbeCtxt = NULL;
+ SECItem *hmacKey = NULL, hmacRes;
+ unsigned char buf[IN_BUF_LEN];
+ void *hmacCx;
+ unsigned int bufLen;
+ int iteration;
+
+ if(!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ /* generate hmac key */
+ if(p12dcx->macData.iter.data) {
+ iteration = (int)DER_GetInteger(&p12dcx->macData.iter);
+ } else {
+ iteration = 1;
+ }
+ pbeCtxt = PBE_CreateContext(SECOID_GetAlgorithmTag(
+ &p12dcx->macData.safeMac.digestAlgorithm),
+ pbeBitGenIntegrityKey, p12dcx->pwitem,
+ &p12dcx->macData.macSalt, 160, iteration);
+ if(!pbeCtxt) {
+ return SECFailure;
+ }
+ hmacKey = PBE_GenerateBits(pbeCtxt);
+ PBE_DestroyContext(pbeCtxt);
+ pbeCtxt = NULL;
+ if(!hmacKey) {
+ return SECFailure;
+ }
+
+ /* init hmac */
+ hmacCx = HMAC_Create(SECOID_GetAlgorithmTag(
+ &p12dcx->macData.safeMac.digestAlgorithm),
+ hmacKey->data, hmacKey->len);
+ SECITEM_ZfreeItem(hmacKey, PR_TRUE);
+ hmacKey = NULL;
+ if(!hmacCx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ HMAC_Begin((HMACContext*)hmacCx);
+
+ /* 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 occured reading from the
+ * file.
+ */
+ while(1) {
+ int bytesRead = (*p12dcx->dRead)(p12dcx->dArg, buf, IN_BUF_LEN);
+ if(bytesRead == -1) {
+ goto loser;
+ }
+
+ HMAC_Update((HMACContext*)hmacCx, buf, bytesRead);
+ if(bytesRead < IN_BUF_LEN) {
+ break;
+ }
+ }
+
+ /* finish the hmac context */
+ HMAC_Finish((HMACContext*)hmacCx, buf, &bufLen, IN_BUF_LEN);
+ HMAC_Destroy((HMACContext*)hmacCx);
+ hmacCx = NULL;
+
+ 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);
+ }
+
+ if(hmacCx) {
+ HMAC_Destroy((HMACContext*)hmacCx);
+ }
+
+ if(hmacKey) {
+ SECITEM_ZfreeItem(hmacKey, PR_TRUE);
+ }
+
+ 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 occured... */
+ if(!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ /* 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);
+ } else {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ }
+ } else {
+ if(SEC_PKCS7VerifySignature(p12dcx->aSafeCinfo, certUsageEmailSigner,
+ PR_FALSE)) {
+ return SECSuccess;
+ } else {
+ 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)
+{
+ void *freedCtxt = NULL;
+
+ if(!p12dcx) {
+ return;
+ }
+
+ if(p12dcx->pfxDcx) {
+ SEC_ASN1DecoderFinish(p12dcx->pfxDcx);
+ p12dcx->pfxDcx = NULL;
+ }
+
+ if(p12dcx->aSafeDcx) {
+ SEC_ASN1DecoderFinish(p12dcx->aSafeDcx);
+ p12dcx->aSafeDcx = NULL;
+ }
+
+ if(p12dcx->currentASafeP7Dcx) {
+ SEC_PKCS7DecoderFinish(p12dcx->currentASafeP7Dcx);
+ p12dcx->currentASafeP7Dcx = NULL;
+ }
+
+ if(p12dcx->aSafeP7Dcx) {
+ SEC_PKCS7DecoderFinish(p12dcx->aSafeP7Dcx);
+ }
+
+ if(p12dcx->hmacDcx) {
+ SEC_ASN1DecoderFinish(p12dcx->hmacDcx);
+ p12dcx->hmacDcx = NULL;
+ }
+
+ 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) {
+ return SECFailure;
+ }
+
+ oid = SECOID_FindOIDByTag(attributeType);
+ if(!oid) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ if(!bag->attribs) {
+ bag->attribs = (sec_PKCS12Attribute**)PORT_ArenaZAlloc(bag->arena,
+ sizeof(sec_PKCS12Attribute *) * 2);
+ } else {
+ while(bag->attribs[i]) i++;
+ bag->attribs = (sec_PKCS12Attribute **)PORT_ArenaGrow(bag->arena,
+ bag->attribs,
+ (i + 1) * sizeof(sec_PKCS12Attribute *),
+ (i + 2) * sizeof(sec_PKCS12Attribute *));
+ }
+
+ if(!bag->attribs) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ bag->attribs[i] = (sec_PKCS12Attribute*)PORT_ArenaZAlloc(bag->arena,
+ sizeof(sec_PKCS12Attribute));
+ if(!bag->attribs) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ bag->attribs[i]->attrValue = (SECItem**)PORT_ArenaZAlloc(bag->arena,
+ sizeof(SECItem *) * 2);
+ if(!bag->attribs[i]->attrValue) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ bag->attribs[i+1] = NULL;
+ bag->attribs[i]->attrValue[0] = attrValue;
+ bag->attribs[i]->attrValue[1] = NULL;
+
+ if(SECITEM_CopyItem(bag->arena, &bag->attribs[i]->attrType, &oid->oid)
+ != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+static SECItem *
+sec_pkcs12_get_attribute_value(sec_PKCS12SafeBag *bag,
+ SECOidTag attributeType)
+{
+ int i = 0;
+
+ if(!bag->attribs) {
+ return NULL;
+ }
+
+ while(bag->attribs[i] != NULL) {
+ if(SECOID_FindOIDTag(&bag->attribs[i]->attrType)
+ == attributeType) {
+ return bag->attribs[i]->attrValue[0];
+ }
+ i++;
+ }
+
+ 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; /*Mac breaks without this type cast*/
+ 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) {
+ bag->problem = PR_TRUE;
+ bag->error = SEC_ERROR_NO_MEMORY;
+ return NULL;
+ }
+
+ src = sec_pkcs12_get_attribute_value(bag, SEC_OID_PKCS9_FRIENDLY_NAME);
+ if(!src) {
+ 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)
+{
+ int i = 0;
+ sec_PKCS12Attribute *attr = NULL;
+ SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_FRIENDLY_NAME);
+
+ if(!bag || !bag->arena || !name) {
+ return SECFailure;
+ }
+
+ if(!bag->attribs) {
+ if(!oid) {
+ goto loser;
+ }
+
+ bag->attribs = (sec_PKCS12Attribute**)PORT_ArenaZAlloc(bag->arena,
+ sizeof(sec_PKCS12Attribute *)*2);
+ if(!bag->attribs) {
+ goto loser;
+ }
+ bag->attribs[0] = (sec_PKCS12Attribute*)PORT_ArenaZAlloc(bag->arena,
+ sizeof(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 {
+ while(bag->attribs[i]) {
+ if(SECOID_FindOIDTag(&bag->attribs[i]->attrType)
+ == SEC_OID_PKCS9_FRIENDLY_NAME) {
+ attr = bag->attribs[i];
+ goto have_attrib;
+
+ }
+ i++;
+ }
+ if(!attr) {
+ bag->attribs = (sec_PKCS12Attribute **)PORT_ArenaGrow(bag->arena,
+ bag->attribs,
+ (i+1) * sizeof(sec_PKCS12Attribute *),
+ (i+2) * sizeof(sec_PKCS12Attribute *));
+ if(!bag->attribs) {
+ goto loser;
+ }
+ bag->attribs[i] =
+ (sec_PKCS12Attribute *)PORT_ArenaZAlloc(bag->arena,
+ sizeof(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;
+ }
+ }
+ }
+have_attrib:
+ PORT_Assert(attr);
+ if(!attr->attrValue) {
+ attr->attrValue = (SECItem **)PORT_ArenaZAlloc(bag->arena,
+ sizeof(SECItem *) * 2);
+ if(!attr->attrValue) {
+ goto loser;
+ }
+ attr->attrValue[0] = (SECItem*)PORT_ArenaZAlloc(bag->arena,
+ sizeof(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 = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+}
+
+static SECStatus
+sec_pkcs12_get_key_info(sec_PKCS12SafeBag *key)
+{
+ int i = 0;
+ SECKEYPrivateKeyInfo *pki = NULL;
+
+ if(!key) {
+ 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]) {
+ SECItem *attrValue = NULL;
+
+ if(SECOID_FindOIDTag(&pki->attributes[i]->attrType) ==
+ SEC_OID_PKCS9_LOCAL_KEY_ID) {
+ attrValue = sec_pkcs12_get_attribute_value(key,
+ SEC_OID_PKCS9_LOCAL_KEY_ID);
+ if(!attrValue) {
+ if(sec_pkcs12_decoder_set_attribute_value(key,
+ SEC_OID_PKCS9_LOCAL_KEY_ID,
+ pki->attributes[i]->attrValue[0])
+ != SECSuccess) {
+ key->problem = PR_TRUE;
+ key->error = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+ }
+ }
+ }
+
+ if(SECOID_FindOIDTag(&pki->attributes[i]->attrType) ==
+ SEC_OID_PKCS9_FRIENDLY_NAME) {
+ attrValue = sec_pkcs12_get_attribute_value(key,
+ SEC_OID_PKCS9_FRIENDLY_NAME);
+ if(!attrValue) {
+ if(sec_pkcs12_decoder_set_attribute_value(key,
+ SEC_OID_PKCS9_FRIENDLY_NAME,
+ pki->attributes[i]->attrValue[0])
+ != SECSuccess) {
+ key->problem = PR_TRUE;
+ key->error = SEC_ERROR_NO_MEMORY;
+ 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,
+ void *wincx)
+{
+ SECItem *nickname;
+
+ if(!cert) {
+ 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) {
+ cert->error = SEC_ERROR_NO_MEMORY;
+ cert->problem = PR_TRUE;
+ if(nickname) {
+ 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,
+ void *wincx)
+{
+ if(!nickname || !cert) {
+ return SECFailure;
+ }
+
+ if(sec_pkcs12_set_nickname(cert, nickname) != SECSuccess) {
+ cert->error = SEC_ERROR_NO_MEMORY;
+ cert->problem = PR_TRUE;
+ return SECFailure;
+ }
+
+ if(key) {
+ if(sec_pkcs12_set_nickname(key, nickname) != SECSuccess) {
+ cert->error = SEC_ERROR_NO_MEMORY;
+ cert->problem = PR_TRUE;
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+/* retrieve the DER cert from the cert bag */
+static SECItem *
+sec_pkcs12_get_der_cert(sec_PKCS12SafeBag *cert)
+{
+ if(!cert) {
+ 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 {
+ PRArenaPool *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) {
+ return SECFailure;
+ }
+
+ if(!cert->nickname) {
+ return SECSuccess;
+ }
+
+ tempNick.data = (unsigned char *)cert->nickname;
+ tempNick.len = PORT_Strlen(cert->nickname) + 1;
+
+ /* 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_NO_MEMORY;
+ 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 */
+ if(nickArg->nNicks == 0) {
+ nickArg->nickList = (SECItem **)PORT_ArenaZAlloc(nickArg->arena,
+ 2 * sizeof(SECItem *));
+ } else {
+ nickArg->nickList = (SECItem **)PORT_ArenaGrow(nickArg->arena,
+ nickArg->nickList,
+ (nickArg->nNicks + 1) * sizeof(SECItem *),
+ (nickArg->nNicks + 2) * sizeof(SECItem *));
+ }
+ if(!nickArg->nickList) {
+ nickArg->error = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+ }
+
+ nickArg->nickList[nickArg->nNicks] =
+ (SECItem *)PORT_ArenaZAlloc(nickArg->arena, sizeof(SECItem));
+ if(!nickArg->nickList[nickArg->nNicks]) {
+ nickArg->error = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+ }
+
+
+ if(SECITEM_CopyItem(nickArg->arena, nickArg->nickList[nickArg->nNicks],
+ &tempNick) != SECSuccess) {
+ nickArg->error = SEC_ERROR_NO_MEMORY;
+ 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, void *wincx)
+{
+ struct certNickInfo *nickArg = NULL;
+ SECItem *derCert, *returnDn = NULL;
+ PRArenaPool *arena = NULL;
+ CERTCertificate *tempCert;
+
+ if(!cert) {
+ return NULL;
+ }
+
+ derCert = sec_pkcs12_get_der_cert(cert);
+ if(!derCert) {
+ return NULL;
+ }
+
+ tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert, NULL,
+ PR_FALSE, PR_TRUE);
+ if(!tempCert) {
+ returnDn = NULL;
+ goto loser;
+ }
+
+ arena = PORT_NewArena(1024);
+ if(!arena) {
+ returnDn = NULL;
+ goto loser;
+ }
+ nickArg = (struct certNickInfo *)PORT_ArenaZAlloc(arena,
+ sizeof(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_IsInternal(cert->slot)) {
+ if(CERT_TraversePermCertsForSubject(CERT_GetDefaultCertDB(),
+ &tempCert->derSubject, gatherNicknames,
+ nickArg) != SECSuccess) {
+ returnDn = NULL;
+ goto loser;
+ }
+ }
+
+ 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) {
+ return SECFailure;
+ }
+
+ (*nCerts)++;
+ return SECSuccess;
+}
+
+static PRBool
+sec_pkcs12_certs_for_nickname_exist(SECItem *nickname, PK11SlotInfo *slot)
+{
+ unsigned int nCerts = 0;
+
+ if(!nickname || !slot) {
+ return PR_TRUE;
+ }
+
+ /* we want to check the local database first if we are importing to it */
+ if(PK11_IsInternal(slot)) {
+ CERT_TraversePermCertsForNickname(CERT_GetDefaultCertDB(),
+ (char *)nickname->data,
+ countCertificate, (void *)&nCerts);
+ }
+
+ PK11_TraverseCertsForNicknameInSlot(nickname, slot, countCertificate,
+ (void *)&nCerts);
+ if(nCerts) return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+/* 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,
+ void *wincx)
+{
+ SECItem *certNickname, *existingDNNick;
+ PRBool setNickname = PR_FALSE, cancel = PR_FALSE;
+ SECItem *newNickname = NULL;
+
+ if(!cert || !cert->hasKey) {
+ return;
+ }
+
+ if(!nicknameCb) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ return;
+ }
+
+ if(cert->hasKey && !key) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ return;
+ }
+
+ certNickname = sec_pkcs12_get_nickname_for_cert(cert, key, wincx);
+ existingDNNick = sec_pkcs12_get_existing_nick_for_dn(cert, wincx);
+
+ /* 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) {
+ if(sec_pkcs12_set_nickname_for_cert(cert, key, existingDNNick, wincx)
+ != SECSuccess) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ }
+ 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) {
+ if(certNickname && certNickname->data) {
+ /* we will use the nickname so long as no other certs have the
+ * same nickname. and the nickname is not NULL.
+ */
+ if(!sec_pkcs12_certs_for_nickname_exist(certNickname, cert->slot)) {
+ if(setNickname) {
+ if(sec_pkcs12_set_nickname_for_cert(cert, key, certNickname,
+ wincx) != SECSuccess) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ }
+ }
+ goto loser;
+ }
+ }
+
+ setNickname = PR_FALSE;
+ newNickname = (*nicknameCb)(certNickname, &cancel, wincx);
+ if(cancel) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_USER_CANCELLED;
+ goto loser;
+ }
+
+ if(!newNickname) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ goto loser;
+ }
+
+ /* 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,
+ void *wincx)
+{
+ CERTCertificate *leafCert, *testCert;
+
+ if(!cert) {
+ return;
+ }
+
+ cert->validated = PR_TRUE;
+
+ if(!nicknameCb) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ cert->noInstall = PR_TRUE;
+ 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->removeExisting = PR_FALSE;
+ cert->problem = PR_FALSE;
+ cert->error = 0;
+
+ leafCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &cert->safeBagContent.certBag->value.x509Cert,
+ NULL, PR_FALSE, PR_TRUE);
+ if(!leafCert) {
+ cert->noInstall = PR_TRUE;
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ return;
+ }
+
+ testCert = PK11_FindCertFromDERCert(cert->slot, leafCert, wincx);
+ CERT_DestroyCertificate(leafCert);
+ /* if we can't find the certificate through the PKCS11 interface,
+ * we should check the cert database directly, if we are
+ * importing to an internal slot.
+ */
+ if(!testCert && PK11_IsInternal(cert->slot)) {
+ testCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(),
+ &cert->safeBagContent.certBag->value.x509Cert);
+ }
+
+ if(testCert) {
+ if(!testCert->nickname) {
+ cert->removeExisting = PR_TRUE;
+ } else {
+ cert->noInstall = PR_TRUE;
+ }
+ CERT_DestroyCertificate(testCert);
+ if(cert->noInstall && !cert->removeExisting) {
+ return;
+ }
+ }
+
+ sec_pkcs12_validate_cert_nickname(cert, key, nicknameCb, wincx);
+}
+
+static void
+sec_pkcs12_validate_key_by_cert(sec_PKCS12SafeBag *cert, sec_PKCS12SafeBag *key,
+ void *wincx)
+{
+ CERTCertificate *leafCert;
+ SECKEYPrivateKey *privk;
+
+ if(!key) {
+ 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_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &(cert->safeBagContent.certBag->value.x509Cert),
+ NULL, PR_FALSE, PR_TRUE);
+ if(!leafCert) {
+ key->problem = PR_TRUE;
+ key->noInstall = PR_TRUE;
+ key->error = SEC_ERROR_NO_MEMORY;
+ 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_remove_existing_cert(sec_PKCS12SafeBag *cert,
+ void *wincx)
+{
+ SECItem *derCert = NULL;
+ CERTCertificate *tempCert = NULL;
+ CK_OBJECT_HANDLE certObj;
+ PK11SlotInfo *slot = NULL;
+ PRBool removed = PR_FALSE;
+
+ if(!cert) {
+ return SECFailure;
+ }
+
+ PORT_Assert(cert->removeExisting);
+
+ cert->removeExisting = PR_FALSE;
+ derCert = &cert->safeBagContent.certBag->value.x509Cert;
+ tempCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert,
+ NULL, PR_FALSE, PR_TRUE);
+ if(!tempCert) {
+ return SECFailure;
+ }
+
+ certObj = PK11_FindCertInSlot(cert->slot, tempCert, wincx);
+ CERT_DestroyCertificate(tempCert);
+ tempCert = NULL;
+
+ if(certObj != CK_INVALID_KEY) {
+ PK11_DestroyObject(cert->slot, certObj);
+ removed = PR_TRUE;
+ } else if(PK11_IsInternal(cert->slot)) {
+ tempCert = CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), derCert);
+ if(tempCert) {
+ if(SEC_DeletePermCertificate(tempCert) == SECSuccess) {
+ removed = PR_TRUE;
+ }
+ CERT_DestroyCertificate(tempCert);
+ tempCert = NULL;
+ }
+ }
+
+ if(!removed) {
+ cert->problem = PR_TRUE;
+ cert->error = SEC_ERROR_NO_MEMORY;
+ cert->noInstall = PR_TRUE;
+ }
+
+ if(tempCert) {
+ CERT_DestroyCertificate(tempCert);
+ }
+
+ return ((removed) ? SECSuccess : SECFailure);
+}
+
+static SECStatus
+sec_pkcs12_add_cert(sec_PKCS12SafeBag *cert, PRBool keyExists, void *wincx)
+{
+ SECItem *derCert, *nickName;
+ char *nickData = NULL;
+ SECStatus rv;
+
+ if(!cert) {
+ return SECFailure;
+ }
+
+ if(cert->problem || cert->noInstall || cert->installed) {
+ return SECSuccess;
+ }
+
+ derCert = &cert->safeBagContent.certBag->value.x509Cert;
+ if(cert->removeExisting) {
+ if(sec_pkcs12_remove_existing_cert(cert, wincx)
+ != SECSuccess) {
+ return SECFailure;
+ }
+ cert->removeExisting = PR_FALSE;
+ }
+
+ PORT_Assert(!cert->problem && !cert->removeExisting && !cert->noInstall);
+
+ nickName = sec_pkcs12_get_nickname(cert);
+ if(nickName) {
+ nickData = (char *)nickName->data;
+ }
+
+ if(keyExists) {
+ CERTCertificate *newCert;
+
+ newCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ derCert, NULL, PR_FALSE, PR_TRUE);
+ if(!newCert) {
+ if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE);
+ cert->error = SEC_ERROR_NO_MEMORY;
+ cert->problem = PR_TRUE;
+ return SECFailure;
+ }
+
+ rv = PK11_ImportCertForKeyToSlot(cert->slot, newCert, nickData,
+ PR_TRUE, wincx);
+ CERT_DestroyCertificate(newCert);
+ } else {
+ SECItem *certList[2];
+ certList[0] = derCert;
+ certList[1] = NULL;
+ rv = CERT_ImportCerts(CERT_GetDefaultCertDB(), certUsageUserCertImport,
+ 1, certList, NULL, PR_TRUE, PR_FALSE, nickData);
+ }
+
+ cert->installed = PR_TRUE;
+ if(nickName) SECITEM_ZfreeItem(nickName, PR_TRUE);
+ return rv;
+}
+
+static SECStatus
+sec_pkcs12_add_key(sec_PKCS12SafeBag *key, SECItem *publicValue,
+ KeyType keyType, unsigned int keyUsage, void *wincx)
+{
+ SECStatus rv;
+ SECItem *nickName;
+
+ if(!key) {
+ return SECFailure;
+ }
+
+ if(key->removeExisting) {
+ key->problem = PR_TRUE;
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ return SECFailure;
+ }
+
+ if(key->problem || key->noInstall) {
+ return SECSuccess;
+ }
+
+ nickName = sec_pkcs12_get_nickname(key);
+
+ 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:
+ rv = PK11_ImportEncryptedPrivateKeyInfo(key->slot,
+ key->safeBagContent.pkcs8ShroudedKeyBag,
+ key->pwitem, nickName, publicValue,
+ PR_TRUE, PR_TRUE, keyType, keyUsage,
+ wincx);
+ break;
+ default:
+ key->error = SEC_ERROR_PKCS12_UNSUPPORTED_VERSION;
+ key->problem = PR_TRUE;
+ if(nickName) {
+ SECITEM_ZfreeItem(nickName, PR_TRUE);
+ }
+ return SECFailure;
+ }
+
+ key->installed = PR_TRUE;
+
+ if(nickName) {
+ SECITEM_ZfreeItem(nickName, PR_TRUE);
+ }
+
+ if(rv != SECSuccess) {
+ key->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ key->problem = PR_TRUE;
+ } else {
+ key->installed = PR_TRUE;
+ }
+
+ return rv;
+}
+
+static SECStatus
+sec_pkcs12_add_item_to_bag_list(sec_PKCS12SafeBag ***bagList,
+ sec_PKCS12SafeBag *bag)
+{
+ int i = 0;
+
+ if(!bagList || !bag) {
+ return SECFailure;
+ }
+
+ if(!(*bagList)) {
+ (*bagList) = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(bag->arena,
+ sizeof(sec_PKCS12SafeBag *) * 2);
+ } else {
+ while((*bagList)[i]) i++;
+ (*bagList) = (sec_PKCS12SafeBag **)PORT_ArenaGrow(bag->arena, *bagList,
+ sizeof(sec_PKCS12SafeBag *) * (i + 1),
+ sizeof(sec_PKCS12SafeBag *) * (i + 2));
+ }
+
+ if(!(*bagList)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ (*bagList)[i] = bag;
+ (*bagList)[i+1] = NULL;
+
+ 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]) {
+ return NULL;
+ }
+
+ keyId = sec_pkcs12_get_attribute_value(key, SEC_OID_PKCS9_LOCAL_KEY_ID);
+ if(!keyId) {
+ return NULL;
+ }
+
+ i = 0;
+ certList = NULL;
+ while(safeBags[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) {
+ return NULL;
+ }
+ }
+ }
+ i++;
+ }
+
+ return certList;
+}
+
+CERTCertList *
+SEC_PKCS12DecoderGetCerts(SEC_PKCS12DecoderContext *p12dcx)
+{
+ CERTCertList *certList = NULL;
+ sec_PKCS12SafeBag **safeBags = p12dcx->safeBags;
+ int i;
+
+ if (!p12dcx || !p12dcx->safeBags || !p12dcx->safeBags[0]) {
+ return NULL;
+ }
+
+ safeBags = p12dcx->safeBags;
+ i = 0;
+ certList = CERT_NewCertList();
+
+ if (certList == NULL) {
+ return NULL;
+ }
+
+ while(safeBags[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);
+ }
+ i++;
+ }
+
+ 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]) {
+ return NULL;
+ }
+
+ i = 0;
+ while(safeBags[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) {
+ return NULL;
+ }
+ break;
+ default:
+ break;
+ }
+ i++;
+ }
+
+ return keyList;
+}
+
+static SECStatus
+sec_pkcs12_validate_bags(sec_PKCS12SafeBag **safeBags,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb,
+ void *wincx)
+{
+ sec_PKCS12SafeBag **keyList;
+ int i;
+
+ if(!safeBags || !nicknameCb) {
+ return SECFailure;
+ }
+
+ if(!safeBags[0]) {
+ return SECSuccess;
+ }
+
+ keyList = sec_pkcs12_get_key_bags(safeBags);
+ if(keyList) {
+ i = 0;
+
+ while(keyList[i]) {
+ sec_PKCS12SafeBag **certList = sec_pkcs12_find_certs_for_key(
+ safeBags, keyList[i]);
+ if(certList) {
+ int j = 0;
+
+ if(SECOID_FindOIDTag(&(keyList[i]->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(keyList[i]) != SECSuccess) {
+ keyList[i]->problem = PR_TRUE;
+ keyList[i]->error = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+ }
+ }
+
+ sec_pkcs12_validate_key_by_cert(certList[0], keyList[i], wincx);
+ while(certList[j]) {
+ certList[j]->hasKey = PR_TRUE;
+ if(keyList[i]->problem) {
+ certList[j]->problem = PR_TRUE;
+ certList[j]->error = keyList[i]->error;
+ } else {
+ sec_pkcs12_validate_cert(certList[j], keyList[i],
+ nicknameCb, wincx);
+ if(certList[j]->problem) {
+ keyList[i]->problem = certList[j]->problem;
+ keyList[i]->error = certList[j]->error;
+ }
+ }
+ j++;
+ }
+ }
+
+ i++;
+ }
+ }
+
+ i = 0;
+ while(safeBags[i]) {
+ if(!safeBags[i]->validated) {
+ SECOidTag bagType = SECOID_FindOIDTag(&safeBags[i]->safeBagType);
+
+ switch(bagType) {
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ sec_pkcs12_validate_cert(safeBags[i], NULL, nicknameCb,
+ wincx);
+ break;
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ safeBags[i]->noInstall = PR_TRUE;
+ safeBags[i]->problem = PR_TRUE;
+ safeBags[i]->error = SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY;
+ break;
+ default:
+ safeBags[i]->noInstall = PR_TRUE;
+ }
+ }
+ i++;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+SEC_PKCS12DecoderValidateBags(SEC_PKCS12DecoderContext *p12dcx,
+ SEC_PKCS12NicknameCollisionCallback nicknameCb)
+{
+ SECStatus rv;
+ int i, noInstallCnt, probCnt, bagCnt, errorVal = 0;
+ if(!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ rv = sec_pkcs12_validate_bags(p12dcx->safeBags, nicknameCb, p12dcx->wincx);
+ if(rv == SECSuccess) {
+ p12dcx->bagsVerified = PR_TRUE;
+ }
+
+ noInstallCnt = probCnt = bagCnt = 0;
+ i = 0;
+ while(p12dcx->safeBags[i]) {
+ bagCnt++;
+ if(p12dcx->safeBags[i]->noInstall) noInstallCnt++;
+ if(p12dcx->safeBags[i]->problem) {
+ probCnt++;
+ errorVal = p12dcx->safeBags[i]->error;
+ }
+ i++;
+ }
+
+ if(bagCnt == noInstallCnt) {
+ PORT_SetError(SEC_ERROR_PKCS12_DUPLICATE_DATA);
+ return SECFailure;
+ }
+
+ if(probCnt) {
+ PORT_SetError(errorVal);
+ return SECFailure;
+ }
+
+ return rv;
+}
+
+static SECItem *
+sec_pkcs12_get_public_value_and_type(sec_PKCS12SafeBag *certBag,
+ KeyType *type, unsigned int *usage)
+{
+ SECKEYPublicKey *pubKey = NULL;
+ CERTCertificate *cert = NULL;
+ SECItem *pubValue;
+
+ *type = nullKey;
+ *usage = 0;
+
+ if(!certBag) {
+ return NULL;
+ }
+
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
+ &certBag->safeBagContent.certBag->value.x509Cert,
+ NULL, PR_FALSE, PR_FALSE);
+ if(!cert) {
+ return NULL;
+ }
+
+ *usage = cert->keyUsage;
+ pubKey = CERT_ExtractPublicKey(cert);
+ CERT_DestroyCertificate(cert);
+ if(!pubKey) {
+ return NULL;
+ }
+
+ *type = pubKey->keyType;
+ switch(pubKey->keyType) {
+ case dsaKey:
+ pubValue = SECITEM_DupItem(&pubKey->u.dsa.publicValue);
+ break;
+ case dhKey:
+ pubValue = SECITEM_DupItem(&pubKey->u.dh.publicValue);
+ break;
+ case rsaKey:
+ pubValue = SECITEM_DupItem(&pubKey->u.rsa.modulus);
+ break;
+ default:
+ pubValue = NULL;
+ }
+
+ SECKEY_DestroyPublicKey(pubKey);
+
+ return pubValue;
+}
+
+static SECStatus
+sec_pkcs12_install_bags(sec_PKCS12SafeBag **safeBags,
+ void *wincx)
+{
+ sec_PKCS12SafeBag **keyList, **certList;
+ int i;
+
+ if(!safeBags) {
+ return SECFailure;
+ }
+
+ if(!safeBags[0]) {
+ return SECSuccess;
+ }
+
+ keyList = sec_pkcs12_get_key_bags(safeBags);
+ if(keyList) {
+ i = 0;
+
+ while(keyList[i]) {
+ SECStatus rv;
+ SECItem *publicValue = NULL;
+ KeyType keyType;
+ unsigned int keyUsage;
+
+ if(keyList[i]->problem) {
+ goto next_key_bag;
+ }
+
+ certList = sec_pkcs12_find_certs_for_key(safeBags,
+ keyList[i]);
+ if(certList) {
+ publicValue = sec_pkcs12_get_public_value_and_type(certList[0],
+ &keyType, &keyUsage);
+ }
+ rv = sec_pkcs12_add_key(keyList[i], publicValue, keyType, keyUsage,
+ wincx);
+ if(publicValue) {
+ SECITEM_FreeItem(publicValue, PR_TRUE);
+ }
+ if(rv != SECSuccess) {
+ PORT_SetError(keyList[i]->error);
+ return SECFailure;
+ }
+
+ if(certList) {
+ int j = 0;
+
+ while(certList[j]) {
+ SECStatus certRv;
+
+ if(rv != SECSuccess) {
+ certList[j]->problem = keyList[i]->problem;
+ certList[j]->error = keyList[i]->error;
+ certList[j]->noInstall = PR_TRUE;
+ goto next_cert_bag;
+ }
+
+ certRv = sec_pkcs12_add_cert(certList[j],
+ certList[j]->hasKey, wincx);
+ if(certRv != SECSuccess) {
+ keyList[i]->problem = certList[j]->problem;
+ keyList[i]->error = certList[j]->error;
+ PORT_SetError(certList[j]->error);
+ return SECFailure;
+ }
+next_cert_bag:
+ j++;
+ }
+ }
+
+next_key_bag:
+ i++;
+ }
+ }
+
+ i = 0;
+ while(safeBags[i]) {
+ if(!safeBags[i]->installed) {
+ SECStatus rv;
+ SECOidTag bagType = SECOID_FindOIDTag(&(safeBags[i]->safeBagType));
+
+ switch(bagType) {
+ case SEC_OID_PKCS12_V1_CERT_BAG_ID:
+ rv = sec_pkcs12_add_cert(safeBags[i], safeBags[i]->hasKey,
+ wincx);
+ if(rv != SECSuccess) {
+ PORT_SetError(safeBags[i]->error);
+ return SECFailure;
+ }
+ break;
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ case SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID:
+ default:
+ break;
+ }
+ }
+ i++;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+SEC_PKCS12DecoderImportBags(SEC_PKCS12DecoderContext *p12dcx)
+{
+ if(!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ if(!p12dcx->bagsVerified) {
+ return SECFailure;
+ }
+
+ return sec_pkcs12_install_bags(p12dcx->safeBags, p12dcx->wincx);
+}
+
+static SECStatus
+sec_pkcs12_decoder_append_bag_to_context(SEC_PKCS12DecoderContext *p12dcx,
+ sec_PKCS12SafeBag *bag)
+{
+ if(!p12dcx || p12dcx->error) {
+ return SECFailure;
+ }
+
+ if(!p12dcx->safeBagCount) {
+ p12dcx->safeBags = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(sec_PKCS12SafeBag *) * 2);
+ } else {
+ p12dcx->safeBags =
+ (sec_PKCS12SafeBag **)PORT_ArenaGrow(p12dcx->arena, p12dcx->safeBags,
+ (p12dcx->safeBagCount + 1) * sizeof(sec_PKCS12SafeBag *),
+ (p12dcx->safeBagCount + 2) * sizeof(sec_PKCS12SafeBag *));
+ }
+
+ 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) {
+ return NULL;
+ }
+
+ newNickName =(SECItem *)PORT_ArenaZAlloc(p12dcx->arena, sizeof(SECItem));
+ keyBag = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(sec_PKCS12SafeBag));
+ if(!keyBag || !newNickName) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ keyBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
+ keyBag->slot = p12dcx->slot;
+ keyBag->arena = p12dcx->arena;
+ keyBag->pwitem = p12dcx->pwitem;
+ 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) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ if(SECITEM_CopyItem(p12dcx->arena, &keyBag->safeBagType, &oid->oid)
+ != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ 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)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ 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)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ 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) {
+ return NULL;
+ }
+
+ keyId = (SECItem *)PORT_ArenaZAlloc(p12dcx->arena, sizeof(SECItem));
+ if(!keyId) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ 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 = (sec_PKCS12SafeBag *)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(sec_PKCS12SafeBag));
+ if(!certBag || !oid || (SECITEM_CopyItem(p12dcx->arena,
+ &certBag->safeBagType, &oid->oid) != SECSuccess)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ certBag->slot = p12dcx->slot;
+ certBag->pwitem = p12dcx->pwitem;
+ certBag->swapUnicodeBytes = p12dcx->swapUnicodeBytes;
+ certBag->arena = p12dcx->arena;
+
+ oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_X509_CERT);
+ certBag->safeBagContent.certBag =
+ (sec_PKCS12CertBag *)PORT_ArenaZAlloc(p12dcx->arena,
+ sizeof(sec_PKCS12CertBag));
+ if(!certBag->safeBagContent.certBag || !oid ||
+ (SECITEM_CopyItem(p12dcx->arena,
+ &certBag->safeBagContent.certBag->bagID,
+ &oid->oid) != SECSuccess)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ if(SECITEM_CopyItem(p12dcx->arena,
+ &(certBag->safeBagContent.certBag->value.x509Cert),
+ derCert) != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ 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) {
+ return NULL;
+ }
+
+ derCertList = SEC_PKCS7GetCertificateList(&oldCert->value.x509->certOrCRL);
+ if(!derCertList) {
+ return NULL;
+ }
+
+ i = 0;
+ while(derCertList[i]) i++;
+
+ certList = (sec_PKCS12SafeBag **)PORT_ArenaZAlloc(p12dcx->arena,
+ (i + 1) * sizeof(sec_PKCS12SafeBag *));
+ if(!certList) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ 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) {
+ 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(PRArenaPool *arena, PK11SlotInfo *slot,
+ PRBool swapUnicode, SECItem *pwitem,
+ void *wincx, SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage)
+{
+ SEC_PKCS12DecoderContext *p12dcx;
+
+ if(!arena || !slot || !pwitem) {
+ return NULL;
+ }
+
+ if(!safe && !baggage) {
+ return NULL;
+ }
+
+ p12dcx = (SEC_PKCS12DecoderContext *)PORT_ArenaZAlloc(arena,
+ sizeof(SEC_PKCS12DecoderContext));
+ if(!p12dcx) {
+ return NULL;
+ }
+
+ p12dcx->arena = arena;
+ p12dcx->slot = slot;
+ p12dcx->wincx = wincx;
+ p12dcx->error = PR_FALSE;
+ p12dcx->swapUnicodeBytes = swapUnicode;
+ p12dcx->pwitem = pwitem;
+
+ 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 000000000..024a61dae
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12dec.c
@@ -0,0 +1,692 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#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"
+
+/* 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;
+ SGN_CopyDigestInfo(pfx->poolp, &pfx->macData.safeMac, &pfx->old_safeMac);
+ SECITEM_CopyItem(pfx->poolp, &pfx->macData.macSalt, &pfx->old_macSalt);
+ } 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 occured 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 occured.
+ */
+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);
+ safe_contents->swapUnicode = pfx->swapUnicode;
+ if(safe_contents == NULL) {
+ rv = SECFailure;
+ } else {
+ 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 000000000..8cc683667
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12e.c
@@ -0,0 +1,2254 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#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 "alghmac.h"
+#include "prcpucfg.h"
+
+/*********************************
+ * 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 {
+ PRArenaPool *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 {
+ PRArenaPool *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 {
+ PRArenaPool *arena;
+ SEC_PKCS12ExportContext *p12exp;
+ PK11SymKey *encryptionKey;
+
+ /* encoder information - this is set up based on whether
+ * password based or public key pased privacy is being used
+ */
+ SEC_ASN1EncoderContext *ecx;
+ 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 *aSafeP7Ecx;
+ SEC_ASN1EncoderContext *aSafeEcx;
+ unsigned int currentSafe;
+
+ /* hmac context */
+ void *hmacCx;
+} 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)
+{
+ PRArenaPool *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) ? 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;
+ 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 */
+ 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;
+
+ /* convert the password to unicode */
+ if(!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem,
+ PR_TRUE, PR_TRUE, PR_TRUE)) {
+ 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 = 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);
+ return safeInfo;
+
+loser:
+ 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(PRArenaPool *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(PRArenaPool *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;
+ PRBool setName = PR_TRUE;
+ 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;
+ setName = PR_FALSE;
+ 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(PRArenaPool *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(PRArenaPool *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;
+
+ if(!safeBag || !p12ctxt) {
+ 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 */
+ tempCert = CERT_NewTempCertificate(certDb,
+ &certList->certs[count], NULL,
+ PR_FALSE, PR_TRUE);
+ 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_PKCS12AddEncryptedKey
+ * 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 encrypted the private key with
+ * keyId - the keyID attribute
+ * nickName - the nickname attribute
+ */
+static SECStatus
+SEC_PKCS12AddEncryptedKey(SEC_PKCS12ExportContext *p12ctxt,
+ SECKEYEncryptedPrivateKeyInfo *epki, SEC_PKCS12SafeInfo *safe,
+ void *nestedDest, SECItem *keyId, SECItem *nickName)
+{
+ void *mark;
+ void *keyItem;
+ SECOidTag keyType;
+ SECStatus rv = SECFailure;
+ sec_PKCS12SafeBag *returnBag;
+
+ if(!p12ctxt || !safe || !epki) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ 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;
+
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* 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_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 = p12ctxt->slot;
+
+ if(!sec_pkcs12_convert_item_to_unicode(p12ctxt->arena, &uniPwitem,
+ pwitem, PR_TRUE, PR_TRUE, PR_TRUE)) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* we want to make sure to take the key out of the key slot */
+ if(PK11_IsInternal(p12ctxt->slot)) {
+ slot = PK11_GetInternalKeySlot();
+ }
+
+ epki = PK11_ExportEncryptedPrivateKeyInfo(slot, algorithm,
+ &uniPwitem, cert, 1,
+ p12ctxt->wincx);
+ if(PK11_IsInternal(p12ctxt->slot)) {
+ PK11_FreeSlot(slot);
+ }
+
+ keyItem = PORT_ArenaZAlloc(p12ctxt->arena,
+ sizeof(SECKEYEncryptedPrivateKeyInfo));
+ if(!keyItem) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if(!epki) {
+ PORT_SetError(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY);
+ return SECFailure;
+ }
+ 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_PKCS12AddCertAndEncryptedKey
+ * 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
+ */
+SECStatus
+SEC_PKCS12AddDERCertAndEncryptedKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ SECItem *derCert, void *keySafe,
+ void *keyNestedDest, SECKEYEncryptedPrivateKeyInfo *epki,
+ char *nickname)
+{
+ SECStatus rv = SECFailure;
+ SGNDigestInfo *digest = NULL;
+ void *mark = NULL;
+ CERTCertificate *cert;
+ SECItem nick = {siBuffer, NULL,0}, *nickPtr = NULL;
+
+ if(!p12ctxt || !certSafe || !keySafe || !derCert) {
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark(p12ctxt->arena);
+
+ cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), derCert,
+ NULL, PR_FALSE, PR_TRUE);
+ if(!cert) {
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+ cert->nickname = nickname;
+
+ /* generate the thumbprint of the cert to use as a keyId */
+ digest = sec_pkcs12_compute_thumbprint(&cert->derCert);
+ if(!digest) {
+ CERT_DestroyCertificate(cert);
+ return SECFailure;
+ }
+
+ /* add the certificate */
+ rv = SEC_PKCS12AddCert(p12ctxt, (SEC_PKCS12SafeInfo*)certSafe,
+ certNestedDest, cert, NULL,
+ &digest->digest, PR_FALSE);
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ if(nickname) {
+ nick.data = (unsigned char *)nickname;
+ nick.len = PORT_Strlen(nickname);
+ nickPtr = &nick;
+ } else {
+ nickPtr = NULL;
+ }
+
+ /* add the key */
+ rv = SEC_PKCS12AddEncryptedKey(p12ctxt, epki, (SEC_PKCS12SafeInfo*)keySafe,
+ keyNestedDest, &digest->digest, nickPtr );
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ SGN_DestroyDigestInfo(digest);
+
+ PORT_ArenaUnmark(p12ctxt->arena, mark);
+ return SECSuccess;
+
+loser:
+ SGN_DestroyDigestInfo(digest);
+ CERT_DestroyCertificate(cert);
+ PORT_ArenaRelease(p12ctxt->arena, mark);
+
+ return SECFailure;
+}
+
+/* SEC_PKCS12AddCertAndKey
+ * 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
+ */
+SECStatus
+SEC_PKCS12AddCertAndKey(SEC_PKCS12ExportContext *p12ctxt,
+ void *certSafe, void *certNestedDest,
+ CERTCertificate *cert, CERTCertDBHandle *certDb,
+ void *keySafe, void *keyNestedDest,
+ PRBool shroudKey, SECItem *pwitem, SECOidTag algorithm)
+{
+ 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, PR_TRUE);
+ 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;
+}
+
+/* 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
+ *********************************/
+
+/* 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.
+ */
+sec_PKCS12EncoderContext *
+sec_pkcs12_encoder_start_context(SEC_PKCS12ExportContext *p12exp)
+{
+ sec_PKCS12EncoderContext *p12enc = NULL;
+ unsigned int i, nonEmptyCnt;
+
+ 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 */
+ p12enc = (sec_PKCS12EncoderContext*)PORT_ArenaZAlloc(p12exp->arena,
+ sizeof(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) {
+ SECStatus rv;
+
+ /* 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) {
+ SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
+ goto loser;
+ }
+ rv = SEC_PKCS7AddSigningTime(p12enc->aSafeCinfo);
+ PORT_Assert(rv == SECSuccess);
+ } else {
+ p12enc->aSafeCinfo = SEC_PKCS7CreateData();
+
+ /* init password pased integrity mode */
+ if(p12exp->integrityEnabled) {
+ SECItem pwd = {siBuffer,NULL, 0}, *key;
+ SECItem *salt = sec_pkcs12_generate_salt();
+ PBEBitGenContext *pbeCtxt = NULL;
+
+ /* 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;
+ }
+ SECITEM_ZfreeItem(salt, PR_TRUE);
+
+ /* generate HMAC key */
+ if(!sec_pkcs12_convert_item_to_unicode(p12exp->arena, &pwd,
+ p12exp->integrityInfo.pwdInfo.password, PR_TRUE,
+ PR_TRUE, PR_TRUE)) {
+ goto loser;
+ }
+ pbeCtxt = PBE_CreateContext(p12exp->integrityInfo.pwdInfo.algorithm,
+ pbeBitGenIntegrityKey, &pwd,
+ &(p12enc->mac.macSalt), 160, 1);
+ if(!pbeCtxt) {
+ goto loser;
+ }
+ key = PBE_GenerateBits(pbeCtxt);
+ PBE_DestroyContext(pbeCtxt);
+ if(!key) {
+ goto loser;
+ }
+
+ /* initialize hmac */
+ p12enc->hmacCx = HMAC_Create(
+ p12exp->integrityInfo.pwdInfo.algorithm,
+ key->data, key->len);
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ if(!p12enc->hmacCx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ HMAC_Begin((HMACContext*)p12enc->hmacCx);
+ }
+ }
+
+ if(!p12enc->aSafeCinfo) {
+ goto loser;
+ }
+
+ return p12enc;
+
+loser:
+ if(p12enc) {
+ if(p12enc->aSafeCinfo) {
+ SEC_PKCS7DestroyContentInfo(p12enc->aSafeCinfo);
+ }
+
+ PORT_Free(p12enc);
+ }
+
+ return NULL;
+}
+
+/* callback wrapper to allow the ASN1 engine to call the PKCS 12
+ * output routines.
+ */
+static void
+sec_pkcs12_encoder_out(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);
+}
+
+/* callback wrapper to wrap SEC_PKCS7EncoderUpdate for ASN1 encoder
+ */
+static void
+sec_pkcs12_wrap_pkcs7_encoder_update(void *arg, const char *buf,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ SEC_PKCS7EncoderContext *ecx;
+ if(!buf || !len) {
+ return;
+ }
+
+ ecx = (SEC_PKCS7EncoderContext*)arg;
+ SEC_PKCS7EncoderUpdate(ecx, buf, len);
+}
+
+/* callback wrapper to wrap SEC_ASN1EncoderUpdate for PKCS 7 encoding
+ */
+static void
+sec_pkcs12_wrap_asn1_update_for_p7_update(void *arg, const char *buf,
+ unsigned long len)
+{
+ if(!buf && !len) return;
+
+ SEC_ASN1EncoderUpdate((SEC_ASN1EncoderContext*)arg, buf, len);
+}
+
+/* callback wrapper which updates the HMAC and passes on bytes to the
+ * appropriate output function.
+ */
+static void
+sec_pkcs12_asafe_update_hmac_and_encode_bits(void *arg, const char *buf,
+ unsigned long len, int depth,
+ SEC_ASN1EncodingPart data_kind)
+{
+ sec_PKCS12EncoderContext *p12ecx;
+
+ p12ecx = (sec_PKCS12EncoderContext*)arg;
+ HMAC_Update((HMACContext*)p12ecx->hmacCx, (unsigned char *)buf, len);
+ sec_pkcs12_wrap_pkcs7_encoder_update(p12ecx->aSafeP7Ecx, buf, len,
+ depth, data_kind);
+}
+
+/* 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)
+{
+ SECStatus rv = SECSuccess;
+ SEC_PKCS5KeyAndPassword keyPwd;
+ SEC_PKCS7EncoderContext *p7ecx;
+ SEC_PKCS7ContentInfo *cinfo;
+ SEC_ASN1EncoderContext *ecx = NULL;
+
+ void *arg = NULL;
+
+ 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:
+ arg = NULL;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ keyPwd.pwitem = &safeInfo->pwitem;
+ keyPwd.key = safeInfo->encryptionKey;
+ arg = &keyPwd;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ arg = NULL;
+ break;
+ default:
+ return SECFailure;
+
+ }
+
+ /* start the PKCS7 encoder */
+ p7ecx = SEC_PKCS7EncoderStart(cinfo,
+ sec_pkcs12_wrap_asn1_update_for_p7_update,
+ p12ecx->aSafeEcx, (PK11SymKey *)arg);
+ if(!p7ecx) {
+ goto loser;
+ }
+
+ /* encode safe contents */
+ ecx = SEC_ASN1EncoderStart(safeInfo->safe, sec_PKCS12SafeContentsTemplate,
+ sec_pkcs12_wrap_pkcs7_encoder_update, p7ecx);
+ if(!ecx) {
+ goto loser;
+ }
+ rv = SEC_ASN1EncoderUpdate(ecx, NULL, 0);
+ SEC_ASN1EncoderFinish(ecx);
+ ecx = NULL;
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+
+ /* finish up safe content info */
+ rv = SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn,
+ p12ecx->p12exp->pwfnarg);
+ }
+
+ return SECSuccess;
+
+loser:
+ if(p7ecx) {
+ SEC_PKCS7EncoderFinish(p7ecx, p12ecx->p12exp->pwfn,
+ p12ecx->p12exp->pwfnarg);
+ }
+
+ if(ecx) {
+ SEC_ASN1EncoderFinish(ecx);
+ }
+
+ return SECFailure;
+}
+
+/* finish the HMAC and encode the macData so that it can be
+ * encoded.
+ */
+static SECStatus
+sec_pkcs12_update_mac(sec_PKCS12EncoderContext *p12ecx)
+{
+ SECItem hmac = { siBuffer, NULL, 0 };
+ 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 */
+ hmac.data = (unsigned char *)PORT_ZAlloc(SHA1_LENGTH);
+ if(!hmac.data) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return SECFailure;
+ }
+
+ rv = HMAC_Finish((HMACContext*)p12ecx->hmacCx,
+ hmac.data, &hmac.len, SHA1_LENGTH);
+
+ if(rv != SECSuccess) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* create the digest info */
+ di = SGN_CreateDigestInfo(p12ecx->p12exp->integrityInfo.pwdInfo.algorithm,
+ hmac.data, hmac.len);
+ 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);
+ }
+ if(hmac.data) {
+ SECITEM_ZfreeItem(&hmac, PR_FALSE);
+ }
+ HMAC_Destroy((HMACContext*)p12ecx->hmacCx);
+ p12ecx->hmacCx = NULL;
+
+ return rv;
+}
+
+/* wraps the ASN1 encoder update for PKCS 7 encoder */
+static void
+sec_pkcs12_wrap_asn1_encoder_update(void *arg, const char *buf,
+ unsigned long len)
+{
+ SEC_ASN1EncoderContext *cx;
+
+ cx = (SEC_ASN1EncoderContext*)arg;
+ SEC_ASN1EncoderUpdate(cx, buf, len);
+}
+
+/* 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->ecx);
+ SEC_ASN1EncoderSetStreaming(p12ecx->ecx);
+ SEC_ASN1EncoderClearNotifyProc(p12ecx->ecx);
+}
+
+/* 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. Set it for streaming */
+ p12enc->ecx = SEC_ASN1EncoderStart(&p12enc->pfx, sec_PKCS12PFXItemTemplate,
+ sec_pkcs12_encoder_out,
+ &outInfo);
+ if(!p12enc->ecx) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ rv = SECFailure;
+ goto loser;
+ }
+ SEC_ASN1EncoderSetStreaming(p12enc->ecx);
+ SEC_ASN1EncoderSetNotifyProc(p12enc->ecx, sec_pkcs12_encoder_pfx_notify, p12enc);
+ rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0);
+ if(rv != SECSuccess) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* set up asafe cinfo - the output of the encoder feeds the PFX encoder */
+ p12enc->aSafeP7Ecx = SEC_PKCS7EncoderStart(p12enc->aSafeCinfo,
+ sec_pkcs12_wrap_asn1_encoder_update,
+ p12enc->ecx, NULL);
+ if(!p12enc->aSafeP7Ecx) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ /* encode asafe */
+ if(p12enc->p12exp->integrityEnabled && p12enc->p12exp->pwdIntegrity) {
+ p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
+ sec_PKCS12AuthenticatedSafeTemplate,
+ sec_pkcs12_asafe_update_hmac_and_encode_bits,
+ p12enc);
+ } else {
+ p12enc->aSafeEcx = SEC_ASN1EncoderStart(&p12enc->p12exp->authSafe,
+ sec_PKCS12AuthenticatedSafeTemplate,
+ sec_pkcs12_wrap_pkcs7_encoder_update,
+ p12enc->aSafeP7Ecx);
+ }
+ if(!p12enc->aSafeEcx) {
+ rv = SECFailure;
+ goto loser;
+ }
+ SEC_ASN1EncoderSetStreaming(p12enc->aSafeEcx);
+ SEC_ASN1EncoderSetTakeFromBuf(p12enc->aSafeEcx);
+
+ /* encode each of the safes */
+ while(p12enc->currentSafe != p12enc->p12exp->safeInfoCount) {
+ sec_pkcs12_encoder_asafe_process(p12enc);
+ p12enc->currentSafe++;
+ }
+ SEC_ASN1EncoderClearTakeFromBuf(p12enc->aSafeEcx);
+ SEC_ASN1EncoderClearStreaming(p12enc->aSafeEcx);
+ SEC_ASN1EncoderUpdate(p12enc->aSafeEcx, NULL, 0);
+ SEC_ASN1EncoderFinish(p12enc->aSafeEcx);
+
+ /* finish the encoding of the authenticated safes */
+ rv = SEC_PKCS7EncoderFinish(p12enc->aSafeP7Ecx, p12exp->pwfn,
+ p12exp->pwfnarg);
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ SEC_ASN1EncoderClearTakeFromBuf(p12enc->ecx);
+ SEC_ASN1EncoderClearStreaming(p12enc->ecx);
+
+ /* update the mac, if necessary */
+ rv = sec_pkcs12_update_mac(p12enc);
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* finish encoding the pfx */
+ rv = SEC_ASN1EncoderUpdate(p12enc->ecx, NULL, 0);
+
+ SEC_ASN1EncoderFinish(p12enc->ecx);
+
+loser:
+ 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++;
+ }
+ }
+
+ PORT_FreeArena(p12ecx->arena, PR_TRUE);
+}
+
+
+/*********************************
+ * All-in-one routines for exporting certificates
+ *********************************/
+struct inPlaceEncodeInfo {
+ PRBool error;
+ SECItem outItem;
+};
+
+static void
+sec_pkcs12_in_place_encoder_output(void *arg, const char *buf, unsigned long len)
+{
+ struct inPlaceEncodeInfo *outInfo = (struct inPlaceEncodeInfo*)arg;
+
+ if(!outInfo || !len || outInfo->error) {
+ return;
+ }
+
+ if(!outInfo->outItem.data) {
+ outInfo->outItem.data = (unsigned char*)PORT_ZAlloc(len);
+ outInfo->outItem.len = 0;
+ } else {
+ if(!PORT_Realloc(&(outInfo->outItem.data), (outInfo->outItem.len + len))) {
+ SECITEM_ZfreeItem(&(outInfo->outItem), PR_FALSE);
+ outInfo->outItem.data = NULL;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ outInfo->error = PR_TRUE;
+ return;
+ }
+ }
+
+ PORT_Memcpy(&(outInfo->outItem.data[outInfo->outItem.len]), buf, len);
+ outInfo->outItem.len += len;
+
+ return;
+}
+
+/*
+ * SEC_PKCS12ExportCertifcateAndKeyUsingPassword
+ * Exports a certificate/key pair using password-based encryption and
+ * authentication.
+ *
+ * pwfn, pwfnarg - password function and argument for the key database
+ * cert - the certificate to export
+ * certDb - certificate database
+ * pwitem - the password to use
+ * shroudKey - encrypt the key externally,
+ * keyShroudAlg - encryption algorithm for key
+ * encryptionAlg - the algorithm with which data is encrypted
+ * integrityAlg - the algorithm for integrity
+ */
+SECItem *
+SEC_PKCS12ExportCertificateAndKeyUsingPassword(
+ SECKEYGetPasswordKey pwfn, void *pwfnarg,
+ CERTCertificate *cert, PK11SlotInfo *slot,
+ CERTCertDBHandle *certDb, SECItem *pwitem,
+ PRBool shroudKey, SECOidTag shroudAlg,
+ PRBool encryptCert, SECOidTag certEncAlg,
+ SECOidTag integrityAlg, void *wincx)
+{
+ struct inPlaceEncodeInfo outInfo;
+ SEC_PKCS12ExportContext *p12ecx = NULL;
+ SEC_PKCS12SafeInfo *keySafe, *certSafe;
+ SECItem *returnItem = NULL;
+
+ if(!cert || !pwitem || !slot) {
+ return NULL;
+ }
+
+ outInfo.error = PR_FALSE;
+ outInfo.outItem.data = NULL;
+ outInfo.outItem.len = 0;
+
+ p12ecx = SEC_PKCS12CreateExportContext(pwfn, pwfnarg, slot, wincx);
+ if(!p12ecx) {
+ return NULL;
+ }
+
+ /* set up cert safe */
+ if(encryptCert) {
+ certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem, certEncAlg);
+ } else {
+ certSafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
+ }
+ if(!certSafe) {
+ goto loser;
+ }
+
+ /* set up key safe */
+ if(shroudKey) {
+ keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
+ } else {
+ keySafe = certSafe;
+ }
+ if(!keySafe) {
+ goto loser;
+ }
+
+ /* add integrity mode */
+ if(SEC_PKCS12AddPasswordIntegrity(p12ecx, pwitem, integrityAlg)
+ != SECSuccess) {
+ goto loser;
+ }
+
+ /* add cert and key pair */
+ if(SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert, certDb,
+ keySafe, NULL, shroudKey, pwitem, shroudAlg)
+ != SECSuccess) {
+ goto loser;
+ }
+
+ /* encode the puppy */
+ if(SEC_PKCS12Encode(p12ecx, sec_pkcs12_in_place_encoder_output, &outInfo)
+ != SECSuccess) {
+ goto loser;
+ }
+ if(outInfo.error) {
+ goto loser;
+ }
+
+ SEC_PKCS12DestroyExportContext(p12ecx);
+
+ returnItem = SECITEM_DupItem(&outInfo.outItem);
+ SECITEM_ZfreeItem(&outInfo.outItem, PR_FALSE);
+
+ return returnItem;
+
+loser:
+ if(outInfo.outItem.data) {
+ SECITEM_ZfreeItem(&(outInfo.outItem), PR_TRUE);
+ }
+
+ if(p12ecx) {
+ SEC_PKCS12DestroyExportContext(p12ecx);
+ }
+
+ return NULL;
+}
+
+
diff --git a/security/nss/lib/pkcs12/p12exp.c b/security/nss/lib/pkcs12/p12exp.c
new file mode 100644
index 000000000..242d98288
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12exp.c
@@ -0,0 +1,1407 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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(PRArenaPool *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)
+{
+ PRArenaPool *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;
+ PRArenaPool *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;
+ PRArenaPool *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 000000000..d6e02ad02
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12local.c
@@ -0,0 +1,1325 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#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 "alghmac.h"
+#include "p12.h"
+
+#define SALT_LENGTH 16
+
+/* 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_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_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_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_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_PointerToAnyTemplate;
+ break;
+ case SEC_OID_PKCS12_PKCS8_KEY_SHROUDING:
+ theTemplate =
+ 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;
+ PRArenaPool *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;
+ PRArenaPool *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;
+ HMACContext *cx;
+
+ 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 *)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) {
+ PORT_Free(mac);
+ return NULL;
+ }
+
+ /* compute MAC using HMAC */
+ cx = HMAC_Create(SEC_OID_SHA1, key->data, key->len);
+ if(cx != NULL) {
+ HMAC_Begin(cx);
+ HMAC_Update(cx, msg->data, msg->len);
+ res = HMAC_Finish(cx, mac->data, &mac->len, SHA1_LENGTH);
+ HMAC_Destroy(cx);
+ }
+
+
+ if(res != SECSuccess) {
+ SECITEM_ZfreeItem(mac, PR_TRUE);
+ mac = NULL;
+ }
+
+ 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;
+ PRArenaPool *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 unicode and encures that the
+ * required double 0 byte be placed at the end of the string
+ */
+PRBool
+sec_pkcs12_convert_item_to_unicode(PRArenaPool *arena, SECItem *dest,
+ SECItem *src, PRBool zeroTerm,
+ PRBool asciiConvert, PRBool toUnicode)
+{
+ PRBool success = PR_FALSE;
+ if(!src || !dest) {
+ return PR_FALSE;
+ }
+
+ dest->len = src->len * 3 + 2;
+ 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;
+ }
+
+ if((dest->data[dest->len-1] || dest->data[dest->len-2]) && zeroTerm) {
+ if(dest->len + 2 > 3 * src->len) {
+ if(arena) {
+ dest->data = (unsigned char*)PORT_ArenaGrow(arena,
+ dest->data, dest->len,
+ dest->len + 2);
+ } else {
+ dest->data = (unsigned char*)PORT_Realloc(dest->data,
+ dest->len + 2);
+ }
+
+ if(!dest->data) {
+ return PR_FALSE;
+ }
+ }
+ dest->len += 2;
+ dest->data[dest->len-1] = dest->data[dest->len-2] = 0;
+ }
+
+ return PR_TRUE;
+}
+
+/* pkcs 12 templates */
+static SEC_ChooseASN1TemplateFunc 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, offsetof(SEC_PKCS12PVKSupportingData, assocCerts),
+ 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, offsetof(SEC_PKCS12PVKSupportingData, assocCerts),
+ 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 SEC_ChooseASN1TemplateFunc sec_pkcs12_bag_chooser =
+ sec_pkcs12_choose_bag_type;
+
+static SEC_ChooseASN1TemplateFunc 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, offsetof(SEC_PKCS12PrivateKey, pkcs8data),
+ 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, offsetof(SEC_PKCS12X509CertCRL, thumbprint),
+ 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 SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_crl_chooser_old =
+ sec_pkcs12_choose_cert_crl_type_old;
+
+static SEC_ChooseASN1TemplateFunc 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, offsetof(SEC_PKCS12MacData, safeMac),
+ 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 | 0,
+ offsetof(SEC_PKCS12PFXItem, old_safeMac), 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 000000000..af56d9ceb
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12local.h
@@ -0,0 +1,87 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+
+#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(PRArenaPool *arena, SECItem *dest,
+ SECItem *src, PRBool zeroTerm,
+ PRBool asciiConvert, PRBool toUnicode);
+
+/* create functions */
+extern SEC_PKCS12PFXItem *sec_pkcs12_new_pfx(void);
+extern SEC_PKCS12SafeContents *sec_pkcs12_create_safe_contents(
+ PRArenaPool *poolp);
+extern SEC_PKCS12Baggage *sec_pkcs12_create_baggage(PRArenaPool *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(PRArenaPool *poolp);
+
+/* conversion from old to new */
+extern SEC_PKCS12DecoderContext *
+sec_PKCS12ConvertOldSafeToNew(PRArenaPool *arena, PK11SlotInfo *slot,
+ PRBool swapUnicode, SECItem *pwitem,
+ void *wincx, SEC_PKCS12SafeContents *safe,
+ SEC_PKCS12Baggage *baggage);
+
+#endif
diff --git a/security/nss/lib/pkcs12/p12plcy.c b/security/nss/lib/pkcs12/p12plcy.c
new file mode 100644
index 000000000..e9616ade0
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12plcy.c
@@ -0,0 +1,198 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+
+#include "p12plcy.h"
+#include "secoid.h"
+#include "secport.h"
+#include "secpkcs5.h" /* LOTS of PKCS5 calls below. XXX EVIL. */
+
+#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_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 */
+PRBool
+SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid)
+{
+ unsigned int keyLengthBits;
+ SECOidTag algId;
+ int i;
+
+ algId = SEC_PKCS5GetCryptoAlgorithm(algid);
+ if(algId == SEC_OID_UNKNOWN) {
+ return PR_FALSE;
+ }
+
+ keyLengthBits = (unsigned int)(SEC_PKCS5GetKeyLength(algid) * 8);
+
+ i = 0;
+ while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) {
+ if((pkcs12SuiteMaps[i].algTag == algId) &&
+ (pkcs12SuiteMaps[i].keyLengthBits == keyLengthBits)) {
+
+ return pkcs12SuiteMaps[i].allowed;
+ }
+ i++;
+ }
+
+ return PR_FALSE;
+}
+
+/* is any encryption allowed? */
+PRBool
+SEC_PKCS12IsEncryptionAllowed(void)
+{
+ int i;
+
+ i = 0;
+ while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) {
+ if(pkcs12SuiteMaps[i].allowed == PR_TRUE) {
+ return PR_TRUE;
+ }
+ i++;
+ }
+
+ return PR_FALSE;
+}
+
+/* get the preferred algorithm.
+ */
+SECOidTag
+SEC_PKCS12GetPreferredEncryptionAlgorithm(void)
+{
+ int i;
+
+ i = 0;
+ while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) {
+ if((pkcs12SuiteMaps[i].preferred == PR_TRUE) &&
+ (pkcs12SuiteMaps[i].allowed == PR_TRUE)) {
+ return SEC_PKCS5GetPBEAlgorithm(pkcs12SuiteMaps[i].algTag,
+ pkcs12SuiteMaps[i].keyLengthBits);
+ }
+ i++;
+ }
+
+ return SEC_OID_UNKNOWN;
+}
+
+/* return the strongest algorithm allowed */
+SECOidTag
+SEC_PKCS12GetStrongestAllowedAlgorithm(void)
+{
+ int i, keyLengthBits = 0;
+ SECOidTag algorithm = SEC_OID_UNKNOWN;
+
+ i = 0;
+ while(pkcs12SuiteMaps[i].algTag != SEC_OID_UNKNOWN) {
+ if((pkcs12SuiteMaps[i].allowed == PR_TRUE) &&
+ (pkcs12SuiteMaps[i].keyLengthBits > (unsigned int)keyLengthBits) &&
+ (pkcs12SuiteMaps[i].algTag != SEC_OID_RC4)) {
+ algorithm = pkcs12SuiteMaps[i].algTag;
+ keyLengthBits = pkcs12SuiteMaps[i].keyLengthBits;
+ }
+ i++;
+ }
+
+ if(algorithm == SEC_OID_UNKNOWN) {
+ return SEC_OID_UNKNOWN;
+ }
+
+ return SEC_PKCS5GetPBEAlgorithm(algorithm, keyLengthBits);
+}
+
+SECStatus
+SEC_PKCS12EnableCipher(long which, int on)
+{
+ int i;
+
+ i = 0;
+ while(pkcs12SuiteMaps[i].suite != 0L) {
+ if(pkcs12SuiteMaps[i].suite == (unsigned long)which) {
+ if(on) {
+ pkcs12SuiteMaps[i].allowed = PR_TRUE;
+ } else {
+ pkcs12SuiteMaps[i].allowed = PR_FALSE;
+ }
+ return SECSuccess;
+ }
+ i++;
+ }
+
+ return SECFailure;
+}
+
+SECStatus
+SEC_PKCS12SetPreferredCipher(long which, int on)
+{
+ int i;
+ PRBool turnedOff = PR_FALSE;
+ PRBool turnedOn = PR_FALSE;
+
+ i = 0;
+ while(pkcs12SuiteMaps[i].suite != 0L) {
+ if(pkcs12SuiteMaps[i].preferred == PR_TRUE) {
+ pkcs12SuiteMaps[i].preferred = PR_FALSE;
+ turnedOff = PR_TRUE;
+ }
+ if(pkcs12SuiteMaps[i].suite == (unsigned long)which) {
+ pkcs12SuiteMaps[i].preferred = PR_TRUE;
+ turnedOn = PR_TRUE;
+ }
+ i++;
+ }
+
+ if((turnedOn) && (turnedOff)) {
+ return SECSuccess;
+ }
+
+ return SECFailure;
+}
+
diff --git a/security/nss/lib/pkcs12/p12plcy.h b/security/nss/lib/pkcs12/p12plcy.h
new file mode 100644
index 000000000..1a3a9ee2f
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12plcy.h
@@ -0,0 +1,57 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+#ifndef _P12PLCY_H_
+#define _P12PLCY_H_
+
+#include "secoid.h"
+#include "ciferfam.h"
+
+/* for the algid specified, can we decrypt it ? */
+extern PRBool SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid);
+
+/* is encryption allowed? */
+extern PRBool SEC_PKCS12IsEncryptionAllowed(void);
+
+/* get the preferred encryption algorithm */
+extern SECOidTag SEC_PKCS12GetPreferredEncryptionAlgorithm(void);
+
+/* get the stronget crypto allowed (based on order in the table */
+extern SECOidTag SEC_PKCS12GetStrongestAllowedAlgorithm(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);
+
+#endif
diff --git a/security/nss/lib/pkcs12/p12t.h b/security/nss/lib/pkcs12/p12t.h
new file mode 100644
index 000000000..6b9d3da1b
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12t.h
@@ -0,0 +1,182 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef _P12T_H_
+#define _P12T_H_
+
+#include "secoid.h"
+#include "key.h"
+#include "pkcs11.h"
+#include "secpkcs7.h"
+#include "secdig.h" /* for SGNDigestInfo */
+
+#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;
+ } safeBagContent;
+
+ sec_PKCS12Attribute **attribs;
+
+ /* used locally */
+ SECOidData *bagTypeTag;
+ PRArenaPool *arena;
+ unsigned int nAttribs;
+
+ /* used for validation/importing */
+ PRBool problem, noInstall, validated, hasKey, removeExisting, installed;
+ int error;
+
+ PRBool swapUnicodeBytes;
+ PK11SlotInfo *slot;
+ SECItem *pwitem;
+ PRBool oldBagType;
+};
+
+struct sec_PKCS12SafeContentsStr {
+ sec_PKCS12SafeBag **safeBags;
+ SECItem **encodedSafeBags;
+
+ /* used locally */
+ PRArenaPool *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 000000000..ebaed1183
--- /dev/null
+++ b/security/nss/lib/pkcs12/p12tmpl.c
@@ -0,0 +1,315 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#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"
+
+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_AnyTemplate;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_AnyTemplate;
+ break;
+ case SEC_OID_PKCS12_V1_KEY_BAG_ID:
+ theTemplate = 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 = SECKEY_PointerToEncryptedPrivateKeyInfoTemplate;
+ break;
+ case SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID:
+ if(encoding) {
+ theTemplate = sec_PKCS12PointerToSafeContentsTemplate;
+ } else {
+ theTemplate = 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_AnyTemplate;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_AnyTemplate;
+ break;
+ case SEC_OID_PKCS9_X509_CRL:
+ theTemplate = 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_AnyTemplate;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_AnyTemplate;
+ break;
+ case SEC_OID_PKCS9_X509_CERT:
+ theTemplate = SEC_OctetStringTemplate;
+ break;
+ case SEC_OID_PKCS9_SDSI_CERT:
+ theTemplate = 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_AnyTemplate;
+ }
+
+ switch (oiddata->offset) {
+ default:
+ theTemplate = SEC_AnyTemplate;
+ break;
+ case SEC_OID_PKCS9_FRIENDLY_NAME:
+ theTemplate = SEC_BMPStringTemplate;
+ break;
+ case SEC_OID_PKCS9_LOCAL_KEY_ID:
+ theTemplate = SEC_OctetStringTemplate;
+ break;
+ case SEC_OID_PKCS12_KEY_USAGE:
+ theTemplate = SEC_BitStringTemplate;
+ break;
+ }
+
+ return theTemplate;
+}
+
+
+const SEC_ASN1Template sec_PKCS12PointerToContentInfoTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_MAY_STREAM, 0, sec_PKCS7ContentInfoTemplate }
+};
+
+static SEC_ChooseASN1TemplateFunc sec_pkcs12_crl_bag_chooser =
+ sec_pkcs12_choose_crl_bag_type;
+
+static SEC_ChooseASN1TemplateFunc sec_pkcs12_cert_bag_chooser =
+ sec_pkcs12_choose_cert_bag_type;
+
+static SEC_ChooseASN1TemplateFunc sec_pkcs12_safe_bag_chooser =
+ sec_pkcs12_choose_safe_bag_type;
+
+static SEC_ChooseASN1TemplateFunc 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, offsetof(sec_PKCS12MacData, safeMac),
+ 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,
+ offsetof(sec_PKCS12AuthenticatedSafe, encodedSafes), 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, 0,
+ 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,
+ offsetof(sec_PKCS12SafeContents, encodedSafeBags),
+ 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.h b/security/nss/lib/pkcs12/pkcs12.h
new file mode 100644
index 000000000..dd9d99594
--- /dev/null
+++ b/security/nss/lib/pkcs12/pkcs12.h
@@ -0,0 +1,67 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+
+#ifndef _PKCS12_H_
+#define _PKCS12_H_
+
+#include "pkcs12t.h"
+#include "p12.h"
+
+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);
+
+#endif
diff --git a/security/nss/lib/pkcs12/pkcs12t.h b/security/nss/lib/pkcs12/pkcs12t.h
new file mode 100644
index 000000000..53c36c3f7
--- /dev/null
+++ b/security/nss/lib/pkcs12/pkcs12t.h
@@ -0,0 +1,386 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef _PKCS12T_H_
+#define _PKCS12T_H_
+
+#include "seccomon.h"
+#include "secoid.h"
+#include "cert.h"
+#include "key.h"
+#include "plarena.h"
+#include "secpkcs7.h"
+#include "secdig.h" /* for SGNDigestInfo */
+
+/* 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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *poolp;
+ SEC_PKCS12PrivateKey **privateKeys;
+
+ int bag_size; /* used locally */
+};
+
+/* container to hold certificates. currently supports x509
+ * and sdsi certificates
+ */
+struct SEC_PKCS12CertAndCRLStr
+{
+ PRArenaPool *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
+{
+ PRArenaPool *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
+{
+ PRArenaPool *poolp;
+ SECItem value;
+ SGNDigestInfo thumbprint;
+};
+
+/* contains a null terminated list of certs and crls */
+struct SEC_PKCS12CertAndCRLBagStr
+{
+ PRArenaPool *poolp;
+ SEC_PKCS12CertAndCRL **certAndCRLs;
+
+ int bag_size; /* used locally */
+};
+
+/* additional secret information. currently no information
+ * stored in this structure.
+ */
+struct SEC_PKCS12SecretAdditionalStr
+{
+ PRArenaPool *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
+{
+ PRArenaPool *poolp;
+ SECItem secretName;
+ SECItem value;
+ SEC_PKCS12SecretAdditional secretAdditional;
+
+ SECItem uniSecretName;
+};
+
+struct SEC_PKCS12SecretItemStr
+{
+ PRArenaPool *poolp;
+ SEC_PKCS12Secret secret;
+ SEC_PKCS12SafeBag subFolder;
+};
+
+/* a bag of secrets. holds a null terminated list of secrets.
+ */
+struct SEC_PKCS12SecretBagStr
+{
+ PRArenaPool *poolp;
+ SEC_PKCS12SecretItem **secrets;
+
+ int bag_size; /* used locally */
+};
+
+struct SEC_PKCS12MacDataStr
+{
+ SGNDigestInfo safeMac;
+ SECItem macSalt;
+};
+
+/* outer transfer unit */
+struct SEC_PKCS12PFXItemStr
+{
+ PRArenaPool *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 {
+ PRArenaPool *poolp;
+ SEC_PKCS12ESPVKItem **espvks;
+ SEC_PKCS12SafeBag **unencSecrets;
+
+ int nEspvks;
+ int nSecrets;
+};
+
+/* stores shrouded keys */
+struct SEC_PKCS12Baggage_OLDStr
+{
+ PRArenaPool *poolp;
+ SEC_PKCS12ESPVKItem **espvks;
+
+ int luggage_size; /* used locally */
+};
+
+/* authenticated safe, stores certs, keys, and shrouded keys */
+struct SEC_PKCS12AuthenticatedSafeStr
+{
+ PRArenaPool *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
diff --git a/security/nss/lib/pkcs7/Makefile b/security/nss/lib/pkcs7/Makefile
new file mode 100644
index 000000000..cb85677bc
--- /dev/null
+++ b/security/nss/lib/pkcs7/Makefile
@@ -0,0 +1,76 @@
+#! gmake
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
+
diff --git a/security/nss/lib/pkcs7/config.mk b/security/nss/lib/pkcs7/config.mk
new file mode 100644
index 000000000..a73a1086e
--- /dev/null
+++ b/security/nss/lib/pkcs7/config.mk
@@ -0,0 +1,44 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+#
+# Override TARGETS variable so that only static libraries
+# are specifed as dependencies within rules.mk.
+#
+
+TARGETS = $(LIBRARY)
+SHARED_LIBRARY =
+IMPORT_LIBRARY =
+PURE_LIBRARY =
+PROGRAM =
+
diff --git a/security/nss/lib/pkcs7/manifest.mn b/security/nss/lib/pkcs7/manifest.mn
new file mode 100644
index 000000000..b2b0e45d9
--- /dev/null
+++ b/security/nss/lib/pkcs7/manifest.mn
@@ -0,0 +1,59 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+
+CORE_DEPTH = ../../..
+
+EXPORTS = \
+ secmime.h \
+ secpkcs7.h \
+ pkcs7t.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ p7local.h \
+ $(NULL)
+
+MODULE = security
+
+CSRCS = \
+ p7common.c \
+ p7create.c \
+ p7decode.c \
+ p7encode.c \
+ p7local.c \
+ secmime.c \
+ $(NULL)
+
+REQUIRES = security dbm
+
+LIBRARY_NAME = pkcs7
diff --git a/security/nss/lib/pkcs7/p7common.c b/security/nss/lib/pkcs7/p7common.c
new file mode 100644
index 000000000..e11a7f586
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7common.c
@@ -0,0 +1,738 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * PKCS7 implementation -- the exported parts that are used whether
+ * creating or decoding.
+ *
+ * $Id$
+ */
+
+#include "p7local.h"
+
+#include "cert.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "secpkcs5.h"
+#include "pk11func.h"
+
+/*
+ * Find out (saving pointer to lookup result for future reference)
+ * and return the inner content type.
+ */
+SECOidTag
+SEC_PKCS7ContentType (SEC_PKCS7ContentInfo *cinfo)
+{
+ if (cinfo->contentTypeTag == NULL)
+ cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
+
+ if (cinfo->contentTypeTag == NULL)
+ return SEC_OID_UNKNOWN;
+
+ return cinfo->contentTypeTag->offset;
+}
+
+
+/*
+ * Destroy a PKCS7 contentInfo and all of its sub-pieces.
+ */
+void
+SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+ CERTCertificate **certs;
+ CERTCertificateList **certlists;
+ SEC_PKCS7SignerInfo **signerinfos;
+ SEC_PKCS7RecipientInfo **recipientinfos;
+
+ PORT_Assert (cinfo->refCount > 0);
+ if (cinfo->refCount <= 0)
+ return;
+
+ cinfo->refCount--;
+ if (cinfo->refCount > 0)
+ return;
+
+ certs = NULL;
+ certlists = NULL;
+ recipientinfos = NULL;
+ signerinfos = NULL;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ {
+ SEC_PKCS7EnvelopedData *edp;
+
+ edp = cinfo->content.envelopedData;
+ if (edp != NULL) {
+ recipientinfos = edp->recipientInfos;
+ }
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ if (sdp != NULL) {
+ certs = sdp->certs;
+ certlists = sdp->certLists;
+ signerinfos = sdp->signerInfos;
+ }
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ if (saedp != NULL) {
+ certs = saedp->certs;
+ certlists = saedp->certLists;
+ recipientinfos = saedp->recipientInfos;
+ signerinfos = saedp->signerInfos;
+ if (saedp->sigKey != NULL)
+ PK11_FreeSymKey (saedp->sigKey);
+ }
+ }
+ break;
+ default:
+ /* XXX Anything else that needs to be "manually" freed/destroyed? */
+ break;
+ }
+
+ if (certs != NULL) {
+ CERTCertificate *cert;
+
+ while ((cert = *certs++) != NULL) {
+ CERT_DestroyCertificate (cert);
+ }
+ }
+
+ if (certlists != NULL) {
+ CERTCertificateList *certlist;
+
+ while ((certlist = *certlists++) != NULL) {
+ CERT_DestroyCertificateList (certlist);
+ }
+ }
+
+ if (recipientinfos != NULL) {
+ SEC_PKCS7RecipientInfo *ri;
+
+ while ((ri = *recipientinfos++) != NULL) {
+ if (ri->cert != NULL)
+ CERT_DestroyCertificate (ri->cert);
+ }
+ }
+
+ if (signerinfos != NULL) {
+ SEC_PKCS7SignerInfo *si;
+
+ while ((si = *signerinfos++) != NULL) {
+ if (si->cert != NULL)
+ CERT_DestroyCertificate (si->cert);
+ if (si->certList != NULL)
+ CERT_DestroyCertificateList (si->certList);
+ }
+ }
+
+ if (cinfo->poolp != NULL) {
+ PORT_FreeArena (cinfo->poolp, PR_FALSE); /* XXX clear it? */
+ }
+}
+
+
+/*
+ * Return a copy of the given contentInfo. The copy may be virtual
+ * or may be real -- either way, the result needs to be passed to
+ * SEC_PKCS7DestroyContentInfo later (as does the original).
+ */
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *cinfo)
+{
+ if (cinfo == NULL)
+ return NULL;
+
+ PORT_Assert (cinfo->refCount > 0);
+
+ if (cinfo->created) {
+ /*
+ * Want to do a real copy of these; otherwise subsequent
+ * changes made to either copy are likely to be a surprise.
+ * XXX I suspect that this will not actually be called for yet,
+ * which is why the assert, so to notice if it is...
+ */
+ PORT_Assert (0);
+ /*
+ * XXX Create a new pool here, and copy everything from
+ * within. For cert stuff, need to call the appropriate
+ * copy functions, etc.
+ */
+ }
+
+ cinfo->refCount++;
+ return cinfo;
+}
+
+
+/*
+ * Return a pointer to the actual content. In the case of those types
+ * which are encrypted, this returns the *plain* content.
+ * XXX Needs revisiting if/when we handle nested encrypted types.
+ */
+SECItem *
+SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_DATA:
+ return cinfo->content.data;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ {
+ SEC_PKCS7DigestedData *digd;
+
+ digd = cinfo->content.digestedData;
+ if (digd == NULL)
+ break;
+ return SEC_PKCS7GetContent (&(digd->contentInfo));
+ }
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ {
+ SEC_PKCS7EncryptedData *encd;
+
+ encd = cinfo->content.encryptedData;
+ if (encd == NULL)
+ break;
+ return &(encd->encContentInfo.plainContent);
+ }
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ {
+ SEC_PKCS7EnvelopedData *envd;
+
+ envd = cinfo->content.envelopedData;
+ if (envd == NULL)
+ break;
+ return &(envd->encContentInfo.plainContent);
+ }
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sigd;
+
+ sigd = cinfo->content.signedData;
+ if (sigd == NULL)
+ break;
+ return SEC_PKCS7GetContent (&(sigd->contentInfo));
+ }
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saed;
+
+ saed = cinfo->content.signedAndEnvelopedData;
+ if (saed == NULL)
+ break;
+ return &(saed->encContentInfo.plainContent);
+ }
+ default:
+ PORT_Assert(0);
+ break;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * XXX Fix the placement and formatting of the
+ * following routines (i.e. make them consistent with the rest of
+ * the pkcs7 code -- I think some/many belong in other files and
+ * they all need a formatting/style rehaul)
+ */
+
+/* retrieve the algorithm identifier for encrypted data.
+ * the identifier returned is a copy of the algorithm identifier
+ * in the content info and needs to be freed after being used.
+ *
+ * cinfo is the content info for which to retrieve the
+ * encryption algorithm.
+ *
+ * if the content info is not encrypted data or an error
+ * occurs NULL is returned.
+ */
+SECAlgorithmID *
+SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECAlgorithmID *alg = 0;
+ switch (SEC_PKCS7ContentType(cinfo))
+ {
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ alg = &cinfo->content.encryptedData->encContentInfo.contentEncAlg;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ alg = &cinfo->content.envelopedData->encContentInfo.contentEncAlg;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ alg = &cinfo->content.signedAndEnvelopedData
+ ->encContentInfo.contentEncAlg;
+ break;
+ default:
+ alg = 0;
+ break;
+ }
+
+ return alg;
+}
+
+/* set the content of the content info. For data content infos,
+ * the data is set. For encrytped content infos, the plainContent
+ * is set, and is expected to be encrypted later.
+ *
+ * cinfo is the content info where the data will be set
+ *
+ * buf is a buffer of the data to set
+ *
+ * len is the length of the data being set.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates the content was successfully set.
+ */
+SECStatus
+SEC_PKCS7SetContent(SEC_PKCS7ContentInfo *cinfo,
+ const char *buf,
+ unsigned long len)
+{
+ SECOidTag cinfo_type;
+ SECStatus rv;
+ SECItem content;
+ SECOidData *contentTypeTag = NULL;
+
+ content.data = (unsigned char *)buf;
+ content.len = len;
+
+ cinfo_type = SEC_PKCS7ContentType(cinfo);
+
+ /* set inner content */
+ switch(cinfo_type)
+ {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ if(content.len > 0) {
+ /* we "leak" the old content here, but as it's all in the pool */
+ /* it does not really matter */
+
+ /* create content item if necessary */
+ if (cinfo->content.signedData->contentInfo.content.data == NULL)
+ cinfo->content.signedData->contentInfo.content.data = SECITEM_AllocItem(cinfo->poolp, NULL, 0);
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ cinfo->content.signedData->contentInfo.content.data,
+ &content);
+ } else {
+ cinfo->content.signedData->contentInfo.content.data->data = NULL;
+ cinfo->content.signedData->contentInfo.content.data->len = 0;
+ rv = SECSuccess;
+ }
+ if(rv == SECFailure)
+ goto loser;
+
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ /* XXX this forces the inner content type to be "data" */
+ /* do we really want to override without asking or reason? */
+ contentTypeTag = SECOID_FindOIDByTag(SEC_OID_PKCS7_DATA);
+ if(contentTypeTag == NULL)
+ goto loser;
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ &(cinfo->content.encryptedData->encContentInfo.contentType),
+ &(contentTypeTag->oid));
+ if(rv == SECFailure)
+ goto loser;
+ if(content.len > 0) {
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ &(cinfo->content.encryptedData->encContentInfo.plainContent),
+ &content);
+ } else {
+ cinfo->content.encryptedData->encContentInfo.plainContent.data = NULL;
+ cinfo->content.encryptedData->encContentInfo.encContent.data = NULL;
+ cinfo->content.encryptedData->encContentInfo.plainContent.len = 0;
+ cinfo->content.encryptedData->encContentInfo.encContent.len = 0;
+ rv = SECSuccess;
+ }
+ if(rv == SECFailure)
+ goto loser;
+ break;
+ case SEC_OID_PKCS7_DATA:
+ cinfo->content.data = (SECItem *)PORT_ArenaZAlloc(cinfo->poolp,
+ sizeof(SECItem));
+ if(cinfo->content.data == NULL)
+ goto loser;
+ if(content.len > 0) {
+ rv = SECITEM_CopyItem(cinfo->poolp,
+ cinfo->content.data, &content);
+ } else {
+ /* handle case with NULL content */
+ rv = SECSuccess;
+ }
+ if(rv == SECFailure)
+ goto loser;
+ break;
+ default:
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+
+ return SECFailure;
+}
+
+/* the content of an encrypted data content info is encrypted.
+ * it is assumed that for encrypted data, that the data has already
+ * been set and is in the "plainContent" field of the content info.
+ *
+ * cinfo is the content info to encrypt
+ *
+ * key is the key with which to perform the encryption. if the
+ * algorithm is a password based encryption algorithm, the
+ * key is actually a password which will be processed per
+ * PKCS #5.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates a success.
+ */
+SECStatus
+SEC_PKCS7EncryptContents(PRArenaPool *poolp,
+ SEC_PKCS7ContentInfo *cinfo,
+ SECItem *key,
+ void *wincx)
+{
+ SECAlgorithmID *algid = NULL;
+ SECItem * result = NULL;
+ SECItem * src;
+ SECItem * dest;
+ SECItem * blocked_data = NULL;
+ void * mark;
+ void * cx;
+ PK11SymKey * eKey = NULL;
+ PK11SlotInfo * slot = NULL;
+
+ CK_MECHANISM pbeMech;
+ CK_MECHANISM cryptoMech;
+ int bs;
+ SECOidTag algtag;
+ SECStatus rv = SECFailure;
+ SECItem c_param;
+
+ if((cinfo == NULL) || (key == NULL))
+ return SECFailure;
+
+ if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
+ return SECFailure;
+
+ algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);
+ if(algid == NULL)
+ return SECFailure;
+
+ if(poolp == NULL)
+ poolp = cinfo->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ src = &cinfo->content.encryptedData->encContentInfo.plainContent;
+ dest = &cinfo->content.encryptedData->encContentInfo.encContent;
+ algtag = SECOID_GetAlgorithmTag(algid);
+ c_param.data = NULL;
+ dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64));
+ dest->len = (src->len + 64);
+ if(dest->data == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ slot = PK11_GetInternalKeySlot();
+ if(slot == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
+ if(eKey == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
+ result = PK11_ParamFromAlgid(algid);
+ pbeMech.pParameter = result->data;
+ pbeMech.ulParameterLen = result->len;
+ if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, key,
+ PR_FALSE) != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ c_param.data = (unsigned char *)cryptoMech.pParameter;
+ c_param.len = cryptoMech.ulParameterLen;
+
+ /* block according to PKCS 8 */
+ bs = PK11_GetBlockSize(cryptoMech.mechanism, &c_param);
+ rv = SECSuccess;
+ if(bs) {
+ char pad_char;
+ pad_char = (char)(bs - (src->len % bs));
+ if(src->len % bs) {
+ rv = SECSuccess;
+ blocked_data = PK11_BlockData(src, bs);
+ if(blocked_data) {
+ PORT_Memset((blocked_data->data + blocked_data->len - (int)pad_char),
+ pad_char, (int)pad_char);
+ } else {
+ rv = SECFailure;
+ goto loser;
+ }
+ } else {
+ blocked_data = SECITEM_DupItem(src);
+ if(blocked_data) {
+ blocked_data->data = (unsigned char*)PORT_Realloc(
+ blocked_data->data,
+ blocked_data->len + bs);
+ if(blocked_data->data) {
+ blocked_data->len += bs;
+ PORT_Memset((blocked_data->data + src->len), (char)bs, bs);
+ } else {
+ rv = SECFailure;
+ goto loser;
+ }
+ } else {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+ } else {
+ blocked_data = SECITEM_DupItem(src);
+ if(!blocked_data) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+ cx = PK11_CreateContextBySymKey(cryptoMech.mechanism, CKA_ENCRYPT,
+ eKey, &c_param);
+ if(cx == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len),
+ (int)(src->len + 64), blocked_data->data,
+ (int)blocked_data->len);
+ PK11_DestroyContext((PK11Context*)cx, PR_TRUE);
+
+loser:
+ /* let success fall through */
+ if(blocked_data != NULL)
+ SECITEM_ZfreeItem(blocked_data, PR_TRUE);
+
+ if(result != NULL)
+ SECITEM_ZfreeItem(result, PR_TRUE);
+
+ if(rv == SECFailure)
+ PORT_ArenaRelease(poolp, mark);
+ else
+ PORT_ArenaUnmark(poolp, mark);
+
+ if(eKey != NULL)
+ PK11_FreeSymKey(eKey);
+
+ if(slot != NULL)
+ PK11_FreeSlot(slot);
+
+ if(c_param.data != NULL)
+ SECITEM_ZfreeItem(&c_param, PR_FALSE);
+
+ return rv;
+}
+
+/* the content of an encrypted data content info is decrypted.
+ * it is assumed that for encrypted data, that the data has already
+ * been set and is in the "encContent" field of the content info.
+ *
+ * cinfo is the content info to decrypt
+ *
+ * key is the key with which to perform the decryption. if the
+ * algorithm is a password based encryption algorithm, the
+ * key is actually a password which will be processed per
+ * PKCS #5.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates a success.
+ */
+SECStatus
+SEC_PKCS7DecryptContents(PRArenaPool *poolp,
+ SEC_PKCS7ContentInfo *cinfo,
+ SECItem *key,
+ void *wincx)
+{
+ SECAlgorithmID *algid = NULL;
+ SECOidTag algtag;
+ SECStatus rv = SECFailure;
+ SECItem *result = NULL, *dest, *src;
+ void *mark;
+
+ PK11SymKey *eKey = NULL;
+ PK11SlotInfo *slot = NULL;
+ CK_MECHANISM pbeMech, cryptoMech;
+ void *cx;
+ SECItem c_param;
+ int bs;
+
+ if((cinfo == NULL) || (key == NULL))
+ return SECFailure;
+
+ if(SEC_PKCS7ContentType(cinfo) != SEC_OID_PKCS7_ENCRYPTED_DATA)
+ return SECFailure;
+
+ algid = SEC_PKCS7GetEncryptionAlgorithm(cinfo);
+ if(algid == NULL)
+ return SECFailure;
+
+ if(poolp == NULL)
+ poolp = cinfo->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ src = &cinfo->content.encryptedData->encContentInfo.encContent;
+ dest = &cinfo->content.encryptedData->encContentInfo.plainContent;
+ algtag = SECOID_GetAlgorithmTag(algid);
+ c_param.data = NULL;
+ dest->data = (unsigned char*)PORT_ArenaZAlloc(poolp, (src->len + 64));
+ dest->len = (src->len + 64);
+ if(dest->data == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ slot = PK11_GetInternalKeySlot();
+ if(slot == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ eKey = PK11_PBEKeyGen(slot, algid, key, PR_FALSE, wincx);
+ if(eKey == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
+ result = PK11_ParamFromAlgid(algid);
+ pbeMech.pParameter = result->data;
+ pbeMech.ulParameterLen = result->len;
+ if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, key,
+ PR_FALSE) != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ c_param.data = (unsigned char *)cryptoMech.pParameter;
+ c_param.len = cryptoMech.ulParameterLen;
+
+ cx = PK11_CreateContextBySymKey(cryptoMech.mechanism, CKA_DECRYPT,
+ eKey, &c_param);
+ if(cx == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = PK11_CipherOp((PK11Context*)cx, dest->data, (int *)(&dest->len),
+ (int)(src->len + 64), src->data, (int)src->len);
+ PK11_DestroyContext((PK11Context *)cx, PR_TRUE);
+
+ bs = PK11_GetBlockSize(cryptoMech.mechanism, &c_param);
+ if(bs) {
+ /* check for proper badding in block algorithms. this assumes
+ * RC2 cbc or a DES cbc variant. and the padding is thus defined
+ */
+ if(((int)dest->data[dest->len-1] <= bs) &&
+ ((int)dest->data[dest->len-1] > 0)) {
+ dest->len -= (int)dest->data[dest->len-1];
+ } else {
+ rv = SECFailure;
+ /* set an error ? */
+ }
+ }
+
+loser:
+ /* let success fall through */
+ if(result != NULL)
+ SECITEM_ZfreeItem(result, PR_TRUE);
+
+ if(rv == SECFailure)
+ PORT_ArenaRelease(poolp, mark);
+ else
+ PORT_ArenaUnmark(poolp, mark);
+
+ if(eKey != NULL)
+ PK11_FreeSymKey(eKey);
+
+ if(slot != NULL)
+ PK11_FreeSlot(slot);
+
+ if(c_param.data != NULL)
+ SECITEM_ZfreeItem(&c_param, PR_FALSE);
+
+ return rv;
+}
+
+SECItem **
+SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo)
+{
+ switch(SEC_PKCS7ContentType(cinfo))
+ {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ return cinfo->content.signedData->rawCerts;
+ break;
+ default:
+ return NULL;
+ break;
+ }
+}
+
+
+int
+SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo)
+{
+ if (cinfo->contentTypeTag->offset == SEC_OID_PKCS7_ENVELOPED_DATA)
+ return cinfo->content.envelopedData->encContentInfo.keysize;
+ else
+ return 0;
+}
+
diff --git a/security/nss/lib/pkcs7/p7create.c b/security/nss/lib/pkcs7/p7create.c
new file mode 100644
index 000000000..665d8495c
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7create.c
@@ -0,0 +1,1320 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * PKCS7 creation.
+ *
+ * $Id$
+ */
+
+#include "p7local.h"
+
+#include "cert.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "secpkcs5.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+
+static SECStatus
+sec_pkcs7_init_content_info (SEC_PKCS7ContentInfo *cinfo, PRArenaPool *poolp,
+ SECOidTag kind, PRBool detached)
+{
+ void *thing;
+ int version;
+ SECItem *versionp;
+ SECStatus rv;
+
+ PORT_Assert (cinfo != NULL && poolp != NULL);
+ if (cinfo == NULL || poolp == NULL)
+ return SECFailure;
+
+ cinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
+ PORT_Assert (cinfo->contentTypeTag
+ && cinfo->contentTypeTag->offset == kind);
+
+ rv = SECITEM_CopyItem (poolp, &(cinfo->contentType),
+ &(cinfo->contentTypeTag->oid));
+ if (rv != SECSuccess)
+ return rv;
+
+ if (detached)
+ return SECSuccess;
+
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ thing = PORT_ArenaZAlloc (poolp, sizeof(SECItem));
+ cinfo->content.data = (SECItem*)thing;
+ versionp = NULL;
+ version = -1;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7DigestedData));
+ cinfo->content.digestedData = (SEC_PKCS7DigestedData*)thing;
+ versionp = &(cinfo->content.digestedData->version);
+ version = SEC_PKCS7_DIGESTED_DATA_VERSION;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EncryptedData));
+ cinfo->content.encryptedData = (SEC_PKCS7EncryptedData*)thing;
+ versionp = &(cinfo->content.encryptedData->version);
+ version = SEC_PKCS7_ENCRYPTED_DATA_VERSION;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7EnvelopedData));
+ cinfo->content.envelopedData =
+ (SEC_PKCS7EnvelopedData*)thing;
+ versionp = &(cinfo->content.envelopedData->version);
+ version = SEC_PKCS7_ENVELOPED_DATA_VERSION;
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ thing = PORT_ArenaZAlloc (poolp, sizeof(SEC_PKCS7SignedData));
+ cinfo->content.signedData =
+ (SEC_PKCS7SignedData*)thing;
+ versionp = &(cinfo->content.signedData->version);
+ version = SEC_PKCS7_SIGNED_DATA_VERSION;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ thing = PORT_ArenaZAlloc(poolp,sizeof(SEC_PKCS7SignedAndEnvelopedData));
+ cinfo->content.signedAndEnvelopedData =
+ (SEC_PKCS7SignedAndEnvelopedData*)thing;
+ versionp = &(cinfo->content.signedAndEnvelopedData->version);
+ version = SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION;
+ break;
+ }
+
+ if (thing == NULL)
+ return SECFailure;
+
+ if (versionp != NULL) {
+ SECItem *dummy;
+
+ PORT_Assert (version >= 0);
+ dummy = SEC_ASN1EncodeInteger (poolp, versionp, version);
+ if (dummy == NULL)
+ return SECFailure;
+ PORT_Assert (dummy == versionp);
+ }
+
+ return SECSuccess;
+}
+
+
+static SEC_PKCS7ContentInfo *
+sec_pkcs7_create_content_info (SECOidTag kind, PRBool detached,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ PRArenaPool *poolp;
+ SECStatus rv;
+
+ poolp = PORT_NewArena (1024); /* XXX what is right value? */
+ if (poolp == NULL)
+ return NULL;
+
+ cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
+ if (cinfo == NULL) {
+ PORT_FreeArena (poolp, PR_FALSE);
+ return NULL;
+ }
+
+ cinfo->poolp = poolp;
+ cinfo->pwfn = pwfn;
+ cinfo->pwfn_arg = pwfn_arg;
+ cinfo->created = PR_TRUE;
+ cinfo->refCount = 1;
+
+ rv = sec_pkcs7_init_content_info (cinfo, poolp, kind, detached);
+ if (rv != SECSuccess) {
+ PORT_FreeArena (poolp, PR_FALSE);
+ return NULL;
+ }
+
+ return cinfo;
+}
+
+
+/*
+ * Add a signer to a PKCS7 thing, verifying the signature cert first.
+ * Any error returns SECFailure.
+ *
+ * XXX Right now this only adds the *first* signer. It fails if you try
+ * to add a second one -- this needs to be fixed.
+ */
+static SECStatus
+sec_pkcs7_add_signer (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate * cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle * certdb,
+ SECOidTag digestalgtag,
+ SECItem * digestdata)
+{
+ SEC_PKCS7SignerInfo *signerinfo, **signerinfos, ***signerinfosp;
+ SECAlgorithmID *digestalg, **digestalgs, ***digestalgsp;
+ SECItem *digest, **digests, ***digestsp;
+ SECItem * dummy;
+ void * mark;
+ SECStatus rv;
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ digestalgsp = &(sdp->digestAlgorithms);
+ digestsp = &(sdp->digests);
+ signerinfosp = &(sdp->signerInfos);
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ digestalgsp = &(saedp->digestAlgorithms);
+ digestsp = &(saedp->digests);
+ signerinfosp = &(saedp->signerInfos);
+ }
+ break;
+ default:
+ return SECFailure; /* XXX set an error? */
+ }
+
+ /*
+ * XXX I think that CERT_VerifyCert should do this if *it* is passed
+ * a NULL database.
+ */
+ if (certdb == NULL) {
+ certdb = CERT_GetDefaultCertDB();
+ if (certdb == NULL)
+ return SECFailure; /* XXX set an error? */
+ }
+
+ if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
+ cinfo->pwfn_arg, NULL) != SECSuccess)
+ {
+ /* XXX Did CERT_VerifyCert set an error? */
+ return SECFailure;
+ }
+
+ /*
+ * XXX This is the check that we do not already have a signer.
+ * This is not what we really want -- we want to allow this
+ * and *add* the new signer.
+ */
+ PORT_Assert (*signerinfosp == NULL
+ && *digestalgsp == NULL && *digestsp == NULL);
+ if (*signerinfosp != NULL || *digestalgsp != NULL || *digestsp != NULL)
+ return SECFailure;
+
+ mark = PORT_ArenaMark (cinfo->poolp);
+
+ signerinfo = (SEC_PKCS7SignerInfo*)PORT_ArenaZAlloc (cinfo->poolp,
+ sizeof(SEC_PKCS7SignerInfo));
+ if (signerinfo == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &signerinfo->version,
+ SEC_PKCS7_SIGNER_INFO_VERSION);
+ if (dummy == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ PORT_Assert (dummy == &signerinfo->version);
+
+ signerinfo->cert = CERT_DupCertificate (cert);
+ if (signerinfo->cert == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ signerinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
+ if (signerinfo->issuerAndSN == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ rv = SECOID_SetAlgorithmID (cinfo->poolp, &signerinfo->digestAlg,
+ digestalgtag, NULL);
+ if (rv != SECSuccess) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ /*
+ * Okay, now signerinfo is all set. We just need to put it and its
+ * companions (another copy of the digest algorithm, and the digest
+ * itself if given) into the main structure.
+ *
+ * XXX If we are handling more than one signer, the following code
+ * needs to look through the digest algorithms already specified
+ * and see if the same one is there already. If it is, it does not
+ * need to be added again. Also, if it is there *and* the digest
+ * is not null, then the digest given should match the digest already
+ * specified -- if not, that is an error. Finally, the new signerinfo
+ * should be *added* to the set already found.
+ */
+
+ signerinfos = (SEC_PKCS7SignerInfo**)PORT_ArenaAlloc (cinfo->poolp,
+ 2 * sizeof(SEC_PKCS7SignerInfo *));
+ if (signerinfos == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ signerinfos[0] = signerinfo;
+ signerinfos[1] = NULL;
+
+ digestalg = PORT_ArenaZAlloc (cinfo->poolp, sizeof(SECAlgorithmID));
+ digestalgs = PORT_ArenaAlloc (cinfo->poolp, 2 * sizeof(SECAlgorithmID *));
+ if (digestalg == NULL || digestalgs == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ rv = SECOID_SetAlgorithmID (cinfo->poolp, digestalg, digestalgtag, NULL);
+ if (rv != SECSuccess) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ digestalgs[0] = digestalg;
+ digestalgs[1] = NULL;
+
+ if (digestdata != NULL) {
+ digest = (SECItem*)PORT_ArenaAlloc (cinfo->poolp, sizeof(SECItem));
+ digests = (SECItem**)PORT_ArenaAlloc (cinfo->poolp,
+ 2 * sizeof(SECItem *));
+ if (digest == NULL || digests == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ rv = SECITEM_CopyItem (cinfo->poolp, digest, digestdata);
+ if (rv != SECSuccess) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ digests[0] = digest;
+ digests[1] = NULL;
+ } else {
+ digests = NULL;
+ }
+
+ *signerinfosp = signerinfos;
+ *digestalgsp = digestalgs;
+ *digestsp = digests;
+
+ PORT_ArenaUnmark(cinfo->poolp, mark);
+ return SECSuccess;
+}
+
+
+/*
+ * Helper function for creating an empty signedData.
+ */
+static SEC_PKCS7ContentInfo *
+sec_pkcs7_create_signed_data (SECKEYGetPasswordKey pwfn, void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ SEC_PKCS7SignedData *sigd;
+ SECStatus rv;
+
+ cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_SIGNED_DATA, PR_FALSE,
+ pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ sigd = cinfo->content.signedData;
+ PORT_Assert (sigd != NULL);
+
+ /*
+ * XXX Might we want to allow content types other than data?
+ * If so, via what interface?
+ */
+ rv = sec_pkcs7_init_content_info (&(sigd->contentInfo), cinfo->poolp,
+ SEC_OID_PKCS7_DATA, PR_TRUE);
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ return cinfo;
+}
+
+
+/*
+ * Start a PKCS7 signing context.
+ *
+ * "cert" is the cert that will be used to sign the data. It will be
+ * checked for validity.
+ *
+ * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
+ * XXX Maybe SECCertUsage should be split so that our caller just says
+ * "email" and *we* add the "signing" part -- otherwise our caller
+ * could be lying about the usage; we do not want to allow encryption
+ * certs for signing or vice versa.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
+ *
+ * "digest" is the actual digest of the data. It must be provided in
+ * the case of detached data or NULL if the content will be included.
+ *
+ * The return value can be passed to functions which add things to
+ * it like attributes, then eventually to SEC_PKCS7Encode() or to
+ * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
+ * SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateSignedData (CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb,
+ SECOidTag digestalg,
+ SECItem *digest,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ SECStatus rv;
+
+ cinfo = sec_pkcs7_create_signed_data (pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ rv = sec_pkcs7_add_signer (cinfo, cert, certusage, certdb,
+ digestalg, digest);
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ return cinfo;
+}
+
+
+static SEC_PKCS7Attribute *
+sec_pkcs7_create_attribute (PRArenaPool *poolp, SECOidTag oidtag,
+ SECItem *value, PRBool encoded)
+{
+ SEC_PKCS7Attribute *attr;
+ SECItem **values;
+ void *mark;
+
+ PORT_Assert (poolp != NULL);
+ mark = PORT_ArenaMark (poolp);
+
+ attr = (SEC_PKCS7Attribute*)PORT_ArenaAlloc (poolp,
+ sizeof(SEC_PKCS7Attribute));
+ if (attr == NULL)
+ goto loser;
+
+ attr->typeTag = SECOID_FindOIDByTag (oidtag);
+ if (attr->typeTag == NULL)
+ goto loser;
+
+ if (SECITEM_CopyItem (poolp, &(attr->type),
+ &(attr->typeTag->oid)) != SECSuccess)
+ goto loser;
+
+ values = (SECItem**)PORT_ArenaAlloc (poolp, 2 * sizeof(SECItem *));
+ if (values == NULL)
+ goto loser;
+
+ if (value != NULL) {
+ SECItem *copy;
+
+ copy = (SECItem*)PORT_ArenaAlloc (poolp, sizeof(SECItem));
+ if (copy == NULL)
+ goto loser;
+
+ if (SECITEM_CopyItem (poolp, copy, value) != SECSuccess)
+ goto loser;
+
+ value = copy;
+ }
+
+ values[0] = value;
+ values[1] = NULL;
+ attr->values = values;
+ attr->encoded = encoded;
+
+ PORT_ArenaUnmark (poolp, mark);
+ return attr;
+
+loser:
+ PORT_Assert (mark != NULL);
+ PORT_ArenaRelease (poolp, mark);
+ return NULL;
+}
+
+
+static SECStatus
+sec_pkcs7_add_attribute (SEC_PKCS7ContentInfo *cinfo,
+ SEC_PKCS7Attribute ***attrsp,
+ SEC_PKCS7Attribute *attr)
+{
+ SEC_PKCS7Attribute **attrs;
+ SECItem *ct_value;
+ void *mark;
+
+ PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
+ if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
+ return SECFailure;
+
+ attrs = *attrsp;
+ if (attrs != NULL) {
+ int count;
+
+ /*
+ * We already have some attributes, and just need to add this
+ * new one.
+ */
+
+ /*
+ * We should already have the *required* attributes, which were
+ * created/added at the same time the first attribute was added.
+ */
+ PORT_Assert (sec_PKCS7FindAttribute (attrs,
+ SEC_OID_PKCS9_CONTENT_TYPE,
+ PR_FALSE) != NULL);
+ PORT_Assert (sec_PKCS7FindAttribute (attrs,
+ SEC_OID_PKCS9_MESSAGE_DIGEST,
+ PR_FALSE) != NULL);
+
+ for (count = 0; attrs[count] != NULL; count++)
+ ;
+ attrs = (SEC_PKCS7Attribute**)PORT_ArenaGrow (cinfo->poolp, attrs,
+ (count + 1) * sizeof(SEC_PKCS7Attribute *),
+ (count + 2) * sizeof(SEC_PKCS7Attribute *));
+ if (attrs == NULL)
+ return SECFailure;
+
+ attrs[count] = attr;
+ attrs[count+1] = NULL;
+ *attrsp = attrs;
+
+ return SECSuccess;
+ }
+
+ /*
+ * This is the first time an attribute is going in.
+ * We need to create and add the required attributes, and then
+ * we will also add in the one our caller gave us.
+ */
+
+ /*
+ * There are 2 required attributes, plus the one our caller wants
+ * to add, plus we always end with a NULL one. Thus, four slots.
+ */
+ attrs = (SEC_PKCS7Attribute**)PORT_ArenaAlloc (cinfo->poolp,
+ 4 * sizeof(SEC_PKCS7Attribute *));
+ if (attrs == NULL)
+ return SECFailure;
+
+ mark = PORT_ArenaMark (cinfo->poolp);
+
+ /*
+ * First required attribute is the content type of the data
+ * being signed.
+ */
+ ct_value = &(cinfo->content.signedData->contentInfo.contentType);
+ attrs[0] = sec_pkcs7_create_attribute (cinfo->poolp,
+ SEC_OID_PKCS9_CONTENT_TYPE,
+ ct_value, PR_FALSE);
+ /*
+ * Second required attribute is the message digest of the data
+ * being signed; we leave the value NULL for now (just create
+ * the place for it to go), and the encoder will fill it in later.
+ */
+ attrs[1] = sec_pkcs7_create_attribute (cinfo->poolp,
+ SEC_OID_PKCS9_MESSAGE_DIGEST,
+ NULL, PR_FALSE);
+ if (attrs[0] == NULL || attrs[1] == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ attrs[2] = attr;
+ attrs[3] = NULL;
+ *attrsp = attrs;
+
+ PORT_ArenaUnmark (cinfo->poolp, mark);
+ return SECSuccess;
+}
+
+
+/*
+ * Add the signing time to the authenticated (i.e. signed) attributes
+ * of "cinfo". This is expected to be included in outgoing signed
+ * messages for email (S/MIME) but is likely useful in other situations.
+ *
+ * This should only be added once; a second call will either do
+ * nothing or replace an old signing time with a newer one.
+ *
+ * XXX This will probably just shove the current time into "cinfo"
+ * but it will not actually get signed until the entire item is
+ * processed for encoding. Is this (expected to be small) delay okay?
+ *
+ * "cinfo" should be of type signedData (the only kind of pkcs7 data
+ * that is allowed authenticated attributes); SECFailure will be returned
+ * if it is not.
+ */
+SECStatus
+SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo)
+{
+ SEC_PKCS7SignerInfo **signerinfos;
+ SEC_PKCS7Attribute *attr;
+ SECItem stime;
+ SECStatus rv;
+ int si;
+
+ PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
+ if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
+ return SECFailure;
+
+ signerinfos = cinfo->content.signedData->signerInfos;
+
+ /* There has to be a signer, or it makes no sense. */
+ if (signerinfos == NULL || signerinfos[0] == NULL)
+ return SECFailure;
+
+ rv = DER_TimeToUTCTime (&stime, PR_Now());
+ if (rv != SECSuccess)
+ return rv;
+
+ attr = sec_pkcs7_create_attribute (cinfo->poolp,
+ SEC_OID_PKCS9_SIGNING_TIME,
+ &stime, PR_FALSE);
+ SECITEM_FreeItem (&stime, PR_FALSE);
+
+ if (attr == NULL)
+ return SECFailure;
+
+ rv = SECSuccess;
+ for (si = 0; signerinfos[si] != NULL; si++) {
+ SEC_PKCS7Attribute *oattr;
+
+ oattr = sec_PKCS7FindAttribute (signerinfos[si]->authAttr,
+ SEC_OID_PKCS9_SIGNING_TIME, PR_FALSE);
+ PORT_Assert (oattr == NULL);
+ if (oattr != NULL)
+ continue; /* XXX or would it be better to replace it? */
+
+ rv = sec_pkcs7_add_attribute (cinfo, &(signerinfos[si]->authAttr),
+ attr);
+ if (rv != SECSuccess)
+ break; /* could try to continue, but may as well give up now */
+ }
+
+ return rv;
+}
+
+
+/*
+ * Add the specified attribute to the authenticated (i.e. signed) attributes
+ * of "cinfo" -- "oidtag" describes the attribute and "value" is the
+ * value to be associated with it. NOTE! "value" must already be encoded;
+ * no interpretation of "oidtag" is done. Also, it is assumed that this
+ * signedData has only one signer -- if we ever need to add attributes
+ * when there is more than one signature, we need a way to specify *which*
+ * signature should get the attribute.
+ *
+ * XXX Technically, a signed attribute can have multiple values; if/when
+ * we ever need to support an attribute which takes multiple values, we
+ * either need to change this interface or create an AddSignedAttributeValue
+ * which can be called subsequently, and would then append a value.
+ *
+ * "cinfo" should be of type signedData (the only kind of pkcs7 data
+ * that is allowed authenticated attributes); SECFailure will be returned
+ * if it is not.
+ */
+SECStatus
+SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo,
+ SECOidTag oidtag,
+ SECItem *value)
+{
+ SEC_PKCS7SignerInfo **signerinfos;
+ SEC_PKCS7Attribute *attr;
+
+ PORT_Assert (SEC_PKCS7ContentType (cinfo) == SEC_OID_PKCS7_SIGNED_DATA);
+ if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
+ return SECFailure;
+
+ signerinfos = cinfo->content.signedData->signerInfos;
+
+ /*
+ * No signature or more than one means no deal.
+ */
+ if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
+ return SECFailure;
+
+ attr = sec_pkcs7_create_attribute (cinfo->poolp, oidtag, value, PR_TRUE);
+ if (attr == NULL)
+ return SECFailure;
+
+ return sec_pkcs7_add_attribute (cinfo, &(signerinfos[0]->authAttr), attr);
+}
+
+
+/*
+ * Mark that the signer certificates and their issuing chain should
+ * be included in the encoded data. This is expected to be used
+ * in outgoing signed messages for email (S/MIME).
+ *
+ * "certdb" is the cert database to use for finding the chain.
+ * It can be NULL, meaning use the default database.
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+SECStatus
+SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertDBHandle *certdb)
+{
+ SECOidTag kind;
+ SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ signerinfos = cinfo->content.signedData->signerInfos;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
+ break;
+ default:
+ return SECFailure; /* XXX set an error? */
+ }
+
+ if (signerinfos == NULL) /* no signer, no certs? */
+ return SECFailure; /* XXX set an error? */
+
+ if (certdb == NULL) {
+ certdb = CERT_GetDefaultCertDB();
+ if (certdb == NULL) {
+ PORT_SetError (SEC_ERROR_BAD_DATABASE);
+ return SECFailure;
+ }
+ }
+
+ /* XXX Should it be an error if we find no signerinfo or no certs? */
+ while ((signerinfo = *signerinfos++) != NULL) {
+ if (signerinfo->cert != NULL)
+ /* get the cert chain. don't send the root to avoid contamination
+ * of old clients with a new root that they don't trust
+ */
+ signerinfo->certList = CERT_CertChainFromCert (signerinfo->cert,
+ certUsageEmailSigner,
+ PR_FALSE);
+ }
+
+ return SECSuccess;
+}
+
+
+/*
+ * Helper function to add a certificate chain for inclusion in the
+ * bag of certificates in a signedData.
+ */
+static SECStatus
+sec_pkcs7_add_cert_chain (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert,
+ CERTCertDBHandle *certdb)
+{
+ SECOidTag kind;
+ CERTCertificateList *certlist, **certlists, ***certlistsp;
+ int count;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ certlistsp = &(sdp->certLists);
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ certlistsp = &(saedp->certLists);
+ }
+ break;
+ default:
+ return SECFailure; /* XXX set an error? */
+ }
+
+ if (certdb == NULL) {
+ certdb = CERT_GetDefaultCertDB();
+ if (certdb == NULL) {
+ PORT_SetError (SEC_ERROR_BAD_DATABASE);
+ return SECFailure;
+ }
+ }
+
+ certlist = CERT_CertChainFromCert (cert, certUsageEmailSigner, PR_FALSE);
+ if (certlist == NULL)
+ return SECFailure;
+
+ certlists = *certlistsp;
+ if (certlists == NULL) {
+ count = 0;
+ certlists = (CERTCertificateList**)PORT_ArenaAlloc (cinfo->poolp,
+ 2 * sizeof(CERTCertificateList *));
+ } else {
+ for (count = 0; certlists[count] != NULL; count++)
+ ;
+ PORT_Assert (count); /* should be at least one already */
+ certlists = (CERTCertificateList**)PORT_ArenaGrow (cinfo->poolp,
+ certlists,
+ (count + 1) * sizeof(CERTCertificateList *),
+ (count + 2) * sizeof(CERTCertificateList *));
+ }
+
+ if (certlists == NULL) {
+ CERT_DestroyCertificateList (certlist);
+ return SECFailure;
+ }
+
+ certlists[count] = certlist;
+ certlists[count + 1] = NULL;
+
+ *certlistsp = certlists;
+
+ return SECSuccess;
+}
+
+
+/*
+ * Helper function to add a certificate for inclusion in the bag of
+ * certificates in a signedData.
+ */
+static SECStatus
+sec_pkcs7_add_certificate (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert)
+{
+ SECOidTag kind;
+ CERTCertificate **certs, ***certsp;
+ int count;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ certsp = &(sdp->certs);
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ certsp = &(saedp->certs);
+ }
+ break;
+ default:
+ return SECFailure; /* XXX set an error? */
+ }
+
+ cert = CERT_DupCertificate (cert);
+ if (cert == NULL)
+ return SECFailure;
+
+ certs = *certsp;
+ if (certs == NULL) {
+ count = 0;
+ certs = (CERTCertificate**)PORT_ArenaAlloc (cinfo->poolp,
+ 2 * sizeof(CERTCertificate *));
+ } else {
+ for (count = 0; certs[count] != NULL; count++)
+ ;
+ PORT_Assert (count); /* should be at least one already */
+ certs = (CERTCertificate**)PORT_ArenaGrow (cinfo->poolp, certs,
+ (count + 1) * sizeof(CERTCertificate *),
+ (count + 2) * sizeof(CERTCertificate *));
+ }
+
+ if (certs == NULL) {
+ CERT_DestroyCertificate (cert);
+ return SECFailure;
+ }
+
+ certs[count] = cert;
+ certs[count + 1] = NULL;
+
+ *certsp = certs;
+
+ return SECSuccess;
+}
+
+
+/*
+ * Create a PKCS7 certs-only container.
+ *
+ * "cert" is the (first) cert that will be included.
+ *
+ * "include_chain" specifies whether the entire chain for "cert" should
+ * be included.
+ *
+ * "certdb" is the cert database to use for finding the chain.
+ * It can be NULL in when "include_chain" is false, or when meaning
+ * use the default database.
+ *
+ * More certs and chains can be added via AddCertificate and AddCertChain.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateCertsOnly (CERTCertificate *cert,
+ PRBool include_chain,
+ CERTCertDBHandle *certdb)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ SECStatus rv;
+
+ cinfo = sec_pkcs7_create_signed_data (NULL, NULL);
+ if (cinfo == NULL)
+ return NULL;
+
+ if (include_chain)
+ rv = sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
+ else
+ rv = sec_pkcs7_add_certificate (cinfo, cert);
+
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ return cinfo;
+}
+
+
+/*
+ * Add "cert" and its entire chain to the set of certs included in "cinfo".
+ *
+ * "certdb" is the cert database to use for finding the chain.
+ * It can be NULL, meaning use the default database.
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+SECStatus
+SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert,
+ CERTCertDBHandle *certdb)
+{
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ if (kind != SEC_OID_PKCS7_SIGNED_DATA
+ && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
+ return SECFailure; /* XXX set an error? */
+
+ return sec_pkcs7_add_cert_chain (cinfo, cert, certdb);
+}
+
+
+/*
+ * Add "cert" to the set of certs included in "cinfo".
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+SECStatus
+SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo, CERTCertificate *cert)
+{
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ if (kind != SEC_OID_PKCS7_SIGNED_DATA
+ && kind != SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA)
+ return SECFailure; /* XXX set an error? */
+
+ return sec_pkcs7_add_certificate (cinfo, cert);
+}
+
+
+static SECStatus
+sec_pkcs7_init_encrypted_content_info (SEC_PKCS7EncryptedContentInfo *enccinfo,
+ PRArenaPool *poolp,
+ SECOidTag kind, PRBool detached,
+ SECOidTag encalg, int keysize)
+{
+ SECStatus rv;
+
+ PORT_Assert (enccinfo != NULL && poolp != NULL);
+ if (enccinfo == NULL || poolp == NULL)
+ return SECFailure;
+
+ /*
+ * XXX Some day we may want to allow for other kinds. That needs
+ * more work and modifications to the creation interface, etc.
+ * For now, allow but notice callers who pass in other kinds.
+ * They are responsible for creating the inner type and encoding,
+ * if it is other than DATA.
+ */
+ PORT_Assert (kind == SEC_OID_PKCS7_DATA);
+
+ enccinfo->contentTypeTag = SECOID_FindOIDByTag (kind);
+ PORT_Assert (enccinfo->contentTypeTag
+ && enccinfo->contentTypeTag->offset == kind);
+
+ rv = SECITEM_CopyItem (poolp, &(enccinfo->contentType),
+ &(enccinfo->contentTypeTag->oid));
+ if (rv != SECSuccess)
+ return rv;
+
+ /* Save keysize and algorithm for later. */
+ enccinfo->keysize = keysize;
+ enccinfo->encalg = encalg;
+
+ return SECSuccess;
+}
+
+
+/*
+ * Add a recipient to a PKCS7 thing, verifying their cert first.
+ * Any error returns SECFailure.
+ */
+static SECStatus
+sec_pkcs7_add_recipient (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb)
+{
+ SECOidTag kind;
+ SEC_PKCS7RecipientInfo *recipientinfo, **recipientinfos, ***recipientinfosp;
+ SECItem *dummy;
+ void *mark;
+ int count;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ {
+ SEC_PKCS7EnvelopedData *edp;
+
+ edp = cinfo->content.envelopedData;
+ recipientinfosp = &(edp->recipientInfos);
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ recipientinfosp = &(saedp->recipientInfos);
+ }
+ break;
+ default:
+ return SECFailure; /* XXX set an error? */
+ }
+
+ /*
+ * XXX I think that CERT_VerifyCert should do this if *it* is passed
+ * a NULL database.
+ */
+ if (certdb == NULL) {
+ certdb = CERT_GetDefaultCertDB();
+ if (certdb == NULL)
+ return SECFailure; /* XXX set an error? */
+ }
+
+ if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage, PR_Now(),
+ cinfo->pwfn_arg, NULL) != SECSuccess)
+ {
+ /* XXX Did CERT_VerifyCert set an error? */
+ return SECFailure;
+ }
+
+ mark = PORT_ArenaMark (cinfo->poolp);
+
+ recipientinfo = (SEC_PKCS7RecipientInfo*)PORT_ArenaZAlloc (cinfo->poolp,
+ sizeof(SEC_PKCS7RecipientInfo));
+ if (recipientinfo == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ dummy = SEC_ASN1EncodeInteger (cinfo->poolp, &recipientinfo->version,
+ SEC_PKCS7_RECIPIENT_INFO_VERSION);
+ if (dummy == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+ PORT_Assert (dummy == &recipientinfo->version);
+
+ recipientinfo->cert = CERT_DupCertificate (cert);
+ if (recipientinfo->cert == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ recipientinfo->issuerAndSN = CERT_GetCertIssuerAndSN (cinfo->poolp, cert);
+ if (recipientinfo->issuerAndSN == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ /*
+ * Okay, now recipientinfo is all set. We just need to put it into
+ * the main structure.
+ *
+ * If this is the first recipient, allocate a new recipientinfos array;
+ * otherwise, reallocate the array, making room for the new entry.
+ */
+ recipientinfos = *recipientinfosp;
+ if (recipientinfos == NULL) {
+ count = 0;
+ recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaAlloc (
+ cinfo->poolp,
+ 2 * sizeof(SEC_PKCS7RecipientInfo *));
+ } else {
+ for (count = 0; recipientinfos[count] != NULL; count++)
+ ;
+ PORT_Assert (count); /* should be at least one already */
+ recipientinfos = (SEC_PKCS7RecipientInfo **)PORT_ArenaGrow (
+ cinfo->poolp, recipientinfos,
+ (count + 1) * sizeof(SEC_PKCS7RecipientInfo *),
+ (count + 2) * sizeof(SEC_PKCS7RecipientInfo *));
+ }
+
+ if (recipientinfos == NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ return SECFailure;
+ }
+
+ recipientinfos[count] = recipientinfo;
+ recipientinfos[count + 1] = NULL;
+
+ *recipientinfosp = recipientinfos;
+
+ PORT_ArenaUnmark (cinfo->poolp, mark);
+ return SECSuccess;
+}
+
+
+/*
+ * Start a PKCS7 enveloping context.
+ *
+ * "cert" is the cert for the recipient. It will be checked for validity.
+ *
+ * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
+ * XXX Maybe SECCertUsage should be split so that our caller just says
+ * "email" and *we* add the "recipient" part -- otherwise our caller
+ * could be lying about the usage; we do not want to allow encryption
+ * certs for signing or vice versa.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
+ *
+ * "keysize" specifies the bulk encryption key size, in bits.
+ *
+ * The return value can be passed to functions which add things to
+ * it like more recipients, then eventually to SEC_PKCS7Encode() or to
+ * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
+ * SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb,
+ SECOidTag encalg,
+ int keysize,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ SEC_PKCS7EnvelopedData *envd;
+ SECStatus rv;
+
+ cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENVELOPED_DATA,
+ PR_FALSE, pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ rv = sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ envd = cinfo->content.envelopedData;
+ PORT_Assert (envd != NULL);
+
+ /*
+ * XXX Might we want to allow content types other than data?
+ * If so, via what interface?
+ */
+ rv = sec_pkcs7_init_encrypted_content_info (&(envd->encContentInfo),
+ cinfo->poolp,
+ SEC_OID_PKCS7_DATA, PR_FALSE,
+ encalg, keysize);
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ /* XXX Anything more to do here? */
+
+ return cinfo;
+}
+
+
+/*
+ * Add another recipient to an encrypted message.
+ *
+ * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ *
+ * "cert" is the cert for the recipient. It will be checked for validity.
+ *
+ * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
+ * XXX Maybe SECCertUsage should be split so that our caller just says
+ * "email" and *we* add the "recipient" part -- otherwise our caller
+ * could be lying about the usage; we do not want to allow encryption
+ * certs for signing or vice versa.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ */
+SECStatus
+SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb)
+{
+ return sec_pkcs7_add_recipient (cinfo, cert, certusage, certdb);
+}
+
+
+/*
+ * Create an empty PKCS7 data content info.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateData (void)
+{
+ return sec_pkcs7_create_content_info (SEC_OID_PKCS7_DATA, PR_FALSE,
+ NULL, NULL);
+}
+
+
+/*
+ * Create an empty PKCS7 encrypted content info.
+ *
+ * "algorithm" specifies the bulk encryption algorithm to use.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ SECAlgorithmID *algid;
+ SEC_PKCS7EncryptedData *enc_data;
+ SECStatus rv;
+
+ cinfo = sec_pkcs7_create_content_info (SEC_OID_PKCS7_ENCRYPTED_DATA,
+ PR_FALSE, pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ enc_data = cinfo->content.encryptedData;
+ algid = &(enc_data->encContentInfo.contentEncAlg);
+
+ switch (algorithm) {
+ case SEC_OID_RC2_CBC:
+ case SEC_OID_DES_EDE3_CBC:
+ case SEC_OID_DES_CBC:
+ rv = SECOID_SetAlgorithmID (cinfo->poolp, algid, algorithm, NULL);
+ break;
+ default:
+ {
+ /*
+ * Assume password-based-encryption. At least, try that.
+ */
+ SECAlgorithmID *pbe_algid;
+ pbe_algid = PK11_CreatePBEAlgorithmID (algorithm, 1, NULL);
+ if (pbe_algid == NULL) {
+ rv = SECFailure;
+ } else {
+ rv = SECOID_CopyAlgorithmID (cinfo->poolp, algid, pbe_algid);
+ SECOID_DestroyAlgorithmID (pbe_algid, PR_TRUE);
+ }
+ }
+ break;
+ }
+
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ rv = sec_pkcs7_init_encrypted_content_info (&(enc_data->encContentInfo),
+ cinfo->poolp,
+ SEC_OID_PKCS7_DATA, PR_FALSE,
+ algorithm, keysize);
+ if (rv != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ return cinfo;
+}
+
diff --git a/security/nss/lib/pkcs7/p7decode.c b/security/nss/lib/pkcs7/p7decode.c
new file mode 100644
index 000000000..0eee743c2
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7decode.c
@@ -0,0 +1,2087 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * PKCS7 decoding, verification.
+ *
+ * $Id$
+ */
+
+#include "p7local.h"
+
+#include "cert.h"
+ /* XXX do not want to have to include */
+#include "certdb.h" /* certdb.h -- the trust stuff needed by */
+ /* the add certificate code needs to get */
+ /* rewritten/abstracted and then this */
+ /* include should be removed! */
+#include "cdbhdl.h"
+#include "cryptohi.h"
+#include "key.h"
+#include "secasn1.h"
+#include "secitem.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "prtime.h"
+#include "secerr.h"
+
+
+struct sec_pkcs7_decoder_worker {
+ int depth;
+ int digcnt;
+ void **digcxs;
+ SECHashObject **digobjs;
+ sec_PKCS7CipherObject *decryptobj;
+ PRBool saw_contents;
+};
+
+struct SEC_PKCS7DecoderContextStr {
+ SEC_ASN1DecoderContext *dcx;
+ SEC_PKCS7ContentInfo *cinfo;
+ SEC_PKCS7DecoderContentCallback cb;
+ void *cb_arg;
+ SECKEYGetPasswordKey pwfn;
+ void *pwfn_arg;
+ struct sec_pkcs7_decoder_worker worker;
+ PRArenaPool *tmp_poolp;
+ int error;
+ SEC_PKCS7GetDecryptKeyCallback dkcb;
+ void *dkcb_arg;
+ SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb;
+};
+
+/*
+ * Handle one worker, decrypting and digesting the data as necessary.
+ *
+ * XXX If/when we support nested contents, this probably needs to be
+ * revised somewhat to get passed the content-info (which unfortunately
+ * can be two different types depending on whether it is encrypted or not)
+ * corresponding to the given worker.
+ */
+static void
+sec_pkcs7_decoder_work_data (SEC_PKCS7DecoderContext *p7dcx,
+ struct sec_pkcs7_decoder_worker *worker,
+ const unsigned char *data, unsigned long len,
+ PRBool final)
+{
+ unsigned char *buf = NULL;
+ SECStatus rv;
+ int i;
+
+ /*
+ * We should really have data to process, or we should be trying
+ * to finish/flush the last block. (This is an overly paranoid
+ * check since all callers are in this file and simple inspection
+ * proves they do it right. But it could find a bug in future
+ * modifications/development, that is why it is here.)
+ */
+ PORT_Assert ((data != NULL && len) || final);
+
+ /*
+ * Decrypt this chunk.
+ *
+ * XXX If we get an error, we do not want to do the digest or callback,
+ * but we want to keep decoding. Or maybe we want to stop decoding
+ * altogether if there is a callback, because obviously we are not
+ * sending the data back and they want to know that.
+ */
+ if (worker->decryptobj != NULL) {
+ /* XXX the following lengths should all be longs? */
+ unsigned int inlen; /* length of data being decrypted */
+ unsigned int outlen; /* length of decrypted data */
+ unsigned int buflen; /* length available for decrypted data */
+ SECItem *plain;
+
+ inlen = len;
+ buflen = sec_PKCS7DecryptLength (worker->decryptobj, inlen, final);
+ if (buflen == 0) {
+ if (inlen == 0) /* no input and no output */
+ return;
+ /*
+ * No output is expected, but the input data may be buffered
+ * so we still have to call Decrypt.
+ */
+ rv = sec_PKCS7Decrypt (worker->decryptobj, NULL, NULL, 0,
+ data, inlen, final);
+ if (rv != SECSuccess) {
+ p7dcx->error = PORT_GetError();
+ return; /* XXX indicate error? */
+ }
+ return;
+ }
+
+ if (p7dcx->cb != NULL) {
+ buf = (unsigned char *) PORT_Alloc (buflen);
+ plain = NULL;
+ } else {
+ unsigned long oldlen;
+
+ /*
+ * XXX This assumes one level of content only.
+ * See comment above about nested content types.
+ * XXX Also, it should work for signedAndEnvelopedData, too!
+ */
+ plain = &(p7dcx->cinfo->
+ content.envelopedData->encContentInfo.plainContent);
+
+ oldlen = plain->len;
+ if (oldlen == 0) {
+ buf = (unsigned char*)PORT_ArenaAlloc (p7dcx->cinfo->poolp,
+ buflen);
+ } else {
+ buf = (unsigned char*)PORT_ArenaGrow (p7dcx->cinfo->poolp,
+ plain->data,
+ oldlen, oldlen + buflen);
+ if (buf != NULL)
+ buf += oldlen;
+ }
+ plain->data = buf;
+ }
+ if (buf == NULL) {
+ p7dcx->error = SEC_ERROR_NO_MEMORY;
+ return; /* XXX indicate error? */
+ }
+ rv = sec_PKCS7Decrypt (worker->decryptobj, buf, &outlen, buflen,
+ data, inlen, final);
+ if (rv != SECSuccess) {
+ p7dcx->error = PORT_GetError();
+ return; /* XXX indicate error? */
+ }
+ if (plain != NULL) {
+ PORT_Assert (final || outlen == buflen);
+ plain->len += outlen;
+ }
+ data = buf;
+ len = outlen;
+ }
+
+ /*
+ * Update the running digests.
+ */
+ if (len) {
+ for (i = 0; i < worker->digcnt; i++) {
+ (* worker->digobjs[i]->update) (worker->digcxs[i], data, len);
+ }
+ }
+
+ /*
+ * Pass back the contents bytes, and free the temporary buffer.
+ */
+ if (p7dcx->cb != NULL) {
+ if (len)
+ (* p7dcx->cb) (p7dcx->cb_arg, (const char *)data, len);
+ if (worker->decryptobj != NULL) {
+ PORT_Assert (buf != NULL);
+ PORT_Free (buf);
+ }
+ }
+}
+
+static void
+sec_pkcs7_decoder_filter (void *arg, const char *data, unsigned long len,
+ int depth, SEC_ASN1EncodingPart data_kind)
+{
+ SEC_PKCS7DecoderContext *p7dcx;
+ struct sec_pkcs7_decoder_worker *worker;
+
+ /*
+ * Since we do not handle any nested contents, the only bytes we
+ * are really interested in are the actual contents bytes (not
+ * the identifier, length, or end-of-contents bytes). If we were
+ * handling nested types we would probably need to do something
+ * smarter based on depth and data_kind.
+ */
+ if (data_kind != SEC_ASN1_Contents)
+ return;
+
+ /*
+ * The ASN.1 decoder should not even call us with a length of 0.
+ * Just being paranoid.
+ */
+ PORT_Assert (len);
+ if (len == 0)
+ return;
+
+ p7dcx = (SEC_PKCS7DecoderContext*)arg;
+
+ /*
+ * Handling nested contents would mean that there is a chain
+ * of workers -- one per each level of content. The following
+ * would start with the first worker and loop over them.
+ */
+ worker = &(p7dcx->worker);
+
+ worker->saw_contents = PR_TRUE;
+
+ sec_pkcs7_decoder_work_data (p7dcx, worker,
+ (const unsigned char *) data, len, PR_FALSE);
+}
+
+
+/*
+ * Create digest contexts for each algorithm in "digestalgs".
+ * No algorithms is not an error, we just do not do anything.
+ * An error (like trouble allocating memory), marks the error
+ * in "p7dcx" and returns SECFailure, which means that our caller
+ * should just give up altogether.
+ */
+static SECStatus
+sec_pkcs7_decoder_start_digests (SEC_PKCS7DecoderContext *p7dcx, int depth,
+ SECAlgorithmID **digestalgs)
+{
+ SECAlgorithmID *algid;
+ SECOidData *oiddata;
+ SECHashObject *digobj;
+ void *digcx;
+ int i, digcnt;
+
+ if (digestalgs == NULL)
+ return SECSuccess;
+
+ /*
+ * Count the algorithms.
+ */
+ digcnt = 0;
+ while (digestalgs[digcnt] != NULL)
+ digcnt++;
+
+ /*
+ * No algorithms means no work to do.
+ * This is not expected, so cause an assert.
+ * But if it does happen, just act as if there were
+ * no algorithms specified.
+ */
+ PORT_Assert (digcnt != 0);
+ if (digcnt == 0)
+ return SECSuccess;
+
+ p7dcx->worker.digcxs = (void**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
+ digcnt * sizeof (void *));
+ p7dcx->worker.digobjs = (SECHashObject**)PORT_ArenaAlloc (p7dcx->tmp_poolp,
+ digcnt * sizeof (SECHashObject *));
+ if (p7dcx->worker.digcxs == NULL || p7dcx->worker.digobjs == NULL) {
+ p7dcx->error = SEC_ERROR_NO_MEMORY;
+ return SECFailure;
+ }
+
+ p7dcx->worker.depth = depth;
+ p7dcx->worker.digcnt = 0;
+
+ /*
+ * Create a digest context for each algorithm.
+ */
+ for (i = 0; i < digcnt; i++) {
+ algid = digestalgs[i];
+ oiddata = SECOID_FindOID(&(algid->algorithm));
+ if (oiddata == NULL) {
+ digobj = NULL;
+ } else {
+ switch (oiddata->offset) {
+ case SEC_OID_MD2:
+ digobj = &SECHashObjects[HASH_AlgMD2];
+ break;
+ case SEC_OID_MD5:
+ digobj = &SECHashObjects[HASH_AlgMD5];
+ break;
+ case SEC_OID_SHA1:
+ digobj = &SECHashObjects[HASH_AlgSHA1];
+ break;
+ default:
+ digobj = NULL;
+ break;
+ }
+ }
+
+ /*
+ * Skip any algorithm we do not even recognize; obviously,
+ * this could be a problem, but if it is critical then the
+ * result will just be that the signature does not verify.
+ * We do not necessarily want to error out here, because
+ * the particular algorithm may not actually be important,
+ * but we cannot know that until later.
+ */
+ if (digobj == NULL) {
+ p7dcx->worker.digcnt--;
+ continue;
+ }
+
+ digcx = (* digobj->create)();
+ if (digcx != NULL) {
+ (* digobj->begin) (digcx);
+ p7dcx->worker.digobjs[p7dcx->worker.digcnt] = digobj;
+ p7dcx->worker.digcxs[p7dcx->worker.digcnt] = digcx;
+ p7dcx->worker.digcnt++;
+ }
+ }
+
+ if (p7dcx->worker.digcnt != 0)
+ SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
+ sec_pkcs7_decoder_filter,
+ p7dcx,
+ (PRBool)(p7dcx->cb != NULL));
+ return SECSuccess;
+}
+
+
+/*
+ * Close out all of the digest contexts, storing the results in "digestsp".
+ */
+static SECStatus
+sec_pkcs7_decoder_finish_digests (SEC_PKCS7DecoderContext *p7dcx,
+ PRArenaPool *poolp,
+ SECItem ***digestsp)
+{
+ struct sec_pkcs7_decoder_worker *worker;
+ SECHashObject *digobj;
+ void *digcx;
+ SECItem **digests, *digest;
+ int i;
+ void *mark;
+
+ /*
+ * XXX Handling nested contents would mean that there is a chain
+ * of workers -- one per each level of content. The following
+ * would want to find the last worker in the chain.
+ */
+ worker = &(p7dcx->worker);
+
+ /*
+ * If no digests, then we have nothing to do.
+ */
+ if (worker->digcnt == 0)
+ return SECSuccess;
+
+ /*
+ * No matter what happens after this, we want to stop filtering.
+ * XXX If we handle nested contents, we only want to stop filtering
+ * if we are finishing off the *last* worker.
+ */
+ SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
+
+ /*
+ * If we ended up with no contents, just destroy each
+ * digest context -- they are meaningless and potentially
+ * confusing, because their presence would imply some content
+ * was digested.
+ */
+ if (! worker->saw_contents) {
+ for (i = 0; i < worker->digcnt; i++) {
+ digcx = worker->digcxs[i];
+ digobj = worker->digobjs[i];
+ (* digobj->destroy) (digcx, PR_TRUE);
+ }
+ return SECSuccess;
+ }
+
+ mark = PORT_ArenaMark (poolp);
+
+ /*
+ * Close out each digest context, saving digest away.
+ */
+ digests =
+ (SECItem**)PORT_ArenaAlloc (poolp,(worker->digcnt+1)*sizeof(SECItem *));
+ digest = (SECItem*)PORT_ArenaAlloc (poolp, worker->digcnt*sizeof(SECItem));
+ if (digests == NULL || digest == NULL) {
+ p7dcx->error = PORT_GetError();
+ PORT_ArenaRelease (poolp, mark);
+ return SECFailure;
+ }
+
+ for (i = 0; i < worker->digcnt; i++, digest++) {
+ digcx = worker->digcxs[i];
+ digobj = worker->digobjs[i];
+
+ digest->data = (unsigned char*)PORT_ArenaAlloc (poolp, digobj->length);
+ if (digest->data == NULL) {
+ p7dcx->error = PORT_GetError();
+ PORT_ArenaRelease (poolp, mark);
+ return SECFailure;
+ }
+
+ digest->len = digobj->length;
+ (* digobj->end) (digcx, digest->data, &(digest->len), digest->len);
+ (* digobj->destroy) (digcx, PR_TRUE);
+
+ digests[i] = digest;
+ }
+ digests[i] = NULL;
+ *digestsp = digests;
+
+ PORT_ArenaUnmark (poolp, mark);
+ return SECSuccess;
+}
+
+/*
+ * XXX Need comment explaining following helper function (which is used
+ * by sec_pkcs7_decoder_start_decrypt).
+ */
+extern const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[];
+
+static PK11SymKey *
+sec_pkcs7_decoder_get_recipient_key (SEC_PKCS7DecoderContext *p7dcx,
+ SEC_PKCS7RecipientInfo **recipientinfos,
+ SEC_PKCS7EncryptedContentInfo *enccinfo)
+{
+ SEC_PKCS7RecipientInfo *ri;
+ CERTCertificate *cert = NULL;
+ SECKEYPrivateKey *privkey = NULL;
+ PK11SymKey *bulkkey;
+ SECOidTag keyalgtag, bulkalgtag, encalgtag;
+ PK11SlotInfo *slot;
+ int i, bulkLength = 0;
+
+ if (recipientinfos == NULL || recipientinfos[0] == NULL) {
+ p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
+ goto no_key_found;
+ }
+
+ cert = PK11_FindCertAndKeyByRecipientList(&slot,recipientinfos,&ri,
+ &privkey, p7dcx->pwfn_arg);
+ if (cert == NULL) {
+ p7dcx->error = SEC_ERROR_NOT_A_RECIPIENT;
+ goto no_key_found;
+ }
+
+ ri->cert = cert; /* so we can find it later */
+ PORT_Assert(privkey != NULL);
+
+ keyalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
+ encalgtag = SECOID_GetAlgorithmTag (&(ri->keyEncAlg));
+ if ((encalgtag != SEC_OID_NETSCAPE_SMIME_KEA) && (keyalgtag != encalgtag)) {
+ p7dcx->error = SEC_ERROR_PKCS7_KEYALG_MISMATCH;
+ goto no_key_found;
+ }
+ bulkalgtag = SECOID_GetAlgorithmTag (&(enccinfo->contentEncAlg));
+
+ switch (encalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ bulkkey = PK11_PubUnwrapSymKey (privkey, &ri->encKey,
+ PK11_AlgtagToMechanism (bulkalgtag),
+ CKA_DECRYPT, 0);
+ if (bulkkey == NULL) {
+ p7dcx->error = PORT_GetError();
+ PORT_SetError(0);
+ goto no_key_found;
+ }
+ break;
+ /* ### mwelch -- KEA */
+ case SEC_OID_NETSCAPE_SMIME_KEA:
+ {
+ SECStatus err;
+ CK_MECHANISM_TYPE bulkType;
+ PK11SymKey *tek;
+ SECKEYPublicKey *senderPubKey;
+ SEC_PKCS7SMIMEKEAParameters keaParams;
+
+ (void) memset(&keaParams, 0, sizeof(keaParams));
+
+ /* Decode the KEA algorithm parameters. */
+ err = SEC_ASN1DecodeItem(NULL,
+ &keaParams,
+ SEC_SMIMEKEAParamTemplateAllParams,
+ &(ri->keyEncAlg.parameters));
+ if (err != SECSuccess)
+ {
+ p7dcx->error = err;
+ PORT_SetError(0);
+ goto no_key_found;
+ }
+
+
+ /* We just got key data, no key structure. So, we
+ create one. */
+ senderPubKey =
+ PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
+ keaParams.originatorKEAKey.len);
+ if (senderPubKey == NULL)
+ {
+ p7dcx->error = PORT_GetError();
+ PORT_SetError(0);
+ goto no_key_found;
+ }
+
+ /* Generate the TEK (token exchange key) which we use
+ to unwrap the bulk encryption key. */
+ tek = PK11_PubDerive(privkey, senderPubKey,
+ PR_FALSE,
+ &keaParams.originatorRA,
+ NULL,
+ CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
+ CKA_WRAP, 0, p7dcx->pwfn_arg);
+ SECKEY_DestroyPublicKey(senderPubKey);
+
+ if (tek == NULL)
+ {
+ p7dcx->error = PORT_GetError();
+ PORT_SetError(0);
+ goto no_key_found;
+ }
+
+ /* Now that we have the TEK, unwrap the bulk key
+ with which to decrypt the message. We have to
+ do one of two different things depending on
+ whether Skipjack was used for bulk encryption
+ of the message. */
+ bulkType = PK11_AlgtagToMechanism (bulkalgtag);
+ switch(bulkType)
+ {
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ /* Skipjack is being used as the bulk encryption algorithm.*/
+ /* Unwrap the bulk key. */
+ bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP,
+ NULL, &ri->encKey,
+ CKM_SKIPJACK_CBC64,
+ CKA_DECRYPT, 0);
+ break;
+ default:
+ /* Skipjack was not used for bulk encryption of this
+ message. Use Skipjack CBC64, with the nonSkipjackIV
+ part of the KEA key parameters, to decrypt
+ the bulk key. If we got a parameter indicating that the
+ bulk key size is different than the encrypted key size,
+ pass in the real key size. */
+
+ /* Check for specified bulk key length (unspecified implies
+ that the bulk key length is the same as encrypted length) */
+ if (keaParams.bulkKeySize.len > 0)
+ {
+ p7dcx->error = SEC_ASN1DecodeItem(NULL, &bulkLength,
+ SEC_IntegerTemplate,
+ &keaParams.bulkKeySize);
+ }
+
+ if (p7dcx->error != SECSuccess)
+ goto no_key_found;
+
+ bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64,
+ &keaParams.nonSkipjackIV,
+ &ri->encKey,
+ bulkType,
+ CKA_DECRYPT, bulkLength);
+ }
+
+
+ if (bulkkey == NULL)
+ {
+ p7dcx->error = PORT_GetError();
+ PORT_SetError(0);
+ goto no_key_found;
+ }
+ break;
+ }
+ default:
+ p7dcx->error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto no_key_found;
+ }
+
+ return bulkkey;
+
+no_key_found:
+ if (privkey != NULL)
+ SECKEY_DestroyPrivateKey (privkey);
+
+ return NULL;
+}
+
+/*
+ * XXX The following comment is old -- the function used to only handle
+ * EnvelopedData or SignedAndEnvelopedData but now handles EncryptedData
+ * as well (and it had all of the code of the helper function above
+ * built into it), though the comment was left as is. Fix it...
+ *
+ * We are just about to decode the content of an EnvelopedData.
+ * Set up a decryption context so we can decrypt as we go.
+ * Presumably we are one of the recipients listed in "recipientinfos".
+ * (XXX And if we are not, or if we have trouble, what should we do?
+ * It would be nice to let the decoding still work. Maybe it should
+ * be an error if there is a content callback, but not an error otherwise?)
+ * The encryption key and related information can be found in "enccinfo".
+ */
+static SECStatus
+sec_pkcs7_decoder_start_decrypt (SEC_PKCS7DecoderContext *p7dcx, int depth,
+ SEC_PKCS7RecipientInfo **recipientinfos,
+ SEC_PKCS7EncryptedContentInfo *enccinfo,
+ PK11SymKey **copy_key_for_signature)
+{
+ PK11SymKey *bulkkey = NULL;
+ sec_PKCS7CipherObject *decryptobj;
+
+ /*
+ * If a callback is supplied to retrieve the encryption key,
+ * for instance, for Encrypted Content infos, then retrieve
+ * the bulkkey from the callback. Otherwise, assume that
+ * we are processing Enveloped or SignedAndEnveloped data
+ * content infos.
+ *
+ * XXX Put an assert here?
+ */
+ if (SEC_PKCS7ContentType(p7dcx->cinfo) == SEC_OID_PKCS7_ENCRYPTED_DATA) {
+ if (p7dcx->dkcb != NULL) {
+ bulkkey = (*p7dcx->dkcb)(p7dcx->dkcb_arg,
+ &(enccinfo->contentEncAlg));
+ }
+ enccinfo->keysize = 0;
+ } else {
+ bulkkey = sec_pkcs7_decoder_get_recipient_key (p7dcx, recipientinfos,
+ enccinfo);
+ if (bulkkey == NULL) goto no_decryption;
+ enccinfo->keysize = PK11_GetKeyStrength(bulkkey,
+ &(enccinfo->contentEncAlg));
+
+ }
+
+ /*
+ * XXX I think following should set error in p7dcx and clear set error
+ * (as used to be done here, or as is done in get_receipient_key above.
+ */
+ if(bulkkey == NULL) {
+ goto no_decryption;
+ }
+
+ /*
+ * We want to make sure decryption is allowed. This is done via
+ * a callback specified in SEC_PKCS7DecoderStart().
+ */
+ if (p7dcx->decrypt_allowed_cb) {
+ if ((*p7dcx->decrypt_allowed_cb) (&(enccinfo->contentEncAlg),
+ bulkkey) == PR_FALSE) {
+ p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
+ goto no_decryption;
+ }
+ } else {
+ p7dcx->error = SEC_ERROR_DECRYPTION_DISALLOWED;
+ goto no_decryption;
+ }
+
+ /*
+ * When decrypting a signedAndEnvelopedData, the signature also has
+ * to be decrypted with the bulk encryption key; to avoid having to
+ * get it all over again later (and do another potentially expensive
+ * RSA operation), copy it for later signature verification to use.
+ */
+ if (copy_key_for_signature != NULL)
+ *copy_key_for_signature = PK11_ReferenceSymKey (bulkkey);
+
+ /*
+ * Now we have the bulk encryption key (in bulkkey) and the
+ * the algorithm (in enccinfo->contentEncAlg). Using those,
+ * create a decryption context.
+ */
+ decryptobj = sec_PKCS7CreateDecryptObject (bulkkey,
+ &(enccinfo->contentEncAlg));
+
+ /*
+ * For PKCS5 Encryption Algorithms, the bulkkey is actually a different
+ * structure. Therefore, we need to set the bulkkey to the actual key
+ * prior to freeing it.
+ */
+ if ( SEC_PKCS5IsAlgorithmPBEAlg(&(enccinfo->contentEncAlg)) && bulkkey ) {
+ SEC_PKCS5KeyAndPassword *keyPwd = (SEC_PKCS5KeyAndPassword *)bulkkey;
+ bulkkey = keyPwd->key;
+ }
+
+ /*
+ * We are done with (this) bulkkey now.
+ */
+ PK11_FreeSymKey (bulkkey);
+
+ if (decryptobj == NULL) {
+ p7dcx->error = PORT_GetError();
+ PORT_SetError(0);
+ goto no_decryption;
+ }
+
+ SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
+ sec_pkcs7_decoder_filter,
+ p7dcx,
+ (PRBool)(p7dcx->cb != NULL));
+
+ p7dcx->worker.depth = depth;
+ p7dcx->worker.decryptobj = decryptobj;
+
+ return SECSuccess;
+
+no_decryption:
+ /*
+ * For some reason (error set already, if appropriate), we cannot
+ * decrypt the content. I am not sure what exactly is the right
+ * thing to do here; in some cases we want to just stop, and in
+ * others we want to let the decoding finish even though we cannot
+ * decrypt the content. My current thinking is that if the caller
+ * set up a content callback, then they are really interested in
+ * getting (decrypted) content, and if they cannot they will want
+ * to know about it. However, if no callback was specified, then
+ * maybe it is not important that the decryption failed.
+ */
+ if (p7dcx->cb != NULL)
+ return SECFailure;
+ else
+ return SECSuccess; /* Let the decoding continue. */
+}
+
+
+static SECStatus
+sec_pkcs7_decoder_finish_decrypt (SEC_PKCS7DecoderContext *p7dcx,
+ PRArenaPool *poolp,
+ SEC_PKCS7EncryptedContentInfo *enccinfo)
+{
+ struct sec_pkcs7_decoder_worker *worker;
+
+ /*
+ * XXX Handling nested contents would mean that there is a chain
+ * of workers -- one per each level of content. The following
+ * would want to find the last worker in the chain.
+ */
+ worker = &(p7dcx->worker);
+
+ /*
+ * If no decryption context, then we have nothing to do.
+ */
+ if (worker->decryptobj == NULL)
+ return SECSuccess;
+
+ /*
+ * No matter what happens after this, we want to stop filtering.
+ * XXX If we handle nested contents, we only want to stop filtering
+ * if we are finishing off the *last* worker.
+ */
+ SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
+
+ /*
+ * Handle the last block.
+ */
+ sec_pkcs7_decoder_work_data (p7dcx, worker, NULL, 0, PR_TRUE);
+
+ /*
+ * All done, destroy it.
+ */
+ sec_PKCS7DestroyDecryptObject (worker->decryptobj);
+
+ return SECSuccess;
+}
+
+
+static void
+sec_pkcs7_decoder_notify (void *arg, PRBool before, void *dest, int depth)
+{
+ SEC_PKCS7DecoderContext *p7dcx;
+ SEC_PKCS7ContentInfo *cinfo;
+ SEC_PKCS7SignedData *sigd;
+ SEC_PKCS7EnvelopedData *envd;
+ SEC_PKCS7SignedAndEnvelopedData *saed;
+ SEC_PKCS7EncryptedData *encd;
+ SEC_PKCS7DigestedData *digd;
+ PRBool after;
+ SECStatus rv;
+
+ /*
+ * Just to make the code easier to read, create an "after" variable
+ * that is equivalent to "not before".
+ * (This used to be just the statement "after = !before", but that
+ * causes a warning on the mac; to avoid that, we do it the long way.)
+ */
+ if (before)
+ after = PR_FALSE;
+ else
+ after = PR_TRUE;
+
+ p7dcx = (SEC_PKCS7DecoderContext*)arg;
+ cinfo = p7dcx->cinfo;
+
+ if (cinfo->contentTypeTag == NULL) {
+ if (after && dest == &(cinfo->contentType))
+ cinfo->contentTypeTag = SECOID_FindOID(&(cinfo->contentType));
+ return;
+ }
+
+ switch (cinfo->contentTypeTag->offset) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ sigd = cinfo->content.signedData;
+ if (sigd == NULL)
+ break;
+
+ if (sigd->contentInfo.contentTypeTag == NULL) {
+ if (after && dest == &(sigd->contentInfo.contentType))
+ sigd->contentInfo.contentTypeTag =
+ SECOID_FindOID(&(sigd->contentInfo.contentType));
+ break;
+ }
+
+ /*
+ * We only set up a filtering digest if the content is
+ * plain DATA; anything else needs more work because a
+ * second pass is required to produce a DER encoding from
+ * an input that can be BER encoded. (This is a requirement
+ * of PKCS7 that is unfortunate, but there you have it.)
+ *
+ * XXX Also, since we stop here if this is not DATA, the
+ * inner content is not getting processed at all. Someday
+ * we may want to fix that.
+ */
+ if (sigd->contentInfo.contentTypeTag->offset != SEC_OID_PKCS7_DATA) {
+ /* XXX Set an error in p7dcx->error */
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ break;
+ }
+
+ /*
+ * Just before the content, we want to set up a digest context
+ * for each digest algorithm listed, and start a filter which
+ * will run all of the contents bytes through that digest.
+ */
+ if (before && dest == &(sigd->contentInfo.content)) {
+ rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
+ sigd->digestAlgorithms);
+ if (rv != SECSuccess)
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+ break;
+ }
+
+ /*
+ * XXX To handle nested types, here is where we would want
+ * to check for inner boundaries that need handling.
+ */
+
+ /*
+ * Are we done?
+ */
+ if (after && dest == &(sigd->contentInfo.content)) {
+ /*
+ * Close out the digest contexts. We ignore any error
+ * because we are stopping anyway; the error status left
+ * behind in p7dcx will be seen by outer functions.
+ */
+ (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
+ &(sigd->digests));
+
+ /*
+ * XXX To handle nested contents, we would need to remove
+ * the worker from the chain (and free it).
+ */
+
+ /*
+ * Stop notify.
+ */
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ }
+ break;
+
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ envd = cinfo->content.envelopedData;
+ if (envd == NULL)
+ break;
+
+ if (envd->encContentInfo.contentTypeTag == NULL) {
+ if (after && dest == &(envd->encContentInfo.contentType))
+ envd->encContentInfo.contentTypeTag =
+ SECOID_FindOID(&(envd->encContentInfo.contentType));
+ break;
+ }
+
+ /*
+ * Just before the content, we want to set up a decryption
+ * context, and start a filter which will run all of the
+ * contents bytes through it to determine the plain content.
+ */
+ if (before && dest == &(envd->encContentInfo.encContent)) {
+ rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
+ envd->recipientInfos,
+ &(envd->encContentInfo),
+ NULL);
+ if (rv != SECSuccess)
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+ break;
+ }
+
+ /*
+ * Are we done?
+ */
+ if (after && dest == &(envd->encContentInfo.encContent)) {
+ /*
+ * Close out the decryption context. We ignore any error
+ * because we are stopping anyway; the error status left
+ * behind in p7dcx will be seen by outer functions.
+ */
+ (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
+ &(envd->encContentInfo));
+
+ /*
+ * XXX To handle nested contents, we would need to remove
+ * the worker from the chain (and free it).
+ */
+
+ /*
+ * Stop notify.
+ */
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ }
+ break;
+
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ saed = cinfo->content.signedAndEnvelopedData;
+ if (saed == NULL)
+ break;
+
+ if (saed->encContentInfo.contentTypeTag == NULL) {
+ if (after && dest == &(saed->encContentInfo.contentType))
+ saed->encContentInfo.contentTypeTag =
+ SECOID_FindOID(&(saed->encContentInfo.contentType));
+ break;
+ }
+
+ /*
+ * Just before the content, we want to set up a decryption
+ * context *and* digest contexts, and start a filter which
+ * will run all of the contents bytes through both.
+ */
+ if (before && dest == &(saed->encContentInfo.encContent)) {
+ rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth,
+ saed->recipientInfos,
+ &(saed->encContentInfo),
+ &(saed->sigKey));
+ if (rv == SECSuccess)
+ rv = sec_pkcs7_decoder_start_digests (p7dcx, depth,
+ saed->digestAlgorithms);
+ if (rv != SECSuccess)
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+ break;
+ }
+
+ /*
+ * Are we done?
+ */
+ if (after && dest == &(saed->encContentInfo.encContent)) {
+ /*
+ * Close out the decryption and digests contexts.
+ * We ignore any errors because we are stopping anyway;
+ * the error status left behind in p7dcx will be seen by
+ * outer functions.
+ *
+ * Note that the decrypt stuff must be called first;
+ * it may have a last buffer to do which in turn has
+ * to be added to the digest.
+ */
+ (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
+ &(saed->encContentInfo));
+ (void) sec_pkcs7_decoder_finish_digests (p7dcx, cinfo->poolp,
+ &(saed->digests));
+
+ /*
+ * XXX To handle nested contents, we would need to remove
+ * the worker from the chain (and free it).
+ */
+
+ /*
+ * Stop notify.
+ */
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ }
+ break;
+
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ digd = cinfo->content.digestedData;
+
+ /*
+ * XXX Want to do the digest or not? Maybe future enhancement...
+ */
+ if (before && dest == &(digd->contentInfo.content.data)) {
+ SEC_ASN1DecoderSetFilterProc (p7dcx->dcx, sec_pkcs7_decoder_filter,
+ p7dcx,
+ (PRBool)(p7dcx->cb != NULL));
+ break;
+ }
+
+ /*
+ * Are we done?
+ */
+ if (after && dest == &(digd->contentInfo.content.data)) {
+ SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
+ }
+ break;
+
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ encd = cinfo->content.encryptedData;
+
+ /*
+ * XXX If the decryption key callback is set, we want to start
+ * the decryption. If the callback is not set, we will treat the
+ * content as plain data, since we do not have the key.
+ *
+ * Is this the proper thing to do?
+ */
+ if (before && dest == &(encd->encContentInfo.encContent)) {
+ /*
+ * Start the encryption process if the decryption key callback
+ * is present. Otherwise, treat the content like plain data.
+ */
+ rv = SECSuccess;
+ if (p7dcx->dkcb != NULL) {
+ rv = sec_pkcs7_decoder_start_decrypt (p7dcx, depth, NULL,
+ &(encd->encContentInfo),
+ NULL);
+ }
+
+ if (rv != SECSuccess)
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+
+ break;
+ }
+
+ /*
+ * Are we done?
+ */
+ if (after && dest == &(encd->encContentInfo.encContent)) {
+ /*
+ * Close out the decryption context. We ignore any error
+ * because we are stopping anyway; the error status left
+ * behind in p7dcx will be seen by outer functions.
+ */
+ (void) sec_pkcs7_decoder_finish_decrypt (p7dcx, cinfo->poolp,
+ &(encd->encContentInfo));
+
+ /*
+ * Stop notify.
+ */
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ }
+ break;
+
+ case SEC_OID_PKCS7_DATA:
+ /*
+ * If a output callback has been specified, we want to set the filter
+ * to call the callback. This is taken care of in
+ * sec_pkcs7_decoder_start_decrypt() or
+ * sec_pkcs7_decoder_start_digests() for the other content types.
+ */
+
+ if (before && dest == &(cinfo->content.data)) {
+
+ /*
+ * Set the filter proc up.
+ */
+ SEC_ASN1DecoderSetFilterProc (p7dcx->dcx,
+ sec_pkcs7_decoder_filter,
+ p7dcx,
+ (PRBool)(p7dcx->cb != NULL));
+ break;
+ }
+
+ if (after && dest == &(cinfo->content.data)) {
+ /*
+ * Time to clean up after ourself, stop the Notify and Filter
+ * procedures.
+ */
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ SEC_ASN1DecoderClearFilterProc (p7dcx->dcx);
+ }
+ break;
+
+ default:
+ SEC_ASN1DecoderClearNotifyProc (p7dcx->dcx);
+ break;
+ }
+}
+
+
+SEC_PKCS7DecoderContext *
+SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg,
+ SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
+ void *decrypt_key_cb_arg,
+ SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
+{
+ SEC_PKCS7DecoderContext *p7dcx;
+ SEC_ASN1DecoderContext *dcx;
+ SEC_PKCS7ContentInfo *cinfo;
+ PRArenaPool *poolp;
+
+ poolp = PORT_NewArena (1024); /* XXX what is right value? */
+ if (poolp == NULL)
+ return NULL;
+
+ cinfo = (SEC_PKCS7ContentInfo*)PORT_ArenaZAlloc (poolp, sizeof(*cinfo));
+ if (cinfo == NULL) {
+ PORT_FreeArena (poolp, PR_FALSE);
+ return NULL;
+ }
+
+ cinfo->poolp = poolp;
+ cinfo->pwfn = pwfn;
+ cinfo->pwfn_arg = pwfn_arg;
+ cinfo->created = PR_FALSE;
+ cinfo->refCount = 1;
+
+ p7dcx =
+ (SEC_PKCS7DecoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7DecoderContext));
+ if (p7dcx == NULL) {
+ PORT_FreeArena (poolp, PR_FALSE);
+ return NULL;
+ }
+
+ p7dcx->tmp_poolp = PORT_NewArena (1024); /* XXX what is right value? */
+ if (p7dcx->tmp_poolp == NULL) {
+ PORT_Free (p7dcx);
+ PORT_FreeArena (poolp, PR_FALSE);
+ return NULL;
+ }
+
+ dcx = SEC_ASN1DecoderStart (poolp, cinfo, sec_PKCS7ContentInfoTemplate);
+ if (dcx == NULL) {
+ PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);
+ PORT_Free (p7dcx);
+ PORT_FreeArena (poolp, PR_FALSE);
+ return NULL;
+ }
+
+ SEC_ASN1DecoderSetNotifyProc (dcx, sec_pkcs7_decoder_notify, p7dcx);
+
+ p7dcx->dcx = dcx;
+ p7dcx->cinfo = cinfo;
+ p7dcx->cb = cb;
+ p7dcx->cb_arg = cb_arg;
+ p7dcx->pwfn = pwfn;
+ p7dcx->pwfn_arg = pwfn_arg;
+ p7dcx->dkcb = decrypt_key_cb;
+ p7dcx->dkcb_arg = decrypt_key_cb_arg;
+ p7dcx->decrypt_allowed_cb = decrypt_allowed_cb;
+
+ return p7dcx;
+}
+
+
+/*
+ * Do the next chunk of PKCS7 decoding. If there is a problem, set
+ * an error and return a failure status. Note that in the case of
+ * an error, this routine is still prepared to be called again and
+ * again in case that is the easiest route for our caller to take.
+ * We simply detect it and do not do anything except keep setting
+ * that error in case our caller has not noticed it yet...
+ */
+SECStatus
+SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,
+ const char *buf, unsigned long len)
+{
+ if (p7dcx->cinfo != NULL && p7dcx->dcx != NULL) {
+ PORT_Assert (p7dcx->error == 0);
+ if (p7dcx->error == 0) {
+ if (SEC_ASN1DecoderUpdate (p7dcx->dcx, buf, len) != SECSuccess) {
+ p7dcx->error = PORT_GetError();
+ PORT_Assert (p7dcx->error);
+ if (p7dcx->error == 0)
+ p7dcx->error = -1;
+ }
+ }
+ }
+
+ if (p7dcx->error) {
+ if (p7dcx->dcx != NULL) {
+ (void) SEC_ASN1DecoderFinish (p7dcx->dcx);
+ p7dcx->dcx = NULL;
+ }
+ if (p7dcx->cinfo != NULL) {
+ SEC_PKCS7DestroyContentInfo (p7dcx->cinfo);
+ p7dcx->cinfo = NULL;
+ }
+ PORT_SetError (p7dcx->error);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+
+SEC_PKCS7ContentInfo *
+SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+
+ cinfo = p7dcx->cinfo;
+ if (p7dcx->dcx != NULL) {
+ if (SEC_ASN1DecoderFinish (p7dcx->dcx) != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ cinfo = NULL;
+ }
+ }
+ PORT_FreeArena (p7dcx->tmp_poolp, PR_FALSE);
+ PORT_Free (p7dcx);
+ return cinfo;
+}
+
+
+SEC_PKCS7ContentInfo *
+SEC_PKCS7DecodeItem(SECItem *p7item,
+ SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg,
+ SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
+ void *decrypt_key_cb_arg,
+ SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb)
+{
+ SEC_PKCS7DecoderContext *p7dcx;
+
+ p7dcx = SEC_PKCS7DecoderStart(cb, cb_arg, pwfn, pwfn_arg, decrypt_key_cb,
+ decrypt_key_cb_arg, decrypt_allowed_cb);
+ (void) SEC_PKCS7DecoderUpdate(p7dcx, (char *) p7item->data, p7item->len);
+ return SEC_PKCS7DecoderFinish(p7dcx);
+}
+
+
+/*
+ * If the thing contains any certs or crls return true; false otherwise.
+ */
+PRBool
+SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+ SECItem **certs;
+ CERTSignedCrl **crls;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ return PR_FALSE;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ certs = cinfo->content.signedData->rawCerts;
+ crls = cinfo->content.signedData->crls;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ certs = cinfo->content.signedAndEnvelopedData->rawCerts;
+ crls = cinfo->content.signedAndEnvelopedData->crls;
+ break;
+ }
+
+ /*
+ * I know this could be collapsed, but I was in a mood to be explicit.
+ */
+ if (certs != NULL && certs[0] != NULL)
+ return PR_TRUE;
+ else if (crls != NULL && crls[0] != NULL)
+ return PR_TRUE;
+ else
+ return PR_FALSE;
+}
+
+/* return the content length...could use GetContent, however we
+ * need the encrypted content length
+ */
+PRBool
+SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen)
+{
+ SECItem *item = NULL;
+
+ if(cinfo == NULL) {
+ return PR_TRUE;
+ }
+
+ switch(SEC_PKCS7ContentType(cinfo))
+ {
+ case SEC_OID_PKCS7_DATA:
+ item = cinfo->content.data;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ item = &cinfo->content.encryptedData->encContentInfo.encContent;
+ break;
+ default:
+ /* add other types */
+ return PR_FALSE;
+ }
+
+ if(!item) {
+ return PR_TRUE;
+ } else if(item->len <= minLen) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+
+PRBool
+SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ return PR_FALSE;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ return PR_TRUE;
+ }
+}
+
+
+/*
+ * If the PKCS7 content has a signature (not just *could* have a signature)
+ * return true; false otherwise. This can/should be called before calling
+ * VerifySignature, which will always indicate failure if no signature is
+ * present, but that does not mean there even was a signature!
+ * Note that the content itself can be empty (detached content was sent
+ * another way); it is the presence of the signature that matters.
+ */
+PRBool
+SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo)
+{
+ SECOidTag kind;
+ SEC_PKCS7SignerInfo **signerinfos;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ return PR_FALSE;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ signerinfos = cinfo->content.signedData->signerInfos;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ signerinfos = cinfo->content.signedAndEnvelopedData->signerInfos;
+ break;
+ }
+
+ /*
+ * I know this could be collapsed; but I kind of think it will get
+ * more complicated before I am finished, so...
+ */
+ if (signerinfos != NULL && signerinfos[0] != NULL)
+ return PR_TRUE;
+ else
+ return PR_FALSE;
+}
+
+
+/*
+ * SEC_PKCS7ContentVerifySignature
+ * Look at a PKCS7 contentInfo and check if the signature is good.
+ * The digest was either calculated earlier (and is stored in the
+ * contentInfo itself) or is passed in via "detached_digest".
+ *
+ * The verification checks that the signing cert is valid and trusted
+ * for the purpose specified by "certusage".
+ *
+ * In addition, if "keepcerts" is true, add any new certificates found
+ * into our local database.
+ *
+ * XXX Each place which returns PR_FALSE should be sure to have a good
+ * error set for inspection by the caller. Alternatively, we could create
+ * an enumeration of success and each type of failure and return that
+ * instead of a boolean. For now, the default in a bad situation is to
+ * set the error to SEC_ERROR_PKCS7_BAD_SIGNATURE. But this should be
+ * reviewed; better (more specific) errors should be possible (to distinguish
+ * a signature failure from a badly-formed pkcs7 signedData, for example).
+ * Some of the errors should probably just be SEC_ERROR_BAD_SIGNATURE,
+ * but that has a less helpful error string associated with it right now;
+ * if/when that changes, review and change these as needed.
+ *
+ * XXX This is broken wrt signedAndEnvelopedData. In that case, the
+ * message digest is doubly encrypted -- first encrypted with the signer
+ * private key but then again encrypted with the bulk encryption key used
+ * to encrypt the content. So before we can pass the digest to VerifyDigest,
+ * we need to decrypt it with the bulk encryption key. Also, in this case,
+ * there should be NO authenticatedAttributes (signerinfo->authAttr should
+ * be NULL).
+ */
+static PRBool
+sec_pkcs7_verify_signature(SEC_PKCS7ContentInfo *cinfo,
+ SECCertUsage certusage,
+ SECItem *detached_digest,
+ HASH_HashType digest_type,
+ PRBool keepcerts)
+{
+ SECAlgorithmID **digestalgs, *bulkid;
+ SECItem *digest;
+ SECItem **digests;
+ SECItem **rawcerts;
+ CERTSignedCrl **crls;
+ SEC_PKCS7SignerInfo **signerinfos, *signerinfo;
+ CERTCertificate *cert, **certs;
+ PRBool goodsig;
+ CERTCertDBHandle local_certdb, *certdb, *defaultdb;
+ SECOidData *algiddata;
+ int i, certcount;
+ SECKEYPublicKey *publickey;
+ SECItem *content_type;
+ PK11SymKey *sigkey;
+ SECItem *utc_stime;
+ int64 stime;
+ SECStatus rv;
+
+ /*
+ * Everything needed in order to "goto done" safely.
+ */
+ goodsig = PR_FALSE;
+ certcount = 0;
+ cert = NULL;
+ certs = NULL;
+ certdb = NULL;
+ defaultdb = CERT_GetDefaultCertDB();
+ publickey = NULL;
+
+ if (! SEC_PKCS7ContentIsSigned(cinfo)) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ PORT_Assert (cinfo->contentTypeTag != NULL);
+
+ switch (cinfo->contentTypeTag->offset) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ /* Could only get here if SEC_PKCS7ContentIsSigned is broken. */
+ PORT_Assert (0);
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ digestalgs = sdp->digestAlgorithms;
+ digests = sdp->digests;
+ rawcerts = sdp->rawCerts;
+ crls = sdp->crls;
+ signerinfos = sdp->signerInfos;
+ content_type = &(sdp->contentInfo.contentType);
+ sigkey = NULL;
+ bulkid = NULL;
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ digestalgs = saedp->digestAlgorithms;
+ digests = saedp->digests;
+ rawcerts = saedp->rawCerts;
+ crls = saedp->crls;
+ signerinfos = saedp->signerInfos;
+ content_type = &(saedp->encContentInfo.contentType);
+ sigkey = saedp->sigKey;
+ bulkid = &(saedp->encContentInfo.contentEncAlg);
+ }
+ break;
+ }
+
+ if ((signerinfos == NULL) || (signerinfos[0] == NULL)) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ /*
+ * XXX Need to handle multiple signatures; checking them is easy,
+ * but what should be the semantics here (like, return value)?
+ */
+ if (signerinfos[1] != NULL) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ signerinfo = signerinfos[0];
+
+ /*
+ * XXX I would like to just pass the issuerAndSN, along with the rawcerts
+ * and crls, to some function that did all of this certificate stuff
+ * (open/close the database if necessary, verifying the certs, etc.)
+ * and gave me back a cert pointer if all was good.
+ */
+ certdb = defaultdb;
+ if (certdb == NULL) {
+ if (CERT_OpenCertDBFilename (&local_certdb, NULL,
+ (PRBool)!keepcerts) != SECSuccess)
+ goto done;
+ certdb = &local_certdb;
+ }
+
+ certcount = 0;
+ if (rawcerts != NULL) {
+ for (; rawcerts[certcount] != NULL; certcount++) {
+ /* just counting */
+ }
+ }
+
+ /*
+ * Note that the result of this is that each cert in "certs"
+ * needs to be destroyed.
+ */
+ rv = CERT_ImportCerts(certdb, certusage, certcount, rawcerts, &certs,
+ keepcerts, PR_FALSE, NULL);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ /*
+ * This cert will also need to be freed, but since we save it
+ * in signerinfo for later, we do not want to destroy it when
+ * we leave this function -- we let the clean-up of the entire
+ * cinfo structure later do the destroy of this cert.
+ */
+ cert = CERT_FindCertByIssuerAndSN(certdb, signerinfo->issuerAndSN);
+ if (cert == NULL) {
+ goto done;
+ }
+
+ signerinfo->cert = cert;
+
+ /*
+ * Get and convert the signing time; if available, it will be used
+ * both on the cert verification and for importing the sender
+ * email profile.
+ */
+ utc_stime = SEC_PKCS7GetSigningTime (cinfo);
+ if (utc_stime != NULL) {
+ if (DER_UTCTimeToTime (&stime, utc_stime) != SECSuccess)
+ utc_stime = NULL; /* conversion failed, so pretend none */
+ }
+
+ /*
+ * XXX This uses the signing time, if available. Additionally, we
+ * might want to, if there is no signing time, get the message time
+ * from the mail header itself, and use that. That would require
+ * a change to our interface though, and for S/MIME callers to pass
+ * in a time (and for non-S/MIME callers to pass in nothing, or
+ * maybe make them pass in the current time, always?).
+ */
+ if (CERT_VerifyCert (certdb, cert, PR_TRUE, certusage,
+ utc_stime != NULL ? stime : PR_Now(),
+ cinfo->pwfn_arg, NULL) != SECSuccess)
+ {
+ /*
+ * XXX Give the user an option to check the signature anyway?
+ * If we want to do this, need to give a way to leave and display
+ * some dialog and get the answer and come back through (or do
+ * the rest of what we do below elsewhere, maybe by putting it
+ * in a function that we call below and could call from a dialog
+ * finish handler).
+ */
+ goto savecert;
+ }
+
+ publickey = CERT_ExtractPublicKey (cert);
+ if (publickey == NULL)
+ goto done;
+
+ /*
+ * XXX No! If digests is empty, see if we can create it now by
+ * digesting the contents. This is necessary if we want to allow
+ * somebody to do a simple decode (without filtering, etc.) and
+ * then later call us here to do the verification.
+ * OR, we can just specify that the interface to this routine
+ * *requires* that the digest(s) be done before calling and either
+ * stashed in the struct itself or passed in explicitly (as would
+ * be done for detached contents).
+ */
+ if ((digests == NULL || digests[0] == NULL)
+ && (detached_digest == NULL || detached_digest->data == NULL))
+ goto done;
+
+ /*
+ * Find and confirm digest algorithm.
+ */
+ algiddata = SECOID_FindOID (&(signerinfo->digestAlg.algorithm));
+
+ if (detached_digest != NULL) {
+ switch (digest_type) {
+ default:
+ case HASH_AlgNULL:
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ case HASH_AlgMD2:
+ PORT_Assert (detached_digest->len == MD2_LENGTH);
+ if (algiddata->offset != SEC_OID_MD2) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+ break;
+ case HASH_AlgMD5:
+ PORT_Assert (detached_digest->len == MD5_LENGTH);
+ if (algiddata->offset != SEC_OID_MD5) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+ break;
+ case HASH_AlgSHA1:
+ PORT_Assert (detached_digest->len == SHA1_LENGTH);
+ if (algiddata->offset != SEC_OID_SHA1) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+ break;
+ }
+ digest = detached_digest;
+ } else {
+ PORT_Assert (digestalgs != NULL && digestalgs[0] != NULL);
+ if (digestalgs == NULL || digestalgs[0] == NULL) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ /*
+ * pick digest matching signerinfo->digestAlg from digests
+ */
+ if (algiddata == NULL) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+ for (i = 0; digestalgs[i] != NULL; i++) {
+ if (SECOID_FindOID (&(digestalgs[i]->algorithm)) == algiddata)
+ break;
+ }
+ if (digestalgs[i] == NULL) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ digest = digests[i];
+ }
+
+ /*
+ * XXX This may not be the right set of algorithms to check.
+ * I'd prefer to trust that just calling VFY_Verify{Data,Digest}
+ * would do the right thing (and set an error if it could not);
+ * then additional algorithms could be handled by that code
+ * and we would Just Work. So this check should just be removed,
+ * but not until the VFY code is better at setting errors.
+ */
+ algiddata = SECOID_FindOID (&(signerinfo->digestEncAlg.algorithm));
+ if (algiddata == NULL ||
+ ((algiddata->offset != SEC_OID_PKCS1_RSA_ENCRYPTION) &&
+ (algiddata->offset != SEC_OID_ANSIX9_DSA_SIGNATURE))) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ if (signerinfo->authAttr != NULL) {
+ SEC_PKCS7Attribute *attr;
+ SECItem *value;
+ SECItem encoded_attrs;
+
+ /*
+ * We have a sigkey only for signedAndEnvelopedData, which is
+ * not supposed to have any authenticated attributes.
+ */
+ if (sigkey != NULL) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ /*
+ * PKCS #7 says that if there are any authenticated attributes,
+ * then there must be one for content type which matches the
+ * content type of the content being signed, and there must
+ * be one for message digest which matches our message digest.
+ * So check these things first.
+ * XXX Might be nice to have a compare-attribute-value function
+ * which could collapse the following nicely.
+ */
+ attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+ SEC_OID_PKCS9_CONTENT_TYPE, PR_TRUE);
+ value = sec_PKCS7AttributeValue (attr);
+ if (value == NULL || value->len != content_type->len) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+ if (PORT_Memcmp (value->data, content_type->data, value->len) != 0) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+ SEC_OID_PKCS9_MESSAGE_DIGEST, PR_TRUE);
+ value = sec_PKCS7AttributeValue (attr);
+ if (value == NULL || value->len != digest->len) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+ if (PORT_Memcmp (value->data, digest->data, value->len) != 0) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ /*
+ * Okay, we met the constraints of the basic attributes.
+ * Now check the signature, which is based on a digest of
+ * the DER-encoded authenticated attributes. So, first we
+ * encode and then we digest/verify.
+ */
+ encoded_attrs.data = NULL;
+ encoded_attrs.len = 0;
+ if (sec_PKCS7EncodeAttributes (NULL, &encoded_attrs,
+ &(signerinfo->authAttr)) == NULL)
+ goto done;
+
+ if (encoded_attrs.data == NULL || encoded_attrs.len == 0) {
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ goodsig = (PRBool)(VFY_VerifyData (encoded_attrs.data,
+ encoded_attrs.len,
+ publickey, &(signerinfo->encDigest),
+ SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
+ cinfo->pwfn_arg) == SECSuccess);
+ PORT_Free (encoded_attrs.data);
+ } else {
+ SECItem *sig;
+ SECItem holder;
+ SECStatus rv;
+
+ /*
+ * No authenticated attributes.
+ * The signature is based on the plain message digest.
+ */
+
+ sig = &(signerinfo->encDigest);
+ if (sig->len == 0) { /* bad signature */
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ goto done;
+ }
+
+ if (sigkey != NULL) {
+ sec_PKCS7CipherObject *decryptobj;
+ unsigned int buflen;
+
+ /*
+ * For signedAndEnvelopedData, we first must decrypt the encrypted
+ * digest with the bulk encryption key. The result is the normal
+ * encrypted digest (aka the signature).
+ */
+ decryptobj = sec_PKCS7CreateDecryptObject (sigkey, bulkid);
+ if (decryptobj == NULL)
+ goto done;
+
+ buflen = sec_PKCS7DecryptLength (decryptobj, sig->len, PR_TRUE);
+ PORT_Assert (buflen);
+ if (buflen == 0) { /* something is wrong */
+ sec_PKCS7DestroyDecryptObject (decryptobj);
+ goto done;
+ }
+
+ holder.data = (unsigned char*)PORT_Alloc (buflen);
+ if (holder.data == NULL) {
+ sec_PKCS7DestroyDecryptObject (decryptobj);
+ goto done;
+ }
+
+ rv = sec_PKCS7Decrypt (decryptobj, holder.data, &holder.len, buflen,
+ sig->data, sig->len, PR_TRUE);
+ if (rv != SECSuccess) {
+ sec_PKCS7DestroyDecryptObject (decryptobj);
+ goto done;
+ }
+
+ sig = &holder;
+ }
+
+ goodsig = (PRBool)(VFY_VerifyDigest (digest, publickey, sig,
+ SECOID_GetAlgorithmTag(&(signerinfo->digestEncAlg)),
+ cinfo->pwfn_arg)
+ == SECSuccess);
+
+ if (sigkey != NULL) {
+ PORT_Assert (sig == &holder);
+ PORT_ZFree (holder.data, holder.len);
+ }
+ }
+
+ if (! goodsig) {
+ /*
+ * XXX Change the generic error into our specific one, because
+ * in that case we get a better explanation out of the Security
+ * Advisor. This is really a bug in our error strings (the
+ * "generic" error has a lousy/wrong message associated with it
+ * which assumes the signature verification was done for the
+ * purposes of checking the issuer signature on a certificate)
+ * but this is at least an easy workaround and/or in the
+ * Security Advisor, which specifically checks for the error
+ * SEC_ERROR_PKCS7_BAD_SIGNATURE and gives more explanation
+ * in that case but does not similarly check for
+ * SEC_ERROR_BAD_SIGNATURE. It probably should, but then would
+ * probably say the wrong thing in the case that it *was* the
+ * certificate signature check that failed during the cert
+ * verification done above. Our error handling is really a mess.
+ */
+ if (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE)
+ PORT_SetError (SEC_ERROR_PKCS7_BAD_SIGNATURE);
+ }
+
+savecert:
+ /*
+ * Only save the smime profile if we are checking an email message and
+ * the cert has an email address in it.
+ */
+ if ( ( cert->emailAddr != NULL ) &&
+ ( ( certusage == certUsageEmailSigner ) ||
+ ( certusage == certUsageEmailRecipient ) ) ) {
+ SECItem *profile = NULL;
+ int save_error;
+
+ /*
+ * Remember the current error set because we do not care about
+ * anything set by the functions we are about to call.
+ */
+ save_error = PORT_GetError();
+
+ if (goodsig && (signerinfo->authAttr != NULL)) {
+ /*
+ * If the signature is good, then we can save the S/MIME profile,
+ * if we have one.
+ */
+ SEC_PKCS7Attribute *attr;
+
+ attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+ SEC_OID_PKCS9_SMIME_CAPABILITIES,
+ PR_TRUE);
+ profile = sec_PKCS7AttributeValue (attr);
+ }
+
+ rv = CERT_SaveSMimeProfile (cert, profile, utc_stime);
+
+ /*
+ * Restore the saved error in case the calls above set a new
+ * one that we do not actually care about.
+ */
+ PORT_SetError (save_error);
+
+ /*
+ * XXX Failure is not indicated anywhere -- the signature
+ * verification itself is unaffected by whether or not the
+ * profile was successfully saved.
+ */
+ }
+
+
+done:
+
+ /*
+ * See comment above about why we do not want to destroy cert
+ * itself here.
+ */
+
+ if (certs != NULL)
+ CERT_DestroyCertArray (certs, certcount);
+
+ if (defaultdb == NULL && certdb != NULL)
+ CERT_ClosePermCertDB (certdb);
+
+ if (publickey != NULL)
+ SECKEY_DestroyPublicKey (publickey);
+
+ return goodsig;
+}
+
+/*
+ * SEC_PKCS7VerifySignature
+ * Look at a PKCS7 contentInfo and check if the signature is good.
+ * The verification checks that the signing cert is valid and trusted
+ * for the purpose specified by "certusage".
+ *
+ * In addition, if "keepcerts" is true, add any new certificates found
+ * into our local database.
+ */
+PRBool
+SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
+ SECCertUsage certusage,
+ PRBool keepcerts)
+{
+ return sec_pkcs7_verify_signature (cinfo, certusage,
+ NULL, HASH_AlgNULL, keepcerts);
+}
+
+/*
+ * SEC_PKCS7VerifyDetachedSignature
+ * Look at a PKCS7 contentInfo and check if the signature matches
+ * a passed-in digest (calculated, supposedly, from detached contents).
+ * The verification checks that the signing cert is valid and trusted
+ * for the purpose specified by "certusage".
+ *
+ * In addition, if "keepcerts" is true, add any new certificates found
+ * into our local database.
+ */
+PRBool
+SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
+ SECCertUsage certusage,
+ SECItem *detached_digest,
+ HASH_HashType digest_type,
+ PRBool keepcerts)
+{
+ return sec_pkcs7_verify_signature (cinfo, certusage,
+ detached_digest, digest_type,
+ keepcerts);
+}
+
+
+/*
+ * Return the asked-for portion of the name of the signer of a PKCS7
+ * signed object.
+ *
+ * Returns a pointer to allocated memory, which must be freed.
+ * A NULL return value is an error.
+ */
+
+#define sec_common_name 1
+#define sec_email_address 2
+
+static char *
+sec_pkcs7_get_signer_cert_info(SEC_PKCS7ContentInfo *cinfo, int selector)
+{
+ SECOidTag kind;
+ SEC_PKCS7SignerInfo **signerinfos;
+ CERTCertificate *signercert;
+ char *container;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ PORT_Assert (0);
+ return NULL;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ signerinfos = sdp->signerInfos;
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ signerinfos = saedp->signerInfos;
+ }
+ break;
+ }
+
+ if (signerinfos == NULL || signerinfos[0] == NULL)
+ return NULL;
+
+ signercert = signerinfos[0]->cert;
+
+ /*
+ * No cert there; see if we can find one by calling verify ourselves.
+ */
+ if (signercert == NULL) {
+ /*
+ * The cert usage does not matter in this case, because we do not
+ * actually care about the verification itself, but we have to pick
+ * some valid usage to pass in.
+ */
+ (void) sec_pkcs7_verify_signature (cinfo, certUsageEmailSigner,
+ NULL, HASH_AlgNULL, PR_FALSE);
+ signercert = signerinfos[0]->cert;
+ if (signercert == NULL)
+ return NULL;
+ }
+
+ switch (selector) {
+ case sec_common_name:
+ container = CERT_GetCommonName (&signercert->subject);
+ break;
+ case sec_email_address:
+ if(signercert->emailAddr) {
+ container = PORT_Strdup(signercert->emailAddr);
+ } else {
+ container = NULL;
+ }
+ break;
+ default:
+ PORT_Assert (0);
+ container = NULL;
+ break;
+ }
+
+ return container;
+}
+
+char *
+SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo)
+{
+ return sec_pkcs7_get_signer_cert_info(cinfo, sec_common_name);
+}
+
+char *
+SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo)
+{
+ return sec_pkcs7_get_signer_cert_info(cinfo, sec_email_address);
+}
+
+
+/*
+ * Return the signing time, in UTCTime format, of a PKCS7 contentInfo.
+ */
+SECItem *
+SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo)
+{
+ SEC_PKCS7SignerInfo **signerinfos;
+ SEC_PKCS7Attribute *attr;
+
+ if (SEC_PKCS7ContentType (cinfo) != SEC_OID_PKCS7_SIGNED_DATA)
+ return NULL;
+
+ signerinfos = cinfo->content.signedData->signerInfos;
+
+ /*
+ * No signature, or more than one, means no deal.
+ */
+ if (signerinfos == NULL || signerinfos[0] == NULL || signerinfos[1] != NULL)
+ return NULL;
+
+ attr = sec_PKCS7FindAttribute (signerinfos[0]->authAttr,
+ SEC_OID_PKCS9_SIGNING_TIME, PR_TRUE);
+ return sec_PKCS7AttributeValue (attr);
+}
diff --git a/security/nss/lib/pkcs7/p7encode.c b/security/nss/lib/pkcs7/p7encode.c
new file mode 100644
index 000000000..b45d2d916
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7encode.c
@@ -0,0 +1,1329 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * PKCS7 encoding.
+ *
+ * $Id$
+ */
+
+#include "p7local.h"
+
+#include "cert.h"
+#include "cryptohi.h"
+#include "keyhi.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "pk11func.h"
+#include "secerr.h"
+
+struct sec_pkcs7_encoder_output {
+ SEC_PKCS7EncoderOutputCallback outputfn;
+ void *outputarg;
+};
+
+struct SEC_PKCS7EncoderContextStr {
+ SEC_ASN1EncoderContext *ecx;
+ SEC_PKCS7ContentInfo *cinfo;
+ struct sec_pkcs7_encoder_output output;
+ sec_PKCS7CipherObject *encryptobj;
+ SECHashObject *digestobj;
+ void *digestcx;
+};
+
+
+/*
+ * The little output function that the ASN.1 encoder calls to hand
+ * us bytes which we in turn hand back to our caller (via the callback
+ * they gave us).
+ */
+static void
+sec_pkcs7_encoder_out(void *arg, const char *buf, unsigned long len,
+ int depth, SEC_ASN1EncodingPart data_kind)
+{
+ struct sec_pkcs7_encoder_output *output;
+
+ output = (struct sec_pkcs7_encoder_output*)arg;
+ output->outputfn (output->outputarg, buf, len);
+}
+
+static sec_PKCS7CipherObject *
+sec_pkcs7_encoder_start_encrypt (SEC_PKCS7ContentInfo *cinfo,
+ PK11SymKey *orig_bulkkey)
+{
+ SECOidTag kind;
+ sec_PKCS7CipherObject *encryptobj;
+ SEC_PKCS7RecipientInfo **recipientinfos, *ri;
+ SEC_PKCS7EncryptedContentInfo *enccinfo;
+ SEC_PKCS7SMIMEKEAParameters keaParams;
+ SECKEYPublicKey *publickey = NULL;
+ SECKEYPrivateKey *ourPrivKey = NULL;
+ PK11SymKey *bulkkey;
+ void *mark, *wincx;
+ int i;
+ PRArenaPool *arena = NULL;
+ unsigned char zero = 0;
+
+ /* Get the context in case we need it below. */
+ wincx = cinfo->pwfn_arg;
+
+ /* Clear keaParams, since cleanup code checks the lengths */
+ (void) memset(&keaParams, 0, sizeof(keaParams));
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ recipientinfos = NULL;
+ enccinfo = NULL;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ {
+ SEC_PKCS7EncryptedData *encdp;
+
+ /* To do EncryptedData we *must* be given a bulk key. */
+ PORT_Assert (orig_bulkkey != NULL);
+ if (orig_bulkkey == NULL) {
+ /* XXX error? */
+ return NULL;
+ }
+
+ encdp = cinfo->content.encryptedData;
+ recipientinfos = NULL;
+ enccinfo = &(encdp->encContentInfo);
+ }
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ {
+ SEC_PKCS7EnvelopedData *envdp;
+
+ envdp = cinfo->content.envelopedData;
+ recipientinfos = envdp->recipientInfos;
+ enccinfo = &(envdp->encContentInfo);
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ recipientinfos = saedp->recipientInfos;
+ enccinfo = &(saedp->encContentInfo);
+ }
+ break;
+ }
+
+ if (enccinfo == NULL)
+ return NULL;
+
+ bulkkey = orig_bulkkey;
+ if (bulkkey == NULL) {
+ CK_MECHANISM_TYPE type = PK11_AlgtagToMechanism(enccinfo->encalg);
+ PK11SlotInfo *slot;
+
+
+ slot = PK11_GetBestSlot(type,cinfo->pwfn_arg);
+ if (slot == NULL) {
+ return NULL;
+ }
+ bulkkey = PK11_KeyGen(slot,type,NULL, enccinfo->keysize/8,
+ cinfo->pwfn_arg);
+ PK11_FreeSlot(slot);
+ if (bulkkey == NULL) {
+ return NULL;
+ }
+ }
+
+ encryptobj = NULL;
+ mark = PORT_ArenaMark (cinfo->poolp);
+
+ /*
+ * Encrypt the bulk key with the public key of each recipient.
+ */
+ for (i = 0; recipientinfos && (ri = recipientinfos[i]) != NULL; i++) {
+ CERTCertificate *cert;
+ SECOidTag certalgtag, encalgtag;
+ SECStatus rv;
+ int data_len;
+ SECItem *params = NULL;
+
+ cert = ri->cert;
+ PORT_Assert (cert != NULL);
+ if (cert == NULL)
+ continue;
+
+ /*
+ * XXX Want an interface that takes a cert and some data and
+ * fills in an algorithmID and encrypts the data with the public
+ * key from the cert. Or, give me two interfaces -- one which
+ * gets the algorithm tag from a cert (I should not have to go
+ * down into the subjectPublicKeyInfo myself) and another which
+ * takes a public key and algorithm tag and data and encrypts
+ * the data. Or something like that. The point is that all
+ * of the following hardwired RSA and KEA stuff should be done
+ * elsewhere.
+ */
+
+ certalgtag=SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
+
+ switch (certalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ encalgtag = certalgtag;
+ publickey = CERT_ExtractPublicKey (cert);
+ if (publickey == NULL) goto loser;
+
+ data_len = SECKEY_PublicKeyStrength(publickey);
+ ri->encKey.data =
+ (unsigned char*)PORT_ArenaAlloc(cinfo->poolp ,data_len);
+ ri->encKey.len = data_len;
+ if (ri->encKey.data == NULL) goto loser;
+
+ rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(certalgtag),publickey,
+ bulkkey,&ri->encKey);
+
+ SECKEY_DestroyPublicKey(publickey);
+ publickey = NULL;
+ if (rv != SECSuccess) goto loser;
+ params = NULL; /* paranoia */
+ break;
+ /* ### mwelch -- KEA */
+ case SEC_OID_MISSI_KEA_DSS_OLD:
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_KEA:
+ {
+#define SMIME_FORTEZZA_RA_LENGTH 128
+#define SMIME_FORTEZZA_IV_LENGTH 24
+#define SMIME_FORTEZZA_MAX_KEY_SIZE 256
+ SECStatus err;
+ PK11SymKey *tek;
+ CERTCertificate *ourCert;
+ SECKEYPublicKey *ourPubKey;
+ SECKEATemplateSelector whichKEA;
+
+ /* We really want to show our KEA tag as the
+ key exchange algorithm tag. */
+ encalgtag = SEC_OID_NETSCAPE_SMIME_KEA;
+
+ /* Get the public key of the recipient. */
+ publickey = CERT_ExtractPublicKey(cert);
+ if (publickey == NULL) goto loser;
+
+ /* Find our own cert, and extract its keys. */
+ ourCert = PK11_FindBestKEAMatch(cert,wincx);
+ if (ourCert == NULL) goto loser;
+
+ arena = PORT_NewArena(1024);
+ if (arena == NULL) goto loser;
+
+ ourPubKey = CERT_ExtractPublicKey(ourCert);
+ if (ourPubKey == NULL)
+ {
+ CERT_DestroyCertificate(ourCert);
+ goto loser;
+ }
+
+ /* While we're here, copy the public key into the outgoing
+ * KEA parameters. */
+ SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey),
+ &(ourPubKey->u.fortezza.KEAKey));
+ SECKEY_DestroyPublicKey(ourPubKey);
+ ourPubKey = NULL;
+
+ /* Extract our private key in order to derive the
+ * KEA key. */
+ ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
+ CERT_DestroyCertificate(ourCert); /* we're done with this */
+ if (!ourPrivKey) goto loser;
+
+ /* Prepare raItem with 128 bytes (filled with zeros). */
+ keaParams.originatorRA.data =
+ (unsigned char*)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH);
+ keaParams.originatorRA.len = SMIME_FORTEZZA_RA_LENGTH;
+
+
+ /* Generate the TEK (token exchange key) which we use
+ * to wrap the bulk encryption key. (raItem) will be
+ * filled with a random seed which we need to send to
+ * the recipient. */
+ tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
+ &keaParams.originatorRA, NULL,
+ CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
+ CKA_WRAP, 0, wincx);
+
+ SECKEY_DestroyPublicKey(publickey);
+ SECKEY_DestroyPrivateKey(ourPrivKey);
+ publickey = NULL;
+ ourPrivKey = NULL;
+
+ if (!tek)
+ goto loser;
+
+ ri->encKey.data = (unsigned char*)PORT_ArenaAlloc(cinfo->poolp,
+ SMIME_FORTEZZA_MAX_KEY_SIZE);
+ ri->encKey.len = SMIME_FORTEZZA_MAX_KEY_SIZE;
+
+ if (ri->encKey.data == NULL)
+ {
+ PK11_FreeSymKey(tek);
+ goto loser;
+ }
+
+ /* Wrap the bulk key. What we do with the resulting data
+ depends on whether we're using Skipjack to wrap the key. */
+ switch(PK11_AlgtagToMechanism(enccinfo->encalg))
+ {
+ case CKM_SKIPJACK_CBC64:
+ case CKM_SKIPJACK_ECB64:
+ case CKM_SKIPJACK_OFB64:
+ case CKM_SKIPJACK_CFB64:
+ case CKM_SKIPJACK_CFB32:
+ case CKM_SKIPJACK_CFB16:
+ case CKM_SKIPJACK_CFB8:
+ /* do SKIPJACK, we use the wrap mechanism */
+ err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL,
+ tek, bulkkey, &ri->encKey);
+ whichKEA = SECKEAUsesSkipjack;
+ break;
+ default:
+ /* Not SKIPJACK, we encrypt the raw key data */
+ keaParams.nonSkipjackIV .data =
+ (unsigned char*)PORT_ArenaAlloc(arena,
+ SMIME_FORTEZZA_IV_LENGTH);
+ keaParams.nonSkipjackIV.len = SMIME_FORTEZZA_IV_LENGTH;
+ err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64,
+ &keaParams.nonSkipjackIV,
+ tek, bulkkey, &ri->encKey);
+ if (err != SECSuccess)
+ goto loser;
+
+ if (ri->encKey.len != PK11_GetKeyLength(bulkkey))
+ {
+ /* The size of the encrypted key is not the same as
+ that of the original bulk key, presumably due to
+ padding. Encode and store the real size of the
+ bulk key. */
+ if (SEC_ASN1EncodeInteger(arena,
+ &keaParams.bulkKeySize,
+ PK11_GetKeyLength(bulkkey))
+ == NULL)
+ err = (SECStatus)PORT_GetError();
+ else
+ /* use full template for encoding */
+ whichKEA = SECKEAUsesNonSkipjackWithPaddedEncKey;
+ }
+ else
+ /* enc key length == bulk key length */
+ whichKEA = SECKEAUsesNonSkipjack;
+ break;
+ }
+
+ PK11_FreeSymKey(tek);
+ if (err != SECSuccess)
+ goto loser;
+
+ /* Encode the KEA parameters into the recipient info. */
+ params = SEC_ASN1EncodeItem(arena,NULL, &keaParams,
+ sec_pkcs7_get_kea_template(whichKEA));
+ if (params == NULL) goto loser;
+ break;
+ }
+ default:
+ PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(cinfo->poolp, &ri->keyEncAlg, encalgtag,
+ params);
+ if (rv != SECSuccess)
+ goto loser;
+ if (arena) PORT_FreeArena(arena,PR_FALSE);
+ arena = NULL;
+ }
+
+ encryptobj = sec_PKCS7CreateEncryptObject (cinfo->poolp, bulkkey,
+ enccinfo->encalg,
+ &(enccinfo->contentEncAlg));
+ if (encryptobj != NULL) {
+ PORT_ArenaUnmark (cinfo->poolp, mark);
+ mark = NULL; /* good one; do not want to release */
+ }
+ /* fallthru */
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ if (publickey) {
+ SECKEY_DestroyPublicKey(publickey);
+ }
+ if (ourPrivKey) {
+ SECKEY_DestroyPrivateKey(ourPrivKey);
+ }
+ if (mark != NULL) {
+ PORT_ArenaRelease (cinfo->poolp, mark);
+ }
+ if (orig_bulkkey == NULL) {
+ if (bulkkey) PK11_FreeSymKey(bulkkey);
+ }
+
+ return encryptobj;
+}
+
+
+static void
+sec_pkcs7_encoder_notify (void *arg, PRBool before, void *dest, int depth)
+{
+ SEC_PKCS7EncoderContext *p7ecx;
+ SEC_PKCS7ContentInfo *cinfo;
+ SECOidTag kind;
+ PRBool before_content;
+
+ /*
+ * We want to notice just before the content field. After fields are
+ * not interesting to us.
+ */
+ if (!before)
+ return;
+
+ p7ecx = (SEC_PKCS7EncoderContext*)arg;
+ cinfo = p7ecx->cinfo;
+
+ before_content = PR_FALSE;
+
+ /*
+ * Watch for the content field, at which point we want to instruct
+ * the ASN.1 encoder to start taking bytes from the buffer.
+ *
+ * XXX The following assumes the inner content type is data;
+ * if/when we want to handle fully nested types, this will have
+ * to recurse until reaching the innermost data content.
+ */
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ if (dest == &(cinfo->content.data))
+ before_content = PR_TRUE;
+ break;
+
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ {
+ SEC_PKCS7DigestedData *digd;
+
+ digd = cinfo->content.digestedData;
+ if (digd == NULL)
+ break;
+
+ if (dest == &(digd->contentInfo.content))
+ before_content = PR_TRUE;
+ }
+ break;
+
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ {
+ SEC_PKCS7EncryptedData *encd;
+
+ encd = cinfo->content.encryptedData;
+ if (encd == NULL)
+ break;
+
+ if (dest == &(encd->encContentInfo.encContent))
+ before_content = PR_TRUE;
+ }
+ break;
+
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ {
+ SEC_PKCS7EnvelopedData *envd;
+
+ envd = cinfo->content.envelopedData;
+ if (envd == NULL)
+ break;
+
+ if (dest == &(envd->encContentInfo.encContent))
+ before_content = PR_TRUE;
+ }
+ break;
+
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sigd;
+
+ sigd = cinfo->content.signedData;
+ if (sigd == NULL)
+ break;
+
+ if (dest == &(sigd->contentInfo.content))
+ before_content = PR_TRUE;
+ }
+ break;
+
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saed;
+
+ saed = cinfo->content.signedAndEnvelopedData;
+ if (saed == NULL)
+ break;
+
+ if (dest == &(saed->encContentInfo.encContent))
+ before_content = PR_TRUE;
+ }
+ break;
+ }
+
+ if (before_content) {
+ /*
+ * This will cause the next SEC_ASN1EncoderUpdate to take the
+ * contents bytes from the passed-in buffer.
+ */
+ SEC_ASN1EncoderSetTakeFromBuf (p7ecx->ecx);
+ /*
+ * And that is all we needed this notify function for.
+ */
+ SEC_ASN1EncoderClearNotifyProc (p7ecx->ecx);
+ }
+}
+
+
+static SEC_PKCS7EncoderContext *
+sec_pkcs7_encoder_start_contexts (SEC_PKCS7ContentInfo *cinfo,
+ PK11SymKey *bulkkey)
+{
+ SEC_PKCS7EncoderContext *p7ecx;
+ SECOidTag kind;
+ PRBool encrypt;
+ SECItem **digests;
+ SECAlgorithmID *digestalg, **digestalgs;
+
+ p7ecx =
+ (SEC_PKCS7EncoderContext*)PORT_ZAlloc (sizeof(SEC_PKCS7EncoderContext));
+ if (p7ecx == NULL)
+ return NULL;
+
+ digests = NULL;
+ digestalg = NULL;
+ digestalgs = NULL;
+ encrypt = PR_FALSE;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ digestalg = &(cinfo->content.digestedData->digestAlg);
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ digests = cinfo->content.signedData->digests;
+ digestalgs = cinfo->content.signedData->digestAlgorithms;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ encrypt = PR_TRUE;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ digests = cinfo->content.signedAndEnvelopedData->digests;
+ digestalgs = cinfo->content.signedAndEnvelopedData->digestAlgorithms;
+ encrypt = PR_TRUE;
+ break;
+ }
+
+ if (encrypt) {
+ p7ecx->encryptobj = sec_pkcs7_encoder_start_encrypt (cinfo, bulkkey);
+ if (p7ecx->encryptobj == NULL) {
+ PORT_Free (p7ecx);
+ return NULL;
+ }
+ }
+
+ if (digestalgs != NULL) {
+ if (digests != NULL) {
+ /* digests already created (probably for detached data) */
+ digestalg = NULL;
+ } else {
+ /*
+ * XXX Some day we should handle multiple digests; for now,
+ * assume only one will be done.
+ */
+ PORT_Assert (digestalgs[0] != NULL && digestalgs[1] == NULL);
+ digestalg = digestalgs[0];
+ }
+ }
+
+ if (digestalg != NULL) {
+ SECOidData *oiddata;
+
+ oiddata = SECOID_FindOID (&(digestalg->algorithm));
+ if (oiddata != NULL) {
+ switch (oiddata->offset) {
+ case SEC_OID_MD2:
+ p7ecx->digestobj = &SECHashObjects[HASH_AlgMD2];
+ break;
+ case SEC_OID_MD5:
+ p7ecx->digestobj = &SECHashObjects[HASH_AlgMD5];
+ break;
+ case SEC_OID_SHA1:
+ p7ecx->digestobj = &SECHashObjects[HASH_AlgSHA1];
+ break;
+ default:
+ /* XXX right error? */
+ PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
+ break;
+ }
+ }
+ if (p7ecx->digestobj != NULL) {
+ p7ecx->digestcx = (* p7ecx->digestobj->create) ();
+ if (p7ecx->digestcx == NULL)
+ p7ecx->digestobj = NULL;
+ else
+ (* p7ecx->digestobj->begin) (p7ecx->digestcx);
+ }
+ if (p7ecx->digestobj == NULL) {
+ if (p7ecx->encryptobj != NULL)
+ sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj);
+ PORT_Free (p7ecx);
+ return NULL;
+ }
+ }
+
+ p7ecx->cinfo = cinfo;
+ return p7ecx;
+}
+
+
+SEC_PKCS7EncoderContext *
+SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo,
+ SEC_PKCS7EncoderOutputCallback outputfn,
+ void *outputarg,
+ PK11SymKey *bulkkey)
+{
+ SEC_PKCS7EncoderContext *p7ecx;
+ SECStatus rv;
+
+ p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey);
+ if (p7ecx == NULL)
+ return NULL;
+
+ p7ecx->output.outputfn = outputfn;
+ p7ecx->output.outputarg = outputarg;
+
+ /*
+ * Initialize the BER encoder.
+ */
+ p7ecx->ecx = SEC_ASN1EncoderStart (cinfo, sec_PKCS7ContentInfoTemplate,
+ sec_pkcs7_encoder_out, &(p7ecx->output));
+ if (p7ecx->ecx == NULL) {
+ PORT_Free (p7ecx);
+ return NULL;
+ }
+
+ /*
+ * Indicate that we are streaming. We will be streaming until we
+ * get past the contents bytes.
+ */
+ SEC_ASN1EncoderSetStreaming (p7ecx->ecx);
+
+ /*
+ * The notify function will watch for the contents field.
+ */
+ SEC_ASN1EncoderSetNotifyProc (p7ecx->ecx, sec_pkcs7_encoder_notify, p7ecx);
+
+ /*
+ * This will encode everything up to the content bytes. (The notify
+ * function will then cause the encoding to stop there.) Then our
+ * caller can start passing contents bytes to our Update, which we
+ * will pass along.
+ */
+ rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0);
+ if (rv != SECSuccess) {
+ PORT_Free (p7ecx);
+ return NULL;
+ }
+
+ return p7ecx;
+}
+
+
+/*
+ * XXX If/when we support nested contents, this needs to be revised.
+ */
+static SECStatus
+sec_pkcs7_encoder_work_data (SEC_PKCS7EncoderContext *p7ecx, SECItem *dest,
+ const unsigned char *data, unsigned long len,
+ PRBool final)
+{
+ unsigned char *buf = NULL;
+ SECStatus rv;
+
+
+ rv = SECSuccess; /* may as well be optimistic */
+
+ /*
+ * We should really have data to process, or we should be trying
+ * to finish/flush the last block. (This is an overly paranoid
+ * check since all callers are in this file and simple inspection
+ * proves they do it right. But it could find a bug in future
+ * modifications/development, that is why it is here.)
+ */
+ PORT_Assert ((data != NULL && len) || final);
+
+ /*
+ * Update the running digest.
+ * XXX This needs modification if/when we handle multiple digests.
+ */
+ if (len && p7ecx->digestobj != NULL) {
+ (* p7ecx->digestobj->update) (p7ecx->digestcx, data, len);
+ }
+
+ /*
+ * Encrypt this chunk.
+ */
+ if (p7ecx->encryptobj != NULL) {
+ /* XXX the following lengths should all be longs? */
+ unsigned int inlen; /* length of data being encrypted */
+ unsigned int outlen; /* length of encrypted data */
+ unsigned int buflen; /* length available for encrypted data */
+
+ inlen = len;
+ buflen = sec_PKCS7EncryptLength (p7ecx->encryptobj, inlen, final);
+ if (buflen == 0) {
+ /*
+ * No output is expected, but the input data may be buffered
+ * so we still have to call Encrypt.
+ */
+ rv = sec_PKCS7Encrypt (p7ecx->encryptobj, NULL, NULL, 0,
+ data, inlen, final);
+ if (final) {
+ len = 0;
+ goto done;
+ }
+ return rv;
+ }
+
+ if (dest != NULL)
+ buf = (unsigned char*)PORT_ArenaAlloc(p7ecx->cinfo->poolp, buflen);
+ else
+ buf = (unsigned char*)PORT_Alloc (buflen);
+
+ if (buf == NULL) {
+ rv = SECFailure;
+ } else {
+ rv = sec_PKCS7Encrypt (p7ecx->encryptobj, buf, &outlen, buflen,
+ data, inlen, final);
+ data = buf;
+ len = outlen;
+ }
+ if (rv != SECSuccess) {
+ if (final)
+ goto done;
+ return rv;
+ }
+ }
+
+ if (p7ecx->ecx != NULL) {
+ /*
+ * Encode the contents bytes.
+ */
+ if(len) {
+ rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, (const char *)data, len);
+ }
+ }
+
+done:
+ if (p7ecx->encryptobj != NULL) {
+ if (final)
+ sec_PKCS7DestroyEncryptObject (p7ecx->encryptobj);
+ if (dest != NULL) {
+ dest->data = buf;
+ dest->len = len;
+ } else if (buf != NULL) {
+ PORT_Free (buf);
+ }
+ }
+
+ if (final && p7ecx->digestobj != NULL) {
+ SECItem *digest, **digests, ***digestsp;
+ unsigned char *digdata;
+ SECOidTag kind;
+
+ kind = SEC_PKCS7ContentType (p7ecx->cinfo);
+ switch (kind) {
+ default:
+ PORT_Assert (0);
+ return SECFailure;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ digest = &(p7ecx->cinfo->content.digestedData->digest);
+ digestsp = NULL;
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ digest = NULL;
+ digestsp = &(p7ecx->cinfo->content.signedData->digests);
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ digest = NULL;
+ digestsp = &(p7ecx->cinfo->content.signedAndEnvelopedData->digests);
+ break;
+ }
+
+ digdata = (unsigned char*)PORT_ArenaAlloc (p7ecx->cinfo->poolp,
+ p7ecx->digestobj->length);
+ if (digdata == NULL)
+ return SECFailure;
+
+ if (digestsp != NULL) {
+ PORT_Assert (digest == NULL);
+
+ digest = (SECItem*)PORT_ArenaAlloc (p7ecx->cinfo->poolp,
+ sizeof(SECItem));
+ digests = (SECItem**)PORT_ArenaAlloc (p7ecx->cinfo->poolp,
+ 2 * sizeof(SECItem *));
+ if (digests == NULL || digest == NULL)
+ return SECFailure;
+
+ digests[0] = digest;
+ digests[1] = NULL;
+
+ *digestsp = digests;
+ }
+
+ PORT_Assert (digest != NULL);
+
+ digest->data = digdata;
+ digest->len = p7ecx->digestobj->length;
+
+ (* p7ecx->digestobj->end) (p7ecx->digestcx, digest->data,
+ &(digest->len), digest->len);
+ (* p7ecx->digestobj->destroy) (p7ecx->digestcx, PR_TRUE);
+ }
+
+ return rv;
+}
+
+
+SECStatus
+SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx,
+ const char *data, unsigned long len)
+{
+ /* XXX Error handling needs help. Return what? Do "Finish" on failure? */
+ return sec_pkcs7_encoder_work_data (p7ecx, NULL,
+ (const unsigned char *)data, len,
+ PR_FALSE);
+}
+
+
+/*
+ * XXX I would *really* like to not have to do this, but the current
+ * signing interface gives me little choice.
+ */
+static SECOidTag
+sec_pkcs7_pick_sign_alg (SECOidTag hashalg, SECOidTag encalg)
+{
+ switch (encalg) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ switch (hashalg) {
+ case SEC_OID_MD2:
+ return SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
+ case SEC_OID_MD5:
+ return SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
+ case SEC_OID_SHA1:
+ return SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_DSS:
+ switch (hashalg) {
+ case SEC_OID_SHA1:
+ return SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
+ default:
+ return SEC_OID_UNKNOWN;
+ }
+ default:
+ break;
+ }
+
+ return encalg; /* maybe it is already the right algid */
+}
+
+
+static SECStatus
+sec_pkcs7_encoder_sig_and_certs (SEC_PKCS7ContentInfo *cinfo,
+ SECKEYGetPasswordKey pwfn, void *pwfnarg)
+{
+ SECOidTag kind;
+ CERTCertificate **certs;
+ CERTCertificateList **certlists;
+ SECAlgorithmID **digestalgs;
+ SECItem **digests;
+ SEC_PKCS7SignerInfo *signerinfo, **signerinfos;
+ SECItem **rawcerts, ***rawcertsp;
+ PRArenaPool *poolp;
+ int certcount;
+ int ci, cli, rci, si;
+
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ case SEC_OID_PKCS7_DATA:
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ certs = NULL;
+ certlists = NULL;
+ digestalgs = NULL;
+ digests = NULL;
+ signerinfos = NULL;
+ rawcertsp = NULL;
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ SEC_PKCS7SignedData *sdp;
+
+ sdp = cinfo->content.signedData;
+ certs = sdp->certs;
+ certlists = sdp->certLists;
+ digestalgs = sdp->digestAlgorithms;
+ digests = sdp->digests;
+ signerinfos = sdp->signerInfos;
+ rawcertsp = &(sdp->rawCerts);
+ }
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ {
+ SEC_PKCS7SignedAndEnvelopedData *saedp;
+
+ saedp = cinfo->content.signedAndEnvelopedData;
+ certs = saedp->certs;
+ certlists = saedp->certLists;
+ digestalgs = saedp->digestAlgorithms;
+ digests = saedp->digests;
+ signerinfos = saedp->signerInfos;
+ rawcertsp = &(saedp->rawCerts);
+ }
+ break;
+ }
+
+ if (certs == NULL && certlists == NULL && signerinfos == NULL)
+ return SECSuccess; /* nothing for us to do! */
+
+ poolp = cinfo->poolp;
+ certcount = 0;
+
+ if (signerinfos != NULL) {
+ SECOidTag digestalgtag;
+ int di;
+ SECStatus rv;
+ CERTCertificate *cert;
+ SECKEYPrivateKey *privkey;
+ SECItem signature;
+ SECOidTag signalgtag;
+
+ PORT_Assert (digestalgs != NULL && digests != NULL);
+
+ /*
+ * If one fails, we bail right then. If we want to continue and
+ * try to do subsequent signatures, this loop, and the departures
+ * from it, will need to be reworked.
+ */
+ for (si = 0; signerinfos[si] != NULL; si++) {
+
+ signerinfo = signerinfos[si];
+
+ /* find right digest */
+ digestalgtag = SECOID_GetAlgorithmTag (&(signerinfo->digestAlg));
+ for (di = 0; digestalgs[di] != NULL; di++) {
+ /* XXX Should I be comparing more than the tag? */
+ if (digestalgtag == SECOID_GetAlgorithmTag (digestalgs[di]))
+ break;
+ }
+ if (digestalgs[di] == NULL) {
+ /* XXX oops; do what? set an error? */
+ return SECFailure;
+ }
+ PORT_Assert (digests[di] != NULL);
+
+ cert = signerinfo->cert;
+ privkey = PK11_FindKeyByAnyCert (cert, pwfnarg);
+ if (privkey == NULL)
+ return SECFailure;
+
+ /*
+ * XXX I think there should be a cert-level interface for this,
+ * so that I do not have to know about subjectPublicKeyInfo...
+ */
+ signalgtag = SECOID_GetAlgorithmTag (&(cert->subjectPublicKeyInfo.algorithm));
+
+ /* Fortezza MISSI have weird signature formats. Map them
+ * to standard DSA formats */
+ signalgtag = PK11_FortezzaMapSig(signalgtag);
+
+ if (signerinfo->authAttr != NULL) {
+ SEC_PKCS7Attribute *attr;
+ SECItem encoded_attrs;
+ SECItem *dummy;
+
+ /*
+ * First, find and fill in the message digest attribute.
+ */
+ attr = sec_PKCS7FindAttribute (signerinfo->authAttr,
+ SEC_OID_PKCS9_MESSAGE_DIGEST,
+ PR_TRUE);
+ PORT_Assert (attr != NULL);
+ if (attr == NULL) {
+ SECKEY_DestroyPrivateKey (privkey);
+ return SECFailure;
+ }
+
+ /*
+ * XXX The second half of the following assertion prevents
+ * the encoder from being called twice on the same content.
+ * Either just remove the second half the assertion, or
+ * change the code to check if the value already there is
+ * the same as digests[di], whichever seems more right.
+ */
+ PORT_Assert (attr->values != NULL && attr->values[0] == NULL);
+ attr->values[0] = digests[di];
+
+ /*
+ * Before encoding, reorder the attributes so that when they
+ * are encoded, they will be conforming DER, which is required
+ * to have a specific order and that is what must be used for
+ * the hash/signature. We do this here, rather than building
+ * it into EncodeAttributes, because we do not want to do
+ * such reordering on incoming messages (which also uses
+ * EncodeAttributes) or our old signatures (and other "broken"
+ * implementations) will not verify. So, we want to guarantee
+ * that we send out good DER encodings of attributes, but not
+ * to expect to receive them.
+ */
+ rv = sec_PKCS7ReorderAttributes (signerinfo->authAttr);
+ if (rv != SECSuccess) {
+ SECKEY_DestroyPrivateKey (privkey);
+ return SECFailure;
+ }
+
+ encoded_attrs.data = NULL;
+ encoded_attrs.len = 0;
+ dummy = sec_PKCS7EncodeAttributes (NULL, &encoded_attrs,
+ &(signerinfo->authAttr));
+ if (dummy == NULL) {
+ SECKEY_DestroyPrivateKey (privkey);
+ return SECFailure;
+ }
+
+ rv = SEC_SignData (&signature,
+ encoded_attrs.data, encoded_attrs.len,
+ privkey,
+ sec_pkcs7_pick_sign_alg (digestalgtag,
+ signalgtag));
+ SECITEM_FreeItem (&encoded_attrs, PR_FALSE);
+ } else {
+ rv = SGN_Digest (privkey, digestalgtag, &signature,
+ digests[di]);
+ }
+
+ SECKEY_DestroyPrivateKey (privkey);
+
+ if (rv != SECSuccess)
+ return rv;
+
+ rv = SECITEM_CopyItem (poolp, &(signerinfo->encDigest), &signature);
+ if (rv != SECSuccess)
+ return rv;
+
+ SECITEM_FreeItem (&signature, PR_FALSE);
+
+ rv = SECOID_SetAlgorithmID (poolp, &(signerinfo->digestEncAlg),
+ signalgtag, NULL);
+ if (rv != SECSuccess)
+ return SECFailure;
+
+ /*
+ * Count the cert chain for this signer.
+ */
+ if (signerinfo->certList != NULL)
+ certcount += signerinfo->certList->len;
+ }
+ }
+
+ if (certs != NULL) {
+ for (ci = 0; certs[ci] != NULL; ci++)
+ certcount++;
+ }
+
+ if (certlists != NULL) {
+ for (cli = 0; certlists[cli] != NULL; cli++)
+ certcount += certlists[cli]->len;
+ }
+
+ if (certcount == 0)
+ return SECSuccess; /* signing done; no certs */
+
+ /*
+ * Combine all of the certs and cert chains into rawcerts.
+ * Note: certcount is an upper bound; we may not need that many slots
+ * but we will allocate anyway to avoid having to do another pass.
+ * (The temporary space saving is not worth it.)
+ */
+ rawcerts = (SECItem**)PORT_ArenaAlloc (poolp,
+ (certcount + 1) * sizeof(SECItem *));
+ if (rawcerts == NULL)
+ return SECFailure;
+
+ /*
+ * XXX Want to check for duplicates and not add *any* cert that is
+ * already in the set. This will be more important when we start
+ * dealing with larger sets of certs, dual-key certs (signing and
+ * encryption), etc. For the time being we can slide by...
+ */
+ rci = 0;
+ if (signerinfos != NULL) {
+ for (si = 0; signerinfos[si] != NULL; si++) {
+ signerinfo = signerinfos[si];
+ for (ci = 0; ci < signerinfo->certList->len; ci++)
+ rawcerts[rci++] = &(signerinfo->certList->certs[ci]);
+ }
+
+ }
+
+ if (certs != NULL) {
+ for (ci = 0; certs[ci] != NULL; ci++)
+ rawcerts[rci++] = &(certs[ci]->derCert);
+ }
+
+ if (certlists != NULL) {
+ for (cli = 0; certlists[cli] != NULL; cli++) {
+ for (ci = 0; ci < certlists[cli]->len; ci++)
+ rawcerts[rci++] = &(certlists[cli]->certs[ci]);
+ }
+ }
+
+ rawcerts[rci] = NULL;
+ *rawcertsp = rawcerts;
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx,
+ SECKEYGetPasswordKey pwfn, void *pwfnarg)
+{
+ SECStatus rv;
+
+ /*
+ * Flush out any remaining data.
+ */
+ rv = sec_pkcs7_encoder_work_data (p7ecx, NULL, NULL, 0, PR_TRUE);
+
+ /*
+ * Turn off streaming stuff.
+ */
+ SEC_ASN1EncoderClearTakeFromBuf (p7ecx->ecx);
+ SEC_ASN1EncoderClearStreaming (p7ecx->ecx);
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = sec_pkcs7_encoder_sig_and_certs (p7ecx->cinfo, pwfn, pwfnarg);
+ if (rv != SECSuccess)
+ goto loser;
+
+ rv = SEC_ASN1EncoderUpdate (p7ecx->ecx, NULL, 0);
+
+loser:
+ SEC_ASN1EncoderFinish (p7ecx->ecx);
+ PORT_Free (p7ecx);
+ return rv;
+}
+
+
+/*
+ * After this routine is called, the entire PKCS7 contentInfo is ready
+ * to be encoded. This is used internally, but can also be called from
+ * elsewhere for those who want to be able to just have pointers to
+ * the ASN1 template for pkcs7 contentInfo built into their own encodings.
+ */
+SECStatus
+SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo,
+ PK11SymKey *bulkkey,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg)
+{
+ SEC_PKCS7EncoderContext *p7ecx;
+ SECItem *content, *enc_content;
+ SECStatus rv;
+
+ p7ecx = sec_pkcs7_encoder_start_contexts (cinfo, bulkkey);
+ if (p7ecx == NULL)
+ return SECFailure;
+
+ content = SEC_PKCS7GetContent (cinfo);
+
+ if (p7ecx->encryptobj != NULL) {
+ SECOidTag kind;
+ SEC_PKCS7EncryptedContentInfo *enccinfo;
+
+ kind = SEC_PKCS7ContentType (p7ecx->cinfo);
+ switch (kind) {
+ default:
+ PORT_Assert (0);
+ rv = SECFailure;
+ goto loser;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ enccinfo = &(p7ecx->cinfo->content.encryptedData->encContentInfo);
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ enccinfo = &(p7ecx->cinfo->content.envelopedData->encContentInfo);
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ enccinfo = &(p7ecx->cinfo->content.signedAndEnvelopedData->encContentInfo);
+ break;
+ }
+ enc_content = &(enccinfo->encContent);
+ } else {
+ enc_content = NULL;
+ }
+
+ if (content != NULL && content->data != NULL && content->len) {
+ rv = sec_pkcs7_encoder_work_data (p7ecx, enc_content,
+ content->data, content->len, PR_TRUE);
+ if (rv != SECSuccess)
+ goto loser;
+ }
+
+ rv = sec_pkcs7_encoder_sig_and_certs (cinfo, pwfn, pwfnarg);
+
+loser:
+ PORT_Free (p7ecx);
+ return rv;
+}
+
+
+/*
+ * Encode a PKCS7 object, in one shot. All necessary components
+ * of the object must already be specified. Either the data has
+ * already been included (via SetContent), or the data is detached,
+ * or there is no data at all (certs-only).
+ *
+ * "cinfo" specifies the object to be encoded.
+ *
+ * "outputfn" is where the encoded bytes will be passed.
+ *
+ * "outputarg" is an opaque argument to the above callback.
+ *
+ * "bulkkey" specifies the bulk encryption key to use. This argument
+ * can be NULL if no encryption is being done, or if the bulk key should
+ * be generated internally (usually the case for EnvelopedData but never
+ * for EncryptedData, which *must* provide a bulk encryption key).
+ *
+ * "pwfn" is a callback for getting the password which protects the
+ * private key of the signer. This argument can be NULL if it is known
+ * that no signing is going to be done.
+ *
+ * "pwfnarg" is an opaque argument to the above callback.
+ */
+SECStatus
+SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo,
+ SEC_PKCS7EncoderOutputCallback outputfn,
+ void *outputarg,
+ PK11SymKey *bulkkey,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg)
+{
+ SECStatus rv;
+
+ rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg);
+ if (rv == SECSuccess) {
+ struct sec_pkcs7_encoder_output outputcx;
+
+ outputcx.outputfn = outputfn;
+ outputcx.outputarg = outputarg;
+
+ rv = SEC_ASN1Encode (cinfo, sec_PKCS7ContentInfoTemplate,
+ sec_pkcs7_encoder_out, &outputcx);
+ }
+
+ return rv;
+}
+
+
+/*
+ * Encode a PKCS7 object, in one shot. All necessary components
+ * of the object must already be specified. Either the data has
+ * already been included (via SetContent), or the data is detached,
+ * or there is no data at all (certs-only). The output, rather than
+ * being passed to an output function as is done above, is all put
+ * into a SECItem.
+ *
+ * "pool" specifies a pool from which to allocate the result.
+ * It can be NULL, in which case memory is allocated generically.
+ *
+ * "dest" specifies a SECItem in which to put the result data.
+ * It can be NULL, in which case the entire item is allocated, too.
+ *
+ * "cinfo" specifies the object to be encoded.
+ *
+ * "bulkkey" specifies the bulk encryption key to use. This argument
+ * can be NULL if no encryption is being done, or if the bulk key should
+ * be generated internally (usually the case for EnvelopedData but never
+ * for EncryptedData, which *must* provide a bulk encryption key).
+ *
+ * "pwfn" is a callback for getting the password which protects the
+ * private key of the signer. This argument can be NULL if it is known
+ * that no signing is going to be done.
+ *
+ * "pwfnarg" is an opaque argument to the above callback.
+ */
+SECItem *
+SEC_PKCS7EncodeItem (PRArenaPool *pool,
+ SECItem *dest,
+ SEC_PKCS7ContentInfo *cinfo,
+ PK11SymKey *bulkkey,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg)
+{
+ SECStatus rv;
+
+ rv = SEC_PKCS7PrepareForEncode (cinfo, bulkkey, pwfn, pwfnarg);
+ if (rv != SECSuccess)
+ return NULL;
+
+ return SEC_ASN1EncodeItem (pool, dest, cinfo, sec_PKCS7ContentInfoTemplate);
+}
+
diff --git a/security/nss/lib/pkcs7/p7local.c b/security/nss/lib/pkcs7/p7local.c
new file mode 100644
index 000000000..d3e58be50
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7local.c
@@ -0,0 +1,1431 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Support routines for PKCS7 implementation, none of which are exported.
+ * This file should only contain things that are needed by both the
+ * encoding/creation side *and* the decoding/decryption side. Anything
+ * else should be static routines in the appropriate file.
+ *
+ * $Id$
+ */
+
+#include "p7local.h"
+
+#include "cryptohi.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "pk11func.h"
+#include "secpkcs5.h"
+#include "secerr.h"
+
+/*
+ * -------------------------------------------------------------------
+ * Cipher stuff.
+ */
+
+typedef SECStatus (*sec_pkcs7_cipher_function) (void *,
+ unsigned char *,
+ unsigned *,
+ unsigned int,
+ const unsigned char *,
+ unsigned int);
+typedef SECStatus (*sec_pkcs7_cipher_destroy) (void *, PRBool);
+
+#define BLOCK_SIZE 4096
+
+struct sec_pkcs7_cipher_object {
+ void *cx;
+ sec_pkcs7_cipher_function doit;
+ sec_pkcs7_cipher_destroy destroy;
+ PRBool encrypt;
+ int block_size;
+ int pad_size;
+ int pending_count;
+ unsigned char pending_buf[BLOCK_SIZE];
+};
+
+/*
+ * Create a cipher object to do decryption, based on the given bulk
+ * encryption key and algorithm identifier (which may include an iv).
+ *
+ * XXX This interface, or one similar, would be really nice available
+ * in general... I tried to keep the pkcs7-specific stuff (mostly
+ * having to do with padding) out of here.
+ *
+ * XXX Once both are working, it might be nice to combine this and the
+ * function below (for starting up encryption) into one routine, and just
+ * have two simple cover functions which call it.
+ */
+sec_PKCS7CipherObject *
+sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid)
+{
+ sec_PKCS7CipherObject *result;
+ SECOidTag algtag;
+ void *ciphercx;
+ CK_MECHANISM_TYPE mechanism;
+ SECItem *param;
+ PK11SlotInfo *slot;
+
+ result = (struct sec_pkcs7_cipher_object*)
+ PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object));
+ if (result == NULL)
+ return NULL;
+
+ ciphercx = NULL;
+ algtag = SECOID_GetAlgorithmTag (algid);
+
+ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+ CK_MECHANISM pbeMech, cryptoMech;
+ SECItem *pbeParams, *pwitem;
+ SEC_PKCS5KeyAndPassword *keyPwd;
+
+ keyPwd = (SEC_PKCS5KeyAndPassword *)key;
+ key = keyPwd->key;
+ pwitem = keyPwd->pwitem;
+
+ pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
+ pbeParams = PK11_ParamFromAlgid(algid);
+ if (!pbeParams) {
+ PORT_Free(result);
+ return NULL;
+ }
+
+ pbeMech.pParameter = pbeParams->data;
+ pbeMech.ulParameterLen = pbeParams->len;
+ if (PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem,
+ PR_FALSE) != CKR_OK) {
+ PORT_Free(result);
+ SECITEM_ZfreeItem(pbeParams, PR_TRUE);
+ return NULL;
+ }
+ SECITEM_ZfreeItem(pbeParams, PR_TRUE);
+
+ param = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(!param) {
+ PORT_Free(result);
+ return NULL;
+ }
+ param->data = (unsigned char *)cryptoMech.pParameter;
+ param->len = cryptoMech.ulParameterLen;
+ mechanism = cryptoMech.mechanism;
+ } else {
+ mechanism = PK11_AlgtagToMechanism(algtag);
+ param = PK11_ParamFromAlgid(algid);
+ if (param == NULL) {
+ PORT_Free(result);
+ return NULL;
+ }
+ }
+
+ result->pad_size = PK11_GetBlockSize(mechanism,param);
+ slot = PK11_GetSlotFromKey(key);
+ result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;
+ PK11_FreeSlot(slot);
+ ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_DECRYPT, key, param);
+ SECITEM_FreeItem(param,PR_TRUE);
+ if (ciphercx == NULL) {
+ PORT_Free (result);
+ return NULL;
+ }
+
+ result->cx = ciphercx;
+ result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp;
+ result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext;
+ result->encrypt = PR_FALSE;
+ result->pending_count = 0;
+
+ return result;
+}
+
+/*
+ * Create a cipher object to do encryption, based on the given bulk
+ * encryption key and algorithm tag. Fill in the algorithm identifier
+ * (which may include an iv) appropriately.
+ *
+ * XXX This interface, or one similar, would be really nice available
+ * in general... I tried to keep the pkcs7-specific stuff (mostly
+ * having to do with padding) out of here.
+ *
+ * XXX Once both are working, it might be nice to combine this and the
+ * function above (for starting up decryption) into one routine, and just
+ * have two simple cover functions which call it.
+ */
+sec_PKCS7CipherObject *
+sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key,
+ SECOidTag algtag, SECAlgorithmID *algid)
+{
+ sec_PKCS7CipherObject *result;
+ void *ciphercx;
+ SECItem *param;
+ SECStatus rv;
+ CK_MECHANISM_TYPE mechanism;
+ PRBool needToEncodeAlgid = PR_FALSE;
+ PK11SlotInfo *slot;
+
+ result = (struct sec_pkcs7_cipher_object*)
+ PORT_ZAlloc (sizeof(struct sec_pkcs7_cipher_object));
+ if (result == NULL)
+ return NULL;
+
+ ciphercx = NULL;
+ if (SEC_PKCS5IsAlgorithmPBEAlg(algid)) {
+ CK_MECHANISM pbeMech, cryptoMech;
+ SECItem *pbeParams;
+ SEC_PKCS5KeyAndPassword *keyPwd;
+
+ PORT_Memset(&pbeMech, 0, sizeof(CK_MECHANISM));
+ PORT_Memset(&cryptoMech, 0, sizeof(CK_MECHANISM));
+
+ pbeMech.mechanism = PK11_AlgtagToMechanism(algtag);
+ pbeParams = PK11_ParamFromAlgid(algid);
+ if(!pbeParams) {
+ PORT_Free(result);
+ return NULL;
+ }
+ keyPwd = (SEC_PKCS5KeyAndPassword *)key;
+ key = keyPwd->key;
+
+ pbeMech.pParameter = pbeParams->data;
+ pbeMech.ulParameterLen = pbeParams->len;
+ if(PK11_MapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech,
+ keyPwd->pwitem, PR_FALSE) != CKR_OK) {
+ PORT_Free(result);
+ SECITEM_ZfreeItem(pbeParams, PR_TRUE);
+ return NULL;
+ }
+ SECITEM_ZfreeItem(pbeParams, PR_TRUE);
+
+ param = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(!param) {
+ PORT_Free(result);
+ return NULL;
+ }
+ param->data = (unsigned char *)cryptoMech.pParameter;
+ param->len = cryptoMech.ulParameterLen;
+ mechanism = cryptoMech.mechanism;
+ } else {
+ mechanism = PK11_AlgtagToMechanism(algtag);
+ param = PK11_GenerateNewParam(mechanism,key);
+ if (param == NULL) {
+ PORT_Free(result);
+ return NULL;
+ }
+ needToEncodeAlgid = PR_TRUE;
+ }
+
+ result->pad_size = PK11_GetBlockSize(mechanism,param);
+ slot = PK11_GetSlotFromKey(key);
+ result->block_size = PK11_IsHW(slot) ? BLOCK_SIZE : result->pad_size;
+ PK11_FreeSlot(slot);
+ ciphercx = PK11_CreateContextBySymKey(mechanism, CKA_ENCRYPT,
+ key, param);
+ if (ciphercx == NULL) {
+ PORT_Free (result);
+ SECITEM_FreeItem(param,PR_TRUE);
+ return NULL;
+ }
+
+ /*
+ * These are placed after the CreateContextBySymKey() because some
+ * mechanisms have to generate their IVs from their card (i.e. FORTEZZA).
+ * Don't move it from here.
+ */
+ if (needToEncodeAlgid) {
+ rv = PK11_ParamToAlgid(algtag,param,poolp,algid);
+ if(rv != SECSuccess) {
+ return NULL;
+ }
+ }
+ SECITEM_FreeItem(param,PR_TRUE);
+
+ result->cx = ciphercx;
+ result->doit = (sec_pkcs7_cipher_function) PK11_CipherOp;
+ result->destroy = (sec_pkcs7_cipher_destroy) PK11_DestroyContext;
+ result->encrypt = PR_TRUE;
+ result->pending_count = 0;
+
+ return result;
+}
+
+
+/*
+ * Destroy the cipher object.
+ */
+static void
+sec_pkcs7_destroy_cipher (sec_PKCS7CipherObject *obj)
+{
+ (* obj->destroy) (obj->cx, PR_TRUE);
+ PORT_Free (obj);
+}
+
+void
+sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj)
+{
+ PORT_Assert (obj != NULL);
+ if (obj == NULL)
+ return;
+ PORT_Assert (! obj->encrypt);
+ sec_pkcs7_destroy_cipher (obj);
+}
+
+void
+sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj)
+{
+ PORT_Assert (obj != NULL);
+ if (obj == NULL)
+ return;
+ PORT_Assert (obj->encrypt);
+ sec_pkcs7_destroy_cipher (obj);
+}
+
+
+/*
+ * XXX I think all of the following lengths should be longs instead
+ * of ints, but our current crypto interface uses ints, so I did too.
+ */
+
+
+/*
+ * What will be the output length of the next call to decrypt?
+ * Result can be used to perform memory allocations. Note that the amount
+ * is exactly accurate only when not doing a block cipher or when final
+ * is false, otherwise it is an upper bound on the amount because until
+ * we see the data we do not know how many padding bytes there are
+ * (always between 1 and bsize).
+ *
+ * Note that this can return zero, which does not mean that the decrypt
+ * operation can be skipped! (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.) However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent decrypt operation, as no output bytes
+ * will be stored.
+ */
+unsigned int
+sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,
+ PRBool final)
+{
+ int blocks, block_size;
+
+ PORT_Assert (! obj->encrypt);
+
+ block_size = obj->block_size;
+
+ /*
+ * If this is not a block cipher, then we always have the same
+ * number of output bytes as we had input bytes.
+ */
+ if (block_size == 0)
+ return input_len;
+
+ /*
+ * On the final call, we will always use up all of the pending
+ * bytes plus all of the input bytes, *but*, there will be padding
+ * at the end and we cannot predict how many bytes of padding we
+ * will end up removing. The amount given here is actually known
+ * to be at least 1 byte too long (because we know we will have
+ * at least 1 byte of padding), but seemed clearer/better to me.
+ */
+ if (final)
+ return obj->pending_count + input_len;
+
+ /*
+ * Okay, this amount is exactly what we will output on the
+ * next cipher operation. We will always hang onto the last
+ * 1 - block_size bytes for non-final operations. That is,
+ * we will do as many complete blocks as we can *except* the
+ * last block (complete or partial). (This is because until
+ * we know we are at the end, we cannot know when to interpret
+ * and removing the padding byte(s), which are guaranteed to
+ * be there.)
+ */
+ blocks = (obj->pending_count + input_len - 1) / block_size;
+ return blocks * block_size;
+}
+
+/*
+ * What will be the output length of the next call to encrypt?
+ * Result can be used to perform memory allocations.
+ *
+ * Note that this can return zero, which does not mean that the encrypt
+ * operation can be skipped! (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.) However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent encrypt operation, as no output bytes
+ * will be stored.
+ */
+unsigned int
+sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj, unsigned int input_len,
+ PRBool final)
+{
+ int blocks, block_size;
+ int pad_size;
+
+ PORT_Assert (obj->encrypt);
+
+ block_size = obj->block_size;
+ pad_size = obj->pad_size;
+
+ /*
+ * If this is not a block cipher, then we always have the same
+ * number of output bytes as we had input bytes.
+ */
+ if (block_size == 0)
+ return input_len;
+
+ /*
+ * On the final call, we only send out what we need for
+ * remaining bytes plus the padding. (There is always padding,
+ * so even if we have an exact number of blocks as input, we
+ * will add another full block that is just padding.)
+ */
+ if (final) {
+ if (pad_size == 0) {
+ return obj->pending_count + input_len;
+ } else {
+ blocks = (obj->pending_count + input_len) / pad_size;
+ blocks++;
+ return blocks*pad_size;
+ }
+ }
+
+ /*
+ * Now, count the number of complete blocks of data we have.
+ */
+ blocks = (obj->pending_count + input_len) / block_size;
+
+
+ return blocks * block_size;
+}
+
+
+/*
+ * Decrypt a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the decrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "obj" is the return value from sec_PKCS7CreateDecryptObject.
+ * When "final" is true, this is the last of the data to be decrypted.
+ *
+ * This is much more complicated than it sounds when the cipher is
+ * a block-type, meaning that the decryption function will only
+ * operate on whole blocks. But our caller is operating stream-wise,
+ * and can pass in any number of bytes. So we need to keep track
+ * of block boundaries. We save excess bytes between calls in "obj".
+ * We also need to determine which bytes are padding, and remove
+ * them from the output. We can only do this step when we know we
+ * have the final block of data. PKCS #7 specifies that the padding
+ * used for a block cipher is a string of bytes, each of whose value is
+ * the same as the length of the padding, and that all data is padded.
+ * (Even data that starts out with an exact multiple of blocks gets
+ * added to it another block, all of which is padding.)
+ */
+SECStatus
+sec_PKCS7Decrypt (sec_PKCS7CipherObject *obj, unsigned char *output,
+ unsigned int *output_len_p, unsigned int max_output_len,
+ const unsigned char *input, unsigned int input_len,
+ PRBool final)
+{
+ int blocks, bsize, pcount, padsize;
+ unsigned int max_needed, ifraglen, ofraglen, output_len;
+ unsigned char *pbuf;
+ SECStatus rv;
+
+ PORT_Assert (! obj->encrypt);
+
+ /*
+ * Check that we have enough room for the output. Our caller should
+ * already handle this; failure is really an internal error (i.e. bug).
+ */
+ max_needed = sec_PKCS7DecryptLength (obj, input_len, final);
+ PORT_Assert (max_output_len >= max_needed);
+ if (max_output_len < max_needed) {
+ /* PORT_SetError (XXX); */
+ return SECFailure;
+ }
+
+ /*
+ * hardware encryption does not like small decryption sizes here, so we
+ * allow both blocking and padding.
+ */
+ bsize = obj->block_size;
+ padsize = obj->pad_size;
+
+ /*
+ * When no blocking or padding work to do, we can simply call the
+ * cipher function and we are done.
+ */
+ if (bsize == 0) {
+ return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,
+ input, input_len);
+ }
+
+ pcount = obj->pending_count;
+ pbuf = obj->pending_buf;
+
+ output_len = 0;
+
+ if (pcount) {
+ /*
+ * Try to fill in an entire block, starting with the bytes
+ * we already have saved away.
+ */
+ while (input_len && pcount < bsize) {
+ pbuf[pcount++] = *input++;
+ input_len--;
+ }
+ /*
+ * If we have at most a whole block and this is not our last call,
+ * then we are done for now. (We do not try to decrypt a lone
+ * single block because we cannot interpret the padding bytes
+ * until we know we are handling the very last block of all input.)
+ */
+ if (input_len == 0 && !final) {
+ obj->pending_count = pcount;
+ if (output_len_p)
+ *output_len_p = 0;
+ return SECSuccess;
+ }
+ /*
+ * Given the logic above, we expect to have a full block by now.
+ * If we do not, there is something wrong, either with our own
+ * logic or with (length of) the data given to us.
+ */
+ PORT_Assert ((padsize == 0) || (pcount % padsize) == 0);
+ if ((padsize != 0) && (pcount % padsize) != 0) {
+ PORT_Assert (final);
+ PORT_SetError (SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ /*
+ * Decrypt the block.
+ */
+ rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+ pbuf, pcount);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7DecryptLength needs to be made smarter!
+ */
+ PORT_Assert (ofraglen == pcount);
+
+ /*
+ * Account for the bytes now in output.
+ */
+ max_output_len -= ofraglen;
+ output_len += ofraglen;
+ output += ofraglen;
+ }
+
+ /*
+ * If this is our last call, we expect to have an exact number of
+ * blocks left to be decrypted; we will decrypt them all.
+ *
+ * If not our last call, we always save between 1 and bsize bytes
+ * until next time. (We must do this because we cannot be sure
+ * that none of the decrypted bytes are padding bytes until we
+ * have at least another whole block of data. You cannot tell by
+ * looking -- the data could be anything -- you can only tell by
+ * context, knowing you are looking at the last block.) We could
+ * decrypt a whole block now but it is easier if we just treat it
+ * the same way we treat partial block bytes.
+ */
+ if (final) {
+ if (padsize) {
+ blocks = input_len / padsize;
+ ifraglen = blocks * padsize;
+ } else ifraglen = input_len;
+ PORT_Assert (ifraglen == input_len);
+
+ if (ifraglen != input_len) {
+ PORT_SetError (SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ } else {
+ blocks = (input_len - 1) / bsize;
+ ifraglen = blocks * bsize;
+ PORT_Assert (ifraglen < input_len);
+
+ pcount = input_len - ifraglen;
+ PORT_Memcpy (pbuf, input + ifraglen, pcount);
+ obj->pending_count = pcount;
+ }
+
+ if (ifraglen) {
+ rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+ input, ifraglen);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7DecryptLength needs to be made smarter!
+ */
+ PORT_Assert (ifraglen == ofraglen);
+ if (ifraglen != ofraglen) {
+ PORT_SetError (SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+
+ output_len += ofraglen;
+ } else {
+ ofraglen = 0;
+ }
+
+ /*
+ * If we just did our very last block, "remove" the padding by
+ * adjusting the output length.
+ */
+ if (final && (padsize != 0)) {
+ unsigned int padlen = *(output + ofraglen - 1);
+ PORT_Assert (padlen > 0 && padlen <= padsize);
+ if (padlen == 0 || padlen > padsize) {
+ PORT_SetError (SEC_ERROR_BAD_DATA);
+ return SECFailure;
+ }
+ output_len -= padlen;
+ }
+
+ PORT_Assert (output_len_p != NULL || output_len == 0);
+ if (output_len_p != NULL)
+ *output_len_p = output_len;
+
+ return SECSuccess;
+}
+
+/*
+ * Encrypt a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the encrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "obj" is the return value from sec_PKCS7CreateEncryptObject.
+ * When "final" is true, this is the last of the data to be encrypted.
+ *
+ * This is much more complicated than it sounds when the cipher is
+ * a block-type, meaning that the encryption function will only
+ * operate on whole blocks. But our caller is operating stream-wise,
+ * and can pass in any number of bytes. So we need to keep track
+ * of block boundaries. We save excess bytes between calls in "obj".
+ * We also need to add padding bytes at the end. PKCS #7 specifies
+ * that the padding used for a block cipher is a string of bytes,
+ * each of whose value is the same as the length of the padding,
+ * and that all data is padded. (Even data that starts out with
+ * an exact multiple of blocks gets added to it another block,
+ * all of which is padding.)
+ *
+ * XXX I would kind of like to combine this with the function above
+ * which does decryption, since they have a lot in common. But the
+ * tricky parts about padding and filling blocks would be much
+ * harder to read that way, so I left them separate. At least for
+ * now until it is clear that they are right.
+ */
+SECStatus
+sec_PKCS7Encrypt (sec_PKCS7CipherObject *obj, unsigned char *output,
+ unsigned int *output_len_p, unsigned int max_output_len,
+ const unsigned char *input, unsigned int input_len,
+ PRBool final)
+{
+ int blocks, bsize, padlen, pcount, padsize;
+ unsigned int max_needed, ifraglen, ofraglen, output_len;
+ unsigned char *pbuf;
+ SECStatus rv;
+
+ PORT_Assert (obj->encrypt);
+
+ /*
+ * Check that we have enough room for the output. Our caller should
+ * already handle this; failure is really an internal error (i.e. bug).
+ */
+ max_needed = sec_PKCS7EncryptLength (obj, input_len, final);
+ PORT_Assert (max_output_len >= max_needed);
+ if (max_output_len < max_needed) {
+ /* PORT_SetError (XXX); */
+ return SECFailure;
+ }
+
+ bsize = obj->block_size;
+ padsize = obj->pad_size;
+
+ /*
+ * When no blocking and padding work to do, we can simply call the
+ * cipher function and we are done.
+ */
+ if (bsize == 0) {
+ return (* obj->doit) (obj->cx, output, output_len_p, max_output_len,
+ input, input_len);
+ }
+
+ pcount = obj->pending_count;
+ pbuf = obj->pending_buf;
+
+ output_len = 0;
+
+ if (pcount) {
+ /*
+ * Try to fill in an entire block, starting with the bytes
+ * we already have saved away.
+ */
+ while (input_len && pcount < bsize) {
+ pbuf[pcount++] = *input++;
+ input_len--;
+ }
+ /*
+ * If we do not have a full block and we know we will be
+ * called again, then we are done for now.
+ */
+ if (pcount < bsize && !final) {
+ obj->pending_count = pcount;
+ if (output_len_p != NULL)
+ *output_len_p = 0;
+ return SECSuccess;
+ }
+ /*
+ * If we have a whole block available, encrypt it.
+ */
+ if ((padsize == 0) || (pcount % padsize) == 0) {
+ rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+ pbuf, pcount);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7EncryptLength needs to be made smarter!
+ */
+ PORT_Assert (ofraglen == pcount);
+
+ /*
+ * Account for the bytes now in output.
+ */
+ max_output_len -= ofraglen;
+ output_len += ofraglen;
+ output += ofraglen;
+
+ pcount = 0;
+ }
+ }
+
+ if (input_len) {
+ PORT_Assert (pcount == 0);
+
+ blocks = input_len / bsize;
+ ifraglen = blocks * bsize;
+
+ if (ifraglen) {
+ rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+ input, ifraglen);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7EncryptLength needs to be made smarter!
+ */
+ PORT_Assert (ifraglen == ofraglen);
+
+ max_output_len -= ofraglen;
+ output_len += ofraglen;
+ output += ofraglen;
+ }
+
+ pcount = input_len - ifraglen;
+ PORT_Assert (pcount < bsize);
+ if (pcount)
+ PORT_Memcpy (pbuf, input + ifraglen, pcount);
+ }
+
+ if (final) {
+ padlen = padsize - (pcount % padsize);
+ PORT_Memset (pbuf + pcount, padlen, padlen);
+ rv = (* obj->doit) (obj->cx, output, &ofraglen, max_output_len,
+ pbuf, pcount+padlen);
+ if (rv != SECSuccess)
+ return rv;
+
+ /*
+ * For now anyway, all of our ciphers have the same number of
+ * bytes of output as they do input. If this ever becomes untrue,
+ * then sec_PKCS7EncryptLength needs to be made smarter!
+ */
+ PORT_Assert (ofraglen == (pcount+padlen));
+ output_len += ofraglen;
+ } else {
+ obj->pending_count = pcount;
+ }
+
+ PORT_Assert (output_len_p != NULL || output_len == 0);
+ if (output_len_p != NULL)
+ *output_len_p = output_len;
+
+ return SECSuccess;
+}
+
+/*
+ * End of cipher stuff.
+ * -------------------------------------------------------------------
+ */
+
+
+/*
+ * -------------------------------------------------------------------
+ * XXX The following Attribute stuff really belongs elsewhere.
+ * The Attribute type is *not* part of pkcs7 but rather X.501.
+ * But for now, since PKCS7 is the only customer of attributes,
+ * we define them here. Once there is a use outside of PKCS7,
+ * then change the attribute types and functions from internal
+ * to external naming convention, and move them elsewhere!
+ */
+
+/*
+ * Look through a set of attributes and find one that matches the
+ * specified object ID. If "only" is true, then make sure that
+ * there is not more than one attribute of the same type. Otherwise,
+ * just return the first one found. (XXX Does anybody really want
+ * that first-found behavior? It was like that when I found it...)
+ */
+SEC_PKCS7Attribute *
+sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs, SECOidTag oidtag,
+ PRBool only)
+{
+ SECOidData *oid;
+ SEC_PKCS7Attribute *attr1, *attr2;
+
+ if (attrs == NULL)
+ return NULL;
+
+ oid = SECOID_FindOIDByTag(oidtag);
+ if (oid == NULL)
+ return NULL;
+
+ while ((attr1 = *attrs++) != NULL) {
+ if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
+ oid->oid.data,
+ oid->oid.len) == 0)
+ break;
+ }
+
+ if (attr1 == NULL)
+ return NULL;
+
+ if (!only)
+ return attr1;
+
+ while ((attr2 = *attrs++) != NULL) {
+ if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
+ oid->oid.data,
+ oid->oid.len) == 0)
+ break;
+ }
+
+ if (attr2 != NULL)
+ return NULL;
+
+ return attr1;
+}
+
+
+/*
+ * Return the single attribute value, doing some sanity checking first:
+ * - Multiple values are *not* expected.
+ * - Empty values are *not* expected.
+ */
+SECItem *
+sec_PKCS7AttributeValue(SEC_PKCS7Attribute *attr)
+{
+ SECItem *value;
+
+ if (attr == NULL)
+ return NULL;
+
+ value = attr->values[0];
+
+ if (value == NULL || value->data == NULL || value->len == 0)
+ return NULL;
+
+ if (attr->values[1] != NULL)
+ return NULL;
+
+ return value;
+}
+
+static const SEC_ASN1Template *
+sec_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS7Attribute *attribute;
+ SECOidData *oiddata;
+ PRBool encoded;
+
+ PORT_Assert (src_or_dest != NULL);
+ if (src_or_dest == NULL)
+ return NULL;
+
+ attribute = (SEC_PKCS7Attribute*)src_or_dest;
+
+ if (encoding && attribute->encoded)
+ return SEC_AnyTemplate;
+
+ oiddata = attribute->typeTag;
+ if (oiddata == NULL) {
+ oiddata = SECOID_FindOID(&attribute->type);
+ attribute->typeTag = oiddata;
+ }
+
+ if (oiddata == NULL) {
+ encoded = PR_TRUE;
+ theTemplate = SEC_AnyTemplate;
+ } else {
+ switch (oiddata->offset) {
+ default:
+ encoded = PR_TRUE;
+ theTemplate = SEC_AnyTemplate;
+ break;
+ case SEC_OID_PKCS9_EMAIL_ADDRESS:
+ case SEC_OID_RFC1274_MAIL:
+ case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
+ encoded = PR_FALSE;
+ theTemplate = SEC_IA5StringTemplate;
+ break;
+ case SEC_OID_PKCS9_CONTENT_TYPE:
+ encoded = PR_FALSE;
+ theTemplate = SEC_ObjectIDTemplate;
+ break;
+ case SEC_OID_PKCS9_MESSAGE_DIGEST:
+ encoded = PR_FALSE;
+ theTemplate = SEC_OctetStringTemplate;
+ break;
+ case SEC_OID_PKCS9_SIGNING_TIME:
+ encoded = PR_FALSE;
+ theTemplate = SEC_UTCTimeTemplate;
+ break;
+ /* XXX Want other types here, too */
+ }
+ }
+
+ if (encoding) {
+ /*
+ * If we are encoding and we think we have an already-encoded value,
+ * then the code which initialized this attribute should have set
+ * the "encoded" property to true (and we would have returned early,
+ * up above). No devastating error, but that code should be fixed.
+ * (It could indicate that the resulting encoded bytes are wrong.)
+ */
+ PORT_Assert (!encoded);
+ } else {
+ /*
+ * We are decoding; record whether the resulting value is
+ * still encoded or not.
+ */
+ attribute->encoded = encoded;
+ }
+ return theTemplate;
+}
+
+static SEC_ChooseASN1TemplateFunc sec_attr_chooser
+ = sec_attr_choose_attr_value_template;
+
+static const SEC_ASN1Template sec_pkcs7_attribute_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS7Attribute) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(SEC_PKCS7Attribute,type) },
+ { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7Attribute,values),
+ &sec_attr_chooser },
+ { 0 }
+};
+
+static const SEC_ASN1Template sec_pkcs7_set_of_attribute_template[] = {
+ { SEC_ASN1_SET_OF, 0, sec_pkcs7_attribute_template },
+};
+
+/*
+ * If you are wondering why this routine does not reorder the attributes
+ * first, and might be tempted to make it do so, see the comment by the
+ * call to ReorderAttributes in p7encode.c. (Or, see who else calls this
+ * and think long and hard about the implications of making it always
+ * do the reordering.)
+ */
+SECItem *
+sec_PKCS7EncodeAttributes (PRArenaPool *poolp, SECItem *dest, void *src)
+{
+ return SEC_ASN1EncodeItem (poolp, dest, src,
+ sec_pkcs7_set_of_attribute_template);
+}
+
+/*
+ * Make sure that the order of the attributes guarantees valid DER
+ * (which must be in lexigraphically ascending order for a SET OF);
+ * if reordering is necessary it will be done in place (in attrs).
+ */
+SECStatus
+sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs)
+{
+ PRArenaPool *poolp;
+ int num_attrs, i, j, pass, besti;
+ SECItem **enc_attrs;
+ SEC_PKCS7Attribute **new_attrs;
+
+ /*
+ * I think we should not be called with NULL. But if we are,
+ * call it a success anyway, because the order *is* okay.
+ */
+ PORT_Assert (attrs != NULL);
+ if (attrs == NULL)
+ return SECSuccess;
+
+ /*
+ * Count how many attributes we are dealing with here.
+ */
+ num_attrs = 0;
+ while (attrs[num_attrs] != NULL)
+ num_attrs++;
+
+ /*
+ * Again, I think we should have some attributes here.
+ * But if we do not, or if there is only one, then call it
+ * a success because it also already has a fine order.
+ */
+ PORT_Assert (num_attrs);
+ if (num_attrs == 0 || num_attrs == 1)
+ return SECSuccess;
+
+ /*
+ * Allocate an arena for us to work with, so it is easy to
+ * clean up all of the memory (fairly small pieces, really).
+ */
+ poolp = PORT_NewArena (1024); /* XXX what is right value? */
+ if (poolp == NULL)
+ return SECFailure; /* no memory; nothing we can do... */
+
+ /*
+ * Allocate arrays to hold the individual encodings which we will use
+ * for comparisons and the reordered attributes as they are sorted.
+ */
+ enc_attrs=(SECItem**)PORT_ArenaZAlloc(poolp, num_attrs*sizeof(SECItem *));
+ new_attrs = (SEC_PKCS7Attribute**)PORT_ArenaZAlloc (poolp,
+ num_attrs * sizeof(SEC_PKCS7Attribute *));
+ if (enc_attrs == NULL || new_attrs == NULL) {
+ PORT_FreeArena (poolp, PR_FALSE);
+ return SECFailure;
+ }
+
+ /*
+ * DER encode each individual attribute.
+ */
+ for (i = 0; i < num_attrs; i++) {
+ enc_attrs[i] = SEC_ASN1EncodeItem (poolp, NULL, attrs[i],
+ sec_pkcs7_attribute_template);
+ if (enc_attrs[i] == NULL) {
+ PORT_FreeArena (poolp, PR_FALSE);
+ return SECFailure;
+ }
+ }
+
+ /*
+ * Now compare and sort them; this is not the most efficient sorting
+ * method, but it is just fine for the problem at hand, because the
+ * number of attributes is (always) going to be small.
+ */
+ for (pass = 0; pass < num_attrs; pass++) {
+ /*
+ * Find the first not-yet-accepted attribute. (Once one is
+ * sorted into the other array, it is cleared from enc_attrs.)
+ */
+ for (i = 0; i < num_attrs; i++) {
+ if (enc_attrs[i] != NULL)
+ break;
+ }
+ PORT_Assert (i < num_attrs);
+ besti = i;
+
+ /*
+ * Find the lowest (lexigraphically) encoding. One that is
+ * shorter than all the rest is known to be "less" because each
+ * attribute is of the same type (a SEQUENCE) and so thus the
+ * first octet of each is the same, and the second octet is
+ * the length (or the length of the length with the high bit
+ * set, followed by the length, which also works out to always
+ * order the shorter first). Two (or more) that have the
+ * same length need to be compared byte by byte until a mismatch
+ * is found.
+ */
+ for (i = besti + 1; i < num_attrs; i++) {
+ if (enc_attrs[i] == NULL) /* slot already handled */
+ continue;
+
+ if (enc_attrs[i]->len != enc_attrs[besti]->len) {
+ if (enc_attrs[i]->len < enc_attrs[besti]->len)
+ besti = i;
+ continue;
+ }
+
+ for (j = 0; j < enc_attrs[i]->len; j++) {
+ if (enc_attrs[i]->data[j] < enc_attrs[besti]->data[j]) {
+ besti = i;
+ break;
+ }
+ }
+
+ /*
+ * For this not to be true, we would have to have encountered
+ * two *identical* attributes, which I think we should not see.
+ * So assert if it happens, but even if it does, let it go
+ * through; the ordering of the two does not matter.
+ */
+ PORT_Assert (j < enc_attrs[i]->len);
+ }
+
+ /*
+ * Now we have found the next-lowest one; copy it over and
+ * remove it from enc_attrs.
+ */
+ new_attrs[pass] = attrs[besti];
+ enc_attrs[besti] = NULL;
+ }
+
+ /*
+ * Now new_attrs has the attributes in the order we want;
+ * copy them back into the attrs array we started with.
+ */
+ for (i = 0; i < num_attrs; i++)
+ attrs[i] = new_attrs[i];
+
+ PORT_FreeArena (poolp, PR_FALSE);
+ return SECSuccess;
+}
+
+/*
+ * End of attribute stuff.
+ * -------------------------------------------------------------------
+ */
+
+
+/*
+ * Templates and stuff. Keep these at the end of the file.
+ */
+
+/* forward declaration */
+static const SEC_ASN1Template *
+sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding);
+
+static SEC_ChooseASN1TemplateFunc sec_pkcs7_chooser
+ = sec_pkcs7_choose_content_template;
+
+const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7ContentInfo) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(SEC_PKCS7ContentInfo,contentType) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_DYNAMIC | SEC_ASN1_MAY_STREAM
+ | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS7ContentInfo,content),
+ &sec_pkcs7_chooser },
+ { 0 }
+};
+
+/* XXX These names should change from external to internal convention. */
+
+static const SEC_ASN1Template SEC_PKCS7SignerInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS7SignerInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7SignerInfo,version) },
+ { SEC_ASN1_POINTER,
+ offsetof(SEC_PKCS7SignerInfo,issuerAndSN),
+ CERT_IssuerAndSNTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7SignerInfo,digestAlg),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS7SignerInfo,authAttr),
+ sec_pkcs7_set_of_attribute_template },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7SignerInfo,digestEncAlg),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS7SignerInfo,encDigest) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS7SignerInfo,unAuthAttr),
+ sec_pkcs7_set_of_attribute_template },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PKCS7SignedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7SignedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7SignedData,version) },
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7SignedData,digestAlgorithms),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7SignedData,contentInfo),
+ sec_PKCS7ContentInfoTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS7SignedData,rawCerts),
+ SEC_SetOfAnyTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS7SignedData,crls),
+ CERT_SetOfSignedCrlTemplate },
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7SignedData,signerInfos),
+ SEC_PKCS7SignerInfoTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7SignedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS7SignedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7RecipientInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS7RecipientInfo) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7RecipientInfo,version) },
+ { SEC_ASN1_POINTER,
+ offsetof(SEC_PKCS7RecipientInfo,issuerAndSN),
+ CERT_IssuerAndSNTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7RecipientInfo,keyEncAlg),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS7RecipientInfo,encKey) },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PKCS7EncryptedContentInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7EncryptedContentInfo) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(SEC_PKCS7EncryptedContentInfo,contentType) },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7EncryptedContentInfo,contentEncAlg),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_MAY_STREAM | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS7EncryptedContentInfo,encContent),
+ SEC_OctetStringTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PKCS7EnvelopedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7EnvelopedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7EnvelopedData,version) },
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7EnvelopedData,recipientInfos),
+ SEC_PKCS7RecipientInfoTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7EnvelopedData,encContentInfo),
+ SEC_PKCS7EncryptedContentInfoTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7EnvelopedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS7EnvelopedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7SignedAndEnvelopedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7SignedAndEnvelopedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,version) },
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,recipientInfos),
+ SEC_PKCS7RecipientInfoTemplate },
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,digestAlgorithms),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,encContentInfo),
+ SEC_PKCS7EncryptedContentInfoTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,rawCerts),
+ SEC_SetOfAnyTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,crls),
+ CERT_SetOfSignedCrlTemplate },
+ { SEC_ASN1_SET_OF,
+ offsetof(SEC_PKCS7SignedAndEnvelopedData,signerInfos),
+ SEC_PKCS7SignerInfoTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template
+SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS7SignedAndEnvelopedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7DigestedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7DigestedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7DigestedData,version) },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7DigestedData,digestAlg),
+ SECOID_AlgorithmIDTemplate },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7DigestedData,contentInfo),
+ sec_PKCS7ContentInfoTemplate },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS7DigestedData,digest) },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7DigestedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS7DigestedDataTemplate }
+};
+
+static const SEC_ASN1Template SEC_PKCS7EncryptedDataTemplate[] = {
+ { SEC_ASN1_SEQUENCE | SEC_ASN1_MAY_STREAM,
+ 0, NULL, sizeof(SEC_PKCS7EncryptedData) },
+ { SEC_ASN1_INTEGER,
+ offsetof(SEC_PKCS7EncryptedData,version) },
+ { SEC_ASN1_INLINE,
+ offsetof(SEC_PKCS7EncryptedData,encContentInfo),
+ SEC_PKCS7EncryptedContentInfoTemplate },
+ { 0 }
+};
+
+static const SEC_ASN1Template SEC_PointerToPKCS7EncryptedDataTemplate[] = {
+ { SEC_ASN1_POINTER, 0, SEC_PKCS7EncryptedDataTemplate }
+};
+
+const SEC_ASN1Template SEC_SMIMEKEAParamTemplateSkipjack[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) },
+ { SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_SMIMEKEAParamTemplateNoSkipjack[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) },
+ { SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) },
+ { SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,nonSkipjackIV) },
+ { 0 }
+};
+
+const SEC_ASN1Template SEC_SMIMEKEAParamTemplateAllParams[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SEC_PKCS7SMIMEKEAParameters) },
+ { SEC_ASN1_OCTET_STRING /* | SEC_ASN1_OPTIONAL */,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,originatorKEAKey) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,originatorRA) },
+ { SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,nonSkipjackIV) },
+ { SEC_ASN1_OCTET_STRING | SEC_ASN1_OPTIONAL ,
+ offsetof(SEC_PKCS7SMIMEKEAParameters,bulkKeySize) },
+ { 0 }
+};
+
+const SEC_ASN1Template*
+sec_pkcs7_get_kea_template(SECKEATemplateSelector whichTemplate)
+{
+ const SEC_ASN1Template *returnVal = NULL;
+
+ switch(whichTemplate)
+ {
+ case SECKEAUsesNonSkipjack:
+ returnVal = SEC_SMIMEKEAParamTemplateNoSkipjack;
+ break;
+ case SECKEAUsesSkipjack:
+ returnVal = SEC_SMIMEKEAParamTemplateSkipjack;
+ break;
+ case SECKEAUsesNonSkipjackWithPaddedEncKey:
+ default:
+ returnVal = SEC_SMIMEKEAParamTemplateAllParams;
+ break;
+ }
+ return returnVal;
+}
+
+static const SEC_ASN1Template *
+sec_pkcs7_choose_content_template(void *src_or_dest, PRBool encoding)
+{
+ const SEC_ASN1Template *theTemplate;
+ SEC_PKCS7ContentInfo *cinfo;
+ SECOidTag kind;
+
+ PORT_Assert (src_or_dest != NULL);
+ if (src_or_dest == NULL)
+ return NULL;
+
+ cinfo = (SEC_PKCS7ContentInfo*)src_or_dest;
+ kind = SEC_PKCS7ContentType (cinfo);
+ switch (kind) {
+ default:
+ theTemplate = SEC_PointerToAnyTemplate;
+ break;
+ case SEC_OID_PKCS7_DATA:
+ theTemplate = SEC_PointerToOctetStringTemplate;
+ break;
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ theTemplate = SEC_PointerToPKCS7SignedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ theTemplate = SEC_PointerToPKCS7EnvelopedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_SIGNED_ENVELOPED_DATA:
+ theTemplate = SEC_PointerToPKCS7SignedAndEnvelopedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_DIGESTED_DATA:
+ theTemplate = SEC_PointerToPKCS7DigestedDataTemplate;
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ theTemplate = SEC_PointerToPKCS7EncryptedDataTemplate;
+ break;
+ }
+ return theTemplate;
+}
+
+/*
+ * End of templates. Do not add stuff after this; put new code
+ * up above the start of the template definitions.
+ */
diff --git a/security/nss/lib/pkcs7/p7local.h b/security/nss/lib/pkcs7/p7local.h
new file mode 100644
index 000000000..80640c930
--- /dev/null
+++ b/security/nss/lib/pkcs7/p7local.h
@@ -0,0 +1,176 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Support routines for PKCS7 implementation, none of which are exported.
+ * This file should only contain things that are needed by both the
+ * encoding/creation side *and* the decoding/decryption side. Anything
+ * else should just be static routines in the appropriate file.
+ *
+ * Do not export this file! If something in here is really needed outside
+ * of pkcs7 code, first try to add a PKCS7 interface which will do it for
+ * you. If that has a problem, then just move out what you need, changing
+ * its name as appropriate!
+ *
+ * $Id$
+ */
+
+#ifndef _P7LOCAL_H_
+#define _P7LOCAL_H_
+
+#include "secpkcs7.h"
+#include "secasn1t.h"
+
+extern const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[];
+
+/* opaque objects */
+typedef struct sec_pkcs7_cipher_object sec_PKCS7CipherObject;
+
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/*
+ * Look through a set of attributes and find one that matches the
+ * specified object ID. If "only" is true, then make sure that
+ * there is not more than one attribute of the same type. Otherwise,
+ * just return the first one found. (XXX Does anybody really want
+ * that first-found behavior? It was like that when I found it...)
+ */
+extern SEC_PKCS7Attribute *sec_PKCS7FindAttribute (SEC_PKCS7Attribute **attrs,
+ SECOidTag oidtag,
+ PRBool only);
+/*
+ * Return the single attribute value, doing some sanity checking first:
+ * - Multiple values are *not* expected.
+ * - Empty values are *not* expected.
+ */
+extern SECItem *sec_PKCS7AttributeValue (SEC_PKCS7Attribute *attr);
+
+/*
+ * Encode a set of attributes (found in "src").
+ */
+extern SECItem *sec_PKCS7EncodeAttributes (PRArenaPool *poolp,
+ SECItem *dest, void *src);
+
+/*
+ * Make sure that the order of the attributes guarantees valid DER
+ * (which must be in lexigraphically ascending order for a SET OF);
+ * if reordering is necessary it will be done in place (in attrs).
+ */
+extern SECStatus sec_PKCS7ReorderAttributes (SEC_PKCS7Attribute **attrs);
+
+
+/*
+ * Create a context for decrypting, based on the given key and algorithm.
+ */
+extern sec_PKCS7CipherObject *
+sec_PKCS7CreateDecryptObject (PK11SymKey *key, SECAlgorithmID *algid);
+
+/*
+ * Create a context for encrypting, based on the given key and algorithm,
+ * and fill in the algorithm id.
+ */
+extern sec_PKCS7CipherObject *
+sec_PKCS7CreateEncryptObject (PRArenaPool *poolp, PK11SymKey *key,
+ SECOidTag algtag, SECAlgorithmID *algid);
+
+/*
+ * Destroy the given decryption or encryption object.
+ */
+extern void sec_PKCS7DestroyDecryptObject (sec_PKCS7CipherObject *obj);
+extern void sec_PKCS7DestroyEncryptObject (sec_PKCS7CipherObject *obj);
+
+/*
+ * What will be the output length of the next call to encrypt/decrypt?
+ * Result can be used to perform memory allocations. Note that the amount
+ * is exactly accurate only when not doing a block cipher or when final
+ * is false, otherwise it is an upper bound on the amount because until
+ * we see the data we do not know how many padding bytes there are
+ * (always between 1 and the cipher block size).
+ *
+ * Note that this can return zero, which does not mean that the cipher
+ * operation can be skipped! (It simply means that there are not enough
+ * bytes to make up an entire block; the bytes will be reserved until
+ * there are enough to encrypt/decrypt at least one block.) However,
+ * if zero is returned it *does* mean that no output buffer need be
+ * passed in to the subsequent cipher operation, as no output bytes
+ * will be stored.
+ */
+extern unsigned int sec_PKCS7DecryptLength (sec_PKCS7CipherObject *obj,
+ unsigned int input_len,
+ PRBool final);
+extern unsigned int sec_PKCS7EncryptLength (sec_PKCS7CipherObject *obj,
+ unsigned int input_len,
+ PRBool final);
+
+/*
+ * Decrypt a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the decrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "obj" is the return value from sec_PKCS7CreateDecryptObject.
+ * When "final" is true, this is the last of the data to be decrypted.
+ */
+extern SECStatus sec_PKCS7Decrypt (sec_PKCS7CipherObject *obj,
+ unsigned char *output,
+ unsigned int *output_len_p,
+ unsigned int max_output_len,
+ const unsigned char *input,
+ unsigned int input_len,
+ PRBool final);
+
+/*
+ * Encrypt a given length of input buffer (starting at "input" and
+ * containing "input_len" bytes), placing the encrypted bytes in
+ * "output" and storing the output length in "*output_len_p".
+ * "obj" is the return value from sec_PKCS7CreateEncryptObject.
+ * When "final" is true, this is the last of the data to be encrypted.
+ */
+extern SECStatus sec_PKCS7Encrypt (sec_PKCS7CipherObject *obj,
+ unsigned char *output,
+ unsigned int *output_len_p,
+ unsigned int max_output_len,
+ const unsigned char *input,
+ unsigned int input_len,
+ PRBool final);
+
+/* return the correct kea template based on the template selector. skipjack
+ * does not have the extra IV.
+ */
+const SEC_ASN1Template *
+sec_pkcs7_get_kea_template(SECKEATemplateSelector whichTemplate);
+
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _P7LOCAL_H_ */
diff --git a/security/nss/lib/pkcs7/pkcs7t.h b/security/nss/lib/pkcs7/pkcs7t.h
new file mode 100644
index 000000000..73b23014d
--- /dev/null
+++ b/security/nss/lib/pkcs7/pkcs7t.h
@@ -0,0 +1,292 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Header for pkcs7 types.
+ *
+ * $Id$
+ */
+
+#ifndef _PKCS7T_H_
+#define _PKCS7T_H_
+
+#include "plarena.h"
+
+#include "seccomon.h"
+#include "secoidt.h"
+#include "certt.h"
+#include "secmodt.h"
+
+/* Opaque objects */
+typedef struct SEC_PKCS7DecoderContextStr SEC_PKCS7DecoderContext;
+typedef struct SEC_PKCS7EncoderContextStr SEC_PKCS7EncoderContext;
+
+/* Non-opaque objects. NOTE, though: I want them to be treated as
+ * opaque as much as possible. If I could hide them completely,
+ * I would. (I tried, but ran into trouble that was taking me too
+ * much time to get out of.) I still intend to try to do so.
+ * In fact, the only type that "outsiders" should even *name* is
+ * SEC_PKCS7ContentInfo, and they should not reference its fields.
+ */
+/* rjr: PKCS #11 cert handling (pk11cert.c) does use SEC_PKCS7RecipientInfo's.
+ * This is because when we search the recipient list for the cert and key we
+ * want, we need to invert the order of the loops we used to have. The old
+ * loops were:
+ *
+ * For each recipient {
+ * find_cert = PK11_Find_AllCert(recipient->issuerSN);
+ * [which unrolls to... ]
+ * For each slot {
+ * Log into slot;
+ * search slot for cert;
+ * }
+ * }
+ *
+ * the new loop searchs all the recipients at once on a slot. this allows
+ * PKCS #11 to order slots in such a way that logout slots don't get checked
+ * if we can find the cert on a logged in slot. This eliminates lots of
+ * spurious password prompts when smart cards are installed... so why this
+ * comment? If you make SEC_PKCS7RecipientInfo completely opaque, you need
+ * to provide a non-opaque list of issuerSN's (the only field PKCS#11 needs
+ * and fix up pk11cert.c first. NOTE: Only S/MIME calls this special PKCS #11
+ * function.
+ */
+typedef struct SEC_PKCS7ContentInfoStr SEC_PKCS7ContentInfo;
+typedef struct SEC_PKCS7SignedDataStr SEC_PKCS7SignedData;
+typedef struct SEC_PKCS7EncryptedContentInfoStr SEC_PKCS7EncryptedContentInfo;
+typedef struct SEC_PKCS7EnvelopedDataStr SEC_PKCS7EnvelopedData;
+typedef struct SEC_PKCS7SignedAndEnvelopedDataStr
+ SEC_PKCS7SignedAndEnvelopedData;
+typedef struct SEC_PKCS7SignerInfoStr SEC_PKCS7SignerInfo;
+typedef struct SEC_PKCS7RecipientInfoStr SEC_PKCS7RecipientInfo;
+typedef struct SEC_PKCS7DigestedDataStr SEC_PKCS7DigestedData;
+typedef struct SEC_PKCS7EncryptedDataStr SEC_PKCS7EncryptedData;
+typedef struct SEC_PKCS7SMIMEKEAParametersStr SEC_PKCS7SMIMEKEAParameters;
+/*
+ * The following is not actually a PKCS7 type, but for now it is only
+ * used by PKCS7, so we have adopted it. If someone else *ever* needs
+ * it, its name should be changed and it should be moved out of here.
+ * Do not dare to use it without doing so!
+ */
+typedef struct SEC_PKCS7AttributeStr SEC_PKCS7Attribute;
+
+
+struct SEC_PKCS7ContentInfoStr {
+ PRArenaPool *poolp; /* local; not part of encoding */
+ PRBool created; /* local; not part of encoding */
+ int refCount; /* local; not part of encoding */
+ SECOidData *contentTypeTag; /* local; not part of encoding */
+ SECKEYGetPasswordKey pwfn; /* local; not part of encoding */
+ void *pwfn_arg; /* local; not part of encoding */
+ SECItem contentType;
+ union {
+ SECItem *data;
+ SEC_PKCS7DigestedData *digestedData;
+ SEC_PKCS7EncryptedData *encryptedData;
+ SEC_PKCS7EnvelopedData *envelopedData;
+ SEC_PKCS7SignedData *signedData;
+ SEC_PKCS7SignedAndEnvelopedData *signedAndEnvelopedData;
+ } content;
+};
+
+struct SEC_PKCS7SignedDataStr {
+ SECItem version;
+ SECAlgorithmID **digestAlgorithms;
+ SEC_PKCS7ContentInfo contentInfo;
+ SECItem **rawCerts;
+ CERTSignedCrl **crls;
+ SEC_PKCS7SignerInfo **signerInfos;
+ SECItem **digests; /* local; not part of encoding */
+ CERTCertificate **certs; /* local; not part of encoding */
+ CERTCertificateList **certLists; /* local; not part of encoding */
+};
+#define SEC_PKCS7_SIGNED_DATA_VERSION 1 /* what we *create* */
+
+struct SEC_PKCS7EncryptedContentInfoStr {
+ SECOidData *contentTypeTag; /* local; not part of encoding */
+ SECItem contentType;
+ SECAlgorithmID contentEncAlg;
+ SECItem encContent;
+ SECItem plainContent; /* local; not part of encoding */
+ /* bytes not encrypted, but encoded */
+ int keysize; /* local; not part of encoding */
+ /* size of bulk encryption key
+ * (only used by creation code) */
+ SECOidTag encalg; /* local; not part of encoding */
+ /* oid tag of encryption algorithm
+ * (only used by creation code) */
+};
+
+struct SEC_PKCS7EnvelopedDataStr {
+ SECItem version;
+ SEC_PKCS7RecipientInfo **recipientInfos;
+ SEC_PKCS7EncryptedContentInfo encContentInfo;
+};
+#define SEC_PKCS7_ENVELOPED_DATA_VERSION 0 /* what we *create* */
+
+struct SEC_PKCS7SignedAndEnvelopedDataStr {
+ SECItem version;
+ SEC_PKCS7RecipientInfo **recipientInfos;
+ SECAlgorithmID **digestAlgorithms;
+ SEC_PKCS7EncryptedContentInfo encContentInfo;
+ SECItem **rawCerts;
+ CERTSignedCrl **crls;
+ SEC_PKCS7SignerInfo **signerInfos;
+ SECItem **digests; /* local; not part of encoding */
+ CERTCertificate **certs; /* local; not part of encoding */
+ CERTCertificateList **certLists; /* local; not part of encoding */
+ PK11SymKey *sigKey; /* local; not part of encoding */
+};
+#define SEC_PKCS7_SIGNED_AND_ENVELOPED_DATA_VERSION 1 /* what we *create* */
+
+struct SEC_PKCS7SignerInfoStr {
+ SECItem version;
+ CERTIssuerAndSN *issuerAndSN;
+ SECAlgorithmID digestAlg;
+ SEC_PKCS7Attribute **authAttr;
+ SECAlgorithmID digestEncAlg;
+ SECItem encDigest;
+ SEC_PKCS7Attribute **unAuthAttr;
+ CERTCertificate *cert; /* local; not part of encoding */
+ CERTCertificateList *certList; /* local; not part of encoding */
+};
+#define SEC_PKCS7_SIGNER_INFO_VERSION 1 /* what we *create* */
+
+struct SEC_PKCS7RecipientInfoStr {
+ SECItem version;
+ CERTIssuerAndSN *issuerAndSN;
+ SECAlgorithmID keyEncAlg;
+ SECItem encKey;
+ CERTCertificate *cert; /* local; not part of encoding */
+};
+#define SEC_PKCS7_RECIPIENT_INFO_VERSION 0 /* what we *create* */
+
+struct SEC_PKCS7DigestedDataStr {
+ SECItem version;
+ SECAlgorithmID digestAlg;
+ SEC_PKCS7ContentInfo contentInfo;
+ SECItem digest;
+};
+#define SEC_PKCS7_DIGESTED_DATA_VERSION 0 /* what we *create* */
+
+struct SEC_PKCS7EncryptedDataStr {
+ SECItem version;
+ SEC_PKCS7EncryptedContentInfo encContentInfo;
+};
+#define SEC_PKCS7_ENCRYPTED_DATA_VERSION 0 /* what we *create* */
+
+/*
+ * See comment above about this type not really belonging to PKCS7.
+ */
+struct SEC_PKCS7AttributeStr {
+ /* The following fields make up an encoded Attribute: */
+ SECItem type;
+ SECItem **values; /* data may or may not be encoded */
+ /* The following fields are not part of an encoded Attribute: */
+ SECOidData *typeTag;
+ PRBool encoded; /* when true, values are encoded */
+};
+
+/* An enumerated type used to select templates based on the encryption
+ scenario and data specifics. */
+typedef enum
+{
+ SECKEAUsesSkipjack,
+ SECKEAUsesNonSkipjack,
+ SECKEAUsesNonSkipjackWithPaddedEncKey
+} SECKEATemplateSelector;
+
+/* ### mwelch - S/MIME KEA parameters. These don't really fit here,
+ but I cannot think of a more appropriate place at this time. */
+struct SEC_PKCS7SMIMEKEAParametersStr {
+ SECItem originatorKEAKey; /* sender KEA key (encrypted?) */
+ SECItem originatorRA; /* random number generated by sender */
+ SECItem nonSkipjackIV; /* init'n vector for SkipjackCBC64
+ decryption of KEA key if Skipjack
+ is not the bulk algorithm used on
+ the message */
+ SECItem bulkKeySize; /* if Skipjack is not the bulk
+ algorithm used on the message,
+ and the size of the bulk encryption
+ key is not the same as that of
+ originatorKEAKey (due to padding
+ perhaps), this field will contain
+ the real size of the bulk encryption
+ key. */
+};
+
+/*
+ * Type of function passed to SEC_PKCS7Decode or SEC_PKCS7DecoderStart.
+ * If specified, this is where the content bytes (only) will be "sent"
+ * as they are recovered during the decoding.
+ *
+ * XXX Should just combine this with SEC_PKCS7EncoderContentCallback type
+ * and use a simpler, common name.
+ */
+typedef void (* SEC_PKCS7DecoderContentCallback)(void *arg,
+ const char *buf,
+ unsigned long len);
+
+/*
+ * Type of function passed to SEC_PKCS7Encode or SEC_PKCS7EncoderStart.
+ * This is where the encoded bytes will be "sent".
+ *
+ * XXX Should just combine this with SEC_PKCS7DecoderContentCallback type
+ * and use a simpler, common name.
+ */
+typedef void (* SEC_PKCS7EncoderOutputCallback)(void *arg,
+ const char *buf,
+ unsigned long len);
+
+
+/*
+ * Type of function passed to SEC_PKCS7Decode or SEC_PKCS7DecoderStart
+ * to retrieve the decryption key. This function is inteded to be
+ * used for EncryptedData content info's which do not have a key available
+ * in a certificate, etc.
+ */
+typedef PK11SymKey * (* SEC_PKCS7GetDecryptKeyCallback)(void *arg,
+ SECAlgorithmID *algid);
+
+/*
+ * Type of function passed to SEC_PKCS7Decode or SEC_PKCS7DecoderStart.
+ * This function in intended to be used to verify that decrypting a
+ * particular crypto algorithm is allowed. Content types which do not
+ * require decryption will not need the callback. If the callback
+ * is not specified for content types which require decryption, the
+ * decryption will be disallowed.
+ */
+typedef PRBool (* SEC_PKCS7DecryptionAllowedCallback)(SECAlgorithmID *algid,
+ PK11SymKey *bulkkey);
+
+#endif /* _PKCS7T_H_ */
diff --git a/security/nss/lib/pkcs7/secmime.c b/security/nss/lib/pkcs7/secmime.c
new file mode 100644
index 000000000..017896bd7
--- /dev/null
+++ b/security/nss/lib/pkcs7/secmime.c
@@ -0,0 +1,901 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Stuff specific to S/MIME policy and interoperability.
+ * Depends on PKCS7, but there should be no dependency the other way around.
+ *
+ * $Id$
+ */
+
+#include "secmime.h"
+#include "secoid.h"
+#include "pk11func.h"
+#include "ciferfam.h" /* for CIPHER_FAMILY symbols */
+#include "secasn1.h"
+#include "secitem.h"
+#include "cert.h"
+#include "key.h"
+#include "secerr.h"
+
+typedef struct smime_cipher_map_struct {
+ unsigned long cipher;
+ SECOidTag algtag;
+ SECItem *parms;
+} smime_cipher_map;
+
+/*
+ * These are macros because I think some subsequent parameters,
+ * like those for RC5, will want to use them, too, separately.
+ */
+#define SMIME_DER_INTVAL_16 SEC_ASN1_INTEGER, 0x01, 0x10
+#define SMIME_DER_INTVAL_40 SEC_ASN1_INTEGER, 0x01, 0x28
+#define SMIME_DER_INTVAL_64 SEC_ASN1_INTEGER, 0x01, 0x40
+#define SMIME_DER_INTVAL_128 SEC_ASN1_INTEGER, 0x02, 0x00, 0x80
+
+#ifdef SMIME_DOES_RC5 /* will be needed; quiet unused warning for now */
+static unsigned char smime_int16[] = { SMIME_DER_INTVAL_16 };
+#endif
+static unsigned char smime_int40[] = { SMIME_DER_INTVAL_40 };
+static unsigned char smime_int64[] = { SMIME_DER_INTVAL_64 };
+static unsigned char smime_int128[] = { SMIME_DER_INTVAL_128 };
+
+static SECItem smime_rc2p40 = { siBuffer, smime_int40, sizeof(smime_int40) };
+static SECItem smime_rc2p64 = { siBuffer, smime_int64, sizeof(smime_int64) };
+static SECItem smime_rc2p128 = { siBuffer, smime_int128, sizeof(smime_int128) };
+
+static smime_cipher_map smime_cipher_maps[] = {
+ { SMIME_RC2_CBC_40, SEC_OID_RC2_CBC, &smime_rc2p40 },
+ { SMIME_RC2_CBC_64, SEC_OID_RC2_CBC, &smime_rc2p64 },
+ { SMIME_RC2_CBC_128, SEC_OID_RC2_CBC, &smime_rc2p128 },
+#ifdef SMIME_DOES_RC5
+ { SMIME_RC5PAD_64_16_40, SEC_OID_RC5_CBC_PAD, &smime_rc5p40 },
+ { SMIME_RC5PAD_64_16_64, SEC_OID_RC5_CBC_PAD, &smime_rc5p64 },
+ { SMIME_RC5PAD_64_16_128, SEC_OID_RC5_CBC_PAD, &smime_rc5p128 },
+#endif
+ { SMIME_DES_CBC_56, SEC_OID_DES_CBC, NULL },
+ { SMIME_DES_EDE3_168, SEC_OID_DES_EDE3_CBC, NULL },
+ { SMIME_FORTEZZA, SEC_OID_FORTEZZA_SKIPJACK, NULL}
+};
+
+/*
+ * Note, the following value really just needs to be an upper bound
+ * on the ciphers.
+ */
+static const int smime_symmetric_count = sizeof(smime_cipher_maps)
+ / sizeof(smime_cipher_map);
+
+static unsigned long *smime_prefs, *smime_newprefs;
+static int smime_current_pref_index = 0;
+static PRBool smime_prefs_complete = PR_FALSE;
+static PRBool smime_prefs_changed = PR_TRUE;
+
+static unsigned long smime_policy_bits = 0;
+
+
+static int
+smime_mapi_by_cipher (unsigned long cipher)
+{
+ int i;
+
+ for (i = 0; i < smime_symmetric_count; i++) {
+ if (smime_cipher_maps[i].cipher == cipher)
+ break;
+ }
+
+ if (i == smime_symmetric_count)
+ return -1;
+
+ return i;
+}
+
+
+/*
+ * this function locally records the user's preference
+ */
+SECStatus
+SECMIME_EnableCipher(long which, int on)
+{
+ unsigned long mask;
+
+ if (smime_newprefs == NULL || smime_prefs_complete) {
+ /*
+ * This is either the very first time, or we are starting over.
+ */
+ smime_newprefs = (unsigned long*)PORT_ZAlloc (smime_symmetric_count
+ * sizeof(*smime_newprefs));
+ if (smime_newprefs == NULL)
+ return SECFailure;
+ smime_current_pref_index = 0;
+ smime_prefs_complete = PR_FALSE;
+ }
+
+ mask = which & CIPHER_FAMILYID_MASK;
+ if (mask == CIPHER_FAMILYID_MASK) {
+ /*
+ * This call signifies that all preferences have been set.
+ * Move "newprefs" over, after checking first whether or
+ * not the new ones are different from the old ones.
+ */
+ if (smime_prefs != NULL) {
+ if (PORT_Memcmp (smime_prefs, smime_newprefs,
+ smime_symmetric_count * sizeof(*smime_prefs)) == 0)
+ smime_prefs_changed = PR_FALSE;
+ else
+ smime_prefs_changed = PR_TRUE;
+ PORT_Free (smime_prefs);
+ }
+
+ smime_prefs = smime_newprefs;
+ smime_prefs_complete = PR_TRUE;
+ return SECSuccess;
+ }
+
+ PORT_Assert (mask == CIPHER_FAMILYID_SMIME);
+ if (mask != CIPHER_FAMILYID_SMIME) {
+ /* XXX set an error! */
+ return SECFailure;
+ }
+
+ if (on) {
+ PORT_Assert (smime_current_pref_index < smime_symmetric_count);
+ if (smime_current_pref_index >= smime_symmetric_count) {
+ /* XXX set an error! */
+ return SECFailure;
+ }
+
+ smime_newprefs[smime_current_pref_index++] = which;
+ }
+
+ return SECSuccess;
+}
+
+
+/*
+ * this function locally records the export policy
+ */
+SECStatus
+SECMIME_SetPolicy(long which, int on)
+{
+ unsigned long mask;
+
+ PORT_Assert ((which & CIPHER_FAMILYID_MASK) == CIPHER_FAMILYID_SMIME);
+ if ((which & CIPHER_FAMILYID_MASK) != CIPHER_FAMILYID_SMIME) {
+ /* XXX set an error! */
+ return SECFailure;
+ }
+
+ which &= ~CIPHER_FAMILYID_MASK;
+
+ PORT_Assert (which < 32); /* bits in the long */
+ if (which >= 32) {
+ /* XXX set an error! */
+ return SECFailure;
+ }
+
+ mask = 1UL << which;
+
+ if (on) {
+ smime_policy_bits |= mask;
+ } else {
+ smime_policy_bits &= ~mask;
+ }
+
+ return SECSuccess;
+}
+
+
+/*
+ * Based on the given algorithm (including its parameters, in some cases!)
+ * and the given key (may or may not be inspected, depending on the
+ * algorithm), find the appropriate policy algorithm specification
+ * and return it. If no match can be made, -1 is returned.
+ */
+static long
+smime_policy_algorithm (SECAlgorithmID *algid, PK11SymKey *key)
+{
+ SECOidTag algtag;
+
+ algtag = SECOID_GetAlgorithmTag (algid);
+ switch (algtag) {
+ case SEC_OID_RC2_CBC:
+ {
+ unsigned int keylen_bits;
+
+ keylen_bits = PK11_GetKeyStrength (key, algid);
+ switch (keylen_bits) {
+ case 40:
+ return SMIME_RC2_CBC_40;
+ case 64:
+ return SMIME_RC2_CBC_64;
+ case 128:
+ return SMIME_RC2_CBC_128;
+ default:
+ break;
+ }
+ }
+ break;
+ case SEC_OID_DES_CBC:
+ return SMIME_DES_CBC_56;
+ case SEC_OID_DES_EDE3_CBC:
+ return SMIME_DES_EDE3_168;
+ case SEC_OID_FORTEZZA_SKIPJACK:
+ return SMIME_FORTEZZA;
+#ifdef SMIME_DOES_RC5
+ case SEC_OID_RC5_CBC_PAD:
+ PORT_Assert (0); /* XXX need to pull out parameters and match */
+ break;
+#endif
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+
+static PRBool
+smime_cipher_allowed (unsigned long which)
+{
+ unsigned long mask;
+
+ which &= ~CIPHER_FAMILYID_MASK;
+ PORT_Assert (which < 32); /* bits per long (min) */
+ if (which >= 32)
+ return PR_FALSE;
+
+ mask = 1UL << which;
+ if ((mask & smime_policy_bits) == 0)
+ return PR_FALSE;
+
+ return PR_TRUE;
+}
+
+
+PRBool
+SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key)
+{
+ long which;
+
+ which = smime_policy_algorithm (algid, key);
+ if (which < 0)
+ return PR_FALSE;
+
+ return smime_cipher_allowed ((unsigned long)which);
+}
+
+
+/*
+ * Does the current policy allow *any* S/MIME encryption (or decryption)?
+ *
+ * This tells whether or not *any* S/MIME encryption can be done,
+ * according to policy. Callers may use this to do nicer user interface
+ * (say, greying out a checkbox so a user does not even try to encrypt
+ * a message when they are not allowed to) or for any reason they want
+ * to check whether S/MIME encryption (or decryption, for that matter)
+ * may be done.
+ *
+ * It takes no arguments. The return value is a simple boolean:
+ * PR_TRUE means encryption (or decryption) is *possible*
+ * (but may still fail due to other reasons, like because we cannot
+ * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
+ * PR_FALSE means encryption (or decryption) is not permitted
+ *
+ * There are no errors from this routine.
+ */
+PRBool
+SECMIME_EncryptionPossible (void)
+{
+ if (smime_policy_bits != 0)
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+/*
+ * XXX Would like the "parameters" field to be a SECItem *, but the
+ * encoder is having trouble with optional pointers to an ANY. Maybe
+ * once that is fixed, can change this back...
+ */
+typedef struct smime_capability_struct {
+ unsigned long cipher; /* local; not part of encoding */
+ SECOidTag capIDTag; /* local; not part of encoding */
+ SECItem capabilityID;
+ SECItem parameters;
+} smime_capability;
+
+static const SEC_ASN1Template smime_capability_template[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(smime_capability) },
+ { SEC_ASN1_OBJECT_ID,
+ offsetof(smime_capability,capabilityID), },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
+ offsetof(smime_capability,parameters), },
+ { 0, }
+};
+
+static const SEC_ASN1Template smime_capabilities_template[] = {
+ { SEC_ASN1_SEQUENCE_OF, 0, smime_capability_template }
+};
+
+
+
+static void
+smime_fill_capability (smime_capability *cap)
+{
+ unsigned long cipher;
+ SECOidTag algtag;
+ int i;
+
+ algtag = SECOID_FindOIDTag (&(cap->capabilityID));
+
+ for (i = 0; i < smime_symmetric_count; i++) {
+ if (smime_cipher_maps[i].algtag != algtag)
+ continue;
+ /*
+ * XXX If SECITEM_CompareItem allowed NULLs as arguments (comparing
+ * 2 NULLs as equal and NULL and non-NULL as not equal), we could
+ * use that here instead of all of the following comparison code.
+ */
+ if (cap->parameters.data != NULL) {
+ if (smime_cipher_maps[i].parms == NULL)
+ continue;
+ if (cap->parameters.len != smime_cipher_maps[i].parms->len)
+ continue;
+ if (PORT_Memcmp (cap->parameters.data,
+ smime_cipher_maps[i].parms->data,
+ cap->parameters.len) == 0)
+ break;
+ } else if (smime_cipher_maps[i].parms == NULL) {
+ break;
+ }
+ }
+
+ if (i == smime_symmetric_count)
+ cipher = 0;
+ else
+ cipher = smime_cipher_maps[i].cipher;
+
+ cap->cipher = cipher;
+ cap->capIDTag = algtag;
+}
+
+
+static long
+smime_choose_cipher (CERTCertificate *scert, CERTCertificate **rcerts)
+{
+ PRArenaPool *poolp;
+ long chosen_cipher;
+ int *cipher_abilities;
+ int *cipher_votes;
+ int strong_mapi;
+ int rcount, mapi, max, i;
+ PRBool isFortezza = PK11_FortezzaHasKEA(scert);
+
+ if (smime_policy_bits == 0) {
+ PORT_SetError (SEC_ERROR_BAD_EXPORT_ALGORITHM);
+ return -1;
+ }
+
+ chosen_cipher = SMIME_RC2_CBC_40; /* the default, LCD */
+
+ poolp = PORT_NewArena (1024); /* XXX what is right value? */
+ if (poolp == NULL)
+ goto done;
+
+ cipher_abilities = (int*)PORT_ArenaZAlloc (poolp,
+ smime_symmetric_count * sizeof(int));
+ if (cipher_abilities == NULL)
+ goto done;
+
+ cipher_votes = (int*)PORT_ArenaZAlloc (poolp,
+ smime_symmetric_count * sizeof(int));
+ if (cipher_votes == NULL)
+ goto done;
+
+ /*
+ * XXX Should have a #define somewhere which specifies default
+ * strong cipher. (Or better, a way to configure, which would
+ * take Fortezza into account as well.)
+ */
+
+ /* If the user has the Fortezza preference turned on, make
+ * that the strong cipher. Otherwise, use triple-DES. */
+ strong_mapi = -1;
+ if (isFortezza) {
+ for(i=0;i < smime_current_pref_index && strong_mapi < 0;i++)
+ {
+ if (smime_prefs[i] == SMIME_FORTEZZA)
+ strong_mapi = smime_mapi_by_cipher(SMIME_FORTEZZA);
+ }
+ }
+
+ if (strong_mapi == -1)
+ strong_mapi = smime_mapi_by_cipher (SMIME_DES_EDE3_168);
+
+ PORT_Assert (strong_mapi >= 0);
+
+ for (rcount = 0; rcerts[rcount] != NULL; rcount++) {
+ SECItem *profile;
+ smime_capability **caps;
+ int capi, pref;
+ SECStatus dstat;
+
+ pref = smime_symmetric_count;
+ profile = CERT_FindSMimeProfile (rcerts[rcount]);
+ if (profile != NULL && profile->data != NULL && profile->len > 0) {
+ caps = NULL;
+ dstat = SEC_ASN1DecodeItem (poolp, &caps,
+ smime_capabilities_template,
+ profile);
+ if (dstat == SECSuccess && caps != NULL) {
+ for (capi = 0; caps[capi] != NULL; capi++) {
+ smime_fill_capability (caps[capi]);
+ mapi = smime_mapi_by_cipher (caps[capi]->cipher);
+ if (mapi >= 0) {
+ cipher_abilities[mapi]++;
+ cipher_votes[mapi] += pref;
+ --pref;
+ }
+ }
+ }
+ } else {
+ SECKEYPublicKey *key;
+ unsigned int pklen_bits;
+
+ /*
+ * XXX This is probably only good for RSA keys. What I would
+ * really like is a function to just say; Is the public key in
+ * this cert an export-length key? Then I would not have to
+ * know things like the value 512, or the kind of key, or what
+ * a subjectPublicKeyInfo is, etc.
+ */
+ key = CERT_ExtractPublicKey (rcerts[rcount]);
+ if (key != NULL) {
+ pklen_bits = SECKEY_PublicKeyStrength (key) * 8;
+ SECKEY_DestroyPublicKey (key);
+
+ if (pklen_bits > 512) {
+ cipher_abilities[strong_mapi]++;
+ cipher_votes[strong_mapi] += pref;
+ }
+ }
+ }
+ if (profile != NULL)
+ SECITEM_FreeItem (profile, PR_TRUE);
+ }
+
+ max = 0;
+ for (mapi = 0; mapi < smime_symmetric_count; mapi++) {
+ if (cipher_abilities[mapi] != rcount)
+ continue;
+ if (! smime_cipher_allowed (smime_cipher_maps[mapi].cipher))
+ continue;
+ if (!isFortezza && (smime_cipher_maps[mapi].cipher == SMIME_FORTEZZA))
+ continue;
+ if (cipher_votes[mapi] > max) {
+ chosen_cipher = smime_cipher_maps[mapi].cipher;
+ max = cipher_votes[mapi];
+ } /* XXX else if a tie, let scert break it? */
+ }
+
+done:
+ if (poolp != NULL)
+ PORT_FreeArena (poolp, PR_FALSE);
+
+ return chosen_cipher;
+}
+
+
+/*
+ * XXX This is a hack for now to satisfy our current interface.
+ * Eventually, with more parameters needing to be specified, just
+ * looking up the keysize is not going to be sufficient.
+ */
+static int
+smime_keysize_by_cipher (unsigned long which)
+{
+ int keysize;
+
+ switch (which) {
+ case SMIME_RC2_CBC_40:
+ keysize = 40;
+ break;
+ case SMIME_RC2_CBC_64:
+ keysize = 64;
+ break;
+ case SMIME_RC2_CBC_128:
+ keysize = 128;
+ break;
+#ifdef SMIME_DOES_RC5
+ case SMIME_RC5PAD_64_16_40:
+ case SMIME_RC5PAD_64_16_64:
+ case SMIME_RC5PAD_64_16_128:
+ /* XXX See comment above; keysize is not enough... */
+ PORT_Assert (0);
+ PORT_SetError (SEC_ERROR_INVALID_ALGORITHM);
+ keysize = -1;
+ break;
+#endif
+ case SMIME_DES_CBC_56:
+ case SMIME_DES_EDE3_168:
+ case SMIME_FORTEZZA:
+ /*
+ * These are special; since the key size is fixed, we actually
+ * want to *avoid* specifying a key size.
+ */
+ keysize = 0;
+ break;
+ default:
+ keysize = -1;
+ break;
+ }
+
+ return keysize;
+}
+
+
+/*
+ * Start an S/MIME encrypting context.
+ *
+ * "scert" is the cert for the sender. It will be checked for validity.
+ * "rcerts" are the certs for the recipients. They will also be checked.
+ *
+ * "certdb" is the cert database to use for verifying the certs.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * This function already does all of the stuff specific to S/MIME protocol
+ * and local policy; the return value just needs to be passed to
+ * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
+ * and finally to SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+SEC_PKCS7ContentInfo *
+SECMIME_CreateEncrypted(CERTCertificate *scert,
+ CERTCertificate **rcerts,
+ CERTCertDBHandle *certdb,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ long cipher;
+ SECOidTag encalg;
+ int keysize;
+ int mapi, rci;
+
+ cipher = smime_choose_cipher (scert, rcerts);
+ if (cipher < 0)
+ return NULL;
+
+ mapi = smime_mapi_by_cipher (cipher);
+ if (mapi < 0)
+ return NULL;
+
+ /*
+ * XXX This is stretching it -- CreateEnvelopedData should probably
+ * take a cipher itself of some sort, because we cannot know what the
+ * future will bring in terms of parameters for each type of algorithm.
+ * For example, just an algorithm and keysize is *not* sufficient to
+ * fully specify the usage of RC5 (which also needs to know rounds and
+ * block size). Work this out into a better API!
+ */
+ encalg = smime_cipher_maps[mapi].algtag;
+ keysize = smime_keysize_by_cipher (cipher);
+ if (keysize < 0)
+ return NULL;
+
+ cinfo = SEC_PKCS7CreateEnvelopedData (scert, certUsageEmailRecipient,
+ certdb, encalg, keysize,
+ pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ for (rci = 0; rcerts[rci] != NULL; rci++) {
+ if (rcerts[rci] == scert)
+ continue;
+ if (SEC_PKCS7AddRecipient (cinfo, rcerts[rci], certUsageEmailRecipient,
+ NULL) != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+ }
+
+ return cinfo;
+}
+
+
+static smime_capability **smime_capabilities;
+static SECItem *smime_encoded_caps;
+static PRBool lastUsedFortezza;
+
+
+static SECStatus
+smime_init_caps (PRBool isFortezza)
+{
+ smime_capability *cap;
+ smime_cipher_map *map;
+ SECOidData *oiddata;
+ SECStatus rv;
+ int i, capIndex;
+
+ if (smime_encoded_caps != NULL
+ && (! smime_prefs_changed)
+ && lastUsedFortezza == isFortezza)
+ return SECSuccess;
+
+ if (smime_encoded_caps != NULL) {
+ SECITEM_FreeItem (smime_encoded_caps, PR_TRUE);
+ smime_encoded_caps = NULL;
+ }
+
+ if (smime_capabilities == NULL) {
+ smime_capabilities = (smime_capability**)PORT_ZAlloc (
+ (smime_symmetric_count + 1)
+ * sizeof(smime_capability *));
+ if (smime_capabilities == NULL)
+ return SECFailure;
+ }
+
+ rv = SECFailure;
+
+ /*
+ The process of creating the encoded PKCS7 cipher capability list
+ involves two basic steps:
+
+ (a) Convert our internal representation of cipher preferences
+ (smime_prefs) into an array containing cipher OIDs and
+ parameter data (smime_capabilities). This step is
+ performed here.
+
+ (b) Encode, using ASN.1, the cipher information in
+ smime_capabilities, leaving the encoded result in
+ smime_encoded_caps.
+
+ (In the process of performing (a), Lisa put in some optimizations
+ which allow us to avoid needlessly re-populating elements in
+ smime_capabilities as we walk through smime_prefs.)
+
+ We want to use separate loop variables for smime_prefs and
+ smime_capabilities because in the case where the Skipjack cipher
+ is turned on in the prefs, but where we don't want to include
+ Skipjack in the encoded capabilities (presumably due to using a
+ non-fortezza cert when sending a message), we want to avoid creating
+ an empty element in smime_capabilities. This would otherwise cause
+ the encoding step to produce an empty set, since Skipjack happens
+ to be the first cipher in smime_prefs, if it is turned on.
+ */
+ for (i = 0, capIndex = 0; i < smime_current_pref_index; i++, capIndex++) {
+ int mapi;
+
+ /* Get the next cipher preference in smime_prefs. */
+ mapi = smime_mapi_by_cipher (smime_prefs[i]);
+ if (mapi < 0)
+ break;
+
+ /* Find the corresponding entry in the cipher map. */
+ PORT_Assert (mapi < smime_symmetric_count);
+ map = &(smime_cipher_maps[mapi]);
+
+ /* If we're using a non-Fortezza cert, only advertise non-Fortezza
+ capabilities. (We advertise all capabilities if we have a
+ Fortezza cert.) */
+ if ((!isFortezza) && (map->cipher == SMIME_FORTEZZA))
+ {
+ capIndex--; /* we want to visit the same caps index entry next time */
+ continue;
+ }
+
+ /*
+ * Convert the next preference found in smime_prefs into an
+ * smime_capability.
+ */
+
+ cap = smime_capabilities[capIndex];
+ if (cap == NULL) {
+ cap = (smime_capability*)PORT_ZAlloc (sizeof(smime_capability));
+ if (cap == NULL)
+ break;
+ smime_capabilities[capIndex] = cap;
+ } else if (cap->cipher == smime_prefs[i]) {
+ continue; /* no change to this one */
+ }
+
+ cap->capIDTag = map->algtag;
+ oiddata = SECOID_FindOIDByTag (map->algtag);
+ if (oiddata == NULL)
+ break;
+
+ if (cap->capabilityID.data != NULL) {
+ SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
+ cap->capabilityID.data = NULL;
+ cap->capabilityID.len = 0;
+ }
+
+ rv = SECITEM_CopyItem (NULL, &(cap->capabilityID), &(oiddata->oid));
+ if (rv != SECSuccess)
+ break;
+
+ if (map->parms == NULL) {
+ cap->parameters.data = NULL;
+ cap->parameters.len = 0;
+ } else {
+ cap->parameters.data = map->parms->data;
+ cap->parameters.len = map->parms->len;
+ }
+
+ cap->cipher = smime_prefs[i];
+ }
+
+ if (i != smime_current_pref_index)
+ return rv;
+
+ while (capIndex < smime_symmetric_count) {
+ cap = smime_capabilities[capIndex];
+ if (cap != NULL) {
+ SECITEM_FreeItem (&(cap->capabilityID), PR_FALSE);
+ PORT_Free (cap);
+ }
+ smime_capabilities[capIndex] = NULL;
+ capIndex++;
+ }
+ smime_capabilities[capIndex] = NULL;
+
+ smime_encoded_caps = SEC_ASN1EncodeItem (NULL, NULL, &smime_capabilities,
+ smime_capabilities_template);
+ if (smime_encoded_caps == NULL)
+ return SECFailure;
+
+ lastUsedFortezza = isFortezza;
+
+ return SECSuccess;
+}
+
+
+static SECStatus
+smime_add_profile (CERTCertificate *cert, SEC_PKCS7ContentInfo *cinfo)
+{
+ PRBool isFortezza = PR_FALSE;
+
+ PORT_Assert (smime_prefs_complete);
+ if (! smime_prefs_complete)
+ return SECFailure;
+
+ /* See if the sender's cert specifies Fortezza key exchange. */
+ if (cert != NULL)
+ isFortezza = PK11_FortezzaHasKEA(cert);
+
+ /* For that matter, if capabilities haven't been initialized yet,
+ do so now. */
+ if (isFortezza != lastUsedFortezza || smime_encoded_caps == NULL || smime_prefs_changed) {
+ SECStatus rv;
+
+ rv = smime_init_caps(isFortezza);
+ if (rv != SECSuccess)
+ return rv;
+
+ PORT_Assert (smime_encoded_caps != NULL);
+ }
+
+ return SEC_PKCS7AddSignedAttribute (cinfo, SEC_OID_PKCS9_SMIME_CAPABILITIES,
+ smime_encoded_caps);
+}
+
+
+/*
+ * Start an S/MIME signing context.
+ *
+ * "scert" is the cert that will be used to sign the data. It will be
+ * checked for validity.
+ *
+ * "ecert" is the signer's encryption cert. If it is different from
+ * scert, then it will be included in the signed message so that the
+ * recipient can save it for future encryptions.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
+ * XXX There should be SECMIME functions for hashing, or the hashing should
+ * be built into this interface, which we would like because we would
+ * support more smartcards that way, and then this argument should go away.)
+ *
+ * "digest" is the actual digest of the data. It must be provided in
+ * the case of detached data or NULL if the content will be included.
+ *
+ * This function already does all of the stuff specific to S/MIME protocol
+ * and local policy; the return value just needs to be passed to
+ * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
+ * and finally to SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+
+SEC_PKCS7ContentInfo *
+SECMIME_CreateSigned (CERTCertificate *scert,
+ CERTCertificate *ecert,
+ CERTCertDBHandle *certdb,
+ SECOidTag digestalg,
+ SECItem *digest,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfn_arg)
+{
+ SEC_PKCS7ContentInfo *cinfo;
+ SECStatus rv;
+
+ /* See note in header comment above about digestalg. */
+ PORT_Assert (digestalg == SEC_OID_SHA1);
+
+ cinfo = SEC_PKCS7CreateSignedData (scert, certUsageEmailSigner,
+ certdb, digestalg, digest,
+ pwfn, pwfn_arg);
+ if (cinfo == NULL)
+ return NULL;
+
+ if (SEC_PKCS7IncludeCertChain (cinfo, NULL) != SECSuccess) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+
+ /* if the encryption cert and the signing cert differ, then include
+ * the encryption cert too.
+ */
+ /* it is ok to compare the pointers since we ref count, and the same
+ * cert will always have the same pointer
+ */
+ if ( ( ecert != NULL ) && ( ecert != scert ) ) {
+ rv = SEC_PKCS7AddCertificate(cinfo, ecert);
+ if ( rv != SECSuccess ) {
+ SEC_PKCS7DestroyContentInfo (cinfo);
+ return NULL;
+ }
+ }
+ /*
+ * Add the signing time. But if it fails for some reason,
+ * may as well not give up altogether -- just assert.
+ */
+ rv = SEC_PKCS7AddSigningTime (cinfo);
+ PORT_Assert (rv == SECSuccess);
+
+ /*
+ * Add the email profile. Again, if it fails for some reason,
+ * may as well not give up altogether -- just assert.
+ */
+ rv = smime_add_profile (ecert, cinfo);
+ PORT_Assert (rv == SECSuccess);
+
+ return cinfo;
+}
diff --git a/security/nss/lib/pkcs7/secmime.h b/security/nss/lib/pkcs7/secmime.h
new file mode 100644
index 000000000..6950e416f
--- /dev/null
+++ b/security/nss/lib/pkcs7/secmime.h
@@ -0,0 +1,192 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Header file for routines specific to S/MIME. Keep things that are pure
+ * pkcs7 out of here; this is for S/MIME policy, S/MIME interoperability, etc.
+ *
+ * $Id$
+ */
+
+#ifndef _SECMIME_H_
+#define _SECMIME_H_ 1
+
+#include "secpkcs7.h"
+
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/*
+ * Initialize the local recording of the user S/MIME cipher preferences.
+ * This function is called once for each cipher, the order being
+ * important (first call records greatest preference, and so on).
+ * When finished, it is called with a "which" of CIPHER_FAMILID_MASK.
+ * If the function is called again after that, it is assumed that
+ * the preferences are being reset, and the old preferences are
+ * discarded.
+ *
+ * XXX This is for a particular user, and right now the storage is
+ * XXX local, static. The preference should be stored elsewhere to allow
+ * XXX for multiple uses of one library? How does SSL handle this;
+ * XXX it has something similar?
+ *
+ * - The "which" values are defined in ciferfam.h (the SMIME_* values,
+ * for example SMIME_DES_CBC_56).
+ * - If "on" is non-zero then the named cipher is enabled, otherwise
+ * it is disabled. (It is not necessary to call the function for
+ * ciphers that are disabled, however, as that is the default.)
+ *
+ * If the cipher preference is successfully recorded, SECSuccess
+ * is returned. Otherwise SECFailure is returned. The only errors
+ * are due to failure allocating memory or bad parameters/calls:
+ * SEC_ERROR_XXX ("which" is not in the S/MIME cipher family)
+ * SEC_ERROR_XXX (function is being called more times than there
+ * are known/expected ciphers)
+ */
+extern SECStatus SECMIME_EnableCipher(long which, int on);
+
+/*
+ * Initialize the local recording of the S/MIME policy.
+ * This function is called to enable/disable a particular cipher.
+ * (S/MIME encryption or decryption using a particular cipher is only
+ * allowed if that cipher is currently enabled.) At startup, all S/MIME
+ * ciphers are disabled. From that point, this function can be called
+ * to enable a cipher -- it is not necessary to call this to disable
+ * a cipher unless that cipher was previously, explicitly enabled via
+ * this function.
+ *
+ * XXX This is for a the current module, I think, so local, static storage
+ * XXX is okay. Is that correct, or could multiple uses of the same
+ * XXX library expect to operate under different policies?
+ *
+ * - The "which" values are defined in ciferfam.h (the SMIME_* values,
+ * for example SMIME_DES_CBC_56).
+ * - If "on" is non-zero then the named cipher is enabled, otherwise
+ * it is disabled.
+ *
+ * If the cipher is successfully enabled/disabled, SECSuccess is
+ * returned. Otherwise SECFailure is returned. The only errors
+ * are due to bad parameters:
+ * SEC_ERROR_XXX ("which" is not in the S/MIME cipher family)
+ * SEC_ERROR_XXX ("which" exceeds expected maximum cipher; this is
+ * really an internal error)
+ */
+extern SECStatus SECMIME_SetPolicy(long which, int on);
+
+/*
+ * Does the current policy allow S/MIME decryption of this particular
+ * algorithm and keysize?
+ */
+extern PRBool SECMIME_DecryptionAllowed(SECAlgorithmID *algid, PK11SymKey *key);
+
+/*
+ * Does the current policy allow *any* S/MIME encryption (or decryption)?
+ *
+ * This tells whether or not *any* S/MIME encryption can be done,
+ * according to policy. Callers may use this to do nicer user interface
+ * (say, greying out a checkbox so a user does not even try to encrypt
+ * a message when they are not allowed to) or for any reason they want
+ * to check whether S/MIME encryption (or decryption, for that matter)
+ * may be done.
+ *
+ * It takes no arguments. The return value is a simple boolean:
+ * PR_TRUE means encryption (or decryption) is *possible*
+ * (but may still fail due to other reasons, like because we cannot
+ * find all the necessary certs, etc.; PR_TRUE is *not* a guarantee)
+ * PR_FALSE means encryption (or decryption) is not permitted
+ *
+ * There are no errors from this routine.
+ */
+extern PRBool SECMIME_EncryptionPossible(void);
+
+/*
+ * Start an S/MIME encrypting context.
+ *
+ * "scert" is the cert for the sender. It will be checked for validity.
+ * "rcerts" are the certs for the recipients. They will also be checked.
+ *
+ * "certdb" is the cert database to use for verifying the certs.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * This function already does all of the stuff specific to S/MIME protocol
+ * and local policy; the return value just needs to be passed to
+ * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
+ * and finally to SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *SECMIME_CreateEncrypted(CERTCertificate *scert,
+ CERTCertificate **rcerts,
+ CERTCertDBHandle *certdb,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfn_arg);
+
+/*
+ * Start an S/MIME signing context.
+ *
+ * "scert" is the cert that will be used to sign the data. It will be
+ * checked for validity.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * "digestalg" names the digest algorithm. (It should be SEC_OID_SHA1;
+ * XXX There should be SECMIME functions for hashing, or the hashing should
+ * be built into this interface, which we would like because we would
+ * support more smartcards that way, and then this argument should go away.)
+ *
+ * "digest" is the actual digest of the data. It must be provided in
+ * the case of detached data or NULL if the content will be included.
+ *
+ * This function already does all of the stuff specific to S/MIME protocol
+ * and local policy; the return value just needs to be passed to
+ * SEC_PKCS7Encode() or to SEC_PKCS7EncoderStart() to create the encoded data,
+ * and finally to SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *SECMIME_CreateSigned(CERTCertificate *scert,
+ CERTCertificate *ecert,
+ CERTCertDBHandle *certdb,
+ SECOidTag digestalg,
+ SECItem *digest,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfn_arg);
+
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _SECMIME_H_ */
diff --git a/security/nss/lib/pkcs7/secpkcs7.h b/security/nss/lib/pkcs7/secpkcs7.h
new file mode 100644
index 000000000..7a2b71b24
--- /dev/null
+++ b/security/nss/lib/pkcs7/secpkcs7.h
@@ -0,0 +1,618 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Interface to the PKCS7 implementation.
+ *
+ * $Id$
+ */
+
+#ifndef _SECPKCS7_H_
+#define _SECPKCS7_H_
+
+#include "seccomon.h"
+#include "mcom_db.h" /* needed by certt.h */
+
+#include "secoidt.h"
+#include "secder.h" /* needed by certt.h; XXX go away when possible */
+#include "certt.h"
+#include "keyt.h"
+#include "hasht.h"
+#include "pkcs7t.h"
+
+extern const SEC_ASN1Template sec_PKCS7ContentInfoTemplate[];
+
+/************************************************************************/
+SEC_BEGIN_PROTOS
+
+/************************************************************************
+ * Miscellaneous
+ ************************************************************************/
+
+/*
+ * Returns the content type of the given contentInfo.
+ */
+extern SECOidTag SEC_PKCS7ContentType (SEC_PKCS7ContentInfo *cinfo);
+
+/*
+ * Destroy a PKCS7 contentInfo and all of its sub-pieces.
+ */
+extern void SEC_PKCS7DestroyContentInfo(SEC_PKCS7ContentInfo *contentInfo);
+
+/*
+ * Copy a PKCS7 contentInfo. A Destroy is needed on *each* copy.
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CopyContentInfo(SEC_PKCS7ContentInfo *contentInfo);
+
+/*
+ * Return a pointer to the actual content. In the case of those types
+ * which are encrypted, this returns the *plain* content.
+ */
+extern SECItem *SEC_PKCS7GetContent(SEC_PKCS7ContentInfo *cinfo);
+
+/************************************************************************
+ * PKCS7 Decoding, Verification, etc..
+ ************************************************************************/
+
+extern SEC_PKCS7DecoderContext *
+SEC_PKCS7DecoderStart(SEC_PKCS7DecoderContentCallback callback,
+ void *callback_arg,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg,
+ SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
+ void *decrypt_key_cb_arg,
+ SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb);
+
+extern SECStatus
+SEC_PKCS7DecoderUpdate(SEC_PKCS7DecoderContext *p7dcx,
+ const char *buf, unsigned long len);
+
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7DecoderFinish(SEC_PKCS7DecoderContext *p7dcx);
+
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7DecodeItem(SECItem *p7item,
+ SEC_PKCS7DecoderContentCallback cb, void *cb_arg,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg,
+ SEC_PKCS7GetDecryptKeyCallback decrypt_key_cb,
+ void *decrypt_key_cb_arg,
+ SEC_PKCS7DecryptionAllowedCallback decrypt_allowed_cb);
+
+extern PRBool SEC_PKCS7ContainsCertsOrCrls(SEC_PKCS7ContentInfo *cinfo);
+
+/* checks to see if the contents of the content info is
+ * empty. it so, PR_TRUE is returned. PR_FALSE, otherwise.
+ *
+ * minLen is used to specify a minimum size. if content size <= minLen,
+ * content is assumed empty.
+ */
+extern PRBool
+SEC_PKCS7IsContentEmpty(SEC_PKCS7ContentInfo *cinfo, unsigned int minLen);
+
+extern PRBool SEC_PKCS7ContentIsEncrypted(SEC_PKCS7ContentInfo *cinfo);
+
+/*
+ * If the PKCS7 content has a signature (not just *could* have a signature)
+ * return true; false otherwise. This can/should be called before calling
+ * VerifySignature, which will always indicate failure if no signature is
+ * present, but that does not mean there even was a signature!
+ * Note that the content itself can be empty (detached content was sent
+ * another way); it is the presence of the signature that matters.
+ */
+extern PRBool SEC_PKCS7ContentIsSigned(SEC_PKCS7ContentInfo *cinfo);
+
+/*
+ * SEC_PKCS7VerifySignature
+ * Look at a PKCS7 contentInfo and check if the signature is good.
+ * The verification checks that the signing cert is valid and trusted
+ * for the purpose specified by "certusage".
+ *
+ * In addition, if "keepcerts" is true, add any new certificates found
+ * into our local database.
+ */
+extern PRBool SEC_PKCS7VerifySignature(SEC_PKCS7ContentInfo *cinfo,
+ SECCertUsage certusage,
+ PRBool keepcerts);
+
+/*
+ * SEC_PKCS7VerifyDetachedSignature
+ * Look at a PKCS7 contentInfo and check if the signature matches
+ * a passed-in digest (calculated, supposedly, from detached contents).
+ * The verification checks that the signing cert is valid and trusted
+ * for the purpose specified by "certusage".
+ *
+ * In addition, if "keepcerts" is true, add any new certificates found
+ * into our local database.
+ */
+extern PRBool SEC_PKCS7VerifyDetachedSignature(SEC_PKCS7ContentInfo *cinfo,
+ SECCertUsage certusage,
+ SECItem *detached_digest,
+ HASH_HashType digest_type,
+ PRBool keepcerts);
+
+/*
+ * SEC_PKCS7GetSignerCommonName, SEC_PKCS7GetSignerEmailAddress
+ * The passed-in contentInfo is espected to be Signed, and these
+ * functions return the specified portion of the full signer name.
+ *
+ * Returns a pointer to allocated memory, which must be freed.
+ * A NULL return value is an error.
+ */
+extern char *SEC_PKCS7GetSignerCommonName(SEC_PKCS7ContentInfo *cinfo);
+extern char *SEC_PKCS7GetSignerEmailAddress(SEC_PKCS7ContentInfo *cinfo);
+
+/*
+ * Return the the signing time, in UTCTime format, of a PKCS7 contentInfo.
+ */
+extern SECItem *SEC_PKCS7GetSigningTime(SEC_PKCS7ContentInfo *cinfo);
+
+
+/************************************************************************
+ * PKCS7 Creation and Encoding.
+ ************************************************************************/
+
+/*
+ * Start a PKCS7 signing context.
+ *
+ * "cert" is the cert that will be used to sign the data. It will be
+ * checked for validity.
+ *
+ * "certusage" describes the signing usage (e.g. certUsageEmailSigner)
+ * XXX Maybe SECCertUsage should be split so that our caller just says
+ * "email" and *we* add the "signing" part -- otherwise our caller
+ * could be lying about the usage; we do not want to allow encryption
+ * certs for signing or vice versa.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * "digestalg" names the digest algorithm (e.g. SEC_OID_SHA1).
+ *
+ * "digest" is the actual digest of the data. It must be provided in
+ * the case of detached data or NULL if the content will be included.
+ *
+ * The return value can be passed to functions which add things to
+ * it like attributes, then eventually to SEC_PKCS7Encode() or to
+ * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
+ * SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateSignedData (CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb,
+ SECOidTag digestalg,
+ SECItem *digest,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg);
+
+/*
+ * Create a PKCS7 certs-only container.
+ *
+ * "cert" is the (first) cert that will be included.
+ *
+ * "include_chain" specifies whether the entire chain for "cert" should
+ * be included.
+ *
+ * "certdb" is the cert database to use for finding the chain.
+ * It can be NULL in when "include_chain" is false, or when meaning
+ * use the default database.
+ *
+ * More certs and chains can be added via AddCertficate and AddCertChain.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateCertsOnly (CERTCertificate *cert,
+ PRBool include_chain,
+ CERTCertDBHandle *certdb);
+
+/*
+ * Start a PKCS7 enveloping context.
+ *
+ * "cert" is the cert for the recipient. It will be checked for validity.
+ *
+ * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
+ * XXX Maybe SECCertUsage should be split so that our caller just says
+ * "email" and *we* add the "recipient" part -- otherwise our caller
+ * could be lying about the usage; we do not want to allow encryption
+ * certs for signing or vice versa.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ *
+ * "encalg" specifies the bulk encryption algorithm to use (e.g. SEC_OID_RC2).
+ *
+ * "keysize" specifies the bulk encryption key size, in bits.
+ *
+ * The return value can be passed to functions which add things to
+ * it like more recipients, then eventually to SEC_PKCS7Encode() or to
+ * SEC_PKCS7EncoderStart() to create the encoded data, and finally to
+ * SEC_PKCS7DestroyContentInfo().
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateEnvelopedData (CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb,
+ SECOidTag encalg,
+ int keysize,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg);
+
+/*
+ * XXX There will be a similar routine for creating signedAndEnvelopedData.
+ * But its parameters will be different and I have no plans to implement
+ * it any time soon because we have no current need for it.
+ */
+
+/*
+ * Create an empty PKCS7 data content info.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *SEC_PKCS7CreateData (void);
+
+/*
+ * Create an empty PKCS7 encrypted content info.
+ *
+ * "algorithm" specifies the bulk encryption algorithm to use.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateEncryptedData (SECOidTag algorithm, int keysize,
+ SECKEYGetPasswordKey pwfn, void *pwfn_arg);
+
+/*
+ * All of the following things return SECStatus to signal success or failure.
+ * Failure should have a more specific error status available via
+ * PORT_GetError()/XP_GetError().
+ */
+
+/*
+ * Add the specified attribute to the authenticated (i.e. signed) attributes
+ * of "cinfo" -- "oidtag" describes the attribute and "value" is the
+ * value to be associated with it. NOTE! "value" must already be encoded;
+ * no interpretation of "oidtag" is done. Also, it is assumed that this
+ * signedData has only one signer -- if we ever need to add attributes
+ * when there is more than one signature, we need a way to specify *which*
+ * signature should get the attribute.
+ *
+ * XXX Technically, a signed attribute can have multiple values; if/when
+ * we ever need to support an attribute which takes multiple values, we
+ * either need to change this interface or create an AddSignedAttributeValue
+ * which can be called subsequently, and would then append a value.
+ *
+ * "cinfo" should be of type signedData (the only kind of pkcs7 data
+ * that is allowed authenticated attributes); SECFailure will be returned
+ * if it is not.
+ */
+extern SECStatus SEC_PKCS7AddSignedAttribute (SEC_PKCS7ContentInfo *cinfo,
+ SECOidTag oidtag,
+ SECItem *value);
+
+/*
+ * Add "cert" and its entire chain to the set of certs included in "cinfo".
+ *
+ * "certdb" is the cert database to use for finding the chain.
+ * It can be NULL, meaning use the default database.
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+extern SECStatus SEC_PKCS7AddCertChain (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert,
+ CERTCertDBHandle *certdb);
+
+/*
+ * Add "cert" to the set of certs included in "cinfo".
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+extern SECStatus SEC_PKCS7AddCertificate (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert);
+
+/*
+ * Add another recipient to an encrypted message.
+ *
+ * "cinfo" should be of type envelopedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ *
+ * "cert" is the cert for the recipient. It will be checked for validity.
+ *
+ * "certusage" describes the encryption usage (e.g. certUsageEmailRecipient)
+ * XXX Maybe SECCertUsage should be split so that our caller just says
+ * "email" and *we* add the "recipient" part -- otherwise our caller
+ * could be lying about the usage; we do not want to allow encryption
+ * certs for signing or vice versa.
+ *
+ * "certdb" is the cert database to use for verifying the cert.
+ * It can be NULL if a default database is available (like in the client).
+ */
+extern SECStatus SEC_PKCS7AddRecipient (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertificate *cert,
+ SECCertUsage certusage,
+ CERTCertDBHandle *certdb);
+
+/*
+ * Add the signing time to the authenticated (i.e. signed) attributes
+ * of "cinfo". This is expected to be included in outgoing signed
+ * messages for email (S/MIME) but is likely useful in other situations.
+ *
+ * This should only be added once; a second call will either do
+ * nothing or replace an old signing time with a newer one.
+ *
+ * XXX This will probably just shove the current time into "cinfo"
+ * but it will not actually get signed until the entire item is
+ * processed for encoding. Is this (expected to be small) delay okay?
+ *
+ * "cinfo" should be of type signedData (the only kind of pkcs7 data
+ * that is allowed authenticated attributes); SECFailure will be returned
+ * if it is not.
+ */
+extern SECStatus SEC_PKCS7AddSigningTime (SEC_PKCS7ContentInfo *cinfo);
+
+/*
+ * Add the signer's symmetric capabilities to the authenticated
+ * (i.e. signed) attributes of "cinfo". This is expected to be
+ * included in outgoing signed messages for email (S/MIME).
+ *
+ * This can only be added once; a second call will return SECFailure.
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+extern SECStatus SEC_PKCS7AddSymmetricCapabilities(SEC_PKCS7ContentInfo *cinfo);
+
+/*
+ * Mark that the signer's certificate and its issuing chain should
+ * be included in the encoded data. This is expected to be used
+ * in outgoing signed messages for email (S/MIME).
+ *
+ * "certdb" is the cert database to use for finding the chain.
+ * It can be NULL, meaning use the default database.
+ *
+ * "cinfo" should be of type signedData or signedAndEnvelopedData;
+ * SECFailure will be returned if it is not.
+ */
+extern SECStatus SEC_PKCS7IncludeCertChain (SEC_PKCS7ContentInfo *cinfo,
+ CERTCertDBHandle *certdb);
+
+
+/*
+ * Set the content; it will be included and also hashed and/or encrypted
+ * as appropriate. This is for in-memory content (expected to be "small")
+ * that will be included in the PKCS7 object. All others should stream the
+ * content through when encoding (see SEC_PKCS7Encoder{Start,Update,Finish}).
+ *
+ * "buf" points to data of length "len"; it will be copied.
+ */
+extern SECStatus SEC_PKCS7SetContent (SEC_PKCS7ContentInfo *cinfo,
+ const char *buf, unsigned long len);
+
+/*
+ * Encode a PKCS7 object, in one shot. All necessary components
+ * of the object must already be specified. Either the data has
+ * already been included (via SetContent), or the data is detached,
+ * or there is no data at all (certs-only).
+ *
+ * "cinfo" specifies the object to be encoded.
+ *
+ * "outputfn" is where the encoded bytes will be passed.
+ *
+ * "outputarg" is an opaque argument to the above callback.
+ *
+ * "bulkkey" specifies the bulk encryption key to use. This argument
+ * can be NULL if no encryption is being done, or if the bulk key should
+ * be generated internally (usually the case for EnvelopedData but never
+ * for EncryptedData, which *must* provide a bulk encryption key).
+ *
+ * "pwfn" is a callback for getting the password which protects the
+ * private key of the signer. This argument can be NULL if it is known
+ * that no signing is going to be done.
+ *
+ * "pwfnarg" is an opaque argument to the above callback.
+ */
+extern SECStatus SEC_PKCS7Encode (SEC_PKCS7ContentInfo *cinfo,
+ SEC_PKCS7EncoderOutputCallback outputfn,
+ void *outputarg,
+ PK11SymKey *bulkkey,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg);
+
+/*
+ * Encode a PKCS7 object, in one shot. All necessary components
+ * of the object must already be specified. Either the data has
+ * already been included (via SetContent), or the data is detached,
+ * or there is no data at all (certs-only). The output, rather than
+ * being passed to an output function as is done above, is all put
+ * into a SECItem.
+ *
+ * "pool" specifies a pool from which to allocate the result.
+ * It can be NULL, in which case memory is allocated generically.
+ *
+ * "dest" specifies a SECItem in which to put the result data.
+ * It can be NULL, in which case the entire item is allocated, too.
+ *
+ * "cinfo" specifies the object to be encoded.
+ *
+ * "bulkkey" specifies the bulk encryption key to use. This argument
+ * can be NULL if no encryption is being done, or if the bulk key should
+ * be generated internally (usually the case for EnvelopedData but never
+ * for EncryptedData, which *must* provide a bulk encryption key).
+ *
+ * "pwfn" is a callback for getting the password which protects the
+ * private key of the signer. This argument can be NULL if it is known
+ * that no signing is going to be done.
+ *
+ * "pwfnarg" is an opaque argument to the above callback.
+ */
+extern SECItem *SEC_PKCS7EncodeItem (PRArenaPool *pool,
+ SECItem *dest,
+ SEC_PKCS7ContentInfo *cinfo,
+ PK11SymKey *bulkkey,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg);
+
+/*
+ * For those who want to simply point to the pkcs7 contentInfo ASN.1
+ * template, and *not* call the encoding functions directly, the
+ * following function can be used -- after it is called, the entire
+ * PKCS7 contentInfo is ready to be encoded.
+ */
+extern SECStatus SEC_PKCS7PrepareForEncode (SEC_PKCS7ContentInfo *cinfo,
+ PK11SymKey *bulkkey,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg);
+
+/*
+ * Start the process of encoding a PKCS7 object. The first part of
+ * the encoded object will be passed to the output function right away;
+ * after that it is expected that SEC_PKCS7EncoderUpdate will be called,
+ * streaming in the actual content that is getting included as well as
+ * signed or encrypted (or both).
+ *
+ * "cinfo" specifies the object to be encoded.
+ *
+ * "outputfn" is where the encoded bytes will be passed.
+ *
+ * "outputarg" is an opaque argument to the above callback.
+ *
+ * "bulkkey" specifies the bulk encryption key to use. This argument
+ * can be NULL if no encryption is being done, or if the bulk key should
+ * be generated internally (usually the case for EnvelopedData but never
+ * for EncryptedData, which *must* provide a bulk encryption key).
+ *
+ * Returns an object to be passed to EncoderUpdate and EncoderFinish.
+ */
+extern SEC_PKCS7EncoderContext *
+SEC_PKCS7EncoderStart (SEC_PKCS7ContentInfo *cinfo,
+ SEC_PKCS7EncoderOutputCallback outputfn,
+ void *outputarg,
+ PK11SymKey *bulkkey);
+
+/*
+ * Encode more contents, hashing and/or encrypting along the way.
+ */
+extern SECStatus SEC_PKCS7EncoderUpdate (SEC_PKCS7EncoderContext *p7ecx,
+ const char *buf,
+ unsigned long len);
+
+/*
+ * No more contents; finish the signature creation, if appropriate,
+ * and then the encoding.
+ *
+ * "pwfn" is a callback for getting the password which protects the
+ * signer's private key. This argument can be NULL if it is known
+ * that no signing is going to be done.
+ *
+ * "pwfnarg" is an opaque argument to the above callback.
+ */
+extern SECStatus SEC_PKCS7EncoderFinish (SEC_PKCS7EncoderContext *p7ecx,
+ SECKEYGetPasswordKey pwfn,
+ void *pwfnarg);
+
+/* retrieve the algorithm ID used to encrypt the content info
+ * for encrypted and enveloped data. The SECAlgorithmID pointer
+ * returned needs to be freed as it is a copy of the algorithm
+ * id in the content info.
+ */
+extern SECAlgorithmID *
+SEC_PKCS7GetEncryptionAlgorithm(SEC_PKCS7ContentInfo *cinfo);
+
+/* the content of an encrypted data content info is encrypted.
+ * it is assumed that for encrypted data, that the data has already
+ * been set and is in the "plainContent" field of the content info.
+ *
+ * cinfo is the content info to encrypt
+ *
+ * key is the key with which to perform the encryption. if the
+ * algorithm is a password based encryption algorithm, the
+ * key is actually a password which will be processed per
+ * PKCS #5.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates a success.
+ */
+extern SECStatus
+SEC_PKCS7EncryptContents(PRArenaPool *poolp,
+ SEC_PKCS7ContentInfo *cinfo,
+ SECItem *key,
+ void *wincx);
+
+/* the content of an encrypted data content info is decrypted.
+ * it is assumed that for encrypted data, that the data has already
+ * been set and is in the "encContent" field of the content info.
+ *
+ * cinfo is the content info to decrypt
+ *
+ * key is the key with which to perform the decryption. if the
+ * algorithm is a password based encryption algorithm, the
+ * key is actually a password which will be processed per
+ * PKCS #5.
+ *
+ * in the event of an error, SECFailure is returned. SECSuccess
+ * indicates a success.
+ */
+extern SECStatus
+SEC_PKCS7DecryptContents(PRArenaPool *poolp,
+ SEC_PKCS7ContentInfo *cinfo,
+ SECItem *key,
+ void *wincx);
+
+/* retrieve the certificate list from the content info. the list
+ * is a pointer to the list in the content info. this should not
+ * be deleted or freed in any way short of calling
+ * SEC_PKCS7DestroyContentInfo
+ */
+extern SECItem **
+SEC_PKCS7GetCertificateList(SEC_PKCS7ContentInfo *cinfo);
+
+/* Returns the key length (in bits) of the algorithm used to encrypt
+ this object. Returns 0 if it's not encrypted, or the key length is
+ irrelevant. */
+extern int
+SEC_PKCS7GetKeyLength(SEC_PKCS7ContentInfo *cinfo);
+
+
+/************************************************************************/
+SEC_END_PROTOS
+
+#endif /* _SECPKCS7_H_ */
diff --git a/security/nss/lib/pki/nsspki.h b/security/nss/lib/pki/nsspki.h
new file mode 100644
index 000000000..c8da14050
--- /dev/null
+++ b/security/nss/lib/pki/nsspki.h
@@ -0,0 +1,3161 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef NSSPKI_H
+#define NSSPKI_H
+
+#ifdef DEBUG
+static const char NSSPKI_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * nsspki.h
+ *
+ * This file prototypes the methods of the top-level PKI objects.
+ */
+
+#ifndef NSSPKIT_H
+#include "nsspkit.h"
+#endif /* NSSPKIT_H */
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * A note about interfaces
+ *
+ * Although these APIs are specified in C, a language which does
+ * not have fancy support for abstract interfaces, this library
+ * was designed from an object-oriented perspective. It may be
+ * useful to consider the standard interfaces which went into
+ * the writing of these APIs.
+ *
+ * Basic operations on all objects:
+ * Destroy -- free a pointer to an object
+ * DeleteStoredObject -- delete an object permanently
+ *
+ * Public Key cryptographic operations:
+ * Encrypt
+ * Verify
+ * VerifyRecover
+ * Wrap
+ * Derive
+ *
+ * Private Key cryptographic operations:
+ * IsStillPresent
+ * Decrypt
+ * Sign
+ * SignRecover
+ * Unwrap
+ * Derive
+ *
+ * Symmetric Key cryptographic operations:
+ * IsStillPresent
+ * Encrypt
+ * Decrypt
+ * Sign
+ * SignRecover
+ * Verify
+ * VerifyRecover
+ * Wrap
+ * Unwrap
+ * Derive
+ *
+ */
+
+/*
+ * NSSCertificate
+ *
+ * These things can do crypto ops like public keys, except that the trust,
+ * usage, and other constraints are checked. These objects are "high-level,"
+ * so trust, usages, etc. are in the form we throw around (client auth,
+ * email signing, etc.). Remember that theoretically another implementation
+ * (think PGP) could be beneath this object.
+ */
+
+/*
+ * NSSCertificate_Destroy
+ *
+ * Free a pointer to a certificate object.
+ */
+
+NSS_EXTERN PRStatus
+NSSCertificate_Destroy
+(
+ NSSCertificate *c
+);
+
+/*
+ * NSSCertificate_DeleteStoredObject
+ *
+ * Permanently remove this certificate from storage. If this is the
+ * only (remaining) certificate corresponding to a private key,
+ * public key, and/or other object; then that object (those objects)
+ * are deleted too.
+ */
+
+NSS_EXTERN PRStatus
+NSSCertificate_DeleteStoredObject
+(
+ NSSCertificate *c,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSCertificate_Validate
+ *
+ * Verify that this certificate is trusted, for the specified usage(s),
+ * at the specified time, {word word} the specified policies.
+ */
+
+NSS_EXTERN PRStatus
+NSSCertificate_Validate
+(
+ NSSCertificate *c,
+ NSSTime *timeOpt, /* NULL for "now" */
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt /* NULL for none */
+);
+
+/*
+ * NSSCertificate_ValidateCompletely
+ *
+ * Verify that this certificate is trusted. The difference between
+ * this and the previous call is that NSSCertificate_Validate merely
+ * returns success or failure with an appropriate error stack.
+ * However, there may be (and often are) multiple problems with a
+ * certificate. This routine returns an array of errors, specifying
+ * every problem.
+ */
+
+/*
+ * Return value must be an array of objects, each of which has
+ * an NSSError, and any corresponding certificate (in the chain)
+ * and/or policy.
+ */
+
+NSS_EXTERN void ** /* void *[] */
+NSSCertificate_ValidateCompletely
+(
+ NSSCertificate *c,
+ NSSTime *timeOpt, /* NULL for "now" */
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt, /* NULL for none */
+ void **rvOpt, /* NULL for allocate */
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt /* NULL for heap */
+);
+
+/*
+ * NSSCertificate_ValidateAndDiscoverUsagesAndPolicies
+ *
+ * Returns PR_SUCCESS if the certificate is valid for at least something.
+ */
+
+NSS_EXTERN PRStatus
+NSSCertificate_ValidateAndDiscoverUsagesAndPolicies
+(
+ NSSCertificate *c,
+ NSSTime **notBeforeOutOpt,
+ NSSTime **notAfterOutOpt,
+ void *allowedUsages,
+ void *disallowedUsages,
+ void *allowedPolicies,
+ void *disallowedPolicies,
+ /* more args.. work on this fgmr */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCertificate_Encode
+ *
+ */
+
+NSS_EXTERN NSSDER *
+NSSCertificate_Encode
+(
+ NSSCertificate *c,
+ NSSDER *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCertificate_BuildChain
+ *
+ * This routine returns NSSCertificate *'s for each certificate
+ * in the "chain" starting from the specified one up to and
+ * including the root. The zeroth element in the array is the
+ * specified ("leaf") certificate.
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSCertificate_BuildChain
+(
+ NSSCertificate *c,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCertificate_GetTrustDomain
+ *
+ */
+
+NSS_EXTERN NSSTrustDomain *
+NSSCertificate_GetTrustDomain
+(
+ NSSCertificate *c
+);
+
+/*
+ * NSSCertificate_GetToken
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSToken *
+NSSCertificate_GetToken
+(
+ NSSCertificate *c,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSCertificate_GetSlot
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSSlot *
+NSSCertificate_GetSlot
+(
+ NSSCertificate *c,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSCertificate_GetModule
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSModule *
+NSSCertificate_GetModule
+(
+ NSSCertificate *c,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSCertificate_Encrypt
+ *
+ * Encrypt a single chunk of data with the public key corresponding to
+ * this certificate.
+ */
+
+NSS_EXTERN NSSItem *
+NSSCertificate_Encrypt
+(
+ NSSCertificate *c,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCertificate_Verify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCertificate_Verify
+(
+ NSSCertificate *c,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSItem *signature,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSCertificate_VerifyRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCertificate_VerifyRecover
+(
+ NSSCertificate *c,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *signature,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCertificate_WrapSymmetricKey
+ *
+ * This method tries very hard to to succeed, even in situations
+ * involving sensitive keys and multiple modules.
+ * { relyea: want to add verbiage? }
+ */
+
+NSS_EXTERN NSSItem *
+NSSCertificate_WrapSymmetricKey
+(
+ NSSCertificate *c,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSSymmetricKey *keyToWrap,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCertificate_CreateCryptoContext
+ *
+ * Create a crypto context, in this certificate's trust domain, with this
+ * as the distinguished certificate.
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSCertificate_CreateCryptoContext
+(
+ NSSCertificate *c,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSCertificate_GetPublicKey
+ *
+ * Returns the public key corresponding to this certificate.
+ */
+
+NSS_EXTERN NSSPublicKey *
+NSSCertificate_GetPublicKey
+(
+ NSSCertificate *c
+);
+
+/*
+ * NSSCertificate_FindPrivateKey
+ *
+ * Finds and returns the private key corresponding to this certificate,
+ * if it is available.
+ *
+ * { Should this hang off of NSSUserCertificate? }
+ */
+
+NSS_EXTERN NSSPrivateKey *
+NSSCertificate_FindPrivateKey
+(
+ NSSCertificate *c,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSCertificate_IsPrivateKeyAvailable
+ *
+ * Returns success if the private key corresponding to this certificate
+ * is available to be used.
+ *
+ * { Should *this* hang off of NSSUserCertificate?? }
+ */
+
+NSS_EXTERN PRBool
+NSSCertificate_IsPrivateKeyAvailable
+(
+ NSSCertificate *c,
+ NSSCallback *uhh,
+ PRStatus *statusOpt
+);
+
+/*
+ * If we make NSSUserCertificate not a typedef of NSSCertificate,
+ * then we'll need implementations of the following:
+ *
+ * NSSUserCertificate_Destroy
+ * NSSUserCertificate_DeleteStoredObject
+ * NSSUserCertificate_Validate
+ * NSSUserCertificate_ValidateCompletely
+ * NSSUserCertificate_ValidateAndDiscoverUsagesAndPolicies
+ * NSSUserCertificate_Encode
+ * NSSUserCertificate_BuildChain
+ * NSSUserCertificate_GetTrustDomain
+ * NSSUserCertificate_GetToken
+ * NSSUserCertificate_GetSlot
+ * NSSUserCertificate_GetModule
+ * NSSUserCertificate_GetCryptoContext
+ * NSSUserCertificate_GetPublicKey
+ */
+
+/*
+ * NSSUserCertificate_IsStillPresent
+ *
+ * Verify that if this certificate lives on a token, that the token
+ * is still present and the certificate still exists. This is a
+ * lightweight call which should be used whenever it should be
+ * verified that the user hasn't perhaps popped out his or her
+ * token and strolled away.
+ */
+
+NSS_EXTERN PRBool
+NSSUserCertificate_IsStillPresent
+(
+ NSSUserCertificate *uc,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSUserCertificate_Decrypt
+ *
+ * Decrypt a single chunk of data with the private key corresponding
+ * to this certificate.
+ */
+
+NSS_EXTERN NSSItem *
+NSSUserCertificate_Decrypt
+(
+ NSSUserCertificate *uc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSUserCertificate_Sign
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSUserCertificate_Sign
+(
+ NSSUserCertificate *uc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSUserCertificate_SignRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSUserCertificate_SignRecover
+(
+ NSSUserCertificate *uc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSUserCertificate_UnwrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSUserCertificate_UnwrapSymmetricKey
+(
+ NSSUserCertificate *uc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *wrappedKey,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSUserCertificate_DeriveSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSUserCertificate_DeriveSymmetricKey
+(
+ NSSUserCertificate *uc, /* provides private key */
+ NSSCertificate *c, /* provides public key */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSOID *target,
+ PRUint32 keySizeOpt, /* zero for best allowed */
+ NSSOperations operations,
+ NSSCallback *uhh
+);
+
+/* filter-certs function(s) */
+
+/**
+ ** fgmr -- trust objects
+ **/
+
+/*
+ * NSSPrivateKey
+ *
+ */
+
+/*
+ * NSSPrivateKey_Destroy
+ *
+ * Free a pointer to a private key object.
+ */
+
+NSS_EXTERN PRStatus
+NSSPrivateKey_Destroy
+(
+ NSSPrivateKey *vk
+);
+
+/*
+ * NSSPrivateKey_DeleteStoredObject
+ *
+ * Permanently remove this object, and any related objects (such as the
+ * certificates corresponding to this key).
+ */
+
+NSS_EXTERN PRStatus
+NSSPrivateKey_DeleteStoredObject
+(
+ NSSPrivateKey *vk,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPrivateKey_GetSignatureLength
+ *
+ */
+
+NSS_EXTERN PRUint32
+NSSPrivateKey_GetSignatureLength
+(
+ NSSPrivateKey *vk
+);
+
+/*
+ * NSSPrivateKey_GetPrivateModulusLength
+ *
+ */
+
+NSS_EXTERN PRUint32
+NSSPrivateKey_GetPrivateModulusLength
+(
+ NSSPrivateKey *vk
+);
+
+/*
+ * NSSPrivateKey_IsStillPresent
+ *
+ */
+
+NSS_EXTERN PRBool
+NSSPrivateKey_IsStillPresent
+(
+ NSSPrivateKey *vk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSPrivateKey_Encode
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPrivateKey_Encode
+(
+ NSSPrivateKey *vk,
+ NSSAlgorithmAndParameters *ap,
+ NSSItem *passwordOpt, /* NULL will cause a callback; "" for no password */
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPrivateKey_GetTrustDomain
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSTrustDomain *
+NSSPrivateKey_GetTrustDomain
+(
+ NSSPrivateKey *vk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSPrivateKey_GetToken
+ *
+ */
+
+NSS_EXTERN NSSToken *
+NSSPrivateKey_GetToken
+(
+ NSSPrivateKey *vk
+);
+
+/*
+ * NSSPrivateKey_GetSlot
+ *
+ */
+
+NSS_EXTERN NSSSlot *
+NSSPrivateKey_GetSlot
+(
+ NSSPrivateKey *vk
+);
+
+/*
+ * NSSPrivateKey_GetModule
+ *
+ */
+
+NSS_EXTERN NSSModule *
+NSSPrivateKey_GetModule
+(
+ NSSPrivateKey *vk
+);
+
+/*
+ * NSSPrivateKey_Decrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPrivateKey_Decrypt
+(
+ NSSPrivateKey *vk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *encryptedData,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPrivateKey_Sign
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPrivateKey_Sign
+(
+ NSSPrivateKey *vk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPrivateKey_SignRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPrivateKey_SignRecover
+(
+ NSSPrivateKey *vk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPrivateKey_UnwrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSPrivateKey_UnwrapSymmetricKey
+(
+ NSSPrivateKey *vk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *wrappedKey,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPrivateKey_DeriveSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSPrivateKey_DeriveSymmetricKey
+(
+ NSSPrivateKey *vk,
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSOID *target,
+ PRUint32 keySizeOpt, /* zero for best allowed */
+ NSSOperations operations,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPrivateKey_FindPublicKey
+ *
+ */
+
+NSS_EXTERN NSSPublicKey *
+NSSPrivateKey_FindPublicKey
+(
+ NSSPrivateKey *vk
+ /* { don't need the callback here, right? } */
+);
+
+/*
+ * NSSPrivateKey_CreateCryptoContext
+ *
+ * Create a crypto context, in this key's trust domain,
+ * with this as the distinguished private key.
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSPrivateKey_CreateCryptoContext
+(
+ NSSPrivateKey *vk
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPrivateKey_FindCertificates
+ *
+ * Note that there may be more than one certificate for this
+ * private key. { FilterCertificates function to further
+ * reduce the list. }
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSPrivateKey_FindCertificates
+(
+ NSSPrivateKey *vk,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPrivateKey_FindBestCertificate
+ *
+ * The parameters for this function will depend on what the users
+ * need. This is just a starting point.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSPrivateKey_FindBestCertificate
+(
+ NSSPrivateKey *vk,
+ NSSTime *timeOpt,
+ NSSUsage *usageOpt,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSPublicKey
+ *
+ * Once you generate, find, or derive one of these, you can use it
+ * to perform (simple) cryptographic operations. Though there may
+ * be certificates associated with these public keys, they are not
+ * verified.
+ */
+
+/*
+ * NSSPublicKey_Destroy
+ *
+ * Free a pointer to a public key object.
+ */
+
+NSS_EXTERN PRStatus
+NSSPublicKey_Destroy
+(
+ NSSPublicKey *bk
+);
+
+/*
+ * NSSPublicKey_DeleteStoredObject
+ *
+ * Permanently remove this object, and any related objects (such as the
+ * corresponding private keys and certificates).
+ */
+
+NSS_EXTERN PRStatus
+NSSPublicKey_DeleteStoredObject
+(
+ NSSPublicKey *bk,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPublicKey_Encode
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPublicKey_Encode
+(
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *ap,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPublicKey_GetTrustDomain
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSTrustDomain *
+NSSPublicKey_GetTrustDomain
+(
+ NSSPublicKey *bk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSPublicKey_GetToken
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSToken *
+NSSPublicKey_GetToken
+(
+ NSSPublicKey *bk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSPublicKey_GetSlot
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSSlot *
+NSSPublicKey_GetSlot
+(
+ NSSPublicKey *bk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSPublicKey_GetModule
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSModule *
+NSSPublicKey_GetModule
+(
+ NSSPublicKey *bk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSPublicKey_Encrypt
+ *
+ * Encrypt a single chunk of data with the public key corresponding to
+ * this certificate.
+ */
+
+NSS_EXTERN NSSItem *
+NSSPublicKey_Encrypt
+(
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPublicKey_Verify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSPublicKey_Verify
+(
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSItem *signature,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPublicKey_VerifyRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPublicKey_VerifyRecover
+(
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *signature,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPublicKey_WrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSPublicKey_WrapSymmetricKey
+(
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSSymmetricKey *keyToWrap,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPublicKey_CreateCryptoContext
+ *
+ * Create a crypto context, in this key's trust domain, with this
+ * as the distinguished public key.
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSPublicKey_CreateCryptoContext
+(
+ NSSPublicKey *bk
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSPublicKey_FindCertificates
+ *
+ * Note that there may be more than one certificate for this
+ * public key. The current implementation may not find every
+ * last certificate available for this public key: that would
+ * involve trolling e.g. huge ldap databases, which will be
+ * grossly inefficient and not generally useful.
+ * { FilterCertificates function to further reduce the list }
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSPublicKey_FindCertificates
+(
+ NSSPublicKey *bk,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSPrivateKey_FindBestCertificate
+ *
+ * The parameters for this function will depend on what the users
+ * need. This is just a starting point.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSPublicKey_FindBestCertificate
+(
+ NSSPublicKey *bk,
+ NSSTime *timeOpt,
+ NSSUsage *usageOpt,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSPublicKey_FindPrivateKey
+ *
+ */
+
+NSS_EXTERN NSSPrivateKey *
+NSSPublicKey_FindPrivateKey
+(
+ NSSPublicKey *bk,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSSymmetricKey
+ *
+ */
+
+/*
+ * NSSSymmetricKey_Destroy
+ *
+ * Free a pointer to a symmetric key object.
+ */
+
+NSS_EXTERN PRStatus
+NSSSymmetricKey_Destroy
+(
+ NSSSymmetricKey *mk
+);
+
+/*
+ * NSSSymmetricKey_DeleteStoredObject
+ *
+ * Permanently remove this object.
+ */
+
+NSS_EXTERN PRStatus
+NSSSymmetricKey_DeleteStoredObject
+(
+ NSSSymmetricKey *mk,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSSymmetricKey_GetKeyLength
+ *
+ */
+
+NSS_EXTERN PRUint32
+NSSSymmetricKey_GetKeyLength
+(
+ NSSSymmetricKey *mk
+);
+
+/*
+ * NSSSymmetricKey_GetKeyStrength
+ *
+ */
+
+NSS_EXTERN PRUint32
+NSSSymmetricKey_GetKeyStrength
+(
+ NSSSymmetricKey *mk
+);
+
+/*
+ * NSSSymmetricKey_IsStillPresent
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSSymmetricKey_IsStillPresent
+(
+ NSSSymmetricKey *mk
+);
+
+/*
+ * NSSSymmetricKey_GetTrustDomain
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSTrustDomain *
+NSSSymmetricKey_GetTrustDomain
+(
+ NSSSymmetricKey *mk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSSymmetricKey_GetToken
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSToken *
+NSSSymmetricKey_GetToken
+(
+ NSSSymmetricKey *mk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSSymmetricKey_GetSlot
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSSlot *
+NSSSymmetricKey_GetSlot
+(
+ NSSSymmetricKey *mk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSSymmetricKey_GetModule
+ *
+ * There doesn't have to be one.
+ */
+
+NSS_EXTERN NSSModule *
+NSSSymmetricKey_GetModule
+(
+ NSSSymmetricKey *mk,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSSymmetricKey_Encrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_Encrypt
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_Decrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_Decrypt
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *encryptedData,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_Sign
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_Sign
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_SignRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_SignRecover
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_Verify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSSymmetricKey_Verify
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSItem *signature,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSSymmetricKey_VerifyRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_VerifyRecover
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *signature,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_WrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_WrapSymmetricKey
+(
+ NSSSymmetricKey *wrappingKey,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSSymmetricKey *keyToWrap,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_WrapPrivateKey
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSSymmetricKey_WrapPrivateKey
+(
+ NSSSymmetricKey *wrappingKey,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPrivateKey *keyToWrap,
+ NSSCallback *uhh,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSSymmetricKey_UnwrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSSymmetricKey_UnwrapSymmetricKey
+(
+ NSSSymmetricKey *wrappingKey,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *wrappedKey,
+ NSSOID *target,
+ PRUint32 keySizeOpt,
+ NSSOperations operations,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSSymmetricKey_UnwrapPrivateKey
+ *
+ */
+
+NSS_EXTERN NSSPrivateKey *
+NSSSymmetricKey_UnwrapPrivateKey
+(
+ NSSSymmetricKey *wrappingKey,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *wrappedKey,
+ NSSUTF8 *labelOpt,
+ NSSItem *keyIDOpt,
+ PRBool persistant,
+ PRBool sensitive,
+ NSSToken *destinationOpt,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSSymmetricKey_DeriveSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSSymmetricKey_DeriveSymmetricKey
+(
+ NSSSymmetricKey *originalKey,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSOID *target,
+ PRUint32 keySizeOpt,
+ NSSOperations operations,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSSymmetricKey_CreateCryptoContext
+ *
+ * Create a crypto context, in this key's trust domain,
+ * with this as the distinguished symmetric key.
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSSymmetricKey_CreateCryptoContext
+(
+ NSSSymmetricKey *mk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhh
+);
+
+/*
+ * NSSTrustDomain
+ *
+ */
+
+/*
+ * NSSTrustDomain_Create
+ *
+ * This creates a trust domain, optionally with an initial cryptoki
+ * module. If the module name is not null, the module is loaded if
+ * needed (using the uriOpt argument), and initialized with the
+ * opaqueOpt argument. If mumble mumble priority settings, then
+ * module-specification objects in the module can cause the loading
+ * and initialization of further modules.
+ *
+ * The uriOpt is defined to take a URI. At present, we only
+ * support file: URLs pointing to platform-native shared libraries.
+ * However, by specifying this as a URI, this keeps open the
+ * possibility of supporting other, possibly remote, resources.
+ *
+ * The "reserved" arguments is held for when we figure out the
+ * module priority stuff.
+ */
+
+NSS_EXTERN NSSTrustDomain *
+NSSTrustDomain_Create
+(
+ NSSUTF8 *moduleOpt,
+ NSSUTF8 *uriOpt,
+ NSSUTF8 *opaqueOpt,
+ void *reserved
+);
+
+/*
+ * NSSTrustDomain_Destroy
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_Destroy
+(
+ NSSTrustDomain *td
+);
+
+/*
+ * NSSTrustDomain_SetDefaultCallback
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_SetDefaultCallback
+(
+ NSSTrustDomain *td,
+ NSSCallback *newCallback,
+ NSSCallback **oldCallbackOpt
+);
+
+/*
+ * NSSTrustDomain_GetDefaultCallback
+ *
+ */
+
+NSS_EXTERN NSSCallback *
+NSSTrustDomain_GetDefaultCallback
+(
+ NSSTrustDomain *td,
+ PRStatus *statusOpt
+);
+
+/*
+ * Default policies?
+ * Default usage?
+ * Default time, for completeness?
+ */
+
+/*
+ * NSSTrustDomain_LoadModule
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_LoadModule
+(
+ NSSUTF8 *moduleOpt,
+ NSSUTF8 *uriOpt,
+ NSSUTF8 *opaqueOpt,
+ void *reserved
+);
+
+/*
+ * NSSTrustDomain_AddModule
+ * NSSTrustDomain_AddSlot
+ * NSSTrustDomain_UnloadModule
+ * Managing modules, slots, tokens; priorities;
+ * Traversing all of the above
+ * this needs more work
+ */
+
+/*
+ * NSSTrustDomain_DisableToken
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_DisableToken
+(
+ NSSTrustDomain *td,
+ NSSToken *token,
+ NSSError why
+);
+
+/*
+ * NSSTrustDomain_EnableToken
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_EnableToken
+(
+ NSSTrustDomain *td,
+ NSSToken *token
+);
+
+/*
+ * NSSTrustDomain_IsTokenEnabled
+ *
+ * If disabled, "why" is always on the error stack.
+ * The optional argument is just for convenience.
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_IsTokenEnabled
+(
+ NSSTrustDomain *td,
+ NSSToken *token,
+ NSSError *whyOpt
+);
+
+/*
+ * NSSTrustDomain_FindSlotByName
+ *
+ */
+
+NSS_EXTERN NSSSlot *
+NSSTrustDomain_FindSlotByName
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *slotName
+);
+
+/*
+ * NSSTrustDomain_FindTokenByName
+ *
+ */
+
+NSS_EXTERN NSSToken *
+NSSTrustDomain_FindTokenByName
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *tokenName
+);
+
+/*
+ * NSSTrustDomain_FindTokenBySlotName
+ *
+ */
+
+NSS_EXTERN NSSToken *
+NSSTrustDomain_FindTokenBySlotName
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *slotName
+);
+
+/*
+ * NSSTrustDomain_FindBestTokenForAlgorithm
+ *
+ */
+
+NSS_EXTERN NSSToken *
+NSSTrustDomain_FindTokenForAlgorithm
+(
+ NSSTrustDomain *td,
+ NSSOID *algorithm
+);
+
+/*
+ * NSSTrustDomain_FindBestTokenForAlgorithms
+ *
+ */
+
+NSS_EXTERN NSSToken *
+NSSTrustDomain_FindBestTokenForAlgorithms
+(
+ NSSTrustDomain *td,
+ NSSOID *algorithms[], /* may be null-terminated */
+ PRUint32 nAlgorithmsOpt /* limits the array if nonzero */
+);
+
+/*
+ * NSSTrustDomain_Login
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_Login
+(
+ NSSTrustDomain *td,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSTrustDomain_Logout
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_Logout
+(
+ NSSTrustDomain *td
+);
+
+/* Importing things */
+
+/*
+ * NSSTrustDomain_ImportCertificate
+ *
+ * The implementation will pull some data out of the certificate
+ * (e.g. e-mail address) for use in pkcs#11 object attributes.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_ImportCertificate
+(
+ NSSTrustDomain *td,
+ NSSCertificate *c
+);
+
+/*
+ * NSSTrustDomain_ImportPKIXCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_ImportPKIXCertificate
+(
+ NSSTrustDomain *td,
+ /* declared as a struct until these "data types" are defined */
+ struct NSSPKIXCertificateStr *pc
+);
+
+/*
+ * NSSTrustDomain_ImportEncodedCertificate
+ *
+ * Imports any type of certificate we support.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_ImportEncodedCertificate
+(
+ NSSTrustDomain *td,
+ NSSBER *ber
+);
+
+/*
+ * NSSTrustDomain_ImportEncodedCertificateChain
+ *
+ * If you just want the leaf, pass in a maximum of one.
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_ImportEncodedCertificateChain
+(
+ NSSTrustDomain *td,
+ NSSBER *ber,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_ImportEncodedPrivateKey
+ *
+ */
+
+NSS_EXTERN NSSPrivateKey *
+NSSTrustDomain_ImportEncodedPrivateKey
+(
+ NSSTrustDomain *td,
+ NSSBER *ber,
+ NSSItem *passwordOpt, /* NULL will cause a callback */
+ NSSCallback *uhhOpt,
+ NSSToken *destination
+);
+
+/*
+ * NSSTrustDomain_ImportEncodedPublicKey
+ *
+ */
+
+NSS_EXTERN NSSPublicKey *
+NSSTrustDomain_ImportEncodedPublicKey
+(
+ NSSTrustDomain *td,
+ NSSBER *ber
+);
+
+/* Other importations: S/MIME capabilities */
+
+/*
+ * NSSTrustDomain_FindBestCertificateByNickname
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindBestCertificateByNickname
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *name,
+ NSSTime *timeOpt, /* NULL for "now" */
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt /* NULL for none */
+);
+
+/*
+ * NSSTrustDomain_FindCertificatesByNickname
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindCertificatesByNickname
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *name,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_FindCertificateByIssuerAndSerialNumber
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindCertificateByIssuerAndSerialNumber
+(
+ NSSTrustDomain *td,
+ NSSDER *issuer,
+ NSSDER *serialNumber
+);
+
+/*
+ * NSSTrustDomain_FindCertificatesByIssuerAndSerialNumber
+ *
+ * Theoretically, this should never happen. However, some companies
+ * we know have issued duplicate certificates with the same issuer
+ * and serial number. Do we just ignore them? I'm thinking yes.
+ */
+
+/*
+ * NSSTrustDomain_FindBestCertificateBySubject
+ *
+ * This does not search through alternate names hidden in extensions.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindBestCertificateBySubject
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *subject,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSTrustDomain_FindCertificatesBySubject
+ *
+ * This does not search through alternate names hidden in extensions.
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindCertificatesBySubject
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *subject,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_FindBestCertificateByNameComponents
+ *
+ * This call does try several tricks, including a pseudo pkcs#11
+ * attribute for the ldap module to try as a query. Eventually
+ * this call falls back to a traversal if that's what's required.
+ * It will search through alternate names hidden in extensions.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindBestCertificateByNameComponents
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *nameComponents,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSTrustDomain_FindCertificatesByNameComponents
+ *
+ * This call, too, tries several tricks. It will stop on the first
+ * attempt that generates results, so it won't e.g. traverse the
+ * entire ldap database.
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindCertificatesByNameComponents
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *nameComponents,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_FindCertificateByEncodedCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindCertificateByEncodedCertificate
+(
+ NSSTrustDomain *td,
+ NSSBER *encodedCertificate
+);
+
+/*
+ * NSSTrustDomain_FindBestCertificateByEmail
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindCertificateByEmail
+(
+ NSSTrustDomain *td,
+ NSSASCII7 *email,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSTrustDomain_FindCertificatesByEmail
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindCertificateByEmail
+(
+ NSSTrustDomain *td,
+ NSSASCII7 *email,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_FindCertificateByOCSPHash
+ *
+ * There can be only one.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindCertificateByOCSPHash
+(
+ NSSTrustDomain *td,
+ NSSItem *hash
+);
+
+/*
+ * NSSTrustDomain_TraverseCertificates
+ *
+ * This function descends from one in older versions of NSS which
+ * traverses the certs in the permanent database. That function
+ * was used to implement selection routines, but was directly
+ * available too. Trust domains are going to contain a lot more
+ * certs now (e.g., an ldap server), so we'd really like to
+ * discourage traversal. Thus for now, this is commented out.
+ * If it's needed, let's look at the situation more closely to
+ * find out what the actual requirements are.
+ *
+ *
+ * NSS_EXTERN PRStatus *
+ * NSSTrustDomain_TraverseCertificates
+ * (
+ * NSSTrustDomain *td,
+ * PRStatus (*callback)(NSSCertificate *c, void *arg),
+ * void *arg
+ * );
+ */
+
+/*
+ * NSSTrustDomain_FindBestUserCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindBestUserCertificate
+(
+ NSSTrustDomain *td,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSTrustDomain_FindUserCertificates
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindUserCertificates
+(
+ NSSTrustDomain *td,
+ NSSTime *timeOpt,
+ NSSUsage *usageOpt,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_FindBestUserCertificateForSSLClientAuth
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindBestUserCertificateForSSLClientAuth
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *sslHostOpt,
+ NSSDER *rootCAsOpt[], /* null pointer for none */
+ PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSTrustDomain_FindUserCertificatesForSSLClientAuth
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindUserCertificatesForSSLClientAuth
+(
+ NSSTrustDomain *td,
+ NSSUTF8 *sslHostOpt,
+ NSSDER *rootCAsOpt[], /* null pointer for none */
+ PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSTrustDomain_FindBestUserCertificateForEmailSigning
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSTrustDomain_FindBestUserCertificateForEmailSigning
+(
+ NSSTrustDomain *td,
+ NSSASCII7 *signerOpt,
+ NSSASCII7 *recipientOpt,
+ /* anything more here? */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSTrustDomain_FindUserCertificatesForEmailSigning
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSTrustDomain_FindUserCertificatesForEmailSigning
+(
+ NSSTrustDomain *td,
+ NSSASCII7 *signerOpt,
+ NSSASCII7 *recipientOpt,
+ /* anything more here? */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/*
+ * Here is where we'd add more Find[Best]UserCertificate[s]For<usage>
+ * routines.
+ */
+
+/* Private Keys */
+
+/*
+ * NSSTrustDomain_GenerateKeyPair
+ *
+ * Creates persistant objects. If you want session objects, use
+ * NSSCryptoContext_GenerateKeyPair. The destination token is where
+ * the keys are stored. If that token can do the required math, then
+ * that's where the keys are generated too. Otherwise, the keys are
+ * generated elsewhere and moved to that token.
+ */
+
+NSS_EXTERN PRStatus
+NSSTrustDomain_GenerateKeyPair
+(
+ NSSTrustDomain *td,
+ NSSAlgorithmAndParameters *ap,
+ NSSPrivateKey **pvkOpt,
+ NSSPublicKey **pbkOpt,
+ PRBool privateKeyIsSensitive,
+ NSSToken *destination,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSTrustDomain_TraversePrivateKeys
+ *
+ *
+ * NSS_EXTERN PRStatus *
+ * NSSTrustDomain_TraversePrivateKeys
+ * (
+ * NSSTrustDomain *td,
+ * PRStatus (*callback)(NSSPrivateKey *vk, void *arg),
+ * void *arg
+ * );
+ */
+
+/* Symmetric Keys */
+
+/*
+ * NSSTrustDomain_GenerateSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSTrustDomain_GenerateSymmetricKey
+(
+ NSSTrustDomain *td,
+ NSSAlgorithmAndParameters *ap,
+ PRUint32 keysize,
+ NSSToken *destination,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSTrustDomain_GenerateSymmetricKeyFromPassword
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSTrustDomain_GenerateSymmetricKeyFromPassword
+(
+ NSSTrustDomain *td,
+ NSSAlgorithmAndParameters *ap,
+ NSSUTF8 *passwordOpt, /* if null, prompt */
+ NSSToken *destinationOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSTrustDomain_FindSymmetricKeyByAlgorithm
+ *
+ * Is this still needed?
+ *
+ * NSS_EXTERN NSSSymmetricKey *
+ * NSSTrustDomain_FindSymmetricKeyByAlgorithm
+ * (
+ * NSSTrustDomain *td,
+ * NSSOID *algorithm,
+ * NSSCallback *uhhOpt
+ * );
+ */
+
+/*
+ * NSSTrustDomain_FindSymmetricKeyByAlgorithmAndKeyID
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSTrustDomain_FindSymmetricKeyByAlgorithmAndKeyID
+(
+ NSSTrustDomain *td,
+ NSSOID *algorithm,
+ NSSItem *keyID,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSTrustDomain_TraverseSymmetricKeys
+ *
+ *
+ * NSS_EXTERN PRStatus *
+ * NSSTrustDomain_TraverseSymmetricKeys
+ * (
+ * NSSTrustDomain *td,
+ * PRStatus (*callback)(NSSSymmetricKey *mk, void *arg),
+ * void *arg
+ * );
+ */
+
+/*
+ * NSSTrustDomain_CreateCryptoContext
+ *
+ * If a callback object is specified, it becomes the for the crypto
+ * context; otherwise, this trust domain's default (if any) is
+ * inherited.
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSTrustDomain_CreateCryptoContext
+(
+ NSSTrustDomain *td,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSTrustDomain_CreateCryptoContextForAlgorithm
+ *
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSTrustDomain_CreateCryptoContextForAlgorithm
+(
+ NSSTrustDomain *td,
+ NSSOID *algorithm
+);
+
+/*
+ * NSSTrustDomain_CreateCryptoContextForAlgorithmAndParameters
+ *
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSTrustDomain_CreateCryptoContextForAlgorithmAndParameters
+(
+ NSSTrustDomain *td,
+ NSSAlgorithmAndParameters *ap
+);
+
+/* find/traverse other objects, e.g. s/mime profiles */
+
+/*
+ * NSSCryptoContext
+ *
+ * A crypto context is sort of a short-term snapshot of a trust domain,
+ * used for the life of "one crypto operation." You can also think of
+ * it as a "temporary database."
+ *
+ * Just about all of the things you can do with a trust domain -- importing
+ * or creating certs, keys, etc. -- can be done with a crypto context.
+ * The difference is that the objects will be temporary ("session") objects.
+ *
+ * Also, if the context was created for a key, cert, and/or algorithm; or
+ * if such objects have been "associated" with the context, then the context
+ * can do everything the keys can, like crypto operations.
+ *
+ * And finally, because it keeps the state of the crypto operations, it
+ * can do streaming crypto ops.
+ */
+
+/*
+ * NSSTrustDomain_Destroy
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_Destroy
+(
+ NSSCryptoContext *td
+);
+
+/* establishing a default callback */
+
+/*
+ * NSSCryptoContext_SetDefaultCallback
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_SetDefaultCallback
+(
+ NSSCryptoContext *td,
+ NSSCallback *newCallback,
+ NSSCallback **oldCallbackOpt
+);
+
+/*
+ * NSSCryptoContext_GetDefaultCallback
+ *
+ */
+
+NSS_EXTERN NSSCallback *
+NSSCryptoContext_GetDefaultCallback
+(
+ NSSCryptoContext *td,
+ PRStatus *statusOpt
+);
+
+/*
+ * NSSCryptoContext_GetTrustDomain
+ *
+ */
+
+NSS_EXTERN NSSTrustDomain *
+NSSCryptoContext_GetTrustDomain
+(
+ NSSCryptoContext *td
+);
+
+/* AddModule, etc: should we allow "temporary" changes here? */
+/* DisableToken, etc: ditto */
+/* Ordering of tokens? */
+/* Finding slots+token etc. */
+/* login+logout */
+
+/* Importing things */
+
+/*
+ * NSSCryptoContext_ImportCertificate
+ *
+ * If there's not a "distinguished certificate" for this context, this
+ * sets the specified one to be it.
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_ImportCertificate
+(
+ NSSCryptoContext *cc,
+ NSSCertificate *c
+);
+
+/*
+ * NSSCryptoContext_ImportPKIXCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_ImportPKIXCertificate
+(
+ NSSCryptoContext *cc,
+ struct NSSPKIXCertificateStr *pc
+);
+
+/*
+ * NSSCryptoContext_ImportEncodedCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_ImportEncodedCertificate
+(
+ NSSCryptoContext *cc,
+ NSSBER *ber
+);
+
+/*
+ * NSSCryptoContext_ImportEncodedPKIXCertificateChain
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_ImportEncodedPKIXCertificateChain
+(
+ NSSCryptoContext *cc,
+ NSSBER *ber
+);
+
+/* Other importations: S/MIME capabilities
+ */
+
+/*
+ * NSSCryptoContext_FindBestCertificateByNickname
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestCertificateByNickname
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *name,
+ NSSTime *timeOpt, /* NULL for "now" */
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt /* NULL for none */
+);
+
+/*
+ * NSSCryptoContext_FindCertificatesByNickname
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSCryptoContext_FindCertificatesByNickname
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *name,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FindCertificateByIssuerAndSerialNumber
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindCertificateByIssuerAndSerialNumber
+(
+ NSSCryptoContext *cc,
+ NSSDER *issuer,
+ NSSDER *serialNumber
+);
+
+/*
+ * NSSCryptoContext_FindBestCertificateBySubject
+ *
+ * This does not search through alternate names hidden in extensions.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestCertificateBySubject
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *subject,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSCryptoContext_FindCertificatesBySubject
+ *
+ * This does not search through alternate names hidden in extensions.
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSCryptoContext_FindCertificatesBySubject
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *subject,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FindBestCertificateByNameComponents
+ *
+ * This call does try several tricks, including a pseudo pkcs#11
+ * attribute for the ldap module to try as a query. Eventually
+ * this call falls back to a traversal if that's what's required.
+ * It will search through alternate names hidden in extensions.
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestCertificateByNameComponents
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *nameComponents,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSCryptoContext_FindCertificatesByNameComponents
+ *
+ * This call, too, tries several tricks. It will stop on the first
+ * attempt that generates results, so it won't e.g. traverse the
+ * entire ldap database.
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSCryptoContext_FindCertificatesByNameComponents
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *nameComponents,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FindCertificateByEncodedCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindCertificateByEncodedCertificate
+(
+ NSSCryptoContext *cc,
+ NSSBER *encodedCertificate
+);
+
+/*
+ * NSSCryptoContext_FindBestCertificateByEmail
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestCertificateByEmail
+(
+ NSSCryptoContext *cc,
+ NSSASCII7 *email
+);
+
+/*
+ * NSSCryptoContext_FindCertificatesByEmail
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindCertificatesByEmail
+(
+ NSSCryptoContext *cc,
+ NSSASCII7 *email,
+ NSSCertificate *rvOpt[],
+ PRUint32 maximumOpt, /* 0 for no max */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FindCertificateByOCSPHash
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindCertificateByOCSPHash
+(
+ NSSCryptoContext *cc,
+ NSSITem *hash
+);
+
+/*
+ * NSSCryptoContext_TraverseCertificates
+ *
+ *
+ * NSS_EXTERN PRStatus *
+ * NSSCryptoContext_TraverseCertificates
+ * (
+ * NSSCryptoContext *cc,
+ * PRStatus (*callback)(NSSCertificate *c, void *arg),
+ * void *arg
+ * );
+ */
+
+/*
+ * NSSCryptoContext_FindBestUserCertificate
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestUserCertificate
+(
+ NSSCryptoContext *cc,
+ NSSTime *timeOpt,
+ NSSUsage *usage,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSCryptoContext_FindUserCertificates
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSCryptoContext_FindUserCertificates
+(
+ NSSCryptoContext *cc,
+ NSSTime *timeOpt,
+ NSSUsage *usageOpt,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FindBestUserCertificateForSSLClientAuth
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestUserCertificateForSSLClientAuth
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *sslHostOpt,
+ NSSDER *rootCAsOpt[], /* null pointer for none */
+ PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSCryptoContext_FindUserCertificatesForSSLClientAuth
+ *
+ */
+
+NSS_EXTERN NSSCertificate **
+NSSCryptoContext_FindUserCertificatesForSSLClientAuth
+(
+ NSSCryptoContext *cc,
+ NSSUTF8 *sslHostOpt,
+ NSSDER *rootCAsOpt[], /* null pointer for none */
+ PRUint32 rootCAsMaxOpt, /* zero means list is null-terminated */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FindBestUserCertificateForEmailSigning
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindBestUserCertificateForEmailSigning
+(
+ NSSCryptoContext *cc,
+ NSSASCII7 *signerOpt,
+ NSSASCII7 *recipientOpt,
+ /* anything more here? */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt
+);
+
+/*
+ * NSSCryptoContext_FindUserCertificatesForEmailSigning
+ *
+ */
+
+NSS_EXTERN NSSCertificate *
+NSSCryptoContext_FindUserCertificatesForEmailSigning
+(
+ NSSCryptoContext *cc,
+ NSSASCII7 *signerOpt, /* fgmr or a more general name? */
+ NSSASCII7 *recipientOpt,
+ /* anything more here? */
+ NSSAlgorithmAndParameters *apOpt,
+ NSSPolicies *policiesOpt,
+ NSSCertificate **rvOpt,
+ PRUint32 rvLimit, /* zero for no limit */
+ NSSArena *arenaOpt
+);
+
+/* Private Keys */
+
+/*
+ * NSSCryptoContext_GenerateKeyPair
+ *
+ * Creates session objects. If you want persistant objects, use
+ * NSSTrustDomain_GenerateKeyPair. The destination token is where
+ * the keys are stored. If that token can do the required math, then
+ * that's where the keys are generated too. Otherwise, the keys are
+ * generated elsewhere and moved to that token.
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_GenerateKeyPair
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *ap,
+ NSSPrivateKey **pvkOpt,
+ NSSPublicKey **pbkOpt,
+ PRBool privateKeyIsSensitive,
+ NSSToken *destination,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_TraversePrivateKeys
+ *
+ *
+ * NSS_EXTERN PRStatus *
+ * NSSCryptoContext_TraversePrivateKeys
+ * (
+ * NSSCryptoContext *cc,
+ * PRStatus (*callback)(NSSPrivateKey *vk, void *arg),
+ * void *arg
+ * );
+ */
+
+/* Symmetric Keys */
+
+/*
+ * NSSCryptoContext_GenerateSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSCryptoContext_GenerateSymmetricKey
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *ap,
+ PRUint32 keysize,
+ NSSToken *destination,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_GenerateSymmetricKeyFromPassword
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSCryptoContext_GenerateSymmetricKeyFromPassword
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *ap,
+ NSSUTF8 *passwordOpt, /* if null, prompt */
+ NSSToken *destinationOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_FindSymmetricKeyByAlgorithm
+ *
+ *
+ * NSS_EXTERN NSSSymmetricKey *
+ * NSSCryptoContext_FindSymmetricKeyByType
+ * (
+ * NSSCryptoContext *cc,
+ * NSSOID *type,
+ * NSSCallback *uhhOpt
+ * );
+ */
+
+/*
+ * NSSCryptoContext_FindSymmetricKeyByAlgorithmAndKeyID
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSCryptoContext_FindSymmetricKeyByAlgorithmAndKeyID
+(
+ NSSCryptoContext *cc,
+ NSSOID *algorithm,
+ NSSItem *keyID,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_TraverseSymmetricKeys
+ *
+ *
+ * NSS_EXTERN PRStatus *
+ * NSSCryptoContext_TraverseSymmetricKeys
+ * (
+ * NSSCryptoContext *cc,
+ * PRStatus (*callback)(NSSSymmetricKey *mk, void *arg),
+ * void *arg
+ * );
+ */
+
+/* Crypto ops on distinguished keys */
+
+/*
+ * NSSCryptoContext_Decrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_Decrypt
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *encryptedData,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_BeginDecrypt
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginDecrypt
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueDecrypt
+ *
+ */
+
+/*
+ * NSSItem semantics:
+ *
+ * If rvOpt is NULL, a new NSSItem and buffer are allocated.
+ * If rvOpt is not null, but the buffer pointer is null,
+ * then rvOpt is returned but a new buffer is allocated.
+ * In this case, if the length value is not zero, then
+ * no more than that much space will be allocated.
+ * If rvOpt is not null and the buffer pointer is not null,
+ * then that buffer is re-used. No more than the buffer
+ * length value will be used; if it's not enough, an
+ * error is returned. If less is used, the number is
+ * adjusted downwards.
+ *
+ * Note that although this is short of some ideal "Item"
+ * definition, we can usually tell how big these buffers
+ * have to be.
+ *
+ * Feedback is requested; and earlier is better than later.
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_ContinueDecrypt
+(
+ NSSCryptoContext *cc,
+ NSSItem *data,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FinishDecrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_FinishDecrypt
+(
+ NSSCryptoContext *cc,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_Sign
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_Sign
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_BeginSign
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginSign
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueSign
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginSign
+(
+ NSSCryptoContext *cc,
+ NSSItem *data
+);
+
+/*
+ * NSSCryptoContext_FinishSign
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_FinishSign
+(
+ NSSCryptoContext *cc,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_SignRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_SignRecover
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_BeginSignRecover
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginSignRecover
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueSignRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_ContinueSignRecover
+(
+ NSSCryptoContext *cc,
+ NSSItem *data,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FinishSignRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_FinishSignRecover
+(
+ NSSCryptoContext *cc,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_UnwrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSCryptoContext_UnwrapSymmetricKey
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *wrappedKey,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_DeriveSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSSymmetricKey *
+NSSCryptoContext_DeriveSymmetricKey
+(
+ NSSCryptoContext *cc,
+ NSSPublicKey *bk,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSOID *target,
+ PRUint32 keySizeOpt, /* zero for best allowed */
+ NSSOperations operations,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_Encrypt
+ *
+ * Encrypt a single chunk of data with the distinguished public key
+ * of this crypto context.
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_Encrypt
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_BeginEncrypt
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginEncrypt
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueEncrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_ContinueEncrypt
+(
+ NSSCryptoContext *cc,
+ NSSItem *data,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FinishEncrypt
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_FinishEncrypt
+(
+ NSSCryptoContext *cc,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_Verify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_Verify
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSItem *signature,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_BeginVerify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginVerify
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *signature,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueVerify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_ContinueVerify
+(
+ NSSCryptoContext *cc,
+ NSSItem *data
+);
+
+/*
+ * NSSCryptoContext_FinishVerify
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_FinishVerify
+(
+ NSSCryptoContext *cc
+);
+
+/*
+ * NSSCryptoContext_VerifyRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_VerifyRecover
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *signature,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_BeginVerifyRecover
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginVerifyRecover
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueVerifyRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_ContinueVerifyRecover
+(
+ NSSCryptoContext *cc,
+ NSSItem *data,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_FinishVerifyRecover
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_FinishVerifyRecover
+(
+ NSSCryptoContext *cc,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_WrapSymmetricKey
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_WrapSymmetricKey
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSSymmetricKey *keyToWrap,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_Digest
+ *
+ * Digest a single chunk of data with the distinguished digest key
+ * of this crypto context.
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_Digest
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *data,
+ NSSCallback *uhhOpt,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSCryptoContext_BeginDigest
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_BeginDigest
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSCallback *uhhOpt
+);
+
+/*
+ * NSSCryptoContext_ContinueDigest
+ *
+ */
+
+NSS_EXTERN PRStatus
+NSSCryptoContext_ContinueDigest
+(
+ NSSCryptoContext *cc,
+ NSSAlgorithmAndParameters *apOpt,
+ NSSItem *item
+);
+
+/*
+ * NSSCryptoContext_FinishDigest
+ *
+ */
+
+NSS_EXTERN NSSItem *
+NSSCryptoContext_FinishDigest
+(
+ NSSCryptoContext *cc,
+ NSSItem *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * tbd: Combination ops
+ */
+
+/*
+ * NSSCryptoContext_Clone
+ *
+ */
+
+NSS_EXTERN NSSCryptoContext *
+NSSCryptoContext_Clone
+(
+ NSSCryptoContext *cc
+);
+
+/*
+ * NSSCryptoContext_Save
+ * NSSCryptoContext_Restore
+ *
+ * We need to be able to save and restore the state of contexts.
+ * Perhaps a mark-and-release mechanism would be better?
+ */
+
+/*
+ * ..._SignTBSCertificate
+ *
+ * This requires feedback from the cert server team.
+ */
+
+/*
+ * PRBool NSSCertificate_GetIsTrustedFor{xxx}(NSSCertificate *c);
+ * PRStatus NSSCertificate_SetIsTrustedFor{xxx}(NSSCertificate *c, PRBool trusted);
+ *
+ * These will be helper functions which get the trust object for a cert,
+ * and then call the corresponding function(s) on it.
+ *
+ * PKIX trust objects will have methods to manipulate the low-level trust
+ * bits (which are based on key usage and extended key usage), and also the
+ * conceptual high-level usages (e.g. ssl client auth, email encryption, etc.)
+ *
+ * Other types of trust objects (if any) might have different low-level
+ * representations, but hopefully high-level concepts would map.
+ *
+ * Only these high-level general routines would be promoted to the
+ * general certificate level here. Hence the {xxx} above would be things
+ * like "EmailSigning."
+ *
+ *
+ * NSSPKIXTrust *NSSCertificate_GetPKIXTrustObject(NSSCertificate *c);
+ * PRStatus NSSCertificate_SetPKIXTrustObject(NSSCertificate *c, NSPKIXTrust *t);
+ *
+ * I want to hold off on any general trust object until we've investigated
+ * other models more thoroughly.
+ */
+
+PR_END_EXTERN_C
+
+#endif /* NSSPKI_H */
diff --git a/security/nss/lib/pki/nsspkit.h b/security/nss/lib/pki/nsspkit.h
new file mode 100644
index 000000000..e1b5888fa
--- /dev/null
+++ b/security/nss/lib/pki/nsspkit.h
@@ -0,0 +1,261 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef NSSPKIT_H
+#define NSSPKIT_H
+
+#ifdef DEBUG
+static const char NSSPKIT_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * nsspkit.h
+ *
+ * This file defines the types of the top-level PKI objects.
+ */
+
+#ifndef NSSBASET_H
+#include "nssbaset.h"
+#endif /* NSSBASET_H */
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * NSSCertificate
+ *
+ * This is the public representation of a Certificate. The certificate
+ * may be one found on a smartcard or other token, one decoded from data
+ * received as part of a protocol, one constructed from constituent
+ * parts, etc. Usually it is associated with ("in") a trust domain; as
+ * it can be verified only within a trust domain. The underlying type
+ * of certificate may be of any supported standard, e.g. PKIX, PGP, etc.
+ *
+ * People speak of "verifying (with) the server's, or correspondant's,
+ * certificate"; for simple operations we support that simplification
+ * by implementing public-key crypto operations as methods on this type.
+ */
+
+struct NSSCertificateStr;
+typedef struct NSSCertificateStr NSSCertificate;
+
+/*
+ * NSSUserCertificate
+ *
+ * A ``User'' certificate is one for which the private key is available.
+ * People speak of "using my certificate to sign my email" and "using
+ * my certificate to authenticate to (or login to) the server"; for
+ * simple operations, we support that simplification by implementing
+ * private-key crypto operations as methods on this type.
+ *
+ * The current design only weakly distinguishes between certificates
+ * and user certificates: as far as the compiler goes they're
+ * interchangable; debug libraries only have one common pointer-tracker;
+ * etc. However, attempts to do private-key operations on a certificate
+ * for which the private key is not available will fail.
+ *
+ * Open design question: should these types be more firmly separated?
+ */
+
+typedef NSSCertificate NSSUserCertificate;
+
+/*
+ * NSSPrivateKey
+ *
+ * This is the public representation of a Private Key. In general,
+ * the actual value of the key is not available, but operations may
+ * be performed with it.
+ */
+
+struct NSSPrivateKeyStr;
+typedef struct NSSPrivateKeyStr NSSPrivateKey;
+
+/*
+ * NSSPublicKey
+ *
+ */
+
+struct NSSPublicKeyStr;
+typedef struct NSSPublicKeyStr NSSPublicKey;
+
+/*
+ * NSSSymmetricKey
+ *
+ */
+
+struct NSSSymmetricKeyStr;
+typedef struct NSSSymmetricKeyStr NSSSymmetricKey;
+
+/*
+ * NSSTrustDomain
+ *
+ * A Trust Domain is the field in which certificates may be validated.
+ * A trust domain will generally have one or more cryptographic modules
+ * open; these modules perform the cryptographic operations, and
+ * provide the basic "root" trust information from which the trust in
+ * a specific certificate or key depends.
+ *
+ * A client program, or a simple server, would typically have one
+ * trust domain. A server supporting multiple "virtual servers" might
+ * have a separate trust domain for each virtual server. The separate
+ * trust domains might share some modules (e.g., a hardware crypto
+ * accelerator) but not others (e.g., the tokens storing the different
+ * servers' private keys, or the databases with each server's trusted
+ * root certificates).
+ *
+ * This object descends from the "permananet database" in the old code.
+ */
+
+struct NSSTrustDomainStr;
+typedef struct NSSTrustDomainStr NSSTrustDomain;
+
+/*
+ * NSSCryptoContext
+ *
+ * A Crypto Context is a short-term, "helper" object which is used
+ * for the lifetime of one ongoing "crypto operation." Such an
+ * operation may be the creation of a signed message, the use of an
+ * TLS socket connection, etc. Each crypto context is "in" a
+ * specific trust domain, and it may have associated with it a
+ * distinguished certificate, public key, private key, and/or
+ * symmetric key. It can also temporarily hold and use temporary
+ * data (e.g. intermediate certificates) which is not stored
+ * permanently in the trust domain.
+ *
+ * In OO terms, this interface inherits interfaces from the trust
+ * domain, the certificates, and the keys. It also provides
+ * streaming crypto operations.
+ *
+ * This object descends from the "temporary database" concept in the
+ * old code, but it has changed a lot as a result of what we've
+ * learned.
+ */
+
+struct NSSCryptoContextStr;
+typedef struct NSSCryptoContextStr NSSCryptoContext;
+
+/*
+ * fgmr others
+ */
+
+/*
+ * NSSTime
+ *
+ * Unfortunately, we need an "exceptional" value to indicate
+ * an error upon return, or "no value" on input. Note that zero
+ * is a perfectly valid value for both time_t and PRTime.
+ *
+ * If we were to create a "range" object, with two times for
+ * Not Before and Not After, we would have an obvious place for
+ * the somewhat arbitrary logic involved in comparing them.
+ *
+ * Failing that, let's have an NSSTime_CompareRanges function.
+ */
+
+struct NSSTimeStr;
+typedef struct NSSTimeStr NSSTime;
+
+/*
+ * NSSUsage
+ *
+ * This is trickier than originally planned; I'll write up a
+ * doc on it.
+ *
+ * We'd still like nsspki.h to have a list of common usages,
+ * e.g.:
+ *
+ * extern const NSSUsage *NSSUsage_ClientAuth;
+ * extern const NSSUsage *NSSUsage_ServerAuth;
+ * extern const NSSUsage *NSSUsage_SignEmail;
+ * extern const NSSUsage *NSSUsage_EncryptEmail;
+ * etc.
+ */
+
+struct NSSUsageStr;
+typedef struct NSSUsageStr NSSUsage;
+
+/*
+ * NSSPolicies
+ *
+ * Placeholder, for now.
+ */
+
+struct NSSPoliciesStr;
+typedef struct NSSPoliciesStr NSSPolicies;
+
+/*
+ * NSSAlgorithmAndParameters
+ *
+ * Algorithm is an OID
+ * Parameters depend on the algorithm
+ */
+
+struct NSSAlgorithmAndParametersStr;
+typedef struct NSSAlgorithmAndParametersStr NSSAlgorithmAndParameters;
+
+/*
+ * NSSCallback
+ *
+ * At minimum, a "challenge" method and a closure argument.
+ * Usually the challenge will just be prompting for a password.
+ * How OO do we want to make it?
+ */
+
+struct NSSCallbackStr;
+typedef struct NSSCallbackStr NSSCallback;
+
+/*
+ * NSSModule and NSSSlot -- placeholders for the PKCS#11 types
+ */
+
+struct NSSModuleStr;
+typedef struct NSSModuleStr NSSModule;
+
+struct NSSSlotStr;
+typedef struct NSSSlotStr NSSSlot;
+
+typedef PRUint32 NSSOperations;
+/* 1) Do we want these to be preprocessor definitions or constants? */
+/* 2) What is the correct and complete list? */
+
+#define NSSOperations_ENCRYPT 0x0001
+#define NSSOperations_DECRYPT 0x0002
+#define NSSOperations_WRAP 0x0004
+#define NSSOperations_UNWRAP 0x0008
+#define NSSOperations_SIGN 0x0010
+#define NSSOperations_SIGN_RECOVER 0x0020
+#define NSSOperations_VERIFY 0x0040
+#define NSSOperations_VERIFY_RECOVER 0x0080
+
+PR_END_EXTERN_C
+
+#endif /* NSSPKIT_H */
diff --git a/security/nss/lib/pki1/.cvsignore b/security/nss/lib/pki1/.cvsignore
new file mode 100644
index 000000000..12bfbfc7c
--- /dev/null
+++ b/security/nss/lib/pki1/.cvsignore
@@ -0,0 +1,3 @@
+oiddata.c
+oiddata.h
+
diff --git a/security/nss/lib/pki1/Makefile b/security/nss/lib/pki1/Makefile
new file mode 100644
index 000000000..03e1fb4c6
--- /dev/null
+++ b/security/nss/lib/pki1/Makefile
@@ -0,0 +1,38 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+MAKEFILE_CVS_ID = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"
+
+include manifest.mn
+include config.mk
+include $(CORE_DEPTH)/coreconf/config.mk
+include $(CORE_DEPTH)/coreconf/rules.mk
diff --git a/security/nss/lib/pki1/atav.c b/security/nss/lib/pki1/atav.c
new file mode 100644
index 000000000..299ddfc0d
--- /dev/null
+++ b/security/nss/lib/pki1/atav.c
@@ -0,0 +1,1803 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * atav.c
+ *
+ * This file contains the implementation of the PKIX part-1 object
+ * AttributeTypeAndValue.
+ */
+
+#ifndef NSSBASE_H
+#include "nssbase.h"
+#endif /* NSSBASE_H */
+
+#ifndef ASN1_H
+#include "asn1.h"
+#endif /* ASN1_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+/*
+ * AttributeTypeAndValue
+ *
+ * From draft-ietf-pkix-ipki-part1-10:
+ *
+ * AttributeTypeAndValue ::= SEQUENCE {
+ * type ATTRIBUTE.&id ({SupportedAttributes}),
+ * value ATTRIBUTE.&Type ({SupportedAttributes}{@type})}
+ *
+ * -- ATTRIBUTE information object class specification
+ * -- Note: This has been greatly simplified for PKIX !!
+ *
+ * ATTRIBUTE ::= CLASS {
+ * &Type,
+ * &id OBJECT IDENTIFIER UNIQUE }
+ * WITH SYNTAX {
+ * WITH SYNTAX &Type ID &id }
+ *
+ * What this means is that the "type" of the value is determined by
+ * the value of the oid. If we hide the structure, our accessors
+ * can (at least in debug builds) assert value semantics beyond what
+ * the compiler can provide. Since these things are only used in
+ * RelativeDistinguishedNames, and since RDNs always contain a SET
+ * of these things, we don't lose anything by hiding the structure
+ * (and its size).
+ */
+
+struct NSSATAVStr {
+ NSSBER ber;
+ const NSSOID *oid;
+ NSSUTF8 *value;
+ nssStringType stringForm;
+};
+
+/*
+ * NSSATAV
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSATAV_CreateFromBER -- constructor
+ * NSSATAV_CreateFromUTF8 -- constructor
+ * NSSATAV_Create -- constructor
+ *
+ * NSSATAV_Destroy
+ * NSSATAV_GetDEREncoding
+ * NSSATAV_GetUTF8Encoding
+ * NSSATAV_GetType
+ * NSSATAV_GetValue
+ * NSSATAV_Compare
+ * NSSATAV_Duplicate
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssATAV_CreateFromBER -- constructor
+ * nssATAV_CreateFromUTF8 -- constructor
+ * nssATAV_Create -- constructor
+ *
+ * nssATAV_Destroy
+ * nssATAV_GetDEREncoding
+ * nssATAV_GetUTF8Encoding
+ * nssATAV_GetType
+ * nssATAV_GetValue
+ * nssATAV_Compare
+ * nssATAV_Duplicate
+ *
+ * In debug builds, the following non-public call is also available:
+ *
+ * nssATAV_verifyPointer
+ */
+
+/*
+ * NSSATAV_CreateFromBER
+ *
+ * This routine creates an NSSATAV by decoding a BER- or DER-encoded
+ * ATAV. If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_IMPLEMENT NSSATAV *
+NSSATAV_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berATAV
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+
+ /*
+ * NSSBERs can be created by the user,
+ * so no pointer-tracking can be checked.
+ */
+
+ if( (NSSBER *)NULL == berATAV ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSATAV *)NULL;
+ }
+
+ if( (void *)NULL == berATAV->data ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSATAV *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssATAV_CreateFromBER(arenaOpt, berATAV);
+}
+
+/*
+ * NSSATAV_CreateFromUTF8
+ *
+ * This routine creates an NSSATAV by decoding a UTF8 string in the
+ * "equals" format, e.g., "c=US." If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_IMPLEMENT NSSATAV *
+NSSATAV_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringATAV
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+
+ /*
+ * NSSUTF8s can be created by the user,
+ * so no pointer-tracking can be checked.
+ */
+
+ if( (NSSUTF8 *)NULL == stringATAV ) {
+ nss_SetError(NSS_ERROR_INVALID_UTF8);
+ return (NSSATAV *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssATAV_CreateFromUTF8(arenaOpt, stringATAV);
+}
+
+/*
+ * NSSATAV_Create
+ *
+ * This routine creates an NSSATAV from the specified NSSOID and the
+ * specified data. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap.If the specified data length is zero,
+ * the data is assumed to be terminated by first zero byte; this allows
+ * UTF8 strings to be easily specified. This routine may return NULL
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ARENA
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_INVALID_POINTER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_IMPLEMENT NSSATAV *
+NSSATAV_Create
+(
+ NSSArena *arenaOpt,
+ const NSSOID *oid,
+ const void *data,
+ PRUint32 length
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSATAV *)NULL;
+ }
+
+ if( (const void *)NULL == data ) {
+ nss_SetError(NSS_ERROR_INVALID_POINTER);
+ return (NSSATAV *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssATAV_Create(arenaOpt, oid, data, length);
+}
+
+/*
+ * NSSATAV_Destroy
+ *
+ * This routine will destroy an ATAV object. It should eventually be
+ * called on all ATAVs created without an arena. While it is not
+ * necessary to call it on ATAVs created within an arena, it is not an
+ * error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_IMPLEMENT PRStatus
+NSSATAV_Destroy
+(
+ NSSATAV *atav
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return PR_FAILURE;
+ }
+#endif /* DEBUG */
+
+ return nssATAV_Destroy(atav);
+}
+
+/*
+ * NSSATAV_GetDEREncoding
+ *
+ * This routine will DER-encode an ATAV object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSATAV
+ */
+
+NSS_IMPLEMENT NSSDER *
+NSSATAV_GetDEREncoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSDER *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSDER *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return nssATAV_GetDEREncoding(atav, arenaOpt);
+}
+
+/*
+ * NSSATAV_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the ATAV in "equals" notation (e.g., "o=Acme").
+ * If the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the "equals" encoding of the
+ * ATAV
+ */
+
+NSS_IMPLEMENT NSSUTF8 *
+NSSATAV_GetUTF8Encoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSUTF8 *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return nssATAV_GetUTF8Encoding(atav, arenaOpt);
+}
+
+/*
+ * NSSATAV_GetType
+ *
+ * This routine returns the NSSOID corresponding to the attribute type
+ * in the specified ATAV. This routine may return NSS_OID_UNKNOWN
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An element of enum NSSOIDenum upon success
+ */
+
+NSS_IMPLEMENT const NSSOID *
+NSSATAV_GetType
+(
+ NSSATAV *atav
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSOID *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssATAV_GetType(atav);
+}
+
+/*
+ * NSSATAV_GetValue
+ *
+ * This routine returns a string containing the attribute value
+ * in the specified ATAV. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSItem containing the attribute value.
+ */
+
+NSS_IMPLEMENT NSSUTF8 *
+NSSATAV_GetValue
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSUTF8 *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return nssATAV_GetValue(atav, arenaOpt);
+}
+
+/*
+ * NSSATAV_Compare
+ *
+ * This routine compares two ATAVs for equality. For two ATAVs to be
+ * equal, the attribute types must be the same, and the attribute
+ * values must have equal length and contents. The result of the
+ * comparison will be stored at the location pointed to by the "equalp"
+ * variable, which must point to a valid PRBool. This routine may
+ * return PR_FAILURE upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_IMPLEMENT PRStatus
+NSSATAV_Compare
+(
+ NSSATAV *atav1,
+ NSSATAV *atav2,
+ PRBool *equalp
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav1) ) {
+ return PR_FAILURE;
+ }
+
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav2) ) {
+ return PR_FAILURE;
+ }
+
+ if( (PRBool *)NULL == equalp ) {
+ nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
+ return PR_FAILURE;
+ }
+#endif /* DEBUG */
+
+ return nssATAV_Compare(atav1, atav2, equalp);
+}
+
+/*
+ * NSSATAV_Duplicate
+ *
+ * This routine duplicates the specified ATAV. If the optional arena
+ * argument is non-null, the memory required will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL on error
+ * A pointer to a new ATAV
+ */
+
+NSS_IMPLEMENT NSSATAV *
+NSSATAV_Duplicate
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSATAV *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return nssATAV_Duplicate(atav, arenaOpt);
+}
+
+/*
+ * The pointer-tracking code
+ */
+
+#ifdef DEBUG
+extern const NSSError NSS_ERROR_INTERNAL_ERROR;
+
+static nssPointerTracker atav_pointer_tracker;
+
+static PRStatus
+atav_add_pointer
+(
+ const NSSATAV *atav
+)
+{
+ PRStatus rv;
+
+ rv = nssPointerTracker_initialize(&atav_pointer_tracker);
+ if( PR_SUCCESS != rv ) {
+ return rv;
+ }
+
+ rv = nssPointerTracker_add(&atav_pointer_tracker, atav);
+ if( PR_SUCCESS != rv ) {
+ NSSError e = NSS_GetError();
+ if( NSS_ERROR_NO_MEMORY != e ) {
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ }
+
+ return rv;
+ }
+
+ return PR_SUCCESS;
+}
+
+static PRStatus
+atav_remove_pointer
+(
+ const NSSATAV *atav
+)
+{
+ PRStatus rv;
+
+ rv = nssPointerTracker_remove(&atav_pointer_tracker, atav);
+ if( PR_SUCCESS != rv ) {
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ }
+
+ return rv;
+}
+
+/*
+ * nssATAV_verifyPointer
+ *
+ * This method is only present in debug builds.
+ *
+ * If the specified pointer is a valid pointer to an NSSATAV object,
+ * this routine will return PR_SUCCESS. Otherwise, it will put an
+ * error on the error stack and return PR_FAILRUE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS if the pointer is valid
+ * PR_FAILURE if it isn't
+ */
+
+NSS_IMPLEMENT PRStatus
+nssATAV_verifyPointer
+(
+ NSSATAV *atav
+)
+{
+ PRStatus rv;
+
+ rv = nssPointerTracker_initialize(&atav_pointer_tracker);
+ if( PR_SUCCESS != rv ) {
+ return PR_FAILURE;
+ }
+
+ rv = nssPointerTracker_verify(&atav_pointer_tracker, atav);
+ if( PR_SUCCESS != rv ) {
+ nss_SetError(NSS_ERROR_INVALID_ATAV);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+#endif /* DEBUG */
+
+typedef struct {
+ NSSBER oid;
+ NSSBER value;
+} atav_holder;
+
+static const nssASN1Template nss_atav_template[] = {
+ { nssASN1_SEQUENCE, 0, NULL, sizeof(atav_holder) },
+ { nssASN1_OBJECT_ID, nsslibc_offsetof(atav_holder, oid), NULL, 0 },
+ { nssASN1_ANY, nsslibc_offsetof(atav_holder, value), NULL, 0 },
+ { 0, 0, NULL, 0 }
+};
+
+/*
+ * There are several common attributes, with well-known type aliases
+ * and value semantics. This table lists the ones we recognize.
+ */
+
+struct nss_attribute_data_str {
+ const NSSOID **oid;
+ nssStringType stringType;
+ PRUint32 minStringLength;
+ PRUint32 maxStringLength; /* zero for no limit */
+};
+
+static const struct nss_attribute_data_str nss_attribute_data[] = {
+ { &NSS_OID_X520_NAME,
+ nssStringType_DirectoryString, 1, 32768 },
+ { &NSS_OID_X520_COMMON_NAME,
+ nssStringType_DirectoryString, 1, 64 },
+ { &NSS_OID_X520_SURNAME,
+ nssStringType_DirectoryString, 1, 40 },
+ { &NSS_OID_X520_GIVEN_NAME,
+ nssStringType_DirectoryString, 1, 16 },
+ { &NSS_OID_X520_INITIALS,
+ nssStringType_DirectoryString, 1, 5 },
+ { &NSS_OID_X520_GENERATION_QUALIFIER,
+ nssStringType_DirectoryString, 1, 3 },
+ { &NSS_OID_X520_DN_QUALIFIER,
+ nssStringType_PrintableString, 1, 0 },
+ { &NSS_OID_X520_COUNTRY_NAME,
+ nssStringType_PrintableString, 2, 2 },
+ { &NSS_OID_X520_LOCALITY_NAME,
+ nssStringType_DirectoryString, 1, 128 },
+ { &NSS_OID_X520_STATE_OR_PROVINCE_NAME,
+ nssStringType_DirectoryString, 1, 128 },
+ { &NSS_OID_X520_ORGANIZATION_NAME,
+ nssStringType_DirectoryString, 1, 64 },
+ { &NSS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
+ nssStringType_DirectoryString, 1,
+ /*
+ * Note, draft #11 defines both "32" and "64" for this maximum,
+ * in two separate places. Until it's settled, "conservative
+ * in what you send." We're always liberal in what we accept.
+ */
+ 32 },
+ { &NSS_OID_X520_TITLE,
+ nssStringType_DirectoryString, 1, 64 },
+ { &NSS_OID_RFC1274_EMAIL,
+ nssStringType_PHGString, 1, 128 }
+};
+
+PRUint32 nss_attribute_data_quantity =
+ (sizeof(nss_attribute_data)/sizeof(nss_attribute_data[0]));
+
+static nssStringType
+nss_attr_underlying_string_form
+(
+ nssStringType type,
+ void *data
+)
+{
+ if( nssStringType_DirectoryString == type ) {
+ PRUint8 tag = *(PRUint8 *)data;
+ switch( tag & nssASN1_TAGNUM_MASK ) {
+ case 20:
+ /*
+ * XXX fgmr-- we have to accept Latin-1 for Teletex; (see
+ * below) but is T61 a suitable value for "Latin-1"?
+ */
+ return nssStringType_TeletexString;
+ case 19:
+ return nssStringType_PrintableString;
+ case 28:
+ return nssStringType_UniversalString;
+ case 30:
+ return nssStringType_BMPString;
+ case 12:
+ return nssStringType_UTF8String;
+ default:
+ return nssStringType_Unknown;
+ }
+ }
+
+ return type;
+}
+
+
+/*
+ * This routine decodes the attribute value, in a type-specific way.
+ *
+ */
+
+static NSSUTF8 *
+nss_attr_to_utf8
+(
+ NSSArena *arenaOpt,
+ const NSSOID *oid,
+ NSSItem *item,
+ nssStringType *stringForm
+)
+{
+ NSSUTF8 *rv = (NSSUTF8 *)NULL;
+ PRUint32 i;
+ const struct nss_attribute_data_str *which =
+ (struct nss_attribute_data_str *)NULL;
+ PRUint32 len = 0;
+
+ for( i = 0; i < nss_attribute_data_quantity; i++ ) {
+ if( *(nss_attribute_data[ i ].oid) == oid ) {
+ which = &nss_attribute_data[i];
+ break;
+ }
+ }
+
+ if( (struct nss_attribute_data_str *)NULL == which ) {
+ /* Unknown OID. Encode it as hex. */
+ PRUint8 *c;
+ PRUint8 *d = (PRUint8 *)item->data;
+ PRUint32 amt = item->size;
+
+ if( item->size >= 0x7FFFFFFF ) {
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ return (NSSUTF8 *)NULL;
+ }
+
+ len = 1 + (item->size * 2) + 1; /* '#' + hex + '\0' */
+ rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len);
+ if( (NSSUTF8 *)NULL == rv ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ c = (PRUint8 *)rv;
+ *c++ = '#'; /* XXX fgmr check this */
+ while( amt > 0 ) {
+ static char hex[16] = "0123456789ABCDEF";
+ *c++ = hex[ ((*d) & 0xf0) >> 4 ];
+ *c++ = hex[ ((*d) & 0x0f) ];
+ }
+
+ /* *c = '\0'; nss_ZAlloc, remember */
+
+ *stringForm = nssStringType_Unknown; /* force exact comparison */
+ } else {
+ rv = nssUTF8_CreateFromBER(arenaOpt, which->stringType,
+ (NSSBER *)item);
+
+ if( (NSSUTF8 *)NULL == rv ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Length(rv, &len) ) {
+ nss_ZFreeIf(rv);
+ return (NSSUTF8 *)NULL;
+ }
+
+ *stringForm = nss_attr_underlying_string_form(which->stringType,
+ item->data);
+ }
+
+ return rv;
+}
+
+/*
+ * nssATAV_CreateFromBER
+ *
+ * This routine creates an NSSATAV by decoding a BER- or DER-encoded
+ * ATAV. If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_IMPLEMENT NSSATAV *
+nssATAV_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ const NSSBER *berATAV
+)
+{
+ atav_holder holder;
+ PRStatus status;
+ NSSATAV *rv;
+
+#ifdef NSSDEBUG
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+
+ /*
+ * NSSBERs can be created by the user,
+ * so no pointer-tracking can be checked.
+ */
+
+ if( (NSSBER *)NULL == berATAV ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSATAV *)NULL;
+ }
+
+ if( (void *)NULL == berATAV->data ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSATAV *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ status = nssASN1_DecodeBER(arenaOpt, &holder,
+ nss_atav_template, berATAV);
+ if( PR_SUCCESS != status ) {
+ return (NSSATAV *)NULL;
+ }
+
+ rv = nss_ZNEW(arenaOpt, NSSATAV);
+ if( (NSSATAV *)NULL == rv ) {
+ nss_ZFreeIf(holder.oid.data);
+ nss_ZFreeIf(holder.value.data);
+ return (NSSATAV *)NULL;
+ }
+
+ rv->oid = nssOID_CreateFromBER(&holder.oid);
+ if( (NSSOID *)NULL == rv->oid ) {
+ nss_ZFreeIf(rv);
+ nss_ZFreeIf(holder.oid.data);
+ nss_ZFreeIf(holder.value.data);
+ return (NSSATAV *)NULL;
+ }
+
+ nss_ZFreeIf(holder.oid.data);
+
+ rv->ber.data = nss_ZAlloc(arenaOpt, berATAV->size);
+ if( (void *)NULL == rv->ber.data ) {
+ nss_ZFreeIf(rv);
+ nss_ZFreeIf(holder.value.data);
+ return (NSSATAV *)NULL;
+ }
+
+ rv->ber.size = berATAV->size;
+ (void)nsslibc_memcpy(rv->ber.data, berATAV->data, berATAV->size);
+
+ rv->value = nss_attr_to_utf8(arenaOpt, rv->oid, &holder.value,
+ &rv->stringForm);
+ if( (NSSUTF8 *)NULL == rv->value ) {
+ nss_ZFreeIf(rv->ber.data);
+ nss_ZFreeIf(rv);
+ nss_ZFreeIf(holder.value.data);
+ return (NSSATAV *)NULL;
+ }
+
+ nss_ZFreeIf(holder.value.data);
+
+#ifdef DEBUG
+ if( PR_SUCCESS != atav_add_pointer(rv) ) {
+ nss_ZFreeIf(rv->ber.data);
+ nss_ZFreeIf(rv->value);
+ nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+#endif /* DEBUG */
+
+ return rv;
+}
+
+static PRBool
+nss_atav_utf8_string_is_hex
+(
+ const NSSUTF8 *s
+)
+{
+ /* All hex digits are ASCII, so this works */
+ PRUint8 *p = (PRUint8 *)s;
+
+ for( ; (PRUint8)0 != *p; p++ ) {
+ if( (('0' <= *p) && (*p <= '9')) ||
+ (('A' <= *p) && (*p <= 'F')) ||
+ (('a' <= *p) && (*p <= 'f')) ) {
+ continue;
+ } else {
+ return PR_FALSE;
+ }
+ }
+
+ return PR_TRUE;
+}
+
+static PRUint8
+nss_atav_fromhex
+(
+ PRUint8 *d
+)
+{
+ PRUint8 rv;
+
+ if( d[0] <= '9' ) {
+ rv = (d[0] - '0') * 16;
+ } else if( d[0] >= 'a' ) {
+ rv = (d[0] - 'a' + 10) * 16;
+ } else {
+ rv = (d[0] - 'A' + 10);
+ }
+
+ if( d[1] <= '9' ) {
+ rv += (d[1] - '0');
+ } else if( d[1] >= 'a' ) {
+ rv += (d[1] - 'a' + 10);
+ } else {
+ rv += (d[1] - 'A' + 10);
+ }
+
+ return rv;
+}
+
+/*
+ * nssATAV_CreateFromUTF8
+ *
+ * This routine creates an NSSATAV by decoding a UTF8 string in the
+ * "equals" format, e.g., "c=US." If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+extern const NSSError NSS_ERROR_INTERNAL_ERROR;
+
+NSS_IMPLEMENT NSSATAV *
+nssATAV_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ const NSSUTF8 *stringATAV
+)
+{
+ char *c;
+ NSSUTF8 *type;
+ NSSUTF8 *value;
+ PRUint32 i;
+ const NSSOID *oid = (NSSOID *)NULL;
+ NSSATAV *rv;
+ NSSItem xitem;
+
+ xitem.data = (void *)NULL;
+
+ for( c = (char *)stringATAV; '\0' != *c; c++ ) {
+ if( '=' == *c ) {
+#ifdef PEDANTIC
+ /*
+ * Theoretically, one could have an '=' in an
+ * attribute string alias. We don't, yet, though.
+ */
+ if( (char *)stringATAV == c ) {
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ return (NSSATAV *)NULL;
+ } else {
+ if( '\\' == c[-1] ) {
+ continue;
+ }
+ }
+#endif /* PEDANTIC */
+ break;
+ }
+ }
+
+ if( '\0' == *c ) {
+ nss_SetError(NSS_ERROR_INVALID_UTF8);
+ return (NSSATAV *)NULL;
+ } else {
+ c++;
+ value = (NSSUTF8 *)c;
+ }
+
+ i = ((NSSUTF8 *)c - stringATAV);
+ type = (NSSUTF8 *)nss_ZAlloc((NSSArena *)NULL, i);
+ if( (NSSUTF8 *)NULL == type ) {
+ return (NSSATAV *)NULL;
+ }
+
+ (void)nsslibc_memcpy(type, stringATAV, i-1);
+
+ c = (char *)stringATAV;
+ if( (('0' <= *c) && (*c <= '9')) || ('#' == *c) ) {
+ oid = nssOID_CreateFromUTF8(type);
+ if( (NSSOID *)NULL == oid ) {
+ nss_ZFreeIf(type);
+ return (NSSATAV *)NULL;
+ }
+ } else {
+ for( i = 0; i < nss_attribute_type_alias_count; i++ ) {
+ const nssAttributeTypeAliasTable *e = &nss_attribute_type_aliases[i];
+ PRBool match = PR_FALSE;
+ if( PR_SUCCESS != nssUTF8_CaseIgnoreMatch(type, e->alias,
+ &match) ) {
+ nss_ZFreeIf(type);
+ return (NSSATAV *)NULL;
+ }
+ if( PR_TRUE == match ) {
+ oid = *(e->oid);
+ break;
+ }
+ }
+
+ if( (NSSOID *)NULL == oid ) {
+ nss_ZFreeIf(type);
+ nss_SetError(NSS_ERROR_UNKNOWN_ATTRIBUTE);
+ return (NSSATAV *)NULL;
+ }
+ }
+
+ nss_ZFreeIf(type);
+ type = (NSSUTF8 *)NULL;
+
+ rv = nss_ZNEW(arenaOpt, NSSATAV);
+ if( (NSSATAV *)NULL == rv ) {
+ return (NSSATAV *)NULL;
+ }
+
+ rv->oid = oid;
+
+ if( '#' == *value ) { /* XXX fgmr.. was it '#'? or backslash? */
+ PRUint32 size;
+ PRUint32 len;
+ PRUint8 *c;
+ PRUint8 *d;
+ /* It's in hex */
+
+ value++;
+ if( PR_TRUE != nss_atav_utf8_string_is_hex(value) ) {
+ (void)nss_ZFreeIf(rv);
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ return (NSSATAV *)NULL;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Size(value, &size) ) {
+ /*
+ * Only returns an error on bad pointer (nope) or string
+ * too long. The defined limits for known attributes are
+ * small enough to fit in PRUint32, and when undefined we
+ * get to apply our own practical limits. Ergo, I say the
+ * string is invalid.
+ */
+ (void)nss_ZFreeIf(rv);
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ return (NSSATAV *)NULL;
+ }
+
+ if( ((size-1) & 1) ) {
+ /* odd length */
+ (void)nss_ZFreeIf(rv);
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ return (NSSATAV *)NULL;
+ }
+
+ len = (size-1)/2;
+
+ rv->value = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1);
+ if( (NSSUTF8 *)NULL == rv->value ) {
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ xitem.size = len;
+ xitem.data = (void *)rv->value;
+
+ for( c = rv->value, d = value; len--; c++, d += 2 ) {
+ *c = nss_atav_fromhex(d);
+ }
+
+ *c = 0;
+ } else {
+ PRUint32 i, len;
+ PRUint8 *s;
+
+ /*
+ * XXX fgmr-- okay, this is a little wasteful, and should
+ * probably be abstracted out a bit. Later.
+ */
+
+ rv->value = nssUTF8_Duplicate(value, arenaOpt);
+ if( (NSSUTF8 *)NULL == rv->value ) {
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Size(rv->value, &len) ) {
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ s = (PRUint8 *)rv->value;
+ for( i = 0; i < len; i++ ) {
+ if( '\\' == s[i] ) {
+ (void)nsslibc_memcpy(&s[i], &s[i+1], len-i-1);
+ }
+ }
+ }
+
+ /* Now just BER-encode the baby and we're through.. */
+ {
+ const struct nss_attribute_data_str *which =
+ (struct nss_attribute_data_str *)NULL;
+ PRUint32 i;
+ NSSArena *a;
+ NSSDER *oidder;
+ NSSItem *vitem;
+ atav_holder ah;
+ NSSDER *status;
+
+ for( i = 0; i < nss_attribute_data_quantity; i++ ) {
+ if( *(nss_attribute_data[ i ].oid) == rv->oid ) {
+ which = &nss_attribute_data[i];
+ break;
+ }
+ }
+
+ a = NSSArena_Create();
+ if( (NSSArena *)NULL == a ) {
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ oidder = nssOID_GetDEREncoding(rv->oid, a);
+ if( (NSSDER *)NULL == oidder ) {
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ if( (struct nss_attribute_data_str *)NULL == which ) {
+ /*
+ * We'll just have to take the user data as an octet stream.
+ */
+ if( (void *)NULL == xitem.data ) {
+ /*
+ * This means that an ATTR entry has been added to oids.txt,
+ * but no corresponding entry has been added to the array
+ * ns_attribute_data[] above.
+ */
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ vitem = nssASN1_EncodeDER(a, (NSSDER *)NULL, &xitem,
+ nssASN1Template_OctetString);
+ if( (NSSItem *)NULL == vitem ) {
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ rv->stringForm = nssStringType_Unknown;
+ } else {
+ PRUint32 length = 0;
+
+ if( PR_SUCCESS != nssUTF8_Length(rv->value, &length) ) {
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ if( ((0 != which->minStringLength) &&
+ (length < which->minStringLength)) ||
+ ((0 != which->maxStringLength) &&
+ (length > which->maxStringLength)) ) {
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ vitem = nssUTF8_GetDEREncoding(a, which->stringType, rv->value);
+ if( (NSSItem *)NULL == vitem ) {
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ if( nssStringType_DirectoryString == which->stringType ) {
+ rv->stringForm = nssStringType_UTF8String;
+ } else {
+ rv->stringForm = which->stringType;
+ }
+ }
+
+ ah.oid = *oidder;
+ ah.value = *vitem;
+
+ status = nssASN1_EncodeDER(arenaOpt, &rv->ber, &ah,
+ nss_atav_template);
+
+ if( (NSSDER *)NULL == status ) {
+ (void)NSSArena_Destroy(a);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ (void)NSSArena_Destroy(a);
+ }
+
+ return rv;
+}
+
+/*
+ * nssATAV_Create
+ *
+ * This routine creates an NSSATAV from the specified NSSOID and the
+ * specified data. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap.If the specified data length is zero,
+ * the data is assumed to be terminated by first zero byte; this allows
+ * UTF8 strings to be easily specified. This routine may return NULL
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ARENA
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_INVALID_POINTER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_IMPLEMENT NSSATAV *
+nssATAV_Create
+(
+ NSSArena *arenaOpt,
+ const NSSOID *oid,
+ const void *data,
+ PRUint32 length
+)
+{
+#ifdef NSSDEBUG
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSATAV *)NULL;
+ }
+
+ if( (const void *)NULL == data ) {
+ nss_SetError(NSS_ERROR_INVALID_POINTER);
+ return (NSSATAV *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ /* XXX fgmr-- oops, forgot this one */
+ return (NSSATAV *)NULL;
+}
+
+/*
+ * nssATAV_Destroy
+ *
+ * This routine will destroy an ATAV object. It should eventually be
+ * called on all ATAVs created without an arena. While it is not
+ * necessary to call it on ATAVs created within an arena, it is not an
+ * error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * set an error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_IMPLEMENT PRStatus
+nssATAV_Destroy
+(
+ NSSATAV *atav
+)
+{
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return PR_FAILURE;
+ }
+#endif /* NSSDEBUG */
+
+ (void)nss_ZFreeIf(atav->ber.data);
+ (void)nss_ZFreeIf(atav->value);
+
+#ifdef DEBUG
+ if( PR_SUCCESS != atav_remove_pointer(atav) ) {
+ return PR_FAILURE;
+ }
+#endif /* DEBUG */
+
+ return PR_SUCCESS;
+}
+
+/*
+ * nssATAV_GetDEREncoding
+ *
+ * This routine will DER-encode an ATAV object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSATAV
+ */
+
+NSS_IMPLEMENT NSSDER *
+nssATAV_GetDEREncoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ NSSDER *rv;
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSDER *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ rv = nss_ZNEW(arenaOpt, NSSDER);
+ if( (NSSDER *)NULL == rv ) {
+ return (NSSDER *)NULL;
+ }
+
+ rv->data = nss_ZAlloc(arenaOpt, atav->ber.size);
+ if( (void *)NULL == rv->data ) {
+ (void)nss_ZFreeIf(rv);
+ return (NSSDER *)NULL;
+ }
+
+ rv->size = atav->ber.size;
+ if( PR_SUCCESS != nsslibc_memcpy(rv->data, atav->ber.data,
+ rv->size) ) {
+ (void)nss_ZFreeIf(rv->data);
+ (void)nss_ZFreeIf(rv);
+ return (NSSDER *)NULL;
+ }
+
+ return rv;
+}
+
+/*
+ * nssATAV_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the ATAV in "equals" notation (e.g., "o=Acme").
+ * If the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the "equals" encoding of the
+ * ATAV
+ */
+
+NSS_IMPLEMENT NSSUTF8 *
+nssATAV_GetUTF8Encoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ NSSUTF8 *rv;
+ PRUint32 i;
+ const NSSUTF8 *alias = (NSSUTF8 *)NULL;
+ NSSUTF8 *oid;
+ NSSUTF8 *value;
+ PRUint32 oidlen;
+ PRUint32 valuelen;
+ PRUint32 totallen;
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSUTF8 *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ for( i = 0; i < nss_attribute_type_alias_count; i++ ) {
+ if( *(nss_attribute_type_aliases[i].oid) == atav->oid ) {
+ alias = nss_attribute_type_aliases[i].alias;
+ break;
+ }
+ }
+
+ if( (NSSUTF8 *)NULL == alias ) {
+ oid = nssOID_GetUTF8Encoding(atav->oid, (NSSArena *)NULL);
+ if( (NSSUTF8 *)NULL == oid ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Size(oid, &oidlen) ) {
+ (void)nss_ZFreeIf(oid);
+ return (NSSUTF8 *)NULL;
+ }
+ } else {
+ if( PR_SUCCESS != nssUTF8_Size(alias, &oidlen) ) {
+ return (NSSUTF8 *)NULL;
+ }
+ oid = (NSSUTF8 *)NULL;
+ }
+
+ value = nssATAV_GetValue(atav, (NSSArena *)NULL);
+ if( (NSSUTF8 *)NULL == value ) {
+ (void)nss_ZFreeIf(oid);
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Size(value, &valuelen) ) {
+ (void)nss_ZFreeIf(value);
+ (void)nss_ZFreeIf(oid);
+ return (NSSUTF8 *)NULL;
+ }
+
+ totallen = oidlen + valuelen - 1 + 1;
+ rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, totallen);
+ if( (NSSUTF8 *)NULL == rv ) {
+ (void)nss_ZFreeIf(value);
+ (void)nss_ZFreeIf(oid);
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( (NSSUTF8 *)NULL == alias ) {
+ if( (void *)NULL == nsslibc_memcpy(rv, oid, oidlen-1) ) {
+ (void)nss_ZFreeIf(rv);
+ (void)nss_ZFreeIf(value);
+ (void)nss_ZFreeIf(oid);
+ return (NSSUTF8 *)NULL;
+ }
+ } else {
+ if( (void *)NULL == nsslibc_memcpy(rv, alias, oidlen-1) ) {
+ (void)nss_ZFreeIf(rv);
+ (void)nss_ZFreeIf(value);
+ return (NSSUTF8 *)NULL;
+ }
+ }
+
+ rv[ oidlen-1 ] = '=';
+
+ if( (void *)NULL == nsslibc_memcpy(&rv[oidlen], value, valuelen) ) {
+ (void)nss_ZFreeIf(rv);
+ (void)nss_ZFreeIf(value);
+ (void)nss_ZFreeIf(oid);
+ return (NSSUTF8 *)NULL;
+ }
+
+ return rv;
+}
+
+/*
+ * nssATAV_GetType
+ *
+ * This routine returns the NSSOID corresponding to the attribute type
+ * in the specified ATAV. This routine may return NSS_OID_UNKNOWN
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * A valid NSSOID pointer upon success
+ */
+
+NSS_IMPLEMENT const NSSOID *
+nssATAV_GetType
+(
+ NSSATAV *atav
+)
+{
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSOID *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ return atav->oid;
+}
+
+/*
+ * nssATAV_GetValue
+ *
+ * This routine returns a NSSUTF8 string containing the attribute value
+ * in the specified ATAV. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error upon the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSItem containing the attribute value.
+ */
+
+NSS_IMPLEMENT NSSUTF8 *
+nssATAV_GetValue
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSUTF8 *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ return nssUTF8_Duplicate(atav->value, arenaOpt);
+}
+
+/*
+ * nssATAV_Compare
+ *
+ * This routine compares two ATAVs for equality. For two ATAVs to be
+ * equal, the attribute types must be the same, and the attribute
+ * values must have equal length and contents. The result of the
+ * comparison will be stored at the location pointed to by the "equalp"
+ * variable, which must point to a valid PRBool. This routine may
+ * return PR_FAILURE upon error, in which case it will have set an
+ * error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_IMPLEMENT PRStatus
+nssATAV_Compare
+(
+ NSSATAV *atav1,
+ NSSATAV *atav2,
+ PRBool *equalp
+)
+{
+ nssStringType comparison;
+ PRUint32 len1;
+ PRUint32 len2;
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav1) ) {
+ return PR_FAILURE;
+ }
+
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav2) ) {
+ return PR_FAILURE;
+ }
+
+ if( (PRBool *)NULL == equalp ) {
+ nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
+ return PR_FAILURE;
+ }
+#endif /* DEBUG */
+
+ if( atav1->oid != atav2->oid ) {
+ *equalp = PR_FALSE;
+ return PR_SUCCESS;
+ }
+
+ if( atav1->stringForm != atav2->stringForm ) {
+ if( (nssStringType_PrintableString == atav1->stringForm) ||
+ (nssStringType_PrintableString == atav2->stringForm) ) {
+ comparison = nssStringType_PrintableString;
+ } else if( (nssStringType_PHGString == atav1->stringForm) ||
+ (nssStringType_PHGString == atav2->stringForm) ) {
+ comparison = nssStringType_PHGString;
+ } else {
+ comparison = atav1->stringForm;
+ }
+ } else {
+ comparison = atav1->stringForm;
+ }
+
+ switch( comparison ) {
+ case nssStringType_DirectoryString:
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ return PR_FAILURE;
+ case nssStringType_TeletexString:
+ break;
+ case nssStringType_PrintableString:
+ return nssUTF8_PrintableMatch(atav1->value, atav2->value,
+ equalp);
+ /* Case-insensitive, with whitespace reduction */
+ break;
+ case nssStringType_UniversalString:
+ break;
+ case nssStringType_BMPString:
+ break;
+ case nssStringType_UTF8String:
+ break;
+ case nssStringType_PHGString:
+ /* Case-insensitive (XXX fgmr, actually see draft-11 pg. 21) */
+ return nssUTF8_CaseIgnoreMatch(atav1->value, atav2->value,
+ equalp);
+ case nssStringType_Unknown:
+ break;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Size(atav1->value, &len1) ) {
+ return PR_FAILURE;
+ }
+
+ if( PR_SUCCESS != nssUTF8_Size(atav2->value, &len2) ) {
+ return PR_FAILURE;
+ }
+
+ if( len1 != len2 ) {
+ *equalp = PR_FALSE;
+ return PR_SUCCESS;
+ }
+
+ return nsslibc_compare(atav1->value, atav2->value, len1, equalp);
+}
+
+
+/*
+ * nssATAV_Duplicate
+ *
+ * This routine duplicates the specified ATAV. If the optional arena
+ * argument is non-null, the memory required will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * placed an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL on error
+ * A pointer to a new ATAV
+ */
+
+NSS_IMPLEMENT NSSATAV *
+nssATAV_Duplicate
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+)
+{
+ NSSATAV *rv;
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssATAV_verifyPointer(atav) ) {
+ return (NSSATAV *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSATAV *)NULL;
+ }
+ }
+#endif /* NSSDEBUG */
+
+ rv = nss_ZNEW(arenaOpt, NSSATAV);
+ if( (NSSATAV *)NULL == rv ) {
+ return (NSSATAV *)NULL;
+ }
+
+ rv->oid = atav->oid;
+ rv->stringForm = atav->stringForm;
+ rv->value = nssUTF8_Duplicate(atav->value, arenaOpt);
+ if( (NSSUTF8 *)NULL == rv->value ) {
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ rv->ber.data = nss_ZAlloc(arenaOpt, atav->ber.size);
+ if( (void *)NULL == rv->ber.data ) {
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ rv->ber.size = atav->ber.size;
+ if( PR_SUCCESS != nsslibc_memcpy(rv->ber.data, atav->ber.data,
+ atav->ber.size) ) {
+ (void)nss_ZFreeIf(rv->ber.data);
+ (void)nss_ZFreeIf(rv->value);
+ (void)nss_ZFreeIf(rv);
+ return (NSSATAV *)NULL;
+ }
+
+ return rv;
+}
diff --git a/security/nss/lib/pki1/config.mk b/security/nss/lib/pki1/config.mk
new file mode 100644
index 000000000..80b3135f4
--- /dev/null
+++ b/security/nss/lib/pki1/config.mk
@@ -0,0 +1,37 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+CONFIG_CVS_ID = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"
+
+ifdef BUILD_IDG
+DEFINES += -DNSSDEBUG
+endif
diff --git a/security/nss/lib/pki1/genname.c b/security/nss/lib/pki1/genname.c
new file mode 100644
index 000000000..e7b12806a
--- /dev/null
+++ b/security/nss/lib/pki1/genname.c
@@ -0,0 +1,94 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * genname.c
+ *
+ * This file contains the implementation of the PKIX part-1 object
+ * GeneralName.
+ */
+
+#ifndef NSSBASE_H
+#include "nssbase.h"
+#endif /* NSSBASE_H */
+
+#ifndef ASN1_H
+#include "asn1.h"
+#endif /* ASN1_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+/*
+ * GeneralName
+ *
+ * From draft-ietf-pkix-ipki-part1-10:
+ *
+ * GeneralName ::= CHOICE {
+ * otherName [0] INSTANCE OF OTHER-NAME,
+ * rfc822Name [1] IA5String,
+ * dNSName [2] IA5String,
+ * x400Address [3] ORAddress,
+ * directoryName [4] Name,
+ * ediPartyName [5] EDIPartyName,
+ * uniformResourceIdentifier [6] IA5String,
+ * iPAddress [7] OCTET STRING,
+ * registeredID [8] OBJECT IDENTIFIER }
+ *
+ * OTHER-NAME ::= TYPE-IDENTIFIER
+ *
+ * EDIPartyName ::= SEQUENCE {
+ * nameAssigner [0] DirectoryString {ub-name} OPTIONAL,
+ * partyName [1] DirectoryString {ub-name} }
+ *
+ */
+
+struct nssGeneralNameStr {
+ NSSGeneralNameChoice choice;
+ union {
+ /* OTHER-NAME otherName */
+ NSSUTF8 *rfc822Name;
+ NSSUTF8 *dNSName;
+ /* ORAddress x400Address */
+ NSSName *directoryName;
+ /* EDIPartyName ediPartyName */
+ NSSUTF8 *uniformResourceIdentifier;
+ NSSItem *iPAddress;
+ NSSOID *registeredID;
+ } u;
+};
diff --git a/security/nss/lib/pki1/gnseq.c b/security/nss/lib/pki1/gnseq.c
new file mode 100644
index 000000000..e56ec93b9
--- /dev/null
+++ b/security/nss/lib/pki1/gnseq.c
@@ -0,0 +1,71 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * gnseq.c
+ *
+ * This file contains the implementation of the PKIX part-1 object
+ * GeneralNames (note the ending 's').
+ */
+
+#ifndef NSSBASE_H
+#include "nssbase.h"
+#endif /* NSSBASE_H */
+
+#ifndef ASN1_H
+#include "asn1.h"
+#endif /* ASN1_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+/*
+ * GeneralNames (or, as we call it, GeneralNameSeq)
+ *
+ * From draft-ietf-pkix-ipki-part1-10:
+ *
+ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ *
+ * A GeneralNames is simply an (ordered) sequence of General Names.
+ * The seqSize variable is "helper" kept for simplicity.
+ */
+
+struct nssGeneralNameSeqStr {
+ PRUint32 seqSize;
+ NSSGeneralName **generalNames;
+};
diff --git a/security/nss/lib/pki1/manifest.mn b/security/nss/lib/pki1/manifest.mn
new file mode 100644
index 000000000..8e032d974
--- /dev/null
+++ b/security/nss/lib/pki1/manifest.mn
@@ -0,0 +1,63 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+MANIFEST_CVS_ID = "@(#) $RCSfile$ $Revision$ $Date$ $Name$"
+
+CORE_DEPTH = ../../..
+
+PRIVATE_EXPORTS = \
+ pki1.h \
+ pki1t.h \
+ $(NULL)
+
+EXPORTS = \
+ oiddata.h \
+ nsspki1.h \
+ nsspki1t.h \
+ $(NULL)
+
+MODULE = security
+
+CSRCS = \
+ atav.c \
+ genname.c \
+ gnseq.c \
+ name.c \
+ oid.c \
+ oiddata.c \
+ rdn.c \
+ rdnseq.c \
+ $(NULL)
+
+REQUIRES = security nspr
+
+LIBRARY_NAME = pki1
diff --git a/security/nss/lib/pki1/name.c b/security/nss/lib/pki1/name.c
new file mode 100644
index 000000000..558e1142f
--- /dev/null
+++ b/security/nss/lib/pki1/name.c
@@ -0,0 +1,77 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * name.c
+ *
+ * This file contains the implementation of the PKIX part-1 object
+ * Name.
+ */
+
+#ifndef NSSBASE_H
+#include "nssbase.h"
+#endif /* NSSBASE_H */
+
+#ifndef ASN1_H
+#include "asn1.h"
+#endif /* ASN1_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+/*
+ * Name
+ *
+ * From draft-ietf-pkix-ipki-part1-10:
+ *
+ * -- naming data types --
+ *
+ * Name ::= CHOICE { -- only one possibility for now --
+ * rdnSequence RDNSequence }
+ *
+ * A name is basically a union of the possible names. At the moment,
+ * there is only one type of name: an RDNSequence.
+ */
+
+struct nssNameStr {
+ PRUint32 tagPlaceHolder;
+ union {
+ NSSRDNSeq *rdnSequence;
+ } n;
+};
+
diff --git a/security/nss/lib/pki1/nsspki1.h b/security/nss/lib/pki1/nsspki1.h
new file mode 100644
index 000000000..3498ab520
--- /dev/null
+++ b/security/nss/lib/pki1/nsspki1.h
@@ -0,0 +1,2869 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef NSSPKI1_H
+#define NSSPKI1_H
+
+#ifdef DEBUG
+static const char NSSPKI1_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * nsspki1.h
+ *
+ * This file contains the prototypes of the public NSS routines
+ * dealing with the PKIX part-1 definitions.
+ */
+
+#ifndef NSSBASET_H
+#include "nssbaset.h"
+#endif /* NSSBASET_H */
+
+#ifndef NSSPKI1T_H
+#include "nsspki1t.h"
+#endif /* NSSPKI1T_H */
+
+#ifndef OIDDATA_H
+#include "oiddata.h"
+#endif /* OIDDATA_H */
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * NSSOID
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSOID_CreateFromBER -- constructor
+ * NSSOID_CreateFromUTF8 -- constructor
+ * (there is no explicit destructor)
+ *
+ * NSSOID_GetDEREncoding
+ * NSSOID_GetUTF8Encoding
+ */
+
+extern const NSSOID *NSS_OID_UNKNOWN;
+
+/*
+ * NSSOID_CreateFromBER
+ *
+ * This routine creates an NSSOID by decoding a BER- or DER-encoded
+ * OID. It may return NSS_OID_UNKNOWN upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+NSSOID_CreateFromBER
+(
+ NSSBER *berOid
+);
+
+extern const NSSError NSS_ERROR_INVALID_BER;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSOID_CreateFromUTF8
+ *
+ * This routine creates an NSSOID by decoding a UTF8 string
+ * representation of an OID in dotted-number format. The string may
+ * optionally begin with an octothorpe. It may return NSS_OID_UNKNOWN
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+NSSOID_CreateFromUTF8
+(
+ NSSUTF8 *stringOid
+);
+
+extern const NSSError NSS_ERROR_INVALID_STRING;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSOID_GetDEREncoding
+ *
+ * This routine returns the DER encoding of the specified NSSOID.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return return null upon error, in
+ * which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSOID
+ */
+
+NSS_EXTERN NSSDER *
+NSSOID_GetDEREncoding
+(
+ const NSSOID *oid,
+ NSSDER *rvOpt,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSOID_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing the dotted-number
+ * encoding of the specified NSSOID. If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return null upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the dotted-digit encoding of
+ * this NSSOID
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSOID_GetUTF8Encoding
+(
+ const NSSOID *oid,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSATAV_CreateFromBER -- constructor
+ * NSSATAV_CreateFromUTF8 -- constructor
+ * NSSATAV_Create -- constructor
+ *
+ * NSSATAV_Destroy
+ * NSSATAV_GetDEREncoding
+ * NSSATAV_GetUTF8Encoding
+ * NSSATAV_GetType
+ * NSSATAV_GetValue
+ * NSSATAV_Compare
+ * NSSATAV_Duplicate
+ */
+
+/*
+ * NSSATAV_CreateFromBER
+ *
+ * This routine creates an NSSATAV by decoding a BER- or DER-encoded
+ * ATAV. If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_EXTERN NSSATAV *
+NSSATAV_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *derATAV
+);
+
+extern const NSSError NSS_ERROR_INVALID_BER;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV_CreateFromUTF8
+ *
+ * This routine creates an NSSATAV by decoding a UTF8 string in the
+ * "equals" format, e.g., "c=US." If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_EXTERN NSSATAV *
+NSSATAV_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringATAV
+);
+
+extern const NSSError NSS_ERROR_UNKNOWN_ATTRIBUTE;
+extern const NSSError NSS_ERROR_INVALID_STRING;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV_Create
+ *
+ * This routine creates an NSSATAV from the specified NSSOID and the
+ * specified data. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap.If the specified data length is zero,
+ * the data is assumed to be terminated by first zero byte; this allows
+ * UTF8 strings to be easily specified. This routine may return NULL
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ARENA
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_INVALID_POINTER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_EXTERN NSSATAV *
+NSSATAV_Create
+(
+ NSSArena *arenaOpt,
+ const NSSOID *oid,
+ const void *data,
+ PRUint32 length
+);
+
+extern const NSSError NSS_ERROR_INVALID_ARENA;
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+extern const NSSError NSS_ERROR_INVALID_POINTER;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV_Destroy
+ *
+ * This routine will destroy an ATAV object. It should eventually be
+ * called on all ATAVs created without an arena. While it is not
+ * necessary to call it on ATAVs created within an arena, it is not an
+ * error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+NSSATAV_Destroy
+(
+ NSSATAV *atav
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+
+/*
+ * NSSATAV_GetDEREncoding
+ *
+ * This routine will DER-encode an ATAV object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSATAV
+ */
+
+NSS_EXTERN NSSDER *
+NSSATAV_GetDEREncoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the ATAV in "equals" notation (e.g., "o=Acme").
+ * If the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the "equals" encoding of the
+ * ATAV
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSATAV_GetUTF8Encoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV_GetType
+ *
+ * This routine returns the NSSOID corresponding to the attribute type
+ * in the specified ATAV. This routine may return NSS_OID_UNKNOWN
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An element of enum NSSOIDenum upon success
+ */
+
+NSS_EXTERN const NSSOID *
+NSSATAV_GetType
+(
+ NSSATAV *atav
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+
+/*
+ * NSSATAV_GetValue
+ *
+ * This routine returns an NSSItem containing the attribute value
+ * in the specified ATAV. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSItem containing the attribute value.
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSATAV_GetValue
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSATAV_Compare
+ *
+ * This routine compares two ATAVs for equality. For two ATAVs to be
+ * equal, the attribute types must be the same, and the attribute
+ * values must have equal length and contents. The result of the
+ * comparison will be stored at the location pointed to by the "equalp"
+ * variable, which must point to a valid PRBool. This routine may
+ * return PR_FAILURE upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+NSSATAV_Compare
+(
+ NSSATAV *atav1,
+ NSSATAV *atav2,
+ PRBool *equalp
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+extern const NSSError NSS_ERROR_INVALID_ARGUMENT;
+
+/*
+ * NSSATAV_Duplicate
+ *
+ * This routine duplicates the specified ATAV. If the optional arena
+ * argument is non-null, the memory required will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL on error
+ * A pointer to a new ATAV
+ */
+
+NSS_EXTERN NSSATAV *
+NSSATAV_Duplicate
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_ATAV;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * NSSRDN
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSRDN_CreateFromBER -- constructor
+ * NSSRDN_CreateFromUTF8 -- constructor
+ * NSSRDN_Create -- constructor
+ * NSSRDN_CreateSimple -- constructor
+ *
+ * NSSRDN_Destroy
+ * NSSRDN_GetDEREncoding
+ * NSSRDN_GetUTF8Encoding
+ * NSSRDN_AddATAV
+ * NSSRDN_GetATAVCount
+ * NSSRDN_GetATAV
+ * NSSRDN_GetSimpleATAV
+ * NSSRDN_Compare
+ * NSSRDN_Duplicate
+ */
+
+/*
+ * NSSRDN_CreateFromBER
+ *
+ * This routine creates an NSSRDN by decoding a BER- or DER-encoded
+ * RDN. If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+NSSRDN_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berRDN
+);
+
+/*
+ * NSSRDN_CreateFromUTF8
+ *
+ * This routine creates an NSSRDN by decoding an UTF8 string
+ * consisting of either a single ATAV in the "equals" format, e.g.,
+ * "uid=smith," or one or more such ATAVs in parentheses, e.g.,
+ * "(sn=Smith,ou=Sales)." If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+NSSRDN_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringRDN
+);
+
+/*
+ * NSSRDN_Create
+ *
+ * This routine creates an NSSRDN from one or more NSSATAVs. The
+ * final argument to this routine must be NULL. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+NSSRDN_Create
+(
+ NSSArena *arenaOpt,
+ NSSATAV *atav1,
+ ...
+);
+
+/*
+ * NSSRDN_CreateSimple
+ *
+ * This routine creates a simple NSSRDN from a single NSSATAV. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+NSSRDN_CreateSimple
+(
+ NSSArena *arenaOpt,
+ NSSATAV *atav
+);
+
+/*
+ * NSSRDN_Destroy
+ *
+ * This routine will destroy an RDN object. It should eventually be
+ * called on all RDNs created without an arena. While it is not
+ * necessary to call it on RDNs created within an arena, it is not an
+ * error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ *
+ * Return value:
+ * PR_FAILURE upon failure
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+NSSRDN_Destroy
+(
+ NSSRDN *rdn
+);
+
+/*
+ * NSSRDN_GetDEREncoding
+ *
+ * This routine will DER-encode an RDN object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSRDN
+ */
+
+NSS_EXTERN NSSDER *
+NSSRDN_GetDEREncoding
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDN_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the RDN. A simple (one-ATAV) RDN will be simply
+ * the string representation of that ATAV; a non-simple RDN will be in
+ * parenthesised form. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * null upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSRDN_GetUTF8Encoding
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDN_AddATAV
+ *
+ * This routine adds an ATAV to the set of ATAVs in the specified RDN.
+ * Remember that RDNs consist of an unordered set of ATAVs. If the
+ * RDN was created with a non-null arena argument, that same arena
+ * will be used for any additional required memory. If the RDN was
+ * created with a NULL arena argument, any additional memory will
+ * be obtained from the heap. This routine returns a PRStatus value;
+ * it will return PR_SUCCESS upon success, and upon failure it will
+ * create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS upon success
+ * PR_FAILURE upon failure
+ */
+
+NSS_EXTERN PRStatus
+NSSRDN_AddATAV
+(
+ NSSRDN *rdn,
+ NSSATAV *atav
+);
+
+/*
+ * NSSRDN_GetATAVCount
+ *
+ * This routine returns the cardinality of the set of ATAVs within
+ * the specified RDN. This routine may return 0 upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ *
+ * Return value:
+ * 0 upon error
+ * A positive number upon success
+ */
+
+NSS_EXTERN PRUint32
+NSSRDN_GetATAVCount
+(
+ NSSRDN *rdn
+);
+
+/*
+ * NSSRDN_GetATAV
+ *
+ * This routine returns a pointer to an ATAV that is a member of
+ * the set of ATAVs within the specified RDN. While the set of
+ * ATAVs within an RDN is unordered, this routine will return
+ * distinct values for distinct values of 'i' as long as the RDN
+ * is not changed in any way. The RDN may be changed by calling
+ * NSSRDN_AddATAV. The value of the variable 'i' is on the range
+ * [0,c) where c is the cardinality returned from NSSRDN_GetATAVCount.
+ * The caller owns the ATAV the pointer to which is returned. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_VALUE_OUT_OF_RANGE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSATAV
+ */
+
+NSS_EXTERN NSSATAV *
+NSSRDN_GetATAV
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt,
+ PRUint32 i
+);
+
+/*
+ * NSSRDN_GetSimpleATAV
+ *
+ * Most RDNs are actually very simple, with a single ATAV. This
+ * routine will return the single ATAV from such an RDN. The caller
+ * owns the ATAV the pointer to which is returned. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, including the case where
+ * the set of ATAVs in the RDN is nonsingular. Upon error, this
+ * routine will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_RDN_NOT_SIMPLE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSATAV
+ */
+
+NSS_EXTERN NSSATAV *
+NSSRDN_GetSimpleATAV
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDN_Compare
+ *
+ * This routine compares two RDNs for equality. For two RDNs to be
+ * equal, they must have the same number of ATAVs, and every ATAV in
+ * one must be equal to an ATAV in the other. (Note that the sets
+ * of ATAVs are unordered.) The result of the comparison will be
+ * stored at the location pointed to by the "equalp" variable, which
+ * must point to a valid PRBool. This routine may return PR_FAILURE
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+NSSRDN_Compare
+(
+ NSSRDN *rdn1,
+ NSSRDN *rdn2,
+ PRBool *equalp
+);
+
+/*
+ * NSSRDN_Duplicate
+ *
+ * This routine duplicates the specified RDN. If the optional arena
+ * argument is non-null, the memory required will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL on error
+ * A pointer to a new RDN
+ */
+
+NSS_EXTERN NSSRDN *
+NSSRDN_Duplicate
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDNSeq
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSRDNSeq_CreateFromBER -- constructor
+ * NSSRDNSeq_CreateFromUTF8 -- constructor
+ * NSSRDNSeq_Create -- constructor
+ *
+ * NSSRDNSeq_Destroy
+ * NSSRDNSeq_GetDEREncoding
+ * NSSRDNSeq_GetUTF8Encoding
+ * NSSRDNSeq_AppendRDN
+ * NSSRDNSeq_GetRDNCount
+ * NSSRDNSeq_GetRDN
+ * NSSRDNSeq_Compare
+ * NSSRDNSeq_Duplicate
+ */
+
+/*
+ * NSSRDNSeq_CreateFromBER
+ *
+ * This routine creates an NSSRDNSeq by decoding a BER- or DER-encoded
+ * sequence of RDNs. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDNSeq upon success
+ */
+
+NSS_EXTERN NSSRDNSeq *
+NSSRDNSeq_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berRDNSeq
+);
+
+/*
+ * NSSRDNSeq_CreateFromUTF8
+ *
+ * This routine creates an NSSRDNSeq by decoding a UTF8 string
+ * consisting of a comma-separated sequence of RDNs, such as
+ * "(sn=Smith,ou=Sales),o=Acme,c=US." If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDNSeq upon success
+ */
+
+NSS_EXTERN NSSRDNSeq *
+NSSRDNSeq_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringRDNSeq
+);
+
+/*
+ * NSSRDNSeq_Create
+ *
+ * This routine creates an NSSRDNSeq from one or more NSSRDNs. The
+ * final argument to this routine must be NULL. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_RDN
+ *
+ * Return value:
+ * NULL upon error
+ * A pointero to an NSSRDNSeq upon success
+ */
+
+NSS_EXTERN NSSRDNSeq *
+NSSRDNSeq_Create
+(
+ NSSArena *arenaOpt,
+ NSSRDN *rdn1,
+ ...
+);
+
+/*
+ * NSSRDNSeq_Destroy
+ *
+ * This routine will destroy an RDNSeq object. It should eventually
+ * be called on all RDNSeqs created without an arena. While it is not
+ * necessary to call it on RDNSeqs created within an arena, it is not
+ * an error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+NSSRDNSeq_Destroy
+(
+ NSSRDNSeq *rdnseq
+);
+
+/*
+ * NSSRDNSeq_GetDEREncoding
+ *
+ * This routine will DER-encode an RDNSeq object. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return null upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSRDNSeq
+ */
+
+NSS_EXTERN NSSDER *
+NSSRDNSeq_GetDEREncoding
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDNSeq_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the RDNSeq as a comma-separated sequence of RDNs.
+ * If the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to the UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSRDNSeq_GetUTF8Encoding
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDNSeq_AppendRDN
+ *
+ * This routine appends an RDN to the end of the existing RDN
+ * sequence. If the RDNSeq was created with a non-null arena
+ * argument, that same arena will be used for any additional required
+ * memory. If the RDNSeq was created with a NULL arena argument, any
+ * additional memory will be obtained from the heap. This routine
+ * returns a PRStatus value; it will return PR_SUCCESS upon success,
+ * and upon failure it will create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS upon success
+ * PR_FAILURE upon failure
+ */
+
+NSS_EXTERN PRStatus
+NSSRDNSeq_AppendRDN
+(
+ NSSRDNSeq *rdnseq,
+ NSSRDN *rdn
+);
+
+/*
+ * NSSRDNSeq_GetRDNCount
+ *
+ * This routine returns the cardinality of the sequence of RDNs within
+ * the specified RDNSeq. This routine may return 0 upon error, in
+ * which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ *
+ * Return value:
+ * 0 upon error
+ * A positive number upon success
+ */
+
+NSS_EXTERN PRUint32
+NSSRDNSeq_GetRDNCount
+(
+ NSSRDNSeq *rdnseq
+);
+
+/*
+ * NSSRDNSeq_GetRDN
+ *
+ * This routine returns a pointer to the i'th RDN in the sequence of
+ * RDNs that make up the specified RDNSeq. The sequence begins with
+ * the top-level (e.g., "c=US") RDN. The value of the variable 'i'
+ * is on the range [0,c) where c is the cardinality returned from
+ * NSSRDNSeq_GetRDNCount. The caller owns the RDN the pointer to which
+ * is returned. If the optional arena argument is non-null, the memory
+ * used will be obtained from that areana; otherwise, the memory will
+ * be obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack. Note that the
+ * usual string representation of RDN Sequences is from last to first.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_VALUE_OUT_OF_RANGE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRDN
+ */
+
+NSS_EXTERN NSSRDN *
+NSSRDNSeq_GetRDN
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt,
+ PRUint32 i
+);
+
+/*
+ * NSSRDNSeq_Compare
+ *
+ * This routine compares two RDNSeqs for equality. For two RDNSeqs to
+ * be equal, they must have the same number of RDNs, and each RDN in
+ * one sequence must be equal to the corresponding RDN in the other
+ * sequence. The result of the comparison will be stored at the
+ * location pointed to by the "equalp" variable, which must point to a
+ * valid PRBool. This routine may return PR_FAILURE upon error, in
+ * which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+NSSRDNSeq_Compare
+(
+ NSSRDNSeq *rdnseq1,
+ NSSRDNSeq *rdnseq2,
+ PRBool *equalp
+);
+
+/*
+ * NSSRDNSeq_Duplicate
+ *
+ * This routine duplicates the specified RDNSeq. If the optional arena
+ * argument is non-null, the memory required will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new RDNSeq
+ */
+
+NSS_EXTERN NSSRDNSeq *
+NSSRDNSeq_Duplicate
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSName_CreateFromBER -- constructor
+ * NSSName_CreateFromUTF8 -- constructor
+ * NSSName_Create -- constructor
+ *
+ * NSSName_Destroy
+ * NSSName_GetDEREncoding
+ * NSSName_GetUTF8Encoding
+ * NSSName_GetChoice
+ * NSSName_GetRDNSequence
+ * NSSName_GetSpecifiedChoice
+ * NSSName_Compare
+ * NSSName_Duplicate
+ *
+ * NSSName_GetUID
+ * NSSName_GetEmail
+ * NSSName_GetCommonName
+ * NSSName_GetOrganization
+ * NSSName_GetOrganizationalUnits
+ * NSSName_GetStateOrProvince
+ * NSSName_GetLocality
+ * NSSName_GetCountry
+ * NSSName_GetAttribute
+ */
+
+/*
+ * NSSName_CreateFromBER
+ *
+ * This routine creates an NSSName by decoding a BER- or DER-encoded
+ * (directory) Name. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise,
+ * the memory will be obtained from the heap. This routine may
+ * return NULL upon error, in which case it will have created an error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSName upon success
+ */
+
+NSS_EXTERN NSSName *
+NSSName_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berName
+);
+
+/*
+ * NSSName_CreateFromUTF8
+ *
+ * This routine creates an NSSName by decoding a UTF8 string
+ * consisting of the string representation of one of the choices of
+ * (directory) names. Currently the only choice is an RDNSeq. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. The routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSName upon success
+ */
+
+NSS_EXTERN NSSName *
+NSSName_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringName
+);
+
+/*
+ * NSSName_Create
+ *
+ * This routine creates an NSSName with the specified choice of
+ * underlying name types. The value of the choice variable must be
+ * one of the values of the NSSNameChoice enumeration, and the type
+ * of the arg variable must be as specified in the following table:
+ *
+ * Choice Type
+ * ======================== ===========
+ * NSSNameChoiceRdnSequence NSSRDNSeq *
+ *
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_CHOICE
+ * NSS_ERROR_INVALID_ARGUMENT
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSName upon success
+ */
+
+NSS_EXTERN NSSName *
+NSSName_Create
+(
+ NSSArena *arenaOpt,
+ NSSNameChoice choice,
+ void *arg
+);
+
+/*
+ * NSSName_Destroy
+ *
+ * This routine will destroy a Name object. It should eventually be
+ * called on all Names created without an arena. While it is not
+ * necessary to call it on Names created within an arena, it is not
+ * an error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+NSSName_Destroy
+(
+ NSSName *name
+);
+
+/*
+ * NSSName_GetDEREncoding
+ *
+ * This routine will DER-encode a name object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSName
+ */
+
+NSS_EXTERN NSSDER *
+NSSName_GetDEREncoding
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the Name in the format specified by the
+ * underlying name choice. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to the UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSName_GetUTF8Encoding
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetChoice
+ *
+ * This routine returns the type of the choice underlying the specified
+ * name. The return value will be a member of the NSSNameChoice
+ * enumeration. This routine may return NSSNameChoiceInvalid upon
+ * error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ *
+ * Return value:
+ * NSSNameChoiceInvalid upon error
+ * An other member of the NSSNameChoice enumeration upon success
+ */
+
+NSS_EXTERN NSSNameChoice
+NSSName_GetChoice
+(
+ NSSName *name
+);
+
+/*
+ * NSSName_GetRDNSequence
+ *
+ * If the choice underlying the specified NSSName is that of an
+ * RDNSequence, this routine will return a pointer to that RDN
+ * sequence. Otherwise, this routine will place an error on the
+ * error stack, and return NULL. If the optional arena argument is
+ * non-null, the memory required will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. The
+ * caller owns the returned pointer. This routine may return NULL
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRDNSeq
+ */
+
+NSS_EXTERN NSSRDNSeq *
+NSSName_GetRDNSequence
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetSpecifiedChoice
+ *
+ * If the choice underlying the specified NSSName matches the specified
+ * choice, a caller-owned pointer to that underlying object will be
+ * returned. Otherwise, an error will be placed on the error stack and
+ * NULL will be returned. If the optional arena argument is non-null,
+ * the memory required will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer, which must be typecast
+ */
+
+NSS_EXTERN void *
+NSSName_GetSpecifiedChoice
+(
+ NSSName *name,
+ NSSNameChoice choice,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_Compare
+ *
+ * This routine compares two Names for equality. For two Names to be
+ * equal, they must have the same choice of underlying types, and the
+ * underlying values must be equal. The result of the comparison will
+ * be stored at the location pointed to by the "equalp" variable, which
+ * must point to a valid PRBool. This routine may return PR_FAILURE
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+NSSName_Compare
+(
+ NSSName *name1,
+ NSSName *name2,
+ PRBool *equalp
+);
+
+/*
+ * NSSName_Duplicate
+ *
+ * This routine duplicates the specified nssname. If the optional
+ * arena argument is non-null, the memory required will be obtained
+ * from that arena; otherwise, the memory will be obtained from the
+ * heap. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new NSSName
+ */
+
+NSS_EXTERN NSSName *
+NSSName_Duplicate
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetUID
+ *
+ * This routine will attempt to derive a user identifier from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished
+ * Names containing a UID attribute, the UID will be the value of
+ * that attribute. Note that no UID attribute is defined in either
+ * PKIX or PKCS#9; rather, this seems to derive from RFC 1274, which
+ * defines the type as a caseIgnoreString. We'll return a Directory
+ * String. If the optional arena argument is non-null, the memory
+ * used will be obtained from that arena; otherwise, the memory will
+ * be obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_UID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String.
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSName_GetUID
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetEmail
+ *
+ * This routine will attempt to derive an email address from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished
+ * Names containing either a PKIX email address or a PKCS#9 email
+ * address, the result will be the value of that attribute. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_EMAIL
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5 String */
+NSSName_GetEmail
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetCommonName
+ *
+ * This routine will attempt to derive a common name from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished Names
+ * containing a PKIX Common Name, the result will be that name. If
+ * the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_COMMON_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSName_GetCommonName
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetOrganization
+ *
+ * This routine will attempt to derive an organisation name from the
+ * specified name, if the choices and content of the name permit.
+ * If Name consists of a Sequence of Relative Distinguished names
+ * containing a PKIX Organization, the result will be the value of
+ * that attribute. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. This routine may return NULL upon
+ * error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_ORGANIZATION
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSName_GetOrganization
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetOrganizationalUnits
+ *
+ * This routine will attempt to derive a sequence of organisational
+ * unit names from the specified name, if the choices and content of
+ * the name permit. If the Name consists of a Sequence of Relative
+ * Distinguished Names containing one or more organisational units,
+ * the result will be the values of those attributes. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_ORGANIZATIONAL_UNITS
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a null-terminated array of UTF8 Strings
+ */
+
+NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */
+NSSName_GetOrganizationalUnits
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetStateOrProvince
+ *
+ * This routine will attempt to derive a state or province name from
+ * the specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished Names
+ * containing a state or province, the result will be the value of
+ * that attribute. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. This routine may return NULL upon
+ * error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_STATE_OR_PROVINCE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSName_GetStateOrProvince
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetLocality
+ *
+ * This routine will attempt to derive a locality name from the
+ * specified name, if the choices and content of the name permit. If
+ * the Name consists of a Sequence of Relative Distinguished names
+ * containing a Locality, the result will be the value of that
+ * attribute. If the optional arena argument is non-null, the memory
+ * used will be obtained from that arena; otherwise, the memory will
+ * be obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_LOCALITY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSName_GetLocality
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetCountry
+ *
+ * This routine will attempt to derive a country name from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished
+ * Names containing a Country, the result will be the value of
+ * that attribute.. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise,
+ * the memory will be obtained from the heap. This routine may
+ * return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_COUNTRY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */
+NSSName_GetCountry
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSName_GetAttribute
+ *
+ * If the specified name consists of a Sequence of Relative
+ * Distinguished Names containing an attribute with the specified
+ * type, and the actual value of that attribute may be expressed
+ * with a Directory String, then the value of that attribute will
+ * be returned as a Directory String. If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_ATTRIBUTE
+ * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSName_GetAttribute
+(
+ NSSName *name,
+ NSSOID *attribute,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSGeneralName_CreateFromBER -- constructor
+ * NSSGeneralName_CreateFromUTF8 -- constructor
+ * NSSGeneralName_Create -- constructor
+ *
+ * NSSGeneralName_Destroy
+ * NSSGeneralName_GetDEREncoding
+ * NSSGeneralName_GetUTF8Encoding
+ * NSSGeneralName_GetChoice
+ * NSSGeneralName_GetOtherName
+ * NSSGeneralName_GetRfc822Name
+ * NSSGeneralName_GetDNSName
+ * NSSGeneralName_GetX400Address
+ * NSSGeneralName_GetDirectoryName
+ * NSSGeneralName_GetEdiPartyName
+ * NSSGeneralName_GetUniformResourceIdentifier
+ * NSSGeneralName_GetIPAddress
+ * NSSGeneralName_GetRegisteredID
+ * NSSGeneralName_GetSpecifiedChoice
+ * NSSGeneralName_Compare
+ * NSSGeneralName_Duplicate
+ *
+ * NSSGeneralName_GetUID
+ * NSSGeneralName_GetEmail
+ * NSSGeneralName_GetCommonName
+ * NSSGeneralName_GetOrganization
+ * NSSGeneralName_GetOrganizationalUnits
+ * NSSGeneralName_GetStateOrProvince
+ * NSSGeneralName_GetLocality
+ * NSSGeneralName_GetCountry
+ * NSSGeneralName_GetAttribute
+ */
+
+/*
+ * NSSGeneralName_CreateFromBER
+ *
+ * This routine creates an NSSGeneralName by decoding a BER- or DER-
+ * encoded general name. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralName upon success
+ */
+
+NSS_EXTERN NSSGeneralName *
+NSSGeneralName_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berGeneralName
+);
+
+/*
+ * NSSGeneralName_CreateFromUTF8
+ *
+ * This routine creates an NSSGeneralName by decoding a UTF8 string
+ * consisting of the string representation of one of the choices of
+ * general names. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The routine may return NULL upon
+ * error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralName upon success
+ */
+
+NSS_EXTERN NSSGeneralName *
+NSSGeneralName_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringGeneralName
+);
+
+/*
+ * NSSGeneralName_Create
+ *
+ * This routine creates an NSSGeneralName with the specified choice of
+ * underlying name types. The value of the choice variable must be one
+ * of the values of the NSSGeneralNameChoice enumeration, and the type
+ * of the arg variable must be as specified in the following table:
+ *
+ * Choice Type
+ * ============================================ =========
+ * NSSGeneralNameChoiceOtherName
+ * NSSGeneralNameChoiceRfc822Name
+ * NSSGeneralNameChoiceDNSName
+ * NSSGeneralNameChoiceX400Address
+ * NSSGeneralNameChoiceDirectoryName NSSName *
+ * NSSGeneralNameChoiceEdiPartyName
+ * NSSGeneralNameChoiceUniformResourceIdentifier
+ * NSSGeneralNameChoiceIPAddress
+ * NSSGeneralNameChoiceRegisteredID
+ *
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one fo the following values:
+ * NSS_ERROR_INVALID_CHOICE
+ * NSS_ERROR_INVALID_ARGUMENT
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralName upon success
+ */
+
+NSS_EXTERN NSSGeneralName *
+NSSGeneralName_Create
+(
+ NSSGeneralNameChoice choice,
+ void *arg
+);
+
+/*
+ * NSSGeneralName_Destroy
+ *
+ * This routine will destroy a General Name object. It should
+ * eventually be called on all General Names created without an arena.
+ * While it is not necessary to call it on General Names created within
+ * an arena, it is not an error to do so. This routine returns a
+ * PRStatus value; if successful, it will return PR_SUCCESS. If
+ * usuccessful, it will create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ *
+ * Return value:
+ * PR_FAILURE upon failure
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+NSSGeneralName_Destroy
+(
+ NSSGeneralName *generalName
+);
+
+/*
+ * NSSGeneralName_GetDEREncoding
+ *
+ * This routine will DER-encode a name object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSGeneralName
+ */
+
+NSS_EXTERN NSSDER *
+NSSGeneralName_GetDEREncoding
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the General Name in the format specified by the
+ * underlying name choice. If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSGeneralName_GetUTF8Encoding
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetChoice
+ *
+ * This routine returns the type of choice underlying the specified
+ * general name. The return value will be a member of the
+ * NSSGeneralNameChoice enumeration. This routine may return
+ * NSSGeneralNameChoiceInvalid upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ *
+ * Return value:
+ * NSSGeneralNameChoiceInvalid upon error
+ * An other member of the NSSGeneralNameChoice enumeration
+ */
+
+NSS_EXTERN NSSGeneralNameChoice
+NSSGeneralName_GetChoice
+(
+ NSSGeneralName *generalName
+);
+
+/*
+ * NSSGeneralName_GetOtherName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * Other Name, this routine will return a pointer to that Other name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSOtherName
+ */
+
+NSS_EXTERN NSSOtherName *
+NSSGeneralName_GetOtherName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetRfc822Name
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * RFC 822 Name, this routine will return a pointer to that name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRFC822Name
+ */
+
+NSS_EXTERN NSSRFC822Name *
+NSSGeneralName_GetRfc822Name
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetDNSName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * DNS Name, this routine will return a pointer to that DNS name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSDNSName
+ */
+
+NSS_EXTERN NSSDNSName *
+NSSGeneralName_GetDNSName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetX400Address
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * X.400 Address, this routine will return a pointer to that Address.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSX400Address
+ */
+
+NSS_EXTERN NSSX400Address *
+NSSGeneralName_GetX400Address
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetDirectoryName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * (directory) Name, this routine will return a pointer to that name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSName
+ */
+
+NSS_EXTERN NSSName *
+NSSGeneralName_GetName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetEdiPartyName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * EDI Party Name, this routine will return a pointer to that name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSEdiPartyName
+ */
+
+NSS_EXTERN NSSEdiPartyName *
+NSSGeneralName_GetEdiPartyName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetUniformResourceIdentifier
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * URI, this routine will return a pointer to that URI.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSURI
+ */
+
+NSS_EXTERN NSSURI *
+NSSGeneralName_GetUniformResourceIdentifier
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetIPAddress
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * IP Address , this routine will return a pointer to that address.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSIPAddress
+ */
+
+NSS_EXTERN NSSIPAddress *
+NSSGeneralName_GetIPAddress
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetRegisteredID
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * Registered ID, this routine will return a pointer to that ID.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRegisteredID
+ */
+
+NSS_EXTERN NSSRegisteredID *
+NSSGeneralName_GetRegisteredID
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetSpecifiedChoice
+ *
+ * If the choice underlying the specified NSSGeneralName matches the
+ * specified choice, a caller-owned pointer to that underlying object
+ * will be returned. Otherwise, an error will be placed on the error
+ * stack and NULL will be returned. If the optional arena argument
+ * is non-null, the memory required will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. The caller
+ * owns the returned pointer. This routine may return NULL upon
+ * error, in which caes it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer, which must be typecast
+ */
+
+NSS_EXTERN void *
+NSSGeneralName_GetSpecifiedChoice
+(
+ NSSGeneralName *generalName,
+ NSSGeneralNameChoice choice,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_Compare
+ *
+ * This routine compares two General Names for equality. For two
+ * General Names to be equal, they must have the same choice of
+ * underlying types, and the underlying values must be equal. The
+ * result of the comparison will be stored at the location pointed
+ * to by the "equalp" variable, which must point to a valid PRBool.
+ * This routine may return PR_FAILURE upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following value:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+NSSGeneralName_Compare
+(
+ NSSGeneralName *generalName1,
+ NSSGeneralName *generalName2,
+ PRBool *equalp
+);
+
+/*
+ * NSSGeneralName_Duplicate
+ *
+ * This routine duplicates the specified General Name. If the optional
+ * arena argument is non-null, the memory required will be obtained
+ * from that arena; otherwise, the memory will be obtained from the
+ * heap. This routine may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new NSSGeneralName
+ */
+
+NSS_EXTERN NSSGeneralName *
+NSSGeneralName_Duplicate
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetUID
+ *
+ * This routine will attempt to derive a user identifier from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished Names containing a UID
+ * attribute, the UID will be the value of that attribute. Note
+ * that no UID attribute is defined in either PKIX or PKCS#9;
+ * rather, this seems to derive from RFC 1274, which defines the
+ * type as a caseIgnoreString. We'll return a Directory String.
+ * If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_UID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String.
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSGeneralName_GetUID
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetEmail
+ *
+ * This routine will attempt to derive an email address from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing either
+ * a PKIX email address or a PKCS#9 email address, the result will
+ * be the value of that attribute. If the General Name is an RFC 822
+ * Name, the result will be the string form of that name. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_EMAIL
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5String */
+NSSGeneralName_GetEmail
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetCommonName
+ *
+ * This routine will attempt to derive a common name from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing a PKIX
+ * Common Name, the result will be that name. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_COMMON_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSGeneralName_GetCommonName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetOrganization
+ *
+ * This routine will attempt to derive an organisation name from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing an
+ * Organization, the result will be the value of that attribute.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_ORGANIZATION
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSGeneralName_GetOrganization
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetOrganizationalUnits
+ *
+ * This routine will attempt to derive a sequence of organisational
+ * unit names from the specified general name, if the choices and
+ * content of the name permit. If the General Name is a (directory)
+ * Name consisting of a Sequence of Relative Distinguished names
+ * containing one or more organisational units, the result will
+ * consist of those units. If the optional arena argument is non-
+ * null, the memory used will be obtained from that arena; otherwise,
+ * the memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_ORGANIZATIONAL_UNITS
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a null-terminated array of UTF8 Strings
+ */
+
+NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */
+NSSGeneralName_GetOrganizationalUnits
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetStateOrProvince
+ *
+ * This routine will attempt to derive a state or province name from
+ * the specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing a state or
+ * province, the result will be the value of that attribute. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_STATE_OR_PROVINCE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSGeneralName_GetStateOrProvince
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetLocality
+ *
+ * This routine will attempt to derive a locality name from
+ * the specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing a Locality,
+ * the result will be the value of that attribute. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_LOCALITY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSGeneralName_GetLocality
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetCountry
+ *
+ * This routine will attempt to derive a country name from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting of a
+ * Sequence of Relative Distinguished names containing a Country, the
+ * result will be the value of that attribute. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_COUNTRY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */
+NSSGeneralName_GetCountry
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralName_GetAttribute
+ *
+ * If the specified general name is a (directory) name consisting
+ * of a Sequence of Relative Distinguished Names containing an
+ * attribute with the specified type, and the actual value of that
+ * attribute may be expressed with a Directory String, then the
+ * value of that attribute will be returned as a Directory String.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_ATTRIBUTE
+ * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+NSSGeneralName_GetAttribute
+(
+ NSSGeneralName *generalName,
+ NSSOID *attribute,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralNameSeq
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSGeneralNameSeq_CreateFromBER -- constructor
+ * NSSGeneralNameSeq_Create -- constructor
+ *
+ * NSSGeneralNameSeq_Destroy
+ * NSSGeneralNameSeq_GetDEREncoding
+ * NSSGeneralNameSeq_AppendGeneralName
+ * NSSGeneralNameSeq_GetGeneralNameCount
+ * NSSGeneralNameSeq_GetGeneralName
+ * NSSGeneralNameSeq_Compare
+ * NSSGeneralnameSeq_Duplicate
+ */
+
+/*
+ * NSSGeneralNameSeq_CreateFromBER
+ *
+ * This routine creates a general name sequence by decoding a BER-
+ * or DER-encoded GeneralNames. If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralNameSeq upon success
+ */
+
+NSS_EXTERN NSSGeneralNameSeq *
+NSSGeneralNameSeq_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berGeneralNameSeq
+);
+
+/*
+ * NSSGeneralNameSeq_Create
+ *
+ * This routine creates an NSSGeneralNameSeq from one or more General
+ * Names. The final argument to this routine must be NULL. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralNameSeq upon success
+ */
+
+NSS_EXTERN NSSGeneralNameSeq *
+NSSGeneralNameSeq_Create
+(
+ NSSArena *arenaOpt,
+ NSSGeneralName *generalName1,
+ ...
+);
+
+/*
+ * NSSGeneralNameSeq_Destroy
+ *
+ * This routine will destroy an NSSGeneralNameSeq object. It should
+ * eventually be called on all NSSGeneralNameSeqs created without an
+ * arena. While it is not necessary to call it on NSSGeneralNameSeq's
+ * created within an arena, it is not an error to do so. This routine
+ * returns a PRStatus value; if successful, it will return PR_SUCCESS.
+ * If unsuccessful, it will create an error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+NSSGeneralNameSeq_Destroy
+(
+ NSSGeneralNameSeq *generalNameSeq
+);
+
+/*
+ * NSSGeneralNameSeq_GetDEREncoding
+ *
+ * This routine will DER-encode an NSSGeneralNameSeq object. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSGeneralNameSeq
+ */
+
+NSS_EXTERN NSSDER *
+NSSGeneralNameSeq_GetDEREncoding
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralNameSeq_AppendGeneralName
+ *
+ * This routine appends a General Name to the end of the existing
+ * General Name Sequence. If the sequence was created with a non-null
+ * arena argument, that same arena will be used for any additional
+ * required memory. If the sequence was created with a NULL arena
+ * argument, any additional memory will be obtained from the heap.
+ * This routine returns a PRStatus value; it will return PR_SUCCESS
+ * upon success, and upon failure it will create an error stack and
+ * return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS upon success
+ * PR_FAILURE upon failure.
+ */
+
+NSS_EXTERN PRStatus
+NSSGeneralNameSeq_AppendGeneralName
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSGeneralName *generalName
+);
+
+/*
+ * NSSGeneralNameSeq_GetGeneralNameCount
+ *
+ * This routine returns the cardinality of the specified General name
+ * Sequence. This routine may return 0 upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ *
+ * Return value;
+ * 0 upon error
+ * A positive number upon success
+ */
+
+NSS_EXTERN PRUint32
+NSSGeneralNameSeq_GetGeneralNameCount
+(
+ NSSGeneralNameSeq *generalNameSeq
+);
+
+/*
+ * NSSGeneralNameSeq_GetGeneralName
+ *
+ * This routine returns a pointer to the i'th General Name in the
+ * specified General Name Sequence. The value of the variable 'i' is
+ * on the range [0,c) where c is the cardinality returned from
+ * NSSGeneralNameSeq_GetGeneralNameCount. The caller owns the General
+ * Name the pointer to which is returned. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have
+ * created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_VALUE_OUT_OF_RANGE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to a General Name.
+ */
+
+NSS_EXTERN NSSGeneralName *
+NSSGeneralNameSeq_GetGeneralName
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSArena *arenaOpt,
+ PRUint32 i
+);
+
+/*
+ * NSSGeneralNameSeq_Compare
+ *
+ * This routine compares two General Name Sequences for equality. For
+ * two General Name Sequences to be equal, they must have the same
+ * cardinality, and each General Name in one sequence must be equal to
+ * the corresponding General Name in the other. The result of the
+ * comparison will be stored at the location pointed to by the "equalp"
+ * variable, which must point to a valid PRBool. This routine may
+ * return PR_FAILURE upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+NSSGeneralNameSeq_Compare
+(
+ NSSGeneralNameSeq *generalNameSeq1,
+ NSSGeneralNameSeq *generalNameSeq2,
+ PRBool *equalp
+);
+
+/*
+ * NSSGeneralNameSeq_Duplicate
+ *
+ * This routine duplicates the specified sequence of general names. If
+ * the optional arena argument is non-null, the memory required will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new General Name Sequence.
+ */
+
+NSS_EXTERN NSSGeneralNameSeq *
+NSSGeneralNameSeq_Duplicate
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSArena *arenaOpt
+);
+
+PR_END_EXTERN_C
+
+#endif /* NSSPT1M_H */
diff --git a/security/nss/lib/pki1/nsspki1t.h b/security/nss/lib/pki1/nsspki1t.h
new file mode 100644
index 000000000..8765a3ad3
--- /dev/null
+++ b/security/nss/lib/pki1/nsspki1t.h
@@ -0,0 +1,202 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef NSSPKI1T_H
+#define NSSPKI1T_H
+
+#ifdef DEBUG
+static const char NSSPKI1T_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * nsspki1t.h
+ *
+ * This file contains the public type definitions for the PKIX part-1
+ * objects.
+ */
+
+#ifndef NSSBASET_H
+#include "nssbaset.h"
+#endif /* NSSBASET_H */
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * OBJECT IDENTIFIER
+ *
+ * This is the basic OID that crops up everywhere.
+ */
+
+struct NSSOIDStr;
+typedef struct NSSOIDStr NSSOID;
+
+/*
+ * AttributeTypeAndValue
+ *
+ * This structure contains an attribute type (indicated by an OID),
+ * and the type-specific value. RelativeDistinguishedNamess consist
+ * of a set of these. These are distinct from Attributes (which have
+ * SET of values), from AttributeDescriptions (which have qualifiers
+ * on the types), and from AttributeValueAssertions (which assert a
+ * a value comparison under some matching rule).
+ */
+
+struct NSSATAVStr;
+typedef struct NSSATAVStr NSSATAV;
+
+/*
+ * RelativeDistinguishedName
+ *
+ * This structure contains an unordered set of AttributeTypeAndValue
+ * objects. RDNs are used to distinguish a set of objects underneath
+ * a common object.
+ *
+ * Often, a single ATAV is sufficient to make a unique distinction.
+ * For example, if a company assigns its people unique uid values,
+ * then in the Name "uid=smith,ou=People,o=Acme,c=US" the "uid=smith"
+ * ATAV by itself forms an RDN. However, sometimes a set of ATAVs is
+ * needed. For example, if a company needed to distinguish between
+ * two Smiths by specifying their corporate divisions, then in the
+ * Name "(cn=Smith,ou=Sales),ou=People,o=Acme,c=US" the parenthesised
+ * set of ATAVs forms the RDN.
+ */
+
+struct NSSRDNStr;
+typedef struct NSSRDNStr NSSRDN;
+
+/*
+ * RDNSequence
+ *
+ * This structure contains a sequence of RelativeDistinguishedName
+ * objects.
+ */
+
+struct NSSRDNSeqStr;
+typedef struct NSSRDNSeqStr NSSRDNSeq;
+
+/*
+ * Name
+ *
+ * This structure contains a union of the possible name formats,
+ * which at the moment is limited to an RDNSequence.
+ */
+
+struct NSSNameStr;
+typedef struct NSSNameStr NSSName;
+
+/*
+ * NameChoice
+ *
+ * This enumeration is used to specify choice within a name.
+ */
+
+enum NSSNameChoiceEnum {
+ NSSNameChoiceInvalid = -1,
+ NSSNameChoiceRdnSequence
+};
+typedef enum NSSNameChoiceEnum NSSNameChoice;
+
+/*
+ * GeneralName
+ *
+ * This structure contains a union of the possible general names,
+ * of which there are several.
+ */
+
+struct NSSGeneralNameStr;
+typedef struct NSSGeneralNameStr NSSGeneralName;
+
+/*
+ * GeneralNameChoice
+ *
+ * This enumerates the possible general name types.
+ */
+
+enum NSSGeneralNameChoiceEnum {
+ NSSGeneralNameChoiceInvalid = -1,
+ NSSGeneralNameChoiceOtherName = 0,
+ NSSGeneralNameChoiceRfc822Name = 1,
+ NSSGeneralNameChoiceDNSName = 2,
+ NSSGeneralNameChoiceX400Address = 3,
+ NSSGeneralNameChoiceDirectoryName = 4,
+ NSSGeneralNameChoiceEdiPartyName = 5,
+ NSSGeneralNameChoiceUniformResourceIdentifier = 6,
+ NSSGeneralNameChoiceIPAddress = 7,
+ NSSGeneralNameChoiceRegisteredID = 8
+};
+typedef enum NSSGeneralNameChoiceEnum NSSGeneralNameChoice;
+
+/*
+ * The "other" types of general names.
+ */
+
+struct NSSOtherNameStr;
+typedef struct NSSOtherNameStr NSSOtherName;
+
+struct NSSRFC822NameStr;
+typedef struct NSSRFC822NameStr NSSRFC822Name;
+
+struct NSSDNSNameStr;
+typedef struct NSSDNSNameStr NSSDNSName;
+
+struct NSSX400AddressStr;
+typedef struct NSSX400AddressStr NSSX400Address;
+
+struct NSSEdiPartyNameStr;
+typedef struct NSSEdiPartyNameStr NSSEdiPartyName;
+
+struct NSSURIStr;
+typedef struct NSSURIStr NSSURI;
+
+struct NSSIPAddressStr;
+typedef struct NSSIPAddressStr NSSIPAddress;
+
+struct NSSRegisteredIDStr;
+typedef struct NSSRegisteredIDStr NSSRegisteredID;
+
+/*
+ * GeneralNameSeq
+ *
+ * This structure contains a sequence of GeneralName objects.
+ * Note that the PKIX documents refer to this as "GeneralNames,"
+ * which differs from "GeneralName" by only one letter. To
+ * try to reduce confusion, we expand the name slightly to
+ * "GeneralNameSeq."
+ */
+
+struct NSSGeneralNameSeqStr;
+typedef struct NSSGeneralNameSeqStr NSSGeneralNameSeq;
+
+PR_END_EXTERN_C
+
+#endif /* NSSPKI1T_H */
diff --git a/security/nss/lib/pki1/oid.c b/security/nss/lib/pki1/oid.c
new file mode 100644
index 000000000..c4028a803
--- /dev/null
+++ b/security/nss/lib/pki1/oid.c
@@ -0,0 +1,1615 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * oid.c
+ *
+ * This file contains the implementation of the basic OID routines.
+ */
+
+#ifndef BASE_H
+#include "base.h"
+#endif /* BASE_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+#include "plhash.h"
+#include "plstr.h"
+
+/*
+ * NSSOID
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * NSSOID_CreateFromBER -- constructor
+ * NSSOID_CreateFromUTF8 -- constructor
+ * (there is no explicit destructor)
+ *
+ * NSSOID_GetDEREncoding
+ * NSSOID_GetUTF8Encoding
+
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssOID_CreateFromBER -- constructor
+ * nssOID_CreateFromUTF8 -- constructor
+ * (there is no explicit destructor)
+ *
+ * nssOID_GetDEREncoding
+ * nssOID_GetUTF8Encoding
+ *
+ * In debug builds, the following non-public calls are also available:
+ *
+ * nssOID_verifyPointer
+ * nssOID_getExplanation
+ * nssOID_getTaggedUTF8
+ */
+
+const NSSOID *NSS_OID_UNKNOWN = (NSSOID *)NULL;
+
+/*
+ * First, the public "wrappers"
+ */
+
+/*
+ * NSSOID_CreateFromBER
+ *
+ * This routine creates an NSSOID by decoding a BER- or DER-encoded
+ * OID. It may return NULL upon error, in which case it
+ * will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+NSSOID_CreateFromBER
+(
+ NSSBER *berOid
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ /*
+ * NSSBERs can be created by the user,
+ * so no pointer-tracking can be checked.
+ */
+
+ if( (NSSBER *)NULL == berOid ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSOID *)NULL;
+ }
+
+ if( (void *)NULL == berOid->data ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSOID *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssOID_CreateFromBER(berOid);
+}
+
+/*
+ * NSSOID_CreateFromUTF8
+ *
+ * This routine creates an NSSOID by decoding a UTF8 string
+ * representation of an OID in dotted-number format. The string may
+ * optionally begin with an octothorpe. It may return NULL
+ * upon error, in which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+NSSOID_CreateFromUTF8
+(
+ NSSUTF8 *stringOid
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ /*
+ * NSSUTF8s can be created by the user,
+ * so no pointer-tracking can be checked.
+ */
+
+ if( (NSSUTF8 *)NULL == stringOid ) {
+ nss_SetError(NSS_ERROR_INVALID_UTF8);
+ return (NSSOID *)NULL;
+ }
+#endif /* DEBUG */
+
+ return nssOID_CreateFromUTF8(stringOid);
+}
+
+/*
+ * NSSOID_GetDEREncoding
+ *
+ * This routine returns the DER encoding of the specified NSSOID.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return return null upon error, in
+ * which case it will have created an error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSOID
+ */
+
+NSS_EXTERN NSSDER *
+NSSOID_GetDEREncoding
+(
+ const NSSOID *oid,
+ NSSDER *rvOpt,
+ NSSArena *arenaOpt
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSDER *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSDER *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return nssOID_GetDEREncoding(oid, rvOpt, arenaOpt);
+}
+
+/*
+ * NSSOID_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing the dotted-number
+ * encoding of the specified NSSOID. If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return null upon error, in which case it will have created an
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the dotted-digit encoding of
+ * this NSSOID
+ */
+
+NSS_EXTERN NSSUTF8 *
+NSSOID_GetUTF8Encoding
+(
+ const NSSOID *oid,
+ NSSArena *arenaOpt
+)
+{
+ nss_ClearErrorStack();
+
+#ifdef DEBUG
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSUTF8 *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return nssOID_GetUTF8Encoding(oid, arenaOpt);
+}
+
+/*
+ * Next, some internal bookkeeping; including the OID "tag" table
+ * and the debug-version pointer tracker.
+ */
+
+/*
+ * For implementation reasons (so NSSOIDs can be compared with ==),
+ * we hash all NSSOIDs. This is the hash table.
+ */
+
+static PLHashTable *oid_hash_table;
+
+/*
+ * And this is its lock.
+ */
+
+static PRLock *oid_hash_lock;
+
+/*
+ * This is the hash function. We simply XOR the encoded form with
+ * itself in sizeof(PLHashNumber)-byte chunks. Improving this
+ * routine is left as an excercise for the more mathematically
+ * inclined student.
+ */
+
+static PR_CALLBACK PLHashNumber
+oid_hash
+(
+ const void *key
+)
+{
+ const NSSItem *item = (const NSSItem *)key;
+ PLHashNumber rv = 0;
+
+ PRUint8 *data = (PRUint8 *)item->data;
+ PRUint32 i;
+ PRUint8 *rvc = (PRUint8 *)&rv;
+
+ for( i = 0; i < item->size; i++ ) {
+ rvc[ i % sizeof(rv) ] ^= *data;
+ data++;
+ }
+
+ return rv;
+}
+
+/*
+ * This is the key-compare function. It simply does a lexical
+ * comparison on the encoded OID form. This does not result in
+ * quite the same ordering as the "sequence of numbers" order,
+ * but heck it's only used internally by the hash table anyway.
+ */
+
+static PR_CALLBACK PRIntn
+oid_hash_compare
+(
+ const void *k1,
+ const void *k2
+)
+{
+ PRIntn rv;
+
+ const NSSItem *i1 = (const NSSItem *)k1;
+ const NSSItem *i2 = (const NSSItem *)k2;
+
+ PRUint32 size = (i1->size < i2->size) ? i1->size : i2->size;
+
+ rv = (PRIntn)nsslibc_memcmp(i1->data, i2->data, size, (PRStatus *)NULL);
+ if( 0 == rv ) {
+ rv = i1->size - i2->size;
+ }
+
+ return !rv;
+}
+
+/*
+ * The pointer-tracking code
+ */
+
+#ifdef DEBUG
+extern const NSSError NSS_ERROR_INTERNAL_ERROR;
+
+static nssPointerTracker oid_pointer_tracker;
+
+static PRStatus
+oid_add_pointer
+(
+ const NSSOID *oid
+)
+{
+ PRStatus rv;
+
+ rv = nssPointerTracker_initialize(&oid_pointer_tracker);
+ if( PR_SUCCESS != rv ) {
+ return rv;
+ }
+
+ rv = nssPointerTracker_add(&oid_pointer_tracker, oid);
+ if( PR_SUCCESS != rv ) {
+ NSSError e = NSS_GetError();
+ if( NSS_ERROR_NO_MEMORY != e ) {
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ }
+
+ return rv;
+ }
+
+ return PR_SUCCESS;
+}
+
+#if defined(CAN_DELETE_OIDS)
+/*
+ * We actually don't define NSSOID deletion, since we keep OIDs
+ * in a hash table for easy comparison. Were we to, this is
+ * what the pointer-removal function would look like.
+ */
+
+static PRStatus
+oid_remove_pointer
+(
+ const NSSOID *oid
+)
+{
+ PRStatus rv;
+
+ rv = nssPointerTracker_remove(&oid_pointer_tracker, oid);
+ if( PR_SUCCESS != rv ) {
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ }
+
+ return rv;
+}
+#endif /* CAN_DELETE_OIDS */
+
+#endif /* DEBUG */
+
+/*
+ * All dynamically-added OIDs get their memory from one statically-
+ * declared arena here, merely so that any cleanup code will have
+ * an easier time of it.
+ */
+
+static NSSArena *oid_arena;
+
+/*
+ * This is the call-once function which initializes the hashtable.
+ * It creates it, then prepopulates it with all of the builtin OIDs.
+ * It also creates the aforementioned NSSArena.
+ */
+
+static PR_CALLBACK PRStatus
+oid_once_func
+(
+ void
+)
+{
+ PRUint32 i;
+
+ /* Initialize the arena */
+ oid_arena = nssArena_Create();
+ if( (NSSArena *)NULL == oid_arena ) {
+ goto loser;
+ }
+
+ /* Create the hash table lock */
+ oid_hash_lock = PR_NewLock();
+ if( (PRLock *)NULL == oid_hash_lock ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* Create the hash table */
+ oid_hash_table = PL_NewHashTable(0, oid_hash, oid_hash_compare,
+ PL_CompareValues,
+ (PLHashAllocOps *)0,
+ (void *)0);
+ if( (PLHashTable *)NULL == oid_hash_table ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* And populate it with all the builtins */
+ for( i = 0; i < nss_builtin_oid_count; i++ ) {
+ NSSOID *oid = (NSSOID *)&nss_builtin_oids[i];
+ PLHashEntry *e = PL_HashTableAdd(oid_hash_table, &oid->data, oid);
+ if( (PLHashEntry *)NULL == e ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+#ifdef DEBUG
+ if( PR_SUCCESS != oid_add_pointer(oid) ) {
+ goto loser;
+ }
+#endif /* DEBUG */
+ }
+
+ return PR_SUCCESS;
+
+ loser:
+ if( (PLHashTable *)NULL != oid_hash_table ) {
+ PL_HashTableDestroy(oid_hash_table);
+ oid_hash_table = (PLHashTable *)NULL;
+ }
+
+ if( (PRLock *)NULL != oid_hash_lock ) {
+ PR_DestroyLock(oid_hash_lock);
+ oid_hash_lock = (PRLock *)NULL;
+ }
+
+ if( (NSSArena *)NULL != oid_arena ) {
+ (void)nssArena_Destroy(oid_arena);
+ oid_arena = (NSSArena *)NULL;
+ }
+
+ return PR_FAILURE;
+}
+
+/*
+ * This is NSPR's once-block.
+ */
+
+static PRCallOnceType oid_call_once;
+
+/*
+ * And this is our multiply-callable internal init routine, which
+ * will call-once our call-once function.
+ */
+
+static PRStatus
+oid_init
+(
+ void
+)
+{
+ return PR_CallOnce(&oid_call_once, oid_once_func);
+}
+
+#ifdef DEBUG
+
+/*
+ * nssOID_verifyPointer
+ *
+ * This method is only present in debug builds.
+ *
+ * If the specified pointer is a valid pointer to an NSSOID object,
+ * this routine will return PR_SUCCESS. Otherwise, it will put an
+ * error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS if the pointer is valid
+ * PR_FAILURE if it isn't
+ */
+
+NSS_EXTERN PRStatus
+nssOID_verifyPointer
+(
+ const NSSOID *oid
+)
+{
+ PRStatus rv;
+
+ rv = oid_init();
+ if( PR_SUCCESS != rv ) {
+ return PR_FAILURE;
+ }
+
+ rv = nssPointerTracker_initialize(&oid_pointer_tracker);
+ if( PR_SUCCESS != rv ) {
+ return PR_FAILURE;
+ }
+
+ rv = nssPointerTracker_verify(&oid_pointer_tracker, oid);
+ if( PR_SUCCESS != rv ) {
+ nss_SetError(NSS_ERROR_INVALID_NSSOID);
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+#endif /* DEBUG */
+
+/*
+ * oid_sanity_check_ber
+ *
+ * This routine merely applies some sanity-checking to the BER-encoded
+ * OID.
+ */
+
+static PRStatus
+oid_sanity_check_ber
+(
+ NSSBER *berOid
+)
+{
+ PRUint32 i;
+ PRUint8 *data = (PRUint8 *)berOid->data;
+
+ /*
+ * The size must be longer than zero bytes.
+ */
+
+ if( berOid->size <= 0 ) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * In general, we can't preclude any number from showing up
+ * someday. We could probably guess that top-level numbers
+ * won't get very big (beyond the current ccitt(0), iso(1),
+ * or joint-ccitt-iso(2)). However, keep in mind that the
+ * encoding rules wrap the first two numbers together, as
+ *
+ * (first * 40) + second
+ *
+ * Also, it is noted in the specs that this implies that the
+ * second number won't go above forty.
+ *
+ * 128 encodes 3.8, which seems pretty safe for now. Let's
+ * check that the first byte is less than that.
+ *
+ * XXX This is a "soft check" -- we may want to exclude it.
+ */
+
+ if( data[0] >= 0x80 ) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * In a normalised format, leading 0x80s will never show up.
+ * This means that no 0x80 will be preceeded by the final
+ * byte of a sequence, which would naturaly be less than 0x80.
+ * Our internal encoding for the single-digit OIDs uses 0x80,
+ * but the only places we use them (loading the builtin table,
+ * and adding a UTF8-encoded OID) bypass this check.
+ */
+
+ for( i = 1; i < berOid->size; i++ ) {
+ if( (0x80 == data[i]) && (data[i-1] < 0x80) ) {
+ return PR_FAILURE;
+ }
+ }
+
+ /*
+ * The high bit of each octet indicates that following octets
+ * are included in the current number. Thus the last byte can't
+ * have the high bit set.
+ */
+
+ if( data[ berOid->size-1 ] >= 0x80 ) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * Other than that, any byte sequence is legit.
+ */
+ return PR_SUCCESS;
+}
+
+/*
+ * nssOID_CreateFromBER
+ *
+ * This routine creates an NSSOID by decoding a BER- or DER-encoded
+ * OID. It may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+nssOID_CreateFromBER
+(
+ NSSBER *berOid
+)
+{
+ NSSOID *rv;
+ PLHashEntry *e;
+
+ if( PR_SUCCESS != oid_init() ) {
+ return (NSSOID *)NULL;
+ }
+
+ if( PR_SUCCESS != oid_sanity_check_ber(berOid) ) {
+ nss_SetError(NSS_ERROR_INVALID_BER);
+ return (NSSOID *)NULL;
+ }
+
+ /*
+ * Does it exist?
+ */
+ PR_Lock(oid_hash_lock);
+ rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, berOid);
+ (void)PR_Unlock(oid_hash_lock);
+ if( (NSSOID *)NULL != rv ) {
+ /* Found it! */
+ return rv;
+ }
+
+ /*
+ * Doesn't exist-- create it.
+ */
+ rv = nss_ZNEW(oid_arena, NSSOID);
+ if( (NSSOID *)NULL == rv ) {
+ return (NSSOID *)NULL;
+ }
+
+ rv->data.data = nss_ZAlloc(oid_arena, berOid->size);
+ if( (void *)NULL == rv->data.data ) {
+ return (NSSOID *)NULL;
+ }
+
+ rv->data.size = berOid->size;
+ nsslibc_memcpy(rv->data.data, berOid->data, berOid->size);
+
+#ifdef DEBUG
+ rv->tag = "<runtime>";
+ rv->expl = "(OID registered at runtime)";
+#endif /* DEBUG */
+
+ PR_Lock(oid_hash_lock);
+ e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
+ (void)PR_Unlock(oid_hash_lock);
+ if( (PLHashEntry *)NULL == e ) {
+ nss_ZFreeIf(rv->data.data);
+ nss_ZFreeIf(rv);
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSOID *)NULL;
+ }
+
+#ifdef DEBUG
+ {
+ PRStatus st;
+ st = oid_add_pointer(rv);
+ if( PR_SUCCESS != st ) {
+ PR_Lock(oid_hash_lock);
+ (void)PL_HashTableRemove(oid_hash_table, &rv->data);
+ (void)PR_Unlock(oid_hash_lock);
+ (void)nss_ZFreeIf(rv->data.data);
+ (void)nss_ZFreeIf(rv);
+ return (NSSOID *)NULL;
+ }
+ }
+#endif /* DEBUG */
+
+ return rv;
+}
+
+/*
+ * oid_sanity_check_utf8
+ *
+ * This routine merely applies some sanity-checking to the
+ * UTF8-encoded OID.
+ */
+
+static PRStatus
+oid_sanity_check_utf8
+(
+ NSSUTF8 *s
+)
+{
+ /*
+ * It may begin with an octothorpe, which we skip.
+ */
+
+ if( '#' == *s ) {
+ s++;
+ }
+
+ /*
+ * It begins with a number
+ */
+
+ if( (*s < '0') || (*s > '9') ) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * First number is only one digit long
+ *
+ * XXX This is a "soft check" -- we may want to exclude it
+ */
+
+ if( (s[1] != '.') && (s[1] != '\0') ) {
+ return PR_FAILURE;
+ }
+
+ /*
+ * Every character is either a digit or a period
+ */
+
+ for( ; '\0' != *s; s++ ) {
+ if( ('.' != *s) && ((*s < '0') || (*s > '9')) ) {
+ return PR_FAILURE;
+ }
+
+ /* No two consecutive periods */
+ if( ('.' == *s) && ('.' == s[1]) ) {
+ return PR_FAILURE;
+ }
+ }
+
+ /*
+ * The last character isn't a period
+ */
+
+ if( '.' == *--s ) {
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+static PRUint32
+oid_encode_number
+(
+ PRUint32 n,
+ PRUint8 *dp,
+ PRUint32 nb
+)
+{
+ PRUint32 a[5];
+ PRUint32 i;
+ PRUint32 rv;
+
+ a[0] = (n >> 28) & 0x7f;
+ a[1] = (n >> 21) & 0x7f;
+ a[2] = (n >> 14) & 0x7f;
+ a[3] = (n >> 7) & 0x7f;
+ a[4] = n & 0x7f;
+
+ for( i = 0; i < 5; i++ ) {
+ if( 0 != a[i] ) {
+ break;
+ }
+ }
+
+ if( 5 == i ) {
+ i--;
+ }
+
+ rv = 5-i;
+ if( rv > nb ) {
+ return rv;
+ }
+
+ for( ; i < 4; i++ ) {
+ *dp = 0x80 | a[i];
+ dp++;
+ }
+
+ *dp = a[4];
+
+ return rv;
+}
+
+/*
+ * oid_encode_huge
+ *
+ * This routine will convert a huge decimal number into the DER
+ * encoding for oid numbers. It is not limited to numbers that will
+ * fit into some wordsize, like oid_encode_number. But it's not
+ * necessarily very fast, either. This is here in case some joker
+ * throws us an ASCII oid like 1.2.3.99999999999999999999999999.
+ */
+
+static PRUint32
+oid_encode_huge
+(
+ NSSUTF8 *s,
+ NSSUTF8 *e,
+ PRUint8 *dp,
+ PRUint32 nb
+)
+{
+ PRUint32 slen = (e-s);
+ PRUint32 blen = (slen+1)/2;
+ PRUint8 *st = (PRUint8 *)NULL;
+ PRUint8 *bd = (PRUint8 *)NULL;
+ PRUint32 i;
+ PRUint32 bitno;
+ PRUint8 *last;
+ PRUint8 *first;
+ PRUint32 byteno;
+ PRUint8 mask;
+
+ /* We'll be munging the data, so duplicate it */
+ st = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, slen);
+ if( (PRUint8 *)NULL == st ) {
+ return 0;
+ }
+
+ /* Don't know ahead of time exactly how long we'll need */
+ bd = (PRUint8 *)nss_ZAlloc((NSSArena *)NULL, blen);
+ if( (PRUint8 *)NULL == bd ) {
+ (void)nss_ZFreeIf(st);
+ return 0;
+ }
+
+ /* Copy the original, and convert ASCII to numbers */
+ for( i = 0; i < slen; i++ ) {
+ st[i] = (PRUint8)(s[i] - '0');
+ }
+
+ last = &st[slen-1];
+ first = &st[0];
+
+ /*
+ * The way we create the binary version is by looking at it
+ * bit by bit. Start with the least significant bit. If the
+ * number is odd, set that bit. Halve the number (with integer
+ * division), and go to the next least significant bit. Keep
+ * going until the number goes to zero.
+ */
+ for( bitno = 0; ; bitno++ ) {
+ PRUint8 *d;
+
+ byteno = bitno/7;
+ mask = (PRUint8)(1 << (bitno%7));
+
+ /* Skip leading zeroes */
+ for( ; first < last; first ++ ) {
+ if( 0 != *first ) {
+ break;
+ }
+ }
+
+ /* Down to one number and it's a zero? Done. */
+ if( (first == last) && (0 == *last) ) {
+ break;
+ }
+
+ /* Last digit is odd? Set the bit */
+ if( *last & 1 ) {
+ bd[ byteno ] |= mask;
+ }
+
+
+ /*
+ * Divide the number in half. This is just a matter
+ * of going from the least significant digit upwards,
+ * halving each one. If any digit is odd (other than
+ * the last, which has already been handled), add five
+ * to the digit to its right.
+ */
+ *last /= 2;
+
+ for( d = &last[-1]; d >= first; d-- ) {
+ if( *d & 1 ) {
+ d[1] += 5;
+ }
+
+ *d /= 2;
+ }
+ }
+
+ /* Is there room to write the encoded data? */
+ if( (byteno+1) > nb ) {
+ return (byteno+1);
+ }
+
+ /* Trim any leading zero that crept in there */
+ for( ; byteno > 0; byteno-- ) {
+ if( 0 != bd[ byteno ] ) {
+ break;
+ }
+ }
+
+ /* Copy all but the last, marking the "continue" bit */
+ for( i = 0; i < byteno; i++ ) {
+ dp[i] = bd[ byteno-i ] | 0x80;
+ }
+ /* And the last with the "continue" bit clear */
+ dp[byteno] = bd[0];
+
+ (void)nss_ZFreeIf(bd);
+ (void)nss_ZFreeIf(st);
+ return (byteno+1);
+}
+
+/*
+ * oid_encode_string
+ *
+ * This routine converts a dotted-number OID into a DER-encoded
+ * one. It assumes we've already sanity-checked the string.
+ */
+
+extern const NSSError NSS_ERROR_INTERNAL_ERROR;
+
+static NSSOID *
+oid_encode_string
+(
+ NSSUTF8 *s
+)
+{
+ PRUint32 nn = 0; /* number of numbers */
+ PRUint32 nb = 0; /* number of bytes (estimated) */
+ NSSUTF8 *t;
+ PRUint32 nd = 0; /* number of digits */
+ NSSOID *rv;
+ PRUint8 *dp;
+ PRUint32 a, b;
+ PRUint32 inc;
+
+ /* Dump any octothorpe */
+ if( '#' == *s ) {
+ s++;
+ }
+
+ /* Count up the bytes needed */
+ for( t = s; '\0' != *t; t++ ) {
+ if( '.' == *t ) {
+ nb += (nd+1)/2; /* errs on the big side */
+ nd = 0;
+ nn++;
+ } else {
+ nd++;
+ }
+ }
+ nb += (nd+1)/2;
+ nn++;
+
+ if( 1 == nn ) {
+ /*
+ * We have our own "denormalised" encoding for these,
+ * which is only used internally.
+ */
+ nb++;
+ }
+
+ /*
+ * Allocate. Note that we don't use the oid_arena here.. this is
+ * because there really isn't a "free()" for stuff allocated out of
+ * arenas (at least with the current implementation), so this would
+ * keep using up memory each time a UTF8-encoded OID were added.
+ * If need be (if this is the first time this oid has been seen),
+ * we'll copy it.
+ */
+ rv = nss_ZNEW((NSSArena *)NULL, NSSOID);
+ if( (NSSOID *)NULL == rv ) {
+ return (NSSOID *)NULL;
+ }
+
+ rv->data.data = nss_ZAlloc((NSSArena *)NULL, nb);
+ if( (void *)NULL == rv->data.data ) {
+ (void)nss_ZFreeIf(rv);
+ return (NSSOID *)NULL;
+ }
+
+ dp = (PRUint8 *)rv->data.data;
+
+ a = atoi(s);
+
+ if( 1 == nn ) {
+ dp[0] = '\x80';
+ inc = oid_encode_number(a, &dp[1], nb-1);
+ if( inc >= nb ) {
+ goto loser;
+ }
+ } else {
+ for( t = s; '.' != *t; t++ ) {
+ ;
+ }
+
+ t++;
+ b = atoi(t);
+ inc = oid_encode_number(a*40+b, dp, nb);
+ if( inc > nb ) {
+ goto loser;
+ }
+ dp += inc;
+ nb -= inc;
+ nn -= 2;
+
+ while( nn-- > 0 ) {
+ NSSUTF8 *u;
+
+ for( ; '.' != *t; t++ ) {
+ ;
+ }
+
+ t++;
+
+ for( u = t; ('\0' != *u) && ('.' != *u); u++ ) {
+ ;
+ }
+
+ if( (u-t > 9) ) {
+ /* In the billions. Rats. */
+ inc = oid_encode_huge(t, u, dp, nb);
+ } else {
+ b = atoi(t);
+ inc = oid_encode_number(b, dp, nb);
+ }
+
+ if( inc > nb ) {
+ goto loser;
+ }
+ dp += inc;
+ nb -= inc;
+ }
+ }
+
+ return rv;
+
+ loser:
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ return (NSSOID *)NULL;
+}
+
+/*
+ * nssOID_CreateFromUTF8
+ *
+ * This routine creates an NSSOID by decoding a UTF8 string
+ * representation of an OID in dotted-number format. The string may
+ * optionally begin with an octothorpe. It may return NULL
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_STRING
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+nssOID_CreateFromUTF8
+(
+ NSSUTF8 *stringOid
+)
+{
+ NSSOID *rv = (NSSOID *)NULL;
+ NSSOID *candidate = (NSSOID *)NULL;
+ PLHashEntry *e;
+
+ if( PR_SUCCESS != oid_init() ) {
+ return (NSSOID *)NULL;
+ }
+
+ if( PR_SUCCESS != oid_sanity_check_utf8(stringOid) ) {
+ nss_SetError(NSS_ERROR_INVALID_STRING);
+ return (NSSOID *)NULL;
+ }
+
+ candidate = oid_encode_string(stringOid);
+ if( (NSSOID *)NULL == candidate ) {
+ /* Internal error only */
+ return rv;
+ }
+
+ /*
+ * Does it exist?
+ */
+ PR_Lock(oid_hash_lock);
+ rv = (NSSOID *)PL_HashTableLookup(oid_hash_table, &candidate->data);
+ (void)PR_Unlock(oid_hash_lock);
+ if( (NSSOID *)NULL != rv ) {
+ /* Already exists. Delete my copy and return the original. */
+ (void)nss_ZFreeIf(candidate->data.data);
+ (void)nss_ZFreeIf(candidate);
+ return rv;
+ }
+
+ /*
+ * Nope. Add it. Remember to allocate it out of the oid arena.
+ */
+
+ rv = nss_ZNEW(oid_arena, NSSOID);
+ if( (NSSOID *)NULL == rv ) {
+ goto loser;
+ }
+
+ rv->data.data = nss_ZAlloc(oid_arena, candidate->data.size);
+ if( (void *)NULL == rv->data.data ) {
+ goto loser;
+ }
+
+ rv->data.size = candidate->data.size;
+ nsslibc_memcpy(rv->data.data, candidate->data.data, rv->data.size);
+
+ (void)nss_ZFreeIf(candidate->data.data);
+ (void)nss_ZFreeIf(candidate);
+
+#ifdef DEBUG
+ rv->tag = "<runtime>";
+ rv->expl = "(OID registered at runtime)";
+#endif /* DEBUG */
+
+ PR_Lock(oid_hash_lock);
+ e = PL_HashTableAdd(oid_hash_table, &rv->data, rv);
+ (void)PR_Unlock(oid_hash_lock);
+ if( (PLHashEntry *)NULL == e ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+#ifdef DEBUG
+ {
+ PRStatus st;
+ st = oid_add_pointer(rv);
+ if( PR_SUCCESS != st ) {
+ PR_Lock(oid_hash_lock);
+ (void)PL_HashTableRemove(oid_hash_table, &rv->data);
+ (void)PR_Unlock(oid_hash_lock);
+ goto loser;
+ }
+ }
+#endif /* DEBUG */
+
+ return rv;
+
+ loser:
+ if( (NSSOID *)NULL != candidate ) {
+ (void)nss_ZFreeIf(candidate->data.data);
+ }
+ (void)nss_ZFreeIf(candidate);
+
+ if( (NSSOID *)NULL != rv ) {
+ (void)nss_ZFreeIf(rv->data.data);
+ }
+ (void)nss_ZFreeIf(rv);
+
+ return (NSSOID *)NULL;
+}
+
+/*
+ * nssOID_GetDEREncoding
+ *
+ * This routine returns the DER encoding of the specified NSSOID.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return return null upon error, in
+ * which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_OID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSOID
+ */
+
+NSS_EXTERN NSSDER *
+nssOID_GetDEREncoding
+(
+ const NSSOID *oid,
+ NSSDER *rvOpt,
+ NSSArena *arenaOpt
+)
+{
+ const NSSItem *it;
+ NSSDER *rv;
+
+ if( PR_SUCCESS != oid_init() ) {
+ return (NSSDER *)NULL;
+ }
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSDER *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSDER *)NULL;
+ }
+ }
+#endif /* NSSDEBUG */
+
+ it = &oid->data;
+
+ if( (NSSDER *)NULL == rvOpt ) {
+ rv = nss_ZNEW(arenaOpt, NSSDER);
+ if( (NSSDER *)NULL == rv ) {
+ return (NSSDER *)NULL;
+ }
+ } else {
+ rv = rvOpt;
+ }
+
+ rv->data = nss_ZAlloc(arenaOpt, it->size);
+ if( (void *)NULL == rv->data ) {
+ if( rv != rvOpt ) {
+ (void)nss_ZFreeIf(rv);
+ }
+ return (NSSDER *)NULL;
+ }
+
+ rv->size = it->size;
+ nsslibc_memcpy(rv->data, it->data, it->size);
+
+ return rv;
+}
+
+/*
+ * nssOID_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing the dotted-number
+ * encoding of the specified NSSOID. If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return null upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_OID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the dotted-digit encoding of
+ * this NSSOID
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssOID_GetUTF8Encoding
+(
+ const NSSOID *oid,
+ NSSArena *arenaOpt
+)
+{
+ NSSUTF8 *rv;
+ PRUint8 *end;
+ PRUint8 *d;
+ PRUint8 *e;
+ char *a;
+ char *b;
+ PRUint32 len;
+
+ if( PR_SUCCESS != oid_init() ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSUTF8 *)NULL;
+ }
+ }
+#endif /* NSSDEBUG */
+
+ a = (char *)NULL;
+
+ /* d will point to the next sequence of bytes to decode */
+ d = (PRUint8 *)oid->data.data;
+ /* end points to one past the legitimate data */
+ end = &d[ oid->data.size ];
+
+#ifdef NSSDEBUG
+ /*
+ * Guarantee that the for(e=d;e<end;e++) loop below will
+ * terminate. Our BER sanity-checking code above will prevent
+ * such a BER from being registered, so the only other way one
+ * might show up is if our dotted-decimal encoder above screws
+ * up or our generated list is wrong. So I'll wrap it with
+ * #ifdef NSSDEBUG and #endif.
+ */
+ if( end[-1] & 0x80 ) {
+ nss_SetError(NSS_ERROR_INTERNAL_ERROR);
+ return (NSSUTF8 *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ /*
+ * Check for our pseudo-encoded single-digit OIDs
+ */
+ if( (*d == 0x80) && (2 == oid->data.size) ) {
+ /* Funky encoding. The second byte is the number */
+ a = PR_smprintf("%lu", (PRUint32)d[1]);
+ if( (char *)NULL == a ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+ goto done;
+ }
+
+ for( ; d < end; d = &e[1] ) {
+
+ for( e = d; e < end; e++ ) {
+ if( 0 == (*e & 0x80) ) {
+ break;
+ }
+ }
+
+ if( ((e-d) > 4) || (((e-d) == 4) && (*d & 0x70)) ) {
+ /* More than a 32-bit number */
+ } else {
+ PRUint32 n = 0;
+
+ switch( e-d ) {
+ case 4:
+ n |= ((PRUint32)(e[-4] & 0x0f)) << 28;
+ case 3:
+ n |= ((PRUint32)(e[-3] & 0x7f)) << 21;
+ case 2:
+ n |= ((PRUint32)(e[-2] & 0x7f)) << 14;
+ case 1:
+ n |= ((PRUint32)(e[-1] & 0x7f)) << 7;
+ case 0:
+ n |= ((PRUint32)(e[-0] & 0x7f)) ;
+ }
+
+ if( (char *)NULL == a ) {
+ /* This is the first number.. decompose it */
+ PRUint32 one = (n/40), two = (n%40);
+
+ a = PR_smprintf("%lu.%lu", one, two);
+ if( (char *)NULL == a ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+ } else {
+ b = PR_smprintf("%s.%lu", a, n);
+ if( (char *)NULL == b ) {
+ PR_smprintf_free(a);
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+
+ PR_smprintf_free(a);
+ a = b;
+ }
+ }
+ }
+
+ done:
+ /*
+ * Even if arenaOpt is NULL, we have to copy the data so that
+ * it'll be freed with the right version of free: ours, not
+ * PR_smprintf_free's.
+ */
+ len = PL_strlen(a);
+ rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len);
+ if( (NSSUTF8 *)NULL == rv ) {
+ PR_smprintf_free(a);
+ return (NSSUTF8 *)NULL;
+ }
+
+ nsslibc_memcpy(rv, a, len);
+ PR_smprintf_free(a);
+
+ return rv;
+}
+
+/*
+ * nssOID_getExplanation
+ *
+ * This method is only present in debug builds.
+ *
+ * This routine will return a static pointer to a UTF8-encoded string
+ * describing (in English) the specified OID. The memory pointed to
+ * by the return value is not owned by the caller, and should not be
+ * freed or modified. Note that explanations are only provided for
+ * the OIDs built into the NSS library; there is no way to specify an
+ * explanation for dynamically created OIDs. This routine is intended
+ * only for use in debugging tools such as "derdump." This routine
+ * may return null upon error, in which case it will have placed an
+ * error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ *
+ * Return value:
+ * NULL upon error
+ * A static pointer to a readonly, non-caller-owned UTF8-encoded
+ * string explaining the specified OID.
+ */
+
+#ifdef DEBUG
+NSS_EXTERN const NSSUTF8 *
+nssOID_getExplanation
+(
+ NSSOID *oid
+)
+{
+ if( PR_SUCCESS != oid_init() ) {
+ return (const NSSUTF8 *)NULL;
+ }
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSUTF8 *)NULL;
+ }
+#endif /* NSSDEBUG */
+
+ return oid->expl;
+}
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+#endif /* DEBUG */
+
+/*
+ * nssOID_getTaggedUTF8
+ *
+ * This method is only present in debug builds.
+ *
+ * This routine will return a pointer to a caller-owned UTF8-encoded
+ * string containing a tagged encoding of the specified OID. Note
+ * that OID (component) tags are only provided for the OIDs built
+ * into the NSS library; there is no way to specify tags for
+ * dynamically created OIDs. This routine is intended for use in
+ * debugging tools such as "derdump." If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the tagged encoding of
+ * this NSSOID
+ */
+
+#ifdef DEBUG
+NSS_EXTERN NSSUTF8 *
+nssOID_getTaggedUTF8
+(
+ NSSOID *oid,
+ NSSArena *arenaOpt
+)
+{
+ NSSUTF8 *rv;
+ char *raw;
+ char *c;
+ char *a = (char *)NULL;
+ char *b;
+ PRBool done = PR_FALSE;
+ PRUint32 len;
+
+ if( PR_SUCCESS != oid_init() ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+#ifdef NSSDEBUG
+ if( PR_SUCCESS != nssOID_verifyPointer(oid) ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ if( (NSSArena *)NULL != arenaOpt ) {
+ if( PR_SUCCESS != nssArena_verifyPointer(arenaOpt) ) {
+ return (NSSUTF8 *)NULL;
+ }
+ }
+#endif /* NSSDEBUG */
+
+ a = PR_smprintf("{");
+ if( (char *)NULL == a ) {
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+
+ /*
+ * What I'm doing here is getting the text version of the OID,
+ * e.g. 1.2.12.92, then looking up each set of leading numbers
+ * as oids.. e.g. "1," then "1.2," then "1.2.12," etc. Each of
+ * those will have the leaf tag, and I just build up the string.
+ * I never said this was the most efficient way of doing it,
+ * but hey it's a debug-build thing, and I'm getting really tired
+ * of writing this stupid low-level PKI code.
+ */
+
+ /* I know it's all ASCII, so I can use char */
+ raw = (char *)nssOID_GetUTF8Encoding(oid, (NSSArena *)NULL);
+ if( (char *)NULL == raw ) {
+ return (NSSUTF8 *)NULL;
+ }
+
+ for( c = raw; !done; c++ ) {
+ NSSOID *lead;
+ char *lastdot;
+
+ for( ; '.' != *c; c++ ) {
+ if( '\0' == *c ) {
+ done = PR_TRUE;
+ break;
+ }
+ }
+
+ *c = '\0';
+ lead = nssOID_CreateFromUTF8((NSSUTF8 *)raw);
+ if( (NSSOID *)NULL == lead ) {
+ PR_smprintf_free(a);
+ nss_ZFreeIf(raw);
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+
+ lastdot = PL_strrchr(raw, '.');
+ if( (char *)NULL == lastdot ) {
+ lastdot = raw;
+ }
+
+ b = PR_smprintf("%s %s(%s) ", a, lead->tag, &lastdot[1]);
+ if( (char *)NULL == b ) {
+ PR_smprintf_free(a);
+ nss_ZFreeIf(raw);
+ /* drop the OID reference on the floor */
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+
+ PR_smprintf_free(a);
+ a = b;
+
+ if( !done ) {
+ *c = '.';
+ }
+ }
+
+ nss_ZFreeIf(raw);
+
+ b = PR_smprintf("%s }", a);
+ if( (char *)NULL == b ) {
+ PR_smprintf_free(a);
+ nss_SetError(NSS_ERROR_NO_MEMORY);
+ return (NSSUTF8 *)NULL;
+ }
+
+ len = PL_strlen(b);
+
+ rv = (NSSUTF8 *)nss_ZAlloc(arenaOpt, len+1);
+ if( (NSSUTF8 *)NULL == rv ) {
+ PR_smprintf_free(b);
+ return (NSSUTF8 *)NULL;
+ }
+
+ nsslibc_memcpy(rv, b, len);
+ PR_smprintf_free(b);
+
+ return rv;
+}
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+#endif /* DEBUG */
diff --git a/security/nss/lib/pki1/oidgen.perl b/security/nss/lib/pki1/oidgen.perl
new file mode 100755
index 000000000..959e3cbaf
--- /dev/null
+++ b/security/nss/lib/pki1/oidgen.perl
@@ -0,0 +1,311 @@
+#!perl
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+$cvs_id = '@(#) $RCSfile$ $Revision$ $Date$ $Name$';
+
+$count = -1;
+while(<>) {
+ s/^((?:[^"#]+|"[^"]*")*)(\s*#.*$)/$1/;
+ next if (/^\s*$/);
+
+ /^([\S]+)\s+([^"][\S]*|"[^"]*")/;
+ $name = $1;
+ $value = $2;
+ # This is certainly not the best way to dequote the data.
+ $value =~ s/"//g;
+
+ if( $name =~ "OID" ) {
+ $count++;
+ $x[$count]{$name} = $value;
+ $enc = encodeoid($value);
+ $x[$count]{" encoding"} = escapeoid($enc);
+ $x[$count]{" encoding length"} = length($enc);
+ } else {
+ if( $count < 0 ) {
+ $g{$name} = $value;
+ } else {
+ $x[$count]{$name} = $value;
+ }
+ }
+}
+
+# dodump();
+doprint();
+
+sub dodump {
+for( $i = 0; $i <= $count; $i++ ) {
+ print "number $i:\n";
+ %y = %{$x[$i]};
+ while(($n,$v) = each(%y)) {
+ print "\t$n ==> $v\n";
+ }
+}
+}
+
+sub doprint {
+open(CFILE, ">oiddata.c") || die "Can't open oiddata.c: $!";
+open(HFILE, ">oiddata.h") || die "Can't open oiddata.h: $!";
+
+print CFILE <<EOD
+/* THIS IS A GENERATED FILE */
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+#ifdef DEBUG
+static const char CVS_ID[] = "$g{CVS_ID} ; $cvs_id";
+#endif /* DEBUG */
+
+#ifndef PKI1T_H
+#include "pki1t.h"
+#endif /* PKI1T_H */
+
+const NSSOID nss_builtin_oids[] = {
+EOD
+ ;
+
+for( $i = 0; $i <= $count; $i++ ) {
+ %y = %{$x[$i]};
+ print CFILE " {\n";
+ print CFILE "#ifdef DEBUG\n";
+ print CFILE " \"$y{TAG}\",\n";
+ print CFILE " \"$y{EXPL}\",\n";
+ print CFILE "#endif /* DEBUG */\n";
+ print CFILE " { \"", $y{" encoding"};
+ print CFILE "\", ", $y{" encoding length"}, " }\n";
+
+ if( $i == $count ) {
+ print CFILE " }\n";
+ } else {
+ print CFILE " },\n";
+ }
+}
+
+print CFILE "};\n\n";
+
+print CFILE "const PRUint32 nss_builtin_oid_count = ", ($count+1), ";\n\n";
+
+for( $i = 0; $i <= $count; $i++ ) {
+ %y = %{$x[$i]};
+ if( defined($y{NAME}) ) {
+ print CFILE "const NSSOID *$y{NAME} = (NSSOID *)&nss_builtin_oids[$i];\n";
+ }
+}
+
+print CFILE "\n";
+
+$attrcount = -1;
+for( $i = 0; $i <= $count; $i++ ) {
+ %y = %{$x[$i]};
+ if( defined($y{ATTR}) ) {
+ if( defined($y{NAME}) ) {
+ $attrcount++;
+ $attr[$attrcount]{ATTR} = $y{ATTR};
+ $attr[$attrcount]{NAME} = $y{NAME};
+ } else {
+ warn "Attribute $y{ATTR} has no name, and will be omitted!";
+ }
+ }
+}
+
+print CFILE "const nssAttributeTypeAliasTable nss_attribute_type_aliases[] = {\n";
+
+for( $i = 0; $i <= $attrcount; $i++ ) {
+ %y = %{$attr[$i]};
+ print CFILE " {\n";
+ print CFILE " \"$y{ATTR}\",\n";
+ print CFILE " &$y{NAME}\n";
+
+ if( $i == $attrcount ) {
+ print CFILE " }\n";
+ } else {
+ print CFILE " },\n";
+ }
+}
+
+print CFILE "};\n\n";
+
+print CFILE "const PRUint32 nss_attribute_type_alias_count = ", ($attrcount+1), ";\n\n";
+
+print HFILE <<EOD
+/* THIS IS A GENERATED FILE */
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef OIDDATA_H
+#define OIDDATA_H
+
+#ifdef DEBUG
+static const char OIDDATA_CVS_ID[] = "$g{CVS_ID} ; $cvs_id";
+#endif /* DEBUG */
+
+#ifndef NSSPKI1T_H
+#include "nsspki1t.h"
+#endif /* NSSPKI1T_H */
+
+extern const NSSOID nss_builtin_oids[];
+extern const PRUint32 nss_builtin_oid_count;
+
+/*extern const nssAttributeTypeAliasTable nss_attribute_type_aliases[];*/
+/*extern const PRUint32 nss_attribute_type_alias_count;*/
+
+EOD
+ ;
+
+for( $i = 0; $i <= $count; $i++ ) {
+ %y = %{$x[$i]};
+ if( defined($y{NAME}) ) {
+ print HFILE "extern const NSSOID *$y{NAME};\n";
+ }
+}
+
+print HFILE <<EOD
+
+#endif /* OIDDATA_H */
+EOD
+ ;
+
+close CFILE;
+close HFILE;
+}
+
+sub encodenum {
+ my $v = $_[0];
+ my @d;
+ my $i;
+ my $rv = "";
+
+ while( $v > 128 ) {
+ push @d, ($v % 128);
+ $v /= 128;
+ };
+ push @d, ($v%128);
+
+ for( $i = @d-1; $i > 0; $i-- ) {
+ $rv = $rv . chr(128 + $d[$i]);
+ }
+
+ $rv = $rv . chr($d[0]);
+
+ return $rv;
+}
+
+sub encodeoid {
+ my @o = split(/\./, $_[0]);
+ my $rv = "";
+ my $i;
+
+ if( @o < 2 ) {
+ # NSS's special "illegal" encoding
+ return chr(128) . encodenum($o[0]);
+ }
+
+ $rv = encodenum($o[0] * 40 + $o[1]);
+ shift @o; shift @o;
+
+ foreach $i (@o) {
+ $rv = $rv . encodenum($i);
+ }
+
+ return $rv;
+}
+
+sub escapeoid {
+ my @v = unpack("C*", $_[0]);
+ my $a;
+ my $rv = "";
+
+ foreach $a (@v) {
+ $rv = $rv . sprintf("\\x%02x", $a);
+ }
+
+ return $rv;
+}
diff --git a/security/nss/lib/pki1/oids.txt b/security/nss/lib/pki1/oids.txt
new file mode 100644
index 000000000..df7b3ab28
--- /dev/null
+++ b/security/nss/lib/pki1/oids.txt
@@ -0,0 +1,2115 @@
+#
+# The contents of this file are subject to the Mozilla Public
+# License Version 1.1 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS
+# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# rights and limitations under the License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is Netscape
+# Communications Corporation. Portions created by Netscape are
+# Copyright (C) 1994-2000 Netscape Communications Corporation. All
+# Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the
+# terms of the GNU General Public License Version 2 or later (the
+# "GPL"), in which case the provisions of the GPL are applicable
+# instead of those above. If you wish to allow use of your
+# version of this file only under the terms of the GPL and not to
+# allow others to use your version of this file under the MPL,
+# indicate your decision by deleting the provisions above and
+# replace them with the notice and other provisions required by
+# the GPL. If you do not delete the provisions above, a recipient
+# may use your version of this file under either the MPL or the
+# GPL.
+#
+CVS_ID "@(#) $RCSfile$ $Revision$ $Date$ $Name$"
+
+# Fields
+# OID -- the OID data itself, in dotted-number format
+# TAG -- the unofficial but common name. e.g., when written like
+# { joint-iso-ccitt(2) country(16) US(840) company(1) netscape(113730) }
+# those words ("joint-iso-ccitt," "country," etc.) are the tags.
+# EXPL -- a textual explanation, that should stand by itself
+# NAME -- the name we use in our code. If no NAME is given, it won't be included
+#
+# Additionally, some sets of OIDs map to some other spaces..
+# additional fields capture that data:
+#
+# CKM -- the corresponding Cryptoki Mechanism, if any
+# CKK -- the corresponding Cryptoki Key type, if any
+# ATTR -- the UTF-8 attribute type encoding, if applicable
+# it should be in the standard presentation style (e.g., lowercase),
+# already-escaped a la RFC 2253 if necessary (which would only
+# be necessary if some idiot defined an attribute with, say,
+# an equals sign in it)
+# CERT_EXTENSION -- SUPPORTED or UNSUPPORTED certificate extension, if applicable
+
+# Top of the OID tree -- see http://www.alvestrand.no/objectid/top.html
+#
+OID 0
+TAG ccitt # ITU-T used to be called CCITT
+EXPL "ITU-T"
+
+# See X.208 Annex C for an explanation of the OIDs below ccitt/itu-t
+
+OID 0.0
+TAG recommendation
+EXPL "ITU-T Recommendation"
+
+OID 0.1
+TAG question
+EXPL "ITU-T Question"
+
+OID 0.2
+TAG administration
+EXPL "ITU-T Administration"
+
+OID 0.3
+TAG network-operator
+EXPL "ITU-T Network Operator"
+
+OID 0.4
+TAG identified-organization
+EXPL "ITU-T Identified Organization"
+
+OID 0.9 # used in some RFCs (unofficial!)
+TAG data
+EXPL "RFC Data"
+
+OID 0.9.2342
+TAG pss
+EXPL "PSS British Telecom X.25 Network"
+
+OID 0.9.2342.19200300
+TAG ucl
+EXPL "RFC 1274 UCL Data networks"
+
+OID 0.9.2342.19200300.100
+TAG pilot
+EXPL "RFC 1274 pilot"
+
+OID 0.9.2342.19200300.100.1
+TAG attributeType
+EXPL "RFC 1274 Attribute Type"
+
+OID 0.9.2342.19200300.100.1.1
+TAG uid
+EXPL "RFC 1274 User Id"
+NAME NSS_OID_RFC1274_UID
+ATTR "uid"
+
+OID 0.9.2342.19200300.100.1.3
+TAG mail
+EXPL "RFC 1274 E-mail Addres"
+NAME NSS_OID_RFC1274_EMAIL
+ATTR "mail" # XXX fgmr
+
+OID 0.9.2342.19200300.100.1.25
+TAG dc
+EXPL "RFC 2247 Domain Component"
+NAME NSS_OID_RFC2247_DC
+ATTR "dc"
+
+OID 0.9.2342.19200300.100.3
+TAG attributeSyntax
+EXPL "RFC 1274 Attribute Syntax"
+
+OID 0.9.2342.19200300.100.3.4
+TAG iA5StringSyntax
+EXPL "RFC 1274 IA5 String Attribute Syntax"
+
+OID 0.9.2342.19200300.100.3.5
+TAG caseIgnoreIA5StringSyntax
+EXPL "RFC 1274 Case-Ignore IA5 String Attribute Syntax"
+
+OID 0.9.2342.19200300.100.4
+TAG objectClass
+EXPL "RFC 1274 Object Class"
+
+OID 0.9.2342.19200300.100.10
+TAG groups
+EXPL "RFC 1274 Groups"
+
+OID 0.9.2342.234219200300
+TAG ucl
+EXPL "RFC 1327 ucl"
+
+OID 1
+TAG iso
+EXPL "ISO"
+
+# See X.208 Annex B for an explanation of the OIDs below iso
+
+OID 1.0
+TAG standard
+EXPL "ISO Standard"
+
+OID 1.1
+TAG registration-authority
+EXPL "ISO Registration Authority"
+
+OID 1.2
+TAG member-body
+EXPL "ISO Member Body"
+
+OID 1.2.36
+TAG australia
+EXPL "Australia (ISO)"
+
+OID 1.2.158
+TAG taiwan
+EXPL "Taiwan (ISO)"
+
+OID 1.2.372
+TAG ireland
+EXPL "Ireland (ISO)"
+
+OID 1.2.578
+TAG norway
+EXPL "Norway (ISO)"
+
+OID 1.2.752
+TAG sweden
+EXPL "Sweden (ISO)"
+
+OID 1.2.826
+TAG great-britain
+EXPL "Great Britain (ISO)"
+
+OID 1.2.840
+TAG us
+EXPL "United States (ISO)"
+
+OID 1.2.840.1
+TAG organization
+EXPL "US (ISO) organization"
+
+OID 1.2.840.10003
+TAG ansi-z30-50
+EXPL "ANSI Z39.50"
+
+OID 1.2.840.10008
+TAG dicom
+EXPL "DICOM"
+
+OID 1.2.840.10017
+TAG ieee-1224
+EXPL "IEEE 1224"
+
+OID 1.2.840.10022
+TAG ieee-802-10
+EXPL "IEEE 802.10"
+
+OID 1.2.840.10036
+TAG ieee-802-11
+EXPL "IEEE 802.11"
+
+OID 1.2.840.10040
+TAG x9-57
+EXPL "ANSI X9.57"
+
+# RFC 2459:
+#
+# holdInstruction OBJECT IDENTIFIER ::=
+# {iso(1) member-body(2) us(840) x9cm(10040) 2}
+#
+# Note that the appendices of RFC 2459 define the (wrong) value
+# of 2.2.840.10040.2 for this oid.
+OID 1.2.840.10040.2
+TAG holdInstruction
+EXPL "ANSI X9.57 Hold Instruction"
+
+# RFC 2459:
+#
+# id-holdinstruction-none OBJECT IDENTIFIER ::=
+# {holdInstruction 1} -- deprecated
+OID 1.2.840.10040.2.1
+TAG id-holdinstruction-none
+EXPL "ANSI X9.57 Hold Instruction: None"
+
+# RFC 2459:
+#
+# id-holdinstruction-callissuer OBJECT IDENTIFIER ::=
+# {holdInstruction 2}
+OID 1.2.840.10040.2.2
+TAG id-holdinstruction-callissuer
+EXPL "ANSI X9.57 Hold Instruction: Call Issuer"
+
+# RFC 2459:
+#
+# id-holdinstruction-reject OBJECT IDENTIFIER ::=
+# {holdInstruction 3}
+OID 1.2.840.10040.2.3
+TAG id-holdinstruction-reject
+EXPL "ANSI X9.57 Hold Instruction: Reject"
+
+OID 1.2.840.10040.4
+TAG x9algorithm
+EXPL "ANSI X9.57 Algorithm"
+
+# RFC 2459:
+#
+# id-dsa OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) x9-57(10040) x9algorithm(4) 1 }
+OID 1.2.840.10040.4.1
+TAG id-dsa
+EXPL "ANSI X9.57 DSA Signature"
+NAME NSS_OID_ANSIX9_DSA_SIGNATURE
+CKM CKM_DSA
+CKK CKK_DSA
+
+# RFC 2459:
+#
+# id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) x9-57 (10040) x9algorithm(4) 3 }
+OID 1.2.840.10040.4.3
+TAG id-dsa-with-sha1
+EXPL "ANSI X9.57 Algorithm DSA Signature with SHA-1 Digest"
+NAME NSS_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST
+CKM CKM_DSA_SHA1
+
+OID 1.2.840.10046
+TAG x942
+EXPL "ANSI X9.42"
+
+OID 1.2.840.10046.2
+TAG algorithm
+EXPL "ANSI X9.42 Algorithm"
+
+# RFC 2459:
+#
+# dhpublicnumber OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) ansi-x942(10046) number-type(2) 1 }
+OID 1.2.840.10046.2.1
+TAG dhpublicnumber
+EXPL "Diffie-Hellman Public Key Algorithm"
+NAME NSS_OID_X942_DIFFIE_HELMAN_KEY
+CKM CKM_DH_PKCS_DERIVE
+CKK CKK_DH
+
+OID 1.2.840.113533
+TAG entrust
+EXPL "Entrust Technologies"
+
+OID 1.2.840.113549
+TAG rsadsi
+EXPL "RSA Data Security Inc."
+
+OID 1.2.840.113549.1
+# http://www.rsa.com/rsalabs/pubs/PKCS/
+TAG pkcs
+EXPL "PKCS"
+
+# RFC 2459:
+#
+# pkcs-1 OBJECT IDENTIFIER ::= {
+# iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+OID 1.2.840.113549.1.1
+# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-1.asc
+TAG pkcs-1
+EXPL "PKCS #1"
+
+# RFC 2459:
+#
+# rsaEncryption OBJECT IDENTIFIER ::= { pkcs-1 1 }
+OID 1.2.840.113549.1.1.1
+TAG rsaEncryption
+EXPL "PKCS #1 RSA Encryption"
+NAME NSS_OID_PKCS1_RSA_ENCRYPTION
+CKM CKM_RSA_PKCS
+CKK CKK_RSA
+
+# RFC 2459:
+#
+# md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
+OID 1.2.840.113549.1.1.2
+TAG md2WithRSAEncryption
+EXPL "PKCS #1 MD2 With RSA Encryption"
+NAME NSS_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION
+CKM CKM_MD2_RSA_PKCS
+
+OID 1.2.840.113549.1.1.3
+TAG md4WithRSAEncryption
+EXPL "PKCS #1 MD4 With RSA Encryption"
+NAME NSS_OID_PKCS1_MD4_WITH_RSA_ENCRYPTION
+# No CKM!
+
+# RFC 2459:
+#
+# md5WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 4 }
+OID 1.2.840.113549.1.1.4
+TAG md5WithRSAEncryption
+EXPL "PKCS #1 MD5 With RSA Encryption"
+NAME NSS_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION
+CKM CKM_MD5_RSA_PKCS
+
+# RFC 2459:
+#
+# sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 }
+OID 1.2.840.113549.1.1.5
+TAG sha1WithRSAEncryption
+EXPL "PKCS #1 SHA-1 With RSA Encryption"
+NAME NSS_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION
+CKM CKM_SHA1_RSA_PKCS
+
+OID 1.2.840.113549.1.5
+# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-5.asc
+TAG pkcs-5
+EXPL "PKCS #5"
+
+OID 1.2.840.113549.1.5.1
+TAG pbeWithMD2AndDES-CBC
+EXPL "PKCS #5 Password Based Encryption With MD2 and DES-CBC"
+NAME NSS_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC
+CKM CKM_PBE_MD2_DES_CBC
+
+OID 1.2.840.113549.1.5.3
+TAG pbeWithMD5AndDES-CBC
+EXPL "PKCS #5 Password Based Encryption With MD5 and DES-CBC"
+NAME NSS_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC
+CKM CKM_PBE_MD5_DES_CBC
+
+OID 1.2.840.113549.1.5.10
+TAG pbeWithSha1AndDES-CBC
+EXPL "PKCS #5 Password Based Encryption With SHA-1 and DES-CBC"
+NAME NSS_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC
+CKM CKM_NETSCAPE_PBE_SHA1_DES_CBC
+
+OID 1.2.840.113549.1.7
+# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-7.asc
+TAG pkcs-7
+EXPL "PKCS #7"
+NAME NSS_OID_PKCS7
+
+OID 1.2.840.113549.1.7.1
+TAG data
+EXPL "PKCS #7 Data"
+NAME NSS_OID_PKCS7_DATA
+
+OID 1.2.840.113549.1.7.2
+TAG signedData
+EXPL "PKCS #7 Signed Data"
+NAME NSS_OID_PKCS7_SIGNED_DATA
+
+OID 1.2.840.113549.1.7.3
+TAG envelopedData
+EXPL "PKCS #7 Enveloped Data"
+NAME NSS_OID_PKCS7_ENVELOPED_DATA
+
+OID 1.2.840.113549.1.7.4
+TAG signedAndEnvelopedData
+EXPL "PKCS #7 Signed and Enveloped Data"
+NAME NSS_OID_PKCS7_SIGNED_ENVELOPED_DATA
+
+OID 1.2.840.113549.1.7.5
+TAG digestedData
+EXPL "PKCS #7 Digested Data"
+NAME NSS_OID_PKCS7_DIGESTED_DATA
+
+OID 1.2.840.113549.1.7.6
+TAG encryptedData
+EXPL "PKCS #7 Encrypted Data"
+NAME NSS_OID_PKCS7_ENCRYPTED_DATA
+
+# RFC 2459:
+#
+# pkcs-9 OBJECT IDENTIFIER ::=
+# { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
+OID 1.2.840.113549.1.9
+# ftp://ftp.rsa.com/pub/pkcs/ascii/pkcs-9.asc
+TAG pkcs-9
+EXPL "PKCS #9"
+
+# RFC 2459:
+#
+# emailAddress AttributeType ::= { pkcs-9 1 }
+OID 1.2.840.113549.1.9.1
+TAG emailAddress
+EXPL "PKCS #9 Email Address"
+NAME NSS_OID_PKCS9_EMAIL_ADDRESS
+
+OID 1.2.840.113549.1.9.2
+TAG unstructuredName
+EXPL "PKCS #9 Unstructured Name"
+NAME NSS_OID_PKCS9_UNSTRUCTURED_NAME
+
+OID 1.2.840.113549.1.9.3
+TAG contentType
+EXPL "PKCS #9 Content Type"
+NAME NSS_OID_PKCS9_CONTENT_TYPE
+
+OID 1.2.840.113549.1.9.4
+TAG messageDigest
+EXPL "PKCS #9 Message Digest"
+NAME NSS_OID_PKCS9_MESSAGE_DIGEST
+
+OID 1.2.840.113549.1.9.5
+TAG signingTime
+EXPL "PKCS #9 Signing Time"
+NAME NSS_OID_PKCS9_SIGNING_TIME
+
+OID 1.2.840.113549.1.9.6
+TAG counterSignature
+EXPL "PKCS #9 Counter Signature"
+NAME NSS_OID_PKCS9_COUNTER_SIGNATURE
+
+OID 1.2.840.113549.1.9.7
+TAG challengePassword
+EXPL "PKCS #9 Challenge Password"
+NAME NSS_OID_PKCS9_CHALLENGE_PASSWORD
+
+OID 1.2.840.113549.1.9.8
+TAG unstructuredAddress
+EXPL "PKCS #9 Unstructured Address"
+NAME NSS_OID_PKCS9_UNSTRUCTURED_ADDRESS
+
+OID 1.2.840.113549.1.9.9
+TAG extendedCertificateAttributes
+EXPL "PKCS #9 Extended Certificate Attributes"
+NAME NSS_OID_PKCS9_EXTENDED_CERTIFICATE_ATTRIBUTES
+
+OID 1.2.840.113549.1.9.15
+TAG sMIMECapabilities
+EXPL "PKCS #9 S/MIME Capabilities"
+NAME NSS_OID_PKCS9_SMIME_CAPABILITIES
+
+OID 1.2.840.113549.1.9.20
+TAG friendlyName
+EXPL "PKCS #9 Friendly Name"
+NAME NSS_OID_PKCS9_FRIENDLY_NAME
+
+OID 1.2.840.113549.1.9.21
+TAG localKeyID
+EXPL "PKCS #9 Local Key ID"
+NAME NSS_OID_PKCS9_LOCAL_KEY_ID
+
+OID 1.2.840.113549.1.9.22
+TAG certTypes
+EXPL "PKCS #9 Certificate Types"
+
+OID 1.2.840.113549.1.9.22.1
+TAG x509Certificate
+EXPL "PKCS #9 Certificate Type = X.509"
+NAME NSS_OID_PKCS9_X509_CERT
+
+OID 1.2.840.113549.1.9.22.2
+TAG sdsiCertificate
+EXPL "PKCS #9 Certificate Type = SDSI"
+NAME NSS_OID_PKCS9_SDSI_CERT
+
+OID 1.2.840.113549.1.9.23
+TAG crlTypes
+EXPL "PKCS #9 Certificate Revocation List Types"
+
+OID 1.2.840.113549.1.9.23.1
+TAG x509Crl
+EXPL "PKCS #9 CRL Type = X.509"
+NAME NSS_OID_PKCS9_X509_CRL
+
+OID 1.2.840.113549.1.12
+# http://www.rsa.com/rsalabs/pubs/PKCS/html/pkcs-12.html
+# NOTE -- PKCS #12 multiple contradictory
+# documents exist. Somebody go figure out the canonical
+# OID list, keeping in mind backwards-compatability..
+TAG pkcs-12
+EXPL "PKCS #12"
+NAME NSS_OID_PKCS12
+
+OID 1.2.840.113549.1.12.1
+TAG pkcs-12PbeIds
+EXPL "PKCS #12 Password Based Encryption IDs"
+NAME NSS_OID_PKCS12_PBE_IDS
+# We called it SEC_OID_PKCS12_MODE_IDS
+
+OID 1.2.840.113549.1.12.1.1
+TAG pbeWithSHA1And128BitRC4
+EXPL "PKCS #12 Password Based Encryption With SHA-1 and 128-bit RC4"
+NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4
+CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4
+# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4
+
+OID 1.2.840.113549.1.12.1.2
+TAG pbeWithSHA1And40BitRC4
+EXPL "PKCS #12 Password Based Encryption With SHA-1 and 40-bit RC4"
+NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4
+CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4
+# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4
+
+OID 1.2.840.113549.1.12.1.3
+TAG pbeWithSHA1And3-KeyTripleDES-CBC
+EXPL "PKCS #12 Password Based Encryption With SHA-1 and 3-key Triple DES-CBC"
+NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_3_KEY_TRIPLE_DES_CBC
+CKM CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC
+# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC
+
+OID 1.2.840.113549.1.12.1.4
+TAG pbeWithSHA1And2-KeyTripleDES-CBC
+EXPL "PKCS #12 Password Based Encryption With SHA-1 and 2-key Triple DES-CBC"
+NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_2_KEY_TRIPLE_DES_CBC
+CKM CKM_PBE_SHA1_DES2_EDE_CBC
+# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC
+
+OID 1.2.840.113549.1.12.1.5
+TAG pbeWithSHA1And128BitRC2-CBC
+EXPL "PKCS #12 Password Based Encryption With SHA-1 and 128-bit RC2-CBC"
+NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC
+CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC
+# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC
+
+OID 1.2.840.113549.1.12.1.6
+TAG pbeWithSHA1And40BitRC2-CBC
+EXPL "PKCS #12 Password Based Encryption With SHA-1 and 40-bit RC2-CBC"
+NAME NSS_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC
+CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC
+# We called it SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC
+
+OID 1.2.840.113549.1.12.2
+TAG pkcs-12EspvkIds
+EXPL "PKCS #12 ESPVK IDs"
+# We called it SEC_OID_PKCS12_ESPVK_IDS
+
+OID 1.2.840.113549.1.12.2.1
+TAG pkcs8-key-shrouding
+EXPL "PKCS #12 Key Shrouding"
+# We called it SEC_OID_PKCS12_PKCS8_KEY_SHROUDING
+
+OID 1.2.840.113549.1.12.3
+TAG draft1Pkcs-12Bag-ids
+EXPL "Draft 1.0 PKCS #12 Bag IDs"
+# We called it SEC_OID_PKCS12_BAG_IDS
+
+OID 1.2.840.113549.1.12.3.1
+TAG keyBag
+EXPL "Draft 1.0 PKCS #12 Key Bag"
+# We called it SEC_OID_PKCS12_KEY_BAG_ID
+
+OID 1.2.840.113549.1.12.3.2
+TAG certAndCRLBagId
+EXPL "Draft 1.0 PKCS #12 Cert and CRL Bag ID"
+# We called it SEC_OID_PKCS12_CERT_AND_CRL_BAG_ID
+
+OID 1.2.840.113549.1.12.3.3
+TAG secretBagId
+EXPL "Draft 1.0 PKCS #12 Secret Bag ID"
+# We called it SEC_OID_PKCS12_SECRET_BAG_ID
+
+OID 1.2.840.113549.1.12.3.4
+TAG safeContentsId
+EXPL "Draft 1.0 PKCS #12 Safe Contents Bag ID"
+# We called it SEC_OID_PKCS12_SAFE_CONTENTS_ID
+
+OID 1.2.840.113549.1.12.3.5
+TAG pkcs-8ShroudedKeyBagId
+EXPL "Draft 1.0 PKCS #12 PKCS #8-shrouded Key Bag ID"
+# We called it SEC_OID_PKCS12_PKCS8_SHROUDED_KEY_BAG_ID
+
+OID 1.2.840.113549.1.12.4
+TAG pkcs-12CertBagIds
+EXPL "PKCS #12 Certificate Bag IDs"
+# We called it SEC_OID_PKCS12_CERT_BAG_IDS
+
+OID 1.2.840.113549.1.12.4.1
+TAG x509CertCRLBagId
+EXPL "PKCS #12 X.509 Certificate and CRL Bag"
+# We called it SEC_OID_PKCS12_X509_CERT_CRL_BAG
+
+OID 1.2.840.113549.1.12.4.2
+TAG SDSICertBagID
+EXPL "PKCS #12 SDSI Certificate Bag"
+# We called it SEC_OID_PKCS12_SDSI_CERT_BAG
+
+OID 1.2.840.113549.1.12.5
+TAG pkcs-12Oids
+EXPL "PKCS #12 OIDs (XXX)"
+# We called it SEC_OID_PKCS12_OIDS
+
+OID 1.2.840.113549.1.12.5.1
+TAG pkcs-12PbeIds
+EXPL "PKCS #12 OIDs PBE IDs (XXX)"
+# We called it SEC_OID_PKCS12_PBE_IDS
+
+OID 1.2.840.113549.1.12.5.1.1
+TAG pbeWithSha1And128BitRC4
+EXPL "PKCS #12 OIDs PBE with SHA-1 and 128-bit RC4 (XXX)"
+CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4
+# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4
+
+OID 1.2.840.113549.1.12.5.1.2
+TAG pbeWithSha1And40BitRC4
+EXPL "PKCS #12 OIDs PBE with SHA-1 and 40-bit RC4 (XXX)"
+CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4
+# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4
+
+OID 1.2.840.113549.1.12.5.1.3
+TAG pbeWithSha1AndTripleDES-CBC
+EXPL "PKCS #12 OIDs PBE with SHA-1 and Triple DES-CBC (XXX)"
+CKM CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC
+# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC
+
+OID 1.2.840.113549.1.12.5.1.4
+TAG pbeWithSha1And128BitRC2-CBC
+EXPL "PKCS #12 OIDs PBE with SHA-1 and 128-bit RC2-CBC (XXX)"
+CKM CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC
+# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC
+
+OID 1.2.840.113549.1.12.5.1.5
+TAG pbeWithSha1And40BitRC2-CBC
+EXPL "PKCS #12 OIDs PBE with SHA-1 and 40-bit RC2-CBC (XXX)"
+CKM CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC
+# We called it SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC
+
+OID 1.2.840.113549.1.12.5.2
+TAG pkcs-12EnvelopingIds
+EXPL "PKCS #12 OIDs Enveloping IDs (XXX)"
+# We called it SEC_OID_PKCS12_ENVELOPING_IDS
+
+OID 1.2.840.113549.1.12.5.2.1
+TAG rsaEncryptionWith128BitRC4
+EXPL "PKCS #12 OIDs Enveloping RSA Encryption with 128-bit RC4"
+# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_128_BIT_RC4
+
+OID 1.2.840.113549.1.12.5.2.2
+TAG rsaEncryptionWith40BitRC4
+EXPL "PKCS #12 OIDs Enveloping RSA Encryption with 40-bit RC4"
+# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_40_BIT_RC4
+
+OID 1.2.840.113549.1.12.5.2.3
+TAG rsaEncryptionWithTripleDES
+EXPL "PKCS #12 OIDs Enveloping RSA Encryption with Triple DES"
+# We called it SEC_OID_PKCS12_RSA_ENCRYPTION_WITH_TRIPLE_DES
+
+OID 1.2.840.113549.1.12.5.3
+TAG pkcs-12SignatureIds
+EXPL "PKCS #12 OIDs Signature IDs (XXX)"
+# We called it SEC_OID_PKCS12_SIGNATURE_IDS
+
+OID 1.2.840.113549.1.12.5.3.1
+TAG rsaSignatureWithSHA1Digest
+EXPL "PKCS #12 OIDs RSA Signature with SHA-1 Digest"
+# We called it SEC_OID_PKCS12_RSA_SIGNATURE_WITH_SHA1_DIGEST
+
+OID 1.2.840.113549.1.12.10
+TAG pkcs-12Version1
+EXPL "PKCS #12 Version 1"
+
+OID 1.2.840.113549.1.12.10.1
+TAG pkcs-12BagIds
+EXPL "PKCS #12 Bag IDs"
+
+OID 1.2.840.113549.1.12.10.1.1
+TAG keyBag
+EXPL "PKCS #12 Key Bag"
+NAME NSS_OID_PKCS12_KEY_BAG
+# We called it SEC_OID_PKCS12_V1_KEY_BAG_ID
+
+OID 1.2.840.113549.1.12.10.1.2
+TAG pkcs-8ShroudedKeyBag
+EXPL "PKCS #12 PKCS #8-shrouded Key Bag"
+NAME NSS_OID_PKCS12_PKCS8_SHROUDED_KEY_BAG
+# We called it SEC_OID_PKCS12_V1_PKCS8_SHROUDED_KEY_BAG_ID
+
+OID 1.2.840.113549.1.12.10.1.3
+TAG certBag
+EXPL "PKCS #12 Certificate Bag"
+NAME NSS_OID_PKCS12_CERT_BAG
+# We called it SEC_OID_PKCS12_V1_CERT_BAG_ID
+
+OID 1.2.840.113549.1.12.10.1.4
+TAG crlBag
+EXPL "PKCS #12 CRL Bag"
+NAME NSS_OID_PKCS12_CRL_BAG
+# We called it SEC_OID_PKCS12_V1_CRL_BAG_ID
+
+OID 1.2.840.113549.1.12.10.1.5
+TAG secretBag
+EXPL "PKCS #12 Secret Bag"
+NAME NSS_OID_PKCS12_SECRET_BAG
+# We called it SEC_OID_PKCS12_V1_SECRET_BAG_ID
+
+OID 1.2.840.113549.1.12.10.1.6
+TAG safeContentsBag
+EXPL "PKCS #12 Safe Contents Bag"
+NAME NSS_OID_PKCS12_SAFE_CONTENTS_BAG
+# We called it SEC_OID_PKCS12_V1_SAFE_CONTENTS_BAG_ID
+
+OID 1.2.840.113549.2
+TAG digest
+EXPL "RSA digest algorithm"
+
+OID 1.2.840.113549.2.2
+TAG md2
+EXPL "MD2"
+NAME NSS_OID_MD2
+CKM CKM_MD2
+
+OID 1.2.840.113549.2.4
+TAG md4
+EXPL "MD4"
+NAME NSS_OID_MD4
+# No CKM
+
+OID 1.2.840.113549.2.5
+TAG md5
+EXPL "MD5"
+NAME NSS_OID_MD5
+CKM CKM_MD5
+
+OID 1.2.840.113549.3
+TAG cipher
+EXPL "RSA cipher algorithm"
+
+OID 1.2.840.113549.3.2
+TAG rc2cbc
+EXPL "RC2-CBC"
+NAME NSS_OID_RC2_CBC
+CKM_RC2_CBC
+
+OID 1.2.840.113549.3.4
+TAG rc4
+EXPL "RC4"
+NAME NSS_OID_RC4
+CKM CKM_RC4
+
+OID 1.2.840.113549.3.7
+TAG desede3cbc
+EXPL "DES-EDE3-CBC"
+NAME NSS_OID_DES_EDE3_CBC
+CKM CKM_DES3_CBC
+
+OID 1.2.840.113549.3.9
+TAG rc5cbcpad
+EXPL "RC5-CBCPad"
+NAME NSS_OID_RC5_CBC_PAD
+CKM CKM_RC5_CBC
+
+OID 1.2.840.113556
+TAG microsoft
+EXPL "Microsoft"
+
+OID 1.2.840.113560
+TAG columbia-university
+EXPL "Columbia University"
+
+OID 1.2.840.113572
+TAG unisys
+EXPL "Unisys"
+
+OID 1.2.840.113658
+TAG xapia
+EXPL "XAPIA"
+
+OID 1.2.840.113699
+TAG wordperfect
+EXPL "WordPerfect"
+
+OID 1.3
+TAG identified-organization
+EXPL "ISO identified organizations"
+
+OID 1.3.6
+TAG us-dod
+EXPL "United States Department of Defense"
+
+OID 1.3.6.1
+TAG internet # See RFC 1065
+EXPL "The Internet"
+
+OID 1.3.6.1.1
+TAG directory
+EXPL "Internet: Directory"
+
+OID 1.3.6.1.2
+TAG management
+EXPL "Internet: Management"
+
+OID 1.3.6.1.3
+TAG experimental
+EXPL "Internet: Experimental"
+
+OID 1.3.6.1.4
+TAG private
+EXPL "Internet: Private"
+
+OID 1.3.6.1.5
+TAG security
+EXPL "Internet: Security"
+
+OID 1.3.6.1.5.5
+
+# RFC 2459:
+#
+# id-pkix OBJECT IDENTIFIER ::=
+# { iso(1) identified-organization(3) dod(6) internet(1)
+# security(5) mechanisms(5) pkix(7) }
+OID 1.3.6.1.5.5.7
+TAG id-pkix
+EXPL "Public Key Infrastructure"
+
+# RFC 2459:
+#
+# PKIX1Explicit88 {iso(1) identified-organization(3) dod(6) internet(1)
+# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit-88(1)}
+OID 1.3.6.1.5.5.7.0.1
+TAG PKIX1Explicit88
+EXPL "RFC 2459 Explicitly Tagged Module, 1988 Syntax"
+
+# RFC 2459:
+#
+# PKIX1Implicit88 {iso(1) identified-organization(3) dod(6) internet(1)
+# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-implicit-88(2)}
+OID 1.3.6.1.5.5.7.0.2
+TAG PKIXImplicit88
+EXPL "RFC 2459 Implicitly Tagged Module, 1988 Syntax"
+
+# RFC 2459:
+#
+# PKIX1Explicit93 {iso(1) identified-organization(3) dod(6) internet(1)
+# security(5) mechanisms(5) pkix(7) id-mod(0) id-pkix1-explicit-93(3)}
+OID 1.3.6.1.5.5.7.0.3
+TAG PKIXExplicit93
+EXPL "RFC 2459 Explicitly Tagged Module, 1993 Syntax"
+
+# RFC 2459:
+#
+# id-pe OBJECT IDENTIFIER ::= { id-pkix 1 }
+# -- arc for private certificate extensions
+OID 1.3.6.1.5.5.7.1
+TAG id-pe
+EXPL "PKIX Private Certificate Extensions"
+
+# RFC 2459:
+#
+# id-pe-authorityInfoAccess OBJECT IDENTIFIER ::= { id-pe 1 }
+OID 1.3.6.1.5.5.7.1.1
+TAG id-pe-authorityInfoAccess
+EXPL "Certificate Authority Information Access"
+NAME NSS_OID_X509_AUTH_INFO_ACCESS
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-qt OBJECT IDENTIFIER ::= { id-pkix 2 }
+# -- arc for policy qualifier types
+OID 1.3.6.1.5.5.7.2
+TAG id-qt
+EXPL "PKIX Policy Qualifier Types"
+
+# RFC 2459:
+#
+# id-qt-cps OBJECT IDENTIFIER ::= { id-qt 1 }
+# -- OID for CPS qualifier
+OID 1.3.6.1.5.5.7.2.1
+TAG id-qt-cps
+EXPL "PKIX CPS Pointer Qualifier"
+NAME NSS_OID_PKIX_CPS_POINTER_QUALIFIER
+
+# RFC 2459:
+#
+# id-qt-unotice OBJECT IDENTIFIER ::= { id-qt 2 }
+# -- OID for user notice qualifier
+OID 1.3.6.1.5.5.7.2.2
+TAG id-qt-unotice
+EXPL "PKIX User Notice Qualifier"
+NAME NSS_OID_PKIX_USER_NOTICE_QUALIFIER
+
+# RFC 2459:
+#
+# id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
+# -- arc for extended key purpose OIDS
+OID 1.3.6.1.5.5.7.3
+TAG id-kp
+EXPL "PKIX Key Purpose"
+
+# RFC 2459:
+#
+# id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
+OID 1.3.6.1.5.5.7.3.1
+TAG id-kp-serverAuth
+EXPL "TLS Web Server Authentication Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_SERVER_AUTH
+
+# RFC 2459:
+#
+# id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
+OID 1.3.6.1.5.5.7.3.2
+TAG id-kp-clientAuth
+EXPL "TLS Web Client Authentication Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_CLIENT_AUTH
+
+# RFC 2459:
+#
+# id-kp-codeSigning OBJECT IDENTIFIER ::= { id-kp 3 }
+OID 1.3.6.1.5.5.7.3.3
+TAG id-kp-codeSigning
+EXPL "Code Signing Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_CODE_SIGN
+
+# RFC 2459:
+#
+# id-kp-emailProtection OBJECT IDENTIFIER ::= { id-kp 4 }
+OID 1.3.6.1.5.5.7.3.4
+TAG id-kp-emailProtection
+EXPL "E-Mail Protection Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_EMAIL_PROTECTION
+
+# RFC 2459:
+#
+# id-kp-ipsecEndSystem OBJECT IDENTIFIER ::= { id-kp 5 }
+OID 1.3.6.1.5.5.7.3.5
+TAG id-kp-ipsecEndSystem
+EXPL "IPSEC End System Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_IPSEC_END_SYSTEM
+
+# RFC 2459:
+#
+# id-kp-ipsecTunnel OBJECT IDENTIFIER ::= { id-kp 6 }
+OID 1.3.6.1.5.5.7.3.6
+TAG id-kp-ipsecTunnel
+EXPL "IPSEC Tunnel Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_IPSEC_TUNNEL
+
+# RFC 2459:
+#
+# id-kp-ipsecUser OBJECT IDENTIFIER ::= { id-kp 7 }
+OID 1.3.6.1.5.5.7.3.7
+TAG id-kp-ipsecUser
+EXPL "IPSEC User Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_IPSEC_USER
+
+# RFC 2459:
+#
+# id-kp-timeStamping OBJECT IDENTIFIER ::= { id-kp 8 }
+OID 1.3.6.1.5.5.7.3.8
+TAG id-kp-timeStamping
+EXPL "Time Stamping Certificate"
+NAME NSS_OID_EXT_KEY_USAGE_TIME_STAMP
+
+OID 1.3.6.1.5.5.7.3.9
+TAG ocsp-responder
+EXPL "OCSP Responder Certificate"
+NAME NSS_OID_OCSP_RESPONDER
+
+OID 1.3.6.1.5.5.7.7
+TAG pkix-id-pkix
+
+OID 1.3.6.1.5.5.7.7.5
+TAG pkix-id-pkip
+
+OID 1.3.6.1.5.5.7.7.5.1
+TAG pkix-id-regctrl
+EXPL "CRMF Registration Control"
+
+OID 1.3.6.1.5.5.7.7.5.1.1
+TAG regtoken
+EXPL "CRMF Registration Control, Registration Token"
+NAME NSS_OID_PKIX_REGCTRL_REGTOKEN
+
+OID 1.3.6.1.5.5.7.7.5.1.2
+TAG authenticator
+EXPL "CRMF Registration Control, Registration Authenticator"
+NAME NSS_OID_PKIX_REGCTRL_AUTHENTICATOR
+
+OID 1.3.6.1.5.5.7.7.5.1.3
+TAG pkipubinfo
+EXPL "CRMF Registration Control, PKI Publication Info"
+NAME NSS_OID_PKIX_REGCTRL_PKIPUBINFO
+
+OID 1.3.6.1.5.5.7.7.5.1.4
+TAG pki-arch-options
+EXPL "CRMF Registration Control, PKI Archive Options"
+NAME NSS_OID_PKIX_REGCTRL_PKI_ARCH_OPTIONS
+
+OID 1.3.6.1.5.5.7.7.5.1.5
+TAG old-cert-id
+EXPL "CRMF Registration Control, Old Certificate ID"
+NAME NSS_OID_PKIX_REGCTRL_OLD_CERT_ID
+
+OID 1.3.6.1.5.5.7.7.5.1.6
+TAG protocol-encryption-key
+EXPL "CRMF Registration Control, Protocol Encryption Key"
+NAME NSS_OID_PKIX_REGCTRL_PROTOCOL_ENC_KEY
+
+OID 1.3.6.1.5.5.7.7.5.2
+TAG pkix-id-reginfo
+EXPL "CRMF Registration Info"
+
+OID 1.3.6.1.5.5.7.7.5.2.1
+TAG utf8-pairs
+EXPL "CRMF Registration Info, UTF8 Pairs"
+NAME NSS_OID_PKIX_REGINFO_UTF8_PAIRS
+
+OID 1.3.6.1.5.5.7.7.5.2.2
+TAG cert-request
+EXPL "CRMF Registration Info, Certificate Request"
+NAME NSS_OID_PKIX_REGINFO_CERT_REQUEST
+
+# RFC 2549:
+#
+# id-ad OBJECT IDENTIFIER ::= { id-pkix 48 }
+# -- arc for access descriptors
+OID 1.3.6.1.5.5.7.48
+TAG id-ad
+EXPL "PKIX Access Descriptors"
+
+# RFC 2549:
+#
+# id-ad-ocsp OBJECT IDENTIFIER ::= { id-ad 1 }
+OID 1.3.6.1.5.5.7.48.1
+TAG id-ad-ocsp
+EXPL "PKIX Online Certificate Status Protocol"
+NAME NSS_OID_OID_PKIX_OCSP
+
+OID 1.3.6.1.5.5.7.48.1.1
+TAG basic-response
+EXPL "OCSP Basic Response"
+NAME NSS_OID_PKIX_OCSP_BASIC_RESPONSE
+
+OID 1.3.6.1.5.5.7.48.1.2
+TAG nonce-extension
+EXPL "OCSP Nonce Extension"
+NAME NSS_OID_PKIX_OCSP_NONCE
+
+OID 1.3.6.1.5.5.7.48.1.3
+TAG response
+EXPL "OCSP Response Types Extension"
+NAME NSS_OID_PKIX_OCSP_RESPONSE
+
+OID 1.3.6.1.5.5.7.48.1.4
+TAG crl
+EXPL "OCSP CRL Reference Extension"
+NAME NSS_OID_PKIX_OCSP_CRL
+
+OID 1.3.6.1.5.5.7.48.1.5
+TAG no-check
+EXPL "OCSP No Check Extension"
+NAME NSS_OID_X509_OCSP_NO_CHECK # X509_... ?
+
+OID 1.3.6.1.5.5.7.48.1.6
+TAG archive-cutoff
+EXPL "OCSP Archive Cutoff Extension"
+NAME NSS_OID_PKIX_OCSP_ARCHIVE_CUTOFF
+
+OID 1.3.6.1.5.5.7.48.1.7
+TAG service-locator
+EXPL "OCSP Service Locator Extension"
+NAME NSS_OID_PKIX_OCSP_SERVICE_LOCATOR
+
+# RFC 2549:
+#
+# id-ad-caIssuers OBJECT IDENTIFIER ::= { id-ad 2 }
+OID 1.3.6.1.5.5.7.48.2
+TAG id-ad-caIssuers
+EXPL "Certificate Authority Issuers"
+
+OID 1.3.6.1.6
+TAG snmpv2
+EXPL "Internet: SNMPv2"
+
+OID 1.3.6.1.7
+TAG mail
+EXPL "Internet: mail"
+
+OID 1.3.6.1.7.1
+TAG mime-mhs
+EXPL "Internet: mail MIME mhs"
+
+OID 1.3.12
+TAG ecma
+EXPL "European Computers Manufacturing Association"
+
+OID 1.3.14
+TAG oiw
+EXPL "Open Systems Implementors Workshop"
+
+OID 1.3.14.3 secsig
+TAG secsig
+EXPL "Open Systems Implementors Workshop Security Special Interest Group"
+
+OID 1.3.14.3.1
+TAG oIWSECSIGAlgorithmObjectIdentifiers
+EXPL "OIW SECSIG Algorithm OIDs"
+
+OID 1.3.14.3.2
+TAG algorithm
+EXPL "OIW SECSIG Algorithm"
+
+OID 1.3.14.3.2.6
+TAG desecb
+EXPL "DES-ECB"
+NAME NSS_OID_DES_ECB
+CKM CKM_DES_ECB
+
+OID 1.3.14.3.2.7
+TAG descbc
+EXPL "DES-CBC"
+NAME NSS_OID_DES_CBC
+CKM CKM_DES_CBC
+
+OID 1.3.14.3.2.8
+TAG desofb
+EXPL "DES-OFB"
+NAME NSS_OID_DES_OFB
+# No CKM..
+
+OID 1.3.14.3.2.9
+TAG descfb
+EXPL "DES-CFB"
+NAME NSS_OID_DES_CFB
+# No CKM..
+
+OID 1.3.14.3.2.10
+TAG desmac
+EXPL "DES-MAC"
+NAME NSS_OID_DES_MAC
+CKM CKM_DES_MAC
+
+OID 1.3.14.3.2.15
+TAG isoSHAWithRSASignature
+EXPL "ISO SHA with RSA Signature"
+NAME NSS_OID_ISO_SHA_WITH_RSA_SIGNATURE
+# No CKM..
+
+OID 1.3.14.3.2.17
+TAG desede
+EXPL "DES-EDE"
+NAME NSS_OID_DES_EDE
+# No CKM..
+
+OID 1.3.14.3.2.26
+TAG sha1
+EXPL "SHA-1"
+NAME NSS_OID_SHA1
+CKM CKM_SHA_1
+
+OID 1.3.14.3.2.27
+TAG bogusDSASignatureWithSHA1Digest
+EXPL "Forgezza DSA Signature with SHA-1 Digest"
+NAME NSS_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST
+CKM CKM_DSA_SHA1
+
+OID 1.3.14.3.3
+TAG authentication-mechanism
+EXPL "OIW SECSIG Authentication Mechanisms"
+
+OID 1.3.14.3.4
+TAG security-attribute
+EXPL "OIW SECSIG Security Attributes"
+
+OID 1.3.14.3.5
+TAG document-definition
+EXPL "OIW SECSIG Document Definitions used in security"
+
+OID 1.3.14.7
+TAG directory-services-sig
+EXPL "OIW directory services sig"
+
+OID 1.3.16
+TAG ewos
+EXPL "European Workshop on Open Systems"
+
+OID 1.3.22
+TAG osf
+EXPL "Open Software Foundation"
+
+OID 1.3.23
+TAG nordunet
+EXPL "Nordunet"
+
+OID 1.3.26
+TAG nato-id-org
+EXPL "NATO identified organisation"
+
+OID 1.3.36
+TAG teletrust
+EXPL "Teletrust"
+
+OID 1.3.52
+TAG smpte
+EXPL "Society of Motion Picture and Television Engineers"
+
+OID 1.3.69
+TAG sita
+EXPL "Societe Internationale de Telecommunications Aeronautiques"
+
+OID 1.3.90
+TAG iana
+EXPL "Internet Assigned Numbers Authority"
+
+OID 1.3.101
+TAG thawte
+EXPL "Thawte"
+
+OID 2
+TAG joint-iso-ccitt
+EXPL "Joint ISO/ITU-T assignment"
+
+OID 2.0
+TAG presentation
+EXPL "Joint ISO/ITU-T Presentation"
+
+OID 2.1
+TAG asn-1
+EXPL "Abstract Syntax Notation One"
+
+OID 2.2
+TAG acse
+EXPL "Association Control"
+
+OID 2.3
+TAG rtse
+EXPL "Reliable Transfer"
+
+OID 2.4
+TAG rose
+EXPL "Remote Operations"
+
+OID 2.5
+TAG x500
+EXPL "Directory"
+
+OID 2.5.1
+TAG modules
+EXPL "X.500 modules"
+
+OID 2.5.2
+TAG service-environment
+EXPL "X.500 service environment"
+
+OID 2.5.3
+TAG application-context
+EXPL "X.500 application context"
+
+# RFC 2459:
+#
+# id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4}
+OID 2.5.4
+TAG id-at
+EXPL "X.520 attribute types"
+
+# RFC 2459:
+#
+# id-at-commonName AttributeType ::= {id-at 3}
+OID 2.5.4.3
+TAG id-at-commonName
+EXPL "X.520 Common Name"
+NAME NSS_OID_X520_COMMON_NAME
+ATTR "cn"
+
+# RFC 2459:
+#
+# id-at-surname AttributeType ::= {id-at 4}
+OID 2.5.4.4
+TAG id-at-surname
+EXPL "X.520 Surname"
+NAME NSS_OID_X520_SURNAME
+ATTR "sn"
+
+# RFC 2459:
+#
+# id-at-countryName AttributeType ::= {id-at 6}
+OID 2.5.4.6
+TAG id-at-countryName
+EXPL "X.520 Country Name"
+NAME NSS_OID_X520_COUNTRY_NAME
+ATTR "c"
+
+# RFC 2459:
+#
+# id-at-localityName AttributeType ::= {id-at 7}
+OID 2.5.4.7
+TAG id-at-localityName
+EXPL "X.520 Locality Name"
+NAME NSS_OID_X520_LOCALITY_NAME
+ATTR "l"
+
+# RFC 2459:
+#
+# id-at-stateOrProvinceName AttributeType ::= {id-at 8}
+OID 2.5.4.8
+TAG id-at-stateOrProvinceName
+EXPL "X.520 State or Province Name"
+NAME NSS_OID_X520_STATE_OR_PROVINCE_NAME
+ATTR "s"
+
+# RFC 2459:
+#
+# id-at-organizationName AttributeType ::= {id-at 10}
+OID 2.5.4.10
+TAG id-at-organizationName
+EXPL "X.520 Organization Name"
+NAME NSS_OID_X520_ORGANIZATION_NAME
+ATTR "o"
+
+# RFC 2459:
+#
+# id-at-organizationalUnitName AttributeType ::= {id-at 11}
+OID 2.5.4.11
+TAG id-at-organizationalUnitName
+EXPL "X.520 Organizational Unit Name"
+NAME NSS_OID_X520_ORGANIZATIONAL_UNIT_NAME
+ATTR "ou"
+
+# RFC 2459:
+#
+# id-at-title AttributeType ::= {id-at 12}
+OID 2.5.4.12
+TAG id-at-title
+EXPL "X.520 Title"
+NAME NSS_OID_X520_TITLE
+ATTR "title"
+
+# RFC 2459:
+#
+# id-at-name AttributeType ::= {id-at 41}
+OID 2.5.4.41
+TAG id-at-name
+EXPL "X.520 Name"
+NAME NSS_OID_X520_NAME
+ATTR "name"
+
+# RFC 2459:
+#
+# id-at-givenName AttributeType ::= {id-at 42}
+OID 2.5.4.42
+TAG id-at-givenName
+EXPL "X.520 Given Name"
+NAME NSS_OID_X520_GIVEN_NAME
+ATTR "givenName"
+
+# RFC 2459:
+#
+# id-at-initials AttributeType ::= {id-at 43}
+OID 2.5.4.43
+TAG id-at-initials
+EXPL "X.520 Initials"
+NAME NSS_OID_X520_INITIALS
+ATTR "initials"
+
+# RFC 2459:
+#
+# id-at-generationQualifier AttributeType ::= {id-at 44}
+OID 2.5.4.44
+TAG id-at-generationQualifier
+EXPL "X.520 Generation Qualifier"
+NAME NSS_OID_X520_GENERATION_QUALIFIER
+ATTR "generationQualifier"
+
+# RFC 2459:
+#
+# id-at-dnQualifier AttributeType ::= {id-at 46}
+OID 2.5.4.46
+TAG id-at-dnQualifier
+EXPL "X.520 DN Qualifier"
+NAME NSS_OID_X520_DN_QUALIFIER
+ATTR "dnQualifier"
+
+OID 2.5.5
+TAG attribute-syntax
+EXPL "X.500 attribute syntaxes"
+
+OID 2.5.6
+TAG object-classes
+EXPL "X.500 standard object classes"
+
+OID 2.5.7
+TAG attribute-set
+EXPL "X.500 attribute sets"
+
+OID 2.5.8
+TAG algorithms
+EXPL "X.500-defined algorithms"
+
+OID 2.5.8.1
+TAG encryption
+EXPL "X.500-defined encryption algorithms"
+
+OID 2.5.8.1.1
+TAG rsa
+EXPL "RSA Encryption Algorithm"
+NAME NSS_OID_X500_RSA_ENCRYPTION
+CKM CKM_RSA_X_509
+
+OID 2.5.9
+TAG abstract-syntax
+EXPL "X.500 abstract syntaxes"
+
+OID 2.5.12
+TAG operational-attribute
+EXPL "DSA Operational Attributes"
+
+OID 2.5.13
+TAG matching-rule
+EXPL "Matching Rule"
+
+OID 2.5.14
+TAG knowledge-matching-rule
+EXPL "X.500 knowledge Matching Rules"
+
+OID 2.5.15
+TAG name-form
+EXPL "X.500 name forms"
+
+OID 2.5.16
+TAG group
+EXPL "X.500 groups"
+
+OID 2.5.17
+TAG subentry
+EXPL "X.500 subentry"
+
+OID 2.5.18
+TAG operational-attribute-type
+EXPL "X.500 operational attribute type"
+
+OID 2.5.19
+TAG operational-binding
+EXPL "X.500 operational binding"
+
+OID 2.5.20
+TAG schema-object-class
+EXPL "X.500 schema Object class"
+
+OID 2.5.21
+TAG schema-operational-attribute
+EXPL "X.500 schema operational attributes"
+
+OID 2.5.23
+TAG administrative-role
+EXPL "X.500 administrative roles"
+
+OID 2.5.24
+TAG access-control-attribute
+EXPL "X.500 access control attribute"
+
+OID 2.5.25
+TAG ros
+EXPL "X.500 ros object"
+
+OID 2.5.26
+TAG contract
+EXPL "X.500 contract"
+
+OID 2.5.27
+TAG package
+EXPL "X.500 package"
+
+OID 2.5.28
+TAG access-control-schema
+EXPL "X.500 access control schema"
+
+# RFC 2459:
+#
+# id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29}
+OID 2.5.29
+TAG id-ce
+EXPL "X.500 Certificate Extension"
+
+OID 2.5.29.5
+TAG subject-directory-attributes
+EXPL "Certificate Subject Directory Attributes"
+NAME NSS_OID_X509_SUBJECT_DIRECTORY_ATTR
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-subjectDirectoryAttributes OBJECT IDENTIFIER ::= { id-ce 9 }
+OID 2.5.29.9
+TAG id-ce-subjectDirectoryAttributes
+EXPL "Certificate Subject Directory Attributes"
+NAME NSS_OID_X509_SUBJECT_DIRECTORY_ATTRIBUTES
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-subjectKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 14 }
+OID 2.5.29.14
+TAG id-ce-subjectKeyIdentifier
+EXPL "Certificate Subject Key ID"
+NAME NSS_OID_X509_SUBJECT_KEY_ID
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 }
+OID 2.5.29.15
+TAG id-ce-keyUsage
+EXPL "Certificate Key Usage"
+NAME NSS_OID_X509_KEY_USAGE
+CERT_EXTENSION SUPPORTED
+# We called it PKCS12_KEY_USAGE
+
+# RFC 2459:
+#
+# id-ce-privateKeyUsagePeriod OBJECT IDENTIFIER ::= { id-ce 16 }
+OID 2.5.29.16
+TAG id-ce-privateKeyUsagePeriod
+EXPL "Certificate Private Key Usage Period"
+NAME NSS_OID_X509_PRIVATE_KEY_USAGE_PERIOD
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 }
+OID 2.5.29.17
+TAG id-ce-subjectAltName
+EXPL "Certificate Subject Alternate Name"
+NAME NSS_OID_X509_SUBJECT_ALT_NAME
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-issuerAltName OBJECT IDENTIFIER ::= { id-ce 18 }
+OID 2.5.29.18
+TAG id-ce-issuerAltName
+EXPL "Certificate Issuer Alternate Name"
+NAME NSS_OID_X509_ISSUER_ALT_NAME
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 }
+OID 2.5.29.19
+TAG id-ce-basicConstraints
+EXPL "Certificate Basic Constraints"
+NAME NSS_OID_X509_BASIC_CONSTRAINTS
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-cRLNumber OBJECT IDENTIFIER ::= { id-ce 20 }
+OID 2.5.29.20
+TAG id-ce-cRLNumber
+EXPL "CRL Number"
+NAME NSS_OID_X509_CRL_NUMBER
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-cRLReasons OBJECT IDENTIFIER ::= { id-ce 21 }
+OID 2.5.29.21
+TAG id-ce-cRLReasons
+EXPL "CRL Reason Code"
+NAME NSS_OID_X509_REASON_CODE
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-holdInstructionCode OBJECT IDENTIFIER ::= { id-ce 23 }
+OID 2.5.29.23
+TAG id-ce-holdInstructionCode
+EXPL "Hold Instruction Code"
+NAME NSS_OID_X509_HOLD_INSTRUCTION_CODE
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-invalidityDate OBJECT IDENTIFIER ::= { id-ce 24 }
+OID 2.5.29.24
+TAG id-ce-invalidityDate
+EXPL "Invalid Date"
+NAME NSS_OID_X509_INVALID_DATE
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-deltaCRLIndicator OBJECT IDENTIFIER ::= { id-ce 27 }
+OID 2.5.29.27
+TAG id-ce-deltaCRLIndicator
+EXPL "Delta CRL Indicator"
+NAME NSS_OID_X509_DELTA_CRL_INDICATOR
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-issuingDistributionPoint OBJECT IDENTIFIER ::= { id-ce 28 }
+OID 2.5.29.28
+TAG id-ce-issuingDistributionPoint
+EXPL "Issuing Distribution Point"
+NAME NSS_OID_X509_ISSUING_DISTRIBUTION_POINT
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-certificateIssuer OBJECT IDENTIFIER ::= { id-ce 29 }
+OID 2.5.29.29
+TAG id-ce-certificateIssuer
+EXPL "Certificate Issuer"
+NAME NSS_OID_X509_CERTIFICATE_ISSUER
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-nameConstraints OBJECT IDENTIFIER ::= { id-ce 30 }
+OID 2.5.29.30
+TAG id-ce-nameConstraints
+EXPL "Certificate Name Constraints"
+NAME NSS_OID_X509_NAME_CONSTRAINTS
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-cRLDistributionPoints OBJECT IDENTIFIER ::= {id-ce 31}
+OID 2.5.29.31
+TAG id-ce-cRLDistributionPoints
+EXPL "CRL Distribution Points"
+NAME NSS_OID_X509_CRL_DIST_POINTS
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 }
+OID 2.5.29.32
+TAG id-ce-certificatePolicies
+EXPL "Certificate Policies"
+NAME NSS_OID_X509_CERTIFICATE_POLICIES
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-policyMappings OBJECT IDENTIFIER ::= { id-ce 33 }
+OID 2.5.29.33
+TAG id-ce-policyMappings
+EXPL "Certificate Policy Mappings"
+NAME NSS_OID_X509_POLICY_MAPPINGS
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.5.29.34
+TAG policy-constraints
+EXPL "Certificate Policy Constraints (old)"
+CERT_EXTENSION UNSUPPORTED
+
+# RFC 2459:
+#
+# id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 }
+OID 2.5.29.35
+TAG id-ce-authorityKeyIdentifier
+EXPL "Certificate Authority Key Identifier"
+NAME NSS_OID_X509_AUTH_KEY_ID
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-policyConstraints OBJECT IDENTIFIER ::= { id-ce 36 }
+OID 2.5.29.36
+TAG id-ce-policyConstraints
+EXPL "Certificate Policy Constraints"
+NAME NSS_OID_X509_POLICY_CONSTRAINTS
+CERT_EXTENSION SUPPORTED
+
+# RFC 2459:
+#
+# id-ce-extKeyUsage OBJECT IDENTIFIER ::= {id-ce 37}
+OID 2.5.29.37
+TAG id-ce-extKeyUsage
+EXPL "Extended Key Usage"
+NAME NSS_OID_X509_EXT_KEY_USAGE
+CERT_EXTENSION SUPPORTED
+
+OID 2.5.30
+TAG id-mgt
+EXPL "X.500 Management Object"
+
+OID 2.6
+TAG x400
+EXPL "X.400 MHS"
+
+OID 2.7
+TAG ccr
+EXPL "Committment, Concurrency and Recovery"
+
+OID 2.8
+TAG oda
+EXPL "Office Document Architecture"
+
+OID 2.9
+TAG osi-management
+EXPL "OSI management"
+
+OID 2.10
+TAG tp
+EXPL "Transaction Processing"
+
+OID 2.11
+TAG dor
+EXPL "Distinguished Object Reference"
+
+OID 2.12
+TAG rdt
+EXPL "Referenced Data Transfer"
+
+OID 2.13
+TAG nlm
+EXPL "Network Layer Management"
+
+OID 2.14
+TAG tlm
+EXPL "Transport Layer Management"
+
+OID 2.15
+TAG llm
+EXPL "Link Layer Management"
+
+OID 2.16
+TAG country
+EXPL "Country Assignments"
+
+OID 2.16.124
+TAG canada
+EXPL "Canada"
+
+OID 2.16.158
+TAG taiwan
+EXPL "Taiwan"
+
+OID 2.16.578
+TAG norway
+EXPL "Norway"
+
+OID 2.16.756
+TAG switzerland
+EXPL "Switzerland"
+
+OID 2.16.840
+TAG us
+EXPL "United States"
+
+OID 2.16.840.1
+TAG us-company
+EXPL "United States Company"
+
+OID 2.16.840.1.101
+TAG us-government
+EXPL "United States Government (1.101)"
+
+OID 2.16.840.1.101.2
+TAG us-dod
+EXPL "United States Department of Defense"
+
+OID 2.16.840.1.101.2.1
+TAG id-infosec
+EXPL "US DOD Infosec"
+
+OID 2.16.840.1.101.2.1.0
+TAG id-modules
+EXPL "US DOD Infosec modules"
+
+OID 2.16.840.1.101.2.1.1
+TAG id-algorithms
+EXPL "US DOD Infosec algorithms (MISSI)"
+
+OID 2.16.840.1.101.2.1.1.2
+TAG old-dss
+EXPL "MISSI DSS Algorithm (Old)"
+NAME NSS_OID_MISSI_DSS_OLD
+
+# This is labeled as "### mwelch temporary"
+# Is it official?? XXX fgmr
+OID 2.16.840.1.101.2.1.1.4
+TAG skipjack-cbc-64
+EXPL "Skipjack CBC64"
+NAME NSS_OID_FORTEZZA_SKIPJACK
+CKM CKM_SKIPJACK_CBC64
+
+OID 2.16.840.1.101.2.1.1.10
+TAG kea
+EXPL "MISSI KEA Algorithm"
+NAME NSS_OID_MISSI_KEA
+
+OID 2.16.840.1.101.2.1.1.12
+TAG old-kea-dss
+EXPL "MISSI KEA and DSS Algorithm (Old)"
+NAME NSS_OID_MISSI_KEA_DSS_OLD
+
+OID 2.16.840.1.101.2.1.1.19
+TAG dss
+EXPL "MISSI DSS Algorithm"
+NAME NSS_OID_MISSI_DSS
+
+OID 2.16.840.1.101.2.1.1.20
+TAG kea-dss
+EXPL "MISSI KEA and DSS Algorithm"
+NAME NSS_OID_MISSI_KEA_DSS
+
+OID 2.16.840.1.101.2.1.1.22
+TAG alt-kea
+EXPL "MISSI Alternate KEA Algorithm"
+NAME NSS_OID_MISSI_ALT_KEY
+
+OID 2.16.840.1.101.2.1.2
+TAG id-formats
+EXPL "US DOD Infosec formats"
+
+OID 2.16.840.1.101.2.1.3
+TAG id-policy
+EXPL "US DOD Infosec policy"
+
+OID 2.16.840.1.101.2.1.4
+TAG id-object-classes
+EXPL "US DOD Infosec object classes"
+
+OID 2.16.840.1.101.2.1.5
+TAG id-attributes
+EXPL "US DOD Infosec attributes"
+
+OID 2.16.840.1.101.2.1.6
+TAG id-attribute-syntax
+EXPL "US DOD Infosec attribute syntax"
+
+OID 2.16.840.1.113730
+# The Netscape OID space
+TAG netscape
+EXPL "Netscape Communications Corp."
+
+OID 2.16.840.1.113730.1
+TAG cert-ext
+EXPL "Netscape Cert Extensions"
+
+OID 2.16.840.1.113730.1.1
+TAG cert-type
+EXPL "Certificate Type"
+NAME NSS_OID_NS_CERT_EXT_CERT_TYPE
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.2
+TAG base-url
+EXPL "Certificate Extension Base URL"
+NAME NSS_OID_NS_CERT_EXT_BASE_URL
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.3
+TAG revocation-url
+EXPL "Certificate Revocation URL"
+NAME NSS_OID_NS_CERT_EXT_REVOCATION_URL
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.4
+TAG ca-revocation-url
+EXPL "Certificate Authority Revocation URL"
+NAME NSS_OID_NS_CERT_EXT_CA_REVOCATION_URL
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.5
+TAG ca-crl-download-url
+EXPL "Certificate Authority CRL Download URL"
+NAME NSS_OID_NS_CERT_EXT_CA_CRL_URL
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.1.113730.1.6
+TAG ca-cert-url
+EXPL "Certificate Authority Certificate Download URL"
+NAME NSS_OID_NS_CERT_EXT_CA_CERT_URL
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.1.113730.1.7
+TAG renewal-url
+EXPL "Certificate Renewal URL"
+NAME NSS_OID_NS_CERT_EXT_CERT_RENEWAL_URL
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.8
+TAG ca-policy-url
+EXPL "Certificate Authority Policy URL"
+NAME NSS_OID_NS_CERT_EXT_CA_POLICY_URL
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.9
+TAG homepage-url
+EXPL "Certificate Homepage URL"
+NAME NSS_OID_NS_CERT_EXT_HOMEPAGE_URL
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.1.113730.1.10
+TAG entity-logo
+EXPL "Certificate Entity Logo"
+NAME NSS_OID_NS_CERT_EXT_ENTITY_LOGO
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.1.113730.1.11
+TAG user-picture
+EXPL "Certificate User Picture"
+NAME NSS_OID_NS_CERT_EXT_USER_PICTURE
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.1.113730.1.12
+TAG ssl-server-name
+EXPL "Certificate SSL Server Name"
+NAME NSS_OID_NS_CERT_EXT_SSL_SERVER_NAME
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.13
+TAG comment
+EXPL "Certificate Comment"
+NAME NSS_OID_NS_CERT_EXT_COMMENT
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.1.14
+TAG thayes
+EXPL ""
+NAME NSS_OID_NS_CERT_EXT_THAYES
+CERT_EXTENSION SUPPORTED
+
+OID 2.16.840.1.113730.2
+TAG data-type
+EXPL "Netscape Data Types"
+
+OID 2.16.840.1.113730.2.1
+TAG gif
+EXPL "image/gif"
+NAME NSS_OID_NS_TYPE_GIF
+
+OID 2.16.840.1.113730.2.2
+TAG jpeg
+EXPL "image/jpeg"
+NAME NSS_OID_NS_TYPE_JPEG
+
+OID 2.16.840.1.113730.2.3
+TAG url
+EXPL "URL"
+NAME NSS_OID_NS_TYPE_URL
+
+OID 2.16.840.1.113730.2.4
+TAG html
+EXPL "text/html"
+NAME NSS_OID_NS_TYPE_HTML
+
+OID 2.16.840.1.113730.2.5
+TAG cert-sequence
+EXPL "Certificate Sequence"
+NAME NSS_OID_NS_TYPE_CERT_SEQUENCE
+
+OID 2.16.840.1.113730.3
+# The Netscape Directory OID space
+TAG directory
+EXPL "Netscape Directory"
+
+OID 2.16.840.1.113730.4
+TAG policy
+EXPL "Netscape Policy Type OIDs"
+
+OID 2.16.840.1.113730.4.1
+TAG export-approved
+EXPL "Strong Crypto Export Approved"
+NAME NSS_OID_NS_KEY_USAGE_GOVT_APPROVED
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.1.113730.5
+TAG cert-server
+EXPL "Netscape Certificate Server"
+
+OID 2.16.840.1.113730.5.1
+
+OID 2.16.840.1.113730.5.1.1
+TAG recovery-request
+EXPL "Netscape Cert Server Recovery Request"
+NAME NSS_OID_NETSCAPE_RECOVERY_REQUEST
+
+OID 2.16.840.1.113730.6
+TAG algs
+EXPL "Netscape algorithm OIDs"
+
+OID 2.16.840.1.113730.6.1
+TAG smime-kea
+EXPL "Netscape S/MIME KEA"
+NAME NSS_OID_NETSCAPE_SMIME_KEA
+
+OID 2.16.840.1.113730.7
+TAG name-components
+EXPL "Netscape Name Components"
+
+OID 2.16.840.1.113730.7.1
+TAG nickname
+EXPL "Netscape Nickname"
+NAME NSS_OID_NETSCAPE_NICKNAME
+
+OID 2.16.840.1.113733
+TAG verisign
+EXPL "Verisign"
+
+OID 2.16.840.1.113733.1
+
+OID 2.16.840.1.113733.1.7
+
+OID 2.16.840.1.113733.1.7.1
+
+OID 2.16.840.1.113733.1.7.1.1
+TAG verisign-user-notices
+EXPL "Verisign User Notices"
+NAME NSS_OID_VERISIGN_USER_NOTICES
+
+OID 2.16.840.101
+TAG us-government
+EXPL "US Government (101)"
+
+OID 2.16.840.102
+TAG us-government2
+EXPL "US Government (102)"
+
+OID 2.16.840.11370
+TAG old-netscape
+EXPL "Netscape Communications Corp. (Old)"
+
+OID 2.16.840.11370.1
+TAG ns-cert-ext
+EXPL "Netscape Cert Extensions (Old NS)"
+
+OID 2.16.840.11370.1.1
+TAG netscape-ok
+EXPL "Netscape says this cert is ok (Old NS)"
+NAME NSS_OID_NS_CERT_EXT_NETSCAPE_OK
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.11370.1.2
+TAG issuer-logo
+EXPL "Certificate Issuer Logo (Old NS)"
+NAME NSS_OID_NS_CERT_EXT_ISSUER_LOGO
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.11370.1.3
+TAG subject-logo
+EXPL "Certificate Subject Logo (Old NS)"
+NAME NSS_OID_NS_CERT_EXT_SUBJECT_LOGO
+CERT_EXTENSION UNSUPPORTED
+
+OID 2.16.840.11370.2
+TAG ns-file-type
+EXPL "Netscape File Type"
+
+OID 2.16.840.11370.3
+TAG ns-image-type
+EXPL "Netscape Image Type"
+
+OID 2.17
+TAG registration-procedures
+EXPL "Registration procedures"
+
+OID 2.18
+TAG physical-layer-management
+EXPL "Physical layer Management"
+
+OID 2.19
+TAG mheg
+EXPL "MHEG"
+
+OID 2.20
+TAG guls
+EXPL "Generic Upper Layer Security"
+
+OID 2.21
+TAG tls
+EXPL "Transport Layer Security Protocol"
+
+OID 2.22
+TAG nls
+EXPL "Network Layer Security Protocol"
+
+OID 2.23
+TAG organization
+EXPL "International organizations"
diff --git a/security/nss/lib/pki1/pki1.h b/security/nss/lib/pki1/pki1.h
new file mode 100644
index 000000000..62049efcc
--- /dev/null
+++ b/security/nss/lib/pki1/pki1.h
@@ -0,0 +1,3032 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef PKI1_H
+#define PKI1_H
+
+#ifdef DEBUG
+static const char PKI1_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * pki1.h
+ *
+ * This file contains the prototypes to the non-public NSS routines
+ * relating to the PKIX part-1 objects.
+ */
+
+#ifndef PKI1T_H
+#include "pki1t.h"
+#endif /* PKI1T_H */
+
+#ifndef NSSPKI1_H
+#include "nsspki1.h"
+#endif /* NSSPKI1_H */
+
+PR_BEGIN_EXTERN_C
+
+/* fgmr 19990505 moved these here from oiddata.h */
+extern const nssAttributeTypeAliasTable nss_attribute_type_aliases[];
+extern const PRUint32 nss_attribute_type_alias_count;
+
+/*
+ * NSSOID
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssOID_CreateFromBER -- constructor
+ * nssOID_CreateFromUTF8 -- constructor
+ * (there is no explicit destructor)
+ *
+ * nssOID_GetDEREncoding
+ * nssOID_GetUTF8Encoding
+ *
+ * In debug builds, the following non-public calls are also available:
+ *
+ * nssOID_verifyPointer
+ * nssOID_getExplanation
+ * nssOID_getTaggedUTF8
+ */
+
+/*
+ * nssOID_CreateFromBER
+ *
+ * This routine creates an NSSOID by decoding a BER- or DER-encoded
+ * OID. It may return NSS_OID_UNKNOWN upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+nssOID_CreateFromBER
+(
+ NSSBER *berOid
+);
+
+extern const NSSError NSS_ERROR_INVALID_BER;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * nssOID_CreateFromUTF8
+ *
+ * This routine creates an NSSOID by decoding a UTF8 string
+ * representation of an OID in dotted-number format. The string may
+ * optionally begin with an octothorpe. It may return NSS_OID_UNKNOWN
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An NSSOID upon success
+ */
+
+NSS_EXTERN NSSOID *
+nssOID_CreateFromUTF8
+(
+ NSSUTF8 *stringOid
+);
+
+extern const NSSError NSS_ERROR_INVALID_UTF8;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+
+/*
+ * nssOID_GetDEREncoding
+ *
+ * This routine returns the DER encoding of the specified NSSOID.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return return null upon error, in
+ * which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSOID
+ */
+
+NSS_EXTERN NSSDER *
+nssOID_GetDEREncoding
+(
+ const NSSOID *oid,
+ NSSDER *rvOpt,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssOID_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing the dotted-number
+ * encoding of the specified NSSOID. If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return null upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the dotted-digit encoding of
+ * this NSSOID
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssOID_GetUTF8Encoding
+(
+ const NSSOID *oid,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssOID_verifyPointer
+ *
+ * This method is only present in debug builds.
+ *
+ * If the specified pointer is a valid poitner to an NSSOID object,
+ * this routine will return PR_SUCCESS. Otherwise, it will put an
+ * error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ *
+ * Return value:
+ * PR_SUCCESS if the pointer is valid
+ * PR_FAILURE if it isn't
+ */
+
+#ifdef DEBUG
+NSS_EXTERN PRStatus
+nssOID_verifyPointer
+(
+ const NSSOID *oid
+);
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+#endif /* DEBUG */
+
+/*
+ * nssOID_getExplanation
+ *
+ * This method is only present in debug builds.
+ *
+ * This routine will return a static pointer to a UTF8-encoded string
+ * describing (in English) the specified OID. The memory pointed to
+ * by the return value is not owned by the caller, and should not be
+ * freed or modified. Note that explanations are only provided for
+ * the OIDs built into the NSS library; there is no way to specify an
+ * explanation for dynamically created OIDs. This routine is intended
+ * only for use in debugging tools such as "derdump." This routine
+ * may return null upon error, in which case it will have placed an
+ * error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSOID
+ *
+ * Return value:
+ * NULL upon error
+ * A static pointer to a readonly, non-caller-owned UTF8-encoded
+ * string explaining the specified OID.
+ */
+
+#ifdef DEBUG
+NSS_EXTERN const char *
+nssOID_getExplanation
+(
+ NSSOID *oid
+);
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+#endif /* DEBUG */
+
+/*
+ * nssOID_getTaggedUTF8
+ *
+ * This method is only present in debug builds.
+ *
+ * This routine will return a pointer to a caller-owned UTF8-encoded
+ * string containing a tagged encoding of the specified OID. Note
+ * that OID (component) tags are only provided for the OIDs built
+ * into the NSS library; there is no way to specify tags for
+ * dynamically created OIDs. This routine is intended for use in
+ * debugging tools such as "derdump." If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the tagged encoding of
+ * this NSSOID
+ */
+
+#ifdef DEBUG
+NSS_EXTERN NSSUTF8 *
+nssOID_getTaggedUTF8
+(
+ NSSOID *oid,
+ NSSArena *arenaOpt
+);
+
+extern const NSSError NSS_ERROR_INVALID_NSSOID;
+extern const NSSError NSS_ERROR_NO_MEMORY;
+#endif /* DEBUG */
+
+/*
+ * NSSATAV
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssATAV_CreateFromBER -- constructor
+ * nssATAV_CreateFromUTF8 -- constructor
+ * nssATAV_Create -- constructor
+ *
+ * nssATAV_Destroy
+ * nssATAV_GetDEREncoding
+ * nssATAV_GetUTF8Encoding
+ * nssATAV_GetType
+ * nssATAV_GetValue
+ * nssATAV_Compare
+ * nssATAV_Duplicate
+ *
+ * In debug builds, the following non-public call is also available:
+ *
+ * nssATAV_verifyPointer
+ */
+
+/*
+ * nssATAV_CreateFromBER
+ *
+ * This routine creates an NSSATAV by decoding a BER- or DER-encoded
+ * ATAV. If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_EXTERN NSSATAV *
+nssATAV_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ const NSSBER *berATAV
+);
+
+/*
+ * nssATAV_CreateFromUTF8
+ *
+ * This routine creates an NSSATAV by decoding a UTF8 string in the
+ * "equals" format, e.g., "c=US." If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_EXTERN NSSATAV *
+nssATAV_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ const NSSUTF8 *stringATAV
+);
+
+/*
+ * nssATAV_Create
+ *
+ * This routine creates an NSSATAV from the specified NSSOID and the
+ * specified data. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap.If the specified data length is zero,
+ * the data is assumed to be terminated by first zero byte; this allows
+ * UTF8 strings to be easily specified. This routine may return NULL
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ARENA
+ * NSS_ERROR_INVALID_NSSOID
+ * NSS_ERROR_INVALID_POINTER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSATAV upon success
+ */
+
+NSS_EXTERN NSSATAV *
+nssATAV_Create
+(
+ NSSArena *arenaOpt,
+ const NSSOID *oid,
+ const void *data,
+ PRUint32 length
+);
+
+/*
+ * nssATAV_Destroy
+ *
+ * This routine will destroy an ATAV object. It should eventually be
+ * called on all ATAVs created without an arena. While it is not
+ * necessary to call it on ATAVs created within an arena, it is not an
+ * error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * set an error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+nssATAV_Destroy
+(
+ NSSATAV *atav
+);
+
+/*
+ * nssATAV_GetDEREncoding
+ *
+ * This routine will DER-encode an ATAV object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSATAV
+ */
+
+NSS_EXTERN NSSDER *
+nssATAV_GetDEREncoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssATAV_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the ATAV in "equals" notation (e.g., "o=Acme").
+ * If the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string containing the "equals" encoding of the
+ * ATAV
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssATAV_GetUTF8Encoding
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssATAV_GetType
+ *
+ * This routine returns the NSSOID corresponding to the attribute type
+ * in the specified ATAV. This routine may return NSS_OID_UNKNOWN
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NSS_OID_UNKNOWN upon error
+ * An element of enum NSSOIDenum upon success
+ */
+
+NSS_EXTERN const NSSOID *
+nssATAV_GetType
+(
+ NSSATAV *atav
+);
+
+/*
+ * nssATAV_GetValue
+ *
+ * This routine returns a string containing the attribute value
+ * in the specified ATAV. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error upon the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSItem containing the attribute value.
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssATAV_GetValue
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssATAV_Compare
+ *
+ * This routine compares two ATAVs for equality. For two ATAVs to be
+ * equal, the attribute types must be the same, and the attribute
+ * values must have equal length and contents. The result of the
+ * comparison will be stored at the location pointed to by the "equalp"
+ * variable, which must point to a valid PRBool. This routine may
+ * return PR_FAILURE upon error, in which case it will have set an
+ * error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+nssATAV_Compare
+(
+ NSSATAV *atav1,
+ NSSATAV *atav2,
+ PRBool *equalp
+);
+
+/*
+ * nssATAV_Duplicate
+ *
+ * This routine duplicates the specified ATAV. If the optional arena
+ * argument is non-null, the memory required will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * placed an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL on error
+ * A pointer to a new ATAV
+ */
+
+NSS_EXTERN NSSATAV *
+nssATAV_Duplicate
+(
+ NSSATAV *atav,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssATAV_verifyPointer
+ *
+ * This method is only present in debug builds.
+ *
+ * If the specified pointer is a valid pointer to an NSSATAV object,
+ * this routine will return PR_SUCCESS. Otherwise, it will put an
+ * error on the error stack and return PR_FAILRUE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NSSATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS if the pointer is valid
+ * PR_FAILURE if it isn't
+ */
+
+#ifdef DEBUG
+NSS_EXTERN PRStatus
+nssATAV_verifyPointer
+(
+ NSSATAV *atav
+);
+#endif /* DEBUG */
+
+/*
+ * NSSRDN
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssRDN_CreateFromBER -- constructor
+ * nssRDN_CreateFromUTF8 -- constructor
+ * nssRDN_Create -- constructor
+ * nssRDN_CreateSimple -- constructor
+ *
+ * nssRDN_Destroy
+ * nssRDN_GetDEREncoding
+ * nssRDN_GetUTF8Encoding
+ * nssRDN_AddATAV
+ * nssRDN_GetATAVCount
+ * nssRDN_GetATAV
+ * nssRDN_GetSimpleATAV
+ * nssRDN_Compare
+ * nssRDN_Duplicate
+ */
+
+/*
+ * nssRDN_CreateFromBER
+ *
+ * This routine creates an NSSRDN by decoding a BER- or DER-encoded
+ * RDN. If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+nssRDN_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berRDN
+);
+
+/*
+ * nssRDN_CreateFromUTF8
+ *
+ * This routine creates an NSSRDN by decoding an UTF8 string
+ * consisting of either a single ATAV in the "equals" format, e.g.,
+ * "uid=smith," or one or more such ATAVs in parentheses, e.g.,
+ * "(sn=Smith,ou=Sales)." If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error on the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+nssRDN_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringRDN
+);
+
+/*
+ * nssRDN_Create
+ *
+ * This routine creates an NSSRDN from one or more NSSATAVs. The
+ * final argument to this routine must be NULL. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+nssRDN_Create
+(
+ NSSArena *arenaOpt,
+ NSSATAV *atav1,
+ ...
+);
+
+/*
+ * nssRDN_CreateSimple
+ *
+ * This routine creates a simple NSSRDN from a single NSSATAV. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_ATAV
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDN upon success
+ */
+
+NSS_EXTERN NSSRDN *
+nssRDN_CreateSimple
+(
+ NSSArena *arenaOpt,
+ NSSATAV *atav
+);
+
+/*
+ * nssRDN_Destroy
+ *
+ * This routine will destroy an RDN object. It should eventually be
+ * called on all RDNs created without an arena. While it is not
+ * necessary to call it on RDNs created within an arena, it is not an
+ * error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * set an error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ *
+ * Return value:
+ * PR_FAILURE upon failure
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+nssRDN_Destroy
+(
+ NSSRDN *rdn
+);
+
+/*
+ * nssRDN_GetDEREncoding
+ *
+ * This routine will DER-encode an RDN object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSRDN
+ */
+
+NSS_EXTERN NSSDER *
+nssRDN_GetDEREncoding
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssRDN_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the RDN. A simple (one-ATAV) RDN will be simply
+ * the string representation of that ATAV; a non-simple RDN will be in
+ * parenthesised form. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * null upon error, in which case it will have set an error on the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssRDN_GetUTF8Encoding
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssRDN_AddATAV
+ *
+ * This routine adds an ATAV to the set of ATAVs in the specified RDN.
+ * Remember that RDNs consist of an unordered set of ATAVs. If the
+ * RDN was created with a non-null arena argument, that same arena
+ * will be used for any additional required memory. If the RDN was
+ * created with a NULL arena argument, any additional memory will
+ * be obtained from the heap. This routine returns a PRStatus value;
+ * it will return PR_SUCCESS upon success, and upon failure it will
+ * set an error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_INVALID_ATAV
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS upon success
+ * PR_FAILURE upon failure
+ */
+
+NSS_EXTERN PRStatus
+nssRDN_AddATAV
+(
+ NSSRDN *rdn,
+ NSSATAV *atav
+);
+
+/*
+ * nssRDN_GetATAVCount
+ *
+ * This routine returns the cardinality of the set of ATAVs within
+ * the specified RDN. This routine may return 0 upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ *
+ * Return value:
+ * 0 upon error
+ * A positive number upon success
+ */
+
+NSS_EXTERN PRUint32
+nssRDN_GetATAVCount
+(
+ NSSRDN *rdn
+);
+
+/*
+ * nssRDN_GetATAV
+ *
+ * This routine returns a pointer to an ATAV that is a member of
+ * the set of ATAVs within the specified RDN. While the set of
+ * ATAVs within an RDN is unordered, this routine will return
+ * distinct values for distinct values of 'i' as long as the RDN
+ * is not changed in any way. The RDN may be changed by calling
+ * NSSRDN_AddATAV. The value of the variable 'i' is on the range
+ * [0,c) where c is the cardinality returned from NSSRDN_GetATAVCount.
+ * The caller owns the ATAV the pointer to which is returned. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error upon the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_VALUE_OUT_OF_RANGE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSATAV
+ */
+
+NSS_EXTERN NSSATAV *
+nssRDN_GetATAV
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt,
+ PRUint32 i
+);
+
+/*
+ * nssRDN_GetSimpleATAV
+ *
+ * Most RDNs are actually very simple, with a single ATAV. This
+ * routine will return the single ATAV from such an RDN. The caller
+ * owns the ATAV the pointer to which is returned. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, including the case where
+ * the set of ATAVs in the RDN is nonsingular. Upon error, this
+ * routine will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_RDN_NOT_SIMPLE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSATAV
+ */
+
+NSS_EXTERN NSSATAV *
+nssRDN_GetSimpleATAV
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssRDN_Compare
+ *
+ * This routine compares two RDNs for equality. For two RDNs to be
+ * equal, they must have the same number of ATAVs, and every ATAV in
+ * one must be equal to an ATAV in the other. (Note that the sets
+ * of ATAVs are unordered.) The result of the comparison will be
+ * stored at the location pointed to by the "equalp" variable, which
+ * must point to a valid PRBool. This routine may return PR_FAILURE
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+nssRDN_Compare
+(
+ NSSRDN *rdn1,
+ NSSRDN *rdn2,
+ PRBool *equalp
+);
+
+/*
+ * nssRDN_Duplicate
+ *
+ * This routine duplicates the specified RDN. If the optional arena
+ * argument is non-null, the memory required will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * placed an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL on error
+ * A pointer to a new RDN
+ */
+
+NSS_EXTERN NSSRDN *
+nssRDN_Duplicate
+(
+ NSSRDN *rdn,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSRDNSeq
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssRDNSeq_CreateFromBER -- constructor
+ * nssRDNSeq_CreateFromUTF8 -- constructor
+ * nssRDNSeq_Create -- constructor
+ *
+ * nssRDNSeq_Destroy
+ * nssRDNSeq_GetDEREncoding
+ * nssRDNSeq_GetUTF8Encoding
+ * nssRDNSeq_AppendRDN
+ * nssRDNSeq_GetRDNCount
+ * nssRDNSeq_GetRDN
+ * nssRDNSeq_Compare
+ * nssRDNSeq_Duplicate
+ *
+ * nssRDNSeq_EvaluateUTF8 -- not an object method
+ */
+
+/*
+ * nssRDNSeq_CreateFromBER
+ *
+ * This routine creates an NSSRDNSeq by decoding a BER- or DER-encoded
+ * sequence of RDNs. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error on the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDNSeq upon success
+ */
+
+NSS_EXTERN NSSRDNSeq *
+nssRDNSeq_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berRDNSeq
+);
+
+/*
+ * nssRDNSeq_CreateFromUTF8
+ *
+ * This routine creates an NSSRDNSeq by decoding a UTF8 string
+ * consisting of a comma-separated sequence of RDNs, such as
+ * "(sn=Smith,ou=Sales),o=Acme,c=US." If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_UNKNOWN_ATTRIBUTE
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSRDNSeq upon success
+ */
+
+NSS_EXTERN NSSRDNSeq *
+nssRDNSeq_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringRDNSeq
+);
+
+/*
+ * nssRDNSeq_Create
+ *
+ * This routine creates an NSSRDNSeq from one or more NSSRDNs. The
+ * final argument to this routine must be NULL. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_RDN
+ *
+ * Return value:
+ * NULL upon error
+ * A pointero to an NSSRDNSeq upon success
+ */
+
+NSS_EXTERN NSSRDNSeq *
+nssRDNSeq_Create
+(
+ NSSArena *arenaOpt,
+ NSSRDN *rdn1,
+ ...
+);
+
+/*
+ * nssRDNSeq_Destroy
+ *
+ * This routine will destroy an RDNSeq object. It should eventually
+ * be called on all RDNSeqs created without an arena. While it is not
+ * necessary to call it on RDNSeqs created within an arena, it is not
+ * an error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * set an error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+nssRDNSeq_Destroy
+(
+ NSSRDNSeq *rdnseq
+);
+
+/*
+ * nssRDNSeq_GetDEREncoding
+ *
+ * This routine will DER-encode an RDNSeq object. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return null upon error, in which case it will have
+ * set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSRDNSeq
+ */
+
+NSS_EXTERN NSSDER *
+nssRDNSeq_GetDEREncoding
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssRDNSeq_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the RDNSeq as a comma-separated sequence of RDNs.
+ * If the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to the UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssRDNSeq_GetUTF8Encoding
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssRDNSeq_AppendRDN
+ *
+ * This routine appends an RDN to the end of the existing RDN
+ * sequence. If the RDNSeq was created with a non-null arena
+ * argument, that same arena will be used for any additional required
+ * memory. If the RDNSeq was created with a NULL arena argument, any
+ * additional memory will be obtained from the heap. This routine
+ * returns a PRStatus value; it will return PR_SUCCESS upon success,
+ * and upon failure it will set an error on the error stack and return
+ * PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_INVALID_RDN
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS upon success
+ * PR_FAILURE upon failure
+ */
+
+NSS_EXTERN PRStatus
+nssRDNSeq_AppendRDN
+(
+ NSSRDNSeq *rdnseq,
+ NSSRDN *rdn
+);
+
+/*
+ * nssRDNSeq_GetRDNCount
+ *
+ * This routine returns the cardinality of the sequence of RDNs within
+ * the specified RDNSeq. This routine may return 0 upon error, in
+ * which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ *
+ * Return value:
+ * 0 upon error
+ * A positive number upon success
+ */
+
+NSS_EXTERN PRUint32
+nssRDNSeq_GetRDNCount
+(
+ NSSRDNSeq *rdnseq
+);
+
+/*
+ * nssRDNSeq_GetRDN
+ *
+ * This routine returns a pointer to the i'th RDN in the sequence of
+ * RDNs that make up the specified RDNSeq. The sequence begins with
+ * the top-level (e.g., "c=US") RDN. The value of the variable 'i'
+ * is on the range [0,c) where c is the cardinality returned from
+ * NSSRDNSeq_GetRDNCount. The caller owns the RDN the pointer to which
+ * is returned. If the optional arena argument is non-null, the memory
+ * used will be obtained from that areana; otherwise, the memory will
+ * be obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error upon the error stack. Note
+ * that the usual UTF8 representation of RDN Sequences is from last
+ * to first.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_VALUE_OUT_OF_RANGE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRDN
+ */
+
+NSS_EXTERN NSSRDN *
+nssRDNSeq_GetRDN
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt,
+ PRUint32 i
+);
+
+/*
+ * nssRDNSeq_Compare
+ *
+ * This routine compares two RDNSeqs for equality. For two RDNSeqs to
+ * be equal, they must have the same number of RDNs, and each RDN in
+ * one sequence must be equal to the corresponding RDN in the other
+ * sequence. The result of the comparison will be stored at the
+ * location pointed to by the "equalp" variable, which must point to a
+ * valid PRBool. This routine may return PR_FAILURE upon error, in
+ * which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+nssRDNSeq_Compare
+(
+ NSSRDNSeq *rdnseq1,
+ NSSRDNSeq *rdnseq2,
+ PRBool *equalp
+);
+
+/*
+ * nssRDNSeq_Duplicate
+ *
+ * This routine duplicates the specified RDNSeq. If the optional arena
+ * argument is non-null, the memory required will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have
+ * placed an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_RDNSEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new RDNSeq
+ */
+
+NSS_EXTERN NSSRDNSeq *
+nssRDNSeq_Duplicate
+(
+ NSSRDNSeq *rdnseq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssRDNSeq_EvaluateUTF8
+ *
+ * This routine evaluates a UTF8 string, and returns PR_TRUE if the
+ * string contains the string representation of an RDNSeq. This
+ * routine is used by the (directory) Name routines
+ * nssName_CreateFromUTF8 and nssName_EvaluateUTF8 to determine which
+ * choice of directory name the string may encode. This routine may
+ * return PR_FALSE upon error, but it subsumes that condition under the
+ * general "string does not evaluate as an RDNSeq" state, and does not
+ * set an error on the error stack.
+ *
+ * Return value:
+ * PR_TRUE if the string represents an RDNSeq
+ * PR_FALSE if otherwise
+ */
+
+NSS_EXTERN PRBool
+nssRDNSeq_EvaluateUTF8
+(
+ NSSUTF8 *str
+);
+
+/*
+ * NSSName
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssName_CreateFromBER -- constructor
+ * nssName_CreateFromUTF8 -- constructor
+ * nssName_Create -- constructor
+ *
+ * nssName_Destroy
+ * nssName_GetDEREncoding
+ * nssName_GetUTF8Encoding
+ * nssName_GetChoice
+ * nssName_GetRDNSequence
+ * nssName_GetSpecifiedChoice
+ * nssName_Compare
+ * nssName_Duplicate
+ *
+ * nssName_GetUID
+ * nssName_GetEmail
+ * nssName_GetCommonName
+ * nssName_GetOrganization
+ * nssName_GetOrganizationalUnits
+ * nssName_GetStateOrProvince
+ * nssName_GetLocality
+ * nssName_GetCountry
+ * nssName_GetAttribute
+ *
+ * nssName_EvaluateUTF8 -- not an object method
+ */
+
+/*
+ * nssName_CreateFromBER
+ *
+ * This routine creates an NSSName by decoding a BER- or DER-encoded
+ * (directory) Name. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise,
+ * the memory will be obtained from the heap. This routine may
+ * return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSName upon success
+ */
+
+NSS_EXTERN NSSName *
+nssName_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berName
+);
+
+/*
+ * nssName_CreateFromUTF8
+ *
+ * This routine creates an NSSName by decoding a UTF8 string
+ * consisting of the string representation of one of the choices of
+ * (directory) names. Currently the only choice is an RDNSeq. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. The routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSName upon success
+ */
+
+NSS_EXTERN NSSName *
+nssName_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringName
+);
+
+/*
+ * nssName_Create
+ *
+ * This routine creates an NSSName with the specified choice of
+ * underlying name types. The value of the choice variable must be
+ * one of the values of the NSSNameChoice enumeration, and the type
+ * of the arg variable must be as specified in the following table:
+ *
+ * Choice Type
+ * ======================== ===========
+ * NSSNameChoiceRdnSequence NSSRDNSeq *
+ *
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_CHOICE
+ * NSS_ERROR_INVALID_ARGUMENT
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSName upon success
+ */
+
+NSS_EXTERN NSSName *
+nssName_Create
+(
+ NSSArena *arenaOpt,
+ NSSNameChoice choice,
+ void *arg
+);
+
+/*
+ * nssName_Destroy
+ *
+ * This routine will destroy a Name object. It should eventually be
+ * called on all Names created without an arena. While it is not
+ * necessary to call it on Names created within an arena, it is not
+ * an error to do so. This routine returns a PRStatus value; if
+ * successful, it will return PR_SUCCESS. If unsuccessful, it will
+ * set an error on the error stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+nssName_Destroy
+(
+ NSSName *name
+);
+
+/*
+ * nssName_GetDEREncoding
+ *
+ * This routine will DER-encode a name object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSName
+ */
+
+NSS_EXTERN NSSDER *
+nssName_GetDEREncoding
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the Name in the format specified by the
+ * underlying name choice. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error on the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to the UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssName_GetUTF8Encoding
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetChoice
+ *
+ * This routine returns the type of the choice underlying the specified
+ * name. The return value will be a member of the NSSNameChoice
+ * enumeration. This routine may return NSSNameChoiceInvalid upon
+ * error, in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ *
+ * Return value:
+ * NSSNameChoiceInvalid upon error
+ * An other member of the NSSNameChoice enumeration upon success
+ */
+
+NSS_EXTERN NSSNameChoice
+nssName_GetChoice
+(
+ NSSName *name
+);
+
+/*
+ * nssName_GetRDNSequence
+ *
+ * If the choice underlying the specified NSSName is that of an
+ * RDNSequence, this routine will return a pointer to that RDN
+ * sequence. Otherwise, this routine will place an error on the
+ * error stack, and return NULL. If the optional arena argument is
+ * non-null, the memory required will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. The
+ * caller owns the returned pointer. This routine may return NULL
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRDNSeq
+ */
+
+NSS_EXTERN NSSRDNSeq *
+nssName_GetRDNSequence
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetSpecifiedChoice
+ *
+ * If the choice underlying the specified NSSName matches the specified
+ * choice, a caller-owned pointer to that underlying object will be
+ * returned. Otherwise, an error will be placed on the error stack and
+ * NULL will be returned. If the optional arena argument is non-null,
+ * the memory required will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer, which must be typecast
+ */
+
+NSS_EXTERN void *
+nssName_GetSpecifiedChoice
+(
+ NSSName *name,
+ NSSNameChoice choice,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_Compare
+ *
+ * This routine compares two Names for equality. For two Names to be
+ * equal, they must have the same choice of underlying types, and the
+ * underlying values must be equal. The result of the comparison will
+ * be stored at the location pointed to by the "equalp" variable, which
+ * must point to a valid PRBool. This routine may return PR_FAILURE
+ * upon error, in which case it will have set an error on the error
+ * stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE on error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+nssName_Compare
+(
+ NSSName *name1,
+ NSSName *name2,
+ PRBool *equalp
+);
+
+/*
+ * nssName_Duplicate
+ *
+ * This routine duplicates the specified nssname. If the optional
+ * arena argument is non-null, the memory required will be obtained
+ * from that arena; otherwise, the memory will be obtained from the
+ * heap. This routine may return NULL upon error, in which case it
+ * will have placed an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new NSSName
+ */
+
+NSS_EXTERN NSSName *
+nssName_Duplicate
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetUID
+ *
+ * This routine will attempt to derive a user identifier from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished
+ * Names containing a UID attribute, the UID will be the value of
+ * that attribute. Note that no UID attribute is defined in either
+ * PKIX or PKCS#9; rather, this seems to derive from RFC 1274, which
+ * defines the type as a caseIgnoreString. We'll return a Directory
+ * String. If the optional arena argument is non-null, the memory
+ * used will be obtained from that arena; otherwise, the memory will
+ * be obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_UID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String.
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssName_GetUID
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetEmail
+ *
+ * This routine will attempt to derive an email address from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished
+ * Names containing either a PKIX email address or a PKCS#9 email
+ * address, the result will be the value of that attribute. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_EMAIL
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5 String */
+nssName_GetEmail
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetCommonName
+ *
+ * This routine will attempt to derive a common name from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished Names
+ * containing a PKIX Common Name, the result will be that name. If
+ * the optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_COMMON_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssName_GetCommonName
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetOrganization
+ *
+ * This routine will attempt to derive an organisation name from the
+ * specified name, if the choices and content of the name permit.
+ * If Name consists of a Sequence of Relative Distinguished names
+ * containing a PKIX Organization, the result will be the value of
+ * that attribute. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. This routine may return NULL upon
+ * error, in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_ORGANIZATION
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssName_GetOrganization
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetOrganizationalUnits
+ *
+ * This routine will attempt to derive a sequence of organisational
+ * unit names from the specified name, if the choices and content of
+ * the name permit. If the Name consists of a Sequence of Relative
+ * Distinguished Names containing one or more organisational units,
+ * the result will be the values of those attributes. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_ORGANIZATIONAL_UNITS
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a null-terminated array of UTF8 Strings
+ */
+
+NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */
+nssName_GetOrganizationalUnits
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetStateOrProvince
+ *
+ * This routine will attempt to derive a state or province name from
+ * the specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished Names
+ * containing a state or province, the result will be the value of
+ * that attribute. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. This routine may return NULL upon
+ * error, in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_STATE_OR_PROVINCE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssName_GetStateOrProvince
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetLocality
+ *
+ * This routine will attempt to derive a locality name from the
+ * specified name, if the choices and content of the name permit. If
+ * the Name consists of a Sequence of Relative Distinguished names
+ * containing a Locality, the result will be the value of that
+ * attribute. If the optional arena argument is non-null, the memory
+ * used will be obtained from that arena; otherwise, the memory will
+ * be obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_LOCALITY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssName_GetLocality
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetCountry
+ *
+ * This routine will attempt to derive a country name from the
+ * specified name, if the choices and content of the name permit.
+ * If the Name consists of a Sequence of Relative Distinguished
+ * Names containing a Country, the result will be the value of
+ * that attribute.. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise,
+ * the memory will be obtained from the heap. This routine may
+ * return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_COUNTRY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */
+nssName_GetCountry
+(
+ NSSName *name,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_GetAttribute
+ *
+ * If the specified name consists of a Sequence of Relative
+ * Distinguished Names containing an attribute with the specified
+ * type, and the actual value of that attribute may be expressed
+ * with a Directory String, then the value of that attribute will
+ * be returned as a Directory String. If the optional arena argument
+ * is non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_NAME
+ * NSS_ERROR_NO_ATTRIBUTE
+ * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssName_GetAttribute
+(
+ NSSName *name,
+ NSSOID *attribute,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssName_EvaluateUTF8
+ *
+ * This routine evaluates a UTF8 string, and returns PR_TRUE if the
+ * string contains the string representation of an NSSName. This
+ * routine is used by the GeneralName routine
+ * nssGeneralName_CreateFromUTF8 to determine which choice of
+ * general name the string may encode. This routine may return
+ * PR_FALSE upon error, but it subsumes that condition under the
+ * general "string does not evaluate as a Name" state, and does not
+ * set an error on the error stack.
+ *
+ * Return value:
+ * PR_TRUE if the string represents a Name
+ * PR_FALSE otherwise
+ */
+
+NSS_EXTERN PRBool
+nssName_EvaluateUTF8
+(
+ NSSUTF8 *str
+);
+
+/*
+ * NSSGeneralName
+ *
+ * The non-public "methods" regarding this "object" are:
+ *
+ * nssGeneralName_CreateFromBER -- constructor
+ * nssGeneralName_CreateFromUTF8 -- constructor
+ * nssGeneralName_Create -- constructor
+ *
+ * nssGeneralName_Destroy
+ * nssGeneralName_GetDEREncoding
+ * nssGeneralName_GetUTF8Encoding
+ * nssGeneralName_GetChoice
+ * nssGeneralName_GetOtherName
+ * nssGeneralName_GetRfc822Name
+ * nssGeneralName_GetDNSName
+ * nssGeneralName_GetX400Address
+ * nssGeneralName_GetDirectoryName
+ * nssGeneralName_GetEdiPartyName
+ * nssGeneralName_GetUniformResourceIdentifier
+ * nssGeneralName_GetIPAddress
+ * nssGeneralName_GetRegisteredID
+ * nssGeneralName_GetSpecifiedChoice
+ * nssGeneralName_Compare
+ * nssGeneralName_Duplicate
+ *
+ * nssGeneralName_GetUID
+ * nssGeneralName_GetEmail
+ * nssGeneralName_GetCommonName
+ * nssGeneralName_GetOrganization
+ * nssGeneralName_GetOrganizationalUnits
+ * nssGeneralName_GetStateOrProvince
+ * nssGeneralName_GetLocality
+ * nssGeneralName_GetCountry
+ * nssGeneralName_GetAttribute
+ */
+
+/*
+ * nssGeneralName_CreateFromBER
+ *
+ * This routine creates an NSSGeneralName by decoding a BER- or DER-
+ * encoded general name. If the optional arena argument is non-null,
+ * the memory used will be obtained from that arena; otherwise, the
+ * memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error on the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralName upon success
+ */
+
+NSS_EXTERN NSSGeneralName *
+nssGeneralName_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berGeneralName
+);
+
+/*
+ * nssGeneralName_CreateFromUTF8
+ *
+ * This routine creates an NSSGeneralName by decoding a UTF8 string
+ * consisting of the string representation of one of the choices of
+ * general names. If the optional arena argument is non-null, the
+ * memory used will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The routine may return NULL upon
+ * error, in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_UTF8
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralName upon success
+ */
+
+NSS_EXTERN NSSGeneralName *
+nssGeneralName_CreateFromUTF8
+(
+ NSSArena *arenaOpt,
+ NSSUTF8 *stringGeneralName
+);
+
+/*
+ * nssGeneralName_Create
+ *
+ * This routine creates an NSSGeneralName with the specified choice of
+ * underlying name types. The value of the choice variable must be one
+ * of the values of the NSSGeneralNameChoice enumeration, and the type
+ * of the arg variable must be as specified in the following table:
+ *
+ * Choice Type
+ * ============================================ =========
+ * NSSGeneralNameChoiceOtherName
+ * NSSGeneralNameChoiceRfc822Name
+ * NSSGeneralNameChoiceDNSName
+ * NSSGeneralNameChoiceX400Address
+ * NSSGeneralNameChoiceDirectoryName NSSName *
+ * NSSGeneralNameChoiceEdiPartyName
+ * NSSGeneralNameChoiceUniformResourceIdentifier
+ * NSSGeneralNameChoiceIPAddress
+ * NSSGeneralNameChoiceRegisteredID
+ *
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one fo the following values:
+ * NSS_ERROR_INVALID_CHOICE
+ * NSS_ERROR_INVALID_ARGUMENT
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralName upon success
+ */
+
+NSS_EXTERN NSSGeneralName *
+nssGeneralName_Create
+(
+ NSSGeneralNameChoice choice,
+ void *arg
+);
+
+/*
+ * nssGeneralName_Destroy
+ *
+ * This routine will destroy a General Name object. It should
+ * eventually be called on all General Names created without an arena.
+ * While it is not necessary to call it on General Names created within
+ * an arena, it is not an error to do so. This routine returns a
+ * PRStatus value; if successful, it will return PR_SUCCESS. If
+ * usuccessful, it will set an error on the error stack and return
+ * PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ *
+ * Return value:
+ * PR_FAILURE upon failure
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+nssGeneralName_Destroy
+(
+ NSSGeneralName *generalName
+);
+
+/*
+ * nssGeneralName_GetDEREncoding
+ *
+ * This routine will DER-encode a name object. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return null upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSGeneralName
+ */
+
+NSS_EXTERN NSSDER *
+nssGeneralName_GetDEREncoding
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetUTF8Encoding
+ *
+ * This routine returns a UTF8 string containing a string
+ * representation of the General Name in the format specified by the
+ * underlying name choice. If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 string
+ */
+
+NSS_EXTERN NSSUTF8 *
+nssGeneralName_GetUTF8Encoding
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetChoice
+ *
+ * This routine returns the type of choice underlying the specified
+ * general name. The return value will be a member of the
+ * NSSGeneralNameChoice enumeration. This routine may return
+ * NSSGeneralNameChoiceInvalid upon error, in which case it will have
+ * set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ *
+ * Return value:
+ * NSSGeneralNameChoiceInvalid upon error
+ * An other member of the NSSGeneralNameChoice enumeration
+ */
+
+NSS_EXTERN NSSGeneralNameChoice
+nssGeneralName_GetChoice
+(
+ NSSGeneralName *generalName
+);
+
+/*
+ * nssGeneralName_GetOtherName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * Other Name, this routine will return a pointer to that Other name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSOtherName
+ */
+
+NSS_EXTERN NSSOtherName *
+nssGeneralName_GetOtherName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetRfc822Name
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * RFC 822 Name, this routine will return a pointer to that name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRFC822Name
+ */
+
+NSS_EXTERN NSSRFC822Name *
+nssGeneralName_GetRfc822Name
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetDNSName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * DNS Name, this routine will return a pointer to that DNS name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSDNSName
+ */
+
+NSS_EXTERN NSSDNSName *
+nssGeneralName_GetDNSName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetX400Address
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * X.400 Address, this routine will return a pointer to that Address.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSX400Address
+ */
+
+NSS_EXTERN NSSX400Address *
+nssGeneralName_GetX400Address
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetDirectoryName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * (directory) Name, this routine will return a pointer to that name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSName
+ */
+
+NSS_EXTERN NSSName *
+nssGeneralName_GetName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetEdiPartyName
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * EDI Party Name, this routine will return a pointer to that name.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSEdiPartyName
+ */
+
+NSS_EXTERN NSSEdiPartyName *
+nssGeneralName_GetEdiPartyName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetUniformResourceIdentifier
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * URI, this routine will return a pointer to that URI.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSURI
+ */
+
+NSS_EXTERN NSSURI *
+nssGeneralName_GetUniformResourceIdentifier
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetIPAddress
+ *
+ * If the choice underlying the specified NSSGeneralName is that of an
+ * IP Address , this routine will return a pointer to that address.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSIPAddress
+ */
+
+NSS_EXTERN NSSIPAddress *
+nssGeneralName_GetIPAddress
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetRegisteredID
+ *
+ * If the choice underlying the specified NSSGeneralName is that of a
+ * Registered ID, this routine will return a pointer to that ID.
+ * Otherwise, this routine will place an error on the error stack, and
+ * return NULL. If the optional arena argument is non-null, the memory
+ * required will be obtained from that arena; otherwise, the memory
+ * will be obtained from the heap. The caller owns the returned
+ * pointer. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to an NSSRegisteredID
+ */
+
+NSS_EXTERN NSSRegisteredID *
+nssGeneralName_GetRegisteredID
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetSpecifiedChoice
+ *
+ * If the choice underlying the specified NSSGeneralName matches the
+ * specified choice, a caller-owned pointer to that underlying object
+ * will be returned. Otherwise, an error will be placed on the error
+ * stack and NULL will be returned. If the optional arena argument
+ * is non-null, the memory required will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. The caller
+ * owns the returned pointer. This routine may return NULL upon
+ * error, in which caes it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_WRONG_CHOICE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer, which must be typecast
+ */
+
+NSS_EXTERN void *
+nssGeneralName_GetSpecifiedChoice
+(
+ NSSGeneralName *generalName,
+ NSSGeneralNameChoice choice,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_Compare
+ *
+ * This routine compares two General Names for equality. For two
+ * General Names to be equal, they must have the same choice of
+ * underlying types, and the underlying values must be equal. The
+ * result of the comparison will be stored at the location pointed
+ * to by the "equalp" variable, which must point to a valid PRBool.
+ * This routine may return PR_FAILURE upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following value:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+nssGeneralName_Compare
+(
+ NSSGeneralName *generalName1,
+ NSSGeneralName *generalName2,
+ PRBool *equalp
+);
+
+/*
+ * nssGeneralName_Duplicate
+ *
+ * This routine duplicates the specified General Name. If the optional
+ * arena argument is non-null, the memory required will be obtained
+ * from that arena; otherwise, the memory will be obtained from the
+ * heap. This routine may return NULL upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new NSSGeneralName
+ */
+
+NSS_EXTERN NSSGeneralName *
+nssGeneralName_Duplicate
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetUID
+ *
+ * This routine will attempt to derive a user identifier from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished Names containing a UID
+ * attribute, the UID will be the value of that attribute. Note
+ * that no UID attribute is defined in either PKIX or PKCS#9;
+ * rather, this seems to derive from RFC 1274, which defines the
+ * type as a caseIgnoreString. We'll return a Directory String.
+ * If the optional arena argument is non-null, the memory used
+ * will be obtained from that arena; otherwise, the memory will be
+ * obtained from the heap. This routine may return NULL upon error,
+ * in which case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_UID
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String.
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssGeneralName_GetUID
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetEmail
+ *
+ * This routine will attempt to derive an email address from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing either
+ * a PKIX email address or a PKCS#9 email address, the result will
+ * be the value of that attribute. If the General Name is an RFC 822
+ * Name, the result will be the string form of that name. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_EMAIL
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr IA5String */
+nssGeneralName_GetEmail
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetCommonName
+ *
+ * This routine will attempt to derive a common name from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing a PKIX
+ * Common Name, the result will be that name. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have set
+ * an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_COMMON_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssGeneralName_GetCommonName
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetOrganization
+ *
+ * This routine will attempt to derive an organisation name from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing an
+ * Organization, the result will be the value of that attribute.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_ORGANIZATION
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssGeneralName_GetOrganization
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetOrganizationalUnits
+ *
+ * This routine will attempt to derive a sequence of organisational
+ * unit names from the specified general name, if the choices and
+ * content of the name permit. If the General Name is a (directory)
+ * Name consisting of a Sequence of Relative Distinguished names
+ * containing one or more organisational units, the result will
+ * consist of those units. If the optional arena argument is non-
+ * null, the memory used will be obtained from that arena; otherwise,
+ * the memory will be obtained from the heap. This routine may return
+ * NULL upon error, in which case it will have set an error on the
+ * error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_ORGANIZATIONAL_UNITS
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a null-terminated array of UTF8 Strings
+ */
+
+NSS_EXTERN NSSUTF8 ** /* XXX fgmr DirectoryString */
+nssGeneralName_GetOrganizationalUnits
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetStateOrProvince
+ *
+ * This routine will attempt to derive a state or province name from
+ * the specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing a state or
+ * province, the result will be the value of that attribute. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_STATE_OR_PROVINCE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssGeneralName_GetStateOrProvince
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetLocality
+ *
+ * This routine will attempt to derive a locality name from
+ * the specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting
+ * of a Sequence of Relative Distinguished names containing a Locality,
+ * the result will be the value of that attribute. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_LOCALITY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssGeneralName_GetLocality
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetCountry
+ *
+ * This routine will attempt to derive a country name from the
+ * specified general name, if the choices and content of the name
+ * permit. If the General Name is a (directory) Name consisting of a
+ * Sequence of Relative Distinguished names containing a Country, the
+ * result will be the value of that attribute. If the optional
+ * arena argument is non-null, the memory used will be obtained from
+ * that arena; otherwise, the memory will be obtained from the heap.
+ * This routine may return NULL upon error, in which case it will have
+ * set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_COUNTRY
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr PrintableString */
+nssGeneralName_GetCountry
+(
+ NSSGeneralName *generalName,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralName_GetAttribute
+ *
+ * If the specified general name is a (directory) name consisting
+ * of a Sequence of Relative Distinguished Names containing an
+ * attribute with the specified type, and the actual value of that
+ * attribute may be expressed with a Directory String, then the
+ * value of that attribute will be returned as a Directory String.
+ * If the optional arena argument is non-null, the memory used will
+ * be obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_ATTRIBUTE
+ * NSS_ERROR_ATTRIBUTE_VALUE_NOT_STRING
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a UTF8 String
+ */
+
+NSS_EXTERN NSSUTF8 * /* XXX fgmr DirectoryString */
+nssGeneralName_GetAttribute
+(
+ NSSGeneralName *generalName,
+ NSSOID *attribute,
+ NSSArena *arenaOpt
+);
+
+/*
+ * NSSGeneralNameSeq
+ *
+ * The public "methods" regarding this "object" are:
+ *
+ * nssGeneralNameSeq_CreateFromBER -- constructor
+ * nssGeneralNameSeq_Create -- constructor
+ *
+ * nssGeneralNameSeq_Destroy
+ * nssGeneralNameSeq_GetDEREncoding
+ * nssGeneralNameSeq_AppendGeneralName
+ * nssGeneralNameSeq_GetGeneralNameCount
+ * nssGeneralNameSeq_GetGeneralName
+ * nssGeneralNameSeq_Compare
+ * nssGeneralnameSeq_Duplicate
+ */
+
+/*
+ * nssGeneralNameSeq_CreateFromBER
+ *
+ * This routine creates a general name sequence by decoding a BER-
+ * or DER-encoded GeneralNames. If the optional arena argument is
+ * non-null, the memory used will be obtained from that arena;
+ * otherwise, the memory will be obtained from the heap. This routine
+ * may return NULL upon error, in which case it will have set an error
+ * on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_BER
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralNameSeq upon success
+ */
+
+NSS_EXTERN NSSGeneralNameSeq *
+nssGeneralNameSeq_CreateFromBER
+(
+ NSSArena *arenaOpt,
+ NSSBER *berGeneralNameSeq
+);
+
+/*
+ * nssGeneralNameSeq_Create
+ *
+ * This routine creates an NSSGeneralNameSeq from one or more General
+ * Names. The final argument to this routine must be NULL. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_NO_MEMORY
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to an NSSGeneralNameSeq upon success
+ */
+
+NSS_EXTERN NSSGeneralNameSeq *
+nssGeneralNameSeq_Create
+(
+ NSSArena *arenaOpt,
+ NSSGeneralName *generalName1,
+ ...
+);
+
+/*
+ * nssGeneralNameSeq_Destroy
+ *
+ * This routine will destroy an NSSGeneralNameSeq object. It should
+ * eventually be called on all NSSGeneralNameSeqs created without an
+ * arena. While it is not necessary to call it on NSSGeneralNameSeq's
+ * created within an arena, it is not an error to do so. This routine
+ * returns a PRStatus value; if successful, it will return PR_SUCCESS.
+ * If unsuccessful, it will set an error on the error stack and return
+ * PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon success
+ */
+
+NSS_EXTERN PRStatus
+nssGeneralNameSeq_Destroy
+(
+ NSSGeneralNameSeq *generalNameSeq
+);
+
+/*
+ * nssGeneralNameSeq_GetDEREncoding
+ *
+ * This routine will DER-encode an NSSGeneralNameSeq object. If the
+ * optional arena argument is non-null, the memory used will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return null upon error, in which
+ * case it will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * The DER encoding of this NSSGeneralNameSeq
+ */
+
+NSS_EXTERN NSSDER *
+nssGeneralNameSeq_GetDEREncoding
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSArena *arenaOpt
+);
+
+/*
+ * nssGeneralNameSeq_AppendGeneralName
+ *
+ * This routine appends a General Name to the end of the existing
+ * General Name Sequence. If the sequence was created with a non-null
+ * arena argument, that same arena will be used for any additional
+ * required memory. If the sequence was created with a NULL arena
+ * argument, any additional memory will be obtained from the heap.
+ * This routine returns a PRStatus value; it will return PR_SUCCESS
+ * upon success, and upon failure it will set an error on the error
+ * stack and return PR_FAILURE.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_INVALID_GENERAL_NAME
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * PR_SUCCESS upon success
+ * PR_FAILURE upon failure.
+ */
+
+NSS_EXTERN PRStatus
+nssGeneralNameSeq_AppendGeneralName
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSGeneralName *generalName
+);
+
+/*
+ * nssGeneralNameSeq_GetGeneralNameCount
+ *
+ * This routine returns the cardinality of the specified General name
+ * Sequence. This routine may return 0 upon error, in which case it
+ * will have set an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ *
+ * Return value;
+ * 0 upon error
+ * A positive number upon success
+ */
+
+NSS_EXTERN PRUint32
+nssGeneralNameSeq_GetGeneralNameCount
+(
+ NSSGeneralNameSeq *generalNameSeq
+);
+
+/*
+ * nssGeneralNameSeq_GetGeneralName
+ *
+ * This routine returns a pointer to the i'th General Name in the
+ * specified General Name Sequence. The value of the variable 'i' is
+ * on the range [0,c) where c is the cardinality returned from
+ * NSSGeneralNameSeq_GetGeneralNameCount. The caller owns the General
+ * Name the pointer to which is returned. If the optional arena
+ * argument is non-null, the memory used will be obtained from that
+ * arena; otherwise, the memory will be obtained from the heap. This
+ * routine may return NULL upon error, in which case it will have set
+ * an error upon the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_VALUE_OUT_OF_RANGE
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A caller-owned pointer to a General Name.
+ */
+
+NSS_EXTERN NSSGeneralName *
+nssGeneralNameSeq_GetGeneralName
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSArena *arenaOpt,
+ PRUint32 i
+);
+
+/*
+ * nssGeneralNameSeq_Compare
+ *
+ * This routine compares two General Name Sequences for equality. For
+ * two General Name Sequences to be equal, they must have the same
+ * cardinality, and each General Name in one sequence must be equal to
+ * the corresponding General Name in the other. The result of the
+ * comparison will be stored at the location pointed to by the "equalp"
+ * variable, which must point to a valid PRBool. This routine may
+ * return PR_FAILURE upon error, in which case it will have set an
+ * error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_INVALID_ARGUMENT
+ *
+ * Return value:
+ * PR_FAILURE upon error
+ * PR_SUCCESS upon a successful comparison (equal or not)
+ */
+
+NSS_EXTERN PRStatus
+nssGeneralNameSeq_Compare
+(
+ NSSGeneralNameSeq *generalNameSeq1,
+ NSSGeneralNameSeq *generalNameSeq2,
+ PRBool *equalp
+);
+
+/*
+ * nssGeneralNameSeq_Duplicate
+ *
+ * This routine duplicates the specified sequence of general names. If
+ * the optional arena argument is non-null, the memory required will be
+ * obtained from that arena; otherwise, the memory will be obtained
+ * from the heap. This routine may return NULL upon error, in which
+ * case it will have placed an error on the error stack.
+ *
+ * The error may be one of the following values:
+ * NSS_ERROR_INVALID_GENERAL_NAME_SEQ
+ * NSS_ERROR_NO_MEMORY
+ *
+ * Return value:
+ * NULL upon error
+ * A pointer to a new General Name Sequence.
+ */
+
+NSS_EXTERN NSSGeneralNameSeq *
+nssGeneralNameSeq_Duplicate
+(
+ NSSGeneralNameSeq *generalNameSeq,
+ NSSArena *arenaOpt
+);
+
+PR_END_EXTERN_C
+
+#endif /* PKI1_H */
diff --git a/security/nss/lib/pki1/pki1t.h b/security/nss/lib/pki1/pki1t.h
new file mode 100644
index 000000000..de6e5c835
--- /dev/null
+++ b/security/nss/lib/pki1/pki1t.h
@@ -0,0 +1,104 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifndef PKI1T_H
+#define PKI1T_H
+
+#ifdef DEBUG
+static const char PKI1T_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * pki1t.h
+ *
+ * This file contains definitions for the types used in the PKIX part-1
+ * code, but not available publicly.
+ */
+
+#ifndef BASET_H
+#include "baset.h"
+#endif /* BASET_H */
+
+#ifndef NSSPKI1T_H
+#include "nsspki1t.h"
+#endif /* NSSPKI1T_H */
+
+PR_BEGIN_EXTERN_C
+
+/*
+ * NSSOID
+ *
+ * This structure is used to hold our internal table of built-in OID
+ * data. The fields are as follows:
+ *
+ * NSSItem data -- this is the actual DER-encoded multinumber oid
+ * const char *expl -- this explains the derivation, and is checked
+ * in a unit test. While the field always exists,
+ * it is only populated or used in debug builds.
+ *
+ */
+
+struct NSSOIDStr {
+#ifdef DEBUG
+ const NSSUTF8 *tag;
+ const NSSUTF8 *expl;
+#endif /* DEBUG */
+ NSSItem data;
+};
+
+/*
+ * nssAttributeTypeAliasTable
+ *
+ * Attribute types are passed around as oids (at least in the X.500
+ * and PKI worlds, as opposed to ldap). However, when written as
+ * strings they usually have well-known aliases, e.g., "ou" or "c."
+ *
+ * This type defines a table, populated in the generated oiddata.c
+ * file, of the aliases we recognize.
+ *
+ * The fields are as follows:
+ *
+ * NSSUTF8 *alias -- a well-known string alias for an oid
+ * NSSOID *oid -- the oid to which the alias corresponds
+ *
+ */
+
+struct nssAttributeTypeAliasTableStr {
+ const NSSUTF8 *alias;
+ const NSSOID **oid;
+};
+typedef struct nssAttributeTypeAliasTableStr nssAttributeTypeAliasTable;
+
+PR_END_EXTERN_C
+
+#endif /* PKI1T_H */
diff --git a/security/nss/lib/pki1/rdn.c b/security/nss/lib/pki1/rdn.c
new file mode 100644
index 000000000..f778de22c
--- /dev/null
+++ b/security/nss/lib/pki1/rdn.c
@@ -0,0 +1,73 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * rdn.c
+ *
+ * This file contains the implementation of the PKIX part-1 object
+ * RelativeDistinguishedName.
+ */
+
+#ifndef NSSBASE_H
+#include "nssbase.h"
+#endif /* NSSBASE_H */
+
+#ifndef ASN1_H
+#include "asn1.h"
+#endif /* ASN1_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+/*
+ * RelativeDistinguishedName
+ *
+ * From draft-ietf-pkix-ipki-part1-10:
+ *
+ * RelativeDistinguishedName ::=
+ * SET SIZE (1 .. MAX) OF AttributeTypeAndValue
+ *
+ * An RDN is merely an (unordered) set of ATAV's. The setSize (that's
+ * a noun, not a verb) variable is a "helper" variable kept for
+ * convenience.
+ */
+
+struct nssRDNStr {
+ PRUint32 setSize;
+ NSSATAV **atavs;
+};
diff --git a/security/nss/lib/pki1/rdnseq.c b/security/nss/lib/pki1/rdnseq.c
new file mode 100644
index 000000000..bbd4dfe4b
--- /dev/null
+++ b/security/nss/lib/pki1/rdnseq.c
@@ -0,0 +1,71 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+#ifdef DEBUG
+static const char CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$ $Name$";
+#endif /* DEBUG */
+
+/*
+ * rdnseq.c
+ *
+ * This file contains the implementation of the PKIX part-1 object
+ * RDNSequence.
+ */
+
+#ifndef NSSBASE_H
+#include "nssbase.h"
+#endif /* NSSBASE_H */
+
+#ifndef ASN1_H
+#include "asn1.h"
+#endif /* ASN1_H */
+
+#ifndef PKI1_H
+#include "pki1.h"
+#endif /* PKI1_H */
+
+/*
+ * RDNSequence
+ *
+ * From draft-ietf-pkix-ipki-part1-10:
+ *
+ * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+ *
+ * An RDNSequence is simply an (ordered) sequence of RDN's. The
+ * seqSize variable is a "helper" kept for simplicity.
+ */
+
+struct nssRDNSeqStr {
+ PRUint32 seqSize;
+ NSSRDN **rdns;
+};