diff --git a/a/nss/lib/ckfw/manifest.mn b/b/nss/lib/ckfw/manifest.mn
index 20bebeb..4f10563 100644
--- a/a/nss/lib/ckfw/manifest.mn
+++ b/b/nss/lib/ckfw/manifest.mn
@@ -5,7 +5,7 @@
 
 CORE_DEPTH = ../..
 
-DIRS = builtins 
+DIRS = builtins pem
 
 PRIVATE_EXPORTS = \
 	ck.h		  \
diff --git a/a/nss/lib/ckfw/pem/Makefile b/b/nss/lib/ckfw/pem/Makefile
new file mode 100644
index 0000000..aec3bbd
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/Makefile
@@ -0,0 +1,107 @@
+# 
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1994-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+MAKEFILE_CVS_ID = "@(#) $RCSfile: Makefile,v $ $Revision: 1.5 $ $Date: 2007/05/09 00:09:37 $"
+
+include manifest.mn
+include $(CORE_DEPTH)/coreconf/config.mk
+include config.mk
+
+EXTRA_LIBS = \
+	$(DIST)/lib/$(LIB_PREFIX)nssckfw.$(LIB_SUFFIX) \
+	$(DIST)/lib/$(LIB_PREFIX)nssb.$(LIB_SUFFIX) \
+	$(DIST)/lib/$(LIB_PREFIX)freebl.$(LIB_SUFFIX) \
+	$(DIST)/lib/$(LIB_PREFIX)nssutil.$(LIB_SUFFIX) \
+	$(NULL)
+
+# can't do this in manifest.mn because OS_TARGET isn't defined there.
+ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+
+ifdef NS_USE_GCC
+EXTRA_LIBS += \
+	-L$(NSPR_LIB_DIR) \
+	-lplc4 \
+	-lplds4 \
+	-lnspr4 \
+	$(NULL)
+else 
+EXTRA_SHARED_LIBS += \
+        $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \
+        $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \
+        $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.lib \
+        crypt32.lib \
+	advapi32.lib \
+	rpcrt4.lib \
+        $(NULL)
+endif # NS_USE_GCC
+else
+
+EXTRA_LIBS += \
+	-L$(NSPR_LIB_DIR) \
+	-lplc4 \
+	-lplds4 \
+	-lnspr4 \
+	$(NULL)
+endif
+
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+# Generate certdata.c.
+generate:
+	$(PERL) certdata.perl < certdata.txt
+
+# This'll need some help from a build person.
+
+
+ifeq ($(OS_TARGET)$(OS_RELEASE), AIX4.1)
+DSO_LDOPTS              = -bM:SRE -bh:4 -bnoentry
+EXTRA_DSO_LDOPTS        = -lc
+MKSHLIB                 = xlC $(DSO_LDOPTS)
+
+$(SHARED_LIBRARY): $(OBJS)
+	@$(MAKE_OBJDIR)
+	rm -f $@
+	$(MKSHLIB) -o $@ $(OBJS) $(EXTRA_LIBS) $(EXTRA_DSO_LDOPTS)
+	chmod +x $@
+
+endif
+
+ifeq ($(OS_TARGET)$(OS_RELEASE), AIX4.2)
+LD      += -G
+endif 
+
+
diff --git a/a/nss/lib/ckfw/pem/anchor.c b/b/nss/lib/ckfw/pem/anchor.c
new file mode 100644
index 0000000..621f919
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/anchor.c
@@ -0,0 +1,50 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+ * anchor.c
+ *
+ * This file "anchors" the actual cryptoki entry points in this module's
+ * shared library, which is required for dynamic loading.  See the 
+ * comments in nssck.api for more information.
+ */
+
+#include "ckpem.h"
+
+#define MODULE_NAME pem
+#define INSTANCE_NAME (NSSCKMDInstance *)&pem_mdInstance
+#include "nssck.api"
diff --git a/a/nss/lib/ckfw/pem/ckpem.h b/b/nss/lib/ckfw/pem/ckpem.h
new file mode 100644
index 0000000..9712ccd
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/ckpem.h
@@ -0,0 +1,263 @@
+#ifndef CKPEM_H
+#define CKPEM_H
+
+#include "nssckmdt.h"
+#include "nssckfw.h"
+#include "ckfwtm.h"
+#include "ckfw.h"
+#include "secder.h"
+#include "secoid.h"
+#include "secasn1.h"
+#include "blapit.h"
+#include "softoken.h"
+
+/*
+ * I'm including this for access to the arena functions.
+ * Looks like we should publish that API.
+ */
+#ifndef BASE_H
+#include "base.h"
+#endif /* BASE_H */
+
+/*
+ * This is where the Netscape extensions live, at least for now.
+ */
+#ifndef CKT_H
+#include "ckt.h"
+#endif /* CKT_H */
+
+#define NUM_SLOTS 8
+
+/*
+ * statically defined raw objects. Allows us to data description objects
+ * to this PKCS #11 module.
+ */
+struct pemRawObjectStr {
+  CK_ULONG n;
+  const CK_ATTRIBUTE_TYPE *types;
+  const NSSItem *items;
+};
+typedef struct pemRawObjectStr pemRawObject;
+
+/*
+ * common values needed for both bare keys and cert referenced keys.
+ */
+struct pemKeyParamsStr {
+  NSSItem         modulus;
+  NSSItem         exponent;
+  NSSItem         privateExponent;
+  NSSItem         prime1;
+  NSSItem         prime2;
+  NSSItem         exponent1;
+  NSSItem         exponent2;
+  NSSItem         coefficient;
+  unsigned char   publicExponentData[sizeof(CK_ULONG)];
+  SECItem         *privateKey;
+  SECItem         *privateKeyOrig; /* deep copy of privateKey until decrypted */
+  void            *pubKey;
+};
+typedef struct pemKeyParamsStr pemKeyParams;
+/*
+ * Key objects. Handles bare keys which do not yet have certs associated
+ * with them. These are usually short lived, but may exist for several days
+ * while the CA is issuing the certificate.
+ */
+struct pemKeyObjectStr {
+  char            *provName;
+  char            *containerName;
+  pemKeyParams    key;
+  char            *ivstring;
+  int             cipher;
+};
+typedef struct pemKeyObjectStr pemKeyObject;
+
+/*
+ * Certificate and certificate referenced keys.
+ */
+struct pemCertObjectStr {
+  const char      *certStore;
+  NSSItem         label;
+  NSSItem         subject;
+  NSSItem         issuer;
+  NSSItem         serial;
+  NSSItem         derCert;
+  unsigned char   sha1_hash[SHA1_LENGTH];
+  unsigned char   md5_hash[MD5_LENGTH];
+  pemKeyParams key;
+  unsigned char   *labelData;
+  /* static data: to do, make this dynamic like labelData */
+  unsigned char   derSerial[128];
+};
+typedef struct pemCertObjectStr pemCertObject;
+
+/*
+ * Trust
+ */
+struct pemTrustObjectStr {
+  char            *nickname;
+};
+typedef struct pemTrustObjectStr pemTrustObject;
+
+typedef enum {
+  pemAll = -1, /* matches all types */
+  pemRaw,
+  pemCert,
+  pemBareKey,
+  pemTrust
+} pemObjectType;
+
+typedef struct pemInternalObjectStr pemInternalObject;
+typedef struct pemObjectListItemStr pemObjectListItem;
+
+/*
+ * singly-linked list of internal objects
+ */
+struct pemObjectListItemStr {
+  pemInternalObject     *io;
+  pemObjectListItem     *next;
+};
+
+/*
+ * all the various types of objects are abstracted away in cobject and
+ * cfind as pemInternalObjects.
+ */
+struct pemInternalObjectStr {
+  pemObjectType type;
+  union {
+    pemRawObject    raw;
+    pemCertObject   cert;
+    pemKeyObject    key;
+    pemTrustObject  trust;
+  } u;
+  CK_OBJECT_CLASS objClass;
+  NSSItem         hashKey;
+  NSSItem         id;
+  unsigned char   hashKeyData[128];
+  SECItem         *derCert;
+  char            *nickname;
+  NSSCKMDObject   mdObject;
+  CK_SLOT_ID      slotID;
+  CK_ULONG        gobjIndex;
+  int             refCount;
+
+  /* used by pem_mdFindObjects_Next */
+  CK_BBOOL        extRef;
+
+  /* If list != NULL, the object contains no useful data except of the list
+   * of slave objects */
+  pemObjectListItem *list;
+};
+
+struct pemTokenStr {
+  PRBool          logged_in;
+};
+typedef struct pemTokenStr pemToken;
+
+/* our raw object data array */
+NSS_EXTERN_DATA pemInternalObject nss_pem_data[];
+NSS_EXTERN_DATA const PRUint32               nss_pem_nObjects;
+
+/* our raw object data array */
+NSS_EXTERN_DATA pemInternalObject nss_pem_data[];
+NSS_EXTERN_DATA const PRUint32               nss_pem_nObjects;
+
+NSS_EXTERN_DATA pemInternalObject pem_data[];
+NSS_EXTERN_DATA const PRUint32               pem_nObjects;
+
+NSS_EXTERN_DATA const CK_VERSION   pem_CryptokiVersion;
+NSS_EXTERN_DATA const NSSUTF8 *    pem_ManufacturerID;
+NSS_EXTERN_DATA const NSSUTF8 *    pem_LibraryDescription;
+NSS_EXTERN_DATA const CK_VERSION   pem_LibraryVersion;
+NSS_EXTERN_DATA const NSSUTF8 *    pem_SlotDescription;
+NSS_EXTERN_DATA const CK_VERSION   pem_HardwareVersion;
+NSS_EXTERN_DATA const CK_VERSION   pem_FirmwareVersion;
+NSS_EXTERN_DATA const NSSUTF8 *    pem_TokenLabel;
+NSS_EXTERN_DATA const NSSUTF8 *    pem_TokenModel;
+NSS_EXTERN_DATA const NSSUTF8 *    pem_TokenSerialNumber;
+
+NSS_EXTERN_DATA const NSSCKMDInstance pem_mdInstance;
+NSS_EXTERN_DATA const NSSCKMDSlot     pem_mdSlot;
+NSS_EXTERN_DATA const NSSCKMDToken    pem_mdToken;
+NSS_EXTERN_DATA const NSSCKMDMechanism pem_mdMechanismRSA;
+
+NSS_EXTERN NSSCKMDSession *
+pem_CreateSession
+(
+  NSSCKFWSession *fwSession,
+  CK_RV *pError
+);
+
+NSS_EXTERN NSSCKMDFindObjects *
+pem_FindObjectsInit
+(
+  NSSCKFWSession *fwSession,
+  CK_ATTRIBUTE_PTR pTemplate,
+  CK_ULONG ulAttributeCount,
+  CK_RV *pError
+);
+
+NSS_EXTERN NSSCKMDObject *
+pem_CreateMDObject
+(
+  NSSArena *arena,
+  pemInternalObject *io,
+  CK_RV *pError
+);
+
+#define NSS_PEM_ARRAY_SIZE(x) ((sizeof (x))/(sizeof ((x)[0])))
+
+typedef enum {
+    pemLOWKEYNullKey = 0,
+    pemLOWKEYRSAKey = 1,
+    pemLOWKEYDSAKey = 2,
+    pemLOWKEYDHKey = 4,
+    pemLOWKEYECKey = 5
+} pemLOWKEYType;
+
+/*
+** Low Level private key object
+** This is only used by the raw Crypto engines (crypto), keydb (keydb),
+** and PKCS #11. Everyone else uses the high level key structure.
+*/
+struct pemLOWKEYPrivateKeyStr {
+    PLArenaPool *arena;
+    pemLOWKEYType keyType;
+    union {
+        RSAPrivateKey rsa;
+        DSAPrivateKey dsa;
+        DHPrivateKey  dh;
+        ECPrivateKey  ec;
+    } u;
+};
+typedef struct pemLOWKEYPrivateKeyStr pemLOWKEYPrivateKey;
+
+SECStatus ReadDERFromFile(SECItem ***derlist, char *filename, PRBool ascii, int *cipher, char **ivstring, PRBool certsonly);
+const NSSItem * pem_FetchAttribute ( pemInternalObject *io, CK_ATTRIBUTE_TYPE type);
+void pem_PopulateModulusExponent(pemInternalObject *io);
+NSSCKMDObject * pem_CreateObject(NSSCKFWInstance *fwInstance, NSSCKFWSession *fwSession, NSSCKMDToken *mdToken, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_RV *pError);
+NSSCKMDSlot *pem_NewSlot( NSSCKFWInstance *fwInstance, CK_RV *pError);
+
+
+PRBool pem_ParseString(const char* inputstring, const char delimiter,
+                       PRInt32* numStrings, char*** returnedstrings);
+PRBool pem_FreeParsedStrings(PRInt32 numStrings, char** instrings);
+
+pemInternalObject *
+AddObjectIfNeeded(CK_OBJECT_CLASS objClass, pemObjectType type,
+                  SECItem *certDER, SECItem *keyDER, char *filename, int objid,
+                  CK_SLOT_ID slotID, PRBool *pAdded);
+
+void pem_DestroyInternalObject (pemInternalObject *io);
+
+
+/* prsa.c */
+unsigned int pem_PrivateModulusLen(pemLOWKEYPrivateKey *privk);
+
+/* ptoken.c */
+NSSCKMDToken * pem_NewToken(NSSCKFWInstance *fwInstance, CK_RV *pError);
+
+/* util.c */
+void open_log();
+void plog(const char *fmt, ...);
+
+#endif /* CKPEM_H */
diff --git a/a/nss/lib/ckfw/pem/ckpemver.c b/b/nss/lib/ckfw/pem/ckpemver.c
new file mode 100644
index 0000000..76ab5df
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/ckpemver.c
@@ -0,0 +1,59 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ * Portions created by Red Hat, Inc, are Copyright (C) 2005
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* Library identity and versioning */
+
+#include "nsspem.h"
+
+#if defined(DEBUG)
+#define _DEBUG_STRING " (debug)"
+#else
+#define _DEBUG_STRING ""
+#endif
+
+/*
+ * Version information for the 'ident' and 'what commands
+ *
+ * NOTE: the first component of the concatenated rcsid string
+ * must not end in a '$' to prevent rcs keyword substitution.
+ */
+const char __nss_ckpem_rcsid[] = "$Header: NSS Access to Flat Files in PEM format"
+        NSS_CKPEM_LIBRARY_VERSION _DEBUG_STRING
+        "  " __DATE__ " " __TIME__ " $";
+const char __nss_ckcapi_sccsid[] = "@(#)NSS Access to Flag Files in PEM format "
+        NSS_CKPEM_LIBRARY_VERSION _DEBUG_STRING
+        "  " __DATE__ " " __TIME__;
diff --git a/a/nss/lib/ckfw/pem/config.mk b/b/nss/lib/ckfw/pem/config.mk
new file mode 100644
index 0000000..ff6cd9a
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/config.mk
@@ -0,0 +1,71 @@
+# 
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1994-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+CONFIG_CVS_ID = "@(#) $RCSfile: config.mk,v $ $Revision: 1.11 $ $Date: 2005/01/20 02:25:46 $"
+
+#
+#  Override TARGETS variable so that only shared libraries
+#  are specifed as dependencies within rules.mk.
+#
+
+TARGETS        = $(SHARED_LIBRARY)
+LIBRARY        =
+IMPORT_LIBRARY =
+PROGRAM        =
+
+ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+    SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
+    RES = $(OBJDIR)/$(LIBRARY_NAME).res
+    RESNAME = $(LIBRARY_NAME).rc
+endif
+
+ifdef BUILD_IDG
+    DEFINES += -DNSSDEBUG
+endif
+
+#
+# To create a loadable module on Darwin, we must use -bundle.
+#
+ifeq ($(OS_TARGET),Darwin)
+DSO_LDOPTS = -bundle
+endif
+
+ifeq ($(OS_TARGET),SunOS)
+# The -R '$ORIGIN' linker option instructs this library to search for its
+# dependencies in the same directory where it resides.
+MKSHLIB += -R '$$ORIGIN'
+endif
+
diff --git a/a/nss/lib/ckfw/pem/constants.c b/b/nss/lib/ckfw/pem/constants.c
new file mode 100644
index 0000000..0ceb443
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/constants.c
@@ -0,0 +1,77 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/*
+ * constants.c
+ *
+ * Identification and other constants, all collected here in one place.
+ */
+
+#ifndef NSSBASET_H
+#include "nssbaset.h"
+#endif /* NSSBASET_H */
+
+#ifndef NSSCKT_H
+#include "nssckt.h"
+#endif /* NSSCKT_H */
+
+#ifndef NSSCKBI_H
+#include "../builtins/nssckbi.h"
+#endif /* NSSCKBI_H */
+
+NSS_IMPLEMENT_DATA const CK_VERSION
+pem_CryptokiVersion =  { 2, 1 };
+
+NSS_IMPLEMENT_DATA const NSSUTF8 *
+pem_ManufacturerID = (NSSUTF8 *) "Red Hat, Inc.";
+
+NSS_IMPLEMENT_DATA const NSSUTF8 *
+pem_LibraryDescription = (NSSUTF8 *) "PEM Reader Cryptoki Module";
+
+NSS_IMPLEMENT_DATA const CK_VERSION
+pem_LibraryVersion = { 1, 0 };
+
+NSS_IMPLEMENT_DATA const CK_VERSION
+pem_HardwareVersion = { 1, 0 };
+
+NSS_IMPLEMENT_DATA const CK_VERSION
+pem_FirmwareVersion = {  1, 0 };
+
+NSS_IMPLEMENT_DATA const NSSUTF8 *
+pem_TokenModel = (NSSUTF8 *) "1";
+
+NSS_IMPLEMENT_DATA const NSSUTF8 *
+pem_TokenSerialNumber = (NSSUTF8 *) "1";
diff --git a/a/nss/lib/ckfw/pem/manifest.mn b/b/nss/lib/ckfw/pem/manifest.mn
new file mode 100644
index 0000000..8de27d1
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/manifest.mn
@@ -0,0 +1,68 @@
+# 
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is the Netscape security libraries.
+#
+# The Initial Developer of the Original Code is
+# Netscape Communications Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1994-2000
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+MANIFEST_CVS_ID = "@(#) $RCSfile: manifest.mn,v $ $Revision: 1.1 $ $Date: 2005/11/04 02:05:04 $"
+
+CORE_DEPTH = ../../..
+
+MODULE = nss
+MAPFILE = $(OBJDIR)/nsspem.def
+
+EXPORTS =		\
+	nsspem.h	\
+	$(NULL)
+
+CSRCS =			\
+	anchor.c	\
+	constants.c	\
+	pargs.c		\
+	pfind.c		\
+	pinst.c 	\
+	pobject.c	\
+	prsa.c		\
+	psession.c	\
+	pslot.c		\
+	ptoken.c	\
+	ckpemver.c	\
+	rsawrapr.c	\
+	util.c		\
+	$(NULL)
+
+REQUIRES = nspr
+
+LIBRARY_NAME = nsspem
+
+#EXTRA_SHARED_LIBS = -L$(DIST)/lib -lnssckfw -lnssb -lplc4 -lplds4
diff --git a/a/nss/lib/ckfw/pem/nsspem.def b/b/nss/lib/ckfw/pem/nsspem.def
new file mode 100644
index 0000000..4978252
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/nsspem.def
@@ -0,0 +1,58 @@
+;+#
+;+# ***** BEGIN LICENSE BLOCK *****
+;+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+;+#
+;+# The contents of this file are subject to the Mozilla Public License Version
+;+# 1.1 (the "License"); you may not use this file except in compliance with
+;+# the License. You may obtain a copy of the License at
+;+# http://www.mozilla.org/MPL/
+;+#
+;+# Software distributed under the License is distributed on an "AS IS" basis,
+;+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+;+# for the specific language governing rights and limitations under the
+;+# License.
+;+#
+;+# The Original Code is the Netscape security libraries.
+;+#
+;+# The Initial Developer of the Original Code is
+;+# Netscape Communications Corporation.
+;+# Portions created by the Initial Developer are Copyright (C) 2003
+;+# the Initial Developer. All Rights Reserved.
+;+#
+;+# Contributor(s):
+;+#
+;+# Alternatively, the contents of this file may be used under the terms of
+;+# either the GNU General Public License Version 2 or later (the "GPL"), or
+;+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+;+# in which case the provisions of the GPL or the LGPL are applicable instead
+;+# of those above. If you wish to allow use of your version of this file only
+;+# under the terms of either the GPL or the LGPL, and not to allow others to
+;+# use your version of this file under the terms of the MPL, indicate your
+;+# decision by deleting the provisions above and replace them with the notice
+;+# and other provisions required by the GPL or the LGPL. If you do not delete
+;+# the provisions above, a recipient may use your version of this file under
+;+# the terms of any one of the MPL, the GPL or the LGPL.
+;+#
+;+# ***** END LICENSE BLOCK *****
+;+#
+;+# OK, this file is meant to support SUN, LINUX, AIX and WINDOWS
+;+#   1. For all unix platforms, the string ";-"  means "remove this line"
+;+#   2. For all unix platforms, the string " DATA " will be removed from any 
+;+#     line on which it occurs.
+;+#   3. Lines containing ";+" will have ";+" removed on SUN and LINUX.
+;+#      On AIX, lines containing ";+" will be removed.
+;+#   4. For all unix platforms, the string ";;" will thave the ";;" removed.
+;+#   5. For all unix platforms, after the above processing has taken place,
+;+#    all characters after the first ";" on the line will be removed.
+;+#    And for AIX, the first ";" will also be removed.
+;+#  This file is passed directly to windows. Since ';' is a comment, all UNIX
+;+#   directives are hidden behind ";", ";+", and ";-"
+;+
+;+NSS_3.1 {       # NSS 3.1 release
+;+    global:
+LIBRARY nsspem ;-
+EXPORTS ;-
+C_GetFunctionList;
+;+    local:
+;+*;
+;+};
diff --git a/a/nss/lib/ckfw/pem/nsspem.h b/b/nss/lib/ckfw/pem/nsspem.h
new file mode 100644
index 0000000..1547bf4
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/nsspem.h
@@ -0,0 +1,75 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ * Portions created by Red Hat, Inc, are Copyright (C) 2005
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NSSPEM_H
+#define NSSPEM_H
+
+/*
+ * NSS CKPEM Version numbers.
+ *
+ * These are the version numbers for the capi module packaged with
+ * this release on NSS. To determine the version numbers of the builtin
+ * module you are using, use the appropriate PKCS #11 calls.
+ *
+ * These version numbers detail changes to the PKCS #11 interface. They map
+ * to the PKCS #11 spec versions.
+ */
+#define NSS_CKPEM_CRYPTOKI_VERSION_MAJOR 2
+#define NSS_CKPEM_CRYPTOKI_VERSION_MINOR 20
+
+/* These version numbers detail the changes 
+ * to the list of trusted certificates.
+ *
+ * NSS_CKPEM_LIBRARY_VERSION_MINOR is a CK_BYTE.  It's not clear
+ * whether we may use its full range (0-255) or only 0-99 because
+ * of the comment in the CK_VERSION type definition.
+ */
+#define NSS_CKPEM_LIBRARY_VERSION_MAJOR 1
+#define NSS_CKPEM_LIBRARY_VERSION_MINOR 1
+#define NSS_CKPEM_LIBRARY_VERSION "1.1"
+
+/* These version numbers detail the semantic changes to the ckfw engine. */
+#define NSS_CKPEM_HARDWARE_VERSION_MAJOR 1
+#define NSS_CKPEM_HARDWARE_VERSION_MINOR 0
+
+/* These version numbers detail the semantic changes to ckbi itself 
+ * (new PKCS #11 objects), etc. */
+#define NSS_CKPEM_FIRMWARE_VERSION_MAJOR 1
+#define NSS_CKPEM_FIRMWARE_VERSION_MINOR 0
+
+#endif /* NSSCKBI_H */
diff --git a/a/nss/lib/ckfw/pem/nsspem.rc b/b/nss/lib/ckfw/pem/nsspem.rc
new file mode 100644
index 0000000..eb208d6
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/nsspem.rc
@@ -0,0 +1,64 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsspem.h"
+#include <winver.h>
+
+#define MY_LIBNAME "nsspem"
+#define MY_FILEDESCRIPTION "NSS PEM support"
+
+#ifdef _DEBUG
+#define MY_DEBUG_STR " (debug)"
+#define MY_FILEFLAGS_1 VS_FF_DEBUG
+#else
+#define MY_DEBUG_STR ""
+#define MY_FILEFLAGS_1 0x0L
+#endif
+#if NSS_BETA
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1|VS_FF_PRERELEASE
+#else
+#define MY_FILEFLAGS_2 MY_FILEFLAGS_1
+#endif
+
+#ifdef WINNT
+#define MY_FILEOS VOS_NT_WINDOWS32
+#else
+#define MY_FILEOS VOS__WINDOWS32
+#endif
+
+#define MY_INTERNAL_NAME MY_LIBNAME
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version-information resource
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION NSS_CKPEM_LIBRARY_VERSION_MAJOR,NSS_CKPEM_LIBRARY_VERSION_MINOR,0,0
+ PRODUCTVERSION NSS_CKPEM_LIBRARY_VERSION_MAJOR,NSS_CKPEM_LIBRARY_VERSION_MINOR,0,0
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS MY_FILEFLAGS_2
+ FILEOS MY_FILEOS
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L // not used
+
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904B0" // Lang=US English, CharSet=Unicode
+        BEGIN
+            VALUE "CompanyName", "Mozilla Foundation\0"
+            VALUE "FileDescription", MY_FILEDESCRIPTION MY_DEBUG_STR "\0"
+            VALUE "FileVersion", NSS_CKPEM_LIBRARY_VERSION "\0"
+            VALUE "InternalName", MY_INTERNAL_NAME "\0"
+            VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
+            VALUE "ProductName", "Network Security Services\0"
+            VALUE "ProductVersion", NSS_CKPEM_LIBRARY_VERSION "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/a/nss/lib/ckfw/pem/pargs.c b/b/nss/lib/ckfw/pem/pargs.c
new file mode 100644
index 0000000..21291a8
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/pargs.c
@@ -0,0 +1,164 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <string.h>
+#include <nspr.h>
+
+void *pem_Malloc(const PRInt32 sz)
+{
+    return PR_Malloc(sz);
+}
+
+char *pem_StrNdup(const char *instr, PRInt32 inlen)
+{
+    size_t len = inlen;
+    char *buffer;
+    if (!instr) {
+        return NULL;
+    }
+
+    if (!len) {
+        return NULL;
+    }
+    buffer = (char *) pem_Malloc(len + 1);
+    if (!buffer) {
+        return NULL;
+    }
+    memcpy(buffer, instr, len);
+    buffer[len] = 0; /* NULL termination */
+    return buffer;
+}
+
+char *pem_Strdup(const char *instr)
+{
+    size_t len;
+    if (!instr) {
+        return NULL;
+    }
+
+    len = strlen(instr);
+    return pem_StrNdup(instr, len);
+}
+
+void pem_Free(char *instr)
+{
+    if (!instr) {
+        PR_ASSERT(0);
+    }
+    PR_Free(instr);
+}
+
+void
+addString(char ***returnedstrings, char *newstring, PRInt32 stringcount)
+{
+    char **stringarray = NULL;
+    if (!returnedstrings || !newstring) {
+        return;
+    }
+    if (!stringcount) {
+        /* first string to be added, allocate buffer */
+        *returnedstrings =
+            (char **) PR_Malloc(sizeof(char *) * (stringcount + 1));
+        stringarray = *returnedstrings;
+    } else {
+        stringarray = (char **) PR_Realloc(*returnedstrings,
+                                           sizeof(char *) * (stringcount + 1));
+        if (stringarray) {
+            *returnedstrings = stringarray;
+        }
+    }
+    if (stringarray) {
+        stringarray[stringcount] = newstring;
+    }
+}
+
+PRBool
+pem_ParseString(const char *inputstring, const char delimiter,
+                PRInt32 * numStrings, char ***returnedstrings)
+{
+    char nextchar;
+    char *instring = (char *) inputstring;
+    if (!inputstring || !delimiter || !numStrings || !returnedstrings) {
+        /* we need a string and a non-zero delimiter, as well as
+         * a valid place to return the strings and count
+         */
+        return PR_FALSE;
+    }
+    *numStrings = 0;
+    *returnedstrings = NULL;
+
+    while ((nextchar = *instring)) {
+        unsigned long len = 0;
+        char *next = (char *) strchr(instring, delimiter);
+        if (next) {
+            /* current string string */
+            len = next - instring;
+        } else {
+            /* last string length */
+            len = strlen(instring);
+        }
+
+        if (len > 0) {
+            char *newstring = pem_StrNdup(instring, len);
+
+            addString(returnedstrings, newstring, (*numStrings)++);
+
+            instring += len;
+        }
+
+        if (delimiter == *instring) {
+            instring++; /* skip past next delimiter */
+        }
+    }
+    return PR_TRUE;
+}
+
+PRBool pem_FreeParsedStrings(PRInt32 numStrings, char **instrings)
+{
+    PRInt32 counter;
+    if (!numStrings || !instrings) {
+        return PR_FALSE;
+    }
+    for (counter = 0; counter < numStrings; counter++) {
+        char *astring = instrings[counter];
+        if (astring) {
+            pem_Free(astring);
+        }
+    }
+    PR_Free((void *) instrings);
+    return PR_TRUE;
+}
diff --git a/a/nss/lib/ckfw/pem/pfind.c b/b/nss/lib/ckfw/pem/pfind.c
new file mode 100644
index 0000000..30b1174
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/pfind.c
@@ -0,0 +1,435 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ckpem.h"
+
+/*
+ * pfind.c
+ *
+ * This file implements the NSSCKMDFindObjects object for the
+ * "PEM objects" cryptoki module.
+ */
+
+NSS_EXTERN_DATA pemInternalObject **gobj;
+NSS_EXTERN_DATA int pem_nobjs;
+
+struct pemFOStr {
+    NSSArena *arena;
+    CK_ULONG n;
+    CK_ULONG i;
+    pemInternalObject **objs;
+};
+
+#define PEM_ITEM_CHUNK  512
+
+#define PUT_Object(obj,err) \
+  { \
+    if (count >= size) { \
+    *listp = *listp ? \
+                nss_ZREALLOCARRAY(*listp, pemInternalObject *, \
+                               (size+PEM_ITEM_CHUNK) ) : \
+                nss_ZNEWARRAY(NULL, pemInternalObject *, \
+                               (size+PEM_ITEM_CHUNK) ) ; \
+      if ((pemInternalObject **)NULL == *listp) { \
+        err = CKR_HOST_MEMORY; \
+        goto loser; \
+      } \
+      size += PEM_ITEM_CHUNK; \
+    } \
+    (*listp)[ count ] = (obj); \
+    count++; \
+  }
+
+static void
+pem_mdFindObjects_Final
+(
+    NSSCKMDFindObjects * mdFindObjects,
+    NSSCKFWFindObjects * fwFindObjects,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    struct pemFOStr *fo = (struct pemFOStr *) mdFindObjects->etc;
+    NSSArena *arena = fo->arena;
+
+    nss_ZFreeIf(fo->objs);
+    nss_ZFreeIf(fo);
+    nss_ZFreeIf(mdFindObjects);
+    if ((NSSArena *) NULL != arena) {
+        NSSArena_Destroy(arena);
+    }
+
+    return;
+}
+
+static NSSCKMDObject *
+pem_mdFindObjects_Next
+(
+    NSSCKMDFindObjects * mdFindObjects,
+    NSSCKFWFindObjects * fwFindObjects,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSArena * arena,
+    CK_RV * pError
+)
+{
+    struct pemFOStr *fo = (struct pemFOStr *) mdFindObjects->etc;
+    pemInternalObject *io;
+
+    plog("pem_FindObjects_Next: ");
+
+    if (fo->i == fo->n) {
+        plog("Done creating objects\n");
+        *pError = CKR_OK;
+        return (NSSCKMDObject *) NULL;
+    }
+
+    io = fo->objs[fo->i];
+    fo->i++;
+
+    plog("Creating object for type %d\n", io->type);
+
+    if (!io->extRef) {
+        /* increase reference count only once as ckfw will free the found
+         * object only once */
+        io->extRef = CK_TRUE;
+        io->refCount ++;
+    }
+
+    return pem_CreateMDObject(arena, io, pError);
+}
+
+#if 0
+static int
+pem_derUnwrapInt(unsigned char *src, int size, unsigned char **dest)
+{
+    unsigned char *start = src;
+    int len = 0;
+
+    if (*src++ != 2) {
+        return 0;
+    }
+    len = *src++;
+    if (len & 0x80) {
+        int count = len & 0x7f;
+        len = 0;
+
+        if (count + 2 > size) {
+            return 0;
+        }
+        while (count-- > 0) {
+            len = (len << 8) | *src++;
+        }
+    }
+    if (len + (src - start) != size) {
+        return 0;
+    }
+    *dest = src;
+    return len;
+}
+#endif
+
+static char * pem_attr_name(CK_ATTRIBUTE_TYPE type) {
+    switch(type) {
+    case CKA_CLASS:
+        return "CKA_CLASS";
+    case CKA_TOKEN:
+        return "CKA_TOKEN";
+    case CKA_PRIVATE:
+        return "CKA_PRIVATE";
+    case CKA_LABEL:
+        return "CKA_LABEL";
+    case CKA_APPLICATION:
+        return "CKA_APPLICATION";
+    case CKA_VALUE:
+        return "CKA_VALUE";
+    case CKA_OBJECT_ID:
+        return "CKA_OBJECT_ID";
+    case CKA_CERTIFICATE_TYPE:
+        return "CKA_CERTIFICATE_TYPE";
+    case CKA_ISSUER:
+        return "CKA_ISSUER";
+    case CKA_SERIAL_NUMBER:
+        return "CKA_SERIAL_NUMBER";
+    case CKA_ID:
+        return "CKA_ID";
+    default:
+        return "unknown";
+    }
+}
+
+static CK_BBOOL
+pem_attrmatch(CK_ATTRIBUTE_PTR a, pemInternalObject * o) {
+    PRBool prb;
+    const NSSItem *b;
+
+    b = pem_FetchAttribute(o, a->type);
+    if (b == NULL) {
+        plog("pem_attrmatch %s %08x: CK_FALSE attr not found\n", pem_attr_name(a->type), a->type);
+        return CK_FALSE;
+    }
+
+    if (a->ulValueLen != b->size) {
+         plog("pem_attrmatch %s %08x: CK_FALSE size mismatch %d vs %d\n", pem_attr_name(a->type), a->type, a->ulValueLen, b->size);
+        return CK_FALSE;
+    }
+
+    prb = nsslibc_memequal(a->pValue, b->data, b->size, (PRStatus *) NULL);
+
+    if (PR_TRUE == prb) {
+        plog("pem_attrmatch %s %08x: CK_TRUE\n", pem_attr_name(a->type), a->type);
+        return CK_TRUE;
+    } else {
+        plog("pem_attrmatch %s %08x: CK_FALSE\n", pem_attr_name(a->type), a->type);
+        plog("type: %08x, label: %s a->pValue %08x, b->data %08x\n", o->objClass, o->u.cert.label.data, a->pValue, b->data);
+        return CK_FALSE;
+    }
+}
+
+static CK_BBOOL
+pem_match
+(
+    CK_ATTRIBUTE_PTR pTemplate,
+    CK_ULONG ulAttributeCount,
+    pemInternalObject * o
+)
+{
+    CK_ULONG i;
+
+    for (i = 0; i < ulAttributeCount; i++) {
+        if (CK_FALSE == pem_attrmatch(&pTemplate[i], o)) {
+            plog("pem_match: CK_FALSE\n");
+            return CK_FALSE;
+        }
+    }
+
+    /* Every attribute passed */
+    plog("pem_match: CK_TRUE\n");
+    return CK_TRUE;
+}
+
+CK_OBJECT_CLASS
+pem_GetObjectClass(CK_ATTRIBUTE_PTR pTemplate,
+                   CK_ULONG ulAttributeCount)
+{
+    CK_ULONG i;
+
+    for (i = 0; i < ulAttributeCount; i++) {
+        if (pTemplate[i].type == CKA_CLASS) {
+            return *(CK_OBJECT_CLASS *) pTemplate[i].pValue;
+        }
+    }
+    /* need to return a value that says 'fetch them all' */
+    return CK_INVALID_HANDLE;
+}
+
+static PRUint32
+collect_objects(CK_ATTRIBUTE_PTR pTemplate,
+                CK_ULONG ulAttributeCount,
+                pemInternalObject *** listp,
+                CK_RV * pError, CK_SLOT_ID slotID)
+{
+    PRUint32 i;
+    PRUint32 count = 0;
+    PRUint32 size = 0;
+    pemObjectType type = pemRaw;
+    CK_OBJECT_CLASS objClass = pem_GetObjectClass(pTemplate, ulAttributeCount);
+
+    *pError = CKR_OK;
+
+    plog("collect_objects slot #%ld, ", slotID);
+    plog("%d attributes, ", ulAttributeCount);
+    plog("%d objects to look through.\n", pem_nobjs);
+    plog("Looking for: ");
+    /*
+     * now determine type of the object
+     */
+    switch (objClass) {
+    case CKO_CERTIFICATE:
+        plog("CKO_CERTIFICATE\n");
+        type = pemCert;
+        break;
+    case CKO_PUBLIC_KEY:
+        plog("CKO_PUBLIC_KEY\n");
+        type = pemBareKey;
+        break;
+    case CKO_PRIVATE_KEY:
+        type = pemBareKey;
+        plog("CKO_PRIVATE_KEY\n");
+        break;
+    case CKO_NETSCAPE_TRUST:
+        type = pemTrust;
+        plog("CKO_NETSCAPE_TRUST\n");
+        break;
+    case CKO_NETSCAPE_CRL:
+        plog("CKO_NETSCAPE_CRL\n");
+        goto done;
+    case CKO_NETSCAPE_SMIME:
+        plog("CKO_NETSCAPE_SMIME\n");
+        goto done;
+    case CKO_NETSCAPE_BUILTIN_ROOT_LIST:
+        plog("CKO_NETSCAPE_BUILTIN_ROOT_LIST\n");
+        goto done;
+    case CK_INVALID_HANDLE:
+        type = pemAll; /* look through all objectclasses - ignore the type field */
+        plog("CK_INVALID_HANDLE\n");
+        break;
+    default:
+        plog("no other object types %08x\n", objClass);
+        goto done; /* no other object types we understand in this module */
+    }
+
+    /* find objects */
+    for (i = 0; i < pem_nobjs; i++) {
+        int match = 1; /* matches type if type not specified */
+        if (NULL == gobj[i])
+            continue;
+
+        plog("  %d type = %d\n", i, gobj[i]->type);
+        if (type != pemAll) {
+            /* type specified - must match given type */
+            match = (type == gobj[i]->type);
+        }
+        if (match) {
+            match = (slotID == gobj[i]->slotID) &&
+                (CK_TRUE == pem_match(pTemplate, ulAttributeCount, gobj[i]));
+        }
+        if (match) {
+            pemInternalObject *o = gobj[i];
+            PUT_Object(o, *pError);
+        }
+    }
+
+    if (CKR_OK != *pError) {
+        goto loser;
+    }
+
+  done:
+    plog("collect_objects: Found %d\n", count);
+    return count;
+  loser:
+    nss_ZFreeIf(*listp);
+    return 0;
+
+}
+
+NSS_IMPLEMENT NSSCKMDFindObjects *
+pem_FindObjectsInit
+(
+    NSSCKFWSession * fwSession,
+    CK_ATTRIBUTE_PTR pTemplate,
+    CK_ULONG ulAttributeCount,
+    CK_RV * pError
+)
+{
+    NSSArena *arena = NULL;
+    NSSCKMDFindObjects *rv = (NSSCKMDFindObjects *) NULL;
+    struct pemFOStr *fo = (struct pemFOStr *) NULL;
+    pemInternalObject **temp = (pemInternalObject **) NULL;
+    NSSCKFWSlot *fwSlot;
+    CK_SLOT_ID slotID;
+
+    plog("pem_FindObjectsInit\n");
+    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
+    if ((NSSCKFWSlot *) NULL == fwSlot) {
+        goto loser;
+    }
+    slotID = nssCKFWSlot_GetSlotID(fwSlot);
+
+    arena = NSSArena_Create();
+    if ((NSSArena *) NULL == arena) {
+        goto loser;
+    }
+
+    rv = nss_ZNEW(arena, NSSCKMDFindObjects);
+    if ((NSSCKMDFindObjects *) NULL == rv) {
+        *pError = CKR_HOST_MEMORY;
+        goto loser;
+    }
+
+    fo = nss_ZNEW(arena, struct pemFOStr);
+    if ((struct pemFOStr *) NULL == fo) {
+        *pError = CKR_HOST_MEMORY;
+        goto loser;
+    }
+
+    fo->arena = arena;
+    /* fo->n and fo->i are already zero */
+
+    rv->etc = (void *) fo;
+    rv->Final = pem_mdFindObjects_Final;
+    rv->Next = pem_mdFindObjects_Next;
+    rv->null = (void *) NULL;
+
+    fo->n =
+        collect_objects(pTemplate, ulAttributeCount, &temp, pError,
+                        slotID);
+    if (*pError != CKR_OK) {
+        goto loser;
+    }
+
+    fo->objs = nss_ZNEWARRAY(arena, pemInternalObject *, fo->n);
+    if ((pemInternalObject **) NULL == fo->objs) {
+        *pError = CKR_HOST_MEMORY;
+        goto loser;
+    }
+
+    (void) nsslibc_memcpy(fo->objs, temp,
+                          sizeof(pemInternalObject *) * fo->n);
+
+    nss_ZFreeIf(temp);
+    temp = (pemInternalObject **) NULL;
+
+    return rv;
+
+  loser:
+    nss_ZFreeIf(temp);
+    nss_ZFreeIf(fo);
+    nss_ZFreeIf(rv);
+    if ((NSSArena *) NULL != arena) {
+        NSSArena_Destroy(arena);
+    }
+    return (NSSCKMDFindObjects *) NULL;
+}
diff --git a/a/nss/lib/ckfw/pem/pinst.c b/b/nss/lib/ckfw/pem/pinst.c
new file mode 100644
index 0000000..9c98e89
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/pinst.c
@@ -0,0 +1,768 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#include <stdlib.h>
+#include "ckpem.h"
+#include "blapi.h"
+#include "prprf.h"
+
+/*
+ * pinstance.c
+ *
+ * This file implements the NSSCKMDInstance object for the 
+ * "PEM objects" cryptoki module.
+ */
+
+static PRBool pemInitialized = PR_FALSE;
+
+pemInternalObject **gobj;
+int pem_nobjs = 0;
+int token_needsLogin[NUM_SLOTS];
+
+PRInt32 size = 0;
+PRInt32 count = 0;
+
+#define PEM_ITEM_CHUNK  512
+
+/*
+ * simple cert decoder to avoid the cost of asn1 engine
+ */
+static unsigned char *
+dataStart(unsigned char *buf, unsigned int length,
+          unsigned int *data_length,
+          PRBool includeTag, unsigned char *rettag)
+{
+    unsigned char tag;
+    unsigned int used_length = 0;
+    if (!length)
+        return NULL;
+
+    tag = buf[used_length++];
+
+    if (rettag) {
+        *rettag = tag;
+    }
+
+    /* blow out when we come to the end */
+    if (tag == 0 || length <= used_length) {
+        return NULL;
+    }
+
+    *data_length = buf[used_length++];
+
+    if (*data_length & 0x80) {
+        int len_count = *data_length & 0x7f;
+
+        *data_length = 0;
+
+        while (len_count-- > 0) {
+            if (length <= used_length)
+                return NULL;
+
+            *data_length = (*data_length << 8) | buf[used_length++];
+        }
+    }
+
+    if (*data_length > (length - used_length)) {
+        *data_length = length - used_length;
+        return NULL;
+    }
+    if (includeTag)
+        *data_length += used_length;
+
+    return (buf + (includeTag ? 0 : used_length));
+}
+
+static int
+GetCertFields(unsigned char *cert, int cert_length,
+              SECItem * issuer, SECItem * serial, SECItem * derSN,
+              SECItem * subject, SECItem * valid, SECItem * subjkey)
+{
+    unsigned char *buf;
+    unsigned int buf_length;
+    unsigned char *dummy;
+    unsigned int dummylen;
+
+    /* get past the signature wrap */
+    buf = dataStart(cert, cert_length, &buf_length, PR_FALSE, NULL);
+    if (buf == NULL)
+        return SECFailure;
+    /* get into the raw cert data */
+    buf = dataStart(buf, buf_length, &buf_length, PR_FALSE, NULL);
+    if (buf == NULL)
+        return SECFailure;
+    /* skip past any optional version number */
+    if ((buf[0] & 0xa0) == 0xa0) {
+        dummy = dataStart(buf, buf_length, &dummylen, PR_FALSE, NULL);
+        if (dummy == NULL)
+            return SECFailure;
+        buf_length -= (dummy - buf) + dummylen;
+        buf = dummy + dummylen;
+    }
+    /* serial number */
+    if (derSN) {
+        derSN->data =
+            dataStart(buf, buf_length, &derSN->len, PR_TRUE, NULL);
+    }
+    serial->data =
+        dataStart(buf, buf_length, &serial->len, PR_FALSE, NULL);
+    if (serial->data == NULL)
+        return SECFailure;
+    buf_length -= (serial->data - buf) + serial->len;
+    buf = serial->data + serial->len;
+    /* skip the OID */
+    dummy = dataStart(buf, buf_length, &dummylen, PR_FALSE, NULL);
+    if (dummy == NULL)
+        return SECFailure;
+    buf_length -= (dummy - buf) + dummylen;
+    buf = dummy + dummylen;
+    /* issuer */
+    issuer->data = dataStart(buf, buf_length, &issuer->len, PR_TRUE, NULL);
+    if (issuer->data == NULL)
+        return SECFailure;
+    buf_length -= (issuer->data - buf) + issuer->len;
+    buf = issuer->data + issuer->len;
+
+    /* only wanted issuer/SN */
+    if (subject == NULL || valid == NULL || subjkey == NULL) {
+        return SECSuccess;
+    }
+    /* validity */
+    valid->data = dataStart(buf, buf_length, &valid->len, PR_FALSE, NULL);
+    if (valid->data == NULL)
+        return SECFailure;
+    buf_length -= (valid->data - buf) + valid->len;
+    buf = valid->data + valid->len;
+    /*subject */
+    subject->data =
+        dataStart(buf, buf_length, &subject->len, PR_TRUE, NULL);
+    if (subject->data == NULL)
+        return SECFailure;
+    buf_length -= (subject->data - buf) + subject->len;
+    buf = subject->data + subject->len;
+    /* subject  key info */
+    subjkey->data =
+        dataStart(buf, buf_length, &subjkey->len, PR_TRUE, NULL);
+    if (subjkey->data == NULL)
+        return SECFailure;
+    buf_length -= (subjkey->data - buf) + subjkey->len;
+    buf = subjkey->data + subjkey->len;
+    return SECSuccess;
+}
+
+static CK_RV
+assignObjectID(pemInternalObject *o, int objid)
+{
+    char id[16];
+    int len;
+
+    sprintf(id, "%d", objid);
+    len = strlen(id) + 1;       /* zero terminate */
+    o->id.size = len;
+    o->id.data = nss_ZAlloc(NULL, len);
+    if (o->id.data == NULL)
+        return CKR_HOST_MEMORY;
+
+    nsslibc_memcpy(o->id.data, id, len);
+    return CKR_OK;
+}
+
+static pemInternalObject *
+CreateObject(CK_OBJECT_CLASS objClass,
+             pemObjectType type, SECItem * certDER,
+             SECItem * keyDER, char *filename,
+             int objid, CK_SLOT_ID slotID)
+{
+    pemInternalObject *o;
+    SECItem subject;
+    SECItem issuer;
+    SECItem serial;
+    SECItem derSN;
+    SECItem valid;
+    SECItem subjkey;
+    char *nickname;
+
+    o = nss_ZNEW(NULL, pemInternalObject);
+    if ((pemInternalObject *) NULL == o) {
+        return NULL;
+    }
+
+    nickname = strrchr(filename, '/');
+    if (nickname)
+        nickname++;
+    else
+        nickname = filename;
+
+    switch (objClass) {
+    case CKO_CERTIFICATE:
+        plog("Creating cert nick %s id %d in slot %ld\n", nickname, objid, slotID);
+        memset(&o->u.cert, 0, sizeof(o->u.cert));
+        break;
+    case CKO_PRIVATE_KEY:
+        plog("Creating key id %d in slot %ld\n", objid, slotID);
+        memset(&o->u.key, 0, sizeof(o->u.key));
+        nickname = filename;
+        break;
+    case CKO_NETSCAPE_TRUST:
+        plog("Creating trust nick %s id %d in slot %ld\n", nickname, objid, slotID);
+        memset(&o->u.trust, 0, sizeof(o->u.trust));
+        break;
+    }
+
+    o->nickname = (char *) nss_ZAlloc(NULL, strlen(nickname) + 1);
+    if (o->nickname == NULL)
+        goto fail;
+    strcpy(o->nickname, nickname);
+
+    if (CKR_OK != assignObjectID(o, objid))
+        goto fail;
+
+    o->objClass = objClass;
+    o->type = type;
+    o->slotID = slotID;
+
+    o->derCert = nss_ZNEW(NULL, SECItem);
+    if (o->derCert == NULL)
+        goto fail;
+    o->derCert->data = (void *) nss_ZAlloc(NULL, certDER->len);
+    if (o->derCert->data == NULL)
+        goto fail;
+    o->derCert->len = certDER->len;
+    nsslibc_memcpy(o->derCert->data, certDER->data, certDER->len);
+
+    switch (objClass) {
+    case CKO_CERTIFICATE:
+    case CKO_NETSCAPE_TRUST:
+        if (SECSuccess != GetCertFields(o->derCert->data, o->derCert->len,
+                                        &issuer, &serial, &derSN, &subject,
+                                        &valid, &subjkey))
+            goto fail;
+
+        o->u.cert.subject.data = (void *) nss_ZAlloc(NULL, subject.len);
+        if (o->u.cert.subject.data == NULL)
+            goto fail;
+        o->u.cert.subject.size = subject.len;
+        nsslibc_memcpy(o->u.cert.subject.data, subject.data, subject.len);
+
+        o->u.cert.issuer.data = (void *) nss_ZAlloc(NULL, issuer.len);
+        if (o->u.cert.issuer.data == NULL) {
+            nss_ZFreeIf(o->u.cert.subject.data);
+            goto fail;
+        }
+        o->u.cert.issuer.size = issuer.len;
+        nsslibc_memcpy(o->u.cert.issuer.data, issuer.data, issuer.len);
+
+        o->u.cert.serial.data = (void *) nss_ZAlloc(NULL, serial.len);
+        if (o->u.cert.serial.data == NULL) {
+            nss_ZFreeIf(o->u.cert.issuer.data);
+            nss_ZFreeIf(o->u.cert.subject.data);
+            goto fail;
+        }
+        o->u.cert.serial.size = serial.len;
+        nsslibc_memcpy(o->u.cert.serial.data, serial.data, serial.len);
+        break;
+    case CKO_PRIVATE_KEY:
+        o->u.key.key.privateKey = nss_ZNEW(NULL, SECItem);
+        if (o->u.key.key.privateKey == NULL)
+            goto fail;
+        o->u.key.key.privateKey->data =
+            (void *) nss_ZAlloc(NULL, keyDER->len);
+        if (o->u.key.key.privateKey->data == NULL) {
+            nss_ZFreeIf(o->u.key.key.privateKey);
+            goto fail;
+        }
+
+        /* store deep copy of original key DER so we can compare it later on */
+        o->u.key.key.privateKeyOrig = SECITEM_DupItem(keyDER);
+        if (o->u.key.key.privateKeyOrig == NULL) {
+            nss_ZFreeIf(o->u.key.key.privateKey->data);
+            nss_ZFreeIf(o->u.key.key.privateKey);
+            goto fail;
+        }
+
+        o->u.key.key.privateKey->len = keyDER->len;
+        nsslibc_memcpy(o->u.key.key.privateKey->data, keyDER->data,
+                       keyDER->len);
+    }
+
+
+    return o;
+
+fail:
+    if (o) {
+        if (o->derCert) {
+            nss_ZFreeIf(o->derCert->data);
+            nss_ZFreeIf(o->derCert);
+        }
+        nss_ZFreeIf(o->id.data);
+        nss_ZFreeIf(o->nickname);
+        nss_ZFreeIf(o);
+    }
+    return NULL;
+}
+
+/* Compare the DER encoding of the internal object against those
+ * of the provided certDER or keyDER according to its objClass.
+ */
+static PRBool
+derEncodingsMatch(CK_OBJECT_CLASS objClass, pemInternalObject * obj,
+                  SECItem * certDER, SECItem * keyDER)
+{
+    SECComparison result;
+
+    switch (objClass) {
+    case CKO_CERTIFICATE:
+    case CKO_NETSCAPE_TRUST:
+        result = SECITEM_CompareItem(obj->derCert, certDER);
+        break;
+
+    case CKO_PRIVATE_KEY:
+        result = SECITEM_CompareItem(obj->u.key.key.privateKeyOrig, keyDER);
+        break;
+
+    default:
+        /* unhandled object class */
+        return PR_FALSE;
+    }
+
+    return SECEqual == result;
+}
+
+static CK_RV
+LinkSharedKeyObject(int oldKeyIdx, int newKeyIdx)
+{
+    int i;
+    for (i = 0; i < pem_nobjs; i++) {
+        CK_RV rv;
+        pemInternalObject *obj = gobj[i];
+        if (NULL == obj)
+            continue;
+
+        if (atoi(obj->id.data) != oldKeyIdx)
+            continue;
+
+        nss_ZFreeIf(obj->id.data);
+        rv = assignObjectID(obj, newKeyIdx);
+        if (CKR_OK != rv)
+            return rv;
+    }
+
+    return CKR_OK;
+}
+
+pemInternalObject *
+AddObjectIfNeeded(CK_OBJECT_CLASS objClass,
+                  pemObjectType type, SECItem * certDER,
+                  SECItem * keyDER, char *filename,
+                  int objid, CK_SLOT_ID slotID, PRBool *pAdded)
+{
+    int i;
+    pemInternalObject *io;
+
+    /* FIXME: copy-pasted from CreateObject */
+    const char *nickname = strrchr(filename, '/');
+    if (nickname && CKO_PRIVATE_KEY != objClass)
+        nickname++;
+    else
+        nickname = filename;
+
+    if (pAdded)
+        *pAdded = PR_FALSE;
+
+    /* first look for the object in gobj, it might be already there */
+    for (i = 0; i < pem_nobjs; i++) {
+        if (NULL == gobj[i])
+            continue;
+
+        /* Comparing DER encodings is dependable and frees the PEM module
+         * from having to require clients to provide unique nicknames.
+         */
+        if ((gobj[i]->objClass == objClass)
+                && (gobj[i]->type == type)
+                && (gobj[i]->slotID == slotID)
+                && derEncodingsMatch(objClass, gobj[i], certDER, keyDER)) {
+
+            /* While adding a client certificate we (wrongly?) assumed that the
+             * key object will follow right after the cert object.  However, if
+             * the key object is shared by multiple client certificates, such
+             * an assumption does not hold.  We have to update the references.
+             */
+            LinkSharedKeyObject(pem_nobjs, i);
+
+            plog("AddObjectIfNeeded: re-using internal object #%i\n", i);
+            gobj[i]->refCount ++;
+            return gobj[i];
+        }
+    }
+
+    /* object not found, we need to create it */
+    io = CreateObject(objClass, type, certDER, keyDER,
+                                         filename, objid, slotID);
+    if (io == NULL)
+        return NULL;
+
+    /* initialize pointers to functions */
+    pem_CreateMDObject(NULL, io, NULL);
+
+    io->gobjIndex = count;
+
+    /* add object to global array */
+    if (count >= size) {
+        gobj = gobj ?
+            nss_ZREALLOCARRAY(gobj, pemInternalObject *,
+                    (size+PEM_ITEM_CHUNK) ) :
+            nss_ZNEWARRAY(NULL, pemInternalObject *,
+                    (size+PEM_ITEM_CHUNK) ) ;
+
+        if ((pemInternalObject **)NULL == gobj)
+            return NULL;
+        size += PEM_ITEM_CHUNK;
+    }
+    gobj[count] = io;
+    count++;
+    pem_nobjs++;
+
+    if (pAdded)
+        *pAdded = PR_TRUE;
+
+    io->refCount ++;
+    return io;
+}
+
+CK_RV
+AddCertificate(char *certfile, char *keyfile, PRBool cacert,
+               CK_SLOT_ID slotID)
+{
+    pemInternalObject *o;
+    CK_RV error = 0;
+    int objid, i;
+    int nobjs = 0;
+    SECItem **objs = NULL;
+    char *ivstring = NULL;
+    int cipher;
+
+    nobjs = ReadDERFromFile(&objs, certfile, PR_TRUE, &cipher, &ivstring, PR_TRUE /* certs only */);
+    if (nobjs <= 0) {
+        nss_ZFreeIf(objs);
+        return CKR_GENERAL_ERROR;
+    }
+
+    /* For now load as many certs as are in the file for CAs only */
+    if (cacert) {
+        for (i = 0; i < nobjs; i++) {
+            char nickname[1024];
+            objid = pem_nobjs + 1;
+
+            PR_snprintf(nickname, 1024, "%s - %d", certfile, i);
+
+            o = AddObjectIfNeeded(CKO_CERTIFICATE, pemCert, objs[i], NULL,
+                                   nickname, 0, slotID, NULL);
+            if (o == NULL) {
+                error = CKR_GENERAL_ERROR;
+                goto loser;
+            }
+
+            /* Add the CA trust object */
+            o = AddObjectIfNeeded(CKO_NETSCAPE_TRUST, pemTrust, objs[i], NULL,
+                                   nickname, 0, slotID, NULL);
+            if (o == NULL) {
+                error = CKR_GENERAL_ERROR;
+                goto loser;
+            }
+        }                       /* for */
+    } else {
+        objid = pem_nobjs + 1;
+        o = AddObjectIfNeeded(CKO_CERTIFICATE, pemCert, objs[0], NULL, certfile,
+                              objid, slotID, NULL);
+        if (o == NULL) {
+            error = CKR_GENERAL_ERROR;
+            goto loser;
+        }
+
+        o = NULL;
+
+        if (keyfile) {          /* add the private key */
+            SECItem **keyobjs = NULL;
+            int kobjs = 0;
+            kobjs =
+                ReadDERFromFile(&keyobjs, keyfile, PR_TRUE, &cipher,
+                                &ivstring, PR_FALSE);
+            if (kobjs < 1) {
+                error = CKR_GENERAL_ERROR;
+                goto loser;
+            }
+            o = AddObjectIfNeeded(CKO_PRIVATE_KEY, pemBareKey, objs[0],
+                                  keyobjs[0], certfile, objid, slotID, NULL);
+            if (o == NULL) {
+                error = CKR_GENERAL_ERROR;
+                goto loser;
+            }
+        }
+    }
+
+    nss_ZFreeIf(objs);
+    return CKR_OK;
+
+  loser:
+    nss_ZFreeIf(objs);
+    nss_ZFreeIf(o);
+    return error;
+}
+
+CK_RV
+pem_Initialize
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSUTF8 * configurationData
+)
+{
+    CK_RV rv;
+    /* parse the initialization string */
+    char **certstrings = NULL;
+    char *modparms = NULL;
+    PRInt32 numcerts = 0;
+    PRBool status, error = PR_FALSE;
+    int i;
+    CK_C_INITIALIZE_ARGS_PTR modArgs = NULL;
+
+    if (!fwInstance) return CKR_ARGUMENTS_BAD;
+
+    modArgs = NSSCKFWInstance_GetInitArgs(fwInstance);
+    if (modArgs &&
+       ((modArgs->flags & CKF_OS_LOCKING_OK) || (modArgs->CreateMutex != 0))) {
+        return CKR_CANT_LOCK;
+    }
+
+    if (pemInitialized) {
+        return CKR_OK;
+    }
+
+    RNG_RNGInit();
+
+    open_log();
+
+    plog("pem_Initialize\n");
+
+    if (!modArgs || !modArgs->LibraryParameters) {
+        goto done;
+    }
+    modparms = (char *) modArgs->LibraryParameters;
+    plog("Initialized with %s\n", modparms);
+
+    /*
+     * The initialization string format is a space-delimited file of
+     * pairs of paths which are delimited by a semi-colon. The first
+     * entry of the pair is the path to the certificate file. The
+     * second is the path to the key file.
+     *
+     * CA certificates do not need the semi-colon.
+     *
+     * Example:
+     *  /etc/certs/server.pem;/etc/certs/server.key /etc/certs/ca.pem
+     *
+     */
+    status =
+        pem_ParseString(modparms, ' ', &numcerts,
+                        &certstrings);
+    if (status == PR_FALSE) {
+        return CKR_ARGUMENTS_BAD;
+    }
+
+    for (i = 0; i < numcerts && error != PR_TRUE; i++) {
+        char *cert = certstrings[i];
+        PRInt32 attrcount = 0;
+        char **certattrs = NULL;
+        status = pem_ParseString(cert, ';', &attrcount, &certattrs);
+        if (status == PR_FALSE) {
+            error = PR_TRUE;
+            break;
+        }
+
+        if (error == PR_FALSE) {
+            if (attrcount == 1) /* CA certificate */
+                rv = AddCertificate(certattrs[0], NULL, PR_TRUE, 0);
+            else
+                rv = AddCertificate(certattrs[0], certattrs[1], PR_FALSE,
+                                    0);
+
+            if (rv != CKR_OK) {
+                error = PR_TRUE;
+                status = PR_FALSE;
+            }
+        }
+        pem_FreeParsedStrings(attrcount, certattrs);
+    }
+    pem_FreeParsedStrings(numcerts, certstrings);
+
+    if (status == PR_FALSE) {
+        return CKR_ARGUMENTS_BAD;
+    }
+
+    for (i = 0; i < NUM_SLOTS; i++)
+        token_needsLogin[i] = PR_FALSE;
+
+  done:
+
+    PR_AtomicSet(&pemInitialized, PR_TRUE);
+
+    return CKR_OK;
+}
+
+void
+pem_Finalize
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    plog("pem_Finalize\n");
+    if (!pemInitialized)
+        return;
+
+    nss_ZFreeIf(gobj);
+    gobj = NULL;
+
+    pem_nobjs = 0;
+    size = 0;
+    count = 0;
+
+    PR_AtomicSet(&pemInitialized, PR_FALSE);
+
+    return;
+}
+
+/*
+ * NSSCKMDInstance methods
+ */
+
+static CK_ULONG
+pem_mdInstance_GetNSlots
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (CK_ULONG) NUM_SLOTS;
+}
+
+static CK_VERSION
+pem_mdInstance_GetCryptokiVersion
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return pem_CryptokiVersion;
+}
+
+static NSSUTF8 *
+pem_mdInstance_GetManufacturerID
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSUTF8 *) pem_ManufacturerID;
+}
+
+static NSSUTF8 *
+pem_mdInstance_GetLibraryDescription
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSUTF8 *) pem_LibraryDescription;
+}
+
+static CK_VERSION
+pem_mdInstance_GetLibraryVersion
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return pem_LibraryVersion;
+}
+
+static CK_RV
+pem_mdInstance_GetSlots
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSCKMDSlot * slots[]
+)
+{
+    int i;
+    CK_RV pError;
+
+    for (i = 0; i < NUM_SLOTS; i++) {
+        slots[i] = (NSSCKMDSlot *) pem_NewSlot(fwInstance, &pError);
+        if (pError != CKR_OK)
+            return pError;
+    }
+    return CKR_OK;
+}
+
+CK_BBOOL
+pem_mdInstance_ModuleHandlesSessionObjects
+(
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return CK_TRUE;
+}
+
+NSS_IMPLEMENT_DATA const NSSCKMDInstance
+pem_mdInstance = {
+    (void *) NULL, /* etc */
+    pem_Initialize, /* Initialize */
+    pem_Finalize, /* Finalize */
+    pem_mdInstance_GetNSlots,
+    pem_mdInstance_GetCryptokiVersion,
+    pem_mdInstance_GetManufacturerID,
+    pem_mdInstance_GetLibraryDescription,
+    pem_mdInstance_GetLibraryVersion,
+    pem_mdInstance_ModuleHandlesSessionObjects,
+    pem_mdInstance_GetSlots,
+    NULL, /* WaitForSlotEvent */
+    (void *) NULL /* null terminator */
+};
diff --git a/a/nss/lib/ckfw/pem/pobject.c b/b/nss/lib/ckfw/pem/pobject.c
new file mode 100644
index 0000000..a13e531
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/pobject.c
@@ -0,0 +1,1240 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ckpem.h"
+#include "secasn1.h"
+#include "certt.h"
+#include "prprf.h"
+#include "pk11pub.h"
+
+/*
+ * pobject.c
+ *
+ * This file implements the NSSCKMDObject object for the
+ * "PEM objects" cryptoki module.
+ */
+
+NSS_EXTERN_DATA pemInternalObject **gobj;
+NSS_EXTERN_DATA int pem_nobjs;
+NSS_EXTERN_DATA int token_needsLogin[NUM_SLOTS];
+
+#define APPEND_LIST_ITEM(item) do { \
+    item->next = nss_ZNEW(NULL, pemObjectListItem); \
+    if (NULL == item->next) \
+      goto loser; \
+    item = item->next; \
+} while (0)
+
+const CK_ATTRIBUTE_TYPE certAttrs[] = {
+    CKA_CLASS,
+    CKA_TOKEN,
+    CKA_PRIVATE,
+    CKA_MODIFIABLE,
+    CKA_LABEL,
+    CKA_CERTIFICATE_TYPE,
+    CKA_SUBJECT,
+    CKA_ISSUER,
+    CKA_SERIAL_NUMBER,
+    CKA_VALUE
+};
+const PRUint32 certAttrsCount = NSS_PEM_ARRAY_SIZE(certAttrs);
+
+/* private keys, for now only support RSA */
+const CK_ATTRIBUTE_TYPE privKeyAttrs[] = {
+    CKA_CLASS,
+    CKA_TOKEN,
+    CKA_PRIVATE,
+    CKA_MODIFIABLE,
+    CKA_LABEL,
+    CKA_KEY_TYPE,
+    CKA_DERIVE,
+    CKA_LOCAL,
+    CKA_SUBJECT,
+    CKA_SENSITIVE,
+    CKA_DECRYPT,
+    CKA_SIGN,
+    CKA_SIGN_RECOVER,
+    CKA_UNWRAP,
+    CKA_EXTRACTABLE,
+    CKA_ALWAYS_SENSITIVE,
+    CKA_NEVER_EXTRACTABLE,
+    CKA_MODULUS,
+    CKA_PUBLIC_EXPONENT,
+};
+const PRUint32 privKeyAttrsCount = NSS_PEM_ARRAY_SIZE(privKeyAttrs);
+
+/* public keys, for now only support RSA */
+const CK_ATTRIBUTE_TYPE pubKeyAttrs[] = {
+    CKA_CLASS,
+    CKA_TOKEN,
+    CKA_PRIVATE,
+    CKA_MODIFIABLE,
+    CKA_LABEL,
+    CKA_KEY_TYPE,
+    CKA_DERIVE,
+    CKA_LOCAL,
+    CKA_SUBJECT,
+    CKA_ENCRYPT,
+    CKA_VERIFY,
+    CKA_VERIFY_RECOVER,
+    CKA_WRAP,
+    CKA_MODULUS,
+    CKA_PUBLIC_EXPONENT,
+};
+const PRUint32 pubKeyAttrsCount = NSS_PEM_ARRAY_SIZE(pubKeyAttrs);
+
+/* Trust */
+const CK_ATTRIBUTE_TYPE trustAttrs[] = {
+    CKA_CLASS,
+    CKA_TOKEN,
+    CKA_LABEL,
+    CKA_CERT_SHA1_HASH,
+    CKA_CERT_MD5_HASH,
+    CKA_ISSUER,
+    CKA_SUBJECT,
+    CKA_TRUST_SERVER_AUTH,
+    CKA_TRUST_CLIENT_AUTH,
+    CKA_TRUST_EMAIL_PROTECTION,
+    CKA_TRUST_CODE_SIGNING
+};
+const PRUint32 trustAttrsCount = NSS_PEM_ARRAY_SIZE(trustAttrs);
+
+static const CK_BBOOL ck_true = CK_TRUE;
+static const CK_BBOOL ck_false = CK_FALSE;
+static const CK_CERTIFICATE_TYPE ckc_x509 = CKC_X_509;
+static const CK_KEY_TYPE ckk_rsa = CKK_RSA;
+static const CK_OBJECT_CLASS cko_certificate = CKO_CERTIFICATE;
+static const CK_OBJECT_CLASS cko_private_key = CKO_PRIVATE_KEY;
+static const CK_OBJECT_CLASS cko_public_key = CKO_PUBLIC_KEY;
+static const CK_OBJECT_CLASS cko_trust = CKO_NETSCAPE_TRUST;
+static const CK_TRUST ckt_netscape_trusted = CKT_NETSCAPE_TRUSTED_DELEGATOR;
+static const NSSItem pem_trueItem = {
+    (void *) &ck_true, (PRUint32) sizeof(CK_BBOOL)
+};
+static const NSSItem pem_falseItem = {
+    (void *) &ck_false, (PRUint32) sizeof(CK_BBOOL)
+};
+static const NSSItem pem_x509Item = {
+    (void *) &ckc_x509, (PRUint32) sizeof(CK_ULONG)
+};
+static const NSSItem pem_rsaItem = {
+    (void *) &ckk_rsa, (PRUint32) sizeof(CK_KEY_TYPE)
+};
+static const NSSItem pem_certClassItem = {
+    (void *) &cko_certificate, (PRUint32) sizeof(CK_OBJECT_CLASS)
+};
+static const NSSItem pem_privKeyClassItem = {
+    (void *) &cko_private_key, (PRUint32) sizeof(CK_OBJECT_CLASS)
+};
+static const NSSItem pem_pubKeyClassItem = {
+    (void *) &cko_public_key, (PRUint32) sizeof(CK_OBJECT_CLASS)
+};
+static const NSSItem pem_trustClassItem = {
+    (void *) &cko_trust, (PRUint32) sizeof(CK_OBJECT_CLASS)
+};
+static const NSSItem pem_emptyItem = {
+    (void *) &ck_true, 0
+};
+static const NSSItem pem_trusted = {
+    (void *) &ckt_netscape_trusted, (PRUint32) sizeof(CK_TRUST)
+};
+
+/* SEC_SkipTemplate is already defined and exported by libnssutil */
+#ifdef SEC_SKIP_TEMPLATE
+/*
+ * Template for skipping a subitem.
+ *
+ * Note that it only makes sense to use this for decoding (when you want
+ * to decode something where you are only interested in one or two of
+ * the fields); you cannot encode a SKIP!
+ */
+const SEC_ASN1Template SEC_SkipTemplate[] = {
+    {SEC_ASN1_SKIP}
+};
+#endif
+
+/*
+ * Find the subjectName in a DER encoded certificate
+ */
+const SEC_ASN1Template SEC_CertSubjectTemplate[] = {
+    {SEC_ASN1_SEQUENCE,
+         0, NULL, sizeof(SECItem)} ,
+    {SEC_ASN1_EXPLICIT | SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+         SEC_ASN1_CONTEXT_SPECIFIC | 0,
+         0, SEC_SkipTemplate} ,  /* version */
+    {SEC_ASN1_SKIP},             /* serial number */
+    {SEC_ASN1_SKIP},             /* signature algorithm */
+    {SEC_ASN1_SKIP},             /* issuer */
+    {SEC_ASN1_SKIP},             /* validity */
+    {SEC_ASN1_ANY, 0, NULL},     /* subject */
+    {SEC_ASN1_SKIP_REST},
+    {0}
+};
+
+void
+pem_FetchLabel
+(
+    pemInternalObject * io
+)
+{
+    pemCertObject *co = &io->u.cert;
+
+    co->label.data = io->nickname;
+    co->label.size = strlen(io->nickname);
+}
+
+const NSSItem
+*pem_FetchCertAttribute
+(
+    pemInternalObject * io,
+    CK_ATTRIBUTE_TYPE type
+)
+{
+    switch (type) {
+    case CKA_CLASS:
+        plog("  fetch cert CKA_CLASS\n");
+        return &pem_certClassItem;
+    case CKA_TOKEN:
+        plog("  fetch cert CKA_TOKEN\n");
+        return &pem_trueItem;
+    case CKA_PRIVATE:
+        return &pem_falseItem;
+    case CKA_CERTIFICATE_TYPE:
+        plog("  fetch cert CKA_CERTIFICATE_TYPE\n");
+        return &pem_x509Item;
+    case CKA_LABEL:
+        if (0 == io->u.cert.label.size) {
+            pem_FetchLabel(io);
+        }
+        plog("  fetch cert CKA_LABEL %s\n", io->u.cert.label.data);
+        return &io->u.cert.label;
+    case CKA_SUBJECT:
+        plog("  fetch cert CKA_SUBJECT size %d\n", io->u.cert.subject.size);
+        return &io->u.cert.subject;
+    case CKA_ISSUER:
+        plog("  fetch cert CKA_ISSUER size %d\n", io->u.cert.issuer.size);
+        return &io->u.cert.issuer;
+    case CKA_SERIAL_NUMBER:
+        plog("  fetch cert CKA_SERIAL_NUMBER size %d value %08x\n", io->u.cert.serial.size, io->u.cert.serial.data);
+        return &io->u.cert.serial;
+    case CKA_VALUE:
+        if (0 == io->u.cert.derCert.size) {
+            io->u.cert.derCert.data = io->derCert->data;
+            io->u.cert.derCert.size = io->derCert->len;
+        }
+        plog("  fetch cert CKA_VALUE\n");
+        return &io->u.cert.derCert;
+    case CKA_ID:
+        plog("  fetch cert CKA_ID val=%s size=%d\n", (char *) io->id.data,
+             io->id.size);
+        return &io->id;
+    case CKA_TRUSTED:
+        plog("  fetch cert CKA_TRUSTED: returning NULL\n");
+        return NULL;
+    default:
+        plog("  fetching cert unknown type %d\n", type);
+        break;
+    }
+    return NULL;
+}
+
+const NSSItem *
+pem_FetchPrivKeyAttribute
+(
+    pemInternalObject * io,
+    CK_ATTRIBUTE_TYPE type
+)
+{
+    PRBool isCertType = (pemCert == io->type);
+    pemKeyParams *kp = isCertType ? &io->u.cert.key : &io->u.key.key;
+
+    switch (type) {
+    case CKA_CLASS:
+        return &pem_privKeyClassItem;
+    case CKA_TOKEN:
+    case CKA_LOCAL:
+    case CKA_SIGN:
+    case CKA_DECRYPT:
+    case CKA_SIGN_RECOVER:
+        return &pem_trueItem;
+    case CKA_SENSITIVE:
+    case CKA_PRIVATE: /* should move in the future */
+    case CKA_MODIFIABLE:
+    case CKA_DERIVE:
+    case CKA_UNWRAP:
+    case CKA_EXTRACTABLE: /* will probably move in the future */
+    case CKA_ALWAYS_SENSITIVE:
+    case CKA_NEVER_EXTRACTABLE:
+        return &pem_falseItem;
+    case CKA_KEY_TYPE:
+        return &pem_rsaItem;
+    case CKA_LABEL:
+        if (!isCertType) {
+            return &pem_emptyItem;
+        }
+        if (0 == io->u.cert.label.size) {
+            pem_FetchLabel(io);
+        }
+        plog("  fetch key CKA_LABEL %s\n", io->u.cert.label.data);
+        return &io->u.cert.label;
+    case CKA_SUBJECT:
+        if (!isCertType) {
+            return &pem_emptyItem;
+        }
+        plog("  fetch key CKA_SUBJECT %s\n", io->u.cert.label.data);
+        return &io->u.cert.subject;
+    case CKA_MODULUS:
+        if (0 == kp->modulus.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_MODULUS\n");
+        return &kp->modulus;
+    case CKA_PUBLIC_EXPONENT:
+        if (0 == kp->modulus.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_PUBLIC_EXPONENT\n");
+        return &kp->exponent;
+    case CKA_PRIVATE_EXPONENT:
+        if (0 == kp->privateExponent.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_PRIVATE_EXPONENT\n");
+        return &kp->privateExponent;
+    case CKA_PRIME_1:
+        if (0 == kp->prime1.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_PRIME_1\n");
+        return &kp->prime1;
+    case CKA_PRIME_2:
+        if (0 == kp->prime2.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_PRIME_2\n");
+        return &kp->prime2;
+    case CKA_EXPONENT_1:
+        if (0 == kp->exponent1.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_EXPONENT_1\n");
+        return &kp->exponent1;
+    case CKA_EXPONENT_2:
+        if (0 == kp->exponent2.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_EXPONENT_2\n");
+        return &kp->exponent2;
+    case CKA_COEFFICIENT:
+        if (0 == kp->coefficient.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        plog("  fetch key CKA_COEFFICIENT_2\n");
+        return &kp->coefficient;
+    case CKA_ID:
+        plog("  fetch key CKA_ID val=%s size=%d\n", (char *) io->id.data,
+             io->id.size);
+        return &io->id;
+    default:
+        return NULL;
+    }
+}
+
+const NSSItem *
+pem_FetchPubKeyAttribute
+(
+    pemInternalObject * io,
+    CK_ATTRIBUTE_TYPE type
+)
+{
+    PRBool isCertType = (pemCert == io->type);
+    pemKeyParams *kp = isCertType ? &io->u.cert.key : &io->u.key.key;
+
+    switch (type) {
+    case CKA_CLASS:
+        return &pem_pubKeyClassItem;
+    case CKA_TOKEN:
+    case CKA_LOCAL:
+    case CKA_ENCRYPT:
+    case CKA_VERIFY:
+    case CKA_VERIFY_RECOVER:
+        return &pem_trueItem;
+    case CKA_PRIVATE:
+    case CKA_MODIFIABLE:
+    case CKA_DERIVE:
+    case CKA_WRAP:
+        return &pem_falseItem;
+    case CKA_KEY_TYPE:
+        return &pem_rsaItem;
+    case CKA_LABEL:
+        if (!isCertType) {
+            return &pem_emptyItem;
+        }
+        if (0 == io->u.cert.label.size) {
+            pem_FetchLabel(io);
+        }
+        return &io->u.cert.label;
+    case CKA_SUBJECT:
+        if (!isCertType) {
+            return &pem_emptyItem;
+        }
+        return &io->u.cert.subject;
+    case CKA_MODULUS:
+        if (0 == kp->modulus.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        return &kp->modulus;
+    case CKA_PUBLIC_EXPONENT:
+        if (0 == kp->modulus.size) {
+            pem_PopulateModulusExponent(io);
+        }
+        return &kp->exponent;
+    case CKA_ID:
+        return &io->id;
+    default:
+        break;
+    }
+    return NULL;
+}
+
+const NSSItem *
+pem_FetchTrustAttribute
+(
+    pemInternalObject * io,
+    CK_ATTRIBUTE_TYPE type
+)
+{
+    static NSSItem hash;
+    SECStatus rv;
+
+    switch (type) {
+    case CKA_CLASS:
+        return &pem_trustClassItem;
+    case CKA_TOKEN:
+        return &pem_trueItem;
+    case CKA_PRIVATE:
+        return &pem_falseItem;
+    case CKA_CERTIFICATE_TYPE:
+        return &pem_x509Item;
+    case CKA_LABEL:
+        if (0 == io->u.cert.label.size) {
+            pem_FetchLabel(io);
+        }
+        plog("  fetch trust CKA_LABEL %s\n", io->u.cert.label.data);
+        return &io->u.cert.label;
+    case CKA_SUBJECT:
+        plog("  fetch trust CKA_SUBJECT\n");
+        return NULL;
+    case CKA_ISSUER:
+        plog("  fetch trust CKA_ISSUER\n");
+        return &io->u.cert.issuer;
+    case CKA_SERIAL_NUMBER:
+        plog("  fetch trust CKA_SERIAL_NUMBER size %d value %08x\n", io->u.cert.serial.size, io->u.cert.serial.data);
+        return &io->u.cert.serial;
+    case CKA_VALUE:
+        return &pem_trueItem;
+    case CKA_ID:
+        plog("  fetch trust CKA_ID val=%s size=%d\n", (char *) io->id.data,
+             io->id.size);
+        return &io->id;
+    case CKA_TRUSTED:
+        return &pem_trusted;
+    case CKA_TRUST_SERVER_AUTH:
+        return &pem_trusted;
+    case CKA_TRUST_CLIENT_AUTH:
+        return &pem_trusted;
+    case CKA_TRUST_CODE_SIGNING:
+        return &pem_trusted;
+    case CKA_TRUST_EMAIL_PROTECTION:
+        return &pem_trusted;
+    case CKA_TRUST_IPSEC_END_SYSTEM:
+        return &pem_trusted;
+    case CKA_TRUST_IPSEC_TUNNEL:
+        return &pem_trusted;
+    case CKA_TRUST_IPSEC_USER:
+        return &pem_trusted;
+    case CKA_TRUST_TIME_STAMPING:
+        return &pem_trusted;
+    case CKA_TRUST_STEP_UP_APPROVED:
+        return &pem_falseItem;
+    case CKA_CERT_SHA1_HASH:
+        hash.size = 0;
+        hash.data = NULL;
+        nsslibc_memset(io->u.cert.sha1_hash, 0, SHA1_LENGTH);
+        rv = SHA1_HashBuf(io->u.cert.sha1_hash, io->derCert->data,
+                          io->derCert->len);
+        if (rv == SECSuccess) {
+            hash.data = io->u.cert.sha1_hash;
+            hash.size = sizeof(io->u.cert.sha1_hash);
+        }
+        return &hash;
+    case CKA_CERT_MD5_HASH:
+        hash.size = 0;
+        hash.data = NULL;
+        nsslibc_memset(io->u.cert.sha1_hash, 0, MD5_LENGTH);
+        rv = MD5_HashBuf(io->u.cert.sha1_hash, io->derCert->data,
+                         io->derCert->len);
+        if (rv == SECSuccess) {
+            hash.data = io->u.cert.sha1_hash;
+            hash.size = sizeof(io->u.cert.sha1_hash);
+        }
+        return &hash;
+    default:
+        return &pem_trusted;
+        break;
+    }
+    return NULL;
+}
+
+const NSSItem *
+pem_FetchAttribute
+(
+    pemInternalObject * io,
+    CK_ATTRIBUTE_TYPE type
+)
+{
+    CK_ULONG i;
+
+    if (io->type == pemRaw) {
+        for (i = 0; i < io->u.raw.n; i++) {
+            if (type == io->u.raw.types[i]) {
+                return &io->u.raw.items[i];
+            }
+        }
+        return NULL;
+    }
+    /* deal with the common attributes */
+    switch (io->objClass) {
+    case CKO_CERTIFICATE:
+        return pem_FetchCertAttribute(io, type);
+    case CKO_PRIVATE_KEY:
+        return pem_FetchPrivKeyAttribute(io, type);
+    case CKO_NETSCAPE_TRUST:
+        return pem_FetchTrustAttribute(io, type);
+    case CKO_PUBLIC_KEY:
+        return pem_FetchPubKeyAttribute(io, type);
+    }
+    return NULL;
+}
+
+/*
+ * Destroy internal object or list object if refCount becomes zero (after
+ * decrement). Safe to call with NULL argument.
+ */
+void
+pem_DestroyInternalObject
+(
+    pemInternalObject * io
+)
+{
+    if (NULL == io)
+        /* nothing to destroy */
+        return;
+
+    if (NULL != io->list) {
+        /* destroy list object */
+        pemObjectListItem *item = io->list;
+        while (item) {
+            pemObjectListItem *next = item->next;
+
+            /* recursion of maximal depth 1 */
+            pem_DestroyInternalObject(item->io);
+
+            nss_ZFreeIf(item);
+            item = next;
+        }
+        nss_ZFreeIf(io);
+        return;
+    }
+
+    io->refCount --;
+    if (0 < io->refCount)
+        return;
+
+    /* destroy internal object */
+    switch (io->type) {
+    case pemRaw:
+        return;
+    case pemCert:
+        nss_ZFreeIf(io->u.cert.labelData);
+        nss_ZFreeIf(io->u.cert.key.privateKey);
+        nss_ZFreeIf(io->u.cert.key.pubKey);
+        /* go through */
+    case pemTrust:
+        nss_ZFreeIf(io->id.data);
+        nss_ZFreeIf(io->nickname);
+        nss_ZFreeIf(io->derCert->data);
+        nss_ZFreeIf(io->derCert);
+        if (io->u.cert.subject.size > 0) {
+            nss_ZFreeIf(io->u.cert.subject.data);
+        }
+        if (io->u.cert.issuer.size > 0) {
+            nss_ZFreeIf(io->u.cert.issuer.data);
+        }
+        if (io->u.cert.serial.size > 0) {
+            nss_ZFreeIf(io->u.cert.serial.data);
+        }
+        break;
+    case pemBareKey:
+        SECITEM_FreeItem(io->u.key.key.privateKeyOrig, PR_TRUE);
+        nss_ZFreeIf(io->u.key.key.coefficient.data);
+        nss_ZFreeIf(io->u.key.key.exponent2.data);
+        nss_ZFreeIf(io->u.key.key.exponent1.data);
+        nss_ZFreeIf(io->u.key.key.prime2.data);
+        nss_ZFreeIf(io->u.key.key.prime1.data);
+        nss_ZFreeIf(io->u.key.key.privateExponent.data);
+        nss_ZFreeIf(io->u.key.key.exponent.data);
+        nss_ZFreeIf(io->u.key.key.modulus.data);
+        nss_ZFreeIf(io->u.key.key.privateKey->data);
+        nss_ZFreeIf(io->u.key.key.privateKey);
+        nss_ZFreeIf(io->u.key.key.pubKey);
+        nss_ZFreeIf(io->id.data);
+        nss_ZFreeIf(io->nickname);
+        nss_ZFreeIf(io->derCert->data);
+        nss_ZFreeIf(io->derCert);
+
+        /* strdup'd in ReadDERFromFile */
+        if (io->u.key.ivstring)
+            free(io->u.key.ivstring);
+        break;
+    }
+
+    if (NULL != gobj)
+        /* remove reference to self from the global array */
+        gobj[io->gobjIndex] = NULL;
+
+    nss_ZFreeIf(io);
+    return;
+}
+
+/*
+ * Finalize - needed
+ * Destroy - CKR_SESSION_READ_ONLY
+ * IsTokenObject - CK_TRUE
+ * GetAttributeCount
+ * GetAttributeTypes
+ * GetAttributeSize
+ * GetAttribute
+ * SetAttribute - unneeded
+ * GetObjectSize - unneeded
+ */
+
+static void
+pem_mdObject_Finalize
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    pem_DestroyInternalObject((pemInternalObject *) mdObject->etc);
+}
+
+static CK_RV
+pem_mdObject_Destroy
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    pemInternalObject *io = (pemInternalObject *) mdObject->etc;
+
+    pem_DestroyInternalObject(io);
+    return CKR_OK;
+}
+
+static CK_BBOOL
+pem_mdObject_IsTokenObject
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return CK_TRUE;
+}
+
+static CK_ULONG
+pem_mdObject_GetAttributeCount
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    pemInternalObject *io = (pemInternalObject *) mdObject->etc;
+
+    if (NULL != io->list) {
+        /* list object --> use the first item in the list */
+        NSSCKMDObject *md = &(io->list->io->mdObject);
+        return md->GetAttributeCount(md, fwObject, mdSession, fwSession,
+                                     mdToken, fwToken, mdInstance, fwInstance,
+                                     pError);
+    }
+
+    if (pemRaw == io->type) {
+        return io->u.raw.n;
+    }
+    switch (io->objClass) {
+    case CKO_CERTIFICATE:
+        return certAttrsCount;
+    case CKO_PUBLIC_KEY:
+        return pubKeyAttrsCount;
+    case CKO_PRIVATE_KEY:
+        return privKeyAttrsCount;
+    case CKO_NETSCAPE_TRUST:
+        return trustAttrsCount;
+    default:
+        break;
+    }
+    return 0;
+}
+
+static CK_RV
+pem_mdObject_GetAttributeTypes
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_ATTRIBUTE_TYPE_PTR typeArray,
+    CK_ULONG ulCount
+)
+{
+    pemInternalObject *io = (pemInternalObject *) mdObject->etc;
+    CK_ULONG i;
+    CK_RV error = CKR_OK;
+    const CK_ATTRIBUTE_TYPE *attrs = NULL;
+    CK_ULONG size;
+
+    if (NULL != io->list) {
+        /* list object --> use the first item in the list */
+        NSSCKMDObject *md = &(io->list->io->mdObject);
+        return md->GetAttributeTypes(md, fwObject, mdSession, fwSession,
+                                     mdToken, fwToken, mdInstance, fwInstance,
+                                     typeArray, ulCount);
+    }
+
+    size = pem_mdObject_GetAttributeCount(mdObject, fwObject, mdSession,
+                                          fwSession, mdToken, fwToken, mdInstance,
+                                          fwInstance, &error);
+
+    if (size != ulCount) {
+        return CKR_BUFFER_TOO_SMALL;
+    }
+    if (io->type == pemRaw) {
+        attrs = io->u.raw.types;
+    } else
+        switch (io->objClass) {
+        case CKO_CERTIFICATE:
+            attrs = certAttrs;
+            break;
+        case CKO_PUBLIC_KEY:
+            attrs = pubKeyAttrs;
+            break;
+        case CKO_PRIVATE_KEY:
+            attrs = privKeyAttrs;
+            break;
+        default:
+            return CKR_OK;
+        }
+
+    for (i = 0; i < size; i++) {
+        typeArray[i] = attrs[i];
+    }
+
+    return CKR_OK;
+}
+
+static CK_ULONG
+pem_mdObject_GetAttributeSize
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_ATTRIBUTE_TYPE attribute,
+    CK_RV * pError
+)
+{
+    pemInternalObject *io = (pemInternalObject *) mdObject->etc;
+    const NSSItem *b;
+
+    if (NULL != io->list) {
+        /* list object --> use the first item in the list */
+        NSSCKMDObject *md = &(io->list->io->mdObject);
+        return md->GetAttributeSize(md, fwObject, mdSession, fwSession,
+                                    mdToken, fwToken, mdInstance, fwInstance,
+                                    attribute, pError);
+    }
+
+    b = pem_FetchAttribute(io, attribute);
+
+    if ((const NSSItem *) NULL == b) {
+        *pError = CKR_ATTRIBUTE_TYPE_INVALID;
+        return 0;
+    }
+    return b->size;
+}
+
+static NSSCKFWItem
+pem_mdObject_GetAttribute
+(
+    NSSCKMDObject * mdObject,
+    NSSCKFWObject * fwObject,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_ATTRIBUTE_TYPE attribute,
+    CK_RV * pError
+)
+{
+    NSSCKFWItem mdItem;
+    pemInternalObject *io = (pemInternalObject *) mdObject->etc;
+
+    if (NULL != io->list) {
+        /* list object --> use the first item in the list */
+        NSSCKMDObject *md = &(io->list->io->mdObject);
+        return md->GetAttribute(md, fwObject, mdSession, fwSession,
+                                mdToken, fwToken, mdInstance, fwInstance,
+                                attribute, pError);
+    }
+
+    mdItem.needsFreeing = PR_FALSE;
+    mdItem.item = (NSSItem *) pem_FetchAttribute(io, attribute);
+
+    if ((NSSItem *) NULL == mdItem.item) {
+        *pError = CKR_ATTRIBUTE_TYPE_INVALID;
+    }
+
+    return mdItem;
+}
+
+/*
+ * get an attribute from a template. Value is returned in NSS item.
+ * data for the item is owned by the template.
+ */
+CK_RV
+pem_GetAttribute
+(
+    CK_ATTRIBUTE_TYPE type,
+    CK_ATTRIBUTE * template,
+    CK_ULONG templateSize,
+    NSSItem * item
+)
+{
+    CK_ULONG i;
+
+    for (i = 0; i < templateSize; i++) {
+        if (template[i].type == type) {
+            item->data = template[i].pValue;
+            item->size = template[i].ulValueLen;
+            return CKR_OK;
+        }
+    }
+    return CKR_TEMPLATE_INCOMPLETE;
+}
+
+/*
+ * get an attribute which is type CK_ULONG.
+ */
+CK_ULONG
+pem_GetULongAttribute
+(
+    CK_ATTRIBUTE_TYPE type,
+    CK_ATTRIBUTE * template,
+    CK_ULONG templateSize,
+    CK_RV * pError
+)
+{
+    NSSItem item;
+
+    *pError = pem_GetAttribute(type, template, templateSize, &item);
+    if (CKR_OK != *pError) {
+        return (CK_ULONG) 0;
+    }
+    if (item.size != sizeof(CK_ULONG)) {
+        *pError = CKR_ATTRIBUTE_VALUE_INVALID;
+        return (CK_ULONG) 0;
+    }
+    return *(CK_ULONG *) item.data;
+}
+
+/*  
+ * get an attribute which is type CK_BBOOL.
+ */
+CK_BBOOL
+pem_GetBoolAttribute
+(
+    CK_ATTRIBUTE_TYPE type,
+    CK_ATTRIBUTE * template,
+    CK_ULONG templateSize,
+    CK_RV * pError
+)
+{
+    NSSItem item;
+
+    *pError = pem_GetAttribute(type, template, templateSize, &item);
+    if (CKR_OK != *pError) {
+        return (CK_BBOOL) 0;
+    }
+    if (item.size != sizeof(CK_BBOOL)) {
+        *pError = CKR_ATTRIBUTE_VALUE_INVALID;
+        return (CK_BBOOL) 0;
+    }
+    return *(CK_BBOOL *) item.data;
+}
+
+/*
+ * Get a string attribute. Caller needs to free this.
+ */
+char *
+pem_GetStringAttribute
+(
+    CK_ATTRIBUTE_TYPE type,
+    CK_ATTRIBUTE * template,
+    CK_ULONG templateSize,
+    CK_RV * pError
+)
+{
+    NSSItem item;
+    char *str;
+
+    /* get the attribute */
+    *pError = pem_GetAttribute(type, template, templateSize, &item);
+    if (CKR_OK != *pError) {
+        return (char *) NULL;
+    }
+    /* make sure it is null terminated */
+    str = nss_ZNEWARRAY(NULL, char, item.size + 1);
+    if ((char *) NULL == str) {
+        *pError = CKR_HOST_MEMORY;
+        return (char *) NULL;
+    }
+
+    nsslibc_memcpy(str, item.data, item.size);
+    str[item.size] = 0;
+
+    return str;
+}
+
+static const NSSCKMDObject
+pem_prototype_mdObject = {
+    (void *) NULL,              /* etc */
+    pem_mdObject_Finalize,
+    pem_mdObject_Destroy,
+    pem_mdObject_IsTokenObject,
+    pem_mdObject_GetAttributeCount,
+    pem_mdObject_GetAttributeTypes,
+    pem_mdObject_GetAttributeSize,
+    pem_mdObject_GetAttribute,
+    NULL,                       /* FreeAttribute */
+    NULL,                       /* SetAttribute */
+    NULL,                       /* GetObjectSize */
+    (void *) NULL               /* null terminator */
+};
+
+NSS_IMPLEMENT NSSCKMDObject *
+pem_CreateMDObject
+(
+    NSSArena * arena,
+    pemInternalObject * io,
+    CK_RV * pError
+)
+{
+    if ((void *) NULL == io->mdObject.etc) {
+        (void) nsslibc_memcpy(&io->mdObject, &pem_prototype_mdObject,
+                              sizeof(pem_prototype_mdObject));
+        io->mdObject.etc = (void *) io;
+    }
+
+    return &io->mdObject;
+}
+
+/*
+ * Each object has an identifier. For a certificate and key pair this id
+ * needs to be the same so we use the right combination. If the target object
+ * is a key we first look to see if its certificate was already added and if
+ * so, use that id. The same thing is done when a key is added.
+ */
+NSS_EXTERN NSSCKMDObject *
+pem_CreateObject
+(
+    NSSCKFWInstance * fwInstance,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    CK_ATTRIBUTE_PTR pTemplate,
+    CK_ULONG ulAttributeCount,
+    CK_RV * pError
+)
+{
+    CK_OBJECT_CLASS objClass;
+    CK_BBOOL isToken;
+    NSSCKFWSlot *fwSlot;
+    CK_SLOT_ID slotID;
+    CK_BBOOL cacert;
+    char *filename;
+    SECItem **derlist = NULL;
+    int nobjs = 0;
+    int i;
+    int objid;
+    pemToken *token;
+    int cipher;
+    char *ivstring = NULL;
+    pemInternalObject *listObj = NULL;
+    pemObjectListItem *listItem = NULL;
+
+    /*
+     * only create token objects
+     */
+    isToken = pem_GetBoolAttribute(CKA_TOKEN, pTemplate,
+                                   ulAttributeCount, pError);
+    if (CKR_OK != *pError) {
+        return (NSSCKMDObject *) NULL;
+    }
+    if (!isToken) {
+        *pError = CKR_ATTRIBUTE_VALUE_INVALID;
+        return (NSSCKMDObject *) NULL;
+    }
+
+    /* What slot are we adding the object to? */
+    fwSlot = nssCKFWSession_GetFWSlot(fwSession);
+    if ((NSSCKFWSlot *) NULL == fwSlot) {
+        *pError = CKR_ATTRIBUTE_VALUE_INVALID;
+        *pError = CKR_GENERAL_ERROR;
+        return (NSSCKMDObject *) NULL;
+
+    }
+    slotID = nssCKFWSlot_GetSlotID(fwSlot);
+
+    token = (pemToken *) mdToken->etc;
+
+    /*
+     * only create keys and certs.
+     */
+    objClass = pem_GetULongAttribute(CKA_CLASS, pTemplate,
+                                     ulAttributeCount, pError);
+    if (CKR_OK != *pError) {
+        return (NSSCKMDObject *) NULL;
+    }
+
+    cacert = pem_GetBoolAttribute(CKA_TRUST, pTemplate,
+                                  ulAttributeCount, pError);
+
+    filename = pem_GetStringAttribute(CKA_LABEL, pTemplate,
+                                      ulAttributeCount, pError);
+    if (CKR_OK != *pError) {
+        return (NSSCKMDObject *) NULL;
+    }
+
+#ifdef notdef
+    if (objClass == CKO_PUBLIC_KEY) {
+        return CKR_OK;  /* fake public key creation, happens as a side effect of
+                         * private key creation */
+    }
+#endif
+
+    listObj = nss_ZNEW(NULL, pemInternalObject);
+    if (NULL == listObj) {
+        nss_ZFreeIf(filename);
+        return NULL;
+    }
+
+    listItem = listObj->list = nss_ZNEW(NULL, pemObjectListItem);
+    if (NULL == listItem) {
+        nss_ZFreeIf(listObj);
+        nss_ZFreeIf(filename);
+        return NULL;
+    }
+
+    if (objClass == CKO_CERTIFICATE) {
+        nobjs = ReadDERFromFile(&derlist, filename, PR_TRUE, &cipher, &ivstring, PR_TRUE /* certs only */);
+        if (nobjs < 1)
+            goto loser;
+
+        /* We're just adding a cert, we'll assume the key is next */
+        objid = pem_nobjs + 1;
+
+        if (cacert) {
+            /* Add the certificate. There may be more than one */
+            int c;
+            for (c = 0; c < nobjs; c++) {
+                char nickname[1024];
+                objid = pem_nobjs + 1;
+
+                PR_snprintf(nickname, 1024, "%s - %d", filename, c);
+
+                if (c)
+                    APPEND_LIST_ITEM(listItem);
+                listItem->io = AddObjectIfNeeded(CKO_CERTIFICATE, pemCert,
+                                                 derlist[c], NULL, nickname, 0,
+                                                 slotID, NULL);
+                if (listItem->io == NULL)
+                    goto loser;
+
+                /* Add the trust object */
+                APPEND_LIST_ITEM(listItem);
+                listItem->io = AddObjectIfNeeded(CKO_NETSCAPE_TRUST, pemTrust,
+                                                 derlist[c], NULL, nickname, 0,
+                                                 slotID, NULL);
+                if (listItem->io == NULL)
+                    goto loser;
+            }
+        } else {
+            listItem->io = AddObjectIfNeeded(CKO_CERTIFICATE, pemCert,
+                                             derlist[0], NULL, filename, objid,
+                                             slotID, NULL);
+            if (listItem->io == NULL)
+                goto loser;
+        }
+    } else if (objClass == CKO_PRIVATE_KEY) {
+        /* Brute force: find the id of the certificate, if any, in this slot */
+        int i;
+        SECItem certDER;
+        CK_SESSION_HANDLE hSession;
+        PRBool added;
+
+        nobjs = ReadDERFromFile(&derlist, filename, PR_TRUE, &cipher, &ivstring, PR_FALSE /* keys only */);
+        if (nobjs < 1)
+            goto loser;
+
+        certDER.len = 0; /* in case there is no equivalent cert */
+        certDER.data = NULL;
+
+        objid = -1;
+        for (i = 0; i < pem_nobjs; i++) {
+            if (NULL == gobj[i])
+                continue;
+
+            if ((slotID == gobj[i]->slotID) && (gobj[i]->type == pemCert)) {
+                objid = atoi(gobj[i]->id.data);
+                certDER.data =
+                    (void *) nss_ZAlloc(NULL, gobj[i]->derCert->len);
+
+                if (certDER.data == NULL)
+                    goto loser;
+
+                certDER.len = gobj[i]->derCert->len;
+                nsslibc_memcpy(certDER.data, gobj[i]->derCert->data,
+                               gobj[i]->derCert->len);
+            }
+        }
+
+        /* We're just adding a key, we'll assume the cert is next */
+        if (objid == -1)
+            objid = pem_nobjs + 1;
+
+        listItem->io =  AddObjectIfNeeded(CKO_PRIVATE_KEY, pemBareKey, &certDER,
+                                          derlist[0], filename, objid, slotID,
+                                          &added);
+        if (listItem->io == NULL)
+            goto loser;
+
+        listItem->io->u.key.ivstring = ivstring;
+        listItem->io->u.key.cipher = cipher;
+        nss_ZFreeIf(certDER.data);
+
+        /* If the key was encrypted then free the session to make it appear that
+         * the token was removed so we can force a login.
+         */
+        if (cipher && added) {
+            /* FIXME: Why 1.0s? Is it enough? Isn't it too much?
+             * What about e.g. 3.14s? */
+            PRIntervalTime onesec = PR_SecondsToInterval(1);
+            token_needsLogin[slotID - 1] = PR_TRUE;
+
+            /* We have to sleep so that NSS will notice that the token was
+             * removed.
+             */
+            PR_Sleep(onesec);
+            hSession =
+                nssCKFWInstance_FindSessionHandle(fwInstance, fwSession);
+            nssCKFWInstance_DestroySessionHandle(fwInstance, hSession);
+        } else {
+            *pError = CKR_KEY_UNEXTRACTABLE;
+        }
+    } else {
+        *pError = CKR_ATTRIBUTE_VALUE_INVALID;
+    }
+
+  loser:
+
+    for (i = 0; i < nobjs; i++) {
+        free(derlist[i]->data);
+        free(derlist[i]);
+    }
+    nss_ZFreeIf(filename);
+    nss_ZFreeIf(derlist);
+    if ((pemInternalObject *) NULL == listItem->io) {
+        pem_DestroyInternalObject(listObj);
+        return (NSSCKMDObject *) NULL;
+    }
+    return pem_CreateMDObject(NULL, listObj, pError);
+}
diff --git a/a/nss/lib/ckfw/pem/prsa.c b/b/nss/lib/ckfw/pem/prsa.c
new file mode 100644
index 0000000..d42e9f7
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/prsa.c
@@ -0,0 +1,702 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ckpem.h"
+#include "secdert.h"
+#include "secoid.h"
+#include "nssckmdt.h"
+
+#define SSL3_SHAMD5_HASH_SIZE  36       /* LEN_MD5 (16) + LEN_SHA1 (20) */
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+/*
+ * prsa.c
+ *
+ * This file implements the NSSCKMDMechnaism and NSSCKMDCryptoOperation objects
+ * for the RSA operation.
+ */
+
+const SEC_ASN1Template pem_RSAPrivateKeyTemplate[] = {
+    {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(pemLOWKEYPrivateKey)} ,
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.version)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.modulus)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.publicExponent)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.privateExponent)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.prime1)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.prime2)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.exponent1)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.exponent2)},
+    {SEC_ASN1_INTEGER, offsetof(pemLOWKEYPrivateKey, u.rsa.coefficient)},
+    {0}
+};
+
+static const SEC_ASN1Template pem_AttributeTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 
+      0, NULL, sizeof(NSSLOWKEYAttribute) },
+    { SEC_ASN1_OBJECT_ID, offsetof(NSSLOWKEYAttribute, attrType) },
+    { SEC_ASN1_SET_OF | SEC_ASN1_XTRN, offsetof(NSSLOWKEYAttribute, attrValue), 
+      SEC_ASN1_SUB(SEC_AnyTemplate) },
+    { 0 }
+};
+
+static const SEC_ASN1Template pem_SetOfAttributeTemplate[] = {
+    { SEC_ASN1_SET_OF, 0, pem_AttributeTemplate },
+};
+
+const SEC_ASN1Template pem_PrivateKeyInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+      0, NULL, sizeof(NSSLOWKEYPrivateKeyInfo) },
+    { SEC_ASN1_INTEGER,
+      offsetof(NSSLOWKEYPrivateKeyInfo,version) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+      offsetof(NSSLOWKEYPrivateKeyInfo,algorithm),
+      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OCTET_STRING,
+      offsetof(NSSLOWKEYPrivateKeyInfo,privateKey) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+      offsetof(NSSLOWKEYPrivateKeyInfo, attributes),
+      pem_SetOfAttributeTemplate },
+    { 0 }
+};
+
+/* Declarations */
+SECStatus pem_RSA_Sign(pemLOWKEYPrivateKey * key, unsigned char *output,
+                       unsigned int *outputLen, unsigned int maxOutputLen,
+                       unsigned char *input, unsigned int inputLen);
+SECStatus pem_RSA_DecryptBlock(pemLOWKEYPrivateKey * key,
+                               unsigned char *output, unsigned int *outputLen,
+                               unsigned int maxOutputLen, unsigned char *input,
+                               unsigned int inputLen);
+
+void prepare_low_rsa_priv_key_for_asn1(pemLOWKEYPrivateKey * key)
+{
+    key->u.rsa.modulus.type = siUnsignedInteger;
+    key->u.rsa.publicExponent.type = siUnsignedInteger;
+    key->u.rsa.privateExponent.type = siUnsignedInteger;
+    key->u.rsa.prime1.type = siUnsignedInteger;
+    key->u.rsa.prime2.type = siUnsignedInteger;
+    key->u.rsa.exponent1.type = siUnsignedInteger;
+    key->u.rsa.exponent2.type = siUnsignedInteger;
+    key->u.rsa.coefficient.type = siUnsignedInteger;
+}
+
+unsigned int
+pem_PrivateModulusLen(pemLOWKEYPrivateKey * privk)
+{
+
+    unsigned char b0;
+
+    switch (privk->keyType) {
+    case pemLOWKEYRSAKey:
+        b0 = privk->u.rsa.modulus.data[0];
+        return b0 ? privk->u.rsa.modulus.len : privk->u.rsa.modulus.len -
+            1;
+    default:
+        break;
+    }
+    return 0;
+}
+
+struct SFTKHashSignInfoStr {
+    SECOidTag hashOid;
+    pemLOWKEYPrivateKey *key;
+};
+typedef struct SFTKHashSignInfoStr SFTKHashSignInfo;
+
+void
+pem_DestroyPrivateKey(pemLOWKEYPrivateKey * privk)
+{
+    if (privk && privk->arena) {
+        PORT_FreeArena(privk->arena, PR_TRUE);
+    }
+    nss_ZFreeIf(privk);
+}
+
+/* decode and parse the rawkey into the lpk structure */
+static pemLOWKEYPrivateKey *
+pem_getPrivateKey(PLArenaPool *arena, SECItem *rawkey, CK_RV * pError, NSSItem *modulus)
+{
+    pemLOWKEYPrivateKey *lpk = NULL;
+    SECStatus rv = SECFailure;
+    NSSLOWKEYPrivateKeyInfo *pki = NULL;
+    SECItem *keysrc = NULL;
+
+    /* make sure SECOID is initialized - not sure why we have to do this outside of nss_Init */
+    if (SECSuccess != (rv = SECOID_Init())) {
+        *pError = CKR_GENERAL_ERROR;
+        return NULL; /* wha???? */
+    }
+
+    pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena, 
+                                                     sizeof(NSSLOWKEYPrivateKeyInfo));
+    if(!pki) {
+        *pError = CKR_HOST_MEMORY;
+        goto done;
+    }
+
+    /* let's first see if this is a "raw" RSA private key or an RSA private key in PKCS#8 format */
+    rv = SEC_ASN1DecodeItem(arena, pki, pem_PrivateKeyInfoTemplate, rawkey);
+    if (rv != SECSuccess) {
+        /* not PKCS#8 - assume it's a "raw" RSA private key */
+        keysrc = rawkey;
+    } else if (SECOID_GetAlgorithmTag(&pki->algorithm) == SEC_OID_PKCS1_RSA_ENCRYPTION) {
+        keysrc = &pki->privateKey;        
+    } else { /* unsupported */
+        *pError = CKR_FUNCTION_NOT_SUPPORTED;
+        goto done;
+    }
+
+    lpk = (pemLOWKEYPrivateKey *) nss_ZAlloc(NULL,
+                                             sizeof(pemLOWKEYPrivateKey));
+    if (lpk == NULL) {
+        *pError = CKR_HOST_MEMORY;
+        goto done;
+    }
+
+    lpk->arena = arena;
+    lpk->keyType = pemLOWKEYRSAKey;
+    prepare_low_rsa_priv_key_for_asn1(lpk);
+
+    /* I don't know what this is supposed to accomplish.  We free the old
+       modulus data and set it again, making a copy of the new data.
+       But we just allocated a new empty key structure above with
+       nss_ZAlloc.  So lpk->u.rsa.modulus.data is NULL and
+       lpk->u.rsa.modulus.len.  If the intention is to free the old 
+       modulus data, why not just set it to NULL after freeing?  Why
+       go through this unnecessary and confusing copying code?
+    */
+    if (modulus) {
+        nss_ZFreeIf(modulus->data);
+        modulus->data = (void *) nss_ZAlloc(NULL, lpk->u.rsa.modulus.len);
+        modulus->size = lpk->u.rsa.modulus.len;
+        nsslibc_memcpy(modulus->data, lpk->u.rsa.modulus.data,
+                       lpk->u.rsa.modulus.len);
+    }
+
+    /* decode the private key and any algorithm parameters */
+    rv = SEC_QuickDERDecodeItem(arena, lpk, pem_RSAPrivateKeyTemplate,
+                                keysrc);
+
+    if (rv != SECSuccess) {
+        goto done;
+    }
+
+done:
+    return lpk;
+}
+
+void
+pem_PopulateModulusExponent(pemInternalObject * io)
+{
+    const NSSItem *classItem = pem_FetchAttribute(io, CKA_CLASS);
+    const NSSItem *keyType = pem_FetchAttribute(io, CKA_KEY_TYPE);
+    pemLOWKEYPrivateKey *lpk = NULL;
+    PLArenaPool *arena;
+    CK_RV pError = 0;
+
+    /* make sure we have the right objects */
+    if (((const NSSItem *) NULL == classItem) ||
+        (sizeof(CK_OBJECT_CLASS) != classItem->size) ||
+        (CKO_PRIVATE_KEY != *(CK_OBJECT_CLASS *) classItem->data) ||
+        ((const NSSItem *) NULL == keyType) ||
+        (sizeof(CK_KEY_TYPE) != keyType->size) ||
+        (CKK_RSA != *(CK_KEY_TYPE *) keyType->data)) {
+        return;
+    }
+
+    arena = PORT_NewArena(2048);
+    if (!arena) {
+        return;
+    }
+
+    lpk = pem_getPrivateKey(arena, io->u.key.key.privateKey, &pError, NULL);
+    if (lpk == NULL) {
+        PORT_FreeArena(arena, PR_FALSE);
+        return;
+    }
+
+    nss_ZFreeIf(io->u.key.key.modulus.data);
+    io->u.key.key.modulus.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.modulus.len);
+    io->u.key.key.modulus.size = lpk->u.rsa.modulus.len;
+    nsslibc_memcpy(io->u.key.key.modulus.data, lpk->u.rsa.modulus.data,
+                   lpk->u.rsa.modulus.len);
+
+    nss_ZFreeIf(io->u.key.key.exponent.data);
+    io->u.key.key.exponent.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.publicExponent.len);
+    io->u.key.key.exponent.size = lpk->u.rsa.publicExponent.len;
+    nsslibc_memcpy(io->u.key.key.exponent.data,
+                   lpk->u.rsa.publicExponent.data,
+                   lpk->u.rsa.publicExponent.len);
+
+    nss_ZFreeIf(io->u.key.key.privateExponent.data);
+    io->u.key.key.privateExponent.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.privateExponent.len);
+    io->u.key.key.privateExponent.size = lpk->u.rsa.privateExponent.len;
+    nsslibc_memcpy(io->u.key.key.privateExponent.data,
+                   lpk->u.rsa.privateExponent.data,
+                   lpk->u.rsa.privateExponent.len);
+
+    nss_ZFreeIf(io->u.key.key.prime1.data);
+    io->u.key.key.prime1.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.prime1.len);
+    io->u.key.key.prime1.size = lpk->u.rsa.prime1.len;
+    nsslibc_memcpy(io->u.key.key.prime1.data, lpk->u.rsa.prime1.data,
+                   lpk->u.rsa.prime1.len);
+
+    nss_ZFreeIf(io->u.key.key.prime2.data);
+    io->u.key.key.prime2.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.prime2.len);
+    io->u.key.key.prime2.size = lpk->u.rsa.prime2.len;
+    nsslibc_memcpy(io->u.key.key.prime2.data, lpk->u.rsa.prime2.data,
+                   lpk->u.rsa.prime2.len);
+
+    nss_ZFreeIf(io->u.key.key.exponent1.data);
+    io->u.key.key.exponent1.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.exponent1.len);
+    io->u.key.key.exponent1.size = lpk->u.rsa.exponent1.len;
+    nsslibc_memcpy(io->u.key.key.exponent1.data, lpk->u.rsa.exponent1.data,
+                   lpk->u.rsa.exponent1.len);
+
+    nss_ZFreeIf(io->u.key.key.exponent2.data);
+    io->u.key.key.exponent2.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.exponent2.len);
+    io->u.key.key.exponent2.size = lpk->u.rsa.exponent2.len;
+    nsslibc_memcpy(io->u.key.key.exponent2.data, lpk->u.rsa.exponent2.data,
+                   lpk->u.rsa.exponent2.len);
+
+    nss_ZFreeIf(io->u.key.key.coefficient.data);
+    io->u.key.key.coefficient.data =
+        (void *) nss_ZAlloc(NULL, lpk->u.rsa.coefficient.len);
+    io->u.key.key.coefficient.size = lpk->u.rsa.coefficient.len;
+    nsslibc_memcpy(io->u.key.key.coefficient.data,
+                   lpk->u.rsa.coefficient.data,
+                   lpk->u.rsa.coefficient.len);
+
+    pem_DestroyPrivateKey(lpk);
+    return;
+}
+
+typedef struct pemInternalCryptoOperationRSAPrivStr
+               pemInternalCryptoOperationRSAPriv;
+struct pemInternalCryptoOperationRSAPrivStr
+{
+    NSSCKMDCryptoOperation mdOperation;
+    NSSCKMDMechanism *mdMechanism;
+    pemInternalObject *iKey;
+    pemLOWKEYPrivateKey *lpk;
+    NSSItem *buffer;
+};
+
+/*
+ * pem_mdCryptoOperationRSAPriv_Create
+ */
+static NSSCKMDCryptoOperation *
+pem_mdCryptoOperationRSAPriv_Create
+(
+    const NSSCKMDCryptoOperation * proto,
+    NSSCKMDMechanism * mdMechanism,
+    NSSCKMDObject * mdKey,
+    CK_RV * pError
+)
+{
+    pemInternalObject *iKey = (pemInternalObject *) mdKey->etc;
+    const NSSItem *classItem = pem_FetchAttribute(iKey, CKA_CLASS);
+    const NSSItem *keyType = pem_FetchAttribute(iKey, CKA_KEY_TYPE);
+    pemInternalCryptoOperationRSAPriv *iOperation;
+    pemLOWKEYPrivateKey *lpk = NULL;
+    PLArenaPool *arena;
+
+    /* make sure we have the right objects */
+    if (((const NSSItem *) NULL == classItem) ||
+        (sizeof(CK_OBJECT_CLASS) != classItem->size) ||
+        (CKO_PRIVATE_KEY != *(CK_OBJECT_CLASS *) classItem->data) ||
+        ((const NSSItem *) NULL == keyType) ||
+        (sizeof(CK_KEY_TYPE) != keyType->size) ||
+        (CKK_RSA != *(CK_KEY_TYPE *) keyType->data)) {
+        *pError = CKR_KEY_TYPE_INCONSISTENT;
+        return (NSSCKMDCryptoOperation *) NULL;
+    }
+
+    arena =  PORT_NewArena(2048);
+    if (!arena) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDCryptoOperation *) NULL;
+    }
+
+    lpk = pem_getPrivateKey(arena, iKey->u.key.key.privateKey, pError, &iKey->u.key.key.modulus);
+    if (lpk == NULL) {
+        PORT_FreeArena(arena, PR_FALSE);
+        return (NSSCKMDCryptoOperation *) NULL;
+    }
+
+    iOperation = nss_ZNEW(NULL, pemInternalCryptoOperationRSAPriv);
+    if ((pemInternalCryptoOperationRSAPriv *) NULL == iOperation) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDCryptoOperation *) NULL;
+    }
+    iOperation->mdMechanism = mdMechanism;
+    iOperation->iKey = iKey;
+    iOperation->lpk = lpk;
+
+    nsslibc_memcpy(&iOperation->mdOperation,
+                   proto, sizeof(NSSCKMDCryptoOperation));
+    iOperation->mdOperation.etc = iOperation;
+
+    return &iOperation->mdOperation;
+}
+
+static void
+pem_mdCryptoOperationRSAPriv_Destroy
+(
+    NSSCKMDCryptoOperation * mdOperation,
+    NSSCKFWCryptoOperation * fwOperation,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    pemInternalCryptoOperationRSAPriv *iOperation =
+        (pemInternalCryptoOperationRSAPriv *) mdOperation->etc;
+
+    if (iOperation->buffer) {
+        nssItem_Destroy(iOperation->buffer);
+        iOperation->buffer = NULL;
+    }
+    pem_DestroyPrivateKey(iOperation->lpk);
+    iOperation->lpk = NULL;
+    nss_ZFreeIf(iOperation);
+}
+
+static CK_ULONG
+pem_mdCryptoOperationRSA_GetFinalLength
+(
+    NSSCKMDCryptoOperation * mdOperation,
+    NSSCKFWCryptoOperation * fwOperation,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    pemInternalCryptoOperationRSAPriv *iOperation =
+        (pemInternalCryptoOperationRSAPriv *) mdOperation->etc;
+    const NSSItem *modulus =
+        pem_FetchAttribute(iOperation->iKey, CKA_MODULUS);
+
+    if (NULL == modulus) {
+        *pError = CKR_FUNCTION_FAILED;
+        return 0;
+    }
+
+    return modulus->size;
+}
+
+
+/*
+ * pem_mdCryptoOperationRSADecrypt_GetOperationLength
+ * we won't know the length until we actually decrypt the
+ * input block. Since we go to all the work to decrypt the
+ * the block, we'll save if for when the block is asked for
+ */
+static CK_ULONG
+pem_mdCryptoOperationRSADecrypt_GetOperationLength
+(
+    NSSCKMDCryptoOperation * mdOperation,
+    NSSCKFWCryptoOperation * fwOperation,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    const NSSItem * input,
+    CK_RV * pError
+)
+{
+    pemInternalCryptoOperationRSAPriv *iOperation =
+        (pemInternalCryptoOperationRSAPriv *) mdOperation->etc;
+    SECStatus rv;
+
+    /* FIXME: Just because Microsoft is broken doesn't mean I have to be.
+     * but this is faster to do for now */
+
+    /* Microsoft's Decrypt operation works in place. Since we don't want
+     * to trash our input buffer, we make a copy of it */
+    iOperation->buffer = nssItem_Duplicate((NSSItem *) input, NULL, NULL);
+    if ((NSSItem *) NULL == iOperation->buffer) {
+        *pError = CKR_HOST_MEMORY;
+        return 0;
+    }
+
+    rv = pem_RSA_DecryptBlock(iOperation->lpk, iOperation->buffer->data,
+                              &iOperation->buffer->size,
+                              iOperation->buffer->size, input->data,
+                              input->size);
+
+    if (rv != SECSuccess) {
+        return 0;
+    }
+
+    return iOperation->buffer->size;
+}
+
+/*
+ * pem_mdCryptoOperationRSADecrypt_UpdateFinal
+ *
+ * NOTE: pem_mdCryptoOperationRSADecrypt_GetOperationLength is presumed to 
+ * have been called previously.
+ */
+static CK_RV
+pem_mdCryptoOperationRSADecrypt_UpdateFinal
+(
+    NSSCKMDCryptoOperation * mdOperation,
+    NSSCKFWCryptoOperation * fwOperation,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    const NSSItem * input,
+    NSSItem * output
+)
+{
+    pemInternalCryptoOperationRSAPriv *iOperation =
+        (pemInternalCryptoOperationRSAPriv *) mdOperation->etc;
+    NSSItem *buffer = iOperation->buffer;
+
+    if ((NSSItem *) NULL == buffer) {
+        return CKR_GENERAL_ERROR;
+    }
+    nsslibc_memcpy(output->data, buffer->data, buffer->size);
+    output->size = buffer->size;
+    return CKR_OK;
+}
+
+/*
+ * pem_mdCryptoOperationRSASign_UpdateFinal
+ *
+ */
+static CK_RV
+pem_mdCryptoOperationRSASign_UpdateFinal
+(
+    NSSCKMDCryptoOperation * mdOperation,
+    NSSCKFWCryptoOperation * fwOperation,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    const NSSItem * input,
+    NSSItem * output
+)
+{
+    pemInternalCryptoOperationRSAPriv *iOperation =
+        (pemInternalCryptoOperationRSAPriv *) mdOperation->etc;
+    CK_RV error = CKR_OK;
+    SECStatus rv = SECSuccess;
+
+    rv = pem_RSA_Sign(iOperation->lpk, output->data, &output->size,
+                      output->size, input->data, input->size);
+
+    if (rv != SECSuccess) {
+        error = CKR_GENERAL_ERROR;
+    }
+
+    return error;
+}
+
+NSS_IMPLEMENT_DATA const NSSCKMDCryptoOperation
+pem_mdCryptoOperationRSADecrypt_proto = {
+    NULL, /* etc */
+    pem_mdCryptoOperationRSAPriv_Destroy,
+    NULL, /* GetFinalLengh - not needed for one shot Decrypt/Encrypt */
+    pem_mdCryptoOperationRSADecrypt_GetOperationLength,
+    NULL, /* Final - not needed for one shot operation */
+    NULL, /* Update - not needed for one shot operation */
+    NULL, /* DigestUpdate - not needed for one shot operation */
+    pem_mdCryptoOperationRSADecrypt_UpdateFinal,
+    NULL, /* UpdateCombo - not needed for one shot operation */
+    NULL, /* DigestKey - not needed for one shot operation */
+    (void *) NULL /* null terminator */
+};
+
+NSS_IMPLEMENT_DATA const NSSCKMDCryptoOperation
+pem_mdCryptoOperationRSASign_proto = {
+    NULL, /* etc */
+    pem_mdCryptoOperationRSAPriv_Destroy,
+    pem_mdCryptoOperationRSA_GetFinalLength,
+    NULL, /* GetOperationLengh - not needed for one shot Sign/Verify */
+    NULL, /* Final - not needed for one shot operation */
+    NULL, /* Update - not needed for one shot operation */
+    NULL, /* DigestUpdate - not needed for one shot operation */
+    pem_mdCryptoOperationRSASign_UpdateFinal,
+    NULL, /* UpdateCombo - not needed for one shot operation */
+    NULL, /* DigestKey - not needed for one shot operation */
+    (void *) NULL /* null terminator */
+};
+
+/********** NSSCKMDMechansim functions ***********************/
+/*
+ * pem_mdMechanismRSA_Destroy
+ */
+static void
+pem_mdMechanismRSA_Destroy
+(
+    NSSCKMDMechanism * mdMechanism,
+    NSSCKFWMechanism * fwMechanism,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    nss_ZFreeIf(fwMechanism);
+}
+
+/*
+ * pem_mdMechanismRSA_GetMinKeySize
+ */
+static CK_ULONG
+pem_mdMechanismRSA_GetMinKeySize
+(
+    NSSCKMDMechanism * mdMechanism,
+    NSSCKFWMechanism * fwMechanism,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return 384;
+}
+
+/*
+ * pem_mdMechanismRSA_GetMaxKeySize
+ */
+static CK_ULONG
+pem_mdMechanismRSA_GetMaxKeySize
+(
+    NSSCKMDMechanism * mdMechanism,
+    NSSCKFWMechanism * fwMechanism,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return 16384;
+}
+
+/*
+ * pem_mdMechanismRSA_DecryptInit
+ */
+static NSSCKMDCryptoOperation *
+pem_mdMechanismRSA_DecryptInit
+(
+    NSSCKMDMechanism * mdMechanism,
+    NSSCKFWMechanism * fwMechanism,
+    CK_MECHANISM * pMechanism,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSCKMDObject * mdKey,
+    NSSCKFWObject * fwKey,
+    CK_RV * pError
+)
+{
+    return pem_mdCryptoOperationRSAPriv_Create
+        (&pem_mdCryptoOperationRSADecrypt_proto, mdMechanism, mdKey,
+         pError);
+}
+
+/*
+ * pem_mdMechanismRSA_SignInit
+ */
+static NSSCKMDCryptoOperation *
+pem_mdMechanismRSA_SignInit
+(
+    NSSCKMDMechanism * mdMechanism,
+    NSSCKFWMechanism * fwMechanism,
+    CK_MECHANISM * pMechanism,
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSCKMDObject * mdKey,
+    NSSCKFWObject * fwKey,
+    CK_RV * pError
+)
+{
+    return pem_mdCryptoOperationRSAPriv_Create
+        (&pem_mdCryptoOperationRSASign_proto, mdMechanism, mdKey, pError);
+}
+
+NSS_IMPLEMENT_DATA const NSSCKMDMechanism
+pem_mdMechanismRSA = {
+    (void *) NULL, /* etc */
+    pem_mdMechanismRSA_Destroy,
+    pem_mdMechanismRSA_GetMinKeySize,
+    pem_mdMechanismRSA_GetMaxKeySize,
+    NULL, /* GetInHardware - default false */
+    NULL, /* EncryptInit - default errs */
+    pem_mdMechanismRSA_DecryptInit,
+    NULL, /* DigestInit - default errs */
+    pem_mdMechanismRSA_SignInit,
+    NULL, /* VerifyInit - default errs */
+    pem_mdMechanismRSA_SignInit,        /* SignRecoverInit */
+    NULL, /* VerifyRecoverInit - default errs */
+    NULL, /* GenerateKey - default errs */
+    NULL, /* GenerateKeyPair - default errs */
+    NULL, /* GetWrapKeyLength - default errs */
+    NULL, /* WrapKey - default errs */
+    NULL, /* UnwrapKey - default errs */
+    NULL, /* DeriveKey - default errs */
+    (void *) NULL /* null terminator */
+};
diff --git a/a/nss/lib/ckfw/pem/psession.c b/b/nss/lib/ckfw/pem/psession.c
new file mode 100644
index 0000000..70c5407
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/psession.c
@@ -0,0 +1,388 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ckpem.h"
+#include "secmodt.h"
+#include "pk11pub.h"
+#include "base64.h"
+#include "blapi.h"
+
+/*
+ * psession.c
+ *
+ * This file implements the NSSCKMDSession object for the 
+ * "PEM objects" cryptoki module.
+ */
+
+NSS_EXTERN_DATA pemInternalObject **gobj;
+NSS_EXTERN_DATA int pem_nobjs;
+NSS_EXTERN_DATA int token_needsLogin[NUM_SLOTS];
+NSS_EXTERN_DATA const SEC_ASN1Template pem_RSAPrivateKeyTemplate[];
+
+void prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey * key);
+void pem_DestroyPrivateKey(NSSLOWKEYPrivateKey * privk);
+
+/*
+ * Convert a hex string into bytes.
+ */
+static unsigned char *convert_iv(char *src, int num)
+{
+    int i;
+    char conv[3];
+    unsigned char *c;
+
+    c = (unsigned char *) malloc((num) + 1);
+    if (c == NULL)
+        return NULL;
+
+    conv[2] = '\0';
+    memset(c, 0, num);
+    for (i = 0; i < num; i++) {
+        conv[0] = src[(i * 2)];
+        conv[1] = src[(i * 2) + 1];
+        c[i] = strtol(conv, NULL, 16);
+    }
+    return c;
+}
+
+/*
+ * The key is a 24-bit hash. The first 16 bits are the MD5 hash of the
+ * password and the IV (salt). This has is then re-hashed with the
+ * password and IV again and the first 8 bytes of that are the remaining
+ * bytes of the 24-bit key.
+ */
+static int
+make_key(const unsigned char *salt, const unsigned char *data, int len,
+         unsigned char *key)
+{
+    int nkey = 0;
+    MD5Context *Md5Ctx = MD5_NewContext();
+    unsigned int digestLen;
+    int count, i;
+    unsigned char H[25];
+
+    nkey = 24;
+    count = 0;
+
+    while (nkey > 0) {
+        MD5_Begin(Md5Ctx);
+        if (count)
+            MD5_Update(Md5Ctx, (const unsigned char *) H, digestLen);
+        MD5_Update(Md5Ctx, (const unsigned char *) data, len);
+        MD5_Update(Md5Ctx, (const unsigned char *) salt, 8);
+        MD5_End(Md5Ctx, (unsigned char *) H, &digestLen, sizeof(H));
+
+        i = 0;
+        while (nkey && (i != digestLen)) {
+            *(key++) = H[i];
+            nkey--;
+            i++;
+        }
+        count++;
+    }
+    MD5_DestroyContext(Md5Ctx, PR_TRUE);
+
+    return 24;
+}
+
+static NSSCKMDFindObjects *
+pem_mdSession_FindObjectsInit
+(
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_ATTRIBUTE_PTR pTemplate,
+    CK_ULONG ulAttributeCount,
+    CK_RV * pError
+)
+{
+    plog("mdSession_FindObjectsInit\n");
+    return pem_FindObjectsInit(fwSession, pTemplate, ulAttributeCount,
+                               pError);
+}
+
+static NSSCKMDObject *
+pem_mdSession_CreateObject
+(
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSArena * arena,
+    CK_ATTRIBUTE_PTR pTemplate,
+    CK_ULONG ulAttributeCount,
+    CK_RV * pError
+)
+{
+    plog("mdSession_CreateObject\n");
+    return pem_CreateObject(fwInstance, fwSession, mdToken, pTemplate,
+                            ulAttributeCount, pError);
+}
+
+/*
+ * increase refCount of internal object(s)
+ */
+NSSCKMDObject *
+pem_mdSession_CopyObject
+(
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSCKMDObject * mdOldObject,
+    NSSCKFWObject * fwOldObject,
+    NSSArena * arena,
+    CK_ATTRIBUTE_PTR pTemplate,
+    CK_ULONG ulAttributeCount,
+    CK_RV * pError
+)
+{
+    NSSCKMDObject *rvmdObject = NULL;
+    pemInternalObject *io = (pemInternalObject *) mdOldObject->etc;
+
+    /* make a new mdObject */
+    rvmdObject = nss_ZNEW(arena, NSSCKMDObject);
+    if ((NSSCKMDObject *) NULL == rvmdObject) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDObject *) NULL;
+    }
+
+    if (NULL == io->list) {
+        io->refCount ++;
+    } else {
+        /* go through list of objects */
+        pemObjectListItem *item = io->list;
+        while (item) {
+            item->io->refCount ++;
+            item = item->next;
+        }
+    }
+    /* struct (shallow) copy the old one */
+    *rvmdObject = *mdOldObject;
+
+    return rvmdObject;
+}
+
+CK_RV
+pem_mdSession_Login
+(
+    NSSCKMDSession * mdSession,
+    NSSCKFWSession * fwSession,
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_USER_TYPE userType,
+    NSSItem * pin,
+    CK_STATE oldState,
+    CK_STATE newState
+)
+{
+    NSSCKFWSlot *fwSlot;
+    CK_SLOT_ID slotID;
+    pemInternalObject *io = NULL;
+    unsigned char *iv = 0;
+    unsigned char mykey[32];
+    unsigned char *output = NULL;
+    DESContext *cx = NULL;
+    SECStatus rv;
+    unsigned int len = 0;
+    NSSLOWKEYPrivateKey *lpk = NULL;
+    PLArenaPool *arena;
+    SECItem plain;
+    int i;
+
+    fwSlot = NSSCKFWToken_GetFWSlot(fwToken);
+    slotID = nssCKFWSlot_GetSlotID(fwSlot);
+
+    arena = PORT_NewArena(2048);
+    if (!arena) {
+        return CKR_HOST_MEMORY;
+    }
+
+    plog("pem_mdSession_Login '%s'\n", (char *) pin->data);
+
+    token_needsLogin[slotID - 1] = PR_FALSE;
+
+    /* Find the right key object */
+    for (i = 0; i < pem_nobjs; i++) {
+        if (NULL == gobj[i])
+            continue;
+
+        if ((slotID == gobj[i]->slotID) && (gobj[i]->type == pemBareKey)) {
+            io = gobj[i];
+            break;
+        }
+    }
+
+    if (NULL == io) {
+        rv = CKR_SLOT_ID_INVALID;
+        goto loser;
+    }
+
+    /* Convert the IV from hex into an array of bytes */
+    iv = convert_iv(io->u.key.ivstring, 8);
+
+    /* Convert the PIN and IV into a DES key */
+    make_key(iv, pin->data, pin->size, mykey);
+
+    output =
+        (unsigned char *) nss_ZAlloc(NULL,
+                                     (io->u.key.key.privateKey->len + 1));
+    if (!output) {
+        rv = CKR_HOST_MEMORY;
+        goto loser;
+    }
+
+    cx = DES_CreateContext((const unsigned char *) mykey, iv,
+                           io->u.key.cipher, PR_FALSE);
+    if (!cx) {
+        rv = CKR_HOST_MEMORY;
+        goto loser;
+    }
+
+    rv = DES_Decrypt(cx, output, &len, io->u.key.key.privateKey->len,
+                     io->u.key.key.privateKey->data,
+                     io->u.key.key.privateKey->len);
+    DES_DestroyContext(cx, PR_TRUE);
+
+    if (iv) {
+        free(iv);
+        iv = NULL;
+    }
+    if (rv != SECSuccess) {
+        rv = CKR_PIN_INCORRECT;
+        goto loser;
+    }
+
+    lpk = (NSSLOWKEYPrivateKey *) nss_ZAlloc(NULL,
+                                             sizeof (NSSLOWKEYPrivateKey));
+    if (lpk == NULL) {
+        rv = CKR_HOST_MEMORY;
+        goto loser;
+    }
+
+    lpk->arena = arena;
+    lpk->keyType = NSSLOWKEYRSAKey;
+    prepare_low_rsa_priv_key_for_asn1(lpk);
+
+
+    /* Decode the resulting blob and see if it is a decodable DER that fits
+     * our private key template. If so we declare success and move on. If not
+     * then we return an error.
+     */
+    memset(&plain, 0, sizeof(plain));
+    plain.data = output;
+    plain.len = len - output[len - 1];
+    rv = SEC_QuickDERDecodeItem(arena, lpk, pem_RSAPrivateKeyTemplate,
+                                &plain);
+    pem_DestroyPrivateKey(lpk);
+    arena = NULL;
+    if (rv != SECSuccess)
+        goto loser;
+
+    nss_ZFreeIf(io->u.key.key.privateKey->data);
+    io->u.key.key.privateKey->len = len - output[len - 1];
+    io->u.key.key.privateKey->data =
+        (void *) nss_ZAlloc(NULL, io->u.key.key.privateKey->len);
+    memcpy(io->u.key.key.privateKey->data, output, len - output[len - 1]);
+
+    rv = CKR_OK;
+
+  loser:
+    if (arena)
+        PORT_FreeArena(arena, PR_FALSE);
+    if (iv)
+        free(iv);
+    nss_ZFreeIf(output);
+
+    return rv;
+}
+
+NSS_IMPLEMENT NSSCKMDSession *
+pem_CreateSession
+(
+    NSSCKFWSession * fwSession,
+    CK_RV * pError
+)
+{
+    NSSArena *arena;
+    NSSCKMDSession *rv;
+
+    plog("pem_CreateSession returning new session\n");
+    arena = NSSCKFWSession_GetArena(fwSession, pError);
+    if ((NSSArena *) NULL == arena) {
+        return (NSSCKMDSession *) NULL;
+    }
+
+    rv = nss_ZNEW(arena, NSSCKMDSession);
+    if ((NSSCKMDSession *) NULL == rv) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDSession *) NULL;
+    }
+
+    /* 
+     * rv was zeroed when allocated, so we only 
+     * need to set the non-zero members.
+     */
+
+    rv->etc = (void *) fwSession;
+    /* rv->Close */
+    /* rv->GetDeviceError */
+    rv->Login = pem_mdSession_Login;
+    /* rv->Logout */
+    /* rv->InitPIN */
+    /* rv->SetPIN */
+    /* rv->GetOperationStateLen */
+    /* rv->GetOperationState */
+    /* rv->SetOperationState */
+    rv->CreateObject = pem_mdSession_CreateObject;
+    rv->CopyObject = pem_mdSession_CopyObject;
+    rv->FindObjectsInit = pem_mdSession_FindObjectsInit;
+    /* rv->SeedRandom */
+    /* rv->GetRandom */
+    /* rv->null */
+
+    return rv;
+}
diff --git a/a/nss/lib/ckfw/pem/pslot.c b/b/nss/lib/ckfw/pem/pslot.c
new file mode 100644
index 0000000..2f9901b
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/pslot.c
@@ -0,0 +1,183 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ckpem.h"
+#include "prprf.h"
+
+/*
+ * pslot.c
+ *
+ * This file implements the NSSCKMDSlot object for the
+ * "PEM objects" cryptoki module.
+ */
+
+static NSSUTF8 *
+pem_mdSlot_GetSlotDescription
+(
+    NSSCKMDSlot * mdSlot,
+    NSSCKFWSlot * fwSlot,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    CK_SLOT_ID slotID;
+    NSSArena *arena;
+    char *slotid;
+
+    arena = NSSCKFWInstance_GetArena(fwInstance, pError);
+    slotID = nssCKFWSlot_GetSlotID(fwSlot);
+
+    slotid = (char *) nss_ZAlloc(arena, 256);
+    PR_snprintf(slotid, 256, "PEM Slot #%ld", slotID);
+
+    return (NSSUTF8 *) slotid;
+}
+
+static NSSUTF8 *
+pem_mdSlot_GetManufacturerID
+(
+    NSSCKMDSlot * mdSlot,
+    NSSCKFWSlot * fwSlot,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSUTF8 *) pem_ManufacturerID;
+}
+
+static CK_VERSION
+pem_mdSlot_GetHardwareVersion
+(
+    NSSCKMDSlot * mdSlot,
+    NSSCKFWSlot * fwSlot,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return pem_HardwareVersion;
+}
+
+static CK_VERSION
+pem_mdSlot_GetFirmwareVersion
+(
+    NSSCKMDSlot * mdSlot,
+    NSSCKFWSlot * fwSlot,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return pem_FirmwareVersion;
+}
+
+static NSSCKMDToken *
+pem_mdSlot_GetToken
+(
+    NSSCKMDSlot * mdSlot,
+    NSSCKFWSlot * fwSlot,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSCKMDToken *) mdSlot->etc;
+}
+
+CK_BBOOL
+pem_mdSlot_GetRemovableDevice
+(
+     NSSCKMDSlot * mdSlot,
+     NSSCKFWSlot * fwSlot,
+     NSSCKMDInstance * mdInstance,
+     NSSCKFWInstance * fwInstance
+)
+{
+    return CK_TRUE;
+}
+
+NSSCKMDSlot *
+pem_NewSlot
+(
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    NSSArena *arena;
+    NSSCKMDSlot *mdSlot;
+
+    plog("pem_NewSlot\n");
+    arena = NSSCKFWInstance_GetArena(fwInstance, pError);
+    if ((NSSArena *) NULL == arena) {
+        if (CKR_OK == *pError) {
+            *pError = CKR_GENERAL_ERROR;
+        }
+    }
+
+    mdSlot = nss_ZNEW(arena, NSSCKMDSlot);
+    if ((NSSCKMDSlot *) NULL == mdSlot) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDSlot *) NULL;
+    }
+
+    mdSlot->etc = pem_NewToken(fwInstance, pError);
+
+    mdSlot->GetSlotDescription = pem_mdSlot_GetSlotDescription;
+    mdSlot->GetManufacturerID = pem_mdSlot_GetManufacturerID;
+    mdSlot->GetHardwareVersion = pem_mdSlot_GetHardwareVersion;
+    mdSlot->GetFirmwareVersion = pem_mdSlot_GetFirmwareVersion;
+    mdSlot->GetRemovableDevice = pem_mdSlot_GetRemovableDevice;
+    mdSlot->GetToken = pem_mdSlot_GetToken;
+
+    return mdSlot;
+}
+
+NSS_IMPLEMENT_DATA const NSSCKMDSlot
+pem_mdSlot = {
+    (void *) NULL, /* etc */
+    NULL, /* Initialize */
+    NULL, /* Destroy */
+    pem_mdSlot_GetSlotDescription,
+    pem_mdSlot_GetManufacturerID,
+    NULL, /* GetTokenPresent -- defaults to true */
+    pem_mdSlot_GetRemovableDevice,
+    NULL, /* GetHardwareSlot -- defaults to false */
+    pem_mdSlot_GetHardwareVersion,
+    pem_mdSlot_GetFirmwareVersion,
+    pem_mdSlot_GetToken,
+    (void *) NULL /* null terminator */
+};
diff --git a/a/nss/lib/ckfw/pem/ptoken.c b/b/nss/lib/ckfw/pem/ptoken.c
new file mode 100644
index 0000000..6c35b21
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/ptoken.c
@@ -0,0 +1,334 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "ckpem.h"
+#include "prprf.h"
+
+/*
+ * ptoken.c
+ *
+ * This file implements the NSSCKMDToken object for the
+ * "PEM objects" cryptoki module.
+ */
+
+NSS_EXTERN_DATA int token_needsLogin[NUM_SLOTS];
+
+static NSSUTF8 *
+pem_mdToken_GetLabel
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    NSSCKFWSlot *fwSlot;
+    CK_SLOT_ID slotID;
+    NSSArena *arena;
+    char *tokenid;
+
+    arena = NSSCKFWInstance_GetArena(fwInstance, pError);
+    fwSlot = NSSCKFWToken_GetFWSlot(fwToken);
+    slotID = nssCKFWSlot_GetSlotID(fwSlot);
+
+    tokenid = (char *) nss_ZAlloc(arena, 256);
+    PR_snprintf(tokenid, 256, "PEM Token #%ld", slotID);
+
+    return (NSSUTF8 *) tokenid;
+}
+
+static NSSUTF8 *
+pem_mdToken_GetManufacturerID
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSUTF8 *) pem_ManufacturerID;
+}
+
+static NSSUTF8 *
+pem_mdToken_GetModel
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSUTF8 *) pem_TokenModel;
+}
+
+static NSSUTF8 *
+pem_mdToken_GetSerialNumber
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    return (NSSUTF8 *) pem_TokenSerialNumber;
+}
+
+static CK_BBOOL
+pem_mdToken_GetIsWriteProtected
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return CK_TRUE;
+}
+
+static CK_VERSION
+pem_mdToken_GetHardwareVersion
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return pem_HardwareVersion;
+}
+
+static CK_VERSION
+pem_mdToken_GetFirmwareVersion
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return pem_FirmwareVersion;
+}
+
+static NSSCKMDSession *pem_mdToken_OpenSession
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    NSSCKFWSession * fwSession,
+    CK_BBOOL rw,
+    CK_RV * pError
+)
+{
+    plog("pem_mdToken_OpenSession\n");
+    return pem_CreateSession(fwSession, pError);
+}
+
+static CK_ULONG
+pem_mdToken_GetMechanismCount
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    return (CK_ULONG) 1;
+}
+
+static CK_RV
+pem_mdToken_GetMechanismTypes
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_MECHANISM_TYPE types[]
+)
+{
+    types[0] = CKM_RSA_PKCS;
+    return CKR_OK;
+}
+
+static NSSCKMDMechanism *
+pem_mdToken_GetMechanism
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance,
+    CK_MECHANISM_TYPE which,
+    CK_RV * pError
+)
+{
+    if (which != CKM_RSA_PKCS) {
+        *pError = CKR_MECHANISM_INVALID;
+        return (NSSCKMDMechanism *) NULL;
+    }
+    return (NSSCKMDMechanism *) & pem_mdMechanismRSA;
+}
+
+static CK_BBOOL
+pem_mdToken_GetUserPinInitialized
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    plog("pem_mdToken_GetUserPinInitialized: always TRUE\n");
+    return CK_TRUE;
+}
+
+static CK_BBOOL
+pem_mdToken_GetLoginRequired
+(
+    NSSCKMDToken * mdToken,
+    NSSCKFWToken * fwToken,
+    NSSCKMDInstance * mdInstance,
+    NSSCKFWInstance * fwInstance
+)
+{
+    char *label;
+    CK_RV pError;
+    NSSCKFWSlot *fwSlot;
+    CK_SLOT_ID slotID;
+
+    fwSlot = NSSCKFWToken_GetFWSlot(fwToken);
+    slotID = nssCKFWSlot_GetSlotID(fwSlot);
+
+    label = pem_mdToken_GetLabel(mdToken, fwToken, mdInstance, fwInstance,
+                             &pError);
+
+    plog("pem_mdToken_GetLoginRequired %s: %d\n", label,
+         token_needsLogin[slotID - 1]);
+
+    if (token_needsLogin[slotID - 1] == PR_TRUE)
+        return CK_TRUE;
+    else
+        return CK_FALSE;
+}
+
+NSSCKMDToken *
+pem_NewToken
+(
+    NSSCKFWInstance * fwInstance,
+    CK_RV * pError
+)
+{
+    NSSArena *arena;
+    NSSCKMDToken *mdToken;
+    pemToken *token;
+
+    arena = NSSCKFWInstance_GetArena(fwInstance, pError);
+    if ((NSSArena *) NULL == arena) {
+        if (CKR_OK == *pError) {
+            *pError = CKR_GENERAL_ERROR;
+        }
+    }
+
+    mdToken = nss_ZNEW(arena, NSSCKMDToken);
+    if ((NSSCKMDToken *) NULL == mdToken) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDToken *) NULL;
+    }
+
+    token = nss_ZNEW(arena, struct pemTokenStr);
+    if ((struct pemTokenStr *) NULL == token) {
+        *pError = CKR_HOST_MEMORY;
+        return (NSSCKMDToken *) NULL;
+    }
+
+    mdToken->etc = (void *) token;
+    mdToken->GetLabel = pem_mdToken_GetLabel;
+    mdToken->GetManufacturerID = pem_mdToken_GetManufacturerID;
+    mdToken->GetModel = pem_mdToken_GetModel;
+    mdToken->GetSerialNumber = pem_mdToken_GetSerialNumber;
+    mdToken->GetIsWriteProtected = pem_mdToken_GetIsWriteProtected;
+    mdToken->GetLoginRequired = pem_mdToken_GetLoginRequired;
+    mdToken->GetUserPinInitialized = pem_mdToken_GetUserPinInitialized;
+    mdToken->GetHardwareVersion = pem_mdToken_GetHardwareVersion;
+    mdToken->GetFirmwareVersion = pem_mdToken_GetFirmwareVersion;
+    mdToken->OpenSession = pem_mdToken_OpenSession;
+    mdToken->GetMechanismCount = pem_mdToken_GetMechanismCount;
+    mdToken->GetMechanismTypes = pem_mdToken_GetMechanismTypes;
+    mdToken->GetMechanism = pem_mdToken_GetMechanism;
+
+    return mdToken;
+}
+
+#if 0
+NSS_IMPLEMENT_DATA const NSSCKMDToken pem_mdToken = {
+    (void *) NULL,              /* etc */
+    NULL,                       /* Setup */
+    NULL,                       /* Invalidate */
+    NULL,                       /* InitToken -- default errs */
+    pem_mdToken_GetLabel,
+    pem_mdToken_GetManufacturerID,
+    pem_mdToken_GetModel,
+    pem_mdToken_GetSerialNumber,
+    NULL,                       /* GetHasRNG -- default is false */
+    pem_mdToken_GetIsWriteProtected,
+    pem_mdToken_GetLoginRequired,
+    pem_mdToken_GetUserPinInitialized,
+    NULL,                       /* GetRestoreKeyNotNeeded -- irrelevant */
+    NULL,                       /* GetHasClockOnToken -- default is false */
+    NULL,                       /* GetHasProtectedAuthenticationPath -- default is false */
+    NULL,                       /* GetSupportsDualCryptoOperations -- default is false */
+    NULL,                       /* GetMaxSessionCount -- default is CK_UNAVAILABLE_INFORMATION */
+    NULL,                       /* GetMaxRwSessionCount -- default is CK_UNAVAILABLE_INFORMATION */
+    NULL,                       /* GetMaxPinLen -- irrelevant */
+    NULL,                       /* GetMinPinLen -- irrelevant */
+    NULL,                       /* GetTotalPublicMemory -- default is CK_UNAVAILABLE_INFORMATION */
+    NULL,                       /* GetFreePublicMemory -- default is CK_UNAVAILABLE_INFORMATION */
+    NULL,                       /* GetTotalPrivateMemory -- default is CK_UNAVAILABLE_INFORMATION */
+    NULL,                       /* GetFreePrivateMemory -- default is CK_UNAVAILABLE_INFORMATION */
+    pem_mdToken_GetHardwareVersion,
+    pem_mdToken_GetFirmwareVersion,
+    NULL,                       /* GetUTCTime -- no clock */
+    pem_mdToken_OpenSession,
+    pem_mdToken_GetMechanismCount,
+    pem_mdToken_GetMechanismTypes,
+    pem_mdToken_GetMechanism,
+    (void *) NULL               /* null terminator */
+};
+#endif
diff --git a/a/nss/lib/ckfw/pem/rsawrapr.c b/b/nss/lib/ckfw/pem/rsawrapr.c
new file mode 100644
index 0000000..1179f2a
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/rsawrapr.c
@@ -0,0 +1,823 @@
+/*
+ * PKCS#1 encoding and decoding functions.
+ * This file is believed to contain no code licensed from other parties.
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: $ */
+
+#include "ckpem.h"
+#include "blapi.h"
+#include "softoken.h"
+#include "sechash.h"
+#include "base.h"
+
+#include "secerr.h"
+
+#define RSA_BLOCK_MIN_PAD_LEN		8
+#define RSA_BLOCK_FIRST_OCTET		0x00
+#define RSA_BLOCK_PRIVATE0_PAD_OCTET	0x00
+#define RSA_BLOCK_PRIVATE_PAD_OCTET	0xff
+#define RSA_BLOCK_AFTER_PAD_OCTET	0x00
+
+#define OAEP_SALT_LEN		8
+#define OAEP_PAD_LEN		8
+#define OAEP_PAD_OCTET		0x00
+
+#define FLAT_BUFSIZE 512        /* bytes to hold flattened SHA1Context. */
+
+unsigned
+pem_PublicModulusLen(NSSLOWKEYPublicKey *pubk)
+{
+    unsigned char b0;
+
+    /* interpret modulus length as key strength... in
+     * fortezza that's the public key length */
+
+    switch (pubk->keyType) {
+    case pemLOWKEYRSAKey:
+        b0 = pubk->u.rsa.modulus.data[0];
+        return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1;
+    default:
+        break;
+    }
+    return 0;
+}
+
+static SHA1Context *SHA1_CloneContext(SHA1Context * original)
+{
+    SHA1Context *clone = NULL;
+    unsigned char *pBuf;
+    int sha1ContextSize = SHA1_FlattenSize(original);
+    SECStatus frv;
+    unsigned char buf[FLAT_BUFSIZE];
+
+    PORT_Assert(sizeof buf >= sha1ContextSize);
+    if (sizeof buf >= sha1ContextSize) {
+        pBuf = buf;
+    } else {
+        pBuf = nss_ZAlloc(NULL, sha1ContextSize);
+        if (!pBuf)
+            goto done;
+    }
+
+    frv = SHA1_Flatten(original, pBuf);
+    if (frv == SECSuccess) {
+        clone = SHA1_Resurrect(pBuf, NULL);
+        memset(pBuf, 0, sha1ContextSize);
+    }
+  done:
+    if (pBuf != buf)
+        nss_ZFreeIf(pBuf);
+    return clone;
+}
+
+/*
+ * Modify data by XORing it with a special hash of salt.
+ */
+static SECStatus
+oaep_xor_with_h1(unsigned char *data, unsigned int datalen,
+                 unsigned char *salt, unsigned int saltlen)
+{
+    SHA1Context *sha1cx;
+    unsigned char *dp, *dataend;
+    unsigned char end_octet;
+
+    sha1cx = SHA1_NewContext();
+    if (sha1cx == NULL) {
+        return SECFailure;
+    }
+
+    /*
+     * Get a hash of salt started; we will use it several times,
+     * adding in a different end octet (x00, x01, x02, ...).
+     */
+    SHA1_Begin(sha1cx);
+    SHA1_Update(sha1cx, salt, saltlen);
+    end_octet = 0;
+
+    dp = data;
+    dataend = data + datalen;
+
+    while (dp < dataend) {
+        SHA1Context *sha1cx_h1;
+        unsigned int sha1len, sha1off;
+        unsigned char sha1[SHA1_LENGTH];
+
+        /*
+         * Create hash of (salt || end_octet)
+         */
+        sha1cx_h1 = SHA1_CloneContext(sha1cx);
+        SHA1_Update(sha1cx_h1, &end_octet, 1);
+        SHA1_End(sha1cx_h1, sha1, &sha1len, sizeof(sha1));
+        SHA1_DestroyContext(sha1cx_h1, PR_TRUE);
+        PORT_Assert(sha1len == SHA1_LENGTH);
+
+        /*
+         * XOR that hash with the data.
+         * When we have fewer than SHA1_LENGTH octets of data
+         * left to xor, use just the low-order ones of the hash.
+         */
+        sha1off = 0;
+        if ((dataend - dp) < SHA1_LENGTH)
+            sha1off = SHA1_LENGTH - (dataend - dp);
+        while (sha1off < SHA1_LENGTH)
+            *dp++ ^= sha1[sha1off++];
+
+        /*
+         * Bump for next hash chunk.
+         */
+        end_octet++;
+    }
+
+    SHA1_DestroyContext(sha1cx, PR_TRUE);
+    return SECSuccess;
+}
+
+/*
+ * Modify salt by XORing it with a special hash of data.
+ */
+static SECStatus
+oaep_xor_with_h2(unsigned char *salt, unsigned int saltlen,
+                 unsigned char *data, unsigned int datalen)
+{
+    unsigned char sha1[SHA1_LENGTH];
+    unsigned char *psalt, *psha1, *saltend;
+    SECStatus rv;
+
+    /*
+     * Create a hash of data.
+     */
+    rv = SHA1_HashBuf(sha1, data, datalen);
+    if (rv != SECSuccess) {
+        return rv;
+    }
+
+    /*
+     * XOR the low-order octets of that hash with salt.
+     */
+    PORT_Assert(saltlen <= SHA1_LENGTH);
+    saltend = salt + saltlen;
+    psalt = salt;
+    psha1 = sha1 + SHA1_LENGTH - saltlen;
+    while (psalt < saltend) {
+        *psalt++ ^= *psha1++;
+    }
+
+    return SECSuccess;
+}
+
+/*
+ * RSA block types
+ *
+ * The actual values are important -- they are fixed, *not* arbitrary.
+ * The explicit value assignments are not needed (because C would give
+ * us those same values anyway) but are included as a reminder...
+ */
+typedef enum {
+    RSA_BlockPrivate0 = 0,      /* unused, really */
+    RSA_BlockPrivate = 1,       /* pad for a private-key operation */
+    RSA_BlockPublic = 2,        /* pad for a public-key operation */
+    RSA_BlockRaw = 4,           /* simply justify the block appropriately */
+    RSA_BlockTotal
+} RSA_BlockType;
+
+/*
+ * Format one block of data for public/private key encryption using
+ * the rules defined in PKCS #1.
+ */
+static unsigned char *rsa_FormatOneBlock(unsigned modulusLen,
+                                         RSA_BlockType blockType,
+                                         SECItem * data)
+{
+    unsigned char *block;
+    unsigned char *bp;
+    int padLen;
+    int i;
+    SECStatus rv;
+
+    block = (unsigned char *) nss_ZAlloc(NULL, modulusLen);
+    if (block == NULL)
+        return NULL;
+
+    bp = block;
+
+    /*
+     * All RSA blocks start with two octets:
+     *  0x00 || BlockType
+     */
+    *bp++ = RSA_BLOCK_FIRST_OCTET;
+    *bp++ = (unsigned char) blockType;
+
+    switch (blockType) {
+
+        /*
+         * Blocks intended for private-key operation.
+         */
+    case RSA_BlockPrivate:     /* preferred method */
+        /*
+         * 0x00 || BT || Pad || 0x00 || ActualData
+         *   1      1   padLen    1      data->len
+         * Pad is either all 0x00 or all 0xff bytes, depending on blockType.
+         */
+        padLen = modulusLen - data->len - 3;
+        PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN);
+        if (padLen < RSA_BLOCK_MIN_PAD_LEN) {
+            nss_ZFreeIf(block);
+            return NULL;
+        }
+        nsslibc_memset(bp, RSA_BLOCK_PRIVATE_PAD_OCTET, padLen);
+        bp += padLen;
+        *bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
+        nsslibc_memcpy(bp, data->data, data->len);
+        break;
+
+        /*
+         * Blocks intended for public-key operation.
+         */
+    case RSA_BlockPublic:
+
+        /*
+         * 0x00 || BT || Pad || 0x00 || ActualData
+         *   1      1   padLen    1      data->len
+         * Pad is all non-zero random bytes.
+         */
+        padLen = modulusLen - data->len - 3;
+        PORT_Assert(padLen >= RSA_BLOCK_MIN_PAD_LEN);
+        if (padLen < RSA_BLOCK_MIN_PAD_LEN) {
+            nss_ZFreeIf(block);
+            return NULL;
+        }
+        for (i = 0; i < padLen; i++) {
+            /* Pad with non-zero random data. */
+            do {
+                rv = RNG_GenerateGlobalRandomBytes(bp + i, 1);
+            } while (rv == SECSuccess
+                     && bp[i] == RSA_BLOCK_AFTER_PAD_OCTET);
+            if (rv != SECSuccess) {
+                nss_ZFreeIf(block);
+                return NULL;
+            }
+        }
+        bp += padLen;
+        *bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
+        nsslibc_memcpy(bp, data->data, data->len);
+
+        break;
+
+    default:
+        PORT_Assert(0);
+        nss_ZFreeIf(block);
+        return NULL;
+    }
+
+    return block;
+}
+
+static SECStatus
+rsa_FormatBlock(SECItem * result, unsigned modulusLen,
+                RSA_BlockType blockType, SECItem * data)
+{
+    /*
+     * XXX For now assume that the data length fits in a single
+     * XXX encryption block; the ASSERTs below force this.
+     * XXX To fix it, each case will have to loop over chunks whose
+     * XXX lengths satisfy the assertions, until all data is handled.
+     * XXX (Unless RSA has more to say about how to handle data
+     * XXX which does not fit in a single encryption block?)
+     * XXX And I do not know what the result is supposed to be,
+     * XXX so the interface to this function may need to change
+     * XXX to allow for returning multiple blocks, if they are
+     * XXX not wanted simply concatenated one after the other.
+     */
+
+    switch (blockType) {
+    case RSA_BlockPrivate:
+    case RSA_BlockPublic:
+        /*
+         * 0x00 || BT || Pad || 0x00 || ActualData
+         *
+         * The "3" below is the first octet + the second octet + the 0x00
+         * octet that always comes just before the ActualData.
+         */
+        PORT_Assert(data->len <=
+                    (modulusLen - (3 + RSA_BLOCK_MIN_PAD_LEN)));
+
+        result->data = rsa_FormatOneBlock(modulusLen, blockType, data);
+        if (result->data == NULL) {
+            result->len = 0;
+            return SECFailure;
+        }
+        result->len = modulusLen;
+
+        break;
+
+    case RSA_BlockRaw:
+        /*
+         * Pad || ActualData
+         * Pad is zeros. The application is responsible for recovering
+         * the actual data.
+         */
+        if (data->len > modulusLen) {
+            return SECFailure;
+        }
+        result->data = (unsigned char *) nss_ZAlloc(NULL, modulusLen);
+        result->len = modulusLen;
+        nsslibc_memcpy(result->data + (modulusLen - data->len), data->data,
+                    data->len);
+        break;
+
+    default:
+        PORT_Assert(0);
+        result->data = NULL;
+        result->len = 0;
+        return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+pem_RSA_Sign(pemLOWKEYPrivateKey * key,
+             unsigned char *output,
+             unsigned int *output_len,
+             unsigned int maxOutputLen,
+             unsigned char *input, unsigned int input_len)
+{
+    SECStatus rv = SECSuccess;
+    unsigned int modulus_len = pem_PrivateModulusLen(key);
+    SECItem formatted;
+    SECItem unformatted;
+
+    if (maxOutputLen < modulus_len)
+        return SECFailure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        return SECFailure;
+
+    unformatted.len = input_len;
+    unformatted.data = input;
+    formatted.data = NULL;
+    rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockPrivate,
+                         &unformatted);
+    if (rv != SECSuccess)
+        goto done;
+
+    rv = RSA_PrivateKeyOpDoubleChecked(&key->u.rsa, output,
+                                       formatted.data);
+    *output_len = modulus_len;
+
+    goto done;
+
+  done:
+    if (formatted.data != NULL)
+        nss_ZFreeIf(formatted.data);
+    return rv;
+}
+
+#if 0
+/* XXX Doesn't set error code */
+SECStatus
+RSA_CheckSign(NSSLOWKEYPublicKey * key,
+              unsigned char *sign,
+              unsigned int sign_len,
+              unsigned char *hash, unsigned int hash_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PublicModulusLen(key);
+    unsigned int i;
+    unsigned char *buffer;
+
+    modulus_len = pem_PublicModulusLen(key);
+    if (sign_len != modulus_len)
+        goto failure;
+    /*
+     * 0x00 || BT || Pad || 0x00 || ActualData
+     *
+     * The "3" below is the first octet + the second octet + the 0x00
+     * octet that always comes just before the ActualData.
+     */
+    if (hash_len > modulus_len - (3 + RSA_BLOCK_MIN_PAD_LEN))
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+
+    buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1);
+    if (!buffer)
+        goto failure;
+
+    rv = RSA_PublicKeyOp(&key->u.rsa, buffer, sign);
+    if (rv != SECSuccess)
+        goto loser;
+
+    /*
+     * check the padding that was used
+     */
+    if (buffer[0] != 0 || buffer[1] != 1)
+        goto loser;
+    for (i = 2; i < modulus_len - hash_len - 1; i++) {
+        if (buffer[i] != 0xff)
+            goto loser;
+    }
+    if (buffer[i] != 0)
+        goto loser;
+
+    /*
+     * make sure we get the same results
+     */
+    if (memcmp(buffer + modulus_len - hash_len, hash, hash_len) != 0)
+        goto loser;
+
+    nss_ZFreeIf(buffer);
+    return SECSuccess;
+
+  loser:
+    nss_ZFreeIf(buffer);
+  failure:
+    return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_CheckSignRecover(NSSLOWKEYPublicKey * key,
+                     unsigned char *data,
+                     unsigned int *data_len,
+                     unsigned int max_output_len,
+                     unsigned char *sign, unsigned int sign_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PublicModulusLen(key);
+    unsigned int i;
+    unsigned char *buffer;
+
+    if (sign_len != modulus_len)
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+
+    buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1);
+    if (!buffer)
+        goto failure;
+
+    rv = RSA_PublicKeyOp(&key->u.rsa, buffer, sign);
+    if (rv != SECSuccess)
+        goto loser;
+    *data_len = 0;
+
+    /*
+     * check the padding that was used
+     */
+    if (buffer[0] != 0 || buffer[1] != 1)
+        goto loser;
+    for (i = 2; i < modulus_len; i++) {
+        if (buffer[i] == 0) {
+            *data_len = modulus_len - i - 1;
+            break;
+        }
+        if (buffer[i] != 0xff)
+            goto loser;
+    }
+    if (*data_len == 0)
+        goto loser;
+    if (*data_len > max_output_len)
+        goto loser;
+
+    /*
+     * make sure we get the same results
+     */
+    nsslibc_memcpy(data, buffer + modulus_len - *data_len, *data_len);
+
+    nss_ZFreeIf(buffer);
+    return SECSuccess;
+
+  loser:
+    nss_ZFreeIf(buffer);
+  failure:
+    return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_EncryptBlock(NSSLOWKEYPublicKey * key,
+                 unsigned char *output,
+                 unsigned int *output_len,
+                 unsigned int max_output_len,
+                 unsigned char *input, unsigned int input_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PublicModulusLen(key);
+    SECItem formatted;
+    SECItem unformatted;
+
+    formatted.data = NULL;
+    if (max_output_len < modulus_len)
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+
+    unformatted.len = input_len;
+    unformatted.data = input;
+    formatted.data = NULL;
+    rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockPublic,
+                         &unformatted);
+    if (rv != SECSuccess)
+        goto failure;
+
+    rv = RSA_PublicKeyOp(&key->u.rsa, output, formatted.data);
+    if (rv != SECSuccess)
+        goto failure;
+
+    nss_ZFreeIf(formatted.data);
+    *output_len = modulus_len;
+    return SECSuccess;
+
+  failure:
+    if (formatted.data != NULL)
+        nss_ZFreeIf(formatted.data);
+    return SECFailure;
+}
+#endif
+
+/* XXX Doesn't set error code */
+SECStatus
+pem_RSA_DecryptBlock(pemLOWKEYPrivateKey * key,
+                     unsigned char *output,
+                     unsigned int *output_len,
+                     unsigned int max_output_len,
+                     unsigned char *input, unsigned int input_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PrivateModulusLen(key);
+    unsigned int i;
+    unsigned char *buffer;
+
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+    if (input_len != modulus_len)
+        goto failure;
+
+    buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1);
+    if (!buffer)
+        goto failure;
+
+    rv = RSA_PrivateKeyOp(&key->u.rsa, buffer, input);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+
+    if (buffer[0] != 0 || buffer[1] != 2)
+        goto loser;
+    *output_len = 0;
+    for (i = 2; i < modulus_len; i++) {
+        if (buffer[i] == 0) {
+            *output_len = modulus_len - i - 1;
+            break;
+        }
+    }
+    if (*output_len == 0)
+        goto loser;
+    if (*output_len > max_output_len)
+        goto loser;
+
+    nsslibc_memcpy(output, buffer + modulus_len - *output_len, *output_len);
+
+    nss_ZFreeIf(buffer);
+    return SECSuccess;
+
+  loser:
+    nss_ZFreeIf(buffer);
+  failure:
+    return SECFailure;
+}
+
+#if 0
+/* XXX Doesn't set error code */
+/*
+ * added to make pkcs #11 happy
+ *   RAW is RSA_X_509
+ */
+SECStatus
+pem_RSA_SignRaw(pemLOWKEYPrivateKey * key,
+                unsigned char *output,
+                unsigned int *output_len,
+                unsigned int maxOutputLen,
+                unsigned char *input, unsigned int input_len)
+{
+    SECStatus rv = SECSuccess;
+    unsigned int modulus_len = pem_PrivateModulusLen(key);
+    SECItem formatted;
+    SECItem unformatted;
+
+    if (maxOutputLen < modulus_len)
+        return SECFailure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        return SECFailure;
+
+    unformatted.len = input_len;
+    unformatted.data = input;
+    formatted.data = NULL;
+    rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockRaw,
+                         &unformatted);
+    if (rv != SECSuccess)
+        goto done;
+
+    rv = RSA_PrivateKeyOpDoubleChecked(&key->u.rsa, output,
+                                       formatted.data);
+    *output_len = modulus_len;
+
+  done:
+    if (formatted.data != NULL)
+        nss_ZFreeIf(formatted.data);
+    return rv;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_CheckSignRaw(NSSLOWKEYPublicKey * key,
+                 unsigned char *sign,
+                 unsigned int sign_len,
+                 unsigned char *hash, unsigned int hash_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PublicModulusLen(key);
+    unsigned char *buffer;
+
+    if (sign_len != modulus_len)
+        goto failure;
+    if (hash_len > modulus_len)
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+
+    buffer = (unsigned char *) nss_ZAlloc(NULL, modulus_len + 1);
+    if (!buffer)
+        goto failure;
+
+    rv = RSA_PublicKeyOp(&key->u.rsa, buffer, sign);
+    if (rv != SECSuccess)
+        goto loser;
+
+    /*
+     * make sure we get the same results
+     */
+    /* NOTE: should we verify the leading zeros? */
+    if (memcmp(buffer + (modulus_len - hash_len), hash, hash_len) !=
+        0)
+        goto loser;
+
+    nss_ZFreeIf(buffer);
+    return SECSuccess;
+
+  loser:
+    nss_ZFreeIf(buffer);
+  failure:
+    return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_CheckSignRecoverRaw(NSSLOWKEYPublicKey * key,
+                        unsigned char *data,
+                        unsigned int *data_len,
+                        unsigned int max_output_len,
+                        unsigned char *sign, unsigned int sign_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PublicModulusLen(key);
+
+    if (sign_len != modulus_len)
+        goto failure;
+    if (max_output_len < modulus_len)
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+
+    rv = RSA_PublicKeyOp(&key->u.rsa, data, sign);
+    if (rv != SECSuccess)
+        goto failure;
+
+    *data_len = modulus_len;
+    return SECSuccess;
+
+  failure:
+    return SECFailure;
+}
+
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_EncryptRaw(NSSLOWKEYPublicKey * key,
+               unsigned char *output,
+               unsigned int *output_len,
+               unsigned int max_output_len,
+               unsigned char *input, unsigned int input_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PublicModulusLen(key);
+    SECItem formatted;
+    SECItem unformatted;
+
+    formatted.data = NULL;
+    if (max_output_len < modulus_len)
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+
+    unformatted.len = input_len;
+    unformatted.data = input;
+    formatted.data = NULL;
+    rv = rsa_FormatBlock(&formatted, modulus_len, RSA_BlockRaw,
+                         &unformatted);
+    if (rv != SECSuccess)
+        goto failure;
+
+    rv = RSA_PublicKeyOp(&key->u.rsa, output, formatted.data);
+    if (rv != SECSuccess)
+        goto failure;
+
+    nss_ZFreeIf(formatted.data);
+    *output_len = modulus_len;
+    return SECSuccess;
+
+  failure:
+    if (formatted.data != NULL)
+        nss_ZFreeIf(formatted.data);
+    return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+pem_RSA_DecryptRaw(pemLOWKEYPrivateKey * key,
+                   unsigned char *output,
+                   unsigned int *output_len,
+                   unsigned int max_output_len,
+                   unsigned char *input, unsigned int input_len)
+{
+    SECStatus rv;
+    unsigned int modulus_len = pem_PrivateModulusLen(key);
+
+    if (modulus_len <= 0)
+        goto failure;
+    if (modulus_len > max_output_len)
+        goto failure;
+    PORT_Assert(key->keyType == pemLOWKEYRSAKey);
+    if (key->keyType != pemLOWKEYRSAKey)
+        goto failure;
+    if (input_len != modulus_len)
+        goto failure;
+
+    rv = RSA_PrivateKeyOp(&key->u.rsa, output, input);
+    if (rv != SECSuccess) {
+        goto failure;
+    }
+
+    *output_len = modulus_len;
+    return SECSuccess;
+
+  failure:
+    return SECFailure;
+}
+#endif
diff --git a/a/nss/lib/ckfw/pem/util.c b/b/nss/lib/ckfw/pem/util.c
new file mode 100644
index 0000000..e5fb4da
--- /dev/null
+++ b/b/nss/lib/ckfw/pem/util.c
@@ -0,0 +1,314 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Crittenden (rcritten@redhat.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/* cribbed from secutil.c */
+
+#include "prtypes.h"
+#include "prtime.h"
+#include "prlong.h"
+#include "prerror.h"
+#include "prlog.h"
+#include "prprf.h"
+#include "plgetopt.h"
+#include "plstr.h"
+#include "prenv.h"
+#include "prnetdb.h"
+#include "base.h"
+#include "base64.h"
+
+#include "cryptohi.h"
+#include "secpkcs7.h"
+#include "secerr.h"
+
+#include "ckpem.h"
+
+#include <stdarg.h>
+
+#define CHUNK_SIZE  512
+#define PUT_Object(obj,err) \
+  { \
+    if (count >= size) { \
+    *derlist = *derlist ? \
+                nss_ZREALLOCARRAY(*derlist, SECItem *, \
+                               (size+CHUNK_SIZE) ) : \
+                nss_ZNEWARRAY(NULL, SECItem *, \
+                               (size+CHUNK_SIZE) ) ; \
+      if ((SECItem **)NULL == *derlist) { \
+        err = CKR_HOST_MEMORY; \
+        goto loser; \
+      } \
+      size += CHUNK_SIZE; \
+    } \
+    (*derlist)[ count ] = (obj); \
+    count++; \
+  }
+
+/* Read certificates from a flat file */
+
+static SECItem *AllocItem(SECItem * item, unsigned int len)
+{
+    SECItem *result = NULL;
+
+    if (item == NULL) {
+	result = nss_ZAlloc(NULL, sizeof(SECItem));
+	if (result == NULL) {
+	    goto loser;
+	}
+    } else {
+	PORT_Assert(item->data == NULL);
+	result = item;
+    }
+
+    result->len = len;
+    if (len) {
+        result->data = nss_ZAlloc(NULL, len);
+    }
+
+    return (result);
+
+  loser:
+    return (NULL);
+}
+
+static SECStatus FileToItem(SECItem * dst, PRFileDesc * src)
+{
+    PRFileInfo info;
+    PRInt32 numBytes;
+    PRStatus prStatus;
+
+    prStatus = PR_GetOpenFileInfo(src, &info);
+
+    if (prStatus != PR_SUCCESS || info.type == PR_FILE_DIRECTORY) {
+	return SECFailure;
+    }
+
+    /* XXX workaround for 3.1, not all utils zero dst before sending */
+    dst->data = 0;
+    if (!AllocItem(dst, info.size+1))
+	goto loser;
+
+    numBytes = PR_Read(src, dst->data, info.size);
+    if (numBytes != info.size) {
+	goto loser;
+    }
+
+    return SECSuccess;
+  loser:
+    nss_ZFreeIf(dst->data);
+    return SECFailure;
+}
+
+int
+ReadDERFromFile(SECItem *** derlist, char *filename, PRBool ascii,
+		int *cipher, char **ivstring, PRBool certsonly)
+{
+    SECStatus rv;
+    PRFileDesc *inFile;
+    int count = 0, size = 0;
+    SECItem *der = NULL;
+    int error;
+    SECItem filedata;
+    char *c, *iv;
+
+    inFile = PR_Open(filename, PR_RDONLY, 0);
+    if (!inFile)
+	return -1;
+
+    if (ascii) {
+	/* First convert ascii to binary */
+	char *asc, *body;
+
+	/* Read in ascii data */
+	rv = FileToItem(&filedata, inFile);
+	if (rv != SECSuccess) {
+	    PR_Close(inFile);
+	    return -1;
+	}
+	asc = (char *) filedata.data;
+	if (!asc) {
+	    PR_Close(inFile);
+	    return -1;
+	}
+
+	/* check for headers and trailers and remove them */
+	if (strstr(asc, "-----BEGIN") != NULL) {
+            int key = 0;
+	    while ((asc) && ((body = strstr(asc, "-----BEGIN")) != NULL)) {
+		char *trailer;
+                key = 0;
+		if ((strncmp(body, "-----BEGIN RSA PRIVATE KEY", 25) == 0) ||
+		    (strncmp(body, "-----BEGIN PRIVATE KEY", 21) == 0)) {
+                    key = 1;
+		    c = body;
+		    body = strchr(body, '\n');
+		    if (NULL == body)
+			goto loser;
+		    body++;
+		    if (strncmp(body, "Proc-Type: 4,ENCRYPTED", 22) == 0) {
+			body = strchr(body, '\n');
+			if (NULL == body)
+			    goto loser;
+			body++;
+			if (strncmp(body, "DEK-Info: ", 10) == 0) {
+			    body += 10;
+			    c = body;
+			    body = strchr(body, ',');
+			    if (body == NULL)
+				goto loser;
+			    *body = '\0';
+			    if (!PL_strcasecmp(c, "DES-EDE3-CBC"))
+				*cipher = NSS_DES_EDE3_CBC;
+			    else if (!PL_strcasecmp(c, "DES-CBC"))
+				*cipher = NSS_DES_CBC;
+			    else {
+				*cipher = -1;
+				goto loser;
+			    }
+			    body++;
+			    iv = body;
+			    body = strchr(body, '\n');
+			    if (body == NULL)
+				goto loser;
+			    *body = '\0';
+			    body++;
+			    *ivstring = strdup(iv);
+			}
+		    } else {	/* Else the private key is not encrypted */
+			*cipher = 0;
+			body = c;
+		    }
+		}
+		der = (SECItem *) malloc(sizeof(SECItem));
+                if (der == NULL)
+                    goto loser;
+
+		trailer = NULL;
+		asc = body;
+		body = strchr(body, '\n');
+		if (!body)
+		    body = strchr(asc, '\r');	/* maybe this is a MAC file */
+		if (body) {
+		    trailer = strstr(++body, "-----END");
+		}
+		if (trailer != NULL) {
+		    asc = trailer + 1;
+		    *trailer = '\0';
+		} else {
+		    free(der);
+		    goto loser;
+		}
+
+		/* Convert to binary */
+		rv = ATOB_ConvertAsciiToItem(der, body);
+		if (rv) {
+                    free(der);
+		    goto loser;
+		}
+                if ((certsonly && !key) || (!certsonly && key)) {
+		    PUT_Object(der, error);
+                } else {
+                    free(der->data);
+                    free(der);
+                }
+	    }			/* while */
+	} else {		/* No headers and footers, translate the blob */
+	    der = (SECItem *) malloc(sizeof(SECItem));
+	    if (der == NULL)
+		goto loser;
+
+	    rv = ATOB_ConvertAsciiToItem(der, asc);
+	    if (rv) {
+		free(der);
+		goto loser;
+	    }
+
+	    /* NOTE: This code path has never been tested. */
+	    PUT_Object(der, error);
+	}
+
+	nss_ZFreeIf(filedata.data);
+        filedata.data = 0;
+	filedata.len = 0;
+    } else {
+	/* Read in binary der */
+	rv = FileToItem(der, inFile);
+	if (rv != SECSuccess) {
+	    PR_Close(inFile);
+	    return -1;
+	}
+    }
+    PR_Close(inFile);
+    return count;
+
+  loser:
+    if (filedata.len > 0)
+	nss_ZFreeIf(filedata.data);
+    PR_Close(inFile);
+    return -1;
+}
+
+#ifdef DEBUG
+#define LOGGING_BUFFER_SIZE 400
+#define PEM_DEFAULT_LOG_FILE "/tmp/pkcs11.log"
+static const char *pemLogModuleName = "PEM";
+static PRLogModuleInfo* pemLogModule;
+#endif
+
+void open_log()
+{
+#ifdef DEBUG
+    const char *nsprLogFile = PR_GetEnv("NSPR_LOG_FILE");
+
+    pemLogModule = PR_NewLogModule(pemLogModuleName);
+
+    (void) PR_SetLogFile(nsprLogFile ? nsprLogFile : PEM_DEFAULT_LOG_FILE);
+    /* If false, the log file will remain what it was before */
+#endif
+}
+
+void plog(const char *fmt, ...)
+{
+#ifdef DEBUG
+    char buf[LOGGING_BUFFER_SIZE];
+    va_list ap;
+
+    va_start(ap, fmt);
+    PR_vsnprintf(buf, sizeof(buf), fmt, ap);
+    va_end(ap);
+    PR_LOG(pemLogModule, PR_LOG_DEBUG, ("%s", buf));
+#endif
+}