summaryrefslogtreecommitdiff
path: root/security/nss/lib/softoken
diff options
context:
space:
mode:
authorcvs2hg <devnull@localhost>2009-05-21 19:50:33 +0000
committercvs2hg <devnull@localhost>2009-05-21 19:50:33 +0000
commitfe7c0a62d359825d537f29602aa213a722e3e7a1 (patch)
treedbdfa03a22dc1eb8293fcf5d597d8ac76e565f2a /security/nss/lib/softoken
parentb5c29c9fea36731e5d72bd223c1ec4c5d17e5e22 (diff)
parentdb382805a1d20e2207f9f7081e18a16474484a0c (diff)
downloadnss-hg-fe7c0a62d359825d537f29602aa213a722e3e7a1.tar.gz
fixup commit for tag 'NSS_3_12_2_WITH_CKBI_1_75_RTM'NSS_3_12_2_WITH_CKBI_1_75_RTM
Diffstat (limited to 'security/nss/lib/softoken')
-rw-r--r--security/nss/lib/softoken/Makefile98
-rw-r--r--security/nss/lib/softoken/config.mk110
-rw-r--r--security/nss/lib/softoken/ecdecode.c641
-rw-r--r--security/nss/lib/softoken/fipsaudt.c351
-rw-r--r--security/nss/lib/softoken/fipstest.c1992
-rw-r--r--security/nss/lib/softoken/fipstokn.c1554
-rw-r--r--security/nss/lib/softoken/legacydb/Makefile82
-rw-r--r--security/nss/lib/softoken/legacydb/cdbhdl.h85
-rw-r--r--security/nss/lib/softoken/legacydb/config.mk103
-rw-r--r--security/nss/lib/softoken/legacydb/dbmshim.c647
-rw-r--r--security/nss/lib/softoken/legacydb/keydb.c2277
-rw-r--r--security/nss/lib/softoken/legacydb/keydbi.h86
-rw-r--r--security/nss/lib/softoken/legacydb/lgattr.c1808
-rw-r--r--security/nss/lib/softoken/legacydb/lgcreate.c957
-rw-r--r--security/nss/lib/softoken/legacydb/lgdb.h198
-rw-r--r--security/nss/lib/softoken/legacydb/lgdestroy.c144
-rw-r--r--security/nss/lib/softoken/legacydb/lgfind.c940
-rw-r--r--security/nss/lib/softoken/legacydb/lginit.c663
-rw-r--r--security/nss/lib/softoken/legacydb/lgutil.c424
-rw-r--r--security/nss/lib/softoken/legacydb/lowcert.c827
-rw-r--r--security/nss/lib/softoken/legacydb/lowkey.c468
-rw-r--r--security/nss/lib/softoken/legacydb/lowkeyi.h198
-rw-r--r--security/nss/lib/softoken/legacydb/lowkeyti.h170
-rw-r--r--security/nss/lib/softoken/legacydb/manifest.mn67
-rw-r--r--security/nss/lib/softoken/legacydb/nssdbm.def64
-rw-r--r--security/nss/lib/softoken/legacydb/nssdbm.rc100
-rw-r--r--security/nss/lib/softoken/legacydb/pcert.h261
-rw-r--r--security/nss/lib/softoken/legacydb/pcertdb.c5366
-rw-r--r--security/nss/lib/softoken/legacydb/pcertt.h450
-rw-r--r--security/nss/lib/softoken/legacydb/pk11db.c773
-rw-r--r--security/nss/lib/softoken/lgglue.c429
-rw-r--r--security/nss/lib/softoken/lgglue.h92
-rw-r--r--security/nss/lib/softoken/lowkey.c525
-rw-r--r--security/nss/lib/softoken/lowkeyi.h108
-rw-r--r--security/nss/lib/softoken/lowkeyti.h127
-rw-r--r--security/nss/lib/softoken/lowpbe.c1410
-rw-r--r--security/nss/lib/softoken/lowpbe.h140
-rw-r--r--security/nss/lib/softoken/manifest.mn103
-rw-r--r--security/nss/lib/softoken/padbuf.c81
-rw-r--r--security/nss/lib/softoken/pk11pars.h871
-rw-r--r--security/nss/lib/softoken/pkcs11.c4218
-rw-r--r--security/nss/lib/softoken/pkcs11.h323
-rw-r--r--security/nss/lib/softoken/pkcs11c.c6124
-rw-r--r--security/nss/lib/softoken/pkcs11f.h937
-rw-r--r--security/nss/lib/softoken/pkcs11i.h715
-rw-r--r--security/nss/lib/softoken/pkcs11n.h289
-rw-r--r--security/nss/lib/softoken/pkcs11ni.h52
-rw-r--r--security/nss/lib/softoken/pkcs11p.h54
-rw-r--r--security/nss/lib/softoken/pkcs11t.h1774
-rw-r--r--security/nss/lib/softoken/pkcs11u.c2012
-rw-r--r--security/nss/lib/softoken/pkcs11u.h52
-rw-r--r--security/nss/lib/softoken/rsawrapr.c917
-rw-r--r--security/nss/lib/softoken/sdb.c2028
-rw-r--r--security/nss/lib/softoken/sdb.h111
-rw-r--r--security/nss/lib/softoken/sftkdb.c2410
-rw-r--r--security/nss/lib/softoken/sftkdb.h119
-rw-r--r--security/nss/lib/softoken/sftkdbt.h51
-rw-r--r--security/nss/lib/softoken/sftkdbti.h92
-rw-r--r--security/nss/lib/softoken/sftkmod.c695
-rw-r--r--security/nss/lib/softoken/sftkpars.c644
-rw-r--r--security/nss/lib/softoken/sftkpars.h49
-rw-r--r--security/nss/lib/softoken/sftkpwd.c1299
-rw-r--r--security/nss/lib/softoken/softkver.c56
-rw-r--r--security/nss/lib/softoken/softkver.h66
-rw-r--r--security/nss/lib/softoken/softoken.h284
-rw-r--r--security/nss/lib/softoken/softokn.def61
-rw-r--r--security/nss/lib/softoken/softokn.rc100
-rw-r--r--security/nss/lib/softoken/softoknt.h73
-rw-r--r--security/nss/lib/softoken/tlsprf.c215
69 files changed, 51610 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/Makefile b/security/nss/lib/softoken/Makefile
new file mode 100644
index 000000000..d70b909a8
--- /dev/null
+++ b/security/nss/lib/softoken/Makefile
@@ -0,0 +1,98 @@
+#! gmake
+#
+# ***** 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 *****
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+export:: private_export
+
+# indicates dependency on freebl static lib
+$(SHARED_LIBRARY): $(CRYPTOLIB)
+
+# On AIX 4.3, IBM xlC_r compiler (version 3.6.6) cannot compile
+# pkcs11c.c in 64-bit mode for unknown reasons. A workaround is
+# to compile it with optimizations turned on. (Bugzilla bug #63815)
+ifeq ($(OS_TARGET)$(OS_RELEASE),AIX4.3)
+ifeq ($(USE_64),1)
+ifndef BUILD_OPT
+$(OBJDIR)/pkcs11.o: pkcs11.c
+ @$(MAKE_OBJDIR)
+ $(CC) -o $@ -c -O2 $(CFLAGS) $<
+$(OBJDIR)/pkcs11c.o: pkcs11c.c
+ @$(MAKE_OBJDIR)
+ $(CC) -o $@ -c -O2 $(CFLAGS) $<
+endif
+endif
+endif
diff --git a/security/nss/lib/softoken/config.mk b/security/nss/lib/softoken/config.mk
new file mode 100644
index 000000000..c22c2008b
--- /dev/null
+++ b/security/nss/lib/softoken/config.mk
@@ -0,0 +1,110 @@
+#
+# ***** 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 *****
+
+# $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS)
+CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
+CRYPTODIR=../freebl
+ifdef MOZILLA_SECURITY_BUILD
+ CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)crypto.$(LIB_SUFFIX)
+ CRYPTODIR=../crypto
+endif
+
+EXTRA_LIBS += \
+ $(CRYPTOLIB) \
+ $(NULL)
+
+# can't do this in manifest.mn because OS_TARGET isn't defined there.
+ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+
+# don't want the 32 in the shared library name
+SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
+IMPORT_LIBRARY = $(OBJDIR)/$(IMPORT_LIB_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION)$(IMPORT_LIB_SUFFIX)
+
+RES = $(OBJDIR)/$(LIBRARY_NAME).res
+RESNAME = $(LIBRARY_NAME).rc
+
+ifdef NS_USE_GCC
+EXTRA_SHARED_LIBS += \
+ -L$(DIST)/lib \
+ -lnssutil3 \
+ -lsqlite3 \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+else # ! NS_USE_GCC
+
+EXTRA_SHARED_LIBS += \
+ $(DIST)/lib/sqlite3.lib \
+ $(DIST)/lib/nssutil3.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \
+ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)nspr4.lib \
+ $(DIST)/lib/nssutil3.lib \
+ $(NULL)
+endif # NS_USE_GCC
+
+else
+
+# $(PROGRAM) has NO explicit dependencies on $(EXTRA_SHARED_LIBS)
+# $(EXTRA_SHARED_LIBS) come before $(OS_LIBS), except on AIX.
+EXTRA_SHARED_LIBS += \
+ -L$(DIST)/lib \
+ -lnssutil3 \
+ -lsqlite3 \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+endif
+
+ifeq ($(OS_TARGET),AIX)
+OS_LIBS += -lpthread
+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'
+OS_LIBS += -lbsm
+endif
+
+ifeq ($(OS_TARGET),WINCE)
+DEFINES += -DDBM_USING_NSPR
+endif
diff --git a/security/nss/lib/softoken/ecdecode.c b/security/nss/lib/softoken/ecdecode.c
new file mode 100644
index 000000000..c037dc519
--- /dev/null
+++ b/security/nss/lib/softoken/ecdecode.c
@@ -0,0 +1,641 @@
+/*
+ * ***** 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 Elliptic Curve Cryptography library.
+ *
+ * The Initial Developer of the Original Code is
+ * Sun Microsystems, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2003
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com> and
+ * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+
+#ifdef NSS_ENABLE_ECC
+
+#include "blapi.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "secerr.h"
+#include "ec.h"
+#include "ecl-curve.h"
+
+#define CHECK_OK(func) if (func == NULL) goto cleanup
+#define CHECK_SEC_OK(func) if (SECSuccess != (rv = func)) goto cleanup
+
+/*
+ * Initializes a SECItem from a hexadecimal string
+ *
+ * Warning: This function ignores leading 00's, so any leading 00's
+ * in the hexadecimal string must be optional.
+ */
+static SECItem *
+hexString2SECItem(PRArenaPool *arena, SECItem *item, const char *str)
+{
+ int i = 0;
+ int byteval = 0;
+ int tmp = PORT_Strlen(str);
+
+ if ((tmp % 2) != 0) return NULL;
+
+ /* skip leading 00's unless the hex string is "00" */
+ while ((tmp > 2) && (str[0] == '0') && (str[1] == '0')) {
+ str += 2;
+ tmp -= 2;
+ }
+
+ item->data = (unsigned char *) PORT_ArenaAlloc(arena, tmp/2);
+ if (item->data == NULL) return NULL;
+ item->len = tmp/2;
+
+ while (str[i]) {
+ if ((str[i] >= '0') && (str[i] <= '9'))
+ tmp = str[i] - '0';
+ else if ((str[i] >= 'a') && (str[i] <= 'f'))
+ tmp = str[i] - 'a' + 10;
+ else if ((str[i] >= 'A') && (str[i] <= 'F'))
+ tmp = str[i] - 'A' + 10;
+ else
+ return NULL;
+
+ byteval = byteval * 16 + tmp;
+ if ((i % 2) != 0) {
+ item->data[i/2] = byteval;
+ byteval = 0;
+ }
+ i++;
+ }
+
+ return item;
+}
+
+/* Copy all of the fields from srcParams into dstParams
+ */
+SECStatus
+EC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
+ const ECParams *srcParams)
+{
+ SECStatus rv = SECFailure;
+
+ dstParams->arena = arena;
+ dstParams->type = srcParams->type;
+ dstParams->fieldID.size = srcParams->fieldID.size;
+ dstParams->fieldID.type = srcParams->fieldID.type;
+ if (srcParams->fieldID.type == ec_field_GFp) {
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->fieldID.u.prime,
+ &srcParams->fieldID.u.prime));
+ } else {
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->fieldID.u.poly,
+ &srcParams->fieldID.u.poly));
+ }
+ dstParams->fieldID.k1 = srcParams->fieldID.k1;
+ dstParams->fieldID.k2 = srcParams->fieldID.k2;
+ dstParams->fieldID.k3 = srcParams->fieldID.k3;
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->curve.a,
+ &srcParams->curve.a));
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->curve.b,
+ &srcParams->curve.b));
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->curve.seed,
+ &srcParams->curve.seed));
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->base,
+ &srcParams->base));
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->order,
+ &srcParams->order));
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->DEREncoding,
+ &srcParams->DEREncoding));
+ dstParams->name = srcParams->name;
+ CHECK_SEC_OK(SECITEM_CopyItem(arena, &dstParams->curveOID,
+ &srcParams->curveOID));
+ dstParams->cofactor = srcParams->cofactor;
+
+ return SECSuccess;
+
+cleanup:
+ return SECFailure;
+}
+
+static SECStatus
+gf_populate_params(ECCurveName name, ECFieldType field_type, ECParams *params)
+{
+ SECStatus rv = SECFailure;
+ const ECCurveParams *curveParams;
+ /* 2 ['0'+'4'] + MAX_ECKEY_LEN * 2 [x,y] * 2 [hex string] + 1 ['\0'] */
+ char genenc[3 + 2 * 2 * MAX_ECKEY_LEN];
+
+ if ((name < ECCurve_noName) || (name > ECCurve_pastLastCurve)) goto cleanup;
+ params->name = name;
+ curveParams = ecCurve_map[params->name];
+ CHECK_OK(curveParams);
+ params->fieldID.size = curveParams->size;
+ params->fieldID.type = field_type;
+ if (field_type == ec_field_GFp) {
+ CHECK_OK(hexString2SECItem(params->arena, &params->fieldID.u.prime,
+ curveParams->irr));
+ } else {
+ CHECK_OK(hexString2SECItem(params->arena, &params->fieldID.u.poly,
+ curveParams->irr));
+ }
+ CHECK_OK(hexString2SECItem(params->arena, &params->curve.a,
+ curveParams->curvea));
+ CHECK_OK(hexString2SECItem(params->arena, &params->curve.b,
+ curveParams->curveb));
+ genenc[0] = '0';
+ genenc[1] = '4';
+ genenc[2] = '\0';
+ strcat(genenc, curveParams->genx);
+ strcat(genenc, curveParams->geny);
+ CHECK_OK(hexString2SECItem(params->arena, &params->base, genenc));
+ CHECK_OK(hexString2SECItem(params->arena, &params->order,
+ curveParams->order));
+ params->cofactor = curveParams->cofactor;
+
+ rv = SECSuccess;
+
+cleanup:
+ return rv;
+}
+
+SECStatus
+EC_FillParams(PRArenaPool *arena, const SECItem *encodedParams,
+ ECParams *params)
+{
+ SECStatus rv = SECFailure;
+ SECOidTag tag;
+ SECItem oid = { siBuffer, NULL, 0};
+
+#if EC_DEBUG
+ int i;
+
+ printf("Encoded params in EC_DecodeParams: ");
+ for (i = 0; i < encodedParams->len; i++) {
+ printf("%02x:", encodedParams->data[i]);
+ }
+ printf("\n");
+#endif
+
+ if ((encodedParams->len != ANSI_X962_CURVE_OID_TOTAL_LEN) &&
+ (encodedParams->len != SECG_CURVE_OID_TOTAL_LEN)) {
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+ return SECFailure;
+ };
+
+ oid.len = encodedParams->len - 2;
+ oid.data = encodedParams->data + 2;
+ if ((encodedParams->data[0] != SEC_ASN1_OBJECT_ID) ||
+ ((tag = SECOID_FindOIDTag(&oid)) == SEC_OID_UNKNOWN)) {
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+ return SECFailure;
+ }
+
+ params->arena = arena;
+ params->cofactor = 0;
+ params->type = ec_params_named;
+ params->name = ECCurve_noName;
+
+ /* For named curves, fill out curveOID */
+ params->curveOID.len = oid.len;
+ params->curveOID.data = (unsigned char *) PORT_ArenaAlloc(arena, oid.len);
+ if (params->curveOID.data == NULL) goto cleanup;
+ memcpy(params->curveOID.data, oid.data, oid.len);
+
+#if EC_DEBUG
+ printf("Curve: %s\n", SECOID_FindOIDTagDescription(tag));
+#endif
+
+ switch (tag) {
+
+ /* Binary curves */
+
+ case SEC_OID_ANSIX962_EC_C2PNB163V1:
+ /* Populate params for c2pnb163v1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB163V1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB163V2:
+ /* Populate params for c2pnb163v2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB163V2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB163V3:
+ /* Populate params for c2pnb163v3 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB163V3, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB176V1:
+ /* Populate params for c2pnb176v1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB176V1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB191V1:
+ /* Populate params for c2tnb191v1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB191V1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB191V2:
+ /* Populate params for c2tnb191v2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB191V2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB191V3:
+ /* Populate params for c2tnb191v3 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB191V3, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB208W1:
+ /* Populate params for c2pnb208w1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB208W1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB239V1:
+ /* Populate params for c2tnb239v1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB239V1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB239V2:
+ /* Populate params for c2tnb239v2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB239V2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB239V3:
+ /* Populate params for c2tnb239v3 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB239V3, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB272W1:
+ /* Populate params for c2pnb272w1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB272W1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB304W1:
+ /* Populate params for c2pnb304w1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB304W1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB359V1:
+ /* Populate params for c2tnb359v1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB359V1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2PNB368W1:
+ /* Populate params for c2pnb368w1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_PNB368W1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_C2TNB431R1:
+ /* Populate params for c2tnb431r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_CHAR2_TNB431R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT113R1:
+ /* Populate params for sect113r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_113R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT113R2:
+ /* Populate params for sect113r2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_113R2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT131R1:
+ /* Populate params for sect131r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_131R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT131R2:
+ /* Populate params for sect131r2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_131R2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT163K1:
+ /* Populate params for sect163k1
+ * (the NIST K-163 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_163K1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT163R1:
+ /* Populate params for sect163r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_163R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT163R2:
+ /* Populate params for sect163r2
+ * (the NIST B-163 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_163R2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT193R1:
+ /* Populate params for sect193r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_193R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT193R2:
+ /* Populate params for sect193r2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_193R2, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT233K1:
+ /* Populate params for sect233k1
+ * (the NIST K-233 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_233K1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT233R1:
+ /* Populate params for sect233r1
+ * (the NIST B-233 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_233R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT239K1:
+ /* Populate params for sect239k1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_239K1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT283K1:
+ /* Populate params for sect283k1
+ * (the NIST K-283 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_283K1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT283R1:
+ /* Populate params for sect283r1
+ * (the NIST B-283 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_283R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT409K1:
+ /* Populate params for sect409k1
+ * (the NIST K-409 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_409K1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT409R1:
+ /* Populate params for sect409r1
+ * (the NIST B-409 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_409R1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT571K1:
+ /* Populate params for sect571k1
+ * (the NIST K-571 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_571K1, ec_field_GF2m,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECT571R1:
+ /* Populate params for sect571r1
+ * (the NIST B-571 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_CHAR2_571R1, ec_field_GF2m,
+ params) );
+ break;
+
+ /* Prime curves */
+
+ case SEC_OID_ANSIX962_EC_PRIME192V1:
+ /* Populate params for prime192v1 aka secp192r1
+ * (the NIST P-192 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_192V1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_PRIME192V2:
+ /* Populate params for prime192v2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_192V2, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_PRIME192V3:
+ /* Populate params for prime192v3 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_192V3, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_PRIME239V1:
+ /* Populate params for prime239v1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_239V1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_PRIME239V2:
+ /* Populate params for prime239v2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_239V2, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_PRIME239V3:
+ /* Populate params for prime239v3 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_239V3, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_ANSIX962_EC_PRIME256V1:
+ /* Populate params for prime256v1 aka secp256r1
+ * (the NIST P-256 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_X9_62_PRIME_256V1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP112R1:
+ /* Populate params for secp112r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_112R1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP112R2:
+ /* Populate params for secp112r2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_112R2, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP128R1:
+ /* Populate params for secp128r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_128R1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP128R2:
+ /* Populate params for secp128r2 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_128R2, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP160K1:
+ /* Populate params for secp160k1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_160K1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP160R1:
+ /* Populate params for secp160r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_160R1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP160R2:
+ /* Populate params for secp160r1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_160R2, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP192K1:
+ /* Populate params for secp192k1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_192K1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP224K1:
+ /* Populate params for secp224k1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_224K1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP224R1:
+ /* Populate params for secp224r1
+ * (the NIST P-224 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_224R1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP256K1:
+ /* Populate params for secp256k1 */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_256K1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP384R1:
+ /* Populate params for secp384r1
+ * (the NIST P-384 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_384R1, ec_field_GFp,
+ params) );
+ break;
+
+ case SEC_OID_SECG_EC_SECP521R1:
+ /* Populate params for secp521r1
+ * (the NIST P-521 curve)
+ */
+ CHECK_SEC_OK( gf_populate_params(ECCurve_SECG_PRIME_521R1, ec_field_GFp,
+ params) );
+ break;
+
+ default:
+ break;
+ };
+
+cleanup:
+ if (!params->cofactor) {
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+#if EC_DEBUG
+ printf("Unrecognized curve, returning NULL params\n");
+#endif
+ }
+
+ return rv;
+}
+
+SECStatus
+EC_DecodeParams(const SECItem *encodedParams, ECParams **ecparams)
+{
+ PRArenaPool *arena;
+ ECParams *params;
+ SECStatus rv = SECFailure;
+
+ /* Initialize an arena for the ECParams structure */
+ if (!(arena = PORT_NewArena(NSS_FREEBL_DEFAULT_CHUNKSIZE)))
+ return SECFailure;
+
+ params = (ECParams *)PORT_ArenaZAlloc(arena, sizeof(ECParams));
+ if (!params) {
+ PORT_FreeArena(arena, PR_TRUE);
+ return SECFailure;
+ }
+
+ /* Copy the encoded params */
+ SECITEM_AllocItem(arena, &(params->DEREncoding),
+ encodedParams->len);
+ memcpy(params->DEREncoding.data, encodedParams->data, encodedParams->len);
+
+ /* Fill out the rest of the ECParams structure based on
+ * the encoded params
+ */
+ rv = EC_FillParams(arena, encodedParams, params);
+ if (rv == SECFailure) {
+ PORT_FreeArena(arena, PR_TRUE);
+ return SECFailure;
+ } else {
+ *ecparams = params;;
+ return SECSuccess;
+ }
+}
+
+#endif /* NSS_ENABLE_ECC */
diff --git a/security/nss/lib/softoken/fipsaudt.c b/security/nss/lib/softoken/fipsaudt.c
new file mode 100644
index 000000000..d17496deb
--- /dev/null
+++ b/security/nss/lib/softoken/fipsaudt.c
@@ -0,0 +1,351 @@
+/* ***** 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 Network Security Services (NSS).
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * 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 ***** */
+
+/*
+ * This file implements audit logging required by FIPS 140-2 Security
+ * Level 2.
+ */
+
+#include "prprf.h"
+#include "softoken.h"
+
+/*
+ * Print the value of the returned object handle in the output buffer
+ * on a successful return of the PKCS #11 function. If the PKCS #11
+ * function failed or the pointer to object handle is NULL (which is
+ * the case for C_DeriveKey with CKM_TLS_KEY_AND_MAC_DERIVE), an empty
+ * string is stored in the output buffer.
+ *
+ * out: the output buffer
+ * outlen: the length of the output buffer
+ * argName: the name of the "pointer to object handle" argument
+ * phObject: the pointer to object handle
+ * rv: the return value of the PKCS #11 function
+ */
+static void sftk_PrintReturnedObjectHandle(char *out, PRUint32 outlen,
+ const char *argName, CK_OBJECT_HANDLE_PTR phObject, CK_RV rv)
+{
+ if ((rv == CKR_OK) && phObject) {
+ PR_snprintf(out, outlen,
+ " *%s=0x%08lX", argName, (PRUint32)*phObject);
+ } else {
+ PORT_Assert(outlen != 0);
+ out[0] = '\0';
+ }
+}
+
+/*
+ * MECHANISM_BUFSIZE needs to be large enough for sftk_PrintMechanism,
+ * which uses <= 49 bytes.
+ */
+#define MECHANISM_BUFSIZE 64
+
+static void sftk_PrintMechanism(char *out, PRUint32 outlen,
+ CK_MECHANISM_PTR pMechanism)
+{
+ if (pMechanism) {
+ /*
+ * If we change the format string, we need to make sure
+ * MECHANISM_BUFSIZE is still large enough. We allow
+ * 20 bytes for %p on a 64-bit platform.
+ */
+ PR_snprintf(out, outlen, "%p {mechanism=0x%08lX, ...}",
+ pMechanism, (PRUint32)pMechanism->mechanism);
+ } else {
+ PR_snprintf(out, outlen, "%p", pMechanism);
+ }
+}
+
+void sftk_AuditCreateObject(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject, CK_RV rv)
+{
+ char msg[256];
+ char shObject[32];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintReturnedObjectHandle(shObject, sizeof shObject,
+ "phObject", phObject, rv);
+ PR_snprintf(msg, sizeof msg,
+ "C_CreateObject(hSession=0x%08lX, pTemplate=%p, ulCount=%lu, "
+ "phObject=%p)=0x%08lX%s",
+ (PRUint32)hSession, pTemplate, (PRUint32)ulCount,
+ phObject, (PRUint32)rv, shObject);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditCopyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject, CK_RV rv)
+{
+ char msg[256];
+ char shNewObject[32];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintReturnedObjectHandle(shNewObject, sizeof shNewObject,
+ "phNewObject", phNewObject, rv);
+ PR_snprintf(msg, sizeof msg,
+ "C_CopyObject(hSession=0x%08lX, hObject=0x%08lX, "
+ "pTemplate=%p, ulCount=%lu, phNewObject=%p)=0x%08lX%s",
+ (PRUint32)hSession, (PRUint32)hObject,
+ pTemplate, (PRUint32)ulCount, phNewObject, (PRUint32)rv, shNewObject);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+/* WARNING: hObject has been destroyed and can only be printed. */
+void sftk_AuditDestroyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_RV rv)
+{
+ char msg[256];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ PR_snprintf(msg, sizeof msg,
+ "C_DestroyObject(hSession=0x%08lX, hObject=0x%08lX)=0x%08lX",
+ (PRUint32)hSession, (PRUint32)hObject, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditGetObjectSize(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize, CK_RV rv)
+{
+ char msg[256];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ PR_snprintf(msg, sizeof msg,
+ "C_GetObjectSize(hSession=0x%08lX, hObject=0x%08lX, "
+ "pulSize=%p)=0x%08lX",
+ (PRUint32)hSession, (PRUint32)hObject,
+ pulSize, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditGetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount, CK_RV rv)
+{
+ char msg[256];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ PR_snprintf(msg, sizeof msg,
+ "C_GetAttributeValue(hSession=0x%08lX, hObject=0x%08lX, "
+ "pTemplate=%p, ulCount=%lu)=0x%08lX",
+ (PRUint32)hSession, (PRUint32)hObject,
+ pTemplate, (PRUint32)ulCount, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditSetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount, CK_RV rv)
+{
+ char msg[256];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ PR_snprintf(msg, sizeof msg,
+ "C_SetAttributeValue(hSession=0x%08lX, hObject=0x%08lX, "
+ "pTemplate=%p, ulCount=%lu)=0x%08lX",
+ (PRUint32)hSession, (PRUint32)hObject,
+ pTemplate, (PRUint32)ulCount, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditCryptInit(const char *opName, CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, CK_RV rv)
+{
+ char msg[256];
+ char mech[MECHANISM_BUFSIZE];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintMechanism(mech, sizeof mech, pMechanism);
+ PR_snprintf(msg, sizeof msg,
+ "C_%sInit(hSession=0x%08lX, pMechanism=%s, "
+ "hKey=0x%08lX)=0x%08lX",
+ opName, (PRUint32)hSession, mech,
+ (PRUint32)hKey, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditGenerateKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey, CK_RV rv)
+{
+ char msg[256];
+ char mech[MECHANISM_BUFSIZE];
+ char shKey[32];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintMechanism(mech, sizeof mech, pMechanism);
+ sftk_PrintReturnedObjectHandle(shKey, sizeof shKey, "phKey", phKey, rv);
+ PR_snprintf(msg, sizeof msg,
+ "C_GenerateKey(hSession=0x%08lX, pMechanism=%s, "
+ "pTemplate=%p, ulCount=%lu, phKey=%p)=0x%08lX%s",
+ (PRUint32)hSession, mech,
+ pTemplate, (PRUint32)ulCount, phKey, (PRUint32)rv, shKey);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditGenerateKeyPair(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey, CK_RV rv)
+{
+ char msg[512];
+ char mech[MECHANISM_BUFSIZE];
+ char shPublicKey[32];
+ char shPrivateKey[32];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintMechanism(mech, sizeof mech, pMechanism);
+ sftk_PrintReturnedObjectHandle(shPublicKey, sizeof shPublicKey,
+ "phPublicKey", phPublicKey, rv);
+ sftk_PrintReturnedObjectHandle(shPrivateKey, sizeof shPrivateKey,
+ "phPrivateKey", phPrivateKey, rv);
+ PR_snprintf(msg, sizeof msg,
+ "C_GenerateKeyPair(hSession=0x%08lX, pMechanism=%s, "
+ "pPublicKeyTemplate=%p, ulPublicKeyAttributeCount=%lu, "
+ "pPrivateKeyTemplate=%p, ulPrivateKeyAttributeCount=%lu, "
+ "phPublicKey=%p, phPrivateKey=%p)=0x%08lX%s%s",
+ (PRUint32)hSession, mech,
+ pPublicKeyTemplate, (PRUint32)ulPublicKeyAttributeCount,
+ pPrivateKeyTemplate, (PRUint32)ulPrivateKeyAttributeCount,
+ phPublicKey, phPrivateKey, (PRUint32)rv, shPublicKey, shPrivateKey);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditWrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey,
+ CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen, CK_RV rv)
+{
+ char msg[256];
+ char mech[MECHANISM_BUFSIZE];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintMechanism(mech, sizeof mech, pMechanism);
+ PR_snprintf(msg, sizeof msg,
+ "C_WrapKey(hSession=0x%08lX, pMechanism=%s, hWrappingKey=0x%08lX, "
+ "hKey=0x%08lX, pWrappedKey=%p, pulWrappedKeyLen=%p)=0x%08lX",
+ (PRUint32)hSession, mech, (PRUint32)hWrappingKey,
+ (PRUint32)hKey, pWrappedKey, pulWrappedKeyLen, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditUnwrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey, CK_RV rv)
+{
+ char msg[256];
+ char mech[MECHANISM_BUFSIZE];
+ char shKey[32];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintMechanism(mech, sizeof mech, pMechanism);
+ sftk_PrintReturnedObjectHandle(shKey, sizeof shKey, "phKey", phKey, rv);
+ PR_snprintf(msg, sizeof msg,
+ "C_UnwrapKey(hSession=0x%08lX, pMechanism=%s, "
+ "hUnwrappingKey=0x%08lX, pWrappedKey=%p, ulWrappedKeyLen=%lu, "
+ "pTemplate=%p, ulAttributeCount=%lu, phKey=%p)=0x%08lX%s",
+ (PRUint32)hSession, mech,
+ (PRUint32)hUnwrappingKey, pWrappedKey, (PRUint32)ulWrappedKeyLen,
+ pTemplate, (PRUint32)ulAttributeCount, phKey, (PRUint32)rv, shKey);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditDeriveKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey, CK_RV rv)
+{
+ char msg[512];
+ char mech[MECHANISM_BUFSIZE];
+ char shKey[32];
+ char sTlsKeys[128];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ sftk_PrintMechanism(mech, sizeof mech, pMechanism);
+ sftk_PrintReturnedObjectHandle(shKey, sizeof shKey, "phKey", phKey, rv);
+ if ((rv == CKR_OK) &&
+ (pMechanism->mechanism == CKM_TLS_KEY_AND_MAC_DERIVE)) {
+ CK_SSL3_KEY_MAT_PARAMS *param =
+ (CK_SSL3_KEY_MAT_PARAMS *)pMechanism->pParameter;
+ CK_SSL3_KEY_MAT_OUT *keymat = param->pReturnedKeyMaterial;
+ PR_snprintf(sTlsKeys, sizeof sTlsKeys,
+ " hClientMacSecret=0x%08lX hServerMacSecret=0x%08lX"
+ " hClientKey=0x%08lX hServerKey=0x%08lX",
+ (PRUint32)keymat->hClientMacSecret,
+ (PRUint32)keymat->hServerMacSecret,
+ (PRUint32)keymat->hClientKey,
+ (PRUint32)keymat->hServerKey);
+ } else {
+ sTlsKeys[0] = '\0';
+ }
+ PR_snprintf(msg, sizeof msg,
+ "C_DeriveKey(hSession=0x%08lX, pMechanism=%s, "
+ "hBaseKey=0x%08lX, pTemplate=%p, ulAttributeCount=%lu, "
+ "phKey=%p)=0x%08lX%s%s",
+ (PRUint32)hSession, mech,
+ (PRUint32)hBaseKey, pTemplate,(PRUint32)ulAttributeCount,
+ phKey, (PRUint32)rv, shKey, sTlsKeys);
+ sftk_LogAuditMessage(severity, msg);
+}
+
+void sftk_AuditDigestKey(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hKey, CK_RV rv)
+{
+ char msg[256];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+
+ PR_snprintf(msg, sizeof msg,
+ "C_DigestKey(hSession=0x%08lX, hKey=0x%08lX)=0x%08lX",
+ (PRUint32)hSession, (PRUint32)hKey, (PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+}
diff --git a/security/nss/lib/softoken/fipstest.c b/security/nss/lib/softoken/fipstest.c
new file mode 100644
index 000000000..3a9276ead
--- /dev/null
+++ b/security/nss/lib/softoken/fipstest.c
@@ -0,0 +1,1992 @@
+/*
+ * PKCS #11 FIPS Power-Up Self Test.
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#include "softoken.h" /* Required for RC2-ECB, RC2-CBC, RC4, DES-ECB, */
+ /* DES-CBC, DES3-ECB, DES3-CBC, RSA */
+ /* and DSA. */
+#include "seccomon.h" /* Required for RSA and DSA. */
+#include "lowkeyi.h" /* Required for RSA and DSA. */
+#include "pkcs11.h" /* Required for PKCS #11. */
+#include "secerr.h"
+
+#ifdef NSS_ENABLE_ECC
+#include "ec.h" /* Required for ECDSA */
+#endif
+
+
+/* FIPS preprocessor directives for RC2-ECB and RC2-CBC. */
+#define FIPS_RC2_KEY_LENGTH 5 /* 40-bits */
+#define FIPS_RC2_ENCRYPT_LENGTH 8 /* 64-bits */
+#define FIPS_RC2_DECRYPT_LENGTH 8 /* 64-bits */
+
+
+/* FIPS preprocessor directives for RC4. */
+#define FIPS_RC4_KEY_LENGTH 5 /* 40-bits */
+#define FIPS_RC4_ENCRYPT_LENGTH 8 /* 64-bits */
+#define FIPS_RC4_DECRYPT_LENGTH 8 /* 64-bits */
+
+
+/* FIPS preprocessor directives for DES-ECB and DES-CBC. */
+#define FIPS_DES_ENCRYPT_LENGTH 8 /* 64-bits */
+#define FIPS_DES_DECRYPT_LENGTH 8 /* 64-bits */
+
+
+/* FIPS preprocessor directives for DES3-CBC and DES3-ECB. */
+#define FIPS_DES3_ENCRYPT_LENGTH 8 /* 64-bits */
+#define FIPS_DES3_DECRYPT_LENGTH 8 /* 64-bits */
+
+
+/* FIPS preprocessor directives for AES-ECB and AES-CBC. */
+#define FIPS_AES_BLOCK_SIZE 16 /* 128-bits */
+#define FIPS_AES_ENCRYPT_LENGTH 16 /* 128-bits */
+#define FIPS_AES_DECRYPT_LENGTH 16 /* 128-bits */
+#define FIPS_AES_128_KEY_SIZE 16 /* 128-bits */
+#define FIPS_AES_192_KEY_SIZE 24 /* 192-bits */
+#define FIPS_AES_256_KEY_SIZE 32 /* 256-bits */
+
+
+/* FIPS preprocessor directives for message digests */
+#define FIPS_KNOWN_HASH_MESSAGE_LENGTH 64 /* 512-bits */
+
+
+/* FIPS preprocessor directives for RSA. */
+#define FIPS_RSA_TYPE siBuffer
+#define FIPS_RSA_PUBLIC_EXPONENT_LENGTH 3 /* 24-bits */
+#define FIPS_RSA_PRIVATE_VERSION_LENGTH 1 /* 8-bits */
+#define FIPS_RSA_MESSAGE_LENGTH 128 /* 1024-bits */
+#define FIPS_RSA_COEFFICIENT_LENGTH 64 /* 512-bits */
+#define FIPS_RSA_PRIME0_LENGTH 64 /* 512-bits */
+#define FIPS_RSA_PRIME1_LENGTH 64 /* 512-bits */
+#define FIPS_RSA_EXPONENT0_LENGTH 64 /* 512-bits */
+#define FIPS_RSA_EXPONENT1_LENGTH 64 /* 512-bits */
+#define FIPS_RSA_PRIVATE_EXPONENT_LENGTH 128 /* 1024-bits */
+#define FIPS_RSA_ENCRYPT_LENGTH 128 /* 1024-bits */
+#define FIPS_RSA_DECRYPT_LENGTH 128 /* 1024-bits */
+#define FIPS_RSA_SIGNATURE_LENGTH 128 /* 1024-bits */
+#define FIPS_RSA_MODULUS_LENGTH 128 /* 1024-bits */
+
+
+/* FIPS preprocessor directives for DSA. */
+#define FIPS_DSA_TYPE siBuffer
+#define FIPS_DSA_DIGEST_LENGTH 20 /* 160-bits */
+#define FIPS_DSA_SUBPRIME_LENGTH 20 /* 160-bits */
+#define FIPS_DSA_SIGNATURE_LENGTH 40 /* 320-bits */
+#define FIPS_DSA_PRIME_LENGTH 128 /* 1024-bits */
+#define FIPS_DSA_BASE_LENGTH 128 /* 1024-bits */
+
+/* FIPS preprocessor directives for RNG. */
+#define FIPS_RNG_XKEY_LENGTH 32 /* 256-bits */
+
+static CK_RV
+sftk_fips_RC2_PowerUpSelfTest( void )
+{
+ /* RC2 Known Key (40-bits). */
+ static const PRUint8 rc2_known_key[] = { "RSARC" };
+
+ /* RC2-CBC Known Initialization Vector (64-bits). */
+ static const PRUint8 rc2_cbc_known_initialization_vector[] = {"Security"};
+
+ /* RC2 Known Plaintext (64-bits). */
+ static const PRUint8 rc2_ecb_known_plaintext[] = {"Netscape"};
+ static const PRUint8 rc2_cbc_known_plaintext[] = {"Netscape"};
+
+ /* RC2 Known Ciphertext (64-bits). */
+ static const PRUint8 rc2_ecb_known_ciphertext[] = {
+ 0x1a,0x71,0x33,0x54,0x8d,0x5c,0xd2,0x30};
+ static const PRUint8 rc2_cbc_known_ciphertext[] = {
+ 0xff,0x41,0xdb,0x94,0x8a,0x4c,0x33,0xb3};
+
+ /* RC2 variables. */
+ PRUint8 rc2_computed_ciphertext[FIPS_RC2_ENCRYPT_LENGTH];
+ PRUint8 rc2_computed_plaintext[FIPS_RC2_DECRYPT_LENGTH];
+ RC2Context * rc2_context;
+ unsigned int rc2_bytes_encrypted;
+ unsigned int rc2_bytes_decrypted;
+ SECStatus rc2_status;
+
+
+ /******************************************************/
+ /* RC2-ECB Single-Round Known Answer Encryption Test: */
+ /******************************************************/
+
+ rc2_context = RC2_CreateContext( rc2_known_key, FIPS_RC2_KEY_LENGTH,
+ NULL, NSS_RC2,
+ FIPS_RC2_KEY_LENGTH );
+
+ if( rc2_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ rc2_status = RC2_Encrypt( rc2_context, rc2_computed_ciphertext,
+ &rc2_bytes_encrypted, FIPS_RC2_ENCRYPT_LENGTH,
+ rc2_ecb_known_plaintext,
+ FIPS_RC2_DECRYPT_LENGTH );
+
+ RC2_DestroyContext( rc2_context, PR_TRUE );
+
+ if( ( rc2_status != SECSuccess ) ||
+ ( rc2_bytes_encrypted != FIPS_RC2_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( rc2_computed_ciphertext, rc2_ecb_known_ciphertext,
+ FIPS_RC2_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* RC2-ECB Single-Round Known Answer Decryption Test: */
+ /******************************************************/
+
+ rc2_context = RC2_CreateContext( rc2_known_key, FIPS_RC2_KEY_LENGTH,
+ NULL, NSS_RC2,
+ FIPS_RC2_KEY_LENGTH );
+
+ if( rc2_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ rc2_status = RC2_Decrypt( rc2_context, rc2_computed_plaintext,
+ &rc2_bytes_decrypted, FIPS_RC2_DECRYPT_LENGTH,
+ rc2_ecb_known_ciphertext,
+ FIPS_RC2_ENCRYPT_LENGTH );
+
+ RC2_DestroyContext( rc2_context, PR_TRUE );
+
+ if( ( rc2_status != SECSuccess ) ||
+ ( rc2_bytes_decrypted != FIPS_RC2_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( rc2_computed_plaintext, rc2_ecb_known_plaintext,
+ FIPS_RC2_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* RC2-CBC Single-Round Known Answer Encryption Test: */
+ /******************************************************/
+
+ rc2_context = RC2_CreateContext( rc2_known_key, FIPS_RC2_KEY_LENGTH,
+ rc2_cbc_known_initialization_vector,
+ NSS_RC2_CBC, FIPS_RC2_KEY_LENGTH );
+
+ if( rc2_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ rc2_status = RC2_Encrypt( rc2_context, rc2_computed_ciphertext,
+ &rc2_bytes_encrypted, FIPS_RC2_ENCRYPT_LENGTH,
+ rc2_cbc_known_plaintext,
+ FIPS_RC2_DECRYPT_LENGTH );
+
+ RC2_DestroyContext( rc2_context, PR_TRUE );
+
+ if( ( rc2_status != SECSuccess ) ||
+ ( rc2_bytes_encrypted != FIPS_RC2_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( rc2_computed_ciphertext, rc2_cbc_known_ciphertext,
+ FIPS_RC2_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* RC2-CBC Single-Round Known Answer Decryption Test: */
+ /******************************************************/
+
+ rc2_context = RC2_CreateContext( rc2_known_key, FIPS_RC2_KEY_LENGTH,
+ rc2_cbc_known_initialization_vector,
+ NSS_RC2_CBC, FIPS_RC2_KEY_LENGTH );
+
+ if( rc2_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ rc2_status = RC2_Decrypt( rc2_context, rc2_computed_plaintext,
+ &rc2_bytes_decrypted, FIPS_RC2_DECRYPT_LENGTH,
+ rc2_cbc_known_ciphertext,
+ FIPS_RC2_ENCRYPT_LENGTH );
+
+ RC2_DestroyContext( rc2_context, PR_TRUE );
+
+ if( ( rc2_status != SECSuccess ) ||
+ ( rc2_bytes_decrypted != FIPS_RC2_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( rc2_computed_plaintext, rc2_ecb_known_plaintext,
+ FIPS_RC2_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+
+static CK_RV
+sftk_fips_RC4_PowerUpSelfTest( void )
+{
+ /* RC4 Known Key (40-bits). */
+ static const PRUint8 rc4_known_key[] = { "RSARC" };
+
+ /* RC4 Known Plaintext (64-bits). */
+ static const PRUint8 rc4_known_plaintext[] = { "Netscape" };
+
+ /* RC4 Known Ciphertext (64-bits). */
+ static const PRUint8 rc4_known_ciphertext[] = {
+ 0x29,0x33,0xc7,0x9a,0x9d,0x6c,0x09,0xdd};
+
+ /* RC4 variables. */
+ PRUint8 rc4_computed_ciphertext[FIPS_RC4_ENCRYPT_LENGTH];
+ PRUint8 rc4_computed_plaintext[FIPS_RC4_DECRYPT_LENGTH];
+ RC4Context * rc4_context;
+ unsigned int rc4_bytes_encrypted;
+ unsigned int rc4_bytes_decrypted;
+ SECStatus rc4_status;
+
+
+ /**************************************************/
+ /* RC4 Single-Round Known Answer Encryption Test: */
+ /**************************************************/
+
+ rc4_context = RC4_CreateContext( rc4_known_key, FIPS_RC4_KEY_LENGTH );
+
+ if( rc4_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ rc4_status = RC4_Encrypt( rc4_context, rc4_computed_ciphertext,
+ &rc4_bytes_encrypted, FIPS_RC4_ENCRYPT_LENGTH,
+ rc4_known_plaintext, FIPS_RC4_DECRYPT_LENGTH );
+
+ RC4_DestroyContext( rc4_context, PR_TRUE );
+
+ if( ( rc4_status != SECSuccess ) ||
+ ( rc4_bytes_encrypted != FIPS_RC4_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( rc4_computed_ciphertext, rc4_known_ciphertext,
+ FIPS_RC4_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /**************************************************/
+ /* RC4 Single-Round Known Answer Decryption Test: */
+ /**************************************************/
+
+ rc4_context = RC4_CreateContext( rc4_known_key, FIPS_RC4_KEY_LENGTH );
+
+ if( rc4_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ rc4_status = RC4_Decrypt( rc4_context, rc4_computed_plaintext,
+ &rc4_bytes_decrypted, FIPS_RC4_DECRYPT_LENGTH,
+ rc4_known_ciphertext, FIPS_RC4_ENCRYPT_LENGTH );
+
+ RC4_DestroyContext( rc4_context, PR_TRUE );
+
+ if( ( rc4_status != SECSuccess ) ||
+ ( rc4_bytes_decrypted != FIPS_RC4_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( rc4_computed_plaintext, rc4_known_plaintext,
+ FIPS_RC4_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+
+static CK_RV
+sftk_fips_DES_PowerUpSelfTest( void )
+{
+ /* DES Known Key (56-bits). */
+ static const PRUint8 des_known_key[] = { "ANSI DES" };
+
+ /* DES-CBC Known Initialization Vector (64-bits). */
+ static const PRUint8 des_cbc_known_initialization_vector[] = { "Security" };
+
+ /* DES Known Plaintext (64-bits). */
+ static const PRUint8 des_ecb_known_plaintext[] = { "Netscape" };
+ static const PRUint8 des_cbc_known_plaintext[] = { "Netscape" };
+
+ /* DES Known Ciphertext (64-bits). */
+ static const PRUint8 des_ecb_known_ciphertext[] = {
+ 0x26,0x14,0xe9,0xc3,0x28,0x80,0x50,0xb0};
+ static const PRUint8 des_cbc_known_ciphertext[] = {
+ 0x5e,0x95,0x94,0x5d,0x76,0xa2,0xd3,0x7d};
+
+ /* DES variables. */
+ PRUint8 des_computed_ciphertext[FIPS_DES_ENCRYPT_LENGTH];
+ PRUint8 des_computed_plaintext[FIPS_DES_DECRYPT_LENGTH];
+ DESContext * des_context;
+ unsigned int des_bytes_encrypted;
+ unsigned int des_bytes_decrypted;
+ SECStatus des_status;
+
+
+ /******************************************************/
+ /* DES-ECB Single-Round Known Answer Encryption Test: */
+ /******************************************************/
+
+ des_context = DES_CreateContext( des_known_key, NULL, NSS_DES, PR_TRUE );
+
+ if( des_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des_status = DES_Encrypt( des_context, des_computed_ciphertext,
+ &des_bytes_encrypted, FIPS_DES_ENCRYPT_LENGTH,
+ des_ecb_known_plaintext,
+ FIPS_DES_DECRYPT_LENGTH );
+
+ DES_DestroyContext( des_context, PR_TRUE );
+
+ if( ( des_status != SECSuccess ) ||
+ ( des_bytes_encrypted != FIPS_DES_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des_computed_ciphertext, des_ecb_known_ciphertext,
+ FIPS_DES_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* DES-ECB Single-Round Known Answer Decryption Test: */
+ /******************************************************/
+
+ des_context = DES_CreateContext( des_known_key, NULL, NSS_DES, PR_FALSE );
+
+ if( des_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des_status = DES_Decrypt( des_context, des_computed_plaintext,
+ &des_bytes_decrypted, FIPS_DES_DECRYPT_LENGTH,
+ des_ecb_known_ciphertext,
+ FIPS_DES_ENCRYPT_LENGTH );
+
+ DES_DestroyContext( des_context, PR_TRUE );
+
+ if( ( des_status != SECSuccess ) ||
+ ( des_bytes_decrypted != FIPS_DES_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des_computed_plaintext, des_ecb_known_plaintext,
+ FIPS_DES_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* DES-CBC Single-Round Known Answer Encryption Test. */
+ /******************************************************/
+
+ des_context = DES_CreateContext( des_known_key,
+ des_cbc_known_initialization_vector,
+ NSS_DES_CBC, PR_TRUE );
+
+ if( des_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des_status = DES_Encrypt( des_context, des_computed_ciphertext,
+ &des_bytes_encrypted, FIPS_DES_ENCRYPT_LENGTH,
+ des_cbc_known_plaintext,
+ FIPS_DES_DECRYPT_LENGTH );
+
+ DES_DestroyContext( des_context, PR_TRUE );
+
+ if( ( des_status != SECSuccess ) ||
+ ( des_bytes_encrypted != FIPS_DES_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des_computed_ciphertext, des_cbc_known_ciphertext,
+ FIPS_DES_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* DES-CBC Single-Round Known Answer Decryption Test. */
+ /******************************************************/
+
+ des_context = DES_CreateContext( des_known_key,
+ des_cbc_known_initialization_vector,
+ NSS_DES_CBC, PR_FALSE );
+
+ if( des_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des_status = DES_Decrypt( des_context, des_computed_plaintext,
+ &des_bytes_decrypted, FIPS_DES_DECRYPT_LENGTH,
+ des_cbc_known_ciphertext,
+ FIPS_DES_ENCRYPT_LENGTH );
+
+ DES_DestroyContext( des_context, PR_TRUE );
+
+ if( ( des_status != SECSuccess ) ||
+ ( des_bytes_decrypted != FIPS_DES_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des_computed_plaintext, des_cbc_known_plaintext,
+ FIPS_DES_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+
+static CK_RV
+sftk_fips_DES3_PowerUpSelfTest( void )
+{
+ /* DES3 Known Key (56-bits). */
+ static const PRUint8 des3_known_key[] = { "ANSI Triple-DES Key Data" };
+
+ /* DES3-CBC Known Initialization Vector (64-bits). */
+ static const PRUint8 des3_cbc_known_initialization_vector[] = { "Security" };
+
+ /* DES3 Known Plaintext (64-bits). */
+ static const PRUint8 des3_ecb_known_plaintext[] = { "Netscape" };
+ static const PRUint8 des3_cbc_known_plaintext[] = { "Netscape" };
+
+ /* DES3 Known Ciphertext (64-bits). */
+ static const PRUint8 des3_ecb_known_ciphertext[] = {
+ 0x55,0x8e,0xad,0x3c,0xee,0x49,0x69,0xbe};
+ static const PRUint8 des3_cbc_known_ciphertext[] = {
+ 0x43,0xdc,0x6a,0xc1,0xaf,0xa6,0x32,0xf5};
+
+ /* DES3 variables. */
+ PRUint8 des3_computed_ciphertext[FIPS_DES3_ENCRYPT_LENGTH];
+ PRUint8 des3_computed_plaintext[FIPS_DES3_DECRYPT_LENGTH];
+ DESContext * des3_context;
+ unsigned int des3_bytes_encrypted;
+ unsigned int des3_bytes_decrypted;
+ SECStatus des3_status;
+
+
+ /*******************************************************/
+ /* DES3-ECB Single-Round Known Answer Encryption Test. */
+ /*******************************************************/
+
+ des3_context = DES_CreateContext( des3_known_key, NULL,
+ NSS_DES_EDE3, PR_TRUE );
+
+ if( des3_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des3_status = DES_Encrypt( des3_context, des3_computed_ciphertext,
+ &des3_bytes_encrypted, FIPS_DES3_ENCRYPT_LENGTH,
+ des3_ecb_known_plaintext,
+ FIPS_DES3_DECRYPT_LENGTH );
+
+ DES_DestroyContext( des3_context, PR_TRUE );
+
+ if( ( des3_status != SECSuccess ) ||
+ ( des3_bytes_encrypted != FIPS_DES3_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des3_computed_ciphertext, des3_ecb_known_ciphertext,
+ FIPS_DES3_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /*******************************************************/
+ /* DES3-ECB Single-Round Known Answer Decryption Test. */
+ /*******************************************************/
+
+ des3_context = DES_CreateContext( des3_known_key, NULL,
+ NSS_DES_EDE3, PR_FALSE );
+
+ if( des3_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des3_status = DES_Decrypt( des3_context, des3_computed_plaintext,
+ &des3_bytes_decrypted, FIPS_DES3_DECRYPT_LENGTH,
+ des3_ecb_known_ciphertext,
+ FIPS_DES3_ENCRYPT_LENGTH );
+
+ DES_DestroyContext( des3_context, PR_TRUE );
+
+ if( ( des3_status != SECSuccess ) ||
+ ( des3_bytes_decrypted != FIPS_DES3_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des3_computed_plaintext, des3_ecb_known_plaintext,
+ FIPS_DES3_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /*******************************************************/
+ /* DES3-CBC Single-Round Known Answer Encryption Test. */
+ /*******************************************************/
+
+ des3_context = DES_CreateContext( des3_known_key,
+ des3_cbc_known_initialization_vector,
+ NSS_DES_EDE3_CBC, PR_TRUE );
+
+ if( des3_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des3_status = DES_Encrypt( des3_context, des3_computed_ciphertext,
+ &des3_bytes_encrypted, FIPS_DES3_ENCRYPT_LENGTH,
+ des3_cbc_known_plaintext,
+ FIPS_DES3_DECRYPT_LENGTH );
+
+ DES_DestroyContext( des3_context, PR_TRUE );
+
+ if( ( des3_status != SECSuccess ) ||
+ ( des3_bytes_encrypted != FIPS_DES3_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des3_computed_ciphertext, des3_cbc_known_ciphertext,
+ FIPS_DES3_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /*******************************************************/
+ /* DES3-CBC Single-Round Known Answer Decryption Test. */
+ /*******************************************************/
+
+ des3_context = DES_CreateContext( des3_known_key,
+ des3_cbc_known_initialization_vector,
+ NSS_DES_EDE3_CBC, PR_FALSE );
+
+ if( des3_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ des3_status = DES_Decrypt( des3_context, des3_computed_plaintext,
+ &des3_bytes_decrypted, FIPS_DES3_DECRYPT_LENGTH,
+ des3_cbc_known_ciphertext,
+ FIPS_DES3_ENCRYPT_LENGTH );
+
+ DES_DestroyContext( des3_context, PR_TRUE );
+
+ if( ( des3_status != SECSuccess ) ||
+ ( des3_bytes_decrypted != FIPS_DES3_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( des3_computed_plaintext, des3_cbc_known_plaintext,
+ FIPS_DES3_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+
+/* AES self-test for 128-bit, 192-bit, or 256-bit key sizes*/
+static CK_RV
+sftk_fips_AES_PowerUpSelfTest( int aes_key_size )
+{
+ /* AES Known Key (up to 256-bits). */
+ static const PRUint8 aes_known_key[] =
+ { "AES-128 RIJNDAELLEADNJIR 821-SEA" };
+
+ /* AES-CBC Known Initialization Vector (128-bits). */
+ static const PRUint8 aes_cbc_known_initialization_vector[] =
+ { "SecurityytiruceS" };
+
+ /* AES Known Plaintext (128-bits). (blocksize is 128-bits) */
+ static const PRUint8 aes_known_plaintext[] = { "NetscapeepacsteN" };
+
+ /* AES Known Ciphertext (128-bit key). */
+ static const PRUint8 aes_ecb128_known_ciphertext[] = {
+ 0x3c,0xa5,0x96,0xf3,0x34,0x6a,0x96,0xc1,
+ 0x03,0x88,0x16,0x7b,0x20,0xbf,0x35,0x47 };
+
+ static const PRUint8 aes_cbc128_known_ciphertext[] = {
+ 0xcf,0x15,0x1d,0x4f,0x96,0xe4,0x4f,0x63,
+ 0x15,0x54,0x14,0x1d,0x4e,0xd8,0xd5,0xea };
+
+ /* AES Known Ciphertext (192-bit key). */
+ static const PRUint8 aes_ecb192_known_ciphertext[] = {
+ 0xa0,0x18,0x62,0xed,0x88,0x19,0xcb,0x62,
+ 0x88,0x1d,0x4d,0xfe,0x84,0x02,0x89,0x0e };
+
+ static const PRUint8 aes_cbc192_known_ciphertext[] = {
+ 0x83,0xf7,0xa4,0x76,0xd1,0x6f,0x07,0xbe,
+ 0x07,0xbc,0x43,0x2f,0x6d,0xad,0x29,0xe1 };
+
+ /* AES Known Ciphertext (256-bit key). */
+ static const PRUint8 aes_ecb256_known_ciphertext[] = {
+ 0xdb,0xa6,0x52,0x01,0x8a,0x70,0xae,0x66,
+ 0x3a,0x99,0xd8,0x95,0x7f,0xfb,0x01,0x67 };
+
+ static const PRUint8 aes_cbc256_known_ciphertext[] = {
+ 0x37,0xea,0x07,0x06,0x31,0x1c,0x59,0x27,
+ 0xc5,0xc5,0x68,0x71,0x6e,0x34,0x40,0x16 };
+
+ const PRUint8 *aes_ecb_known_ciphertext =
+ ( aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_ecb128_known_ciphertext :
+ ( aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_ecb192_known_ciphertext :
+ aes_ecb256_known_ciphertext;
+
+ const PRUint8 *aes_cbc_known_ciphertext =
+ ( aes_key_size == FIPS_AES_128_KEY_SIZE) ? aes_cbc128_known_ciphertext :
+ ( aes_key_size == FIPS_AES_192_KEY_SIZE) ? aes_cbc192_known_ciphertext :
+ aes_cbc256_known_ciphertext;
+
+ /* AES variables. */
+ PRUint8 aes_computed_ciphertext[FIPS_AES_ENCRYPT_LENGTH];
+ PRUint8 aes_computed_plaintext[FIPS_AES_DECRYPT_LENGTH];
+ AESContext * aes_context;
+ unsigned int aes_bytes_encrypted;
+ unsigned int aes_bytes_decrypted;
+ SECStatus aes_status;
+
+ /*check if aes_key_size is 128, 192, or 256 bits */
+ if ((aes_key_size != FIPS_AES_128_KEY_SIZE) &&
+ (aes_key_size != FIPS_AES_192_KEY_SIZE) &&
+ (aes_key_size != FIPS_AES_256_KEY_SIZE))
+ return( CKR_DEVICE_ERROR );
+
+ /******************************************************/
+ /* AES-ECB Single-Round Known Answer Encryption Test: */
+ /******************************************************/
+
+ aes_context = AES_CreateContext( aes_known_key, NULL, NSS_AES, PR_TRUE,
+ aes_key_size, FIPS_AES_BLOCK_SIZE );
+
+ if( aes_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ aes_status = AES_Encrypt( aes_context, aes_computed_ciphertext,
+ &aes_bytes_encrypted, FIPS_AES_ENCRYPT_LENGTH,
+ aes_known_plaintext,
+ FIPS_AES_DECRYPT_LENGTH );
+
+ AES_DestroyContext( aes_context, PR_TRUE );
+
+ if( ( aes_status != SECSuccess ) ||
+ ( aes_bytes_encrypted != FIPS_AES_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( aes_computed_ciphertext, aes_ecb_known_ciphertext,
+ FIPS_AES_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* AES-ECB Single-Round Known Answer Decryption Test: */
+ /******************************************************/
+
+ aes_context = AES_CreateContext( aes_known_key, NULL, NSS_AES, PR_FALSE,
+ aes_key_size, FIPS_AES_BLOCK_SIZE );
+
+ if( aes_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ aes_status = AES_Decrypt( aes_context, aes_computed_plaintext,
+ &aes_bytes_decrypted, FIPS_AES_DECRYPT_LENGTH,
+ aes_ecb_known_ciphertext,
+ FIPS_AES_ENCRYPT_LENGTH );
+
+ AES_DestroyContext( aes_context, PR_TRUE );
+
+ if( ( aes_status != SECSuccess ) ||
+ ( aes_bytes_decrypted != FIPS_AES_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( aes_computed_plaintext, aes_known_plaintext,
+ FIPS_AES_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* AES-CBC Single-Round Known Answer Encryption Test. */
+ /******************************************************/
+
+ aes_context = AES_CreateContext( aes_known_key,
+ aes_cbc_known_initialization_vector,
+ NSS_AES_CBC, PR_TRUE, aes_key_size,
+ FIPS_AES_BLOCK_SIZE );
+
+ if( aes_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ aes_status = AES_Encrypt( aes_context, aes_computed_ciphertext,
+ &aes_bytes_encrypted, FIPS_AES_ENCRYPT_LENGTH,
+ aes_known_plaintext,
+ FIPS_AES_DECRYPT_LENGTH );
+
+ AES_DestroyContext( aes_context, PR_TRUE );
+
+ if( ( aes_status != SECSuccess ) ||
+ ( aes_bytes_encrypted != FIPS_AES_ENCRYPT_LENGTH ) ||
+ ( PORT_Memcmp( aes_computed_ciphertext, aes_cbc_known_ciphertext,
+ FIPS_AES_ENCRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+
+ /******************************************************/
+ /* AES-CBC Single-Round Known Answer Decryption Test. */
+ /******************************************************/
+
+ aes_context = AES_CreateContext( aes_known_key,
+ aes_cbc_known_initialization_vector,
+ NSS_AES_CBC, PR_FALSE, aes_key_size,
+ FIPS_AES_BLOCK_SIZE );
+
+ if( aes_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ aes_status = AES_Decrypt( aes_context, aes_computed_plaintext,
+ &aes_bytes_decrypted, FIPS_AES_DECRYPT_LENGTH,
+ aes_cbc_known_ciphertext,
+ FIPS_AES_ENCRYPT_LENGTH );
+
+ AES_DestroyContext( aes_context, PR_TRUE );
+
+ if( ( aes_status != SECSuccess ) ||
+ ( aes_bytes_decrypted != FIPS_AES_DECRYPT_LENGTH ) ||
+ ( PORT_Memcmp( aes_computed_plaintext, aes_known_plaintext,
+ FIPS_AES_DECRYPT_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+/* Known Hash Message (512-bits). Used for all hashes (incl. SHA-N [N>1]). */
+static const PRUint8 known_hash_message[] = {
+ "The test message for the MD2, MD5, and SHA-1 hashing algorithms." };
+
+
+static CK_RV
+sftk_fips_MD2_PowerUpSelfTest( void )
+{
+ /* MD2 Known Digest Message (128-bits). */
+ static const PRUint8 md2_known_digest[] = {
+ 0x41,0x5a,0x12,0xb2,0x3f,0x28,0x97,0x17,
+ 0x0c,0x71,0x4e,0xcc,0x40,0xc8,0x1d,0x1b};
+
+ /* MD2 variables. */
+ MD2Context * md2_context;
+ unsigned int md2_bytes_hashed;
+ PRUint8 md2_computed_digest[MD2_LENGTH];
+
+
+ /***********************************************/
+ /* MD2 Single-Round Known Answer Hashing Test. */
+ /***********************************************/
+
+ md2_context = MD2_NewContext();
+
+ if( md2_context == NULL )
+ return( CKR_HOST_MEMORY );
+
+ MD2_Begin( md2_context );
+
+ MD2_Update( md2_context, known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH );
+
+ MD2_End( md2_context, md2_computed_digest, &md2_bytes_hashed, MD2_LENGTH );
+
+ MD2_DestroyContext( md2_context , PR_TRUE );
+
+ if( ( md2_bytes_hashed != MD2_LENGTH ) ||
+ ( PORT_Memcmp( md2_computed_digest, md2_known_digest,
+ MD2_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+
+static CK_RV
+sftk_fips_MD5_PowerUpSelfTest( void )
+{
+ /* MD5 Known Digest Message (128-bits). */
+ static const PRUint8 md5_known_digest[] = {
+ 0x25,0xc8,0xc0,0x10,0xc5,0x6e,0x68,0x28,
+ 0x28,0xa4,0xa5,0xd2,0x98,0x9a,0xea,0x2d};
+
+ /* MD5 variables. */
+ PRUint8 md5_computed_digest[MD5_LENGTH];
+ SECStatus md5_status;
+
+
+ /***********************************************/
+ /* MD5 Single-Round Known Answer Hashing Test. */
+ /***********************************************/
+
+ md5_status = MD5_HashBuf( md5_computed_digest, known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH );
+
+ if( ( md5_status != SECSuccess ) ||
+ ( PORT_Memcmp( md5_computed_digest, md5_known_digest,
+ MD5_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+/****************************************************/
+/* Single Round HMAC SHA-X test */
+/****************************************************/
+static SECStatus
+sftk_fips_HMAC(unsigned char *hmac_computed,
+ const PRUint8 *secret_key,
+ unsigned int secret_key_length,
+ const PRUint8 *message,
+ unsigned int message_length,
+ HASH_HashType hashAlg )
+{
+ SECStatus hmac_status = SECFailure;
+ HMACContext *cx = NULL;
+ SECHashObject *hashObj = NULL;
+ unsigned int bytes_hashed = 0;
+
+ hashObj = (SECHashObject *) HASH_GetRawHashObject(hashAlg);
+
+ if (!hashObj)
+ return( SECFailure );
+
+ cx = HMAC_Create(hashObj, secret_key,
+ secret_key_length,
+ PR_TRUE); /* PR_TRUE for in FIPS mode */
+
+ if (cx == NULL)
+ return( SECFailure );
+
+ HMAC_Begin(cx);
+ HMAC_Update(cx, message, message_length);
+ hmac_status = HMAC_Finish(cx, hmac_computed, &bytes_hashed,
+ hashObj->length);
+
+ HMAC_Destroy(cx, PR_TRUE);
+
+ return( hmac_status );
+}
+
+static CK_RV
+sftk_fips_HMAC_PowerUpSelfTest( void )
+{
+ static const PRUint8 HMAC_known_secret_key[] = {
+ "Firefox and ThunderBird are awesome!"};
+
+ static const PRUint8 HMAC_known_secret_key_length
+ = sizeof HMAC_known_secret_key;
+
+ /* known SHA1 hmac (20 bytes) */
+ static const PRUint8 known_SHA1_hmac[] = {
+ 0xd5, 0x85, 0xf6, 0x5b, 0x39, 0xfa, 0xb9, 0x05,
+ 0x3b, 0x57, 0x1d, 0x61, 0xe7, 0xb8, 0x84, 0x1e,
+ 0x5d, 0x0e, 0x1e, 0x11};
+
+ /* known SHA256 hmac (32 bytes) */
+ static const PRUint8 known_SHA256_hmac[] = {
+ 0x05, 0x75, 0x9a, 0x9e, 0x70, 0x5e, 0xe7, 0x44,
+ 0xe2, 0x46, 0x4b, 0x92, 0x22, 0x14, 0x22, 0xe0,
+ 0x1b, 0x92, 0x8a, 0x0c, 0xfe, 0xf5, 0x49, 0xe9,
+ 0xa7, 0x1b, 0x56, 0x7d, 0x1d, 0x29, 0x40, 0x48};
+
+ /* known SHA384 hmac (48 bytes) */
+ static const PRUint8 known_SHA384_hmac[] = {
+ 0xcd, 0x56, 0x14, 0xec, 0x05, 0x53, 0x06, 0x2b,
+ 0x7e, 0x9c, 0x8a, 0x18, 0x5e, 0xea, 0xf3, 0x91,
+ 0x33, 0xfb, 0x64, 0xf6, 0xe3, 0x9f, 0x89, 0x0b,
+ 0xaf, 0xbe, 0x83, 0x4d, 0x3f, 0x3c, 0x43, 0x4d,
+ 0x4a, 0x0c, 0x56, 0x98, 0xf8, 0xca, 0xb4, 0xaa,
+ 0x9a, 0xf4, 0x0a, 0xaf, 0x4f, 0x69, 0xca, 0x87};
+
+ /* known SHA512 hmac (64 bytes) */
+ static const PRUint8 known_SHA512_hmac[] = {
+ 0xf6, 0x0e, 0x97, 0x12, 0x00, 0x67, 0x6e, 0xb9,
+ 0x0c, 0xb2, 0x63, 0xf0, 0x60, 0xac, 0x75, 0x62,
+ 0x70, 0x95, 0x2a, 0x52, 0x22, 0xee, 0xdd, 0xd2,
+ 0x71, 0xb1, 0xe8, 0x26, 0x33, 0xd3, 0x13, 0x27,
+ 0xcb, 0xff, 0x44, 0xef, 0x87, 0x97, 0x16, 0xfb,
+ 0xd3, 0x0b, 0x48, 0xbe, 0x12, 0x4e, 0xda, 0xb1,
+ 0x89, 0x90, 0xfb, 0x06, 0x0c, 0xbe, 0xe5, 0xc4,
+ 0xff, 0x24, 0x37, 0x3d, 0xc7, 0xe4, 0xe4, 0x37};
+
+ SECStatus hmac_status;
+ PRUint8 hmac_computed[HASH_LENGTH_MAX];
+
+ /***************************************************/
+ /* HMAC SHA-1 Single-Round Known Answer HMAC Test. */
+ /***************************************************/
+
+ hmac_status = sftk_fips_HMAC(hmac_computed,
+ HMAC_known_secret_key,
+ HMAC_known_secret_key_length,
+ known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH,
+ HASH_AlgSHA1);
+
+ if( ( hmac_status != SECSuccess ) ||
+ ( PORT_Memcmp( hmac_computed, known_SHA1_hmac,
+ SHA1_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /***************************************************/
+ /* HMAC SHA-256 Single-Round Known Answer Test. */
+ /***************************************************/
+
+ hmac_status = sftk_fips_HMAC(hmac_computed,
+ HMAC_known_secret_key,
+ HMAC_known_secret_key_length,
+ known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH,
+ HASH_AlgSHA256);
+
+ if( ( hmac_status != SECSuccess ) ||
+ ( PORT_Memcmp( hmac_computed, known_SHA256_hmac,
+ SHA256_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /***************************************************/
+ /* HMAC SHA-384 Single-Round Known Answer Test. */
+ /***************************************************/
+
+ hmac_status = sftk_fips_HMAC(hmac_computed,
+ HMAC_known_secret_key,
+ HMAC_known_secret_key_length,
+ known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH,
+ HASH_AlgSHA384);
+
+ if( ( hmac_status != SECSuccess ) ||
+ ( PORT_Memcmp( hmac_computed, known_SHA384_hmac,
+ SHA384_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /***************************************************/
+ /* HMAC SHA-512 Single-Round Known Answer Test. */
+ /***************************************************/
+
+ hmac_status = sftk_fips_HMAC(hmac_computed,
+ HMAC_known_secret_key,
+ HMAC_known_secret_key_length,
+ known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH,
+ HASH_AlgSHA512);
+
+ if( ( hmac_status != SECSuccess ) ||
+ ( PORT_Memcmp( hmac_computed, known_SHA512_hmac,
+ SHA512_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+static CK_RV
+sftk_fips_SHA_PowerUpSelfTest( void )
+{
+ /* SHA-1 Known Digest Message (160-bits). */
+ static const PRUint8 sha1_known_digest[] = {
+ 0x0a,0x6d,0x07,0xba,0x1e,0xbd,0x8a,0x1b,
+ 0x72,0xf6,0xc7,0x22,0xf1,0x27,0x9f,0xf0,
+ 0xe0,0x68,0x47,0x7a};
+
+ /* SHA-256 Known Digest Message (256-bits). */
+ static const PRUint8 sha256_known_digest[] = {
+ 0x38,0xa9,0xc1,0xf0,0x35,0xf6,0x5d,0x61,
+ 0x11,0xd4,0x0b,0xdc,0xce,0x35,0x14,0x8d,
+ 0xf2,0xdd,0xaf,0xaf,0xcf,0xb7,0x87,0xe9,
+ 0x96,0xa5,0xd2,0x83,0x62,0x46,0x56,0x79};
+
+ /* SHA-384 Known Digest Message (384-bits). */
+ static const PRUint8 sha384_known_digest[] = {
+ 0x11,0xfe,0x1c,0x00,0x89,0x48,0xde,0xb3,
+ 0x99,0xee,0x1c,0x18,0xb4,0x10,0xfb,0xfe,
+ 0xe3,0xa8,0x2c,0xf3,0x04,0xb0,0x2f,0xc8,
+ 0xa3,0xc4,0x5e,0xea,0x7e,0x60,0x48,0x7b,
+ 0xce,0x2c,0x62,0xf7,0xbc,0xa7,0xe8,0xa3,
+ 0xcf,0x24,0xce,0x9c,0xe2,0x8b,0x09,0x72};
+
+ /* SHA-512 Known Digest Message (512-bits). */
+ static const PRUint8 sha512_known_digest[] = {
+ 0xc8,0xb3,0x27,0xf9,0x0b,0x24,0xc8,0xbf,
+ 0x4c,0xba,0x33,0x54,0xf2,0x31,0xbf,0xdb,
+ 0xab,0xfd,0xb3,0x15,0xd7,0xfa,0x48,0x99,
+ 0x07,0x60,0x0f,0x57,0x41,0x1a,0xdd,0x28,
+ 0x12,0x55,0x25,0xac,0xba,0x3a,0x99,0x12,
+ 0x2c,0x7a,0x8f,0x75,0x3a,0xe1,0x06,0x6f,
+ 0x30,0x31,0xc9,0x33,0xc6,0x1b,0x90,0x1a,
+ 0x6c,0x98,0x9a,0x87,0xd0,0xb2,0xf8,0x07};
+
+ /* SHA-X variables. */
+ PRUint8 sha_computed_digest[HASH_LENGTH_MAX];
+ SECStatus sha_status;
+
+ /*************************************************/
+ /* SHA-1 Single-Round Known Answer Hashing Test. */
+ /*************************************************/
+
+ sha_status = SHA1_HashBuf( sha_computed_digest, known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH );
+
+ if( ( sha_status != SECSuccess ) ||
+ ( PORT_Memcmp( sha_computed_digest, sha1_known_digest,
+ SHA1_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /***************************************************/
+ /* SHA-256 Single-Round Known Answer Hashing Test. */
+ /***************************************************/
+
+ sha_status = SHA256_HashBuf( sha_computed_digest, known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH );
+
+ if( ( sha_status != SECSuccess ) ||
+ ( PORT_Memcmp( sha_computed_digest, sha256_known_digest,
+ SHA256_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /***************************************************/
+ /* SHA-384 Single-Round Known Answer Hashing Test. */
+ /***************************************************/
+
+ sha_status = SHA384_HashBuf( sha_computed_digest, known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH );
+
+ if( ( sha_status != SECSuccess ) ||
+ ( PORT_Memcmp( sha_computed_digest, sha384_known_digest,
+ SHA384_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /***************************************************/
+ /* SHA-512 Single-Round Known Answer Hashing Test. */
+ /***************************************************/
+
+ sha_status = SHA512_HashBuf( sha_computed_digest, known_hash_message,
+ FIPS_KNOWN_HASH_MESSAGE_LENGTH );
+
+ if( ( sha_status != SECSuccess ) ||
+ ( PORT_Memcmp( sha_computed_digest, sha512_known_digest,
+ SHA512_LENGTH ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+/*
+* Single round RSA Signature Known Answer Test
+*/
+static SECStatus
+sftk_fips_RSA_PowerUpSigSelfTest (HASH_HashType shaAlg,
+ NSSLOWKEYPublicKey *rsa_public_key,
+ NSSLOWKEYPrivateKey *rsa_private_key,
+ const unsigned char *rsa_known_msg,
+ const unsigned int rsa_kmsg_length,
+ const unsigned char *rsa_known_signature)
+{
+ SECOidTag shaOid; /* SHA OID */
+ unsigned char sha[HASH_LENGTH_MAX]; /* SHA digest */
+ unsigned int shaLength = 0; /* length of SHA */
+ unsigned int rsa_bytes_signed;
+ unsigned char rsa_computed_signature[FIPS_RSA_SIGNATURE_LENGTH];
+ SECStatus rv;
+
+ if (shaAlg == HASH_AlgSHA1) {
+ if (SHA1_HashBuf(sha, rsa_known_msg, rsa_kmsg_length)
+ != SECSuccess) {
+ goto loser;
+ }
+ shaLength = SHA1_LENGTH;
+ shaOid = SEC_OID_SHA1;
+ } else if (shaAlg == HASH_AlgSHA256) {
+ if (SHA256_HashBuf(sha, rsa_known_msg, rsa_kmsg_length)
+ != SECSuccess) {
+ goto loser;
+ }
+ shaLength = SHA256_LENGTH;
+ shaOid = SEC_OID_SHA256;
+ } else if (shaAlg == HASH_AlgSHA384) {
+ if (SHA384_HashBuf(sha, rsa_known_msg, rsa_kmsg_length)
+ != SECSuccess) {
+ goto loser;
+ }
+ shaLength = SHA384_LENGTH;
+ shaOid = SEC_OID_SHA384;
+ } else if (shaAlg == HASH_AlgSHA512) {
+ if (SHA512_HashBuf(sha, rsa_known_msg, rsa_kmsg_length)
+ != SECSuccess) {
+ goto loser;
+ }
+ shaLength = SHA512_LENGTH;
+ shaOid = SEC_OID_SHA512;
+ } else {
+ goto loser;
+ }
+
+ /*************************************************/
+ /* RSA Single-Round Known Answer Signature Test. */
+ /*************************************************/
+
+ /* Perform RSA signature with the RSA private key. */
+ rv = RSA_HashSign( shaOid,
+ rsa_private_key,
+ rsa_computed_signature,
+ &rsa_bytes_signed,
+ FIPS_RSA_SIGNATURE_LENGTH,
+ sha,
+ shaLength);
+
+ if( ( rv != SECSuccess ) ||
+ ( rsa_bytes_signed != FIPS_RSA_SIGNATURE_LENGTH ) ||
+ ( PORT_Memcmp( rsa_computed_signature, rsa_known_signature,
+ FIPS_RSA_SIGNATURE_LENGTH ) != 0 ) ) {
+ goto loser;
+ }
+
+ /****************************************************/
+ /* RSA Single-Round Known Answer Verification Test. */
+ /****************************************************/
+
+ /* Perform RSA verification with the RSA public key. */
+ rv = RSA_HashCheckSign( shaOid,
+ rsa_public_key,
+ rsa_computed_signature,
+ rsa_bytes_signed,
+ sha,
+ shaLength);
+
+ if( rv != SECSuccess ) {
+ goto loser;
+ }
+ return( SECSuccess );
+
+loser:
+
+ return( SECFailure );
+
+}
+
+static CK_RV
+sftk_fips_RSA_PowerUpSelfTest( void )
+{
+ /* RSA Known Modulus used in both Public/Private Key Values (1024-bits). */
+ static const PRUint8 rsa_modulus[FIPS_RSA_MODULUS_LENGTH] = {
+ 0xd5, 0x84, 0x95, 0x07, 0xf4, 0xd0, 0x1f, 0x82,
+ 0xf3, 0x79, 0xf4, 0x99, 0x48, 0x10, 0xe1, 0x71,
+ 0xa5, 0x62, 0x22, 0xa3, 0x4b, 0x00, 0xe3, 0x5b,
+ 0x3a, 0xcc, 0x10, 0x83, 0xe0, 0xaf, 0x61, 0x13,
+ 0x54, 0x6a, 0xa2, 0x6a, 0x2c, 0x5e, 0xb3, 0xcc,
+ 0xa3, 0x71, 0x9a, 0xb2, 0x3e, 0x78, 0xec, 0xb5,
+ 0x0e, 0x6e, 0x31, 0x3b, 0x77, 0x1f, 0x6e, 0x94,
+ 0x41, 0x60, 0xd5, 0x6e, 0xd9, 0xc6, 0xf9, 0x29,
+ 0xc3, 0x40, 0x36, 0x25, 0xdb, 0xea, 0x0b, 0x07,
+ 0xae, 0x76, 0xfd, 0x99, 0x29, 0xf4, 0x22, 0xc1,
+ 0x1a, 0x8f, 0x05, 0xfe, 0x98, 0x09, 0x07, 0x05,
+ 0xc2, 0x0f, 0x0b, 0x11, 0x83, 0x39, 0xca, 0xc7,
+ 0x43, 0x63, 0xff, 0x33, 0x80, 0xe7, 0xc3, 0x78,
+ 0xae, 0xf1, 0x73, 0x52, 0x98, 0x1d, 0xde, 0x5c,
+ 0x53, 0x6e, 0x01, 0x73, 0x0d, 0x12, 0x7e, 0x77,
+ 0x03, 0xf1, 0xef, 0x1b, 0xc8, 0xa8, 0x0f, 0x97};
+
+ /* RSA Known Public Key Values (24-bits). */
+ static const PRUint8 rsa_public_exponent[FIPS_RSA_PUBLIC_EXPONENT_LENGTH]
+ = { 0x01, 0x00, 0x01 };
+ /* RSA Known Private Key Values (version is 8-bits), */
+ /* (private exponent is 1024-bits), */
+ /* (private prime0 is 512-bits), */
+ /* (private prime1 is 512-bits), */
+ /* (private prime exponent0 is 512-bits), */
+ /* (private prime exponent1 is 512-bits), */
+ /* and (private coefficient is 512-bits). */
+ static const PRUint8 rsa_version[] = { 0x00 };
+
+ static const PRUint8 rsa_private_exponent[FIPS_RSA_PRIVATE_EXPONENT_LENGTH]
+ = { 0x85, 0x27, 0x47, 0x61, 0x4c, 0xd4, 0xb5, 0xb2,
+ 0x0e, 0x70, 0x91, 0x8f, 0x3d, 0x97, 0xf9, 0x5f,
+ 0xcc, 0x09, 0x65, 0x1c, 0x7c, 0x5b, 0xb3, 0x6d,
+ 0x63, 0x3f, 0x7b, 0x55, 0x22, 0xbb, 0x7c, 0x48,
+ 0x77, 0xae, 0x80, 0x56, 0xc2, 0x10, 0xd5, 0x03,
+ 0xdb, 0x31, 0xaf, 0x8d, 0x54, 0xd4, 0x48, 0x99,
+ 0xa8, 0xc4, 0x23, 0x43, 0xb8, 0x48, 0x0b, 0xc7,
+ 0xbc, 0xf5, 0xcc, 0x64, 0x72, 0xbf, 0x59, 0x06,
+ 0x04, 0x1c, 0x32, 0xf5, 0x14, 0x2e, 0x6e, 0xe2,
+ 0x0f, 0x5c, 0xde, 0x36, 0x3c, 0x6e, 0x7c, 0x4d,
+ 0xcc, 0xd3, 0x00, 0x6e, 0xe5, 0x45, 0x46, 0xef,
+ 0x4d, 0x25, 0x46, 0x6d, 0x7f, 0xed, 0xbb, 0x4f,
+ 0x4d, 0x9f, 0xda, 0x87, 0x47, 0x8f, 0x74, 0x44,
+ 0xb7, 0xbe, 0x9d, 0xf5, 0xdd, 0xd2, 0x4c, 0xa5,
+ 0xab, 0x74, 0xe5, 0x29, 0xa1, 0xd2, 0x45, 0x3b,
+ 0x33, 0xde, 0xd5, 0xae, 0xf7, 0x03, 0x10, 0x21};
+
+ static const PRUint8 rsa_prime0[FIPS_RSA_PRIME0_LENGTH] = {
+ 0xf9, 0x74, 0x8f, 0x16, 0x02, 0x6b, 0xa0, 0xee,
+ 0x7f, 0x28, 0x97, 0x91, 0xdc, 0xec, 0xc0, 0x7c,
+ 0x49, 0xc2, 0x85, 0x76, 0xee, 0x66, 0x74, 0x2d,
+ 0x1a, 0xb8, 0xf7, 0x2f, 0x11, 0x5b, 0x36, 0xd8,
+ 0x46, 0x33, 0x3b, 0xd8, 0xf3, 0x2d, 0xa1, 0x03,
+ 0x83, 0x2b, 0xec, 0x35, 0x43, 0x32, 0xff, 0xdd,
+ 0x81, 0x7c, 0xfd, 0x65, 0x13, 0x04, 0x7c, 0xfc,
+ 0x03, 0x97, 0xf0, 0xd5, 0x62, 0xdc, 0x0d, 0xbf};
+ static const PRUint8 rsa_prime1[FIPS_RSA_PRIME1_LENGTH] = {
+ 0xdb, 0x1e, 0xa7, 0x3d, 0xe7, 0xfa, 0x8b, 0x04,
+ 0x83, 0x48, 0xf3, 0xa5, 0x31, 0x9d, 0x35, 0x5e,
+ 0x4d, 0x54, 0x77, 0xcc, 0x84, 0x09, 0xf3, 0x11,
+ 0x0d, 0x54, 0xed, 0x85, 0x39, 0xa9, 0xca, 0xa8,
+ 0xea, 0xae, 0x19, 0x9c, 0x75, 0xdb, 0x88, 0xb8,
+ 0x04, 0x8d, 0x54, 0xc6, 0xa4, 0x80, 0xf8, 0x93,
+ 0xf0, 0xdb, 0x19, 0xef, 0xd7, 0x87, 0x8a, 0x8f,
+ 0x5a, 0x09, 0x2e, 0x54, 0xf3, 0x45, 0x24, 0x29};
+ static const PRUint8 rsa_exponent0[FIPS_RSA_EXPONENT0_LENGTH] = {
+ 0x6a, 0xd1, 0x25, 0x80, 0x18, 0x33, 0x3c, 0x2b,
+ 0x44, 0x19, 0xfe, 0xa5, 0x40, 0x03, 0xc4, 0xfc,
+ 0xb3, 0x9c, 0xef, 0x07, 0x99, 0x58, 0x17, 0xc1,
+ 0x44, 0xa3, 0x15, 0x7d, 0x7b, 0x22, 0x22, 0xdf,
+ 0x03, 0x58, 0x66, 0xf5, 0x24, 0x54, 0x52, 0x91,
+ 0x2d, 0x76, 0xfe, 0x63, 0x64, 0x4e, 0x0f, 0x50,
+ 0x2b, 0x65, 0x79, 0x1f, 0xf1, 0xbf, 0xc7, 0x41,
+ 0x26, 0xcc, 0xc6, 0x1c, 0xa9, 0x83, 0x6f, 0x03};
+ static const PRUint8 rsa_exponent1[FIPS_RSA_EXPONENT1_LENGTH] = {
+ 0x12, 0x84, 0x1a, 0x99, 0xce, 0x9a, 0x8b, 0x58,
+ 0xcc, 0x47, 0x43, 0xdf, 0x77, 0xbb, 0xd3, 0x20,
+ 0xae, 0xe4, 0x2e, 0x63, 0x67, 0xdc, 0xf7, 0x5f,
+ 0x3f, 0x83, 0x27, 0xb7, 0x14, 0x52, 0x56, 0xbf,
+ 0xc3, 0x65, 0x06, 0xe1, 0x03, 0xcc, 0x93, 0x57,
+ 0x09, 0x7b, 0x6f, 0xe8, 0x81, 0x4a, 0x2c, 0xb7,
+ 0x43, 0xa9, 0x20, 0x1d, 0xf6, 0x56, 0x8b, 0xcc,
+ 0xe5, 0x4c, 0xd5, 0x4f, 0x74, 0x67, 0x29, 0x51};
+ static const PRUint8 rsa_coefficient[FIPS_RSA_COEFFICIENT_LENGTH] = {
+ 0x23, 0xab, 0xf4, 0x03, 0x2f, 0x29, 0x95, 0x74,
+ 0xac, 0x1a, 0x33, 0x96, 0x62, 0xed, 0xf7, 0xf6,
+ 0xae, 0x07, 0x2a, 0x2e, 0xe8, 0xab, 0xfb, 0x1e,
+ 0xb9, 0xb2, 0x88, 0x1e, 0x85, 0x05, 0x42, 0x64,
+ 0x03, 0xb2, 0x8b, 0xc1, 0x81, 0x75, 0xd7, 0xba,
+ 0xaa, 0xd4, 0x31, 0x3c, 0x8a, 0x96, 0x23, 0x9d,
+ 0x3f, 0x06, 0x3e, 0x44, 0xa9, 0x62, 0x2f, 0x61,
+ 0x5a, 0x51, 0x82, 0x2c, 0x04, 0x85, 0x73, 0xd1};
+
+ /* RSA Known Plaintext Message (1024-bits). */
+ static const PRUint8 rsa_known_plaintext_msg[FIPS_RSA_MESSAGE_LENGTH] = {
+ "Known plaintext message utilized"
+ "for RSA Encryption & Decryption"
+ "block, SHA1, SHA256, SHA384 and"
+ "SHA512 RSA Signature KAT tests."};
+
+ /* RSA Known Ciphertext (1024-bits). */
+ static const PRUint8 rsa_known_ciphertext[] = {
+ 0x1e, 0x7e, 0x12, 0xbb, 0x15, 0x62, 0xd0, 0x23,
+ 0x53, 0x4c, 0x51, 0x97, 0x77, 0x06, 0xa0, 0xbb,
+ 0x26, 0x99, 0x9a, 0x8f, 0x39, 0xad, 0x88, 0x5c,
+ 0xc4, 0xce, 0x33, 0x40, 0x94, 0x92, 0xb4, 0x0e,
+ 0xab, 0x71, 0xa9, 0x5d, 0x9a, 0x37, 0xe3, 0x9a,
+ 0x24, 0x95, 0x13, 0xea, 0x0f, 0xbb, 0xf7, 0xff,
+ 0xdf, 0x31, 0x33, 0x23, 0x1d, 0xce, 0x26, 0x9e,
+ 0xd1, 0xde, 0x98, 0x40, 0xde, 0x57, 0x86, 0x12,
+ 0xf1, 0xe6, 0x5a, 0x3f, 0x08, 0x02, 0x81, 0x85,
+ 0xe0, 0xd9, 0xad, 0x3c, 0x8c, 0x71, 0xf8, 0xcf,
+ 0x0a, 0x98, 0xc5, 0x08, 0xdc, 0xc4, 0xca, 0x8c,
+ 0x23, 0x1b, 0x4d, 0x9b, 0xb5, 0x13, 0x44, 0xe1,
+ 0x5f, 0xf9, 0x30, 0x80, 0x25, 0xe0, 0x1e, 0x94,
+ 0xa3, 0x0c, 0xdc, 0x82, 0x2e, 0xfb, 0x30, 0xbe,
+ 0x89, 0xba, 0x76, 0xb6, 0x23, 0xf7, 0xda, 0x7c,
+ 0xca, 0xe6, 0x02, 0xbd, 0x92, 0xce, 0x64, 0xfc};
+
+ /* RSA Known Signed Hash (1024-bits). */
+ static const PRUint8 rsa_known_sha1_signature[] = {
+ 0xd2, 0xa4, 0xe0, 0x2b, 0xc7, 0x03, 0x7f, 0xc6,
+ 0x06, 0x9e, 0xa2, 0x82, 0x19, 0xe9, 0x2b, 0xaf,
+ 0xe3, 0x48, 0x88, 0xc1, 0xf3, 0xb5, 0x0d, 0xe4,
+ 0x52, 0x9e, 0xad, 0xd5, 0x58, 0xb5, 0x9f, 0xe8,
+ 0x40, 0xe9, 0xb7, 0x2e, 0xc6, 0x71, 0x58, 0x56,
+ 0x04, 0xac, 0xb0, 0xf3, 0x3a, 0x42, 0x38, 0x08,
+ 0xc4, 0x43, 0x39, 0xba, 0x19, 0xce, 0xb1, 0x99,
+ 0xf1, 0x8d, 0x89, 0xd8, 0x50, 0x07, 0x14, 0x3d,
+ 0xcf, 0xd0, 0xb6, 0x79, 0xde, 0x9c, 0x89, 0x32,
+ 0xb0, 0x73, 0x3f, 0xed, 0x03, 0x0b, 0xdf, 0x6d,
+ 0x7e, 0xc9, 0x1c, 0x39, 0xe8, 0x2b, 0x16, 0x09,
+ 0xbb, 0x5f, 0x99, 0x2f, 0xeb, 0xf3, 0x37, 0x73,
+ 0x0d, 0x0e, 0xcc, 0x95, 0xad, 0x90, 0x80, 0x03,
+ 0x1d, 0x80, 0x55, 0x37, 0xa1, 0x2a, 0x71, 0x76,
+ 0x23, 0x87, 0x8c, 0x9b, 0x41, 0x07, 0xc6, 0x3d,
+ 0xc6, 0xa3, 0x7d, 0x1b, 0xff, 0x4e, 0x11, 0x19};
+
+ /* RSA Known Signed Hash (1024-bits). */
+ static const PRUint8 rsa_known_sha256_signature[] = {
+ 0x27, 0x35, 0xdd, 0xc4, 0xf8, 0xe2, 0x0b, 0xa3,
+ 0xef, 0x63, 0x57, 0x3b, 0xe1, 0x58, 0x9a, 0xbc,
+ 0x20, 0x9c, 0x25, 0x12, 0x01, 0xbf, 0xbb, 0x29,
+ 0x80, 0x1a, 0xb1, 0x37, 0x9c, 0xcd, 0x67, 0xc7,
+ 0x0d, 0xf8, 0x64, 0x10, 0x9f, 0xe2, 0xa1, 0x9b,
+ 0x21, 0x90, 0xcc, 0xda, 0x8b, 0x76, 0x5e, 0x79,
+ 0x00, 0x9d, 0x58, 0x8b, 0x8a, 0xb3, 0xc3, 0xb5,
+ 0xf1, 0x54, 0xc5, 0x8c, 0x72, 0xba, 0xde, 0x51,
+ 0x3c, 0x6b, 0x94, 0xd6, 0xf3, 0x1b, 0xa2, 0x53,
+ 0xe6, 0x1a, 0x46, 0x1d, 0x7f, 0x14, 0x86, 0xcc,
+ 0xa6, 0x30, 0x92, 0x96, 0xc0, 0x96, 0x24, 0xf0,
+ 0x42, 0x53, 0x4c, 0xdd, 0x27, 0xdf, 0x1d, 0x2e,
+ 0x8b, 0x83, 0xbe, 0xed, 0x85, 0x1d, 0x50, 0x46,
+ 0xa3, 0x7d, 0x20, 0xea, 0x3e, 0x91, 0xfb, 0xf6,
+ 0x86, 0x51, 0xfd, 0x8c, 0xe5, 0x31, 0xe6, 0x7e,
+ 0x60, 0x08, 0x0e, 0xec, 0xa6, 0xea, 0x24, 0x8d};
+
+ /* RSA Known Signed Hash (1024-bits). */
+ static const PRUint8 rsa_known_sha384_signature[] = {
+ 0x0b, 0x03, 0x94, 0x4f, 0x94, 0x78, 0x9b, 0x96,
+ 0x76, 0xeb, 0x72, 0x58, 0xe1, 0xc5, 0xc7, 0x5f,
+ 0x85, 0x01, 0xa8, 0xc4, 0xf6, 0x1a, 0xb5, 0x2c,
+ 0xd1, 0xd8, 0x87, 0xde, 0x3a, 0x9c, 0x9f, 0x57,
+ 0x81, 0x2a, 0x1e, 0x23, 0x07, 0x70, 0xb0, 0xf9,
+ 0x28, 0x3d, 0xfa, 0xe5, 0x2e, 0x1b, 0x9a, 0x72,
+ 0xc3, 0x74, 0xb3, 0x42, 0x1c, 0x9a, 0x13, 0xdc,
+ 0xc9, 0xd6, 0xd5, 0x88, 0xc9, 0x9c, 0x46, 0xf1,
+ 0x0c, 0xa6, 0xf7, 0xd8, 0x06, 0xa3, 0x1b, 0xdf,
+ 0x55, 0xb3, 0x1b, 0x7b, 0x58, 0x1d, 0xff, 0x19,
+ 0xc7, 0xe0, 0xdd, 0x59, 0xac, 0x2f, 0x78, 0x71,
+ 0xe7, 0xe0, 0x17, 0xa3, 0x1c, 0x5c, 0x92, 0xef,
+ 0xb6, 0x75, 0xed, 0xbe, 0x18, 0x39, 0x6b, 0xd7,
+ 0xc9, 0x08, 0x62, 0x55, 0x62, 0xac, 0x5d, 0xa1,
+ 0x9b, 0xd5, 0xb8, 0x98, 0x15, 0xc0, 0xf5, 0x41,
+ 0x85, 0x44, 0x96, 0xca, 0x10, 0xdc, 0x57, 0x21};
+
+ /* RSA Known Signed Hash (1024-bits). */
+ static const PRUint8 rsa_known_sha512_signature[] = {
+ 0xa5, 0xd0, 0x80, 0x04, 0x22, 0xfc, 0x80, 0x73,
+ 0x7d, 0x46, 0xc8, 0x7b, 0xac, 0x44, 0x7b, 0xe6,
+ 0x07, 0xe5, 0x61, 0x4c, 0x33, 0x7f, 0x6f, 0x46,
+ 0x7c, 0x30, 0xe3, 0x75, 0x59, 0x4b, 0x42, 0xf3,
+ 0x9f, 0x35, 0x3c, 0x10, 0x56, 0xdb, 0xd2, 0x69,
+ 0x43, 0xcb, 0x77, 0xe9, 0x7d, 0xcd, 0x07, 0x43,
+ 0xc5, 0xd4, 0x0c, 0x9d, 0xf5, 0x92, 0xbd, 0x0e,
+ 0x3b, 0xb7, 0x68, 0x88, 0x84, 0xca, 0xae, 0x0d,
+ 0xab, 0x71, 0x10, 0xad, 0xab, 0x27, 0xe4, 0xa3,
+ 0x24, 0x41, 0xeb, 0x1c, 0xa6, 0x5f, 0xf1, 0x85,
+ 0xd0, 0xf6, 0x22, 0x74, 0x3d, 0x81, 0xbe, 0xdd,
+ 0x1b, 0x2a, 0x4c, 0xd1, 0x6c, 0xb5, 0x6d, 0x7a,
+ 0xbb, 0x99, 0x69, 0x01, 0xa6, 0xc0, 0x98, 0xfa,
+ 0x97, 0xa3, 0xd1, 0xb0, 0xdf, 0x09, 0xe3, 0x3d,
+ 0x88, 0xee, 0x90, 0xf3, 0x10, 0x41, 0x0f, 0x06,
+ 0x31, 0xe9, 0x60, 0x2d, 0xbf, 0x63, 0x7b, 0xf8};
+
+ static const RSAPublicKey bl_public_key = { NULL,
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_modulus,
+ FIPS_RSA_MODULUS_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_public_exponent,
+ FIPS_RSA_PUBLIC_EXPONENT_LENGTH }
+ };
+ static const RSAPrivateKey bl_private_key = { NULL,
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_version,
+ FIPS_RSA_PRIVATE_VERSION_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_modulus,
+ FIPS_RSA_MODULUS_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_public_exponent,
+ FIPS_RSA_PUBLIC_EXPONENT_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_private_exponent,
+ FIPS_RSA_PRIVATE_EXPONENT_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_prime0,
+ FIPS_RSA_PRIME0_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_prime1,
+ FIPS_RSA_PRIME1_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_exponent0,
+ FIPS_RSA_EXPONENT0_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_exponent1,
+ FIPS_RSA_EXPONENT1_LENGTH },
+ { FIPS_RSA_TYPE, (unsigned char *)rsa_coefficient,
+ FIPS_RSA_COEFFICIENT_LENGTH }
+ };
+
+ /* RSA variables. */
+#ifdef CREATE_TEMP_ARENAS
+ PLArenaPool * rsa_public_arena;
+ PLArenaPool * rsa_private_arena;
+#endif
+ NSSLOWKEYPublicKey * rsa_public_key;
+ NSSLOWKEYPrivateKey * rsa_private_key;
+ SECStatus rsa_status;
+
+ NSSLOWKEYPublicKey low_public_key = { NULL, NSSLOWKEYRSAKey, };
+ NSSLOWKEYPrivateKey low_private_key = { NULL, NSSLOWKEYRSAKey, };
+ PRUint8 rsa_computed_ciphertext[FIPS_RSA_ENCRYPT_LENGTH];
+ PRUint8 rsa_computed_plaintext[FIPS_RSA_DECRYPT_LENGTH];
+
+ /****************************************/
+ /* Compose RSA Public/Private Key Pair. */
+ /****************************************/
+
+ low_public_key.u.rsa = bl_public_key;
+ low_private_key.u.rsa = bl_private_key;
+
+ rsa_public_key = &low_public_key;
+ rsa_private_key = &low_private_key;
+
+#ifdef CREATE_TEMP_ARENAS
+ /* Create some space for the RSA public key. */
+ rsa_public_arena = PORT_NewArena( NSS_SOFTOKEN_DEFAULT_CHUNKSIZE );
+
+ if( rsa_public_arena == NULL ) {
+ PORT_SetError( SEC_ERROR_NO_MEMORY );
+ return( CKR_HOST_MEMORY );
+ }
+
+ /* Create some space for the RSA private key. */
+ rsa_private_arena = PORT_NewArena( NSS_SOFTOKEN_DEFAULT_CHUNKSIZE );
+
+ if( rsa_private_arena == NULL ) {
+ PORT_FreeArena( rsa_public_arena, PR_TRUE );
+ PORT_SetError( SEC_ERROR_NO_MEMORY );
+ return( CKR_HOST_MEMORY );
+ }
+
+ rsa_public_key->arena = rsa_public_arena;
+ rsa_private_key->arena = rsa_private_arena;
+#endif
+
+ /**************************************************/
+ /* RSA Single-Round Known Answer Encryption Test. */
+ /**************************************************/
+
+ /* Perform RSA Public Key Encryption. */
+ rsa_status = RSA_PublicKeyOp(&rsa_public_key->u.rsa,
+ rsa_computed_ciphertext,
+ rsa_known_plaintext_msg);
+
+ if( ( rsa_status != SECSuccess ) ||
+ ( PORT_Memcmp( rsa_computed_ciphertext, rsa_known_ciphertext,
+ FIPS_RSA_ENCRYPT_LENGTH ) != 0 ) )
+ goto rsa_loser;
+
+ /**************************************************/
+ /* RSA Single-Round Known Answer Decryption Test. */
+ /**************************************************/
+
+ /* Perform RSA Private Key Decryption. */
+ rsa_status = RSA_PrivateKeyOp(&rsa_private_key->u.rsa,
+ rsa_computed_plaintext,
+ rsa_known_ciphertext);
+
+ if( ( rsa_status != SECSuccess ) ||
+ ( PORT_Memcmp( rsa_computed_plaintext, rsa_known_plaintext_msg,
+ FIPS_RSA_DECRYPT_LENGTH ) != 0 ) )
+ goto rsa_loser;
+
+ rsa_status = sftk_fips_RSA_PowerUpSigSelfTest (HASH_AlgSHA1,
+ rsa_public_key, rsa_private_key,
+ rsa_known_plaintext_msg, FIPS_RSA_MESSAGE_LENGTH,
+ rsa_known_sha1_signature);
+ if( rsa_status != SECSuccess )
+ goto rsa_loser;
+
+ rsa_status = sftk_fips_RSA_PowerUpSigSelfTest (HASH_AlgSHA256,
+ rsa_public_key, rsa_private_key,
+ rsa_known_plaintext_msg, FIPS_RSA_MESSAGE_LENGTH,
+ rsa_known_sha256_signature);
+ if( rsa_status != SECSuccess )
+ goto rsa_loser;
+
+ rsa_status = sftk_fips_RSA_PowerUpSigSelfTest (HASH_AlgSHA384,
+ rsa_public_key, rsa_private_key,
+ rsa_known_plaintext_msg, FIPS_RSA_MESSAGE_LENGTH,
+ rsa_known_sha384_signature);
+ if( rsa_status != SECSuccess )
+ goto rsa_loser;
+
+ rsa_status = sftk_fips_RSA_PowerUpSigSelfTest (HASH_AlgSHA512,
+ rsa_public_key, rsa_private_key,
+ rsa_known_plaintext_msg, FIPS_RSA_MESSAGE_LENGTH,
+ rsa_known_sha512_signature);
+ if( rsa_status != SECSuccess )
+ goto rsa_loser;
+
+ /* Dispose of all RSA key material. */
+ nsslowkey_DestroyPublicKey( rsa_public_key );
+ nsslowkey_DestroyPrivateKey( rsa_private_key );
+
+ return( CKR_OK );
+
+rsa_loser:
+
+ nsslowkey_DestroyPublicKey( rsa_public_key );
+ nsslowkey_DestroyPrivateKey( rsa_private_key );
+
+ return( CKR_DEVICE_ERROR );
+}
+
+#ifdef NSS_ENABLE_ECC
+
+static CK_RV
+sftk_fips_ECDSA_Test(const PRUint8 *encodedParams,
+ unsigned int encodedParamsLen,
+ const PRUint8 *knownSignature,
+ unsigned int knownSignatureLen) {
+
+ /* ECDSA Known Seed info for curves nistp256 and nistk283 */
+ static const PRUint8 ecdsa_Known_Seed[] = {
+ 0x6a, 0x9b, 0xf6, 0xf7, 0xce, 0xed, 0x79, 0x11,
+ 0xf0, 0xc7, 0xc8, 0x9a, 0xa5, 0xd1, 0x57, 0xb1,
+ 0x7b, 0x5a, 0x3b, 0x76, 0x4e, 0x7b, 0x7c, 0xbc,
+ 0xf2, 0x76, 0x1c, 0x1c, 0x7f, 0xc5, 0x53, 0x2f};
+
+ static const PRUint8 msg[] = {
+ "Firefox and ThunderBird are awesome!"};
+
+ unsigned char sha1[SHA1_LENGTH]; /* SHA-1 hash (160 bits) */
+ unsigned char sig[2*MAX_ECKEY_LEN];
+ SECItem signature, digest;
+ SECItem encodedparams;
+ ECParams *ecparams = NULL;
+ ECPrivateKey *ecdsa_private_key = NULL;
+ ECPublicKey ecdsa_public_key;
+ SECStatus ecdsaStatus = SECSuccess;
+
+ /* construct the ECDSA private/public key pair */
+ encodedparams.type = siBuffer;
+ encodedparams.data = (unsigned char *) encodedParams;
+ encodedparams.len = encodedParamsLen;
+
+ if (EC_DecodeParams(&encodedparams, &ecparams) != SECSuccess) {
+ return( CKR_DEVICE_ERROR );
+ }
+
+ /* Generates a new EC key pair. The private key is a supplied
+ * random value (in seed) and the public key is the result of
+ * performing a scalar point multiplication of that value with
+ * the curve's base point.
+ */
+ ecdsaStatus = EC_NewKeyFromSeed(ecparams, &ecdsa_private_key,
+ ecdsa_Known_Seed,
+ sizeof(ecdsa_Known_Seed));
+ /* free the ecparams they are no longer needed */
+ PORT_FreeArena(ecparams->arena, PR_FALSE);
+ ecparams = NULL;
+ if (ecdsaStatus != SECSuccess) {
+ return ( CKR_DEVICE_ERROR );
+ }
+
+ /* construct public key from private key. */
+ ecdsaStatus = EC_CopyParams(ecdsa_private_key->ecParams.arena,
+ &ecdsa_public_key.ecParams,
+ &ecdsa_private_key->ecParams);
+ if (ecdsaStatus != SECSuccess) {
+ goto loser;
+ }
+ ecdsa_public_key.publicValue = ecdsa_private_key->publicValue;
+
+ /* validate public key value */
+ ecdsaStatus = EC_ValidatePublicKey(&ecdsa_public_key.ecParams,
+ &ecdsa_public_key.publicValue);
+ if (ecdsaStatus != SECSuccess) {
+ goto loser;
+ }
+
+ /* validate public key value */
+ ecdsaStatus = EC_ValidatePublicKey(&ecdsa_private_key->ecParams,
+ &ecdsa_private_key->publicValue);
+ if (ecdsaStatus != SECSuccess) {
+ goto loser;
+ }
+
+ /***************************************************/
+ /* ECDSA Single-Round Known Answer Signature Test. */
+ /***************************************************/
+
+ ecdsaStatus = SHA1_HashBuf(sha1, msg, sizeof msg);
+ if (ecdsaStatus != SECSuccess) {
+ goto loser;
+ }
+ digest.type = siBuffer;
+ digest.data = sha1;
+ digest.len = SHA1_LENGTH;
+
+ memset(sig, 0, sizeof sig);
+ signature.type = siBuffer;
+ signature.data = sig;
+ signature.len = sizeof sig;
+
+ ecdsaStatus = ECDSA_SignDigestWithSeed(ecdsa_private_key, &signature,
+ &digest, ecdsa_Known_Seed, sizeof ecdsa_Known_Seed);
+ if (ecdsaStatus != SECSuccess) {
+ goto loser;
+ }
+
+ if( ( signature.len != knownSignatureLen ) ||
+ ( PORT_Memcmp( signature.data, knownSignature,
+ knownSignatureLen ) != 0 ) ) {
+ ecdsaStatus = SECFailure;
+ goto loser;
+ }
+
+ /******************************************************/
+ /* ECDSA Single-Round Known Answer Verification Test. */
+ /******************************************************/
+
+ /* Perform ECDSA verification process. */
+ ecdsaStatus = ECDSA_VerifyDigest(&ecdsa_public_key, &signature, &digest);
+
+loser:
+ /* free the memory for the private key arena*/
+ if (ecdsa_private_key != NULL) {
+ PORT_FreeArena(ecdsa_private_key->ecParams.arena, PR_FALSE);
+ }
+
+ if (ecdsaStatus != SECSuccess) {
+ return CKR_DEVICE_ERROR ;
+ }
+ return( CKR_OK );
+}
+
+static CK_RV
+sftk_fips_ECDSA_PowerUpSelfTest() {
+
+ /* ECDSA Known curve nistp256 == SEC_OID_SECG_EC_SECP256R1 params */
+ static const PRUint8 ecdsa_known_P256_EncodedParams[] = {
+ 0x06,0x08,0x2a,0x86,0x48,0xce,0x3d,0x03,
+ 0x01,0x07};
+
+ static const PRUint8 ecdsa_known_P256_signature[] = {
+ 0x07,0xb1,0xcb,0x57,0x20,0xa7,0x10,0xd6,
+ 0x9d,0x37,0x4b,0x1c,0xdc,0x35,0x90,0xff,
+ 0x1a,0x2d,0x98,0x95,0x1b,0x2f,0xeb,0x7f,
+ 0xbb,0x81,0xca,0xc0,0x69,0x75,0xea,0xc5,
+ 0x59,0x6a,0x62,0x49,0x3d,0x50,0xc9,0xe1,
+ 0x27,0x3b,0xff,0x9b,0x13,0x66,0x67,0xdd,
+ 0x7d,0xd1,0x0d,0x2d,0x7c,0x44,0x04,0x1b,
+ 0x16,0x21,0x12,0xc5,0xcb,0xbd,0x9e,0x75};
+
+#ifdef NSS_ECC_MORE_THAN_SUITE_B
+ /* ECDSA Known curve nistk283 == SEC_OID_SECG_EC_SECT283K1 params */
+ static const PRUint8 ecdsa_known_K283_EncodedParams[] = {
+ 0x06,0x05,0x2b,0x81,0x04,0x00,0x10};
+
+ static const PRUint8 ecdsa_known_K283_signature[] = {
+ 0x00,0x45,0x88,0xc0,0x79,0x09,0x07,0xd1,
+ 0x4e,0x88,0xe6,0xd5,0x2f,0x22,0x04,0x74,
+ 0x35,0x24,0x65,0xe8,0x15,0xde,0x90,0x66,
+ 0x94,0x70,0xdd,0x3a,0x14,0x70,0x02,0xd1,
+ 0xef,0x86,0xbd,0x15,0x00,0xd9,0xdc,0xfc,
+ 0x87,0x2e,0x7c,0x99,0xe2,0xe3,0x79,0xb8,
+ 0xd9,0x10,0x49,0x78,0x4b,0x59,0x8b,0x05,
+ 0x77,0xec,0x6c,0xe8,0x35,0xe6,0x2e,0xa9,
+ 0xf9,0x77,0x1f,0x71,0x86,0xa5,0x4a,0xd0};
+#endif
+
+ CK_RV crv;
+
+ /* ECDSA GF(p) prime field curve test */
+ crv = sftk_fips_ECDSA_Test(ecdsa_known_P256_EncodedParams,
+ sizeof ecdsa_known_P256_EncodedParams,
+ ecdsa_known_P256_signature,
+ sizeof ecdsa_known_P256_signature );
+ if (crv != CKR_OK) {
+ return( CKR_DEVICE_ERROR );
+ }
+
+#ifdef NSS_ECC_MORE_THAN_SUITE_B
+ /* ECDSA GF(2m) binary field curve test */
+ crv = sftk_fips_ECDSA_Test(ecdsa_known_K283_EncodedParams,
+ sizeof ecdsa_known_K283_EncodedParams,
+ ecdsa_known_K283_signature,
+ sizeof ecdsa_known_K283_signature );
+ if (crv != CKR_OK) {
+ return( CKR_DEVICE_ERROR );
+ }
+#endif
+
+ return( CKR_OK );
+}
+
+#endif /* NSS_ENABLE_ECC */
+
+static CK_RV
+sftk_fips_DSA_PowerUpSelfTest( void )
+{
+ /* DSA Known P (1024-bits), Q (160-bits), and G (1024-bits) Values. */
+ static const PRUint8 dsa_P[] = {
+ 0x80,0xb0,0xd1,0x9d,0x6e,0xa4,0xf3,0x28,
+ 0x9f,0x24,0xa9,0x8a,0x49,0xd0,0x0c,0x63,
+ 0xe8,0x59,0x04,0xf9,0x89,0x4a,0x5e,0xc0,
+ 0x6d,0xd2,0x67,0x6b,0x37,0x81,0x83,0x0c,
+ 0xfe,0x3a,0x8a,0xfd,0xa0,0x3b,0x08,0x91,
+ 0x1c,0xcb,0xb5,0x63,0xb0,0x1c,0x70,0xd0,
+ 0xae,0xe1,0x60,0x2e,0x12,0xeb,0x54,0xc7,
+ 0xcf,0xc6,0xcc,0xae,0x97,0x52,0x32,0x63,
+ 0xd3,0xeb,0x55,0xea,0x2f,0x4c,0xd5,0xd7,
+ 0x3f,0xda,0xec,0x49,0x27,0x0b,0x14,0x56,
+ 0xc5,0x09,0xbe,0x4d,0x09,0x15,0x75,0x2b,
+ 0xa3,0x42,0x0d,0x03,0x71,0xdf,0x0f,0xf4,
+ 0x0e,0xe9,0x0c,0x46,0x93,0x3d,0x3f,0xa6,
+ 0x6c,0xdb,0xca,0xe5,0xac,0x96,0xc8,0x64,
+ 0x5c,0xec,0x4b,0x35,0x65,0xfc,0xfb,0x5a,
+ 0x1b,0x04,0x1b,0xa1,0x0e,0xfd,0x88,0x15};
+
+ static const PRUint8 dsa_Q[] = {
+ 0xad,0x22,0x59,0xdf,0xe5,0xec,0x4c,0x6e,
+ 0xf9,0x43,0xf0,0x4b,0x2d,0x50,0x51,0xc6,
+ 0x91,0x99,0x8b,0xcf};
+
+ static const PRUint8 dsa_G[] = {
+ 0x78,0x6e,0xa9,0xd8,0xcd,0x4a,0x85,0xa4,
+ 0x45,0xb6,0x6e,0x5d,0x21,0x50,0x61,0xf6,
+ 0x5f,0xdf,0x5c,0x7a,0xde,0x0d,0x19,0xd3,
+ 0xc1,0x3b,0x14,0xcc,0x8e,0xed,0xdb,0x17,
+ 0xb6,0xca,0xba,0x86,0xa9,0xea,0x51,0x2d,
+ 0xc1,0xa9,0x16,0xda,0xf8,0x7b,0x59,0x8a,
+ 0xdf,0xcb,0xa4,0x67,0x00,0x44,0xea,0x24,
+ 0x73,0xe5,0xcb,0x4b,0xaf,0x2a,0x31,0x25,
+ 0x22,0x28,0x3f,0x16,0x10,0x82,0xf7,0xeb,
+ 0x94,0x0d,0xdd,0x09,0x22,0x14,0x08,0x79,
+ 0xba,0x11,0x0b,0xf1,0xff,0x2d,0x67,0xac,
+ 0xeb,0xb6,0x55,0x51,0x69,0x97,0xa7,0x25,
+ 0x6b,0x9c,0xa0,0x9b,0xd5,0x08,0x9b,0x27,
+ 0x42,0x1c,0x7a,0x69,0x57,0xe6,0x2e,0xed,
+ 0xa9,0x5b,0x25,0xe8,0x1f,0xd2,0xed,0x1f,
+ 0xdf,0xe7,0x80,0x17,0xba,0x0d,0x4d,0x38};
+
+ /* DSA Known Random Values (known random key block is 160-bits) */
+ /* and (known random signature block is 160-bits). */
+ static const PRUint8 dsa_known_random_key_block[] = {
+ "Mozilla Rules World!"};
+ static const PRUint8 dsa_known_random_signature_block[] = {
+ "Random DSA Signature"};
+
+ /* DSA Known Digest (160-bits) */
+ static const PRUint8 dsa_known_digest[] = { "DSA Signature Digest" };
+
+ /* DSA Known Signature (320-bits). */
+ static const PRUint8 dsa_known_signature[] = {
+ 0x25,0x7c,0x3a,0x79,0x32,0x45,0xb7,0x32,
+ 0x70,0xca,0x62,0x63,0x2b,0xf6,0x29,0x2c,
+ 0x22,0x2a,0x03,0xce,0x48,0x15,0x11,0x72,
+ 0x7b,0x7e,0xf5,0x7a,0xf3,0x10,0x3b,0xde,
+ 0x34,0xc1,0x9e,0xd7,0x27,0x9e,0x77,0x38};
+
+ /* DSA variables. */
+ DSAPrivateKey * dsa_private_key;
+ SECStatus dsa_status;
+ SECItem dsa_signature_item;
+ SECItem dsa_digest_item;
+ DSAPublicKey dsa_public_key;
+ PRUint8 dsa_computed_signature[FIPS_DSA_SIGNATURE_LENGTH];
+ static const PQGParams dsa_pqg = { NULL,
+ { FIPS_DSA_TYPE, (unsigned char *)dsa_P, FIPS_DSA_PRIME_LENGTH },
+ { FIPS_DSA_TYPE, (unsigned char *)dsa_Q, FIPS_DSA_SUBPRIME_LENGTH },
+ { FIPS_DSA_TYPE, (unsigned char *)dsa_G, FIPS_DSA_BASE_LENGTH }};
+
+ /*******************************************/
+ /* Generate a DSA public/private key pair. */
+ /*******************************************/
+
+ /* Generate a DSA public/private key pair. */
+ dsa_status = DSA_NewKeyFromSeed(&dsa_pqg, dsa_known_random_key_block,
+ &dsa_private_key);
+
+ if( dsa_status != SECSuccess )
+ return( CKR_HOST_MEMORY );
+
+ /* construct public key from private key. */
+ dsa_public_key.params = dsa_private_key->params;
+ dsa_public_key.publicValue = dsa_private_key->publicValue;
+
+ /*************************************************/
+ /* DSA Single-Round Known Answer Signature Test. */
+ /*************************************************/
+
+ dsa_signature_item.data = dsa_computed_signature;
+ dsa_signature_item.len = sizeof dsa_computed_signature;
+
+ dsa_digest_item.data = (unsigned char *)dsa_known_digest;
+ dsa_digest_item.len = SHA1_LENGTH;
+
+ /* Perform DSA signature process. */
+ dsa_status = DSA_SignDigestWithSeed( dsa_private_key,
+ &dsa_signature_item,
+ &dsa_digest_item,
+ dsa_known_random_signature_block );
+
+ if( ( dsa_status != SECSuccess ) ||
+ ( dsa_signature_item.len != FIPS_DSA_SIGNATURE_LENGTH ) ||
+ ( PORT_Memcmp( dsa_computed_signature, dsa_known_signature,
+ FIPS_DSA_SIGNATURE_LENGTH ) != 0 ) ) {
+ dsa_status = SECFailure;
+ } else {
+
+ /****************************************************/
+ /* DSA Single-Round Known Answer Verification Test. */
+ /****************************************************/
+
+ /* Perform DSA verification process. */
+ dsa_status = DSA_VerifyDigest( &dsa_public_key,
+ &dsa_signature_item,
+ &dsa_digest_item);
+ }
+
+ PORT_FreeArena(dsa_private_key->params.arena, PR_TRUE);
+ /* Don't free public key, it uses same arena as private key */
+
+ /* Verify DSA signature. */
+ if( dsa_status != SECSuccess )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+
+
+}
+
+static CK_RV
+sftk_fips_RNG_PowerUpSelfTest( void )
+{
+ static const PRUint8 XKeyValue[] = {
+ 0x8d,0xf2,0xa4,0x94,0x49,0x22,0x76,0xaa,
+ 0x3d,0x25,0x75,0x9b,0xb0,0x68,0x69,0xcb,
+ 0xea,0xc0,0xd8,0x3a,0xfb,0x8d,0x0c,0xf7,
+ 0xcb,0xb8,0x32,0x4f,0x0d,0x78,0x82,0xe5};
+ static const PRUint8 XSeed[] = {
+ 0xea,0xc0,0xd8,0x3a,0xfb,0x8d,0x0c,0xf7,
+ 0xcb,0xb8,0x32,0x4f,0x0d,0x78,0x82,0xe5,
+ 0xd0,0x76,0x2f,0xc5,0xb7,0x21,0x0e,0xaf,
+ 0xc2,0xe9,0xad,0xac,0x32,0xab,0x7a,0xac};
+ static const PRUint8 Q[] = {
+ 0x85,0x89,0x9c,0x77,0xa3,0x79,0xff,0x1a,
+ 0x86,0x6f,0x2f,0x3e,0x2e,0xf9,0x8c,0x9c,
+ 0x9d,0xef,0xeb,0xed};
+ static const PRUint8 rng_known_GENX[] = {
+ 0x65,0x48,0xe3,0xca,0xac,0x64,0x2d,0xf7,
+ 0x7b,0xd3,0x4e,0x79,0xc9,0x7d,0xa6,0xa8,
+ 0xa2,0xc2,0x1f,0x8f,0xe9,0xb9,0xd3,0xa1,
+ 0x3f,0xf7,0x0c,0xcd,0xa6,0xca,0xbf,0xce,
+ 0x84,0x0e,0xb6,0xf1,0x0d,0xbe,0xa9,0xa3};
+ static const PRUint8 rng_known_DSAX[] = {
+ 0x7a,0x86,0xf1,0x7f,0xbd,0x4e,0x6e,0xd9,
+ 0x0a,0x26,0x21,0xd0,0x19,0xcb,0x86,0x73,
+ 0x10,0x1f,0x60,0xd7};
+
+ SECStatus rng_status = SECSuccess;
+ PRUint8 GENX[2*SHA1_LENGTH];
+ PRUint8 DSAX[FIPS_DSA_SUBPRIME_LENGTH];
+ PRUint8 XKey[FIPS_RNG_XKEY_LENGTH];
+
+ PORT_Memcpy (XKey, XKeyValue, FIPS_RNG_XKEY_LENGTH);
+
+ /*******************************************/
+ /* Generate X with a known seed. */
+ /*******************************************/
+ rng_status = FIPS186Change_GenerateX(XKey, XSeed, GENX);
+
+ /* Verify GENX to perform the RNG integrity check */
+ if( ( rng_status != SECSuccess ) ||
+ ( PORT_Memcmp( GENX, rng_known_GENX,
+ (2*SHA1_LENGTH) ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ /*******************************************/
+ /* Generate DSAX fow given Q. */
+ /*******************************************/
+
+ rng_status = FIPS186Change_ReduceModQForDSA(GENX, Q, DSAX);
+
+ /* Verify DSAX to perform the RNG integrity check */
+ if( ( rng_status != SECSuccess ) ||
+ ( PORT_Memcmp( DSAX, rng_known_DSAX,
+ (FIPS_DSA_SUBPRIME_LENGTH) ) != 0 ) )
+ return( CKR_DEVICE_ERROR );
+
+ return( CKR_OK );
+}
+
+static CK_RV
+sftk_fipsSoftwareIntegrityTest(void)
+{
+ CK_RV crv = CKR_OK;
+
+ /* make sure that our check file signatures are OK */
+ if( !BLAPI_VerifySelf( NULL ) ||
+ !BLAPI_SHVerify( SOFTOKEN_LIB_NAME, (PRFuncPtr) sftk_fips_HMAC ) ) {
+ crv = CKR_DEVICE_ERROR; /* better error code? checksum error? */
+ }
+ return crv;
+}
+
+CK_RV
+sftk_fipsPowerUpSelfTest( void )
+{
+ CK_RV rv;
+
+ /* RC2 Power-Up SelfTest(s). */
+ rv = sftk_fips_RC2_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* RC4 Power-Up SelfTest(s). */
+ rv = sftk_fips_RC4_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* DES Power-Up SelfTest(s). */
+ rv = sftk_fips_DES_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* DES3 Power-Up SelfTest(s). */
+ rv = sftk_fips_DES3_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* AES Power-Up SelfTest(s) for 128-bit key. */
+ rv = sftk_fips_AES_PowerUpSelfTest(FIPS_AES_128_KEY_SIZE);
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* AES Power-Up SelfTest(s) for 192-bit key. */
+ rv = sftk_fips_AES_PowerUpSelfTest(FIPS_AES_192_KEY_SIZE);
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* AES Power-Up SelfTest(s) for 256-bit key. */
+ rv = sftk_fips_AES_PowerUpSelfTest(FIPS_AES_256_KEY_SIZE);
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* MD2 Power-Up SelfTest(s). */
+ rv = sftk_fips_MD2_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* MD5 Power-Up SelfTest(s). */
+ rv = sftk_fips_MD5_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* SHA-X Power-Up SelfTest(s). */
+ rv = sftk_fips_SHA_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* HMAC SHA-X Power-Up SelfTest(s). */
+ rv = sftk_fips_HMAC_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* RSA Power-Up SelfTest(s). */
+ rv = sftk_fips_RSA_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* DSA Power-Up SelfTest(s). */
+ rv = sftk_fips_DSA_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* RNG Power-Up SelfTest(s). */
+ rv = sftk_fips_RNG_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+#ifdef NSS_ENABLE_ECC
+ /* ECDSA Power-Up SelfTest(s). */
+ rv = sftk_fips_ECDSA_PowerUpSelfTest();
+
+ if( rv != CKR_OK )
+ return rv;
+#endif
+
+ /* Software/Firmware Integrity Test. */
+ rv = sftk_fipsSoftwareIntegrityTest();
+
+ if( rv != CKR_OK )
+ return rv;
+
+ /* Passed Power-Up SelfTest(s). */
+ return( CKR_OK );
+}
+
diff --git a/security/nss/lib/softoken/fipstokn.c b/security/nss/lib/softoken/fipstokn.c
new file mode 100644
index 000000000..ca6aa34f2
--- /dev/null
+++ b/security/nss/lib/softoken/fipstokn.c
@@ -0,0 +1,1554 @@
+/* ***** 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 ***** */
+/*
+ * This file implements PKCS 11 on top of our existing security modules
+ *
+ * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
+ * This implementation has two slots:
+ * slot 1 is our generic crypto support. It does not require login
+ * (unless you've enabled FIPS). It supports Public Key ops, and all they
+ * bulk ciphers and hashes. It can also support Private Key ops for imported
+ * Private keys. It does not have any token storage.
+ * slot 2 is our private key support. It requires a login before use. It
+ * can store Private Keys and Certs as token objects. Currently only private
+ * keys and their associated Certificates are saved on the token.
+ *
+ * In this implementation, session objects are only visible to the session
+ * that created or generated them.
+ */
+#include "seccomon.h"
+#include "softoken.h"
+#include "lowkeyi.h"
+#include "pkcs11.h"
+#include "pkcs11i.h"
+#include "prenv.h"
+#include "prprf.h"
+
+#include <ctype.h>
+
+#ifdef XP_UNIX
+#define NSS_AUDIT_WITH_SYSLOG 1
+#include <syslog.h>
+#include <unistd.h>
+#endif
+
+#ifdef SOLARIS
+#include <bsm/libbsm.h>
+#define AUE_FIPS_AUDIT 34444
+#endif
+
+#ifdef LINUX
+#include <pthread.h>
+#include <dlfcn.h>
+#define LIBAUDIT_NAME "libaudit.so.0"
+#ifndef AUDIT_USER
+#define AUDIT_USER 1005 /* message type: message from userspace */
+#endif
+static void *libaudit_handle;
+static int (*audit_open_func)(void);
+static void (*audit_close_func)(int fd);
+static int (*audit_log_user_message_func)(int audit_fd, int type,
+ const char *message, const char *hostname, const char *addr,
+ const char *tty, int result);
+static int (*audit_send_user_message_func)(int fd, int type,
+ const char *message);
+
+static pthread_once_t libaudit_once_control = PTHREAD_ONCE_INIT;
+
+static void
+libaudit_init(void)
+{
+ libaudit_handle = dlopen(LIBAUDIT_NAME, RTLD_LAZY);
+ if (!libaudit_handle) {
+ return;
+ }
+ audit_open_func = dlsym(libaudit_handle, "audit_open");
+ audit_close_func = dlsym(libaudit_handle, "audit_close");
+ /*
+ * audit_send_user_message is the older function.
+ * audit_log_user_message, if available, is preferred.
+ */
+ audit_log_user_message_func = dlsym(libaudit_handle,
+ "audit_log_user_message");
+ if (!audit_log_user_message_func) {
+ audit_send_user_message_func = dlsym(libaudit_handle,
+ "audit_send_user_message");
+ }
+ if (!audit_open_func || !audit_close_func ||
+ (!audit_log_user_message_func && !audit_send_user_message_func)) {
+ dlclose(libaudit_handle);
+ libaudit_handle = NULL;
+ audit_open_func = NULL;
+ audit_close_func = NULL;
+ audit_log_user_message_func = NULL;
+ audit_send_user_message_func = NULL;
+ }
+}
+#endif /* LINUX */
+
+
+/*
+ * ******************** Password Utilities *******************************
+ */
+static PRBool isLoggedIn = PR_FALSE;
+PRBool sftk_fatalError = PR_FALSE;
+
+/*
+ * This function returns
+ * - CKR_PIN_INVALID if the password/PIN is not a legal UTF8 string
+ * - CKR_PIN_LEN_RANGE if the password/PIN is too short or does not
+ * consist of characters from three or more character classes.
+ * - CKR_OK otherwise
+ *
+ * The minimum password/PIN length is FIPS_MIN_PIN Unicode characters.
+ * We define five character classes: digits (0-9), ASCII lowercase letters,
+ * ASCII uppercase letters, ASCII non-alphanumeric characters (such as
+ * space and punctuation marks), and non-ASCII characters. If an ASCII
+ * uppercase letter is the first character of the password/PIN, the
+ * uppercase letter is not counted toward its character class. Similarly,
+ * if a digit is the last character of the password/PIN, the digit is not
+ * counted toward its character class.
+ *
+ * Although NSC_SetPIN and NSC_InitPIN already do the maximum and minimum
+ * password/PIN length checks, they check the length in bytes as opposed
+ * to characters. To meet the minimum password/PIN guessing probability
+ * requirements in FIPS 140-2, we need to check the length in characters.
+ */
+static CK_RV sftk_newPinCheck(CK_CHAR_PTR pPin, CK_ULONG ulPinLen) {
+ unsigned int i;
+ int nchar = 0; /* number of characters */
+ int ntrail = 0; /* number of trailing bytes to follow */
+ int ndigit = 0; /* number of decimal digits */
+ int nlower = 0; /* number of ASCII lowercase letters */
+ int nupper = 0; /* number of ASCII uppercase letters */
+ int nnonalnum = 0; /* number of ASCII non-alphanumeric characters */
+ int nnonascii = 0; /* number of non-ASCII characters */
+ int nclass; /* number of character classes */
+
+ for (i = 0; i < ulPinLen; i++) {
+ unsigned int byte = pPin[i];
+
+ if (ntrail) {
+ if ((byte & 0xc0) != 0x80) {
+ /* illegal */
+ nchar = -1;
+ break;
+ }
+ if (--ntrail == 0) {
+ nchar++;
+ nnonascii++;
+ }
+ continue;
+ }
+ if ((byte & 0x80) == 0x00) {
+ /* single-byte (ASCII) character */
+ nchar++;
+ if (isdigit(byte)) {
+ if (i < ulPinLen - 1) {
+ ndigit++;
+ }
+ } else if (islower(byte)) {
+ nlower++;
+ } else if (isupper(byte)) {
+ if (i > 0) {
+ nupper++;
+ }
+ } else {
+ nnonalnum++;
+ }
+ } else if ((byte & 0xe0) == 0xc0) {
+ /* leading byte of two-byte character */
+ ntrail = 1;
+ } else if ((byte & 0xf0) == 0xe0) {
+ /* leading byte of three-byte character */
+ ntrail = 2;
+ } else if ((byte & 0xf8) == 0xf0) {
+ /* leading byte of four-byte character */
+ ntrail = 3;
+ } else {
+ /* illegal */
+ nchar = -1;
+ break;
+ }
+ }
+ if (nchar == -1) {
+ /* illegal UTF8 string */
+ return CKR_PIN_INVALID;
+ }
+ if (nchar < FIPS_MIN_PIN) {
+ return CKR_PIN_LEN_RANGE;
+ }
+ nclass = (ndigit != 0) + (nlower != 0) + (nupper != 0) +
+ (nnonalnum != 0) + (nnonascii != 0);
+ if (nclass < 3) {
+ return CKR_PIN_LEN_RANGE;
+ }
+ return CKR_OK;
+}
+
+
+/* FIPS required checks before any useful cryptographic services */
+static CK_RV sftk_fipsCheck(void) {
+ if (sftk_fatalError)
+ return CKR_DEVICE_ERROR;
+ if (!isLoggedIn)
+ return CKR_USER_NOT_LOGGED_IN;
+ return CKR_OK;
+}
+
+
+#define SFTK_FIPSCHECK() \
+ CK_RV rv; \
+ if ((rv = sftk_fipsCheck()) != CKR_OK) return rv;
+
+#define SFTK_FIPSFATALCHECK() \
+ if (sftk_fatalError) return CKR_DEVICE_ERROR;
+
+
+/* grab an attribute out of a raw template */
+void *
+fc_getAttribute(CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount, CK_ATTRIBUTE_TYPE type)
+{
+ int i;
+
+ for (i=0; i < (int) ulCount; i++) {
+ if (pTemplate[i].type == type) {
+ return pTemplate[i].pValue;
+ }
+ }
+ return NULL;
+}
+
+
+#define __PASTE(x,y) x##y
+
+/* ------------- forward declare all the NSC_ functions ------------- */
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+#define CK_PKCS11_FUNCTION_INFO(name) CK_RV __PASTE(NS,name)
+#define CK_NEED_ARG_LIST 1
+
+#include "pkcs11f.h"
+
+/* ------------- forward declare all the FIPS functions ------------- */
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+#define CK_PKCS11_FUNCTION_INFO(name) CK_RV __PASTE(F,name)
+#define CK_NEED_ARG_LIST 1
+
+#include "pkcs11f.h"
+
+/* ------------- build the CK_CRYPTO_TABLE ------------------------- */
+static CK_FUNCTION_LIST sftk_fipsTable = {
+ { 1, 10 },
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+#define CK_PKCS11_FUNCTION_INFO(name) __PASTE(F,name),
+
+
+#include "pkcs11f.h"
+
+};
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+/* CKO_NOT_A_KEY can be any object class that's not a key object. */
+#define CKO_NOT_A_KEY CKO_DATA
+
+#define SFTK_IS_KEY_OBJECT(objClass) \
+ (((objClass) == CKO_PUBLIC_KEY) || \
+ ((objClass) == CKO_PRIVATE_KEY) || \
+ ((objClass) == CKO_SECRET_KEY))
+
+#define SFTK_IS_NONPUBLIC_KEY_OBJECT(objClass) \
+ (((objClass) == CKO_PRIVATE_KEY) || ((objClass) == CKO_SECRET_KEY))
+
+static CK_RV
+sftk_get_object_class_and_fipsCheck(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_OBJECT_CLASS *pObjClass)
+{
+ CK_RV rv;
+ CK_ATTRIBUTE class;
+ class.type = CKA_CLASS;
+ class.pValue = pObjClass;
+ class.ulValueLen = sizeof(*pObjClass);
+ rv = NSC_GetAttributeValue(hSession, hObject, &class, 1);
+ if ((rv == CKR_OK) && SFTK_IS_NONPUBLIC_KEY_OBJECT(*pObjClass)) {
+ rv = sftk_fipsCheck();
+ }
+ return rv;
+}
+
+/**********************************************************************
+ *
+ * FIPS 140 auditable event logging
+ *
+ **********************************************************************/
+
+PRBool sftk_audit_enabled = PR_FALSE;
+
+/*
+ * Each audit record must have the following information:
+ * - Date and time of the event
+ * - Type of event
+ * - user (subject) identity
+ * - outcome (success or failure) of the event
+ * - process ID
+ * - name (ID) of the object
+ * - for changes to data (except for authentication data and CSPs), the new
+ * and old values of the data
+ * - for authentication attempts, the origin of the attempt (e.g., terminal
+ * identifier)
+ * - for assuming a role, the type of role, and the location of the request
+ */
+void
+sftk_LogAuditMessage(NSSAuditSeverity severity, const char *msg)
+{
+#ifdef NSS_AUDIT_WITH_SYSLOG
+ int level;
+
+ switch (severity) {
+ case NSS_AUDIT_ERROR:
+ level = LOG_ERR;
+ break;
+ case NSS_AUDIT_WARNING:
+ level = LOG_WARNING;
+ break;
+ default:
+ level = LOG_INFO;
+ break;
+ }
+ /* timestamp is provided by syslog in the message header */
+ syslog(level | LOG_USER /* facility */,
+ "NSS " SOFTOKEN_LIB_NAME "[pid=%d uid=%d]: %s",
+ (int)getpid(), (int)getuid(), msg);
+#ifdef LINUX
+ if (pthread_once(&libaudit_once_control, libaudit_init) != 0) {
+ return;
+ }
+ if (libaudit_handle) {
+ int audit_fd;
+ int result = (severity != NSS_AUDIT_ERROR); /* 1=success; 0=failed */
+ char *message = PR_smprintf("NSS " SOFTOKEN_LIB_NAME ": %s", msg);
+ if (!message) {
+ return;
+ }
+ audit_fd = audit_open_func();
+ if (audit_fd < 0) {
+ PR_smprintf_free(message);
+ return;
+ }
+ if (audit_log_user_message_func) {
+ audit_log_user_message_func(audit_fd, AUDIT_USER, message,
+ NULL, NULL, NULL, result);
+ } else {
+ audit_send_user_message_func(audit_fd, AUDIT_USER, message);
+ }
+ audit_close_func(audit_fd);
+ PR_smprintf_free(message);
+ }
+#endif /* LINUX */
+#ifdef SOLARIS
+ {
+ int rd;
+ char *message = PR_smprintf("NSS " SOFTOKEN_LIB_NAME ": %s", msg);
+
+ if (!message) {
+ return;
+ }
+
+ /* open the record descriptor */
+ if ((rd = au_open()) == -1) {
+ PR_smprintf_free(message);
+ return;
+ }
+
+ /* write the audit tokens to the audit record */
+ if (au_write(rd, au_to_text(message))) {
+ (void)au_close(rd, AU_TO_NO_WRITE, AUE_FIPS_AUDIT);
+ PR_smprintf_free(message);
+ return;
+ }
+
+ /* close the record and send it to the audit trail */
+ (void)au_close(rd, AU_TO_WRITE, AUE_FIPS_AUDIT);
+
+ PR_smprintf_free(message);
+ }
+#endif /* SOLARIS */
+#else
+ /* do nothing */
+#endif
+}
+
+
+/**********************************************************************
+ *
+ * Start of PKCS 11 functions
+ *
+ **********************************************************************/
+/* return the function list */
+CK_RV FC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList) {
+
+ CHECK_FORK();
+
+ *pFunctionList = &sftk_fipsTable;
+ return CKR_OK;
+}
+
+/* sigh global so pkcs11 can read it */
+PRBool nsf_init = PR_FALSE;
+
+/* FC_Initialize initializes the PKCS #11 library. */
+CK_RV FC_Initialize(CK_VOID_PTR pReserved) {
+ const char *envp;
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ if (nsf_init) {
+ return CKR_CRYPTOKI_ALREADY_INITIALIZED;
+ }
+
+ if ((envp = PR_GetEnv("NSS_ENABLE_AUDIT")) != NULL) {
+ sftk_audit_enabled = (atoi(envp) == 1);
+ }
+
+ crv = nsc_CommonInitialize(pReserved, PR_TRUE);
+
+ /* not an 'else' rv can be set by either SFTK_LowInit or SFTK_SlotInit*/
+ if (crv != CKR_OK) {
+ sftk_fatalError = PR_TRUE;
+ return crv;
+ }
+
+ sftk_fatalError = PR_FALSE; /* any error has been reset */
+
+ crv = sftk_fipsPowerUpSelfTest();
+ if (crv != CKR_OK) {
+ nsc_CommonFinalize(NULL, PR_TRUE);
+ sftk_fatalError = PR_TRUE;
+ if (sftk_audit_enabled) {
+ char msg[128];
+ PR_snprintf(msg,sizeof msg,
+ "C_Initialize()=0x%08lX "
+ "power-up self-tests failed",
+ (PRUint32)crv);
+ sftk_LogAuditMessage(NSS_AUDIT_ERROR, msg);
+ }
+ return crv;
+ }
+ nsf_init = PR_TRUE;
+
+ return CKR_OK;
+}
+
+/*FC_Finalize indicates that an application is done with the PKCS #11 library.*/
+CK_RV FC_Finalize (CK_VOID_PTR pReserved) {
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ if (!nsf_init) {
+ return CKR_OK;
+ }
+ crv = nsc_CommonFinalize (pReserved, PR_TRUE);
+ nsf_init = (PRBool) !(crv == CKR_OK);
+ return crv;
+}
+
+
+/* FC_GetInfo returns general information about PKCS #11. */
+CK_RV FC_GetInfo(CK_INFO_PTR pInfo) {
+ CHECK_FORK();
+
+ return NSC_GetInfo(pInfo);
+}
+
+/* FC_GetSlotList obtains a list of slots in the system. */
+CK_RV FC_GetSlotList(CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) {
+ CHECK_FORK();
+
+ return nsc_CommonGetSlotList(tokenPresent,pSlotList,pulCount,
+ NSC_FIPS_MODULE);
+}
+
+/* FC_GetSlotInfo obtains information about a particular slot in the system. */
+CK_RV FC_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) {
+ CHECK_FORK();
+
+ return NSC_GetSlotInfo(slotID,pInfo);
+}
+
+
+/*FC_GetTokenInfo obtains information about a particular token in the system.*/
+ CK_RV FC_GetTokenInfo(CK_SLOT_ID slotID,CK_TOKEN_INFO_PTR pInfo) {
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ crv = NSC_GetTokenInfo(slotID,pInfo);
+ if (crv == CKR_OK)
+ pInfo->flags |= CKF_LOGIN_REQUIRED;
+ return crv;
+
+}
+
+
+
+/*FC_GetMechanismList obtains a list of mechanism types supported by a token.*/
+ CK_RV FC_GetMechanismList(CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pusCount) {
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ if (slotID == FIPS_SLOT_ID) slotID = NETSCAPE_SLOT_ID;
+ /* FIPS Slot supports all functions */
+ return NSC_GetMechanismList(slotID,pMechanismList,pusCount);
+}
+
+
+/* FC_GetMechanismInfo obtains information about a particular mechanism
+ * possibly supported by a token. */
+ CK_RV FC_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo) {
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ if (slotID == FIPS_SLOT_ID) slotID = NETSCAPE_SLOT_ID;
+ /* FIPS Slot supports all functions */
+ return NSC_GetMechanismInfo(slotID,type,pInfo);
+}
+
+
+/* FC_InitToken initializes a token. */
+ CK_RV FC_InitToken(CK_SLOT_ID slotID,CK_CHAR_PTR pPin,
+ CK_ULONG usPinLen,CK_CHAR_PTR pLabel) {
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ crv = NSC_InitToken(slotID,pPin,usPinLen,pLabel);
+ if (sftk_audit_enabled) {
+ char msg[128];
+ NSSAuditSeverity severity = (crv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+ /* pLabel points to a 32-byte label, which is not null-terminated */
+ PR_snprintf(msg,sizeof msg,
+ "C_InitToken(slotID=%lu, pLabel=\"%.32s\")=0x%08lX",
+ (PRUint32)slotID,pLabel,(PRUint32)crv);
+ sftk_LogAuditMessage(severity, msg);
+ }
+ return crv;
+}
+
+
+/* FC_InitPIN initializes the normal user's PIN. */
+ CK_RV FC_InitPIN(CK_SESSION_HANDLE hSession,
+ CK_CHAR_PTR pPin, CK_ULONG ulPinLen) {
+ CK_RV rv;
+
+ CHECK_FORK();
+
+ if (sftk_fatalError) return CKR_DEVICE_ERROR;
+ if ((rv = sftk_newPinCheck(pPin,ulPinLen)) == CKR_OK) {
+ rv = NSC_InitPIN(hSession,pPin,ulPinLen);
+ }
+ if (sftk_audit_enabled) {
+ char msg[128];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+ PR_snprintf(msg,sizeof msg,
+ "C_InitPIN(hSession=0x%08lX)=0x%08lX",
+ (PRUint32)hSession,(PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+ }
+ return rv;
+}
+
+
+/* FC_SetPIN modifies the PIN of user that is currently logged in. */
+/* NOTE: This is only valid for the PRIVATE_KEY_SLOT */
+ CK_RV FC_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin,
+ CK_ULONG usOldLen, CK_CHAR_PTR pNewPin, CK_ULONG usNewLen) {
+ CK_RV rv;
+
+ CHECK_FORK();
+
+ if ((rv = sftk_fipsCheck()) == CKR_OK &&
+ (rv = sftk_newPinCheck(pNewPin,usNewLen)) == CKR_OK) {
+ rv = NSC_SetPIN(hSession,pOldPin,usOldLen,pNewPin,usNewLen);
+ }
+ if (sftk_audit_enabled) {
+ char msg[128];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+ PR_snprintf(msg,sizeof msg,
+ "C_SetPIN(hSession=0x%08lX)=0x%08lX",
+ (PRUint32)hSession,(PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+ }
+ return rv;
+}
+
+/* FC_OpenSession opens a session between an application and a token. */
+ CK_RV FC_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags,
+ CK_VOID_PTR pApplication,CK_NOTIFY Notify,CK_SESSION_HANDLE_PTR phSession) {
+ SFTK_FIPSFATALCHECK();
+
+ CHECK_FORK();
+
+ return NSC_OpenSession(slotID,flags,pApplication,Notify,phSession);
+}
+
+
+/* FC_CloseSession closes a session between an application and a token. */
+ CK_RV FC_CloseSession(CK_SESSION_HANDLE hSession) {
+ CHECK_FORK();
+
+ return NSC_CloseSession(hSession);
+}
+
+
+/* FC_CloseAllSessions closes all sessions with a token. */
+ CK_RV FC_CloseAllSessions (CK_SLOT_ID slotID) {
+
+ CHECK_FORK();
+
+ return NSC_CloseAllSessions (slotID);
+}
+
+
+/* FC_GetSessionInfo obtains information about the session. */
+ CK_RV FC_GetSessionInfo(CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo) {
+ CK_RV rv;
+ SFTK_FIPSFATALCHECK();
+
+ CHECK_FORK();
+
+ rv = NSC_GetSessionInfo(hSession,pInfo);
+ if (rv == CKR_OK) {
+ if ((isLoggedIn) && (pInfo->state == CKS_RO_PUBLIC_SESSION)) {
+ pInfo->state = CKS_RO_USER_FUNCTIONS;
+ }
+ if ((isLoggedIn) && (pInfo->state == CKS_RW_PUBLIC_SESSION)) {
+ pInfo->state = CKS_RW_USER_FUNCTIONS;
+ }
+ }
+ return rv;
+}
+
+/* FC_Login logs a user into a token. */
+ CK_RV FC_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
+ CK_CHAR_PTR pPin, CK_ULONG usPinLen) {
+ CK_RV rv;
+ PRBool successful;
+ if (sftk_fatalError) return CKR_DEVICE_ERROR;
+ rv = NSC_Login(hSession,userType,pPin,usPinLen);
+ successful = (rv == CKR_OK) || (rv == CKR_USER_ALREADY_LOGGED_IN);
+ if (successful)
+ isLoggedIn = PR_TRUE;
+ if (sftk_audit_enabled) {
+ char msg[128];
+ NSSAuditSeverity severity;
+ severity = successful ? NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+ PR_snprintf(msg,sizeof msg,
+ "C_Login(hSession=0x%08lX, userType=%lu)=0x%08lX",
+ (PRUint32)hSession,(PRUint32)userType,(PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+ }
+ return rv;
+}
+
+/* FC_Logout logs a user out from a token. */
+ CK_RV FC_Logout(CK_SESSION_HANDLE hSession) {
+ CK_RV rv;
+
+ CHECK_FORK();
+
+ if ((rv = sftk_fipsCheck()) == CKR_OK) {
+ rv = NSC_Logout(hSession);
+ isLoggedIn = PR_FALSE;
+ }
+ if (sftk_audit_enabled) {
+ char msg[128];
+ NSSAuditSeverity severity = (rv == CKR_OK) ?
+ NSS_AUDIT_INFO : NSS_AUDIT_ERROR;
+ PR_snprintf(msg,sizeof msg,
+ "C_Logout(hSession=0x%08lX)=0x%08lX",
+ (PRUint32)hSession,(PRUint32)rv);
+ sftk_LogAuditMessage(severity, msg);
+ }
+ return rv;
+}
+
+
+/* FC_CreateObject creates a new object. */
+ CK_RV FC_CreateObject(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject) {
+ CK_OBJECT_CLASS * classptr;
+
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ classptr = (CK_OBJECT_CLASS *)fc_getAttribute(pTemplate,ulCount,CKA_CLASS);
+ if (classptr == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ /* FIPS can't create keys from raw key material */
+ if (SFTK_IS_NONPUBLIC_KEY_OBJECT(*classptr)) {
+ rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ } else {
+ rv = NSC_CreateObject(hSession,pTemplate,ulCount,phObject);
+ }
+ if (sftk_audit_enabled && SFTK_IS_KEY_OBJECT(*classptr)) {
+ sftk_AuditCreateObject(hSession,pTemplate,ulCount,phObject,rv);
+ }
+ return rv;
+}
+
+
+
+
+
+/* FC_CopyObject copies an object, creating a new object for the copy. */
+ CK_RV FC_CopyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject) {
+ CK_RV rv;
+ CK_OBJECT_CLASS objClass = CKO_NOT_A_KEY;
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ rv = sftk_get_object_class_and_fipsCheck(hSession, hObject, &objClass);
+ if (rv == CKR_OK) {
+ rv = NSC_CopyObject(hSession,hObject,pTemplate,ulCount,phNewObject);
+ }
+ if (sftk_audit_enabled && SFTK_IS_KEY_OBJECT(objClass)) {
+ sftk_AuditCopyObject(hSession,
+ hObject,pTemplate,ulCount,phNewObject,rv);
+ }
+ return rv;
+}
+
+
+/* FC_DestroyObject destroys an object. */
+ CK_RV FC_DestroyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject) {
+ CK_RV rv;
+ CK_OBJECT_CLASS objClass = CKO_NOT_A_KEY;
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ rv = sftk_get_object_class_and_fipsCheck(hSession, hObject, &objClass);
+ if (rv == CKR_OK) {
+ rv = NSC_DestroyObject(hSession,hObject);
+ }
+ if (sftk_audit_enabled && SFTK_IS_KEY_OBJECT(objClass)) {
+ sftk_AuditDestroyObject(hSession,hObject,rv);
+ }
+ return rv;
+}
+
+
+/* FC_GetObjectSize gets the size of an object in bytes. */
+ CK_RV FC_GetObjectSize(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) {
+ CK_RV rv;
+ CK_OBJECT_CLASS objClass = CKO_NOT_A_KEY;
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ rv = sftk_get_object_class_and_fipsCheck(hSession, hObject, &objClass);
+ if (rv == CKR_OK) {
+ rv = NSC_GetObjectSize(hSession, hObject, pulSize);
+ }
+ if (sftk_audit_enabled && SFTK_IS_KEY_OBJECT(objClass)) {
+ sftk_AuditGetObjectSize(hSession, hObject, pulSize, rv);
+ }
+ return rv;
+}
+
+
+/* FC_GetAttributeValue obtains the value of one or more object attributes. */
+ CK_RV FC_GetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount) {
+ CK_RV rv;
+ CK_OBJECT_CLASS objClass = CKO_NOT_A_KEY;
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ rv = sftk_get_object_class_and_fipsCheck(hSession, hObject, &objClass);
+ if (rv == CKR_OK) {
+ rv = NSC_GetAttributeValue(hSession,hObject,pTemplate,ulCount);
+ }
+ if (sftk_audit_enabled && SFTK_IS_KEY_OBJECT(objClass)) {
+ sftk_AuditGetAttributeValue(hSession,hObject,pTemplate,ulCount,rv);
+ }
+ return rv;
+}
+
+
+/* FC_SetAttributeValue modifies the value of one or more object attributes */
+ CK_RV FC_SetAttributeValue (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount) {
+ CK_RV rv;
+ CK_OBJECT_CLASS objClass = CKO_NOT_A_KEY;
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ rv = sftk_get_object_class_and_fipsCheck(hSession, hObject, &objClass);
+ if (rv == CKR_OK) {
+ rv = NSC_SetAttributeValue(hSession,hObject,pTemplate,ulCount);
+ }
+ if (sftk_audit_enabled && SFTK_IS_KEY_OBJECT(objClass)) {
+ sftk_AuditSetAttributeValue(hSession,hObject,pTemplate,ulCount,rv);
+ }
+ return rv;
+}
+
+
+
+/* FC_FindObjectsInit initializes a search for token and session objects
+ * that match a template. */
+ CK_RV FC_FindObjectsInit(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,CK_ULONG usCount) {
+ /* let publically readable object be found */
+ unsigned int i;
+ CK_RV rv;
+ PRBool needLogin = PR_FALSE;
+
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+
+ for (i=0; i < usCount; i++) {
+ CK_OBJECT_CLASS class;
+ if (pTemplate[i].type != CKA_CLASS) {
+ continue;
+ }
+ if (pTemplate[i].ulValueLen != sizeof(CK_OBJECT_CLASS)) {
+ continue;
+ }
+ if (pTemplate[i].pValue == NULL) {
+ continue;
+ }
+ class = *(CK_OBJECT_CLASS *)pTemplate[i].pValue;
+ if ((class == CKO_PRIVATE_KEY) || (class == CKO_SECRET_KEY)) {
+ needLogin = PR_TRUE;
+ break;
+ }
+ }
+ if (needLogin) {
+ if ((rv = sftk_fipsCheck()) != CKR_OK) return rv;
+ }
+ return NSC_FindObjectsInit(hSession,pTemplate,usCount);
+}
+
+
+/* FC_FindObjects continues a search for token and session objects
+ * that match a template, obtaining additional object handles. */
+ CK_RV FC_FindObjects(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,CK_ULONG usMaxObjectCount,
+ CK_ULONG_PTR pusObjectCount) {
+ CHECK_FORK();
+
+ /* let publically readable object be found */
+ SFTK_FIPSFATALCHECK();
+ return NSC_FindObjects(hSession,phObject,usMaxObjectCount,
+ pusObjectCount);
+}
+
+
+/*
+ ************** Crypto Functions: Encrypt ************************
+ */
+
+/* FC_EncryptInit initializes an encryption operation. */
+ CK_RV FC_EncryptInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_EncryptInit(hSession,pMechanism,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditCryptInit("Encrypt",hSession,pMechanism,hKey,rv);
+ }
+ return rv;
+}
+
+/* FC_Encrypt encrypts single-part data. */
+ CK_RV FC_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+ CK_ULONG usDataLen, CK_BYTE_PTR pEncryptedData,
+ CK_ULONG_PTR pusEncryptedDataLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_Encrypt(hSession,pData,usDataLen,pEncryptedData,
+ pusEncryptedDataLen);
+}
+
+
+/* FC_EncryptUpdate continues a multiple-part encryption operation. */
+ CK_RV FC_EncryptUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart, CK_ULONG usPartLen, CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pusEncryptedPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_EncryptUpdate(hSession,pPart,usPartLen,pEncryptedPart,
+ pusEncryptedPartLen);
+}
+
+
+/* FC_EncryptFinal finishes a multiple-part encryption operation. */
+ CK_RV FC_EncryptFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pusLastEncryptedPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_EncryptFinal(hSession,pLastEncryptedPart,
+ pusLastEncryptedPartLen);
+}
+
+/*
+ ************** Crypto Functions: Decrypt ************************
+ */
+
+
+/* FC_DecryptInit initializes a decryption operation. */
+ CK_RV FC_DecryptInit( CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_DecryptInit(hSession,pMechanism,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditCryptInit("Decrypt",hSession,pMechanism,hKey,rv);
+ }
+ return rv;
+}
+
+/* FC_Decrypt decrypts encrypted data in a single part. */
+ CK_RV FC_Decrypt(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData,CK_ULONG usEncryptedDataLen,CK_BYTE_PTR pData,
+ CK_ULONG_PTR pusDataLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_Decrypt(hSession,pEncryptedData,usEncryptedDataLen,pData,
+ pusDataLen);
+}
+
+
+/* FC_DecryptUpdate continues a multiple-part decryption operation. */
+ CK_RV FC_DecryptUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart, CK_ULONG usEncryptedPartLen,
+ CK_BYTE_PTR pPart, CK_ULONG_PTR pusPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_DecryptUpdate(hSession,pEncryptedPart,usEncryptedPartLen,
+ pPart,pusPartLen);
+}
+
+
+/* FC_DecryptFinal finishes a multiple-part decryption operation. */
+ CK_RV FC_DecryptFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastPart, CK_ULONG_PTR pusLastPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_DecryptFinal(hSession,pLastPart,pusLastPartLen);
+}
+
+
+/*
+ ************** Crypto Functions: Digest (HASH) ************************
+ */
+
+/* FC_DigestInit initializes a message-digesting operation. */
+ CK_RV FC_DigestInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism) {
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_DigestInit(hSession, pMechanism);
+}
+
+
+/* FC_Digest digests data in a single part. */
+ CK_RV FC_Digest(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData, CK_ULONG usDataLen, CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pusDigestLen) {
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_Digest(hSession,pData,usDataLen,pDigest,pusDigestLen);
+}
+
+
+/* FC_DigestUpdate continues a multiple-part message-digesting operation. */
+ CK_RV FC_DigestUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+ CK_ULONG usPartLen) {
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_DigestUpdate(hSession,pPart,usPartLen);
+}
+
+
+/* FC_DigestFinal finishes a multiple-part message-digesting operation. */
+ CK_RV FC_DigestFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pusDigestLen) {
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_DigestFinal(hSession,pDigest,pusDigestLen);
+}
+
+
+/*
+ ************** Crypto Functions: Sign ************************
+ */
+
+/* FC_SignInit initializes a signature (private key encryption) operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+ CK_RV FC_SignInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_SignInit(hSession,pMechanism,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditCryptInit("Sign",hSession,pMechanism,hKey,rv);
+ }
+ return rv;
+}
+
+
+/* FC_Sign signs (encrypts with private key) data in a single part,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+ CK_RV FC_Sign(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,CK_ULONG usDataLen,CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pusSignatureLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_Sign(hSession,pData,usDataLen,pSignature,pusSignatureLen);
+}
+
+
+/* FC_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+ CK_RV FC_SignUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+ CK_ULONG usPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_SignUpdate(hSession,pPart,usPartLen);
+}
+
+
+/* FC_SignFinal finishes a multiple-part signature operation,
+ * returning the signature. */
+ CK_RV FC_SignFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pusSignatureLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_SignFinal(hSession,pSignature,pusSignatureLen);
+}
+
+/*
+ ************** Crypto Functions: Sign Recover ************************
+ */
+/* FC_SignRecoverInit initializes a signature operation,
+ * where the (digest) data can be recovered from the signature.
+ * E.g. encryption with the user's private key */
+ CK_RV FC_SignRecoverInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_SignRecoverInit(hSession,pMechanism,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditCryptInit("SignRecover",hSession,pMechanism,hKey,rv);
+ }
+ return rv;
+}
+
+
+/* FC_SignRecover signs data in a single operation
+ * where the (digest) data can be recovered from the signature.
+ * E.g. encryption with the user's private key */
+ CK_RV FC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+ CK_ULONG usDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pusSignatureLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_SignRecover(hSession,pData,usDataLen,pSignature,pusSignatureLen);
+}
+
+/*
+ ************** Crypto Functions: verify ************************
+ */
+
+/* FC_VerifyInit initializes a verification operation,
+ * where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature (e.g. DSA) */
+ CK_RV FC_VerifyInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_VerifyInit(hSession,pMechanism,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditCryptInit("Verify",hSession,pMechanism,hKey,rv);
+ }
+ return rv;
+}
+
+
+/* FC_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+ CK_RV FC_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+ CK_ULONG usDataLen, CK_BYTE_PTR pSignature, CK_ULONG usSignatureLen) {
+ /* make sure we're legal */
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_Verify(hSession,pData,usDataLen,pSignature,usSignatureLen);
+}
+
+
+/* FC_VerifyUpdate continues a multiple-part verification operation,
+ * where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+ CK_RV FC_VerifyUpdate( CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+ CK_ULONG usPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_VerifyUpdate(hSession,pPart,usPartLen);
+}
+
+
+/* FC_VerifyFinal finishes a multiple-part verification operation,
+ * checking the signature. */
+ CK_RV FC_VerifyFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,CK_ULONG usSignatureLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_VerifyFinal(hSession,pSignature,usSignatureLen);
+}
+
+/*
+ ************** Crypto Functions: Verify Recover ************************
+ */
+
+/* FC_VerifyRecoverInit initializes a signature verification operation,
+ * where the data is recovered from the signature.
+ * E.g. Decryption with the user's public key */
+ CK_RV FC_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_VerifyRecoverInit(hSession,pMechanism,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditCryptInit("VerifyRecover",hSession,pMechanism,hKey,rv);
+ }
+ return rv;
+}
+
+
+/* FC_VerifyRecover verifies a signature in a single-part operation,
+ * where the data is recovered from the signature.
+ * E.g. Decryption with the user's public key */
+ CK_RV FC_VerifyRecover(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,CK_ULONG usSignatureLen,
+ CK_BYTE_PTR pData,CK_ULONG_PTR pusDataLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_VerifyRecover(hSession,pSignature,usSignatureLen,pData,
+ pusDataLen);
+}
+
+/*
+ **************************** Key Functions: ************************
+ */
+
+/* FC_GenerateKey generates a secret key, creating a new key object. */
+ CK_RV FC_GenerateKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey) {
+ CK_BBOOL *boolptr;
+
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ /* all secret keys must be sensitive, if the upper level code tries to say
+ * otherwise, reject it. */
+ boolptr = (CK_BBOOL *) fc_getAttribute(pTemplate, ulCount, CKA_SENSITIVE);
+ if (boolptr != NULL) {
+ if (!(*boolptr)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ }
+
+ rv = NSC_GenerateKey(hSession,pMechanism,pTemplate,ulCount,phKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditGenerateKey(hSession,pMechanism,pTemplate,ulCount,phKey,rv);
+ }
+ return rv;
+}
+
+
+/* FC_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects. */
+ CK_RV FC_GenerateKeyPair (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG usPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG usPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey) {
+ CK_BBOOL *boolptr;
+ CK_RV crv;
+
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+
+ /* all private keys must be sensitive, if the upper level code tries to say
+ * otherwise, reject it. */
+ boolptr = (CK_BBOOL *) fc_getAttribute(pPrivateKeyTemplate,
+ usPrivateKeyAttributeCount, CKA_SENSITIVE);
+ if (boolptr != NULL) {
+ if (!(*boolptr)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ }
+ crv = NSC_GenerateKeyPair (hSession,pMechanism,pPublicKeyTemplate,
+ usPublicKeyAttributeCount,pPrivateKeyTemplate,
+ usPrivateKeyAttributeCount,phPublicKey,phPrivateKey);
+ if (crv == CKR_GENERAL_ERROR) {
+ /* pairwise consistency check failed. */
+ sftk_fatalError = PR_TRUE;
+ }
+ if (sftk_audit_enabled) {
+ sftk_AuditGenerateKeyPair(hSession,pMechanism,pPublicKeyTemplate,
+ usPublicKeyAttributeCount,pPrivateKeyTemplate,
+ usPrivateKeyAttributeCount,phPublicKey,phPrivateKey,crv);
+ }
+ return crv;
+}
+
+
+/* FC_WrapKey wraps (i.e., encrypts) a key. */
+ CK_RV FC_WrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey,
+ CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_WrapKey(hSession,pMechanism,hWrappingKey,hKey,pWrappedKey,
+ pulWrappedKeyLen);
+ if (sftk_audit_enabled) {
+ sftk_AuditWrapKey(hSession,pMechanism,hWrappingKey,hKey,pWrappedKey,
+ pulWrappedKeyLen,rv);
+ }
+ return rv;
+}
+
+
+/* FC_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */
+ CK_RV FC_UnwrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey) {
+ CK_BBOOL *boolptr;
+
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ /* all secret keys must be sensitive, if the upper level code tries to say
+ * otherwise, reject it. */
+ boolptr = (CK_BBOOL *) fc_getAttribute(pTemplate,
+ ulAttributeCount, CKA_SENSITIVE);
+ if (boolptr != NULL) {
+ if (!(*boolptr)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ }
+ rv = NSC_UnwrapKey(hSession,pMechanism,hUnwrappingKey,pWrappedKey,
+ ulWrappedKeyLen,pTemplate,ulAttributeCount,phKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditUnwrapKey(hSession,pMechanism,hUnwrappingKey,pWrappedKey,
+ ulWrappedKeyLen,pTemplate,ulAttributeCount,phKey,rv);
+ }
+ return rv;
+}
+
+
+/* FC_DeriveKey derives a key from a base key, creating a new key object. */
+ CK_RV FC_DeriveKey( CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey) {
+ CK_BBOOL *boolptr;
+
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ /* all secret keys must be sensitive, if the upper level code tries to say
+ * otherwise, reject it. */
+ boolptr = (CK_BBOOL *) fc_getAttribute(pTemplate,
+ ulAttributeCount, CKA_SENSITIVE);
+ if (boolptr != NULL) {
+ if (!(*boolptr)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ }
+ rv = NSC_DeriveKey(hSession,pMechanism,hBaseKey,pTemplate,
+ ulAttributeCount, phKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditDeriveKey(hSession,pMechanism,hBaseKey,pTemplate,
+ ulAttributeCount,phKey,rv);
+ }
+ return rv;
+}
+
+/*
+ **************************** Radom Functions: ************************
+ */
+
+/* FC_SeedRandom mixes additional seed material into the token's random number
+ * generator. */
+ CK_RV FC_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed,
+ CK_ULONG usSeedLen) {
+ CK_RV crv;
+
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ crv = NSC_SeedRandom(hSession,pSeed,usSeedLen);
+ if (crv != CKR_OK) {
+ sftk_fatalError = PR_TRUE;
+ }
+ return crv;
+}
+
+
+/* FC_GenerateRandom generates random data. */
+ CK_RV FC_GenerateRandom(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen) {
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ SFTK_FIPSFATALCHECK();
+ crv = NSC_GenerateRandom(hSession,pRandomData,ulRandomLen);
+ if (crv != CKR_OK) {
+ sftk_fatalError = PR_TRUE;
+ if (sftk_audit_enabled) {
+ char msg[128];
+ PR_snprintf(msg,sizeof msg,
+ "C_GenerateRandom(hSession=0x%08lX, pRandomData=%p, "
+ "ulRandomLen=%lu)=0x%08lX "
+ "self-test: continuous RNG test failed",
+ (PRUint32)hSession,pRandomData,
+ (PRUint32)ulRandomLen,(PRUint32)crv);
+ sftk_LogAuditMessage(NSS_AUDIT_ERROR, msg);
+ }
+ }
+ return crv;
+}
+
+
+/* FC_GetFunctionStatus obtains an updated status of a function running
+ * in parallel with an application. */
+ CK_RV FC_GetFunctionStatus(CK_SESSION_HANDLE hSession) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_GetFunctionStatus(hSession);
+}
+
+
+/* FC_CancelFunction cancels a function running in parallel */
+ CK_RV FC_CancelFunction(CK_SESSION_HANDLE hSession) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_CancelFunction(hSession);
+}
+
+/*
+ **************************** Version 1.1 Functions: ************************
+ */
+
+/* FC_GetOperationState saves the state of the cryptographic
+ *operation in a session. */
+CK_RV FC_GetOperationState(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) {
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_GetOperationState(hSession,pOperationState,pulOperationStateLen);
+}
+
+
+/* FC_SetOperationState restores the state of the cryptographic operation
+ * in a session. */
+CK_RV FC_SetOperationState(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen,
+ CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) {
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_SetOperationState(hSession,pOperationState,ulOperationStateLen,
+ hEncryptionKey,hAuthenticationKey);
+}
+
+/* FC_FindObjectsFinal finishes a search for token and session objects. */
+CK_RV FC_FindObjectsFinal(CK_SESSION_HANDLE hSession) {
+ /* let publically readable object be found */
+ SFTK_FIPSFATALCHECK();
+ CHECK_FORK();
+
+ return NSC_FindObjectsFinal(hSession);
+}
+
+
+/* Dual-function cryptographic operations */
+
+/* FC_DigestEncryptUpdate continues a multiple-part digesting and encryption
+ * operation. */
+CK_RV FC_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_DigestEncryptUpdate(hSession,pPart,ulPartLen,pEncryptedPart,
+ pulEncryptedPartLen);
+}
+
+
+/* FC_DecryptDigestUpdate continues a multiple-part decryption and digesting
+ * operation. */
+CK_RV FC_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_DecryptDigestUpdate(hSession, pEncryptedPart,ulEncryptedPartLen,
+ pPart,pulPartLen);
+}
+
+/* FC_SignEncryptUpdate continues a multiple-part signing and encryption
+ * operation. */
+CK_RV FC_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_SignEncryptUpdate(hSession,pPart,ulPartLen,pEncryptedPart,
+ pulEncryptedPartLen);
+}
+
+/* FC_DecryptVerifyUpdate continues a multiple-part decryption and verify
+ * operation. */
+CK_RV FC_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ return NSC_DecryptVerifyUpdate(hSession,pEncryptedData,ulEncryptedDataLen,
+ pData,pulDataLen);
+}
+
+
+/* FC_DigestKey continues a multi-part message-digesting operation,
+ * by digesting the value of a secret key as part of the data already digested.
+ */
+CK_RV FC_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) {
+ SFTK_FIPSCHECK();
+ CHECK_FORK();
+
+ rv = NSC_DigestKey(hSession,hKey);
+ if (sftk_audit_enabled) {
+ sftk_AuditDigestKey(hSession,hKey,rv);
+ }
+ return rv;
+}
+
+
+CK_RV FC_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot,
+ CK_VOID_PTR pReserved)
+{
+ CHECK_FORK();
+
+ return NSC_WaitForSlotEvent(flags, pSlot, pReserved);
+}
diff --git a/security/nss/lib/softoken/legacydb/Makefile b/security/nss/lib/softoken/legacydb/Makefile
new file mode 100644
index 000000000..78a7c78c7
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/Makefile
@@ -0,0 +1,82 @@
+#! gmake
+#
+# ***** 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 *****
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include config.mk
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+export:: private_export
+
+# indicates dependency on freebl static lib
+$(SHARED_LIBRARY): $(CRYPTOLIB)
diff --git a/security/nss/lib/softoken/legacydb/cdbhdl.h b/security/nss/lib/softoken/legacydb/cdbhdl.h
new file mode 100644
index 000000000..aec617b73
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/cdbhdl.h
@@ -0,0 +1,85 @@
+/* ***** 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 ***** */
+/*
+ * cdbhdl.h - certificate database handle
+ * private to the certdb module
+ *
+ * $Id$
+ */
+#ifndef _CDBHDL_H_
+#define _CDBHDL_H_
+
+#include "nspr.h"
+#include "mcom_db.h"
+#include "pcertt.h"
+#include "prtypes.h"
+
+/*
+ * Handle structure for open certificate databases
+ */
+struct NSSLOWCERTCertDBHandleStr {
+ DB *permCertDB;
+ PZMonitor *dbMon;
+ PRBool dbVerify;
+ PRInt32 ref; /* reference count */
+};
+
+#ifdef DBM_USING_NSPR
+#define NO_RDONLY PR_RDONLY
+#define NO_RDWR PR_RDWR
+#define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
+#else
+#define NO_RDONLY O_RDONLY
+#define NO_RDWR O_RDWR
+#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
+#endif
+
+typedef DB * (*rdbfunc)(const char *appName, const char *prefix,
+ const char *type, int flags);
+typedef int (*rdbstatusfunc)(void);
+
+#define RDB_FAIL 1
+#define RDB_RETRY 2
+
+DB * rdbopen(const char *appName, const char *prefix,
+ const char *type, int flags, int *status);
+
+DB *dbsopen (const char *dbname , int flags, int mode, DBTYPE type,
+ const void * appData);
+SECStatus db_Copy(DB *dest,DB *src);
+int db_InitComplete(DB *db);
+
+#endif
diff --git a/security/nss/lib/softoken/legacydb/config.mk b/security/nss/lib/softoken/legacydb/config.mk
new file mode 100644
index 000000000..4f1839426
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/config.mk
@@ -0,0 +1,103 @@
+#
+# ***** 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 *****
+
+# $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS)
+CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)freebl.$(LIB_SUFFIX)
+CRYPTODIR=../freebl
+ifdef MOZILLA_SECURITY_BUILD
+ CRYPTOLIB=$(DIST)/lib/$(LIB_PREFIX)crypto.$(LIB_SUFFIX)
+ CRYPTODIR=../crypto
+endif
+
+EXTRA_LIBS += \
+ $(CRYPTOLIB) \
+ $(DIST)/lib/$(LIB_PREFIX)dbm.$(LIB_SUFFIX) \
+ $(NULL)
+
+# can't do this in manifest.mn because OS_TARGET isn't defined there.
+ifeq (,$(filter-out WIN%,$(OS_TARGET)))
+
+# don't want the 32 in the shared library name
+SHARED_LIBRARY = $(OBJDIR)/$(DLL_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION).$(DLL_SUFFIX)
+IMPORT_LIBRARY = $(OBJDIR)/$(IMPORT_LIB_PREFIX)$(LIBRARY_NAME)$(LIBRARY_VERSION)$(IMPORT_LIB_SUFFIX)
+
+RES = $(OBJDIR)/$(LIBRARY_NAME).res
+RESNAME = $(LIBRARY_NAME).rc
+
+ifdef NS_USE_GCC
+EXTRA_SHARED_LIBS += \
+ -L$(DIST)/lib \
+ -lnssutil3 \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+else # ! NS_USE_GCC
+
+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 \
+ $(DIST)/lib/nssutil3.lib \
+ $(NULL)
+endif # NS_USE_GCC
+
+else
+
+# $(PROGRAM) has NO explicit dependencies on $(EXTRA_SHARED_LIBS)
+# $(EXTRA_SHARED_LIBS) come before $(OS_LIBS), except on AIX.
+EXTRA_SHARED_LIBS += \
+ -L$(DIST)/lib \
+ -lnssutil3 \
+ -L$(NSPR_LIB_DIR) \
+ -lplc4 \
+ -lplds4 \
+ -lnspr4 \
+ $(NULL)
+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'
+OS_LIBS += -lbsm
+endif
+
+ifeq ($(OS_TARGET),WINCE)
+DEFINES += -DDBM_USING_NSPR
+endif
diff --git a/security/nss/lib/softoken/legacydb/dbmshim.c b/security/nss/lib/softoken/legacydb/dbmshim.c
new file mode 100644
index 000000000..9d512ac41
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/dbmshim.c
@@ -0,0 +1,647 @@
+/* ***** 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 ***** */
+
+/*
+ * Berkeley DB 1.85 Shim code to handle blobs.
+ *
+ * $Id$
+ */
+#include "mcom_db.h"
+#include "secitem.h"
+#include "nssb64.h"
+#include "blapi.h"
+#include "secerr.h"
+
+#include "lgdb.h"
+
+/*
+ * Blob block:
+ * Byte 0 CERTDB Version -+ -+
+ * Byte 1 certDBEntryTypeBlob | BLOB_HEAD_LEN |
+ * Byte 2 flags (always '0'); | |
+ * Byte 3 reserved (always '0'); -+ |
+ * Byte 4 LSB length | <--BLOB_LENGTH_START | BLOB_BUF_LEN
+ * Byte 5 . | |
+ * Byte 6 . | BLOB_LENGTH_LEN |
+ * Byte 7 MSB length | |
+ * Byte 8 blob_filename -+ -+ <-- BLOB_NAME_START |
+ * Byte 9 . | BLOB_NAME_LEN |
+ * . . | |
+ * Byte 37 . -+ -+
+ */
+#define DBS_BLOCK_SIZE (16*1024) /* 16 k */
+#define DBS_MAX_ENTRY_SIZE (DBS_BLOCK_SIZE - (2048)) /* 14 k */
+#define DBS_CACHE_SIZE DBS_BLOCK_SIZE*8
+#define ROUNDDIV(x,y) (x+(y-1))/y
+#define BLOB_HEAD_LEN 4
+#define BLOB_LENGTH_START BLOB_HEAD_LEN
+#define BLOB_LENGTH_LEN 4
+#define BLOB_NAME_START BLOB_LENGTH_START+BLOB_LENGTH_LEN
+#define BLOB_NAME_LEN 1+ROUNDDIV(SHA1_LENGTH,3)*4+1
+#define BLOB_BUF_LEN BLOB_HEAD_LEN+BLOB_LENGTH_LEN+BLOB_NAME_LEN
+
+/* a Shim data structure. This data structure has a db built into it. */
+typedef struct DBSStr DBS;
+
+struct DBSStr {
+ DB db;
+ char *blobdir;
+ int mode;
+ PRBool readOnly;
+ PRFileMap *dbs_mapfile;
+ unsigned char *dbs_addr;
+ PRUint32 dbs_len;
+ char staticBlobArea[BLOB_BUF_LEN];
+};
+
+
+
+/*
+ * return true if the Datablock contains a blobtype
+ */
+static PRBool
+dbs_IsBlob(DBT *blobData)
+{
+ unsigned char *addr = (unsigned char *)blobData->data;
+ if (blobData->size < BLOB_BUF_LEN) {
+ return PR_FALSE;
+ }
+ return addr && ((certDBEntryType) addr[1] == certDBEntryTypeBlob);
+}
+
+/*
+ * extract the filename in the blob of the real data set.
+ * This value is not malloced (does not need to be freed by the caller.
+ */
+static const char *
+dbs_getBlobFileName(DBT *blobData)
+{
+ char *addr = (char *)blobData->data;
+
+ return &addr[BLOB_NAME_START];
+}
+
+/*
+ * extract the size of the actual blob from the blob record
+ */
+static PRUint32
+dbs_getBlobSize(DBT *blobData)
+{
+ unsigned char *addr = (unsigned char *)blobData->data;
+
+ return (PRUint32)(addr[BLOB_LENGTH_START+3] << 24) |
+ (addr[BLOB_LENGTH_START+2] << 16) |
+ (addr[BLOB_LENGTH_START+1] << 8) |
+ addr[BLOB_LENGTH_START];
+}
+
+
+/* We are using base64 data for the filename, but base64 data can include a
+ * '/' which is interpreted as a path separator on many platforms. Replace it
+ * with an inocuous '-'. We don't need to convert back because we never actual
+ * decode the filename.
+ */
+
+static void
+dbs_replaceSlash(char *cp, int len)
+{
+ while (len--) {
+ if (*cp == '/') *cp = '-';
+ cp++;
+ }
+}
+
+/*
+ * create a blob record from a key, data and return it in blobData.
+ * NOTE: The data element is static data (keeping with the dbm model).
+ */
+static void
+dbs_mkBlob(DBS *dbsp,const DBT *key, const DBT *data, DBT *blobData)
+{
+ unsigned char sha1_data[SHA1_LENGTH];
+ char *b = dbsp->staticBlobArea;
+ PRUint32 length = data->size;
+ SECItem sha1Item;
+
+ b[0] = CERT_DB_FILE_VERSION; /* certdb version number */
+ b[1] = (char) certDBEntryTypeBlob; /* type */
+ b[2] = 0; /* flags */
+ b[3] = 0; /* reserved */
+ b[BLOB_LENGTH_START] = length & 0xff;
+ b[BLOB_LENGTH_START+1] = (length >> 8) & 0xff;
+ b[BLOB_LENGTH_START+2] = (length >> 16) & 0xff;
+ b[BLOB_LENGTH_START+3] = (length >> 24) & 0xff;
+ sha1Item.data = sha1_data;
+ sha1Item.len = SHA1_LENGTH;
+ SHA1_HashBuf(sha1_data,key->data,key->size);
+ b[BLOB_NAME_START]='b'; /* Make sure we start with a alpha */
+ NSSBase64_EncodeItem(NULL,&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1,&sha1Item);
+ b[BLOB_BUF_LEN-1] = 0;
+ dbs_replaceSlash(&b[BLOB_NAME_START+1],BLOB_NAME_LEN-1);
+ blobData->data = b;
+ blobData->size = BLOB_BUF_LEN;
+ return;
+}
+
+
+/*
+ * construct a path to the actual blob. The string returned must be
+ * freed by the caller with PR_smprintf_free.
+ *
+ * Note: this file does lots of consistancy checks on the DBT. The
+ * routines that call this depend on these checks, so they don't worry
+ * about them (success of this routine implies a good blobdata record).
+ */
+static char *
+dbs_getBlobFilePath(char *blobdir,DBT *blobData)
+{
+ const char *name;
+
+ if (blobdir == NULL) {
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ return NULL;
+ }
+ if (!dbs_IsBlob(blobData)) {
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ return NULL;
+ }
+ name = dbs_getBlobFileName(blobData);
+ if (!name || *name == 0) {
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ return NULL;
+ }
+ return PR_smprintf("%s" PATH_SEPARATOR "%s", blobdir, name);
+}
+
+/*
+ * Delete a blob file pointed to by the blob record.
+ */
+static void
+dbs_removeBlob(DBS *dbsp, DBT *blobData)
+{
+ char *file;
+
+ file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
+ if (!file) {
+ return;
+ }
+ PR_Delete(file);
+ PR_smprintf_free(file);
+}
+
+/*
+ * Directory modes are slightly different, the 'x' bit needs to be on to
+ * access them. Copy all the read bits to 'x' bits
+ */
+static int
+dbs_DirMode(int mode)
+{
+ int x_bits = (mode >> 2) & 0111;
+ return mode | x_bits;
+}
+
+/*
+ * write a data blob to it's file. blobdData is the blob record that will be
+ * stored in the database. data is the actual data to go out on disk.
+ */
+static int
+dbs_writeBlob(DBS *dbsp, int mode, DBT *blobData, const DBT *data)
+{
+ char *file = NULL;
+ PRFileDesc *filed;
+ PRStatus status;
+ int len;
+ int error = 0;
+
+ file = dbs_getBlobFilePath(dbsp->blobdir, blobData);
+ if (!file) {
+ goto loser;
+ }
+ if (PR_Access(dbsp->blobdir, PR_ACCESS_EXISTS) != PR_SUCCESS) {
+ status = PR_MkDir(dbsp->blobdir,dbs_DirMode(mode));
+ if (status != PR_SUCCESS) {
+ goto loser;
+ }
+ }
+ filed = PR_OpenFile(file,PR_CREATE_FILE|PR_TRUNCATE|PR_WRONLY, mode);
+ if (filed == NULL) {
+ error = PR_GetError();
+ goto loser;
+ }
+ len = PR_Write(filed,data->data,data->size);
+ error = PR_GetError();
+ PR_Close(filed);
+ if (len < (int)data->size) {
+ goto loser;
+ }
+ PR_smprintf_free(file);
+ return 0;
+
+loser:
+ if (file) {
+ PR_Delete(file);
+ PR_smprintf_free(file);
+ }
+ /* don't let close or delete reset the error */
+ PR_SetError(error,0);
+ return -1;
+}
+
+
+/*
+ * we need to keep a address map in memory between calls to DBM.
+ * remember what we have mapped can close it when we get another dbm
+ * call.
+ *
+ * NOTE: Not all platforms support mapped files. This code is designed to
+ * detect this at runtime. If map files aren't supported the OS will indicate
+ * this by failing the PR_Memmap call. In this case we emulate mapped files
+ * by just reading in the file into regular memory. We signal this state by
+ * making dbs_mapfile NULL and dbs_addr non-NULL.
+ */
+
+static void
+dbs_freemap(DBS *dbsp)
+{
+ if (dbsp->dbs_mapfile) {
+ PR_MemUnmap(dbsp->dbs_addr,dbsp->dbs_len);
+ PR_CloseFileMap(dbsp->dbs_mapfile);
+ dbsp->dbs_mapfile = NULL;
+ dbsp->dbs_addr = NULL;
+ dbsp->dbs_len = 0;
+ } else if (dbsp->dbs_addr) {
+ PORT_Free(dbsp->dbs_addr);
+ dbsp->dbs_addr = NULL;
+ dbsp->dbs_len = 0;
+ }
+ return;
+}
+
+static void
+dbs_setmap(DBS *dbsp, PRFileMap *mapfile, unsigned char *addr, PRUint32 len)
+{
+ dbsp->dbs_mapfile = mapfile;
+ dbsp->dbs_addr = addr;
+ dbsp->dbs_len = len;
+}
+
+/*
+ * platforms that cannot map the file need to read it into a temp buffer.
+ */
+static unsigned char *
+dbs_EmulateMap(PRFileDesc *filed, int len)
+{
+ unsigned char *addr;
+ PRInt32 dataRead;
+
+ addr = PORT_Alloc(len);
+ if (addr == NULL) {
+ return NULL;
+ }
+
+ dataRead = PR_Read(filed,addr,len);
+ if (dataRead != len) {
+ PORT_Free(addr);
+ if (dataRead > 0) {
+ /* PR_Read didn't set an error, we need to */
+ PR_SetError(SEC_ERROR_BAD_DATABASE,0);
+ }
+ return NULL;
+ }
+
+ return addr;
+}
+
+
+/*
+ * pull a database record off the disk
+ * data points to the blob record on input and the real record (if we could
+ * read it) on output. if there is an error data is not modified.
+ */
+static int
+dbs_readBlob(DBS *dbsp, DBT *data)
+{
+ char *file = NULL;
+ PRFileDesc *filed = NULL;
+ PRFileMap *mapfile = NULL;
+ unsigned char *addr = NULL;
+ int error;
+ int len = -1;
+
+ file = dbs_getBlobFilePath(dbsp->blobdir, data);
+ if (!file) {
+ goto loser;
+ }
+ filed = PR_OpenFile(file,PR_RDONLY,0);
+ PR_smprintf_free(file); file = NULL;
+ if (filed == NULL) {
+ goto loser;
+ }
+
+ len = dbs_getBlobSize(data);
+ mapfile = PR_CreateFileMap(filed, len, PR_PROT_READONLY);
+ if (mapfile == NULL) {
+ /* USE PR_GetError instead of PORT_GetError here
+ * because we are getting the error from PR_xxx
+ * function */
+ if (PR_GetError() != PR_NOT_IMPLEMENTED_ERROR) {
+ goto loser;
+ }
+ addr = dbs_EmulateMap(filed, len);
+ } else {
+ addr = PR_MemMap(mapfile, 0, len);
+ }
+ if (addr == NULL) {
+ goto loser;
+ }
+ PR_Close(filed);
+ dbs_setmap(dbsp,mapfile,addr,len);
+
+ data->data = addr;
+ data->size = len;
+ return 0;
+
+loser:
+ /* preserve the error code */
+ error = PR_GetError();
+ if (mapfile) {
+ PR_CloseFileMap(mapfile);
+ }
+ if (filed) {
+ PR_Close(filed);
+ }
+ PR_SetError(error,0);
+ return -1;
+}
+
+/*
+ * actual DBM shims
+ */
+static int
+dbs_get(const DB *dbs, const DBT *key, DBT *data, unsigned int flags)
+{
+ int ret;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+
+ dbs_freemap(dbsp);
+
+ ret = (* db->get)(db, key, data, flags);
+ if ((ret == 0) && dbs_IsBlob(data)) {
+ ret = dbs_readBlob(dbsp,data);
+ }
+
+ return(ret);
+}
+
+static int
+dbs_put(const DB *dbs, DBT *key, const DBT *data, unsigned int flags)
+{
+ DBT blob;
+ int ret = 0;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+ dbs_freemap(dbsp);
+
+ /* If the db is readonly, just pass the data down to rdb and let it fail */
+ if (!dbsp->readOnly) {
+ DBT oldData;
+ int ret1;
+
+ /* make sure the current record is deleted if it's a blob */
+ ret1 = (*db->get)(db,key,&oldData,0);
+ if ((ret1 == 0) && flags == R_NOOVERWRITE) {
+ /* let DBM return the error to maintain consistancy */
+ return (* db->put)(db, key, data, flags);
+ }
+ if ((ret1 == 0) && dbs_IsBlob(&oldData)) {
+ dbs_removeBlob(dbsp, &oldData);
+ }
+
+ if (data->size > DBS_MAX_ENTRY_SIZE) {
+ dbs_mkBlob(dbsp,key,data,&blob);
+ ret = dbs_writeBlob(dbsp, dbsp->mode, &blob, data);
+ data = &blob;
+ }
+ }
+
+ if (ret == 0) {
+ ret = (* db->put)(db, key, data, flags);
+ }
+ return(ret);
+}
+
+static int
+dbs_sync(const DB *dbs, unsigned int flags)
+{
+ DB *db = (DB *)dbs->internal;
+ DBS *dbsp = (DBS *)dbs;
+
+ dbs_freemap(dbsp);
+
+ return (* db->sync)(db, flags);
+}
+
+static int
+dbs_del(const DB *dbs, const DBT *key, unsigned int flags)
+{
+ int ret;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+ dbs_freemap(dbsp);
+
+ if (!dbsp->readOnly) {
+ DBT oldData;
+ ret = (*db->get)(db,key,&oldData,0);
+ if ((ret == 0) && dbs_IsBlob(&oldData)) {
+ dbs_removeBlob(dbsp,&oldData);
+ }
+ }
+
+ return (* db->del)(db, key, flags);
+}
+
+static int
+dbs_seq(const DB *dbs, DBT *key, DBT *data, unsigned int flags)
+{
+ int ret;
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+
+ dbs_freemap(dbsp);
+
+ ret = (* db->seq)(db, key, data, flags);
+ if ((ret == 0) && dbs_IsBlob(data)) {
+ /* don't return a blob read as an error so traversals keep going */
+ (void) dbs_readBlob(dbsp,data);
+ }
+
+ return(ret);
+}
+
+static int
+dbs_close(DB *dbs)
+{
+ DBS *dbsp = (DBS *)dbs;
+ DB *db = (DB *)dbs->internal;
+ int ret;
+
+ dbs_freemap(dbsp);
+ ret = (* db->close)(db);
+ PORT_Free(dbsp->blobdir);
+ PORT_Free(dbsp);
+ return ret;
+}
+
+static int
+dbs_fd(const DB *dbs)
+{
+ DB *db = (DB *)dbs->internal;
+
+ return (* db->fd)(db);
+}
+
+/*
+ * the naming convention we use is
+ * change the .xxx into .dir. (for nss it's always .db);
+ * if no .extension exists or is equal to .dir, add a .dir
+ * the returned data must be freed.
+ */
+#define DIRSUFFIX ".dir"
+static char *
+dbs_mkBlobDirName(const char *dbname)
+{
+ int dbname_len = PORT_Strlen(dbname);
+ int dbname_end = dbname_len;
+ const char *cp;
+ char *blobDir = NULL;
+
+ /* scan back from the end looking for either a directory separator, a '.',
+ * or the end of the string. NOTE: Windows should check for both separators
+ * here. For now this is safe because we know NSS always uses a '.'
+ */
+ for (cp = &dbname[dbname_len];
+ (cp > dbname) && (*cp != '.') && (*cp != *PATH_SEPARATOR) ;
+ cp--)
+ /* Empty */ ;
+ if (*cp == '.') {
+ dbname_end = cp - dbname;
+ if (PORT_Strcmp(cp,DIRSUFFIX) == 0) {
+ dbname_end = dbname_len;
+ }
+ }
+ blobDir = PORT_ZAlloc(dbname_end+sizeof(DIRSUFFIX));
+ if (blobDir == NULL) {
+ return NULL;
+ }
+ PORT_Memcpy(blobDir,dbname,dbname_end);
+ PORT_Memcpy(&blobDir[dbname_end],DIRSUFFIX,sizeof(DIRSUFFIX));
+ return blobDir;
+}
+
+#define DBM_DEFAULT 0
+static const HASHINFO dbs_hashInfo = {
+ DBS_BLOCK_SIZE, /* bucket size, must be greater than = to
+ * or maximum entry size (+ header)
+ * we allow before blobing */
+ DBM_DEFAULT, /* Fill Factor */
+ DBM_DEFAULT, /* number of elements */
+ DBS_CACHE_SIZE, /* cache size */
+ DBM_DEFAULT, /* hash function */
+ DBM_DEFAULT, /* byte order */
+};
+
+/*
+ * the open function. NOTE: this is the only exposed function in this file.
+ * everything else is called through the function table pointer.
+ */
+DB *
+dbsopen(const char *dbname, int flags, int mode, DBTYPE type,
+ const void *userData)
+{
+ DB *db = NULL,*dbs = NULL;
+ DBS *dbsp = NULL;
+
+ /* NOTE: we are overriding userData with dbs_hashInfo. since all known
+ * callers pass 0, this is ok, otherwise we should merge the two */
+
+ dbsp = (DBS *)PORT_ZAlloc(sizeof(DBS));
+ if (!dbsp) {
+ return NULL;
+ }
+ dbs = &dbsp->db;
+
+ dbsp->blobdir=dbs_mkBlobDirName(dbname);
+ if (dbsp->blobdir == NULL) {
+ goto loser;
+ }
+ dbsp->mode = mode;
+ dbsp->readOnly = (PRBool)(flags == NO_RDONLY);
+ dbsp->dbs_mapfile = NULL;
+ dbsp->dbs_addr = NULL;
+ dbsp->dbs_len = 0;
+
+ /* the real dbm call */
+ db = dbopen(dbname, flags, mode, type, &dbs_hashInfo);
+ if (db == NULL) {
+ goto loser;
+ }
+ dbs->internal = (void *) db;
+ dbs->type = type;
+ dbs->close = dbs_close;
+ dbs->get = dbs_get;
+ dbs->del = dbs_del;
+ dbs->put = dbs_put;
+ dbs->seq = dbs_seq;
+ dbs->sync = dbs_sync;
+ dbs->fd = dbs_fd;
+
+ return dbs;
+loser:
+ if (db) {
+ (*db->close)(db);
+ }
+ if (dbsp) {
+ if (dbsp->blobdir) {
+ PORT_Free(dbsp->blobdir);
+ }
+ PORT_Free(dbsp);
+ }
+ return NULL;
+}
diff --git a/security/nss/lib/softoken/legacydb/keydb.c b/security/nss/lib/softoken/legacydb/keydb.c
new file mode 100644
index 000000000..89c274aeb
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/keydb.c
@@ -0,0 +1,2277 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "lowkeyi.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "secoid.h"
+#include "blapi.h"
+#include "secitem.h"
+#include "pcert.h"
+#include "mcom_db.h"
+#include "secerr.h"
+
+#include "keydbi.h"
+#include "lgdb.h"
+
+/*
+ * Record keys for keydb
+ */
+#define SALT_STRING "global-salt"
+#define VERSION_STRING "Version"
+#define KEYDB_PW_CHECK_STRING "password-check"
+#define KEYDB_PW_CHECK_LEN 14
+#define KEYDB_FAKE_PW_CHECK_STRING "fake-password-check"
+#define KEYDB_FAKE_PW_CHECK_LEN 19
+
+/* Size of the global salt for key database */
+#define SALT_LENGTH 16
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSLOWKEYEncryptedPrivateKeyInfo) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,algorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSLOWKEYEncryptedPrivateKeyInfo,encryptedData) },
+ { 0 }
+};
+
+const SEC_ASN1Template nsslowkey_PointerToEncryptedPrivateKeyInfoTemplate[] = {
+ { SEC_ASN1_POINTER, 0, nsslowkey_EncryptedPrivateKeyInfoTemplate }
+};
+
+
+/* ====== Default key databse encryption algorithm ====== */
+static void
+sec_destroy_dbkey(NSSLOWKEYDBKey *dbkey)
+{
+ if ( dbkey && dbkey->arena ) {
+ PORT_FreeArena(dbkey->arena, PR_FALSE);
+ }
+}
+
+static void
+free_dbt(DBT *dbt)
+{
+ if ( dbt ) {
+ PORT_Free(dbt->data);
+ PORT_Free(dbt);
+ }
+
+ return;
+}
+
+static int keydb_Get(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
+ unsigned int flags);
+static int keydb_Put(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
+ unsigned int flags);
+static int keydb_Sync(NSSLOWKEYDBHandle *db, unsigned int flags);
+static int keydb_Del(NSSLOWKEYDBHandle *db, DBT *key, unsigned int flags);
+static int keydb_Seq(NSSLOWKEYDBHandle *db, DBT *key, DBT *data,
+ unsigned int flags);
+static void keydb_Close(NSSLOWKEYDBHandle *db);
+
+/*
+ * format of key database entries for version 3 of database:
+ * byte offset field
+ * ----------- -----
+ * 0 version
+ * 1 salt-len
+ * 2 nn-len
+ * 3.. salt-data
+ * ... nickname
+ * ... encrypted-key-data
+ */
+static DBT *
+encode_dbkey(NSSLOWKEYDBKey *dbkey,unsigned char version)
+{
+ DBT *bufitem = NULL;
+ unsigned char *buf;
+ int nnlen;
+ char *nn;
+
+ bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT));
+ if ( bufitem == NULL ) {
+ goto loser;
+ }
+
+ if ( dbkey->nickname ) {
+ nn = dbkey->nickname;
+ nnlen = PORT_Strlen(nn) + 1;
+ } else {
+ nn = "";
+ nnlen = 1;
+ }
+
+ /* compute the length of the record */
+ /* 1 + 1 + 1 == version number header + salt length + nn len */
+ bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1;
+
+ bufitem->data = (void *)PORT_ZAlloc(bufitem->size);
+ if ( bufitem->data == NULL ) {
+ goto loser;
+ }
+
+ buf = (unsigned char *)bufitem->data;
+
+ /* set version number */
+ buf[0] = version;
+
+ /* set length of salt */
+ PORT_Assert(dbkey->salt.len < 256);
+ buf[1] = dbkey->salt.len;
+
+ /* set length of nickname */
+ PORT_Assert(nnlen < 256);
+ buf[2] = nnlen;
+
+ /* copy salt */
+ PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len);
+
+ /* copy nickname */
+ PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen);
+
+ /* copy encrypted key */
+ PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data,
+ dbkey->derPK.len);
+
+ return(bufitem);
+
+loser:
+ if ( bufitem ) {
+ free_dbt(bufitem);
+ }
+
+ return(NULL);
+}
+
+static NSSLOWKEYDBKey *
+decode_dbkey(DBT *bufitem, int expectedVersion)
+{
+ NSSLOWKEYDBKey *dbkey;
+ PLArenaPool *arena = NULL;
+ unsigned char *buf;
+ int version;
+ int keyoff;
+ int nnlen;
+ int saltoff;
+
+ buf = (unsigned char *)bufitem->data;
+
+ version = buf[0];
+
+ if ( version != expectedVersion ) {
+ goto loser;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
+ if ( dbkey == NULL ) {
+ goto loser;
+ }
+
+ dbkey->arena = arena;
+ dbkey->salt.data = NULL;
+ dbkey->derPK.data = NULL;
+
+ dbkey->salt.len = buf[1];
+ dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len);
+ if ( dbkey->salt.data == NULL ) {
+ goto loser;
+ }
+
+ saltoff = 2;
+ keyoff = 2 + dbkey->salt.len;
+
+ if ( expectedVersion >= 3 ) {
+ nnlen = buf[2];
+ if ( nnlen ) {
+ dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1);
+ if ( dbkey->nickname ) {
+ PORT_Memcpy(dbkey->nickname, &buf[keyoff+1], nnlen);
+ }
+ }
+ keyoff += ( nnlen + 1 );
+ saltoff = 3;
+ }
+
+ PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len);
+
+ dbkey->derPK.len = bufitem->size - keyoff;
+ dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena,dbkey->derPK.len);
+ if ( dbkey->derPK.data == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len);
+
+ return(dbkey);
+
+loser:
+
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+static NSSLOWKEYDBKey *
+get_dbkey(NSSLOWKEYDBHandle *handle, DBT *index)
+{
+ NSSLOWKEYDBKey *dbkey;
+ DBT entry;
+ int ret;
+
+ /* get it from the database */
+ ret = keydb_Get(handle, index, &entry, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up dbkey struct */
+
+ dbkey = decode_dbkey(&entry, handle->version);
+
+ return(dbkey);
+}
+
+static SECStatus
+put_dbkey(NSSLOWKEYDBHandle *handle, DBT *index, NSSLOWKEYDBKey *dbkey, PRBool update)
+{
+ DBT *keydata = NULL;
+ int status;
+
+ keydata = encode_dbkey(dbkey, handle->version);
+ if ( keydata == NULL ) {
+ goto loser;
+ }
+
+ /* put it in the database */
+ if ( update ) {
+ status = keydb_Put(handle, index, keydata, 0);
+ } else {
+ status = keydb_Put(handle, index, keydata, R_NOOVERWRITE);
+ }
+
+ if ( status ) {
+ goto loser;
+ }
+
+ /* sync the database */
+ status = keydb_Sync(handle, 0);
+ if ( status ) {
+ goto loser;
+ }
+
+ free_dbt(keydata);
+ return(SECSuccess);
+
+loser:
+ if ( keydata ) {
+ free_dbt(keydata);
+ }
+
+ return(SECFailure);
+}
+
+SECStatus
+nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle,
+ SECStatus (* keyfunc)(DBT *k, DBT *d, void *pdata),
+ void *udata )
+{
+ DBT data;
+ DBT key;
+ SECStatus status;
+ int ret;
+
+ if (handle == NULL) {
+ return(SECFailure);
+ }
+
+ ret = keydb_Seq(handle, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+
+ /* skip password check */
+ if ( key.size == KEYDB_PW_CHECK_LEN ) {
+ if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
+ KEYDB_PW_CHECK_LEN) == 0 ) {
+ continue;
+ }
+ }
+
+ status = (* keyfunc)(&key, &data, udata);
+ if (status != SECSuccess) {
+ return(status);
+ }
+ }
+ } while ( keydb_Seq(handle, &key, &data, R_NEXT) == 0 );
+
+ return(SECSuccess);
+}
+
+#ifdef notdef
+typedef struct keyNode {
+ struct keyNode *next;
+ DBT key;
+} keyNode;
+
+typedef struct {
+ PLArenaPool *arena;
+ keyNode *head;
+} keyList;
+
+static SECStatus
+sec_add_key_to_list(DBT *key, DBT *data, void *arg)
+{
+ keyList *keylist;
+ keyNode *node;
+ void *keydata;
+
+ keylist = (keyList *)arg;
+
+ /* allocate the node struct */
+ node = (keyNode*)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode));
+ if ( node == NULL ) {
+ return(SECFailure);
+ }
+
+ /* allocate room for key data */
+ keydata = PORT_ArenaZAlloc(keylist->arena, key->size);
+ if ( keydata == NULL ) {
+ return(SECFailure);
+ }
+
+ /* link node into list */
+ node->next = keylist->head;
+ keylist->head = node;
+
+ /* copy key into node */
+ PORT_Memcpy(keydata, key->data, key->size);
+ node->key.size = key->size;
+ node->key.data = keydata;
+
+ return(SECSuccess);
+}
+#endif
+
+static SECItem *
+decodeKeyDBGlobalSalt(DBT *saltData)
+{
+ SECItem *saltitem;
+
+ saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if ( saltitem == NULL ) {
+ return(NULL);
+ }
+
+ saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size);
+ if ( saltitem->data == NULL ) {
+ PORT_Free(saltitem);
+ return(NULL);
+ }
+
+ saltitem->len = saltData->size;
+ PORT_Memcpy(saltitem->data, saltData->data, saltitem->len);
+
+ return(saltitem);
+}
+
+static SECItem *
+GetKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle)
+{
+ DBT saltKey;
+ DBT saltData;
+ int ret;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ ret = keydb_Get(handle, &saltKey, &saltData, 0);
+ if ( ret ) {
+ return(NULL);
+ }
+
+ return(decodeKeyDBGlobalSalt(&saltData));
+}
+
+static SECStatus
+StoreKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle, SECItem *salt)
+{
+ DBT saltKey;
+ DBT saltData;
+ int status;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ saltData.data = (void *)salt->data;
+ saltData.size = salt->len;
+
+ /* put global salt into the database now */
+ status = keydb_Put(handle, &saltKey, &saltData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+makeGlobalVersion(NSSLOWKEYDBHandle *handle)
+{
+ unsigned char version;
+ DBT versionData;
+ DBT versionKey;
+ int status;
+
+ version = NSSLOWKEY_DB_FILE_VERSION;
+ versionData.data = &version;
+ versionData.size = 1;
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ /* put version string into the database now */
+ status = keydb_Put(handle, &versionKey, &versionData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+ handle->version = version;
+
+ return(SECSuccess);
+}
+
+
+static SECStatus
+makeGlobalSalt(NSSLOWKEYDBHandle *handle)
+{
+ DBT saltKey;
+ DBT saltData;
+ unsigned char saltbuf[16];
+ int status;
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ saltData.data = (void *)saltbuf;
+ saltData.size = sizeof(saltbuf);
+ RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf));
+
+ /* put global salt into the database now */
+ status = keydb_Put(handle, &saltKey, &saltData, 0);
+ if ( status ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
+ SECItem *encCheck);
+
+static unsigned char
+nsslowkey_version(NSSLOWKEYDBHandle *handle)
+{
+ DBT versionKey;
+ DBT versionData;
+ int ret;
+ versionKey.data = VERSION_STRING;
+ versionKey.size = sizeof(VERSION_STRING)-1;
+
+ if (handle->db == NULL) {
+ return 255;
+ }
+
+ /* lookup version string in database */
+ ret = keydb_Get( handle, &versionKey, &versionData, 0 );
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ return 255;
+ }
+
+ if ( ret >= 1 ) {
+ return 0;
+ }
+ return *( (unsigned char *)versionData.data);
+}
+
+static PRBool
+seckey_HasAServerKey(NSSLOWKEYDBHandle *handle)
+{
+ DBT key;
+ DBT data;
+ int ret;
+ PRBool found = PR_FALSE;
+
+ ret = keydb_Seq(handle, &key, &data, R_FIRST);
+ if ( ret ) {
+ return PR_FALSE;
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ /* skip salt */
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+ /* skip pw check entry */
+ if ( key.size == KEYDB_PW_CHECK_LEN ) {
+ if ( PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING,
+ KEYDB_PW_CHECK_LEN) == 0 ) {
+ continue;
+ }
+ }
+
+ /* keys stored by nickname will have 0 as the last byte of the
+ * db key. Other keys must be stored by modulus. We will not
+ * update those because they are left over from a keygen that
+ * never resulted in a cert.
+ */
+ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
+ continue;
+ }
+
+ if (PORT_Strcmp(key.data,"Server-Key") == 0) {
+ found = PR_TRUE;
+ break;
+ }
+
+ }
+ } while ( keydb_Seq(handle, &key, &data, R_NEXT) == 0 );
+
+ return found;
+}
+
+/* forward declare local create function */
+static NSSLOWKEYDBHandle * nsslowkey_NewHandle(DB *dbHandle);
+
+/*
+ * currently updates key database from v2 to v3
+ */
+static SECStatus
+nsslowkey_UpdateKeyDBPass1(NSSLOWKEYDBHandle *handle)
+{
+ SECStatus rv;
+ DBT checkKey;
+ DBT checkData;
+ DBT saltKey;
+ DBT saltData;
+ DBT key;
+ DBT data;
+ unsigned char version;
+ NSSLOWKEYDBKey *dbkey = NULL;
+ NSSLOWKEYDBHandle *update = NULL;
+ SECItem *oldSalt = NULL;
+ int ret;
+ SECItem checkitem;
+
+ if ( handle->updatedb == NULL ) {
+ return SECSuccess;
+ }
+
+ /* create a full DB Handle for our update so we
+ * can use the correct locks for the db primatives */
+ update = nsslowkey_NewHandle(handle->updatedb);
+ if ( update == NULL) {
+ return SECSuccess;
+ }
+
+ /* update has now inherited the database handle */
+ handle->updatedb = NULL;
+
+ /*
+ * check the version record
+ */
+ version = nsslowkey_version(update);
+ if (version != 2) {
+ goto done;
+ }
+
+ saltKey.data = SALT_STRING;
+ saltKey.size = sizeof(SALT_STRING) - 1;
+
+ ret = keydb_Get(update, &saltKey, &saltData, 0);
+ if ( ret ) {
+ /* no salt in old db, so it is corrupted */
+ goto done;
+ }
+
+ oldSalt = decodeKeyDBGlobalSalt(&saltData);
+ if ( oldSalt == NULL ) {
+ /* bad salt in old db, so it is corrupted */
+ goto done;
+ }
+
+ /*
+ * look for a pw check entry
+ */
+ checkKey.data = KEYDB_PW_CHECK_STRING;
+ checkKey.size = KEYDB_PW_CHECK_LEN;
+
+ ret = keydb_Get(update, &checkKey, &checkData, 0 );
+ if (ret) {
+ /*
+ * if we have a key, but no KEYDB_PW_CHECK_STRING, then this must
+ * be an old server database, and it does have a password associated
+ * with it. Put a fake entry in so we can identify this db when we do
+ * get the password for it.
+ */
+ if (seckey_HasAServerKey(update)) {
+ DBT fcheckKey;
+ DBT fcheckData;
+
+ /*
+ * include a fake string
+ */
+ fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING;
+ fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN;
+ fcheckData.data = "1";
+ fcheckData.size = 1;
+ /* put global salt into the new database now */
+ ret = keydb_Put( handle, &saltKey, &saltData, 0);
+ if ( ret ) {
+ goto done;
+ }
+ ret = keydb_Put( handle, &fcheckKey, &fcheckData, 0);
+ if ( ret ) {
+ goto done;
+ }
+ } else {
+ goto done;
+ }
+ } else {
+ /* put global salt into the new database now */
+ ret = keydb_Put( handle, &saltKey, &saltData, 0);
+ if ( ret ) {
+ goto done;
+ }
+
+ dbkey = decode_dbkey(&checkData, 2);
+ if ( dbkey == NULL ) {
+ goto done;
+ }
+ checkitem = dbkey->derPK;
+ dbkey->derPK.data = NULL;
+
+ /* format the new pw check entry */
+ rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE);
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ /* free the dbkey */
+ sec_destroy_dbkey(dbkey);
+ dbkey = NULL;
+ }
+
+
+ /* now traverse the database */
+ ret = keydb_Seq(update, &key, &data, R_FIRST);
+ if ( ret ) {
+ goto done;
+ }
+
+ do {
+ /* skip version record */
+ if ( data.size > 1 ) {
+ /* skip salt */
+ if ( key.size == ( sizeof(SALT_STRING) - 1 ) ) {
+ if ( PORT_Memcmp(key.data, SALT_STRING, key.size) == 0 ) {
+ continue;
+ }
+ }
+ /* skip pw check entry */
+ if ( key.size == checkKey.size ) {
+ if ( PORT_Memcmp(key.data, checkKey.data, key.size) == 0 ) {
+ continue;
+ }
+ }
+
+ /* keys stored by nickname will have 0 as the last byte of the
+ * db key. Other keys must be stored by modulus. We will not
+ * update those because they are left over from a keygen that
+ * never resulted in a cert.
+ */
+ if ( ((unsigned char *)key.data)[key.size-1] != 0 ) {
+ continue;
+ }
+
+ dbkey = decode_dbkey(&data, 2);
+ if ( dbkey == NULL ) {
+ continue;
+ }
+
+ /* This puts the key into the new database with the same
+ * index (nickname) that it had before. The second pass
+ * of the update will have the password. It will decrypt
+ * and re-encrypt the entries using a new algorithm.
+ */
+ dbkey->nickname = (char *)key.data;
+ rv = put_dbkey(handle, &key, dbkey, PR_FALSE);
+ dbkey->nickname = NULL;
+
+ sec_destroy_dbkey(dbkey);
+ }
+ } while ( keydb_Seq(update, &key, &data, R_NEXT) == 0 );
+
+ dbkey = NULL;
+
+done:
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+
+ nsslowkey_CloseKeyDB(update);
+
+ if ( oldSalt ) {
+ SECITEM_FreeItem(oldSalt, PR_TRUE);
+ }
+
+ if ( dbkey ) {
+ sec_destroy_dbkey(dbkey);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+openNewDB(const char *appName, const char *prefix, const char *dbname,
+ NSSLOWKEYDBHandle *handle, NSSLOWKEYDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv = SECFailure;
+ int status = RDB_FAIL;
+ char *updname = NULL;
+ DB *updatedb = NULL;
+ PRBool updated = PR_FALSE;
+ int ret;
+
+ if (appName) {
+ handle->db = rdbopen( appName, prefix, "key", NO_CREATE, &status);
+ } else {
+ handle->db = dbopen( dbname, NO_CREATE, 0600, DB_HASH, 0 );
+ }
+ /* if create fails then we lose */
+ if ( handle->db == NULL ) {
+ return (status == RDB_RETRY) ? SECWouldBlock: SECFailure;
+ }
+
+ /* force a transactional read, which will verify that one and only one
+ * process attempts the update. */
+ if (nsslowkey_version(handle) == NSSLOWKEY_DB_FILE_VERSION) {
+ /* someone else has already updated the database for us */
+ db_InitComplete(handle->db);
+ return SECSuccess;
+ }
+
+ /*
+ * if we are creating a multiaccess database, see if there is a
+ * local database we can update from.
+ */
+ if (appName) {
+ NSSLOWKEYDBHandle *updateHandle;
+ updatedb = dbopen( dbname, NO_RDONLY, 0600, DB_HASH, 0 );
+ if (!updatedb) {
+ goto noupdate;
+ }
+
+ /* nsslowkey_version needs a full handle because it calls
+ * the kdb_Get() function, which needs to lock.
+ */
+ updateHandle = nsslowkey_NewHandle(updatedb);
+ if (!updateHandle) {
+ updatedb->close(updatedb);
+ goto noupdate;
+ }
+
+ handle->version = nsslowkey_version(updateHandle);
+ if (handle->version != NSSLOWKEY_DB_FILE_VERSION) {
+ nsslowkey_CloseKeyDB(updateHandle);
+ goto noupdate;
+ }
+
+ /* copy the new DB from the old one */
+ db_Copy(handle->db, updatedb);
+ nsslowkey_CloseKeyDB(updateHandle);
+ db_InitComplete(handle->db);
+ return SECSuccess;
+ }
+noupdate:
+
+ /* update the version number */
+ rv = makeGlobalVersion(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /*
+ * try to update from v2 db
+ */
+ updname = (*namecb)(cbarg, 2);
+ if ( updname != NULL ) {
+ handle->updatedb = dbopen( updname, NO_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free( updname );
+
+ if ( handle->updatedb ) {
+ /*
+ * Try to update the db using a null password. If the db
+ * doesn't have a password, then this will work. If it does
+ * have a password, then this will fail and we will do the
+ * update later
+ */
+ rv = nsslowkey_UpdateKeyDBPass1(handle);
+ if ( rv == SECSuccess ) {
+ updated = PR_TRUE;
+ }
+ }
+
+ }
+
+ /* we are using the old salt if we updated from an old db */
+ if ( ! updated ) {
+ rv = makeGlobalSalt(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+ if ( ret ) {
+ rv = SECFailure;
+ goto loser;
+ }
+ rv = SECSuccess;
+
+loser:
+ db_InitComplete(handle->db);
+ return rv;
+}
+
+
+static DB *
+openOldDB(const char *appName, const char *prefix, const char *dbname,
+ PRBool openflags) {
+ DB *db = NULL;
+
+ if (appName) {
+ db = rdbopen( appName, prefix, "key", openflags, NULL);
+ } else {
+ db = dbopen( dbname, openflags, 0600, DB_HASH, 0 );
+ }
+
+ return db;
+}
+
+/* check for correct version number */
+static PRBool
+verifyVersion(NSSLOWKEYDBHandle *handle)
+{
+ int version = nsslowkey_version(handle);
+
+ handle->version = version;
+ if (version != NSSLOWKEY_DB_FILE_VERSION ) {
+ if (handle->db) {
+ keydb_Close(handle);
+ handle->db = NULL;
+ }
+ }
+ return handle->db != NULL;
+}
+
+static NSSLOWKEYDBHandle *
+nsslowkey_NewHandle(DB *dbHandle)
+{
+ NSSLOWKEYDBHandle *handle;
+ handle = (NSSLOWKEYDBHandle *)PORT_ZAlloc (sizeof(NSSLOWKEYDBHandle));
+ if (handle == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ handle->appname = NULL;
+ handle->dbname = NULL;
+ handle->global_salt = NULL;
+ handle->updatedb = NULL;
+ handle->db = dbHandle;
+ handle->ref = 1;
+ handle->lock = PZ_NewLock(nssILockKeyDB);
+
+ return handle;
+}
+
+NSSLOWKEYDBHandle *
+nsslowkey_OpenKeyDB(PRBool readOnly, const char *appName, const char *prefix,
+ NSSLOWKEYDBNameFunc namecb, void *cbarg)
+{
+ NSSLOWKEYDBHandle *handle = NULL;
+ SECStatus rv;
+ int openflags;
+ char *dbname = NULL;
+
+
+ handle = nsslowkey_NewHandle(NULL);
+
+ openflags = readOnly ? NO_RDONLY : NO_RDWR;
+
+
+ dbname = (*namecb)(cbarg, NSSLOWKEY_DB_FILE_VERSION);
+ if ( dbname == NULL ) {
+ goto loser;
+ }
+ handle->appname = appName ? PORT_Strdup(appName) : NULL ;
+ handle->dbname = (appName == NULL) ? PORT_Strdup(dbname) :
+ (prefix ? PORT_Strdup(prefix) : NULL);
+ handle->readOnly = readOnly;
+
+
+
+ handle->db = openOldDB(appName, prefix, dbname, openflags);
+ if (handle->db) {
+ verifyVersion(handle);
+ if (handle->version == 255) {
+ goto loser;
+ }
+ }
+
+ /* if first open fails, try to create a new DB */
+ if ( handle->db == NULL ) {
+ if ( readOnly ) {
+ goto loser;
+ }
+
+ rv = openNewDB(appName, prefix, dbname, handle, namecb, cbarg);
+ /* two processes started to initialize the database at the same time.
+ * The multiprocess code blocked the second one, then had it retry to
+ * see if it can just open the database normally */
+ if (rv == SECWouldBlock) {
+ handle->db = openOldDB(appName,prefix,dbname, openflags);
+ verifyVersion(handle);
+ if (handle->db == NULL) {
+ goto loser;
+ }
+ } else if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+ if ( dbname )
+ PORT_Free( dbname );
+ return handle;
+
+loser:
+
+ if ( dbname )
+ PORT_Free( dbname );
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ nsslowkey_CloseKeyDB(handle);
+ return NULL;
+}
+
+/*
+ * Close the database
+ */
+void
+nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle)
+{
+ if (handle != NULL) {
+ if (handle->db != NULL) {
+ keydb_Close(handle);
+ }
+ if (handle->updatedb) {
+ handle->updatedb->close(handle->updatedb);
+ }
+ if (handle->dbname) PORT_Free(handle->dbname);
+ if (handle->appname) PORT_Free(handle->appname);
+ if (handle->global_salt) {
+ SECITEM_FreeItem(handle->global_salt,PR_TRUE);
+ }
+ if (handle->lock != NULL) {
+ PZ_DestroyLock(handle->lock);
+ }
+
+ PORT_Free(handle);
+ }
+}
+
+/* Get the key database version */
+int
+nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle)
+{
+ PORT_Assert(handle != NULL);
+
+ return handle->version;
+}
+
+/*
+ * Delete a private key that was stored in the database
+ */
+SECStatus
+nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle, const SECItem *pubkey)
+{
+ DBT namekey;
+ int ret;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* set up db key and data */
+ namekey.data = pubkey->data;
+ namekey.size = pubkey->len;
+
+ /* delete it from the database */
+ ret = keydb_Del(handle, &namekey, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * Store a key in the database, indexed by its public key modulus.(value!)
+ */
+SECStatus
+nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb)
+{
+ return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData,
+ nickname, sdb, PR_FALSE);
+}
+
+SECStatus
+nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb)
+{
+ return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData,
+ nickname, sdb, PR_TRUE);
+}
+
+/* see if the symetric CKA_ID already Exists.
+ */
+PRBool
+nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id)
+{
+ DBT namekey;
+ DBT dummy;
+ int status;
+
+ namekey.data = (char *)id->data;
+ namekey.size = id->len;
+ status = keydb_Get(handle, &namekey, &dummy, 0);
+ if ( status ) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+/* see if the public key for this cert is in the database filed
+ * by modulus
+ */
+PRBool
+nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle, NSSLOWCERTCertificate *cert)
+{
+ NSSLOWKEYPublicKey *pubkey = NULL;
+ DBT namekey;
+ DBT dummy;
+ int status;
+
+ /* get cert's public key */
+ pubkey = nsslowcert_ExtractPublicKey(cert);
+ if ( pubkey == NULL ) {
+ return PR_FALSE;
+ }
+
+ /* TNH - make key from NSSLOWKEYPublicKey */
+ switch (pubkey->keyType) {
+ case NSSLOWKEYRSAKey:
+ namekey.data = pubkey->u.rsa.modulus.data;
+ namekey.size = pubkey->u.rsa.modulus.len;
+ break;
+ case NSSLOWKEYDSAKey:
+ namekey.data = pubkey->u.dsa.publicValue.data;
+ namekey.size = pubkey->u.dsa.publicValue.len;
+ break;
+ case NSSLOWKEYDHKey:
+ namekey.data = pubkey->u.dh.publicValue.data;
+ namekey.size = pubkey->u.dh.publicValue.len;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ namekey.data = pubkey->u.ec.publicValue.data;
+ namekey.size = pubkey->u.ec.publicValue.len;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ /* XXX We don't do Fortezza or DH yet. */
+ return PR_FALSE;
+ }
+
+ if (handle->version != 3) {
+ unsigned char buf[SHA1_LENGTH];
+ SHA1_HashBuf(buf,namekey.data,namekey.size);
+ /* NOTE: don't use pubkey after this! it's now thrashed */
+ PORT_Memcpy(namekey.data,buf,sizeof(buf));
+ namekey.size = sizeof(buf);
+ }
+
+ status = keydb_Get(handle, &namekey, &dummy, 0);
+ /* some databases have the key stored as a signed value */
+ if (status) {
+ unsigned char *buf = (unsigned char *)PORT_Alloc(namekey.size+1);
+ if (buf) {
+ PORT_Memcpy(&buf[1], namekey.data, namekey.size);
+ buf[0] = 0;
+ namekey.data = buf;
+ namekey.size ++;
+ status = keydb_Get(handle, &namekey, &dummy, 0);
+ PORT_Free(buf);
+ }
+ }
+ nsslowkey_DestroyPublicKey(pubkey);
+ if ( status ) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+typedef struct NSSLowPasswordDataParamStr {
+ SECItem salt;
+ SECItem iter;
+} NSSLowPasswordDataParam;
+
+static const SEC_ASN1Template NSSLOWPasswordParamTemplate[] =
+{
+ {SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLowPasswordDataParam) },
+ {SEC_ASN1_OCTET_STRING, offsetof(NSSLowPasswordDataParam, salt) },
+ {SEC_ASN1_INTEGER, offsetof(NSSLowPasswordDataParam, iter) },
+ {0}
+};
+struct LGEncryptedDataInfoStr {
+ SECAlgorithmID algorithm;
+ SECItem encryptedData;
+};
+typedef struct LGEncryptedDataInfoStr LGEncryptedDataInfo;
+
+const SEC_ASN1Template lg_EncryptedDataInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(LGEncryptedDataInfo) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(LGEncryptedDataInfo,algorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(LGEncryptedDataInfo,encryptedData) },
+ { 0 }
+};
+static const unsigned char def_iter_data[] = { SEC_ASN1_INTEGER, 0x01, 0x01 };
+static const SECItem def_iter = { siBuffer ,
+ (unsigned char *)def_iter_data,
+ sizeof(def_iter_data) };
+
+static SECItem *
+nsslowkey_EncodePW(SECOidTag alg, const SECItem *salt, SECItem *data)
+{
+ NSSLowPasswordDataParam param;
+ LGEncryptedDataInfo edi;
+ PLArenaPool *arena;
+ unsigned char one = 1;
+ SECItem *epw = NULL;
+ SECItem *encParam;
+ SECStatus rv;
+
+ param.salt = *salt;
+ param.iter.data = &one;
+ param.iter.len = 1;
+ edi.encryptedData = *data;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ encParam = SEC_ASN1EncodeItem(arena, NULL, &param,
+ NSSLOWPasswordParamTemplate);
+ if (encParam == NULL) {
+ goto loser;
+ }
+ rv = SECOID_SetAlgorithmID(arena, &edi.algorithm, alg, encParam);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ epw = SEC_ASN1EncodeItem(NULL, NULL, &edi, lg_EncryptedDataInfoTemplate);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return epw;
+}
+
+static SECItem *
+nsslowkey_DecodePW(const SECItem *derData, SECOidTag *alg, SECItem *salt)
+{
+ NSSLowPasswordDataParam param;
+ LGEncryptedDataInfo edi;
+ PLArenaPool *arena;
+ SECItem *pwe = NULL;
+ SECStatus rv;
+
+ salt->data = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ rv = SEC_QuickDERDecodeItem(arena, &edi, lg_EncryptedDataInfoTemplate,
+ derData);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ *alg = SECOID_GetAlgorithmTag(&edi.algorithm);
+ rv = SEC_QuickDERDecodeItem(arena, &param, NSSLOWPasswordParamTemplate,
+ &edi.algorithm.parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (SECITEM_ItemsAreEqual(&param.iter, &def_iter) ) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(NULL, salt, &param.salt);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ pwe = SECITEM_DupItem(&edi.encryptedData);
+
+loser:
+ if (!pwe && salt->data) {
+ PORT_Free(salt->data);
+ salt->data = NULL;
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ return pwe;
+}
+
+
+/*
+ * check to see if the user has a password
+ */
+static SECStatus
+nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry)
+{
+ DBT checkkey; /*, checkdata; */
+ NSSLOWKEYDBKey *dbkey = NULL;
+ SECItem *global_salt = NULL;
+ SECItem *item = NULL;
+ SECItem entryData, oid;
+ SECItem none = { siBuffer, NULL, 0 };
+ SECStatus rv = SECFailure;
+ SECOidTag algorithm;
+
+ if (handle == NULL) {
+ /* PORT_SetError */
+ return(SECFailure);
+ }
+
+ global_salt = GetKeyDBGlobalSalt(handle);
+ if (!global_salt) {
+ global_salt = &none;
+ }
+ if (global_salt->len > sizeof(entry->data)) {
+ /* PORT_SetError */
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->data, global_salt->data, global_salt->len);
+ entry->salt.data = entry->data;
+ entry->salt.len = global_salt->len;
+ entry->value.data = &entry->data[entry->salt.len];
+
+ checkkey.data = KEYDB_PW_CHECK_STRING;
+ checkkey.size = KEYDB_PW_CHECK_LEN;
+ dbkey = get_dbkey(handle, &checkkey);
+ if (dbkey == NULL) {
+ /* handle 'FAKE' check here */
+ goto loser;
+ }
+
+ oid.len = dbkey->derPK.data[0];
+ oid.data = &dbkey->derPK.data[1];
+
+ if (dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 +oid.len)) {
+ goto loser;
+ }
+ algorithm = SECOID_FindOIDTag(&oid);
+ entryData.type = siBuffer;
+ entryData.len = dbkey->derPK.len - (oid.len+1);
+ entryData.data = &dbkey->derPK.data[oid.len+1];
+
+ item = nsslowkey_EncodePW(algorithm, &dbkey->salt, &entryData);
+ if (!item || (item->len + entry->salt.len) > sizeof(entry->data)) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->value.data, item->data, item->len);
+ entry->value.len = item->len;
+ rv = SECSuccess;
+
+loser:
+ if (item) {
+ SECITEM_FreeItem(item, PR_TRUE);
+ }
+ if (dbkey) {
+ sec_destroy_dbkey(dbkey);
+ }
+ if (global_salt && global_salt != &none) {
+ SECITEM_FreeItem(global_salt,PR_TRUE);
+ }
+ return rv;
+}
+
+/*
+ * check to see if the user has a password
+ */
+static SECStatus
+nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry)
+{
+ DBT checkkey;
+ NSSLOWKEYDBKey *dbkey = NULL;
+ SECItem *item = NULL;
+ SECItem salt;
+ SECOidTag algid;
+ SECStatus rv = SECFailure;
+ PLArenaPool *arena;
+ int ret;
+
+ if (handle == NULL) {
+ /* PORT_SetError */
+ return(SECFailure);
+ }
+
+ checkkey.data = KEYDB_PW_CHECK_STRING;
+ checkkey.size = KEYDB_PW_CHECK_LEN;
+
+ salt.data = NULL;
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return SECFailure;
+ }
+
+ item = nsslowkey_DecodePW(&entry->value, &algid, &salt);
+ if (item == NULL) {
+ goto loser;
+ }
+
+ dbkey = PORT_ArenaZNew(arena, NSSLOWKEYDBKey);
+ if (dbkey == NULL) {
+ goto loser;
+ }
+
+ dbkey->arena = arena;
+
+ rv = SECITEM_CopyItem(arena, &dbkey->salt, &salt);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = encodePWCheckEntry(arena, &dbkey->derPK, algid, item);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (handle->global_salt) {
+ SECITEM_FreeItem(handle->global_salt, PR_TRUE);
+ handle->global_salt = NULL;
+ }
+ rv = StoreKeyDBGlobalSalt(handle, &entry->salt);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ ret = keydb_Sync(handle, 0);
+ if ( ret ) {
+ rv = SECFailure;
+ goto loser;
+ }
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+
+loser:
+ if (item) {
+ SECITEM_FreeItem(item, PR_TRUE);
+ }
+ if (arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+ if (salt.data) {
+ PORT_Free(salt.data);
+ }
+ return rv;
+}
+
+#ifdef EC_DEBUG
+#define SEC_PRINT(str1, str2, num, sitem) \
+ printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
+ str1, str2, num, sitem->len); \
+ for (i = 0; i < sitem->len; i++) { \
+ printf("%02x:", sitem->data[i]); \
+ } \
+ printf("\n")
+#else
+#define SEC_PRINT(a, b, c, d)
+#endif /* EC_DEBUG */
+
+
+SECStatus
+seckey_encrypt_private_key( PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk,
+ SDB *sdbpw, SECItem *result)
+{
+ NSSLOWKEYPrivateKeyInfo *pki = NULL;
+ SECStatus rv = SECFailure;
+ PLArenaPool *temparena = NULL;
+ SECItem *der_item = NULL;
+ SECItem *cipherText = NULL;
+ SECItem *dummy = NULL;
+#ifdef NSS_ENABLE_ECC
+ SECItem *fordebug = NULL;
+ int savelen;
+#endif
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(temparena == NULL)
+ goto loser;
+
+ /* allocate structures */
+ pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
+ sizeof(NSSLOWKEYPrivateKeyInfo));
+ der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem));
+ if((pki == NULL) || (der_item == NULL))
+ goto loser;
+
+
+ /* setup private key info */
+ dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version),
+ NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
+ if(dummy == NULL)
+ goto loser;
+
+ /* Encode the key, and set the algorithm (with params) */
+ switch (pk->keyType) {
+ case NSSLOWKEYRSAKey:
+ prepare_low_rsa_priv_key_for_asn1(pk);
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_RSAPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_PKCS1_RSA_ENCRYPTION, 0);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ break;
+ case NSSLOWKEYDSAKey:
+ prepare_low_dsa_priv_key_for_asn1(pk);
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_DSAPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
+ dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params,
+ nsslowkey_PQGParamsTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_ANSIX9_DSA_SIGNATURE, dummy);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ break;
+ case NSSLOWKEYDHKey:
+ prepare_low_dh_priv_key_for_asn1(pk);
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_DHPrivateKeyTemplate);
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ prepare_low_ec_priv_key_for_asn1(pk);
+ /* Public value is encoded as a bit string so adjust length
+ * to be in bits before ASN encoding and readjust
+ * immediately after.
+ *
+ * Since the SECG specification recommends not including the
+ * parameters as part of ECPrivateKey, we zero out the curveOID
+ * length before encoding and restore it later.
+ */
+ pk->u.ec.publicValue.len <<= 3;
+ savelen = pk->u.ec.ecParams.curveOID.len;
+ pk->u.ec.ecParams.curveOID.len = 0;
+ dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk,
+ nsslowkey_ECPrivateKeyTemplate);
+ pk->u.ec.ecParams.curveOID.len = savelen;
+ pk->u.ec.publicValue.len >>= 3;
+
+ if (dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ dummy = &pk->u.ec.ecParams.DEREncoding;
+
+ /* At this point dummy should contain the encoded params */
+ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm),
+ SEC_OID_ANSIX962_EC_PUBLIC_KEY, dummy);
+
+ if (rv == SECFailure) {
+ goto loser;
+ }
+
+ fordebug = &(pki->privateKey);
+ SEC_PRINT("seckey_encrypt_private_key()", "PrivateKey",
+ pk->keyType, fordebug);
+
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ /* We don't support DH or Fortezza private keys yet */
+ PORT_Assert(PR_FALSE);
+ break;
+ }
+
+ /* setup encrypted private key info */
+ dummy = SEC_ASN1EncodeItem(temparena, der_item, pki,
+ nsslowkey_PrivateKeyInfoTemplate);
+
+ SEC_PRINT("seckey_encrypt_private_key()", "PrivateKeyInfo",
+ pk->keyType, der_item);
+
+ if(dummy == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = lg_util_encrypt(temparena, sdbpw, dummy, &cipherText);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = SECITEM_CopyItem ( permarena, result, cipherText);
+
+loser:
+
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+
+ return rv;
+}
+
+static SECStatus
+seckey_put_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, SDB *sdbpw,
+ NSSLOWKEYPrivateKey *pk, char *nickname, PRBool update)
+{
+ NSSLOWKEYDBKey *dbkey = NULL;
+ PLArenaPool *arena = NULL;
+ SECStatus rv = SECFailure;
+
+ if((keydb == NULL) || (index == NULL) || (sdbpw == NULL) ||
+ (pk == NULL))
+ return SECFailure;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if(arena == NULL)
+ return SECFailure;
+
+ dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey));
+ if(dbkey == NULL)
+ goto loser;
+ dbkey->arena = arena;
+ dbkey->nickname = nickname;
+
+ rv = seckey_encrypt_private_key(arena, pk, sdbpw, &dbkey->derPK);
+ if(rv != SECSuccess)
+ goto loser;
+
+ rv = put_dbkey(keydb, index, dbkey, update);
+
+ /* let success fall through */
+loser:
+ if(arena != NULL)
+ PORT_FreeArena(arena, PR_TRUE);
+
+ return rv;
+}
+
+/*
+ * Store a key in the database, indexed by its public key modulus.
+ * Note that the nickname is optional. It was only used by keyutil.
+ */
+SECStatus
+nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdbpw,
+ PRBool update)
+{
+ DBT namekey;
+ SECStatus rv;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+ }
+
+ /* set up db key and data */
+ namekey.data = pubKeyData->data;
+ namekey.size = pubKeyData->len;
+
+ /* encrypt the private key */
+ rv = seckey_put_private_key(handle, &namekey, sdbpw, privkey, nickname,
+ update);
+
+ return(rv);
+}
+
+static NSSLOWKEYPrivateKey *
+seckey_decrypt_private_key(SECItem*epki,
+ SDB *sdbpw)
+{
+ NSSLOWKEYPrivateKey *pk = NULL;
+ NSSLOWKEYPrivateKeyInfo *pki = NULL;
+ SECStatus rv = SECFailure;
+ PLArenaPool *temparena = NULL, *permarena = NULL;
+ SECItem *dest = NULL;
+#ifdef NSS_ENABLE_ECC
+ SECItem *fordebug = NULL;
+#endif
+
+ if((epki == NULL) || (sdbpw == NULL))
+ goto loser;
+
+ temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if((temparena == NULL) || (permarena == NULL))
+ goto loser;
+
+ /* allocate temporary items */
+ pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena,
+ sizeof(NSSLOWKEYPrivateKeyInfo));
+
+ /* allocate permanent arena items */
+ pk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(permarena,
+ sizeof(NSSLOWKEYPrivateKey));
+
+ if((pk == NULL) || (pki == NULL))
+ goto loser;
+
+ pk->arena = permarena;
+
+ rv = lg_util_decrypt(sdbpw, epki, &dest);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if(dest != NULL)
+ {
+ SECItem newPrivateKey;
+ SECItem newAlgParms;
+
+ SEC_PRINT("seckey_decrypt_private_key()", "PrivateKeyInfo", -1,
+ dest);
+
+ rv = SEC_QuickDERDecodeItem(temparena, pki,
+ nsslowkey_PrivateKeyInfoTemplate, dest);
+ if(rv == SECSuccess)
+ {
+ switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ pk->keyType = NSSLOWKEYRSAKey;
+ prepare_low_rsa_priv_key_for_asn1(pk);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_RSAPrivateKeyTemplate,
+ &newPrivateKey);
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ pk->keyType = NSSLOWKEYDSAKey;
+ prepare_low_dsa_priv_key_for_asn1(pk);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_DSAPrivateKeyTemplate,
+ &newPrivateKey);
+ if (rv != SECSuccess)
+ goto loser;
+ prepare_low_pqg_params_for_asn1(&pk->u.dsa.params);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newAlgParms,
+ &pki->algorithm.parameters) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, &pk->u.dsa.params,
+ nsslowkey_PQGParamsTemplate,
+ &newAlgParms);
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ pk->keyType = NSSLOWKEYDHKey;
+ prepare_low_dh_priv_key_for_asn1(pk);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_DHPrivateKeyTemplate,
+ &newPrivateKey);
+ break;
+#ifdef NSS_ENABLE_ECC
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ pk->keyType = NSSLOWKEYECKey;
+ prepare_low_ec_priv_key_for_asn1(pk);
+
+ fordebug = &pki->privateKey;
+ SEC_PRINT("seckey_decrypt_private_key()", "PrivateKey",
+ pk->keyType, fordebug);
+ if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey,
+ &pki->privateKey) ) break;
+ rv = SEC_QuickDERDecodeItem(permarena, pk,
+ nsslowkey_ECPrivateKeyTemplate,
+ &newPrivateKey);
+ if (rv != SECSuccess)
+ goto loser;
+
+ prepare_low_ecparams_for_asn1(&pk->u.ec.ecParams);
+
+ rv = SECITEM_CopyItem(permarena,
+ &pk->u.ec.ecParams.DEREncoding,
+ &pki->algorithm.parameters);
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ /* Fill out the rest of EC params */
+ rv = LGEC_FillParams(permarena, &pk->u.ec.ecParams.DEREncoding,
+ &pk->u.ec.ecParams);
+
+ if (rv != SECSuccess)
+ goto loser;
+
+ if (pk->u.ec.publicValue.len != 0) {
+ pk->u.ec.publicValue.len >>= 3;
+ }
+
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ rv = SECFailure;
+ break;
+ }
+ }
+ else if(PORT_GetError() == SEC_ERROR_BAD_DER)
+ {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ goto loser;
+ }
+ }
+
+ /* let success fall through */
+loser:
+ if(temparena != NULL)
+ PORT_FreeArena(temparena, PR_TRUE);
+ if(dest != NULL)
+ SECITEM_ZfreeItem(dest, PR_TRUE);
+
+ if(rv != SECSuccess)
+ {
+ if(permarena != NULL)
+ PORT_FreeArena(permarena, PR_TRUE);
+ pk = NULL;
+ }
+
+ return pk;
+}
+
+static NSSLOWKEYPrivateKey *
+seckey_decode_encrypted_private_key(NSSLOWKEYDBKey *dbkey, SDB *sdbpw)
+{
+ if( ( dbkey == NULL ) || ( sdbpw == NULL ) ) {
+ return NULL;
+ }
+
+ return seckey_decrypt_private_key(&(dbkey->derPK), sdbpw);
+}
+
+static NSSLOWKEYPrivateKey *
+seckey_get_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, char **nickname,
+ SDB *sdbpw)
+{
+ NSSLOWKEYDBKey *dbkey = NULL;
+ NSSLOWKEYPrivateKey *pk = NULL;
+
+ if( ( keydb == NULL ) || ( index == NULL ) || ( sdbpw == NULL ) ) {
+ return NULL;
+ }
+
+ dbkey = get_dbkey(keydb, index);
+ if(dbkey == NULL) {
+ goto loser;
+ }
+
+ if ( nickname ) {
+ if ( dbkey->nickname && ( dbkey->nickname[0] != 0 ) ) {
+ *nickname = PORT_Strdup(dbkey->nickname);
+ } else {
+ *nickname = NULL;
+ }
+ }
+
+ pk = seckey_decode_encrypted_private_key(dbkey, sdbpw);
+
+ /* let success fall through */
+loser:
+
+ if ( dbkey != NULL ) {
+ sec_destroy_dbkey(dbkey);
+ }
+
+ return pk;
+}
+
+/*
+ * Find a key in the database, indexed by its public key modulus
+ * This is used to find keys that have been stored before their
+ * certificate arrives. Once the certificate arrives the key
+ * is looked up by the public modulus in the certificate, and the
+ * re-stored by its nickname.
+ */
+NSSLOWKEYPrivateKey *
+nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus,
+ SDB *sdbpw)
+{
+ DBT namekey;
+ NSSLOWKEYPrivateKey *pk = NULL;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up db key */
+ namekey.data = modulus->data;
+ namekey.size = modulus->len;
+
+ pk = seckey_get_private_key(handle, &namekey, NULL, sdbpw);
+
+ /* no need to free dbkey, since its on the stack, and the data it
+ * points to is owned by the database
+ */
+ return(pk);
+}
+
+char *
+nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle,
+ SECItem *modulus, SDB *sdbpw)
+{
+ DBT namekey;
+ NSSLOWKEYPrivateKey *pk = NULL;
+ char *nickname = NULL;
+
+ if (handle == NULL) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return NULL;
+ }
+
+ /* set up db key */
+ namekey.data = modulus->data;
+ namekey.size = modulus->len;
+
+ pk = seckey_get_private_key(handle, &namekey, &nickname, sdbpw);
+ if (pk) {
+ nsslowkey_DestroyPrivateKey(pk);
+ }
+
+ /* no need to free dbkey, since its on the stack, and the data it
+ * points to is owned by the database
+ */
+ return(nickname);
+}
+/* ===== ENCODING ROUTINES ===== */
+
+static SECStatus
+encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg,
+ SECItem *encCheck)
+{
+ SECOidData *oidData;
+ SECStatus rv;
+
+ oidData = SECOID_FindOIDByTag(alg);
+ if ( oidData == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ entry->len = 1 + oidData->oid.len + encCheck->len;
+ if ( arena ) {
+ entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len);
+ } else {
+ entry->data = (unsigned char *)PORT_Alloc(entry->len);
+ }
+
+ if ( entry->data == NULL ) {
+ goto loser;
+ }
+
+ /* first length of oid */
+ entry->data[0] = (unsigned char)oidData->oid.len;
+ /* next oid itself */
+ PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len);
+ /* finally the encrypted check string */
+ PORT_Memcpy(&entry->data[1+oidData->oid.len], encCheck->data,
+ encCheck->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+
+#define MAX_DB_SIZE 0xffff
+/*
+ * Clear out all the keys in the existing database
+ */
+static SECStatus
+nsslowkey_ResetKeyDB(NSSLOWKEYDBHandle *handle)
+{
+ SECStatus rv;
+ int ret;
+ int errors = 0;
+
+ if ( handle->db == NULL ) {
+ return(SECSuccess);
+ }
+
+ if (handle->readOnly) {
+ /* set an error code */
+ return SECFailure;
+ }
+
+ if (handle->appname == NULL && handle->dbname == NULL) {
+ return SECFailure;
+ }
+
+ keydb_Close(handle);
+ if (handle->appname) {
+ handle->db=
+ rdbopen(handle->appname, handle->dbname, "key", NO_CREATE, NULL);
+ } else {
+ handle->db = dbopen( handle->dbname, NO_CREATE, 0600, DB_HASH, 0 );
+ }
+ if (handle->db == NULL) {
+ /* set an error code */
+ return SECFailure;
+ }
+
+ rv = makeGlobalVersion(handle);
+ if ( rv != SECSuccess ) {
+ errors++;
+ goto done;
+ }
+
+ if (handle->global_salt) {
+ rv = StoreKeyDBGlobalSalt(handle, handle->global_salt);
+ } else {
+ rv = makeGlobalSalt(handle);
+ if ( rv == SECSuccess ) {
+ handle->global_salt = GetKeyDBGlobalSalt(handle);
+ }
+ }
+ if ( rv != SECSuccess ) {
+ errors++;
+ }
+
+done:
+ /* sync the database */
+ ret = keydb_Sync(handle, 0);
+ db_InitComplete(handle->db);
+
+ return (errors == 0 ? SECSuccess : SECFailure);
+}
+
+static int
+keydb_Get(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->get)(db, key, data, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Put(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret = 0;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->put)(db, key, data, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Sync(NSSLOWKEYDBHandle *kdb, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->sync)(db, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Del(NSSLOWKEYDBHandle *kdb, DBT *key, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->del)(db, key, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static int
+keydb_Seq(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ ret = (* db->seq)(db, key, data, flags);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return(ret);
+}
+
+static void
+keydb_Close(NSSLOWKEYDBHandle *kdb)
+{
+ PRStatus prstat;
+ PRLock *kdbLock = kdb->lock;
+ DB *db = kdb->db;
+
+ PORT_Assert(kdbLock != NULL);
+ PZ_Lock(kdbLock);
+
+ (* db->close)(db);
+
+ prstat = PZ_Unlock(kdbLock);
+
+ return;
+}
+
+/*
+ * SDB Entry Points for the Key DB
+ */
+
+CK_RV
+lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
+{
+ NSSLOWKEYDBHandle *keydb;
+ NSSLOWKEYPasswordEntry entry;
+ SECStatus rv;
+
+ keydb = lg_getKeyDB(sdb);
+ if (keydb == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ if (PORT_Strcmp(id,"password") != 0) {
+ /* shouldn't happen */
+ return CKR_GENERAL_ERROR; /* no extra data stored */
+ }
+ rv = nsslowkey_GetPWCheckEntry(keydb, &entry);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ item1->len = entry.salt.len;
+ PORT_Memcpy(item1->data, entry.salt.data, item1->len);
+ item2->len = entry.value.len;
+ PORT_Memcpy(item2->data, entry.value.data, item2->len);
+ return CKR_OK;
+}
+
+CK_RV
+lg_PutMetaData(SDB *sdb, const char *id,
+ const SECItem *item1, const SECItem *item2)
+{
+ NSSLOWKEYDBHandle *keydb;
+ NSSLOWKEYPasswordEntry entry;
+ SECStatus rv;
+
+ keydb = lg_getKeyDB(sdb);
+ if (keydb == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ if (PORT_Strcmp(id,"password") != 0) {
+ /* shouldn't happen */
+ return CKR_GENERAL_ERROR; /* no extra data stored */
+ }
+ entry.salt = *item1;
+ entry.value = *item2;
+ rv = nsslowkey_PutPWCheckEntry(keydb, &entry);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_Reset(SDB *sdb)
+{
+ NSSLOWKEYDBHandle *keydb;
+ SECStatus rv;
+
+ keydb = lg_getKeyDB(sdb);
+ if (keydb == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ rv = nsslowkey_ResetKeyDB(keydb);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ return CKR_OK;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/keydbi.h b/security/nss/lib/softoken/legacydb/keydbi.h
new file mode 100644
index 000000000..f7f427266
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/keydbi.h
@@ -0,0 +1,86 @@
+/*
+ * private.h - Private data structures for the software token library
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#ifndef _KEYDBI_H_
+#define _KEYDBI_H_
+
+#include "nspr.h"
+#include "seccomon.h"
+#include "mcom_db.h"
+
+/*
+ * Handle structure for open key databases
+ */
+struct NSSLOWKEYDBHandleStr {
+ DB *db;
+ DB *updatedb; /* used when updating an old version */
+ SECItem *global_salt; /* password hashing salt for this db */
+ int version; /* version of the database */
+ char *appname; /* multiaccess app name */
+ char *dbname; /* name of the openned DB */
+ PRBool readOnly; /* is the DB read only */
+ PRLock *lock;
+ PRInt32 ref; /* reference count */
+};
+
+/*
+** Typedef for callback for traversing key database.
+** "key" is the key used to index the data in the database (nickname)
+** "data" is the key data
+** "pdata" is the user's data
+*/
+typedef SECStatus (* NSSLOWKEYTraverseKeysFunc)(DBT *key, DBT *data, void *pdata);
+
+
+SEC_BEGIN_PROTOS
+
+/*
+** Traverse the entire key database, and pass the nicknames and keys to a
+** user supplied function.
+** "f" is the user function to call for each key
+** "udata" is the user's data, which is passed through to "f"
+*/
+extern SECStatus nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYTraverseKeysFunc f,
+ void *udata);
+
+SEC_END_PROTOS
+
+#endif /* _KEYDBI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/lgattr.c b/security/nss/lib/softoken/legacydb/lgattr.c
new file mode 100644
index 000000000..15cfc63ec
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgattr.c
@@ -0,0 +1,1808 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * Internal PKCS #11 functions. Should only be called by pkcs11.c
+ */
+#include "pkcs11.h"
+#include "lgdb.h"
+
+#include "pcertt.h"
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "blapi.h"
+#include "secerr.h"
+
+/*
+ * Cache the object we are working on during Set's and Get's
+ */
+typedef struct LGObjectCacheStr {
+ CK_OBJECT_CLASS objclass;
+ CK_OBJECT_HANDLE handle;
+ SDB *sdb;
+ void *objectInfo;
+ LGFreeFunc infoFree;
+ SECItem dbKey;
+} LGObjectCache;
+
+static const CK_OBJECT_HANDLE lg_classArray[] = {
+ 0, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY, CKO_SECRET_KEY,
+ CKO_NETSCAPE_TRUST, CKO_NETSCAPE_CRL, CKO_NETSCAPE_SMIME,
+ CKO_CERTIFICATE };
+
+#define handleToClass(handle) \
+ lg_classArray[((handle & LG_TOKEN_TYPE_MASK))>>LG_TOKEN_TYPE_SHIFT]
+
+
+static void lg_DestroyObjectCache(LGObjectCache *obj);
+
+static LGObjectCache *
+lg_NewObjectCache(SDB *sdb, const SECItem *dbKey, CK_OBJECT_HANDLE handle)
+{
+ LGObjectCache *obj = NULL;
+ SECStatus rv;
+
+ obj = PORT_New(LGObjectCache);
+ if (obj == NULL) {
+ return NULL;
+ }
+
+ obj->objclass = handleToClass(handle);
+ obj->handle = handle;
+ obj->sdb = sdb;
+ obj->objectInfo = NULL;
+ obj->infoFree = NULL;
+ obj->dbKey.data = NULL;
+ obj->dbKey.len = 0;
+ lg_DBLock(sdb);
+ if (dbKey == NULL) {
+ dbKey = lg_lookupTokenKeyByHandle(sdb,handle);
+ }
+ if (dbKey == NULL) {
+ lg_DBUnlock(sdb);
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(NULL,&obj->dbKey,dbKey);
+ lg_DBUnlock(sdb);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ return obj;
+loser:
+ if (obj) {
+ (void) lg_DestroyObjectCache(obj);
+ }
+ return NULL;
+
+}
+
+/*
+ * free all the data associated with an object. Object reference count must
+ * be 'zero'.
+ */
+static void
+lg_DestroyObjectCache(LGObjectCache *obj)
+{
+ if (obj->dbKey.data) {
+ PORT_Free(obj->dbKey.data);
+ obj->dbKey.data = NULL;
+ }
+ if (obj->objectInfo) {
+ (*obj->infoFree)(obj->objectInfo);
+ obj->objectInfo = NULL;
+ obj->infoFree = NULL;
+ }
+ PORT_Free(obj);
+}
+/*
+ * ******************** Attribute Utilities *******************************
+ */
+
+static CK_RV
+lg_ULongAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE_TYPE type, CK_ULONG value)
+{
+ unsigned char *data;
+ int i;
+
+ if (attr->pValue == NULL) {
+ attr->ulValueLen = 4;
+ return CKR_OK;
+ }
+ if (attr->ulValueLen < 4) {
+ attr->ulValueLen = (CK_ULONG) -1;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+
+ data = (unsigned char *)attr->pValue;
+ for (i=0; i < 4; i++) {
+ data[i] = (value >> ((3-i)*8)) & 0xff;
+ }
+ attr->ulValueLen = 4;
+ return CKR_OK;
+}
+
+static CK_RV
+lg_CopyAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE_TYPE type,
+ CK_VOID_PTR value, CK_ULONG len)
+{
+
+ if (attr->pValue == NULL) {
+ attr->ulValueLen = len;
+ return CKR_OK;
+ }
+ if (attr->ulValueLen < len) {
+ attr->ulValueLen = (CK_ULONG) -1;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ PORT_Memcpy(attr->pValue,value,len);
+ attr->ulValueLen = len;
+ return CKR_OK;
+}
+
+static CK_RV
+lg_CopyAttributeSigned(CK_ATTRIBUTE *attribute, CK_ATTRIBUTE_TYPE type,
+ void *value, CK_ULONG len)
+{
+ unsigned char * dval = (unsigned char *)value;
+ if (*dval == 0) {
+ dval++;
+ len--;
+ }
+ return lg_CopyAttribute(attribute,type,dval,len);
+}
+
+static CK_RV
+lg_CopyPrivAttribute(CK_ATTRIBUTE *attribute, CK_ATTRIBUTE_TYPE type,
+ void *value, CK_ULONG len, SDB *sdbpw)
+{
+ SECItem plainText, *cipherText = NULL;
+ CK_RV crv = CKR_USER_NOT_LOGGED_IN;
+ SECStatus rv;
+
+ plainText.data = value;
+ plainText.len = len;
+ rv = lg_util_encrypt(NULL, sdbpw, &plainText, &cipherText);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ crv = lg_CopyAttribute(attribute,type,cipherText->data,cipherText->len);
+loser:
+ if (cipherText) {
+ SECITEM_FreeItem(cipherText,PR_TRUE);
+ }
+ return crv;
+}
+
+static CK_RV
+lg_CopyPrivAttrSigned(CK_ATTRIBUTE *attribute, CK_ATTRIBUTE_TYPE type,
+ void *value, CK_ULONG len, SDB *sdbpw)
+{
+ unsigned char * dval = (unsigned char *)value;
+
+ if (*dval == 0) {
+ dval++;
+ len--;
+ }
+ return lg_CopyPrivAttribute(attribute,type,dval,len,sdbpw);
+}
+
+static CK_RV
+lg_invalidAttribute(CK_ATTRIBUTE *attr)
+{
+ attr->ulValueLen = (CK_ULONG) -1;
+ return CKR_ATTRIBUTE_TYPE_INVALID;
+}
+
+
+#define LG_DEF_ATTRIBUTE(value,len) \
+ { 0, value, len }
+
+#define LG_CLONE_ATTR(attribute, type, staticAttr) \
+ lg_CopyAttribute(attribute, type, staticAttr.pValue, staticAttr.ulValueLen)
+
+CK_BBOOL lg_staticTrueValue = CK_TRUE;
+CK_BBOOL lg_staticFalseValue = CK_FALSE;
+static const CK_ATTRIBUTE lg_StaticTrueAttr =
+ LG_DEF_ATTRIBUTE(&lg_staticTrueValue,sizeof(lg_staticTrueValue));
+static const CK_ATTRIBUTE lg_StaticFalseAttr =
+ LG_DEF_ATTRIBUTE(&lg_staticFalseValue,sizeof(lg_staticFalseValue));
+static const CK_ATTRIBUTE lg_StaticNullAttr = LG_DEF_ATTRIBUTE(NULL,0);
+char lg_StaticOneValue = 1;
+static const CK_ATTRIBUTE lg_StaticOneAttr =
+ LG_DEF_ATTRIBUTE(&lg_StaticOneValue,sizeof(lg_StaticOneValue));
+
+/*
+ * helper functions which get the database and call the underlying
+ * low level database function.
+ */
+static char *
+lg_FindKeyNicknameByPublicKey(SDB *sdb, SECItem *dbKey)
+{
+ NSSLOWKEYDBHandle *keyHandle;
+ char * label;
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (!keyHandle) {
+ return NULL;
+ }
+
+ label = nsslowkey_FindKeyNicknameByPublicKey(keyHandle, dbKey,
+ sdb);
+ return label;
+}
+
+
+NSSLOWKEYPrivateKey *
+lg_FindKeyByPublicKey(SDB *sdb, SECItem *dbKey)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ NSSLOWKEYDBHandle *keyHandle;
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle == NULL) {
+ return NULL;
+ }
+ privKey = nsslowkey_FindKeyByPublicKey(keyHandle, dbKey, sdb);
+ if (privKey == NULL) {
+ return NULL;
+ }
+ return privKey;
+}
+
+static certDBEntrySMime *
+lg_getSMime(LGObjectCache *obj)
+{
+ certDBEntrySMime *entry;
+ NSSLOWCERTCertDBHandle *certHandle;
+
+ if (obj->objclass != CKO_NETSCAPE_SMIME) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (certDBEntrySMime *)obj->objectInfo;
+ }
+
+ certHandle = lg_getCertDB(obj->sdb);
+ if (!certHandle) {
+ return NULL;
+ }
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle, (char *)obj->dbKey.data);
+ obj->objectInfo = (void *)entry;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyDBEntry;
+ return entry;
+}
+
+static certDBEntryRevocation *
+lg_getCrl(LGObjectCache *obj)
+{
+ certDBEntryRevocation *crl;
+ PRBool isKrl;
+ NSSLOWCERTCertDBHandle *certHandle;
+
+ if (obj->objclass != CKO_NETSCAPE_CRL) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (certDBEntryRevocation *)obj->objectInfo;
+ }
+
+ isKrl = (PRBool) (obj->handle == LG_TOKEN_KRL_HANDLE);
+ certHandle = lg_getCertDB(obj->sdb);
+ if (!certHandle) {
+ return NULL;
+ }
+
+ crl = nsslowcert_FindCrlByKey(certHandle, &obj->dbKey, isKrl);
+ obj->objectInfo = (void *)crl;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyDBEntry;
+ return crl;
+}
+
+static NSSLOWCERTCertificate *
+lg_getCert(LGObjectCache *obj, NSSLOWCERTCertDBHandle *certHandle)
+{
+ NSSLOWCERTCertificate *cert;
+ CK_OBJECT_CLASS objClass = obj->objclass;
+
+ if ((objClass != CKO_CERTIFICATE) && (objClass != CKO_NETSCAPE_TRUST)) {
+ return NULL;
+ }
+ if (objClass == CKO_CERTIFICATE && obj->objectInfo) {
+ return (NSSLOWCERTCertificate *)obj->objectInfo;
+ }
+ cert = nsslowcert_FindCertByKey(certHandle, &obj->dbKey);
+ if (objClass == CKO_CERTIFICATE) {
+ obj->objectInfo = (void *)cert;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyCertificate ;
+ }
+ return cert;
+}
+
+static NSSLOWCERTTrust *
+lg_getTrust(LGObjectCache *obj, NSSLOWCERTCertDBHandle *certHandle)
+{
+ NSSLOWCERTTrust *trust;
+
+ if (obj->objclass != CKO_NETSCAPE_TRUST) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (NSSLOWCERTTrust *)obj->objectInfo;
+ }
+ trust = nsslowcert_FindTrustByKey(certHandle, &obj->dbKey);
+ obj->objectInfo = (void *)trust;
+ obj->infoFree = (LGFreeFunc) nsslowcert_DestroyTrust ;
+ return trust;
+}
+
+static NSSLOWKEYPublicKey *
+lg_GetPublicKey(LGObjectCache *obj)
+{
+ NSSLOWKEYPublicKey *pubKey;
+ NSSLOWKEYPrivateKey *privKey;
+
+ if (obj->objclass != CKO_PUBLIC_KEY) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (NSSLOWKEYPublicKey *)obj->objectInfo;
+ }
+ privKey = lg_FindKeyByPublicKey(obj->sdb, &obj->dbKey);
+ if (privKey == NULL) {
+ return NULL;
+ }
+ pubKey = nsslowkey_ConvertToPublicKey(privKey);
+ nsslowkey_DestroyPrivateKey(privKey);
+ obj->objectInfo = (void *) pubKey;
+ obj->infoFree = (LGFreeFunc) nsslowkey_DestroyPublicKey ;
+ return pubKey;
+}
+
+/*
+ * we need two versions of lg_GetPrivateKey. One version that takes the
+ * DB handle so we can pass the handle we have already acquired in,
+ * rather than going through the 'getKeyDB' code again,
+ * which may fail the second time and another which just aquires
+ * the key handle from the sdb (where we don't already have a key handle.
+ * This version does the former.
+ */
+static NSSLOWKEYPrivateKey *
+lg_GetPrivateKeyWithDB(LGObjectCache *obj, NSSLOWKEYDBHandle *keyHandle)
+{
+ NSSLOWKEYPrivateKey *privKey;
+
+ if ((obj->objclass != CKO_PRIVATE_KEY) &&
+ (obj->objclass != CKO_SECRET_KEY)) {
+ return NULL;
+ }
+ if (obj->objectInfo) {
+ return (NSSLOWKEYPrivateKey *)obj->objectInfo;
+ }
+ privKey = nsslowkey_FindKeyByPublicKey(keyHandle, &obj->dbKey, obj->sdb);
+ if (privKey == NULL) {
+ return NULL;
+ }
+ obj->objectInfo = (void *) privKey;
+ obj->infoFree = (LGFreeFunc) nsslowkey_DestroyPrivateKey ;
+ return privKey;
+}
+
+/* this version does the latter */
+static NSSLOWKEYPrivateKey *
+lg_GetPrivateKey(LGObjectCache *obj)
+{
+ NSSLOWKEYDBHandle *keyHandle;
+ NSSLOWKEYPrivateKey *privKey;
+
+ keyHandle = lg_getKeyDB(obj->sdb);
+ if (!keyHandle) {
+ return NULL;
+ }
+ privKey = lg_GetPrivateKeyWithDB(obj, keyHandle);
+ return privKey;
+}
+
+/* lg_GetPubItem returns data associated with the public key.
+ * one only needs to free the public key. This comment is here
+ * because this sematic would be non-obvious otherwise. All callers
+ * should include this comment.
+ */
+static SECItem *
+lg_GetPubItem(NSSLOWKEYPublicKey *pubKey) {
+ SECItem *pubItem = NULL;
+ /* get value to compare from the cert's public key */
+ switch ( pubKey->keyType ) {
+ case NSSLOWKEYRSAKey:
+ pubItem = &pubKey->u.rsa.modulus;
+ break;
+ case NSSLOWKEYDSAKey:
+ pubItem = &pubKey->u.dsa.publicValue;
+ break;
+ case NSSLOWKEYDHKey:
+ pubItem = &pubKey->u.dh.publicValue;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ pubItem = &pubKey->u.ec.publicValue;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ break;
+ }
+ return pubItem;
+}
+
+static const SEC_ASN1Template lg_SerialTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWCERTCertificate,serialNumber) },
+ { 0 }
+};
+
+static CK_RV
+lg_FindRSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_RSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_MODULUS:
+ return lg_CopyAttributeSigned(attribute,type,key->u.rsa.modulus.data,
+ key->u.rsa.modulus.len);
+ case CKA_PUBLIC_EXPONENT:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.rsa.publicExponent.data,
+ key->u.rsa.publicExponent.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VERIFY:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_VALUE:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.params.prime.data,
+ key->u.dsa.params.prime.len);
+ case CKA_SUBPRIME:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.params.subPrime.data,
+ key->u.dsa.params.subPrime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dsa.params.base.data,
+ key->u.dsa.params.base.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDHPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DH;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VALUE:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.dh.publicValue.data,
+ key->u.dh.publicValue.len);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute,type,key->u.dh.prime.data,
+ key->u.dh.prime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute,type,key->u.dh.base.data,
+ key->u.dh.base.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+#ifdef NSS_ENABLE_ECC
+static CK_RV
+lg_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_EC;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash, key->u.ec.publicValue.data,
+ key->u.ec.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_VERIFY:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_ENCRYPT:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_EC_PARAMS:
+ return lg_CopyAttributeSigned(attribute,type,
+ key->u.ec.ecParams.DEREncoding.data,
+ key->u.ec.ecParams.DEREncoding.len);
+ case CKA_EC_POINT:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.ec.publicValue.data,
+ key->u.ec.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+#endif /* NSS_ENABLE_ECC */
+
+
+static CK_RV
+lg_FindPublicKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWKEYPublicKey *key;
+ CK_RV crv;
+ char *label;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_NEVER_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_MODIFIABLE:
+ case CKA_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_SUBJECT:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_LABEL:
+ label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+ if (label == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ crv = lg_CopyAttribute(attribute,type,label,PORT_Strlen(label));
+ PORT_Free(label);
+ return crv;
+ default:
+ break;
+ }
+
+ key = lg_GetPublicKey(obj);
+ if (key == NULL) {
+ if (type == CKA_ID) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ switch (key->keyType) {
+ case NSSLOWKEYRSAKey:
+ return lg_FindRSAPublicKeyAttribute(key,type,attribute);
+ case NSSLOWKEYDSAKey:
+ return lg_FindDSAPublicKeyAttribute(key,type,attribute);
+ case NSSLOWKEYDHKey:
+ return lg_FindDHPublicKeyAttribute(key,type,attribute);
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ return lg_FindECPublicKeyAttribute(key,type,attribute);
+#endif /* NSS_ENABLE_ECC */
+ default:
+ break;
+ }
+
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindSecretKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWKEYPrivateKey *key;
+ char *label;
+ unsigned char *keyString;
+ CK_RV crv;
+ int keyTypeLen;
+ CK_ULONG keyLen;
+ CK_KEY_TYPE keyType;
+ PRUint32 keyTypeStorage;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_VERIFY:
+ case CKA_WRAP:
+ case CKA_UNWRAP:
+ case CKA_MODIFIABLE:
+ case CKA_LOCAL:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_NEVER_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_LABEL:
+ label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+ if (label == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ crv = lg_CopyAttribute(attribute,type,label,PORT_Strlen(label));
+ PORT_Free(label);
+ return crv;
+ case CKA_ID:
+ return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+ obj->dbKey.len);
+ case CKA_KEY_TYPE:
+ case CKA_VALUE_LEN:
+ case CKA_VALUE:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+
+ key = lg_GetPrivateKey(obj);
+ if (key == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_KEY_TYPE:
+ /* handle legacy databases. In legacy databases key_type was stored
+ * in host order, with any leading zeros stripped off. Only key types
+ * under 0x1f (AES) were stored. We assume that any values which are
+ * either 1 byte long (big endian), or have byte[0] between 0 and
+ * 0x7f and bytes[1]-bytes[3] equal to '0' (little endian). All other
+ * values are assumed to be from the new database, which is always 4
+ * bytes in network order */
+ keyType=0;
+ keyString = key->u.rsa.coefficient.data;
+ keyTypeLen = key->u.rsa.coefficient.len;
+
+
+ /*
+ * Because of various endian and word lengths The database may have
+ * stored the keyType value in one of the following formats:
+ * (kt) <= 0x1f
+ * length data
+ * Big Endian, pre-3.9, all lengths: 1 (kt)
+ * Little Endian, pre-3.9, 32 bits: 4 (kt) 0 0 0
+ * Little Endian, pre-3.9, 64 bits: 8 (kt) 0 0 0 0 0 0 0
+ * All platforms, 3.9, 32 bits: 4 0 0 0 (kt)
+ * Big Endian, 3.9, 64 bits: 8 0 0 0 (kt) 0 0 0 0
+ * Little Endian, 3.9, 64 bits: 8 0 0 0 0 0 0 0 (kt)
+ * All platforms, >= 3.9.1, all lengths: 4 (a) k1 k2 k3
+ * where (a) is 0 or >= 0x80. currently (a) can only be 0.
+ */
+ /*
+ * this key was written on a 64 bit platform with a using NSS 3.9
+ * or earlier. Reduce the 64 bit possibilities above. When we are
+ * through, we will only have:
+ *
+ * Big Endian, pre-3.9, all lengths: 1 (kt)
+ * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0
+ * All platforms, 3.9, all lengths: 4 0 0 0 (kt)
+ * All platforms, => 3.9.1, all lengths: 4 (a) k1 k2 k3
+ */
+ if (keyTypeLen == 8) {
+ keyTypeStorage = *(PRUint32 *) keyString;
+ if (keyTypeStorage == 0) {
+ keyString += sizeof(PRUint32);
+ }
+ keyTypeLen = 4;
+ }
+ /*
+ * Now Handle:
+ *
+ * All platforms, 3.9, all lengths: 4 0 0 0 (kt)
+ * All platforms, => 3.9.1, all lengths: 4 (a) k1 k2 k3
+ *
+ * NOTE: if kt == 0 or ak1k2k3 == 0, the test fails and
+ * we handle it as:
+ *
+ * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0
+ */
+ if (keyTypeLen == sizeof(keyTypeStorage) &&
+ (((keyString[0] & 0x80) == 0x80) ||
+ !((keyString[1] == 0) && (keyString[2] == 0)
+ && (keyString[3] == 0))) ) {
+ PORT_Memcpy(&keyTypeStorage, keyString, sizeof(keyTypeStorage));
+ keyType = (CK_KEY_TYPE) PR_ntohl(keyTypeStorage);
+ } else {
+ /*
+ * Now Handle:
+ *
+ * Big Endian, pre-3.9, all lengths: 1 (kt)
+ * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0
+ * -- KeyType == 0 all other cases ---: 4 0 0 0 0
+ */
+ keyType = (CK_KEY_TYPE) keyString[0] ;
+ }
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_VALUE:
+ return lg_CopyPrivAttribute(attribute,type,key->u.rsa.privateExponent.data,
+ key->u.rsa.privateExponent.len, obj->sdb);
+ case CKA_VALUE_LEN:
+ keyLen=key->u.rsa.privateExponent.len;
+ return lg_ULongAttribute(attribute,type, keyLen);
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindRSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_RSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute, type,lg_StaticTrueAttr);
+ case CKA_MODULUS:
+ return lg_CopyAttributeSigned(attribute,type,key->u.rsa.modulus.data,
+ key->u.rsa.modulus.len);
+ case CKA_PUBLIC_EXPONENT:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.rsa.publicExponent.data,
+ key->u.rsa.publicExponent.len);
+ case CKA_PRIVATE_EXPONENT:
+ return lg_CopyPrivAttrSigned(attribute,type,
+ key->u.rsa.privateExponent.data,
+ key->u.rsa.privateExponent.len, sdbpw);
+ case CKA_PRIME_1:
+ return lg_CopyPrivAttrSigned(attribute, type, key->u.rsa.prime1.data,
+ key->u.rsa.prime1.len, sdbpw);
+ case CKA_PRIME_2:
+ return lg_CopyPrivAttrSigned(attribute, type, key->u.rsa.prime2.data,
+ key->u.rsa.prime2.len, sdbpw);
+ case CKA_EXPONENT_1:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.rsa.exponent1.data,
+ key->u.rsa.exponent1.len, sdbpw);
+ case CKA_EXPONENT_2:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.rsa.exponent2.data,
+ key->u.rsa.exponent2.len, sdbpw);
+ case CKA_COEFFICIENT:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.rsa.coefficient.data,
+ key->u.rsa.coefficient.len, sdbpw);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DSA;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_DECRYPT:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_SIGN:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_VALUE:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.dsa.privateValue.data,
+ key->u.dsa.privateValue.len, sdbpw);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.params.prime.data,
+ key->u.dsa.params.prime.len);
+ case CKA_SUBPRIME:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.params.subPrime.data,
+ key->u.dsa.params.subPrime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.params.base.data,
+ key->u.dsa.params.base.len);
+ case CKA_NETSCAPE_DB:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dsa.publicValue.data,
+ key->u.dsa.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindDHPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_DH;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VALUE:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.dh.privateValue.data,
+ key->u.dh.privateValue.len, sdbpw);
+ case CKA_PRIME:
+ return lg_CopyAttributeSigned(attribute, type, key->u.dh.prime.data,
+ key->u.dh.prime.len);
+ case CKA_BASE:
+ return lg_CopyAttributeSigned(attribute, type, key->u.dh.base.data,
+ key->u.dh.base.len);
+ case CKA_NETSCAPE_DB:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.dh.publicValue.data,
+ key->u.dh.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+#ifdef NSS_ENABLE_ECC
+static CK_RV
+lg_FindECPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute, SDB *sdbpw)
+{
+ unsigned char hash[SHA1_LENGTH];
+ CK_KEY_TYPE keyType = CKK_EC;
+
+ switch (type) {
+ case CKA_KEY_TYPE:
+ return lg_ULongAttribute(attribute, type, keyType);
+ case CKA_ID:
+ SHA1_HashBuf(hash,key->u.ec.publicValue.data,key->u.ec.publicValue.len);
+ return lg_CopyAttribute(attribute,type,hash,SHA1_LENGTH);
+ case CKA_DERIVE:
+ case CKA_SIGN:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_DECRYPT:
+ case CKA_SIGN_RECOVER:
+ case CKA_UNWRAP:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_VALUE:
+ return lg_CopyPrivAttrSigned(attribute, type,
+ key->u.ec.privateValue.data,
+ key->u.ec.privateValue.len, sdbpw);
+ case CKA_EC_PARAMS:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.ec.ecParams.DEREncoding.data,
+ key->u.ec.ecParams.DEREncoding.len);
+ case CKA_NETSCAPE_DB:
+ return lg_CopyAttributeSigned(attribute, type,
+ key->u.ec.publicValue.data,
+ key->u.ec.publicValue.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+#endif /* NSS_ENABLE_ECC */
+
+static CK_RV
+lg_FindPrivateKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWKEYPrivateKey *key;
+ char *label;
+ CK_RV crv;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_SENSITIVE:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ case CKA_MODIFIABLE:
+ case CKA_LOCAL:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_NEVER_EXTRACTABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_SUBJECT:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_LABEL:
+ label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+ if (label == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ crv = lg_CopyAttribute(attribute,type,label,PORT_Strlen(label));
+ PORT_Free(label);
+ return crv;
+ default:
+ break;
+ }
+ key = lg_GetPrivateKey(obj);
+ if (key == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (key->keyType) {
+ case NSSLOWKEYRSAKey:
+ return lg_FindRSAPrivateKeyAttribute(key,type,attribute,obj->sdb);
+ case NSSLOWKEYDSAKey:
+ return lg_FindDSAPrivateKeyAttribute(key,type,attribute,obj->sdb);
+ case NSSLOWKEYDHKey:
+ return lg_FindDHPrivateKeyAttribute(key,type,attribute,obj->sdb);
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ return lg_FindECPrivateKeyAttribute(key,type,attribute,obj->sdb);
+#endif /* NSS_ENABLE_ECC */
+ default:
+ break;
+ }
+
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindSMIMEAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ certDBEntrySMime *entry;
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_NETSCAPE_EMAIL:
+ return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+ obj->dbKey.len-1);
+ case CKA_NETSCAPE_SMIME_TIMESTAMP:
+ case CKA_SUBJECT:
+ case CKA_VALUE:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+ entry = lg_getSMime(obj);
+ if (entry == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_NETSCAPE_SMIME_TIMESTAMP:
+ return lg_CopyAttribute(attribute,type,entry->optionsDate.data,
+ entry->optionsDate.len);
+ case CKA_SUBJECT:
+ return lg_CopyAttribute(attribute,type,entry->subjectName.data,
+ entry->subjectName.len);
+ case CKA_VALUE:
+ return lg_CopyAttribute(attribute,type,entry->smimeOptions.data,
+ entry->smimeOptions.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindTrustAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWCERTTrust *trust;
+ NSSLOWCERTCertDBHandle *certHandle;
+ NSSLOWCERTCertificate *cert;
+ unsigned char hash[SHA1_LENGTH];
+ unsigned int trustFlags;
+ CK_RV crv;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_CERT_SHA1_HASH:
+ case CKA_CERT_MD5_HASH:
+ case CKA_TRUST_CLIENT_AUTH:
+ case CKA_TRUST_SERVER_AUTH:
+ case CKA_TRUST_EMAIL_PROTECTION:
+ case CKA_TRUST_CODE_SIGNING:
+ case CKA_TRUST_STEP_UP_APPROVED:
+ case CKA_ISSUER:
+ case CKA_SERIAL_NUMBER:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+ certHandle = lg_getCertDB(obj->sdb);
+ if (!certHandle) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ trust = lg_getTrust(obj, certHandle);
+ if (trust == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_CERT_SHA1_HASH:
+ SHA1_HashBuf(hash,trust->derCert->data,trust->derCert->len);
+ return lg_CopyAttribute(attribute, type, hash, SHA1_LENGTH);
+ case CKA_CERT_MD5_HASH:
+ MD5_HashBuf(hash,trust->derCert->data,trust->derCert->len);
+ return lg_CopyAttribute(attribute, type, hash, MD5_LENGTH);
+ case CKA_TRUST_CLIENT_AUTH:
+ trustFlags = trust->trust->sslFlags & CERTDB_TRUSTED_CLIENT_CA ?
+ trust->trust->sslFlags | CERTDB_TRUSTED_CA : 0 ;
+ goto trust;
+ case CKA_TRUST_SERVER_AUTH:
+ trustFlags = trust->trust->sslFlags;
+ goto trust;
+ case CKA_TRUST_EMAIL_PROTECTION:
+ trustFlags = trust->trust->emailFlags;
+ goto trust;
+ case CKA_TRUST_CODE_SIGNING:
+ trustFlags = trust->trust->objectSigningFlags;
+trust:
+ if (trustFlags & CERTDB_TRUSTED_CA ) {
+ return lg_ULongAttribute(attribute, type,
+ CKT_NETSCAPE_TRUSTED_DELEGATOR);
+ }
+ if (trustFlags & CERTDB_TRUSTED) {
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_TRUSTED);
+ }
+ if (trustFlags & CERTDB_NOT_TRUSTED) {
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_UNTRUSTED);
+ }
+ if (trustFlags & CERTDB_TRUSTED_UNKNOWN) {
+ return lg_ULongAttribute(attribute, type,
+ CKT_NETSCAPE_TRUST_UNKNOWN);
+ }
+ if (trustFlags & CERTDB_VALID_CA) {
+ return lg_ULongAttribute(attribute, type,
+ CKT_NETSCAPE_VALID_DELEGATOR);
+ }
+ if (trustFlags & CERTDB_VALID_PEER) {
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_VALID);
+ }
+ return lg_ULongAttribute(attribute, type, CKT_NETSCAPE_MUST_VERIFY);
+ case CKA_TRUST_STEP_UP_APPROVED:
+ if (trust->trust->sslFlags & CERTDB_GOVT_APPROVED_CA) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ } else {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ }
+ default:
+ break;
+ }
+
+
+ switch (type) {
+ case CKA_ISSUER:
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) break;
+ crv = lg_CopyAttribute(attribute,type,cert->derIssuer.data,
+ cert->derIssuer.len);
+ break;
+ case CKA_SERIAL_NUMBER:
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) break;
+ crv = lg_CopyAttribute(attribute,type,cert->derSN.data,
+ cert->derSN.len);
+ break;
+ default:
+ cert = NULL;
+ break;
+ }
+ if (cert) {
+ nsslowcert_DestroyCertificate(cert);
+ return crv;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindCrlAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ certDBEntryRevocation *crl;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_NETSCAPE_KRL:
+ return ((obj->handle == LG_TOKEN_KRL_HANDLE)
+ ? LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr)
+ : LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr));
+ case CKA_SUBJECT:
+ return lg_CopyAttribute(attribute,type,obj->dbKey.data,
+ obj->dbKey.len);
+ case CKA_NETSCAPE_URL:
+ case CKA_VALUE:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+ crl = lg_getCrl(obj);
+ if (!crl) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_NETSCAPE_URL:
+ if (crl->url == NULL) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ return lg_CopyAttribute(attribute, type, crl->url,
+ PORT_Strlen(crl->url)+1);
+ case CKA_VALUE:
+ return lg_CopyAttribute(attribute, type, crl->derCrl.data,
+ crl->derCrl.len);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+static CK_RV
+lg_FindCertAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *attribute)
+{
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertDBHandle *certHandle;
+ NSSLOWKEYPublicKey *pubKey;
+ unsigned char hash[SHA1_LENGTH];
+ SECItem *item;
+
+ switch (type) {
+ case CKA_PRIVATE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticFalseAttr);
+ case CKA_MODIFIABLE:
+ return LG_CLONE_ATTR(attribute,type,lg_StaticTrueAttr);
+ case CKA_CERTIFICATE_TYPE:
+ /* hardcoding X.509 into here */
+ return lg_ULongAttribute(attribute, type, CKC_X_509);
+ case CKA_VALUE:
+ case CKA_ID:
+ case CKA_LABEL:
+ case CKA_SUBJECT:
+ case CKA_ISSUER:
+ case CKA_SERIAL_NUMBER:
+ case CKA_NETSCAPE_EMAIL:
+ break;
+ default:
+ return lg_invalidAttribute(attribute);
+ }
+
+ certHandle = lg_getCertDB(obj->sdb);
+ if (certHandle == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ switch (type) {
+ case CKA_VALUE:
+ return lg_CopyAttribute(attribute,type,cert->derCert.data,
+ cert->derCert.len);
+ case CKA_ID:
+ if (((cert->trust->sslFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->emailFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) {
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ }
+ pubKey = nsslowcert_ExtractPublicKey(cert);
+ if (pubKey == NULL) break;
+ item = lg_GetPubItem(pubKey);
+ if (item == NULL) {
+ nsslowkey_DestroyPublicKey(pubKey);
+ break;
+ }
+ SHA1_HashBuf(hash,item->data,item->len);
+ /* item is imbedded in pubKey, just free the key */
+ nsslowkey_DestroyPublicKey(pubKey);
+ return lg_CopyAttribute(attribute, type, hash, SHA1_LENGTH);
+ case CKA_LABEL:
+ return cert->nickname
+ ? lg_CopyAttribute(attribute, type, cert->nickname,
+ PORT_Strlen(cert->nickname))
+ : LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ case CKA_SUBJECT:
+ return lg_CopyAttribute(attribute,type,cert->derSubject.data,
+ cert->derSubject.len);
+ case CKA_ISSUER:
+ return lg_CopyAttribute(attribute,type,cert->derIssuer.data,
+ cert->derIssuer.len);
+ case CKA_SERIAL_NUMBER:
+ return lg_CopyAttribute(attribute,type,cert->derSN.data,
+ cert->derSN.len);
+ case CKA_NETSCAPE_EMAIL:
+ return (cert->emailAddr && cert->emailAddr[0])
+ ? lg_CopyAttribute(attribute, type, cert->emailAddr,
+ PORT_Strlen(cert->emailAddr))
+ : LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+CK_RV
+lg_GetSingleAttribute(LGObjectCache *obj, CK_ATTRIBUTE *attribute)
+{
+ /* handle the common ones */
+ CK_ATTRIBUTE_TYPE type = attribute->type;
+ switch (type) {
+ case CKA_CLASS:
+ return lg_ULongAttribute(attribute,type,obj->objclass);
+ case CKA_TOKEN:
+ return LG_CLONE_ATTR(attribute, type,lg_StaticTrueAttr);
+ case CKA_LABEL:
+ if ( (obj->objclass == CKO_CERTIFICATE)
+ || (obj->objclass == CKO_PRIVATE_KEY)
+ || (obj->objclass == CKO_PUBLIC_KEY)
+ || (obj->objclass == CKO_SECRET_KEY)) {
+ break;
+ }
+ return LG_CLONE_ATTR(attribute,type,lg_StaticNullAttr);
+ default:
+ break;
+ }
+ switch (obj->objclass) {
+ case CKO_CERTIFICATE:
+ return lg_FindCertAttribute(obj,type,attribute);
+ case CKO_NETSCAPE_CRL:
+ return lg_FindCrlAttribute(obj,type,attribute);
+ case CKO_NETSCAPE_TRUST:
+ return lg_FindTrustAttribute(obj,type,attribute);
+ case CKO_NETSCAPE_SMIME:
+ return lg_FindSMIMEAttribute(obj,type,attribute);
+ case CKO_PUBLIC_KEY:
+ return lg_FindPublicKeyAttribute(obj,type,attribute);
+ case CKO_PRIVATE_KEY:
+ return lg_FindPrivateKeyAttribute(obj,type,attribute);
+ case CKO_SECRET_KEY:
+ return lg_FindSecretKeyAttribute(obj,type,attribute);
+ default:
+ break;
+ }
+ return lg_invalidAttribute(attribute);
+}
+
+/*
+ * Fill in the attribute template based on the data in the database.
+ */
+CK_RV
+lg_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE handle, CK_ATTRIBUTE *templ,
+ CK_ULONG count)
+{
+ LGObjectCache *obj = lg_NewObjectCache(sdb, NULL, handle & ~LG_TOKEN_MASK);
+ CK_RV crv, crvCollect = CKR_OK;
+ int i;
+
+ if (obj == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ for (i=0; i < count; i++) {
+ crv = lg_GetSingleAttribute(obj, &templ[i]);
+ if (crvCollect == CKR_OK) crvCollect = crv;
+ }
+
+ lg_DestroyObjectCache(obj);
+ return crvCollect;
+}
+
+PRBool
+lg_cmpAttribute(LGObjectCache *obj, const CK_ATTRIBUTE *attribute)
+{
+ unsigned char buf[LG_BUF_SPACE];
+ CK_ATTRIBUTE testAttr;
+ unsigned char *tempBuf = NULL;
+ PRBool match = PR_TRUE;
+ CK_RV crv;
+
+ /* we're going to compare 'attribute' with the actual attribute from
+ * the object. We'll use the length of 'attribute' to decide how much
+ * space we need to read the test attribute. If 'attribute' doesn't give
+ * enough space, then we know the values don't match and that will
+ * show up as ckr != CKR_OK */
+ testAttr = *attribute;
+ testAttr.pValue = buf;
+
+ /* if we don't have enough space, malloc it */
+ if (attribute->ulValueLen > LG_BUF_SPACE) {
+ tempBuf = PORT_Alloc(attribute->ulValueLen);
+ if (!tempBuf) {
+ return PR_FALSE;
+ }
+ testAttr.pValue = tempBuf;
+ }
+
+ /* get the attribute */
+ crv = lg_GetSingleAttribute(obj, &testAttr);
+ /* if the attribute was read OK, compare it */
+ if ((crv != CKR_OK) || (attribute->ulValueLen != testAttr.ulValueLen) ||
+ (PORT_Memcmp(attribute->pValue,testAttr.pValue,testAttr.ulValueLen)!= 0)){
+ /* something didn't match, this isn't the object we are looking for */
+ match = PR_FALSE;
+ }
+ /* free the buffer we may have allocated */
+ if (tempBuf) {
+ PORT_Free(tempBuf);
+ }
+ return match;
+}
+
+PRBool
+lg_tokenMatch(SDB *sdb, const SECItem *dbKey, CK_OBJECT_HANDLE class,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ PRBool match = PR_TRUE;
+ LGObjectCache *obj = lg_NewObjectCache(sdb, dbKey, class);
+ int i;
+
+ if (obj == NULL) {
+ return PR_FALSE;
+ }
+
+ for (i=0; i < count; i++) {
+ match = lg_cmpAttribute(obj, &templ[i]);
+ if (!match) {
+ break;
+ }
+ }
+
+ /* done looking, free up our cache */
+ lg_DestroyObjectCache(obj);
+
+ /* if we get through the whole list without finding a mismatched attribute,
+ * then this object fits the criteria we are matching */
+ return match;
+}
+
+static CK_RV
+lg_SetCertAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len)
+{
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertDBHandle *certHandle;
+ char *nickname = NULL;
+ SECStatus rv;
+ CK_RV crv;
+
+ /* we can't change the EMAIL values, but let the
+ * upper layers feel better about the fact we tried to set these */
+ if (type == CKA_NETSCAPE_EMAIL) {
+ return CKR_OK;
+ }
+
+ certHandle = lg_getCertDB(obj->sdb);
+ if (certHandle == NULL) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
+ }
+
+ if ((type != CKA_LABEL) && (type != CKA_ID)) {
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ goto done;
+ }
+
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto done;
+ }
+
+ /* if the app is trying to set CKA_ID, it's probably because it just
+ * imported the key. Look to see if we need to set the CERTDB_USER bits.
+ */
+ if (type == CKA_ID) {
+ if (((cert->trust->sslFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->emailFlags & CERTDB_USER) == 0) &&
+ ((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) {
+ NSSLOWKEYDBHandle *keyHandle;
+
+ keyHandle = lg_getKeyDB(obj->sdb);
+ if (keyHandle) {
+ if (nsslowkey_KeyForCertExists(keyHandle, cert)) {
+ NSSLOWCERTCertTrust trust = *cert->trust;
+ trust.sslFlags |= CERTDB_USER;
+ trust.emailFlags |= CERTDB_USER;
+ trust.objectSigningFlags |= CERTDB_USER;
+ nsslowcert_ChangeCertTrust(certHandle,cert,&trust);
+ }
+ }
+ }
+ crv = CKR_OK;
+ goto done;
+ }
+
+ /* must be CKA_LABEL */
+ if (value != NULL) {
+ nickname = PORT_ZAlloc(len+1);
+ if (nickname == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto done;
+ }
+ PORT_Memcpy(nickname,value,len);
+ nickname[len] = 0;
+ }
+ rv = nsslowcert_AddPermNickname(certHandle, cert, nickname);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+
+done:
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ return crv;
+}
+
+static CK_RV
+lg_SetPrivateKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len,
+ PRBool *writePrivate)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ NSSLOWKEYDBHandle *keyHandle;
+ char *nickname = NULL;
+ SECStatus rv;
+ CK_RV crv;
+
+ /* we can't change the ID and we don't store the subject, but let the
+ * upper layers feel better about the fact we tried to set these */
+ if ((type == CKA_ID) || (type == CKA_SUBJECT) ||
+ (type == CKA_LOCAL) || (type == CKA_NEVER_EXTRACTABLE) ||
+ (type == CKA_ALWAYS_SENSITIVE)) {
+ return CKR_OK;
+ }
+
+ keyHandle = lg_getKeyDB(obj->sdb);
+ if (keyHandle == NULL) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
+ }
+
+ privKey = lg_GetPrivateKeyWithDB(obj, keyHandle);
+ if (privKey == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto done;
+ }
+
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ switch(type) {
+ case CKA_LABEL:
+ if (value != NULL) {
+ nickname = PORT_ZAlloc(len+1);
+ if (nickname == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto done;
+ }
+ PORT_Memcpy(nickname,value,len);
+ nickname[len] = 0;
+ }
+ rv = nsslowkey_UpdateNickname(keyHandle, privKey, &obj->dbKey,
+ nickname, obj->sdb);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+ break;
+ case CKA_UNWRAP:
+ case CKA_SIGN:
+ case CKA_DERIVE:
+ case CKA_SIGN_RECOVER:
+ case CKA_DECRYPT:
+ /* ignore attempts to change restrict these.
+ * legacyDB ignore these flags and always presents all of them
+ * that are valid as true.
+ * NOTE: We only get here if the current value and the new value do
+ * not match. */
+ if (*(char *)value == 0) {
+ crv = CKR_OK;
+ }
+ break;
+ case CKA_VALUE:
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ /* We aren't really changing these values, we are just triggering
+ * the database to update it's entry */
+ *writePrivate = PR_TRUE;
+ crv = CKR_OK;
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ break;
+ }
+done:
+ if (nickname) {
+ PORT_Free(nickname);
+ }
+ return crv;
+}
+
+static CK_RV
+lg_SetPublicKeyAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type,
+ const void *value, unsigned int len,
+ PRBool *writePrivate)
+{
+ /* we can't change the ID and we don't store the subject, but let the
+ * upper layers feel better about the fact we tried to set these */
+ if ((type == CKA_ID) || (type == CKA_SUBJECT) || (type == CKA_LABEL)) {
+ return CKR_OK;
+ }
+ return CKR_ATTRIBUTE_READ_ONLY;
+}
+
+static CK_RV
+lg_SetTrustAttribute(LGObjectCache *obj, const CK_ATTRIBUTE *attr)
+{
+ unsigned int flags;
+ CK_TRUST trust;
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertDBHandle *certHandle;
+ NSSLOWCERTCertTrust dbTrust;
+ SECStatus rv;
+ CK_RV crv;
+
+ if (attr->type == CKA_LABEL) {
+ return CKR_OK;
+ }
+
+ crv = lg_GetULongAttribute(attr->type, attr, 1, &trust);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ flags = lg_MapTrust(trust, (PRBool) (attr->type == CKA_TRUST_CLIENT_AUTH));
+
+ certHandle = lg_getCertDB(obj->sdb);
+
+ if (certHandle == NULL) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ goto done;
+ }
+
+ cert = lg_getCert(obj, certHandle);
+ if (cert == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto done;
+ }
+ dbTrust = *cert->trust;
+
+ switch (attr->type) {
+ case CKA_TRUST_EMAIL_PROTECTION:
+ dbTrust.emailFlags = flags |
+ (cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS);
+ break;
+ case CKA_TRUST_CODE_SIGNING:
+ dbTrust.objectSigningFlags = flags |
+ (cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS);
+ break;
+ case CKA_TRUST_CLIENT_AUTH:
+ dbTrust.sslFlags = flags | (cert->trust->sslFlags &
+ (CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CA));
+ break;
+ case CKA_TRUST_SERVER_AUTH:
+ dbTrust.sslFlags = flags | (cert->trust->sslFlags &
+ (CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CLIENT_CA));
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ goto done;
+ }
+
+ rv = nsslowcert_ChangeCertTrust(certHandle, cert, &dbTrust);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+done:
+ return crv;
+}
+
+static CK_RV
+lg_SetSingleAttribute(LGObjectCache *obj, const CK_ATTRIBUTE *attr,
+ PRBool *writePrivate)
+{
+ CK_ATTRIBUTE attribLocal;
+ CK_RV crv;
+
+ if ((attr->type == CKA_NETSCAPE_DB) && (obj->objclass == CKO_PRIVATE_KEY)) {
+ *writePrivate = PR_TRUE;
+ return CKR_OK;
+ }
+
+ /* Make sure the attribute exists first */
+ attribLocal.type = attr->type;
+ attribLocal.pValue = NULL;
+ attribLocal.ulValueLen = 0;
+ crv = lg_GetSingleAttribute(obj, &attribLocal);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /* if we are just setting it to the value we already have,
+ * allow it to happen. Let label setting go through so
+ * we have the opportunity to repair any database corruption. */
+ if (attr->type != CKA_LABEL) {
+ if (lg_cmpAttribute(obj,attr)) {
+ return CKR_OK;
+ }
+ }
+
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ switch (obj->objclass) {
+ case CKO_CERTIFICATE:
+ /* change NICKNAME, EMAIL, */
+ crv = lg_SetCertAttribute(obj,attr->type,
+ attr->pValue,attr->ulValueLen);
+ break;
+ case CKO_NETSCAPE_CRL:
+ /* change URL */
+ break;
+ case CKO_NETSCAPE_TRUST:
+ crv = lg_SetTrustAttribute(obj,attr);
+ break;
+ case CKO_PRIVATE_KEY:
+ case CKO_SECRET_KEY:
+ crv = lg_SetPrivateKeyAttribute(obj,attr->type,
+ attr->pValue,attr->ulValueLen, writePrivate);
+ break;
+ case CKO_PUBLIC_KEY:
+ crv = lg_SetPublicKeyAttribute(obj,attr->type,
+ attr->pValue,attr->ulValueLen, writePrivate);
+ break;
+ }
+ return crv;
+}
+
+/*
+ * Fill in the attribute template based on the data in the database.
+ */
+CK_RV
+lg_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ LGObjectCache *obj = lg_NewObjectCache(sdb, NULL, handle & ~LG_TOKEN_MASK);
+ CK_RV crv, crvCollect = CKR_OK;
+ PRBool writePrivate = PR_FALSE;
+ int i;
+
+ if (obj == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ for (i=0; i < count; i++) {
+ crv = lg_SetSingleAttribute(obj, &templ[i], &writePrivate);
+ if (crvCollect == CKR_OK) crvCollect = crv;
+ }
+
+ /* Write any collected changes out for private and secret keys.
+ * don't do the write for just the label */
+ if (writePrivate) {
+ NSSLOWKEYPrivateKey *privKey = lg_GetPrivateKey(obj);
+ SECStatus rv = SECFailure;
+ char * label = lg_FindKeyNicknameByPublicKey(obj->sdb, &obj->dbKey);
+
+ if (privKey) {
+ rv = nsslowkey_StoreKeyByPublicKeyAlg(lg_getKeyDB(sdb), privKey,
+ &obj->dbKey, label, sdb, PR_TRUE );
+ }
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ }
+ }
+
+ lg_DestroyObjectCache(obj);
+ return crvCollect;
+}
diff --git a/security/nss/lib/softoken/legacydb/lgcreate.c b/security/nss/lib/softoken/legacydb/lgcreate.c
new file mode 100644
index 000000000..1dd1b2709
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgcreate.c
@@ -0,0 +1,957 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "secitem.h"
+#include "pkcs11.h"
+#include "lgdb.h"
+#include "pcert.h"
+#include "lowkeyi.h"
+#include "blapi.h"
+#include "secder.h"
+
+#include "keydbi.h"
+
+/*
+ * ******************** Object Creation Utilities ***************************
+ */
+
+/*
+ * check the consistancy and initialize a Certificate Object
+ */
+static CK_RV
+lg_createCertObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ SECItem derCert;
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertTrust *trust = NULL;
+ NSSLOWCERTCertTrust userTrust =
+ { CERTDB_USER, CERTDB_USER, CERTDB_USER };
+ NSSLOWCERTCertTrust defTrust =
+ { CERTDB_TRUSTED_UNKNOWN,
+ CERTDB_TRUSTED_UNKNOWN, CERTDB_TRUSTED_UNKNOWN };
+ char *label = NULL;
+ char *email = NULL;
+ SECStatus rv;
+ CK_RV crv;
+ PRBool inDB = PR_TRUE;
+ NSSLOWCERTCertDBHandle *certHandle = lg_getCertDB(sdb);
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+ CK_CERTIFICATE_TYPE type;
+ const CK_ATTRIBUTE *attribute;
+
+ /* we can't store any certs private */
+ if (lg_isTrue(CKA_PRIVATE, templ, count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* We only support X.509 Certs for now */
+ crv = lg_GetULongAttribute(CKA_CERTIFICATE_TYPE, templ, count, &type);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ if (type != CKC_X_509) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* X.509 Certificate */
+
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* get the der cert */
+ attribute = lg_FindAttribute(CKA_VALUE, templ, count);
+ if (!attribute) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ derCert.type = 0;
+ derCert.data = (unsigned char *)attribute->pValue;
+ derCert.len = attribute->ulValueLen ;
+
+ label = lg_getString(CKA_LABEL, templ, count);
+
+ cert = nsslowcert_FindCertByDERCert(certHandle, &derCert);
+ if (cert == NULL) {
+ cert = nsslowcert_DecodeDERCertificate(&derCert, label);
+ inDB = PR_FALSE;
+ }
+ if (cert == NULL) {
+ if (label) PORT_Free(label);
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle) {
+ if (nsslowkey_KeyForCertExists(keyHandle,cert)) {
+ trust = &userTrust;
+ }
+ }
+
+ if (!inDB) {
+ if (!trust) trust = &defTrust;
+ rv = nsslowcert_AddPermCert(certHandle, cert, label, trust);
+ } else {
+ rv = trust ? nsslowcert_ChangeCertTrust(certHandle,cert,trust) :
+ SECSuccess;
+ }
+
+ if (label) PORT_Free(label);
+
+ if (rv != SECSuccess) {
+ nsslowcert_DestroyCertificate(cert);
+ return CKR_DEVICE_ERROR;
+ }
+
+ /*
+ * Add a NULL S/MIME profile if necessary.
+ */
+ email = lg_getString(CKA_NETSCAPE_EMAIL, templ, count);
+ if (email) {
+ certDBEntrySMime *entry;
+
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle,email);
+ if (!entry) {
+ nsslowcert_SaveSMimeProfile(certHandle, email,
+ &cert->derSubject, NULL, NULL);
+ } else {
+ nsslowcert_DestroyDBEntry((certDBEntry *)entry);
+ }
+ PORT_Free(email);
+ }
+ *handle=lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_CERT);
+ nsslowcert_DestroyCertificate(cert);
+
+ return CKR_OK;
+}
+
+unsigned int
+lg_MapTrust(CK_TRUST trust, PRBool clientAuth)
+{
+ unsigned int trustCA = clientAuth ? CERTDB_TRUSTED_CLIENT_CA :
+ CERTDB_TRUSTED_CA;
+ switch (trust) {
+ case CKT_NETSCAPE_TRUSTED:
+ return CERTDB_VALID_PEER|CERTDB_TRUSTED;
+ case CKT_NETSCAPE_TRUSTED_DELEGATOR:
+ return CERTDB_VALID_CA|trustCA;
+ case CKT_NETSCAPE_UNTRUSTED:
+ return CERTDB_NOT_TRUSTED;
+ case CKT_NETSCAPE_MUST_VERIFY:
+ return 0;
+ case CKT_NETSCAPE_VALID: /* implies must verify */
+ return CERTDB_VALID_PEER;
+ case CKT_NETSCAPE_VALID_DELEGATOR: /* implies must verify */
+ return CERTDB_VALID_CA;
+ default:
+ break;
+ }
+ return CERTDB_TRUSTED_UNKNOWN;
+}
+
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+lg_createTrustObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ const CK_ATTRIBUTE *issuer = NULL;
+ const CK_ATTRIBUTE *serial = NULL;
+ NSSLOWCERTCertificate *cert = NULL;
+ const CK_ATTRIBUTE *trust;
+ CK_TRUST sslTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_TRUST clientTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_TRUST emailTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_TRUST signTrust = CKT_NETSCAPE_TRUST_UNKNOWN;
+ CK_BBOOL stepUp;
+ NSSLOWCERTCertTrust dbTrust = { 0 };
+ SECStatus rv;
+ NSSLOWCERTCertDBHandle *certHandle = lg_getCertDB(sdb);
+ NSSLOWCERTIssuerAndSN issuerSN;
+
+ /* we can't store any certs private */
+ if (lg_isTrue(CKA_PRIVATE, templ, count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ issuer = lg_FindAttribute(CKA_ISSUER, templ, count);
+ serial = lg_FindAttribute(CKA_SERIAL_NUMBER, templ, count);
+
+ if (issuer && serial) {
+ issuerSN.derIssuer.data = (unsigned char *)issuer->pValue;
+ issuerSN.derIssuer.len = issuer->ulValueLen ;
+
+ issuerSN.serialNumber.data = (unsigned char *)serial->pValue;
+ issuerSN.serialNumber.len = serial->ulValueLen ;
+
+ cert = nsslowcert_FindCertByIssuerAndSN(certHandle,&issuerSN);
+ }
+
+ if (cert == NULL) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ lg_GetULongAttribute(CKA_TRUST_SERVER_AUTH, templ, count, &sslTrust);
+ lg_GetULongAttribute(CKA_TRUST_CLIENT_AUTH, templ, count, &clientTrust);
+ lg_GetULongAttribute(CKA_TRUST_EMAIL_PROTECTION, templ, count, &emailTrust);
+ lg_GetULongAttribute(CKA_TRUST_CODE_SIGNING, templ, count, &signTrust);
+ stepUp = CK_FALSE;
+ trust = lg_FindAttribute(CKA_TRUST_STEP_UP_APPROVED, templ, count);
+ if (trust) {
+ if (trust->ulValueLen == sizeof(CK_BBOOL)) {
+ stepUp = *(CK_BBOOL*)trust->pValue;
+ }
+ }
+
+ /* preserve certain old fields */
+ if (cert->trust) {
+ dbTrust.sslFlags = cert->trust->sslFlags & CERTDB_PRESERVE_TRUST_BITS;
+ dbTrust.emailFlags=
+ cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS;
+ dbTrust.objectSigningFlags =
+ cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS;
+ }
+
+ dbTrust.sslFlags |= lg_MapTrust(sslTrust,PR_FALSE);
+ dbTrust.sslFlags |= lg_MapTrust(clientTrust,PR_TRUE);
+ dbTrust.emailFlags |= lg_MapTrust(emailTrust,PR_FALSE);
+ dbTrust.objectSigningFlags |= lg_MapTrust(signTrust,PR_FALSE);
+ if (stepUp) {
+ dbTrust.sslFlags |= CERTDB_GOVT_APPROVED_CA;
+ }
+
+ rv = nsslowcert_ChangeCertTrust(certHandle,cert,&dbTrust);
+ *handle=lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_TRUST);
+ nsslowcert_DestroyCertificate(cert);
+ if (rv != SECSuccess) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+lg_createSMimeObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ SECItem derSubj,rawProfile,rawTime,emailKey;
+ SECItem *pRawProfile = NULL;
+ SECItem *pRawTime = NULL;
+ char *email = NULL;
+ const CK_ATTRIBUTE *subject = NULL,
+ *profile = NULL,
+ *time = NULL;
+ SECStatus rv;
+ NSSLOWCERTCertDBHandle *certHandle;
+ CK_RV ck_rv = CKR_OK;
+
+ /* we can't store any certs private */
+ if (lg_isTrue(CKA_PRIVATE,templ,count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* lookup SUBJECT */
+ subject = lg_FindAttribute(CKA_SUBJECT,templ,count);
+ PORT_Assert(subject);
+ if (!subject) {
+ ck_rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto loser;
+ }
+
+ derSubj.data = (unsigned char *)subject->pValue;
+ derSubj.len = subject->ulValueLen ;
+ derSubj.type = 0;
+
+ /* lookup VALUE */
+ profile = lg_FindAttribute(CKA_VALUE,templ,count);
+ if (profile) {
+ rawProfile.data = (unsigned char *)profile->pValue;
+ rawProfile.len = profile->ulValueLen ;
+ rawProfile.type = siBuffer;
+ pRawProfile = &rawProfile;
+ }
+
+ /* lookup Time */
+ time = lg_FindAttribute(CKA_NETSCAPE_SMIME_TIMESTAMP,templ,count);
+ if (time) {
+ rawTime.data = (unsigned char *)time->pValue;
+ rawTime.len = time->ulValueLen ;
+ rawTime.type = siBuffer;
+ pRawTime = &rawTime;
+ }
+
+
+ email = lg_getString(CKA_NETSCAPE_EMAIL,templ,count);
+ if (!email) {
+ ck_rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto loser;
+ }
+
+ /* Store S/MIME Profile by SUBJECT */
+ rv = nsslowcert_SaveSMimeProfile(certHandle, email, &derSubj,
+ pRawProfile,pRawTime);
+ if (rv != SECSuccess) {
+ ck_rv = CKR_DEVICE_ERROR;
+ goto loser;
+ }
+ emailKey.data = (unsigned char *)email;
+ emailKey.len = PORT_Strlen(email)+1;
+
+ *handle = lg_mkHandle(sdb, &emailKey, LG_TOKEN_TYPE_SMIME);
+
+loser:
+ if (email) PORT_Free(email);
+
+ return ck_rv;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+lg_createCrlObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ PRBool isKRL = PR_FALSE;
+ SECItem derSubj,derCrl;
+ char *url = NULL;
+ const CK_ATTRIBUTE *subject,*crl;
+ SECStatus rv;
+ NSSLOWCERTCertDBHandle *certHandle;
+
+ certHandle = lg_getCertDB(sdb);
+
+ /* we can't store any private crls */
+ if (lg_isTrue(CKA_PRIVATE,templ,count)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* lookup SUBJECT */
+ subject = lg_FindAttribute(CKA_SUBJECT,templ,count);
+ if (!subject) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ derSubj.data = (unsigned char *)subject->pValue;
+ derSubj.len = subject->ulValueLen ;
+
+ /* lookup VALUE */
+ crl = lg_FindAttribute(CKA_VALUE,templ,count);
+ PORT_Assert(crl);
+ if (!crl) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ derCrl.data = (unsigned char *)crl->pValue;
+ derCrl.len = crl->ulValueLen ;
+
+ url = lg_getString(CKA_NETSCAPE_URL,templ,count);
+ isKRL = lg_isTrue(CKA_NETSCAPE_KRL,templ,count);
+
+ /* Store CRL by SUBJECT */
+ rv = nsslowcert_AddCrl(certHandle, &derCrl, &derSubj, url, isKRL);
+
+ if (url) {
+ PORT_Free(url);
+ }
+ if (rv != SECSuccess) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ /* if we overwrote the existing CRL, poison the handle entry so we get
+ * a new object handle */
+ (void) lg_poisonHandle(sdb, &derSubj,
+ isKRL ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL);
+ *handle = lg_mkHandle(sdb, &derSubj,
+ isKRL ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL);
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Public Key Object
+ */
+static CK_RV
+lg_createPublicKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_ATTRIBUTE_TYPE pubKeyAttr = CKA_VALUE;
+ CK_RV crv;
+ NSSLOWKEYPrivateKey *priv;
+ SECItem pubKey;
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+
+ switch (key_type) {
+ case CKK_RSA:
+ pubKeyAttr = CKA_MODULUS;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ pubKeyAttr = CKA_EC_POINT;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ case CKK_DSA:
+ case CKK_DH:
+ break;
+ default:
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+
+ crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,&pubKey);
+ if (crv != CKR_OK) return crv;
+
+ PORT_Assert(pubKey.data);
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle == NULL) {
+ PORT_Free(pubKey.data);
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ if (keyHandle->version != 3) {
+ unsigned char buf[SHA1_LENGTH];
+ SHA1_HashBuf(buf,pubKey.data,pubKey.len);
+ PORT_Memcpy(pubKey.data,buf,sizeof(buf));
+ pubKey.len = sizeof(buf);
+ }
+ /* make sure the associated private key already exists */
+ /* only works if we are logged in */
+ priv = nsslowkey_FindKeyByPublicKey(keyHandle, &pubKey, sdb /*password*/);
+ if (priv == NULL) {
+ PORT_Free(pubKey.data);
+ return crv;
+ }
+ nsslowkey_DestroyPrivateKey(priv);
+
+ *handle = lg_mkHandle(sdb, &pubKey, LG_TOKEN_TYPE_PUB);
+ PORT_Free(pubKey.data);
+
+ return CKR_OK;
+}
+
+/* make a private key from a verified object */
+static NSSLOWKEYPrivateKey *
+lg_mkPrivKey(SDB *sdb, const CK_ATTRIBUTE *templ, CK_ULONG count,
+ CK_KEY_TYPE key_type, CK_RV *crvp)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ PLArenaPool *arena;
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ privKey = (NSSLOWKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey));
+ if (privKey == NULL) {
+ PORT_FreeArena(arena,PR_FALSE);
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ /* in future this would be a switch on key_type */
+ privKey->arena = arena;
+ switch (key_type) {
+ case CKK_RSA:
+ privKey->keyType = NSSLOWKEYRSAKey;
+ crv=lg_Attribute2SSecItem(arena,CKA_MODULUS,templ,count,
+ &privKey->u.rsa.modulus);
+ if (crv != CKR_OK) break;
+ crv=lg_Attribute2SSecItem(arena,CKA_PUBLIC_EXPONENT,templ,count,
+ &privKey->u.rsa.publicExponent);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_PRIVATE_EXPONENT,templ,count,
+ &privKey->u.rsa.privateExponent, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_PRIME_1,templ,count,
+ &privKey->u.rsa.prime1, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_PRIME_2,templ,count,
+ &privKey->u.rsa.prime2, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_EXPONENT_1,templ,count,
+ &privKey->u.rsa.exponent1, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_EXPONENT_2,templ,count,
+ &privKey->u.rsa.exponent2, sdb);
+ if (crv != CKR_OK) break;
+ crv=lg_PrivAttr2SSecItem(arena,CKA_COEFFICIENT,templ,count,
+ &privKey->u.rsa.coefficient, sdb);
+ if (crv != CKR_OK) break;
+ rv = DER_SetUInteger(privKey->arena, &privKey->u.rsa.version,
+ NSSLOWKEY_VERSION);
+ if (rv != SECSuccess) crv = CKR_HOST_MEMORY;
+ break;
+
+ case CKK_DSA:
+ privKey->keyType = NSSLOWKEYDSAKey;
+ crv = lg_Attribute2SSecItem(arena,CKA_PRIME,templ,count,
+ &privKey->u.dsa.params.prime);
+ if (crv != CKR_OK) break;
+ crv = lg_Attribute2SSecItem(arena,CKA_SUBPRIME,templ,count,
+ &privKey->u.dsa.params.subPrime);
+ if (crv != CKR_OK) break;
+ crv = lg_Attribute2SSecItem(arena,CKA_BASE,templ,count,
+ &privKey->u.dsa.params.base);
+ if (crv != CKR_OK) break;
+ crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count,
+ &privKey->u.dsa.privateValue, sdb);
+ if (crv != CKR_OK) break;
+ if (lg_hasAttribute(CKA_NETSCAPE_DB, templ,count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count,
+ &privKey->u.dsa.publicValue);
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ break;
+
+ case CKK_DH:
+ privKey->keyType = NSSLOWKEYDHKey;
+ crv = lg_Attribute2SSecItem(arena,CKA_PRIME,templ,count,
+ &privKey->u.dh.prime);
+ if (crv != CKR_OK) break;
+ crv = lg_Attribute2SSecItem(arena,CKA_BASE,templ,count,
+ &privKey->u.dh.base);
+ if (crv != CKR_OK) break;
+ crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count,
+ &privKey->u.dh.privateValue, sdb);
+ if (crv != CKR_OK) break;
+ if (lg_hasAttribute(CKA_NETSCAPE_DB, templ, count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count,
+ &privKey->u.dh.publicValue);
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ break;
+
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ privKey->keyType = NSSLOWKEYECKey;
+ crv = lg_Attribute2SSecItem(arena, CKA_EC_PARAMS,templ,count,
+ &privKey->u.ec.ecParams.DEREncoding);
+ if (crv != CKR_OK) break;
+
+ /* Fill out the rest of the ecParams structure
+ * based on the encoded params
+ */
+ if (LGEC_FillParams(arena, &privKey->u.ec.ecParams.DEREncoding,
+ &privKey->u.ec.ecParams) != SECSuccess) {
+ crv = CKR_DOMAIN_PARAMS_INVALID;
+ break;
+ }
+ crv = lg_PrivAttr2SSecItem(arena,CKA_VALUE,templ,count,
+ &privKey->u.ec.privateValue, sdb);
+ if (crv != CKR_OK) break;
+ if (lg_hasAttribute(CKA_NETSCAPE_DB,templ,count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_DB,templ,count,
+ &privKey->u.ec.publicValue);
+ if (crv != CKR_OK) break;
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ rv = DER_SetUInteger(privKey->arena, &privKey->u.ec.version,
+ NSSLOWKEY_EC_PRIVATE_KEY_VERSION);
+ if (rv != SECSuccess) crv = CKR_HOST_MEMORY;
+ break;
+#endif /* NSS_ENABLE_ECC */
+
+ default:
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ *crvp = crv;
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ return NULL;
+ }
+ return privKey;
+}
+
+/*
+ * check the consistancy and initialize a Private Key Object
+ */
+static CK_RV
+lg_createPrivateKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ char *label;
+ SECStatus rv = SECSuccess;
+ CK_RV crv = CKR_DEVICE_ERROR;
+ SECItem pubKey;
+ NSSLOWKEYDBHandle *keyHandle = lg_getKeyDB(sdb);
+
+ if (keyHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ privKey=lg_mkPrivKey(sdb, templ,count,key_type,&crv);
+ if (privKey == NULL) return crv;
+ label = lg_getString(CKA_LABEL,templ,count);
+
+ crv = lg_Attribute2SSecItem(NULL,CKA_NETSCAPE_DB,templ,count,&pubKey);
+ if (crv != CKR_OK) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ rv = SECFailure;
+ goto fail;
+ }
+#ifdef notdef
+ if (keyHandle->version != 3) {
+ unsigned char buf[SHA1_LENGTH];
+ SHA1_HashBuf(buf,pubKey.data,pubKey.len);
+ PORT_Memcpy(pubKey.data,buf,sizeof(buf));
+ pubKey.len = sizeof(buf);
+ }
+#endif
+ /* get the key type */
+ if (key_type == CKK_RSA) {
+ rv = RSA_PrivateKeyCheck(&privKey->u.rsa);
+ if (rv == SECFailure) {
+ goto fail;
+ }
+ }
+ rv = nsslowkey_StoreKeyByPublicKey(keyHandle, privKey, &pubKey,
+ label, sdb /*->password*/);
+
+fail:
+ if (label) PORT_Free(label);
+ *handle = lg_mkHandle(sdb,&pubKey,LG_TOKEN_TYPE_PRIV);
+ if (pubKey.data) PORT_Free(pubKey.data);
+ nsslowkey_DestroyPrivateKey(privKey);
+ if (rv != SECSuccess) return crv;
+
+ return CKR_OK;
+}
+
+
+#define LG_KEY_MAX_RETRIES 10 /* don't hang if we are having problems with the rng */
+#define LG_KEY_ID_SIZE 18 /* don't use either SHA1 or MD5 sizes */
+/*
+ * Secret keys must have a CKA_ID value to be stored in the database. This code
+ * will generate one if there wasn't one already.
+ */
+static CK_RV
+lg_GenerateSecretCKA_ID(NSSLOWKEYDBHandle *handle, SECItem *id, char *label)
+{
+ unsigned int retries;
+ SECStatus rv = SECSuccess;
+ CK_RV crv = CKR_OK;
+
+ id->data = NULL;
+ if (label) {
+ id->data = (unsigned char *)PORT_Strdup(label);
+ if (id->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ id->len = PORT_Strlen(label)+1;
+ if (!nsslowkey_KeyForIDExists(handle,id)) {
+ return CKR_OK;
+ }
+ PORT_Free(id->data);
+ id->data = NULL;
+ id->len = 0;
+ }
+ id->data = (unsigned char *)PORT_Alloc(LG_KEY_ID_SIZE);
+ if (id->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ id->len = LG_KEY_ID_SIZE;
+
+ retries = 0;
+ do {
+ rv = RNG_GenerateGlobalRandomBytes(id->data,id->len);
+ } while (rv == SECSuccess && nsslowkey_KeyForIDExists(handle,id) &&
+ (++retries <= LG_KEY_MAX_RETRIES));
+
+ if ((rv != SECSuccess) || (retries > LG_KEY_MAX_RETRIES)) {
+ crv = CKR_DEVICE_ERROR; /* random number generator is bad */
+ PORT_Free(id->data);
+ id->data = NULL;
+ id->len = 0;
+ }
+ return crv;
+}
+
+
+static NSSLOWKEYPrivateKey *lg_mkSecretKeyRep(const CK_ATTRIBUTE *templ,
+ CK_ULONG count, CK_KEY_TYPE key_type,
+ SECItem *pubkey, SDB *sdbpw)
+{
+ NSSLOWKEYPrivateKey *privKey = 0;
+ PLArenaPool *arena = 0;
+ CK_KEY_TYPE keyType;
+ PRUint32 keyTypeStorage;
+ SECItem keyTypeItem;
+ CK_RV crv;
+ SECStatus rv;
+ static unsigned char derZero[1] = { 0 };
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) { crv = CKR_HOST_MEMORY; goto loser; }
+
+ privKey = (NSSLOWKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey));
+ if (privKey == NULL) { crv = CKR_HOST_MEMORY; goto loser; }
+
+ privKey->arena = arena;
+
+ /* Secret keys are represented in the database as "fake" RSA keys.
+ * The RSA key is marked as a secret key representation by setting the
+ * public exponent field to 0, which is an invalid RSA exponent.
+ * The other fields are set as follows:
+ * modulus - CKA_ID value for the secret key
+ * private exponent - CKA_VALUE (the key itself)
+ * coefficient - CKA_KEY_TYPE, which indicates what encryption algorithm
+ * is used for the key.
+ * all others - set to integer 0
+ */
+ privKey->keyType = NSSLOWKEYRSAKey;
+
+ /* The modulus is set to the key id of the symmetric key */
+ crv = lg_Attribute2SecItem(arena, CKA_ID, templ, count,
+ &privKey->u.rsa.modulus);
+ if (crv != CKR_OK) goto loser;
+
+ /* The public exponent is set to 0 length to indicate a special key */
+ privKey->u.rsa.publicExponent.len = sizeof derZero;
+ privKey->u.rsa.publicExponent.data = derZero;
+
+ /* The private exponent is the actual key value */
+ crv = lg_PrivAttr2SecItem(arena, CKA_VALUE, templ, count,
+ &privKey->u.rsa.privateExponent, sdbpw);
+ if (crv != CKR_OK) goto loser;
+
+ /* All other fields empty - needs testing */
+ privKey->u.rsa.prime1.len = sizeof derZero;
+ privKey->u.rsa.prime1.data = derZero;
+
+ privKey->u.rsa.prime2.len = sizeof derZero;
+ privKey->u.rsa.prime2.data = derZero;
+
+ privKey->u.rsa.exponent1.len = sizeof derZero;
+ privKey->u.rsa.exponent1.data = derZero;
+
+ privKey->u.rsa.exponent2.len = sizeof derZero;
+ privKey->u.rsa.exponent2.data = derZero;
+
+ /* Coeficient set to KEY_TYPE */
+ crv = lg_GetULongAttribute(CKA_KEY_TYPE, templ, count, &keyType);
+ if (crv != CKR_OK) goto loser;
+ /* on 64 bit platforms, we still want to store 32 bits of keyType (This is
+ * safe since the PKCS #11 defines for all types are 32 bits or less). */
+ keyTypeStorage = (PRUint32) keyType;
+ keyTypeStorage = PR_htonl(keyTypeStorage);
+ keyTypeItem.data = (unsigned char *)&keyTypeStorage;
+ keyTypeItem.len = sizeof (keyTypeStorage);
+ rv = SECITEM_CopyItem(arena, &privKey->u.rsa.coefficient, &keyTypeItem);
+ if (rv != SECSuccess) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ /* Private key version field set normally for compatibility */
+ rv = DER_SetUInteger(privKey->arena,
+ &privKey->u.rsa.version, NSSLOWKEY_VERSION);
+ if (rv != SECSuccess) { crv = CKR_HOST_MEMORY; goto loser; }
+
+loser:
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ privKey = 0;
+ }
+
+ return privKey;
+}
+
+/*
+ * check the consistancy and initialize a Secret Key Object
+ */
+static CK_RV
+lg_createSecretKeyObject(SDB *sdb, CK_KEY_TYPE key_type,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_RV crv;
+ NSSLOWKEYPrivateKey *privKey = NULL;
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+ SECItem pubKey;
+ char *label = NULL;
+ SECStatus rv = SECSuccess;
+
+ pubKey.data = 0;
+
+ /* If the object is a TOKEN object, store in the database */
+ keyHandle = lg_getKeyDB(sdb);
+
+ if (keyHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ label = lg_getString(CKA_LABEL,templ,count);
+
+ crv = lg_Attribute2SecItem(NULL,CKA_ID,templ,count,&pubKey);
+ /* Should this be ID? */
+ if (crv != CKR_OK) goto loser;
+
+ /* if we don't have an ID, generate one */
+ if (pubKey.len == 0) {
+ if (pubKey.data) {
+ PORT_Free(pubKey.data);
+ pubKey.data = NULL;
+ }
+ crv = lg_GenerateSecretCKA_ID(keyHandle, &pubKey, label);
+ if (crv != CKR_OK) goto loser;
+ }
+
+ privKey = lg_mkSecretKeyRep(templ, count, key_type, &pubKey, sdb);
+ if (privKey == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ rv = nsslowkey_StoreKeyByPublicKey(keyHandle,
+ privKey, &pubKey, label, sdb /*->password*/);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ goto loser;
+ }
+
+ *handle = lg_mkHandle(sdb, &pubKey, LG_TOKEN_TYPE_KEY);
+
+loser:
+ if (label) PORT_Free(label);
+ if (privKey) nsslowkey_DestroyPrivateKey(privKey);
+ if (pubKey.data) PORT_Free(pubKey.data);
+
+ return crv;
+}
+
+/*
+ * check the consistancy and initialize a Key Object
+ */
+static CK_RV
+lg_createKeyObject(SDB *sdb, CK_OBJECT_CLASS objclass,
+ CK_OBJECT_HANDLE *handle, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_RV crv;
+ CK_KEY_TYPE key_type;
+
+ /* get the key type */
+ crv = lg_GetULongAttribute(CKA_KEY_TYPE, templ, count, &key_type);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ switch (objclass) {
+ case CKO_PUBLIC_KEY:
+ return lg_createPublicKeyObject(sdb,key_type,handle,templ,count);
+ case CKO_PRIVATE_KEY:
+ return lg_createPrivateKeyObject(sdb,key_type,handle,templ,count);
+ case CKO_SECRET_KEY:
+ return lg_createSecretKeyObject(sdb,key_type,handle,templ,count);
+ default:
+ break;
+ }
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+}
+
+/*
+ * Parse the template and create an object stored in the DB that reflects.
+ * the object specified in the database.
+ */
+CK_RV
+lg_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ CK_RV crv;
+ CK_OBJECT_CLASS objclass;
+
+ /* get the object class */
+ crv = lg_GetULongAttribute(CKA_CLASS, templ, count, &objclass);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /* Now handle the specific object class.
+ */
+ switch (objclass) {
+ case CKO_CERTIFICATE:
+ crv = lg_createCertObject(sdb,handle,templ,count);
+ break;
+ case CKO_NETSCAPE_TRUST:
+ crv = lg_createTrustObject(sdb,handle,templ,count);
+ break;
+ case CKO_NETSCAPE_CRL:
+ crv = lg_createCrlObject(sdb,handle,templ,count);
+ break;
+ case CKO_NETSCAPE_SMIME:
+ crv = lg_createSMimeObject(sdb,handle,templ,count);
+ break;
+ case CKO_PRIVATE_KEY:
+ case CKO_PUBLIC_KEY:
+ case CKO_SECRET_KEY:
+ crv = lg_createKeyObject(sdb,objclass,handle,templ,count);
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+
+ return crv;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/lgdb.h b/security/nss/lib/softoken/legacydb/lgdb.h
new file mode 100644
index 000000000..207b7aecf
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgdb.h
@@ -0,0 +1,198 @@
+/* ***** 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 ***** */
+/*
+ * Internal data structures and functions used by pkcs11.c
+ */
+#ifndef _LGDB_H_
+#define _LGDB_H_ 1
+
+#include "nssilock.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "lowkeyti.h"
+#include "pkcs11t.h"
+#include "sdb.h"
+#include "cdbhdl.h"
+
+
+#define MULTIACCESS "multiaccess:"
+
+
+/* machine dependent path stuff used by dbinit.c and pk11db.c */
+#ifdef macintosh
+#define PATH_SEPARATOR ":"
+#define SECMOD_DB "Security Modules"
+#define CERT_DB_FMT "%sCertificates%s"
+#define KEY_DB_FMT "%sKey Database%s"
+#else
+#define PATH_SEPARATOR "/"
+#define SECMOD_DB "secmod.db"
+#define CERT_DB_FMT "%scert%s.db"
+#define KEY_DB_FMT "%skey%s.db"
+#endif
+
+SEC_BEGIN_PROTOS
+
+
+/* internal utility functions used by pkcs11.c */
+extern const CK_ATTRIBUTE *lg_FindAttribute(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern CK_RV lg_Attribute2SecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item);
+extern CK_RV lg_Attribute2SSecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item);
+extern CK_RV lg_PrivAttr2SecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw);
+extern CK_RV lg_PrivAttr2SSecItem(PLArenaPool *,CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw);
+extern CK_RV lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ CK_ULONG *out);
+extern PRBool lg_hasAttribute(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern PRBool lg_isTrue(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern PRBool lg_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass);
+extern char *lg_getString(CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+extern unsigned int lg_MapTrust(CK_TRUST trust, PRBool clientAuth);
+
+/* clear out all the existing object ID to database key mappings.
+ * used to reinit a token */
+extern CK_RV lg_ClearTokenKeyHashTable(SDB *sdb);
+
+
+extern void lg_FreeSearch(SDBFind *search);
+
+NSSLOWCERTCertDBHandle *lg_getCertDB(SDB *sdb);
+NSSLOWKEYDBHandle *lg_getKeyDB(SDB *sdb);
+
+const char *lg_EvaluateConfigDir(const char *configdir, char **domain);
+
+
+/*
+ * object handle modifiers
+ */
+#define LG_TOKEN_MASK 0xc0000000L
+#define LG_TOKEN_TYPE_MASK 0x38000000L
+#define LG_TOKEN_TYPE_SHIFT 27
+/* keydb (high bit == 0) */
+#define LG_TOKEN_TYPE_PRIV 0x08000000L
+#define LG_TOKEN_TYPE_PUB 0x10000000L
+#define LG_TOKEN_TYPE_KEY 0x18000000L
+/* certdb (high bit == 1) */
+#define LG_TOKEN_TYPE_TRUST 0x20000000L
+#define LG_TOKEN_TYPE_CRL 0x28000000L
+#define LG_TOKEN_TYPE_SMIME 0x30000000L
+#define LG_TOKEN_TYPE_CERT 0x38000000L
+
+#define LG_TOKEN_KRL_HANDLE (LG_TOKEN_TYPE_CRL|1)
+
+#define LG_SEARCH_BLOCK_SIZE 10
+#define LG_BUF_SPACE 50
+#define LG_STRICT PR_FALSE
+
+/*
+ * token object utilities
+ */
+void lg_addHandle(SDBFind *search, CK_OBJECT_HANDLE handle);
+PRBool lg_poisonHandle(SDB *sdb, SECItem *dbkey, CK_OBJECT_HANDLE handle);
+PRBool lg_tokenMatch(SDB *sdb, const SECItem *dbKey, CK_OBJECT_HANDLE class,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+const SECItem *lg_lookupTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle);
+CK_OBJECT_HANDLE lg_mkHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class);
+SECStatus lg_deleteTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle);
+
+SECStatus lg_util_encrypt(PLArenaPool *arena, SDB *sdbpw,
+ SECItem *plainText, SECItem **cipherText);
+SECStatus lg_util_decrypt(SDB *sdbpw,
+ SECItem *cipherText, SECItem **plainText);
+PLHashTable *lg_GetHashTable(SDB *sdb);
+void lg_DBLock(SDB *sdb);
+void lg_DBUnlock(SDB *sdb);
+
+typedef void (*LGFreeFunc)(void *);
+
+
+/*
+ * database functions
+ */
+
+/* lg_FindObjectsInit initializes a search for token and session objects
+ * that match a template. */
+CK_RV lg_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *pTemplate,
+ CK_ULONG ulCount, SDBFind **search);
+/* lg_FindObjects continues a search for token and session objects
+ * that match a template, obtaining additional object handles. */
+CK_RV lg_FindObjects(SDB *sdb, SDBFind *search,
+ CK_OBJECT_HANDLE *phObject,CK_ULONG ulMaxObjectCount,
+ CK_ULONG *pulObjectCount);
+
+/* lg_FindObjectsFinal finishes a search for token and session objects. */
+CK_RV lg_FindObjectsFinal(SDB* lgdb, SDBFind *search);
+
+/* lg_CreateObject parses the template and create an object stored in the
+ * DB that reflects the object specified in the template. */
+CK_RV lg_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *handle,
+ const CK_ATTRIBUTE *templ, CK_ULONG count);
+
+CK_RV lg_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ CK_ATTRIBUTE *template, CK_ULONG count);
+CK_RV lg_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ const CK_ATTRIBUTE *template, CK_ULONG count);
+CK_RV lg_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id);
+
+CK_RV lg_Close(SDB *sdb);
+CK_RV lg_Reset(SDB *sdb);
+
+/*
+ * The old database doesn't share and doesn't support
+ * transactions.
+ */
+CK_RV lg_Begin(SDB *sdb);
+CK_RV lg_Commit(SDB *sdb);
+CK_RV lg_Abort(SDB *sdb);
+CK_RV lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2);
+CK_RV lg_PutMetaData(SDB *sdb, const char *id,
+ const SECItem *item1, const SECItem *item2);
+
+SEC_END_PROTOS
+
+#endif /* _LGDB_H_ */
diff --git a/security/nss/lib/softoken/legacydb/lgdestroy.c b/security/nss/lib/softoken/legacydb/lgdestroy.c
new file mode 100644
index 000000000..37db2e50e
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgdestroy.c
@@ -0,0 +1,144 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * Internal PKCS #11 functions. Should only be called by pkcs11.c
+ */
+#include "pkcs11.h"
+#include "lgdb.h"
+#include "pcert.h"
+#include "lowkeyi.h"
+
+/*
+ * remove an object.
+ */
+CK_RV
+lg_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+ NSSLOWCERTCertificate *cert;
+ NSSLOWCERTCertTrust tmptrust;
+ PRBool isKrl;
+ NSSLOWKEYDBHandle *keyHandle;
+ NSSLOWCERTCertDBHandle *certHandle;
+ const SECItem *dbKey;
+
+ object_id &= ~LG_TOKEN_MASK;
+ dbKey = lg_lookupTokenKeyByHandle(sdb,object_id);
+ if (dbKey == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ /* remove the objects from the real data base */
+ switch (object_id & LG_TOKEN_TYPE_MASK) {
+ case LG_TOKEN_TYPE_PRIV:
+ case LG_TOKEN_TYPE_KEY:
+ /* KEYID is the public KEY for DSA and DH, and the MODULUS for
+ * RSA */
+ keyHandle = lg_getKeyDB(sdb);
+ if (!keyHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ rv = nsslowkey_DeleteKey(keyHandle, dbKey);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ }
+ break;
+ case LG_TOKEN_TYPE_PUB:
+ break; /* public keys only exist at the behest of the priv key */
+ case LG_TOKEN_TYPE_CERT:
+ certHandle = lg_getCertDB(sdb);
+ if (!certHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ cert = nsslowcert_FindCertByKey(certHandle,dbKey);
+ if (cert == NULL) {
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+ rv = nsslowcert_DeletePermCertificate(cert);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ }
+ nsslowcert_DestroyCertificate(cert);
+ break;
+ case LG_TOKEN_TYPE_CRL:
+ certHandle = lg_getCertDB(sdb);
+ if (!certHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ isKrl = (PRBool) (object_id == LG_TOKEN_KRL_HANDLE);
+ rv = nsslowcert_DeletePermCRL(certHandle, dbKey, isKrl);
+ if (rv == SECFailure) crv = CKR_DEVICE_ERROR;
+ break;
+ case LG_TOKEN_TYPE_TRUST:
+ certHandle = lg_getCertDB(sdb);
+ if (!certHandle) {
+ crv = CKR_TOKEN_WRITE_PROTECTED;
+ break;
+ }
+ cert = nsslowcert_FindCertByKey(certHandle, dbKey);
+ if (cert == NULL) {
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+ tmptrust = *cert->trust;
+ tmptrust.sslFlags &= CERTDB_PRESERVE_TRUST_BITS;
+ tmptrust.emailFlags &= CERTDB_PRESERVE_TRUST_BITS;
+ tmptrust.objectSigningFlags &= CERTDB_PRESERVE_TRUST_BITS;
+ tmptrust.sslFlags |= CERTDB_TRUSTED_UNKNOWN;
+ tmptrust.emailFlags |= CERTDB_TRUSTED_UNKNOWN;
+ tmptrust.objectSigningFlags |= CERTDB_TRUSTED_UNKNOWN;
+ rv = nsslowcert_ChangeCertTrust(certHandle, cert, &tmptrust);
+ if (rv != SECSuccess) crv = CKR_DEVICE_ERROR;
+ nsslowcert_DestroyCertificate(cert);
+ break;
+ default:
+ break;
+ }
+ lg_DBLock(sdb);
+ lg_deleteTokenKeyByHandle(sdb,object_id);
+ lg_DBUnlock(sdb);
+
+ return crv;
+}
+
+
+
diff --git a/security/nss/lib/softoken/legacydb/lgfind.c b/security/nss/lib/softoken/legacydb/lgfind.c
new file mode 100644
index 000000000..b45f0096c
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgfind.c
@@ -0,0 +1,940 @@
+/* ***** 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.
+ *
+ *
+ * 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 "secitem.h"
+#include "pkcs11.h"
+#include "lgdb.h"
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "blapi.h"
+
+#include "keydbi.h"
+
+/*
+ * This code maps PKCS #11 Finds to legacy database searches. This code
+ * was orginally in pkcs11.c in previous versions of NSS.
+ */
+
+struct SDBFindStr {
+ CK_OBJECT_HANDLE *handles;
+ int size;
+ int index;
+ int array_size;
+};
+
+
+/*
+ * free a search structure
+ */
+void
+lg_FreeSearch(SDBFind *search)
+{
+ if (search->handles) {
+ PORT_Free(search->handles);
+ }
+ PORT_Free(search);
+}
+
+void
+lg_addHandle(SDBFind *search, CK_OBJECT_HANDLE handle)
+{
+ if (search->handles == NULL) {
+ return;
+ }
+ if (search->size >= search->array_size) {
+ search->array_size += LG_SEARCH_BLOCK_SIZE;
+ search->handles = (CK_OBJECT_HANDLE *) PORT_Realloc(search->handles,
+ sizeof(CK_OBJECT_HANDLE)* search->array_size);
+ if (search->handles == NULL) {
+ return;
+ }
+ }
+ search->handles[search->size] = handle;
+ search->size++;
+}
+
+/*
+ * find any certs that may match the template and load them.
+ */
+#define LG_CERT 0x00000001
+#define LG_TRUST 0x00000002
+#define LG_CRL 0x00000004
+#define LG_SMIME 0x00000008
+#define LG_PRIVATE 0x00000010
+#define LG_PUBLIC 0x00000020
+#define LG_KEY 0x00000040
+
+/*
+ * structure to collect key handles.
+ */
+typedef struct lgEntryDataStr {
+ SDB *sdb;
+ SDBFind *searchHandles;
+ const CK_ATTRIBUTE *template;
+ CK_ULONG templ_count;
+} lgEntryData;
+
+
+static SECStatus
+lg_crl_collect(SECItem *data, SECItem *key, certDBEntryType type, void *arg)
+{
+ lgEntryData *crlData;
+ CK_OBJECT_HANDLE class_handle;
+ SDB *sdb;
+
+ crlData = (lgEntryData *)arg;
+ sdb = crlData->sdb;
+
+ class_handle = (type == certDBEntryTypeRevocation) ? LG_TOKEN_TYPE_CRL :
+ LG_TOKEN_KRL_HANDLE;
+ if (lg_tokenMatch(sdb, key, class_handle,
+ crlData->template, crlData->templ_count)) {
+ lg_addHandle(crlData->searchHandles,
+ lg_mkHandle(sdb,key,class_handle));
+ }
+ return(SECSuccess);
+}
+
+static void
+lg_searchCrls(SDB *sdb, SECItem *derSubject, PRBool isKrl,
+ unsigned long classFlags, SDBFind *search,
+ const CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
+{
+ NSSLOWCERTCertDBHandle *certHandle = NULL;
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) {
+ return;
+ }
+ if (derSubject->data != NULL) {
+ certDBEntryRevocation *crl =
+ nsslowcert_FindCrlByKey(certHandle, derSubject, isKrl);
+
+ if (crl != NULL) {
+ lg_addHandle(search, lg_mkHandle(sdb, derSubject,
+ isKrl ? LG_TOKEN_KRL_HANDLE : LG_TOKEN_TYPE_CRL));
+ nsslowcert_DestroyDBEntry((certDBEntry *)crl);
+ }
+ } else {
+ lgEntryData crlData;
+
+ /* traverse */
+ crlData.sdb = sdb;
+ crlData.searchHandles = search;
+ crlData.template = pTemplate;
+ crlData.templ_count = ulCount;
+ nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeRevocation,
+ lg_crl_collect, (void *)&crlData);
+ nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeKeyRevocation,
+ lg_crl_collect, (void *)&crlData);
+ }
+}
+
+/*
+ * structure to collect key handles.
+ */
+typedef struct lgKeyDataStr {
+ SDB *sdb;
+ NSSLOWKEYDBHandle *keyHandle;
+ SDBFind *searchHandles;
+ SECItem *id;
+ const CK_ATTRIBUTE *template;
+ CK_ULONG templ_count;
+ unsigned long classFlags;
+ PRBool strict;
+} lgKeyData;
+
+static PRBool
+isSecretKey(NSSLOWKEYPrivateKey *privKey)
+{
+ if (privKey->keyType == NSSLOWKEYRSAKey &&
+ privKey->u.rsa.publicExponent.len == 1 &&
+ privKey->u.rsa.publicExponent.data[0] == 0)
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+
+
+static SECStatus
+lg_key_collect(DBT *key, DBT *data, void *arg)
+{
+ lgKeyData *keyData;
+ NSSLOWKEYPrivateKey *privKey = NULL;
+ SECItem tmpDBKey;
+ SDB *sdb;
+ unsigned long classFlags;
+
+ keyData = (lgKeyData *)arg;
+ sdb = keyData->sdb;
+ classFlags = keyData->classFlags;
+
+ tmpDBKey.data = key->data;
+ tmpDBKey.len = key->size;
+ tmpDBKey.type = siBuffer;
+
+ PORT_Assert(keyData->keyHandle);
+ if (!keyData->strict && keyData->id && keyData->id->data) {
+ SECItem result;
+ PRBool haveMatch= PR_FALSE;
+ unsigned char hashKey[SHA1_LENGTH];
+ result.data = hashKey;
+ result.len = sizeof(hashKey);
+
+ if (keyData->id->len == 0) {
+ /* Make sure this isn't a LG_KEY */
+ privKey = nsslowkey_FindKeyByPublicKey(keyData->keyHandle,
+ &tmpDBKey, keyData->sdb/*->password*/);
+ if (privKey) {
+ /* turn off the unneeded class flags */
+ classFlags &= isSecretKey(privKey) ? ~(LG_PRIVATE|LG_PUBLIC) :
+ ~LG_KEY;
+ haveMatch = (PRBool)
+ ((classFlags & (LG_KEY|LG_PRIVATE|LG_PUBLIC)) != 0);
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ } else {
+ SHA1_HashBuf( hashKey, key->data, key->size ); /* match id */
+ haveMatch = SECITEM_ItemsAreEqual(keyData->id,&result);
+ if (!haveMatch && ((unsigned char *)key->data)[0] == 0) {
+ /* This is a fix for backwards compatibility. The key
+ * database indexes private keys by the public key, and
+ * versions of NSS prior to 3.4 stored the public key as
+ * a signed integer. The public key is now treated as an
+ * unsigned integer, with no leading zero. In order to
+ * correctly compute the hash of an old key, it is necessary
+ * to fallback and detect the leading zero.
+ */
+ SHA1_HashBuf(hashKey,
+ (unsigned char *)key->data + 1, key->size - 1);
+ haveMatch = SECITEM_ItemsAreEqual(keyData->id,&result);
+ }
+ }
+ if (haveMatch) {
+ if (classFlags & LG_PRIVATE) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_PRIV));
+ }
+ if (classFlags & LG_PUBLIC) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_PUB));
+ }
+ if (classFlags & LG_KEY) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(sdb,&tmpDBKey,LG_TOKEN_TYPE_KEY));
+ }
+ }
+ return SECSuccess;
+ }
+
+ privKey = nsslowkey_FindKeyByPublicKey(keyData->keyHandle, &tmpDBKey,
+ keyData->sdb/*->password*/);
+ if ( privKey == NULL ) {
+ goto loser;
+ }
+
+ if (isSecretKey(privKey)) {
+ if ((classFlags & LG_KEY) &&
+ lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_KEY,
+ keyData->template, keyData->templ_count)) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_KEY));
+ }
+ } else {
+ if ((classFlags & LG_PRIVATE) &&
+ lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_PRIV,
+ keyData->template, keyData->templ_count)) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(keyData->sdb,&tmpDBKey,LG_TOKEN_TYPE_PRIV));
+ }
+ if ((classFlags & LG_PUBLIC) &&
+ lg_tokenMatch(keyData->sdb, &tmpDBKey, LG_TOKEN_TYPE_PUB,
+ keyData->template, keyData->templ_count)) {
+ lg_addHandle(keyData->searchHandles,
+ lg_mkHandle(keyData->sdb, &tmpDBKey,LG_TOKEN_TYPE_PUB));
+ }
+ }
+
+loser:
+ if ( privKey ) {
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ return(SECSuccess);
+}
+
+static void
+lg_searchKeys(SDB *sdb, SECItem *key_id,
+ unsigned long classFlags, SDBFind *search, PRBool mustStrict,
+ const CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount)
+{
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+ NSSLOWKEYPrivateKey *privKey;
+ lgKeyData keyData;
+ PRBool found = PR_FALSE;
+
+ keyHandle = lg_getKeyDB(sdb);
+ if (keyHandle == NULL) {
+ return;
+ }
+
+ if (key_id->data) {
+ privKey = nsslowkey_FindKeyByPublicKey(keyHandle, key_id, sdb);
+ if (privKey) {
+ if ((classFlags & LG_KEY) && isSecretKey(privKey)) {
+ lg_addHandle(search,
+ lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_KEY));
+ found = PR_TRUE;
+ }
+ if ((classFlags & LG_PRIVATE) && !isSecretKey(privKey)) {
+ lg_addHandle(search,
+ lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_PRIV));
+ found = PR_TRUE;
+ }
+ if ((classFlags & LG_PUBLIC) && !isSecretKey(privKey)) {
+ lg_addHandle(search,
+ lg_mkHandle(sdb,key_id,LG_TOKEN_TYPE_PUB));
+ found = PR_TRUE;
+ }
+ nsslowkey_DestroyPrivateKey(privKey);
+ }
+ /* don't do the traversal if we have an up to date db */
+ if (keyHandle->version != 3) {
+ goto loser;
+ }
+ /* don't do the traversal if it can't possibly be the correct id */
+ /* all soft token id's are SHA1_HASH_LEN's */
+ if (key_id->len != SHA1_LENGTH) {
+ goto loser;
+ }
+ if (found) {
+ /* if we already found some keys, don't do the traversal */
+ goto loser;
+ }
+ }
+ keyData.sdb = sdb;
+ keyData.keyHandle = keyHandle;
+ keyData.searchHandles = search;
+ keyData.id = key_id;
+ keyData.template = pTemplate;
+ keyData.templ_count = ulCount;
+ keyData.classFlags = classFlags;
+ keyData.strict = mustStrict ? mustStrict : LG_STRICT;
+
+ nsslowkey_TraverseKeys(keyHandle, lg_key_collect, &keyData);
+
+loser:
+ return;
+}
+
+/*
+ * structure to collect certs into
+ */
+typedef struct lgCertDataStr {
+ SDB *sdb;
+ int cert_count;
+ int max_cert_count;
+ NSSLOWCERTCertificate **certs;
+ const CK_ATTRIBUTE *template;
+ CK_ULONG templ_count;
+ unsigned long classFlags;
+ PRBool strict;
+} lgCertData;
+
+/*
+ * collect all the certs from the traverse call.
+ */
+static SECStatus
+lg_cert_collect(NSSLOWCERTCertificate *cert,void *arg)
+{
+ lgCertData *cd = (lgCertData *)arg;
+
+ if (cert == NULL) {
+ return SECSuccess;
+ }
+
+ if (cd->certs == NULL) {
+ return SECFailure;
+ }
+
+ if (cd->strict) {
+ if ((cd->classFlags & LG_CERT) && !lg_tokenMatch(cd->sdb,
+ &cert->certKey, LG_TOKEN_TYPE_CERT, cd->template,cd->templ_count)) {
+ return SECSuccess;
+ }
+ if ((cd->classFlags & LG_TRUST) && !lg_tokenMatch(cd->sdb,
+ &cert->certKey, LG_TOKEN_TYPE_TRUST,
+ cd->template, cd->templ_count)) {
+ return SECSuccess;
+ }
+ }
+
+ /* allocate more space if we need it. This should only happen in
+ * the general traversal case */
+ if (cd->cert_count >= cd->max_cert_count) {
+ int size;
+ cd->max_cert_count += LG_SEARCH_BLOCK_SIZE;
+ size = cd->max_cert_count * sizeof (NSSLOWCERTCertificate *);
+ cd->certs = (NSSLOWCERTCertificate **)PORT_Realloc(cd->certs,size);
+ if (cd->certs == NULL) {
+ return SECFailure;
+ }
+ }
+
+ cd->certs[cd->cert_count++] = nsslowcert_DupCertificate(cert);
+ return SECSuccess;
+}
+
+/* provide impedence matching ... */
+static SECStatus
+lg_cert_collect2(NSSLOWCERTCertificate *cert, SECItem *dymmy, void *arg)
+{
+ return lg_cert_collect(cert, arg);
+}
+
+static void
+lg_searchSingleCert(lgCertData *certData,NSSLOWCERTCertificate *cert)
+{
+ if (cert == NULL) {
+ return;
+ }
+ if (certData->strict &&
+ !lg_tokenMatch(certData->sdb, &cert->certKey, LG_TOKEN_TYPE_CERT,
+ certData->template,certData->templ_count)) {
+ nsslowcert_DestroyCertificate(cert);
+ return;
+ }
+ certData->certs = (NSSLOWCERTCertificate **)
+ PORT_Alloc(sizeof (NSSLOWCERTCertificate *));
+ if (certData->certs == NULL) {
+ nsslowcert_DestroyCertificate(cert);
+ return;
+ }
+ certData->certs[0] = cert;
+ certData->cert_count = 1;
+}
+
+static void
+lg_CertSetupData(lgCertData *certData,int count)
+{
+ certData->max_cert_count = count;
+
+ if (certData->max_cert_count <= 0) {
+ return;
+ }
+ certData->certs = (NSSLOWCERTCertificate **)
+ PORT_Alloc( count * sizeof(NSSLOWCERTCertificate *));
+ return;
+}
+
+static void
+lg_searchCertsAndTrust(SDB *sdb, SECItem *derCert, SECItem *name,
+ SECItem *derSubject, NSSLOWCERTIssuerAndSN *issuerSN,
+ SECItem *email,
+ unsigned long classFlags, SDBFind *handles,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ NSSLOWCERTCertDBHandle *certHandle = NULL;
+ lgCertData certData;
+ int i;
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) return;
+
+ certData.sdb = sdb;
+ certData.max_cert_count = 0;
+ certData.certs = NULL;
+ certData.cert_count = 0;
+ certData.template = pTemplate;
+ certData.templ_count = ulCount;
+ certData.classFlags = classFlags;
+ certData.strict = LG_STRICT;
+
+
+ /*
+ * Find the Cert.
+ */
+ if (derCert->data != NULL) {
+ NSSLOWCERTCertificate *cert =
+ nsslowcert_FindCertByDERCert(certHandle,derCert);
+ lg_searchSingleCert(&certData,cert);
+ } else if (name->data != NULL) {
+ char *tmp_name = (char*)PORT_Alloc(name->len+1);
+ int count;
+
+ if (tmp_name == NULL) {
+ return;
+ }
+ PORT_Memcpy(tmp_name,name->data,name->len);
+ tmp_name[name->len] = 0;
+
+ count= nsslowcert_NumPermCertsForNickname(certHandle,tmp_name);
+ lg_CertSetupData(&certData,count);
+ nsslowcert_TraversePermCertsForNickname(certHandle,tmp_name,
+ lg_cert_collect, &certData);
+ PORT_Free(tmp_name);
+ } else if (derSubject->data != NULL) {
+ int count;
+
+ count = nsslowcert_NumPermCertsForSubject(certHandle,derSubject);
+ lg_CertSetupData(&certData,count);
+ nsslowcert_TraversePermCertsForSubject(certHandle,derSubject,
+ lg_cert_collect, &certData);
+ } else if ((issuerSN->derIssuer.data != NULL) &&
+ (issuerSN->serialNumber.data != NULL)) {
+ if (classFlags & LG_CERT) {
+ NSSLOWCERTCertificate *cert =
+ nsslowcert_FindCertByIssuerAndSN(certHandle,issuerSN);
+
+ lg_searchSingleCert(&certData,cert);
+ }
+ if (classFlags & LG_TRUST) {
+ NSSLOWCERTTrust *trust =
+ nsslowcert_FindTrustByIssuerAndSN(certHandle, issuerSN);
+
+ if (trust) {
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&trust->dbKey,LG_TOKEN_TYPE_TRUST));
+ nsslowcert_DestroyTrust(trust);
+ }
+ }
+ } else if (email->data != NULL) {
+ char *tmp_name = (char*)PORT_Alloc(email->len+1);
+ certDBEntrySMime *entry = NULL;
+
+ if (tmp_name == NULL) {
+ return;
+ }
+ PORT_Memcpy(tmp_name,email->data,email->len);
+ tmp_name[email->len] = 0;
+
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle,tmp_name);
+ if (entry) {
+ int count;
+ SECItem *subjectName = &entry->subjectName;
+
+ count = nsslowcert_NumPermCertsForSubject(certHandle, subjectName);
+ lg_CertSetupData(&certData,count);
+ nsslowcert_TraversePermCertsForSubject(certHandle, subjectName,
+ lg_cert_collect, &certData);
+
+ nsslowcert_DestroyDBEntry((certDBEntry *)entry);
+ }
+ PORT_Free(tmp_name);
+ } else {
+ /* we aren't filtering the certs, we are working on all, so turn
+ * on the strict filters. */
+ certData.strict = PR_TRUE;
+ lg_CertSetupData(&certData,LG_SEARCH_BLOCK_SIZE);
+ nsslowcert_TraversePermCerts(certHandle, lg_cert_collect2, &certData);
+ }
+
+ /*
+ * build the handles
+ */
+ for (i=0 ; i < certData.cert_count ; i++) {
+ NSSLOWCERTCertificate *cert = certData.certs[i];
+
+ /* if we filtered it would have been on the stuff above */
+ if (classFlags & LG_CERT) {
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_CERT));
+ }
+ if ((classFlags & LG_TRUST) && nsslowcert_hasTrust(cert->trust)) {
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&cert->certKey,LG_TOKEN_TYPE_TRUST));
+ }
+ nsslowcert_DestroyCertificate(cert);
+ }
+
+ if (certData.certs) PORT_Free(certData.certs);
+ return;
+}
+
+static SECStatus
+lg_smime_collect(SECItem *data, SECItem *key, certDBEntryType type, void *arg)
+{
+ lgEntryData *smimeData;
+ SDB *sdb;
+
+ smimeData = (lgEntryData *)arg;
+ sdb = smimeData->sdb;
+
+ if (lg_tokenMatch(sdb, key, LG_TOKEN_TYPE_SMIME,
+ smimeData->template, smimeData->templ_count)) {
+ lg_addHandle(smimeData->searchHandles,
+ lg_mkHandle(sdb,key,LG_TOKEN_TYPE_SMIME));
+ }
+ return(SECSuccess);
+}
+
+static void
+lg_searchSMime(SDB *sdb, SECItem *email, SDBFind *handles,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ NSSLOWCERTCertDBHandle *certHandle = NULL;
+ certDBEntrySMime *entry;
+
+ certHandle = lg_getCertDB(sdb);
+ if (certHandle == NULL) return;
+
+ if (email->data != NULL) {
+ char *tmp_name = (char*)PORT_Alloc(email->len+1);
+
+ if (tmp_name == NULL) {
+ return;
+ }
+ PORT_Memcpy(tmp_name,email->data,email->len);
+ tmp_name[email->len] = 0;
+
+ entry = nsslowcert_ReadDBSMimeEntry(certHandle,tmp_name);
+ if (entry) {
+ SECItem emailKey;
+
+ emailKey.data = (unsigned char *)tmp_name;
+ emailKey.len = PORT_Strlen(tmp_name)+1;
+ emailKey.type = 0;
+ lg_addHandle(handles,
+ lg_mkHandle(sdb,&emailKey,LG_TOKEN_TYPE_SMIME));
+ nsslowcert_DestroyDBEntry((certDBEntry *)entry);
+ }
+ PORT_Free(tmp_name);
+ } else {
+ /* traverse */
+ lgEntryData smimeData;
+
+ /* traverse */
+ smimeData.sdb = sdb;
+ smimeData.searchHandles = handles;
+ smimeData.template = pTemplate;
+ smimeData.templ_count = ulCount;
+ nsslowcert_TraverseDBEntries(certHandle, certDBEntryTypeSMimeProfile,
+ lg_smime_collect, (void *)&smimeData);
+ }
+ return;
+}
+
+static CK_RV
+lg_searchTokenList(SDB *sdb, SDBFind *search,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ int i;
+ PRBool isKrl = PR_FALSE;
+ SECItem derCert = { siBuffer, NULL, 0 };
+ SECItem derSubject = { siBuffer, NULL, 0 };
+ SECItem name = { siBuffer, NULL, 0 };
+ SECItem email = { siBuffer, NULL, 0 };
+ SECItem key_id = { siBuffer, NULL, 0 };
+ SECItem cert_sha1_hash = { siBuffer, NULL, 0 };
+ SECItem cert_md5_hash = { siBuffer, NULL, 0 };
+ NSSLOWCERTIssuerAndSN issuerSN = {
+ { siBuffer, NULL, 0 },
+ { siBuffer, NULL, 0 }
+ };
+ SECItem *copy = NULL;
+ CK_CERTIFICATE_TYPE certType;
+ CK_OBJECT_CLASS objectClass;
+ CK_RV crv;
+ unsigned long classFlags;
+
+ if (lg_getCertDB(sdb) == NULL) {
+ classFlags = LG_PRIVATE|LG_KEY;
+ } else {
+ classFlags = LG_CERT|LG_TRUST|LG_PUBLIC|LG_SMIME|LG_CRL;
+ }
+
+ /*
+ * look for things to search on token objects for. If the right options
+ * are specified, we can use them as direct indeces into the database
+ * (rather than using linear searches. We can also use the attributes to
+ * limit the kinds of objects we are searching for. Later we can use this
+ * array to filter the remaining objects more finely.
+ */
+ for (i=0 ;classFlags && i < (int)ulCount; i++) {
+
+ switch (pTemplate[i].type) {
+ case CKA_SUBJECT:
+ copy = &derSubject;
+ classFlags &= (LG_CERT|LG_PRIVATE|LG_PUBLIC|LG_SMIME|LG_CRL);
+ break;
+ case CKA_ISSUER:
+ copy = &issuerSN.derIssuer;
+ classFlags &= (LG_CERT|LG_TRUST);
+ break;
+ case CKA_SERIAL_NUMBER:
+ copy = &issuerSN.serialNumber;
+ classFlags &= (LG_CERT|LG_TRUST);
+ break;
+ case CKA_VALUE:
+ copy = &derCert;
+ classFlags &= (LG_CERT|LG_CRL|LG_SMIME);
+ break;
+ case CKA_LABEL:
+ copy = &name;
+ break;
+ case CKA_NETSCAPE_EMAIL:
+ copy = &email;
+ classFlags &= LG_SMIME|LG_CERT;
+ break;
+ case CKA_NETSCAPE_SMIME_TIMESTAMP:
+ classFlags &= LG_SMIME;
+ break;
+ case CKA_CLASS:
+ crv = lg_GetULongAttribute(CKA_CLASS,&pTemplate[i],1, &objectClass);
+ if (crv != CKR_OK) {
+ classFlags = 0;
+ break;;
+ }
+ switch (objectClass) {
+ case CKO_CERTIFICATE:
+ classFlags &= LG_CERT;
+ break;
+ case CKO_NETSCAPE_TRUST:
+ classFlags &= LG_TRUST;
+ break;
+ case CKO_NETSCAPE_CRL:
+ classFlags &= LG_CRL;
+ break;
+ case CKO_NETSCAPE_SMIME:
+ classFlags &= LG_SMIME;
+ break;
+ case CKO_PRIVATE_KEY:
+ classFlags &= LG_PRIVATE;
+ break;
+ case CKO_PUBLIC_KEY:
+ classFlags &= LG_PUBLIC;
+ break;
+ case CKO_SECRET_KEY:
+ classFlags &= LG_KEY;
+ break;
+ default:
+ classFlags = 0;
+ break;
+ }
+ break;
+ case CKA_PRIVATE:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ if (*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE) {
+ classFlags &= (LG_PRIVATE|LG_KEY);
+ } else {
+ classFlags &= ~(LG_PRIVATE|LG_KEY);
+ }
+ break;
+ case CKA_SENSITIVE:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ if (*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE) {
+ classFlags &= (LG_PRIVATE|LG_KEY);
+ } else {
+ classFlags = 0;
+ }
+ break;
+ case CKA_TOKEN:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ if (*((CK_BBOOL *)pTemplate[i].pValue) != CK_TRUE) {
+ classFlags = 0;
+ }
+ break;
+ case CKA_CERT_SHA1_HASH:
+ classFlags &= LG_TRUST;
+ copy = &cert_sha1_hash; break;
+ case CKA_CERT_MD5_HASH:
+ classFlags &= LG_TRUST;
+ copy = &cert_md5_hash; break;
+ case CKA_CERTIFICATE_TYPE:
+ crv = lg_GetULongAttribute(CKA_CLASS,&pTemplate[i],1,&certType);
+ if (crv != CKR_OK) {
+ classFlags = 0;
+ }
+ classFlags &= LG_CERT;
+ if (certType != CKC_X_509) {
+ classFlags = 0;
+ }
+ break;
+ case CKA_ID:
+ copy = &key_id;
+ classFlags &= (LG_CERT|LG_PRIVATE|LG_KEY|LG_PUBLIC);
+ break;
+ case CKA_NETSCAPE_KRL:
+ if (pTemplate[i].ulValueLen != sizeof(CK_BBOOL)) {
+ classFlags = 0;
+ }
+ classFlags &= LG_CRL;
+ isKrl = (PRBool)(*((CK_BBOOL *)pTemplate[i].pValue) == CK_TRUE);
+ break;
+ case CKA_MODIFIABLE:
+ break;
+ case CKA_KEY_TYPE:
+ case CKA_DERIVE:
+ classFlags &= LG_PUBLIC|LG_PRIVATE|LG_KEY;
+ break;
+ case CKA_VERIFY_RECOVER:
+ classFlags &= LG_PUBLIC;
+ break;
+ case CKA_SIGN_RECOVER:
+ classFlags &= LG_PRIVATE;
+ break;
+ case CKA_ENCRYPT:
+ case CKA_VERIFY:
+ case CKA_WRAP:
+ classFlags &= LG_PUBLIC|LG_KEY;
+ break;
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_UNWRAP:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ case CKA_NEVER_EXTRACTABLE:
+ classFlags &= LG_PRIVATE|LG_KEY;
+ break;
+ /* can't be a certificate if it doesn't match one of the above
+ * attributes */
+ default:
+ classFlags = 0;
+ break;
+ }
+ if (copy) {
+ copy->data = (unsigned char*)pTemplate[i].pValue;
+ copy->len = pTemplate[i].ulValueLen;
+ }
+ copy = NULL;
+ }
+
+ /* certs */
+ if (classFlags & (LG_CERT|LG_TRUST)) {
+ lg_searchCertsAndTrust(sdb,&derCert,&name,&derSubject,
+ &issuerSN, &email,classFlags,search,
+ pTemplate, ulCount);
+ }
+
+ /* keys */
+ if (classFlags & (LG_PRIVATE|LG_PUBLIC|LG_KEY)) {
+ PRBool mustStrict = (name.len != 0);
+ lg_searchKeys(sdb, &key_id, classFlags, search,
+ mustStrict, pTemplate, ulCount);
+ }
+
+ /* crl's */
+ if (classFlags & LG_CRL) {
+ lg_searchCrls(sdb, &derSubject, isKrl, classFlags, search,
+ pTemplate, ulCount);
+ }
+ /* Add S/MIME entry stuff */
+ if (classFlags & LG_SMIME) {
+ lg_searchSMime(sdb, &email, search, pTemplate, ulCount);
+ }
+ return CKR_OK;
+}
+
+
+/* lg_FindObjectsInit initializes a search for token and session objects
+ * that match a template. */
+CK_RV lg_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *pTemplate,
+ CK_ULONG ulCount, SDBFind **retSearch)
+{
+ SDBFind *search;
+ CK_RV crv = CKR_OK;
+
+ *retSearch = NULL;
+
+ search = (SDBFind *)PORT_Alloc(sizeof(SDBFind));
+ if (search == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ search->handles = (CK_OBJECT_HANDLE *)
+ PORT_Alloc(sizeof(CK_OBJECT_HANDLE) * LG_SEARCH_BLOCK_SIZE);
+ if (search->handles == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ search->index = 0;
+ search->size = 0;
+ search->array_size = LG_SEARCH_BLOCK_SIZE;
+ /* FIXME - do we still need to get Login state? */
+
+ crv = lg_searchTokenList(sdb, search, pTemplate, ulCount);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ *retSearch = search;
+ return CKR_OK;
+
+loser:
+ if (search) {
+ lg_FreeSearch(search);
+ }
+ return crv;
+}
+
+
+/* lg_FindObjects continues a search for token and session objects
+ * that match a template, obtaining additional object handles. */
+CK_RV lg_FindObjects(SDB *sdb, SDBFind *search,
+ CK_OBJECT_HANDLE *phObject,CK_ULONG ulMaxObjectCount,
+ CK_ULONG *pulObjectCount)
+{
+ int transfer;
+ int left;
+
+ *pulObjectCount = 0;
+ left = search->size - search->index;
+ transfer = ((int)ulMaxObjectCount > left) ? left : ulMaxObjectCount;
+ if (transfer > 0) {
+ PORT_Memcpy(phObject,&search->handles[search->index],
+ transfer*sizeof(CK_OBJECT_HANDLE));
+ } else {
+ *phObject = CK_INVALID_HANDLE;
+ }
+
+ search->index += transfer;
+ *pulObjectCount = transfer;
+ return CKR_OK;
+}
+
+/* lg_FindObjectsFinal finishes a search for token and session objects. */
+CK_RV lg_FindObjectsFinal(SDB* lgdb, SDBFind *search)
+{
+
+ if (search != NULL) {
+ lg_FreeSearch(search);
+ }
+ return CKR_OK;
+}
diff --git a/security/nss/lib/softoken/legacydb/lginit.c b/security/nss/lib/softoken/legacydb/lginit.c
new file mode 100644
index 000000000..e77836939
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lginit.c
@@ -0,0 +1,663 @@
+/*
+ * NSS utility functions
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#include "lowkeyi.h"
+#include "pcert.h"
+#include "keydbi.h"
+#include "lgdb.h"
+#include "secoid.h"
+#include "prenv.h"
+
+typedef struct LGPrivateStr {
+ NSSLOWCERTCertDBHandle *certDB;
+ NSSLOWKEYDBHandle *keyDB;
+ PRLock *dbLock;
+ PLHashTable *hashTable;
+} LGPrivate;
+
+static char *
+lg_certdb_name_cb(void *arg, int dbVersion)
+{
+ const char *configdir = (const char *)arg;
+ const char *dbver;
+ char *smpname = NULL;
+ char *dbname = NULL;
+
+ switch (dbVersion) {
+ case 8:
+ dbver = "8";
+ break;
+ case 7:
+ dbver = "7";
+ break;
+ case 6:
+ dbver = "6";
+ break;
+ case 5:
+ dbver = "5";
+ break;
+ case 4:
+ default:
+ dbver = "";
+ break;
+ }
+
+ /* make sure we return something allocated with PORT_ so we have properly
+ * matched frees at the end */
+ smpname = PR_smprintf(CERT_DB_FMT, configdir, dbver);
+ if (smpname) {
+ dbname = PORT_Strdup(smpname);
+ PR_smprintf_free(smpname);
+ }
+ return dbname;
+}
+
+static char *
+lg_keydb_name_cb(void *arg, int dbVersion)
+{
+ const char *configdir = (const char *)arg;
+ const char *dbver;
+ char *smpname = NULL;
+ char *dbname = NULL;
+
+ switch (dbVersion) {
+ case 4:
+ dbver = "4";
+ break;
+ case 3:
+ dbver = "3";
+ break;
+ case 1:
+ dbver = "1";
+ break;
+ case 2:
+ default:
+ dbver = "";
+ break;
+ }
+
+ smpname = PR_smprintf(KEY_DB_FMT, configdir, dbver);
+ if (smpname) {
+ dbname = PORT_Strdup(smpname);
+ PR_smprintf_free(smpname);
+ }
+ return dbname;
+}
+
+const char *
+lg_EvaluateConfigDir(const char *configdir,char **appName)
+{
+ if (PORT_Strncmp(configdir, MULTIACCESS, sizeof(MULTIACCESS)-1) == 0) {
+ char *cdir;
+
+ *appName = PORT_Strdup(configdir+sizeof(MULTIACCESS)-1);
+ if (*appName == NULL) {
+ return configdir;
+ }
+ cdir = *appName;
+ while (*cdir && *cdir != ':') {
+ cdir++;
+ }
+ if (*cdir == ':') {
+ *cdir = 0;
+ cdir++;
+ }
+ configdir = cdir;
+ }
+ return configdir;
+}
+
+static int rdbmapflags(int flags);
+static rdbfunc lg_rdbfunc = NULL;
+static rdbstatusfunc lg_rdbstatusfunc = NULL;
+
+/* NOTE: SHLIB_SUFFIX is defined on the command line */
+#define RDBLIB SHLIB_PREFIX"rdb."SHLIB_SUFFIX
+
+DB * rdbopen(const char *appName, const char *prefix,
+ const char *type, int flags, int *status)
+{
+ PRLibrary *lib;
+ DB *db;
+ char *disableUnload = NULL;
+
+ if (lg_rdbfunc) {
+ db = (*lg_rdbfunc)(appName,prefix,type,rdbmapflags(flags));
+ if (!db && status && lg_rdbstatusfunc) {
+ *status = (*lg_rdbstatusfunc)();
+ }
+ return db;
+ }
+
+ /*
+ * try to open the library.
+ */
+ lib = PR_LoadLibrary(RDBLIB);
+
+ if (!lib) {
+ return NULL;
+ }
+
+ /* get the entry points */
+ lg_rdbstatusfunc = (rdbstatusfunc) PR_FindSymbol(lib,"rdbstatus");
+ lg_rdbfunc = (rdbfunc) PR_FindSymbol(lib,"rdbopen");
+ if (lg_rdbfunc) {
+ db = (*lg_rdbfunc)(appName,prefix,type,rdbmapflags(flags));
+ if (!db && status && lg_rdbstatusfunc) {
+ *status = (*lg_rdbstatusfunc)();
+ }
+ return db;
+ }
+
+ /* couldn't find the entry point, unload the library and fail */
+ disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
+ if (!disableUnload) {
+ PR_UnloadLibrary(lib);
+ }
+ return NULL;
+}
+
+/*
+ * the following data structures are from rdb.h.
+ */
+struct RDBStr {
+ DB db;
+ int (*xactstart)(DB *db);
+ int (*xactdone)(DB *db, PRBool abort);
+ int version;
+ int (*dbinitcomplete)(DB *db);
+};
+
+#define DB_RDB ((DBTYPE) 0xff)
+#define RDB_RDONLY 1
+#define RDB_RDWR 2
+#define RDB_CREATE 4
+
+static int
+rdbmapflags(int flags) {
+ switch (flags) {
+ case NO_RDONLY:
+ return RDB_RDONLY;
+ case NO_RDWR:
+ return RDB_RDWR;
+ case NO_CREATE:
+ return RDB_CREATE;
+ default:
+ break;
+ }
+ return 0;
+}
+
+PRBool
+db_IsRDB(DB *db)
+{
+ return (PRBool) db->type == DB_RDB;
+}
+
+int
+db_BeginTransaction(DB *db)
+{
+ struct RDBStr *rdb = (struct RDBStr *)db;
+ if (db->type != DB_RDB) {
+ return 0;
+ }
+
+ return rdb->xactstart(db);
+}
+
+int
+db_FinishTransaction(DB *db, PRBool abort)
+{
+ struct RDBStr *rdb = (struct RDBStr *)db;
+ if (db->type != DB_RDB) {
+ return 0;
+ }
+
+ return rdb->xactdone(db, abort);
+}
+
+static DB *
+lg_getRawDB(SDB *sdb)
+{
+ NSSLOWCERTCertDBHandle *certDB;
+ NSSLOWKEYDBHandle *keyDB;
+
+ certDB = lg_getCertDB(sdb);
+ if (certDB) {
+ return certDB->permCertDB;
+ }
+ keyDB = lg_getKeyDB(sdb);
+ if (keyDB) {
+ return keyDB->db;
+ }
+ return NULL;
+}
+
+CK_RV
+lg_Begin(SDB *sdb)
+{
+ DB *db = lg_getRawDB(sdb);
+ int ret;
+
+ if (db == NULL) {
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ ret = db_BeginTransaction(db);
+ if (ret != 0) {
+ return CKR_GENERAL_ERROR; /* could happen */
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_Commit(SDB *sdb)
+{
+ DB *db = lg_getRawDB(sdb);
+ int ret;
+
+ if (db == NULL) {
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ ret = db_FinishTransaction(db, PR_FALSE);
+ if (ret != 0) {
+ return CKR_GENERAL_ERROR; /* could happen */
+ }
+ return CKR_OK;
+}
+
+CK_RV
+lg_Abort(SDB *sdb)
+{
+ DB *db = lg_getRawDB(sdb);
+ int ret;
+
+ if (db == NULL) {
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ ret = db_FinishTransaction(db, PR_TRUE);
+ if (ret != 0) {
+ return CKR_GENERAL_ERROR; /* could happen */
+ }
+ return CKR_OK;
+}
+
+int
+db_InitComplete(DB *db)
+{
+ struct RDBStr *rdb = (struct RDBStr *)db;
+ if (db->type != DB_RDB) {
+ return 0;
+ }
+ /* we should have added a version number to the RDBS structure. Since we
+ * didn't, we detect that we have and 'extended' structure if the rdbstatus
+ * func exists */
+ if (!lg_rdbstatusfunc) {
+ return 0;
+ }
+
+ return rdb->dbinitcomplete(db);
+}
+
+
+
+SECStatus
+db_Copy(DB *dest,DB *src)
+{
+ int ret;
+ DBT key,data;
+ ret = (*src->seq)(src, &key, &data, R_FIRST);
+ if (ret) {
+ return SECSuccess;
+ }
+
+ do {
+ (void)(*dest->put)(dest,&key,&data, R_NOOVERWRITE);
+ } while ( (*src->seq)(src, &key, &data, R_NEXT) == 0);
+ (void)(*dest->sync)(dest,0);
+
+ return SECSuccess;
+}
+
+
+static CK_RV
+lg_OpenCertDB(const char * configdir, const char *prefix, PRBool readOnly,
+ NSSLOWCERTCertDBHandle **certdbPtr)
+{
+ NSSLOWCERTCertDBHandle *certdb = NULL;
+ CK_RV crv = CKR_NETSCAPE_CERTDB_FAILED;
+ SECStatus rv;
+ char * name = NULL;
+ char * appName = NULL;
+
+ if (prefix == NULL) {
+ prefix = "";
+ }
+
+ configdir = lg_EvaluateConfigDir(configdir, &appName);
+
+ name = PR_smprintf("%s" PATH_SEPARATOR "%s",configdir,prefix);
+ if (name == NULL) goto loser;
+
+ certdb = (NSSLOWCERTCertDBHandle*)PORT_ZAlloc(sizeof(NSSLOWCERTCertDBHandle));
+ if (certdb == NULL)
+ goto loser;
+
+ certdb->ref = 1;
+/* fix when we get the DB in */
+ rv = nsslowcert_OpenCertDB(certdb, readOnly, appName, prefix,
+ lg_certdb_name_cb, (void *)name, PR_FALSE);
+ if (rv == SECSuccess) {
+ crv = CKR_OK;
+ *certdbPtr = certdb;
+ certdb = NULL;
+ }
+loser:
+ if (certdb) PR_Free(certdb);
+ if (name) PR_smprintf_free(name);
+ if (appName) PORT_Free(appName);
+ return crv;
+}
+
+static CK_RV
+lg_OpenKeyDB(const char * configdir, const char *prefix, PRBool readOnly,
+ NSSLOWKEYDBHandle **keydbPtr)
+{
+ NSSLOWKEYDBHandle *keydb;
+ char * name = NULL;
+ char * appName = NULL;
+
+ if (prefix == NULL) {
+ prefix = "";
+ }
+ configdir = lg_EvaluateConfigDir(configdir, &appName);
+
+ name = PR_smprintf("%s" PATH_SEPARATOR "%s",configdir,prefix);
+ if (name == NULL)
+ return CKR_HOST_MEMORY;
+ keydb = nsslowkey_OpenKeyDB(readOnly, appName, prefix,
+ lg_keydb_name_cb, (void *)name);
+ PR_smprintf_free(name);
+ if (appName) PORT_Free(appName);
+ if (keydb == NULL)
+ return CKR_NETSCAPE_KEYDB_FAILED;
+ *keydbPtr = keydb;
+
+ return CKR_OK;
+}
+
+/*
+ * Accessors for the private parts of the sdb structure.
+ */
+void
+lg_DBLock(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ PR_Lock(lgdb_p->dbLock);
+}
+
+void
+lg_DBUnlock(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ PR_Unlock(lgdb_p->dbLock);
+}
+
+PLHashTable *
+lg_GetHashTable(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ return lgdb_p->hashTable;
+}
+
+NSSLOWCERTCertDBHandle *
+lg_getCertDB(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+
+ return lgdb_p->certDB;
+}
+
+NSSLOWKEYDBHandle *
+lg_getKeyDB(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+
+ return lgdb_p->keyDB;
+}
+
+CK_RV
+lg_Close(SDB *sdb)
+{
+ LGPrivate *lgdb_p = (LGPrivate *)sdb->private;
+ lg_ClearTokenKeyHashTable(sdb);
+ if (lgdb_p) {
+ if (lgdb_p->certDB) {
+ nsslowcert_ClosePermCertDB(lgdb_p->certDB);
+ } else if (lgdb_p->keyDB) {
+ nsslowkey_CloseKeyDB(lgdb_p->keyDB);
+ }
+ if (lgdb_p->dbLock) {
+ PR_DestroyLock(lgdb_p->dbLock);
+ }
+ if (lgdb_p->hashTable) {
+ PL_HashTableDestroy(lgdb_p->hashTable);
+ }
+ PORT_Free(lgdb_p);
+ }
+ PORT_Free(sdb);
+ return CKR_OK;
+}
+
+static PLHashNumber
+lg_HashNumber(const void *key)
+{
+ return (PLHashNumber) key;
+}
+
+PRIntn
+lg_CompareValues(const void *v1, const void *v2)
+{
+ PLHashNumber value1 = (PLHashNumber) v1;
+ PLHashNumber value2 = (PLHashNumber) v2;
+ return (value1 == value2);
+}
+
+
+/*
+ * helper function to wrap a NSSLOWCERTCertDBHandle or a NSSLOWKEYDBHandle
+ * with and sdb structure.
+ */
+CK_RV
+lg_init(SDB **pSdb, int flags, NSSLOWCERTCertDBHandle *certdbPtr,
+ NSSLOWKEYDBHandle *keydbPtr)
+{
+ SDB *sdb = NULL;
+ LGPrivate *lgdb_p = NULL;
+ CK_RV error = CKR_HOST_MEMORY;
+
+ *pSdb = NULL;
+ sdb = (SDB *) PORT_Alloc(sizeof(SDB));
+ if (sdb == NULL) {
+ goto loser;
+ }
+ lgdb_p = (LGPrivate *) PORT_Alloc(sizeof(LGPrivate));
+ if (lgdb_p == NULL) {
+ goto loser;
+ }
+ /* invariant fields */
+ lgdb_p->certDB = certdbPtr;
+ lgdb_p->keyDB = keydbPtr;
+ lgdb_p->dbLock = PR_NewLock();
+ if (lgdb_p->dbLock == NULL) {
+ goto loser;
+ }
+ lgdb_p->hashTable = PL_NewHashTable(64, lg_HashNumber, lg_CompareValues,
+ SECITEM_HashCompare, NULL, 0);
+ if (lgdb_p->hashTable == NULL) {
+ goto loser;
+ }
+
+ sdb->sdb_type = SDB_LEGACY;
+ sdb->sdb_flags = flags;
+ sdb->private = lgdb_p;
+ sdb->sdb_FindObjectsInit = lg_FindObjectsInit;
+ sdb->sdb_FindObjects = lg_FindObjects;
+ sdb->sdb_FindObjectsFinal = lg_FindObjectsFinal;
+ sdb->sdb_GetAttributeValue = lg_GetAttributeValue;
+ sdb->sdb_SetAttributeValue = lg_SetAttributeValue;
+ sdb->sdb_CreateObject = lg_CreateObject;
+ sdb->sdb_DestroyObject = lg_DestroyObject;
+ sdb->sdb_GetMetaData = lg_GetMetaData;
+ sdb->sdb_PutMetaData = lg_PutMetaData;
+ sdb->sdb_Begin = lg_Begin;
+ sdb->sdb_Commit = lg_Commit;
+ sdb->sdb_Abort = lg_Abort;
+ sdb->sdb_Reset = lg_Reset;
+ sdb->sdb_Close = lg_Close;
+
+
+ *pSdb = sdb;
+ return CKR_OK;
+
+loser:
+ if (sdb) {
+ PORT_Free(sdb);
+ }
+ if (lgdb_p) {
+ if (lgdb_p->dbLock) {
+ PR_DestroyLock(lgdb_p->dbLock);
+ }
+ if (lgdb_p->hashTable) {
+ PL_HashTableDestroy(lgdb_p->hashTable);
+ }
+ PORT_Free(lgdb_p);
+ }
+ return error;
+
+}
+
+/*
+ * OK there are now lots of options here, lets go through them all:
+ *
+ * configdir - base directory where all the cert, key, and module datbases live.
+ * certPrefix - prefix added to the beginning of the cert database example: "
+ * "https-server1-"
+ * keyPrefix - prefix added to the beginning of the key database example: "
+ * "https-server1-"
+ * secmodName - name of the security module database (usually "secmod.db").
+ * readOnly - Boolean: true if the databases are to be openned read only.
+ * nocertdb - Don't open the cert DB and key DB's, just initialize the
+ * Volatile certdb.
+ * nomoddb - Don't open the security module DB, just initialize the
+ * PKCS #11 module.
+ * forceOpen - Continue to force initializations even if the databases cannot
+ * be opened.
+ */
+CK_RV
+legacy_Open(const char *configdir, const char *certPrefix,
+ const char *keyPrefix, int certVersion, int keyVersion,
+ int flags, SDB **certDB, SDB **keyDB)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+ PRBool readOnly = (flags == SDB_RDONLY)? PR_TRUE: PR_FALSE;
+
+ rv = SECOID_Init();
+ if (SECSuccess != rv) {
+ return CKR_DEVICE_ERROR;
+ }
+ nsslowcert_InitLocks();
+
+ if (keyDB) *keyDB = NULL;
+ if (certDB) *certDB = NULL;
+
+ if (certDB) {
+ NSSLOWCERTCertDBHandle *certdbPtr;
+
+ crv = lg_OpenCertDB(configdir, certPrefix, readOnly, &certdbPtr);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = lg_init(certDB, flags, certdbPtr, NULL);
+ if (crv != CKR_OK) {
+ nsslowcert_ClosePermCertDB(certdbPtr);
+ goto loser;
+ }
+ }
+ if (keyDB) {
+ NSSLOWKEYDBHandle *keydbPtr;
+
+ crv = lg_OpenKeyDB(configdir, keyPrefix, readOnly, &keydbPtr);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = lg_init(keyDB, flags, NULL, keydbPtr);
+ if (crv != CKR_OK) {
+ nsslowkey_CloseKeyDB(keydbPtr);
+ goto loser;
+ }
+ if (certDB && *certDB) {
+ LGPrivate *lgdb_p = (LGPrivate *)(*certDB)->private;
+ lgdb_p->keyDB = keydbPtr;
+ }
+ }
+
+loser:
+ if (crv != CKR_OK) {
+ if (keyDB && *keyDB) {
+ lg_Close(*keyDB);
+ *keyDB = NULL;
+ }
+ if (certDB && *certDB) {
+ lg_Close(*certDB);
+ *certDB = NULL;
+ }
+ }
+ return crv;
+}
+
+CK_RV
+legacy_Shutdown(void)
+{
+ nsslowcert_DestroyFreeLists();
+ nsslowcert_DestroyGlobalLocks();
+ SECOID_Shutdown();
+ return CKR_OK;
+}
diff --git a/security/nss/lib/softoken/legacydb/lgutil.c b/security/nss/lib/softoken/legacydb/lgutil.c
new file mode 100644
index 000000000..f474b5fea
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgutil.c
@@ -0,0 +1,424 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "lgdb.h"
+#include "secerr.h"
+#include "lgglue.h"
+
+/*
+ * ******************** Attribute Utilities *******************************
+ */
+
+/*
+ * look up and attribute structure from a type and Object structure.
+ * The returned attribute is referenced and needs to be freed when
+ * it is no longer needed.
+ */
+const CK_ATTRIBUTE *
+lg_FindAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
+ CK_ULONG count )
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ if (templ[i].type == type) {
+ return &templ[i];
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * return true if object has attribute
+ */
+PRBool
+lg_hasAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
+ CK_ULONG count )
+{
+ if (lg_FindAttribute(type, templ, count) == NULL) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/*
+ * copy an attribute into a SECItem. Secitem is allocated in the specified
+ * arena.
+ */
+CK_RV
+lg_Attribute2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item)
+{
+ int len;
+ const CK_ATTRIBUTE *attribute;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+ len = attribute->ulValueLen;
+
+ if (arena) {
+ item->data = (unsigned char *) PORT_ArenaAlloc(arena,len);
+ } else {
+ item->data = (unsigned char *) PORT_Alloc(len);
+ }
+ if (item->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ item->len = len;
+ PORT_Memcpy(item->data, attribute->pValue, len);
+ return CKR_OK;
+}
+
+
+/*
+ * copy an unsigned attribute into a SECItem. Secitem is allocated in
+ * the specified arena.
+ */
+CK_RV
+lg_Attribute2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item)
+{
+ const CK_ATTRIBUTE *attribute;
+ item->data = NULL;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ (void)SECITEM_AllocItem(arena, item, attribute->ulValueLen);
+ if (item->data == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy(item->data, attribute->pValue, item->len);
+ return CKR_OK;
+}
+
+/*
+ * copy an unsigned attribute into a SECItem. Secitem is allocated in
+ * the specified arena.
+ */
+CK_RV
+lg_PrivAttr2SSecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw)
+{
+ const CK_ATTRIBUTE *attribute;
+ SECItem epki, *dest = NULL;
+ SECStatus rv;
+
+ item->data = NULL;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ epki.data = attribute->pValue;
+ epki.len = attribute->ulValueLen;
+
+ rv = lg_util_decrypt(sdbpw, &epki, &dest);
+ if (rv != SECSuccess) {
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+ (void)SECITEM_AllocItem(arena, item, dest->len);
+ if (item->data == NULL) {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ return CKR_HOST_MEMORY;
+ }
+
+ PORT_Memcpy(item->data, dest->data, item->len);
+ SECITEM_FreeItem(dest, PR_TRUE);
+ return CKR_OK;
+}
+
+CK_RV
+lg_PrivAttr2SecItem(PLArenaPool *arena, CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE *templ, CK_ULONG count,
+ SECItem *item, SDB *sdbpw)
+{
+ return lg_PrivAttr2SSecItem(arena, type, templ, count, item, sdbpw);
+}
+
+/*
+ * this is only valid for CK_BBOOL type attributes. Return the state
+ * of that attribute.
+ */
+PRBool
+lg_isTrue(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ const CK_ATTRIBUTE *attribute;
+ PRBool tok = PR_FALSE;
+
+ attribute=lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) { return PR_FALSE; }
+ tok = (PRBool)(*(CK_BBOOL *)attribute->pValue);
+
+ return tok;
+}
+
+/*
+ * return a null terminated string from attribute 'type'. This string
+ * is allocated and needs to be freed with PORT_Free() When complete.
+ */
+char *
+lg_getString(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ, CK_ULONG count)
+{
+ const CK_ATTRIBUTE *attribute;
+ char *label = NULL;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return NULL;
+
+ if (attribute->pValue != NULL) {
+ label = (char *) PORT_Alloc(attribute->ulValueLen+1);
+ if (label == NULL) {
+ return NULL;
+ }
+
+ PORT_Memcpy(label,attribute->pValue, attribute->ulValueLen);
+ label[attribute->ulValueLen] = 0;
+ }
+ return label;
+}
+
+CK_RV
+lg_GetULongAttribute(CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE *templ,
+ CK_ULONG count, CK_ULONG *longData)
+{
+ const CK_ATTRIBUTE *attribute;
+ CK_ULONG value = 0;
+ const unsigned char *data;
+ int i;
+
+ attribute = lg_FindAttribute(type, templ, count);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ if (attribute->ulValueLen != 4) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ data = (const unsigned char *)attribute->pValue;
+ for (i=0; i < 4; i++) {
+ value |= (CK_ULONG)(data[i]) << ((3-i)*8);
+ }
+
+ *longData = value;
+ return CKR_OK;
+}
+
+/*
+ * ******************** Object Utilities *******************************
+ */
+
+SECStatus
+lg_deleteTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle)
+{
+ SECItem *item;
+ PRBool rem;
+ PLHashTable *hashTable= lg_GetHashTable(sdb);
+
+ item = (SECItem *)PL_HashTableLookup(hashTable, (void *)handle);
+ rem = PL_HashTableRemove(hashTable,(void *)handle) ;
+ if (rem && item) {
+ SECITEM_FreeItem(item,PR_TRUE);
+ }
+ return rem ? SECSuccess : SECFailure;
+}
+
+/* must be called holding lg_DBLock(sdb) */
+static SECStatus
+lg_addTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle, SECItem *key)
+{
+ PLHashEntry *entry;
+ SECItem *item;
+ PLHashTable *hashTable= lg_GetHashTable(sdb);
+
+ item = SECITEM_DupItem(key);
+ if (item == NULL) {
+ return SECFailure;
+ }
+ entry = PL_HashTableAdd(hashTable,(void *)handle,item);
+ if (entry == NULL) {
+ SECITEM_FreeItem(item,PR_TRUE);
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/* must be called holding lg_DBLock(sdb) */
+const SECItem *
+lg_lookupTokenKeyByHandle(SDB *sdb, CK_OBJECT_HANDLE handle)
+{
+ PLHashTable *hashTable= lg_GetHashTable(sdb);
+ return (const SECItem *)PL_HashTableLookup(hashTable, (void *)handle);
+}
+
+
+static PRIntn
+lg_freeHashItem(PLHashEntry* entry, PRIntn index, void *arg)
+{
+ SECItem *item = (SECItem *)entry->value;
+
+ SECITEM_FreeItem(item, PR_TRUE);
+ return HT_ENUMERATE_NEXT;
+}
+
+CK_RV
+lg_ClearTokenKeyHashTable(SDB *sdb)
+{
+ PLHashTable *hashTable;
+ lg_DBLock(sdb);
+ hashTable= lg_GetHashTable(sdb);
+ PL_HashTableEnumerateEntries(hashTable, lg_freeHashItem, NULL);
+ lg_DBUnlock(sdb);
+ return CKR_OK;
+}
+
+/*
+ * handle Token Object stuff
+ */
+static void
+lg_XORHash(unsigned char *key, unsigned char *dbkey, int len)
+{
+ int i;
+
+ PORT_Memset(key, 0, 4);
+
+ for (i=0; i < len-4; i += 4) {
+ key[0] ^= dbkey[i];
+ key[1] ^= dbkey[i+1];
+ key[2] ^= dbkey[i+2];
+ key[3] ^= dbkey[i+3];
+ }
+}
+
+/* Make a token handle for an object and record it so we can find it again */
+CK_OBJECT_HANDLE
+lg_mkHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class)
+{
+ unsigned char hashBuf[4];
+ CK_OBJECT_HANDLE handle;
+ const SECItem *key;
+
+ handle = class;
+ /* there is only one KRL, use a fixed handle for it */
+ if (handle != LG_TOKEN_KRL_HANDLE) {
+ lg_XORHash(hashBuf,dbKey->data,dbKey->len);
+ handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) |
+ (hashBuf[2] << 8) | hashBuf[3];
+ handle = class | (handle & ~(LG_TOKEN_TYPE_MASK|LG_TOKEN_MASK));
+ /* we have a CRL who's handle has randomly matched the reserved KRL
+ * handle, increment it */
+ if (handle == LG_TOKEN_KRL_HANDLE) {
+ handle++;
+ }
+ }
+
+ lg_DBLock(sdb);
+ while ((key = lg_lookupTokenKeyByHandle(sdb,handle)) != NULL) {
+ if (SECITEM_ItemsAreEqual(key,dbKey)) {
+ lg_DBUnlock(sdb);
+ return handle;
+ }
+ handle++;
+ }
+ lg_addTokenKeyByHandle(sdb,handle,dbKey);
+ lg_DBUnlock(sdb);
+ return handle;
+}
+
+PRBool
+lg_poisonHandle(SDB *sdb, SECItem *dbKey, CK_OBJECT_HANDLE class)
+{
+ unsigned char hashBuf[4];
+ CK_OBJECT_HANDLE handle;
+ const SECItem *key;
+
+ handle = class;
+ /* there is only one KRL, use a fixed handle for it */
+ if (handle != LG_TOKEN_KRL_HANDLE) {
+ lg_XORHash(hashBuf,dbKey->data,dbKey->len);
+ handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) |
+ (hashBuf[2] << 8) | hashBuf[3];
+ handle = class | (handle & ~(LG_TOKEN_TYPE_MASK|LG_TOKEN_MASK));
+ /* we have a CRL who's handle has randomly matched the reserved KRL
+ * handle, increment it */
+ if (handle == LG_TOKEN_KRL_HANDLE) {
+ handle++;
+ }
+ }
+ lg_DBLock(sdb);
+ while ((key = lg_lookupTokenKeyByHandle(sdb,handle)) != NULL) {
+ if (SECITEM_ItemsAreEqual(key,dbKey)) {
+ key->data[0] ^= 0x80;
+ lg_DBUnlock(sdb);
+ return PR_TRUE;
+ }
+ handle++;
+ }
+ lg_DBUnlock(sdb);
+ return PR_FALSE;
+}
+
+static LGEncryptFunc lg_encrypt_stub = NULL;
+static LGDecryptFunc lg_decrypt_stub = NULL;
+
+void
+legacy_SetCryptFunctions(LGEncryptFunc enc, LGDecryptFunc dec)
+{
+ lg_encrypt_stub = enc;
+ lg_decrypt_stub = dec;
+}
+
+SECStatus lg_util_encrypt(PLArenaPool *arena, SDB *sdb,
+ SECItem *plainText, SECItem **cipherText)
+{
+ if (lg_encrypt_stub == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*lg_encrypt_stub)(arena, sdb, plainText, cipherText);
+}
+
+SECStatus lg_util_decrypt(SDB *sdb, SECItem *cipherText, SECItem **plainText)
+{
+ if (lg_decrypt_stub == NULL) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*lg_decrypt_stub)(sdb, cipherText, plainText);
+}
+
+
diff --git a/security/nss/lib/softoken/legacydb/lowcert.c b/security/nss/lib/softoken/legacydb/lowcert.c
new file mode 100644
index 000000000..143999045
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowcert.c
@@ -0,0 +1,827 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+
+/*
+ * Certificate handling code
+ *
+ * $Id$
+ */
+
+#include "seccomon.h"
+#include "secder.h"
+#include "nssilock.h"
+#include "lowkeyi.h"
+#include "secasn1.h"
+#include "secoid.h"
+#include "secerr.h"
+#include "pcert.h"
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+static const SEC_ASN1Template nsslowcert_SubjectPublicKeyInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWCERTSubjectPublicKeyInfo) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSLOWCERTSubjectPublicKeyInfo,algorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_BIT_STRING,
+ offsetof(NSSLOWCERTSubjectPublicKeyInfo,subjectPublicKey), },
+ { 0, }
+};
+
+static const SEC_ASN1Template nsslowcert_RSAPublicKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPublicKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.modulus), },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.rsa.publicExponent), },
+ { 0, }
+};
+static const SEC_ASN1Template nsslowcert_DSAPublicKeyTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dsa.publicValue), },
+ { 0, }
+};
+static const SEC_ASN1Template nsslowcert_DHPublicKeyTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPublicKey,u.dh.publicValue), },
+ { 0, }
+};
+
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+
+static void
+prepare_low_rsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
+{
+ pubk->u.rsa.modulus.type = siUnsignedInteger;
+ pubk->u.rsa.publicExponent.type = siUnsignedInteger;
+}
+
+static void
+prepare_low_dsa_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
+{
+ pubk->u.dsa.publicValue.type = siUnsignedInteger;
+ pubk->u.dsa.params.prime.type = siUnsignedInteger;
+ pubk->u.dsa.params.subPrime.type = siUnsignedInteger;
+ pubk->u.dsa.params.base.type = siUnsignedInteger;
+}
+
+static void
+prepare_low_dh_pub_key_for_asn1(NSSLOWKEYPublicKey *pubk)
+{
+ pubk->u.dh.prime.type = siUnsignedInteger;
+ pubk->u.dh.base.type = siUnsignedInteger;
+ pubk->u.dh.publicValue.type = siUnsignedInteger;
+}
+
+/*
+ * simple cert decoder to avoid the cost of asn1 engine
+ */
+static unsigned char *
+nsslowcert_dataStart(unsigned char *buf, unsigned int length,
+ unsigned int *data_length, PRBool includeTag,
+ unsigned char* rettag) {
+ unsigned char tag;
+ unsigned int used_length= 0;
+
+ tag = buf[used_length++];
+
+ if (rettag) {
+ *rettag = tag;
+ }
+
+ /* blow out when we come to the end */
+ if (tag == 0) {
+ return NULL;
+ }
+
+ *data_length = buf[used_length++];
+
+ if (*data_length&0x80) {
+ int len_count = *data_length & 0x7f;
+
+ *data_length = 0;
+
+ while (len_count-- > 0) {
+ *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 void SetTimeType(SECItem* item, unsigned char tagtype)
+{
+ switch (tagtype) {
+ case SEC_ASN1_UTC_TIME:
+ item->type = siUTCTime;
+ break;
+
+ case SEC_ASN1_GENERALIZED_TIME:
+ item->type = siGeneralizedTime;
+ break;
+
+ default:
+ PORT_Assert(0);
+ break;
+ }
+}
+
+static int
+nsslowcert_GetValidityFields(unsigned char *buf,int buf_length,
+ SECItem *notBefore, SECItem *notAfter)
+{
+ unsigned char tagtype;
+ notBefore->data = nsslowcert_dataStart(buf,buf_length,
+ &notBefore->len,PR_FALSE, &tagtype);
+ if (notBefore->data == NULL) return SECFailure;
+ SetTimeType(notBefore, tagtype);
+ buf_length -= (notBefore->data-buf) + notBefore->len;
+ buf = notBefore->data + notBefore->len;
+ notAfter->data = nsslowcert_dataStart(buf,buf_length,
+ &notAfter->len,PR_FALSE, &tagtype);
+ if (notAfter->data == NULL) return SECFailure;
+ SetTimeType(notAfter, tagtype);
+ return SECSuccess;
+}
+
+static int
+nsslowcert_GetCertFields(unsigned char *cert,int cert_length,
+ SECItem *issuer, SECItem *serial, SECItem *derSN, SECItem *subject,
+ SECItem *valid, SECItem *subjkey, SECItem *extensions)
+{
+ unsigned char *buf;
+ unsigned int buf_length;
+ unsigned char *dummy;
+ unsigned int dummylen;
+
+ /* get past the signature wrap */
+ buf = nsslowcert_dataStart(cert,cert_length,&buf_length,PR_FALSE, NULL);
+ if (buf == NULL) return SECFailure;
+ /* get into the raw cert data */
+ buf = nsslowcert_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 = nsslowcert_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=nsslowcert_dataStart(buf,buf_length,&derSN->len,PR_TRUE, NULL);
+ }
+ serial->data = nsslowcert_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 = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE, NULL);
+ if (dummy == NULL) return SECFailure;
+ buf_length -= (dummy-buf) + dummylen;
+ buf = dummy + dummylen;
+ /* issuer */
+ issuer->data = nsslowcert_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 (valid == NULL) {
+ return SECSuccess;
+ }
+ /* validity */
+ valid->data = nsslowcert_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=nsslowcert_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=nsslowcert_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;
+
+ extensions->data = NULL;
+ extensions->len = 0;
+ while (buf_length > 0) {
+ /* EXTENSIONS */
+ if (buf[0] == 0xa3) {
+ extensions->data = nsslowcert_dataStart(buf,buf_length,
+ &extensions->len, PR_FALSE, NULL);
+ break;
+ }
+ dummy = nsslowcert_dataStart(buf,buf_length,&dummylen,PR_FALSE,NULL);
+ if (dummy == NULL) return SECFailure;
+ buf_length -= (dummy - buf) + dummylen;
+ buf = dummy + dummylen;
+ }
+ return SECSuccess;
+}
+
+static SECStatus
+nsslowcert_GetCertTimes(NSSLOWCERTCertificate *c, PRTime *notBefore, PRTime *notAfter)
+{
+ int rv;
+ NSSLOWCERTValidity validity;
+
+ rv = nsslowcert_GetValidityFields(c->validity.data,c->validity.len,
+ &validity.notBefore,&validity.notAfter);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ /* convert DER not-before time */
+ rv = DER_DecodeTimeChoice(notBefore, &validity.notBefore);
+ if (rv) {
+ return(SECFailure);
+ }
+
+ /* convert DER not-after time */
+ rv = DER_DecodeTimeChoice(notAfter, &validity.notAfter);
+ if (rv) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * is certa newer than certb? If one is expired, pick the other one.
+ */
+PRBool
+nsslowcert_IsNewer(NSSLOWCERTCertificate *certa, NSSLOWCERTCertificate *certb)
+{
+ PRTime notBeforeA, notAfterA, notBeforeB, notAfterB, now;
+ SECStatus rv;
+ PRBool newerbefore, newerafter;
+
+ rv = nsslowcert_GetCertTimes(certa, &notBeforeA, &notAfterA);
+ if ( rv != SECSuccess ) {
+ return(PR_FALSE);
+ }
+
+ rv = nsslowcert_GetCertTimes(certb, &notBeforeB, &notAfterB);
+ if ( rv != SECSuccess ) {
+ return(PR_TRUE);
+ }
+
+ newerbefore = PR_FALSE;
+ if ( LL_CMP(notBeforeA, >, notBeforeB) ) {
+ newerbefore = PR_TRUE;
+ }
+
+ newerafter = PR_FALSE;
+ if ( LL_CMP(notAfterA, >, notAfterB) ) {
+ newerafter = PR_TRUE;
+ }
+
+ if ( newerbefore && newerafter ) {
+ return(PR_TRUE);
+ }
+
+ if ( ( !newerbefore ) && ( !newerafter ) ) {
+ return(PR_FALSE);
+ }
+
+ /* get current time */
+ now = PR_Now();
+
+ if ( newerbefore ) {
+ /* cert A was issued after cert B, but expires sooner */
+ /* if A is expired, then pick B */
+ if ( LL_CMP(notAfterA, <, now ) ) {
+ return(PR_FALSE);
+ }
+ return(PR_TRUE);
+ } else {
+ /* cert B was issued after cert A, but expires sooner */
+ /* if B is expired, then pick A */
+ if ( LL_CMP(notAfterB, <, now ) ) {
+ return(PR_TRUE);
+ }
+ return(PR_FALSE);
+ }
+}
+
+#define SOFT_DEFAULT_CHUNKSIZE 2048
+
+static SECStatus
+nsslowcert_KeyFromIssuerAndSN(PRArenaPool *arena,
+ SECItem *issuer, SECItem *sn, SECItem *key)
+{
+ unsigned int len = sn->len + issuer->len;
+
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ key->data = (unsigned char*)PORT_ArenaAlloc(arena, len);
+ if ( !key->data ) {
+ goto loser;
+ }
+
+ key->len = len;
+ /* copy the serialNumber */
+ PORT_Memcpy(key->data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+nsslowcert_KeyFromIssuerAndSNStatic(unsigned char *space,
+ int spaceLen, SECItem *issuer, SECItem *sn, SECItem *key)
+{
+ unsigned int len = sn->len + issuer->len;
+
+ key->data = pkcs11_allocStaticData(len, space, spaceLen);
+ if ( !key->data ) {
+ goto loser;
+ }
+
+ key->len = len;
+ /* copy the serialNumber */
+ PORT_Memcpy(key->data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy(&key->data[sn->len], issuer->data, issuer->len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+
+static char *
+nsslowcert_EmailName(SECItem *derDN, char *space, unsigned int len)
+{
+ unsigned char *buf;
+ unsigned int buf_length;
+
+ /* unwrap outer sequence */
+ buf=nsslowcert_dataStart(derDN->data,derDN->len,&buf_length,PR_FALSE,NULL);
+ if (buf == NULL) return NULL;
+
+ /* Walk each RDN */
+ while (buf_length > 0) {
+ unsigned char *rdn;
+ unsigned int rdn_length;
+
+ /* grab next rdn */
+ rdn=nsslowcert_dataStart(buf, buf_length, &rdn_length, PR_FALSE, NULL);
+ if (rdn == NULL) { return NULL; }
+ buf_length -= (rdn - buf) + rdn_length;
+ buf = rdn+rdn_length;
+
+ while (rdn_length > 0) {
+ unsigned char *ava;
+ unsigned int ava_length;
+ unsigned char *oid;
+ unsigned int oid_length;
+ unsigned char *name;
+ unsigned int name_length;
+ SECItem oidItem;
+ SECOidTag type;
+
+ /* unwrap the ava */
+ ava=nsslowcert_dataStart(rdn, rdn_length, &ava_length, PR_FALSE,
+ NULL);
+ if (ava == NULL) return NULL;
+ rdn_length -= (ava-rdn)+ava_length;
+ rdn = ava + ava_length;
+
+ oid=nsslowcert_dataStart(ava, ava_length, &oid_length, PR_FALSE,
+ NULL);
+ if (oid == NULL) { return NULL; }
+ ava_length -= (oid-ava)+oid_length;
+ ava = oid+oid_length;
+
+ name=nsslowcert_dataStart(ava, ava_length, &name_length, PR_FALSE,
+ NULL);
+ if (oid == NULL) { return NULL; }
+ ava_length -= (name-ava)+name_length;
+ ava = name+name_length;
+
+ oidItem.data = oid;
+ oidItem.len = oid_length;
+ type = SECOID_FindOIDTag(&oidItem);
+ if ((type == SEC_OID_PKCS9_EMAIL_ADDRESS) ||
+ (type == SEC_OID_RFC1274_MAIL)) {
+ /* Email is supposed to be IA5String, so no
+ * translation necessary */
+ char *emailAddr;
+ emailAddr = (char *)pkcs11_copyStaticData(name,name_length+1,
+ (unsigned char *)space,len);
+ if (emailAddr) {
+ emailAddr[name_length] = 0;
+ }
+ return emailAddr;
+ }
+ }
+ }
+ return NULL;
+}
+
+static char *
+nsslowcert_EmailAltName(NSSLOWCERTCertificate *cert, char *space,
+ unsigned int len)
+{
+ unsigned char *exts;
+ unsigned int exts_length;
+
+ /* unwrap the sequence */
+ exts = nsslowcert_dataStart(cert->extensions.data, cert->extensions.len,
+ &exts_length, PR_FALSE, NULL);
+ /* loop through extension */
+ while (exts && exts_length > 0) {
+ unsigned char * ext;
+ unsigned int ext_length;
+ unsigned char *oid;
+ unsigned int oid_length;
+ unsigned char *nameList;
+ unsigned int nameList_length;
+ SECItem oidItem;
+ SECOidTag type;
+
+ ext = nsslowcert_dataStart(exts, exts_length, &ext_length,
+ PR_FALSE, NULL);
+ if (ext == NULL) { break; }
+ exts_length -= (ext - exts) + ext_length;
+ exts = ext+ext_length;
+
+ oid=nsslowcert_dataStart(ext, ext_length, &oid_length, PR_FALSE, NULL);
+ if (oid == NULL) { break; }
+ ext_length -= (oid - ext) + oid_length;
+ ext = oid+oid_length;
+ oidItem.data = oid;
+ oidItem.len = oid_length;
+ type = SECOID_FindOIDTag(&oidItem);
+
+ /* get Alt Extension */
+ if (type != SEC_OID_X509_SUBJECT_ALT_NAME) {
+ continue;
+ }
+
+ /* skip passed the critical flag */
+ if (ext[0] == 0x01) { /* BOOLEAN */
+ unsigned char *dummy;
+ unsigned int dummy_length;
+ dummy = nsslowcert_dataStart(ext, ext_length, &dummy_length,
+ PR_FALSE, NULL);
+ if (dummy == NULL) { break; }
+ ext_length -= (dummy - ext) + dummy_length;
+ ext = dummy+dummy_length;
+ }
+
+
+ /* unwrap the name list */
+ nameList = nsslowcert_dataStart(ext, ext_length, &nameList_length,
+ PR_FALSE, NULL);
+ if (nameList == NULL) { break; }
+ ext_length -= (nameList - ext) + nameList_length;
+ ext = nameList+nameList_length;
+ nameList = nsslowcert_dataStart(nameList, nameList_length,
+ &nameList_length, PR_FALSE, NULL);
+ /* loop through the name list */
+ while (nameList && nameList_length > 0) {
+ unsigned char *thisName;
+ unsigned int thisName_length;
+
+ thisName = nsslowcert_dataStart(nameList, nameList_length,
+ &thisName_length, PR_FALSE, NULL);
+ if (thisName == NULL) { break; }
+ if (nameList[0] == 0xa2) { /* DNS Name */
+ SECItem dn;
+ char *emailAddr;
+
+ dn.data = thisName;
+ dn.len = thisName_length;
+ emailAddr = nsslowcert_EmailName(&dn, space, len);
+ if (emailAddr) {
+ return emailAddr;
+ }
+ }
+ if (nameList[0] == 0x81) { /* RFC 822name */
+ char *emailAddr;
+ emailAddr = (char *)pkcs11_copyStaticData(thisName,
+ thisName_length+1, (unsigned char *)space,len);
+ if (emailAddr) {
+ emailAddr[thisName_length] = 0;
+ }
+ return emailAddr;
+ }
+ nameList_length -= (thisName-nameList) + thisName_length;
+ nameList = thisName + thisName_length;
+ }
+ break;
+ }
+ return NULL;
+}
+
+static char *
+nsslowcert_GetCertificateEmailAddress(NSSLOWCERTCertificate *cert)
+{
+ char *emailAddr = NULL;
+ char *str;
+
+ emailAddr = nsslowcert_EmailName(&cert->derSubject,cert->emailAddrSpace,
+ sizeof(cert->emailAddrSpace));
+ /* couldn't find the email address in the DN, check the subject Alt name */
+ if (!emailAddr && cert->extensions.data) {
+ emailAddr = nsslowcert_EmailAltName(cert, cert->emailAddrSpace,
+ sizeof(cert->emailAddrSpace));
+ }
+
+
+ /* make it lower case */
+ str = emailAddr;
+ while ( str && *str ) {
+ *str = tolower( *str );
+ str++;
+ }
+ return emailAddr;
+
+}
+
+/*
+ * take a DER certificate and decode it into a certificate structure
+ */
+NSSLOWCERTCertificate *
+nsslowcert_DecodeDERCertificate(SECItem *derSignedCert, char *nickname)
+{
+ NSSLOWCERTCertificate *cert;
+ int rv;
+
+ /* allocate the certificate structure */
+ cert = nsslowcert_CreateCert();
+
+ if ( !cert ) {
+ goto loser;
+ }
+
+ /* point to passed in DER data */
+ cert->derCert = *derSignedCert;
+ cert->nickname = NULL;
+ cert->certKey.data = NULL;
+ cert->referenceCount = 1;
+
+ /* decode the certificate info */
+ rv = nsslowcert_GetCertFields(cert->derCert.data, cert->derCert.len,
+ &cert->derIssuer, &cert->serialNumber, &cert->derSN, &cert->derSubject,
+ &cert->validity, &cert->derSubjKeyInfo, &cert->extensions);
+
+ /* cert->subjectKeyID; x509v3 subject key identifier */
+ cert->subjectKeyID.data = NULL;
+ cert->subjectKeyID.len = 0;
+ cert->dbEntry = NULL;
+ cert ->trust = NULL;
+ cert ->dbhandle = NULL;
+
+ /* generate and save the database key for the cert */
+ rv = nsslowcert_KeyFromIssuerAndSNStatic(cert->certKeySpace,
+ sizeof(cert->certKeySpace), &cert->derIssuer,
+ &cert->serialNumber, &cert->certKey);
+ if ( rv ) {
+ goto loser;
+ }
+
+ /* set the nickname */
+ if ( nickname == NULL ) {
+ cert->nickname = NULL;
+ } else {
+ /* copy and install the nickname */
+ cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
+ sizeof(cert->nicknameSpace));
+ }
+
+#ifdef FIXME
+ /* initialize the subjectKeyID */
+ rv = cert_GetKeyID(cert);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+#endif
+
+ /* set the email address */
+ cert->emailAddr = nsslowcert_GetCertificateEmailAddress(cert);
+
+
+ cert->referenceCount = 1;
+
+ return(cert);
+
+loser:
+ if (cert) {
+ nsslowcert_DestroyCertificate(cert);
+ }
+
+ return(0);
+}
+
+char *
+nsslowcert_FixupEmailAddr(char *emailAddr)
+{
+ char *retaddr;
+ char *str;
+
+ if ( emailAddr == NULL ) {
+ return(NULL);
+ }
+
+ /* copy the string */
+ str = retaddr = PORT_Strdup(emailAddr);
+ if ( str == NULL ) {
+ return(NULL);
+ }
+
+ /* make it lower case */
+ while ( *str ) {
+ *str = tolower( *str );
+ str++;
+ }
+
+ return(retaddr);
+}
+
+
+/*
+ * Generate a database key, based on serial number and issuer, from a
+ * DER certificate.
+ */
+SECStatus
+nsslowcert_KeyFromDERCert(PRArenaPool *arena, SECItem *derCert, SECItem *key)
+{
+ int rv;
+ NSSLOWCERTCertKey certkey;
+
+ PORT_Memset(&certkey, 0, sizeof(NSSLOWCERTCertKey));
+
+ rv = nsslowcert_GetCertFields(derCert->data, derCert->len,
+ &certkey.derIssuer, &certkey.serialNumber, NULL, NULL,
+ NULL, NULL, NULL);
+
+ if ( rv ) {
+ goto loser;
+ }
+
+ return(nsslowcert_KeyFromIssuerAndSN(arena, &certkey.derIssuer,
+ &certkey.serialNumber, key));
+loser:
+ return(SECFailure);
+}
+
+NSSLOWKEYPublicKey *
+nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *cert)
+{
+ NSSLOWCERTSubjectPublicKeyInfo spki;
+ NSSLOWKEYPublicKey *pubk;
+ SECItem os;
+ SECStatus rv;
+ PRArenaPool *arena;
+ SECOidTag tag;
+ SECItem newDerSubjKeyInfo;
+
+ arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL)
+ return NULL;
+
+ pubk = (NSSLOWKEYPublicKey *)
+ PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYPublicKey));
+ if (pubk == NULL) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ pubk->arena = arena;
+ PORT_Memset(&spki,0,sizeof(spki));
+
+ /* copy the DER into the arena, since Quick DER returns data that points
+ into the DER input, which may get freed by the caller */
+ rv = SECITEM_CopyItem(arena, &newDerSubjKeyInfo, &cert->derSubjKeyInfo);
+ if ( rv != SECSuccess ) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ /* we haven't bothered decoding the spki struct yet, do it now */
+ rv = SEC_QuickDERDecodeItem(arena, &spki,
+ nsslowcert_SubjectPublicKeyInfoTemplate, &newDerSubjKeyInfo);
+ if (rv != SECSuccess) {
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+ }
+
+ /* Convert bit string length from bits to bytes */
+ os = spki.subjectPublicKey;
+ DER_ConvertBitString (&os);
+
+ tag = SECOID_GetAlgorithmTag(&spki.algorithm);
+ switch ( tag ) {
+ case SEC_OID_X500_RSA_ENCRYPTION:
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ pubk->keyType = NSSLOWKEYRSAKey;
+ prepare_low_rsa_pub_key_for_asn1(pubk);
+ rv = SEC_QuickDERDecodeItem(arena, pubk,
+ nsslowcert_RSAPublicKeyTemplate, &os);
+ if (rv == SECSuccess)
+ return pubk;
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ pubk->keyType = NSSLOWKEYDSAKey;
+ prepare_low_dsa_pub_key_for_asn1(pubk);
+ rv = SEC_QuickDERDecodeItem(arena, pubk,
+ nsslowcert_DSAPublicKeyTemplate, &os);
+ if (rv == SECSuccess) return pubk;
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ pubk->keyType = NSSLOWKEYDHKey;
+ prepare_low_dh_pub_key_for_asn1(pubk);
+ rv = SEC_QuickDERDecodeItem(arena, pubk,
+ nsslowcert_DHPublicKeyTemplate, &os);
+ if (rv == SECSuccess) return pubk;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ pubk->keyType = NSSLOWKEYECKey;
+ /* Since PKCS#11 directly takes the DER encoding of EC params
+ * and public value, we don't need any decoding here.
+ */
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.ecParams.DEREncoding,
+ &spki.algorithm.parameters);
+ if ( rv != SECSuccess )
+ break;
+
+ /* Fill out the rest of the ecParams structure
+ * based on the encoded params
+ */
+ if (LGEC_FillParams(arena, &pubk->u.ec.ecParams.DEREncoding,
+ &pubk->u.ec.ecParams) != SECSuccess)
+ break;
+
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue, &os);
+ if (rv == SECSuccess) return pubk;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ rv = SECFailure;
+ break;
+ }
+
+ nsslowkey_DestroyPublicKey (pubk);
+ return NULL;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/lowkey.c b/security/nss/lib/softoken/legacydb/lowkey.c
new file mode 100644
index 000000000..5ee64d1f2
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowkey.c
@@ -0,0 +1,468 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "lowkeyi.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "secder.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+static const SEC_ASN1Template nsslowkey_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 nsslowkey_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, nsslowkey_AttributeTemplate },
+};
+/* ASN1 Templates for new decoder/encoder */
+const SEC_ASN1Template nsslowkey_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),
+ nsslowkey_SetOfAttributeTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template nsslowkey_PQGParamsTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PQGParams) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,prime) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,subPrime) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,base) },
+ { 0, }
+};
+
+const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.version) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.modulus) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.publicExponent) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.privateExponent) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.prime1) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.prime2) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.exponent1) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.exponent2) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.coefficient) },
+ { 0 }
+};
+
+
+const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.publicValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.privateValue) },
+ { 0, }
+};
+
+const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.privateValue) },
+};
+
+const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.publicValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.privateValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.base) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.prime) },
+ { 0, }
+};
+
+#ifdef NSS_ENABLE_ECC
+
+/* XXX This is just a placeholder for later when we support
+ * generic curves and need full-blown support for parsing EC
+ * parameters. For now, we only support named curves in which
+ * EC params are simply encoded as an object ID and we don't
+ * use nsslowkey_ECParamsTemplate.
+ */
+const SEC_ASN1Template nsslowkey_ECParamsTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(ECParams,type), NULL, sizeof(ECParams) },
+ { SEC_ASN1_OBJECT_ID, offsetof(ECParams,curveOID), NULL, ec_params_named },
+ { 0, }
+};
+
+
+/* NOTE: The SECG specification allows the private key structure
+ * to contain curve parameters but recommends that they be stored
+ * in the PrivateKeyAlgorithmIdentifier field of the PrivateKeyInfo
+ * instead.
+ */
+const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.ec.version) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.privateValue) },
+ /* XXX The following template works for now since we only
+ * support named curves for which the parameters are
+ * encoded as an object ID. When we support generic curves,
+ * we'll need to define nsslowkey_ECParamsTemplate
+ */
+#if 1
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams.curveOID),
+ SEC_ASN1_SUB(SEC_ObjectIDTemplate) },
+#else
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams),
+ nsslowkey_ECParamsTemplate },
+#endif
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.publicValue),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { 0, }
+};
+
+
+/*
+ * smaller version of EC_FillParams. In this code, we only need
+ * oid and DER data.
+ */
+SECStatus
+LGEC_FillParams(PRArenaPool *arena, const SECItem *encodedParams,
+ ECParams *params)
+{
+ SECOidTag tag;
+ SECItem oid = { siBuffer, NULL, 0};
+
+#if EC_DEBUG
+ int i;
+
+ printf("Encoded params in EC_DecodeParams: ");
+ for (i = 0; i < encodedParams->len; i++) {
+ printf("%02x:", encodedParams->data[i]);
+ }
+ printf("\n");
+#endif
+
+ oid.len = encodedParams->len - 2;
+ oid.data = encodedParams->data + 2;
+ if ((encodedParams->data[0] != SEC_ASN1_OBJECT_ID) ||
+ ((tag = SECOID_FindOIDTag(&oid)) == SEC_OID_UNKNOWN)) {
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+ return SECFailure;
+ }
+
+ params->arena = arena;
+
+ /* For named curves, fill out curveOID */
+ params->curveOID.len = oid.len;
+ params->curveOID.data = (unsigned char *) PORT_ArenaAlloc(arena, oid.len);
+ if (params->curveOID.data == NULL) {
+ return SECFailure;
+ }
+ memcpy(params->curveOID.data, oid.data, oid.len);
+
+ return SECSuccess;
+}
+
+/* Copy all of the fields from srcParams into dstParams
+ */
+SECStatus
+LGEC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
+ const ECParams *srcParams)
+{
+ SECStatus rv = SECFailure;
+
+ dstParams->arena = arena;
+ rv = SECITEM_CopyItem(arena, &dstParams->DEREncoding,
+ &srcParams->DEREncoding);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ rv =SECITEM_CopyItem(arena, &dstParams->curveOID,
+ &srcParams->curveOID);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ return SECSuccess;
+
+loser:
+ return SECFailure;
+}
+#endif /* NSS_ENABLE_ECC */
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+
+void
+prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *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;
+}
+
+void
+prepare_low_pqg_params_for_asn1(PQGParams *params)
+{
+ params->prime.type = siUnsignedInteger;
+ params->subPrime.type = siUnsignedInteger;
+ params->base.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dsa.publicValue.type = siUnsignedInteger;
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+ key->u.dsa.params.prime.type = siUnsignedInteger;
+ key->u.dsa.params.subPrime.type = siUnsignedInteger;
+ key->u.dsa.params.base.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dh.prime.type = siUnsignedInteger;
+ key->u.dh.base.type = siUnsignedInteger;
+ key->u.dh.publicValue.type = siUnsignedInteger;
+ key->u.dh.privateValue.type = siUnsignedInteger;
+}
+
+#ifdef NSS_ENABLE_ECC
+void
+prepare_low_ecparams_for_asn1(ECParams *params)
+{
+ params->DEREncoding.type = siUnsignedInteger;
+ params->curveOID.type = siUnsignedInteger;
+}
+
+void
+prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.ec.version.type = siUnsignedInteger;
+ key->u.ec.ecParams.DEREncoding.type = siUnsignedInteger;
+ key->u.ec.ecParams.curveOID.type = siUnsignedInteger;
+ key->u.ec.privateValue.type = siUnsignedInteger;
+ key->u.ec.publicValue.type = siUnsignedInteger;
+}
+#endif /* NSS_ENABLE_ECC */
+
+void
+nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
+{
+ if (privk && privk->arena) {
+ PORT_FreeArena(privk->arena, PR_TRUE);
+ }
+}
+
+void
+nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *pubk)
+{
+ if (pubk && pubk->arena) {
+ PORT_FreeArena(pubk->arena, PR_FALSE);
+ }
+}
+unsigned
+nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubk)
+{
+ unsigned char b0;
+
+ /* interpret modulus length as key strength... in
+ * fortezza that's the public key length */
+
+ switch (pubk->keyType) {
+ case NSSLOWKEYRSAKey:
+ b0 = pubk->u.rsa.modulus.data[0];
+ return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+unsigned
+nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privk)
+{
+
+ unsigned char b0;
+
+ switch (privk->keyType) {
+ case NSSLOWKEYRSAKey:
+ b0 = privk->u.rsa.modulus.data[0];
+ return b0 ? privk->u.rsa.modulus.len : privk->u.rsa.modulus.len - 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+NSSLOWKEYPublicKey *
+nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
+{
+ NSSLOWKEYPublicKey *pubk;
+ PLArenaPool *arena;
+
+
+ arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ switch(privk->keyType) {
+ case NSSLOWKEYRSAKey:
+ case NSSLOWKEYNullKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof (NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ if (privk->keyType == NSSLOWKEYNullKey) return pubk;
+ rv = SECITEM_CopyItem(arena, &pubk->u.rsa.modulus,
+ &privk->u.rsa.modulus);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem (arena, &pubk->u.rsa.publicExponent,
+ &privk->u.rsa.publicExponent);
+ if (rv == SECSuccess)
+ return pubk;
+ }
+ } else {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ }
+ break;
+ case NSSLOWKEYDSAKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.publicValue,
+ &privk->u.dsa.publicValue);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.prime,
+ &privk->u.dsa.params.prime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.subPrime,
+ &privk->u.dsa.params.subPrime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.base,
+ &privk->u.dsa.params.base);
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+ case NSSLOWKEYDHKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.publicValue,
+ &privk->u.dh.publicValue);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.prime,
+ &privk->u.dh.prime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.base,
+ &privk->u.dh.base);
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue,
+ &privk->u.ec.publicValue);
+ if (rv != SECSuccess) break;
+ pubk->u.ec.ecParams.arena = arena;
+ /* Copy the rest of the params */
+ rv = LGEC_CopyParams(arena, &(pubk->u.ec.ecParams),
+ &(privk->u.ec.ecParams));
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+#endif /* NSS_ENABLE_ECC */
+ /* No Fortezza in Low Key implementations (Fortezza keys aren't
+ * stored in our data base */
+ default:
+ break;
+ }
+
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/lowkeyi.h b/security/nss/lib/softoken/legacydb/lowkeyi.h
new file mode 100644
index 000000000..45359d4ef
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowkeyi.h
@@ -0,0 +1,198 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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$ */
+
+#ifndef _LOWKEYI_H_
+#define _LOWKEYI_H_
+
+#include "prtypes.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "pcertt.h"
+#include "lowkeyti.h"
+#include "sdb.h"
+
+SEC_BEGIN_PROTOS
+
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+extern void prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_pqg_params_for_asn1(PQGParams *params);
+extern void prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+#ifdef NSS_ENABLE_ECC
+extern void prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_ecparams_for_asn1(ECParams *params);
+#endif /* NSS_ENABLE_ECC */
+
+typedef char * (* NSSLOWKEYDBNameFunc)(void *arg, int dbVersion);
+
+/*
+** Open a key database.
+*/
+extern NSSLOWKEYDBHandle *nsslowkey_OpenKeyDB(PRBool readOnly,
+ const char *domain,
+ const char *prefix,
+ NSSLOWKEYDBNameFunc namecb,
+ void *cbarg);
+
+/*
+** Close the specified key database.
+*/
+extern void nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle);
+
+/*
+ * Get the version number of the database
+ */
+extern int nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle);
+
+/*
+** Delete a key from the database
+*/
+extern SECStatus nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle,
+ const SECItem *pubkey);
+
+/*
+** Store a key in the database, indexed by its public key modulus.
+** "pk" is the private key to store
+** "f" is a the callback function for getting the password
+** "arg" is the argument for the callback
+*/
+extern SECStatus nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *pk,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb);
+
+/* does the key for this cert exist in the database filed by modulus */
+extern PRBool nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle,
+ NSSLOWCERTCertificate *cert);
+/* does a key with this ID already exist? */
+extern PRBool nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id);
+
+/*
+** Destroy a private key object.
+** "key" the object
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *key);
+
+/*
+** Destroy a public key object.
+** "key" the object
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *key);
+
+/*
+** Return the modulus length of "pubKey".
+*/
+extern unsigned int nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubKey);
+
+
+/*
+** Return the modulus length of "privKey".
+*/
+extern unsigned int nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privKey);
+
+
+/*
+** Convert a low private key "privateKey" into a public low key
+*/
+extern NSSLOWKEYPublicKey
+ *nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privateKey);
+
+
+SECStatus
+nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb);
+
+/* Store key by modulus and specify an encryption algorithm to use.
+ * handle is the pointer to the key database,
+ * privkey is the private key to be stored,
+ * f and arg are the function and arguments to the callback
+ * to get a password,
+ * algorithm is the algorithm which the privKey is to be stored.
+ * A return of anything but SECSuccess indicates failure.
+ */
+extern SECStatus
+nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle,
+ NSSLOWKEYPrivateKey *privkey,
+ SECItem *pubKeyData,
+ char *nickname,
+ SDB *sdb,
+ PRBool update);
+
+/* Find key by modulus. This function is the inverse of store key
+ * by modulus. An attempt to locate the key with "modulus" is
+ * performed. If the key is found, the private key is returned,
+ * else NULL is returned.
+ * modulus is the modulus to locate
+ */
+extern NSSLOWKEYPrivateKey *
+nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus,
+ SDB *sdb);
+
+extern char *
+nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle,
+ SECItem *modulus, SDB *sdb);
+
+#ifdef NSS_ENABLE_ECC
+/*
+ * smaller version of EC_FillParams. In this code, we only need
+ * oid and DER data.
+ */
+SECStatus LGEC_FillParams(PRArenaPool *arena, const SECItem *encodedParams,
+ ECParams *params);
+
+/* Copy all of the fields from srcParams into dstParams */
+SECStatus LGEC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
+ const ECParams *srcParams);
+#endif
+SEC_END_PROTOS
+
+#endif /* _LOWKEYI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/lowkeyti.h b/security/nss/lib/softoken/legacydb/lowkeyti.h
new file mode 100644
index 000000000..7fa74da72
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lowkeyti.h
@@ -0,0 +1,170 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 _LOWKEYTI_H_
+#define _LOWKEYTI_H_ 1
+
+#include "blapit.h"
+#include "prtypes.h"
+#include "plarena.h"
+#include "secitem.h"
+#include "secasn1t.h"
+#include "secoidt.h"
+
+
+/*
+ * a key in/for the data base
+ */
+struct NSSLOWKEYDBKeyStr {
+ PLArenaPool *arena;
+ int version;
+ char *nickname;
+ SECItem salt;
+ SECItem derPK;
+};
+typedef struct NSSLOWKEYDBKeyStr NSSLOWKEYDBKey;
+
+typedef struct NSSLOWKEYDBHandleStr NSSLOWKEYDBHandle;
+
+#ifdef NSS_USE_KEY4_DB
+#define NSSLOWKEY_DB_FILE_VERSION 4
+#else
+#define NSSLOWKEY_DB_FILE_VERSION 3
+#endif
+
+#define NSSLOWKEY_VERSION 0 /* what we *create* */
+
+/*
+** Typedef for callback to get a password "key".
+*/
+extern const SEC_ASN1Template nsslowkey_PQGParamsTemplate[];
+extern const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[];
+extern const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DHPrivateKeyExportTemplate[];
+#ifdef NSS_ENABLE_ECC
+#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION 1 /* as per SECG 1 C.4 */
+extern const SEC_ASN1Template nsslowkey_ECParamsTemplate[];
+extern const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[];
+#endif /* NSS_ENABLE_ECC */
+
+extern const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[];
+extern const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[];
+
+/*
+ * PKCS #8 attributes
+ */
+struct NSSLOWKEYAttributeStr {
+ SECItem attrType;
+ SECItem *attrValue;
+};
+typedef struct NSSLOWKEYAttributeStr NSSLOWKEYAttribute;
+
+/*
+** A PKCS#8 private key info object
+*/
+struct NSSLOWKEYPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECItem version;
+ SECAlgorithmID algorithm;
+ SECItem privateKey;
+ NSSLOWKEYAttribute **attributes;
+};
+typedef struct NSSLOWKEYPrivateKeyInfoStr NSSLOWKEYPrivateKeyInfo;
+#define NSSLOWKEY_PRIVATE_KEY_INFO_VERSION 0 /* what we *create* */
+
+/*
+** A PKCS#8 private key info object
+*/
+struct NSSLOWKEYEncryptedPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECAlgorithmID algorithm;
+ SECItem encryptedData;
+};
+typedef struct NSSLOWKEYEncryptedPrivateKeyInfoStr NSSLOWKEYEncryptedPrivateKeyInfo;
+
+
+typedef enum {
+ NSSLOWKEYNullKey = 0,
+ NSSLOWKEYRSAKey = 1,
+ NSSLOWKEYDSAKey = 2,
+ NSSLOWKEYDHKey = 4,
+ NSSLOWKEYECKey = 5
+} NSSLOWKEYType;
+
+/*
+** An RSA public key object.
+*/
+struct NSSLOWKEYPublicKeyStr {
+ PLArenaPool *arena;
+ NSSLOWKEYType keyType ;
+ union {
+ RSAPublicKey rsa;
+ DSAPublicKey dsa;
+ DHPublicKey dh;
+ ECPublicKey ec;
+ } u;
+};
+typedef struct NSSLOWKEYPublicKeyStr NSSLOWKEYPublicKey;
+
+/*
+** 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 NSSLOWKEYPrivateKeyStr {
+ PLArenaPool *arena;
+ NSSLOWKEYType keyType;
+ union {
+ RSAPrivateKey rsa;
+ DSAPrivateKey dsa;
+ DHPrivateKey dh;
+ ECPrivateKey ec;
+ } u;
+};
+typedef struct NSSLOWKEYPrivateKeyStr NSSLOWKEYPrivateKey;
+
+
+typedef struct NSSLOWKEYPasswordEntryStr NSSLOWKEYPasswordEntry;
+struct NSSLOWKEYPasswordEntryStr {
+ SECItem salt;
+ SECItem value;
+ unsigned char data[128];
+};
+
+
+#endif /* _LOWKEYTI_H_ */
diff --git a/security/nss/lib/softoken/legacydb/manifest.mn b/security/nss/lib/softoken/legacydb/manifest.mn
new file mode 100644
index 000000000..c3048646e
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/manifest.mn
@@ -0,0 +1,67 @@
+#
+# ***** 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 *****
+CORE_DEPTH = ../../../..
+
+MODULE = nss
+
+REQUIRES = dbm
+
+LIBRARY_NAME = nssdbm
+LIBRARY_VERSION = 3
+MAPFILE = $(OBJDIR)/nssdbm.def
+
+DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\" -DSOFTOKEN_LIB_NAME=\"$(notdir $(SHARED_LIBRARY))\"
+
+ifdef MOZILLA_CLIENT
+INCLUDES += -I$(DIST)/include/sqlite3
+endif
+
+CSRCS = \
+ dbmshim.c \
+ keydb.c \
+ lgattr.c \
+ lgcreate.c \
+ lgdestroy.c \
+ lgfind.c \
+ lginit.c \
+ lgutil.c \
+ lowcert.c \
+ lowkey.c \
+ pcertdb.c \
+ pk11db.c \
+ $(NULL)
+
diff --git a/security/nss/lib/softoken/legacydb/nssdbm.def b/security/nss/lib/softoken/legacydb/nssdbm.def
new file mode 100644
index 000000000..bf7f7a5a1
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/nssdbm.def
@@ -0,0 +1,64 @@
+;+#
+;+# ***** 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) 2000
+;+# the Initial Developer. All Rights Reserved.
+;+#
+;+# Contributor(s):
+;+# Dr Stephen Henson <stephen.henson@gemplus.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 *****
+;+#
+;+# 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 ";-"
+;+NSSDBM_3.12 { # NSS 3.12 release
+;+ global:
+LIBRARY nssdbm3 ;-
+EXPORTS ;-
+legacy_Open;
+legacy_Shutdown;
+legacy_ReadSecmodDB;
+legacy_ReleaseSecmodDBData;
+legacy_AddSecmodDB;
+legacy_DeleteSecmodDB;
+legacy_SetCryptFunctions;
+;+ local:
+;+ *;
+;+};
diff --git a/security/nss/lib/softoken/legacydb/nssdbm.rc b/security/nss/lib/softoken/legacydb/nssdbm.rc
new file mode 100644
index 000000000..d89126a37
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/nssdbm.rc
@@ -0,0 +1,100 @@
+/* ***** 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) 2001
+ * 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 ***** */
+
+#include "../softkver.h"
+#include <winver.h>
+
+#define MY_LIBNAME "nssdbm"
+#define MY_FILEDESCRIPTION "Legacy Database Driver"
+
+#define STRINGIZE(x) #x
+#define STRINGIZE2(x) STRINGIZE(x)
+#define SOFTOKEN_VMAJOR_STR STRINGIZE2(SOFTOKEN_VMAJOR)
+
+#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 SOFTOKEN_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 SOFTOKEN_VMAJOR_STR
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version-information resource
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,0
+ PRODUCTVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,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", SOFTOKEN_VERSION "\0"
+ VALUE "InternalName", MY_INTERNAL_NAME "\0"
+ VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
+ VALUE "ProductName", "Network Security Services\0"
+ VALUE "ProductVersion", SOFTOKEN_VERSION "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/security/nss/lib/softoken/legacydb/pcert.h b/security/nss/lib/softoken/legacydb/pcert.h
new file mode 100644
index 000000000..68956abd6
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pcert.h
@@ -0,0 +1,261 @@
+/* ***** 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 ***** */
+
+#ifndef _PCERTDB_H_
+#define _PCERTDB_H_
+
+#include "plarena.h"
+#include "prlong.h"
+#include "pcertt.h"
+
+#include "lowkeyti.h" /* for struct NSSLOWKEYPublicKeyStr */
+
+SEC_BEGIN_PROTOS
+
+/*
+ * initialize any global certificate locks
+ */
+SECStatus nsslowcert_InitLocks(void);
+
+/*
+** Add a DER encoded certificate to the permanent database.
+** "derCert" is the DER encoded certificate.
+** "nickname" is the nickname to use for the cert
+** "trust" is the trust parameters for the cert
+*/
+SECStatus nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTCertificate *cert,
+ char *nickname, NSSLOWCERTCertTrust *trust);
+SECStatus nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname);
+
+SECStatus nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert);
+
+typedef SECStatus (PR_CALLBACK * PermCertCallback)(NSSLOWCERTCertificate *cert,
+ SECItem *k, void *pdata);
+/*
+** Traverse the entire permanent database, and pass the certs off to a
+** user supplied function.
+** "certfunc" is the user function to call for each certificate
+** "udata" is the user's data, which is passed through to "certfunc"
+*/
+SECStatus
+nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
+ PermCertCallback certfunc,
+ void *udata );
+
+PRBool
+nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle);
+
+certDBEntryRevocation *
+nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
+ SECItem *crlKey, PRBool isKRL);
+
+SECStatus
+nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle,const SECItem *derName,
+ PRBool isKRL);
+SECStatus
+nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl ,
+ SECItem *derKey, char *url, PRBool isKRL);
+
+NSSLOWCERTCertDBHandle *nsslowcert_GetDefaultCertDB();
+NSSLOWKEYPublicKey *nsslowcert_ExtractPublicKey(NSSLOWCERTCertificate *);
+
+NSSLOWCERTCertificate *
+nsslowcert_NewTempCertificate(NSSLOWCERTCertDBHandle *handle, SECItem *derCert,
+ char *nickname, PRBool isperm, PRBool copyDER);
+NSSLOWCERTCertificate *
+nsslowcert_DupCertificate(NSSLOWCERTCertificate *cert);
+void nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert);
+void nsslowcert_DestroyTrust(NSSLOWCERTTrust *Trust);
+
+/*
+ * Lookup a certificate in the databases without locking
+ * "certKey" is the database key to look for
+ *
+ * XXX - this should be internal, but pkcs 11 needs to call it during a
+ * traversal.
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey);
+
+/*
+ * Lookup trust for a certificate in the databases without locking
+ * "certKey" is the database key to look for
+ *
+ * XXX - this should be internal, but pkcs 11 needs to call it during a
+ * traversal.
+ */
+NSSLOWCERTTrust *
+nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey);
+
+/*
+** Generate a certificate key from the issuer and serialnumber, then look it
+** up in the database. Return the cert if found.
+** "issuerAndSN" is the issuer and serial number to look for
+*/
+extern NSSLOWCERTCertificate *
+nsslowcert_FindCertByIssuerAndSN (NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN);
+
+/*
+** Generate a certificate key from the issuer and serialnumber, then look it
+** up in the database. Return the cert if found.
+** "issuerAndSN" is the issuer and serial number to look for
+*/
+extern NSSLOWCERTTrust *
+nsslowcert_FindTrustByIssuerAndSN (NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN);
+
+/*
+** Find a certificate in the database by a DER encoded certificate
+** "derCert" is the DER encoded certificate
+*/
+extern NSSLOWCERTCertificate *
+nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
+
+/* convert an email address to lower case */
+char *nsslowcert_FixupEmailAddr(char *emailAddr);
+
+/*
+** Decode a DER encoded certificate into an NSSLOWCERTCertificate structure
+** "derSignedCert" is the DER encoded signed certificate
+** "copyDER" is true if the DER should be copied, false if the
+** existing copy should be referenced
+** "nickname" is the nickname to use in the database. If it is NULL
+** then a temporary nickname is generated.
+*/
+extern NSSLOWCERTCertificate *
+nsslowcert_DecodeDERCertificate (SECItem *derSignedCert, char *nickname);
+
+SECStatus
+nsslowcert_KeyFromDERCert(PRArenaPool *arena, SECItem *derCert, SECItem *key);
+
+certDBEntrySMime *
+nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *certHandle,
+ char *emailAddr);
+void
+nsslowcert_DestroyDBEntry(certDBEntry *entry);
+
+SECStatus
+nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
+ const char *domain, const char *prefix,
+ NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile);
+
+void
+nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle);
+
+/*
+ * is certa newer than certb? If one is expired, pick the other one.
+ */
+PRBool
+nsslowcert_IsNewer(NSSLOWCERTCertificate *certa, NSSLOWCERTCertificate *certb);
+
+
+SECStatus
+nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
+ certDBEntryType type,
+ SECStatus (* callback)(SECItem *data, SECItem *key,
+ certDBEntryType type, void *pdata),
+ void *udata );
+SECStatus
+nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject,
+ NSSLOWCERTCertCallback cb, void *cbarg);
+int
+nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject);
+SECStatus
+nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname, NSSLOWCERTCertCallback cb, void *cbarg);
+
+int
+nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname);
+SECStatus
+nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert,
+ NSSLOWCERTCertTrust *trust);
+
+SECStatus
+nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
+ SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime);
+
+/*
+ * Change the trust attributes of a certificate and make them permanent
+ * in the database.
+ */
+SECStatus
+nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust);
+
+PRBool
+nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle);
+
+void
+nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value);
+
+PRBool
+nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust);
+
+void
+nsslowcert_DestroyFreeLists(void);
+
+void
+nsslowcert_DestroyGlobalLocks(void);
+
+void
+pkcs11_freeNickname(char *nickname, char *space);
+
+char *
+pkcs11_copyNickname(char *nickname, char *space, int spaceLen);
+
+void
+pkcs11_freeStaticData(unsigned char *data, unsigned char *space);
+
+unsigned char *
+pkcs11_allocStaticData(int datalen, unsigned char *space, int spaceLen);
+
+unsigned char *
+pkcs11_copyStaticData(unsigned char *data, int datalen, unsigned char *space,
+ int spaceLen);
+NSSLOWCERTCertificate *
+nsslowcert_CreateCert(void);
+
+certDBEntry *
+nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
+ certDBEntryType entryType, void *pdata);
+
+SEC_END_PROTOS
+
+ #endif /* _PCERTDB_H_ */
diff --git a/security/nss/lib/softoken/legacydb/pcertdb.c b/security/nss/lib/softoken/legacydb/pcertdb.c
new file mode 100644
index 000000000..966164db1
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pcertdb.c
@@ -0,0 +1,5366 @@
+/* ***** 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 ***** */
+
+/*
+ * Permanent Certificate database handling code
+ *
+ * $Id$
+ */
+#include "lowkeyti.h"
+#include "pcert.h"
+#include "mcom_db.h"
+#include "pcert.h"
+#include "secitem.h"
+#include "secder.h"
+
+#include "secerr.h"
+#include "lgdb.h"
+
+/* forward declaration */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
+static SECStatus
+nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
+ char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
+ SECItem *profileTime);
+static SECStatus
+nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
+static SECStatus
+nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
+ SECItem *crlKey, char *url, PRBool isKRL);
+
+static NSSLOWCERTCertificate *certListHead = NULL;
+static NSSLOWCERTTrust *trustListHead = NULL;
+static certDBEntryCert *entryListHead = NULL;
+static int certListCount = 0;
+static int trustListCount = 0;
+static int entryListCount = 0;
+#define MAX_CERT_LIST_COUNT 10
+#define MAX_TRUST_LIST_COUNT 10
+#define MAX_ENTRY_LIST_COUNT 10
+
+/*
+ * the following functions are wrappers for the db library that implement
+ * a global lock to make the database thread safe.
+ */
+static PZLock *dbLock = NULL;
+static PZLock *certRefCountLock = NULL;
+static PZLock *certTrustLock = NULL;
+static PZLock *freeListLock = NULL;
+
+void
+certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
+{
+ if (dbLock == NULL) {
+ dbLock = PZ_NewLock(nssILockCertDB);
+ PORT_Assert(dbLock != NULL);
+ }
+}
+
+SECStatus
+nsslowcert_InitLocks(void)
+{
+ if (freeListLock == NULL) {
+ freeListLock = PZ_NewLock(nssILockRefLock);
+ if (freeListLock == NULL) {
+ return SECFailure;
+ }
+ }
+ if (certRefCountLock == NULL) {
+ certRefCountLock = PZ_NewLock(nssILockRefLock);
+ if (certRefCountLock == NULL) {
+ return SECFailure;
+ }
+ }
+ if (certTrustLock == NULL ) {
+ certTrustLock = PZ_NewLock(nssILockCertDB);
+ if (certTrustLock == NULL) {
+ return SECFailure;
+ }
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * Acquire the global lock on the cert database.
+ * This lock is currently used for the following operations:
+ * adding or deleting a cert to either the temp or perm databases
+ * converting a temp to perm or perm to temp
+ * changing (maybe just adding!?) the trust of a cert
+ * chaning the DB status checking Configuration
+ */
+static void
+nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
+{
+ PZ_EnterMonitor(handle->dbMon);
+ return;
+}
+
+/*
+ * Free the global cert database lock.
+ */
+static void
+nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
+{
+ PRStatus prstat;
+
+ prstat = PZ_ExitMonitor(handle->dbMon);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+
+/*
+ * Acquire the cert reference count lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+static void
+nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
+{
+ PORT_Assert(certRefCountLock != NULL);
+
+ PZ_Lock(certRefCountLock);
+ return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+static void
+nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
+{
+ PRStatus prstat;
+
+ PORT_Assert(certRefCountLock != NULL);
+
+ prstat = PZ_Unlock(certRefCountLock);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+/*
+ * Acquire the cert trust lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+static void
+nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
+{
+ PORT_Assert(certTrustLock != NULL);
+
+ PZ_Lock(certTrustLock);
+ return;
+}
+
+/*
+ * Free the cert trust lock
+ */
+static void
+nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
+{
+ PRStatus prstat;
+
+ PORT_Assert(certTrustLock != NULL);
+
+ prstat = PZ_Unlock(certTrustLock);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+
+/*
+ * Acquire the cert reference count lock
+ * There is currently one global lock for all certs, but I'm putting a cert
+ * arg here so that it will be easy to make it per-cert in the future if
+ * that turns out to be necessary.
+ */
+static void
+nsslowcert_LockFreeList(void)
+{
+ PORT_Assert(freeListLock != NULL);
+
+ PZ_Lock(freeListLock);
+ return;
+}
+
+/*
+ * Free the cert reference count lock
+ */
+static void
+nsslowcert_UnlockFreeList(void)
+{
+ PRStatus prstat;
+
+ PORT_Assert(freeListLock != NULL);
+
+ prstat = PZ_Unlock(freeListLock);
+
+ PORT_Assert(prstat == PR_SUCCESS);
+
+ return;
+}
+
+NSSLOWCERTCertificate *
+nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
+{
+ if (c) {
+ nsslowcert_LockCertRefCount(c);
+ ++c->referenceCount;
+ nsslowcert_UnlockCertRefCount(c);
+ }
+ return c;
+}
+
+static int
+certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->get)(db, key, data, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret = 0;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->put)(db, key, data, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Sync(DB *db, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->sync)(db, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+#define DB_NOT_FOUND -30991 /* from DBM 3.2 */
+static int
+certdb_Del(DB *db, DBT *key, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->del)(db, key, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ /* don't fail if the record is already deleted */
+ if (ret == DB_NOT_FOUND) {
+ ret = 0;
+ }
+
+ return(ret);
+}
+
+static int
+certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ ret = (* db->seq)(db, key, data, flags);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return(ret);
+}
+
+static void
+certdb_Close(DB *db)
+{
+ PRStatus prstat;
+
+ PORT_Assert(dbLock != NULL);
+ PZ_Lock(dbLock);
+
+ (* db->close)(db);
+
+ prstat = PZ_Unlock(dbLock);
+
+ return;
+}
+
+void
+pkcs11_freeNickname(char *nickname, char *space)
+{
+ if (nickname && nickname != space) {
+ PORT_Free(nickname);
+ }
+}
+
+char *
+pkcs11_copyNickname(char *nickname,char *space, int spaceLen)
+{
+ int len;
+ char *copy = NULL;
+
+ len = PORT_Strlen(nickname)+1;
+ if (len <= spaceLen) {
+ copy = space;
+ PORT_Memcpy(copy,nickname,len);
+ } else {
+ copy = PORT_Strdup(nickname);
+ }
+
+ return copy;
+}
+
+void
+pkcs11_freeStaticData (unsigned char *data, unsigned char *space)
+{
+ if (data && data != space) {
+ PORT_Free(data);
+ }
+}
+
+unsigned char *
+pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
+{
+ unsigned char *data = NULL;
+
+ if (len <= spaceLen) {
+ data = space;
+ } else {
+ data = (unsigned char *) PORT_Alloc(len);
+ }
+
+ return data;
+}
+
+unsigned char *
+pkcs11_copyStaticData(unsigned char *data, int len,
+ unsigned char *space, int spaceLen)
+{
+ unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
+ if (copy) {
+ PORT_Memcpy(copy,data,len);
+ }
+
+ return copy;
+}
+
+/*
+ * destroy a database entry
+ */
+static void
+DestroyDBEntry(certDBEntry *entry)
+{
+ PRArenaPool *arena = entry->common.arena;
+
+ /* must be one of our certDBEntry from the free list */
+ if (arena == NULL) {
+ certDBEntryCert *certEntry;
+ if ( entry->common.type != certDBEntryTypeCert) {
+ return;
+ }
+ certEntry = (certDBEntryCert *)entry;
+
+ pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
+ pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
+
+ nsslowcert_LockFreeList();
+ if (entryListCount > MAX_ENTRY_LIST_COUNT) {
+ PORT_Free(certEntry);
+ } else {
+ entryListCount++;
+ PORT_Memset(certEntry, 0, sizeof( *certEntry));
+ certEntry->next = entryListHead;
+ entryListHead = certEntry;
+ }
+ nsslowcert_UnlockFreeList();
+ return;
+ }
+
+
+ /* Zero out the entry struct, so that any further attempts to use it
+ * will cause an exception (e.g. null pointer reference). */
+ PORT_Memset(&entry->common, 0, sizeof entry->common);
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return;
+}
+
+/* forward references */
+static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
+
+static SECStatus
+DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
+{
+ DBT key;
+ int ret;
+
+ /* init the database key */
+ key.data = dbkey->data;
+ key.size = dbkey->len;
+
+ dbkey->data[0] = (unsigned char)type;
+
+ /* delete entry from database */
+ ret = certdb_Del(handle->permCertDB, &key, 0 );
+ if ( ret != 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ ret = certdb_Sync(handle->permCertDB, 0);
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
+ SECItem *dbkey, SECItem *dbentry, PRArenaPool *arena)
+{
+ DBT data, key;
+ int ret;
+ unsigned char *buf;
+
+ /* init the database key */
+ key.data = dbkey->data;
+ key.size = dbkey->len;
+
+ dbkey->data[0] = (unsigned char)entry->type;
+
+ /* read entry from database */
+ ret = certdb_Get(handle->permCertDB, &key, &data, 0 );
+ if ( ret != 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* validate the entry */
+ if ( data.size < SEC_DB_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+ buf = (unsigned char *)data.data;
+ /* version 7 has the same schema, we may be using a v7 db if we openned
+ * the databases readonly. */
+ if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION)
+ || (buf[0] == (unsigned char) CERT_DB_V7_FILE_VERSION))) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+ if ( buf[1] != (unsigned char)entry->type ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy out header information */
+ entry->version = (unsigned int)buf[0];
+ entry->type = (certDBEntryType)buf[1];
+ entry->flags = (unsigned int)buf[2];
+
+ /* format body of entry for return to caller */
+ dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
+ if ( dbentry->len ) {
+ if (arena) {
+ dbentry->data = (unsigned char *)
+ PORT_ArenaAlloc(arena, dbentry->len);
+ if ( dbentry->data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
+ dbentry->len);
+ } else {
+ dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
+ }
+ } else {
+ dbentry->data = NULL;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/**
+ ** Implement low level database access
+ **/
+static SECStatus
+WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
+ SECItem *dbkey, SECItem *dbentry)
+{
+ int ret;
+ DBT data, key;
+ unsigned char *buf;
+
+ data.data = dbentry->data;
+ data.size = dbentry->len;
+
+ buf = (unsigned char*)data.data;
+
+ buf[0] = (unsigned char)entry->version;
+ buf[1] = (unsigned char)entry->type;
+ buf[2] = (unsigned char)entry->flags;
+
+ key.data = dbkey->data;
+ key.size = dbkey->len;
+
+ dbkey->data[0] = (unsigned char)entry->type;
+
+ /* put the record into the database now */
+ ret = certdb_Put(handle->permCertDB, &key, &data, 0);
+
+ if ( ret != 0 ) {
+ goto loser;
+ }
+
+ ret = certdb_Sync( handle->permCertDB, 0 );
+
+ if ( ret ) {
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * encode a database cert record
+ */
+static SECStatus
+EncodeDBCertEntry(certDBEntryCert *entry, PRArenaPool *arena, SECItem *dbitem)
+{
+ unsigned int nnlen;
+ unsigned char *buf;
+ char *nn;
+ char zbuf = 0;
+
+ if ( entry->nickname ) {
+ nn = entry->nickname;
+ } else {
+ nn = &zbuf;
+ }
+ nnlen = PORT_Strlen(nn) + 1;
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
+ SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->trust.sslFlags >> 8 ) & 0xff;
+ buf[1] = entry->trust.sslFlags & 0xff;
+ buf[2] = ( entry->trust.emailFlags >> 8 ) & 0xff;
+ buf[3] = entry->trust.emailFlags & 0xff;
+ buf[4] = ( entry->trust.objectSigningFlags >> 8 ) & 0xff;
+ buf[5] = entry->trust.objectSigningFlags & 0xff;
+ buf[6] = ( entry->derCert.len >> 8 ) & 0xff;
+ buf[7] = entry->derCert.len & 0xff;
+ buf[8] = ( nnlen >> 8 ) & 0xff;
+ buf[9] = nnlen & 0xff;
+
+ PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
+ entry->derCert.len);
+
+ PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
+ nn, nnlen);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * encode a database key for a cert record
+ */
+static SECStatus
+EncodeDBCertKey(const SECItem *certKey, PRArenaPool *arena, SECItem *dbkey)
+{
+ unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
+ if (arena) {
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
+ } else {
+ if (dbkey->len < len) {
+ dbkey->data = (unsigned char *)PORT_Alloc(len);
+ }
+ }
+ dbkey->len = len;
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
+ certKey->data, certKey->len);
+ dbkey->data[0] = certDBEntryTypeCert;
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+EncodeDBGenericKey(const SECItem *certKey, PRArenaPool *arena, SECItem *dbkey,
+ certDBEntryType entryType)
+{
+ /*
+ * we only allow _one_ KRL key!
+ */
+ if (entryType == certDBEntryTypeKeyRevocation) {
+ dbkey->len = SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ dbkey->data[0] = (unsigned char) entryType;
+ return(SECSuccess);
+ }
+
+
+ dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
+ certKey->data, certKey->len);
+ dbkey->data[0] = (unsigned char) entryType;
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
+{
+ unsigned int nnlen;
+ unsigned int headerlen;
+ int lenoff;
+
+ /* allow updates of old versions of the database */
+ switch ( entry->common.version ) {
+ case 5:
+ headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
+ lenoff = 3;
+ break;
+ case 6:
+ /* should not get here */
+ PORT_Assert(0);
+ headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
+ lenoff = 3;
+ break;
+ case 7:
+ case 8:
+ headerlen = DB_CERT_ENTRY_HEADER_LEN;
+ lenoff = 6;
+ break;
+ default:
+ /* better not get here */
+ PORT_Assert(0);
+ headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
+ lenoff = 3;
+ break;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry->len < headerlen ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->derCert.len = ( ( dbentry->data[lenoff] << 8 ) |
+ dbentry->data[lenoff+1] );
+ nnlen = ( ( dbentry->data[lenoff+2] << 8 ) | dbentry->data[lenoff+3] );
+ if ( ( entry->derCert.len + nnlen + headerlen )
+ != dbentry->len) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the dercert */
+
+ entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
+ entry->derCert.len,entry->derCertSpace,sizeof(entry->derCertSpace));
+ if ( entry->derCert.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* copy the nickname */
+ if ( nnlen > 1 ) {
+ entry->nickname = (char *)pkcs11_copyStaticData(
+ &dbentry->data[headerlen+entry->derCert.len], nnlen,
+ (unsigned char *)entry->nicknameSpace,
+ sizeof(entry->nicknameSpace));
+ if ( entry->nickname == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ } else {
+ entry->nickname = NULL;
+ }
+
+ if ( entry->common.version < 7 ) {
+ /* allow updates of v5 db */
+ entry->trust.sslFlags = dbentry->data[0];
+ entry->trust.emailFlags = dbentry->data[1];
+ entry->trust.objectSigningFlags = dbentry->data[2];
+ } else {
+ entry->trust.sslFlags = ( dbentry->data[0] << 8 ) | dbentry->data[1];
+ entry->trust.emailFlags = ( dbentry->data[2] << 8 ) | dbentry->data[3];
+ entry->trust.objectSigningFlags =
+ ( dbentry->data[4] << 8 ) | dbentry->data[5];
+ }
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+
+/*
+ * Create a new certDBEntryCert from existing data
+ */
+static certDBEntryCert *
+NewDBCertEntry(SECItem *derCert, char *nickname,
+ NSSLOWCERTCertTrust *trust, int flags)
+{
+ certDBEntryCert *entry;
+ PRArenaPool *arena = NULL;
+ int nnlen;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ goto loser;
+ }
+
+ entry = PORT_ArenaZNew(arena, certDBEntryCert);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ /* fill in the dbCert */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeCert;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ if ( trust ) {
+ entry->trust = *trust;
+ }
+
+ entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
+ if ( !entry->derCert.data ) {
+ goto loser;
+ }
+ entry->derCert.len = derCert->len;
+ PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
+
+ nnlen = ( nickname ? strlen(nickname) + 1 : 0 );
+
+ if ( nnlen ) {
+ entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->nickname ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+
+ } else {
+ entry->nickname = 0;
+ }
+
+ return(entry);
+
+loser:
+
+ /* allocation error, free arena and return */
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+}
+
+/*
+ * Decode a version 4 DBCert from the byte stream database format
+ * and construct a current database entry struct
+ */
+static certDBEntryCert *
+DecodeV4DBCertEntry(unsigned char *buf, int len)
+{
+ certDBEntryCert *entry;
+ int certlen;
+ int nnlen;
+ PRArenaPool *arena;
+
+ /* make sure length is at least long enough for the header */
+ if ( len < DBCERT_V4_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(0);
+ }
+
+ /* get other lengths */
+ certlen = buf[3] << 8 | buf[4];
+ nnlen = buf[5] << 8 | buf[6];
+
+ /* make sure DB entry is the right size */
+ if ( ( certlen + nnlen + DBCERT_V4_HEADER_LEN ) != len ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(0);
+ }
+
+ /* allocate arena */
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+ }
+
+ /* allocate structure and members */
+ entry = (certDBEntryCert *) PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
+
+ if ( !entry ) {
+ goto loser;
+ }
+
+ entry->common.arena = arena;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.type = certDBEntryTypeCert;
+ entry->common.flags = 0;
+ entry->trust.sslFlags = buf[0];
+ entry->trust.emailFlags = buf[1];
+ entry->trust.objectSigningFlags = buf[2];
+
+ entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
+ if ( !entry->derCert.data ) {
+ goto loser;
+ }
+ entry->derCert.len = certlen;
+ PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
+
+ if ( nnlen ) {
+ entry->nickname = (char *) PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->nickname ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
+
+ if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
+ entry->trust.sslFlags |= CERTDB_USER;
+ }
+ } else {
+ entry->nickname = 0;
+ }
+
+ return(entry);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+}
+
+/*
+ * Encode a Certificate database entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECItem tmpitem;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* get the database key and format it */
+ rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+
+/*
+ * delete a certificate entry
+ */
+static SECStatus
+DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
+{
+ SECItem dbkey;
+ SECStatus rv;
+
+ dbkey.data= NULL;
+ dbkey.len = 0;
+
+ rv = EncodeDBCertKey(certKey, NULL, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ if (dbkey.data) {
+ PORT_Free(dbkey.data);
+ }
+ return(SECSuccess);
+
+loser:
+ if (dbkey.data) {
+ PORT_Free(dbkey.data);
+ }
+ return(SECFailure);
+}
+
+static certDBEntryCert *
+CreateCertEntry(void)
+{
+ certDBEntryCert *entry;
+
+ nsslowcert_LockFreeList();
+ entry = entryListHead;
+ if (entry) {
+ entryListCount--;
+ entryListHead = entry->next;
+ }
+ PORT_Assert(entryListCount >= 0);
+ nsslowcert_UnlockFreeList();
+ if (entry) {
+ return entry;
+ }
+
+ return PORT_ZNew(certDBEntryCert);
+}
+
+static void
+DestroyCertEntryFreeList(void)
+{
+ certDBEntryCert *entry;
+
+ nsslowcert_LockFreeList();
+ while (NULL != (entry = entryListHead)) {
+ entryListCount--;
+ entryListHead = entry->next;
+ PORT_Free(entry);
+ }
+ PORT_Assert(!entryListCount);
+ entryListCount = 0;
+ nsslowcert_UnlockFreeList();
+}
+
+/*
+ * Read a certificate entry
+ */
+static certDBEntryCert *
+ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
+{
+ certDBEntryCert *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+ unsigned char buf[512];
+
+ dbkey.data = buf;
+ dbkey.len = sizeof(buf);
+
+ entry = CreateCertEntry();
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = NULL;
+ entry->common.type = certDBEntryTypeCert;
+
+ rv = EncodeDBCertKey(certKey, NULL, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBCertEntry(entry, &dbentry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ pkcs11_freeStaticData(dbkey.data,buf);
+ dbkey.data = NULL;
+ return(entry);
+
+loser:
+ pkcs11_freeStaticData(dbkey.data,buf);
+ dbkey.data = NULL;
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ return(NULL);
+}
+
+/*
+ * encode a database cert record
+ */
+static SECStatus
+EncodeDBCrlEntry(certDBEntryRevocation *entry, PRArenaPool *arena, SECItem *dbitem)
+{
+ unsigned int nnlen = 0;
+ unsigned char *buf;
+
+ if (entry->url) {
+ nnlen = PORT_Strlen(entry->url) + 1;
+ }
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->derCrl.len + nnlen
+ + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->derCrl.len >> 8 ) & 0xff;
+ buf[1] = entry->derCrl.len & 0xff;
+ buf[2] = ( nnlen >> 8 ) & 0xff;
+ buf[3] = nnlen & 0xff;
+
+ PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
+ entry->derCrl.len);
+
+ if (nnlen != 0) {
+ PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
+ entry->url, nnlen);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
+{
+ unsigned int nnlen;
+
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_CRL_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->derCrl.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ if ( ( entry->derCrl.len + nnlen + DB_CRL_ENTRY_HEADER_LEN )
+ != dbentry->len) {
+ /* CRL entry is greater than 64 K. Hack to make this continue to work */
+ if (dbentry->len >= (0xffff - DB_CRL_ENTRY_HEADER_LEN) - nnlen) {
+ entry->derCrl.len =
+ (dbentry->len - DB_CRL_ENTRY_HEADER_LEN) - nnlen;
+ } else {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+ }
+
+ /* copy the dercert */
+ entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->derCrl.len);
+ if ( entry->derCrl.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
+ entry->derCrl.len);
+
+ /* copy the url */
+ entry->url = NULL;
+ if (nnlen != 0) {
+ entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, nnlen);
+ if ( entry->url == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->url,
+ &dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
+ nnlen);
+ }
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Create a new certDBEntryRevocation from existing data
+ */
+static certDBEntryRevocation *
+NewDBCrlEntry(SECItem *derCrl, char * url, certDBEntryType crlType, int flags)
+{
+ certDBEntryRevocation *entry;
+ PRArenaPool *arena = NULL;
+ int nnlen;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ goto loser;
+ }
+
+ entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ /* fill in the dbRevolcation */
+ entry->common.arena = arena;
+ entry->common.type = crlType;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+
+ entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
+ if ( !entry->derCrl.data ) {
+ goto loser;
+ }
+
+ if (url) {
+ nnlen = PORT_Strlen(url) + 1;
+ entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->url ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->url, url, nnlen);
+ } else {
+ entry->url = NULL;
+ }
+
+
+ entry->derCrl.len = derCrl->len;
+ PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
+
+ return(entry);
+
+loser:
+
+ /* allocation error, free arena and return */
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ return(0);
+}
+
+
+static SECStatus
+WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
+ SECItem *crlKey )
+{
+ SECItem dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECItem encodedEntry;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+/*
+ * delete a crl entry
+ */
+static SECStatus
+DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
+ certDBEntryType crlType)
+{
+ SECItem dbkey;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, crlType, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read a certificate entry
+ */
+static certDBEntryRevocation *
+ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
+ certDBEntryType crlType)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryRevocation *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryRevocation *)
+ PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = crlType;
+
+ rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBCrlEntry(entry, &dbentry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+void
+nsslowcert_DestroyDBEntry(certDBEntry *entry)
+{
+ DestroyDBEntry(entry);
+ return;
+}
+
+/*
+ * Encode a database nickname record
+ */
+static SECStatus
+EncodeDBNicknameEntry(certDBEntryNickname *entry, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
+ SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
+ buf[1] = entry->subjectName.len & 0xff;
+
+ PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
+ entry->subjectName.len);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a nickname record
+ */
+static SECStatus
+EncodeDBNicknameKey(char *nickname, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ unsigned int nnlen;
+
+ nnlen = PORT_Strlen(nickname) + 1; /* includes null */
+
+ /* now get the database key and format it */
+ dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
+ dbkey->data[0] = certDBEntryTypeNickname;
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
+ char *nickname)
+{
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ if (( entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN ) !=
+ dbentry->len ){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the certkey */
+ entry->subjectName.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->subjectName.len);
+ if ( entry->subjectName.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->subjectName.data,
+ &dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
+ entry->subjectName.len);
+ entry->subjectName.type = siBuffer;
+
+ entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
+ PORT_Strlen(nickname)+1);
+ if ( entry->nickname ) {
+ PORT_Strcpy(entry->nickname, nickname);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new nickname entry
+ */
+static certDBEntryNickname *
+NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntryNickname *entry;
+ int nnlen;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntryNickname));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* init common fields */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeNickname;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ /* copy the nickname */
+ nnlen = PORT_Strlen(nickname) + 1;
+
+ entry->nickname = (char*)PORT_ArenaAlloc(arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+
+ rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * delete a nickname entry
+ */
+static SECStatus
+DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
+{
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+ SECItem dbkey;
+
+ if ( nickname == NULL ) {
+ return(SECSuccess);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read a nickname entry
+ */
+static certDBEntryNickname *
+ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryNickname *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntryNickname));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeNickname;
+
+ rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Encode a nickname entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+
+}
+
+static SECStatus
+EncodeDBSMimeEntry(certDBEntrySMime *entry, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
+ entry->optionsDate.len +
+ DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
+ buf[1] = entry->subjectName.len & 0xff;
+ buf[2] = ( entry->smimeOptions.len >> 8 ) & 0xff;
+ buf[3] = entry->smimeOptions.len & 0xff;
+ buf[4] = ( entry->optionsDate.len >> 8 ) & 0xff;
+ buf[5] = entry->optionsDate.len & 0xff;
+
+ /* if no smime options, then there should not be an options date either */
+ PORT_Assert( ! ( ( entry->smimeOptions.len == 0 ) &&
+ ( entry->optionsDate.len != 0 ) ) );
+
+ PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
+ entry->subjectName.len);
+ if ( entry->smimeOptions.len ) {
+ PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],
+ entry->smimeOptions.data,
+ entry->smimeOptions.len);
+ PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
+ entry->smimeOptions.len],
+ entry->optionsDate.data,
+ entry->optionsDate.len);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a SMIME record
+ */
+static SECStatus
+EncodeDBSMimeKey(char *emailAddr, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ unsigned int addrlen;
+
+ addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
+
+ /* now get the database key and format it */
+ dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
+ dbkey->data[0] = certDBEntryTypeSMimeProfile;
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Decode a database SMIME record
+ */
+static SECStatus
+DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
+{
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_SMIME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* is database entry correct length? */
+ entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ entry->smimeOptions.len = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ entry->optionsDate.len = ( ( dbentry->data[4] << 8 ) | dbentry->data[5] );
+ if (( entry->subjectName.len + entry->smimeOptions.len +
+ entry->optionsDate.len + DB_SMIME_ENTRY_HEADER_LEN ) != dbentry->len){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the subject name */
+ entry->subjectName.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->subjectName.len);
+ if ( entry->subjectName.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->subjectName.data,
+ &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
+ entry->subjectName.len);
+
+ /* copy the smime options */
+ if ( entry->smimeOptions.len ) {
+ entry->smimeOptions.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->smimeOptions.len);
+ if ( entry->smimeOptions.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->smimeOptions.data,
+ &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
+ entry->subjectName.len],
+ entry->smimeOptions.len);
+ }
+ if ( entry->optionsDate.len ) {
+ entry->optionsDate.data =
+ (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->optionsDate.len);
+ if ( entry->optionsDate.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->optionsDate.data,
+ &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
+ entry->subjectName.len +
+ entry->smimeOptions.len],
+ entry->optionsDate.len);
+ }
+
+ /* both options and options date must either exist or not exist */
+ if ( ( ( entry->optionsDate.len == 0 ) ||
+ ( entry->smimeOptions.len == 0 ) ) &&
+ entry->smimeOptions.len != entry->optionsDate.len ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
+ PORT_Strlen(emailAddr)+1);
+ if ( entry->emailAddr ) {
+ PORT_Strcpy(entry->emailAddr, emailAddr);
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new SMIME entry
+ */
+static certDBEntrySMime *
+NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
+ SECItem *optionsDate, unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntrySMime *entry;
+ int addrlen;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySMime));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* init common fields */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSMimeProfile;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ /* copy the email addr */
+ addrlen = PORT_Strlen(emailAddr) + 1;
+
+ entry->emailAddr = (char*)PORT_ArenaAlloc(arena, addrlen);
+ if ( entry->emailAddr == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
+
+ /* copy the subject name */
+ rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* copy the smime options */
+ if ( smimeOptions ) {
+ rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ PORT_Assert(optionsDate == NULL);
+ entry->smimeOptions.data = NULL;
+ entry->smimeOptions.len = 0;
+ }
+
+ /* copy the options date */
+ if ( optionsDate ) {
+ rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ PORT_Assert(smimeOptions == NULL);
+ entry->optionsDate.data = NULL;
+ entry->optionsDate.len = 0;
+ }
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * delete a SMIME entry
+ */
+static SECStatus
+DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
+{
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+ SECItem dbkey;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read a SMIME entry
+ */
+certDBEntrySMime *
+nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntrySMime *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySMime));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSMimeProfile;
+
+ rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry.len < DB_SMIME_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Encode a SMIME entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+
+}
+
+/*
+ * Encode a database subject record
+ */
+static SECStatus
+EncodeDBSubjectEntry(certDBEntrySubject *entry, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+ int len;
+ unsigned int ncerts;
+ unsigned int i;
+ unsigned char *tmpbuf;
+ unsigned int nnlen = 0;
+ unsigned int eaddrslen = 0;
+ int keyidoff;
+ SECItem *certKeys;
+ SECItem *keyIDs;
+
+ if ( entry->nickname ) {
+ nnlen = PORT_Strlen(entry->nickname) + 1;
+ }
+ if ( entry->emailAddrs ) {
+ eaddrslen = 2;
+ for (i=0; i < entry->nemailAddrs; i++) {
+ eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
+ }
+ }
+
+ ncerts = entry->ncerts;
+
+ /* compute the length of the entry */
+ keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen ;
+ len = keyidoff + 4 * ncerts + eaddrslen;
+ for ( i = 0; i < ncerts; i++ ) {
+ len += entry->certKeys[i].len;
+ len += entry->keyIDs[i].len;
+ }
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
+ if ( dbitem->data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( ncerts >> 8 ) & 0xff;
+ buf[1] = ncerts & 0xff;
+ buf[2] = ( nnlen >> 8 ) & 0xff;
+ buf[3] = nnlen & 0xff;
+ /* v7 email field is NULL in v8 */
+ buf[4] = 0;
+ buf[5] = 0;
+
+ PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
+
+ for ( i = 0; i < ncerts; i++ ) {
+
+ certKeys = entry->certKeys;
+ keyIDs = entry->keyIDs;
+
+ buf[keyidoff+i*2] = ( certKeys[i].len >> 8 ) & 0xff;
+ buf[keyidoff+1+i*2] = certKeys[i].len & 0xff;
+ buf[keyidoff+ncerts*2+i*2] = ( keyIDs[i].len >> 8 ) & 0xff;
+ buf[keyidoff+1+ncerts*2+i*2] = keyIDs[i].len & 0xff;
+ }
+
+ /* temp pointer used to stuff certkeys and keyids into the buffer */
+ tmpbuf = &buf[keyidoff+ncerts*4];
+
+ for ( i = 0; i < ncerts; i++ ) {
+ certKeys = entry->certKeys;
+ PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
+ tmpbuf = tmpbuf + certKeys[i].len;
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ keyIDs = entry->keyIDs;
+ PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
+ tmpbuf = tmpbuf + keyIDs[i].len;
+ }
+
+ if (entry->emailAddrs) {
+ tmpbuf[0] = (entry->nemailAddrs >> 8) & 0xff;
+ tmpbuf[1] = entry->nemailAddrs & 0xff;
+ tmpbuf += 2;
+ for (i=0; i < entry->nemailAddrs; i++) {
+ int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
+ tmpbuf[0] = (nameLen >> 8) & 0xff;
+ tmpbuf[1] = nameLen & 0xff;
+ tmpbuf += 2;
+ PORT_Memcpy(tmpbuf,entry->emailAddrs[i],nameLen);
+ tmpbuf +=nameLen;
+ }
+ }
+
+ PORT_Assert(tmpbuf == &buf[len]);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a subject record
+ */
+static SECStatus
+EncodeDBSubjectKey(SECItem *derSubject, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
+ dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
+ if ( dbkey->data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
+ derSubject->len);
+ dbkey->data[0] = certDBEntryTypeSubject;
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
+ const SECItem *derSubject)
+{
+ unsigned int ncerts;
+ PRArenaPool *arena;
+ unsigned int len, itemlen;
+ unsigned char *tmpbuf;
+ unsigned char *end;
+ unsigned int i;
+ SECStatus rv;
+ unsigned int keyidoff;
+ unsigned int nnlen, eaddrlen;
+ unsigned int stdlen;
+
+ arena = entry->common.arena;
+
+ rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* is record long enough for header? */
+ if ( dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->ncerts = ncerts = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
+ nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ eaddrlen = ( ( dbentry->data[4] << 8 ) | dbentry->data[5] );
+ stdlen = ncerts * 4 + DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+ if ( dbentry->len < stdlen) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena,
+ sizeof(SECItem) * ncerts);
+ entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena,
+ sizeof(SECItem) * ncerts);
+
+ if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ if ( nnlen > 1 ) { /* null terminator is stored */
+ entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname,
+ &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
+ nnlen);
+ } else {
+ entry->nickname = NULL;
+ }
+
+ /* if we have an old style email entry, there is only one */
+ entry->nemailAddrs = 0;
+ if ( eaddrlen > 1 ) { /* null terminator is stored */
+ entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
+ if ( entry->emailAddrs == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
+ if ( entry->emailAddrs[0] == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->emailAddrs[0],
+ &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],
+ eaddrlen);
+ entry->nemailAddrs = 1;
+ } else {
+ entry->emailAddrs = NULL;
+ }
+
+ /* collect the lengths of the certKeys and keyIDs, and total the
+ * overall length.
+ */
+ keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+ len = keyidoff + 4 * ncerts;
+
+ tmpbuf = &dbentry->data[0];
+
+ for ( i = 0; i < ncerts; i++ ) {
+
+ itemlen = ( tmpbuf[keyidoff + 2*i] << 8 ) | tmpbuf[keyidoff + 1 + 2*i] ;
+ len += itemlen;
+ entry->certKeys[i].len = itemlen;
+
+ itemlen = ( tmpbuf[keyidoff + 2*ncerts + 2*i] << 8 ) |
+ tmpbuf[keyidoff + 1 + 2*ncerts + 2*i] ;
+ len += itemlen;
+ entry->keyIDs[i].len = itemlen;
+ }
+
+ /* is database entry correct length? */
+ if ( len > dbentry->len ){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ tmpbuf = &tmpbuf[keyidoff + 4*ncerts];
+ for ( i = 0; i < ncerts; i++ ) {
+ entry->certKeys[i].data =
+ (unsigned char *)PORT_ArenaAlloc(arena, entry->certKeys[i].len);
+ if ( entry->certKeys[i].data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->certKeys[i].data, tmpbuf, entry->certKeys[i].len);
+ tmpbuf = &tmpbuf[entry->certKeys[i].len];
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ entry->keyIDs[i].data =
+ (unsigned char *)PORT_ArenaAlloc(arena, entry->keyIDs[i].len);
+ if ( entry->keyIDs[i].data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, entry->keyIDs[i].len);
+ tmpbuf = &tmpbuf[entry->keyIDs[i].len];
+ }
+
+ end = &dbentry->data[dbentry->len];
+ if ((eaddrlen == 0) && (tmpbuf+1 < end)) {
+ /* read in the additional email addresses */
+ entry->nemailAddrs = tmpbuf[0] << 8 | tmpbuf[1];
+ tmpbuf += 2;
+ entry->emailAddrs = (char **)
+ PORT_ArenaAlloc(arena, entry->nemailAddrs * sizeof(char *));
+ if (entry->emailAddrs == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ for (i=0; i < entry->nemailAddrs; i++) {
+ int nameLen;
+ if (tmpbuf + 2 > end) {
+ goto loser;
+ }
+
+ nameLen = tmpbuf[0] << 8 | tmpbuf[1];
+ entry->emailAddrs[i] = PORT_ArenaAlloc(arena,nameLen);
+ if (entry->emailAddrs == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ if (tmpbuf + (nameLen+2) > end) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->emailAddrs[i],&tmpbuf[2],nameLen);
+ tmpbuf += 2 + nameLen;
+ }
+ }
+
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new subject entry with a single cert
+ */
+static certDBEntrySubject *
+NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
+ SECItem *keyID, char *nickname, char *emailAddr,
+ unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntrySubject *entry;
+ SECStatus rv;
+ unsigned int nnlen;
+ unsigned int eaddrlen;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySubject));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* init common fields */
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSubject;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ /* copy the subject */
+ rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ entry->ncerts = 1;
+ entry->nemailAddrs = 0;
+ /* copy nickname */
+ if ( nickname && ( *nickname != '\0' ) ) {
+ nnlen = PORT_Strlen(nickname) + 1;
+ entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+ } else {
+ entry->nickname = NULL;
+ }
+
+ /* copy email addr */
+ if ( emailAddr && ( *emailAddr != '\0' ) ) {
+ emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
+ if ( emailAddr == NULL ) {
+ entry->emailAddrs = NULL;
+ goto loser;
+ }
+
+ eaddrlen = PORT_Strlen(emailAddr) + 1;
+ entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
+ if ( entry->emailAddrs == NULL ) {
+ PORT_Free(emailAddr);
+ goto loser;
+ }
+ entry->emailAddrs[0] = PORT_ArenaStrdup(arena,emailAddr);
+ if (entry->emailAddrs[0]) {
+ entry->nemailAddrs = 1;
+ }
+
+ PORT_Free(emailAddr);
+ } else {
+ entry->emailAddrs = NULL;
+ }
+
+ /* allocate space for certKeys and keyIDs */
+ entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
+ entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
+ if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
+ goto loser;
+ }
+
+ /* copy the certKey and keyID */
+ rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * delete a subject entry
+ */
+static SECStatus
+DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
+{
+ SECItem dbkey;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Read the subject entry
+ */
+static certDBEntrySubject *
+ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntrySubject *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntrySubject));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeSubject;
+
+ rv = EncodeDBSubjectKey(derSubject, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Encode a subject name entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySubject *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+
+}
+
+typedef enum { nsslowcert_remove, nsslowcert_add } nsslowcertUpdateType;
+
+static SECStatus
+nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle *dbhandle,
+ SECItem *derSubject, char *emailAddr, nsslowcertUpdateType updateType)
+{
+ certDBEntrySubject *entry = NULL;
+ int index = -1, i;
+ SECStatus rv;
+
+ if (emailAddr) {
+ emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
+ if (emailAddr == NULL) {
+ return SECFailure;
+ }
+ } else {
+ return SECSuccess;
+ }
+
+ entry = ReadDBSubjectEntry(dbhandle,derSubject);
+ if (entry == NULL) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ for (i=0; i < (int)(entry->nemailAddrs); i++) {
+ if (PORT_Strcmp(entry->emailAddrs[i],emailAddr) == 0) {
+ index = i;
+ }
+ }
+
+ if (updateType == nsslowcert_remove) {
+ if (index == -1) {
+ rv = SECSuccess;
+ goto done;
+ }
+ entry->nemailAddrs--;
+ for (i=index; i < (int)(entry->nemailAddrs); i++) {
+ entry->emailAddrs[i] = entry->emailAddrs[i+1];
+ }
+ } else {
+ char **newAddrs = NULL;
+
+ if (index != -1) {
+ rv = SECSuccess;
+ goto done;
+ }
+ newAddrs = (char **)PORT_ArenaAlloc(entry->common.arena,
+ (entry->nemailAddrs+1)* sizeof(char *));
+ if (!newAddrs) {
+ rv = SECFailure;
+ goto done;
+ }
+ for (i=0; i < (int)(entry->nemailAddrs); i++) {
+ newAddrs[i] = entry->emailAddrs[i];
+ }
+ newAddrs[entry->nemailAddrs] =
+ PORT_ArenaStrdup(entry->common.arena,emailAddr);
+ if (!newAddrs[entry->nemailAddrs]) {
+ rv = SECFailure;
+ goto done;
+ }
+ entry->emailAddrs = newAddrs;
+ entry->nemailAddrs++;
+ }
+
+ /* delete the subject entry */
+ DeleteDBSubjectEntry(dbhandle, derSubject);
+
+ /* write the new one */
+ rv = WriteDBSubjectEntry(dbhandle, entry);
+
+ done:
+ if (entry) DestroyDBEntry((certDBEntry *)entry);
+ if (emailAddr) PORT_Free(emailAddr);
+ return rv;
+}
+
+/*
+ * writes a nickname to an existing subject entry that does not currently
+ * have one
+ */
+static SECStatus
+AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname)
+{
+ certDBEntrySubject *entry;
+ SECStatus rv;
+
+ if ( nickname == NULL ) {
+ return(SECFailure);
+ }
+
+ entry = ReadDBSubjectEntry(dbhandle,&cert->derSubject);
+ PORT_Assert(entry != NULL);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ PORT_Assert(entry->nickname == NULL);
+ if ( entry->nickname != NULL ) {
+ goto loser;
+ }
+
+ entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
+
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ /* delete the subject entry */
+ DeleteDBSubjectEntry(dbhandle, &cert->derSubject);
+
+ /* write the new one */
+ rv = WriteDBSubjectEntry(dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new version entry
+ */
+static certDBEntryVersion *
+NewDBVersionEntry(unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntryVersion *entry;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
+ sizeof(certDBEntryVersion));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeVersion;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Read the version entry
+ */
+static certDBEntryVersion *
+ReadDBVersionEntry(NSSLOWCERTCertDBHandle *handle)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryVersion *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = PORT_ArenaZNew(arena, certDBEntryVersion);
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeVersion;
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
+ dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
+ if ( dbkey.data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
+ SEC_DB_VERSION_KEY_LEN);
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(entry);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Encode a version entry into byte stream suitable for
+ * the database
+ */
+static SECStatus
+WriteDBVersionEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryVersion *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ /* allocate space for encoded database record, including space
+ * for low level header
+ */
+ dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
+
+ dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
+ if ( dbitem.data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
+ dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
+ if ( dbkey.data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
+ SEC_DB_VERSION_KEY_LEN);
+
+ /* now write it to the database */
+ rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(tmparena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( tmparena ) {
+ PORT_FreeArena(tmparena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+/*
+ * cert is no longer a perm cert, but will remain a temp cert
+ */
+static SECStatus
+RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
+{
+ certDBEntrySubject *entry;
+ unsigned int i;
+ SECStatus rv;
+
+ entry = ReadDBSubjectEntry(cert->dbhandle,&cert->derSubject);
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ PORT_Assert(entry->ncerts);
+ rv = SECFailure;
+
+ if ( entry->ncerts > 1 ) {
+ for ( i = 0; i < entry->ncerts; i++ ) {
+ if ( SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
+ SECEqual ) {
+ /* copy rest of list forward one entry */
+ for ( i = i + 1; i < entry->ncerts; i++ ) {
+ entry->certKeys[i-1] = entry->certKeys[i];
+ entry->keyIDs[i-1] = entry->keyIDs[i];
+ }
+ entry->ncerts--;
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ break;
+ }
+ }
+ } else {
+ /* no entries left, delete the perm entry in the DB */
+ if ( entry->emailAddrs ) {
+ /* if the subject had an email record, then delete it too */
+ for (i=0; i < entry->nemailAddrs; i++) {
+ DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
+ }
+ }
+ if ( entry->nickname ) {
+ DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
+ }
+
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ }
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(rv);
+}
+
+/*
+ * add a cert to the perm subject list
+ */
+static SECStatus
+AddPermSubjectNode(certDBEntrySubject *entry, NSSLOWCERTCertificate *cert,
+ char *nickname)
+{
+ SECItem *newCertKeys, *newKeyIDs;
+ unsigned int i, new_i;
+ SECStatus rv;
+ unsigned int ncerts;
+
+ PORT_Assert(entry);
+ ncerts = entry->ncerts;
+
+ if ( nickname && entry->nickname ) {
+ /* nicknames must be the same */
+ PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
+ }
+
+ if ( ( entry->nickname == NULL ) && ( nickname != NULL ) ) {
+ /* copy nickname into the entry */
+ entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
+ if ( entry->nickname == NULL ) {
+ return(SECFailure);
+ }
+ }
+
+ /* a DB entry already exists, so add this cert */
+ newCertKeys = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
+ newKeyIDs = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
+
+ if ( ( newCertKeys == NULL ) || ( newKeyIDs == NULL ) ) {
+ return(SECFailure);
+ }
+
+ /* Step 1: copy certs older than "cert" into new entry. */
+ for ( i = 0, new_i=0; i < ncerts; i++ ) {
+ NSSLOWCERTCertificate *cmpcert;
+ PRBool isNewer;
+ cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
+ &entry->certKeys[i]);
+ /* The entry has been corrupted, remove it from the list */
+ if (!cmpcert) {
+ continue;
+ }
+
+ isNewer = nsslowcert_IsNewer(cert, cmpcert);
+ nsslowcert_DestroyCertificate(cmpcert);
+ if ( isNewer )
+ break;
+ /* copy this cert entry */
+ newCertKeys[new_i] = entry->certKeys[i];
+ newKeyIDs[new_i] = entry->keyIDs[i];
+ new_i++;
+ }
+
+ /* Step 2: Add "cert" to the entry. */
+ rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[new_i],
+ &cert->certKey);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[new_i],
+ &cert->subjectKeyID);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ new_i++;
+
+ /* Step 3: copy remaining certs (if any) from old entry to new. */
+ for ( ; i < ncerts; i++ ,new_i++) {
+ newCertKeys[new_i] = entry->certKeys[i];
+ newKeyIDs[new_i] = entry->keyIDs[i];
+ }
+
+ /* update certKeys and keyIDs */
+ entry->certKeys = newCertKeys;
+ entry->keyIDs = newKeyIDs;
+
+ /* set new count value */
+ entry->ncerts = new_i;
+
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ return(rv);
+}
+
+
+SECStatus
+nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject,
+ NSSLOWCERTCertCallback cb, void *cbarg)
+{
+ certDBEntrySubject *entry;
+ unsigned int i;
+ NSSLOWCERTCertificate *cert;
+ SECStatus rv = SECSuccess;
+
+ entry = ReadDBSubjectEntry(handle, derSubject);
+
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ for( i = 0; i < entry->ncerts; i++ ) {
+ cert = nsslowcert_FindCertByKey(handle, &entry->certKeys[i]);
+ if (!cert) {
+ continue;
+ }
+ rv = (* cb)(cert, cbarg);
+ nsslowcert_DestroyCertificate(cert);
+ if ( rv == SECFailure ) {
+ break;
+ }
+ }
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(rv);
+}
+
+int
+nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
+ SECItem *derSubject)
+{
+ certDBEntrySubject *entry;
+ int ret;
+
+ entry = ReadDBSubjectEntry(handle, derSubject);
+
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ ret = entry->ncerts;
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(ret);
+}
+
+SECStatus
+nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname, NSSLOWCERTCertCallback cb, void *cbarg)
+{
+ certDBEntryNickname *nnentry = NULL;
+ certDBEntrySMime *smentry = NULL;
+ SECStatus rv;
+ SECItem *derSubject = NULL;
+
+ nnentry = ReadDBNicknameEntry(handle, nickname);
+ if ( nnentry ) {
+ derSubject = &nnentry->subjectName;
+ } else {
+ smentry = nsslowcert_ReadDBSMimeEntry(handle, nickname);
+ if ( smentry ) {
+ derSubject = &smentry->subjectName;
+ }
+ }
+
+ if ( derSubject ) {
+ rv = nsslowcert_TraversePermCertsForSubject(handle, derSubject,
+ cb, cbarg);
+ } else {
+ rv = SECFailure;
+ }
+
+ if ( nnentry ) {
+ DestroyDBEntry((certDBEntry *)nnentry);
+ }
+ if ( smentry ) {
+ DestroyDBEntry((certDBEntry *)smentry);
+ }
+
+ return(rv);
+}
+
+int
+nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
+ char *nickname)
+{
+ certDBEntryNickname *entry;
+ int ret;
+
+ entry = ReadDBNicknameEntry(handle, nickname);
+
+ if ( entry ) {
+ ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
+ DestroyDBEntry((certDBEntry *)entry);
+ } else {
+ ret = 0;
+ }
+ return(ret);
+}
+
+/*
+ * add a nickname to a cert that doesn't have one
+ */
+static SECStatus
+AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname)
+{
+ certDBEntryCert *entry;
+ int rv;
+
+ entry = cert->dbEntry;
+ PORT_Assert(entry != NULL);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ pkcs11_freeNickname(entry->nickname,entry->nicknameSpace);
+ entry->nickname = NULL;
+ entry->nickname = pkcs11_copyNickname(nickname,entry->nicknameSpace,
+ sizeof(entry->nicknameSpace));
+
+ rv = WriteDBCertEntry(dbhandle, entry);
+ if ( rv ) {
+ goto loser;
+ }
+
+ pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
+ cert->nickname = NULL;
+ cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
+ sizeof(cert->nicknameSpace));
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * add a nickname to a cert that is already in the perm database, but doesn't
+ * have one yet (it is probably an e-mail cert).
+ */
+SECStatus
+nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname)
+{
+ SECStatus rv = SECFailure;
+ certDBEntrySubject *entry = NULL;
+ certDBEntryNickname *nicknameEntry = NULL;
+
+ nsslowcert_LockDB(dbhandle);
+
+ entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
+ if (entry == NULL) goto loser;
+
+ if ( entry->nickname == NULL ) {
+
+ /* no nickname for subject */
+ rv = AddNicknameToSubject(dbhandle, cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = AddNicknameToPermCert(dbhandle, cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+
+ rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ /* subject already has a nickname */
+ rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ /* make sure nickname entry exists. If the database was corrupted,
+ * we may have lost the nickname entry. Add it back now */
+ nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname);
+ if (nicknameEntry == NULL ) {
+ nicknameEntry = NewDBNicknameEntry(entry->nickname,
+ &cert->derSubject, 0);
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+
+ rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+ }
+ rv = SECSuccess;
+
+loser:
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ if (nicknameEntry) {
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+ }
+ nsslowcert_UnlockDB(dbhandle);
+ return(rv);
+}
+
+static certDBEntryCert *
+AddCertToPermDB(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTCertificate *cert,
+ char *nickname, NSSLOWCERTCertTrust *trust)
+{
+ certDBEntryCert *certEntry = NULL;
+ certDBEntryNickname *nicknameEntry = NULL;
+ certDBEntrySubject *subjectEntry = NULL;
+ int state = 0;
+ SECStatus rv;
+ PRBool donnentry = PR_FALSE;
+
+ if ( nickname ) {
+ donnentry = PR_TRUE;
+ }
+
+ subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
+
+ if ( subjectEntry && subjectEntry->nickname ) {
+ donnentry = PR_FALSE;
+ nickname = subjectEntry->nickname;
+ }
+
+ certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
+ if ( certEntry == NULL ) {
+ goto loser;
+ }
+
+ if ( donnentry ) {
+ nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+ }
+
+ rv = WriteDBCertEntry(handle, certEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ state = 1;
+
+ if ( nicknameEntry ) {
+ rv = WriteDBNicknameEntry(handle, nicknameEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ state = 2;
+
+ /* "Change" handles if necessary */
+ cert->dbhandle = handle;
+
+ /* add to or create new subject entry */
+ if ( subjectEntry ) {
+ /* REWRITE BASED ON SUBJECT ENTRY */
+ rv = AddPermSubjectNode(subjectEntry, cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ /* make a new subject entry - this case is only used when updating
+ * an old version of the database. This is OK because the oldnickname
+ * db format didn't allow multiple certs with the same subject.
+ */
+ /* where does subjectKeyID and certKey come from? */
+ subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
+ &cert->subjectKeyID, nickname,
+ NULL, 0);
+ if ( subjectEntry == NULL ) {
+ goto loser;
+ }
+ rv = WriteDBSubjectEntry(handle, subjectEntry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ state = 3;
+
+ if ( nicknameEntry ) {
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+ }
+
+ if ( subjectEntry ) {
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+ }
+
+ return(certEntry);
+
+loser:
+ /* don't leave partial entry in the database */
+ if ( state > 0 ) {
+ rv = DeleteDBCertEntry(handle, &cert->certKey);
+ }
+ if ( ( state > 1 ) && donnentry ) {
+ rv = DeleteDBNicknameEntry(handle, nickname);
+ }
+ if ( state > 2 ) {
+ rv = DeleteDBSubjectEntry(handle, &cert->derSubject);
+ }
+ if ( certEntry ) {
+ DestroyDBEntry((certDBEntry *)certEntry);
+ }
+ if ( nicknameEntry ) {
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+ }
+ if ( subjectEntry ) {
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+ }
+
+ return(NULL);
+}
+
+/* forward declaration */
+static SECStatus
+UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
+
+/*
+ * version 8 uses the same schema as version 7. The only differences are
+ * 1) version 8 db uses the blob shim to store data entries > 32k.
+ * 2) version 8 db sets the db block size to 32k.
+ * both of these are dealt with by the handle.
+ */
+
+static SECStatus
+UpdateV8DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ return UpdateV7DB(handle,updatedb);
+}
+
+
+/*
+ * we could just blindly sequence through reading key data pairs and writing
+ * them back out, but some cert.db's have gotten quite large and may have some
+ * subtle corruption problems, so instead we cycle through the certs and
+ * CRL's and S/MIME profiles and rebuild our subject lists from those records.
+ */
+static SECStatus
+UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ DBT key, data;
+ int ret;
+ NSSLOWCERTCertificate *cert;
+ PRBool isKRL = PR_FALSE;
+ certDBEntryType entryType;
+ SECItem dbEntry, dbKey;
+ certDBEntryRevocation crlEntry;
+ certDBEntryCert certEntry;
+ certDBEntrySMime smimeEntry;
+ SECStatus rv;
+
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ unsigned char *dataBuf = (unsigned char *)data.data;
+ unsigned char *keyBuf = (unsigned char *)key.data;
+ dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
+ dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
+ entryType = (certDBEntryType) keyBuf[0];
+ dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
+ dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
+ if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
+ continue;
+ }
+
+ switch (entryType) {
+ /* these entries will get regenerated as we read the
+ * rest of the data from the database */
+ case certDBEntryTypeVersion:
+ case certDBEntryTypeSubject:
+ case certDBEntryTypeContentVersion:
+ case certDBEntryTypeNickname:
+ /* smime profiles need entries created after the certs have
+ * been imported, loop over them in a second run */
+ case certDBEntryTypeSMimeProfile:
+ break;
+
+ case certDBEntryTypeCert:
+ /* decode Entry */
+ certEntry.common.version = (unsigned int)dataBuf[0];
+ certEntry.common.type = entryType;
+ certEntry.common.flags = (unsigned int)dataBuf[2];
+ rv = DecodeDBCertEntry(&certEntry,&dbEntry);
+ if (rv != SECSuccess) {
+ break;
+ }
+ /* should we check for existing duplicates? */
+ cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert,
+ certEntry.nickname);
+ if (cert) {
+ nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
+ &certEntry.trust);
+ nsslowcert_DestroyCertificate(cert);
+ }
+ /* free any data the decode may have allocated. */
+ pkcs11_freeStaticData(certEntry.derCert.data,
+ certEntry.derCertSpace);
+ pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace);
+ break;
+
+ case certDBEntryTypeKeyRevocation:
+ isKRL = PR_TRUE;
+ /* fall through */
+ case certDBEntryTypeRevocation:
+ crlEntry.common.version = (unsigned int)dataBuf[0];
+ crlEntry.common.type = entryType;
+ crlEntry.common.flags = (unsigned int)dataBuf[2];
+ crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (crlEntry.common.arena == NULL) {
+ break;
+ }
+ rv = DecodeDBCrlEntry(&crlEntry,&dbEntry);
+ if (rv != SECSuccess) {
+ break;
+ }
+ nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey,
+ crlEntry.url, isKRL);
+ /* free data allocated by the decode */
+ PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
+ crlEntry.common.arena = NULL;
+ break;
+
+ default:
+ break;
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ /* now loop again updating just the SMimeProfile. */
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ unsigned char *dataBuf = (unsigned char *)data.data;
+ unsigned char *keyBuf = (unsigned char *)key.data;
+ dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
+ dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
+ entryType = (certDBEntryType) keyBuf[0];
+ if (entryType != certDBEntryTypeSMimeProfile) {
+ continue;
+ }
+ dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
+ dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
+ if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
+ continue;
+ }
+ smimeEntry.common.version = (unsigned int)dataBuf[0];
+ smimeEntry.common.type = entryType;
+ smimeEntry.common.flags = (unsigned int)dataBuf[2];
+ smimeEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ /* decode entry */
+ rv = DecodeDBSMimeEntry(&smimeEntry,&dbEntry,(char *)dbKey.data);
+ if (rv == SECSuccess) {
+ nsslowcert_UpdateSMimeProfile(handle, smimeEntry.emailAddr,
+ &smimeEntry.subjectName, &smimeEntry.smimeOptions,
+ &smimeEntry.optionsDate);
+ }
+ PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
+ smimeEntry.common.arena = NULL;
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ (* updatedb->close)(updatedb);
+
+ /* a database update is a good time to go back and verify the integrity of
+ * the keys and certs */
+ handle->dbVerify = PR_TRUE;
+ return(SECSuccess);
+}
+
+/*
+ * NOTE - Version 6 DB did not go out to the real world in a release,
+ * so we can remove this function in a later release.
+ */
+static SECStatus
+UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ int ret;
+ DBT key, data;
+ unsigned char *buf, *tmpbuf = NULL;
+ certDBEntryType type;
+ certDBEntryNickname *nnEntry = NULL;
+ certDBEntrySubject *subjectEntry = NULL;
+ certDBEntrySMime *emailEntry = NULL;
+ char *nickname;
+ char *emailAddr;
+ SECStatus rv;
+
+ /*
+ * Sequence through the old database and copy all of the entries
+ * to the new database. Subject name entries will have the new
+ * fields inserted into them (with zero length).
+ */
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ buf = (unsigned char *)data.data;
+
+ if ( data.size >= 3 ) {
+ if ( buf[0] == 6 ) { /* version number */
+ type = (certDBEntryType)buf[1];
+ if ( type == certDBEntryTypeSubject ) {
+ /* expando subjecto entrieo */
+ tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
+ if ( tmpbuf ) {
+ /* copy header stuff */
+ PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
+ /* insert 4 more bytes of zero'd header */
+ PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
+ 0, 4);
+ /* copy rest of the data */
+ PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
+ &buf[SEC_DB_ENTRY_HEADER_LEN + 2],
+ data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
+
+ data.data = (void *)tmpbuf;
+ data.size += 4;
+ buf = tmpbuf;
+ }
+ } else if ( type == certDBEntryTypeCert ) {
+ /* expando certo entrieo */
+ tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
+ if ( tmpbuf ) {
+ /* copy header stuff */
+ PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
+
+ /* copy trust flage, setting msb's to 0 */
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+1] =
+ buf[SEC_DB_ENTRY_HEADER_LEN];
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2] = 0;
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+3] =
+ buf[SEC_DB_ENTRY_HEADER_LEN+1];
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+4] = 0;
+ tmpbuf[SEC_DB_ENTRY_HEADER_LEN+5] =
+ buf[SEC_DB_ENTRY_HEADER_LEN+2];
+
+ /* copy rest of the data */
+ PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
+ &buf[SEC_DB_ENTRY_HEADER_LEN + 3],
+ data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
+
+ data.data = (void *)tmpbuf;
+ data.size += 3;
+ buf = tmpbuf;
+ }
+
+ }
+
+ /* update the record version number */
+ buf[0] = CERT_DB_FILE_VERSION;
+
+ /* copy to the new database */
+ ret = certdb_Put(handle->permCertDB, &key, &data, 0);
+ if ( tmpbuf ) {
+ PORT_Free(tmpbuf);
+ tmpbuf = NULL;
+ }
+ }
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ ret = certdb_Sync(handle->permCertDB, 0);
+
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ buf = (unsigned char *)data.data;
+
+ if ( data.size >= 3 ) {
+ if ( buf[0] == CERT_DB_FILE_VERSION ) { /* version number */
+ type = (certDBEntryType)buf[1];
+ if ( type == certDBEntryTypeNickname ) {
+ nickname = &((char *)key.data)[1];
+
+ /* get the matching nickname entry in the new DB */
+ nnEntry = ReadDBNicknameEntry(handle, nickname);
+ if ( nnEntry == NULL ) {
+ goto endloop;
+ }
+
+ /* find the subject entry pointed to by nickname */
+ subjectEntry = ReadDBSubjectEntry(handle,
+ &nnEntry->subjectName);
+ if ( subjectEntry == NULL ) {
+ goto endloop;
+ }
+
+ subjectEntry->nickname =
+ (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
+ key.size - 1);
+ if ( subjectEntry->nickname ) {
+ PORT_Memcpy(subjectEntry->nickname, nickname,
+ key.size - 1);
+ rv = WriteDBSubjectEntry(handle, subjectEntry);
+ }
+ } else if ( type == certDBEntryTypeSMimeProfile ) {
+ emailAddr = &((char *)key.data)[1];
+
+ /* get the matching smime entry in the new DB */
+ emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr);
+ if ( emailEntry == NULL ) {
+ goto endloop;
+ }
+
+ /* find the subject entry pointed to by nickname */
+ subjectEntry = ReadDBSubjectEntry(handle,
+ &emailEntry->subjectName);
+ if ( subjectEntry == NULL ) {
+ goto endloop;
+ }
+
+ subjectEntry->emailAddrs = (char **)
+ PORT_ArenaAlloc(subjectEntry->common.arena,
+ sizeof(char *));
+ if ( subjectEntry->emailAddrs ) {
+ subjectEntry->emailAddrs[0] =
+ (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
+ key.size - 1);
+ if ( subjectEntry->emailAddrs[0] ) {
+ PORT_Memcpy(subjectEntry->emailAddrs[0], emailAddr,
+ key.size - 1);
+ subjectEntry->nemailAddrs = 1;
+ rv = WriteDBSubjectEntry(handle, subjectEntry);
+ }
+ }
+ }
+
+endloop:
+ if ( subjectEntry ) {
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+ subjectEntry = NULL;
+ }
+ if ( nnEntry ) {
+ DestroyDBEntry((certDBEntry *)nnEntry);
+ nnEntry = NULL;
+ }
+ if ( emailEntry ) {
+ DestroyDBEntry((certDBEntry *)emailEntry);
+ emailEntry = NULL;
+ }
+ }
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ ret = certdb_Sync(handle->permCertDB, 0);
+
+ (* updatedb->close)(updatedb);
+ return(SECSuccess);
+}
+
+
+static SECStatus
+updateV5Callback(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata)
+{
+ NSSLOWCERTCertDBHandle *handle;
+ certDBEntryCert *entry;
+ NSSLOWCERTCertTrust *trust;
+
+ handle = (NSSLOWCERTCertDBHandle *)pdata;
+ trust = &cert->dbEntry->trust;
+
+ /* SSL user certs can be used for email if they have an email addr */
+ if ( cert->emailAddr && ( trust->sslFlags & CERTDB_USER ) &&
+ ( trust->emailFlags == 0 ) ) {
+ trust->emailFlags = CERTDB_USER;
+ }
+ /* servers didn't set the user flags on the server cert.. */
+ if (PORT_Strcmp(cert->dbEntry->nickname,"Server-Cert") == 0) {
+ trust->sslFlags |= CERTDB_USER;
+ }
+
+ entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
+ &cert->dbEntry->trust);
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+UpdateV5DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ NSSLOWCERTCertDBHandle updatehandle;
+ SECStatus rv;
+
+ updatehandle.permCertDB = updatedb;
+ updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
+ updatehandle.dbVerify = 0;
+ updatehandle.ref = 1; /* prevent premature close */
+
+ rv = nsslowcert_TraversePermCerts(&updatehandle, updateV5Callback,
+ (void *)handle);
+
+ PZ_DestroyMonitor(updatehandle.dbMon);
+
+ (* updatedb->close)(updatedb);
+ return(SECSuccess);
+}
+
+static PRBool
+isV4DB(DB *db) {
+ DBT key,data;
+ int ret;
+
+ key.data = "Version";
+ key.size = 7;
+
+ ret = (*db->get)(db, &key, &data, 0);
+ if (ret) {
+ return PR_FALSE;
+ }
+
+ if ((data.size == 1) && (*(unsigned char *)data.data <= 4)) {
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+static SECStatus
+UpdateV4DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
+{
+ DBT key, data;
+ certDBEntryCert *entry, *entry2;
+ int ret;
+ PRArenaPool *arena = NULL;
+ NSSLOWCERTCertificate *cert;
+
+ ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return(SECFailure);
+ }
+
+ do {
+ if ( data.size != 1 ) { /* skip version number */
+
+ /* decode the old DB entry */
+ entry = (certDBEntryCert *)
+ DecodeV4DBCertEntry((unsigned char*)data.data, data.size);
+
+ if ( entry ) {
+ cert = nsslowcert_DecodeDERCertificate(&entry->derCert,
+ entry->nickname);
+
+ if ( cert != NULL ) {
+ /* add to new database */
+ entry2 = AddCertToPermDB(handle, cert, entry->nickname,
+ &entry->trust);
+
+ nsslowcert_DestroyCertificate(cert);
+ if ( entry2 ) {
+ DestroyDBEntry((certDBEntry *)entry2);
+ }
+ }
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ }
+ } while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
+
+ PORT_FreeArena(arena, PR_FALSE);
+ (* updatedb->close)(updatedb);
+ return(SECSuccess);
+}
+
+
+/*
+ * return true if a database key conflict exists
+ */
+PRBool
+nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle)
+{
+ SECStatus rv;
+ DBT tmpdata;
+ DBT namekey;
+ int ret;
+ SECItem keyitem;
+ PRArenaPool *arena = NULL;
+ SECItem derKey;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ /* get the db key of the cert */
+ rv = nsslowcert_KeyFromDERCert(arena, derCert, &derKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertKey(&derKey, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = keyitem.data;
+ namekey.size = keyitem.len;
+
+ ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
+ if ( ret == 0 ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return(PR_FALSE);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(PR_TRUE);
+}
+
+/*
+ * return true if a nickname conflict exists
+ * NOTE: caller must have already made sure that this exact cert
+ * doesn't exist in the DB
+ */
+static PRBool
+nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
+ NSSLOWCERTCertDBHandle *handle)
+{
+ PRBool rv;
+ certDBEntryNickname *entry;
+
+ if ( nickname == NULL ) {
+ return(PR_FALSE);
+ }
+
+ entry = ReadDBNicknameEntry(handle, nickname);
+
+ if ( entry == NULL ) {
+ /* no entry for this nickname, so no conflict */
+ return(PR_FALSE);
+ }
+
+ rv = PR_TRUE;
+ if ( SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual ) {
+ /* if subject names are the same, then no conflict */
+ rv = PR_FALSE;
+ }
+
+ DestroyDBEntry((certDBEntry *)entry);
+ return(rv);
+}
+
+#ifdef DBM_USING_NSPR
+#define NO_RDONLY PR_RDONLY
+#define NO_RDWR PR_RDWR
+#define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
+#else
+#define NO_RDONLY O_RDONLY
+#define NO_RDWR O_RDWR
+#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
+#endif
+
+/*
+ * open an old database that needs to be updated
+ */
+static DB *
+nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
+{
+ char * tmpname;
+ DB *updatedb = NULL;
+
+ tmpname = (* namecb)(cbarg, version); /* get v6 db name */
+ if ( tmpname ) {
+ updatedb = dbopen( tmpname, NO_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free(tmpname);
+ }
+ return updatedb;
+}
+
+static SECStatus
+openNewCertDB(const char *appName, const char *prefix, const char *certdbname,
+ NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv;
+ certDBEntryVersion *versionEntry = NULL;
+ DB *updatedb = NULL;
+ int status = RDB_FAIL;
+
+ if (appName) {
+ handle->permCertDB=rdbopen( appName, prefix, "cert", NO_CREATE, &status);
+ } else {
+ handle->permCertDB=dbsopen(certdbname, NO_CREATE, 0600, DB_HASH, 0);
+ }
+
+ /* if create fails then we lose */
+ if ( handle->permCertDB == 0 ) {
+ return status == RDB_RETRY ? SECWouldBlock : SECFailure;
+ }
+
+ /* Verify version number; */
+ versionEntry = NewDBVersionEntry(0);
+ if ( versionEntry == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = WriteDBVersionEntry(handle, versionEntry);
+
+ DestroyDBEntry((certDBEntry *)versionEntry);
+
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* rv must already be Success here because of previous if statement */
+ /* try to upgrade old db here */
+ if (appName &&
+ (updatedb = dbsopen(certdbname, NO_RDONLY, 0600, DB_HASH, 0)) != NULL) {
+ rv = UpdateV8DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,7)) != NULL) {
+ rv = UpdateV7DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,6)) != NULL) {
+ rv = UpdateV6DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,5)) != NULL) {
+ rv = UpdateV5DB(handle, updatedb);
+ } else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,4)) != NULL) {
+ /* NES has v5 format db's with v4 db names! */
+ if (isV4DB(updatedb)) {
+ rv = UpdateV4DB(handle,updatedb);
+ } else {
+ rv = UpdateV5DB(handle,updatedb);
+ }
+ }
+
+
+loser:
+ db_InitComplete(handle->permCertDB);
+ return rv;
+}
+
+static int
+nsslowcert_GetVersionNumber( NSSLOWCERTCertDBHandle *handle)
+{
+ certDBEntryVersion *versionEntry = NULL;
+ int version = 0;
+
+ versionEntry = ReadDBVersionEntry(handle);
+ if ( versionEntry == NULL ) {
+ return 0;
+ }
+ version = versionEntry->common.version;
+ DestroyDBEntry((certDBEntry *)versionEntry);
+ return version;
+}
+
+/*
+ * Open the certificate database and index databases. Create them if
+ * they are not there or bad.
+ */
+static SECStatus
+nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
+ const char *appName, const char *prefix,
+ NSSLOWCERTDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv;
+ int openflags;
+ char *certdbname;
+ int version = 0;
+
+ certdbname = (* namecb)(cbarg, CERT_DB_FILE_VERSION);
+ if ( certdbname == NULL ) {
+ return(SECFailure);
+ }
+
+ openflags = readOnly ? NO_RDONLY : NO_RDWR;
+
+ /*
+ * first open the permanent file based database.
+ */
+ if (appName) {
+ handle->permCertDB = rdbopen( appName, prefix, "cert", openflags, NULL);
+ } else {
+ handle->permCertDB = dbsopen( certdbname, openflags, 0600, DB_HASH, 0 );
+ }
+
+ /* check for correct version number */
+ if ( handle->permCertDB ) {
+ version = nsslowcert_GetVersionNumber(handle);
+ if ((version != CERT_DB_FILE_VERSION) &&
+ !(appName && version == CERT_DB_V7_FILE_VERSION)) {
+ goto loser;
+ }
+ } else if ( readOnly ) {
+ /* don't create if readonly */
+ /* Try openning a version 7 database */
+ handle->permCertDB = nsslowcert_openolddb(namecb,cbarg, 7);
+ if (!handle->permCertDB) {
+ goto loser;
+ }
+ if (nsslowcert_GetVersionNumber(handle) != 7) {
+ goto loser;
+ }
+ } else {
+ /* if first open fails, try to create a new DB */
+ rv = openNewCertDB(appName,prefix,certdbname,handle,namecb,cbarg);
+ if (rv == SECWouldBlock) {
+ /* only the rdb version can fail with wouldblock */
+ handle->permCertDB =
+ rdbopen( appName, prefix, "cert", openflags, NULL);
+
+ /* check for correct version number */
+ if ( !handle->permCertDB ) {
+ goto loser;
+ }
+ version = nsslowcert_GetVersionNumber(handle);
+ if ((version != CERT_DB_FILE_VERSION) &&
+ !(appName && version == CERT_DB_V7_FILE_VERSION)) {
+ goto loser;
+ }
+ } else if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ PORT_Free(certdbname);
+
+ return (SECSuccess);
+
+loser:
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+
+ if ( handle->permCertDB ) {
+ certdb_Close(handle->permCertDB);
+ handle->permCertDB = 0;
+ }
+
+ PORT_Free(certdbname);
+
+ return(SECFailure);
+}
+
+/*
+ * delete all DB records associated with a particular certificate
+ */
+static SECStatus
+DeletePermCert(NSSLOWCERTCertificate *cert)
+{
+ SECStatus rv;
+ SECStatus ret;
+
+ ret = SECSuccess;
+
+ rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
+ if ( rv != SECSuccess ) {
+ ret = SECFailure;
+ }
+
+ rv = RemovePermSubjectNode(cert);
+
+
+ return(ret);
+}
+
+/*
+ * Delete a certificate from the permanent database.
+ */
+SECStatus
+nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
+{
+ SECStatus rv;
+
+ nsslowcert_LockDB(cert->dbhandle);
+
+ /* delete the records from the permanent database */
+ rv = DeletePermCert(cert);
+
+ /* get rid of dbcert and stuff pointing to it */
+ DestroyDBEntry((certDBEntry *)cert->dbEntry);
+ cert->dbEntry = NULL;
+ cert->trust = NULL;
+
+ nsslowcert_UnlockDB(cert->dbhandle);
+ return(rv);
+}
+
+/*
+ * Traverse all of the entries in the database of a particular type
+ * call the given function for each one.
+ */
+SECStatus
+nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
+ certDBEntryType type,
+ SECStatus (* callback)(SECItem *data, SECItem *key,
+ certDBEntryType type, void *pdata),
+ void *udata )
+{
+ DBT data;
+ DBT key;
+ SECStatus rv;
+ int ret;
+ SECItem dataitem;
+ SECItem keyitem;
+ unsigned char *buf;
+ unsigned char *keybuf;
+
+ ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
+
+ if ( ret ) {
+ return(SECFailure);
+ }
+
+ do {
+ buf = (unsigned char *)data.data;
+
+ if ( buf[1] == (unsigned char)type ) {
+ dataitem.len = data.size;
+ dataitem.data = buf;
+ dataitem.type = siBuffer;
+ keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
+ keybuf = (unsigned char *)key.data;
+ keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
+ keyitem.type = siBuffer;
+ /* type should equal keybuf[0]. */
+
+ rv = (* callback)(&dataitem, &keyitem, type, udata);
+ if ( rv != SECSuccess ) {
+ return(rv);
+ }
+ }
+ } while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
+
+ return(SECSuccess);
+}
+/*
+ * Decode a certificate and enter it into the temporary certificate database.
+ * Deal with nicknames correctly
+ *
+ * This is the private entry point.
+ */
+static NSSLOWCERTCertificate *
+DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
+{
+ NSSLOWCERTCertificate *cert = NULL;
+
+ cert = nsslowcert_DecodeDERCertificate(&entry->derCert, entry->nickname );
+
+ if ( cert == NULL ) {
+ goto loser;
+ }
+
+ cert->dbhandle = handle;
+ cert->dbEntry = entry;
+ cert->trust = &entry->trust;
+
+ return(cert);
+
+loser:
+ return(0);
+}
+
+static NSSLOWCERTTrust *
+CreateTrust(void)
+{
+ NSSLOWCERTTrust *trust = NULL;
+
+ nsslowcert_LockFreeList();
+ trust = trustListHead;
+ if (trust) {
+ trustListCount--;
+ trustListHead = trust->next;
+ }
+ PORT_Assert(trustListCount >= 0);
+ nsslowcert_UnlockFreeList();
+ if (trust) {
+ return trust;
+ }
+
+ return PORT_ZNew(NSSLOWCERTTrust);
+}
+
+static void
+DestroyTrustFreeList(void)
+{
+ NSSLOWCERTTrust *trust;
+
+ nsslowcert_LockFreeList();
+ while (NULL != (trust = trustListHead)) {
+ trustListCount--;
+ trustListHead = trust->next;
+ PORT_Free(trust);
+ }
+ PORT_Assert(!trustListCount);
+ trustListCount = 0;
+ nsslowcert_UnlockFreeList();
+}
+
+static NSSLOWCERTTrust *
+DecodeTrustEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry,
+ const SECItem *dbKey)
+{
+ NSSLOWCERTTrust *trust = CreateTrust();
+ if (trust == NULL) {
+ return trust;
+ }
+ trust->dbhandle = handle;
+ trust->dbEntry = entry;
+ trust->dbKey.data = pkcs11_copyStaticData(dbKey->data,dbKey->len,
+ trust->dbKeySpace, sizeof(trust->dbKeySpace));
+ if (!trust->dbKey.data) {
+ PORT_Free(trust);
+ return NULL;
+ }
+ trust->dbKey.len = dbKey->len;
+
+ trust->trust = &entry->trust;
+ trust->derCert = &entry->derCert;
+
+ return(trust);
+}
+
+typedef struct {
+ PermCertCallback certfunc;
+ NSSLOWCERTCertDBHandle *handle;
+ void *data;
+} PermCertCallbackState;
+
+/*
+ * traversal callback to decode certs and call callers callback
+ */
+static SECStatus
+certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
+{
+ PermCertCallbackState *mystate;
+ SECStatus rv;
+ certDBEntryCert *entry;
+ SECItem entryitem;
+ NSSLOWCERTCertificate *cert;
+ PRArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
+ mystate = (PermCertCallbackState *)data;
+ entry->common.version = (unsigned int)dbdata->data[0];
+ entry->common.type = (certDBEntryType)dbdata->data[1];
+ entry->common.flags = (unsigned int)dbdata->data[2];
+ entry->common.arena = arena;
+
+ entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
+ entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ rv = DecodeDBCertEntry(entry, &entryitem);
+ if (rv != SECSuccess ) {
+ goto loser;
+ }
+ entry->derCert.type = siBuffer;
+
+ /* note: Entry is 'inheritted'. */
+ cert = DecodeACert(mystate->handle, entry);
+
+ rv = (* mystate->certfunc)(cert, dbkey, mystate->data);
+
+ /* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
+ nsslowcert_DestroyCertificateNoLocking(cert);
+
+ return(rv);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+/*
+ * Traverse all of the certificates in the permanent database and
+ * call the given function for each one; expect the caller to have lock.
+ */
+static SECStatus
+TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
+ SECStatus (* certfunc)(NSSLOWCERTCertificate *cert,
+ SECItem *k,
+ void *pdata),
+ void *udata )
+{
+ SECStatus rv;
+ PermCertCallbackState mystate;
+
+ mystate.certfunc = certfunc;
+ mystate.handle = handle;
+ mystate.data = udata;
+ rv = nsslowcert_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
+ (void *)&mystate);
+
+ return(rv);
+}
+
+/*
+ * Traverse all of the certificates in the permanent database and
+ * call the given function for each one.
+ */
+SECStatus
+nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
+ SECStatus (* certfunc)(NSSLOWCERTCertificate *cert, SECItem *k,
+ void *pdata),
+ void *udata )
+{
+ SECStatus rv;
+
+ nsslowcert_LockDB(handle);
+ rv = TraversePermCertsNoLocking(handle, certfunc, udata);
+ nsslowcert_UnlockDB(handle);
+
+ return(rv);
+}
+
+
+
+/*
+ * Close the database
+ */
+void
+nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
+{
+ if ( handle ) {
+ if ( handle->permCertDB ) {
+ certdb_Close( handle->permCertDB );
+ handle->permCertDB = NULL;
+ }
+ if (handle->dbMon) {
+ PZ_DestroyMonitor(handle->dbMon);
+ handle->dbMon = NULL;
+ }
+ PORT_Free(handle);
+ }
+ return;
+}
+
+/*
+ * Get the trust attributes from a certificate
+ */
+SECStatus
+nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
+{
+ SECStatus rv;
+
+ nsslowcert_LockCertTrust(cert);
+
+ if ( cert->trust == NULL ) {
+ rv = SECFailure;
+ } else {
+ *trust = *cert->trust;
+ rv = SECSuccess;
+ }
+
+ nsslowcert_UnlockCertTrust(cert);
+ return(rv);
+}
+
+/*
+ * Change the trust attributes of a certificate and make them permanent
+ * in the database.
+ */
+SECStatus
+nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
+{
+ certDBEntryCert *entry;
+ int rv;
+ SECStatus ret;
+
+ nsslowcert_LockDB(handle);
+ nsslowcert_LockCertTrust(cert);
+ /* only set the trust on permanent certs */
+ if ( cert->trust == NULL ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ *cert->trust = *trust;
+ if ( cert->dbEntry == NULL ) {
+ ret = SECSuccess; /* not in permanent database */
+ goto done;
+ }
+
+ entry = cert->dbEntry;
+ entry->trust = *trust;
+
+ rv = WriteDBCertEntry(handle, entry);
+ if ( rv ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ ret = SECSuccess;
+
+done:
+ nsslowcert_UnlockCertTrust(cert);
+ nsslowcert_UnlockDB(handle);
+ return(ret);
+}
+
+
+static SECStatus
+nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
+{
+ char *oldnn;
+ certDBEntryCert *entry;
+ PRBool conflict;
+ SECStatus ret;
+
+ PORT_Assert(!cert->dbEntry);
+
+ /* don't add a conflicting nickname */
+ conflict = nsslowcert_CertNicknameConflict(nickname, &cert->derSubject,
+ dbhandle);
+ if ( conflict ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ /* save old nickname so that we can delete it */
+ oldnn = cert->nickname;
+
+ entry = AddCertToPermDB(dbhandle, cert, nickname, trust);
+
+ if ( entry == NULL ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ pkcs11_freeNickname(oldnn,cert->nicknameSpace);
+
+ cert->nickname = (entry->nickname) ? pkcs11_copyNickname(entry->nickname,
+ cert->nicknameSpace, sizeof(cert->nicknameSpace)) : NULL;
+ cert->trust = &entry->trust;
+ cert->dbEntry = entry;
+
+ ret = SECSuccess;
+done:
+ return(ret);
+}
+
+SECStatus
+nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *dbhandle,
+ NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
+{
+ SECStatus ret;
+
+ nsslowcert_LockDB(dbhandle);
+
+ ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
+
+ nsslowcert_UnlockDB(dbhandle);
+ return(ret);
+}
+
+/*
+ * Open the certificate database and index databases. Create them if
+ * they are not there or bad.
+ */
+SECStatus
+nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
+ const char *appName, const char *prefix,
+ NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
+{
+ int rv;
+
+ certdb_InitDBLock(handle);
+
+ handle->dbMon = PZ_NewMonitor(nssILockCertDB);
+ PORT_Assert(handle->dbMon != NULL);
+ handle->dbVerify = PR_FALSE;
+
+ rv = nsslowcert_OpenPermCertDB(handle, readOnly, appName, prefix,
+ namecb, cbarg);
+ if ( rv ) {
+ goto loser;
+ }
+
+ return (SECSuccess);
+
+loser:
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(SECFailure);
+}
+
+PRBool
+nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle)
+{
+ if (!handle) return PR_FALSE;
+ return handle->dbVerify;
+}
+
+void
+nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value)
+{
+ handle->dbVerify = value;
+}
+
+
+/*
+ * Lookup a certificate in the databases.
+ */
+static NSSLOWCERTCertificate *
+FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
+{
+ NSSLOWCERTCertificate *cert = NULL;
+ certDBEntryCert *entry;
+ PRBool locked = PR_FALSE;
+
+ if ( lockdb ) {
+ locked = PR_TRUE;
+ nsslowcert_LockDB(handle);
+ }
+
+ /* find in perm database */
+ entry = ReadDBCertEntry(handle, certKey);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ /* inherit entry */
+ cert = DecodeACert(handle, entry);
+
+loser:
+ if (cert == NULL) {
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ }
+
+ if ( locked ) {
+ nsslowcert_UnlockDB(handle);
+ }
+
+ return(cert);
+}
+
+/*
+ * Lookup a certificate in the databases.
+ */
+static NSSLOWCERTTrust *
+FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
+{
+ NSSLOWCERTTrust *trust = NULL;
+ certDBEntryCert *entry;
+ PRBool locked = PR_FALSE;
+
+ if ( lockdb ) {
+ locked = PR_TRUE;
+ nsslowcert_LockDB(handle);
+ }
+
+ /* find in perm database */
+ entry = ReadDBCertEntry(handle, certKey);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ if (!nsslowcert_hasTrust(&entry->trust)) {
+ goto loser;
+ }
+
+ /* inherit entry */
+ trust = DecodeTrustEntry(handle, entry, certKey);
+
+loser:
+ if (trust == NULL) {
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ }
+
+ if ( locked ) {
+ nsslowcert_UnlockDB(handle);
+ }
+
+ return(trust);
+}
+
+/*
+ * Lookup a certificate in the databases without locking
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
+{
+ return(FindCertByKey(handle, certKey, PR_FALSE));
+}
+
+/*
+ * Lookup a trust object in the databases without locking
+ */
+NSSLOWCERTTrust *
+nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
+{
+ return(FindTrustByKey(handle, certKey, PR_FALSE));
+}
+
+/*
+ * Generate a key from an issuerAndSerialNumber, and find the
+ * associated cert in the database.
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
+{
+ SECItem certKey;
+ SECItem *sn = &issuerAndSN->serialNumber;
+ SECItem *issuer = &issuerAndSN->derIssuer;
+ NSSLOWCERTCertificate *cert;
+ int data_left = sn->len-1;
+ int data_len = sn->len;
+ int index = 0;
+
+ /* automatically detect DER encoded serial numbers and remove the der
+ * encoding since the database expects unencoded data.
+ * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
+ if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
+ /* remove the der encoding of the serial number before generating the
+ * key.. */
+ data_left = sn->len-2;
+ data_len = sn->data[1];
+ index = 2;
+
+ /* extended length ? (not very likely for a serial number) */
+ if (data_len & 0x80) {
+ int len_count = data_len & 0x7f;
+
+ data_len = 0;
+ data_left -= len_count;
+ if (data_left > 0) {
+ while (len_count --) {
+ data_len = (data_len << 8) | sn->data[index++];
+ }
+ }
+ }
+ /* XXX leaving any leading zeros on the serial number for backwards
+ * compatibility
+ */
+ /* not a valid der, must be just an unlucky serial number value */
+ if (data_len != data_left) {
+ data_len = sn->len;
+ index = 0;
+ }
+ }
+
+ certKey.type = 0;
+ certKey.data = (unsigned char*)PORT_Alloc(sn->len + issuer->len);
+ certKey.len = data_len + issuer->len;
+
+ if ( certKey.data == NULL ) {
+ return(0);
+ }
+
+ /* first try the serial number as hand-decoded above*/
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, &sn->data[index], data_len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
+
+ cert = nsslowcert_FindCertByKey(handle, &certKey);
+ if (cert) {
+ PORT_Free(certKey.data);
+ return (cert);
+ }
+
+ /* didn't find it, try by der encoded serial number */
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
+ certKey.len = sn->len + issuer->len;
+
+ cert = nsslowcert_FindCertByKey(handle, &certKey);
+
+ PORT_Free(certKey.data);
+
+ return(cert);
+}
+
+/*
+ * Generate a key from an issuerAndSerialNumber, and find the
+ * associated cert in the database.
+ */
+NSSLOWCERTTrust *
+nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle,
+ NSSLOWCERTIssuerAndSN *issuerAndSN)
+{
+ SECItem certKey;
+ SECItem *sn = &issuerAndSN->serialNumber;
+ SECItem *issuer = &issuerAndSN->derIssuer;
+ NSSLOWCERTTrust *trust;
+ unsigned char keyBuf[512];
+ int data_left = sn->len-1;
+ int data_len = sn->len;
+ int index = 0;
+ int len;
+
+ /* automatically detect DER encoded serial numbers and remove the der
+ * encoding since the database expects unencoded data.
+ * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
+ if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
+ /* remove the der encoding of the serial number before generating the
+ * key.. */
+ data_left = sn->len-2;
+ data_len = sn->data[1];
+ index = 2;
+
+ /* extended length ? (not very likely for a serial number) */
+ if (data_len & 0x80) {
+ int len_count = data_len & 0x7f;
+
+ data_len = 0;
+ data_left -= len_count;
+ if (data_left > 0) {
+ while (len_count --) {
+ data_len = (data_len << 8) | sn->data[index++];
+ }
+ }
+ }
+ /* XXX leaving any leading zeros on the serial number for backwards
+ * compatibility
+ */
+ /* not a valid der, must be just an unlucky serial number value */
+ if (data_len != data_left) {
+ data_len = sn->len;
+ index = 0;
+ }
+ }
+
+ certKey.type = 0;
+ certKey.len = data_len + issuer->len;
+ len = sn->len + issuer->len;
+ if (len > sizeof (keyBuf)) {
+ certKey.data = (unsigned char*)PORT_Alloc(len);
+ } else {
+ certKey.data = keyBuf;
+ }
+
+ if ( certKey.data == NULL ) {
+ return(0);
+ }
+
+ /* first try the serial number as hand-decoded above*/
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, &sn->data[index], data_len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
+
+ trust = nsslowcert_FindTrustByKey(handle, &certKey);
+ if (trust) {
+ pkcs11_freeStaticData(certKey.data, keyBuf);
+ return (trust);
+ }
+
+ if (index == 0) {
+ pkcs11_freeStaticData(certKey.data, keyBuf);
+ return NULL;
+ }
+
+ /* didn't find it, try by der encoded serial number */
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, sn->data, sn->len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
+ certKey.len = sn->len + issuer->len;
+
+ trust = nsslowcert_FindTrustByKey(handle, &certKey);
+
+ pkcs11_freeStaticData(certKey.data, keyBuf);
+
+ return(trust);
+}
+
+/*
+ * look for the given DER certificate in the database
+ */
+NSSLOWCERTCertificate *
+nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
+{
+ PRArenaPool *arena;
+ SECItem certKey;
+ SECStatus rv;
+ NSSLOWCERTCertificate *cert = NULL;
+
+ /* create a scratch arena */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ return(NULL);
+ }
+
+ /* extract the database key from the cert */
+ rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* find the certificate */
+ cert = nsslowcert_FindCertByKey(handle, &certKey);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return(cert);
+}
+
+static void
+DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
+{
+ int refCount;
+ NSSLOWCERTCertDBHandle *handle;
+
+ if ( cert ) {
+
+ handle = cert->dbhandle;
+
+ /*
+ * handle may be NULL, for example if the cert was created with
+ * nsslowcert_DecodeDERCertificate.
+ */
+ if ( lockdb && handle ) {
+ nsslowcert_LockDB(handle);
+ }
+
+ nsslowcert_LockCertRefCount(cert);
+ PORT_Assert(cert->referenceCount > 0);
+ refCount = --cert->referenceCount;
+ nsslowcert_UnlockCertRefCount(cert);
+
+ if ( ( refCount == 0 ) ) {
+ certDBEntryCert *entry = cert->dbEntry;
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
+ pkcs11_freeNickname(cert->emailAddr,cert->emailAddrSpace);
+ pkcs11_freeStaticData(cert->certKey.data,cert->certKeySpace);
+ cert->certKey.data = NULL;
+ cert->nickname = NULL;
+
+ /* zero cert before freeing. Any stale references to this cert
+ * after this point will probably cause an exception. */
+ PORT_Memset(cert, 0, sizeof *cert);
+
+ /* use reflock to protect the free list */
+ nsslowcert_LockFreeList();
+ if (certListCount > MAX_CERT_LIST_COUNT) {
+ PORT_Free(cert);
+ } else {
+ certListCount++;
+ cert->next = certListHead;
+ certListHead = cert;
+ }
+ nsslowcert_UnlockFreeList();
+ cert = NULL;
+ }
+ if ( lockdb && handle ) {
+ nsslowcert_UnlockDB(handle);
+ }
+ }
+
+ return;
+}
+
+NSSLOWCERTCertificate *
+nsslowcert_CreateCert(void)
+{
+ NSSLOWCERTCertificate *cert;
+ nsslowcert_LockFreeList();
+ cert = certListHead;
+ if (cert) {
+ certListHead = cert->next;
+ certListCount--;
+ }
+ PORT_Assert(certListCount >= 0);
+ nsslowcert_UnlockFreeList();
+ if (cert) {
+ return cert;
+ }
+ return PORT_ZNew(NSSLOWCERTCertificate);
+}
+
+static void
+DestroyCertFreeList(void)
+{
+ NSSLOWCERTCertificate *cert;
+
+ nsslowcert_LockFreeList();
+ while (NULL != (cert = certListHead)) {
+ certListCount--;
+ certListHead = cert->next;
+ PORT_Free(cert);
+ }
+ PORT_Assert(!certListCount);
+ certListCount = 0;
+ nsslowcert_UnlockFreeList();
+}
+
+void
+nsslowcert_DestroyTrust(NSSLOWCERTTrust *trust)
+{
+ certDBEntryCert *entry = trust->dbEntry;
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ pkcs11_freeStaticData(trust->dbKey.data,trust->dbKeySpace);
+ PORT_Memset(trust, 0, sizeof(*trust));
+
+ nsslowcert_LockFreeList();
+ if (trustListCount > MAX_TRUST_LIST_COUNT) {
+ PORT_Free(trust);
+ } else {
+ trustListCount++;
+ trust->next = trustListHead;
+ trustListHead = trust;
+ }
+ nsslowcert_UnlockFreeList();
+
+ return;
+}
+
+void
+nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert)
+{
+ DestroyCertificate(cert, PR_TRUE);
+ return;
+}
+
+static void
+nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert)
+{
+ DestroyCertificate(cert, PR_FALSE);
+ return;
+}
+
+/*
+ * Lookup a CRL in the databases. We mirror the same fast caching data base
+ * caching stuff used by certificates....?
+ */
+certDBEntryRevocation *
+nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
+ SECItem *crlKey, PRBool isKRL)
+{
+ SECItem keyitem;
+ DBT key;
+ SECStatus rv;
+ PRArenaPool *arena = NULL;
+ certDBEntryRevocation *entry = NULL;
+ certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
+ : certDBEntryTypeRevocation;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ key.data = keyitem.data;
+ key.size = keyitem.len;
+
+ /* find in perm database */
+ entry = ReadDBCrlEntry(handle, crlKey, crlType);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return entry;
+}
+
+/*
+ * replace the existing URL in the data base with a new one
+ */
+static SECStatus
+nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
+ SECItem *crlKey, char *url, PRBool isKRL)
+{
+ SECStatus rv = SECFailure;
+ certDBEntryRevocation *entry = NULL;
+ certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
+ : certDBEntryTypeRevocation;
+ DeleteDBCrlEntry(handle, crlKey, crlType);
+
+ /* Write the new entry into the data base */
+ entry = NewDBCrlEntry(derCrl, url, crlType, 0);
+ if (entry == NULL) goto done;
+
+ rv = WriteDBCrlEntry(handle, entry, crlKey);
+ if (rv != SECSuccess) goto done;
+
+done:
+ if (entry) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ return rv;
+}
+
+SECStatus
+nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
+ SECItem *crlKey, char *url, PRBool isKRL)
+{
+ SECStatus rv;
+
+ rv = nsslowcert_UpdateCrl(handle, derCrl, crlKey, url, isKRL);
+
+ return rv;
+}
+
+SECStatus
+nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle, const SECItem *derName,
+ PRBool isKRL)
+{
+ SECStatus rv;
+ certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
+ : certDBEntryTypeRevocation;
+
+ rv = DeleteDBCrlEntry(handle, derName, crlType);
+ if (rv != SECSuccess) goto done;
+
+done:
+ return rv;
+}
+
+
+PRBool
+nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
+{
+ if (trust == NULL) {
+ return PR_FALSE;
+ }
+ return !((trust->sslFlags & CERTDB_TRUSTED_UNKNOWN) &&
+ (trust->emailFlags & CERTDB_TRUSTED_UNKNOWN) &&
+ (trust->objectSigningFlags & CERTDB_TRUSTED_UNKNOWN));
+}
+
+/*
+ * This function has the logic that decides if another person's cert and
+ * email profile from an S/MIME message should be saved. It can deal with
+ * the case when there is no profile.
+ */
+static SECStatus
+nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
+ char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
+ SECItem *profileTime)
+{
+ certDBEntrySMime *entry = NULL;
+ SECStatus rv = SECFailure;;
+
+
+ /* find our existing entry */
+ entry = nsslowcert_ReadDBSMimeEntry(dbhandle, emailAddr);
+
+ if ( entry ) {
+ /* keep our old db entry consistant for old applications. */
+ if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
+ nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName,
+ emailAddr, nsslowcert_remove);
+ }
+ DestroyDBEntry((certDBEntry *)entry);
+ entry = NULL;
+ }
+
+ /* now save the entry */
+ entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
+ profileTime, 0);
+ if ( entry == NULL ) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ nsslowcert_LockDB(dbhandle);
+
+ rv = DeleteDBSMimeEntry(dbhandle, emailAddr);
+ /* if delete fails, try to write new entry anyway... */
+
+ /* link subject entry back here */
+ rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
+ nsslowcert_add);
+ if ( rv != SECSuccess ) {
+ nsslowcert_UnlockDB(dbhandle);
+ goto loser;
+ }
+
+ rv = WriteDBSMimeEntry(dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ nsslowcert_UnlockDB(dbhandle);
+ goto loser;
+ }
+
+ nsslowcert_UnlockDB(dbhandle);
+
+ rv = SECSuccess;
+
+loser:
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ return(rv);
+}
+
+SECStatus
+nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
+ SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime)
+{
+ SECStatus rv = SECFailure;;
+
+
+ rv = nsslowcert_UpdateSMimeProfile(dbhandle, emailAddr,
+ derSubject, emailProfile, profileTime);
+
+ return(rv);
+}
+
+void
+nsslowcert_DestroyFreeLists(void)
+{
+ if (freeListLock == NULL) {
+ return;
+ }
+ DestroyCertEntryFreeList();
+ DestroyTrustFreeList();
+ DestroyCertFreeList();
+ PZ_DestroyLock(freeListLock);
+ freeListLock = NULL;
+}
+
+void
+nsslowcert_DestroyGlobalLocks(void)
+{
+ if (dbLock) {
+ PZ_DestroyLock(dbLock);
+ dbLock = NULL;
+ }
+ if (certRefCountLock) {
+ PZ_DestroyLock(certRefCountLock);
+ certRefCountLock = NULL;
+ }
+ if (certTrustLock) {
+ PZ_DestroyLock(certTrustLock);
+ certTrustLock = NULL;
+ }
+}
+
+certDBEntry *
+nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
+ certDBEntryType entryType, void *pdata)
+{
+ PLArenaPool *arena = NULL;
+ certDBEntry *entry;
+ SECStatus rv;
+ SECItem dbEntry;
+
+
+ if ((dbData->len < SEC_DB_ENTRY_HEADER_LEN) || (dbKey->len == 0)) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ goto loser;
+ }
+ dbEntry.data = &dbData->data[SEC_DB_ENTRY_HEADER_LEN];
+ dbEntry.len = dbData->len - SEC_DB_ENTRY_HEADER_LEN;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ goto loser;
+ }
+ entry = PORT_ArenaZNew(arena, certDBEntry);
+ if (!entry)
+ goto loser;
+
+ entry->common.version = (unsigned int)dbData->data[0];
+ entry->common.flags = (unsigned int)dbData->data[2];
+ entry->common.type = entryType;
+ entry->common.arena = arena;
+
+ switch (entryType) {
+ case certDBEntryTypeContentVersion: /* This type appears to be unused */
+ case certDBEntryTypeVersion: /* This type has only the common hdr */
+ rv = SECSuccess;
+ break;
+
+ case certDBEntryTypeSubject:
+ rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey);
+ break;
+
+ case certDBEntryTypeNickname:
+ rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
+ (char *)dbKey->data);
+ break;
+
+ /* smime profiles need entries created after the certs have
+ * been imported, loop over them in a second run */
+ case certDBEntryTypeSMimeProfile:
+ rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data);
+ break;
+
+ case certDBEntryTypeCert:
+ rv = DecodeDBCertEntry(&entry->cert, &dbEntry);
+ break;
+
+ case certDBEntryTypeKeyRevocation:
+ case certDBEntryTypeRevocation:
+ rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry);
+ break;
+
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ }
+
+ if (rv == SECSuccess)
+ return entry;
+
+loser:
+ if (arena)
+ PORT_FreeArena(arena, PR_FALSE);
+ return NULL;
+}
+
diff --git a/security/nss/lib/softoken/legacydb/pcertt.h b/security/nss/lib/softoken/legacydb/pcertt.h
new file mode 100644
index 000000000..476a2d079
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pcertt.h
@@ -0,0 +1,450 @@
+/* ***** 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 ***** */
+/*
+ * certt.h - public data structures for the certificate library
+ *
+ * $Id$
+ */
+#ifndef _PCERTT_H_
+#define _PCERTT_H_
+
+#include "prclist.h"
+#include "pkcs11t.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "plarena.h"
+#include "prcvar.h"
+#include "nssilock.h"
+#include "prio.h"
+#include "prmon.h"
+
+/* Non-opaque objects */
+typedef struct NSSLOWCERTCertDBHandleStr NSSLOWCERTCertDBHandle;
+typedef struct NSSLOWCERTCertKeyStr NSSLOWCERTCertKey;
+
+typedef struct NSSLOWCERTTrustStr NSSLOWCERTTrust;
+typedef struct NSSLOWCERTCertTrustStr NSSLOWCERTCertTrust;
+typedef struct NSSLOWCERTCertificateStr NSSLOWCERTCertificate;
+typedef struct NSSLOWCERTCertificateListStr NSSLOWCERTCertificateList;
+typedef struct NSSLOWCERTIssuerAndSNStr NSSLOWCERTIssuerAndSN;
+typedef struct NSSLOWCERTSignedDataStr NSSLOWCERTSignedData;
+typedef struct NSSLOWCERTSubjectPublicKeyInfoStr NSSLOWCERTSubjectPublicKeyInfo;
+typedef struct NSSLOWCERTValidityStr NSSLOWCERTValidity;
+
+/*
+** An X.509 validity object
+*/
+struct NSSLOWCERTValidityStr {
+ PRArenaPool *arena;
+ SECItem notBefore;
+ SECItem notAfter;
+};
+
+/*
+ * A serial number and issuer name, which is used as a database key
+ */
+struct NSSLOWCERTCertKeyStr {
+ SECItem serialNumber;
+ SECItem derIssuer;
+};
+
+/*
+** A signed data object. Used to implement the "signed" macro used
+** in the X.500 specs.
+*/
+struct NSSLOWCERTSignedDataStr {
+ SECItem data;
+ SECAlgorithmID signatureAlgorithm;
+ SECItem signature;
+};
+
+/*
+** An X.509 subject-public-key-info object
+*/
+struct NSSLOWCERTSubjectPublicKeyInfoStr {
+ PRArenaPool *arena;
+ SECAlgorithmID algorithm;
+ SECItem subjectPublicKey;
+};
+
+typedef struct _certDBEntryCert certDBEntryCert;
+typedef struct _certDBEntryRevocation certDBEntryRevocation;
+
+struct NSSLOWCERTCertTrustStr {
+ unsigned int sslFlags;
+ unsigned int emailFlags;
+ unsigned int objectSigningFlags;
+};
+
+/*
+** PKCS11 Trust representation
+*/
+struct NSSLOWCERTTrustStr {
+ NSSLOWCERTTrust *next;
+ NSSLOWCERTCertDBHandle *dbhandle;
+ SECItem dbKey; /* database key for this cert */
+ certDBEntryCert *dbEntry; /* database entry struct */
+ NSSLOWCERTCertTrust *trust;
+ SECItem *derCert; /* original DER for the cert */
+ unsigned char dbKeySpace[512];
+};
+
+/*
+** An X.509 certificate object (the unsigned form)
+*/
+struct NSSLOWCERTCertificateStr {
+ /* the arena is used to allocate any data structures that have the same
+ * lifetime as the cert. This is all stuff that hangs off of the cert
+ * structure, and is all freed at the same time. I is used when the
+ * cert is decoded, destroyed, and at some times when it changes
+ * state
+ */
+ NSSLOWCERTCertificate *next;
+ NSSLOWCERTCertDBHandle *dbhandle;
+
+ SECItem derCert; /* original DER for the cert */
+ SECItem derIssuer; /* DER for issuer name */
+ SECItem derSN;
+ SECItem serialNumber;
+ SECItem derSubject; /* DER for subject name */
+ SECItem derSubjKeyInfo;
+ NSSLOWCERTSubjectPublicKeyInfo *subjectPublicKeyInfo;
+ SECItem certKey; /* database key for this cert */
+ SECItem validity;
+ certDBEntryCert *dbEntry; /* database entry struct */
+ SECItem subjectKeyID; /* x509v3 subject key identifier */
+ SECItem extensions;
+ char *nickname;
+ char *emailAddr;
+ NSSLOWCERTCertTrust *trust;
+
+ /* the reference count is modified whenever someone looks up, dups
+ * or destroys a certificate
+ */
+ int referenceCount;
+
+ char nicknameSpace[200];
+ char emailAddrSpace[200];
+ unsigned char certKeySpace[512];
+};
+
+#define SEC_CERTIFICATE_VERSION_1 0 /* default created */
+#define SEC_CERTIFICATE_VERSION_2 1 /* v2 */
+#define SEC_CERTIFICATE_VERSION_3 2 /* v3 extensions */
+
+#define SEC_CRL_VERSION_1 0 /* default */
+#define SEC_CRL_VERSION_2 1 /* v2 extensions */
+
+struct NSSLOWCERTIssuerAndSNStr {
+ SECItem derIssuer;
+ SECItem serialNumber;
+};
+
+typedef SECStatus (* NSSLOWCERTCertCallback)(NSSLOWCERTCertificate *cert, void *arg);
+
+/* This is the typedef for the callback passed to nsslowcert_OpenCertDB() */
+/* callback to return database name based on version number */
+typedef char * (*NSSLOWCERTDBNameFunc)(void *arg, int dbVersion);
+
+/* XXX Lisa thinks the template declarations belong in cert.h, not here? */
+
+#include "secasn1t.h" /* way down here because I expect template stuff to
+ * move out of here anyway */
+
+/*
+ * Certificate Database related definitions and data structures
+ */
+
+/* version number of certificate database */
+#define CERT_DB_FILE_VERSION 8
+#define CERT_DB_V7_FILE_VERSION 7
+#define CERT_DB_CONTENT_VERSION 2
+
+#define SEC_DB_ENTRY_HEADER_LEN 3
+#define SEC_DB_KEY_HEADER_LEN 1
+
+/* All database entries have this form:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 version
+ * 1 type
+ * 2 flags
+ */
+
+/* database entry types */
+typedef enum {
+ certDBEntryTypeVersion = 0,
+ certDBEntryTypeCert = 1,
+ certDBEntryTypeNickname = 2,
+ certDBEntryTypeSubject = 3,
+ certDBEntryTypeRevocation = 4,
+ certDBEntryTypeKeyRevocation = 5,
+ certDBEntryTypeSMimeProfile = 6,
+ certDBEntryTypeContentVersion = 7,
+ certDBEntryTypeBlob = 8
+} certDBEntryType;
+
+typedef struct {
+ certDBEntryType type;
+ unsigned int version;
+ unsigned int flags;
+ PRArenaPool *arena;
+} certDBEntryCommon;
+
+/*
+ * Certificate entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 sslFlags-msb
+ * 1 sslFlags-lsb
+ * 2 emailFlags-msb
+ * 3 emailFlags-lsb
+ * 4 objectSigningFlags-msb
+ * 5 objectSigningFlags-lsb
+ * 6 derCert-len-msb
+ * 7 derCert-len-lsb
+ * 8 nickname-len-msb
+ * 9 nickname-len-lsb
+ * ... derCert
+ * ... nickname
+ *
+ * NOTE: the nickname string as stored in the database is null terminated,
+ * in other words, the last byte of the db entry is always 0
+ * if a nickname is present.
+ * NOTE: if nickname is not present, then nickname-len-msb and
+ * nickname-len-lsb will both be zero.
+ */
+struct _certDBEntryCert {
+ certDBEntryCommon common;
+ certDBEntryCert *next;
+ NSSLOWCERTCertTrust trust;
+ SECItem derCert;
+ char *nickname;
+ char nicknameSpace[200];
+ unsigned char derCertSpace[2048];
+};
+
+/*
+ * Certificate Nickname entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 subjectname-len-msb
+ * 1 subjectname-len-lsb
+ * 2... subjectname
+ *
+ * The database key for this type of entry is a nickname string
+ * The "subjectname" value is the DER encoded DN of the identity
+ * that matches this nickname.
+ */
+typedef struct {
+ certDBEntryCommon common;
+ char *nickname;
+ SECItem subjectName;
+} certDBEntryNickname;
+
+#define DB_NICKNAME_ENTRY_HEADER_LEN 2
+
+/*
+ * Certificate Subject entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 ncerts-msb
+ * 1 ncerts-lsb
+ * 2 nickname-msb
+ * 3 nickname-lsb
+ * 4 emailAddr-msb
+ * 5 emailAddr-lsb
+ * ... nickname
+ * ... emailAddr
+ * ...+2*i certkey-len-msb
+ * ...+1+2*i certkey-len-lsb
+ * ...+2*ncerts+2*i keyid-len-msb
+ * ...+1+2*ncerts+2*i keyid-len-lsb
+ * ... certkeys
+ * ... keyids
+ *
+ * The database key for this type of entry is the DER encoded subject name
+ * The "certkey" value is an array of certificate database lookup keys that
+ * points to the database entries for the certificates that matche
+ * this subject.
+ *
+ */
+typedef struct _certDBEntrySubject {
+ certDBEntryCommon common;
+ SECItem derSubject;
+ unsigned int ncerts;
+ char *nickname;
+ SECItem *certKeys;
+ SECItem *keyIDs;
+ char **emailAddrs;
+ unsigned int nemailAddrs;
+} certDBEntrySubject;
+
+#define DB_SUBJECT_ENTRY_HEADER_LEN 6
+
+/*
+ * Certificate SMIME profile entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 subjectname-len-msb
+ * 1 subjectname-len-lsb
+ * 2 smimeoptions-len-msb
+ * 3 smimeoptions-len-lsb
+ * 4 options-date-len-msb
+ * 5 options-date-len-lsb
+ * 6... subjectname
+ * ... smimeoptions
+ * ... options-date
+ *
+ * The database key for this type of entry is the email address string
+ * The "subjectname" value is the DER encoded DN of the identity
+ * that matches this nickname.
+ * The "smimeoptions" value is a string that represents the algorithm
+ * capabilities on the remote user.
+ * The "options-date" is the date that the smime options value was created.
+ * This is generally the signing time of the signed message that contained
+ * the options. It is a UTCTime value.
+ */
+typedef struct {
+ certDBEntryCommon common;
+ char *emailAddr;
+ SECItem subjectName;
+ SECItem smimeOptions;
+ SECItem optionsDate;
+} certDBEntrySMime;
+
+#define DB_SMIME_ENTRY_HEADER_LEN 6
+
+/*
+ * Crl/krl entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 derCert-len-msb
+ * 1 derCert-len-lsb
+ * 2 url-len-msb
+ * 3 url-len-lsb
+ * ... derCert
+ * ... url
+ *
+ * NOTE: the url string as stored in the database is null terminated,
+ * in other words, the last byte of the db entry is always 0
+ * if a nickname is present.
+ * NOTE: if url is not present, then url-len-msb and
+ * url-len-lsb will both be zero.
+ */
+#define DB_CRL_ENTRY_HEADER_LEN 4
+struct _certDBEntryRevocation {
+ certDBEntryCommon common;
+ SECItem derCrl;
+ char *url; /* where to load the crl from */
+};
+
+/*
+ * Database Version Entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * only the low level header...
+ *
+ * The database key for this type of entry is the string "Version"
+ */
+typedef struct {
+ certDBEntryCommon common;
+} certDBEntryVersion;
+
+#define SEC_DB_VERSION_KEY "Version"
+#define SEC_DB_VERSION_KEY_LEN sizeof(SEC_DB_VERSION_KEY)
+
+/*
+ * Database Content Version Entry:
+ *
+ * byte offset field
+ * ----------- -----
+ * 0 contentVersion
+ *
+ * The database key for this type of entry is the string "ContentVersion"
+ */
+typedef struct {
+ certDBEntryCommon common;
+ char contentVersion;
+} certDBEntryContentVersion;
+
+#define SEC_DB_CONTENT_VERSION_KEY "ContentVersion"
+#define SEC_DB_CONTENT_VERSION_KEY_LEN sizeof(SEC_DB_CONTENT_VERSION_KEY)
+
+typedef union {
+ certDBEntryCommon common;
+ certDBEntryCert cert;
+ certDBEntryContentVersion content;
+ certDBEntryNickname nickname;
+ certDBEntryRevocation revocation;
+ certDBEntrySMime smime;
+ certDBEntrySubject subject;
+ certDBEntryVersion version;
+} certDBEntry;
+
+/* length of the fixed part of a database entry */
+#define DBCERT_V4_HEADER_LEN 7
+#define DB_CERT_V5_ENTRY_HEADER_LEN 7
+#define DB_CERT_V6_ENTRY_HEADER_LEN 7
+#define DB_CERT_ENTRY_HEADER_LEN 10
+
+/* common flags for all types of certificates */
+#define CERTDB_VALID_PEER (1<<0)
+#define CERTDB_TRUSTED (1<<1)
+#define CERTDB_SEND_WARN (1<<2)
+#define CERTDB_VALID_CA (1<<3)
+#define CERTDB_TRUSTED_CA (1<<4) /* trusted for issuing server certs */
+#define CERTDB_NS_TRUSTED_CA (1<<5)
+#define CERTDB_USER (1<<6)
+#define CERTDB_TRUSTED_CLIENT_CA (1<<7) /* trusted for issuing client certs */
+#define CERTDB_INVISIBLE_CA (1<<8) /* don't show in UI */
+#define CERTDB_GOVT_APPROVED_CA (1<<9) /* can do strong crypto in export ver */
+#define CERTDB_NOT_TRUSTED (1<<10) /* explicitly don't trust this cert */
+#define CERTDB_TRUSTED_UNKNOWN (1<<11) /* accept trust from another source */
+
+/* bits not affected by the CKO_NETSCAPE_TRUST object */
+#define CERTDB_PRESERVE_TRUST_BITS (CERTDB_USER | CERTDB_VALID_PEER | \
+ CERTDB_NS_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_INVISIBLE_CA | \
+ CERTDB_GOVT_APPROVED_CA)
+
+#endif /* _PCERTT_H_ */
diff --git a/security/nss/lib/softoken/legacydb/pk11db.c b/security/nss/lib/softoken/legacydb/pk11db.c
new file mode 100644
index 000000000..f85a0a6c3
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/pk11db.c
@@ -0,0 +1,773 @@
+/* ***** 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. This file is written to abstract away how the modules are
+ * stored so we can deside that later.
+ */
+
+#include "pk11pars.h"
+#include "lgdb.h"
+#include "mcom_db.h"
+#include "secerr.h"
+
+#define FREE_CLEAR(p) if (p) { PORT_Free(p); p = NULL; }
+
+/* Construct a database key for a given module */
+static SECStatus secmod_MakeKey(DBT *key, char * module) {
+ int len = 0;
+ char *commonName;
+
+ commonName = secmod_argGetParamValue("name",module);
+ if (commonName == NULL) {
+ commonName = secmod_argGetParamValue("library",module);
+ }
+ if (commonName == NULL) return SECFailure;
+ len = PORT_Strlen(commonName);
+ key->data = commonName;
+ key->size = len;
+ return SECSuccess;
+}
+
+/* free out constructed database key */
+static void
+secmod_FreeKey(DBT *key)
+{
+ if (key->data) {
+ PORT_Free(key->data);
+ }
+ key->data = NULL;
+ key->size = 0;
+}
+
+typedef struct secmodDataStr secmodData;
+typedef struct secmodSlotDataStr secmodSlotData;
+struct secmodDataStr {
+ unsigned char major;
+ unsigned char minor;
+ unsigned char nameStart[2];
+ unsigned char slotOffset[2];
+ unsigned char internal;
+ unsigned char fips;
+ unsigned char ssl[8];
+ unsigned char trustOrder[4];
+ unsigned char cipherOrder[4];
+ unsigned char reserved1;
+ unsigned char isModuleDB;
+ unsigned char isModuleDBOnly;
+ unsigned char isCritical;
+ unsigned char reserved[4];
+ unsigned char names[6]; /* enough space for the length fields */
+};
+
+struct secmodSlotDataStr {
+ unsigned char slotID[4];
+ unsigned char defaultFlags[4];
+ unsigned char timeout[4];
+ unsigned char askpw;
+ unsigned char hasRootCerts;
+ unsigned char reserved[18]; /* this makes it a round 32 bytes */
+};
+
+#define SECMOD_DB_VERSION_MAJOR 0
+#define SECMOD_DB_VERSION_MINOR 6
+#define SECMOD_DB_EXT1_VERSION_MAJOR 0
+#define SECMOD_DB_EXT1_VERSION_MINOR 6
+#define SECMOD_DB_NOUI_VERSION_MAJOR 0
+#define SECMOD_DB_NOUI_VERSION_MINOR 4
+
+#define SECMOD_PUTSHORT(dest,src) \
+ (dest)[1] = (unsigned char) ((src)&0xff); \
+ (dest)[0] = (unsigned char) (((src) >> 8) & 0xff);
+#define SECMOD_PUTLONG(dest,src) \
+ (dest)[3] = (unsigned char) ((src)&0xff); \
+ (dest)[2] = (unsigned char) (((src) >> 8) & 0xff); \
+ (dest)[1] = (unsigned char) (((src) >> 16) & 0xff); \
+ (dest)[0] = (unsigned char) (((src) >> 24) & 0xff);
+#define SECMOD_GETSHORT(src) \
+ ((unsigned short) (((src)[0] << 8) | (src)[1]))
+#define SECMOD_GETLONG(src) \
+ ((unsigned long) (( (unsigned long) (src)[0] << 24) | \
+ ( (unsigned long) (src)[1] << 16) | \
+ ( (unsigned long) (src)[2] << 8) | \
+ (unsigned long) (src)[3]))
+
+/*
+ * build a data base entry from a module
+ */
+static SECStatus
+secmod_EncodeData(DBT *data, char * module)
+{
+ secmodData *encoded = NULL;
+ secmodSlotData *slot;
+ unsigned char *dataPtr;
+ unsigned short len, len2 = 0, len3 = 0;
+ int count = 0;
+ unsigned short offset;
+ int dataLen, i;
+ unsigned long order;
+ unsigned long ssl[2];
+ char *commonName = NULL , *dllName = NULL, *param = NULL, *nss = NULL;
+ char *slotParams, *ciphers;
+ PK11PreSlotInfo *slotInfo = NULL;
+ SECStatus rv = SECFailure;
+
+ rv = secmod_argParseModuleSpec(module,&dllName,&commonName,&param,&nss);
+ if (rv != SECSuccess) return rv;
+ rv = SECFailure;
+
+ if (commonName == NULL) {
+ /* set error */
+ goto loser;
+ }
+
+ len = PORT_Strlen(commonName);
+ if (dllName) {
+ len2 = PORT_Strlen(dllName);
+ }
+ if (param) {
+ len3 = PORT_Strlen(param);
+ }
+
+ slotParams = secmod_argGetParamValue("slotParams",nss);
+ slotInfo = secmod_argParseSlotInfo(NULL,slotParams,&count);
+ if (slotParams) PORT_Free(slotParams);
+
+ if (count && slotInfo == NULL) {
+ /* set error */
+ goto loser;
+ }
+
+ dataLen = sizeof(secmodData) + len + len2 + len3 + sizeof(unsigned short) +
+ count*sizeof(secmodSlotData);
+
+ data->data = (unsigned char *) PORT_ZAlloc(dataLen);
+ encoded = (secmodData *)data->data;
+ dataPtr = (unsigned char *) data->data;
+ data->size = dataLen;
+
+ if (encoded == NULL) {
+ /* set error */
+ goto loser;
+ }
+
+ encoded->major = SECMOD_DB_VERSION_MAJOR;
+ encoded->minor = SECMOD_DB_VERSION_MINOR;
+ encoded->internal = (unsigned char)
+ (secmod_argHasFlag("flags","internal",nss) ? 1 : 0);
+ encoded->fips = (unsigned char)
+ (secmod_argHasFlag("flags","FIPS",nss) ? 1 : 0);
+ encoded->isModuleDB = (unsigned char)
+ (secmod_argHasFlag("flags","isModuleDB",nss) ? 1 : 0);
+ encoded->isModuleDBOnly = (unsigned char)
+ (secmod_argHasFlag("flags","isModuleDBOnly",nss) ? 1 : 0);
+ encoded->isCritical = (unsigned char)
+ (secmod_argHasFlag("flags","critical",nss) ? 1 : 0);
+
+ order = secmod_argReadLong("trustOrder", nss, SECMOD_DEFAULT_TRUST_ORDER,
+ NULL);
+ SECMOD_PUTLONG(encoded->trustOrder,order);
+ order = secmod_argReadLong("cipherOrder", nss, SECMOD_DEFAULT_CIPHER_ORDER,
+ NULL);
+ SECMOD_PUTLONG(encoded->cipherOrder,order);
+
+
+ ciphers = secmod_argGetParamValue("ciphers",nss);
+ secmod_argSetNewCipherFlags(&ssl[0], ciphers);
+ SECMOD_PUTLONG(encoded->ssl,ssl[0]);
+ SECMOD_PUTLONG(&encoded->ssl[4],ssl[1]);
+ if (ciphers) PORT_Free(ciphers);
+
+ offset = (unsigned short) &(((secmodData *)0)->names[0]);
+ SECMOD_PUTSHORT(encoded->nameStart,offset);
+ offset = offset + len + len2 + len3 + 3*sizeof(unsigned short);
+ SECMOD_PUTSHORT(encoded->slotOffset,offset);
+
+
+ SECMOD_PUTSHORT(&dataPtr[offset],((unsigned short)count));
+ slot = (secmodSlotData *)(dataPtr+offset+sizeof(unsigned short));
+
+ offset = 0;
+ SECMOD_PUTSHORT(encoded->names,len);
+ offset += sizeof(unsigned short);
+ PORT_Memcpy(&encoded->names[offset],commonName,len);
+ offset += len;
+
+
+ SECMOD_PUTSHORT(&encoded->names[offset],len2);
+ offset += sizeof(unsigned short);
+ if (len2) PORT_Memcpy(&encoded->names[offset],dllName,len2);
+ offset += len2;
+
+ SECMOD_PUTSHORT(&encoded->names[offset],len3);
+ offset += sizeof(unsigned short);
+ if (len3) PORT_Memcpy(&encoded->names[offset],param,len3);
+ offset += len3;
+
+ if (count) {
+ for (i=0; i < count; i++) {
+ SECMOD_PUTLONG(slot[i].slotID, slotInfo[i].slotID);
+ SECMOD_PUTLONG(slot[i].defaultFlags,
+ slotInfo[i].defaultFlags);
+ SECMOD_PUTLONG(slot[i].timeout,slotInfo[i].timeout);
+ slot[i].askpw = slotInfo[i].askpw;
+ slot[i].hasRootCerts = slotInfo[i].hasRootCerts;
+ PORT_Memset(slot[i].reserved, 0, sizeof(slot[i].reserved));
+ }
+ }
+ rv = SECSuccess;
+
+loser:
+ if (commonName) PORT_Free(commonName);
+ if (dllName) PORT_Free(dllName);
+ if (param) PORT_Free(param);
+ if (slotInfo) PORT_Free(slotInfo);
+ if (nss) PORT_Free(nss);
+ return rv;
+
+}
+
+static void
+secmod_FreeData(DBT *data)
+{
+ if (data->data) {
+ PORT_Free(data->data);
+ }
+}
+
+static void
+secmod_FreeSlotStrings(char **slotStrings, int count)
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ if (slotStrings[i]) {
+ PR_smprintf_free(slotStrings[i]);
+ slotStrings[i] = NULL;
+ }
+ }
+}
+
+/*
+ * build a module from the data base entry.
+ */
+static char *
+secmod_DecodeData(char *defParams, DBT *data, PRBool *retInternal)
+{
+ secmodData *encoded;
+ secmodSlotData *slots;
+ PLArenaPool *arena;
+ char *commonName = NULL;
+ char *dllName = NULL;
+ char *parameters = NULL;
+ char *nss;
+ char *moduleSpec;
+ char **slotStrings = NULL;
+ unsigned char *names;
+ unsigned long slotCount;
+ unsigned long ssl0 =0;
+ unsigned long ssl1 =0;
+ unsigned long slotID;
+ unsigned long defaultFlags;
+ unsigned long timeout;
+ unsigned long trustOrder =SECMOD_DEFAULT_TRUST_ORDER;
+ unsigned long cipherOrder =SECMOD_DEFAULT_CIPHER_ORDER;
+ unsigned short len;
+ unsigned short namesOffset = 0; /* start of the names block */
+ unsigned long namesRunningOffset; /* offset to name we are
+ * currently processing */
+ unsigned short slotOffset;
+ PRBool isOldVersion = PR_FALSE;
+ PRBool internal;
+ PRBool isFIPS;
+ PRBool isModuleDB =PR_FALSE;
+ PRBool isModuleDBOnly =PR_FALSE;
+ PRBool extended =PR_FALSE;
+ int i;
+
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL)
+ return NULL;
+
+#define CHECK_SIZE(x) \
+ if ((unsigned int) data->size < (unsigned int)(x)) goto db_loser
+
+ /* -------------------------------------------------------------
+ ** Process the buffer header, which is the secmodData struct.
+ ** It may be an old or new version. Check the length for each.
+ */
+
+ CHECK_SIZE( offsetof(secmodData, trustOrder[0]) );
+
+ encoded = (secmodData *)data->data;
+
+ internal = (encoded->internal != 0) ? PR_TRUE: PR_FALSE;
+ isFIPS = (encoded->fips != 0) ? PR_TRUE: PR_FALSE;
+
+ if (retInternal)
+ *retInternal = internal;
+ if (internal) {
+ parameters = PORT_ArenaStrdup(arena,defParams);
+ if (parameters == NULL)
+ goto loser;
+ }
+ if (internal && (encoded->major == SECMOD_DB_NOUI_VERSION_MAJOR) &&
+ (encoded->minor <= SECMOD_DB_NOUI_VERSION_MINOR)) {
+ isOldVersion = PR_TRUE;
+ }
+ if ((encoded->major == SECMOD_DB_EXT1_VERSION_MAJOR) &&
+ (encoded->minor >= SECMOD_DB_EXT1_VERSION_MINOR)) {
+ CHECK_SIZE( sizeof(secmodData));
+ trustOrder = SECMOD_GETLONG(encoded->trustOrder);
+ cipherOrder = SECMOD_GETLONG(encoded->cipherOrder);
+ isModuleDB = (encoded->isModuleDB != 0) ? PR_TRUE: PR_FALSE;
+ isModuleDBOnly = (encoded->isModuleDBOnly != 0) ? PR_TRUE: PR_FALSE;
+ extended = PR_TRUE;
+ }
+ if (internal && !extended) {
+ trustOrder = 0;
+ cipherOrder = 100;
+ }
+ /* decode SSL cipher enable flags */
+ ssl0 = SECMOD_GETLONG(encoded->ssl);
+ ssl1 = SECMOD_GETLONG(encoded->ssl + 4);
+
+ slotOffset = SECMOD_GETSHORT(encoded->slotOffset);
+ namesOffset = SECMOD_GETSHORT(encoded->nameStart);
+
+
+ /*--------------------------------------------------------------
+ ** Now process the variable length set of names.
+ ** The names have this structure:
+ ** struct {
+ ** BYTE commonNameLen[ 2 ];
+ ** BYTE commonName [ commonNameLen ];
+ ** BTTE libNameLen [ 2 ];
+ ** BYTE libName [ libNameLen ];
+ ** If it is "extended" it also has these members:
+ ** BYTE initStringLen[ 2 ];
+ ** BYTE initString [ initStringLen ];
+ ** }
+ */
+
+ namesRunningOffset = namesOffset;
+ /* copy the module's common name */
+ CHECK_SIZE( namesRunningOffset + 2);
+ names = (unsigned char *)data->data;
+ len = SECMOD_GETSHORT(names+namesRunningOffset);
+
+ CHECK_SIZE( namesRunningOffset + 2 + len);
+ commonName = (char*)PORT_ArenaAlloc(arena,len+1);
+ if (commonName == NULL)
+ goto loser;
+ PORT_Memcpy(commonName, names + namesRunningOffset + 2, len);
+ commonName[len] = 0;
+ namesRunningOffset += len + 2;
+
+ /* copy the module's shared library file name. */
+ CHECK_SIZE( namesRunningOffset + 2);
+ len = SECMOD_GETSHORT(names + namesRunningOffset);
+ if (len) {
+ CHECK_SIZE( namesRunningOffset + 2 + len);
+ dllName = (char*)PORT_ArenaAlloc(arena,len + 1);
+ if (dllName == NULL)
+ goto loser;
+ PORT_Memcpy(dllName, names + namesRunningOffset + 2, len);
+ dllName[len] = 0;
+ }
+ namesRunningOffset += len + 2;
+
+ /* copy the module's initialization string, if present. */
+ if (!internal && extended) {
+ CHECK_SIZE( namesRunningOffset + 2);
+ len = SECMOD_GETSHORT(names+namesRunningOffset);
+ if (len) {
+ CHECK_SIZE( namesRunningOffset + 2 + len );
+ parameters = (char*)PORT_ArenaAlloc(arena,len + 1);
+ if (parameters == NULL)
+ goto loser;
+ PORT_Memcpy(parameters,names + namesRunningOffset + 2, len);
+ parameters[len] = 0;
+ }
+ namesRunningOffset += len + 2;
+ }
+
+ /*
+ * Consistency check: Make sure the slot and names blocks don't
+ * overlap. These blocks can occur in any order, so this check is made
+ * in 2 parts. First we check the case where the slot block starts
+ * after the name block. Later, when we have the slot block length,
+ * we check the case where slot block starts before the name block.
+ * NOTE: in most cases any overlap will likely be detected by invalid
+ * data read from the blocks, but it's better to find out sooner
+ * than later.
+ */
+ if (slotOffset >= namesOffset) { /* slot block starts after name block */
+ if (slotOffset < namesRunningOffset) {
+ goto db_loser;
+ }
+ }
+
+ /* ------------------------------------------------------------------
+ ** Part 3, process the slot table.
+ ** This part has this structure:
+ ** struct {
+ ** BYTE slotCount [ 2 ];
+ ** secmodSlotData [ slotCount ];
+ ** {
+ */
+
+ CHECK_SIZE( slotOffset + 2 );
+ slotCount = SECMOD_GETSHORT((unsigned char *)data->data + slotOffset);
+
+ /*
+ * Consistency check: Part 2. We now have the slot block length, we can
+ * check the case where the slotblock procedes the name block.
+ */
+ if (slotOffset < namesOffset) { /* slot block starts before name block */
+ if (namesOffset < slotOffset + 2 + slotCount*sizeof(secmodSlotData)) {
+ goto db_loser;
+ }
+ }
+
+ CHECK_SIZE( (slotOffset + 2 + slotCount * sizeof(secmodSlotData)));
+ slots = (secmodSlotData *) ((unsigned char *)data->data + slotOffset + 2);
+
+ /* slotCount; */
+ slotStrings = (char **)PORT_ArenaZAlloc(arena, slotCount * sizeof(char *));
+ if (slotStrings == NULL)
+ goto loser;
+ for (i=0; i < (int) slotCount; i++, slots++) {
+ PRBool hasRootCerts =PR_FALSE;
+ PRBool hasRootTrust =PR_FALSE;
+ slotID = SECMOD_GETLONG(slots->slotID);
+ defaultFlags = SECMOD_GETLONG(slots->defaultFlags);
+ timeout = SECMOD_GETLONG(slots->timeout);
+ hasRootCerts = slots->hasRootCerts;
+ if (isOldVersion && internal && (slotID != 2)) {
+ unsigned long internalFlags=
+ secmod_argSlotFlags("slotFlags",SECMOD_SLOT_FLAGS);
+ defaultFlags |= internalFlags;
+ }
+ if (hasRootCerts && !extended) {
+ trustOrder = 100;
+ }
+
+ slotStrings[i] = secmod_mkSlotString(slotID, defaultFlags, timeout,
+ (unsigned char)slots->askpw,
+ hasRootCerts, hasRootTrust);
+ if (slotStrings[i] == NULL) {
+ secmod_FreeSlotStrings(slotStrings,i);
+ goto loser;
+ }
+ }
+
+ nss = secmod_mkNSS(slotStrings, slotCount, internal, isFIPS, isModuleDB,
+ isModuleDBOnly, internal, trustOrder, cipherOrder,
+ ssl0, ssl1);
+ secmod_FreeSlotStrings(slotStrings,slotCount);
+ /* it's permissible (and normal) for nss to be NULL. it simply means
+ * there are no NSS specific parameters in the database */
+ moduleSpec = secmod_mkNewModuleSpec(dllName,commonName,parameters,nss);
+ PR_smprintf_free(nss);
+ PORT_FreeArena(arena,PR_TRUE);
+ return moduleSpec;
+
+db_loser:
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+loser:
+ PORT_FreeArena(arena,PR_TRUE);
+ return NULL;
+}
+
+static DB *
+secmod_OpenDB(const char *appName, const char *filename, const char *dbName,
+ PRBool readOnly, PRBool update)
+{
+ DB *pkcs11db = NULL;
+
+
+ if (appName) {
+ char *secname = PORT_Strdup(filename);
+ int len = strlen(secname);
+ int status = RDB_FAIL;
+
+ if (len >= 3 && PORT_Strcmp(&secname[len-3],".db") == 0) {
+ secname[len-3] = 0;
+ }
+ pkcs11db=
+ rdbopen(appName, "", secname, readOnly ? NO_RDONLY:NO_RDWR, NULL);
+ if (update && !pkcs11db) {
+ DB *updatedb;
+
+ pkcs11db = rdbopen(appName, "", secname, NO_CREATE, &status);
+ if (!pkcs11db) {
+ if (status == RDB_RETRY) {
+ pkcs11db= rdbopen(appName, "", secname,
+ readOnly ? NO_RDONLY:NO_RDWR, NULL);
+ }
+ PORT_Free(secname);
+ return pkcs11db;
+ }
+ updatedb = dbopen(dbName, NO_RDONLY, 0600, DB_HASH, 0);
+ if (updatedb) {
+ db_Copy(pkcs11db,updatedb);
+ (*updatedb->close)(updatedb);
+ } else {
+ (*pkcs11db->close)(pkcs11db);
+ PORT_Free(secname);
+ return NULL;
+ }
+ }
+ PORT_Free(secname);
+ return pkcs11db;
+ }
+
+ /* I'm sure we should do more checks here sometime... */
+ pkcs11db = dbopen(dbName, readOnly ? NO_RDONLY : NO_RDWR, 0600, DB_HASH, 0);
+
+ /* didn't exist? create it */
+ if (pkcs11db == NULL) {
+ if (readOnly)
+ return NULL;
+
+ pkcs11db = dbopen( dbName, NO_CREATE, 0600, DB_HASH, 0 );
+ if (pkcs11db)
+ (* pkcs11db->sync)(pkcs11db, 0);
+ }
+ return pkcs11db;
+}
+
+static void
+secmod_CloseDB(DB *pkcs11db)
+{
+ (*pkcs11db->close)(pkcs11db);
+}
+
+static char *
+secmod_addEscape(const char *string, char quote)
+{
+ char *newString = 0;
+ int escapes = 0, size = 0;
+ const char *src;
+ char *dest;
+
+ for (src=string; *src ; src++) {
+ if ((*src == quote) || (*src == '\\')) escapes++;
+ size++;
+ }
+
+ newString = PORT_ZAlloc(escapes+size+1);
+ if (newString == NULL) {
+ return NULL;
+ }
+
+ for (src=string, dest=newString; *src; src++,dest++) {
+ if ((*src == '\\') || (*src == quote)) {
+ *dest++ = '\\';
+ }
+ *dest = *src;
+ }
+
+ return newString;
+}
+
+SECStatus legacy_AddSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *module, PRBool rw);
+
+#define SECMOD_STEP 10
+#define SFTK_DEFAULT_INTERNAL_INIT "library= name=\"NSS Internal PKCS #11 Module\" parameters=\"%s\" NSS=\"Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={%s askpw=any timeout=30})\""
+/*
+ * Read all the existing modules in
+ */
+char **
+legacy_ReadSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *params, PRBool rw)
+{
+ DBT key,data;
+ int ret;
+ DB *pkcs11db = NULL;
+ char **moduleList = NULL, **newModuleList = NULL;
+ int moduleCount = 1;
+ int useCount = SECMOD_STEP;
+
+ moduleList = (char **) PORT_ZAlloc(useCount*sizeof(char **));
+ if (moduleList == NULL) return NULL;
+
+ pkcs11db = secmod_OpenDB(appName,filename,dbname,PR_TRUE,rw);
+ if (pkcs11db == NULL) goto done;
+
+ /* read and parse the file or data base */
+ ret = (*pkcs11db->seq)(pkcs11db, &key, &data, R_FIRST);
+ if (ret) goto done;
+
+
+ do {
+ char *moduleString;
+ PRBool internal = PR_FALSE;
+ if ((moduleCount+1) >= useCount) {
+ useCount += SECMOD_STEP;
+ newModuleList =
+ (char **)PORT_Realloc(moduleList,useCount*sizeof(char *));
+ if (newModuleList == NULL) goto done;
+ moduleList = newModuleList;
+ PORT_Memset(&moduleList[moduleCount+1],0,
+ sizeof(char *)*SECMOD_STEP);
+ }
+ moduleString = secmod_DecodeData(params,&data,&internal);
+ if (internal) {
+ moduleList[0] = moduleString;
+ } else {
+ moduleList[moduleCount] = moduleString;
+ moduleCount++;
+ }
+ } while ( (*pkcs11db->seq)(pkcs11db, &key, &data, R_NEXT) == 0);
+
+done:
+ if (!moduleList[0]) {
+ char * newparams = secmod_addEscape(params,'"');
+ if (newparams) {
+ moduleList[0] = PR_smprintf(SFTK_DEFAULT_INTERNAL_INIT,newparams,
+ SECMOD_SLOT_FLAGS);
+ PORT_Free(newparams);
+ }
+ }
+ /* deal with trust cert db here */
+
+ if (pkcs11db) {
+ secmod_CloseDB(pkcs11db);
+ } else if (moduleList[0] && rw) {
+ legacy_AddSecmodDB(appName,filename,dbname,moduleList[0], rw) ;
+ }
+ if (!moduleList[0]) {
+ PORT_Free(moduleList);
+ moduleList = NULL;
+ }
+ return moduleList;
+}
+
+SECStatus
+legacy_ReleaseSecmodDBData(const char *appName, const char *filename,
+ const char *dbname, char **moduleSpecList, PRBool rw)
+{
+ if (moduleSpecList) {
+ char **index;
+ for(index = moduleSpecList; *index; index++) {
+ PR_smprintf_free(*index);
+ }
+ PORT_Free(moduleSpecList);
+ }
+ return SECSuccess;
+}
+
+/*
+ * Delete a module from the Data Base
+ */
+SECStatus
+legacy_DeleteSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *args, PRBool rw)
+{
+ DBT key;
+ SECStatus rv = SECFailure;
+ DB *pkcs11db = NULL;
+ int ret;
+
+ if (!rw) return SECFailure;
+
+ /* make sure we have a db handle */
+ pkcs11db = secmod_OpenDB(appName,filename,dbname,PR_FALSE,PR_FALSE);
+ if (pkcs11db == NULL) {
+ return SECFailure;
+ }
+
+ rv = secmod_MakeKey(&key,args);
+ if (rv != SECSuccess) goto done;
+ rv = SECFailure;
+ ret = (*pkcs11db->del)(pkcs11db, &key, 0);
+ secmod_FreeKey(&key);
+ if (ret != 0) goto done;
+
+
+ ret = (*pkcs11db->sync)(pkcs11db, 0);
+ if (ret == 0) rv = SECSuccess;
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return rv;
+}
+
+/*
+ * Add a module to the Data base
+ */
+SECStatus
+legacy_AddSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *module, PRBool rw)
+{
+ DBT key,data;
+ SECStatus rv = SECFailure;
+ DB *pkcs11db = NULL;
+ int ret;
+
+
+ if (!rw) return SECFailure;
+
+ /* make sure we have a db handle */
+ pkcs11db = secmod_OpenDB(appName,filename,dbname,PR_FALSE,PR_FALSE);
+ if (pkcs11db == NULL) {
+ return SECFailure;
+ }
+
+ rv = secmod_MakeKey(&key,module);
+ if (rv != SECSuccess) goto done;
+ rv = secmod_EncodeData(&data,module);
+ if (rv != SECSuccess) {
+ secmod_FreeKey(&key);
+ goto done;
+ }
+ rv = SECFailure;
+ ret = (*pkcs11db->put)(pkcs11db, &key, &data, 0);
+ secmod_FreeKey(&key);
+ secmod_FreeData(&data);
+ if (ret != 0) goto done;
+
+ ret = (*pkcs11db->sync)(pkcs11db, 0);
+ if (ret == 0) rv = SECSuccess;
+
+done:
+ secmod_CloseDB(pkcs11db);
+ return rv;
+}
diff --git a/security/nss/lib/softoken/lgglue.c b/security/nss/lib/softoken/lgglue.c
new file mode 100644
index 000000000..d541fb722
--- /dev/null
+++ b/security/nss/lib/softoken/lgglue.c
@@ -0,0 +1,429 @@
+/* ***** 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. This file is written to abstract away how the modules are
+ * stored so we can deside that later.
+ */
+#include "sftkdb.h"
+#include "sftkdbti.h"
+#include "sdb.h"
+#include "prsystem.h"
+#include "prprf.h"
+#include "prenv.h"
+#include "lgglue.h"
+#include "secerr.h"
+
+static LGOpenFunc legacy_glue_open = NULL;
+static LGReadSecmodFunc legacy_glue_readSecmod = NULL;
+static LGReleaseSecmodFunc legacy_glue_releaseSecmod = NULL;
+static LGDeleteSecmodFunc legacy_glue_deleteSecmod = NULL;
+static LGAddSecmodFunc legacy_glue_addSecmod = NULL;
+static LGShutdownFunc legacy_glue_shutdown = NULL;
+
+/*
+ * The following 3 functions duplicate the work done by bl_LoadLibrary.
+ * We should make bl_LoadLibrary a global and replace the call to
+ * sftkdb_LoadLibrary(const char *libname) with it.
+ */
+#ifdef XP_UNIX
+#include <unistd.h>
+#define LG_MAX_LINKS 20
+static char *
+sftkdb_resolvePath(const char *orig)
+{
+ int count = 0;
+ int len =0;
+ int ret = -1;
+ char *resolved = NULL;
+ char *source = NULL;
+
+ len = 1025; /* MAX PATH +1*/
+ if (strlen(orig)+1 > len) {
+ /* PATH TOO LONG */
+ return NULL;
+ }
+ resolved = PORT_Alloc(len);
+ if (!resolved) {
+ return NULL;
+ }
+ source = PORT_Alloc(len);
+ if (!source) {
+ goto loser;
+ }
+ PORT_Strcpy(source, orig);
+ /* Walk down all the links */
+ while ( count++ < LG_MAX_LINKS) {
+ char *tmp;
+ /* swap our previous sorce out with resolved */
+ /* read it */
+ ret = readlink(source, resolved, len-1);
+ if (ret < 0) {
+ break;
+ }
+ resolved[ret] = 0;
+ tmp = source; source = resolved; resolved = tmp;
+ }
+ if (count > 1) {
+ ret = 0;
+ }
+loser:
+ if (resolved) {
+ PORT_Free(resolved);
+ }
+ if (ret < 0) {
+ if (source) {
+ PORT_Free(source);
+ source = NULL;
+ }
+ }
+ return source;
+}
+
+#endif
+
+static PRLibrary *
+sftkdb_LoadFromPath(const char *path, const char *libname)
+{
+ char *c;
+ int pathLen, nameLen, fullPathLen;
+ char *fullPathName = NULL;
+ PRLibSpec libSpec;
+ PRLibrary *lib = NULL;
+
+
+ /* strip of our parent's library name */
+ c = strrchr(path, PR_GetDirectorySeparator());
+ if (!c) {
+ return NULL; /* invalid path */
+ }
+ pathLen = (c-path)+1;
+ nameLen = strlen(libname);
+ fullPathLen = pathLen + nameLen +1;
+ fullPathName = (char *)PORT_Alloc(fullPathLen);
+ if (fullPathName == NULL) {
+ return NULL; /* memory allocation error */
+ }
+ PORT_Memcpy(fullPathName, path, pathLen);
+ PORT_Memcpy(fullPathName+pathLen, libname, nameLen);
+ fullPathName[fullPathLen-1] = 0;
+
+ libSpec.type = PR_LibSpec_Pathname;
+ libSpec.value.pathname = fullPathName;
+ lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL);
+ PORT_Free(fullPathName);
+ return lib;
+}
+
+static PRLibrary *
+sftkdb_LoadLibrary(const char *libname)
+{
+ PRLibrary *lib = NULL;
+ PRFuncPtr fn_addr;
+ char *parentLibPath = NULL;
+
+ fn_addr = (PRFuncPtr) &sftkdb_LoadLibrary;
+ parentLibPath = PR_GetLibraryFilePathname(SOFTOKEN_LIB_NAME, fn_addr);
+
+ if (!parentLibPath) {
+ goto done;
+ }
+
+ lib = sftkdb_LoadFromPath(parentLibPath, libname);
+#ifdef XP_UNIX
+ /* handle symbolic link case */
+ if (!lib) {
+ char *trueParentLibPath = sftkdb_resolvePath(parentLibPath);
+ if (!trueParentLibPath) {
+ goto done;
+ }
+ lib = sftkdb_LoadFromPath(trueParentLibPath, libname);
+ PORT_Free(trueParentLibPath);
+ }
+#endif
+
+done:
+ if (parentLibPath) {
+ PORT_Free(parentLibPath);
+ }
+
+ /* still couldn't load it, try the generic path */
+ if (!lib) {
+ PRLibSpec libSpec;
+ libSpec.type = PR_LibSpec_Pathname;
+ libSpec.value.pathname = libname;
+ lib = PR_LoadLibraryWithFlags(libSpec, PR_LD_NOW | PR_LD_LOCAL);
+ }
+ return lib;
+}
+
+/*
+ * stub files for legacy db's to be able to encrypt and decrypt
+ * various keys and attributes.
+ */
+static SECStatus
+sftkdb_encrypt_stub(PRArenaPool *arena, SDB *sdb, SECItem *plainText,
+ SECItem **cipherText)
+{
+ SFTKDBHandle *handle = sdb->app_private;
+ SECStatus rv;
+
+ if (handle == NULL) {
+ return SECFailure;
+ }
+
+ /* if we aren't the key handle, try the other handle */
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ handle = handle->peerDB;
+ }
+
+ /* not a key handle */
+ if (handle == NULL || handle->passwordLock == NULL) {
+ return SECFailure;
+ }
+
+ PZ_Lock(handle->passwordLock);
+ if (handle->passwordKey.data == NULL) {
+ PZ_Unlock(handle->passwordLock);
+ /* PORT_SetError */
+ return SECFailure;
+ }
+
+ rv = sftkdb_EncryptAttribute(arena,
+ handle->newKey?handle->newKey:&handle->passwordKey,
+ plainText, cipherText);
+ PZ_Unlock(handle->passwordLock);
+
+ return rv;
+}
+
+/*
+ * stub files for legacy db's to be able to encrypt and decrypt
+ * various keys and attributes.
+ */
+static SECStatus
+sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText)
+{
+ SFTKDBHandle *handle = sdb->app_private;
+ SECStatus rv;
+ SECItem *oldKey = NULL;
+
+ if (handle == NULL) {
+ return SECFailure;
+ }
+
+ /* if we aren't th handle, try the other handle */
+ oldKey = handle->oldKey;
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ handle = handle->peerDB;
+ }
+
+ /* not a key handle */
+ if (handle == NULL || handle->passwordLock == NULL) {
+ return SECFailure;
+ }
+
+ PZ_Lock(handle->passwordLock);
+ if (handle->passwordKey.data == NULL) {
+ PZ_Unlock(handle->passwordLock);
+ /* PORT_SetError */
+ return SECFailure;
+ }
+ rv = sftkdb_DecryptAttribute( oldKey ? oldKey : &handle->passwordKey,
+ cipherText, plainText);
+ PZ_Unlock(handle->passwordLock);
+
+ return rv;
+}
+
+static PRLibrary *legacy_glue_lib = NULL;
+static SECStatus
+sftkdbLoad_Legacy()
+{
+ PRLibrary *lib = NULL;
+ LGSetCryptFunc setCryptFunction = NULL;
+
+ if (legacy_glue_lib) {
+ return SECSuccess;
+ }
+
+ lib = sftkdb_LoadLibrary(SHLIB_PREFIX"nssdbm"SHLIB_VERSION"."SHLIB_SUFFIX);
+ if (lib == NULL) {
+ return SECFailure;
+ }
+
+ legacy_glue_open = (LGOpenFunc)PR_FindFunctionSymbol(lib, "legacy_Open");
+ legacy_glue_readSecmod = (LGReadSecmodFunc) PR_FindFunctionSymbol(lib,
+ "legacy_ReadSecmodDB");
+ legacy_glue_releaseSecmod = (LGReleaseSecmodFunc) PR_FindFunctionSymbol(lib,
+ "legacy_ReleaseSecmodDBData");
+ legacy_glue_deleteSecmod = (LGDeleteSecmodFunc) PR_FindFunctionSymbol(lib,
+ "legacy_DeleteSecmodDB");
+ legacy_glue_addSecmod = (LGAddSecmodFunc)PR_FindFunctionSymbol(lib,
+ "legacy_AddSecmodDB");
+ legacy_glue_shutdown = (LGShutdownFunc) PR_FindFunctionSymbol(lib,
+ "legacy_Shutdown");
+ setCryptFunction = (LGSetCryptFunc) PR_FindFunctionSymbol(lib,
+ "legacy_SetCryptFunctions");
+
+ if (!legacy_glue_open || !legacy_glue_readSecmod ||
+ !legacy_glue_releaseSecmod || !legacy_glue_deleteSecmod ||
+ !legacy_glue_addSecmod || !setCryptFunction) {
+ PR_UnloadLibrary(lib);
+ return SECFailure;
+ }
+ setCryptFunction(sftkdb_encrypt_stub,sftkdb_decrypt_stub);
+ legacy_glue_lib = lib;
+ return SECSuccess;
+}
+
+CK_RV
+sftkdbCall_open(const char *dir, const char *certPrefix, const char *keyPrefix,
+ int certVersion, int keyVersion, int flags,
+ SDB **certDB, SDB **keyDB)
+{
+ SECStatus rv;
+
+ rv = sftkdbLoad_Legacy();
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ if (!legacy_glue_open) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*legacy_glue_open)(dir, certPrefix, keyPrefix,
+ certVersion, keyVersion,
+ flags, certDB, keyDB);
+}
+
+char **
+sftkdbCall_ReadSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *params, PRBool rw)
+{
+ SECStatus rv;
+
+ rv = sftkdbLoad_Legacy();
+ if (rv != SECSuccess) {
+ return NULL;
+ }
+ if (!legacy_glue_readSecmod) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return NULL;
+ }
+ return (*legacy_glue_readSecmod)(appName, filename, dbname, params, rw);
+}
+
+SECStatus
+sftkdbCall_ReleaseSecmodDBData(const char *appName,
+ const char *filename, const char *dbname,
+ char **moduleSpecList, PRBool rw)
+{
+ SECStatus rv;
+
+ rv = sftkdbLoad_Legacy();
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ if (!legacy_glue_releaseSecmod) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*legacy_glue_releaseSecmod)(appName, filename, dbname,
+ moduleSpecList, rw);
+}
+
+SECStatus
+sftkdbCall_DeleteSecmodDB(const char *appName,
+ const char *filename, const char *dbname,
+ char *args, PRBool rw)
+{
+ SECStatus rv;
+
+ rv = sftkdbLoad_Legacy();
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ if (!legacy_glue_deleteSecmod) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*legacy_glue_deleteSecmod)(appName, filename, dbname, args, rw);
+}
+
+SECStatus
+sftkdbCall_AddSecmodDB(const char *appName,
+ const char *filename, const char *dbname,
+ char *module, PRBool rw)
+{
+ SECStatus rv;
+
+ rv = sftkdbLoad_Legacy();
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ if (!legacy_glue_addSecmod) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+ return (*legacy_glue_addSecmod)(appName, filename, dbname, module, rw);
+}
+
+CK_RV
+sftkdbCall_Shutdown(void)
+{
+ CK_RV crv = CKR_OK;
+ char *disableUnload = NULL;
+ if (!legacy_glue_lib) {
+ return CKR_OK;
+ }
+ if (legacy_glue_shutdown) {
+ crv = (*legacy_glue_shutdown)();
+ }
+ disableUnload = PR_GetEnv("NSS_DISABLE_UNLOAD");
+ if (!disableUnload) {
+ PR_UnloadLibrary(legacy_glue_lib);
+ }
+ legacy_glue_lib = NULL;
+ legacy_glue_open = NULL;
+ legacy_glue_readSecmod = NULL;
+ legacy_glue_releaseSecmod = NULL;
+ legacy_glue_deleteSecmod = NULL;
+ legacy_glue_addSecmod = NULL;
+ return crv;
+}
+
+
diff --git a/security/nss/lib/softoken/lgglue.h b/security/nss/lib/softoken/lgglue.h
new file mode 100644
index 000000000..8ced909d3
--- /dev/null
+++ b/security/nss/lib/softoken/lgglue.h
@@ -0,0 +1,92 @@
+/* ***** 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 ***** */
+/*
+ * This code defines the glue layer between softoken and the legacy DB library
+ */
+#include "sdb.h"
+
+/*
+ * function prototypes for the callbacks into softoken from the legacyDB
+ */
+
+typedef SECStatus (*LGEncryptFunc)(PRArenaPool *arena, SDB *sdb,
+ SECItem *plainText, SECItem **cipherText);
+typedef SECStatus (*LGDecryptFunc)(SDB *sdb, SECItem *cipherText,
+ SECItem **plainText);
+
+/*
+ * function prototypes for the exported functions.
+ */
+typedef CK_RV (*LGOpenFunc) (const char *dir, const char *certPrefix,
+ const char *keyPrefix,
+ int certVersion, int keyVersion, int flags,
+ SDB **certDB, SDB **keyDB);
+typedef char ** (*LGReadSecmodFunc)(const char *appName,
+ const char *filename,
+ const char *dbname, char *params, PRBool rw);
+typedef SECStatus (*LGReleaseSecmodFunc)(const char *appName,
+ const char *filename,
+ const char *dbname, char **params, PRBool rw);
+typedef SECStatus (*LGDeleteSecmodFunc)(const char *appName,
+ const char *filename,
+ const char *dbname, char *params, PRBool rw);
+typedef SECStatus (*LGAddSecmodFunc)(const char *appName,
+ const char *filename,
+ const char *dbname, char *params, PRBool rw);
+typedef SECStatus (*LGShutdownFunc)(void);
+typedef void (*LGSetCryptFunc)(LGEncryptFunc, LGDecryptFunc);
+
+
+/*
+ * Softoken Glue Functions
+ */
+CK_RV sftkdbCall_open(const char *dir, const char *certPrefix,
+ const char *keyPrefix,
+ int certVersion, int keyVersion, int flags,
+ SDB **certDB, SDB **keyDB);
+char ** sftkdbCall_ReadSecmodDB(const char *appName, const char *filename,
+ const char *dbname, char *params, PRBool rw);
+SECStatus sftkdbCall_ReleaseSecmodDBData(const char *appName,
+ const char *filename, const char *dbname,
+ char **moduleSpecList, PRBool rw);
+SECStatus sftkdbCall_DeleteSecmodDB(const char *appName,
+ const char *filename, const char *dbname,
+ char *args, PRBool rw);
+SECStatus sftkdbCall_AddSecmodDB(const char *appName,
+ const char *filename, const char *dbname,
+ char *module, PRBool rw);
+CK_RV sftkdbCall_Shutdown(void);
+
diff --git a/security/nss/lib/softoken/lowkey.c b/security/nss/lib/softoken/lowkey.c
new file mode 100644
index 000000000..4174cef63
--- /dev/null
+++ b/security/nss/lib/softoken/lowkey.c
@@ -0,0 +1,525 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 "lowkeyi.h"
+#include "secoid.h"
+#include "secitem.h"
+#include "secder.h"
+#include "base64.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+#ifdef NSS_ENABLE_ECC
+#include "softoken.h"
+#endif
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_BitStringTemplate)
+SEC_ASN1_MKSUB(SEC_ObjectIDTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+const SEC_ASN1Template nsslowkey_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 }
+};
+
+const SEC_ASN1Template nsslowkey_SetOfAttributeTemplate[] = {
+ { SEC_ASN1_SET_OF, 0, nsslowkey_AttributeTemplate },
+};
+/* ASN1 Templates for new decoder/encoder */
+const SEC_ASN1Template nsslowkey_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),
+ nsslowkey_SetOfAttributeTemplate },
+ { 0 }
+};
+
+const SEC_ASN1Template nsslowkey_PQGParamsTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PQGParams) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,prime) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,subPrime) },
+ { SEC_ASN1_INTEGER, offsetof(PQGParams,base) },
+ { 0, }
+};
+
+const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.version) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.modulus) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.publicExponent) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.privateExponent) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.prime1) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.prime2) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.exponent1) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.exponent2) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.rsa.coefficient) },
+ { 0 }
+};
+
+
+const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.publicValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.privateValue) },
+ { 0, }
+};
+
+const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[] = {
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dsa.privateValue) },
+};
+
+const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.publicValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.privateValue) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.base) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.dh.prime) },
+ { 0, }
+};
+
+#ifdef NSS_ENABLE_ECC
+
+/* XXX This is just a placeholder for later when we support
+ * generic curves and need full-blown support for parsing EC
+ * parameters. For now, we only support named curves in which
+ * EC params are simply encoded as an object ID and we don't
+ * use nsslowkey_ECParamsTemplate.
+ */
+const SEC_ASN1Template nsslowkey_ECParamsTemplate[] = {
+ { SEC_ASN1_CHOICE, offsetof(ECParams,type), NULL, sizeof(ECParams) },
+ { SEC_ASN1_OBJECT_ID, offsetof(ECParams,curveOID), NULL, ec_params_named },
+ { 0, }
+};
+
+
+/* NOTE: The SECG specification allows the private key structure
+ * to contain curve parameters but recommends that they be stored
+ * in the PrivateKeyAlgorithmIdentifier field of the PrivateKeyInfo
+ * instead.
+ */
+const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[] = {
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYPrivateKey) },
+ { SEC_ASN1_INTEGER, offsetof(NSSLOWKEYPrivateKey,u.ec.version) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.privateValue) },
+ /* XXX The following template works for now since we only
+ * support named curves for which the parameters are
+ * encoded as an object ID. When we support generic curves,
+ * we'll need to define nsslowkey_ECParamsTemplate
+ */
+#if 1
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 0,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams.curveOID),
+ SEC_ASN1_SUB(SEC_ObjectIDTemplate) },
+#else
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.ecParams),
+ nsslowkey_ECParamsTemplate },
+#endif
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC |
+ SEC_ASN1_XTRN | 1,
+ offsetof(NSSLOWKEYPrivateKey,u.ec.publicValue),
+ SEC_ASN1_SUB(SEC_BitStringTemplate) },
+ { 0, }
+};
+#endif /* NSS_ENABLE_ECC */
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+
+void
+prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *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;
+}
+
+void
+prepare_low_pqg_params_for_asn1(PQGParams *params)
+{
+ params->prime.type = siUnsignedInteger;
+ params->subPrime.type = siUnsignedInteger;
+ params->base.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dsa.publicValue.type = siUnsignedInteger;
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+ key->u.dsa.params.prime.type = siUnsignedInteger;
+ key->u.dsa.params.subPrime.type = siUnsignedInteger;
+ key->u.dsa.params.base.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dsa.privateValue.type = siUnsignedInteger;
+}
+
+void
+prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.dh.prime.type = siUnsignedInteger;
+ key->u.dh.base.type = siUnsignedInteger;
+ key->u.dh.publicValue.type = siUnsignedInteger;
+ key->u.dh.privateValue.type = siUnsignedInteger;
+}
+
+#ifdef NSS_ENABLE_ECC
+void
+prepare_low_ecparams_for_asn1(ECParams *params)
+{
+ params->DEREncoding.type = siUnsignedInteger;
+ params->curveOID.type = siUnsignedInteger;
+}
+
+void
+prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key)
+{
+ key->u.ec.version.type = siUnsignedInteger;
+ key->u.ec.ecParams.DEREncoding.type = siUnsignedInteger;
+ key->u.ec.ecParams.curveOID.type = siUnsignedInteger;
+ key->u.ec.privateValue.type = siUnsignedInteger;
+ key->u.ec.publicValue.type = siUnsignedInteger;
+}
+#endif /* NSS_ENABLE_ECC */
+
+void
+nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *privk)
+{
+ if (privk && privk->arena) {
+ PORT_FreeArena(privk->arena, PR_TRUE);
+ }
+}
+
+void
+nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *pubk)
+{
+ if (pubk && pubk->arena) {
+ PORT_FreeArena(pubk->arena, PR_FALSE);
+ }
+}
+unsigned
+nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubk)
+{
+ unsigned char b0;
+
+ /* interpret modulus length as key strength... in
+ * fortezza that's the public key length */
+
+ switch (pubk->keyType) {
+ case NSSLOWKEYRSAKey:
+ b0 = pubk->u.rsa.modulus.data[0];
+ return b0 ? pubk->u.rsa.modulus.len : pubk->u.rsa.modulus.len - 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+unsigned
+nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privk)
+{
+
+ unsigned char b0;
+
+ switch (privk->keyType) {
+ case NSSLOWKEYRSAKey:
+ b0 = privk->u.rsa.modulus.data[0];
+ return b0 ? privk->u.rsa.modulus.len : privk->u.rsa.modulus.len - 1;
+ default:
+ break;
+ }
+ return 0;
+}
+
+NSSLOWKEYPublicKey *
+nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk)
+{
+ NSSLOWKEYPublicKey *pubk;
+ PLArenaPool *arena;
+
+
+ arena = PORT_NewArena (DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ switch(privk->keyType) {
+ case NSSLOWKEYRSAKey:
+ case NSSLOWKEYNullKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof (NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ if (privk->keyType == NSSLOWKEYNullKey) return pubk;
+ rv = SECITEM_CopyItem(arena, &pubk->u.rsa.modulus,
+ &privk->u.rsa.modulus);
+ if (rv == SECSuccess) {
+ rv = SECITEM_CopyItem (arena, &pubk->u.rsa.publicExponent,
+ &privk->u.rsa.publicExponent);
+ if (rv == SECSuccess)
+ return pubk;
+ }
+ } else {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ }
+ break;
+ case NSSLOWKEYDSAKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.publicValue,
+ &privk->u.dsa.publicValue);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.prime,
+ &privk->u.dsa.params.prime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.subPrime,
+ &privk->u.dsa.params.subPrime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dsa.params.base,
+ &privk->u.dsa.params.base);
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+ case NSSLOWKEYDHKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.publicValue,
+ &privk->u.dh.publicValue);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.prime,
+ &privk->u.dh.prime);
+ if (rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(arena, &pubk->u.dh.base,
+ &privk->u.dh.base);
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ pubk = (NSSLOWKEYPublicKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPublicKey));
+ if (pubk != NULL) {
+ SECStatus rv;
+
+ pubk->arena = arena;
+ pubk->keyType = privk->keyType;
+ rv = SECITEM_CopyItem(arena, &pubk->u.ec.publicValue,
+ &privk->u.ec.publicValue);
+ if (rv != SECSuccess) break;
+ pubk->u.ec.ecParams.arena = arena;
+ /* Copy the rest of the params */
+ rv = EC_CopyParams(arena, &(pubk->u.ec.ecParams),
+ &(privk->u.ec.ecParams));
+ if (rv == SECSuccess) return pubk;
+ }
+ break;
+#endif /* NSS_ENABLE_ECC */
+ /* No Fortezza in Low Key implementations (Fortezza keys aren't
+ * stored in our data base */
+ default:
+ break;
+ }
+
+ PORT_FreeArena (arena, PR_FALSE);
+ return NULL;
+}
+
+NSSLOWKEYPrivateKey *
+nsslowkey_CopyPrivateKey(NSSLOWKEYPrivateKey *privKey)
+{
+ NSSLOWKEYPrivateKey *returnKey = NULL;
+ SECStatus rv = SECFailure;
+ PLArenaPool *poolp;
+
+ if(!privKey) {
+ return NULL;
+ }
+
+ poolp = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if(!poolp) {
+ return NULL;
+ }
+
+ returnKey = (NSSLOWKEYPrivateKey*)PORT_ArenaZAlloc(poolp, sizeof(NSSLOWKEYPrivateKey));
+ if(!returnKey) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ returnKey->keyType = privKey->keyType;
+ returnKey->arena = poolp;
+
+ switch(privKey->keyType) {
+ case NSSLOWKEYRSAKey:
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.modulus),
+ &(privKey->u.rsa.modulus));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.version),
+ &(privKey->u.rsa.version));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.publicExponent),
+ &(privKey->u.rsa.publicExponent));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.privateExponent),
+ &(privKey->u.rsa.privateExponent));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.prime1),
+ &(privKey->u.rsa.prime1));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.prime2),
+ &(privKey->u.rsa.prime2));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.exponent1),
+ &(privKey->u.rsa.exponent1));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.exponent2),
+ &(privKey->u.rsa.exponent2));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.rsa.coefficient),
+ &(privKey->u.rsa.coefficient));
+ if(rv != SECSuccess) break;
+ break;
+ case NSSLOWKEYDSAKey:
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.publicValue),
+ &(privKey->u.dsa.publicValue));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.privateValue),
+ &(privKey->u.dsa.privateValue));
+ if(rv != SECSuccess) break;
+ returnKey->u.dsa.params.arena = poolp;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.params.prime),
+ &(privKey->u.dsa.params.prime));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.params.subPrime),
+ &(privKey->u.dsa.params.subPrime));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dsa.params.base),
+ &(privKey->u.dsa.params.base));
+ if(rv != SECSuccess) break;
+ break;
+ case NSSLOWKEYDHKey:
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.publicValue),
+ &(privKey->u.dh.publicValue));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.privateValue),
+ &(privKey->u.dh.privateValue));
+ if(rv != SECSuccess) break;
+ returnKey->u.dsa.params.arena = poolp;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.prime),
+ &(privKey->u.dh.prime));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.dh.base),
+ &(privKey->u.dh.base));
+ if(rv != SECSuccess) break;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.version),
+ &(privKey->u.ec.version));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.publicValue),
+ &(privKey->u.ec.publicValue));
+ if(rv != SECSuccess) break;
+ rv = SECITEM_CopyItem(poolp, &(returnKey->u.ec.privateValue),
+ &(privKey->u.ec.privateValue));
+ if(rv != SECSuccess) break;
+ returnKey->u.ec.ecParams.arena = poolp;
+ /* Copy the rest of the params */
+ rv = EC_CopyParams(poolp, &(returnKey->u.ec.ecParams),
+ &(privKey->u.ec.ecParams));
+ if (rv != SECSuccess) break;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ rv = SECFailure;
+ }
+
+loser:
+
+ if(rv != SECSuccess) {
+ PORT_FreeArena(poolp, PR_TRUE);
+ returnKey = NULL;
+ }
+
+ return returnKey;
+}
diff --git a/security/nss/lib/softoken/lowkeyi.h b/security/nss/lib/softoken/lowkeyi.h
new file mode 100644
index 000000000..9c13bfa10
--- /dev/null
+++ b/security/nss/lib/softoken/lowkeyi.h
@@ -0,0 +1,108 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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$ */
+
+#ifndef _LOWKEYI_H_
+#define _LOWKEYI_H_
+
+#include "prtypes.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "lowkeyti.h"
+
+SEC_BEGIN_PROTOS
+
+/*
+ * See bugzilla bug 125359
+ * Since NSS (via PKCS#11) wants to handle big integers as unsigned ints,
+ * all of the templates above that en/decode into integers must be converted
+ * from ASN.1's signed integer type. This is done by marking either the
+ * source or destination (encoding or decoding, respectively) type as
+ * siUnsignedInteger.
+ */
+extern void prepare_low_rsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_pqg_params_for_asn1(PQGParams *params);
+extern void prepare_low_dsa_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_dsa_priv_key_export_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_dh_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+#ifdef NSS_ENABLE_ECC
+extern void prepare_low_ec_priv_key_for_asn1(NSSLOWKEYPrivateKey *key);
+extern void prepare_low_ecparams_for_asn1(ECParams *params);
+#endif /* NSS_ENABLE_ECC */
+
+/*
+** Destroy a private key object.
+** "key" the object
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void nsslowkey_DestroyPrivateKey(NSSLOWKEYPrivateKey *key);
+
+/*
+** Destroy a public key object.
+** "key" the object
+** "freeit" if PR_TRUE then free the object as well as its sub-objects
+*/
+extern void nsslowkey_DestroyPublicKey(NSSLOWKEYPublicKey *key);
+
+/*
+** Return the modulus length of "pubKey".
+*/
+extern unsigned int nsslowkey_PublicModulusLen(NSSLOWKEYPublicKey *pubKey);
+
+
+/*
+** Return the modulus length of "privKey".
+*/
+extern unsigned int nsslowkey_PrivateModulusLen(NSSLOWKEYPrivateKey *privKey);
+
+
+/*
+** Convert a low private key "privateKey" into a public low key
+*/
+extern NSSLOWKEYPublicKey
+ *nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privateKey);
+
+/* Make a copy of a low private key in it's own arena.
+ * a return of NULL indicates an error.
+ */
+extern NSSLOWKEYPrivateKey *
+nsslowkey_CopyPrivateKey(NSSLOWKEYPrivateKey *privKey);
+
+
+SEC_END_PROTOS
+
+#endif /* _LOWKEYI_H_ */
diff --git a/security/nss/lib/softoken/lowkeyti.h b/security/nss/lib/softoken/lowkeyti.h
new file mode 100644
index 000000000..15bb9730a
--- /dev/null
+++ b/security/nss/lib/softoken/lowkeyti.h
@@ -0,0 +1,127 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 _LOWKEYTI_H_
+#define _LOWKEYTI_H_ 1
+
+#include "blapit.h"
+#include "prtypes.h"
+#include "plarena.h"
+#include "secitem.h"
+#include "secasn1t.h"
+#include "secoidt.h"
+
+/*
+** Typedef for callback to get a password "key".
+*/
+extern const SEC_ASN1Template nsslowkey_PQGParamsTemplate[];
+extern const SEC_ASN1Template nsslowkey_RSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DSAPrivateKeyExportTemplate[];
+extern const SEC_ASN1Template nsslowkey_DHPrivateKeyTemplate[];
+extern const SEC_ASN1Template nsslowkey_DHPrivateKeyExportTemplate[];
+#ifdef NSS_ENABLE_ECC
+#define NSSLOWKEY_EC_PRIVATE_KEY_VERSION 1 /* as per SECG 1 C.4 */
+extern const SEC_ASN1Template nsslowkey_ECParamsTemplate[];
+extern const SEC_ASN1Template nsslowkey_ECPrivateKeyTemplate[];
+#endif /* NSS_ENABLE_ECC */
+
+extern const SEC_ASN1Template nsslowkey_PrivateKeyInfoTemplate[];
+extern const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[];
+
+/*
+ * PKCS #8 attributes
+ */
+struct NSSLOWKEYAttributeStr {
+ SECItem attrType;
+ SECItem *attrValue;
+};
+typedef struct NSSLOWKEYAttributeStr NSSLOWKEYAttribute;
+
+/*
+** A PKCS#8 private key info object
+*/
+struct NSSLOWKEYPrivateKeyInfoStr {
+ PLArenaPool *arena;
+ SECItem version;
+ SECAlgorithmID algorithm;
+ SECItem privateKey;
+ NSSLOWKEYAttribute **attributes;
+};
+typedef struct NSSLOWKEYPrivateKeyInfoStr NSSLOWKEYPrivateKeyInfo;
+#define NSSLOWKEY_PRIVATE_KEY_INFO_VERSION 0 /* what we *create* */
+
+typedef enum {
+ NSSLOWKEYNullKey = 0,
+ NSSLOWKEYRSAKey = 1,
+ NSSLOWKEYDSAKey = 2,
+ NSSLOWKEYDHKey = 4,
+ NSSLOWKEYECKey = 5
+} NSSLOWKEYType;
+
+/*
+** An RSA public key object.
+*/
+struct NSSLOWKEYPublicKeyStr {
+ PLArenaPool *arena;
+ NSSLOWKEYType keyType ;
+ union {
+ RSAPublicKey rsa;
+ DSAPublicKey dsa;
+ DHPublicKey dh;
+ ECPublicKey ec;
+ } u;
+};
+typedef struct NSSLOWKEYPublicKeyStr NSSLOWKEYPublicKey;
+
+/*
+** 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 NSSLOWKEYPrivateKeyStr {
+ PLArenaPool *arena;
+ NSSLOWKEYType keyType;
+ union {
+ RSAPrivateKey rsa;
+ DSAPrivateKey dsa;
+ DHPrivateKey dh;
+ ECPrivateKey ec;
+ } u;
+};
+typedef struct NSSLOWKEYPrivateKeyStr NSSLOWKEYPrivateKey;
+
+#endif /* _LOWKEYTI_H_ */
diff --git a/security/nss/lib/softoken/lowpbe.c b/security/nss/lib/softoken/lowpbe.c
new file mode 100644
index 000000000..f91839836
--- /dev/null
+++ b/security/nss/lib/softoken/lowpbe.c
@@ -0,0 +1,1410 @@
+/* ***** 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 ***** */
+
+#include "plarena.h"
+
+#include "seccomon.h"
+#include "secitem.h"
+#include "secport.h"
+#include "hasht.h"
+#include "pkcs11t.h"
+#include "blapi.h"
+#include "hasht.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "lowpbe.h"
+#include "secoid.h"
+#include "alghmac.h"
+#include "softoken.h"
+#include "secerr.h"
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+/* template for PKCS 5 PBE Parameter. This template has been expanded
+ * based upon the additions in PKCS 12. This should eventually be moved
+ * if RSA updates PKCS 5.
+ */
+static const SEC_ASN1Template NSSPKCS5PBEParameterTemplate[] =
+{
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(NSSPKCS5PBEParameter) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(NSSPKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER,
+ offsetof(NSSPKCS5PBEParameter, iteration) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSPKCS5PKCS12V2PBEParameterTemplate[] =
+{
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSPKCS5PBEParameter) },
+ { SEC_ASN1_OCTET_STRING, offsetof(NSSPKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, iteration) },
+ { 0 }
+};
+
+
+/* PKCS5 v2 */
+
+struct nsspkcs5V2PBEParameterStr {
+ SECAlgorithmID keyParams; /* parameters of the key generation */
+ SECAlgorithmID algParams; /* paramters for the encryption or mac op */
+};
+
+typedef struct nsspkcs5V2PBEParameterStr nsspkcs5V2PBEParameter;
+#define PBKDF2
+
+#ifdef PBKDF2
+static const SEC_ASN1Template NSSPKCS5V2PBES2ParameterTemplate[] =
+{
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(nsspkcs5V2PBEParameter) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(nsspkcs5V2PBEParameter, keyParams),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(nsspkcs5V2PBEParameter, algParams),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { 0 }
+};
+
+static const SEC_ASN1Template NSSPKCS5V2PBEParameterTemplate[] =
+{
+ { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSPKCS5PBEParameter) },
+ /* this is really a choice, but since we don't understand any other
+ *choice, just inline it. */
+ { SEC_ASN1_OCTET_STRING, offsetof(NSSPKCS5PBEParameter, salt) },
+ { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, iteration) },
+ { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, keyLength) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+ offsetof(NSSPKCS5PBEParameter, prfAlg),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { 0 }
+};
+#endif
+
+SECStatus
+nsspkcs5_HashBuf(const SECHashObject *hashObj, unsigned char *dest,
+ unsigned char *src, int len)
+{
+ void *ctx;
+ unsigned int retLen;
+
+ ctx = hashObj->create();
+ if(ctx == NULL) {
+ return SECFailure;
+ }
+ hashObj->begin(ctx);
+ hashObj->update(ctx, src, len);
+ hashObj->end(ctx, dest, &retLen, hashObj->length);
+ hashObj->destroy(ctx, PR_TRUE);
+ return SECSuccess;
+}
+
+/* generate bits using any hash
+ */
+static SECItem *
+nsspkcs5_PBKDF1(const SECHashObject *hashObj, SECItem *salt, SECItem *pwd,
+ int iter, PRBool faulty3DES)
+{
+ SECItem *hash = NULL, *pre_hash = NULL;
+ SECStatus rv = SECFailure;
+
+ if((salt == NULL) || (pwd == NULL) || (iter < 0)) {
+ return NULL;
+ }
+
+ hash = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ pre_hash = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+
+ if((hash != NULL) && (pre_hash != NULL)) {
+ int i, ph_len;
+
+ ph_len = hashObj->length;
+ if((salt->len + pwd->len) > hashObj->length) {
+ ph_len = salt->len + pwd->len;
+ }
+
+ rv = SECFailure;
+
+ /* allocate buffers */
+ hash->len = hashObj->length;
+ hash->data = (unsigned char *)PORT_ZAlloc(hash->len);
+ pre_hash->data = (unsigned char *)PORT_ZAlloc(ph_len);
+
+ /* in pbeSHA1TripleDESCBC there was an allocation error that made
+ * it into the caller. We do not want to propagate those errors
+ * further, so we are doing it correctly, but reading the old method.
+ */
+ if (faulty3DES) {
+ pre_hash->len = ph_len;
+ } else {
+ pre_hash->len = salt->len + pwd->len;
+ }
+
+ /* preform hash */
+ if ((hash->data != NULL) && (pre_hash->data != NULL)) {
+ rv = SECSuccess;
+ /* check for 0 length password */
+ if(pwd->len > 0) {
+ PORT_Memcpy(pre_hash->data, pwd->data, pwd->len);
+ }
+ if(salt->len > 0) {
+ PORT_Memcpy((pre_hash->data+pwd->len), salt->data, salt->len);
+ }
+ for(i = 0; ((i < iter) && (rv == SECSuccess)); i++) {
+ rv = nsspkcs5_HashBuf(hashObj, hash->data,
+ pre_hash->data, pre_hash->len);
+ if(rv != SECFailure) {
+ pre_hash->len = hashObj->length;
+ PORT_Memcpy(pre_hash->data, hash->data, hashObj->length);
+ }
+ }
+ }
+ }
+
+ if(pre_hash != NULL) {
+ SECITEM_FreeItem(pre_hash, PR_TRUE);
+ }
+
+ if((rv != SECSuccess) && (hash != NULL)) {
+ SECITEM_FreeItem(hash, PR_TRUE);
+ hash = NULL;
+ }
+
+ return hash;
+}
+
+/* this bit generation routine is described in PKCS 12 and the proposed
+ * extensions to PKCS 5. an initial hash is generated following the
+ * instructions laid out in PKCS 5. If the number of bits generated is
+ * insufficient, then the method discussed in the proposed extensions to
+ * PKCS 5 in PKCS 12 are used. This extension makes use of the HMAC
+ * function. And the P_Hash function from the TLS standard.
+ */
+static SECItem *
+nsspkcs5_PFXPBE(const SECHashObject *hashObj, NSSPKCS5PBEParameter *pbe_param,
+ SECItem *init_hash, unsigned int bytes_needed)
+{
+ SECItem *ret_bits = NULL;
+ int hash_size = 0;
+ unsigned int i;
+ unsigned int hash_iter;
+ unsigned int dig_len;
+ SECStatus rv = SECFailure;
+ unsigned char *state = NULL;
+ unsigned int state_len;
+ HMACContext *cx = NULL;
+
+ hash_size = hashObj->length;
+ hash_iter = (bytes_needed + (unsigned int)hash_size - 1) / hash_size;
+
+ /* allocate return buffer */
+ ret_bits = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(ret_bits == NULL)
+ return NULL;
+ ret_bits->data = (unsigned char *)PORT_ZAlloc((hash_iter * hash_size) + 1);
+ ret_bits->len = (hash_iter * hash_size);
+ if(ret_bits->data == NULL) {
+ PORT_Free(ret_bits);
+ return NULL;
+ }
+
+ /* allocate intermediate hash buffer. 8 is for the 8 bytes of
+ * data which are added based on iteration number
+ */
+
+ if ((unsigned int)hash_size > pbe_param->salt.len) {
+ state_len = hash_size;
+ } else {
+ state_len = pbe_param->salt.len;
+ }
+ state = (unsigned char *)PORT_ZAlloc(state_len);
+ if(state == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ if(pbe_param->salt.len > 0) {
+ PORT_Memcpy(state, pbe_param->salt.data, pbe_param->salt.len);
+ }
+
+ cx = HMAC_Create(hashObj, init_hash->data, init_hash->len, PR_TRUE);
+ if (cx == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ for(i = 0; i < hash_iter; i++) {
+
+ /* generate output bits */
+ HMAC_Begin(cx);
+ HMAC_Update(cx, state, state_len);
+ HMAC_Update(cx, pbe_param->salt.data, pbe_param->salt.len);
+ rv = HMAC_Finish(cx, ret_bits->data + (i * hash_size),
+ &dig_len, hash_size);
+ if (rv != SECSuccess)
+ goto loser;
+ PORT_Assert((unsigned int)hash_size == dig_len);
+
+ /* generate new state */
+ HMAC_Begin(cx);
+ HMAC_Update(cx, state, state_len);
+ rv = HMAC_Finish(cx, state, &state_len, state_len);
+ if (rv != SECSuccess)
+ goto loser;
+ PORT_Assert(state_len == dig_len);
+ }
+
+loser:
+ if (state != NULL)
+ PORT_ZFree(state, state_len);
+ HMAC_Destroy(cx, PR_TRUE);
+
+ if(rv != SECSuccess) {
+ SECITEM_ZfreeItem(ret_bits, PR_TRUE);
+ ret_bits = NULL;
+ }
+
+ return ret_bits;
+}
+
+/* generate bits for the key and iv determination. if enough bits
+ * are not generated using PKCS 5, then we need to generate more bits
+ * based on the extension proposed in PKCS 12
+ */
+static SECItem *
+nsspkcs5_PBKDF1Extended(const SECHashObject *hashObj,
+ NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, PRBool faulty3DES)
+{
+ SECItem * hash = NULL;
+ SECItem * newHash = NULL;
+ int bytes_needed;
+ int bytes_available;
+
+ bytes_needed = pbe_param->ivLen + pbe_param->keyLen;
+ bytes_available = hashObj->length;
+
+ hash = nsspkcs5_PBKDF1(hashObj, &pbe_param->salt, pwitem,
+ pbe_param->iter, faulty3DES);
+
+ if(hash == NULL) {
+ return NULL;
+ }
+
+ if(bytes_needed <= bytes_available) {
+ return hash;
+ }
+
+ newHash = nsspkcs5_PFXPBE(hashObj, pbe_param, hash, bytes_needed);
+ if (hash != newHash)
+ SECITEM_FreeItem(hash, PR_TRUE);
+ return newHash;
+}
+
+#ifdef PBKDF2
+
+/*
+ * PBDKDF2 is PKCS #5 v2.0 it's currently not used by NSS
+ */
+static void
+do_xor(unsigned char *dest, unsigned char *src, int len)
+{
+ /* use byt xor, not all platforms are happy about inaligned
+ * integer fetches */
+ while (len--) {
+ *dest = *dest ^ *src;
+ dest++;
+ src++;
+ }
+}
+
+static SECStatus
+nsspkcs5_PBKFD2_F(const SECHashObject *hashobj, SECItem *pwitem, SECItem *salt,
+ int iterations, unsigned int i, unsigned char *T)
+{
+ int j;
+ HMACContext *cx = NULL;
+ unsigned int hLen = hashobj->length;
+ SECStatus rv = SECFailure;
+ unsigned char *last = NULL;
+ unsigned int lastLength = salt->len + 4;
+ unsigned int lastBufLength;
+
+ cx=HMAC_Create(hashobj,pwitem->data,pwitem->len,PR_FALSE);
+ if (cx == NULL) {
+ goto loser;
+ }
+ PORT_Memset(T,0,hLen);
+ lastBufLength = PR_MAX(lastLength, hLen);
+ last = PORT_Alloc(lastBufLength);
+ if (last == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(last,salt->data,salt->len);
+ last[salt->len ] = (i >> 24) & 0xff;
+ last[salt->len+1] = (i >> 16) & 0xff;
+ last[salt->len+2] = (i >> 8) & 0xff;
+ last[salt->len+3] = i & 0xff;
+
+ /* NOTE: we need at least one iteration to return success! */
+ for (j=0; j < iterations; j++) {
+ HMAC_Begin(cx);
+ HMAC_Update(cx,last,lastLength);
+ rv =HMAC_Finish(cx,last,&lastLength,hLen);
+ if (rv !=SECSuccess) {
+ break;
+ }
+ do_xor(T,last,hLen);
+ }
+loser:
+ if (cx) {
+ HMAC_Destroy(cx, PR_TRUE);
+ }
+ if (last) {
+ PORT_ZFree(last,lastBufLength);
+ }
+ return rv;
+}
+
+static SECItem *
+nsspkcs5_PBKDF2(const SECHashObject *hashobj, NSSPKCS5PBEParameter *pbe_param,
+ SECItem *pwitem)
+{
+ int iterations = pbe_param->iter;
+ int bytesNeeded = pbe_param->keyLen;
+ unsigned int dkLen = bytesNeeded;
+ unsigned int hLen = hashobj->length;
+ unsigned int nblocks = (dkLen+hLen-1) / hLen;
+ unsigned int i;
+ unsigned char *rp;
+ unsigned char *T = NULL;
+ SECItem *result = NULL;
+ SECItem *salt = &pbe_param->salt;
+ SECStatus rv = SECFailure;
+
+ result = SECITEM_AllocItem(NULL,NULL,nblocks*hLen);
+ if (result == NULL) {
+ return NULL;
+ }
+
+ T = PORT_Alloc(hLen);
+ if (T == NULL) {
+ goto loser;
+ }
+
+ for (i=1,rp=result->data; i <= nblocks ; i++, rp +=hLen) {
+ rv = nsspkcs5_PBKFD2_F(hashobj,pwitem,salt,iterations,i,T);
+ if (rv != SECSuccess) {
+ break;
+ }
+ PORT_Memcpy(rp,T,hLen);
+ }
+
+loser:
+ if (T) {
+ PORT_ZFree(T,hLen);
+ }
+ if (rv != SECSuccess) {
+ SECITEM_FreeItem(result,PR_TRUE);
+ result = NULL;
+ } else {
+ result->len = dkLen;
+ }
+
+ return result;
+}
+#endif
+
+#define HMAC_BUFFER 64
+#define NSSPBE_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+#define NSSPBE_MIN(x,y) ((x) < (y) ? (x) : (y))
+/*
+ * This is the extended PBE function defined by the final PKCS #12 spec.
+ */
+static SECItem *
+nsspkcs5_PKCS12PBE(const SECHashObject *hashObject,
+ NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
+ PBEBitGenID bitGenPurpose, unsigned int bytesNeeded)
+{
+ PRArenaPool *arena = NULL;
+ unsigned int SLen,PLen;
+ unsigned int hashLength = hashObject->length;
+ unsigned char *S, *P;
+ SECItem *A = NULL, B, D, I;
+ SECItem *salt = &pbe_param->salt;
+ unsigned int c,i = 0;
+ unsigned int hashLen;
+ int iter;
+ unsigned char *iterBuf;
+ void *hash = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if(!arena) {
+ return NULL;
+ }
+
+ /* how many hash object lengths are needed */
+ c = (bytesNeeded + (hashLength-1))/hashLength;
+
+ /* initialize our buffers */
+ D.len = HMAC_BUFFER;
+ /* B and D are the same length, use one alloc go get both */
+ D.data = (unsigned char*)PORT_ArenaZAlloc(arena, D.len*2);
+ B.len = D.len;
+ B.data = D.data + D.len;
+
+ /* if all goes well, A will be returned, so don't use our temp arena */
+ A = SECITEM_AllocItem(NULL,NULL,c*hashLength);
+ if (A == NULL) {
+ goto loser;
+ }
+
+ SLen = NSSPBE_ROUNDUP(salt->len,HMAC_BUFFER);
+ PLen = NSSPBE_ROUNDUP(pwitem->len,HMAC_BUFFER);
+ I.len = SLen+PLen;
+ I.data = (unsigned char*)PORT_ArenaZAlloc(arena, I.len);
+ if (I.data == NULL) {
+ goto loser;
+ }
+
+ /* S & P are only used to initialize I */
+ S = I.data;
+ P = S + SLen;
+
+ PORT_Memset(D.data, (char)bitGenPurpose, D.len);
+ if (SLen) {
+ for (i=0; i < SLen; i += salt->len) {
+ PORT_Memcpy(S+i, salt->data, NSSPBE_MIN(SLen-i,salt->len));
+ }
+ }
+ if (PLen) {
+ for (i=0; i < PLen; i += pwitem->len) {
+ PORT_Memcpy(P+i, pwitem->data, NSSPBE_MIN(PLen-i,pwitem->len));
+ }
+ }
+
+ iterBuf = (unsigned char*)PORT_ArenaZAlloc(arena,hashLength);
+ if (iterBuf == NULL) {
+ goto loser;
+ }
+
+ hash = hashObject->create();
+ if(!hash) {
+ goto loser;
+ }
+ /* calculate the PBE now */
+ for(i = 0; i < c; i++) {
+ int Bidx; /* must be signed or the for loop won't terminate */
+ unsigned int k, j;
+ unsigned char *Ai = A->data+i*hashLength;
+
+
+ for(iter = 0; iter < pbe_param->iter; iter++) {
+ hashObject->begin(hash);
+
+ if (iter) {
+ hashObject->update(hash, iterBuf, hashLen);
+ } else {
+ hashObject->update(hash, D.data, D.len);
+ hashObject->update(hash, I.data, I.len);
+ }
+
+ hashObject->end(hash, iterBuf, &hashLen, hashObject->length);
+ if(hashLen != hashObject->length) {
+ break;
+ }
+ }
+
+ PORT_Memcpy(Ai, iterBuf, hashLength);
+ for (Bidx = 0; Bidx < B.len; Bidx += hashLength) {
+ PORT_Memcpy(B.data+Bidx,iterBuf,NSSPBE_MIN(B.len-Bidx,hashLength));
+ }
+
+ k = I.len/B.len;
+ for(j = 0; j < k; j++) {
+ unsigned int q, carryBit;
+ unsigned char *Ij = I.data + j*B.len;
+
+ /* (Ij = Ij+B+1) */
+ for (Bidx = (B.len-1), q=1, carryBit=0; Bidx >= 0; Bidx--,q=0) {
+ q += (unsigned int)Ij[Bidx];
+ q += (unsigned int)B.data[Bidx];
+ q += carryBit;
+
+ carryBit = (q > 0xff);
+ Ij[Bidx] = (unsigned char)(q & 0xff);
+ }
+ }
+ }
+loser:
+ if (hash) {
+ hashObject->destroy(hash, PR_TRUE);
+ }
+ if(arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+
+ if (A) {
+ /* if i != c, then we didn't complete the loop above and must of failed
+ * somwhere along the way */
+ if (i != c) {
+ SECITEM_ZfreeItem(A,PR_TRUE);
+ A = NULL;
+ } else {
+ A->len = bytesNeeded;
+ }
+ }
+
+ return A;
+}
+
+/*
+ * generate key as per PKCS 5
+ */
+SECItem *
+nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
+ SECItem *iv, PRBool faulty3DES)
+{
+ SECItem *hash = NULL, *key = NULL;
+ const SECHashObject *hashObj;
+ PRBool getIV = PR_FALSE;
+
+ if((pbe_param == NULL) || (pwitem == NULL)) {
+ return NULL;
+ }
+
+ key = SECITEM_AllocItem(NULL,NULL,pbe_param->keyLen);
+ if (key == NULL) {
+ return NULL;
+ }
+
+ if (iv && (pbe_param->ivLen) && (iv->data == NULL)) {
+ getIV = PR_TRUE;
+ iv->data = (unsigned char *)PORT_Alloc(pbe_param->ivLen);
+ if (iv->data == NULL) {
+ goto loser;
+ }
+ iv->len = pbe_param->ivLen;
+ }
+
+ hashObj = HASH_GetRawHashObject(pbe_param->hashType);
+ switch (pbe_param->pbeType) {
+ case NSSPKCS5_PBKDF1:
+ hash = nsspkcs5_PBKDF1Extended(hashObj,pbe_param,pwitem,faulty3DES);
+ if (hash == NULL) {
+ goto loser;
+ }
+ PORT_Assert(hash->len >= key->len+(getIV ? iv->len : 0));
+ if (getIV) {
+ PORT_Memcpy(iv->data, hash->data+(hash->len - iv->len),iv->len);
+ }
+
+ break;
+#ifdef PBKDF2
+ case NSSPKCS5_PBKDF2:
+ hash = nsspkcs5_PBKDF2(hashObj,pbe_param,pwitem);
+ if (getIV) {
+ PORT_Memcpy(iv->data, pbe_param->ivData, iv->len);
+ }
+ break;
+#endif
+ case NSSPKCS5_PKCS12_V2:
+ if (getIV) {
+ hash = nsspkcs5_PKCS12PBE(hashObj,pbe_param,pwitem,
+ pbeBitGenCipherIV,iv->len);
+ if (hash == NULL) {
+ goto loser;
+ }
+ PORT_Memcpy(iv->data,hash->data,iv->len);
+ SECITEM_ZfreeItem(hash,PR_TRUE);
+ hash = NULL;
+ }
+ hash = nsspkcs5_PKCS12PBE(hashObj,pbe_param,pwitem,
+ pbe_param->keyID,key->len);
+ default:
+ break;
+ }
+
+ if (hash == NULL) {
+ goto loser;
+ }
+
+ if (pbe_param->is2KeyDES) {
+ PORT_Memcpy(key->data, hash->data, (key->len * 2) / 3);
+ PORT_Memcpy(&(key->data[(key->len * 2) / 3]), key->data,
+ key->len / 3);
+ } else {
+ PORT_Memcpy(key->data, hash->data, key->len);
+ }
+
+ SECITEM_ZfreeItem(hash, PR_TRUE);
+ return key;
+
+loser:
+ if (getIV && iv->data) {
+ PORT_ZFree(iv->data,iv->len);
+ iv->data = NULL;
+ }
+
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ return NULL;
+}
+
+static SECStatus
+nsspkcs5_FillInParam(SECOidTag algorithm, NSSPKCS5PBEParameter *pbe_param)
+{
+ PRBool skipType = PR_FALSE;
+
+ pbe_param->keyLen = 5;
+ pbe_param->ivLen = 8;
+ pbe_param->hashType = HASH_AlgSHA1;
+ pbe_param->pbeType = NSSPKCS5_PBKDF1;
+ pbe_param->encAlg = SEC_OID_RC2_CBC;
+ pbe_param->is2KeyDES = PR_FALSE;
+ switch(algorithm) {
+ /* DES3 Algorithms */
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+ pbe_param->is2KeyDES = PR_TRUE;
+ /* fall through */
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+ pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
+ /* fall through */
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+ pbe_param->keyLen = 24;
+ pbe_param->encAlg = SEC_OID_DES_EDE3_CBC;
+ break;
+
+ /* DES Algorithms */
+ case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+ pbe_param->hashType = HASH_AlgMD2;
+ goto finish_des;
+ case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+ pbe_param->hashType = HASH_AlgMD5;
+ /* fall through */
+ case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+finish_des:
+ pbe_param->keyLen = 8;
+ pbe_param->encAlg = SEC_OID_DES_CBC;
+ break;
+
+ /* RC2 Algorithms */
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ pbe_param->keyLen = 16;
+ /* fall through */
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
+ break;
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+ pbe_param->keyLen = 16;
+ /* fall through */
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+ break;
+
+ /* RC4 algorithms */
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ skipType = PR_TRUE;
+ /* fall through */
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+ pbe_param->keyLen = 16;
+ /* fall through */
+ case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ if (!skipType) {
+ pbe_param->pbeType = NSSPKCS5_PKCS12_V2;
+ }
+ /* fall through */
+ case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+ pbe_param->ivLen = 0;
+ pbe_param->encAlg = SEC_OID_RC4;
+ break;
+
+#ifdef PBKDF2
+ case SEC_OID_PKCS5_PBKDF2:
+ case SEC_OID_PKCS5_PBES2:
+ case SEC_OID_PKCS5_PBMAC1:
+ /* everything else will be filled in by the template */
+ pbe_param->ivLen = 0;
+ pbe_param->pbeType = NSSPKCS5_PBKDF2;
+ pbe_param->encAlg = SEC_OID_PKCS5_PBKDF2;
+ pbe_param->keyLen = 0; /* needs to be set by caller after return */
+ break;
+#endif
+
+ default:
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* decode the algid and generate a PKCS 5 parameter from it
+ */
+NSSPKCS5PBEParameter *
+nsspkcs5_NewParam(SECOidTag alg, SECItem *salt, int iterator)
+{
+ PRArenaPool *arena = NULL;
+ NSSPKCS5PBEParameter *pbe_param = NULL;
+ SECStatus rv = SECFailure;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL)
+ return NULL;
+
+ /* allocate memory for the parameter */
+ pbe_param = (NSSPKCS5PBEParameter *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSPKCS5PBEParameter));
+
+ if (pbe_param == NULL) {
+ goto loser;
+ }
+
+ pbe_param->poolp = arena;
+
+ rv = nsspkcs5_FillInParam(alg, pbe_param);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ pbe_param->iter = iterator;
+ if (salt) {
+ rv = SECITEM_CopyItem(arena,&pbe_param->salt,salt);
+ }
+
+ /* default key gen */
+ pbe_param->keyID = pbeBitGenCipherKey;
+
+loser:
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena, PR_TRUE);
+ pbe_param = NULL;
+ }
+
+ return pbe_param;
+}
+
+/*
+ * find the hash type needed to implement a specific HMAC.
+ * OID definitions are from pkcs 5 v2.0 and 2.1
+ */
+HASH_HashType
+HASH_FromHMACOid(SECOidTag hmac)
+{
+ switch (hmac) {
+ case SEC_OID_HMAC_SHA1:
+ return HASH_AlgSHA1;
+ case SEC_OID_HMAC_SHA256:
+ return HASH_AlgSHA256;
+ case SEC_OID_HMAC_SHA384:
+ return HASH_AlgSHA384;
+ case SEC_OID_HMAC_SHA512:
+ return HASH_AlgSHA512;
+ case SEC_OID_HMAC_SHA224:
+ default:
+ break;
+ }
+ return HASH_AlgNULL;
+}
+
+/* decode the algid and generate a PKCS 5 parameter from it
+ */
+NSSPKCS5PBEParameter *
+nsspkcs5_AlgidToParam(SECAlgorithmID *algid)
+{
+ NSSPKCS5PBEParameter *pbe_param = NULL;
+ nsspkcs5V2PBEParameter pbev2_param;
+ SECOidTag algorithm;
+ SECStatus rv = SECFailure;
+
+ if (algid == NULL) {
+ return NULL;
+ }
+
+ algorithm = SECOID_GetAlgorithmTag(algid);
+ if (algorithm == SEC_OID_UNKNOWN) {
+ goto loser;
+ }
+
+ pbe_param = nsspkcs5_NewParam(algorithm, NULL, 1);
+ if (pbe_param == NULL) {
+ goto loser;
+ }
+
+ /* decode parameter */
+ rv = SECFailure;
+ switch (pbe_param->pbeType) {
+ case NSSPKCS5_PBKDF1:
+ rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param,
+ NSSPKCS5PBEParameterTemplate, &algid->parameters);
+ break;
+ case NSSPKCS5_PKCS12_V2:
+ rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param,
+ NSSPKCS5PKCS12V2PBEParameterTemplate, &algid->parameters);
+ break;
+#ifdef PBKDF2
+ case NSSPKCS5_PBKDF2:
+ PORT_Memset(&pbev2_param,0, sizeof(pbev2_param));
+ /* just the PBE */
+ if (algorithm == SEC_OID_PKCS5_PBKDF2) {
+ rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param,
+ NSSPKCS5V2PBEParameterTemplate, &algid->parameters);
+ } else {
+ /* PBE data an others */
+ rv = SEC_ASN1DecodeItem(pbe_param->poolp, &pbev2_param,
+ NSSPKCS5V2PBES2ParameterTemplate, &algid->parameters);
+ if (rv != SECSuccess) {
+ break;
+ }
+ pbe_param->encAlg = SECOID_GetAlgorithmTag(&pbev2_param.algParams);
+ rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param,
+ NSSPKCS5V2PBEParameterTemplate,
+ &pbev2_param.keyParams.parameters);
+ if (rv != SECSuccess) {
+ break;
+ }
+ pbe_param->keyLen = DER_GetInteger(&pbe_param->keyLength);
+ }
+ /* we we are encrypting, save any iv's */
+ if (algorithm == SEC_OID_PKCS5_PBES2) {
+ pbe_param->ivLen = pbev2_param.algParams.parameters.len;
+ pbe_param->ivData = pbev2_param.algParams.parameters.data;
+ }
+ pbe_param->hashType =
+ HASH_FromHMACOid(SECOID_GetAlgorithmTag(&pbe_param->prfAlg));
+ if (pbe_param->hashType == HASH_AlgNULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ rv = SECFailure;
+ }
+ break;
+#endif
+ }
+
+loser:
+ if (rv == SECSuccess) {
+ pbe_param->iter = DER_GetInteger(&pbe_param->iteration);
+ } else {
+ nsspkcs5_DestroyPBEParameter(pbe_param);
+ pbe_param = NULL;
+ }
+
+ return pbe_param;
+}
+
+/* destroy a pbe parameter. it assumes that the parameter was
+ * generated using the appropriate create function and therefor
+ * contains an arena pool.
+ */
+void
+nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *pbe_param)
+{
+ if (pbe_param != NULL) {
+ PORT_FreeArena(pbe_param->poolp, PR_FALSE);
+ }
+}
+
+
+/* crypto routines */
+/* perform DES encryption and decryption. these routines are called
+ * by nsspkcs5_CipherData. In the case of an error, NULL is returned.
+ */
+static SECItem *
+sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des,
+ PRBool encrypt)
+{
+ SECItem *dest;
+ SECItem *dup_src;
+ SECStatus rv = SECFailure;
+ int pad;
+
+ if((src == NULL) || (key == NULL) || (iv == NULL))
+ return NULL;
+
+ dup_src = SECITEM_DupItem(src);
+ if(dup_src == NULL) {
+ return NULL;
+ }
+
+ if(encrypt != PR_FALSE) {
+ void *dummy;
+
+ dummy = CBC_PadBuffer(NULL, dup_src->data,
+ dup_src->len, &dup_src->len, 8 /* DES_BLOCK_SIZE */);
+ if(dummy == NULL) {
+ SECITEM_FreeItem(dup_src, PR_TRUE);
+ return NULL;
+ }
+ dup_src->data = (unsigned char*)dummy;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(dest != NULL) {
+ /* allocate with over flow */
+ dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64);
+ if(dest->data != NULL) {
+ DESContext *ctxt;
+ ctxt = DES_CreateContext(key->data, iv->data,
+ (triple_des ? NSS_DES_EDE3_CBC : NSS_DES_CBC),
+ encrypt);
+
+ if(ctxt != NULL) {
+ rv = (encrypt ? DES_Encrypt : DES_Decrypt)(
+ ctxt, dest->data, &dest->len,
+ dup_src->len + 64, dup_src->data, dup_src->len);
+
+ /* remove padding -- assumes 64 bit blocks */
+ if((encrypt == PR_FALSE) && (rv == SECSuccess)) {
+ pad = dest->data[dest->len-1];
+ if((pad > 0) && (pad <= 8)) {
+ if(dest->data[dest->len-pad] != pad) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ } else {
+ dest->len -= pad;
+ }
+ } else {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ }
+ }
+ DES_DestroyContext(ctxt, PR_TRUE);
+ }
+ }
+ }
+
+ if(rv == SECFailure) {
+ if(dest != NULL) {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ }
+ dest = NULL;
+ }
+
+ if(dup_src != NULL) {
+ SECITEM_FreeItem(dup_src, PR_TRUE);
+ }
+
+ return dest;
+}
+
+/* perform aes encryption/decryption if an error occurs, NULL is returned
+ */
+static SECItem *
+sec_pkcs5_aes(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des,
+ PRBool encrypt)
+{
+ SECItem *dest;
+ SECItem *dup_src;
+ SECStatus rv = SECFailure;
+ int pad;
+
+ if((src == NULL) || (key == NULL) || (iv == NULL))
+ return NULL;
+
+ dup_src = SECITEM_DupItem(src);
+ if(dup_src == NULL) {
+ return NULL;
+ }
+
+ if(encrypt != PR_FALSE) {
+ void *dummy;
+
+ dummy = CBC_PadBuffer(NULL, dup_src->data,
+ dup_src->len, &dup_src->len,AES_BLOCK_SIZE);
+ if(dummy == NULL) {
+ SECITEM_FreeItem(dup_src, PR_TRUE);
+ return NULL;
+ }
+ dup_src->data = (unsigned char*)dummy;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(dest != NULL) {
+ /* allocate with over flow */
+ dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64);
+ if(dest->data != NULL) {
+ AESContext *ctxt;
+ ctxt = AES_CreateContext(key->data, iv->data,
+ NSS_AES_CBC, encrypt, key->len, 16);
+
+ if(ctxt != NULL) {
+ rv = (encrypt ? AES_Encrypt : AES_Decrypt)(
+ ctxt, dest->data, &dest->len,
+ dup_src->len + 64, dup_src->data, dup_src->len);
+
+ /* remove padding -- assumes 64 bit blocks */
+ if((encrypt == PR_FALSE) && (rv == SECSuccess)) {
+ pad = dest->data[dest->len-1];
+ if((pad > 0) && (pad <= 16)) {
+ if(dest->data[dest->len-pad] != pad) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ } else {
+ dest->len -= pad;
+ }
+ } else {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ }
+ }
+ AES_DestroyContext(ctxt, PR_TRUE);
+ }
+ }
+ }
+
+ if(rv == SECFailure) {
+ if(dest != NULL) {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ }
+ dest = NULL;
+ }
+
+ if(dup_src != NULL) {
+ SECITEM_FreeItem(dup_src, PR_TRUE);
+ }
+
+ return dest;
+}
+
+/* perform rc2 encryption/decryption if an error occurs, NULL is returned
+ */
+static SECItem *
+sec_pkcs5_rc2(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy,
+ PRBool encrypt)
+{
+ SECItem *dest;
+ SECItem *dup_src;
+ SECStatus rv = SECFailure;
+ int pad;
+
+ if((src == NULL) || (key == NULL) || (iv == NULL)) {
+ return NULL;
+ }
+
+ dup_src = SECITEM_DupItem(src);
+ if(dup_src == NULL) {
+ return NULL;
+ }
+
+ if(encrypt != PR_FALSE) {
+ void *dummy;
+
+ dummy = CBC_PadBuffer(NULL, dup_src->data,
+ dup_src->len, &dup_src->len, 8 /* RC2_BLOCK_SIZE */);
+ if(dummy == NULL) {
+ SECITEM_FreeItem(dup_src, PR_TRUE);
+ return NULL;
+ }
+ dup_src->data = (unsigned char*)dummy;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(dest != NULL) {
+ dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64);
+ if(dest->data != NULL) {
+ RC2Context *ctxt;
+
+ ctxt = RC2_CreateContext(key->data, key->len, iv->data,
+ NSS_RC2_CBC, key->len);
+
+ if(ctxt != NULL) {
+ rv = (encrypt ? RC2_Encrypt: RC2_Decrypt)(
+ ctxt, dest->data, &dest->len,
+ dup_src->len + 64, dup_src->data, dup_src->len);
+
+ /* assumes 8 byte blocks -- remove padding */
+ if((rv == SECSuccess) && (encrypt != PR_TRUE)) {
+ pad = dest->data[dest->len-1];
+ if((pad > 0) && (pad <= 8)) {
+ if(dest->data[dest->len-pad] != pad) {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECFailure;
+ } else {
+ dest->len -= pad;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_BAD_PASSWORD);
+ rv = SECFailure;
+ }
+ }
+
+ }
+ }
+ }
+
+ if((rv != SECSuccess) && (dest != NULL)) {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ dest = NULL;
+ }
+
+ if(dup_src != NULL) {
+ SECITEM_FreeItem(dup_src, PR_TRUE);
+ }
+
+ return dest;
+}
+
+/* perform rc4 encryption and decryption */
+static SECItem *
+sec_pkcs5_rc4(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy_op,
+ PRBool encrypt)
+{
+ SECItem *dest;
+ SECStatus rv = SECFailure;
+
+ if((src == NULL) || (key == NULL) || (iv == NULL)) {
+ return NULL;
+ }
+
+ dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem));
+ if(dest != NULL) {
+ dest->data = (unsigned char *)PORT_ZAlloc(sizeof(unsigned char) *
+ (src->len + 64));
+ if(dest->data != NULL) {
+ RC4Context *ctxt;
+
+ ctxt = RC4_CreateContext(key->data, key->len);
+ if(ctxt) {
+ rv = (encrypt ? RC4_Encrypt : RC4_Decrypt)(
+ ctxt, dest->data, &dest->len,
+ src->len + 64, src->data, src->len);
+ RC4_DestroyContext(ctxt, PR_TRUE);
+ }
+ }
+ }
+
+ if((rv != SECSuccess) && (dest)) {
+ SECITEM_FreeItem(dest, PR_TRUE);
+ dest = NULL;
+ }
+
+ return dest;
+}
+/* function pointer template for crypto functions */
+typedef SECItem *(* pkcs5_crypto_func)(SECItem *key, SECItem *iv,
+ SECItem *src, PRBool op1, PRBool op2);
+
+/* performs the cipher operation on the src and returns the result.
+ * if an error occurs, NULL is returned.
+ *
+ * a null length password is allowed. this corresponds to encrypting
+ * the data with ust the salt.
+ */
+/* change this to use PKCS 11? */
+SECItem *
+nsspkcs5_CipherData(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
+ SECItem *src, PRBool encrypt, PRBool *update)
+{
+ SECItem *key = NULL, iv;
+ SECItem *dest = NULL;
+ PRBool tripleDES = PR_TRUE;
+ pkcs5_crypto_func cryptof;
+
+ iv.data = NULL;
+
+ if (update) {
+ *update = PR_FALSE;
+ }
+
+ if ((pwitem == NULL) || (src == NULL)) {
+ return NULL;
+ }
+
+ /* get key, and iv */
+ key = nsspkcs5_ComputeKeyAndIV(pbe_param, pwitem, &iv, PR_FALSE);
+ if(key == NULL) {
+ return NULL;
+ }
+
+ switch(pbe_param->encAlg) {
+ /* PKCS 5 v2 only */
+ case SEC_OID_AES_128_CBC:
+ case SEC_OID_AES_192_CBC:
+ case SEC_OID_AES_256_CBC:
+ cryptof = sec_pkcs5_aes;
+ break;
+ case SEC_OID_DES_EDE3_CBC:
+ cryptof = sec_pkcs5_des;
+ tripleDES = PR_TRUE;
+ break;
+ case SEC_OID_DES_CBC:
+ cryptof = sec_pkcs5_des;
+ tripleDES = PR_FALSE;
+ break;
+ case SEC_OID_RC2_CBC:
+ cryptof = sec_pkcs5_rc2;
+ break;
+ case SEC_OID_RC4:
+ cryptof = sec_pkcs5_rc4;
+ break;
+ default:
+ cryptof = NULL;
+ break;
+ }
+
+ if (cryptof == NULL) {
+ goto loser;
+ }
+
+ dest = (*cryptof)(key, &iv, src, tripleDES, encrypt);
+ /*
+ * it's possible for some keys and keydb's to claim to
+ * be triple des when they're really des. In this case
+ * we simply try des. If des works we set the update flag
+ * so the key db knows it needs to update all it's entries.
+ * The case can only happen on decrypted of a
+ * SEC_OID_DES_EDE3_CBD.
+ */
+ if ((dest == NULL) && (encrypt == PR_FALSE) &&
+ (pbe_param->encAlg == SEC_OID_DES_EDE3_CBC)) {
+ dest = (*cryptof)(key, &iv, src, PR_FALSE, encrypt);
+ if (update && (dest != NULL)) *update = PR_TRUE;
+ }
+
+loser:
+ if (key != NULL) {
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ }
+ if (iv.data != NULL) {
+ SECITEM_ZfreeItem(&iv, PR_FALSE);
+ }
+
+ return dest;
+}
+
+/* creates a algorithm ID containing the PBE algorithm and appropriate
+ * parameters. the required parameter is the algorithm. if salt is
+ * not specified, it is generated randomly. if IV is specified, it overrides
+ * the PKCS 5 generation of the IV.
+ *
+ * the returned SECAlgorithmID should be destroyed using
+ * SECOID_DestroyAlgorithmID
+ */
+SECAlgorithmID *
+nsspkcs5_CreateAlgorithmID(PRArenaPool *arena, SECOidTag algorithm,
+ NSSPKCS5PBEParameter *pbe_param)
+{
+ SECAlgorithmID *algid, *ret_algid = NULL;
+ SECItem der_param;
+ nsspkcs5V2PBEParameter pkcs5v2_param;
+
+ SECStatus rv = SECFailure;
+ void *dummy = NULL;
+
+ if (arena == NULL) {
+ return NULL;
+ }
+
+ der_param.data = NULL;
+ der_param.len = 0;
+
+ /* generate the algorithm id */
+ algid = (SECAlgorithmID *)PORT_ArenaZAlloc(arena, sizeof(SECAlgorithmID));
+ if (algid == NULL) {
+ goto loser;
+ }
+
+ if (pbe_param->iteration.data == NULL) {
+ dummy = SEC_ASN1EncodeInteger(pbe_param->poolp,&pbe_param->iteration,
+ pbe_param->iter);
+ if (dummy == NULL) {
+ goto loser;
+ }
+ }
+ switch (pbe_param->pbeType) {
+ case NSSPKCS5_PBKDF1:
+ dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
+ NSSPKCS5PBEParameterTemplate);
+ break;
+ case NSSPKCS5_PKCS12_V2:
+ dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
+ NSSPKCS5PKCS12V2PBEParameterTemplate);
+ break;
+#ifdef PBKDF2
+ case NSSPKCS5_PBKDF2:
+ if (pbe_param->keyLength.data == NULL) {
+ dummy = SEC_ASN1EncodeInteger(pbe_param->poolp,
+ &pbe_param->keyLength, pbe_param->keyLen);
+ if (dummy == NULL) {
+ goto loser;
+ }
+ }
+ PORT_Memset(&pkcs5v2_param, 0, sizeof(pkcs5v2_param));
+ dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param,
+ NSSPKCS5V2PBEParameterTemplate);
+ if (dummy == NULL) {
+ break;
+ }
+ dummy = NULL;
+ rv = SECOID_SetAlgorithmID(arena, &pkcs5v2_param.keyParams,
+ SEC_OID_PKCS5_PBKDF2, &der_param);
+ if (rv != SECSuccess) {
+ break;
+ }
+ der_param.data = pbe_param->ivData;
+ der_param.len = pbe_param->ivLen;
+ rv = SECOID_SetAlgorithmID(arena, &pkcs5v2_param.algParams,
+ pbe_param->encAlg, pbe_param->ivLen ? &der_param : NULL);
+ if (rv != SECSuccess) {
+ break;
+ }
+ dummy = SEC_ASN1EncodeItem(arena, &der_param, &pkcs5v2_param,
+ NSSPKCS5V2PBES2ParameterTemplate);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ if (dummy == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(arena, algid, algorithm, &der_param);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ ret_algid = (SECAlgorithmID *)PORT_ZAlloc(sizeof(SECAlgorithmID));
+ if (ret_algid == NULL) {
+ goto loser;
+ }
+
+ rv = SECOID_CopyAlgorithmID(NULL, ret_algid, algid);
+ if (rv != SECSuccess) {
+ SECOID_DestroyAlgorithmID(ret_algid, PR_TRUE);
+ ret_algid = NULL;
+ }
+
+loser:
+
+ return ret_algid;
+}
diff --git a/security/nss/lib/softoken/lowpbe.h b/security/nss/lib/softoken/lowpbe.h
new file mode 100644
index 000000000..974b2fa4f
--- /dev/null
+++ b/security/nss/lib/softoken/lowpbe.h
@@ -0,0 +1,140 @@
+/* ***** 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 ***** */
+
+#ifndef _SECPKCS5_H_
+#define _SECPKCS5_H_
+
+#include "plarena.h"
+#include "secitem.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "hasht.h"
+
+typedef SECItem * (* SEC_PKCS5GetPBEPassword)(void *arg);
+
+/* used for V2 PKCS 12 Draft Spec */
+typedef enum {
+ pbeBitGenIDNull = 0,
+ pbeBitGenCipherKey = 0x01,
+ pbeBitGenCipherIV = 0x02,
+ pbeBitGenIntegrityKey = 0x03
+} PBEBitGenID;
+
+typedef enum {
+ NSSPKCS5_PBKDF1 = 0,
+ NSSPKCS5_PBKDF2 = 1,
+ NSSPKCS5_PKCS12_V2 = 2
+} NSSPKCS5PBEType;
+
+typedef struct NSSPKCS5PBEParameterStr NSSPKCS5PBEParameter;
+
+struct NSSPKCS5PBEParameterStr {
+ PRArenaPool *poolp;
+ SECItem salt; /* octet string */
+ SECItem iteration; /* integer */
+ SECItem keyLength; /* integer */
+
+ /* used locally */
+ int iter;
+ int keyLen;
+ int ivLen;
+ unsigned char *ivData;
+ HASH_HashType hashType;
+ NSSPKCS5PBEType pbeType;
+ SECAlgorithmID prfAlg;
+ PBEBitGenID keyID;
+ SECOidTag encAlg;
+ PRBool is2KeyDES;
+};
+
+
+SEC_BEGIN_PROTOS
+/* Create a PKCS5 Algorithm ID
+ * The algorithm ID is set up using the PKCS #5 parameter structure
+ * algorithm is the PBE algorithm ID for the desired algorithm
+ * pbe is a pbe param block with all the info needed to create the
+ * algorithm id.
+ * If an error occurs or the algorithm specified is not supported
+ * or is not a password based encryption algorithm, NULL is returned.
+ * Otherwise, a pointer to the algorithm id is returned.
+ */
+extern SECAlgorithmID *
+nsspkcs5_CreateAlgorithmID(PRArenaPool *arena, SECOidTag algorithm,
+ NSSPKCS5PBEParameter *pbe);
+
+/*
+ * Convert an Algorithm ID to a PBE Param.
+ * NOTE: this does not suppport PKCS 5 v2 because it's only used for the
+ * keyDB which only support PKCS 5 v1, PFX, and PKCS 12.
+ */
+NSSPKCS5PBEParameter *
+nsspkcs5_AlgidToParam(SECAlgorithmID *algid);
+
+/*
+ * Convert an Algorithm ID to a PBE Param.
+ * NOTE: this does not suppport PKCS 5 v2 because it's only used for the
+ * keyDB which only support PKCS 5 v1, PFX, and PKCS 12.
+ */
+NSSPKCS5PBEParameter *
+nsspkcs5_NewParam(SECOidTag alg, SECItem *salt, int iterator);
+
+
+/* Encrypt/Decrypt data using password based encryption.
+ * algid is the PBE algorithm identifier,
+ * pwitem is the password,
+ * src is the source for encryption/decryption,
+ * encrypt is PR_TRUE for encryption, PR_FALSE for decryption.
+ * The key and iv are generated based upon PKCS #5 then the src
+ * is either encrypted or decrypted. If an error occurs, NULL
+ * is returned, otherwise the ciphered contents is returned.
+ */
+extern SECItem *
+nsspkcs5_CipherData(NSSPKCS5PBEParameter *, SECItem *pwitem,
+ SECItem *src, PRBool encrypt, PRBool *update);
+
+extern SECItem *
+nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *, SECItem *pwitem,
+ SECItem *iv, PRBool faulty3DES);
+
+/* Destroys PBE parameter */
+extern void
+nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *param);
+
+HASH_HashType HASH_FromHMACOid(SECOidTag oid);
+
+SEC_END_PROTOS
+
+#endif
diff --git a/security/nss/lib/softoken/manifest.mn b/security/nss/lib/softoken/manifest.mn
new file mode 100644
index 000000000..24f026624
--- /dev/null
+++ b/security/nss/lib/softoken/manifest.mn
@@ -0,0 +1,103 @@
+#
+# ***** 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 *****
+CORE_DEPTH = ../../..
+
+MODULE = nss
+DIRS = legacydb
+
+REQUIRES = dbm
+
+LIBRARY_NAME = softokn
+LIBRARY_VERSION = 3
+MAPFILE = $(OBJDIR)/softokn.def
+
+DEFINES += -DSHLIB_SUFFIX=\"$(DLL_SUFFIX)\" -DSHLIB_PREFIX=\"$(DLL_PREFIX)\" -DSOFTOKEN_LIB_NAME=\"$(notdir $(SHARED_LIBRARY))\" -DSHLIB_VERSION=\"$(LIBRARY_VERSION)\"
+
+ifdef MOZILLA_CLIENT
+INCLUDES += -I$(DIST)/include/sqlite3
+endif
+
+EXPORTS = \
+ pkcs11.h \
+ pkcs11f.h \
+ pkcs11p.h \
+ pkcs11t.h \
+ pkcs11n.h \
+ pkcs11u.h \
+ $(NULL)
+
+PRIVATE_EXPORTS = \
+ lgglue.h \
+ lowkeyi.h \
+ lowkeyti.h \
+ pk11pars.h \
+ pkcs11ni.h \
+ softoken.h \
+ softoknt.h \
+ softkver.h \
+ sdb.h \
+ sftkdbt.h \
+ $(NULL)
+
+CSRCS = \
+ ecdecode.c \
+ fipsaudt.c \
+ fipstest.c \
+ fipstokn.c \
+ lgglue.c \
+ lowkey.c \
+ lowpbe.c \
+ padbuf.c \
+ pkcs11.c \
+ pkcs11c.c \
+ pkcs11u.c \
+ rsawrapr.c \
+ sdb.c \
+ sftkdb.c \
+ sftkmod.c \
+ sftkpars.c \
+ sftkpwd.c \
+ softkver.c \
+ tlsprf.c \
+ $(NULL)
+
+ifdef SQLITE_UNSAFE_THREADS
+DEFINES += -DSQLITE_UNSAFE_THREADS
+endif
+
+# This part of the code, including all sub-dirs, can be optimized for size
+export ALLOW_OPT_CODE_SIZE = 1
diff --git a/security/nss/lib/softoken/padbuf.c b/security/nss/lib/softoken/padbuf.c
new file mode 100644
index 000000000..8c43fa287
--- /dev/null
+++ b/security/nss/lib/softoken/padbuf.c
@@ -0,0 +1,81 @@
+/* ***** 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 ***** */
+#include "blapit.h"
+#include "secport.h"
+#include "secerr.h"
+
+/*
+ * Prepare a buffer for any padded CBC encryption algorithm, growing to the
+ * appropriate boundary and filling with the appropriate padding.
+ * blockSize must be a power of 2.
+ *
+ * NOTE: If arena is non-NULL, we re-allocate from there, otherwise
+ * we assume (and use) XP memory (re)allocation.
+ */
+unsigned char *
+CBC_PadBuffer(PRArenaPool *arena, unsigned char *inbuf, unsigned int inlen,
+ unsigned int *outlen, int blockSize)
+{
+ unsigned char *outbuf;
+ unsigned int des_len;
+ unsigned int i;
+ unsigned char des_pad_len;
+
+ /*
+ * We need from 1 to blockSize bytes -- we *always* grow.
+ * The extra bytes contain the value of the length of the padding:
+ * if we have 2 bytes of padding, then the padding is "0x02, 0x02".
+ */
+ des_len = (inlen + blockSize) & ~(blockSize - 1);
+
+ if (arena != NULL) {
+ outbuf = (unsigned char*)PORT_ArenaGrow (arena, inbuf, inlen, des_len);
+ } else {
+ outbuf = (unsigned char*)PORT_Realloc (inbuf, des_len);
+ }
+
+ if (outbuf == NULL) {
+ PORT_SetError (SEC_ERROR_NO_MEMORY);
+ return NULL;
+ }
+
+ des_pad_len = des_len - inlen;
+ for (i = inlen; i < des_len; i++)
+ outbuf[i] = des_pad_len;
+
+ *outlen = des_len;
+ return outbuf;
+}
diff --git a/security/nss/lib/softoken/pk11pars.h b/security/nss/lib/softoken/pk11pars.h
new file mode 100644
index 000000000..b745c93e9
--- /dev/null
+++ b/security/nss/lib/softoken/pk11pars.h
@@ -0,0 +1,871 @@
+/* ***** 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) 2001
+ * 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 ***** */
+/*
+ * The following handles the loading, unloading and management of
+ * various PCKS #11 modules
+ */
+
+
+/*
+ * this header file contains routines for parsing PKCS #11 module spec
+ * strings. It contains 'C' code and should only be included in one module.
+ * Currently it is included in both softoken and the wrapper.
+ */
+#include <ctype.h>
+#include "pkcs11.h"
+#include "seccomon.h"
+#include "prprf.h"
+#include "secmodt.h"
+#include "pk11init.h"
+
+#define SECMOD_ARG_LIBRARY_PARAMETER "library="
+#define SECMOD_ARG_NAME_PARAMETER "name="
+#define SECMOD_ARG_MODULE_PARAMETER "parameters="
+#define SECMOD_ARG_NSS_PARAMETER "NSS="
+#define SECMOD_ARG_FORTEZZA_FLAG "FORTEZZA"
+#define SECMOD_ARG_ESCAPE '\\'
+
+struct secmodargSlotFlagTable {
+ char *name;
+ int len;
+ unsigned long value;
+};
+
+#define SECMOD_DEFAULT_CIPHER_ORDER 0
+#define SECMOD_DEFAULT_TRUST_ORDER 50
+
+
+#define SECMOD_ARG_ENTRY(arg,flag) \
+{ #arg , sizeof(#arg)-1, flag }
+static struct secmodargSlotFlagTable secmod_argSlotFlagTable[] = {
+ SECMOD_ARG_ENTRY(RSA,SECMOD_RSA_FLAG),
+ SECMOD_ARG_ENTRY(DSA,SECMOD_RSA_FLAG),
+ SECMOD_ARG_ENTRY(RC2,SECMOD_RC4_FLAG),
+ SECMOD_ARG_ENTRY(RC4,SECMOD_RC2_FLAG),
+ SECMOD_ARG_ENTRY(DES,SECMOD_DES_FLAG),
+ SECMOD_ARG_ENTRY(DH,SECMOD_DH_FLAG),
+ SECMOD_ARG_ENTRY(FORTEZZA,SECMOD_FORTEZZA_FLAG),
+ SECMOD_ARG_ENTRY(RC5,SECMOD_RC5_FLAG),
+ SECMOD_ARG_ENTRY(SHA1,SECMOD_SHA1_FLAG),
+ SECMOD_ARG_ENTRY(MD5,SECMOD_MD5_FLAG),
+ SECMOD_ARG_ENTRY(MD2,SECMOD_MD2_FLAG),
+ SECMOD_ARG_ENTRY(SSL,SECMOD_SSL_FLAG),
+ SECMOD_ARG_ENTRY(TLS,SECMOD_TLS_FLAG),
+ SECMOD_ARG_ENTRY(AES,SECMOD_AES_FLAG),
+ SECMOD_ARG_ENTRY(Camellia,SECMOD_CAMELLIA_FLAG),
+ SECMOD_ARG_ENTRY(PublicCerts,SECMOD_FRIENDLY_FLAG),
+ SECMOD_ARG_ENTRY(RANDOM,SECMOD_RANDOM_FLAG),
+};
+
+#define SECMOD_HANDLE_STRING_ARG(param,target,value,command) \
+ if (PORT_Strncasecmp(param,value,sizeof(value)-1) == 0) { \
+ param += sizeof(value)-1; \
+ if (target) PORT_Free(target); \
+ target = secmod_argFetchValue(param,&next); \
+ param += next; \
+ command ;\
+ } else
+
+#define SECMOD_HANDLE_FINAL_ARG(param) \
+ { param = secmod_argSkipParameter(param); } param = secmod_argStrip(param);
+
+
+static int secmod_argSlotFlagTableSize =
+ sizeof(secmod_argSlotFlagTable)/sizeof(secmod_argSlotFlagTable[0]);
+
+
+static PRBool secmod_argGetPair(char c) {
+ switch (c) {
+ case '\'': return c;
+ case '\"': return c;
+ case '<': return '>';
+ case '{': return '}';
+ case '[': return ']';
+ case '(': return ')';
+ default: break;
+ }
+ return ' ';
+}
+
+static PRBool secmod_argIsBlank(char c) {
+ return isspace(c);
+}
+
+static PRBool secmod_argIsEscape(char c) {
+ return c == '\\';
+}
+
+static PRBool secmod_argIsQuote(char c) {
+ switch (c) {
+ case '\'':
+ case '\"':
+ case '<':
+ case '{': /* } end curly to keep vi bracket matching working */
+ case '(': /* ) */
+ case '[': /* ] */ return PR_TRUE;
+ default: break;
+ }
+ return PR_FALSE;
+}
+
+static PRBool secmod_argHasChar(char *v, char c)
+{
+ for ( ;*v; v++) {
+ if (*v == c) return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+static PRBool secmod_argHasBlanks(char *v)
+{
+ for ( ;*v; v++) {
+ if (secmod_argIsBlank(*v)) return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+static char *secmod_argStrip(char *c) {
+ while (*c && secmod_argIsBlank(*c)) c++;
+ return c;
+}
+
+static char *
+secmod_argFindEnd(char *string) {
+ char endChar = ' ';
+ PRBool lastEscape = PR_FALSE;
+
+ if (secmod_argIsQuote(*string)) {
+ endChar = secmod_argGetPair(*string);
+ string++;
+ }
+
+ for (;*string; string++) {
+ if (lastEscape) {
+ lastEscape = PR_FALSE;
+ continue;
+ }
+ if (secmod_argIsEscape(*string) && !lastEscape) {
+ lastEscape = PR_TRUE;
+ continue;
+ }
+ if ((endChar == ' ') && secmod_argIsBlank(*string)) break;
+ if (*string == endChar) {
+ break;
+ }
+ }
+
+ return string;
+}
+
+static char *
+secmod_argFetchValue(char *string, int *pcount)
+{
+ char *end = secmod_argFindEnd(string);
+ char *retString, *copyString;
+ PRBool lastEscape = PR_FALSE;
+ int len;
+
+ len = end - string;
+ if (len == 0) {
+ *pcount = 0;
+ return NULL;
+ }
+
+ copyString = retString = (char *)PORT_Alloc(len+1);
+
+ if (*end) len++;
+ *pcount = len;
+ if (retString == NULL) return NULL;
+
+
+ if (secmod_argIsQuote(*string)) string++;
+ for (; string < end; string++) {
+ if (secmod_argIsEscape(*string) && !lastEscape) {
+ lastEscape = PR_TRUE;
+ continue;
+ }
+ lastEscape = PR_FALSE;
+ *copyString++ = *string;
+ }
+ *copyString = 0;
+ return retString;
+}
+
+static char *
+secmod_argSkipParameter(char *string)
+{
+ char *end;
+ /* look for the end of the <name>= */
+ for (;*string; string++) {
+ if (*string == '=') { string++; break; }
+ if (secmod_argIsBlank(*string)) return(string);
+ }
+
+ end = secmod_argFindEnd(string);
+ if (*end) end++;
+ return end;
+}
+
+
+static SECStatus
+secmod_argParseModuleSpec(char *modulespec, char **lib, char **mod,
+ char **parameters, char **nss)
+{
+ int next;
+ modulespec = secmod_argStrip(modulespec);
+
+ *lib = *mod = *parameters = *nss = 0;
+
+ while (*modulespec) {
+ SECMOD_HANDLE_STRING_ARG(modulespec,*lib,SECMOD_ARG_LIBRARY_PARAMETER,;)
+ SECMOD_HANDLE_STRING_ARG(modulespec,*mod,SECMOD_ARG_NAME_PARAMETER,;)
+ SECMOD_HANDLE_STRING_ARG(modulespec,*parameters,
+ SECMOD_ARG_MODULE_PARAMETER,;)
+ SECMOD_HANDLE_STRING_ARG(modulespec,*nss,SECMOD_ARG_NSS_PARAMETER,;)
+ SECMOD_HANDLE_FINAL_ARG(modulespec)
+ }
+ return SECSuccess;
+}
+
+
+static char *
+secmod_argGetParamValue(char *paramName,char *parameters)
+{
+ char searchValue[256];
+ int paramLen = strlen(paramName);
+ char *returnValue = NULL;
+ int next;
+
+ if ((parameters == NULL) || (*parameters == 0)) return NULL;
+
+ PORT_Assert(paramLen+2 < sizeof(searchValue));
+
+ PORT_Strcpy(searchValue,paramName);
+ PORT_Strcat(searchValue,"=");
+ while (*parameters) {
+ if (PORT_Strncasecmp(parameters,searchValue,paramLen+1) == 0) {
+ parameters += paramLen+1;
+ returnValue = secmod_argFetchValue(parameters,&next);
+ break;
+ } else {
+ parameters = secmod_argSkipParameter(parameters);
+ }
+ parameters = secmod_argStrip(parameters);
+ }
+ return returnValue;
+}
+
+
+static char *
+secmod_argNextFlag(char *flags)
+{
+ for (; *flags ; flags++) {
+ if (*flags == ',') {
+ flags++;
+ break;
+ }
+ }
+ return flags;
+}
+
+static PRBool
+secmod_argHasFlag(char *label, char *flag, char *parameters)
+{
+ char *flags,*index;
+ int len = strlen(flag);
+ PRBool found = PR_FALSE;
+
+ flags = secmod_argGetParamValue(label,parameters);
+ if (flags == NULL) return PR_FALSE;
+
+ for (index=flags; *index; index=secmod_argNextFlag(index)) {
+ if (PORT_Strncasecmp(index,flag,len) == 0) {
+ found=PR_TRUE;
+ break;
+ }
+ }
+ PORT_Free(flags);
+ return found;
+}
+
+static void
+secmod_argSetNewCipherFlags(unsigned long *newCiphers,char *cipherList)
+{
+ newCiphers[0] = newCiphers[1] = 0;
+ if ((cipherList == NULL) || (*cipherList == 0)) return;
+
+ for (;*cipherList; cipherList=secmod_argNextFlag(cipherList)) {
+ if (PORT_Strncasecmp(cipherList,SECMOD_ARG_FORTEZZA_FLAG,
+ sizeof(SECMOD_ARG_FORTEZZA_FLAG)-1) == 0) {
+ newCiphers[0] |= SECMOD_FORTEZZA_FLAG;
+ }
+
+ /* add additional flags here as necessary */
+ /* direct bit mapping escape */
+ if (*cipherList == 0) {
+ if (cipherList[1] == 'l') {
+ newCiphers[1] |= atoi(&cipherList[2]);
+ } else {
+ newCiphers[0] |= atoi(&cipherList[2]);
+ }
+ }
+ }
+}
+
+
+/*
+ * decode a number. handle octal (leading '0'), hex (leading '0x') or decimal
+ */
+static long
+secmod_argDecodeNumber(char *num)
+{
+ int radix = 10;
+ unsigned long value = 0;
+ long retValue = 0;
+ int sign = 1;
+ int digit;
+
+ if (num == NULL) return retValue;
+
+ num = secmod_argStrip(num);
+
+ if (*num == '-') {
+ sign = -1;
+ num++;
+ }
+
+ if (*num == '0') {
+ radix = 8;
+ num++;
+ if ((*num == 'x') || (*num == 'X')) {
+ radix = 16;
+ num++;
+ }
+ }
+
+
+ for ( ;*num; num++ ) {
+ if (isdigit(*num)) {
+ digit = *num - '0';
+ } else if ((*num >= 'a') && (*num <= 'f')) {
+ digit = *num - 'a' + 10;
+ } else if ((*num >= 'A') && (*num <= 'F')) {
+ digit = *num - 'A' + 10;
+ } else {
+ break;
+ }
+ if (digit >= radix) break;
+ value = value*radix + digit;
+ }
+
+ retValue = ((int) value) * sign;
+ return retValue;
+}
+
+static long
+secmod_argReadLong(char *label,char *params, long defValue, PRBool *isdefault)
+{
+ char *value;
+ long retValue;
+ if (isdefault) *isdefault = PR_FALSE;
+
+ value = secmod_argGetParamValue(label,params);
+ if (value == NULL) {
+ if (isdefault) *isdefault = PR_TRUE;
+ return defValue;
+ }
+ retValue = secmod_argDecodeNumber(value);
+ if (value) PORT_Free(value);
+
+ return retValue;
+}
+
+
+static unsigned long
+secmod_argSlotFlags(char *label,char *params)
+{
+ char *flags,*index;
+ unsigned long retValue = 0;
+ int i;
+ PRBool all = PR_FALSE;
+
+ flags = secmod_argGetParamValue(label,params);
+ if (flags == NULL) return 0;
+
+ if (PORT_Strcasecmp(flags,"all") == 0) all = PR_TRUE;
+
+ for (index=flags; *index; index=secmod_argNextFlag(index)) {
+ for (i=0; i < secmod_argSlotFlagTableSize; i++) {
+ if (all || (PORT_Strncasecmp(index, secmod_argSlotFlagTable[i].name,
+ secmod_argSlotFlagTable[i].len) == 0)) {
+ retValue |= secmod_argSlotFlagTable[i].value;
+ }
+ }
+ }
+ PORT_Free(flags);
+ return retValue;
+}
+
+
+static void
+secmod_argDecodeSingleSlotInfo(char *name, char *params,
+ PK11PreSlotInfo *slotInfo)
+{
+ char *askpw;
+
+ slotInfo->slotID=secmod_argDecodeNumber(name);
+ slotInfo->defaultFlags=secmod_argSlotFlags("slotFlags",params);
+ slotInfo->timeout=secmod_argReadLong("timeout",params, 0, NULL);
+
+ askpw = secmod_argGetParamValue("askpw",params);
+ slotInfo->askpw = 0;
+
+ if (askpw) {
+ if (PORT_Strcasecmp(askpw,"every") == 0) {
+ slotInfo->askpw = -1;
+ } else if (PORT_Strcasecmp(askpw,"timeout") == 0) {
+ slotInfo->askpw = 1;
+ }
+ PORT_Free(askpw);
+ slotInfo->defaultFlags |= PK11_OWN_PW_DEFAULTS;
+ }
+ slotInfo->hasRootCerts = secmod_argHasFlag("rootFlags", "hasRootCerts",
+ params);
+ slotInfo->hasRootTrust = secmod_argHasFlag("rootFlags", "hasRootTrust",
+ params);
+}
+
+static char *
+secmod_argGetName(char *inString, int *next)
+{
+ char *name=NULL;
+ char *string;
+ int len;
+
+ /* look for the end of the <name>= */
+ for (string = inString;*string; string++) {
+ if (*string == '=') { break; }
+ if (secmod_argIsBlank(*string)) break;
+ }
+
+ len = string - inString;
+
+ *next = len;
+ if (*string == '=') (*next) += 1;
+ if (len > 0) {
+ name = PORT_Alloc(len+1);
+ PORT_Strncpy(name,inString,len);
+ name[len] = 0;
+ }
+ return name;
+}
+
+static PK11PreSlotInfo *
+secmod_argParseSlotInfo(PRArenaPool *arena, char *slotParams, int *retCount)
+{
+ char *slotIndex;
+ PK11PreSlotInfo *slotInfo = NULL;
+ int i=0,count = 0,next;
+
+ *retCount = 0;
+ if ((slotParams == NULL) || (*slotParams == 0)) return NULL;
+
+ /* first count the number of slots */
+ for (slotIndex = secmod_argStrip(slotParams); *slotIndex;
+ slotIndex = secmod_argStrip(secmod_argSkipParameter(slotIndex))) {
+ count++;
+ }
+
+ /* get the data structures */
+ if (arena) {
+ slotInfo = (PK11PreSlotInfo *)
+ PORT_ArenaAlloc(arena,count*sizeof(PK11PreSlotInfo));
+ PORT_Memset(slotInfo,0,count*sizeof(PK11PreSlotInfo));
+ } else {
+ slotInfo = (PK11PreSlotInfo *)
+ PORT_ZAlloc(count*sizeof(PK11PreSlotInfo));
+ }
+ if (slotInfo == NULL) return NULL;
+
+ for (slotIndex = secmod_argStrip(slotParams), i = 0;
+ *slotIndex && i < count ; ) {
+ char *name;
+ name = secmod_argGetName(slotIndex,&next);
+ slotIndex += next;
+
+ if (!secmod_argIsBlank(*slotIndex)) {
+ char *args = secmod_argFetchValue(slotIndex,&next);
+ slotIndex += next;
+ if (args) {
+ secmod_argDecodeSingleSlotInfo(name,args,&slotInfo[i]);
+ i++;
+ PORT_Free(args);
+ }
+ }
+ if (name) PORT_Free(name);
+ slotIndex = secmod_argStrip(slotIndex);
+ }
+ *retCount = i;
+ return slotInfo;
+}
+
+static char *secmod_nullString = "";
+
+static char *
+secmod_formatValue(PRArenaPool *arena, char *value, char quote)
+{
+ char *vp,*vp2,*retval;
+ int size = 0, escapes = 0;
+
+ for (vp=value; *vp ;vp++) {
+ if ((*vp == quote) || (*vp == SECMOD_ARG_ESCAPE)) escapes++;
+ size++;
+ }
+ if (arena) {
+ retval = PORT_ArenaZAlloc(arena,size+escapes+1);
+ } else {
+ retval = PORT_ZAlloc(size+escapes+1);
+ }
+ if (retval == NULL) return NULL;
+ vp2 = retval;
+ for (vp=value; *vp; vp++) {
+ if ((*vp == quote) || (*vp == SECMOD_ARG_ESCAPE))
+ *vp2++ = SECMOD_ARG_ESCAPE;
+ *vp2++ = *vp;
+ }
+ return retval;
+}
+
+static char *secmod_formatPair(char *name,char *value, char quote)
+{
+ char openQuote = quote;
+ char closeQuote = secmod_argGetPair(quote);
+ char *newValue = NULL;
+ char *returnValue;
+ PRBool need_quote = PR_FALSE;
+
+ if (!value || (*value == 0)) return secmod_nullString;
+
+ if (secmod_argHasBlanks(value) || secmod_argIsQuote(value[0]))
+ need_quote=PR_TRUE;
+
+ if ((need_quote && secmod_argHasChar(value,closeQuote))
+ || secmod_argHasChar(value,SECMOD_ARG_ESCAPE)) {
+ value = newValue = secmod_formatValue(NULL, value,quote);
+ if (newValue == NULL) return secmod_nullString;
+ }
+ if (need_quote) {
+ returnValue = PR_smprintf("%s=%c%s%c",name,openQuote,value,closeQuote);
+ } else {
+ returnValue = PR_smprintf("%s=%s",name,value);
+ }
+ if (returnValue == NULL) returnValue = secmod_nullString;
+
+ if (newValue) PORT_Free(newValue);
+
+ return returnValue;
+}
+
+static char *secmod_formatIntPair(char *name, unsigned long value,
+ unsigned long def)
+{
+ char *returnValue;
+
+ if (value == def) return secmod_nullString;
+
+ returnValue = PR_smprintf("%s=%d",name,value);
+
+ return returnValue;
+}
+
+static void
+secmod_freePair(char *pair)
+{
+ if (pair && pair != secmod_nullString) {
+ PR_smprintf_free(pair);
+ }
+}
+
+#define MAX_FLAG_SIZE sizeof("internal")+sizeof("FIPS")+sizeof("moduleDB")+\
+ sizeof("moduleDBOnly")+sizeof("critical")
+static char *
+secmod_mkNSSFlags(PRBool internal, PRBool isFIPS,
+ PRBool isModuleDB, PRBool isModuleDBOnly, PRBool isCritical)
+{
+ char *flags = (char *)PORT_ZAlloc(MAX_FLAG_SIZE);
+ PRBool first = PR_TRUE;
+
+ PORT_Memset(flags,0,MAX_FLAG_SIZE);
+ if (internal) {
+ PORT_Strcat(flags,"internal");
+ first = PR_FALSE;
+ }
+ if (isFIPS) {
+ if (!first) PORT_Strcat(flags,",");
+ PORT_Strcat(flags,"FIPS");
+ first = PR_FALSE;
+ }
+ if (isModuleDB) {
+ if (!first) PORT_Strcat(flags,",");
+ PORT_Strcat(flags,"moduleDB");
+ first = PR_FALSE;
+ }
+ if (isModuleDBOnly) {
+ if (!first) PORT_Strcat(flags,",");
+ PORT_Strcat(flags,"moduleDBOnly");
+ first = PR_FALSE;
+ }
+ if (isCritical) {
+ if (!first) PORT_Strcat(flags,",");
+ PORT_Strcat(flags,"critical");
+ first = PR_FALSE;
+ }
+ return flags;
+}
+
+static char *
+secmod_mkCipherFlags(unsigned long ssl0, unsigned long ssl1)
+{
+ char *cipher = NULL;
+ int i;
+
+ for (i=0; i < sizeof(ssl0)*8; i++) {
+ if (ssl0 & (1<<i)) {
+ char *string;
+ if ((1<<i) == SECMOD_FORTEZZA_FLAG) {
+ string = PR_smprintf("%s","FORTEZZA");
+ } else {
+ string = PR_smprintf("0h0x%08x",1<<i);
+ }
+ if (cipher) {
+ char *tmp;
+ tmp = PR_smprintf("%s,%s",cipher,string);
+ PR_smprintf_free(cipher);
+ PR_smprintf_free(string);
+ cipher = tmp;
+ } else {
+ cipher = string;
+ }
+ }
+ }
+ for (i=0; i < sizeof(ssl0)*8; i++) {
+ if (ssl1 & (1<<i)) {
+ if (cipher) {
+ char *tmp;
+ tmp = PR_smprintf("%s,0l0x%08x",cipher,1<<i);
+ PR_smprintf_free(cipher);
+ cipher = tmp;
+ } else {
+ cipher = PR_smprintf("0l0x%08x",1<<i);
+ }
+ }
+ }
+
+ return cipher;
+}
+
+static char *
+secmod_mkSlotFlags(unsigned long defaultFlags)
+{
+ char *flags=NULL;
+ int i,j;
+
+ for (i=0; i < sizeof(defaultFlags)*8; i++) {
+ if (defaultFlags & (1<<i)) {
+ char *string = NULL;
+
+ for (j=0; j < secmod_argSlotFlagTableSize; j++) {
+ if (secmod_argSlotFlagTable[j].value == ( 1UL << i )) {
+ string = secmod_argSlotFlagTable[j].name;
+ break;
+ }
+ }
+ if (string) {
+ if (flags) {
+ char *tmp;
+ tmp = PR_smprintf("%s,%s",flags,string);
+ PR_smprintf_free(flags);
+ flags = tmp;
+ } else {
+ flags = PR_smprintf("%s",string);
+ }
+ }
+ }
+ }
+
+ return flags;
+}
+
+#define SECMOD_MAX_ROOT_FLAG_SIZE sizeof("hasRootCerts")+sizeof("hasRootTrust")
+
+static char *
+secmod_mkRootFlags(PRBool hasRootCerts, PRBool hasRootTrust)
+{
+ char *flags= (char *)PORT_ZAlloc(SECMOD_MAX_ROOT_FLAG_SIZE);
+ PRBool first = PR_TRUE;
+
+ PORT_Memset(flags,0,SECMOD_MAX_ROOT_FLAG_SIZE);
+ if (hasRootCerts) {
+ PORT_Strcat(flags,"hasRootCerts");
+ first = PR_FALSE;
+ }
+ if (hasRootTrust) {
+ if (!first) PORT_Strcat(flags,",");
+ PORT_Strcat(flags,"hasRootTrust");
+ first = PR_FALSE;
+ }
+ return flags;
+}
+
+static char *
+secmod_mkSlotString(unsigned long slotID, unsigned long defaultFlags,
+ unsigned long timeout, unsigned char askpw_in,
+ PRBool hasRootCerts, PRBool hasRootTrust) {
+ char *askpw,*flags,*rootFlags,*slotString;
+ char *flagPair,*rootFlagsPair;
+
+ switch (askpw_in) {
+ case 0xff:
+ askpw = "every";
+ break;
+ case 1:
+ askpw = "timeout";
+ break;
+ default:
+ askpw = "any";
+ break;
+ }
+ flags = secmod_mkSlotFlags(defaultFlags);
+ rootFlags = secmod_mkRootFlags(hasRootCerts,hasRootTrust);
+ flagPair=secmod_formatPair("slotFlags",flags,'\'');
+ rootFlagsPair=secmod_formatPair("rootFlags",rootFlags,'\'');
+ if (flags) PR_smprintf_free(flags);
+ if (rootFlags) PORT_Free(rootFlags);
+ if (defaultFlags & PK11_OWN_PW_DEFAULTS) {
+ slotString = PR_smprintf("0x%08lx=[%s askpw=%s timeout=%d %s]",
+ (PRUint32)slotID,flagPair,askpw,timeout,
+ rootFlagsPair);
+ } else {
+ slotString = PR_smprintf("0x%08lx=[%s %s]",
+ (PRUint32)slotID,flagPair,rootFlagsPair);
+ }
+ secmod_freePair(flagPair);
+ secmod_freePair(rootFlagsPair);
+ return slotString;
+}
+
+static char *
+secmod_mkNSS(char **slotStrings, int slotCount, PRBool internal, PRBool isFIPS,
+ PRBool isModuleDB, PRBool isModuleDBOnly, PRBool isCritical,
+ unsigned long trustOrder, unsigned long cipherOrder,
+ unsigned long ssl0, unsigned long ssl1) {
+ int slotLen, i;
+ char *slotParams, *ciphers, *nss, *nssFlags, *tmp;
+ char *trustOrderPair,*cipherOrderPair,*slotPair,*cipherPair,*flagPair;
+
+
+ /* now let's build up the string
+ * first the slot infos
+ */
+ slotLen=0;
+ for (i=0; i < (int)slotCount; i++) {
+ slotLen += PORT_Strlen(slotStrings[i])+1;
+ }
+ slotLen += 1; /* space for the final NULL */
+
+ slotParams = (char *)PORT_ZAlloc(slotLen);
+ PORT_Memset(slotParams,0,slotLen);
+ for (i=0; i < (int)slotCount; i++) {
+ PORT_Strcat(slotParams,slotStrings[i]);
+ PORT_Strcat(slotParams," ");
+ PR_smprintf_free(slotStrings[i]);
+ slotStrings[i]=NULL;
+ }
+
+ /*
+ * now the NSS structure
+ */
+ nssFlags = secmod_mkNSSFlags(internal,isFIPS,isModuleDB,isModuleDBOnly,
+ isCritical);
+ /* for now only the internal module is critical */
+ ciphers = secmod_mkCipherFlags(ssl0, ssl1);
+
+ trustOrderPair=secmod_formatIntPair("trustOrder",trustOrder,
+ SECMOD_DEFAULT_TRUST_ORDER);
+ cipherOrderPair=secmod_formatIntPair("cipherOrder",cipherOrder,
+ SECMOD_DEFAULT_CIPHER_ORDER);
+ slotPair=secmod_formatPair("slotParams",slotParams,'{'); /* } */
+ if (slotParams) PORT_Free(slotParams);
+ cipherPair=secmod_formatPair("ciphers",ciphers,'\'');
+ if (ciphers) PR_smprintf_free(ciphers);
+ flagPair=secmod_formatPair("Flags",nssFlags,'\'');
+ if (nssFlags) PORT_Free(nssFlags);
+ nss = PR_smprintf("%s %s %s %s %s",trustOrderPair,
+ cipherOrderPair,slotPair,cipherPair,flagPair);
+ secmod_freePair(trustOrderPair);
+ secmod_freePair(cipherOrderPair);
+ secmod_freePair(slotPair);
+ secmod_freePair(cipherPair);
+ secmod_freePair(flagPair);
+ tmp = secmod_argStrip(nss);
+ if (*tmp == '\0') {
+ PR_smprintf_free(nss);
+ nss = NULL;
+ }
+ return nss;
+}
+
+static char *
+secmod_mkNewModuleSpec(char *dllName, char *commonName, char *parameters,
+ char *NSS) {
+ char *moduleSpec;
+ char *lib,*name,*param,*nss;
+
+ /*
+ * now the final spec
+ */
+ lib = secmod_formatPair("library",dllName,'\"');
+ name = secmod_formatPair("name",commonName,'\"');
+ param = secmod_formatPair("parameters",parameters,'\"');
+ nss = secmod_formatPair("NSS",NSS,'\"');
+ moduleSpec = PR_smprintf("%s %s %s %s", lib,name,param,nss);
+ secmod_freePair(lib);
+ secmod_freePair(name);
+ secmod_freePair(param);
+ secmod_freePair(nss);
+ return (moduleSpec);
+}
+
diff --git a/security/nss/lib/softoken/pkcs11.c b/security/nss/lib/softoken/pkcs11.c
new file mode 100644
index 000000000..3f0771b33
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11.c
@@ -0,0 +1,4218 @@
+/* ***** 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):
+ * Dr Stephen Henson <stephen.henson@gemplus.com>
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * This file implements PKCS 11 on top of our existing security modules
+ *
+ * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
+ * This implementation has two slots:
+ * slot 1 is our generic crypto support. It does not require login.
+ * It supports Public Key ops, and all they bulk ciphers and hashes.
+ * It can also support Private Key ops for imported Private keys. It does
+ * not have any token storage.
+ * slot 2 is our private key support. It requires a login before use. It
+ * can store Private Keys and Certs as token objects. Currently only private
+ * keys and their associated Certificates are saved on the token.
+ *
+ * In this implementation, session objects are only visible to the session
+ * that created or generated them.
+ */
+#include "seccomon.h"
+#include "secitem.h"
+#include "pkcs11.h"
+#include "pkcs11i.h"
+#include "softoken.h"
+#include "lowkeyi.h"
+#include "blapi.h"
+#include "secder.h"
+#include "secport.h"
+#include "secrng.h"
+#include "nss.h"
+#include "prtypes.h"
+#include "nspr.h"
+#include "softkver.h"
+#include "secoid.h"
+#include "sftkdb.h"
+#include "sftkpars.h"
+
+/*
+ * ******************** Static data *******************************
+ */
+
+/* The next three strings must be exactly 32 characters long */
+static char *manufacturerID = "Mozilla Foundation ";
+static char manufacturerID_space[33];
+static char *libraryDescription = "NSS Internal Crypto Services ";
+static char libraryDescription_space[33];
+
+/*
+ * In FIPS mode, we disallow login attempts for 1 second after a login
+ * failure so that there are at most 60 login attempts per minute.
+ */
+static PRIntervalTime loginWaitTime;
+static PRUint32 minSessionObjectHandle = 1U;
+
+#define __PASTE(x,y) x##y
+
+/*
+ * we renamed all our internal functions, get the correct
+ * definitions for them...
+ */
+#undef CK_PKCS11_FUNCTION_INFO
+#undef CK_NEED_ARG_LIST
+
+#define CK_EXTERN extern
+#define CK_PKCS11_FUNCTION_INFO(func) \
+ CK_RV __PASTE(NS,func)
+#define CK_NEED_ARG_LIST 1
+
+#include "pkcs11f.h"
+
+
+
+/* build the crypto module table */
+static const CK_FUNCTION_LIST sftk_funcList = {
+ { 1, 10 },
+
+#undef CK_PKCS11_FUNCTION_INFO
+#undef CK_NEED_ARG_LIST
+
+#define CK_PKCS11_FUNCTION_INFO(func) \
+ __PASTE(NS,func),
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+#undef CK_NEED_ARG_LIST
+
+
+#undef __PASTE
+
+/* List of DES Weak Keys */
+typedef unsigned char desKey[8];
+static const desKey sftk_desWeakTable[] = {
+#ifdef noParity
+ /* weak */
+ { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ { 0x1e, 0x1e, 0x1e, 0x1e, 0x0e, 0x0e, 0x0e, 0x0e },
+ { 0xe0, 0xe0, 0xe0, 0xe0, 0xf0, 0xf0, 0xf0, 0xf0 },
+ { 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe },
+ /* semi-weak */
+ { 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe, 0x00, 0xfe },
+ { 0xfe, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0xfe },
+
+ { 0x1e, 0xe0, 0x1e, 0xe0, 0x0e, 0xf0, 0x0e, 0xf0 },
+ { 0xe0, 0x1e, 0xe0, 0x1e, 0xf0, 0x0e, 0xf0, 0x0e },
+
+ { 0x00, 0xe0, 0x00, 0xe0, 0x00, 0x0f, 0x00, 0x0f },
+ { 0xe0, 0x00, 0xe0, 0x00, 0xf0, 0x00, 0xf0, 0x00 },
+
+ { 0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe },
+ { 0xfe, 0x1e, 0xfe, 0x1e, 0xfe, 0x0e, 0xfe, 0x0e },
+
+ { 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e },
+ { 0x1e, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x0e, 0x00 },
+
+ { 0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0, 0xfe },
+ { 0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf0, 0xfe, 0xf0 },
+#else
+ /* weak */
+ { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 },
+ { 0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0x0e, 0x0e, 0x0e },
+ { 0xe0, 0xe0, 0xe0, 0xe0, 0xf1, 0xf1, 0xf1, 0xf1 },
+ { 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe },
+
+ /* semi-weak */
+ { 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe },
+ { 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01, 0xfe, 0x01 },
+
+ { 0x1f, 0xe0, 0x1f, 0xe0, 0x0e, 0xf1, 0x0e, 0xf1 },
+ { 0xe0, 0x1f, 0xe0, 0x1f, 0xf1, 0x0e, 0xf1, 0x0e },
+
+ { 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xf1, 0x01, 0xf1 },
+ { 0xe0, 0x01, 0xe0, 0x01, 0xf1, 0x01, 0xf1, 0x01 },
+
+ { 0x1f, 0xfe, 0x1f, 0xfe, 0x0e, 0xfe, 0x0e, 0xfe },
+ { 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x0e, 0xfe, 0x0e },
+
+ { 0x01, 0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e },
+ { 0x1f, 0x01, 0x1f, 0x01, 0x0e, 0x01, 0x0e, 0x01 },
+
+ { 0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1, 0xfe },
+ { 0xfe, 0xe0, 0xfe, 0xe0, 0xfe, 0xf1, 0xfe, 0xf1 }
+#endif
+};
+
+
+static const int sftk_desWeakTableSize = sizeof(sftk_desWeakTable)/
+ sizeof(sftk_desWeakTable[0]);
+
+/* DES KEY Parity conversion table. Takes each byte/2 as an index, returns
+ * that byte with the proper parity bit set */
+static const unsigned char parityTable[256] = {
+/* Even...0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e */
+/* E */ 0x01,0x02,0x04,0x07,0x08,0x0b,0x0d,0x0e,
+/* Odd....0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e */
+/* O */ 0x10,0x13,0x15,0x16,0x19,0x1a,0x1c,0x1f,
+/* Odd....0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e */
+/* O */ 0x20,0x23,0x25,0x26,0x29,0x2a,0x2c,0x2f,
+/* Even...0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e */
+/* E */ 0x31,0x32,0x34,0x37,0x38,0x3b,0x3d,0x3e,
+/* Odd....0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e */
+/* O */ 0x40,0x43,0x45,0x46,0x49,0x4a,0x4c,0x4f,
+/* Even...0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e */
+/* E */ 0x51,0x52,0x54,0x57,0x58,0x5b,0x5d,0x5e,
+/* Even...0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e */
+/* E */ 0x61,0x62,0x64,0x67,0x68,0x6b,0x6d,0x6e,
+/* Odd....0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e */
+/* O */ 0x70,0x73,0x75,0x76,0x79,0x7a,0x7c,0x7f,
+/* Odd....0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e */
+/* O */ 0x80,0x83,0x85,0x86,0x89,0x8a,0x8c,0x8f,
+/* Even...0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e */
+/* E */ 0x91,0x92,0x94,0x97,0x98,0x9b,0x9d,0x9e,
+/* Even...0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae */
+/* E */ 0xa1,0xa2,0xa4,0xa7,0xa8,0xab,0xad,0xae,
+/* Odd....0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe */
+/* O */ 0xb0,0xb3,0xb5,0xb6,0xb9,0xba,0xbc,0xbf,
+/* Even...0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce */
+/* E */ 0xc1,0xc2,0xc4,0xc7,0xc8,0xcb,0xcd,0xce,
+/* Odd....0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde */
+/* O */ 0xd0,0xd3,0xd5,0xd6,0xd9,0xda,0xdc,0xdf,
+/* Odd....0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee */
+/* O */ 0xe0,0xe3,0xe5,0xe6,0xe9,0xea,0xec,0xef,
+/* Even...0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe */
+/* E */ 0xf1,0xf2,0xf4,0xf7,0xf8,0xfb,0xfd,0xfe,
+};
+
+/* Mechanisms */
+struct mechanismList {
+ CK_MECHANISM_TYPE type;
+ CK_MECHANISM_INFO info;
+ PRBool privkey;
+};
+
+/*
+ * the following table includes a complete list of mechanism defined by
+ * PKCS #11 version 2.01. Those Mechanisms not supported by this PKCS #11
+ * module are ifdef'ed out.
+ */
+#define CKF_EN_DE CKF_ENCRYPT | CKF_DECRYPT
+#define CKF_WR_UN CKF_WRAP | CKF_UNWRAP
+#define CKF_SN_VR CKF_SIGN | CKF_VERIFY
+#define CKF_SN_RE CKF_SIGN_RECOVER | CKF_VERIFY_RECOVER
+
+#define CKF_EN_DE_WR_UN CKF_EN_DE | CKF_WR_UN
+#define CKF_SN_VR_RE CKF_SN_VR | CKF_SN_RE
+#define CKF_DUZ_IT_ALL CKF_EN_DE_WR_UN | CKF_SN_VR_RE
+
+#define CKF_EC_PNU CKF_EC_FP | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS
+
+#define CKF_EC_BPNU CKF_EC_F_2M | CKF_EC_PNU
+
+#define CK_MAX 0xffffffff
+
+static const struct mechanismList mechanisms[] = {
+
+ /*
+ * PKCS #11 Mechanism List.
+ *
+ * The first argument is the PKCS #11 Mechanism we support.
+ * The second argument is Mechanism info structure. It includes:
+ * The minimum key size,
+ * in bits for RSA, DSA, DH, EC*, KEA, RC2 and RC4 * algs.
+ * in bytes for RC5, AES, Camellia, and CAST*
+ * ignored for DES*, IDEA and FORTEZZA based
+ * The maximum key size,
+ * in bits for RSA, DSA, DH, EC*, KEA, RC2 and RC4 * algs.
+ * in bytes for RC5, AES, Camellia, and CAST*
+ * ignored for DES*, IDEA and FORTEZZA based
+ * Flags
+ * What operations are supported by this mechanism.
+ * The third argument is a bool which tells if this mechanism is
+ * supported in the database token.
+ *
+ */
+
+ /* ------------------------- RSA Operations ---------------------------*/
+ {CKM_RSA_PKCS_KEY_PAIR_GEN,{RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_GENERATE_KEY_PAIR},PR_TRUE},
+ {CKM_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_DUZ_IT_ALL}, PR_TRUE},
+#ifdef SFTK_RSA9796_SUPPORTED
+ {CKM_RSA_9796, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_DUZ_IT_ALL}, PR_TRUE},
+#endif
+ {CKM_RSA_X_509, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_DUZ_IT_ALL}, PR_TRUE},
+ /* -------------- RSA Multipart Signing Operations -------------------- */
+ {CKM_MD2_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_SN_VR}, PR_TRUE},
+ {CKM_MD5_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA1_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA256_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA384_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA512_RSA_PKCS, {RSA_MIN_MODULUS_BITS,CK_MAX,
+ CKF_SN_VR}, PR_TRUE},
+ /* ------------------------- DSA Operations --------------------------- */
+ {CKM_DSA_KEY_PAIR_GEN, {DSA_MIN_P_BITS, DSA_MAX_P_BITS,
+ CKF_GENERATE_KEY_PAIR}, PR_TRUE},
+ {CKM_DSA, {DSA_MIN_P_BITS, DSA_MAX_P_BITS,
+ CKF_SN_VR}, PR_TRUE},
+ {CKM_DSA_SHA1, {DSA_MIN_P_BITS, DSA_MAX_P_BITS,
+ CKF_SN_VR}, PR_TRUE},
+ /* -------------------- Diffie Hellman Operations --------------------- */
+ /* no diffie hellman yet */
+ {CKM_DH_PKCS_KEY_PAIR_GEN, {DH_MIN_P_BITS, DH_MAX_P_BITS,
+ CKF_GENERATE_KEY_PAIR}, PR_TRUE},
+ {CKM_DH_PKCS_DERIVE, {DH_MIN_P_BITS, DH_MAX_P_BITS,
+ CKF_DERIVE}, PR_TRUE},
+#ifdef NSS_ENABLE_ECC
+ /* -------------------- Elliptic Curve Operations --------------------- */
+ {CKM_EC_KEY_PAIR_GEN, {112, 571, CKF_GENERATE_KEY_PAIR|CKF_EC_BPNU}, PR_TRUE},
+ {CKM_ECDH1_DERIVE, {112, 571, CKF_DERIVE|CKF_EC_BPNU}, PR_TRUE},
+ {CKM_ECDSA, {112, 571, CKF_SN_VR|CKF_EC_BPNU}, PR_TRUE},
+ {CKM_ECDSA_SHA1, {112, 571, CKF_SN_VR|CKF_EC_BPNU}, PR_TRUE},
+#endif /* NSS_ENABLE_ECC */
+ /* ------------------------- RC2 Operations --------------------------- */
+ {CKM_RC2_KEY_GEN, {1, 128, CKF_GENERATE}, PR_TRUE},
+ {CKM_RC2_ECB, {1, 128, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_RC2_CBC, {1, 128, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_RC2_MAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_RC2_MAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_RC2_CBC_PAD, {1, 128, CKF_EN_DE_WR_UN}, PR_TRUE},
+ /* ------------------------- RC4 Operations --------------------------- */
+ {CKM_RC4_KEY_GEN, {1, 256, CKF_GENERATE}, PR_FALSE},
+ {CKM_RC4, {1, 256, CKF_EN_DE_WR_UN}, PR_FALSE},
+ /* ------------------------- DES Operations --------------------------- */
+ {CKM_DES_KEY_GEN, { 8, 8, CKF_GENERATE}, PR_TRUE},
+ {CKM_DES_ECB, { 8, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_DES_CBC, { 8, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_DES_MAC, { 8, 8, CKF_SN_VR}, PR_TRUE},
+ {CKM_DES_MAC_GENERAL, { 8, 8, CKF_SN_VR}, PR_TRUE},
+ {CKM_DES_CBC_PAD, { 8, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_DES2_KEY_GEN, {24, 24, CKF_GENERATE}, PR_TRUE},
+ {CKM_DES3_KEY_GEN, {24, 24, CKF_GENERATE}, PR_TRUE },
+ {CKM_DES3_ECB, {24, 24, CKF_EN_DE_WR_UN}, PR_TRUE },
+ {CKM_DES3_CBC, {24, 24, CKF_EN_DE_WR_UN}, PR_TRUE },
+ {CKM_DES3_MAC, {24, 24, CKF_SN_VR}, PR_TRUE },
+ {CKM_DES3_MAC_GENERAL, {24, 24, CKF_SN_VR}, PR_TRUE },
+ {CKM_DES3_CBC_PAD, {24, 24, CKF_EN_DE_WR_UN}, PR_TRUE },
+ /* ------------------------- CDMF Operations --------------------------- */
+ {CKM_CDMF_KEY_GEN, {8, 8, CKF_GENERATE}, PR_TRUE},
+ {CKM_CDMF_ECB, {8, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CDMF_CBC, {8, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CDMF_MAC, {8, 8, CKF_SN_VR}, PR_TRUE},
+ {CKM_CDMF_MAC_GENERAL, {8, 8, CKF_SN_VR}, PR_TRUE},
+ {CKM_CDMF_CBC_PAD, {8, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ /* ------------------------- AES Operations --------------------------- */
+ {CKM_AES_KEY_GEN, {16, 32, CKF_GENERATE}, PR_TRUE},
+ {CKM_AES_ECB, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_AES_CBC, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_AES_MAC, {16, 32, CKF_SN_VR}, PR_TRUE},
+ {CKM_AES_MAC_GENERAL, {16, 32, CKF_SN_VR}, PR_TRUE},
+ {CKM_AES_CBC_PAD, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ /* ------------------------- Camellia Operations --------------------- */
+ {CKM_CAMELLIA_KEY_GEN, {16, 32, CKF_GENERATE}, PR_TRUE},
+ {CKM_CAMELLIA_ECB, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAMELLIA_CBC, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAMELLIA_MAC, {16, 32, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAMELLIA_MAC_GENERAL, {16, 32, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAMELLIA_CBC_PAD, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ /* ------------------------- Hashing Operations ----------------------- */
+ {CKM_MD2, {0, 0, CKF_DIGEST}, PR_FALSE},
+ {CKM_MD2_HMAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_MD2_HMAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_MD5, {0, 0, CKF_DIGEST}, PR_FALSE},
+ {CKM_MD5_HMAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_MD5_HMAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA_1, {0, 0, CKF_DIGEST}, PR_FALSE},
+ {CKM_SHA_1_HMAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA_1_HMAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA256, {0, 0, CKF_DIGEST}, PR_FALSE},
+ {CKM_SHA256_HMAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA256_HMAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA384, {0, 0, CKF_DIGEST}, PR_FALSE},
+ {CKM_SHA384_HMAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA384_HMAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA512, {0, 0, CKF_DIGEST}, PR_FALSE},
+ {CKM_SHA512_HMAC, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_SHA512_HMAC_GENERAL, {1, 128, CKF_SN_VR}, PR_TRUE},
+ {CKM_TLS_PRF_GENERAL, {0, 512, CKF_SN_VR}, PR_FALSE},
+ /* ------------------------- CAST Operations --------------------------- */
+#ifdef NSS_SOFTOKEN_DOES_CAST
+ /* Cast operations are not supported ( yet? ) */
+ {CKM_CAST_KEY_GEN, {1, 8, CKF_GENERATE}, PR_TRUE},
+ {CKM_CAST_ECB, {1, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST_CBC, {1, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST_MAC, {1, 8, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAST_MAC_GENERAL, {1, 8, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAST_CBC_PAD, {1, 8, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST3_KEY_GEN, {1, 16, CKF_GENERATE}, PR_TRUE},
+ {CKM_CAST3_ECB, {1, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST3_CBC, {1, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST3_MAC, {1, 16, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAST3_MAC_GENERAL, {1, 16, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAST3_CBC_PAD, {1, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST5_KEY_GEN, {1, 16, CKF_GENERATE}, PR_TRUE},
+ {CKM_CAST5_ECB, {1, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST5_CBC, {1, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_CAST5_MAC, {1, 16, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAST5_MAC_GENERAL, {1, 16, CKF_SN_VR}, PR_TRUE},
+ {CKM_CAST5_CBC_PAD, {1, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+#endif
+#if NSS_SOFTOKEN_DOES_RC5
+ /* ------------------------- RC5 Operations --------------------------- */
+ {CKM_RC5_KEY_GEN, {1, 32, CKF_GENERATE}, PR_TRUE},
+ {CKM_RC5_ECB, {1, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_RC5_CBC, {1, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_RC5_MAC, {1, 32, CKF_SN_VR}, PR_TRUE},
+ {CKM_RC5_MAC_GENERAL, {1, 32, CKF_SN_VR}, PR_TRUE},
+ {CKM_RC5_CBC_PAD, {1, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+#endif
+#ifdef NSS_SOFTOKEN_DOES_IDEA
+ /* ------------------------- IDEA Operations -------------------------- */
+ {CKM_IDEA_KEY_GEN, {16, 16, CKF_GENERATE}, PR_TRUE},
+ {CKM_IDEA_ECB, {16, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_IDEA_CBC, {16, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_IDEA_MAC, {16, 16, CKF_SN_VR}, PR_TRUE},
+ {CKM_IDEA_MAC_GENERAL, {16, 16, CKF_SN_VR}, PR_TRUE},
+ {CKM_IDEA_CBC_PAD, {16, 16, CKF_EN_DE_WR_UN}, PR_TRUE},
+#endif
+ /* --------------------- Secret Key Operations ------------------------ */
+ {CKM_GENERIC_SECRET_KEY_GEN, {1, 32, CKF_GENERATE}, PR_TRUE},
+ {CKM_CONCATENATE_BASE_AND_KEY, {1, 32, CKF_GENERATE}, PR_FALSE},
+ {CKM_CONCATENATE_BASE_AND_DATA, {1, 32, CKF_GENERATE}, PR_FALSE},
+ {CKM_CONCATENATE_DATA_AND_BASE, {1, 32, CKF_GENERATE}, PR_FALSE},
+ {CKM_XOR_BASE_AND_DATA, {1, 32, CKF_GENERATE}, PR_FALSE},
+ {CKM_EXTRACT_KEY_FROM_KEY, {1, 32, CKF_DERIVE}, PR_FALSE},
+ /* ---------------------- SSL Key Derivations ------------------------- */
+ {CKM_SSL3_PRE_MASTER_KEY_GEN, {48, 48, CKF_GENERATE}, PR_FALSE},
+ {CKM_SSL3_MASTER_KEY_DERIVE, {48, 48, CKF_DERIVE}, PR_FALSE},
+ {CKM_SSL3_MASTER_KEY_DERIVE_DH, {8, 128, CKF_DERIVE}, PR_FALSE},
+ {CKM_SSL3_KEY_AND_MAC_DERIVE, {48, 48, CKF_DERIVE}, PR_FALSE},
+ {CKM_SSL3_MD5_MAC, { 0, 16, CKF_DERIVE}, PR_FALSE},
+ {CKM_SSL3_SHA1_MAC, { 0, 20, CKF_DERIVE}, PR_FALSE},
+ {CKM_MD5_KEY_DERIVATION, { 0, 16, CKF_DERIVE}, PR_FALSE},
+ {CKM_MD2_KEY_DERIVATION, { 0, 16, CKF_DERIVE}, PR_FALSE},
+ {CKM_SHA1_KEY_DERIVATION, { 0, 20, CKF_DERIVE}, PR_FALSE},
+ {CKM_TLS_MASTER_KEY_DERIVE, {48, 48, CKF_DERIVE}, PR_FALSE},
+ {CKM_TLS_MASTER_KEY_DERIVE_DH, {8, 128, CKF_DERIVE}, PR_FALSE},
+ {CKM_TLS_KEY_AND_MAC_DERIVE, {48, 48, CKF_DERIVE}, PR_FALSE},
+ /* ---------------------- PBE Key Derivations ------------------------ */
+ {CKM_PBE_MD2_DES_CBC, {8, 8, CKF_DERIVE}, PR_TRUE},
+ {CKM_PBE_MD5_DES_CBC, {8, 8, CKF_DERIVE}, PR_TRUE},
+ /* ------------------ NETSCAPE PBE Key Derivations ------------------- */
+ {CKM_NETSCAPE_PBE_SHA1_DES_CBC, { 8, 8, CKF_GENERATE}, PR_TRUE},
+ {CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC, {24,24, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBE_SHA1_DES3_EDE_CBC, {24,24, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBE_SHA1_DES2_EDE_CBC, {24,24, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBE_SHA1_RC2_40_CBC, {40,40, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBE_SHA1_RC2_128_CBC, {128,128, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBE_SHA1_RC4_40, {40,40, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBE_SHA1_RC4_128, {128,128, CKF_GENERATE}, PR_TRUE},
+ {CKM_PBA_SHA1_WITH_SHA1_HMAC, {20,20, CKF_GENERATE}, PR_TRUE},
+ {CKM_PKCS5_PBKD2, {1,256, CKF_GENERATE}, PR_TRUE},
+ {CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN, {20,20, CKF_GENERATE}, PR_TRUE},
+ {CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN, {16,16, CKF_GENERATE}, PR_TRUE},
+ {CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN, {16,16, CKF_GENERATE}, PR_TRUE},
+ /* ------------------ AES Key Wrap (also encrypt) ------------------- */
+ {CKM_NETSCAPE_AES_KEY_WRAP, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+ {CKM_NETSCAPE_AES_KEY_WRAP_PAD, {16, 32, CKF_EN_DE_WR_UN}, PR_TRUE},
+};
+static const CK_ULONG mechanismCount = sizeof(mechanisms)/sizeof(mechanisms[0]);
+
+static PRBool nsc_init = PR_FALSE;
+
+#if defined(XP_UNIX) && !defined(NO_PTHREADS)
+
+#include <pthread.h>
+
+PRBool forked = PR_FALSE;
+
+void ForkedChild(void)
+{
+ if (nsc_init || nsf_init) {
+ forked = PR_TRUE;
+ }
+}
+
+#endif
+
+static char *
+sftk_setStringName(const char *inString, char *buffer, int buffer_length, PRBool nullTerminate)
+{
+ int full_length, string_length;
+
+ full_length = nullTerminate ? buffer_length -1 : buffer_length;
+ string_length = PORT_Strlen(inString);
+ /*
+ * shorten the string, respecting utf8 encoding
+ * to do so, we work backward from the end
+ * bytes looking from the end are either:
+ * - ascii [0x00,0x7f]
+ * - the [2-n]th byte of a multibyte sequence
+ * [0x3F,0xBF], i.e, most significant 2 bits are '10'
+ * - the first byte of a multibyte sequence [0xC0,0xFD],
+ * i.e, most significant 2 bits are '11'
+ *
+ * When the string is too long, we lop off any trailing '10' bytes,
+ * if any. When these are all eliminated we lop off
+ * one additional byte. Thus if we lopped any '10'
+ * we'll be lopping a '11' byte (the first byte of the multibyte sequence),
+ * otherwise we're lopping off an ascii character.
+ *
+ * To test for '10' bytes, we first AND it with
+ * 11000000 (0xc0) so that we get 10000000 (0x80) if and only if
+ * the byte starts with 10. We test for equality.
+ */
+ while ( string_length > full_length ) {
+ /* need to shorten */
+ while ( string_length > 0 &&
+ ((inString[string_length-1]&(char)0xc0) == (char)0x80)) {
+ /* lop off '10' byte */
+ string_length--;
+ }
+ /*
+ * test string_length in case bad data is received
+ * and string consisted of all '10' bytes,
+ * avoiding any infinite loop
+ */
+ if ( string_length ) {
+ /* remove either '11' byte or an asci byte */
+ string_length--;
+ }
+ }
+ PORT_Memset(buffer,' ',full_length);
+ if (nullTerminate) {
+ buffer[full_length] = 0;
+ }
+ PORT_Memcpy(buffer,inString,string_length);
+ return buffer;
+}
+/*
+ * Configuration utils
+ */
+static CK_RV
+sftk_configure(const char *man, const char *libdes)
+{
+
+ /* make sure the internationalization was done correctly... */
+ if (man) {
+ manufacturerID = sftk_setStringName(man,manufacturerID_space,
+ sizeof(manufacturerID_space), PR_TRUE);
+ }
+ if (libdes) {
+ libraryDescription = sftk_setStringName(libdes,
+ libraryDescription_space, sizeof(libraryDescription_space),
+ PR_TRUE);
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * ******************** Password Utilities *******************************
+ */
+
+/*
+ * see if the key DB password is enabled
+ */
+static PRBool
+sftk_hasNullPassword(SFTKSlot *slot, SFTKDBHandle *keydb)
+{
+ PRBool pwenabled;
+
+ pwenabled = PR_FALSE;
+ if (sftkdb_HasPasswordSet(keydb) == SECSuccess) {
+ PRBool tokenRemoved = PR_FALSE;
+ SECStatus rv = sftkdb_CheckPassword(keydb, "", &tokenRemoved);
+ if (tokenRemoved) {
+ sftk_CloseAllSessions(slot);
+ }
+ return (rv == SECSuccess);
+ }
+
+ return pwenabled;
+}
+
+/*
+ * ******************** Object Creation Utilities ***************************
+ */
+
+
+/* Make sure a given attribute exists. If it doesn't, initialize it to
+ * value and len
+ */
+CK_RV
+sftk_defaultAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type,void *value,
+ unsigned int len)
+{
+ if ( !sftk_hasAttribute(object, type)) {
+ return sftk_AddAttributeType(object,type,value,len);
+ }
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Data Object
+ */
+static CK_RV
+sftk_handleDataObject(SFTKSession *session,SFTKObject *object)
+{
+ CK_RV crv;
+
+ /* first reject private and token data objects */
+ if (sftk_isTrue(object,CKA_PRIVATE) || sftk_isTrue(object,CKA_TOKEN)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* now just verify the required date fields */
+ crv = sftk_defaultAttribute(object,CKA_APPLICATION,NULL,0);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_VALUE,NULL,0);
+ if (crv != CKR_OK) return crv;
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Certificate Object
+ */
+static CK_RV
+sftk_handleCertObject(SFTKSession *session,SFTKObject *object)
+{
+ CK_CERTIFICATE_TYPE type;
+ SFTKAttribute *attribute;
+ CK_RV crv;
+
+ /* certificates must have a type */
+ if ( !sftk_hasAttribute(object,CKA_CERTIFICATE_TYPE) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* we can't store any certs private */
+ if (sftk_isTrue(object,CKA_PRIVATE)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* We only support X.509 Certs for now */
+ attribute = sftk_FindAttribute(object,CKA_CERTIFICATE_TYPE);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+ type = *(CK_CERTIFICATE_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ if (type != CKC_X_509) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* X.509 Certificate */
+
+ /* make sure we have a cert */
+ if ( !sftk_hasAttribute(object,CKA_VALUE) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* in PKCS #11, Subject is a required field */
+ if ( !sftk_hasAttribute(object,CKA_SUBJECT) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* in PKCS #11, Issuer is a required field */
+ if ( !sftk_hasAttribute(object,CKA_ISSUER) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* in PKCS #11, Serial is a required field */
+ if ( !sftk_hasAttribute(object,CKA_SERIAL_NUMBER) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* add it to the object */
+ object->objectInfo = NULL;
+ object->infoFree = (SFTKFree) NULL;
+
+ /* now just verify the required date fields */
+ crv = sftk_defaultAttribute(object, CKA_ID, NULL, 0);
+ if (crv != CKR_OK) { return crv; }
+
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *certHandle = sftk_getCertDB(slot);
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(certHandle, object, &object->handle);
+ sftk_freeDB(certHandle);
+ return crv;
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+sftk_handleTrustObject(SFTKSession *session,SFTKObject *object)
+{
+ /* we can't store any certs private */
+ if (sftk_isTrue(object,CKA_PRIVATE)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* certificates must have a type */
+ if ( !sftk_hasAttribute(object,CKA_ISSUER) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object,CKA_SERIAL_NUMBER) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object,CKA_CERT_SHA1_HASH) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object,CKA_CERT_MD5_HASH) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *certHandle = sftk_getCertDB(slot);
+ CK_RV crv;
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(certHandle, object, &object->handle);
+ sftk_freeDB(certHandle);
+ return crv;
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+sftk_handleSMimeObject(SFTKSession *session,SFTKObject *object)
+{
+
+ /* we can't store any certs private */
+ if (sftk_isTrue(object,CKA_PRIVATE)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* certificates must have a type */
+ if ( !sftk_hasAttribute(object,CKA_SUBJECT) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object,CKA_NETSCAPE_EMAIL) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *certHandle;
+ CK_RV crv;
+
+ PORT_Assert(slot);
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ certHandle = sftk_getCertDB(slot);
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(certHandle, object, &object->handle);
+ sftk_freeDB(certHandle);
+ return crv;
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Trust Object
+ */
+static CK_RV
+sftk_handleCrlObject(SFTKSession *session,SFTKObject *object)
+{
+
+ /* we can't store any certs private */
+ if (sftk_isTrue(object,CKA_PRIVATE)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* certificates must have a type */
+ if ( !sftk_hasAttribute(object,CKA_SUBJECT) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object,CKA_VALUE) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *certHandle = sftk_getCertDB(slot);
+ CK_RV crv;
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(certHandle, object, &object->handle);
+ sftk_freeDB(certHandle);
+ return crv;
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * check the consistancy and initialize a Public Key Object
+ */
+static CK_RV
+sftk_handlePublicKeyObject(SFTKSession *session, SFTKObject *object,
+ CK_KEY_TYPE key_type)
+{
+ CK_BBOOL encrypt = CK_TRUE;
+ CK_BBOOL recover = CK_TRUE;
+ CK_BBOOL wrap = CK_TRUE;
+ CK_BBOOL derive = CK_FALSE;
+ CK_BBOOL verify = CK_TRUE;
+ CK_ATTRIBUTE_TYPE pubKeyAttr = CKA_VALUE;
+ CK_RV crv;
+
+ switch (key_type) {
+ case CKK_RSA:
+ crv = sftk_ConstrainAttribute(object, CKA_MODULUS,
+ RSA_MIN_MODULUS_BITS, 0, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_ConstrainAttribute(object, CKA_PUBLIC_EXPONENT, 2, 0, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ pubKeyAttr = CKA_MODULUS;
+ break;
+ case CKK_DSA:
+ crv = sftk_ConstrainAttribute(object, CKA_SUBPRIME,
+ DSA_Q_BITS, DSA_Q_BITS, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_ConstrainAttribute(object, CKA_PRIME,
+ DSA_MIN_P_BITS, DSA_MAX_P_BITS, 64);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_ConstrainAttribute(object, CKA_BASE, 1, DSA_MAX_P_BITS, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_ConstrainAttribute(object, CKA_VALUE, 1, DSA_MAX_P_BITS, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ encrypt = CK_FALSE;
+ recover = CK_FALSE;
+ wrap = CK_FALSE;
+ break;
+ case CKK_DH:
+ crv = sftk_ConstrainAttribute(object, CKA_PRIME,
+ DH_MIN_P_BITS, DH_MAX_P_BITS, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_ConstrainAttribute(object, CKA_BASE, 1, DH_MAX_P_BITS, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_ConstrainAttribute(object, CKA_VALUE, 1, DH_MAX_P_BITS, 0);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ verify = CK_FALSE;
+ derive = CK_TRUE;
+ encrypt = CK_FALSE;
+ recover = CK_FALSE;
+ wrap = CK_FALSE;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ if ( !sftk_hasAttribute(object, CKA_EC_PARAMS)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_EC_POINT)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ pubKeyAttr = CKA_EC_POINT;
+ derive = CK_TRUE; /* for ECDH */
+ verify = CK_TRUE; /* for ECDSA */
+ encrypt = CK_FALSE;
+ recover = CK_FALSE;
+ wrap = CK_FALSE;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ /* make sure the required fields exist */
+ crv = sftk_defaultAttribute(object,CKA_SUBJECT,NULL,0);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_ENCRYPT,&encrypt,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_VERIFY,&verify,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_VERIFY_RECOVER,
+ &recover,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_WRAP,&wrap,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_DERIVE,&derive,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ object->objectInfo = sftk_GetPubKey(object,key_type, &crv);
+ if (object->objectInfo == NULL) {
+ return crv;
+ }
+ object->infoFree = (SFTKFree) nsslowkey_DestroyPublicKey;
+
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *certHandle = sftk_getCertDB(slot);
+
+ if (certHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(certHandle, object, &object->handle);
+ sftk_freeDB(certHandle);
+ return crv;
+ }
+
+ return CKR_OK;
+}
+
+static NSSLOWKEYPrivateKey *
+sftk_mkPrivKey(SFTKObject *object,CK_KEY_TYPE key, CK_RV *rvp);
+
+/*
+ * check the consistancy and initialize a Private Key Object
+ */
+static CK_RV
+sftk_handlePrivateKeyObject(SFTKSession *session,SFTKObject *object,CK_KEY_TYPE key_type)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL encrypt = CK_TRUE;
+ CK_BBOOL sign = CK_FALSE;
+ CK_BBOOL recover = CK_TRUE;
+ CK_BBOOL wrap = CK_TRUE;
+ CK_BBOOL derive = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ SECItem mod;
+ CK_RV crv;
+
+ switch (key_type) {
+ case CKK_RSA:
+ if ( !sftk_hasAttribute(object, CKA_MODULUS)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_PUBLIC_EXPONENT)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_PRIVATE_EXPONENT)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_PRIME_1)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_PRIME_2)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_EXPONENT_1)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_EXPONENT_2)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_COEFFICIENT)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ /* make sure Netscape DB attribute is set correctly */
+ crv = sftk_Attribute2SSecItem(NULL, &mod, object, CKA_MODULUS);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_forceAttribute(object, CKA_NETSCAPE_DB,
+ sftk_item_expand(&mod));
+ if (mod.data) PORT_Free(mod.data);
+ if (crv != CKR_OK) return crv;
+
+ sign = CK_TRUE;
+ derive = CK_FALSE;
+ break;
+ case CKK_DSA:
+ if ( !sftk_hasAttribute(object, CKA_SUBPRIME)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ sign = CK_TRUE;
+ derive = CK_FALSE;
+ /* fall through */
+ case CKK_DH:
+ if ( !sftk_hasAttribute(object, CKA_PRIME)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_BASE)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_VALUE)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ encrypt = CK_FALSE;
+ recover = CK_FALSE;
+ wrap = CK_FALSE;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ if ( !sftk_hasAttribute(object, CKA_EC_PARAMS)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ if ( !sftk_hasAttribute(object, CKA_VALUE)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ encrypt = CK_FALSE;
+ sign = CK_TRUE;
+ recover = CK_FALSE;
+ wrap = CK_FALSE;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ crv = sftk_defaultAttribute(object,CKA_SUBJECT,NULL,0);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_SENSITIVE,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_EXTRACTABLE,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_DECRYPT,&encrypt,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_SIGN,&sign,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_SIGN_RECOVER,&recover,
+ sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_UNWRAP,&wrap,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_DERIVE,&derive,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ /* the next two bits get modified only in the key gen and token cases */
+ crv = sftk_forceAttribute(object,CKA_ALWAYS_SENSITIVE,
+ &ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_forceAttribute(object,CKA_NEVER_EXTRACTABLE,
+ &ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ /* should we check the non-token RSA private keys? */
+
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *keyHandle = sftk_getKeyDB(slot);
+ CK_RV crv;
+
+ if (keyHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(keyHandle, object, &object->handle);
+ sftk_freeDB(keyHandle);
+ return crv;
+ } else {
+ object->objectInfo = sftk_mkPrivKey(object,key_type,&crv);
+ if (object->objectInfo == NULL) return crv;
+ object->infoFree = (SFTKFree) nsslowkey_DestroyPrivateKey;
+ }
+ return CKR_OK;
+}
+
+/* forward declare the DES formating function for handleSecretKey */
+void sftk_FormatDESKey(unsigned char *key, int length);
+
+/* Validate secret key data, and set defaults */
+static CK_RV
+validateSecretKey(SFTKSession *session, SFTKObject *object,
+ CK_KEY_TYPE key_type, PRBool isFIPS)
+{
+ CK_RV crv;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ SFTKAttribute *attribute = NULL;
+ unsigned long requiredLen;
+
+ crv = sftk_defaultAttribute(object,CKA_SENSITIVE,
+ isFIPS?&cktrue:&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_EXTRACTABLE,
+ &cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_ENCRYPT,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_DECRYPT,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_SIGN,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_VERIFY,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_WRAP,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_UNWRAP,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ if ( !sftk_hasAttribute(object, CKA_VALUE)) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ /* the next two bits get modified only in the key gen and token cases */
+ crv = sftk_forceAttribute(object,CKA_ALWAYS_SENSITIVE,
+ &ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_forceAttribute(object,CKA_NEVER_EXTRACTABLE,
+ &ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ /* some types of keys have a value length */
+ crv = CKR_OK;
+ switch (key_type) {
+ /* force CKA_VALUE_LEN to be set */
+ case CKK_GENERIC_SECRET:
+ case CKK_RC2:
+ case CKK_RC4:
+#if NSS_SOFTOKEN_DOES_RC5
+ case CKK_RC5:
+#endif
+#ifdef NSS_SOFTOKEN_DOES_CAST
+ case CKK_CAST:
+ case CKK_CAST3:
+ case CKK_CAST5:
+#endif
+#if NSS_SOFTOKEN_DOES_IDEA
+ case CKK_IDEA:
+#endif
+ attribute = sftk_FindAttribute(object,CKA_VALUE);
+ /* shouldn't happen */
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+ crv = sftk_forceAttribute(object, CKA_VALUE_LEN,
+ &attribute->attrib.ulValueLen, sizeof(CK_ULONG));
+ sftk_FreeAttribute(attribute);
+ break;
+ /* force the value to have the correct parity */
+ case CKK_DES:
+ case CKK_DES2:
+ case CKK_DES3:
+ case CKK_CDMF:
+ attribute = sftk_FindAttribute(object,CKA_VALUE);
+ /* shouldn't happen */
+ if (attribute == NULL)
+ return CKR_TEMPLATE_INCOMPLETE;
+ requiredLen = sftk_MapKeySize(key_type);
+ if (attribute->attrib.ulValueLen != requiredLen) {
+ sftk_FreeAttribute(attribute);
+ return CKR_KEY_SIZE_RANGE;
+ }
+ sftk_FormatDESKey((unsigned char*)attribute->attrib.pValue,
+ attribute->attrib.ulValueLen);
+ sftk_FreeAttribute(attribute);
+ break;
+ default:
+ break;
+ }
+
+ return crv;
+}
+
+/*
+ * check the consistancy and initialize a Secret Key Object
+ */
+static CK_RV
+sftk_handleSecretKeyObject(SFTKSession *session,SFTKObject *object,
+ CK_KEY_TYPE key_type, PRBool isFIPS)
+{
+ CK_RV crv;
+
+ /* First validate and set defaults */
+ crv = validateSecretKey(session, object, key_type, isFIPS);
+ if (crv != CKR_OK) goto loser;
+
+ /* If the object is a TOKEN object, store in the database */
+ if (sftk_isTrue(object,CKA_TOKEN)) {
+ SFTKSlot *slot = session->slot;
+ SFTKDBHandle *keyHandle = sftk_getKeyDB(slot);
+ CK_RV crv;
+
+ if (keyHandle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ crv = sftkdb_write(keyHandle, object, &object->handle);
+ sftk_freeDB(keyHandle);
+ return crv;
+ }
+
+loser:
+
+ return crv;
+}
+
+/*
+ * check the consistancy and initialize a Key Object
+ */
+static CK_RV
+sftk_handleKeyObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKAttribute *attribute;
+ CK_KEY_TYPE key_type;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_RV crv;
+
+ /* verify the required fields */
+ if ( !sftk_hasAttribute(object,CKA_KEY_TYPE) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* now verify the common fields */
+ crv = sftk_defaultAttribute(object,CKA_ID,NULL,0);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_START_DATE,NULL,0);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_END_DATE,NULL,0);
+ if (crv != CKR_OK) return crv;
+ /* CKA_DERIVE is common to all keys, but it's default value is
+ * key dependent */
+ crv = sftk_defaultAttribute(object,CKA_LOCAL,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ /* get the key type */
+ attribute = sftk_FindAttribute(object,CKA_KEY_TYPE);
+ if (!attribute) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ switch (object->objclass) {
+ case CKO_PUBLIC_KEY:
+ return sftk_handlePublicKeyObject(session,object,key_type);
+ case CKO_PRIVATE_KEY:
+ return sftk_handlePrivateKeyObject(session,object,key_type);
+ case CKO_SECRET_KEY:
+ /* make sure the required fields exist */
+ return sftk_handleSecretKeyObject(session,object,key_type,
+ (PRBool)(session->slot->slotID == FIPS_SLOT_ID));
+ default:
+ break;
+ }
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+}
+
+/*
+ * check the consistancy and Verify a DSA Parameter Object
+ */
+static CK_RV
+sftk_handleDSAParameterObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKAttribute *primeAttr = NULL;
+ SFTKAttribute *subPrimeAttr = NULL;
+ SFTKAttribute *baseAttr = NULL;
+ SFTKAttribute *seedAttr = NULL;
+ SFTKAttribute *hAttr = NULL;
+ SFTKAttribute *attribute;
+ CK_RV crv = CKR_TEMPLATE_INCOMPLETE;
+ PQGParams params;
+ PQGVerify vfy, *verify = NULL;
+ SECStatus result,rv;
+
+ primeAttr = sftk_FindAttribute(object,CKA_PRIME);
+ if (primeAttr == NULL) goto loser;
+ params.prime.data = primeAttr->attrib.pValue;
+ params.prime.len = primeAttr->attrib.ulValueLen;
+
+ subPrimeAttr = sftk_FindAttribute(object,CKA_SUBPRIME);
+ if (subPrimeAttr == NULL) goto loser;
+ params.subPrime.data = subPrimeAttr->attrib.pValue;
+ params.subPrime.len = subPrimeAttr->attrib.ulValueLen;
+
+ baseAttr = sftk_FindAttribute(object,CKA_BASE);
+ if (baseAttr == NULL) goto loser;
+ params.base.data = baseAttr->attrib.pValue;
+ params.base.len = baseAttr->attrib.ulValueLen;
+
+ attribute = sftk_FindAttribute(object, CKA_NETSCAPE_PQG_COUNTER);
+ if (attribute != NULL) {
+ vfy.counter = *(CK_ULONG *) attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ seedAttr = sftk_FindAttribute(object, CKA_NETSCAPE_PQG_SEED);
+ if (seedAttr == NULL) goto loser;
+ vfy.seed.data = seedAttr->attrib.pValue;
+ vfy.seed.len = seedAttr->attrib.ulValueLen;
+
+ hAttr = sftk_FindAttribute(object, CKA_NETSCAPE_PQG_H);
+ if (hAttr == NULL) goto loser;
+ vfy.h.data = hAttr->attrib.pValue;
+ vfy.h.len = hAttr->attrib.ulValueLen;
+
+ verify = &vfy;
+ }
+
+ crv = CKR_FUNCTION_FAILED;
+ rv = PQG_VerifyParams(&params,verify,&result);
+ if (rv == SECSuccess) {
+ crv = (result== SECSuccess) ? CKR_OK : CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+loser:
+ if (hAttr) sftk_FreeAttribute(hAttr);
+ if (seedAttr) sftk_FreeAttribute(seedAttr);
+ if (baseAttr) sftk_FreeAttribute(baseAttr);
+ if (subPrimeAttr) sftk_FreeAttribute(subPrimeAttr);
+ if (primeAttr) sftk_FreeAttribute(primeAttr);
+
+ return crv;
+}
+
+/*
+ * check the consistancy and initialize a Key Parameter Object
+ */
+static CK_RV
+sftk_handleKeyParameterObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKAttribute *attribute;
+ CK_KEY_TYPE key_type;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_RV crv;
+
+ /* verify the required fields */
+ if ( !sftk_hasAttribute(object,CKA_KEY_TYPE) ) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+
+ /* now verify the common fields */
+ crv = sftk_defaultAttribute(object,CKA_LOCAL,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ /* get the key type */
+ attribute = sftk_FindAttribute(object,CKA_KEY_TYPE);
+ if (!attribute) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ switch (key_type) {
+ case CKK_DSA:
+ return sftk_handleDSAParameterObject(session,object);
+
+ default:
+ break;
+ }
+ return CKR_KEY_TYPE_INCONSISTENT;
+}
+
+/*
+ * Handle Object does all the object consistancy checks, automatic attribute
+ * generation, attribute defaulting, etc. If handleObject succeeds, the object
+ * will be assigned an object handle, and the object installed in the session
+ * or stored in the DB.
+ */
+CK_RV
+sftk_handleObject(SFTKObject *object, SFTKSession *session)
+{
+ SFTKSlot *slot = session->slot;
+ SFTKAttribute *attribute;
+ SFTKObject *duplicateObject = NULL;
+ CK_OBJECT_HANDLE handle;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_RV crv;
+
+ /* make sure all the base object types are defined. If not set the
+ * defaults */
+ crv = sftk_defaultAttribute(object,CKA_TOKEN,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_PRIVATE,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_LABEL,NULL,0);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_defaultAttribute(object,CKA_MODIFIABLE,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) return crv;
+
+ /* don't create a private object if we aren't logged in */
+ if ((!slot->isLoggedIn) && (slot->needLogin) &&
+ (sftk_isTrue(object,CKA_PRIVATE))) {
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+
+
+ if (((session->info.flags & CKF_RW_SESSION) == 0) &&
+ (sftk_isTrue(object,CKA_TOKEN))) {
+ return CKR_SESSION_READ_ONLY;
+ }
+
+ /* Assign a unique SESSION object handle to every new object,
+ * whether it is a session object or a token object.
+ * At this point, all new objects are structured as session objects.
+ * Objects with the CKA_TOKEN attribute true will be turned into
+ * token objects and will have a token object handle assigned to
+ * them by a call to sftk_mkHandle in the handler for each object
+ * class, invoked below.
+ *
+ * It may be helpful to note/remember that
+ * sftk_narrowToXxxObject uses sftk_isToken,
+ * sftk_isToken examines the sign bit of the object's handle, but
+ * sftk_isTrue(...,CKA_TOKEN) examines the CKA_TOKEN attribute.
+ */
+ do {
+ PRUint32 wrappedAround;
+
+ duplicateObject = NULL;
+ PZ_Lock(slot->objectLock);
+ wrappedAround = slot->sessionObjectHandleCount & SFTK_TOKEN_MASK;
+ handle = slot->sessionObjectHandleCount & ~SFTK_TOKEN_MASK;
+ if (!handle) /* don't allow zero handle */
+ handle = minSessionObjectHandle;
+ slot->sessionObjectHandleCount = (handle + 1U) | wrappedAround;
+ /* Is there already a session object with this handle? */
+ if (wrappedAround) {
+ sftkqueue_find(duplicateObject, handle, slot->sessObjHashTable, \
+ slot->sessObjHashSize);
+ }
+ PZ_Unlock(slot->objectLock);
+ } while (duplicateObject != NULL);
+ object->handle = handle;
+
+ /* get the object class */
+ attribute = sftk_FindAttribute(object,CKA_CLASS);
+ if (attribute == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ object->objclass = *(CK_OBJECT_CLASS *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ /* Now handle the specific object class.
+ * At this point, all objects are session objects, and the session
+ * number must be passed to the object class handlers.
+ */
+ switch (object->objclass) {
+ case CKO_DATA:
+ crv = sftk_handleDataObject(session,object);
+ break;
+ case CKO_CERTIFICATE:
+ crv = sftk_handleCertObject(session,object);
+ break;
+ case CKO_NETSCAPE_TRUST:
+ crv = sftk_handleTrustObject(session,object);
+ break;
+ case CKO_NETSCAPE_CRL:
+ crv = sftk_handleCrlObject(session,object);
+ break;
+ case CKO_NETSCAPE_SMIME:
+ crv = sftk_handleSMimeObject(session,object);
+ break;
+ case CKO_PRIVATE_KEY:
+ case CKO_PUBLIC_KEY:
+ case CKO_SECRET_KEY:
+ crv = sftk_handleKeyObject(session,object);
+ break;
+ case CKO_KG_PARAMETERS:
+ crv = sftk_handleKeyParameterObject(session,object);
+ break;
+ default:
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+
+ /* can't fail from here on out unless the pk_handlXXX functions have
+ * failed the request */
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /* Now link the object into the slot and session structures.
+ * If the object has a true CKA_TOKEN attribute, the above object
+ * class handlers will have set the sign bit in the object handle,
+ * causing the following test to be true.
+ */
+ if (sftk_isToken(object->handle)) {
+ sftk_convertSessionToToken(object);
+ } else {
+ object->slot = slot;
+ sftk_AddObject(session,object);
+ }
+
+ return CKR_OK;
+}
+
+/*
+ * ******************** Public Key Utilities ***************************
+ */
+/* Generate a low public key structure from an object */
+NSSLOWKEYPublicKey *sftk_GetPubKey(SFTKObject *object,CK_KEY_TYPE key_type,
+ CK_RV *crvp)
+{
+ NSSLOWKEYPublicKey *pubKey;
+ PLArenaPool *arena;
+ CK_RV crv;
+
+ if (object->objclass != CKO_PUBLIC_KEY) {
+ *crvp = CKR_KEY_TYPE_INCONSISTENT;
+ return NULL;
+ }
+
+ if (sftk_isToken(object->handle)) {
+/* ferret out the token object handle */
+ }
+
+ /* If we already have a key, use it */
+ if (object->objectInfo) {
+ *crvp = CKR_OK;
+ return (NSSLOWKEYPublicKey *)object->objectInfo;
+ }
+
+ /* allocate the structure */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ pubKey = (NSSLOWKEYPublicKey *)
+ PORT_ArenaAlloc(arena,sizeof(NSSLOWKEYPublicKey));
+ if (pubKey == NULL) {
+ PORT_FreeArena(arena,PR_FALSE);
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ /* fill in the structure */
+ pubKey->arena = arena;
+ switch (key_type) {
+ case CKK_RSA:
+ pubKey->keyType = NSSLOWKEYRSAKey;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.rsa.modulus,
+ object,CKA_MODULUS);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.rsa.publicExponent,
+ object,CKA_PUBLIC_EXPONENT);
+ break;
+ case CKK_DSA:
+ pubKey->keyType = NSSLOWKEYDSAKey;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dsa.params.prime,
+ object,CKA_PRIME);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dsa.params.subPrime,
+ object,CKA_SUBPRIME);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dsa.params.base,
+ object,CKA_BASE);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dsa.publicValue,
+ object,CKA_VALUE);
+ break;
+ case CKK_DH:
+ pubKey->keyType = NSSLOWKEYDHKey;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dh.prime,
+ object,CKA_PRIME);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dh.base,
+ object,CKA_BASE);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.dh.publicValue,
+ object,CKA_VALUE);
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ pubKey->keyType = NSSLOWKEYECKey;
+ crv = sftk_Attribute2SSecItem(arena,
+ &pubKey->u.ec.ecParams.DEREncoding,
+ object,CKA_EC_PARAMS);
+ if (crv != CKR_OK) break;
+
+ /* Fill out the rest of the ecParams structure
+ * based on the encoded params
+ */
+ if (EC_FillParams(arena, &pubKey->u.ec.ecParams.DEREncoding,
+ &pubKey->u.ec.ecParams) != SECSuccess) {
+ crv = CKR_DOMAIN_PARAMS_INVALID;
+ break;
+ }
+
+ crv = sftk_Attribute2SSecItem(arena,&pubKey->u.ec.publicValue,
+ object,CKA_EC_POINT);
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ *crvp = crv;
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ return NULL;
+ }
+
+ object->objectInfo = pubKey;
+ object->infoFree = (SFTKFree) nsslowkey_DestroyPublicKey;
+ return pubKey;
+}
+
+/* make a private key from a verified object */
+static NSSLOWKEYPrivateKey *
+sftk_mkPrivKey(SFTKObject *object, CK_KEY_TYPE key_type, CK_RV *crvp)
+{
+ NSSLOWKEYPrivateKey *privKey;
+ SFTKItemTemplate itemTemplate[SFTK_MAX_ITEM_TEMPLATE];
+ int itemTemplateCount = 0;
+ PLArenaPool *arena;
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ privKey = (NSSLOWKEYPrivateKey *)
+ PORT_ArenaZAlloc(arena,sizeof(NSSLOWKEYPrivateKey));
+ if (privKey == NULL) {
+ PORT_FreeArena(arena,PR_FALSE);
+ *crvp = CKR_HOST_MEMORY;
+ return NULL;
+ }
+
+ /* in future this would be a switch on key_type */
+ privKey->arena = arena;
+ switch (key_type) {
+ case CKK_RSA:
+ privKey->keyType = NSSLOWKEYRSAKey;
+
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.modulus,CKA_MODULUS);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.publicExponent, CKA_PUBLIC_EXPONENT);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.privateExponent, CKA_PRIVATE_EXPONENT);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.prime1, CKA_PRIME_1);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.prime2, CKA_PRIME_2);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.exponent1, CKA_EXPONENT_1);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.exponent2, CKA_EXPONENT_2);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.rsa.coefficient, CKA_COEFFICIENT);
+ itemTemplateCount++;
+ rv = DER_SetUInteger(privKey->arena, &privKey->u.rsa.version,
+ NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
+ if (rv != SECSuccess) crv = CKR_HOST_MEMORY;
+ break;
+
+ case CKK_DSA:
+ privKey->keyType = NSSLOWKEYDSAKey;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dsa.params.prime, CKA_PRIME);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dsa.params.subPrime, CKA_SUBPRIME);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dsa.params.base, CKA_BASE);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dsa.privateValue, CKA_VALUE);
+ itemTemplateCount++;
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ break;
+
+ case CKK_DH:
+ privKey->keyType = NSSLOWKEYDHKey;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dh.prime, CKA_PRIME);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dh.base, CKA_BASE);
+ itemTemplateCount++;
+ SFTK_SET_ITEM_TEMPLATE(itemTemplate, itemTemplateCount,
+ &privKey->u.dh.privateValue, CKA_VALUE);
+ itemTemplateCount++;
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ break;
+
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ privKey->keyType = NSSLOWKEYECKey;
+ crv = sftk_Attribute2SSecItem(arena,
+ &privKey->u.ec.ecParams.DEREncoding,
+ object,CKA_EC_PARAMS);
+ if (crv != CKR_OK) break;
+
+ /* Fill out the rest of the ecParams structure
+ * based on the encoded params
+ */
+ if (EC_FillParams(arena, &privKey->u.ec.ecParams.DEREncoding,
+ &privKey->u.ec.ecParams) != SECSuccess) {
+ crv = CKR_DOMAIN_PARAMS_INVALID;
+ break;
+ }
+ crv = sftk_Attribute2SSecItem(arena,&privKey->u.ec.privateValue,
+ object,CKA_VALUE);
+ if (crv != CKR_OK) break;
+
+ if (sftk_hasAttribute(object, CKA_NETSCAPE_DB)) {
+ crv = sftk_Attribute2SSecItem(arena, &privKey->u.ec.publicValue,
+ object, CKA_NETSCAPE_DB);
+ if (crv != CKR_OK) break;
+ /* privKey was zero'd so public value is already set to NULL, 0
+ * if we don't set it explicitly */
+ }
+ rv = DER_SetUInteger(privKey->arena, &privKey->u.ec.version,
+ NSSLOWKEY_EC_PRIVATE_KEY_VERSION);
+ if (rv != SECSuccess) crv = CKR_HOST_MEMORY;
+ break;
+#endif /* NSS_ENABLE_ECC */
+
+ default:
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ if (crv == CKR_OK && itemTemplateCount != 0) {
+ PORT_Assert(itemTemplateCount > 0);
+ PORT_Assert(itemTemplateCount <= SFTK_MAX_ITEM_TEMPLATE);
+ crv = sftk_MultipleAttribute2SecItem(arena, object, itemTemplate,
+ itemTemplateCount);
+ }
+ *crvp = crv;
+ if (crv != CKR_OK) {
+ PORT_FreeArena(arena,PR_FALSE);
+ return NULL;
+ }
+ return privKey;
+}
+
+
+/* Generate a low private key structure from an object */
+NSSLOWKEYPrivateKey *
+sftk_GetPrivKey(SFTKObject *object,CK_KEY_TYPE key_type, CK_RV *crvp)
+{
+ NSSLOWKEYPrivateKey *priv = NULL;
+
+ if (object->objclass != CKO_PRIVATE_KEY) {
+ *crvp = CKR_KEY_TYPE_INCONSISTENT;
+ return NULL;
+ }
+ if (object->objectInfo) {
+ *crvp = CKR_OK;
+ return (NSSLOWKEYPrivateKey *)object->objectInfo;
+ }
+
+ priv = sftk_mkPrivKey(object, key_type, crvp);
+ object->objectInfo = priv;
+ object->infoFree = (SFTKFree) nsslowkey_DestroyPrivateKey;
+ return priv;
+}
+
+/*
+ **************************** Symetric Key utils ************************
+ */
+/*
+ * set the DES key with parity bits correctly
+ */
+void
+sftk_FormatDESKey(unsigned char *key, int length)
+{
+ int i;
+
+ /* format the des key */
+ for (i=0; i < length; i++) {
+ key[i] = parityTable[key[i]>>1];
+ }
+}
+
+/*
+ * check a des key (des2 or des3 subkey) for weak keys.
+ */
+PRBool
+sftk_CheckDESKey(unsigned char *key)
+{
+ int i;
+
+ /* format the des key with parity */
+ sftk_FormatDESKey(key, 8);
+
+ for (i=0; i < sftk_desWeakTableSize; i++) {
+ if (PORT_Memcmp(key,sftk_desWeakTable[i],8) == 0) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+/*
+ * check if a des or triple des key is weak.
+ */
+PRBool
+sftk_IsWeakKey(unsigned char *key,CK_KEY_TYPE key_type)
+{
+
+ switch(key_type) {
+ case CKK_DES:
+ return sftk_CheckDESKey(key);
+ case CKM_DES2_KEY_GEN:
+ if (sftk_CheckDESKey(key)) return PR_TRUE;
+ return sftk_CheckDESKey(&key[8]);
+ case CKM_DES3_KEY_GEN:
+ if (sftk_CheckDESKey(key)) return PR_TRUE;
+ if (sftk_CheckDESKey(&key[8])) return PR_TRUE;
+ return sftk_CheckDESKey(&key[16]);
+ default:
+ break;
+ }
+ return PR_FALSE;
+}
+
+
+/**********************************************************************
+ *
+ * Start of PKCS 11 functions
+ *
+ **********************************************************************/
+
+
+/* return the function list */
+CK_RV NSC_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList)
+{
+ CHECK_FORK();
+
+ *pFunctionList = (CK_FUNCTION_LIST_PTR) &sftk_funcList;
+ return CKR_OK;
+}
+
+/* return the function list */
+CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR *pFunctionList)
+{
+ CHECK_FORK();
+
+ return NSC_GetFunctionList(pFunctionList);
+}
+
+static PLHashNumber
+sftk_HashNumber(const void *key)
+{
+ return (PLHashNumber) key;
+}
+
+/*
+ * eventually I'd like to expunge all occurances of XXX_SLOT_ID and
+ * just go with the info in the slot. This is one place, however,
+ * where it might be a little difficult.
+ */
+const char *
+sftk_getDefTokName(CK_SLOT_ID slotID)
+{
+ static char buf[33];
+
+ switch (slotID) {
+ case NETSCAPE_SLOT_ID:
+ return "NSS Generic Crypto Services ";
+ case PRIVATE_KEY_SLOT_ID:
+ return "NSS Certificate DB ";
+ case FIPS_SLOT_ID:
+ return "NSS FIPS 140-2 Certificate DB ";
+ default:
+ break;
+ }
+ sprintf(buf,"NSS Application Token %08x ",(unsigned int) slotID);
+ return buf;
+}
+
+const char *
+sftk_getDefSlotName(CK_SLOT_ID slotID)
+{
+ static char buf[65];
+
+ switch (slotID) {
+ case NETSCAPE_SLOT_ID:
+ return
+ "NSS Internal Cryptographic Services ";
+ case PRIVATE_KEY_SLOT_ID:
+ return
+ "NSS User Private Key and Certificate Services ";
+ case FIPS_SLOT_ID:
+ return
+ "NSS FIPS 140-2 User Private Key Services ";
+ default:
+ break;
+ }
+ sprintf(buf,
+ "NSS Application Slot %08x ",
+ (unsigned int) slotID);
+ return buf;
+}
+
+static CK_ULONG nscSlotCount[2] = {0 , 0};
+static CK_SLOT_ID_PTR nscSlotList[2] = {NULL, NULL};
+static CK_ULONG nscSlotListSize[2] = {0, 0};
+static PLHashTable *nscSlotHashTable[2] = {NULL, NULL};
+
+static int
+sftk_GetModuleIndex(CK_SLOT_ID slotID)
+{
+ if ((slotID == FIPS_SLOT_ID) || (slotID >= SFTK_MIN_FIPS_USER_SLOT_ID)) {
+ return NSC_FIPS_MODULE;
+ }
+ return NSC_NON_FIPS_MODULE;
+}
+
+/* look up a slot structure from the ID (used to be a macro when we only
+ * had two slots) */
+/* if all is true, return the slot even if it has been 'unloaded' */
+/* if all is false, only return the slots which are present */
+SFTKSlot *
+sftk_SlotFromID(CK_SLOT_ID slotID, PRBool all)
+{
+ SFTKSlot *slot;
+ int index = sftk_GetModuleIndex(slotID);
+
+ if (nscSlotHashTable[index] == NULL) return NULL;
+ slot = (SFTKSlot *)PL_HashTableLookupConst(nscSlotHashTable[index],
+ (void *)slotID);
+ /* cleared slots shouldn't 'show up' */
+ if (slot && !all && !slot->present) slot = NULL;
+ return slot;
+}
+
+SFTKSlot *
+sftk_SlotFromSessionHandle(CK_SESSION_HANDLE handle)
+{
+ CK_ULONG slotIDIndex = (handle >> 24) & 0x7f;
+ CK_ULONG moduleIndex = (handle >> 31) & 1;
+
+ if (slotIDIndex >= nscSlotCount[moduleIndex]) {
+ return NULL;
+ }
+
+ return sftk_SlotFromID(nscSlotList[moduleIndex][slotIDIndex], PR_FALSE);
+}
+
+static CK_RV
+sftk_RegisterSlot(SFTKSlot *slot, int moduleIndex)
+{
+ PLHashEntry *entry;
+ int index;
+
+ index = sftk_GetModuleIndex(slot->slotID);
+
+ /* make sure the slotID for this module is valid */
+ if (moduleIndex != index) {
+ return CKR_SLOT_ID_INVALID;
+ }
+
+ if (nscSlotList[index] == NULL) {
+ nscSlotListSize[index] = NSC_SLOT_LIST_BLOCK_SIZE;
+ nscSlotList[index] = (CK_SLOT_ID *)
+ PORT_ZAlloc(nscSlotListSize[index]*sizeof(CK_SLOT_ID));
+ if (nscSlotList[index] == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ }
+ if (nscSlotCount[index] >= nscSlotListSize[index]) {
+ CK_SLOT_ID* oldNscSlotList = nscSlotList[index];
+ CK_ULONG oldNscSlotListSize = nscSlotListSize[index];
+ nscSlotListSize[index] += NSC_SLOT_LIST_BLOCK_SIZE;
+ nscSlotList[index] = (CK_SLOT_ID *) PORT_Realloc(oldNscSlotList,
+ nscSlotListSize[index]*sizeof(CK_SLOT_ID));
+ if (nscSlotList[index] == NULL) {
+ nscSlotList[index] = oldNscSlotList;
+ nscSlotListSize[index] = oldNscSlotListSize;
+ return CKR_HOST_MEMORY;
+ }
+ }
+
+ if (nscSlotHashTable[index] == NULL) {
+ nscSlotHashTable[index] = PL_NewHashTable(64,sftk_HashNumber,
+ PL_CompareValues, PL_CompareValues, NULL, 0);
+ if (nscSlotHashTable[index] == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ }
+
+ entry = PL_HashTableAdd(nscSlotHashTable[index],(void *)slot->slotID,slot);
+ if (entry == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ slot->index = (nscSlotCount[index] & 0x7f) | ((index << 7) & 0x80);
+ nscSlotList[index][nscSlotCount[index]++] = slot->slotID;
+
+ return CKR_OK;
+}
+
+
+/*
+ * ths function has all the common initialization that happens whenever we
+ * create a new slot or repurpose an old slot (only valid for slotID's 4
+ * and greater).
+ *
+ * things that are not reinitialized are:
+ * slotID (can't change)
+ * slotDescription (can't change once defined)
+ * the locks and hash tables (difficult to change in running code, and
+ * unnecessary. hash tables and list are cleared on shutdown, but they
+ * are cleared in a 'friendly' way).
+ * session and object ID counters -- so any old sessions and objects in the
+ * application will get properly notified that the world has changed.
+ *
+ * things that are reinitialized:
+ * database (otherwise what would the point be;).
+ * state variables related to databases.
+ * session count stat info.
+ * tokenDescription.
+ *
+ * NOTE: slotID's 4 and greater show up as removable devices.
+ *
+ */
+CK_RV
+SFTK_SlotReInit(SFTKSlot *slot, char *configdir, char *updatedir,
+ char *updateID, sftk_token_parameters *params, int moduleIndex)
+{
+ PRBool needLogin = !params->noKeyDB;
+ CK_RV crv;
+
+ slot->hasTokens = PR_FALSE;
+ slot->sessionIDConflict = 0;
+ slot->sessionCount = 0;
+ slot->rwSessionCount = 0;
+ slot->needLogin = PR_FALSE;
+ slot->isLoggedIn = PR_FALSE;
+ slot->ssoLoggedIn = PR_FALSE;
+ slot->DB_loaded = PR_FALSE;
+ slot->certDB = NULL;
+ slot->keyDB = NULL;
+ slot->minimumPinLen = 0;
+ slot->readOnly = params->readOnly;
+ sftk_setStringName(params->tokdes ? params->tokdes :
+ sftk_getDefTokName(slot->slotID), slot->tokDescription,
+ sizeof(slot->tokDescription),PR_TRUE);
+ sftk_setStringName(params->updtokdes ? params->updtokdes : " ",
+ slot->updateTokDescription,
+ sizeof(slot->updateTokDescription),PR_TRUE);
+
+ if ((!params->noCertDB) || (!params->noKeyDB)) {
+ SFTKDBHandle * certHandle = NULL;
+ SFTKDBHandle *keyHandle = NULL;
+ crv = sftk_DBInit(params->configdir ? params->configdir : configdir,
+ params->certPrefix, params->keyPrefix,
+ params->updatedir ? params->updatedir : updatedir,
+ params->updCertPrefix, params->updKeyPrefix,
+ params->updateID ? params->updateID : updateID,
+ params->readOnly, params->noCertDB, params->noKeyDB,
+ params->forceOpen, &certHandle, &keyHandle);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ slot->certDB = certHandle;
+ slot->keyDB = keyHandle;
+ }
+ if (needLogin) {
+ /* if the data base is initialized with a null password,remember that */
+ slot->needLogin =
+ (PRBool)!sftk_hasNullPassword(slot, slot->keyDB);
+ if ((params->minPW >= 0) && (params->minPW <= SFTK_MAX_PIN)) {
+ slot->minimumPinLen = params->minPW;
+ }
+ if ((slot->minimumPinLen == 0) && (params->pwRequired)) {
+ slot->minimumPinLen = 1;
+ }
+ if ((moduleIndex == NSC_FIPS_MODULE) &&
+ (slot->minimumPinLen < FIPS_MIN_PIN)) {
+ slot->minimumPinLen = FIPS_MIN_PIN;
+ }
+ }
+
+ slot->present = PR_TRUE;
+ return CKR_OK;
+
+loser:
+ SFTK_ShutdownSlot(slot);
+ return crv;
+}
+
+/*
+ * initialize one of the slot structures. figure out which by the ID
+ */
+CK_RV
+SFTK_SlotInit(char *configdir, char *updatedir, char *updateID,
+ sftk_token_parameters *params, int moduleIndex)
+{
+ unsigned int i;
+ CK_SLOT_ID slotID = params->slotID;
+ SFTKSlot *slot;
+ CK_RV crv = CKR_HOST_MEMORY;
+
+ /*
+ * first we initialize everything that is 'permanent' with this slot.
+ * that is everything we aren't going to shutdown if we close this slot
+ * and open it up again with different databases */
+
+ slot = PORT_ZNew(SFTKSlot);
+
+ if (slot == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ slot->optimizeSpace = params->optimizeSpace;
+ if (slot->optimizeSpace) {
+ slot->sessObjHashSize = SPACE_SESSION_OBJECT_HASH_SIZE;
+ slot->sessHashSize = SPACE_SESSION_HASH_SIZE;
+ slot->numSessionLocks = 1;
+ } else {
+ slot->sessObjHashSize = TIME_SESSION_OBJECT_HASH_SIZE;
+ slot->sessHashSize = TIME_SESSION_HASH_SIZE;
+ slot->numSessionLocks = slot->sessHashSize/BUCKETS_PER_SESSION_LOCK;
+ }
+ slot->sessionLockMask = slot->numSessionLocks-1;
+
+ slot->slotLock = PZ_NewLock(nssILockSession);
+ if (slot->slotLock == NULL)
+ goto mem_loser;
+ slot->sessionLock = PORT_ZNewArray(PZLock *, slot->numSessionLocks);
+ if (slot->sessionLock == NULL)
+ goto mem_loser;
+ for (i=0; i < slot->numSessionLocks; i++) {
+ slot->sessionLock[i] = PZ_NewLock(nssILockSession);
+ if (slot->sessionLock[i] == NULL)
+ goto mem_loser;
+ }
+ slot->objectLock = PZ_NewLock(nssILockObject);
+ if (slot->objectLock == NULL)
+ goto mem_loser;
+ slot->pwCheckLock = PR_NewLock();
+ if (slot->pwCheckLock == NULL)
+ goto mem_loser;
+ slot->head = PORT_ZNewArray(SFTKSession *, slot->sessHashSize);
+ if (slot->head == NULL)
+ goto mem_loser;
+ slot->sessObjHashTable = PORT_ZNewArray(SFTKObject *, slot->sessObjHashSize);
+ if (slot->sessObjHashTable == NULL)
+ goto mem_loser;
+ slot->tokObjHashTable = PL_NewHashTable(64,sftk_HashNumber,PL_CompareValues,
+ SECITEM_HashCompare, NULL, 0);
+ if (slot->tokObjHashTable == NULL)
+ goto mem_loser;
+
+ slot->sessionIDCount = 0;
+ slot->sessionObjectHandleCount = minSessionObjectHandle;
+ slot->slotID = slotID;
+ sftk_setStringName(params->slotdes ? params->slotdes :
+ sftk_getDefSlotName(slotID), slot->slotDescription,
+ sizeof(slot->slotDescription), PR_TRUE);
+
+ /* call the reinit code to set everything that changes between token
+ * init calls */
+ crv = SFTK_SlotReInit(slot, configdir, updatedir, updateID,
+ params, moduleIndex);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftk_RegisterSlot(slot, moduleIndex);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ return CKR_OK;
+
+mem_loser:
+ crv = CKR_HOST_MEMORY;
+loser:
+ SFTK_DestroySlotData(slot);
+ return crv;
+}
+
+
+CK_RV sftk_CloseAllSessions(SFTKSlot *slot)
+{
+ SFTKSession *session;
+ unsigned int i;
+ SFTKDBHandle *handle;
+
+ /* first log out the card */
+ handle = sftk_getKeyDB(slot);
+ PZ_Lock(slot->slotLock);
+ slot->isLoggedIn = PR_FALSE;
+ if (handle) {
+ sftkdb_ClearPassword(handle);
+ }
+ PZ_Unlock(slot->slotLock);
+ if (handle) {
+ sftk_freeDB(handle);
+ }
+
+ /* now close all the current sessions */
+ /* NOTE: If you try to open new sessions before NSC_CloseAllSessions
+ * completes, some of those new sessions may or may not be closed by
+ * NSC_CloseAllSessions... but any session running when this code starts
+ * will guarrenteed be close, and no session will be partially closed */
+ for (i=0; i < slot->sessHashSize; i++) {
+ PZLock *lock = SFTK_SESSION_LOCK(slot,i);
+ do {
+ PZ_Lock(lock);
+ session = slot->head[i];
+ /* hand deque */
+ /* this duplicates function of NSC_close session functions, but
+ * because we know that we are freeing all the sessions, we can
+ * do more efficient processing */
+ if (session) {
+ slot->head[i] = session->next;
+ if (session->next) session->next->prev = NULL;
+ session->next = session->prev = NULL;
+ PZ_Unlock(lock);
+ PZ_Lock(slot->slotLock);
+ --slot->sessionCount;
+ PZ_Unlock(slot->slotLock);
+ if (session->info.flags & CKF_RW_SESSION) {
+ PR_AtomicDecrement(&slot->rwSessionCount);
+ }
+ } else {
+ PZ_Unlock(lock);
+ }
+ if (session) sftk_FreeSession(session);
+ } while (session != NULL);
+ }
+ return CKR_OK;
+}
+
+/*
+ * shut down the databases.
+ * we get the slot lock (which also protects slot->certDB and slot->keyDB)
+ * and clear the values so the new users will not find the databases.
+ * once things are clear, we can release our references to the databases.
+ * The databases will close when the last reference is released.
+ *
+ * We use reference counts so that we don't crash if someone shuts down
+ * a token that another thread is actively using.
+ */
+static void
+sftk_DBShutdown(SFTKSlot *slot)
+{
+ SFTKDBHandle *certHandle;
+ SFTKDBHandle *keyHandle;
+ PZ_Lock(slot->slotLock);
+ certHandle = slot->certDB;
+ slot->certDB = NULL;
+ keyHandle = slot->keyDB;
+ slot->keyDB = NULL;
+ PZ_Unlock(slot->slotLock);
+ if (certHandle) {
+ sftk_freeDB(certHandle);
+ }
+ if (keyHandle) {
+ sftk_freeDB(keyHandle);
+ }
+}
+
+CK_RV
+SFTK_ShutdownSlot(SFTKSlot *slot)
+{
+ /* make sure no new PK11 calls work except C_GetSlotInfo */
+ slot->present = PR_FALSE;
+
+ /* close all outstanding sessions
+ * the sessHashSize variable guarentees we have all the session
+ * mechanism set up */
+ if (slot->head) {
+ sftk_CloseAllSessions(slot);
+ }
+
+ /* clear all objects.. session objects are cleared as a result of
+ * closing all the sessions. We just need to clear the token object
+ * cache. slot->tokObjHashTable guarentees we have the token
+ * infrastructure set up. */
+ if (slot->tokObjHashTable) {
+ SFTK_ClearTokenKeyHashTable(slot);
+ }
+
+ /* clear the slot description for the next guy */
+ PORT_Memset(slot->tokDescription, 0, sizeof(slot->tokDescription));
+
+ /* now shut down the databases. */
+ sftk_DBShutdown(slot);
+ return CKR_OK;
+}
+
+/*
+ * initialize one of the slot structures. figure out which by the ID
+ */
+CK_RV
+SFTK_DestroySlotData(SFTKSlot *slot)
+{
+ unsigned int i;
+
+ SFTK_ShutdownSlot(slot);
+
+ if (slot->tokObjHashTable) {
+ PL_HashTableDestroy(slot->tokObjHashTable);
+ slot->tokObjHashTable = NULL;
+ }
+
+ if (slot->sessObjHashTable) {
+ PORT_Free(slot->sessObjHashTable);
+ slot->sessObjHashTable = NULL;
+ }
+ slot->sessObjHashSize = 0;
+
+ if (slot->head) {
+ PORT_Free(slot->head);
+ slot->head = NULL;
+ }
+ slot->sessHashSize = 0;
+
+ /* OK everything has been disassembled, now we can finally get rid
+ * of the locks */
+ PZ_DestroyLock(slot->slotLock);
+ slot->slotLock = NULL;
+ if (slot->sessionLock) {
+ for (i=0; i < slot->numSessionLocks; i++) {
+ if (slot->sessionLock[i]) {
+ PZ_DestroyLock(slot->sessionLock[i]);
+ slot->sessionLock[i] = NULL;
+ }
+ }
+ PORT_Free(slot->sessionLock);
+ slot->sessionLock = NULL;
+ }
+ if (slot->objectLock) {
+ PZ_DestroyLock(slot->objectLock);
+ slot->objectLock = NULL;
+ }
+ if (slot->pwCheckLock) {
+ PR_DestroyLock(slot->pwCheckLock);
+ slot->pwCheckLock = NULL;
+ }
+ PORT_Free(slot);
+ return CKR_OK;
+}
+
+/*
+ * handle the SECMOD.db
+ */
+char **
+NSC_ModuleDBFunc(unsigned long function,char *parameters, void *args)
+{
+ char *secmod = NULL;
+ char *appName = NULL;
+ char *filename = NULL;
+ SDBType dbType = SDB_LEGACY;
+ PRBool rw;
+ static char *success="Success";
+ char **rvstr = NULL;
+
+#if defined(XP_UNIX) && !defined(NO_PTHREADS)
+ if (forked) return NULL;
+#endif
+
+ secmod = sftk_getSecmodName(parameters, &dbType, &appName,&filename, &rw);
+
+ switch (function) {
+ case SECMOD_MODULE_DB_FUNCTION_FIND:
+ rvstr = sftkdb_ReadSecmodDB(dbType,appName,filename,secmod,(char *)parameters,rw);
+ break;
+ case SECMOD_MODULE_DB_FUNCTION_ADD:
+ rvstr = (sftkdb_AddSecmodDB(dbType,appName,filename,secmod,(char *)args,rw)
+ == SECSuccess) ? &success: NULL;
+ break;
+ case SECMOD_MODULE_DB_FUNCTION_DEL:
+ rvstr = (sftkdb_DeleteSecmodDB(dbType,appName,filename,secmod,(char *)args,rw)
+ == SECSuccess) ? &success: NULL;
+ break;
+ case SECMOD_MODULE_DB_FUNCTION_RELEASE:
+ rvstr = (sftkdb_ReleaseSecmodDBData(dbType, appName,filename,secmod,
+ (char **)args,rw) == SECSuccess) ? &success: NULL;
+ break;
+ }
+ if (secmod) PR_smprintf_free(secmod);
+ if (appName) PORT_Free(appName);
+ if (filename) PORT_Free(filename);
+ return rvstr;
+}
+
+static void nscFreeAllSlots(int moduleIndex)
+{
+ /* free all the slots */
+ SFTKSlot *slot = NULL;
+ CK_SLOT_ID slotID;
+ int i;
+
+ if (nscSlotList[moduleIndex]) {
+ CK_ULONG tmpSlotCount = nscSlotCount[moduleIndex];
+ CK_SLOT_ID_PTR tmpSlotList = nscSlotList[moduleIndex];
+ PLHashTable *tmpSlotHashTable = nscSlotHashTable[moduleIndex];
+
+ /* first close all the session */
+ for (i=0; i < (int) tmpSlotCount; i++) {
+ slotID = tmpSlotList[i];
+ (void) NSC_CloseAllSessions(slotID);
+ }
+
+ /* now clear out the statics */
+ nscSlotList[moduleIndex] = NULL;
+ nscSlotCount[moduleIndex] = 0;
+ nscSlotHashTable[moduleIndex] = NULL;
+ nscSlotListSize[moduleIndex] = 0;
+
+ for (i=0; i < (int) tmpSlotCount; i++) {
+ slotID = tmpSlotList[i];
+ slot = (SFTKSlot *)
+ PL_HashTableLookup(tmpSlotHashTable, (void *)slotID);
+ PORT_Assert(slot);
+ if (!slot) continue;
+ SFTK_DestroySlotData(slot);
+ PL_HashTableRemove(tmpSlotHashTable, (void *)slotID);
+ }
+ PORT_Free(tmpSlotList);
+ PL_HashTableDestroy(tmpSlotHashTable);
+ }
+}
+
+static void
+sftk_closePeer(PRBool isFIPS)
+{
+ CK_SLOT_ID slotID = isFIPS ? PRIVATE_KEY_SLOT_ID: FIPS_SLOT_ID;
+ SFTKSlot *slot;
+ int moduleIndex = isFIPS? NSC_NON_FIPS_MODULE : NSC_FIPS_MODULE;
+ PLHashTable *tmpSlotHashTable = nscSlotHashTable[moduleIndex];
+
+ slot = (SFTKSlot *) PL_HashTableLookup(tmpSlotHashTable, (void *)slotID);
+ if (slot == NULL) {
+ return;
+ }
+ sftk_DBShutdown(slot);
+ return;
+}
+
+/* NSC_Initialize initializes the Cryptoki library. */
+CK_RV nsc_CommonInitialize(CK_VOID_PTR pReserved, PRBool isFIPS)
+{
+ CK_RV crv = CKR_OK;
+ SECStatus rv;
+ CK_C_INITIALIZE_ARGS *init_args = (CK_C_INITIALIZE_ARGS *) pReserved;
+ int i;
+ int moduleIndex = isFIPS? NSC_FIPS_MODULE : NSC_NON_FIPS_MODULE;
+
+ if (isFIPS) {
+ loginWaitTime = PR_SecondsToInterval(1);
+ }
+
+ rv = SECOID_Init();
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ return crv;
+ }
+
+ rv = RNG_RNGInit(); /* initialize random number generator */
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ return crv;
+ }
+ RNG_SystemInfoForRNG();
+
+
+ /* NOTE:
+ * we should be getting out mutexes from this list, not statically binding
+ * them from NSPR. This should happen before we allow the internal to split
+ * off from the rest on NSS.
+ */
+
+ /* initialize the key and cert db's */
+ if (init_args && (!(init_args->flags & CKF_OS_LOCKING_OK))) {
+ if (init_args->CreateMutex && init_args->DestroyMutex &&
+ init_args->LockMutex && init_args->UnlockMutex) {
+ /* softoken always uses NSPR (ie. OS locking), and doesn't know how
+ * to use the lock functions provided by the application.
+ */
+ crv = CKR_CANT_LOCK;
+ return crv;
+ }
+ if (init_args->CreateMutex || init_args->DestroyMutex ||
+ init_args->LockMutex || init_args->UnlockMutex) {
+ /* only some of the lock functions were provided by the
+ * application. This is invalid per PKCS#11 spec.
+ */
+ crv = CKR_ARGUMENTS_BAD;
+ return crv;
+ }
+ }
+ crv = CKR_ARGUMENTS_BAD;
+ if ((init_args && init_args->LibraryParameters)) {
+ sftk_parameters paramStrings;
+
+ crv = sftk_parseParameters
+ ((char *)init_args->LibraryParameters, &paramStrings, isFIPS);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_configure(paramStrings.man, paramStrings.libdes);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* if we have a peer already open, have him close his DB's so we
+ * don't clobber each other. */
+ if ((isFIPS && nsc_init) || (!isFIPS && nsf_init)) {
+ sftk_closePeer(isFIPS);
+ if (sftk_audit_enabled) {
+ if (isFIPS && nsc_init) {
+ sftk_LogAuditMessage(NSS_AUDIT_INFO, "enabled FIPS mode");
+ } else {
+ sftk_LogAuditMessage(NSS_AUDIT_INFO, "disabled FIPS mode");
+ }
+ }
+ }
+
+ for (i=0; i < paramStrings.token_count; i++) {
+ crv = SFTK_SlotInit(paramStrings.configdir,
+ paramStrings.updatedir, paramStrings.updateID,
+ &paramStrings.tokens[i], moduleIndex);
+ if (crv != CKR_OK) {
+ nscFreeAllSlots(moduleIndex);
+ break;
+ }
+ }
+loser:
+ sftk_freeParams(&paramStrings);
+ }
+ if (CKR_OK == crv) {
+ sftk_InitFreeLists();
+ }
+
+#if defined(XP_UNIX) && !defined(NO_PTHREADS)
+ if (CKR_OK == crv) {
+ pthread_atfork(NULL, NULL, ForkedChild);
+ }
+#endif
+ return crv;
+}
+
+CK_RV NSC_Initialize(CK_VOID_PTR pReserved)
+{
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ if (nsc_init) {
+ return CKR_CRYPTOKI_ALREADY_INITIALIZED;
+ }
+ crv = nsc_CommonInitialize(pReserved,PR_FALSE);
+ nsc_init = (PRBool) (crv == CKR_OK);
+ return crv;
+}
+
+
+/* NSC_Finalize indicates that an application is done with the
+ * Cryptoki library.*/
+CK_RV nsc_CommonFinalize (CK_VOID_PTR pReserved, PRBool isFIPS)
+{
+ nscFreeAllSlots(isFIPS ? NSC_FIPS_MODULE : NSC_NON_FIPS_MODULE);
+
+ /* don't muck with the globals is our peer is still initialized */
+ if (isFIPS && nsc_init) {
+ return CKR_OK;
+ }
+ if (!isFIPS && nsf_init) {
+ return CKR_OK;
+ }
+
+ sftk_CleanupFreeLists();
+ sftkdb_Shutdown();
+
+ /* This function does not discard all our previously aquired entropy. */
+ RNG_RNGShutdown();
+
+ /* tell freeBL to clean up after itself */
+ BL_Cleanup();
+ /* unload freeBL shared library from memory */
+ BL_Unload();
+ /* clean up the default OID table */
+ SECOID_Shutdown();
+ nsc_init = PR_FALSE;
+
+ return CKR_OK;
+}
+
+/* NSC_Finalize indicates that an application is done with the
+ * Cryptoki library.*/
+CK_RV NSC_Finalize (CK_VOID_PTR pReserved)
+{
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ if (!nsc_init) {
+ return CKR_OK;
+ }
+
+ crv = nsc_CommonFinalize (pReserved, PR_FALSE);
+
+ nsc_init = (PRBool) !(crv == CKR_OK);
+
+ return crv;
+}
+
+extern const char __nss_softokn_rcsid[];
+extern const char __nss_softokn_sccsid[];
+
+/* NSC_GetInfo returns general information about Cryptoki. */
+CK_RV NSC_GetInfo(CK_INFO_PTR pInfo)
+{
+ volatile char c; /* force a reference that won't get optimized away */
+
+ CHECK_FORK();
+
+ c = __nss_softokn_rcsid[0] + __nss_softokn_sccsid[0];
+ pInfo->cryptokiVersion.major = 2;
+ pInfo->cryptokiVersion.minor = 20;
+ PORT_Memcpy(pInfo->manufacturerID,manufacturerID,32);
+ pInfo->libraryVersion.major = SOFTOKEN_VMAJOR;
+ pInfo->libraryVersion.minor = SOFTOKEN_VMINOR;
+ PORT_Memcpy(pInfo->libraryDescription,libraryDescription,32);
+ pInfo->flags = 0;
+ return CKR_OK;
+}
+
+
+/* NSC_GetSlotList obtains a list of slots in the system. */
+CK_RV nsc_CommonGetSlotList(CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount, int moduleIndex)
+{
+ *pulCount = nscSlotCount[moduleIndex];
+ if (pSlotList != NULL) {
+ PORT_Memcpy(pSlotList,nscSlotList[moduleIndex],
+ nscSlotCount[moduleIndex]*sizeof(CK_SLOT_ID));
+ }
+ return CKR_OK;
+}
+
+/* NSC_GetSlotList obtains a list of slots in the system. */
+CK_RV NSC_GetSlotList(CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount)
+{
+ CHECK_FORK();
+ return nsc_CommonGetSlotList(tokenPresent, pSlotList, pulCount,
+ NSC_NON_FIPS_MODULE);
+}
+
+/* NSC_GetSlotInfo obtains information about a particular slot in the system. */
+CK_RV NSC_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
+{
+ SFTKSlot *slot = sftk_SlotFromID(slotID, PR_TRUE);
+
+ CHECK_FORK();
+
+ if (slot == NULL) return CKR_SLOT_ID_INVALID;
+
+ pInfo->firmwareVersion.major = 0;
+ pInfo->firmwareVersion.minor = 0;
+
+ PORT_Memcpy(pInfo->manufacturerID,manufacturerID,
+ sizeof(pInfo->manufacturerID));
+ PORT_Memcpy(pInfo->slotDescription,slot->slotDescription,
+ sizeof(pInfo->slotDescription));
+ pInfo->flags = (slot->present) ? CKF_TOKEN_PRESENT : 0;
+
+ /* all user defined slots are defined as removable */
+ if (slotID >= SFTK_MIN_USER_SLOT_ID) {
+ pInfo->flags |= CKF_REMOVABLE_DEVICE;
+ } else {
+ /* In the case where we are doing a merge update, we need
+ * the DB slot to be removable so the token name can change
+ * appropriately. */
+ SFTKDBHandle *handle = sftk_getKeyDB(slot);
+ if (handle) {
+ if (sftkdb_InUpdateMerge(handle)) {
+ pInfo->flags |= CKF_REMOVABLE_DEVICE;
+ }
+ sftk_freeDB(handle);
+ }
+ }
+
+ /* ok we really should read it out of the keydb file. */
+ /* pInfo->hardwareVersion.major = NSSLOWKEY_DB_FILE_VERSION; */
+ pInfo->hardwareVersion.major = SOFTOKEN_VMAJOR;
+ pInfo->hardwareVersion.minor = SOFTOKEN_VMINOR;
+ return CKR_OK;
+}
+
+/*
+ * check the current state of the 'needLogin' flag in case the database has
+ * been changed underneath us.
+ */
+static PRBool
+sftk_checkNeedLogin(SFTKSlot *slot, SFTKDBHandle *keyHandle)
+{
+ if (sftkdb_PWCached(keyHandle) == SECSuccess) {
+ return slot->needLogin;
+ }
+ slot->needLogin = (PRBool)!sftk_hasNullPassword(slot, keyHandle);
+ return (slot->needLogin);
+}
+
+static PRBool
+sftk_isBlank(const char *s, int len)
+{
+ int i;
+ for (i=0; i < len; i++) {
+ if (s[i] != ' ') {
+ return PR_FALSE;
+ }
+ }
+ return PR_TRUE;
+}
+
+/* NSC_GetTokenInfo obtains information about a particular token in
+ * the system. */
+CK_RV NSC_GetTokenInfo(CK_SLOT_ID slotID,CK_TOKEN_INFO_PTR pInfo)
+{
+ SFTKSlot *slot;
+ SFTKDBHandle *handle;
+
+ CHECK_FORK();
+
+ if (!nsc_init && !nsf_init) return CKR_CRYPTOKI_NOT_INITIALIZED;
+ slot = sftk_SlotFromID(slotID, PR_FALSE);
+ if (slot == NULL) return CKR_SLOT_ID_INVALID;
+
+ PORT_Memcpy(pInfo->manufacturerID,manufacturerID,32);
+ PORT_Memcpy(pInfo->model,"NSS 3 ",16);
+ PORT_Memcpy(pInfo->serialNumber,"0000000000000000",16);
+ PORT_Memcpy(pInfo->utcTime,"0000000000000000",16);
+ pInfo->ulMaxSessionCount = 0; /* arbitrarily large */
+ pInfo->ulSessionCount = slot->sessionCount;
+ pInfo->ulMaxRwSessionCount = 0; /* arbitarily large */
+ pInfo->ulRwSessionCount = slot->rwSessionCount;
+ pInfo->firmwareVersion.major = 0;
+ pInfo->firmwareVersion.minor = 0;
+ PORT_Memcpy(pInfo->label,slot->tokDescription,sizeof(pInfo->label));
+ handle = sftk_getKeyDB(slot);
+ pInfo->flags = CKF_RNG | CKF_DUAL_CRYPTO_OPERATIONS;
+ if (handle == NULL) {
+ pInfo->flags |= CKF_WRITE_PROTECTED;
+ pInfo->ulMaxPinLen = 0;
+ pInfo->ulMinPinLen = 0;
+ pInfo->ulTotalPublicMemory = 0;
+ pInfo->ulFreePublicMemory = 0;
+ pInfo->ulTotalPrivateMemory = 0;
+ pInfo->ulFreePrivateMemory = 0;
+ pInfo->hardwareVersion.major = 4;
+ pInfo->hardwareVersion.minor = 0;
+ } else {
+ /*
+ * we have three possible states which we may be in:
+ * (1) No DB password has been initialized. This also means we
+ * have no keys in the key db.
+ * (2) Password initialized to NULL. This means we have keys, but
+ * the user has chosen not use a password.
+ * (3) Finally we have an initialized password whicn is not NULL, and
+ * we will need to prompt for it.
+ */
+ if (sftkdb_HasPasswordSet(handle) == SECFailure) {
+ pInfo->flags |= CKF_LOGIN_REQUIRED;
+ } else if (!sftk_checkNeedLogin(slot,handle)) {
+ pInfo->flags |= CKF_USER_PIN_INITIALIZED;
+ } else {
+ pInfo->flags |= CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED;
+ /*
+ * if we are doing a merge style update, and we need to get the password
+ * of our source database (the database we are updating from), make sure we
+ * return a token name that will match the database we are prompting for.
+ */
+ if (sftkdb_NeedUpdateDBPassword(handle)) {
+ /* if we have an update tok description, use it. otherwise
+ * use the updateID for this database */
+ if (!sftk_isBlank(slot->updateTokDescription,
+ sizeof(pInfo->label))) {
+ PORT_Memcpy(pInfo->label,slot->updateTokDescription,
+ sizeof(pInfo->label));
+ } else {
+ /* build from updateID */
+ const char *updateID = sftkdb_GetUpdateID(handle);
+ if (updateID) {
+ sftk_setStringName(updateID, (char *)pInfo->label,
+ sizeof(pInfo->label), PR_FALSE);
+ }
+ }
+ }
+ }
+ pInfo->ulMaxPinLen = SFTK_MAX_PIN;
+ pInfo->ulMinPinLen = (CK_ULONG)slot->minimumPinLen;
+ pInfo->ulTotalPublicMemory = 1;
+ pInfo->ulFreePublicMemory = 1;
+ pInfo->ulTotalPrivateMemory = 1;
+ pInfo->ulFreePrivateMemory = 1;
+#ifdef SHDB_FIXME
+ pInfo->hardwareVersion.major = CERT_DB_FILE_VERSION;
+ pInfo->hardwareVersion.minor = handle->version;
+#else
+ pInfo->hardwareVersion.major = 0;
+ pInfo->hardwareVersion.minor = 0;
+#endif
+ sftk_freeDB(handle);
+ }
+ /*
+ * CKF_LOGIN_REQUIRED CKF_USER_PIN_INITIALIZED how CKF_TOKEN_INITIALIZED
+ * should be set
+ * 0 0 1
+ * 1 0 0
+ * 0 1 1
+ * 1 1 1
+ */
+ if (!(pInfo->flags & CKF_LOGIN_REQUIRED) ||
+ (pInfo->flags & CKF_USER_PIN_INITIALIZED)) {
+ pInfo->flags |= CKF_TOKEN_INITIALIZED;
+ }
+ return CKR_OK;
+}
+
+/* NSC_GetMechanismList obtains a list of mechanism types
+ * supported by a token. */
+CK_RV NSC_GetMechanismList(CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount)
+{
+ CK_ULONG i;
+
+ CHECK_FORK();
+
+ switch (slotID) {
+ /* default: */
+ case NETSCAPE_SLOT_ID:
+ *pulCount = mechanismCount;
+ if (pMechanismList != NULL) {
+ for (i=0; i < mechanismCount; i++) {
+ pMechanismList[i] = mechanisms[i].type;
+ }
+ }
+ break;
+ default:
+ *pulCount = 0;
+ for (i=0; i < mechanismCount; i++) {
+ if (mechanisms[i].privkey) {
+ (*pulCount)++;
+ if (pMechanismList != NULL) {
+ *pMechanismList++ = mechanisms[i].type;
+ }
+ }
+ }
+ break;
+ }
+ return CKR_OK;
+}
+
+
+/* NSC_GetMechanismInfo obtains information about a particular mechanism
+ * possibly supported by a token. */
+CK_RV NSC_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo)
+{
+ PRBool isPrivateKey;
+ CK_ULONG i;
+
+ CHECK_FORK();
+
+ switch (slotID) {
+ case NETSCAPE_SLOT_ID:
+ isPrivateKey = PR_FALSE;
+ break;
+ default:
+ isPrivateKey = PR_TRUE;
+ break;
+ }
+ for (i=0; i < mechanismCount; i++) {
+ if (type == mechanisms[i].type) {
+ if (isPrivateKey && !mechanisms[i].privkey) {
+ return CKR_MECHANISM_INVALID;
+ }
+ PORT_Memcpy(pInfo,&mechanisms[i].info, sizeof(CK_MECHANISM_INFO));
+ return CKR_OK;
+ }
+ }
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV sftk_MechAllowsOperation(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE op)
+{
+ CK_ULONG i;
+ CK_FLAGS flags;
+
+ switch (op) {
+ case CKA_ENCRYPT: flags = CKF_ENCRYPT; break;
+ case CKA_DECRYPT: flags = CKF_DECRYPT; break;
+ case CKA_WRAP: flags = CKF_WRAP; break;
+ case CKA_UNWRAP: flags = CKF_UNWRAP; break;
+ case CKA_SIGN: flags = CKF_SIGN; break;
+ case CKA_SIGN_RECOVER: flags = CKF_SIGN_RECOVER; break;
+ case CKA_VERIFY: flags = CKF_VERIFY; break;
+ case CKA_VERIFY_RECOVER: flags = CKF_VERIFY_RECOVER; break;
+ case CKA_DERIVE: flags = CKF_DERIVE; break;
+ default:
+ return CKR_ARGUMENTS_BAD;
+ }
+ for (i=0; i < mechanismCount; i++) {
+ if (type == mechanisms[i].type) {
+ return (flags & mechanisms[i].info.flags) ? CKR_OK
+ : CKR_MECHANISM_INVALID;
+ }
+ }
+ return CKR_MECHANISM_INVALID;
+}
+
+/* NSC_InitToken initializes a token. */
+CK_RV NSC_InitToken(CK_SLOT_ID slotID,CK_CHAR_PTR pPin,
+ CK_ULONG ulPinLen,CK_CHAR_PTR pLabel) {
+ SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
+ SFTKDBHandle *handle;
+ SFTKDBHandle *certHandle;
+ SECStatus rv;
+ unsigned int i;
+ SFTKObject *object;
+
+ CHECK_FORK();
+
+ if (slot == NULL) return CKR_SLOT_ID_INVALID;
+
+ /* don't initialize the database if we aren't talking to a token
+ * that uses the key database.
+ */
+ if (slotID == NETSCAPE_SLOT_ID) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* first, delete all our loaded key and cert objects from our
+ * internal list. */
+ PZ_Lock(slot->objectLock);
+ for (i=0; i < slot->sessObjHashSize; i++) {
+ do {
+ object = slot->sessObjHashTable[i];
+ /* hand deque */
+ /* this duplicates function of NSC_close session functions, but
+ * because we know that we are freeing all the sessions, we can
+ * do more efficient processing */
+ if (object) {
+ slot->sessObjHashTable[i] = object->next;
+
+ if (object->next) object->next->prev = NULL;
+ object->next = object->prev = NULL;
+ }
+ if (object) sftk_FreeObject(object);
+ } while (object != NULL);
+ }
+ slot->DB_loaded = PR_FALSE;
+ PZ_Unlock(slot->objectLock);
+
+ /* then clear out the key database */
+ handle = sftk_getKeyDB(slot);
+ if (handle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ rv = sftkdb_ResetKeyDB(handle);
+ sftk_freeDB(handle);
+ if (rv != SECSuccess) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ /* finally mark all the user certs as non-user certs */
+ certHandle = sftk_getCertDB(slot);
+ if (certHandle == NULL) return CKR_OK;
+
+ sftk_freeDB(certHandle);
+
+ return CKR_OK; /*is this the right function for not implemented*/
+}
+
+
+/* NSC_InitPIN initializes the normal user's PIN. */
+CK_RV NSC_InitPIN(CK_SESSION_HANDLE hSession,
+ CK_CHAR_PTR pPin, CK_ULONG ulPinLen)
+{
+ SFTKSession *sp = NULL;
+ SFTKSlot *slot;
+ SFTKDBHandle *handle = NULL;
+ char newPinStr[SFTK_MAX_PIN+1];
+ SECStatus rv;
+ CK_RV crv = CKR_SESSION_HANDLE_INVALID;
+ PRBool tokenRemoved = PR_FALSE;
+
+ CHECK_FORK();
+
+ sp = sftk_SessionFromHandle(hSession);
+ if (sp == NULL) {
+ goto loser;
+ }
+
+ slot = sftk_SlotFromSession(sp);
+ if (slot == NULL) {
+ goto loser;
+ }
+
+ handle = sftk_getKeyDB(slot);
+ if (handle == NULL) {
+ crv = CKR_PIN_LEN_RANGE;
+ goto loser;
+ }
+
+
+ if (sp->info.state != CKS_RW_SO_FUNCTIONS) {
+ crv = CKR_USER_NOT_LOGGED_IN;
+ goto loser;
+ }
+
+ sftk_FreeSession(sp);
+ sp = NULL;
+
+ /* make sure the pins aren't too long */
+ if (ulPinLen > SFTK_MAX_PIN) {
+ crv = CKR_PIN_LEN_RANGE;
+ goto loser;
+ }
+ if (ulPinLen < (CK_ULONG)slot->minimumPinLen) {
+ crv = CKR_PIN_LEN_RANGE;
+ goto loser;
+ }
+
+ if (sftkdb_HasPasswordSet(handle) != SECFailure) {
+ crv = CKR_DEVICE_ERROR;
+ goto loser;
+ }
+
+ /* convert to null terminated string */
+ PORT_Memcpy(newPinStr, pPin, ulPinLen);
+ newPinStr[ulPinLen] = 0;
+
+ /* build the hashed pins which we pass around */
+
+ /* change the data base */
+ rv = sftkdb_ChangePassword(handle, NULL, newPinStr, &tokenRemoved);
+ if (tokenRemoved) {
+ sftk_CloseAllSessions(slot);
+ }
+ sftk_freeDB(handle);
+ handle = NULL;
+
+ /* Now update our local copy of the pin */
+ if (rv == SECSuccess) {
+ if (ulPinLen == 0) slot->needLogin = PR_FALSE;
+ return CKR_OK;
+ }
+ crv = CKR_PIN_INCORRECT;
+
+loser:
+ if (sp) {
+ sftk_FreeSession(sp);
+ }
+ if (handle) {
+ sftk_freeDB(handle);
+ }
+ return crv;
+}
+
+
+/* NSC_SetPIN modifies the PIN of user that is currently logged in. */
+/* NOTE: This is only valid for the PRIVATE_KEY_SLOT */
+CK_RV NSC_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin,
+ CK_ULONG ulOldLen, CK_CHAR_PTR pNewPin, CK_ULONG ulNewLen)
+{
+ SFTKSession *sp = NULL;
+ SFTKSlot *slot;
+ SFTKDBHandle *handle = NULL;
+ char newPinStr[SFTK_MAX_PIN+1],oldPinStr[SFTK_MAX_PIN+1];
+ SECStatus rv;
+ CK_RV crv = CKR_SESSION_HANDLE_INVALID;
+ PRBool tokenRemoved = PR_FALSE;
+
+ CHECK_FORK();
+
+ sp = sftk_SessionFromHandle(hSession);
+ if (sp == NULL) {
+ goto loser;
+ }
+
+ slot = sftk_SlotFromSession(sp);
+ if (!slot) {
+ goto loser;
+ }
+
+ handle = sftk_getKeyDB(slot);
+ if (handle == NULL) {
+ sftk_FreeSession(sp);
+ return CKR_PIN_LEN_RANGE; /* XXX FIXME wrong return value */
+ }
+
+ if (slot->needLogin && sp->info.state != CKS_RW_USER_FUNCTIONS) {
+ crv = CKR_USER_NOT_LOGGED_IN;
+ goto loser;
+ }
+
+ sftk_FreeSession(sp);
+ sp = NULL;
+
+ /* make sure the pins aren't too long */
+ if ((ulNewLen > SFTK_MAX_PIN) || (ulOldLen > SFTK_MAX_PIN)) {
+ crv = CKR_PIN_LEN_RANGE;
+ goto loser;
+ }
+ if (ulNewLen < (CK_ULONG)slot->minimumPinLen) {
+ crv = CKR_PIN_LEN_RANGE;
+ goto loser;
+ }
+
+
+ /* convert to null terminated string */
+ PORT_Memcpy(newPinStr,pNewPin,ulNewLen);
+ newPinStr[ulNewLen] = 0;
+ PORT_Memcpy(oldPinStr,pOldPin,ulOldLen);
+ oldPinStr[ulOldLen] = 0;
+
+ /* change the data base password */
+ PR_Lock(slot->pwCheckLock);
+ rv = sftkdb_ChangePassword(handle, oldPinStr, newPinStr, &tokenRemoved);
+ if (tokenRemoved) {
+ sftk_CloseAllSessions(slot);
+ }
+ sftk_freeDB(handle);
+ handle = NULL;
+ if ((rv != SECSuccess) && (slot->slotID == FIPS_SLOT_ID)) {
+ PR_Sleep(loginWaitTime);
+ }
+ PR_Unlock(slot->pwCheckLock);
+
+ /* Now update our local copy of the pin */
+ if (rv == SECSuccess) {
+ slot->needLogin = (PRBool)(ulNewLen != 0);
+ return CKR_OK;
+ }
+ crv = CKR_PIN_INCORRECT;
+loser:
+ if (sp) {
+ sftk_FreeSession(sp);
+ }
+ if (handle) {
+ sftk_freeDB(handle);
+ }
+ return crv;
+}
+
+/* NSC_OpenSession opens a session between an application and a token. */
+CK_RV NSC_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags,
+ CK_VOID_PTR pApplication,CK_NOTIFY Notify,CK_SESSION_HANDLE_PTR phSession)
+{
+ SFTKSlot *slot;
+ CK_SESSION_HANDLE sessionID;
+ SFTKSession *session;
+ SFTKSession *sameID;
+
+ CHECK_FORK();
+
+ slot = sftk_SlotFromID(slotID, PR_FALSE);
+ if (slot == NULL) return CKR_SLOT_ID_INVALID;
+
+ /* new session (we only have serial sessions) */
+ session = sftk_NewSession(slotID, Notify, pApplication,
+ flags | CKF_SERIAL_SESSION);
+ if (session == NULL) return CKR_HOST_MEMORY;
+
+ if (slot->readOnly && (flags & CKF_RW_SESSION)) {
+ /* NETSCAPE_SLOT_ID is Read ONLY */
+ session->info.flags &= ~CKF_RW_SESSION;
+ }
+ PZ_Lock(slot->slotLock);
+ ++slot->sessionCount;
+ PZ_Unlock(slot->slotLock);
+ if (session->info.flags & CKF_RW_SESSION) {
+ PR_AtomicIncrement(&slot->rwSessionCount);
+ }
+
+ do {
+ PZLock *lock;
+ do {
+ sessionID = (PR_AtomicIncrement(&slot->sessionIDCount) & 0xffffff)
+ | (slot->index << 24);
+ } while (sessionID == CK_INVALID_HANDLE);
+ lock = SFTK_SESSION_LOCK(slot,sessionID);
+ PZ_Lock(lock);
+ sftkqueue_find(sameID, sessionID, slot->head, slot->sessHashSize);
+ if (sameID == NULL) {
+ session->handle = sessionID;
+ sftk_update_state(slot, session);
+ sftkqueue_add(session, sessionID, slot->head,slot->sessHashSize);
+ } else {
+ slot->sessionIDConflict++; /* for debugging */
+ }
+ PZ_Unlock(lock);
+ } while (sameID != NULL);
+
+ *phSession = sessionID;
+ return CKR_OK;
+}
+
+
+/* NSC_CloseSession closes a session between an application and a token. */
+CK_RV NSC_CloseSession(CK_SESSION_HANDLE hSession)
+{
+ SFTKSlot *slot;
+ SFTKSession *session;
+ PRBool sessionFound;
+ PZLock *lock;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ slot = sftk_SlotFromSession(session);
+ sessionFound = PR_FALSE;
+
+ /* lock */
+ lock = SFTK_SESSION_LOCK(slot,hSession);
+ PZ_Lock(lock);
+ if (sftkqueue_is_queued(session,hSession,slot->head,slot->sessHashSize)) {
+ sessionFound = PR_TRUE;
+ sftkqueue_delete(session,hSession,slot->head,slot->sessHashSize);
+ session->refCount--; /* can't go to zero while we hold the reference */
+ PORT_Assert(session->refCount > 0);
+ }
+ PZ_Unlock(lock);
+
+ if (sessionFound) {
+ SFTKDBHandle *handle;
+ handle = sftk_getKeyDB(slot);
+ PZ_Lock(slot->slotLock);
+ if (--slot->sessionCount == 0) {
+ slot->isLoggedIn = PR_FALSE;
+ if (handle) {
+ sftkdb_ClearPassword(handle);
+ }
+ }
+ PZ_Unlock(slot->slotLock);
+ if (handle) {
+ sftk_freeDB(handle);
+ }
+ if (session->info.flags & CKF_RW_SESSION) {
+ PR_AtomicDecrement(&slot->rwSessionCount);
+ }
+ }
+
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+
+/* NSC_CloseAllSessions closes all sessions with a token. */
+CK_RV NSC_CloseAllSessions (CK_SLOT_ID slotID)
+{
+ SFTKSlot *slot;
+
+ CHECK_FORK();
+
+ slot = sftk_SlotFromID(slotID, PR_FALSE);
+ if (slot == NULL) return CKR_SLOT_ID_INVALID;
+
+ return sftk_CloseAllSessions(slot);
+}
+
+
+
+/* NSC_GetSessionInfo obtains information about the session. */
+CK_RV NSC_GetSessionInfo(CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo)
+{
+ SFTKSession *session;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+ PORT_Memcpy(pInfo,&session->info,sizeof(CK_SESSION_INFO));
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+/* NSC_Login logs a user into a token. */
+CK_RV NSC_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType,
+ CK_CHAR_PTR pPin, CK_ULONG ulPinLen)
+{
+ SFTKSlot *slot;
+ SFTKSession *session;
+ SFTKDBHandle *handle;
+ CK_FLAGS sessionFlags;
+ SECStatus rv;
+ CK_RV crv;
+ char pinStr[SFTK_MAX_PIN+1];
+ PRBool tokenRemoved = PR_FALSE;
+
+ CHECK_FORK();
+
+ /* get the slot */
+ slot = sftk_SlotFromSessionHandle(hSession);
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /* make sure the session is valid */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ sessionFlags = session->info.flags;
+ sftk_FreeSession(session);
+ session = NULL;
+
+ /* can't log into the Netscape Slot */
+ if (slot->slotID == NETSCAPE_SLOT_ID) {
+ return CKR_USER_TYPE_INVALID;
+ }
+
+ if (slot->isLoggedIn) return CKR_USER_ALREADY_LOGGED_IN;
+ slot->ssoLoggedIn = PR_FALSE;
+
+ if (ulPinLen > SFTK_MAX_PIN) return CKR_PIN_LEN_RANGE;
+
+ /* convert to null terminated string */
+ PORT_Memcpy(pinStr,pPin,ulPinLen);
+ pinStr[ulPinLen] = 0;
+
+ handle = sftk_getKeyDB(slot);
+ if (handle == NULL) {
+ return CKR_USER_TYPE_INVALID;
+ }
+
+ /*
+ * Deal with bootstrap. We allow the SSO to login in with a NULL
+ * password if and only if we haven't initialized the KEY DB yet.
+ * We only allow this on a RW session.
+ */
+ rv = sftkdb_HasPasswordSet(handle);
+ if (rv == SECFailure) {
+ /* allow SSO's to log in only if there is not password on the
+ * key database */
+ if (((userType == CKU_SO) && (sessionFlags & CKF_RW_SESSION))
+ /* fips always needs to authenticate, even if there isn't a db */
+ || (slot->slotID == FIPS_SLOT_ID)) {
+ /* should this be a fixed password? */
+ if (ulPinLen == 0) {
+ sftkdb_ClearPassword(handle);
+ PZ_Lock(slot->slotLock);
+ slot->isLoggedIn = PR_TRUE;
+ slot->ssoLoggedIn = (PRBool)(userType == CKU_SO);
+ PZ_Unlock(slot->slotLock);
+ sftk_update_all_states(slot);
+ crv = CKR_OK;
+ goto done;
+ }
+ crv = CKR_PIN_INCORRECT;
+ goto done;
+ }
+ crv = CKR_USER_TYPE_INVALID;
+ goto done;
+ }
+
+ /* don't allow the SSO to log in if the user is already initialized */
+ if (userType != CKU_USER) {
+ crv = CKR_USER_TYPE_INVALID;
+ goto done;
+ }
+
+
+ /* build the hashed pins which we pass around */
+ PR_Lock(slot->pwCheckLock);
+ rv = sftkdb_CheckPassword(handle,pinStr, &tokenRemoved);
+ if (tokenRemoved) {
+ sftk_CloseAllSessions(slot);
+ }
+ if ((rv != SECSuccess) && (slot->slotID == FIPS_SLOT_ID)) {
+ PR_Sleep(loginWaitTime);
+ }
+ PR_Unlock(slot->pwCheckLock);
+ if (rv == SECSuccess) {
+ PZ_Lock(slot->slotLock);
+ /* make sure the login state matches the underlying
+ * database state */
+ slot->isLoggedIn = sftkdb_PWCached(handle) == SECSuccess ?
+ PR_TRUE : PR_FALSE;
+ PZ_Unlock(slot->slotLock);
+
+ sftk_freeDB(handle);
+ handle = NULL;
+
+ /* update all sessions */
+ sftk_update_all_states(slot);
+ return CKR_OK;
+ }
+
+ crv = CKR_PIN_INCORRECT;
+done:
+ if (handle) {
+ sftk_freeDB(handle);
+ }
+ return crv;
+}
+
+/* NSC_Logout logs a user out from a token. */
+CK_RV NSC_Logout(CK_SESSION_HANDLE hSession)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ SFTKSession *session;
+ SFTKDBHandle *handle;
+
+ CHECK_FORK();
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ sftk_FreeSession(session);
+ session = NULL;
+
+ if (!slot->isLoggedIn) return CKR_USER_NOT_LOGGED_IN;
+
+ handle = sftk_getKeyDB(slot);
+ PZ_Lock(slot->slotLock);
+ slot->isLoggedIn = PR_FALSE;
+ slot->ssoLoggedIn = PR_FALSE;
+ if (handle) {
+ sftkdb_ClearPassword(handle);
+ }
+ PZ_Unlock(slot->slotLock);
+ if (handle) {
+ sftk_freeDB(handle);
+ }
+
+ sftk_update_all_states(slot);
+ return CKR_OK;
+}
+
+/*
+ * Create a new slot on the fly. The slot that is passed in is the
+ * slot the request came from. Only the crypto or FIPS slots can
+ * be used. The resulting slot will live in the same module as
+ * the slot the request was passed to. object is the creation object
+ * that specifies the module spec for the new slot.
+ */
+static CK_RV sftk_CreateNewSlot(SFTKSlot *slot, CK_OBJECT_CLASS class,
+ SFTKObject *object)
+{
+ CK_SLOT_ID idMin, idMax;
+ PRBool isFIPS = PR_FALSE;
+ unsigned long moduleIndex;
+ SFTKAttribute *attribute;
+ sftk_parameters paramStrings;
+ char *paramString;
+ CK_SLOT_ID slotID = 0;
+ SFTKSlot *newSlot = NULL;
+ CK_RV crv = CKR_OK;
+
+ /* only the crypto or FIPS slots can create new slot objects */
+ if (slot->slotID == NETSCAPE_SLOT_ID) {
+ idMin = SFTK_MIN_USER_SLOT_ID;
+ idMax = SFTK_MAX_USER_SLOT_ID;
+ moduleIndex = NSC_NON_FIPS_MODULE;
+ isFIPS = PR_FALSE;
+ } else if (slot->slotID == FIPS_SLOT_ID) {
+ idMin = SFTK_MIN_FIPS_USER_SLOT_ID;
+ idMax = SFTK_MAX_FIPS_USER_SLOT_ID;
+ moduleIndex = NSC_FIPS_MODULE;
+ isFIPS = PR_TRUE;
+ } else {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ attribute = sftk_FindAttribute(object,CKA_NETSCAPE_MODULE_SPEC);
+ if (attribute == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ paramString = (char *)attribute->attrib.pValue;
+ crv = sftk_parseParameters(paramString, &paramStrings, isFIPS);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* enforce only one at a time */
+ if (paramStrings.token_count != 1) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto loser;
+ }
+
+ slotID = paramStrings.tokens[0].slotID;
+
+ /* stay within the valid ID space */
+ if ((slotID < idMin) || (slotID > idMax)) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto loser;
+ }
+
+ /* unload any existing slot at this id */
+ newSlot = sftk_SlotFromID(slotID, PR_TRUE);
+ if (newSlot && newSlot->present) {
+ crv = SFTK_ShutdownSlot(newSlot);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ }
+
+ /* if we were just planning on deleting the slot, then do so now */
+ if (class == CKO_NETSCAPE_DELSLOT) {
+ /* sort of a unconventional use of this error code, be we are
+ * overusing CKR_ATTRIBUTE_VALUE_INVALID, and it does apply */
+ crv = newSlot ? CKR_OK : CKR_SLOT_ID_INVALID;
+ goto loser; /* really exit */
+ }
+
+ if (newSlot) {
+ crv = SFTK_SlotReInit(newSlot, paramStrings.configdir,
+ paramStrings.updatedir, paramStrings.updateID,
+ &paramStrings.tokens[0], moduleIndex);
+ } else {
+ crv = SFTK_SlotInit(paramStrings.configdir,
+ paramStrings.updatedir, paramStrings.updateID,
+ &paramStrings.tokens[0], moduleIndex);
+ }
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+loser:
+ sftk_freeParams(&paramStrings);
+ sftk_FreeAttribute(attribute);
+
+ return crv;
+}
+
+
+/* NSC_CreateObject creates a new object. */
+CK_RV NSC_CreateObject(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ SFTKSession *session;
+ SFTKObject *object;
+ /* make sure class isn't randomly CKO_NETSCAPE_NEWSLOT or
+ * CKO_NETSCPE_DELSLOT. */
+ CK_OBJECT_CLASS class = CKO_VENDOR_DEFINED;
+ CK_RV crv;
+ int i;
+
+ CHECK_FORK();
+
+ *phObject = CK_INVALID_HANDLE;
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * now lets create an object to hang the attributes off of
+ */
+ object = sftk_NewObject(slot); /* fill in the handle later */
+ if (object == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * load the template values into the object
+ */
+ for (i=0; i < (int) ulCount; i++) {
+ crv = sftk_AddAttributeType(object,sftk_attr_expand(&pTemplate[i]));
+ if (crv != CKR_OK) {
+ sftk_FreeObject(object);
+ return crv;
+ }
+ if ((pTemplate[i].type == CKA_CLASS) && pTemplate[i].pValue) {
+ class = *(CK_OBJECT_CLASS *)pTemplate[i].pValue;
+ }
+ }
+
+ /* get the session */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ sftk_FreeObject(object);
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /*
+ * handle pseudo objects (CKO_NEWSLOT)
+ */
+ if ((class == CKO_NETSCAPE_NEWSLOT) || (class == CKO_NETSCAPE_DELSLOT)) {
+ crv = sftk_CreateNewSlot(slot, class, object);
+ goto done;
+ }
+
+ /*
+ * handle the base object stuff
+ */
+ crv = sftk_handleObject(object,session);
+ *phObject = object->handle;
+done:
+ sftk_FreeSession(session);
+ sftk_FreeObject(object);
+
+ return crv;
+}
+
+
+
+/* NSC_CopyObject copies an object, creating a new object for the copy. */
+CK_RV NSC_CopyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject)
+{
+ SFTKObject *destObject,*srcObject;
+ SFTKSession *session;
+ CK_RV crv = CKR_OK;
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ int i;
+
+ CHECK_FORK();
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /* Get srcObject so we can find the class */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ srcObject = sftk_ObjectFromHandle(hObject,session);
+ if (srcObject == NULL) {
+ sftk_FreeSession(session);
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+ /*
+ * create an object to hang the attributes off of
+ */
+ destObject = sftk_NewObject(slot); /* fill in the handle later */
+ if (destObject == NULL) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(srcObject);
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * load the template values into the object
+ */
+ for (i=0; i < (int) ulCount; i++) {
+ if (sftk_modifyType(pTemplate[i].type,srcObject->objclass) == SFTK_NEVER) {
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ break;
+ }
+ crv = sftk_AddAttributeType(destObject,sftk_attr_expand(&pTemplate[i]));
+ if (crv != CKR_OK) { break; }
+ }
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(srcObject);
+ sftk_FreeObject(destObject);
+ return crv;
+ }
+
+ /* sensitive can only be changed to CK_TRUE */
+ if (sftk_hasAttribute(destObject,CKA_SENSITIVE)) {
+ if (!sftk_isTrue(destObject,CKA_SENSITIVE)) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(srcObject);
+ sftk_FreeObject(destObject);
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+ }
+
+ /*
+ * now copy the old attributes from the new attributes
+ */
+ /* don't create a token object if we aren't in a rw session */
+ /* we need to hold the lock to copy a consistant version of
+ * the object. */
+ crv = sftk_CopyObject(destObject,srcObject);
+
+ destObject->objclass = srcObject->objclass;
+ sftk_FreeObject(srcObject);
+ if (crv != CKR_OK) {
+ sftk_FreeObject(destObject);
+ sftk_FreeSession(session);
+ return crv;
+ }
+
+ crv = sftk_handleObject(destObject,session);
+ *phNewObject = destObject->handle;
+ sftk_FreeSession(session);
+ sftk_FreeObject(destObject);
+
+ return crv;
+}
+
+
+/* NSC_GetObjectSize gets the size of an object in bytes. */
+CK_RV NSC_GetObjectSize(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize)
+{
+ CHECK_FORK();
+
+ *pulSize = 0;
+ return CKR_OK;
+}
+
+
+/* NSC_GetAttributeValue obtains the value of one or more object attributes. */
+CK_RV NSC_GetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ SFTKSession *session;
+ SFTKObject *object;
+ SFTKAttribute *attribute;
+ PRBool sensitive;
+ CK_RV crv;
+ int i;
+
+ CHECK_FORK();
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * make sure we're allowed
+ */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /* short circuit everything for token objects */
+ if (sftk_isToken(hObject)) {
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+ SFTKDBHandle *dbHandle = sftk_getDBForTokenObject(slot, hObject);
+ SFTKDBHandle *keydb = NULL;
+
+ if (dbHandle == NULL) {
+ sftk_FreeSession(session);
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ crv = sftkdb_GetAttributeValue(dbHandle, hObject, pTemplate, ulCount);
+
+ /* make sure we don't export any sensitive information */
+ keydb = sftk_getKeyDB(slot);
+ if (dbHandle == keydb) {
+ for (i=0; i < (int) ulCount; i++) {
+ if (sftk_isSensitive(pTemplate[i].type,CKO_PRIVATE_KEY)) {
+ crv = CKR_ATTRIBUTE_SENSITIVE;
+ if (pTemplate[i].pValue && (pTemplate[i].ulValueLen!= -1)){
+ PORT_Memset(pTemplate[i].pValue, 0,
+ pTemplate[i].ulValueLen);
+ }
+ pTemplate[i].ulValueLen = -1;
+ }
+ }
+ }
+
+ sftk_FreeSession(session);
+ sftk_freeDB(dbHandle);
+ if (keydb) {
+ sftk_freeDB(keydb);
+ }
+ return crv;
+ }
+
+ /* handle the session object */
+ object = sftk_ObjectFromHandle(hObject,session);
+ sftk_FreeSession(session);
+ if (object == NULL) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ /* don't read a private object if we aren't logged in */
+ if ((!slot->isLoggedIn) && (slot->needLogin) &&
+ (sftk_isTrue(object,CKA_PRIVATE))) {
+ sftk_FreeObject(object);
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+
+ crv = CKR_OK;
+ sensitive = sftk_isTrue(object,CKA_SENSITIVE);
+ for (i=0; i < (int) ulCount; i++) {
+ /* Make sure that this attribute is retrievable */
+ if (sensitive && sftk_isSensitive(pTemplate[i].type,object->objclass)) {
+ crv = CKR_ATTRIBUTE_SENSITIVE;
+ pTemplate[i].ulValueLen = -1;
+ continue;
+ }
+ attribute = sftk_FindAttribute(object,pTemplate[i].type);
+ if (attribute == NULL) {
+ crv = CKR_ATTRIBUTE_TYPE_INVALID;
+ pTemplate[i].ulValueLen = -1;
+ continue;
+ }
+ if (pTemplate[i].pValue != NULL) {
+ PORT_Memcpy(pTemplate[i].pValue,attribute->attrib.pValue,
+ attribute->attrib.ulValueLen);
+ }
+ pTemplate[i].ulValueLen = attribute->attrib.ulValueLen;
+ sftk_FreeAttribute(attribute);
+ }
+
+ sftk_FreeObject(object);
+ return crv;
+}
+
+/* NSC_SetAttributeValue modifies the value of one or more object attributes */
+CK_RV NSC_SetAttributeValue (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ SFTKSession *session;
+ SFTKAttribute *attribute;
+ SFTKObject *object;
+ PRBool isToken;
+ CK_RV crv = CKR_OK;
+ CK_BBOOL legal;
+ int i;
+
+ CHECK_FORK();
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * make sure we're allowed
+ */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ object = sftk_ObjectFromHandle(hObject,session);
+ if (object == NULL) {
+ sftk_FreeSession(session);
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ /* don't modify a private object if we aren't logged in */
+ if ((!slot->isLoggedIn) && (slot->needLogin) &&
+ (sftk_isTrue(object,CKA_PRIVATE))) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(object);
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+
+ /* don't modify a token object if we aren't in a rw session */
+ isToken = sftk_isTrue(object,CKA_TOKEN);
+ if (((session->info.flags & CKF_RW_SESSION) == 0) && isToken) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(object);
+ return CKR_SESSION_READ_ONLY;
+ }
+ sftk_FreeSession(session);
+
+ /* only change modifiable objects */
+ if (!sftk_isTrue(object,CKA_MODIFIABLE)) {
+ sftk_FreeObject(object);
+ return CKR_ATTRIBUTE_READ_ONLY;
+ }
+
+ for (i=0; i < (int) ulCount; i++) {
+ /* Make sure that this attribute is changeable */
+ switch (sftk_modifyType(pTemplate[i].type,object->objclass)) {
+ case SFTK_NEVER:
+ case SFTK_ONCOPY:
+ default:
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ break;
+
+ case SFTK_SENSITIVE:
+ legal = (pTemplate[i].type == CKA_EXTRACTABLE) ? CK_FALSE : CK_TRUE;
+ if ((*(CK_BBOOL *)pTemplate[i].pValue) != legal) {
+ crv = CKR_ATTRIBUTE_READ_ONLY;
+ }
+ break;
+ case SFTK_ALWAYS:
+ break;
+ }
+ if (crv != CKR_OK) break;
+
+ /* find the old attribute */
+ attribute = sftk_FindAttribute(object,pTemplate[i].type);
+ if (attribute == NULL) {
+ crv =CKR_ATTRIBUTE_TYPE_INVALID;
+ break;
+ }
+ sftk_FreeAttribute(attribute);
+ crv = sftk_forceAttribute(object,sftk_attr_expand(&pTemplate[i]));
+ if (crv != CKR_OK) break;
+
+ }
+
+ sftk_FreeObject(object);
+ return crv;
+}
+
+static CK_RV
+sftk_expandSearchList(SFTKSearchResults *search, int count)
+{
+ search->array_size += count;
+ search->handles = (CK_OBJECT_HANDLE *)PORT_Realloc(search->handles,
+ sizeof(CK_OBJECT_HANDLE)*search->array_size);
+ return search->handles ? CKR_OK : CKR_HOST_MEMORY;
+}
+
+
+
+static CK_RV
+sftk_searchDatabase(SFTKDBHandle *handle, SFTKSearchResults *search,
+ const CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ CK_RV crv;
+ int objectListSize = search->array_size-search->size;
+ CK_OBJECT_HANDLE *array = &search->handles[search->size];
+ SDBFind *find;
+ CK_ULONG count;
+
+ crv = sftkdb_FindObjectsInit(handle, pTemplate, ulCount, &find);
+ if (crv != CKR_OK)
+ return crv;
+ do {
+ crv = sftkdb_FindObjects(handle, find, array, objectListSize, &count);
+ if ((crv != CKR_OK) || (count == 0))
+ break;
+ search->size += count;
+ objectListSize -= count;
+ if (objectListSize > 0)
+ break;
+ crv = sftk_expandSearchList(search,NSC_SEARCH_BLOCK_SIZE);
+ objectListSize = NSC_SEARCH_BLOCK_SIZE;
+ array = &search->handles[search->size];
+ } while (crv == CKR_OK);
+ sftkdb_FindObjectsFinal(handle, find);
+
+ return crv;
+}
+
+/* softoken used to search the SMimeEntries automatically instead of
+ * doing this in pk11wrap. This code should really be up in
+ * pk11wrap so that it will work with other tokens other than softoken.
+ */
+CK_RV
+sftk_emailhack(SFTKSlot *slot, SFTKDBHandle *handle,
+ SFTKSearchResults *search, CK_ATTRIBUTE *pTemplate, CK_LONG ulCount)
+{
+ PRBool isCert = PR_FALSE;
+ int emailIndex = -1;
+ int i;
+ SFTKSearchResults smime_search;
+ CK_ATTRIBUTE smime_template[2];
+ CK_OBJECT_CLASS smime_class = CKO_NETSCAPE_SMIME;
+ SFTKAttribute *attribute = NULL;
+ SFTKObject *object = NULL;
+ CK_RV crv = CKR_OK;
+
+
+ smime_search.handles = NULL; /* paranoia, some one is bound to add a goto
+ * loser before this gets initialized */
+
+ /* see if we are looking for email certs */
+ for (i=0; i < ulCount; i++) {
+ if (pTemplate[i].type == CKA_CLASS) {
+ if ((pTemplate[i].ulValueLen != sizeof(CK_OBJECT_CLASS) ||
+ (*(CK_OBJECT_CLASS *)pTemplate[i].pValue) != CKO_CERTIFICATE)) {
+ /* not a cert, skip out */
+ break;
+ }
+ isCert = PR_TRUE;
+ } else if (pTemplate[i].type == CKA_NETSCAPE_EMAIL) {
+ emailIndex = i;
+
+ }
+ if (isCert && (emailIndex != -1)) break;
+ }
+
+ if (!isCert || (emailIndex == -1)) {
+ return CKR_OK;
+ }
+
+ /* we are doing a cert and email search, find the SMimeEntry */
+ smime_template[0].type = CKA_CLASS;
+ smime_template[0].pValue = &smime_class;
+ smime_template[0].ulValueLen = sizeof(smime_class);
+ smime_template[1] = pTemplate[emailIndex];
+
+ smime_search.handles = (CK_OBJECT_HANDLE *)
+ PORT_Alloc(sizeof(CK_OBJECT_HANDLE) * NSC_SEARCH_BLOCK_SIZE);
+ if (smime_search.handles == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ smime_search.index = 0;
+ smime_search.size = 0;
+ smime_search.array_size = NSC_SEARCH_BLOCK_SIZE;
+
+ crv = sftk_searchDatabase(handle, &smime_search, smime_template, 2);
+ if (crv != CKR_OK || smime_search.size == 0) {
+ goto loser;
+ }
+
+ /* get the SMime subject */
+ object = sftk_NewTokenObject(slot, NULL, smime_search.handles[0]);
+ if (object == NULL) {
+ crv = CKR_HOST_MEMORY; /* is there any other reason for this failure? */
+ goto loser;
+ }
+ attribute = sftk_FindAttribute(object,CKA_SUBJECT);
+ if (attribute == NULL) {
+ crv = CKR_ATTRIBUTE_TYPE_INVALID;
+ goto loser;
+ }
+
+ /* now find the certs with that subject */
+ pTemplate[emailIndex] = attribute->attrib;
+ /* now add the appropriate certs to the search list */
+ crv = sftk_searchDatabase(handle, search, pTemplate, ulCount);
+ pTemplate[emailIndex] = smime_template[1]; /* restore the user's template*/
+
+loser:
+ if (attribute) {
+ sftk_FreeAttribute(attribute);
+ }
+ if (object) {
+ sftk_FreeObject(object);
+ }
+ if (smime_search.handles) {
+ PORT_Free(smime_search.handles);
+ }
+
+ return crv;
+}
+
+
+static CK_RV
+sftk_searchTokenList(SFTKSlot *slot, SFTKSearchResults *search,
+ CK_ATTRIBUTE *pTemplate, CK_LONG ulCount,
+ PRBool *tokenOnly, PRBool isLoggedIn)
+{
+ CK_RV crv;
+ CK_RV crv2;
+ SFTKDBHandle *certHandle = sftk_getCertDB(slot);
+
+ crv = sftk_searchDatabase(certHandle, search, pTemplate, ulCount);
+ crv2 = sftk_emailhack(slot, certHandle, search, pTemplate, ulCount);
+ if (crv == CKR_OK) crv2 = crv;
+ sftk_freeDB(certHandle);
+
+ if (crv == CKR_OK && isLoggedIn) {
+ SFTKDBHandle *keyHandle = sftk_getKeyDB(slot);
+ crv = sftk_searchDatabase(keyHandle, search, pTemplate, ulCount);
+ sftk_freeDB(keyHandle);
+ }
+ return crv;
+}
+
+/* NSC_FindObjectsInit initializes a search for token and session objects
+ * that match a template. */
+CK_RV NSC_FindObjectsInit(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount)
+{
+ SFTKSearchResults *search = NULL, *freeSearch = NULL;
+ SFTKSession *session = NULL;
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ PRBool tokenOnly = PR_FALSE;
+ CK_RV crv = CKR_OK;
+ PRBool isLoggedIn;
+
+ CHECK_FORK();
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ crv = CKR_SESSION_HANDLE_INVALID;
+ goto loser;
+ }
+
+ search = (SFTKSearchResults *)PORT_Alloc(sizeof(SFTKSearchResults));
+ if (search == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ search->handles = (CK_OBJECT_HANDLE *)
+ PORT_Alloc(sizeof(CK_OBJECT_HANDLE) * NSC_SEARCH_BLOCK_SIZE);
+ if (search->handles == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ search->index = 0;
+ search->size = 0;
+ search->array_size = NSC_SEARCH_BLOCK_SIZE;
+ isLoggedIn = (PRBool)((!slot->needLogin) || slot->isLoggedIn);
+
+ crv = sftk_searchTokenList(slot, search, pTemplate, ulCount, &tokenOnly,
+ isLoggedIn);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* build list of found objects in the session */
+ if (!tokenOnly) {
+ crv = sftk_searchObjectList(search, slot->sessObjHashTable,
+ slot->sessObjHashSize, slot->objectLock,
+ pTemplate, ulCount, isLoggedIn);
+ }
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ if ((freeSearch = session->search) != NULL) {
+ session->search = NULL;
+ sftk_FreeSearch(freeSearch);
+ }
+ session->search = search;
+ sftk_FreeSession(session);
+ return CKR_OK;
+
+loser:
+ if (search) {
+ sftk_FreeSearch(search);
+ }
+ if (session) {
+ sftk_FreeSession(session);
+ }
+ return crv;
+}
+
+
+/* NSC_FindObjects continues a search for token and session objects
+ * that match a template, obtaining additional object handles. */
+CK_RV NSC_FindObjects(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount)
+{
+ SFTKSession *session;
+ SFTKSearchResults *search;
+ int transfer;
+ int left;
+
+ CHECK_FORK();
+
+ *pulObjectCount = 0;
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ if (session->search == NULL) {
+ sftk_FreeSession(session);
+ return CKR_OK;
+ }
+ search = session->search;
+ left = session->search->size - session->search->index;
+ transfer = ((int)ulMaxObjectCount > left) ? left : ulMaxObjectCount;
+ if (transfer > 0) {
+ PORT_Memcpy(phObject,&search->handles[search->index],
+ transfer*sizeof(CK_OBJECT_HANDLE));
+ } else {
+ *phObject = CK_INVALID_HANDLE;
+ }
+
+ search->index += transfer;
+ if (search->index == search->size) {
+ session->search = NULL;
+ sftk_FreeSearch(search);
+ }
+ *pulObjectCount = transfer;
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+/* NSC_FindObjectsFinal finishes a search for token and session objects. */
+CK_RV NSC_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+{
+ SFTKSession *session;
+ SFTKSearchResults *search;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ search = session->search;
+ session->search = NULL;
+ sftk_FreeSession(session);
+ if (search != NULL) {
+ sftk_FreeSearch(search);
+ }
+ return CKR_OK;
+}
+
+
+
+CK_RV NSC_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot,
+ CK_VOID_PTR pReserved)
+{
+ CHECK_FORK();
+
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
diff --git a/security/nss/lib/softoken/pkcs11.h b/security/nss/lib/softoken/pkcs11.h
new file mode 100644
index 000000000..9352fc1c6
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11.h
@@ -0,0 +1,323 @@
+/* ***** 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):
+ * RSA Labs
+ *
+ * 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 ***** */
+/*
+ * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document
+ * is granted provided that it is identified as "RSA Security In.c Public-Key
+ * Cryptography Standards (PKCS)" in all material mentioning or referencing
+ * this document.
+ *
+ * The latest version of this header can be found at:
+ * http://www.rsalabs.com/pkcs/pkcs-11/index.html
+ */
+#ifndef _PKCS11_H_
+#define _PKCS11_H_ 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Before including this file (pkcs11.h) (or pkcs11t.h by
+ * itself), 6 platform-specific macros must be defined. These
+ * macros are described below, and typical definitions for them
+ * are also given. Be advised that these definitions can depend
+ * on both the platform and the compiler used (and possibly also
+ * on whether a PKCS #11 library is linked statically or
+ * dynamically).
+ *
+ * In addition to defining these 6 macros, the packing convention
+ * for PKCS #11 structures should be set. The PKCS #11
+ * convention on packing is that structures should be 1-byte
+ * aligned.
+ *
+ * In a Win32 environment, this might be done by using the
+ * following preprocessor directive before including pkcs11.h
+ * or pkcs11t.h:
+ *
+ * #pragma pack(push, cryptoki, 1)
+ *
+ * and using the following preprocessor directive after including
+ * pkcs11.h or pkcs11t.h:
+ *
+ * #pragma pack(pop, cryptoki)
+ *
+ * In a Win16 environment, this might be done by using the
+ * following preprocessor directive before including pkcs11.h
+ * or pkcs11t.h:
+ *
+ * #pragma pack(1)
+ *
+ * In a UNIX environment, you're on your own here. You might
+ * not need to do anything.
+ *
+ *
+ * Now for the macros:
+ *
+ *
+ * 1. CK_PTR: The indirection string for making a pointer to an
+ * object. It can be used like this:
+ *
+ * typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+ *
+ * In a Win32 environment, it might be defined by
+ *
+ * #define CK_PTR *
+ *
+ * In a Win16 environment, it might be defined by
+ *
+ * #define CK_PTR far *
+ *
+ * In a UNIX environment, it might be defined by
+ *
+ * #define CK_PTR *
+ *
+ *
+ * 2. CK_DEFINE_FUNCTION(returnType, name): A macro which makes
+ * an exportable PKCS #11 library function definition out of a
+ * return type and a function name. It should be used in the
+ * following fashion to define the exposed PKCS #11 functions in
+ * a PKCS #11 library:
+ *
+ * CK_DEFINE_FUNCTION(CK_RV, C_Initialize)(
+ * CK_VOID_PTR pReserved
+ * )
+ * {
+ * ...
+ * }
+ *
+ * For defining a function in a Win32 PKCS #11 .dll, it might be
+ * defined by
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ * returnType __declspec(dllexport) name
+ *
+ * For defining a function in a Win16 PKCS #11 .dll, it might be
+ * defined by
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ * returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by
+ *
+ * #define CK_DEFINE_FUNCTION(returnType, name) \
+ * returnType name
+ *
+ *
+ * 3. CK_DECLARE_FUNCTION(returnType, name): A macro which makes
+ * an importable PKCS #11 library function declaration out of a
+ * return type and a function name. It should be used in the
+ * following fashion:
+ *
+ * extern CK_DECLARE_FUNCTION(CK_RV, C_Initialize)(
+ * CK_VOID_PTR pReserved
+ * );
+ *
+ * For declaring a function in a Win32 PKCS #11 .dll, it might
+ * be defined by
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __declspec(dllimport) name
+ *
+ * For declaring a function in a Win16 PKCS #11 .dll, it might
+ * be defined by
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType __export _far _pascal name
+ *
+ * In a UNIX environment, it might be defined by
+ *
+ * #define CK_DECLARE_FUNCTION(returnType, name) \
+ * returnType name
+ *
+ *
+ * 4. CK_DECLARE_FUNCTION_POINTER(returnType, name): A macro
+ * which makes a PKCS #11 API function pointer declaration or
+ * function pointer type declaration out of a return type and a
+ * function name. It should be used in the following fashion:
+ *
+ * // Define funcPtr to be a pointer to a PKCS #11 API function
+ * // taking arguments args and returning CK_RV.
+ * CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtr)(args);
+ *
+ * or
+ *
+ * // Define funcPtrType to be the type of a pointer to a
+ * // PKCS #11 API function taking arguments args and returning
+ * // CK_RV, and then define funcPtr to be a variable of type
+ * // funcPtrType.
+ * typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, funcPtrType)(args);
+ * funcPtrType funcPtr;
+ *
+ * For accessing functions in a Win32 PKCS #11 .dll, in might be
+ * defined by
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __declspec(dllimport) (* name)
+ *
+ * For accessing functions in a Win16 PKCS #11 .dll, it might be
+ * defined by
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType __export _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by
+ *
+ * #define CK_DECLARE_FUNCTION_POINTER(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 5. CK_CALLBACK_FUNCTION(returnType, name): A macro which makes
+ * a function pointer type for an application callback out of
+ * a return type for the callback and a name for the callback.
+ * It should be used in the following fashion:
+ *
+ * CK_CALLBACK_FUNCTION(CK_RV, myCallback)(args);
+ *
+ * to declare a function pointer, myCallback, to a callback
+ * which takes arguments args and returns a CK_RV. It can also
+ * be used like this:
+ *
+ * typedef CK_CALLBACK_FUNCTION(CK_RV, myCallbackType)(args);
+ * myCallbackType myCallback;
+ *
+ * In a Win32 environment, it might be defined by
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ * In a Win16 environment, it might be defined by
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType _far _pascal (* name)
+ *
+ * In a UNIX environment, it might be defined by
+ *
+ * #define CK_CALLBACK_FUNCTION(returnType, name) \
+ * returnType (* name)
+ *
+ *
+ * 6. NULL_PTR: This macro is the value of a NULL pointer.
+ *
+ * In any ANSI/ISO C environment (and in many others as well),
+ * this should be defined by
+ *
+ * #ifndef NULL_PTR
+ * #define NULL_PTR 0
+ * #endif
+ */
+
+
+/* All the various PKCS #11 types and #define'd values are in the
+ * file pkcs11t.h. */
+#include "pkcs11t.h"
+
+#define __PASTE(x,y) x##y
+
+
+/* packing defines */
+#include "pkcs11p.h"
+/* ==============================================================
+ * Define the "extern" form of all the entry points.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ CK_DECLARE_FUNCTION(CK_RV, name)
+
+/* pkcs11f.h has all the information about the PKCS #11
+ * function prototypes. */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define the typedef form of all the entry points. That is, for
+ * each PKCS #11 function C_XXX, define a type CK_C_XXX which is
+ * a pointer to that kind of function.
+ * ==============================================================
+ */
+
+#define CK_NEED_ARG_LIST 1
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ typedef CK_DECLARE_FUNCTION_POINTER(CK_RV, __PASTE(CK_,name))
+
+/* pkcs11f.h has all the information about the PKCS #11
+ * function prototypes. */
+#include "pkcs11f.h"
+
+#undef CK_NEED_ARG_LIST
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+/* ==============================================================
+ * Define structed vector of entry points. A CK_FUNCTION_LIST
+ * contains a CK_VERSION indicating a library's PKCS #11 version
+ * and then a whole slew of function pointers to the routines in
+ * the library. This type was declared, but not defined, in
+ * pkcs11t.h.
+ * ==============================================================
+ */
+
+#define CK_PKCS11_FUNCTION_INFO(name) \
+ __PASTE(CK_,name) name;
+
+struct CK_FUNCTION_LIST {
+
+ CK_VERSION version; /* PKCS #11 version */
+
+/* Pile all the function pointers into the CK_FUNCTION_LIST. */
+/* pkcs11f.h has all the information about the PKCS #11
+ * function prototypes. */
+#include "pkcs11f.h"
+
+};
+
+#undef CK_PKCS11_FUNCTION_INFO
+
+
+#undef __PASTE
+
+/* unpack */
+#include "pkcs11u.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c
new file mode 100644
index 000000000..76d648f95
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11c.c
@@ -0,0 +1,6124 @@
+/* ***** 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):
+ * Dr Stephen Henson <stephen.henson@gemplus.com>
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * This file implements PKCS 11 on top of our existing security modules
+ *
+ * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
+ * This implementation has two slots:
+ * slot 1 is our generic crypto support. It does not require login.
+ * It supports Public Key ops, and all they bulk ciphers and hashes.
+ * It can also support Private Key ops for imported Private keys. It does
+ * not have any token storage.
+ * slot 2 is our private key support. It requires a login before use. It
+ * can store Private Keys and Certs as token objects. Currently only private
+ * keys and their associated Certificates are saved on the token.
+ *
+ * In this implementation, session objects are only visible to the session
+ * that created or generated them.
+ */
+#include "seccomon.h"
+#include "secitem.h"
+#include "secport.h"
+#include "blapi.h"
+#include "pkcs11.h"
+#include "pkcs11i.h"
+#include "lowkeyi.h"
+#include "sechash.h"
+#include "secder.h"
+#include "secdig.h"
+#include "lowpbe.h" /* We do PBE below */
+#include "pkcs11t.h"
+#include "secoid.h"
+#include "alghmac.h"
+#include "softoken.h"
+#include "secasn1.h"
+#include "secerr.h"
+
+#include "prprf.h"
+
+#define __PASTE(x,y) x##y
+
+/*
+ * we renamed all our internal functions, get the correct
+ * definitions for them...
+ */
+#undef CK_PKCS11_FUNCTION_INFO
+#undef CK_NEED_ARG_LIST
+
+#define CK_EXTERN extern
+#define CK_PKCS11_FUNCTION_INFO(func) \
+ CK_RV __PASTE(NS,func)
+#define CK_NEED_ARG_LIST 1
+
+#include "pkcs11f.h"
+
+typedef struct {
+ uint8 client_version[2];
+ uint8 random[46];
+} SSL3RSAPreMasterSecret;
+
+static void sftk_Null(void *data, PRBool freeit)
+{
+ return;
+}
+
+#ifdef NSS_ENABLE_ECC
+#ifdef EC_DEBUG
+#define SEC_PRINT(str1, str2, num, sitem) \
+ printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \
+ str1, str2, num, sitem->len); \
+ for (i = 0; i < sitem->len; i++) { \
+ printf("%02x:", sitem->data[i]); \
+ } \
+ printf("\n")
+#else
+#define SEC_PRINT(a, b, c, d)
+#endif
+#endif /* NSS_ENABLE_ECC */
+
+/*
+ * free routines.... Free local type allocated data, and convert
+ * other free routines to the destroy signature.
+ */
+static void
+sftk_FreePrivKey(NSSLOWKEYPrivateKey *key, PRBool freeit)
+{
+ nsslowkey_DestroyPrivateKey(key);
+}
+
+static void
+sftk_Space(void *data, PRBool freeit)
+{
+ PORT_Free(data);
+}
+
+
+/*
+ * turn a CDMF key into a des key. CDMF is an old IBM scheme to export DES by
+ * Deprecating a full des key to 40 bit key strenth.
+ */
+static CK_RV
+sftk_cdmf2des(unsigned char *cdmfkey, unsigned char *deskey)
+{
+ unsigned char key1[8] = { 0xc4, 0x08, 0xb0, 0x54, 0x0b, 0xa1, 0xe0, 0xae };
+ unsigned char key2[8] = { 0xef, 0x2c, 0x04, 0x1c, 0xe6, 0x38, 0x2f, 0xe6 };
+ unsigned char enc_src[8];
+ unsigned char enc_dest[8];
+ unsigned int leng,i;
+ DESContext *descx;
+ SECStatus rv;
+
+
+ /* zero the parity bits */
+ for (i=0; i < 8; i++) {
+ enc_src[i] = cdmfkey[i] & 0xfe;
+ }
+
+ /* encrypt with key 1 */
+ descx = DES_CreateContext(key1, NULL, NSS_DES, PR_TRUE);
+ if (descx == NULL) return CKR_HOST_MEMORY;
+ rv = DES_Encrypt(descx, enc_dest, &leng, 8, enc_src, 8);
+ DES_DestroyContext(descx,PR_TRUE);
+ if (rv != SECSuccess) return CKR_DEVICE_ERROR;
+
+ /* xor source with des, zero the parity bits and deprecate the key*/
+ for (i=0; i < 8; i++) {
+ if (i & 1) {
+ enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0xfe;
+ } else {
+ enc_src[i] = (enc_src[i] ^ enc_dest[i]) & 0x0e;
+ }
+ }
+
+ /* encrypt with key 2 */
+ descx = DES_CreateContext(key2, NULL, NSS_DES, PR_TRUE);
+ if (descx == NULL) return CKR_HOST_MEMORY;
+ rv = DES_Encrypt(descx, deskey, &leng, 8, enc_src, 8);
+ DES_DestroyContext(descx,PR_TRUE);
+ if (rv != SECSuccess) return CKR_DEVICE_ERROR;
+
+ /* set the corret parity on our new des key */
+ sftk_FormatDESKey(deskey, 8);
+ return CKR_OK;
+}
+
+
+/* NSC_DestroyObject destroys an object. */
+CK_RV
+NSC_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ SFTKSession *session;
+ SFTKObject *object;
+ SFTKFreeStatus status;
+
+ CHECK_FORK();
+
+ if (slot == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * This whole block just makes sure we really can destroy the
+ * requested object.
+ */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ object = sftk_ObjectFromHandle(hObject,session);
+ if (object == NULL) {
+ sftk_FreeSession(session);
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ /* don't destroy a private object if we aren't logged in */
+ if ((!slot->isLoggedIn) && (slot->needLogin) &&
+ (sftk_isTrue(object,CKA_PRIVATE))) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(object);
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+
+ /* don't destroy a token object if we aren't in a rw session */
+
+ if (((session->info.flags & CKF_RW_SESSION) == 0) &&
+ (sftk_isTrue(object,CKA_TOKEN))) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(object);
+ return CKR_SESSION_READ_ONLY;
+ }
+
+ sftk_DeleteObject(session,object);
+
+ sftk_FreeSession(session);
+
+ /*
+ * get some indication if the object is destroyed. Note: this is not
+ * 100%. Someone may have an object reference outstanding (though that
+ * should not be the case by here. Also note that the object is "half"
+ * destroyed. Our internal representation is destroyed, but it may still
+ * be in the data base.
+ */
+ status = sftk_FreeObject(object);
+
+ return (status != SFTK_DestroyFailure) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+
+/*
+ ************** Crypto Functions: Utilities ************************
+ */
+
+
+/*
+ * map SEC_ERROR_xxx to CKR_xxx.
+ */
+static CK_RV
+sftk_MapCryptError(int error)
+{
+ switch (error) {
+ case SEC_ERROR_INVALID_ARGS:
+ return CKR_ARGUMENTS_BAD;
+ case SEC_ERROR_INPUT_LEN:
+ return CKR_DATA_LEN_RANGE;
+ case SEC_ERROR_OUTPUT_LEN:
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ return CKR_DEVICE_ERROR;
+}
+
+/*
+ * return a context based on the SFTKContext type.
+ */
+SFTKSessionContext *
+sftk_ReturnContextByType(SFTKSession *session, SFTKContextType type)
+{
+ switch (type) {
+ case SFTK_ENCRYPT:
+ case SFTK_DECRYPT:
+ return session->enc_context;
+ case SFTK_HASH:
+ return session->hash_context;
+ case SFTK_SIGN:
+ case SFTK_SIGN_RECOVER:
+ case SFTK_VERIFY:
+ case SFTK_VERIFY_RECOVER:
+ return session->hash_context;
+ }
+ return NULL;
+}
+
+/*
+ * change a context based on the SFTKContext type.
+ */
+void
+sftk_SetContextByType(SFTKSession *session, SFTKContextType type,
+ SFTKSessionContext *context)
+{
+ switch (type) {
+ case SFTK_ENCRYPT:
+ case SFTK_DECRYPT:
+ session->enc_context = context;
+ break;
+ case SFTK_HASH:
+ session->hash_context = context;
+ break;
+ case SFTK_SIGN:
+ case SFTK_SIGN_RECOVER:
+ case SFTK_VERIFY:
+ case SFTK_VERIFY_RECOVER:
+ session->hash_context = context;
+ break;
+ }
+ return;
+}
+
+/*
+ * code to grab the context. Needed by every C_XXXUpdate, C_XXXFinal,
+ * and C_XXX function. The function takes a session handle, the context type,
+ * and wether or not the session needs to be multipart. It returns the context,
+ * and optionally returns the session pointer (if sessionPtr != NULL) if session
+ * pointer is returned, the caller is responsible for freeing it.
+ */
+static CK_RV
+sftk_GetContext(CK_SESSION_HANDLE handle,SFTKSessionContext **contextPtr,
+ SFTKContextType type, PRBool needMulti, SFTKSession **sessionPtr)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+
+ session = sftk_SessionFromHandle(handle);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ context = sftk_ReturnContextByType(session,type);
+ /* make sure the context is valid */
+ if((context==NULL)||(context->type!=type)||(needMulti&&!(context->multi))){
+ sftk_FreeSession(session);
+ return CKR_OPERATION_NOT_INITIALIZED;
+ }
+ *contextPtr = context;
+ if (sessionPtr != NULL) {
+ *sessionPtr = session;
+ } else {
+ sftk_FreeSession(session);
+ }
+ return CKR_OK;
+}
+
+/*
+ ************** Crypto Functions: Encrypt ************************
+ */
+
+/*
+ * All the NSC_InitXXX functions have a set of common checks and processing they
+ * all need to do at the beginning. This is done here.
+ */
+static CK_RV
+sftk_InitGeneric(SFTKSession *session,SFTKSessionContext **contextPtr,
+ SFTKContextType ctype,SFTKObject **keyPtr,
+ CK_OBJECT_HANDLE hKey, CK_KEY_TYPE *keyTypePtr,
+ CK_OBJECT_CLASS pubKeyType, CK_ATTRIBUTE_TYPE operation)
+{
+ SFTKObject *key = NULL;
+ SFTKAttribute *att;
+ SFTKSessionContext *context;
+
+ /* We can only init if there is not current context active */
+ if (sftk_ReturnContextByType(session,ctype) != NULL) {
+ return CKR_OPERATION_ACTIVE;
+ }
+
+ /* find the key */
+ if (keyPtr) {
+ key = sftk_ObjectFromHandle(hKey,session);
+ if (key == NULL) {
+ return CKR_KEY_HANDLE_INVALID;
+ }
+
+ /* make sure it's a valid key for this operation */
+ if (((key->objclass != CKO_SECRET_KEY) && (key->objclass != pubKeyType))
+ || !sftk_isTrue(key,operation)) {
+ sftk_FreeObject(key);
+ return CKR_KEY_TYPE_INCONSISTENT;
+ }
+ /* get the key type */
+ att = sftk_FindAttribute(key,CKA_KEY_TYPE);
+ if (att == NULL) {
+ sftk_FreeObject(key);
+ return CKR_KEY_TYPE_INCONSISTENT;
+ }
+ PORT_Assert(att->attrib.ulValueLen == sizeof(CK_KEY_TYPE));
+ if (att->attrib.ulValueLen != sizeof(CK_KEY_TYPE)) {
+ sftk_FreeAttribute(att);
+ sftk_FreeObject(key);
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ PORT_Memcpy(keyTypePtr, att->attrib.pValue, sizeof(CK_KEY_TYPE));
+ sftk_FreeAttribute(att);
+ *keyPtr = key;
+ }
+
+ /* allocate the context structure */
+ context = (SFTKSessionContext *)PORT_Alloc(sizeof(SFTKSessionContext));
+ if (context == NULL) {
+ if (key) sftk_FreeObject(key);
+ return CKR_HOST_MEMORY;
+ }
+ context->type = ctype;
+ context->multi = PR_TRUE;
+ context->cipherInfo = NULL;
+ context->hashInfo = NULL;
+ context->doPad = PR_FALSE;
+ context->padDataLength = 0;
+ context->key = key;
+ context->blockSize = 0;
+
+ *contextPtr = context;
+ return CKR_OK;
+}
+
+/* NSC_CryptInit initializes an encryption/Decryption operation. */
+/* This function is used by NSC_EncryptInit, NSC_DecryptInit,
+ * NSC_WrapKey, NSC_UnwrapKey,
+ * NSC_SignInit, NSC_VerifyInit (via sftk_InitCBCMac),
+ * The only difference in their uses is the value of etype.
+ */
+static CK_RV
+sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE etype,
+ SFTKContextType contextType, PRBool isEncrypt)
+{
+ SFTKSession *session;
+ SFTKObject *key;
+ SFTKSessionContext *context;
+ SFTKAttribute *att;
+ CK_RC2_CBC_PARAMS *rc2_param;
+#if NSS_SOFTOKEN_DOES_RC5
+ CK_RC5_CBC_PARAMS *rc5_param;
+ SECItem rc5Key;
+#endif
+ CK_KEY_TYPE key_type;
+ CK_RV crv = CKR_OK;
+ unsigned effectiveKeyLength;
+ unsigned char newdeskey[24];
+ PRBool useNewKey=PR_FALSE;
+ int t;
+
+ crv = sftk_MechAllowsOperation(pMechanism->mechanism, etype);
+ if (crv != CKR_OK)
+ return crv;
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+ crv = sftk_InitGeneric(session,&context,contextType,&key,hKey,&key_type,
+ isEncrypt ?CKO_PUBLIC_KEY:CKO_PRIVATE_KEY, etype);
+
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ return crv;
+ }
+
+ context->doPad = PR_FALSE;
+ switch(pMechanism->mechanism) {
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ if (key_type != CKK_RSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ context->multi = PR_FALSE;
+ if (isEncrypt) {
+ NSSLOWKEYPublicKey *pubKey = sftk_GetPubKey(key,CKK_RSA,&crv);
+ if (pubKey == NULL) {
+ break;
+ }
+ context->maxLen = nsslowkey_PublicModulusLen(pubKey);
+ context->cipherInfo = (void *)pubKey;
+ context->update = (SFTKCipher)
+ (pMechanism->mechanism == CKM_RSA_X_509
+ ? RSA_EncryptRaw : RSA_EncryptBlock);
+ } else {
+ NSSLOWKEYPrivateKey *privKey = sftk_GetPrivKey(key,CKK_RSA,&crv);
+ if (privKey == NULL) {
+ break;
+ }
+ context->maxLen = nsslowkey_PrivateModulusLen(privKey);
+ context->cipherInfo = (void *)privKey;
+ context->update = (SFTKCipher)
+ (pMechanism->mechanism == CKM_RSA_X_509
+ ? RSA_DecryptRaw : RSA_DecryptBlock);
+ }
+ context->destroy = sftk_Null;
+ break;
+ case CKM_RC2_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_RC2_ECB:
+ case CKM_RC2_CBC:
+ context->blockSize = 8;
+ if (key_type != CKK_RC2) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter;
+ effectiveKeyLength = (rc2_param->ulEffectiveBits+7)/8;
+ context->cipherInfo =
+ RC2_CreateContext((unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen, rc2_param->iv,
+ pMechanism->mechanism == CKM_RC2_ECB ? NSS_RC2 :
+ NSS_RC2_CBC,effectiveKeyLength);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ? RC2_Encrypt : RC2_Decrypt);
+ context->destroy = (SFTKDestroy) RC2_DestroyContext;
+ break;
+#if NSS_SOFTOKEN_DOES_RC5
+ case CKM_RC5_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_RC5_ECB:
+ case CKM_RC5_CBC:
+ if (key_type != CKK_RC5) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter;
+ context->blockSize = rc5_param->ulWordsize*2;
+ rc5Key.data = (unsigned char*)att->attrib.pValue;
+ rc5Key.len = att->attrib.ulValueLen;
+ context->cipherInfo = RC5_CreateContext(&rc5Key,rc5_param->ulRounds,
+ rc5_param->ulWordsize,rc5_param->pIv,
+ pMechanism->mechanism == CKM_RC5_ECB ? NSS_RC5 : NSS_RC5_CBC);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ? RC5_Encrypt : RC5_Decrypt);
+ context->destroy = (SFTKDestroy) RC5_DestroyContext;
+ break;
+#endif
+ case CKM_RC4:
+ if (key_type != CKK_RC4) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ context->cipherInfo =
+ RC4_CreateContext((unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY; /* WRONG !!! */
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ? RC4_Encrypt : RC4_Decrypt);
+ context->destroy = (SFTKDestroy) RC4_DestroyContext;
+ break;
+ case CKM_CDMF_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_CDMF_ECB:
+ case CKM_CDMF_CBC:
+ if (key_type != CKK_CDMF) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ t = (pMechanism->mechanism == CKM_CDMF_ECB) ? NSS_DES : NSS_DES_CBC;
+ if (crv != CKR_OK) break;
+ goto finish_des;
+ case CKM_DES_ECB:
+ if (key_type != CKK_DES) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ t = NSS_DES;
+ goto finish_des;
+ case CKM_DES_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_DES_CBC:
+ if (key_type != CKK_DES) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ t = NSS_DES_CBC;
+ goto finish_des;
+ case CKM_DES3_ECB:
+ if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ t = NSS_DES_EDE3;
+ goto finish_des;
+ case CKM_DES3_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_DES3_CBC:
+ if ((key_type != CKK_DES2) && (key_type != CKK_DES3)) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ t = NSS_DES_EDE3_CBC;
+finish_des:
+ context->blockSize = 8;
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ if (key_type == CKK_DES2 &&
+ (t == NSS_DES_EDE3_CBC || t == NSS_DES_EDE3)) {
+ /* extend DES2 key to DES3 key. */
+ memcpy(newdeskey, att->attrib.pValue, 16);
+ memcpy(newdeskey + 16, newdeskey, 8);
+ useNewKey=PR_TRUE;
+ } else if (key_type == CKK_CDMF) {
+ crv = sftk_cdmf2des((unsigned char*)att->attrib.pValue,newdeskey);
+ if (crv != CKR_OK) {
+ sftk_FreeAttribute(att);
+ break;
+ }
+ useNewKey=PR_TRUE;
+ }
+ context->cipherInfo = DES_CreateContext(
+ useNewKey ? newdeskey : (unsigned char*)att->attrib.pValue,
+ (unsigned char*)pMechanism->pParameter,t, isEncrypt);
+ if (useNewKey)
+ memset(newdeskey, 0, sizeof newdeskey);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ? DES_Encrypt : DES_Decrypt);
+ context->destroy = (SFTKDestroy) DES_DestroyContext;
+ break;
+
+ case CKM_CAMELLIA_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_CAMELLIA_CBC:
+ if (!pMechanism->pParameter ||
+ pMechanism->ulParameterLen != 16) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+ /* fall thru */
+ case CKM_CAMELLIA_ECB:
+ context->blockSize = 16;
+ if (key_type != CKK_CAMELLIA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ context->cipherInfo = Camellia_CreateContext(
+ (unsigned char*)att->attrib.pValue,
+ (unsigned char*)pMechanism->pParameter,
+ pMechanism->mechanism ==
+ CKM_CAMELLIA_ECB ? NSS_CAMELLIA : NSS_CAMELLIA_CBC,
+ isEncrypt, att->attrib.ulValueLen);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ?
+ Camellia_Encrypt : Camellia_Decrypt);
+ context->destroy = (SFTKDestroy) Camellia_DestroyContext;
+ break;
+
+ case CKM_AES_CBC_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_AES_ECB:
+ case CKM_AES_CBC:
+ context->blockSize = 16;
+ if (key_type != CKK_AES) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ context->cipherInfo = AES_CreateContext(
+ (unsigned char*)att->attrib.pValue,
+ (unsigned char*)pMechanism->pParameter,
+ pMechanism->mechanism == CKM_AES_ECB ? NSS_AES : NSS_AES_CBC,
+ isEncrypt, att->attrib.ulValueLen, 16);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ? AES_Encrypt : AES_Decrypt);
+ context->destroy = (SFTKDestroy) AES_DestroyContext;
+ break;
+
+ case CKM_NETSCAPE_AES_KEY_WRAP_PAD:
+ context->doPad = PR_TRUE;
+ /* fall thru */
+ case CKM_NETSCAPE_AES_KEY_WRAP:
+ context->multi = PR_FALSE;
+ context->blockSize = 8;
+ if (key_type != CKK_AES) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ if (att == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ context->cipherInfo = AESKeyWrap_CreateContext(
+ (unsigned char*)att->attrib.pValue,
+ (unsigned char*)pMechanism->pParameter,
+ isEncrypt, att->attrib.ulValueLen);
+ sftk_FreeAttribute(att);
+ if (context->cipherInfo == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->update = (SFTKCipher) (isEncrypt ? AESKeyWrap_Encrypt
+ : AESKeyWrap_Decrypt);
+ context->destroy = (SFTKDestroy) AESKeyWrap_DestroyContext;
+ break;
+
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ sftk_FreeContext(context);
+ sftk_FreeSession(session);
+ return crv;
+ }
+ sftk_SetContextByType(session, contextType, context);
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+/* NSC_EncryptInit initializes an encryption operation. */
+CK_RV NSC_EncryptInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+ CHECK_FORK();
+ return sftk_CryptInit(hSession, pMechanism, hKey, CKA_ENCRYPT,
+ SFTK_ENCRYPT, PR_TRUE);
+}
+
+/* NSC_EncryptUpdate continues a multiple-part encryption operation. */
+CK_RV NSC_EncryptUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ SFTKSessionContext *context;
+ unsigned int outlen,i;
+ unsigned int padoutlen = 0;
+ unsigned int maxout = *pulEncryptedPartLen;
+ CK_RV crv;
+ SECStatus rv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_TRUE,NULL);
+ if (crv != CKR_OK) return crv;
+
+ if (!pEncryptedPart) {
+ if (context->doPad) {
+ CK_ULONG totalDataAvailable = ulPartLen + context->padDataLength;
+ CK_ULONG blocksToSend = totalDataAvailable/context->blockSize;
+
+ *pulEncryptedPartLen = blocksToSend * context->blockSize;
+ return CKR_OK;
+ }
+ *pulEncryptedPartLen = ulPartLen;
+ return CKR_OK;
+ }
+
+ /* do padding */
+ if (context->doPad) {
+ /* deal with previous buffered data */
+ if (context->padDataLength != 0) {
+ /* fill in the padded to a full block size */
+ for (i=context->padDataLength;
+ (ulPartLen != 0) && i < context->blockSize; i++) {
+ context->padBuf[i] = *pPart++;
+ ulPartLen--;
+ context->padDataLength++;
+ }
+
+ /* not enough data to encrypt yet? then return */
+ if (context->padDataLength != context->blockSize) {
+ *pulEncryptedPartLen = 0;
+ return CKR_OK;
+ }
+ /* encrypt the current padded data */
+ rv = (*context->update)(context->cipherInfo, pEncryptedPart,
+ &padoutlen, context->blockSize, context->padBuf,
+ context->blockSize);
+ if (rv != SECSuccess) {
+ return sftk_MapCryptError(PORT_GetError());
+ }
+ pEncryptedPart += padoutlen;
+ maxout -= padoutlen;
+ }
+ /* save the residual */
+ context->padDataLength = ulPartLen % context->blockSize;
+ if (context->padDataLength) {
+ PORT_Memcpy(context->padBuf,
+ &pPart[ulPartLen-context->padDataLength],
+ context->padDataLength);
+ ulPartLen -= context->padDataLength;
+ }
+ /* if we've exhausted our new buffer, we're done */
+ if (ulPartLen == 0) {
+ *pulEncryptedPartLen = padoutlen;
+ return CKR_OK;
+ }
+ }
+
+
+ /* do it: NOTE: this assumes buf size in is >= buf size out! */
+ rv = (*context->update)(context->cipherInfo,pEncryptedPart,
+ &outlen, maxout, pPart, ulPartLen);
+ *pulEncryptedPartLen = (CK_ULONG) (outlen + padoutlen);
+ if (rv != SECSuccess) {
+ return sftk_MapCryptError(PORT_GetError());
+ }
+ return CKR_OK;
+}
+
+
+/* NSC_EncryptFinal finishes a multiple-part encryption operation. */
+CK_RV NSC_EncryptFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen,i;
+ unsigned int maxout = *pulLastEncryptedPartLen;
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+ PRBool contextFinished = PR_TRUE;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_TRUE,&session);
+ if (crv != CKR_OK) return crv;
+
+ *pulLastEncryptedPartLen = 0;
+ if (!pLastEncryptedPart) {
+ /* caller is checking the amount of remaining data */
+ if (context->blockSize > 0 && context->doPad) {
+ *pulLastEncryptedPartLen = context->blockSize;
+ contextFinished = PR_FALSE; /* still have padding to go */
+ }
+ goto finish;
+ }
+
+ /* do padding */
+ if (context->doPad) {
+ unsigned char padbyte = (unsigned char)
+ (context->blockSize - context->padDataLength);
+ /* fill out rest of pad buffer with pad magic*/
+ for (i=context->padDataLength; i < context->blockSize; i++) {
+ context->padBuf[i] = padbyte;
+ }
+ rv = (*context->update)(context->cipherInfo,pLastEncryptedPart,
+ &outlen, maxout, context->padBuf, context->blockSize);
+ if (rv == SECSuccess) *pulLastEncryptedPartLen = (CK_ULONG) outlen;
+ }
+
+finish:
+ if (contextFinished) {
+ sftk_SetContextByType(session, SFTK_ENCRYPT, NULL);
+ sftk_FreeContext(context);
+ }
+ sftk_FreeSession(session);
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+/* NSC_Encrypt encrypts single-part data. */
+CK_RV NSC_Encrypt (CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData,
+ CK_ULONG_PTR pulEncryptedDataLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int maxoutlen = *pulEncryptedDataLen;
+ CK_RV crv;
+ CK_RV crv2;
+ SECStatus rv = SECSuccess;
+ SECItem pText;
+
+ pText.type = siBuffer;
+ pText.data = pData;
+ pText.len = ulDataLen;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_FALSE,&session);
+ if (crv != CKR_OK) return crv;
+
+ if (!pEncryptedData) {
+ *pulEncryptedDataLen = context->multi ?
+ ulDataLen + 2 * context->blockSize : context->maxLen;
+ goto finish;
+ }
+
+ if (context->doPad) {
+ if (context->multi) {
+ CK_ULONG finalLen;
+ /* padding is fairly complicated, have the update and final
+ * code deal with it */
+ sftk_FreeSession(session);
+ crv = NSC_EncryptUpdate(hSession, pData, ulDataLen, pEncryptedData,
+ pulEncryptedDataLen);
+ if (crv != CKR_OK)
+ *pulEncryptedDataLen = 0;
+ maxoutlen -= *pulEncryptedDataLen;
+ pEncryptedData += *pulEncryptedDataLen;
+ finalLen = maxoutlen;
+ crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen);
+ if (crv2 == CKR_OK)
+ *pulEncryptedDataLen += finalLen;
+ return crv == CKR_OK ? crv2 : crv;
+ }
+ /* doPad without multi means that padding must be done on the first
+ ** and only update. There will be no final.
+ */
+ PORT_Assert(context->blockSize > 1);
+ if (context->blockSize > 1) {
+ CK_ULONG remainder = ulDataLen % context->blockSize;
+ CK_ULONG padding = context->blockSize - remainder;
+ pText.len += padding;
+ pText.data = PORT_ZAlloc(pText.len);
+ if (pText.data) {
+ memcpy(pText.data, pData, ulDataLen);
+ memset(pText.data + ulDataLen, padding, padding);
+ } else {
+ crv = CKR_HOST_MEMORY;
+ goto fail;
+ }
+ }
+ }
+
+ /* do it: NOTE: this assumes buf size is big enough. */
+ rv = (*context->update)(context->cipherInfo, pEncryptedData,
+ &outlen, maxoutlen, pText.data, pText.len);
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+ *pulEncryptedDataLen = (CK_ULONG) outlen;
+ if (pText.data != pData)
+ PORT_ZFree(pText.data, pText.len);
+fail:
+ sftk_SetContextByType(session, SFTK_ENCRYPT, NULL);
+ sftk_FreeContext(context);
+finish:
+ sftk_FreeSession(session);
+
+ return crv;
+}
+
+
+/*
+ ************** Crypto Functions: Decrypt ************************
+ */
+
+/* NSC_DecryptInit initializes a decryption operation. */
+CK_RV NSC_DecryptInit( CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+ CHECK_FORK();
+
+ return sftk_CryptInit(hSession, pMechanism, hKey, CKA_DECRYPT,
+ SFTK_DECRYPT, PR_FALSE);
+}
+
+/* NSC_DecryptUpdate continues a multiple-part decryption operation. */
+CK_RV NSC_DecryptUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen)
+{
+ SFTKSessionContext *context;
+ unsigned int padoutlen = 0;
+ unsigned int outlen;
+ unsigned int maxout = *pulPartLen;
+ CK_RV crv;
+ SECStatus rv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_TRUE,NULL);
+ if (crv != CKR_OK) return crv;
+
+ /* this can only happen on an NSS programming error */
+ PORT_Assert((context->padDataLength == 0)
+ || context->padDataLength == context->blockSize);
+
+
+ if (!pPart) {
+ if (context->doPad) {
+ /* we can check the data length here because if we are padding,
+ * then we must be using a block cipher. In the non-padding case
+ * the error will be returned by the underlying decryption
+ * function when do do the actual decrypt. We need to do the
+ * check here to avoid returning a negative length to the caller.
+ */
+ if ((ulEncryptedPartLen == 0) ||
+ (ulEncryptedPartLen % context->blockSize) != 0) {
+ return CKR_ENCRYPTED_DATA_LEN_RANGE;
+ }
+ *pulPartLen =
+ ulEncryptedPartLen + context->padDataLength - context->blockSize;
+ return CKR_OK;
+ }
+ /* for stream ciphers there is are no constraints on ulEncryptedPartLen.
+ * for block ciphers, it must be a multiple of blockSize. The error is
+ * detected when this function is called again do decrypt the output.
+ */
+ *pulPartLen = ulEncryptedPartLen;
+ return CKR_OK;
+ }
+
+ if (context->doPad) {
+ /* first decrypt our saved buffer */
+ if (context->padDataLength != 0) {
+ rv = (*context->update)(context->cipherInfo, pPart, &padoutlen,
+ maxout, context->padBuf, context->blockSize);
+ if (rv != SECSuccess) return CKR_DEVICE_ERROR;
+ pPart += padoutlen;
+ maxout -= padoutlen;
+ }
+ /* now save the final block for the next decrypt or the final */
+ PORT_Memcpy(context->padBuf,&pEncryptedPart[ulEncryptedPartLen -
+ context->blockSize], context->blockSize);
+ context->padDataLength = context->blockSize;
+ ulEncryptedPartLen -= context->padDataLength;
+ }
+
+ /* do it: NOTE: this assumes buf size in is >= buf size out! */
+ rv = (*context->update)(context->cipherInfo,pPart, &outlen,
+ maxout, pEncryptedPart, ulEncryptedPartLen);
+ *pulPartLen = (CK_ULONG) (outlen + padoutlen);
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+
+/* NSC_DecryptFinal finishes a multiple-part decryption operation. */
+CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int maxout = *pulLastPartLen;
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_TRUE,&session);
+ if (crv != CKR_OK) return crv;
+
+ *pulLastPartLen = 0;
+ if (!pLastPart) {
+ /* caller is checking the amount of remaining data */
+ if (context->padDataLength > 0) {
+ *pulLastPartLen = context->padDataLength;
+ }
+ rv = SECSuccess;
+ goto finish;
+ }
+
+ if (context->doPad) {
+ /* decrypt our saved buffer */
+ if (context->padDataLength != 0) {
+ /* this assumes that pLastPart is big enough to hold the *whole*
+ * buffer!!! */
+ rv = (*context->update)(context->cipherInfo, pLastPart, &outlen,
+ maxout, context->padBuf, context->blockSize);
+ if (rv == SECSuccess) {
+ unsigned int padSize =
+ (unsigned int) pLastPart[context->blockSize-1];
+ if ((padSize > context->blockSize) || (padSize == 0)) {
+ rv = SECFailure;
+ } else {
+ *pulLastPartLen = outlen - padSize;
+ }
+ }
+ }
+ }
+
+ sftk_SetContextByType(session, SFTK_DECRYPT, NULL);
+ sftk_FreeContext(context);
+finish:
+ sftk_FreeSession(session);
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+/* NSC_Decrypt decrypts encrypted data in a single part. */
+CK_RV NSC_Decrypt(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData,CK_ULONG ulEncryptedDataLen,CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int maxoutlen = *pulDataLen;
+ CK_RV crv;
+ CK_RV crv2;
+ SECStatus rv = SECSuccess;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_DECRYPT,PR_FALSE,&session);
+ if (crv != CKR_OK) return crv;
+
+ if (!pData) {
+ *pulDataLen = ulEncryptedDataLen + context->blockSize;
+ goto finish;
+ }
+
+ if (context->doPad && context->multi) {
+ CK_ULONG finalLen;
+ /* padding is fairly complicated, have the update and final
+ * code deal with it */
+ sftk_FreeSession(session);
+ crv = NSC_DecryptUpdate(hSession,pEncryptedData,ulEncryptedDataLen,
+ pData, pulDataLen);
+ if (crv != CKR_OK)
+ *pulDataLen = 0;
+ maxoutlen -= *pulDataLen;
+ pData += *pulDataLen;
+ finalLen = maxoutlen;
+ crv2 = NSC_DecryptFinal(hSession, pData, &finalLen);
+ if (crv2 == CKR_OK)
+ *pulDataLen += finalLen;
+ return crv == CKR_OK ? crv2 : crv;
+ }
+
+ rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen,
+ pEncryptedData, ulEncryptedDataLen);
+ /* XXX need to do MUCH better error mapping than this. */
+ crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+ if (rv == SECSuccess && context->doPad) {
+ CK_ULONG padding = pData[outlen - 1];
+ if (padding > context->blockSize || !padding) {
+ crv = CKR_ENCRYPTED_DATA_INVALID;
+ } else
+ outlen -= padding;
+ }
+ *pulDataLen = (CK_ULONG) outlen;
+ sftk_SetContextByType(session, SFTK_DECRYPT, NULL);
+ sftk_FreeContext(context);
+finish:
+ sftk_FreeSession(session);
+ return crv;
+}
+
+
+
+/*
+ ************** Crypto Functions: Digest (HASH) ************************
+ */
+
+/* NSC_DigestInit initializes a message-digesting operation. */
+CK_RV NSC_DigestInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ CK_RV crv = CKR_OK;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+ crv = sftk_InitGeneric(session,&context,SFTK_HASH,NULL,0,NULL, 0, 0);
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ return crv;
+ }
+
+
+#define INIT_MECH(mech,mmm) \
+ case mech: { \
+ mmm ## Context * mmm ## _ctx = mmm ## _NewContext(); \
+ context->cipherInfo = (void *)mmm ## _ctx; \
+ context->cipherInfoLen = mmm ## _FlattenSize(mmm ## _ctx); \
+ context->currentMech = mech; \
+ context->hashUpdate = (SFTKHash) mmm ## _Update; \
+ context->end = (SFTKEnd) mmm ## _End; \
+ context->destroy = (SFTKDestroy) mmm ## _DestroyContext; \
+ context->maxLen = mmm ## _LENGTH; \
+ if (mmm ## _ctx) \
+ mmm ## _Begin(mmm ## _ctx); \
+ else \
+ crv = CKR_HOST_MEMORY; \
+ break; \
+ }
+
+ switch(pMechanism->mechanism) {
+ INIT_MECH(CKM_MD2, MD2)
+ INIT_MECH(CKM_MD5, MD5)
+ INIT_MECH(CKM_SHA_1, SHA1)
+ INIT_MECH(CKM_SHA256, SHA256)
+ INIT_MECH(CKM_SHA384, SHA384)
+ INIT_MECH(CKM_SHA512, SHA512)
+
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ sftk_FreeContext(context);
+ sftk_FreeSession(session);
+ return crv;
+ }
+ sftk_SetContextByType(session, SFTK_HASH, context);
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+
+/* NSC_Digest digests data in a single part. */
+CK_RV NSC_Digest(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int digestLen;
+ unsigned int maxout = *pulDigestLen;
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_HASH,PR_FALSE,&session);
+ if (crv != CKR_OK) return crv;
+
+ if (pDigest == NULL) {
+ *pulDigestLen = context->maxLen;
+ goto finish;
+ }
+
+ /* do it: */
+ (*context->hashUpdate)(context->cipherInfo, pData, ulDataLen);
+ /* NOTE: this assumes buf size is bigenough for the algorithm */
+ (*context->end)(context->cipherInfo, pDigest, &digestLen,maxout);
+ *pulDigestLen = digestLen;
+
+ sftk_SetContextByType(session, SFTK_HASH, NULL);
+ sftk_FreeContext(context);
+finish:
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+
+/* NSC_DigestUpdate continues a multiple-part message-digesting operation. */
+CK_RV NSC_DigestUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ SFTKSessionContext *context;
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_HASH,PR_TRUE,NULL);
+ if (crv != CKR_OK) return crv;
+ /* do it: */
+ (*context->hashUpdate)(context->cipherInfo, pPart, ulPartLen);
+ return CKR_OK;
+}
+
+
+/* NSC_DigestFinal finishes a multiple-part message-digesting operation. */
+CK_RV NSC_DigestFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int maxout = *pulDigestLen;
+ unsigned int digestLen;
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session);
+ if (crv != CKR_OK) return crv;
+
+ if (pDigest != NULL) {
+ (*context->end)(context->cipherInfo, pDigest, &digestLen, maxout);
+ *pulDigestLen = digestLen;
+ sftk_SetContextByType(session, SFTK_HASH, NULL);
+ sftk_FreeContext(context);
+ } else {
+ *pulDigestLen = context->maxLen;
+ }
+
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+/*
+ * these helper functions are used by Generic Macing and Signing functions
+ * that use hashes as part of their operations.
+ */
+#define DOSUB(mmm) \
+static CK_RV \
+sftk_doSub ## mmm(SFTKSessionContext *context) { \
+ mmm ## Context * mmm ## _ctx = mmm ## _NewContext(); \
+ context->hashInfo = (void *) mmm ## _ctx; \
+ context->hashUpdate = (SFTKHash) mmm ## _Update; \
+ context->end = (SFTKEnd) mmm ## _End; \
+ context->hashdestroy = (SFTKDestroy) mmm ## _DestroyContext; \
+ if (!context->hashInfo) { \
+ return CKR_HOST_MEMORY; \
+ } \
+ mmm ## _Begin( mmm ## _ctx ); \
+ return CKR_OK; \
+}
+
+DOSUB(MD2)
+DOSUB(MD5)
+DOSUB(SHA1)
+DOSUB(SHA256)
+DOSUB(SHA384)
+DOSUB(SHA512)
+
+/*
+ * HMAC General copies only a portion of the result. This update routine likes
+ * the final HMAC output with the signature.
+ */
+static SECStatus
+sftk_HMACCopy(CK_ULONG *copyLen,unsigned char *sig,unsigned int *sigLen,
+ unsigned int maxLen,unsigned char *hash, unsigned int hashLen)
+{
+ if (maxLen < *copyLen) return SECFailure;
+ PORT_Memcpy(sig,hash,*copyLen);
+ *sigLen = *copyLen;
+ return SECSuccess;
+}
+
+/* Verify is just a compare for HMAC */
+static SECStatus
+sftk_HMACCmp(CK_ULONG *copyLen,unsigned char *sig,unsigned int sigLen,
+ unsigned char *hash, unsigned int hashLen)
+{
+ return (PORT_Memcmp(sig,hash,*copyLen) == 0) ? SECSuccess : SECFailure ;
+}
+
+/*
+ * common HMAC initalization routine
+ */
+static CK_RV
+sftk_doHMACInit(SFTKSessionContext *context,HASH_HashType hash,
+ SFTKObject *key, CK_ULONG mac_size)
+{
+ SFTKAttribute *keyval;
+ HMACContext *HMACcontext;
+ CK_ULONG *intpointer;
+ const SECHashObject *hashObj = HASH_GetRawHashObject(hash);
+ PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID);
+
+ /* required by FIPS 198 Section 4 */
+ if (isFIPS && (mac_size < 4 || mac_size < hashObj->length/2)) {
+ return CKR_BUFFER_TOO_SMALL;
+ }
+
+ keyval = sftk_FindAttribute(key,CKA_VALUE);
+ if (keyval == NULL) return CKR_KEY_SIZE_RANGE;
+
+ HMACcontext = HMAC_Create(hashObj,
+ (const unsigned char*)keyval->attrib.pValue,
+ keyval->attrib.ulValueLen, isFIPS);
+ context->hashInfo = HMACcontext;
+ context->multi = PR_TRUE;
+ sftk_FreeAttribute(keyval);
+ if (context->hashInfo == NULL) {
+ if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
+ return CKR_KEY_SIZE_RANGE;
+ }
+ return CKR_HOST_MEMORY;
+ }
+ context->hashUpdate = (SFTKHash) HMAC_Update;
+ context->end = (SFTKEnd) HMAC_Finish;
+
+ context->hashdestroy = (SFTKDestroy) HMAC_Destroy;
+ intpointer = (CK_ULONG *) PORT_Alloc(sizeof(CK_ULONG));
+ if (intpointer == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ *intpointer = mac_size;
+ context->cipherInfo = (void *) intpointer;
+ context->destroy = (SFTKDestroy) sftk_Space;
+ context->update = (SFTKCipher) sftk_HMACCopy;
+ context->verify = (SFTKVerify) sftk_HMACCmp;
+ context->maxLen = hashObj->length;
+ HMAC_Begin(HMACcontext);
+ return CKR_OK;
+}
+
+/*
+ * SSL Macing support. SSL Macs are inited, then update with the base
+ * hashing algorithm, then finalized in sign and verify
+ */
+
+/*
+ * FROM SSL:
+ * 60 bytes is 3 times the maximum length MAC size that is supported.
+ * We probably should have one copy of this table. We still need this table
+ * in ssl to 'sign' the handshake hashes.
+ */
+static unsigned char ssl_pad_1 [60] = {
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+ 0x36, 0x36, 0x36, 0x36
+};
+static unsigned char ssl_pad_2 [60] = {
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+ 0x5c, 0x5c, 0x5c, 0x5c
+};
+
+static SECStatus
+sftk_SSLMACSign(SFTKSSLMACInfo *info,unsigned char *sig,unsigned int *sigLen,
+ unsigned int maxLen,unsigned char *hash, unsigned int hashLen)
+{
+ unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH];
+ unsigned int out;
+
+ info->begin(info->hashContext);
+ info->update(info->hashContext,info->key,info->keySize);
+ info->update(info->hashContext,ssl_pad_2,info->padSize);
+ info->update(info->hashContext,hash,hashLen);
+ info->end(info->hashContext,tmpBuf,&out,SFTK_MAX_MAC_LENGTH);
+ PORT_Memcpy(sig,tmpBuf,info->macSize);
+ *sigLen = info->macSize;
+ return SECSuccess;
+}
+
+static SECStatus
+sftk_SSLMACVerify(SFTKSSLMACInfo *info,unsigned char *sig,unsigned int sigLen,
+ unsigned char *hash, unsigned int hashLen)
+{
+ unsigned char tmpBuf[SFTK_MAX_MAC_LENGTH];
+ unsigned int out;
+
+ info->begin(info->hashContext);
+ info->update(info->hashContext,info->key,info->keySize);
+ info->update(info->hashContext,ssl_pad_2,info->padSize);
+ info->update(info->hashContext,hash,hashLen);
+ info->end(info->hashContext,tmpBuf,&out,SFTK_MAX_MAC_LENGTH);
+ return (PORT_Memcmp(sig,tmpBuf,info->macSize) == 0) ?
+ SECSuccess : SECFailure;
+}
+
+/*
+ * common HMAC initalization routine
+ */
+static CK_RV
+sftk_doSSLMACInit(SFTKSessionContext *context,SECOidTag oid,
+ SFTKObject *key, CK_ULONG mac_size)
+{
+ SFTKAttribute *keyval;
+ SFTKBegin begin;
+ int padSize;
+ SFTKSSLMACInfo *sslmacinfo;
+ CK_RV crv = CKR_MECHANISM_INVALID;
+
+ if (oid == SEC_OID_SHA1) {
+ crv = sftk_doSubSHA1(context);
+ if (crv != CKR_OK) return crv;
+ begin = (SFTKBegin) SHA1_Begin;
+ padSize = 40;
+ } else {
+ crv = sftk_doSubMD5(context);
+ if (crv != CKR_OK) return crv;
+ begin = (SFTKBegin) MD5_Begin;
+ padSize = 48;
+ }
+ context->multi = PR_TRUE;
+
+ keyval = sftk_FindAttribute(key,CKA_VALUE);
+ if (keyval == NULL) return CKR_KEY_SIZE_RANGE;
+
+ context->hashUpdate(context->hashInfo,keyval->attrib.pValue,
+ keyval->attrib.ulValueLen);
+ context->hashUpdate(context->hashInfo,ssl_pad_1,padSize);
+ sslmacinfo = (SFTKSSLMACInfo *) PORT_Alloc(sizeof(SFTKSSLMACInfo));
+ if (sslmacinfo == NULL) {
+ sftk_FreeAttribute(keyval);
+ return CKR_HOST_MEMORY;
+ }
+ sslmacinfo->macSize = mac_size;
+ sslmacinfo->hashContext = context->hashInfo;
+ PORT_Memcpy(sslmacinfo->key,keyval->attrib.pValue,
+ keyval->attrib.ulValueLen);
+ sslmacinfo->keySize = keyval->attrib.ulValueLen;
+ sslmacinfo->begin = begin;
+ sslmacinfo->end = context->end;
+ sslmacinfo->update = context->hashUpdate;
+ sslmacinfo->padSize = padSize;
+ sftk_FreeAttribute(keyval);
+ context->cipherInfo = (void *) sslmacinfo;
+ context->destroy = (SFTKDestroy) sftk_Space;
+ context->update = (SFTKCipher) sftk_SSLMACSign;
+ context->verify = (SFTKVerify) sftk_SSLMACVerify;
+ context->maxLen = mac_size;
+ return CKR_OK;
+}
+
+/*
+ ************** Crypto Functions: Sign ************************
+ */
+
+/*
+ * Check if We're using CBCMacing and initialize the session context if we are.
+ */
+static CK_RV
+sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey, CK_ATTRIBUTE_TYPE keyUsage,
+ SFTKContextType contextType)
+
+{
+ CK_MECHANISM cbc_mechanism;
+ CK_ULONG mac_bytes = SFTK_INVALID_MAC_SIZE;
+ CK_RC2_CBC_PARAMS rc2_params;
+#if NSS_SOFTOKEN_DOES_RC5
+ CK_RC5_CBC_PARAMS rc5_params;
+ CK_RC5_MAC_GENERAL_PARAMS *rc5_mac;
+#endif
+ unsigned char ivBlock[SFTK_MAX_BLOCK_SIZE];
+ SFTKSessionContext *context;
+ CK_RV crv;
+ int blockSize;
+
+ switch (pMechanism->mechanism) {
+ case CKM_RC2_MAC_GENERAL:
+ mac_bytes =
+ ((CK_RC2_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength;
+ /* fall through */
+ case CKM_RC2_MAC:
+ /* this works because ulEffectiveBits is in the same place in both the
+ * CK_RC2_MAC_GENERAL_PARAMS and CK_RC2_CBC_PARAMS */
+ rc2_params.ulEffectiveBits = ((CK_RC2_MAC_GENERAL_PARAMS *)
+ pMechanism->pParameter)->ulEffectiveBits;
+ PORT_Memset(rc2_params.iv,0,sizeof(rc2_params.iv));
+ cbc_mechanism.mechanism = CKM_RC2_CBC;
+ cbc_mechanism.pParameter = &rc2_params;
+ cbc_mechanism.ulParameterLen = sizeof(rc2_params);
+ blockSize = 8;
+ break;
+#if NSS_SOFTOKEN_DOES_RC5
+ case CKM_RC5_MAC_GENERAL:
+ mac_bytes =
+ ((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength;
+ /* fall through */
+ case CKM_RC5_MAC:
+ /* this works because ulEffectiveBits is in the same place in both the
+ * CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */
+ rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter;
+ rc5_params.ulWordsize = rc5_mac->ulWordsize;
+ rc5_params.ulRounds = rc5_mac->ulRounds;
+ rc5_params.pIv = ivBlock;
+ blockSize = rc5_mac->ulWordsize*2;
+ rc5_params.ulIvLen = blockSize;
+ PORT_Memset(ivBlock,0,blockSize);
+ cbc_mechanism.mechanism = CKM_RC5_CBC;
+ cbc_mechanism.pParameter = &rc5_params;
+ cbc_mechanism.ulParameterLen = sizeof(rc5_params);
+ break;
+#endif
+ /* add cast and idea later */
+ case CKM_DES_MAC_GENERAL:
+ mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+ /* fall through */
+ case CKM_DES_MAC:
+ blockSize = 8;
+ PORT_Memset(ivBlock,0,blockSize);
+ cbc_mechanism.mechanism = CKM_DES_CBC;
+ cbc_mechanism.pParameter = &ivBlock;
+ cbc_mechanism.ulParameterLen = blockSize;
+ break;
+ case CKM_DES3_MAC_GENERAL:
+ mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+ /* fall through */
+ case CKM_DES3_MAC:
+ blockSize = 8;
+ PORT_Memset(ivBlock,0,blockSize);
+ cbc_mechanism.mechanism = CKM_DES3_CBC;
+ cbc_mechanism.pParameter = &ivBlock;
+ cbc_mechanism.ulParameterLen = blockSize;
+ break;
+ case CKM_CDMF_MAC_GENERAL:
+ mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+ /* fall through */
+ case CKM_CDMF_MAC:
+ blockSize = 8;
+ PORT_Memset(ivBlock,0,blockSize);
+ cbc_mechanism.mechanism = CKM_CDMF_CBC;
+ cbc_mechanism.pParameter = &ivBlock;
+ cbc_mechanism.ulParameterLen = blockSize;
+ break;
+ case CKM_CAMELLIA_MAC_GENERAL:
+ mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+ /* fall through */
+ case CKM_CAMELLIA_MAC:
+ blockSize = 16;
+ PORT_Memset(ivBlock,0,blockSize);
+ cbc_mechanism.mechanism = CKM_CAMELLIA_CBC;
+ cbc_mechanism.pParameter = &ivBlock;
+ cbc_mechanism.ulParameterLen = blockSize;
+ break;
+ case CKM_AES_MAC_GENERAL:
+ mac_bytes = *(CK_ULONG *)pMechanism->pParameter;
+ /* fall through */
+ case CKM_AES_MAC:
+ blockSize = 16;
+ PORT_Memset(ivBlock,0,blockSize);
+ cbc_mechanism.mechanism = CKM_AES_CBC;
+ cbc_mechanism.pParameter = &ivBlock;
+ cbc_mechanism.ulParameterLen = blockSize;
+ break;
+ default:
+ return CKR_FUNCTION_NOT_SUPPORTED;
+ }
+
+ crv = sftk_CryptInit(hSession, &cbc_mechanism, hKey, keyUsage,
+ contextType, PR_TRUE);
+ if (crv != CKR_OK) return crv;
+ crv = sftk_GetContext(hSession,&context,contextType,PR_TRUE,NULL);
+
+ /* this shouldn't happen! */
+ PORT_Assert(crv == CKR_OK);
+ if (crv != CKR_OK) return crv;
+ context->blockSize = blockSize;
+ if (mac_bytes == SFTK_INVALID_MAC_SIZE) mac_bytes = blockSize/2;
+ context->macSize = mac_bytes;
+ return CKR_OK;
+}
+
+/*
+ * encode RSA PKCS #1 Signature data before signing...
+ */
+static SECStatus
+sftk_HashSign(SFTKHashSignInfo *info,unsigned char *sig,unsigned int *sigLen,
+ unsigned int maxLen,unsigned char *hash, unsigned int hashLen)
+{
+ return RSA_HashSign(info->hashOid,info->key,sig,sigLen,maxLen,
+ hash,hashLen);
+}
+
+/* XXX Old template; want to expunge it eventually. */
+static DERTemplate SECAlgorithmIDTemplate[] = {
+ { DER_SEQUENCE,
+ 0, NULL, sizeof(SECAlgorithmID) },
+ { DER_OBJECT_ID,
+ offsetof(SECAlgorithmID,algorithm), },
+ { DER_OPTIONAL | DER_ANY,
+ offsetof(SECAlgorithmID,parameters), },
+ { 0, }
+};
+
+/*
+ * XXX OLD Template. Once all uses have been switched over to new one,
+ * remove this.
+ */
+static DERTemplate SGNDigestInfoTemplate[] = {
+ { DER_SEQUENCE,
+ 0, NULL, sizeof(SGNDigestInfo) },
+ { DER_INLINE,
+ offsetof(SGNDigestInfo,digestAlgorithm),
+ SECAlgorithmIDTemplate, },
+ { DER_OCTET_STRING,
+ offsetof(SGNDigestInfo,digest), },
+ { 0, }
+};
+
+SECStatus
+RSA_HashSign(SECOidTag hashOid, NSSLOWKEYPrivateKey *key,
+ unsigned char *sig, unsigned int *sigLen, unsigned int maxLen,
+ unsigned char *hash, unsigned int hashLen)
+{
+
+ SECStatus rv = SECFailure;
+ SECItem digder;
+ PLArenaPool *arena = NULL;
+ SGNDigestInfo *di = NULL;
+
+ digder.data = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( !arena ) { goto loser; }
+
+ /* Construct digest info */
+ di = SGN_CreateDigestInfo(hashOid, hash, hashLen);
+ if (!di) { goto loser; }
+
+ /* Der encode the digest as a DigestInfo */
+ rv = DER_Encode(arena, &digder, SGNDigestInfoTemplate, di);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /*
+ ** Encrypt signature after constructing appropriate PKCS#1 signature
+ ** block
+ */
+ rv = RSA_Sign(key,sig,sigLen,maxLen,digder.data,digder.len);
+
+ loser:
+ SGN_DestroyDigestInfo(di);
+ if (arena != NULL) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return rv;
+}
+
+static SECStatus
+nsc_DSA_Verify_Stub(void *ctx, void *sigBuf, unsigned int sigLen,
+ void *dataBuf, unsigned int dataLen)
+{
+ SECItem signature, digest;
+ NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx;
+
+ signature.data = (unsigned char *)sigBuf;
+ signature.len = sigLen;
+ digest.data = (unsigned char *)dataBuf;
+ digest.len = dataLen;
+ return DSA_VerifyDigest(&(key->u.dsa), &signature, &digest);
+}
+
+static SECStatus
+nsc_DSA_Sign_Stub(void *ctx, void *sigBuf,
+ unsigned int *sigLen, unsigned int maxSigLen,
+ void *dataBuf, unsigned int dataLen)
+{
+ SECItem signature, digest;
+ SECStatus rv;
+ NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx;
+
+ signature.data = (unsigned char *)sigBuf;
+ signature.len = maxSigLen;
+ digest.data = (unsigned char *)dataBuf;
+ digest.len = dataLen;
+ rv = DSA_SignDigest(&(key->u.dsa), &signature, &digest);
+ if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ *sigLen = signature.len;
+ return rv;
+}
+
+#ifdef NSS_ENABLE_ECC
+static SECStatus
+nsc_ECDSAVerifyStub(void *ctx, void *sigBuf, unsigned int sigLen,
+ void *dataBuf, unsigned int dataLen)
+{
+ SECItem signature, digest;
+ NSSLOWKEYPublicKey *key = (NSSLOWKEYPublicKey *)ctx;
+
+ signature.data = (unsigned char *)sigBuf;
+ signature.len = sigLen;
+ digest.data = (unsigned char *)dataBuf;
+ digest.len = dataLen;
+ return ECDSA_VerifyDigest(&(key->u.ec), &signature, &digest);
+}
+
+static SECStatus
+nsc_ECDSASignStub(void *ctx, void *sigBuf,
+ unsigned int *sigLen, unsigned int maxSigLen,
+ void *dataBuf, unsigned int dataLen)
+{
+ SECItem signature, digest;
+ SECStatus rv;
+ NSSLOWKEYPrivateKey *key = (NSSLOWKEYPrivateKey *)ctx;
+
+ signature.data = (unsigned char *)sigBuf;
+ signature.len = maxSigLen;
+ digest.data = (unsigned char *)dataBuf;
+ digest.len = dataLen;
+ rv = ECDSA_SignDigest(&(key->u.ec), &signature, &digest);
+ if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ *sigLen = signature.len;
+ return rv;
+}
+#endif /* NSS_ENABLE_ECC */
+
+/* NSC_SignInit setups up the signing operations. There are three basic
+ * types of signing:
+ * (1) the tradition single part, where "Raw RSA" or "Raw DSA" is applied
+ * to data in a single Sign operation (which often looks a lot like an
+ * encrypt, with data coming in and data going out).
+ * (2) Hash based signing, where we continually hash the data, then apply
+ * some sort of signature to the end.
+ * (3) Block Encryption CBC MAC's, where the Data is encrypted with a key,
+ * and only the final block is part of the mac.
+ *
+ * For case number 3, we initialize a context much like the Encryption Context
+ * (in fact we share code). We detect case 3 in C_SignUpdate, C_Sign, and
+ * C_Final by the following method... if it's not multi-part, and it's doesn't
+ * have a hash context, it must be a block Encryption CBC MAC.
+ *
+ * For case number 2, we initialize a hash structure, as well as make it
+ * multi-part. Updates are simple calls to the hash update function. Final
+ * calls the hashend, then passes the result to the 'update' function (which
+ * operates as a final signature function). In some hash based MAC'ing (as
+ * opposed to hash base signatures), the update function is can be simply a
+ * copy (as is the case with HMAC).
+ */
+CK_RV NSC_SignInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey)
+{
+ SFTKSession *session;
+ SFTKObject *key;
+ SFTKSessionContext *context;
+ CK_KEY_TYPE key_type;
+ CK_RV crv = CKR_OK;
+ NSSLOWKEYPrivateKey *privKey;
+ SFTKHashSignInfo *info = NULL;
+
+ CHECK_FORK();
+
+ /* Block Cipher MACing Algorithms use a different Context init method..*/
+ crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_SIGN, SFTK_SIGN);
+ if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv;
+
+ /* we're not using a block cipher mac */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ crv = sftk_InitGeneric(session,&context,SFTK_SIGN,&key,hKey,&key_type,
+ CKO_PRIVATE_KEY,CKA_SIGN);
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ return crv;
+ }
+
+ context->multi = PR_FALSE;
+
+#define INIT_RSA_SIGN_MECH(mmm) \
+ case CKM_ ## mmm ## _RSA_PKCS: \
+ context->multi = PR_TRUE; \
+ crv = sftk_doSub ## mmm (context); \
+ if (crv != CKR_OK) break; \
+ context->update = (SFTKCipher) sftk_HashSign; \
+ info = PORT_New(SFTKHashSignInfo); \
+ if (info == NULL) { crv = CKR_HOST_MEMORY; break; } \
+ info->hashOid = SEC_OID_ ## mmm ; \
+ goto finish_rsa;
+
+ switch(pMechanism->mechanism) {
+ INIT_RSA_SIGN_MECH(MD5)
+ INIT_RSA_SIGN_MECH(MD2)
+ INIT_RSA_SIGN_MECH(SHA1)
+ INIT_RSA_SIGN_MECH(SHA256)
+ INIT_RSA_SIGN_MECH(SHA384)
+ INIT_RSA_SIGN_MECH(SHA512)
+
+ case CKM_RSA_PKCS:
+ context->update = (SFTKCipher) RSA_Sign;
+ goto finish_rsa;
+ case CKM_RSA_X_509:
+ context->update = (SFTKCipher) RSA_SignRaw;
+finish_rsa:
+ if (key_type != CKK_RSA) {
+ if (info) PORT_Free(info);
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ privKey = sftk_GetPrivKey(key,CKK_RSA,&crv);
+ if (privKey == NULL) {
+ if (info) PORT_Free(info);
+ break;
+ }
+ /* OK, info is allocated only if we're doing hash and sign mechanism.
+ * It's necessary to be able to set the correct OID in the final
+ * signature.
+ */
+ if (info) {
+ info->key = privKey;
+ context->cipherInfo = info;
+ context->destroy = (SFTKDestroy)sftk_Space;
+ } else {
+ context->cipherInfo = privKey;
+ context->destroy = (SFTKDestroy)sftk_Null;
+ }
+ context->maxLen = nsslowkey_PrivateModulusLen(privKey);
+ break;
+
+ case CKM_DSA_SHA1:
+ context->multi = PR_TRUE;
+ crv = sftk_doSubSHA1(context);
+ if (crv != CKR_OK) break;
+ /* fall through */
+ case CKM_DSA:
+ if (key_type != CKK_DSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ privKey = sftk_GetPrivKey(key,CKK_DSA,&crv);
+ if (privKey == NULL) {
+ break;
+ }
+ context->cipherInfo = privKey;
+ context->update = (SFTKCipher) nsc_DSA_Sign_Stub;
+ context->destroy = (privKey == key->objectInfo) ?
+ (SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey;
+ context->maxLen = DSA_SIGNATURE_LEN;
+
+ break;
+
+#ifdef NSS_ENABLE_ECC
+ case CKM_ECDSA_SHA1:
+ context->multi = PR_TRUE;
+ crv = sftk_doSubSHA1(context);
+ if (crv != CKR_OK) break;
+ /* fall through */
+ case CKM_ECDSA:
+ if (key_type != CKK_EC) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ privKey = sftk_GetPrivKey(key,CKK_EC,&crv);
+ if (privKey == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->cipherInfo = privKey;
+ context->update = (SFTKCipher) nsc_ECDSASignStub;
+ context->destroy = (privKey == key->objectInfo) ?
+ (SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey;
+ context->maxLen = MAX_ECKEY_LEN * 2;
+
+ break;
+#endif /* NSS_ENABLE_ECC */
+
+#define INIT_HMAC_MECH(mmm) \
+ case CKM_ ## mmm ## _HMAC_GENERAL: \
+ crv = sftk_doHMACInit(context, HASH_Alg ## mmm ,key, \
+ *(CK_ULONG *)pMechanism->pParameter); \
+ break; \
+ case CKM_ ## mmm ## _HMAC: \
+ crv = sftk_doHMACInit(context, HASH_Alg ## mmm ,key, mmm ## _LENGTH); \
+ break;
+
+ INIT_HMAC_MECH(MD2)
+ INIT_HMAC_MECH(MD5)
+ INIT_HMAC_MECH(SHA256)
+ INIT_HMAC_MECH(SHA384)
+ INIT_HMAC_MECH(SHA512)
+
+ case CKM_SHA_1_HMAC_GENERAL:
+ crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,
+ *(CK_ULONG *)pMechanism->pParameter);
+ break;
+ case CKM_SHA_1_HMAC:
+ crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH);
+ break;
+
+ case CKM_SSL3_MD5_MAC:
+ crv = sftk_doSSLMACInit(context,SEC_OID_MD5,key,
+ *(CK_ULONG *)pMechanism->pParameter);
+ break;
+ case CKM_SSL3_SHA1_MAC:
+ crv = sftk_doSSLMACInit(context,SEC_OID_SHA1,key,
+ *(CK_ULONG *)pMechanism->pParameter);
+ break;
+ case CKM_TLS_PRF_GENERAL:
+ crv = sftk_TLSPRFInit(context, key, key_type);
+ break;
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ sftk_FreeContext(context);
+ sftk_FreeSession(session);
+ return crv;
+ }
+ sftk_SetContextByType(session, SFTK_SIGN, context);
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+
+/* MACUpdate is the common implementation for SignUpdate and VerifyUpdate.
+ * (sign and verify only very in their setup and final operations) */
+static CK_RV
+sftk_MACUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,SFTKContextType type)
+{
+ unsigned int outlen;
+ SFTKSessionContext *context;
+ CK_RV crv;
+ SECStatus rv;
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,type,PR_TRUE,NULL);
+ if (crv != CKR_OK) return crv;
+
+ if (context->hashInfo) {
+ (*context->hashUpdate)(context->hashInfo, pPart, ulPartLen);
+ return CKR_OK;
+ }
+
+ /* must be block cipher macing */
+
+ /* deal with previous buffered data */
+ if (context->padDataLength != 0) {
+ int i;
+ /* fill in the padded to a full block size */
+ for (i=context->padDataLength; (ulPartLen != 0) &&
+ i < (int)context->blockSize; i++) {
+ context->padBuf[i] = *pPart++;
+ ulPartLen--;
+ context->padDataLength++;
+ }
+
+ /* not enough data to encrypt yet? then return */
+ if (context->padDataLength != context->blockSize) return CKR_OK;
+ /* encrypt the current padded data */
+ rv = (*context->update)(context->cipherInfo,context->macBuf,&outlen,
+ SFTK_MAX_BLOCK_SIZE,context->padBuf,context->blockSize);
+ if (rv != SECSuccess) return CKR_DEVICE_ERROR;
+ }
+
+ /* save the residual */
+ context->padDataLength = ulPartLen % context->blockSize;
+ if (context->padDataLength) {
+ PORT_Memcpy(context->padBuf,
+ &pPart[ulPartLen-context->padDataLength],
+ context->padDataLength);
+ ulPartLen -= context->padDataLength;
+ }
+
+ /* if we've exhausted our new buffer, we're done */
+ if (ulPartLen == 0) { return CKR_OK; }
+
+ /* run the data through out encrypter */
+ while (ulPartLen) {
+ rv = (*context->update)(context->cipherInfo, context->padBuf, &outlen,
+ SFTK_MAX_BLOCK_SIZE, pPart, context->blockSize);
+ if (rv != SECSuccess) return CKR_DEVICE_ERROR;
+ /* paranoia.. make sure we exit the loop */
+ PORT_Assert(ulPartLen >= context->blockSize);
+ if (ulPartLen < context->blockSize) break;
+ ulPartLen -= context->blockSize;
+ }
+
+ return CKR_OK;
+
+}
+
+/* NSC_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+CK_RV NSC_SignUpdate(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ CHECK_FORK();
+
+ return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_SIGN);
+}
+
+
+/* NSC_SignFinal finishes a multiple-part signature operation,
+ * returning the signature. */
+CK_RV NSC_SignFinal(CK_SESSION_HANDLE hSession,CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int digestLen;
+ unsigned int maxoutlen = *pulSignatureLen;
+ unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH];
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ *pulSignatureLen = 0;
+ crv = sftk_GetContext(hSession,&context,SFTK_SIGN,PR_TRUE,&session);
+ if (crv != CKR_OK) return crv;
+
+ if (!pSignature) {
+ *pulSignatureLen = context->maxLen;
+ goto finish;
+ } else if (context->hashInfo) {
+ (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf));
+ rv = (*context->update)(context->cipherInfo, pSignature,
+ &outlen, maxoutlen, tmpbuf, digestLen);
+ *pulSignatureLen = (CK_ULONG) outlen;
+ } else {
+ /* deal with the last block if any residual */
+ if (context->padDataLength) {
+ /* fill out rest of pad buffer with pad magic*/
+ int i;
+ for (i=context->padDataLength; i < (int)context->blockSize; i++) {
+ context->padBuf[i] = 0;
+ }
+ rv = (*context->update)(context->cipherInfo,context->macBuf,
+ &outlen,SFTK_MAX_BLOCK_SIZE,context->padBuf,context->blockSize);
+ }
+ if (rv == SECSuccess) {
+ PORT_Memcpy(pSignature,context->macBuf,context->macSize);
+ *pulSignatureLen = context->macSize;
+ }
+ }
+
+ sftk_FreeContext(context);
+ sftk_SetContextByType(session, SFTK_SIGN, NULL);
+
+finish:
+ sftk_FreeSession(session);
+
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+/* NSC_Sign signs (encrypts with private key) data in a single part,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+CK_RV NSC_Sign(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,CK_ULONG ulDataLen,CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int maxoutlen = *pulSignatureLen;
+ CK_RV crv,crv2;
+ SECStatus rv = SECSuccess;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_SIGN,PR_FALSE,&session);
+ if (crv != CKR_OK) return crv;
+
+ if (!pSignature) {
+ *pulSignatureLen = context->maxLen;
+ goto finish;
+ }
+
+ /* multi part Signing are completely implemented by SignUpdate and
+ * sign Final */
+ if (context->multi) {
+ sftk_FreeSession(session);
+ crv = NSC_SignUpdate(hSession,pData,ulDataLen);
+ if (crv != CKR_OK) *pulSignatureLen = 0;
+ crv2 = NSC_SignFinal(hSession, pSignature, pulSignatureLen);
+ return crv == CKR_OK ? crv2 :crv;
+ }
+
+ rv = (*context->update)(context->cipherInfo, pSignature,
+ &outlen, maxoutlen, pData, ulDataLen);
+ *pulSignatureLen = (CK_ULONG) outlen;
+ sftk_FreeContext(context);
+ sftk_SetContextByType(session, SFTK_SIGN, NULL);
+
+finish:
+ sftk_FreeSession(session);
+
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+
+/*
+ ************** Crypto Functions: Sign Recover ************************
+ */
+/* NSC_SignRecoverInit initializes a signature operation,
+ * where the (digest) data can be recovered from the signature.
+ * E.g. encryption with the user's private key */
+CK_RV NSC_SignRecoverInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey)
+{
+ CHECK_FORK();
+
+ switch (pMechanism->mechanism) {
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ return NSC_SignInit(hSession,pMechanism,hKey);
+ default:
+ break;
+ }
+ return CKR_MECHANISM_INVALID;
+}
+
+
+/* NSC_SignRecover signs data in a single operation
+ * where the (digest) data can be recovered from the signature.
+ * E.g. encryption with the user's private key */
+CK_RV NSC_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen)
+{
+ CHECK_FORK();
+
+ return NSC_Sign(hSession,pData,ulDataLen,pSignature,pulSignatureLen);
+}
+
+/*
+ ************** Crypto Functions: verify ************************
+ */
+
+/* Handle RSA Signature formatting */
+static SECStatus
+sftk_hashCheckSign(SFTKHashVerifyInfo *info, unsigned char *sig,
+ unsigned int sigLen, unsigned char *digest, unsigned int digestLen)
+{
+ return RSA_HashCheckSign(info->hashOid, info->key, sig, sigLen,
+ digest, digestLen);
+}
+
+SECStatus
+RSA_HashCheckSign(SECOidTag hashOid, NSSLOWKEYPublicKey *key,
+ unsigned char *sig, unsigned int sigLen,
+ unsigned char *digest, unsigned int digestLen)
+{
+
+ SECItem it;
+ SGNDigestInfo *di = NULL;
+ SECStatus rv = SECSuccess;
+
+ it.data = NULL;
+
+ if (key == NULL) goto loser;
+
+ it.len = nsslowkey_PublicModulusLen(key);
+ if (!it.len) goto loser;
+
+ it.data = (unsigned char *) PORT_Alloc(it.len);
+ if (it.data == NULL) goto loser;
+
+ /* decrypt the block */
+ rv = RSA_CheckSignRecover(key, it.data, &it.len, it.len, sig, sigLen);
+ if (rv != SECSuccess) goto loser;
+
+ di = SGN_DecodeDigestInfo(&it);
+ if (di == NULL) goto loser;
+ if (di->digest.len != digestLen) goto loser;
+
+ /* make sure the tag is OK */
+ if (SECOID_GetAlgorithmTag(&di->digestAlgorithm) != hashOid) {
+ goto loser;
+ }
+ /* make sure the "parameters" are not too bogus. */
+ if (di->digestAlgorithm.parameters.len > 2) {
+ goto loser;
+ }
+ /* Now check the signature */
+ if (PORT_Memcmp(digest, di->digest.data, di->digest.len) == 0) {
+ goto done;
+ }
+
+ loser:
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ rv = SECFailure;
+
+ done:
+ if (it.data != NULL) PORT_Free(it.data);
+ if (di != NULL) SGN_DestroyDigestInfo(di);
+
+ return rv;
+}
+
+/* NSC_VerifyInit initializes a verification operation,
+ * where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature (e.g. DSA) */
+CK_RV NSC_VerifyInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey)
+{
+ SFTKSession *session;
+ SFTKObject *key;
+ SFTKSessionContext *context;
+ CK_KEY_TYPE key_type;
+ CK_RV crv = CKR_OK;
+ NSSLOWKEYPublicKey *pubKey;
+ SFTKHashVerifyInfo *info = NULL;
+
+ CHECK_FORK();
+
+ /* Block Cipher MACing Algorithms use a different Context init method..*/
+ crv = sftk_InitCBCMac(hSession, pMechanism, hKey, CKA_VERIFY, SFTK_VERIFY);
+ if (crv != CKR_FUNCTION_NOT_SUPPORTED) return crv;
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ crv = sftk_InitGeneric(session,&context,SFTK_VERIFY,&key,hKey,&key_type,
+ CKO_PUBLIC_KEY,CKA_VERIFY);
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ return crv;
+ }
+
+ context->multi = PR_FALSE;
+
+#define INIT_RSA_VFY_MECH(mmm) \
+ case CKM_ ## mmm ## _RSA_PKCS: \
+ context->multi = PR_TRUE; \
+ crv = sftk_doSub ## mmm (context); \
+ if (crv != CKR_OK) break; \
+ context->verify = (SFTKVerify) sftk_hashCheckSign; \
+ info = PORT_New(SFTKHashVerifyInfo); \
+ if (info == NULL) { crv = CKR_HOST_MEMORY; break; } \
+ info->hashOid = SEC_OID_ ## mmm ; \
+ goto finish_rsa;
+
+ switch(pMechanism->mechanism) {
+ INIT_RSA_VFY_MECH(MD5)
+ INIT_RSA_VFY_MECH(MD2)
+ INIT_RSA_VFY_MECH(SHA1)
+ INIT_RSA_VFY_MECH(SHA256)
+ INIT_RSA_VFY_MECH(SHA384)
+ INIT_RSA_VFY_MECH(SHA512)
+
+ case CKM_RSA_PKCS:
+ context->verify = (SFTKVerify) RSA_CheckSign;
+ goto finish_rsa;
+ case CKM_RSA_X_509:
+ context->verify = (SFTKVerify) RSA_CheckSignRaw;
+finish_rsa:
+ if (key_type != CKK_RSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ pubKey = sftk_GetPubKey(key,CKK_RSA,&crv);
+ if (pubKey == NULL) {
+ break;
+ }
+ if (info) {
+ info->key = pubKey;
+ context->cipherInfo = info;
+ context->destroy = sftk_Space;
+ } else {
+ context->cipherInfo = pubKey;
+ context->destroy = sftk_Null;
+ }
+ break;
+ case CKM_DSA_SHA1:
+ context->multi = PR_TRUE;
+ crv = sftk_doSubSHA1(context);
+ if (crv != CKR_OK) break;
+ /* fall through */
+ case CKM_DSA:
+ if (key_type != CKK_DSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ pubKey = sftk_GetPubKey(key,CKK_DSA,&crv);
+ if (pubKey == NULL) {
+ break;
+ }
+ context->cipherInfo = pubKey;
+ context->verify = (SFTKVerify) nsc_DSA_Verify_Stub;
+ context->destroy = sftk_Null;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKM_ECDSA_SHA1:
+ context->multi = PR_TRUE;
+ crv = sftk_doSubSHA1(context);
+ if (crv != CKR_OK) break;
+ /* fall through */
+ case CKM_ECDSA:
+ if (key_type != CKK_EC) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ pubKey = sftk_GetPubKey(key,CKK_EC,&crv);
+ if (pubKey == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ context->cipherInfo = pubKey;
+ context->verify = (SFTKVerify) nsc_ECDSAVerifyStub;
+ context->destroy = sftk_Null;
+ break;
+#endif /* NSS_ENABLE_ECC */
+
+ INIT_HMAC_MECH(MD2)
+ INIT_HMAC_MECH(MD5)
+ INIT_HMAC_MECH(SHA256)
+ INIT_HMAC_MECH(SHA384)
+ INIT_HMAC_MECH(SHA512)
+
+ case CKM_SHA_1_HMAC_GENERAL:
+ crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,
+ *(CK_ULONG *)pMechanism->pParameter);
+ break;
+ case CKM_SHA_1_HMAC:
+ crv = sftk_doHMACInit(context,HASH_AlgSHA1,key,SHA1_LENGTH);
+ break;
+
+ case CKM_SSL3_MD5_MAC:
+ crv = sftk_doSSLMACInit(context,SEC_OID_MD5,key,
+ *(CK_ULONG *)pMechanism->pParameter);
+ break;
+ case CKM_SSL3_SHA1_MAC:
+ crv = sftk_doSSLMACInit(context,SEC_OID_SHA1,key,
+ *(CK_ULONG *)pMechanism->pParameter);
+ break;
+ case CKM_TLS_PRF_GENERAL:
+ crv = sftk_TLSPRFInit(context, key, key_type);
+ break;
+
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ if (info) PORT_Free(info);
+ PORT_Free(context);
+ sftk_FreeSession(session);
+ return crv;
+ }
+ sftk_SetContextByType(session, SFTK_VERIFY, context);
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+/* NSC_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+CK_RV NSC_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ CK_RV crv, crv2;
+ SECStatus rv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_VERIFY,PR_FALSE,&session);
+ if (crv != CKR_OK) return crv;
+
+ /* multi part Verifying are completely implemented by VerifyUpdate and
+ * VerifyFinal */
+ if (context->multi) {
+ sftk_FreeSession(session);
+ crv = NSC_VerifyUpdate(hSession, pData, ulDataLen);
+ crv2 = NSC_VerifyFinal(hSession, pSignature, ulSignatureLen);
+ return crv == CKR_OK ? crv2 :crv;
+ }
+
+ rv = (*context->verify)(context->cipherInfo,pSignature, ulSignatureLen,
+ pData, ulDataLen);
+ sftk_FreeContext(context);
+ sftk_SetContextByType(session, SFTK_VERIFY, NULL);
+ sftk_FreeSession(session);
+
+ return (rv == SECSuccess) ? CKR_OK : CKR_SIGNATURE_INVALID;
+
+}
+
+
+/* NSC_VerifyUpdate continues a multiple-part verification operation,
+ * where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature */
+CK_RV NSC_VerifyUpdate( CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ CHECK_FORK();
+
+ return sftk_MACUpdate(hSession, pPart, ulPartLen, SFTK_VERIFY);
+}
+
+
+/* NSC_VerifyFinal finishes a multiple-part verification operation,
+ * checking the signature. */
+CK_RV NSC_VerifyFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int digestLen;
+ unsigned char tmpbuf[SFTK_MAX_MAC_LENGTH];
+ CK_RV crv;
+ SECStatus rv = SECSuccess;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_VERIFY,PR_TRUE,&session);
+ if (crv != CKR_OK) return crv;
+
+ if (context->hashInfo) {
+ (*context->end)(context->hashInfo, tmpbuf, &digestLen, sizeof(tmpbuf));
+ rv = (*context->verify)(context->cipherInfo, pSignature,
+ ulSignatureLen, tmpbuf, digestLen);
+ } else {
+ if (context->padDataLength) {
+ /* fill out rest of pad buffer with pad magic*/
+ int i;
+ for (i=context->padDataLength; i < (int)context->blockSize; i++) {
+ context->padBuf[i] = 0;
+ }
+ rv = (*context->update)(context->cipherInfo,context->macBuf,
+ &outlen,SFTK_MAX_BLOCK_SIZE,context->padBuf,context->blockSize);
+ }
+ if (rv == SECSuccess) {
+ rv =(PORT_Memcmp(pSignature,context->macBuf,context->macSize) == 0)
+ ? SECSuccess : SECFailure;
+ }
+ }
+
+ sftk_FreeContext(context);
+ sftk_SetContextByType(session, SFTK_VERIFY, NULL);
+ sftk_FreeSession(session);
+ return (rv == SECSuccess) ? CKR_OK : CKR_SIGNATURE_INVALID;
+
+}
+
+/*
+ ************** Crypto Functions: Verify Recover ************************
+ */
+
+/* NSC_VerifyRecoverInit initializes a signature verification operation,
+ * where the data is recovered from the signature.
+ * E.g. Decryption with the user's public key */
+CK_RV NSC_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_OBJECT_HANDLE hKey)
+{
+ SFTKSession *session;
+ SFTKObject *key;
+ SFTKSessionContext *context;
+ CK_KEY_TYPE key_type;
+ CK_RV crv = CKR_OK;
+ NSSLOWKEYPublicKey *pubKey;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ crv = sftk_InitGeneric(session,&context,SFTK_VERIFY_RECOVER,
+ &key,hKey,&key_type,CKO_PUBLIC_KEY,CKA_VERIFY_RECOVER);
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ return crv;
+ }
+
+ context->multi = PR_TRUE;
+
+ switch(pMechanism->mechanism) {
+ case CKM_RSA_PKCS:
+ case CKM_RSA_X_509:
+ if (key_type != CKK_RSA) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ context->multi = PR_FALSE;
+ pubKey = sftk_GetPubKey(key,CKK_RSA,&crv);
+ if (pubKey == NULL) {
+ break;
+ }
+ context->cipherInfo = pubKey;
+ context->update = (SFTKCipher) (pMechanism->mechanism == CKM_RSA_X_509
+ ? RSA_CheckSignRecoverRaw : RSA_CheckSignRecover);
+ context->destroy = sftk_Null;
+ break;
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ if (crv != CKR_OK) {
+ PORT_Free(context);
+ sftk_FreeSession(session);
+ return crv;
+ }
+ sftk_SetContextByType(session, SFTK_VERIFY_RECOVER, context);
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+
+/* NSC_VerifyRecover verifies a signature in a single-part operation,
+ * where the data is recovered from the signature.
+ * E.g. Decryption with the user's public key */
+CK_RV NSC_VerifyRecover(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,CK_ULONG ulSignatureLen,
+ CK_BYTE_PTR pData,CK_ULONG_PTR pulDataLen)
+{
+ SFTKSession *session;
+ SFTKSessionContext *context;
+ unsigned int outlen;
+ unsigned int maxoutlen = *pulDataLen;
+ CK_RV crv;
+ SECStatus rv;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession,&context,SFTK_VERIFY_RECOVER,
+ PR_FALSE,&session);
+ if (crv != CKR_OK) return crv;
+ if (pData == NULL) {
+ /* to return the actual size, we need to do the decrypt, just return
+ * the max size, which is the size of the input signature. */
+ *pulDataLen = ulSignatureLen;
+ rv = SECSuccess;
+ goto finish;
+ }
+
+ rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen,
+ pSignature, ulSignatureLen);
+ *pulDataLen = (CK_ULONG) outlen;
+
+ sftk_FreeContext(context);
+ sftk_SetContextByType(session, SFTK_VERIFY_RECOVER, NULL);
+finish:
+ sftk_FreeSession(session);
+ return (rv == SECSuccess) ? CKR_OK : CKR_SIGNATURE_INVALID;
+}
+
+/*
+ **************************** Random Functions: ************************
+ */
+
+/* NSC_SeedRandom mixes additional seed material into the token's random number
+ * generator. */
+CK_RV NSC_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed,
+ CK_ULONG ulSeedLen)
+{
+ SECStatus rv;
+
+ CHECK_FORK();
+
+ rv = RNG_RandomUpdate(pSeed, ulSeedLen);
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+/* NSC_GenerateRandom generates random data. */
+CK_RV NSC_GenerateRandom(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pRandomData, CK_ULONG ulRandomLen)
+{
+ SECStatus rv;
+
+ CHECK_FORK();
+
+ rv = RNG_GenerateGlobalRandomBytes(pRandomData, ulRandomLen);
+ return (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR;
+}
+
+/*
+ **************************** Key Functions: ************************
+ */
+
+
+/*
+ * generate a password based encryption key. This code uses
+ * PKCS5 to do the work.
+ */
+static CK_RV
+nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism,
+ void *buf, CK_ULONG *key_length, PRBool faulty3DES)
+{
+ SECItem *pbe_key = NULL, iv, pwitem;
+ CK_PBE_PARAMS *pbe_params = NULL;
+ CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL;
+
+ *key_length = 0;
+ iv.data = NULL; iv.len = 0;
+
+ if (pMechanism->mechanism == CKM_PKCS5_PBKD2) {
+ pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter;
+ pwitem.data = (unsigned char *)pbkd2_params->pPassword;
+ /* was this a typo in the PKCS #11 spec? */
+ pwitem.len = *pbkd2_params->ulPasswordLen;
+ } else {
+ pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
+ pwitem.data = (unsigned char *)pbe_params->pPassword;
+ pwitem.len = pbe_params->ulPasswordLen;
+ }
+ pbe_key = nsspkcs5_ComputeKeyAndIV(pkcs5_pbe, &pwitem, &iv, faulty3DES);
+ if (pbe_key == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ PORT_Memcpy(buf, pbe_key->data, pbe_key->len);
+ *key_length = pbe_key->len;
+ SECITEM_ZfreeItem(pbe_key, PR_TRUE);
+ pbe_key = NULL;
+
+ if (iv.data) {
+ if (pbe_params && pbe_params->pInitVector != NULL) {
+ PORT_Memcpy(pbe_params->pInitVector, iv.data, iv.len);
+ }
+ PORT_Free(iv.data);
+ }
+
+ return CKR_OK;
+}
+static CK_RV
+nsc_parameter_gen(CK_KEY_TYPE key_type, SFTKObject *key)
+{
+ SFTKAttribute *attribute;
+ CK_ULONG counter;
+ unsigned int seedBits = 0;
+ unsigned int primeBits;
+ unsigned int j;
+ CK_RV crv = CKR_OK;
+ PQGParams *params = NULL;
+ PQGVerify *vfy = NULL;
+ SECStatus rv;
+
+ attribute = sftk_FindAttribute(key, CKA_PRIME_BITS);
+ if (attribute == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ primeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+ j = PQG_PBITS_TO_INDEX(primeBits);
+ if (j == (unsigned int)-1) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ attribute = sftk_FindAttribute(key, CKA_NETSCAPE_PQG_SEED_BITS);
+ if (attribute != NULL) {
+ seedBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+ }
+
+ sftk_DeleteAttributeType(key,CKA_PRIME_BITS);
+ sftk_DeleteAttributeType(key,CKA_NETSCAPE_PQG_SEED_BITS);
+
+ if (seedBits == 0) {
+ rv = PQG_ParamGen(j, &params, &vfy);
+ } else {
+ rv = PQG_ParamGenSeedLen(j,seedBits/8, &params, &vfy);
+ }
+
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ return CKR_DEVICE_ERROR;
+ }
+ crv = sftk_AddAttributeType(key,CKA_PRIME,
+ params->prime.data, params->prime.len);
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_AddAttributeType(key,CKA_SUBPRIME,
+ params->subPrime.data, params->subPrime.len);
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_AddAttributeType(key,CKA_BASE,
+ params->base.data, params->base.len);
+ if (crv != CKR_OK) goto loser;
+ counter = vfy->counter;
+ crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_COUNTER,
+ &counter, sizeof(counter));
+ crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_SEED,
+ vfy->seed.data, vfy->seed.len);
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_AddAttributeType(key,CKA_NETSCAPE_PQG_H,
+ vfy->h.data, vfy->h.len);
+ if (crv != CKR_OK) goto loser;
+
+loser:
+ if (params) {
+ PQG_DestroyParams(params);
+ }
+ if (vfy) {
+ PQG_DestroyVerify(vfy);
+ }
+ return crv;
+}
+
+
+static CK_RV
+nsc_SetupBulkKeyGen(CK_MECHANISM_TYPE mechanism, CK_KEY_TYPE *key_type,
+ CK_ULONG *key_length)
+{
+ CK_RV crv = CKR_OK;
+
+ switch (mechanism) {
+ case CKM_RC2_KEY_GEN:
+ *key_type = CKK_RC2;
+ if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+#if NSS_SOFTOKEN_DOES_RC5
+ case CKM_RC5_KEY_GEN:
+ *key_type = CKK_RC5;
+ if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+#endif
+ case CKM_RC4_KEY_GEN:
+ *key_type = CKK_RC4;
+ if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ case CKM_GENERIC_SECRET_KEY_GEN:
+ *key_type = CKK_GENERIC_SECRET;
+ if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ case CKM_CDMF_KEY_GEN:
+ *key_type = CKK_CDMF;
+ *key_length = 8;
+ break;
+ case CKM_DES_KEY_GEN:
+ *key_type = CKK_DES;
+ *key_length = 8;
+ break;
+ case CKM_DES2_KEY_GEN:
+ *key_type = CKK_DES2;
+ *key_length = 16;
+ break;
+ case CKM_DES3_KEY_GEN:
+ *key_type = CKK_DES3;
+ *key_length = 24;
+ break;
+ case CKM_CAMELLIA_KEY_GEN:
+ *key_type = CKK_CAMELLIA;
+ if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ case CKM_AES_KEY_GEN:
+ *key_type = CKK_AES;
+ if (*key_length == 0) crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ default:
+ PORT_Assert(0);
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ return crv;
+}
+
+CK_RV
+nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe)
+{
+ SECItem salt;
+ CK_PBE_PARAMS *pbe_params = NULL;
+ NSSPKCS5PBEParameter *params;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ *pbe = NULL;
+
+ arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
+ if (arena == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ params = (NSSPKCS5PBEParameter *) PORT_ArenaZAlloc(arena,
+ sizeof(NSSPKCS5PBEParameter));
+ if (params == NULL) {
+ PORT_FreeArena(arena,PR_TRUE);
+ return CKR_HOST_MEMORY;
+ }
+
+ params->poolp = arena;
+ params->ivLen = 0;
+ params->pbeType = NSSPKCS5_PKCS12_V2;
+ params->hashType = HASH_AlgSHA1;
+ params->encAlg = SEC_OID_SHA1; /* any invalid value */
+ params->is2KeyDES = PR_FALSE;
+ params->keyID = pbeBitGenIntegrityKey;
+ pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
+ params->iter = pbe_params->ulIteration;
+
+ salt.data = (unsigned char *)pbe_params->pSalt;
+ salt.len = (unsigned int)pbe_params->ulSaltLen;
+ rv = SECITEM_CopyItem(arena,&params->salt,&salt);
+ if (rv != SECSuccess) {
+ PORT_FreeArena(arena,PR_TRUE);
+ return CKR_HOST_MEMORY;
+ }
+ switch (pMechanism->mechanism) {
+ case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN:
+ case CKM_PBA_SHA1_WITH_SHA1_HMAC:
+ params->hashType = HASH_AlgSHA1;
+ params->keyLen = 20;
+ break;
+ case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN:
+ params->hashType = HASH_AlgMD5;
+ params->keyLen = 16;
+ break;
+ case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN:
+ params->hashType = HASH_AlgMD2;
+ params->keyLen = 16;
+ break;
+ default:
+ PORT_FreeArena(arena,PR_TRUE);
+ return CKR_MECHANISM_INVALID;
+ }
+ *pbe = params;
+ return CKR_OK;
+}
+
+/* maybe this should be table driven? */
+static CK_RV
+nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe,
+ CK_KEY_TYPE *key_type, CK_ULONG *key_length)
+{
+ CK_RV crv = CKR_OK;
+ SECOidData *oid;
+ CK_PBE_PARAMS *pbe_params = NULL;
+ NSSPKCS5PBEParameter *params = NULL;
+ CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL;
+ SECItem salt;
+ CK_ULONG iteration = 0;
+
+ *pbe = NULL;
+
+ oid = SECOID_FindOIDByMechanism(pMechanism->mechanism);
+ if (oid == NULL) {
+ return CKR_MECHANISM_INVALID;
+ }
+
+ if (pMechanism->mechanism == CKM_PKCS5_PBKD2) {
+ pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter;
+ if (pbkd2_params->saltSource != CKZ_SALT_SPECIFIED) {
+ return CKR_MECHANISM_PARAM_INVALID;
+ }
+ salt.data = (unsigned char *)pbkd2_params->pSaltSourceData;
+ salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen;
+ iteration = pbkd2_params->iterations;
+ } else {
+ pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter;
+ salt.data = (unsigned char *)pbe_params->pSalt;
+ salt.len = (unsigned int)pbe_params->ulSaltLen;
+ iteration = pbe_params->ulIteration;
+ }
+ params=nsspkcs5_NewParam(oid->offset, &salt, iteration);
+ if (params == NULL) {
+ return CKR_MECHANISM_INVALID;
+ }
+
+ switch (params->encAlg) {
+ case SEC_OID_DES_CBC:
+ *key_type = CKK_DES;
+ *key_length = params->keyLen;
+ break;
+ case SEC_OID_DES_EDE3_CBC:
+ *key_type = params->is2KeyDES ? CKK_DES2 : CKK_DES3;
+ *key_length = params->keyLen;
+ break;
+ case SEC_OID_RC2_CBC:
+ *key_type = CKK_RC2;
+ *key_length = params->keyLen;
+ break;
+ case SEC_OID_RC4:
+ *key_type = CKK_RC4;
+ *key_length = params->keyLen;
+ break;
+ case SEC_OID_PKCS5_PBKDF2:
+ /* sigh, PKCS #11 currently only defines SHA1 for the KDF hash type.
+ * we do the check here because this where we would handle multiple
+ * hash types in the future */
+ if (pbkd2_params == NULL ||
+ pbkd2_params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+ /* key type must already be set */
+ if (*key_type == CKK_INVALID_KEY_TYPE) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ }
+ /* PBKDF2 needs to calculate the key length from the other parameters
+ */
+ if (*key_length == 0) {
+ *key_length = sftk_MapKeySize(*key_type);
+ }
+ if (*key_length == 0) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ }
+ params->keyLen = *key_length;
+ break;
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ nsspkcs5_DestroyPBEParameter(params);
+ break;
+ }
+ if (crv == CKR_OK) {
+ *pbe = params;
+ }
+ return crv;
+}
+
+/* NSC_GenerateKey generates a secret key, creating a new key object. */
+CK_RV NSC_GenerateKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ SFTKObject *key;
+ SFTKSession *session;
+ PRBool checkWeak = PR_FALSE;
+ CK_ULONG key_length = 0;
+ CK_KEY_TYPE key_type = CKK_INVALID_KEY_TYPE;
+ CK_OBJECT_CLASS objclass = CKO_SECRET_KEY;
+ CK_RV crv = CKR_OK;
+ CK_BBOOL cktrue = CK_TRUE;
+ int i;
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ unsigned char buf[MAX_KEY_LEN];
+ enum {nsc_pbe, nsc_ssl, nsc_bulk, nsc_param} key_gen_type;
+ NSSPKCS5PBEParameter *pbe_param;
+ SSL3RSAPreMasterSecret *rsa_pms;
+ CK_VERSION *version;
+ /* in very old versions of NSS, there were implementation errors with key
+ * generation methods. We want to beable to read these, but not
+ * produce them any more. The affected algorithm was 3DES.
+ */
+ PRBool faultyPBE3DES = PR_FALSE;
+
+ CHECK_FORK();
+
+ if (!slot) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * now lets create an object to hang the attributes off of
+ */
+ key = sftk_NewObject(slot); /* fill in the handle later */
+ if (key == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * load the template values into the object
+ */
+ for (i=0; i < (int) ulCount; i++) {
+ if (pTemplate[i].type == CKA_VALUE_LEN) {
+ key_length = *(CK_ULONG *)pTemplate[i].pValue;
+ continue;
+ }
+ /* some algorithms need keytype specified */
+ if (pTemplate[i].type == CKA_KEY_TYPE) {
+ key_type = *(CK_ULONG *)pTemplate[i].pValue;
+ continue;
+ }
+
+ crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i]));
+ if (crv != CKR_OK) break;
+ }
+ if (crv != CKR_OK) {
+ sftk_FreeObject(key);
+ return crv;
+ }
+
+ /* make sure we don't have any class, key_type, or value fields */
+ sftk_DeleteAttributeType(key,CKA_CLASS);
+ sftk_DeleteAttributeType(key,CKA_KEY_TYPE);
+ sftk_DeleteAttributeType(key,CKA_VALUE);
+
+ /* Now Set up the parameters to generate the key (based on mechanism) */
+ key_gen_type = nsc_bulk; /* bulk key by default */
+ switch (pMechanism->mechanism) {
+ case CKM_CDMF_KEY_GEN:
+ case CKM_DES_KEY_GEN:
+ case CKM_DES2_KEY_GEN:
+ case CKM_DES3_KEY_GEN:
+ checkWeak = PR_TRUE;
+ case CKM_RC2_KEY_GEN:
+ case CKM_RC4_KEY_GEN:
+ case CKM_GENERIC_SECRET_KEY_GEN:
+ case CKM_CAMELLIA_KEY_GEN:
+ case CKM_AES_KEY_GEN:
+#if NSS_SOFTOKEN_DOES_RC5
+ case CKM_RC5_KEY_GEN:
+#endif
+ crv = nsc_SetupBulkKeyGen(pMechanism->mechanism,&key_type,&key_length);
+ break;
+ case CKM_SSL3_PRE_MASTER_KEY_GEN:
+ key_type = CKK_GENERIC_SECRET;
+ key_length = 48;
+ key_gen_type = nsc_ssl;
+ break;
+ case CKM_PBA_SHA1_WITH_SHA1_HMAC:
+ case CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN:
+ case CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN:
+ case CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN:
+ key_gen_type = nsc_pbe;
+ key_type = CKK_GENERIC_SECRET;
+ crv = nsc_SetupHMACKeyGen(pMechanism, &pbe_param);
+ break;
+ case CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC:
+ faultyPBE3DES = PR_TRUE;
+ case CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_DES_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC:
+ case CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4:
+ case CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4:
+ case CKM_PBE_SHA1_DES3_EDE_CBC:
+ case CKM_PBE_SHA1_DES2_EDE_CBC:
+ case CKM_PBE_SHA1_RC2_128_CBC:
+ case CKM_PBE_SHA1_RC2_40_CBC:
+ case CKM_PBE_SHA1_RC4_128:
+ case CKM_PBE_SHA1_RC4_40:
+ case CKM_PBE_MD5_DES_CBC:
+ case CKM_PBE_MD2_DES_CBC:
+ case CKM_PKCS5_PBKD2:
+ key_gen_type = nsc_pbe;
+ crv = nsc_SetupPBEKeyGen(pMechanism,&pbe_param, &key_type, &key_length);
+ break;
+ case CKM_DSA_PARAMETER_GEN:
+ key_gen_type = nsc_param;
+ key_type = CKK_DSA;
+ objclass = CKO_KG_PARAMETERS;
+ crv = CKR_OK;
+ break;
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ break;
+ }
+
+ /* make sure we aren't going to overflow the buffer */
+ if (sizeof(buf) < key_length) {
+ /* someone is getting pretty optimistic about how big their key can
+ * be... */
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ }
+
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+ /* if there was no error,
+ * key_type *MUST* be set in the switch statement above */
+ PORT_Assert( key_type != CKK_INVALID_KEY_TYPE );
+
+ /*
+ * now to the actual key gen.
+ */
+ switch (key_gen_type) {
+ case nsc_pbe:
+ crv = nsc_pbe_key_gen(pbe_param, pMechanism, buf, &key_length,
+ faultyPBE3DES);
+ nsspkcs5_DestroyPBEParameter(pbe_param);
+ break;
+ case nsc_ssl:
+ rsa_pms = (SSL3RSAPreMasterSecret *)buf;
+ version = (CK_VERSION *)pMechanism->pParameter;
+ rsa_pms->client_version[0] = version->major;
+ rsa_pms->client_version[1] = version->minor;
+ crv =
+ NSC_GenerateRandom(0,&rsa_pms->random[0], sizeof(rsa_pms->random));
+ break;
+ case nsc_bulk:
+ /* get the key, check for weak keys and repeat if found */
+ do {
+ crv = NSC_GenerateRandom(0, buf, key_length);
+ } while (crv == CKR_OK && checkWeak && sftk_IsWeakKey(buf,key_type));
+ break;
+ case nsc_param:
+ /* generate parameters */
+ *buf = 0;
+ crv = nsc_parameter_gen(key_type,key);
+ break;
+ }
+
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+ /* Add the class, key_type, and value */
+ crv = sftk_AddAttributeType(key,CKA_CLASS,&objclass,sizeof(CK_OBJECT_CLASS));
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+ crv = sftk_AddAttributeType(key,CKA_KEY_TYPE,&key_type,sizeof(CK_KEY_TYPE));
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+ if (key_length != 0) {
+ crv = sftk_AddAttributeType(key,CKA_VALUE,buf,key_length);
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+ }
+
+ /* get the session */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ sftk_FreeObject(key);
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /*
+ * handle the base object stuff
+ */
+ crv = sftk_handleObject(key,session);
+ sftk_FreeSession(session);
+ if (sftk_isTrue(key,CKA_SENSITIVE)) {
+ sftk_forceAttribute(key,CKA_ALWAYS_SENSITIVE,&cktrue,sizeof(CK_BBOOL));
+ }
+ if (!sftk_isTrue(key,CKA_EXTRACTABLE)) {
+ sftk_forceAttribute(key,CKA_NEVER_EXTRACTABLE,&cktrue,sizeof(CK_BBOOL));
+ }
+
+ *phKey = key->handle;
+ sftk_FreeObject(key);
+ return crv;
+}
+
+#define PAIRWISE_DIGEST_LENGTH SHA1_LENGTH /* 160-bits */
+#define PAIRWISE_MESSAGE_LENGTH 20 /* 160-bits */
+
+/*
+ * FIPS 140-2 pairwise consistency check utilized to validate key pair.
+ *
+ * This function returns
+ * CKR_OK if pairwise consistency check passed
+ * CKR_GENERAL_ERROR if pairwise consistency check failed
+ * other error codes if paiswise consistency check could not be
+ * performed, for example, CKR_HOST_MEMORY.
+ */
+static CK_RV
+sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession,
+ SFTKObject *publicKey, SFTKObject *privateKey, CK_KEY_TYPE keyType)
+{
+ /*
+ * Key type Mechanism type
+ * --------------------------------
+ * For encrypt/decrypt: CKK_RSA => CKM_RSA_PKCS
+ * others => CKM_INVALID_MECHANISM
+ *
+ * For sign/verify: CKK_RSA => CKM_RSA_PKCS
+ * CKK_DSA => CKM_DSA
+ * CKK_EC => CKM_ECDSA
+ * others => CKM_INVALID_MECHANISM
+ *
+ * None of these mechanisms has a parameter.
+ */
+ CK_MECHANISM mech = {0, NULL, 0};
+
+ CK_ULONG modulusLen;
+ PRBool isEncryptable = PR_FALSE;
+ PRBool canSignVerify = PR_FALSE;
+ PRBool isDerivable = PR_FALSE;
+ CK_RV crv;
+
+ /* Variables used for Encrypt/Decrypt functions. */
+ unsigned char *known_message = (unsigned char *)"Known Crypto Message";
+ unsigned char plaintext[PAIRWISE_MESSAGE_LENGTH];
+ CK_ULONG bytes_decrypted;
+ unsigned char *ciphertext;
+ unsigned char *text_compared;
+ CK_ULONG bytes_encrypted;
+ CK_ULONG bytes_compared;
+
+ /* Variables used for Signature/Verification functions. */
+ /* always uses SHA-1 digest */
+ unsigned char *known_digest = (unsigned char *)"Mozilla Rules World!";
+ unsigned char *signature;
+ CK_ULONG signature_length;
+
+ if (keyType == CKK_RSA) {
+ SFTKAttribute *attribute;
+
+ /* Get modulus length of private key. */
+ attribute = sftk_FindAttribute(privateKey, CKA_MODULUS);
+ if (attribute == NULL) {
+ return CKR_DEVICE_ERROR;
+ }
+ modulusLen = attribute->attrib.ulValueLen;
+ if (*(unsigned char *)attribute->attrib.pValue == 0) {
+ modulusLen--;
+ }
+ sftk_FreeAttribute(attribute);
+ }
+
+ /**************************************************/
+ /* Pairwise Consistency Check of Encrypt/Decrypt. */
+ /**************************************************/
+
+ isEncryptable = sftk_isTrue(privateKey, CKA_DECRYPT);
+
+ /*
+ * If the decryption attribute is set, attempt to encrypt
+ * with the public key and decrypt with the private key.
+ */
+ if (isEncryptable) {
+ if (keyType != CKK_RSA) {
+ return CKR_DEVICE_ERROR;
+ }
+ bytes_encrypted = modulusLen;
+ mech.mechanism = CKM_RSA_PKCS;
+
+ /* Allocate space for ciphertext. */
+ ciphertext = (unsigned char *) PORT_ZAlloc(bytes_encrypted);
+ if (ciphertext == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /* Prepare for encryption using the public key. */
+ crv = NSC_EncryptInit(hSession, &mech, publicKey->handle);
+ if (crv != CKR_OK) {
+ PORT_Free(ciphertext);
+ return crv;
+ }
+
+ /* Encrypt using the public key. */
+ crv = NSC_Encrypt(hSession,
+ known_message,
+ PAIRWISE_MESSAGE_LENGTH,
+ ciphertext,
+ &bytes_encrypted);
+ if (crv != CKR_OK) {
+ PORT_Free(ciphertext);
+ return crv;
+ }
+
+ /* Always use the smaller of these two values . . . */
+ bytes_compared = PR_MIN(bytes_encrypted, PAIRWISE_MESSAGE_LENGTH);
+
+ /*
+ * If there was a failure, the plaintext
+ * goes at the end, therefore . . .
+ */
+ text_compared = ciphertext + bytes_encrypted - bytes_compared;
+
+ /*
+ * Check to ensure that ciphertext does
+ * NOT EQUAL known input message text
+ * per FIPS PUB 140-2 directive.
+ */
+ if (PORT_Memcmp(text_compared, known_message,
+ bytes_compared) == 0) {
+ /* Set error to Invalid PRIVATE Key. */
+ PORT_SetError(SEC_ERROR_INVALID_KEY);
+ PORT_Free(ciphertext);
+ return CKR_GENERAL_ERROR;
+ }
+
+ /* Prepare for decryption using the private key. */
+ crv = NSC_DecryptInit(hSession, &mech, privateKey->handle);
+ if (crv != CKR_OK) {
+ PORT_Free(ciphertext);
+ return crv;
+ }
+
+ memset(plaintext, 0, PAIRWISE_MESSAGE_LENGTH);
+
+ /*
+ * Initialize bytes decrypted to be the
+ * expected PAIRWISE_MESSAGE_LENGTH.
+ */
+ bytes_decrypted = PAIRWISE_MESSAGE_LENGTH;
+
+ /*
+ * Decrypt using the private key.
+ * NOTE: No need to reset the
+ * value of bytes_encrypted.
+ */
+ crv = NSC_Decrypt(hSession,
+ ciphertext,
+ bytes_encrypted,
+ plaintext,
+ &bytes_decrypted);
+
+ /* Finished with ciphertext; free it. */
+ PORT_Free(ciphertext);
+
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /*
+ * Check to ensure that the output plaintext
+ * does EQUAL known input message text.
+ */
+ if ((bytes_decrypted != PAIRWISE_MESSAGE_LENGTH) ||
+ (PORT_Memcmp(plaintext, known_message,
+ PAIRWISE_MESSAGE_LENGTH) != 0)) {
+ /* Set error to Bad PUBLIC Key. */
+ PORT_SetError(SEC_ERROR_BAD_KEY);
+ return CKR_GENERAL_ERROR;
+ }
+ }
+
+ /**********************************************/
+ /* Pairwise Consistency Check of Sign/Verify. */
+ /**********************************************/
+
+ canSignVerify = sftk_isTrue(privateKey, CKA_SIGN);
+
+ if (canSignVerify) {
+ /* Determine length of signature. */
+ switch (keyType) {
+ case CKK_RSA:
+ signature_length = modulusLen;
+ mech.mechanism = CKM_RSA_PKCS;
+ break;
+ case CKK_DSA:
+ signature_length = DSA_SIGNATURE_LEN;
+ mech.mechanism = CKM_DSA;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ signature_length = MAX_ECKEY_LEN * 2;
+ mech.mechanism = CKM_ECDSA;
+ break;
+#endif
+ default:
+ return CKR_DEVICE_ERROR;
+ }
+
+ /* Allocate space for signature data. */
+ signature = (unsigned char *) PORT_ZAlloc(signature_length);
+ if (signature == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /* Sign the known hash using the private key. */
+ crv = NSC_SignInit(hSession, &mech, privateKey->handle);
+ if (crv != CKR_OK) {
+ PORT_Free(signature);
+ return crv;
+ }
+
+ crv = NSC_Sign(hSession,
+ known_digest,
+ PAIRWISE_DIGEST_LENGTH,
+ signature,
+ &signature_length);
+ if (crv != CKR_OK) {
+ PORT_Free(signature);
+ return crv;
+ }
+
+ /* Verify the known hash using the public key. */
+ crv = NSC_VerifyInit(hSession, &mech, publicKey->handle);
+ if (crv != CKR_OK) {
+ PORT_Free(signature);
+ return crv;
+ }
+
+ crv = NSC_Verify(hSession,
+ known_digest,
+ PAIRWISE_DIGEST_LENGTH,
+ signature,
+ signature_length);
+
+ /* Free signature data. */
+ PORT_Free(signature);
+
+ if ((crv == CKR_SIGNATURE_LEN_RANGE) ||
+ (crv == CKR_SIGNATURE_INVALID)) {
+ return CKR_GENERAL_ERROR;
+ }
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ }
+
+ /**********************************************/
+ /* Pairwise Consistency Check for Derivation */
+ /**********************************************/
+
+ isDerivable = sftk_isTrue(privateKey, CKA_DERIVE);
+
+ if (isDerivable) {
+ /*
+ * We are not doing consistency check for Diffie-Hellman Key -
+ * otherwise it would be here
+ * This is also true for Elliptic Curve Diffie-Hellman keys
+ * NOTE: EC keys are currently subjected to pairwise
+ * consistency check for signing/verification.
+ */
+ /*
+ * FIPS 140-2 had the following pairwise consistency test for
+ * public and private keys used for key agreement:
+ * If the keys are used to perform key agreement, then the
+ * cryptographic module shall create a second, compatible
+ * key pair. The cryptographic module shall perform both
+ * sides of the key agreement algorithm and shall compare
+ * the resulting shared values. If the shared values are
+ * not equal, the test shall fail.
+ * This test was removed in Change Notice 3.
+ */
+
+ }
+
+ return CKR_OK;
+}
+
+/* NSC_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects. */
+CK_RV NSC_GenerateKeyPair (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+ SFTKObject * publicKey,*privateKey;
+ SFTKSession * session;
+ CK_KEY_TYPE key_type;
+ CK_RV crv = CKR_OK;
+ CK_BBOOL cktrue = CK_TRUE;
+ SECStatus rv;
+ CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY;
+ CK_OBJECT_CLASS privClass = CKO_PRIVATE_KEY;
+ int i;
+ SFTKSlot * slot = sftk_SlotFromSessionHandle(hSession);
+ unsigned int bitSize;
+
+ /* RSA */
+ int public_modulus_bits = 0;
+ SECItem pubExp;
+ RSAPrivateKey * rsaPriv;
+
+ /* DSA */
+ PQGParams pqgParam;
+ DHParams dhParam;
+ DSAPrivateKey * dsaPriv;
+
+ /* Diffie Hellman */
+ int private_value_bits = 0;
+ DHPrivateKey * dhPriv;
+
+#ifdef NSS_ENABLE_ECC
+ /* Elliptic Curve Cryptography */
+ SECItem ecEncodedParams; /* DER Encoded parameters */
+ ECPrivateKey * ecPriv;
+ ECParams * ecParams;
+#endif /* NSS_ENABLE_ECC */
+
+ CHECK_FORK();
+
+ if (!slot) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * now lets create an object to hang the attributes off of
+ */
+ publicKey = sftk_NewObject(slot); /* fill in the handle later */
+ if (publicKey == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * load the template values into the publicKey
+ */
+ for (i=0; i < (int) ulPublicKeyAttributeCount; i++) {
+ if (pPublicKeyTemplate[i].type == CKA_MODULUS_BITS) {
+ public_modulus_bits = *(CK_ULONG *)pPublicKeyTemplate[i].pValue;
+ continue;
+ }
+
+ crv = sftk_AddAttributeType(publicKey,
+ sftk_attr_expand(&pPublicKeyTemplate[i]));
+ if (crv != CKR_OK) break;
+ }
+
+ if (crv != CKR_OK) {
+ sftk_FreeObject(publicKey);
+ return CKR_HOST_MEMORY;
+ }
+
+ privateKey = sftk_NewObject(slot); /* fill in the handle later */
+ if (privateKey == NULL) {
+ sftk_FreeObject(publicKey);
+ return CKR_HOST_MEMORY;
+ }
+ /*
+ * now load the private key template
+ */
+ for (i=0; i < (int) ulPrivateKeyAttributeCount; i++) {
+ if (pPrivateKeyTemplate[i].type == CKA_VALUE_BITS) {
+ private_value_bits = *(CK_ULONG *)pPrivateKeyTemplate[i].pValue;
+ continue;
+ }
+
+ crv = sftk_AddAttributeType(privateKey,
+ sftk_attr_expand(&pPrivateKeyTemplate[i]));
+ if (crv != CKR_OK) break;
+ }
+
+ if (crv != CKR_OK) {
+ sftk_FreeObject(publicKey);
+ sftk_FreeObject(privateKey);
+ return CKR_HOST_MEMORY;
+ }
+ sftk_DeleteAttributeType(privateKey,CKA_CLASS);
+ sftk_DeleteAttributeType(privateKey,CKA_KEY_TYPE);
+ sftk_DeleteAttributeType(privateKey,CKA_VALUE);
+ sftk_DeleteAttributeType(publicKey,CKA_CLASS);
+ sftk_DeleteAttributeType(publicKey,CKA_KEY_TYPE);
+ sftk_DeleteAttributeType(publicKey,CKA_VALUE);
+
+ /* Now Set up the parameters to generate the key (based on mechanism) */
+ switch (pMechanism->mechanism) {
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ /* format the keys */
+ sftk_DeleteAttributeType(publicKey,CKA_MODULUS);
+ sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+ sftk_DeleteAttributeType(privateKey,CKA_MODULUS);
+ sftk_DeleteAttributeType(privateKey,CKA_PRIVATE_EXPONENT);
+ sftk_DeleteAttributeType(privateKey,CKA_PUBLIC_EXPONENT);
+ sftk_DeleteAttributeType(privateKey,CKA_PRIME_1);
+ sftk_DeleteAttributeType(privateKey,CKA_PRIME_2);
+ sftk_DeleteAttributeType(privateKey,CKA_EXPONENT_1);
+ sftk_DeleteAttributeType(privateKey,CKA_EXPONENT_2);
+ sftk_DeleteAttributeType(privateKey,CKA_COEFFICIENT);
+ key_type = CKK_RSA;
+ if (public_modulus_bits == 0) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ }
+ if (public_modulus_bits < RSA_MIN_MODULUS_BITS) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+ if (public_modulus_bits % 2 != 0) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+
+ /* extract the exponent */
+ crv=sftk_Attribute2SSecItem(NULL,&pubExp,publicKey,CKA_PUBLIC_EXPONENT);
+ if (crv != CKR_OK) break;
+ bitSize = sftk_GetLengthInBits(pubExp.data, pubExp.len);
+ if (bitSize < 2) {
+ crv = CKR_ATTRIBUTE_VALUE_INVALID;
+ break;
+ }
+ crv = sftk_AddAttributeType(privateKey,CKA_PUBLIC_EXPONENT,
+ sftk_item_expand(&pubExp));
+ if (crv != CKR_OK) {
+ PORT_Free(pubExp.data);
+ break;
+ }
+
+ rsaPriv = RSA_NewKey(public_modulus_bits, &pubExp);
+ PORT_Free(pubExp.data);
+ if (rsaPriv == NULL) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+ /* now fill in the RSA dependent paramenters in the public key */
+ crv = sftk_AddAttributeType(publicKey,CKA_MODULUS,
+ sftk_item_expand(&rsaPriv->modulus));
+ if (crv != CKR_OK) goto kpg_done;
+ /* now fill in the RSA dependent paramenters in the private key */
+ crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+ sftk_item_expand(&rsaPriv->modulus));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_MODULUS,
+ sftk_item_expand(&rsaPriv->modulus));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_PRIVATE_EXPONENT,
+ sftk_item_expand(&rsaPriv->privateExponent));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_PRIME_1,
+ sftk_item_expand(&rsaPriv->prime1));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_PRIME_2,
+ sftk_item_expand(&rsaPriv->prime2));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_EXPONENT_1,
+ sftk_item_expand(&rsaPriv->exponent1));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_EXPONENT_2,
+ sftk_item_expand(&rsaPriv->exponent2));
+ if (crv != CKR_OK) goto kpg_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_COEFFICIENT,
+ sftk_item_expand(&rsaPriv->coefficient));
+kpg_done:
+ /* Should zeroize the contents first, since this func doesn't. */
+ PORT_FreeArena(rsaPriv->arena, PR_TRUE);
+ break;
+ case CKM_DSA_KEY_PAIR_GEN:
+ sftk_DeleteAttributeType(publicKey,CKA_VALUE);
+ sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+ sftk_DeleteAttributeType(privateKey,CKA_PRIME);
+ sftk_DeleteAttributeType(privateKey,CKA_SUBPRIME);
+ sftk_DeleteAttributeType(privateKey,CKA_BASE);
+ key_type = CKK_DSA;
+
+ /* extract the necessary paramters and copy them to the private key */
+ crv=sftk_Attribute2SSecItem(NULL,&pqgParam.prime,publicKey,CKA_PRIME);
+ if (crv != CKR_OK) break;
+ crv=sftk_Attribute2SSecItem(NULL,&pqgParam.subPrime,publicKey,
+ CKA_SUBPRIME);
+ if (crv != CKR_OK) {
+ PORT_Free(pqgParam.prime.data);
+ break;
+ }
+ crv=sftk_Attribute2SSecItem(NULL,&pqgParam.base,publicKey,CKA_BASE);
+ if (crv != CKR_OK) {
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ break;
+ }
+ crv = sftk_AddAttributeType(privateKey,CKA_PRIME,
+ sftk_item_expand(&pqgParam.prime));
+ if (crv != CKR_OK) {
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+ break;
+ }
+ crv = sftk_AddAttributeType(privateKey,CKA_SUBPRIME,
+ sftk_item_expand(&pqgParam.subPrime));
+ if (crv != CKR_OK) {
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+ break;
+ }
+ crv = sftk_AddAttributeType(privateKey,CKA_BASE,
+ sftk_item_expand(&pqgParam.base));
+ if (crv != CKR_OK) {
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+ break;
+ }
+
+ bitSize = sftk_GetLengthInBits(pqgParam.subPrime.data,
+ pqgParam.subPrime.len);
+ if (bitSize != DSA_Q_BITS) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+ break;
+ }
+ bitSize = sftk_GetLengthInBits(pqgParam.prime.data,pqgParam.prime.len);
+ if ((bitSize < DSA_MIN_P_BITS) || (bitSize > DSA_MAX_P_BITS)) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+ break;
+ }
+ bitSize = sftk_GetLengthInBits(pqgParam.base.data,pqgParam.base.len);
+ if ((bitSize < 1) || (bitSize > DSA_MAX_P_BITS)) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+ break;
+ }
+
+ /* Generate the key */
+ rv = DSA_NewKey(&pqgParam, &dsaPriv);
+
+ PORT_Free(pqgParam.prime.data);
+ PORT_Free(pqgParam.subPrime.data);
+ PORT_Free(pqgParam.base.data);
+
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+
+ /* store the generated key into the attributes */
+ crv = sftk_AddAttributeType(publicKey,CKA_VALUE,
+ sftk_item_expand(&dsaPriv->publicValue));
+ if (crv != CKR_OK) goto dsagn_done;
+
+ /* now fill in the RSA dependent paramenters in the private key */
+ crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+ sftk_item_expand(&dsaPriv->publicValue));
+ if (crv != CKR_OK) goto dsagn_done;
+ crv = sftk_AddAttributeType(privateKey,CKA_VALUE,
+ sftk_item_expand(&dsaPriv->privateValue));
+
+dsagn_done:
+ /* should zeroize, since this function doesn't. */
+ PORT_FreeArena(dsaPriv->params.arena, PR_TRUE);
+ break;
+
+ case CKM_DH_PKCS_KEY_PAIR_GEN:
+ sftk_DeleteAttributeType(privateKey,CKA_PRIME);
+ sftk_DeleteAttributeType(privateKey,CKA_BASE);
+ sftk_DeleteAttributeType(privateKey,CKA_VALUE);
+ sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+ key_type = CKK_DH;
+
+ /* extract the necessary parameters and copy them to private keys */
+ crv = sftk_Attribute2SSecItem(NULL, &dhParam.prime, publicKey,
+ CKA_PRIME);
+ if (crv != CKR_OK) break;
+ crv = sftk_Attribute2SSecItem(NULL, &dhParam.base, publicKey, CKA_BASE);
+ if (crv != CKR_OK) {
+ PORT_Free(dhParam.prime.data);
+ break;
+ }
+ crv = sftk_AddAttributeType(privateKey, CKA_PRIME,
+ sftk_item_expand(&dhParam.prime));
+ if (crv != CKR_OK) {
+ PORT_Free(dhParam.prime.data);
+ PORT_Free(dhParam.base.data);
+ break;
+ }
+ crv = sftk_AddAttributeType(privateKey, CKA_BASE,
+ sftk_item_expand(&dhParam.base));
+ if (crv != CKR_OK) {
+ PORT_Free(dhParam.prime.data);
+ PORT_Free(dhParam.base.data);
+ break;
+ }
+ bitSize = sftk_GetLengthInBits(dhParam.prime.data,dhParam.prime.len);
+ if ((bitSize < DH_MIN_P_BITS) || (bitSize > DH_MAX_P_BITS)) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ PORT_Free(dhParam.prime.data);
+ PORT_Free(dhParam.base.data);
+ break;
+ }
+ bitSize = sftk_GetLengthInBits(dhParam.base.data,dhParam.base.len);
+ if ((bitSize < 1) || (bitSize > DH_MAX_P_BITS)) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ PORT_Free(dhParam.prime.data);
+ PORT_Free(dhParam.base.data);
+ break;
+ }
+
+ rv = DH_NewKey(&dhParam, &dhPriv);
+ PORT_Free(dhParam.prime.data);
+ PORT_Free(dhParam.base.data);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+
+ crv=sftk_AddAttributeType(publicKey, CKA_VALUE,
+ sftk_item_expand(&dhPriv->publicValue));
+ if (crv != CKR_OK) goto dhgn_done;
+
+ crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+ sftk_item_expand(&dhPriv->publicValue));
+ if (crv != CKR_OK) goto dhgn_done;
+
+ crv=sftk_AddAttributeType(privateKey, CKA_VALUE,
+ sftk_item_expand(&dhPriv->privateValue));
+
+dhgn_done:
+ /* should zeroize, since this function doesn't. */
+ PORT_FreeArena(dhPriv->arena, PR_TRUE);
+ break;
+
+#ifdef NSS_ENABLE_ECC
+ case CKM_EC_KEY_PAIR_GEN:
+ sftk_DeleteAttributeType(privateKey,CKA_EC_PARAMS);
+ sftk_DeleteAttributeType(privateKey,CKA_VALUE);
+ sftk_DeleteAttributeType(privateKey,CKA_NETSCAPE_DB);
+ key_type = CKK_EC;
+
+ /* extract the necessary parameters and copy them to private keys */
+ crv = sftk_Attribute2SSecItem(NULL, &ecEncodedParams, publicKey,
+ CKA_EC_PARAMS);
+ if (crv != CKR_OK) break;
+
+ crv = sftk_AddAttributeType(privateKey, CKA_EC_PARAMS,
+ sftk_item_expand(&ecEncodedParams));
+ if (crv != CKR_OK) {
+ PORT_Free(ecEncodedParams.data);
+ break;
+ }
+
+ /* Decode ec params before calling EC_NewKey */
+ rv = EC_DecodeParams(&ecEncodedParams, &ecParams);
+ PORT_Free(ecEncodedParams.data);
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+ rv = EC_NewKey(ecParams, &ecPriv);
+ PORT_FreeArena(ecParams->arena, PR_TRUE);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+
+ crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT,
+ sftk_item_expand(&ecPriv->publicValue));
+ if (crv != CKR_OK) goto ecgn_done;
+
+ crv = sftk_AddAttributeType(privateKey, CKA_VALUE,
+ sftk_item_expand(&ecPriv->privateValue));
+ if (crv != CKR_OK) goto ecgn_done;
+
+ crv = sftk_AddAttributeType(privateKey,CKA_NETSCAPE_DB,
+ sftk_item_expand(&ecPriv->publicValue));
+ecgn_done:
+ /* should zeroize, since this function doesn't. */
+ PORT_FreeArena(ecPriv->ecParams.arena, PR_TRUE);
+ break;
+#endif /* NSS_ENABLE_ECC */
+
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ }
+
+ if (crv != CKR_OK) {
+ sftk_FreeObject(privateKey);
+ sftk_FreeObject(publicKey);
+ return crv;
+ }
+
+
+ /* Add the class, key_type The loop lets us check errors blow out
+ * on errors and clean up at the bottom */
+ session = NULL; /* make pedtantic happy... session cannot leave the*/
+ /* loop below NULL unless an error is set... */
+ do {
+ crv = sftk_AddAttributeType(privateKey,CKA_CLASS,&privClass,
+ sizeof(CK_OBJECT_CLASS));
+ if (crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(publicKey,CKA_CLASS,&pubClass,
+ sizeof(CK_OBJECT_CLASS));
+ if (crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(privateKey,CKA_KEY_TYPE,&key_type,
+ sizeof(CK_KEY_TYPE));
+ if (crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(publicKey,CKA_KEY_TYPE,&key_type,
+ sizeof(CK_KEY_TYPE));
+ if (crv != CKR_OK) break;
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) crv = CKR_SESSION_HANDLE_INVALID;
+ } while (0);
+
+ if (crv != CKR_OK) {
+ sftk_FreeObject(privateKey);
+ sftk_FreeObject(publicKey);
+ return crv;
+ }
+
+ /*
+ * handle the base object cleanup for the public Key
+ */
+ crv = sftk_handleObject(privateKey,session);
+ if (crv != CKR_OK) {
+ sftk_FreeSession(session);
+ sftk_FreeObject(privateKey);
+ sftk_FreeObject(publicKey);
+ return crv;
+ }
+
+ /*
+ * handle the base object cleanup for the private Key
+ * If we have any problems, we destroy the public Key we've
+ * created and linked.
+ */
+ crv = sftk_handleObject(publicKey,session);
+ sftk_FreeSession(session);
+ if (crv != CKR_OK) {
+ sftk_FreeObject(publicKey);
+ NSC_DestroyObject(hSession,privateKey->handle);
+ sftk_FreeObject(privateKey);
+ return crv;
+ }
+ if (sftk_isTrue(privateKey,CKA_SENSITIVE)) {
+ sftk_forceAttribute(privateKey,CKA_ALWAYS_SENSITIVE,
+ &cktrue,sizeof(CK_BBOOL));
+ }
+ if (sftk_isTrue(publicKey,CKA_SENSITIVE)) {
+ sftk_forceAttribute(publicKey,CKA_ALWAYS_SENSITIVE,
+ &cktrue,sizeof(CK_BBOOL));
+ }
+ if (!sftk_isTrue(privateKey,CKA_EXTRACTABLE)) {
+ sftk_forceAttribute(privateKey,CKA_NEVER_EXTRACTABLE,
+ &cktrue,sizeof(CK_BBOOL));
+ }
+ if (!sftk_isTrue(publicKey,CKA_EXTRACTABLE)) {
+ sftk_forceAttribute(publicKey,CKA_NEVER_EXTRACTABLE,
+ &cktrue,sizeof(CK_BBOOL));
+ }
+
+ /* Perform FIPS 140-2 pairwise consistency check. */
+ crv = sftk_PairwiseConsistencyCheck(hSession,
+ publicKey, privateKey, key_type);
+ if (crv != CKR_OK) {
+ NSC_DestroyObject(hSession,publicKey->handle);
+ sftk_FreeObject(publicKey);
+ NSC_DestroyObject(hSession,privateKey->handle);
+ sftk_FreeObject(privateKey);
+ if (sftk_audit_enabled) {
+ char msg[128];
+ PR_snprintf(msg,sizeof msg,
+ "C_GenerateKeyPair(hSession=0x%08lX, "
+ "pMechanism->mechanism=0x%08lX)=0x%08lX "
+ "self-test: pair-wise consistency test failed",
+ (PRUint32)hSession,(PRUint32)pMechanism->mechanism,
+ (PRUint32)crv);
+ sftk_LogAuditMessage(NSS_AUDIT_ERROR, msg);
+ }
+ return crv;
+ }
+
+ *phPrivateKey = privateKey->handle;
+ *phPublicKey = publicKey->handle;
+ sftk_FreeObject(publicKey);
+ sftk_FreeObject(privateKey);
+
+ return CKR_OK;
+}
+
+static SECItem *sftk_PackagePrivateKey(SFTKObject *key, CK_RV *crvp)
+{
+ NSSLOWKEYPrivateKey *lk = NULL;
+ NSSLOWKEYPrivateKeyInfo *pki = NULL;
+ SFTKAttribute *attribute = NULL;
+ PLArenaPool *arena = NULL;
+ SECOidTag algorithm = SEC_OID_UNKNOWN;
+ void *dummy, *param = NULL;
+ SECStatus rv = SECSuccess;
+ SECItem *encodedKey = NULL;
+#ifdef NSS_ENABLE_ECC
+ SECItem *fordebug;
+ int savelen;
+#endif
+
+ if(!key) {
+ *crvp = CKR_KEY_HANDLE_INVALID; /* really can't happen */
+ return NULL;
+ }
+
+ attribute = sftk_FindAttribute(key, CKA_KEY_TYPE);
+ if(!attribute) {
+ *crvp = CKR_KEY_TYPE_INCONSISTENT;
+ return NULL;
+ }
+
+ lk = sftk_GetPrivKey(key, *(CK_KEY_TYPE *)attribute->attrib.pValue, crvp);
+ sftk_FreeAttribute(attribute);
+ if(!lk) {
+ return NULL;
+ }
+
+ arena = PORT_NewArena(2048); /* XXX different size? */
+ if(!arena) {
+ *crvp = CKR_HOST_MEMORY;
+ rv = SECFailure;
+ goto loser;
+ }
+
+ pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPrivateKeyInfo));
+ if(!pki) {
+ *crvp = CKR_HOST_MEMORY;
+ rv = SECFailure;
+ goto loser;
+ }
+ pki->arena = arena;
+
+ param = NULL;
+ switch(lk->keyType) {
+ case NSSLOWKEYRSAKey:
+ prepare_low_rsa_priv_key_for_asn1(lk);
+ dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+ nsslowkey_RSAPrivateKeyTemplate);
+ algorithm = SEC_OID_PKCS1_RSA_ENCRYPTION;
+ break;
+ case NSSLOWKEYDSAKey:
+ prepare_low_dsa_priv_key_export_for_asn1(lk);
+ dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+ nsslowkey_DSAPrivateKeyExportTemplate);
+ prepare_low_pqg_params_for_asn1(&lk->u.dsa.params);
+ param = SEC_ASN1EncodeItem(NULL, NULL, &(lk->u.dsa.params),
+ nsslowkey_PQGParamsTemplate);
+ algorithm = SEC_OID_ANSIX9_DSA_SIGNATURE;
+ break;
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ prepare_low_ec_priv_key_for_asn1(lk);
+ /* Public value is encoded as a bit string so adjust length
+ * to be in bits before ASN encoding and readjust
+ * immediately after.
+ *
+ * Since the SECG specification recommends not including the
+ * parameters as part of ECPrivateKey, we zero out the curveOID
+ * length before encoding and restore it later.
+ */
+ lk->u.ec.publicValue.len <<= 3;
+ savelen = lk->u.ec.ecParams.curveOID.len;
+ lk->u.ec.ecParams.curveOID.len = 0;
+ dummy = SEC_ASN1EncodeItem(arena, &pki->privateKey, lk,
+ nsslowkey_ECPrivateKeyTemplate);
+ lk->u.ec.ecParams.curveOID.len = savelen;
+ lk->u.ec.publicValue.len >>= 3;
+
+ fordebug = &pki->privateKey;
+ SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKey", lk->keyType,
+ fordebug);
+
+ param = SECITEM_DupItem(&lk->u.ec.ecParams.DEREncoding);
+
+ algorithm = SEC_OID_ANSIX962_EC_PUBLIC_KEY;
+ break;
+#endif /* NSS_ENABLE_ECC */
+ case NSSLOWKEYDHKey:
+ default:
+ dummy = NULL;
+ break;
+ }
+
+ if(!dummy || ((lk->keyType == NSSLOWKEYDSAKey) && !param)) {
+ *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
+ rv = SECFailure;
+ goto loser;
+ }
+
+ rv = SECOID_SetAlgorithmID(arena, &pki->algorithm, algorithm,
+ (SECItem*)param);
+ if(rv != SECSuccess) {
+ *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
+ rv = SECFailure;
+ goto loser;
+ }
+
+ dummy = SEC_ASN1EncodeInteger(arena, &pki->version,
+ NSSLOWKEY_PRIVATE_KEY_INFO_VERSION);
+ if(!dummy) {
+ *crvp = CKR_DEVICE_ERROR; /* should map NSS SECError */
+ rv = SECFailure;
+ goto loser;
+ }
+
+ encodedKey = SEC_ASN1EncodeItem(NULL, NULL, pki,
+ nsslowkey_PrivateKeyInfoTemplate);
+ *crvp = encodedKey ? CKR_OK : CKR_DEVICE_ERROR;
+
+#ifdef NSS_ENABLE_ECC
+ fordebug = encodedKey;
+ SEC_PRINT("sftk_PackagePrivateKey()", "PrivateKeyInfo", lk->keyType,
+ fordebug);
+#endif
+loser:
+ if(arena) {
+ PORT_FreeArena(arena, PR_TRUE);
+ }
+
+ if(lk && (lk != key->objectInfo)) {
+ nsslowkey_DestroyPrivateKey(lk);
+ }
+
+ if(param) {
+ SECITEM_ZfreeItem((SECItem*)param, PR_TRUE);
+ }
+
+ if(rv != SECSuccess) {
+ return NULL;
+ }
+
+ return encodedKey;
+}
+
+/* it doesn't matter yet, since we colapse error conditions in the
+ * level above, but we really should map those few key error differences */
+static CK_RV
+sftk_mapWrap(CK_RV crv)
+{
+ switch (crv) {
+ case CKR_ENCRYPTED_DATA_INVALID: crv = CKR_WRAPPED_KEY_INVALID; break;
+ }
+ return crv;
+}
+
+/* NSC_WrapKey wraps (i.e., encrypts) a key. */
+CK_RV NSC_WrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey,
+ CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen)
+{
+ SFTKSession *session;
+ SFTKAttribute *attribute;
+ SFTKObject *key;
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ key = sftk_ObjectFromHandle(hKey,session);
+ sftk_FreeSession(session);
+ if (key == NULL) {
+ return CKR_KEY_HANDLE_INVALID;
+ }
+
+ switch(key->objclass) {
+ case CKO_SECRET_KEY:
+ {
+ SFTKSessionContext *context = NULL;
+ SECItem pText;
+
+ attribute = sftk_FindAttribute(key,CKA_VALUE);
+
+ if (attribute == NULL) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey,
+ CKA_WRAP, SFTK_ENCRYPT, PR_TRUE);
+ if (crv != CKR_OK) {
+ sftk_FreeAttribute(attribute);
+ break;
+ }
+
+ pText.type = siBuffer;
+ pText.data = (unsigned char *)attribute->attrib.pValue;
+ pText.len = attribute->attrib.ulValueLen;
+
+ /* Find out if this is a block cipher. */
+ crv = sftk_GetContext(hSession,&context,SFTK_ENCRYPT,PR_FALSE,NULL);
+ if (crv != CKR_OK || !context)
+ break;
+ if (context->blockSize > 1) {
+ unsigned int remainder = pText.len % context->blockSize;
+ if (!context->doPad && remainder) {
+ /* When wrapping secret keys with unpadded block ciphers,
+ ** the keys are zero padded, if necessary, to fill out
+ ** a full block.
+ */
+ pText.len += context->blockSize - remainder;
+ pText.data = PORT_ZAlloc(pText.len);
+ if (pText.data)
+ memcpy(pText.data, attribute->attrib.pValue,
+ attribute->attrib.ulValueLen);
+ else {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ }
+ }
+
+ crv = NSC_Encrypt(hSession, (CK_BYTE_PTR)pText.data,
+ pText.len, pWrappedKey, pulWrappedKeyLen);
+ /* always force a finalize, both on errors and when
+ * we are just getting the size */
+ if (crv != CKR_OK || pWrappedKey == NULL) {
+ CK_RV lcrv ;
+ lcrv = sftk_GetContext(hSession,&context,
+ SFTK_ENCRYPT,PR_FALSE,NULL);
+ sftk_SetContextByType(session, SFTK_ENCRYPT, NULL);
+ if (lcrv == CKR_OK && context) {
+ sftk_FreeContext(context);
+ }
+ }
+
+ if (pText.data != (unsigned char *)attribute->attrib.pValue)
+ PORT_ZFree(pText.data, pText.len);
+ sftk_FreeAttribute(attribute);
+ break;
+ }
+
+ case CKO_PRIVATE_KEY:
+ {
+ SECItem *bpki = sftk_PackagePrivateKey(key, &crv);
+ SFTKSessionContext *context = NULL;
+
+ if(!bpki) {
+ break;
+ }
+
+ crv = sftk_CryptInit(hSession, pMechanism, hWrappingKey,
+ CKA_WRAP, SFTK_ENCRYPT, PR_TRUE);
+ if(crv != CKR_OK) {
+ SECITEM_ZfreeItem(bpki, PR_TRUE);
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+
+ crv = NSC_Encrypt(hSession, bpki->data, bpki->len,
+ pWrappedKey, pulWrappedKeyLen);
+ /* always force a finalize */
+ if (crv != CKR_OK || pWrappedKey == NULL) {
+ CK_RV lcrv ;
+ lcrv = sftk_GetContext(hSession,&context,
+ SFTK_ENCRYPT,PR_FALSE,NULL);
+ sftk_SetContextByType(session, SFTK_ENCRYPT, NULL);
+ if (lcrv == CKR_OK && context) {
+ sftk_FreeContext(context);
+ }
+ }
+ SECITEM_ZfreeItem(bpki, PR_TRUE);
+ break;
+ }
+
+ default:
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ sftk_FreeObject(key);
+
+ return sftk_mapWrap(crv);
+}
+
+/*
+ * import a pprivate key info into the desired slot
+ */
+static SECStatus
+sftk_unwrapPrivateKey(SFTKObject *key, SECItem *bpki)
+{
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_KEY_TYPE keyType = CKK_RSA;
+ SECStatus rv = SECFailure;
+ const SEC_ASN1Template *keyTemplate, *paramTemplate;
+ void *paramDest = NULL;
+ PLArenaPool *arena;
+ NSSLOWKEYPrivateKey *lpk = NULL;
+ NSSLOWKEYPrivateKeyInfo *pki = NULL;
+ CK_RV crv = CKR_KEY_TYPE_INCONSISTENT;
+
+ arena = PORT_NewArena(2048);
+ if(!arena) {
+ return SECFailure;
+ }
+
+ pki = (NSSLOWKEYPrivateKeyInfo*)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPrivateKeyInfo));
+ if(!pki) {
+ PORT_FreeArena(arena, PR_FALSE);
+ return SECFailure;
+ }
+
+ if(SEC_ASN1DecodeItem(arena, pki, nsslowkey_PrivateKeyInfoTemplate, bpki)
+ != SECSuccess) {
+ PORT_FreeArena(arena, PR_TRUE);
+ return SECFailure;
+ }
+
+ lpk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(arena,
+ sizeof(NSSLOWKEYPrivateKey));
+ if(lpk == NULL) {
+ goto loser;
+ }
+ lpk->arena = arena;
+
+ switch(SECOID_GetAlgorithmTag(&pki->algorithm)) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ keyTemplate = nsslowkey_RSAPrivateKeyTemplate;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ lpk->keyType = NSSLOWKEYRSAKey;
+ prepare_low_rsa_priv_key_for_asn1(lpk);
+ break;
+ case SEC_OID_ANSIX9_DSA_SIGNATURE:
+ keyTemplate = nsslowkey_DSAPrivateKeyExportTemplate;
+ paramTemplate = nsslowkey_PQGParamsTemplate;
+ paramDest = &(lpk->u.dsa.params);
+ lpk->keyType = NSSLOWKEYDSAKey;
+ prepare_low_dsa_priv_key_export_for_asn1(lpk);
+ prepare_low_pqg_params_for_asn1(&lpk->u.dsa.params);
+ break;
+ /* case NSSLOWKEYDHKey: */
+#ifdef NSS_ENABLE_ECC
+ case SEC_OID_ANSIX962_EC_PUBLIC_KEY:
+ keyTemplate = nsslowkey_ECPrivateKeyTemplate;
+ paramTemplate = NULL;
+ paramDest = &(lpk->u.ec.ecParams.DEREncoding);
+ lpk->keyType = NSSLOWKEYECKey;
+ prepare_low_ec_priv_key_for_asn1(lpk);
+ prepare_low_ecparams_for_asn1(&lpk->u.ec.ecParams);
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ keyTemplate = NULL;
+ paramTemplate = NULL;
+ paramDest = NULL;
+ break;
+ }
+
+ if(!keyTemplate) {
+ goto loser;
+ }
+
+ /* decode the private key and any algorithm parameters */
+ rv = SEC_QuickDERDecodeItem(arena, lpk, keyTemplate, &pki->privateKey);
+
+#ifdef NSS_ENABLE_ECC
+ if (lpk->keyType == NSSLOWKEYECKey) {
+ /* convert length in bits to length in bytes */
+ lpk->u.ec.publicValue.len >>= 3;
+ rv = SECITEM_CopyItem(arena,
+ &(lpk->u.ec.ecParams.DEREncoding),
+ &(pki->algorithm.parameters));
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+ }
+#endif /* NSS_ENABLE_ECC */
+
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+ if(paramDest && paramTemplate) {
+ rv = SEC_QuickDERDecodeItem(arena, paramDest, paramTemplate,
+ &(pki->algorithm.parameters));
+ if(rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ rv = SECFailure;
+
+ switch (lpk->keyType) {
+ case NSSLOWKEYRSAKey:
+ keyType = CKK_RSA;
+ if(sftk_hasAttribute(key, CKA_NETSCAPE_DB)) {
+ sftk_DeleteAttributeType(key, CKA_NETSCAPE_DB);
+ }
+ crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType,
+ sizeof(keyType));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_UNWRAP, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_DECRYPT, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_MODULUS,
+ sftk_item_expand(&lpk->u.rsa.modulus));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_PUBLIC_EXPONENT,
+ sftk_item_expand(&lpk->u.rsa.publicExponent));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_PRIVATE_EXPONENT,
+ sftk_item_expand(&lpk->u.rsa.privateExponent));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_PRIME_1,
+ sftk_item_expand(&lpk->u.rsa.prime1));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_PRIME_2,
+ sftk_item_expand(&lpk->u.rsa.prime2));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_EXPONENT_1,
+ sftk_item_expand(&lpk->u.rsa.exponent1));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_EXPONENT_2,
+ sftk_item_expand(&lpk->u.rsa.exponent2));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_COEFFICIENT,
+ sftk_item_expand(&lpk->u.rsa.coefficient));
+ break;
+ case NSSLOWKEYDSAKey:
+ keyType = CKK_DSA;
+ crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK :
+ CKR_KEY_TYPE_INCONSISTENT;
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType,
+ sizeof(keyType));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_PRIME,
+ sftk_item_expand(&lpk->u.dsa.params.prime));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SUBPRIME,
+ sftk_item_expand(&lpk->u.dsa.params.subPrime));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_BASE,
+ sftk_item_expand(&lpk->u.dsa.params.base));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_VALUE,
+ sftk_item_expand(&lpk->u.dsa.privateValue));
+ if(crv != CKR_OK) break;
+ break;
+#ifdef notdef
+ case NSSLOWKEYDHKey:
+ template = dhTemplate;
+ templateCount = sizeof(dhTemplate)/sizeof(CK_ATTRIBUTE);
+ keyType = CKK_DH;
+ break;
+#endif
+ /* what about fortezza??? */
+#ifdef NSS_ENABLE_ECC
+ case NSSLOWKEYECKey:
+ keyType = CKK_EC;
+ crv = (sftk_hasAttribute(key, CKA_NETSCAPE_DB)) ? CKR_OK :
+ CKR_KEY_TYPE_INCONSISTENT;
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &keyType,
+ sizeof(keyType));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_SIGN_RECOVER, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_DERIVE, &cktrue,
+ sizeof(CK_BBOOL));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_EC_PARAMS,
+ sftk_item_expand(&lpk->u.ec.ecParams.DEREncoding));
+ if(crv != CKR_OK) break;
+ crv = sftk_AddAttributeType(key, CKA_VALUE,
+ sftk_item_expand(&lpk->u.ec.privateValue));
+ if(crv != CKR_OK) break;
+ /* XXX Do we need to decode the EC Params here ?? */
+ break;
+#endif /* NSS_ENABLE_ECC */
+ default:
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+
+loser:
+ if(lpk) {
+ nsslowkey_DestroyPrivateKey(lpk);
+ }
+
+ if(crv != CKR_OK) {
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+
+/* NSC_UnwrapKey unwraps (decrypts) a wrapped key, creating a new key object. */
+CK_RV NSC_UnwrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ SFTKObject *key = NULL;
+ SFTKSession *session;
+ CK_ULONG key_length = 0;
+ unsigned char * buf = NULL;
+ CK_RV crv = CKR_OK;
+ int i;
+ CK_ULONG bsize = ulWrappedKeyLen;
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession);
+ SECItem bpki;
+ CK_OBJECT_CLASS target_type = CKO_SECRET_KEY;
+
+ CHECK_FORK();
+
+ if (!slot) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * now lets create an object to hang the attributes off of
+ */
+ key = sftk_NewObject(slot); /* fill in the handle later */
+ if (key == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * load the template values into the object
+ */
+ for (i=0; i < (int) ulAttributeCount; i++) {
+ if (pTemplate[i].type == CKA_VALUE_LEN) {
+ key_length = *(CK_ULONG *)pTemplate[i].pValue;
+ continue;
+ }
+ if (pTemplate[i].type == CKA_CLASS) {
+ target_type = *(CK_OBJECT_CLASS *)pTemplate[i].pValue;
+ }
+ crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i]));
+ if (crv != CKR_OK) break;
+ }
+ if (crv != CKR_OK) {
+ sftk_FreeObject(key);
+ return crv;
+ }
+
+ crv = sftk_CryptInit(hSession,pMechanism,hUnwrappingKey,CKA_UNWRAP,
+ SFTK_DECRYPT, PR_FALSE);
+ if (crv != CKR_OK) {
+ sftk_FreeObject(key);
+ return sftk_mapWrap(crv);
+ }
+
+ /* allocate the buffer to decrypt into
+ * this assumes the unwrapped key is never larger than the
+ * wrapped key. For all the mechanisms we support this is true */
+ buf = (unsigned char *)PORT_Alloc( ulWrappedKeyLen);
+ bsize = ulWrappedKeyLen;
+
+ crv = NSC_Decrypt(hSession, pWrappedKey, ulWrappedKeyLen, buf, &bsize);
+ if (crv != CKR_OK) {
+ sftk_FreeObject(key);
+ PORT_Free(buf);
+ return sftk_mapWrap(crv);
+ }
+
+ switch(target_type) {
+ case CKO_SECRET_KEY:
+ if (!sftk_hasAttribute(key,CKA_KEY_TYPE)) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ }
+
+ if (key_length == 0 || key_length > bsize) {
+ key_length = bsize;
+ }
+ if (key_length > MAX_KEY_LEN) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+
+ /* add the value */
+ crv = sftk_AddAttributeType(key,CKA_VALUE,buf,key_length);
+ break;
+ case CKO_PRIVATE_KEY:
+ bpki.data = (unsigned char *)buf;
+ bpki.len = bsize;
+ crv = CKR_OK;
+ if(sftk_unwrapPrivateKey(key, &bpki) != SECSuccess) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ }
+ break;
+ default:
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+
+ PORT_ZFree(buf, bsize);
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+ /* get the session */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ sftk_FreeObject(key);
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /*
+ * handle the base object stuff
+ */
+ crv = sftk_handleObject(key,session);
+ *phKey = key->handle;
+ sftk_FreeSession(session);
+ sftk_FreeObject(key);
+
+ return crv;
+
+}
+
+/*
+ * The SSL key gen mechanism create's lots of keys. This function handles the
+ * details of each of these key creation.
+ */
+static CK_RV
+sftk_buildSSLKey(CK_SESSION_HANDLE hSession, SFTKObject *baseKey,
+ PRBool isMacKey, unsigned char *keyBlock, unsigned int keySize,
+ CK_OBJECT_HANDLE *keyHandle)
+{
+ SFTKObject *key;
+ SFTKSession *session;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_BBOOL ckfalse = CK_FALSE;
+ CK_RV crv = CKR_HOST_MEMORY;
+
+ /*
+ * now lets create an object to hang the attributes off of
+ */
+ *keyHandle = CK_INVALID_HANDLE;
+ key = sftk_NewObject(baseKey->slot);
+ if (key == NULL) return CKR_HOST_MEMORY;
+ sftk_narrowToSessionObject(key)->wasDerived = PR_TRUE;
+
+ crv = sftk_CopyObject(key,baseKey);
+ if (crv != CKR_OK) goto loser;
+ if (isMacKey) {
+ crv = sftk_forceAttribute(key,CKA_KEY_TYPE,&keyType,sizeof(keyType));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_ENCRYPT,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_DECRYPT,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_SIGN,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_WRAP,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ crv = sftk_forceAttribute(key,CKA_UNWRAP,&ckfalse,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) goto loser;
+ }
+ crv = sftk_forceAttribute(key,CKA_VALUE,keyBlock,keySize);
+ if (crv != CKR_OK) goto loser;
+
+ /* get the session */
+ crv = CKR_HOST_MEMORY;
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) { goto loser; }
+
+ crv = sftk_handleObject(key,session);
+ sftk_FreeSession(session);
+ *keyHandle = key->handle;
+loser:
+ if (key) sftk_FreeObject(key);
+ return crv;
+}
+
+/*
+ * if there is an error, we need to free the keys we already created in SSL
+ * This is the routine that will do it..
+ */
+static void
+sftk_freeSSLKeys(CK_SESSION_HANDLE session,
+ CK_SSL3_KEY_MAT_OUT *returnedMaterial )
+{
+ if (returnedMaterial->hClientMacSecret != CK_INVALID_HANDLE) {
+ NSC_DestroyObject(session,returnedMaterial->hClientMacSecret);
+ }
+ if (returnedMaterial->hServerMacSecret != CK_INVALID_HANDLE) {
+ NSC_DestroyObject(session, returnedMaterial->hServerMacSecret);
+ }
+ if (returnedMaterial->hClientKey != CK_INVALID_HANDLE) {
+ NSC_DestroyObject(session, returnedMaterial->hClientKey);
+ }
+ if (returnedMaterial->hServerKey != CK_INVALID_HANDLE) {
+ NSC_DestroyObject(session, returnedMaterial->hServerKey);
+ }
+}
+
+/*
+ * when deriving from sensitive and extractable keys, we need to preserve some
+ * of the semantics in the derived key. This helper routine maintains these
+ * semantics.
+ */
+static CK_RV
+sftk_DeriveSensitiveCheck(SFTKObject *baseKey,SFTKObject *destKey)
+{
+ PRBool hasSensitive;
+ PRBool sensitive = PR_FALSE;
+ PRBool hasExtractable;
+ PRBool extractable = PR_TRUE;
+ CK_RV crv = CKR_OK;
+ SFTKAttribute *att;
+
+ hasSensitive = PR_FALSE;
+ att = sftk_FindAttribute(destKey,CKA_SENSITIVE);
+ if (att) {
+ hasSensitive = PR_TRUE;
+ sensitive = (PRBool) *(CK_BBOOL *)att->attrib.pValue;
+ sftk_FreeAttribute(att);
+ }
+
+ hasExtractable = PR_FALSE;
+ att = sftk_FindAttribute(destKey,CKA_EXTRACTABLE);
+ if (att) {
+ hasExtractable = PR_TRUE;
+ extractable = (PRBool) *(CK_BBOOL *)att->attrib.pValue;
+ sftk_FreeAttribute(att);
+ }
+
+
+ /* don't make a key more accessible */
+ if (sftk_isTrue(baseKey,CKA_SENSITIVE) && hasSensitive &&
+ (sensitive == PR_FALSE)) {
+ return CKR_KEY_FUNCTION_NOT_PERMITTED;
+ }
+ if (!sftk_isTrue(baseKey,CKA_EXTRACTABLE) && hasExtractable &&
+ (extractable == PR_TRUE)) {
+ return CKR_KEY_FUNCTION_NOT_PERMITTED;
+ }
+
+ /* inherit parent's sensitivity */
+ if (!hasSensitive) {
+ att = sftk_FindAttribute(baseKey,CKA_SENSITIVE);
+ if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT;
+ crv = sftk_defaultAttribute(destKey,sftk_attr_expand(&att->attrib));
+ sftk_FreeAttribute(att);
+ if (crv != CKR_OK) return crv;
+ }
+ if (!hasExtractable) {
+ att = sftk_FindAttribute(baseKey,CKA_EXTRACTABLE);
+ if (att == NULL) return CKR_KEY_TYPE_INCONSISTENT;
+ crv = sftk_defaultAttribute(destKey,sftk_attr_expand(&att->attrib));
+ sftk_FreeAttribute(att);
+ if (crv != CKR_OK) return crv;
+ }
+
+ /* we should inherit the parent's always extractable/ never sensitive info,
+ * but handleObject always forces this attributes, so we would need to do
+ * something special. */
+ return CKR_OK;
+}
+
+/*
+ * make known fixed PKCS #11 key types to their sizes in bytes
+ */
+unsigned long
+sftk_MapKeySize(CK_KEY_TYPE keyType)
+{
+ switch (keyType) {
+ case CKK_CDMF:
+ return 8;
+ case CKK_DES:
+ return 8;
+ case CKK_DES2:
+ return 16;
+ case CKK_DES3:
+ return 24;
+ /* IDEA and CAST need to be added */
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * SSL Key generation given pre master secret
+ */
+#define NUM_MIXERS 9
+static const char * const mixers[NUM_MIXERS] = {
+ "A",
+ "BB",
+ "CCC",
+ "DDDD",
+ "EEEEE",
+ "FFFFFF",
+ "GGGGGGG",
+ "HHHHHHHH",
+ "IIIIIIIII" };
+#define SSL3_PMS_LENGTH 48
+#define SSL3_MASTER_SECRET_LENGTH 48
+#define SSL3_RANDOM_LENGTH 32
+
+
+/* NSC_DeriveKey derives a key from a base key, creating a new key object. */
+CK_RV NSC_DeriveKey( CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ SFTKSession * session;
+ SFTKSlot * slot = sftk_SlotFromSessionHandle(hSession);
+ SFTKObject * key;
+ SFTKObject * sourceKey;
+ SFTKAttribute * att;
+ SFTKAttribute * att2;
+ unsigned char * buf;
+ SHA1Context * sha;
+ MD5Context * md5;
+ MD2Context * md2;
+ CK_ULONG macSize;
+ CK_ULONG tmpKeySize;
+ CK_ULONG IVSize;
+ CK_ULONG keySize = 0;
+ CK_RV crv = CKR_OK;
+ CK_BBOOL cktrue = CK_TRUE;
+ CK_KEY_TYPE keyType = CKK_GENERIC_SECRET;
+ CK_OBJECT_CLASS classType = CKO_SECRET_KEY;
+ CK_KEY_DERIVATION_STRING_DATA *stringPtr;
+ PRBool isTLS = PR_FALSE;
+ PRBool isDH = PR_FALSE;
+ SECStatus rv;
+ int i;
+ unsigned int outLen;
+ unsigned char sha_out[SHA1_LENGTH];
+ unsigned char key_block[NUM_MIXERS * MD5_LENGTH];
+ unsigned char key_block2[MD5_LENGTH];
+ PRBool isFIPS;
+
+ CHECK_FORK();
+
+ if (!slot) {
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+ /*
+ * now lets create an object to hang the attributes off of
+ */
+ if (phKey) *phKey = CK_INVALID_HANDLE;
+
+ key = sftk_NewObject(slot); /* fill in the handle later */
+ if (key == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ isFIPS = (slot->slotID == FIPS_SLOT_ID);
+
+ /*
+ * load the template values into the object
+ */
+ for (i=0; i < (int) ulAttributeCount; i++) {
+ crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i]));
+ if (crv != CKR_OK) break;
+
+ if (pTemplate[i].type == CKA_KEY_TYPE) {
+ keyType = *(CK_KEY_TYPE *)pTemplate[i].pValue;
+ }
+ if (pTemplate[i].type == CKA_VALUE_LEN) {
+ keySize = *(CK_ULONG *)pTemplate[i].pValue;
+ }
+ }
+ if (crv != CKR_OK) { sftk_FreeObject(key); return crv; }
+
+ if (keySize == 0) {
+ keySize = sftk_MapKeySize(keyType);
+ }
+
+ /* Derive can only create SECRET KEY's currently... */
+ classType = CKO_SECRET_KEY;
+ crv = sftk_forceAttribute (key,CKA_CLASS,&classType,sizeof(classType));
+ if (crv != CKR_OK) {
+ sftk_FreeObject(key);
+ return crv;
+ }
+
+ /* look up the base key we're deriving with */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ sftk_FreeObject(key);
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ sourceKey = sftk_ObjectFromHandle(hBaseKey,session);
+ sftk_FreeSession(session);
+ if (sourceKey == NULL) {
+ sftk_FreeObject(key);
+ return CKR_KEY_HANDLE_INVALID;
+ }
+
+ /* get the value of the base key */
+ att = sftk_FindAttribute(sourceKey,CKA_VALUE);
+ if (att == NULL) {
+ sftk_FreeObject(key);
+ sftk_FreeObject(sourceKey);
+ return CKR_KEY_HANDLE_INVALID;
+ }
+
+ switch (pMechanism->mechanism) {
+ /*
+ * generate the master secret
+ */
+ case CKM_TLS_MASTER_KEY_DERIVE:
+ case CKM_TLS_MASTER_KEY_DERIVE_DH:
+ isTLS = PR_TRUE;
+ /* fall thru */
+ case CKM_SSL3_MASTER_KEY_DERIVE:
+ case CKM_SSL3_MASTER_KEY_DERIVE_DH:
+ {
+ CK_SSL3_MASTER_KEY_DERIVE_PARAMS *ssl3_master;
+ SSL3RSAPreMasterSecret * rsa_pms;
+ unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2];
+
+ if ((pMechanism->mechanism == CKM_SSL3_MASTER_KEY_DERIVE_DH) ||
+ (pMechanism->mechanism == CKM_TLS_MASTER_KEY_DERIVE_DH))
+ isDH = PR_TRUE;
+
+ /* first do the consistancy checks */
+ if (!isDH && (att->attrib.ulValueLen != SSL3_PMS_LENGTH)) {
+ crv = CKR_KEY_TYPE_INCONSISTENT;
+ break;
+ }
+ att2 = sftk_FindAttribute(sourceKey,CKA_KEY_TYPE);
+ if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue !=
+ CKK_GENERIC_SECRET)) {
+ if (att2) sftk_FreeAttribute(att2);
+ crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+ break;
+ }
+ sftk_FreeAttribute(att2);
+ if (keyType != CKK_GENERIC_SECRET) {
+ crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+ break;
+ }
+ if ((keySize != 0) && (keySize != SSL3_MASTER_SECRET_LENGTH)) {
+ crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+ break;
+ }
+
+ /* finally do the key gen */
+ ssl3_master = (CK_SSL3_MASTER_KEY_DERIVE_PARAMS *)
+ pMechanism->pParameter;
+
+ PORT_Memcpy(crsrdata,
+ ssl3_master->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH);
+ PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH,
+ ssl3_master->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH);
+
+ if (ssl3_master->pVersion) {
+ SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key);
+ rsa_pms = (SSL3RSAPreMasterSecret *) att->attrib.pValue;
+ /* don't leak more key material then necessary for SSL to work */
+ if ((sessKey == NULL) || sessKey->wasDerived) {
+ ssl3_master->pVersion->major = 0xff;
+ ssl3_master->pVersion->minor = 0xff;
+ } else {
+ ssl3_master->pVersion->major = rsa_pms->client_version[0];
+ ssl3_master->pVersion->minor = rsa_pms->client_version[1];
+ }
+ }
+ if (ssl3_master->RandomInfo.ulClientRandomLen != SSL3_RANDOM_LENGTH) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+ if (ssl3_master->RandomInfo.ulServerRandomLen != SSL3_RANDOM_LENGTH) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+
+ if (isTLS) {
+ SECStatus status;
+ SECItem crsr = { siBuffer, NULL, 0 };
+ SECItem master = { siBuffer, NULL, 0 };
+ SECItem pms = { siBuffer, NULL, 0 };
+
+ crsr.data = crsrdata;
+ crsr.len = sizeof crsrdata;
+ master.data = key_block;
+ master.len = SSL3_MASTER_SECRET_LENGTH;
+ pms.data = (unsigned char*)att->attrib.pValue;
+ pms.len = att->attrib.ulValueLen;
+
+ status = TLS_PRF(&pms, "master secret", &crsr, &master, isFIPS);
+ if (status != SECSuccess) {
+ crv = CKR_FUNCTION_FAILED;
+ break;
+ }
+ } else {
+ /* now allocate the hash contexts */
+ md5 = MD5_NewContext();
+ if (md5 == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ sha = SHA1_NewContext();
+ if (sha == NULL) {
+ PORT_Free(md5);
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ for (i = 0; i < 3; i++) {
+ SHA1_Begin(sha);
+ SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i]));
+ SHA1_Update(sha, (const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ SHA1_Update(sha, crsrdata, sizeof crsrdata);
+ SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH);
+ PORT_Assert(outLen == SHA1_LENGTH);
+
+ MD5_Begin(md5);
+ MD5_Update(md5, (const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ MD5_Update(md5, sha_out, outLen);
+ MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH);
+ PORT_Assert(outLen == MD5_LENGTH);
+ }
+ PORT_Free(md5);
+ PORT_Free(sha);
+ }
+
+ /* store the results */
+ crv = sftk_forceAttribute
+ (key,CKA_VALUE,key_block,SSL3_MASTER_SECRET_LENGTH);
+ if (crv != CKR_OK) break;
+ keyType = CKK_GENERIC_SECRET;
+ crv = sftk_forceAttribute (key,CKA_KEY_TYPE,&keyType,sizeof(keyType));
+ if (isTLS) {
+ /* TLS's master secret is used to "sign" finished msgs with PRF. */
+ /* XXX This seems like a hack. But SFTK_Derive only accepts
+ * one "operation" argument. */
+ crv = sftk_forceAttribute(key,CKA_SIGN, &cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) break;
+ crv = sftk_forceAttribute(key,CKA_VERIFY,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) break;
+ /* While we're here, we might as well force this, too. */
+ crv = sftk_forceAttribute(key,CKA_DERIVE,&cktrue,sizeof(CK_BBOOL));
+ if (crv != CKR_OK) break;
+ }
+ break;
+ }
+
+ case CKM_TLS_KEY_AND_MAC_DERIVE:
+ isTLS = PR_TRUE;
+ /* fall thru */
+ case CKM_SSL3_KEY_AND_MAC_DERIVE:
+ {
+ CK_SSL3_KEY_MAT_PARAMS *ssl3_keys;
+ CK_SSL3_KEY_MAT_OUT * ssl3_keys_out;
+ CK_ULONG effKeySize;
+ unsigned int block_needed;
+ unsigned char srcrdata[SSL3_RANDOM_LENGTH * 2];
+ unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2];
+
+ crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+ if (crv != CKR_OK) break;
+
+ if (att->attrib.ulValueLen != SSL3_MASTER_SECRET_LENGTH) {
+ crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+ break;
+ }
+ att2 = sftk_FindAttribute(sourceKey,CKA_KEY_TYPE);
+ if ((att2 == NULL) || (*(CK_KEY_TYPE *)att2->attrib.pValue !=
+ CKK_GENERIC_SECRET)) {
+ if (att2) sftk_FreeAttribute(att2);
+ crv = CKR_KEY_FUNCTION_NOT_PERMITTED;
+ break;
+ }
+ sftk_FreeAttribute(att2);
+ md5 = MD5_NewContext();
+ if (md5 == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ sha = SHA1_NewContext();
+ if (sha == NULL) {
+ PORT_Free(md5);
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ ssl3_keys = (CK_SSL3_KEY_MAT_PARAMS *) pMechanism->pParameter;
+
+ PORT_Memcpy(srcrdata,
+ ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH);
+ PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH,
+ ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH);
+
+ PORT_Memcpy(crsrdata,
+ ssl3_keys->RandomInfo.pClientRandom, SSL3_RANDOM_LENGTH);
+ PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH,
+ ssl3_keys->RandomInfo.pServerRandom, SSL3_RANDOM_LENGTH);
+
+ /*
+ * clear out our returned keys so we can recover on failure
+ */
+ ssl3_keys_out = ssl3_keys->pReturnedKeyMaterial;
+ ssl3_keys_out->hClientMacSecret = CK_INVALID_HANDLE;
+ ssl3_keys_out->hServerMacSecret = CK_INVALID_HANDLE;
+ ssl3_keys_out->hClientKey = CK_INVALID_HANDLE;
+ ssl3_keys_out->hServerKey = CK_INVALID_HANDLE;
+
+ /*
+ * How much key material do we need?
+ */
+ macSize = ssl3_keys->ulMacSizeInBits/8;
+ effKeySize = ssl3_keys->ulKeySizeInBits/8;
+ IVSize = ssl3_keys->ulIVSizeInBits/8;
+ if (keySize == 0) {
+ effKeySize = keySize;
+ }
+ block_needed = 2 * (macSize + effKeySize +
+ ((!ssl3_keys->bIsExport) * IVSize));
+ PORT_Assert(block_needed <= sizeof key_block);
+ if (block_needed > sizeof key_block)
+ block_needed = sizeof key_block;
+
+ /*
+ * generate the key material: This looks amazingly similar to the
+ * PMS code, and is clearly crying out for a function to provide it.
+ */
+ if (isTLS) {
+ SECStatus status;
+ SECItem srcr = { siBuffer, NULL, 0 };
+ SECItem keyblk = { siBuffer, NULL, 0 };
+ SECItem master = { siBuffer, NULL, 0 };
+
+ srcr.data = srcrdata;
+ srcr.len = sizeof srcrdata;
+ keyblk.data = key_block;
+ keyblk.len = block_needed;
+ master.data = (unsigned char*)att->attrib.pValue;
+ master.len = att->attrib.ulValueLen;
+
+ status = TLS_PRF(&master, "key expansion", &srcr, &keyblk,
+ isFIPS);
+ if (status != SECSuccess) {
+ goto key_and_mac_derive_fail;
+ }
+ } else {
+ unsigned int block_bytes = 0;
+ /* key_block =
+ * MD5(master_secret + SHA('A' + master_secret +
+ * ServerHello.random + ClientHello.random)) +
+ * MD5(master_secret + SHA('BB' + master_secret +
+ * ServerHello.random + ClientHello.random)) +
+ * MD5(master_secret + SHA('CCC' + master_secret +
+ * ServerHello.random + ClientHello.random)) +
+ * [...];
+ */
+ for (i = 0; i < NUM_MIXERS && block_bytes < block_needed; i++) {
+ SHA1_Begin(sha);
+ SHA1_Update(sha, (unsigned char*) mixers[i], strlen(mixers[i]));
+ SHA1_Update(sha, (const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ SHA1_Update(sha, srcrdata, sizeof srcrdata);
+ SHA1_End(sha, sha_out, &outLen, SHA1_LENGTH);
+ PORT_Assert(outLen == SHA1_LENGTH);
+ MD5_Begin(md5);
+ MD5_Update(md5, (const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ MD5_Update(md5, sha_out, outLen);
+ MD5_End(md5, &key_block[i*MD5_LENGTH], &outLen, MD5_LENGTH);
+ PORT_Assert(outLen == MD5_LENGTH);
+ block_bytes += outLen;
+ }
+ }
+
+ /*
+ * Put the key material where it goes.
+ */
+ i = 0; /* now shows how much consumed */
+
+ /*
+ * The key_block is partitioned as follows:
+ * client_write_MAC_secret[CipherSpec.hash_size]
+ */
+ crv = sftk_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize,
+ &ssl3_keys_out->hClientMacSecret);
+ if (crv != CKR_OK)
+ goto key_and_mac_derive_fail;
+
+ i += macSize;
+
+ /*
+ * server_write_MAC_secret[CipherSpec.hash_size]
+ */
+ crv = sftk_buildSSLKey(hSession,key,PR_TRUE,&key_block[i],macSize,
+ &ssl3_keys_out->hServerMacSecret);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+ i += macSize;
+
+ if (keySize) {
+ if (!ssl3_keys->bIsExport) {
+ /*
+ ** Generate Domestic write keys and IVs.
+ ** client_write_key[CipherSpec.key_material]
+ */
+ crv = sftk_buildSSLKey(hSession,key,PR_FALSE,&key_block[i],
+ keySize, &ssl3_keys_out->hClientKey);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+ i += keySize;
+
+ /*
+ ** server_write_key[CipherSpec.key_material]
+ */
+ crv = sftk_buildSSLKey(hSession,key,PR_FALSE,&key_block[i],
+ keySize, &ssl3_keys_out->hServerKey);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+ i += keySize;
+
+ /*
+ ** client_write_IV[CipherSpec.IV_size]
+ */
+ if (IVSize > 0) {
+ PORT_Memcpy(ssl3_keys_out->pIVClient,
+ &key_block[i], IVSize);
+ i += IVSize;
+ }
+
+ /*
+ ** server_write_IV[CipherSpec.IV_size]
+ */
+ if (IVSize > 0) {
+ PORT_Memcpy(ssl3_keys_out->pIVServer,
+ &key_block[i], IVSize);
+ i += IVSize;
+ }
+ PORT_Assert(i <= sizeof key_block);
+
+ } else if (!isTLS) {
+
+ /*
+ ** Generate SSL3 Export write keys and IVs.
+ ** client_write_key[CipherSpec.key_material]
+ ** final_client_write_key = MD5(client_write_key +
+ ** ClientHello.random + ServerHello.random);
+ */
+ MD5_Begin(md5);
+ MD5_Update(md5, &key_block[i], effKeySize);
+ MD5_Update(md5, crsrdata, sizeof crsrdata);
+ MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+ i += effKeySize;
+ crv = sftk_buildSSLKey(hSession,key,PR_FALSE,key_block2,
+ keySize,&ssl3_keys_out->hClientKey);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+
+ /*
+ ** server_write_key[CipherSpec.key_material]
+ ** final_server_write_key = MD5(server_write_key +
+ ** ServerHello.random + ClientHello.random);
+ */
+ MD5_Begin(md5);
+ MD5_Update(md5, &key_block[i], effKeySize);
+ MD5_Update(md5, srcrdata, sizeof srcrdata);
+ MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+ i += effKeySize;
+ crv = sftk_buildSSLKey(hSession,key,PR_FALSE,key_block2,
+ keySize,&ssl3_keys_out->hServerKey);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+
+ /*
+ ** client_write_IV =
+ ** MD5(ClientHello.random + ServerHello.random);
+ */
+ MD5_Begin(md5);
+ MD5_Update(md5, crsrdata, sizeof crsrdata);
+ MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+ PORT_Memcpy(ssl3_keys_out->pIVClient, key_block2, IVSize);
+
+ /*
+ ** server_write_IV =
+ ** MD5(ServerHello.random + ClientHello.random);
+ */
+ MD5_Begin(md5);
+ MD5_Update(md5, srcrdata, sizeof srcrdata);
+ MD5_End(md5, key_block2, &outLen, MD5_LENGTH);
+ PORT_Memcpy(ssl3_keys_out->pIVServer, key_block2, IVSize);
+
+ } else {
+
+ /*
+ ** Generate TLS Export write keys and IVs.
+ */
+ SECStatus status;
+ SECItem secret = { siBuffer, NULL, 0 };
+ SECItem crsr = { siBuffer, NULL, 0 };
+ SECItem keyblk = { siBuffer, NULL, 0 };
+
+ /*
+ ** client_write_key[CipherSpec.key_material]
+ ** final_client_write_key = PRF(client_write_key,
+ ** "client write key",
+ ** client_random + server_random);
+ */
+ secret.data = &key_block[i];
+ secret.len = effKeySize;
+ i += effKeySize;
+ crsr.data = crsrdata;
+ crsr.len = sizeof crsrdata;
+ keyblk.data = key_block2;
+ keyblk.len = sizeof key_block2;
+ status = TLS_PRF(&secret, "client write key", &crsr, &keyblk,
+ isFIPS);
+ if (status != SECSuccess) {
+ goto key_and_mac_derive_fail;
+ }
+ crv = sftk_buildSSLKey(hSession, key, PR_FALSE, key_block2,
+ keySize, &ssl3_keys_out->hClientKey);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+
+ /*
+ ** server_write_key[CipherSpec.key_material]
+ ** final_server_write_key = PRF(server_write_key,
+ ** "server write key",
+ ** client_random + server_random);
+ */
+ secret.data = &key_block[i];
+ secret.len = effKeySize;
+ i += effKeySize;
+ keyblk.data = key_block2;
+ keyblk.len = sizeof key_block2;
+ status = TLS_PRF(&secret, "server write key", &crsr, &keyblk,
+ isFIPS);
+ if (status != SECSuccess) {
+ goto key_and_mac_derive_fail;
+ }
+ crv = sftk_buildSSLKey(hSession, key, PR_FALSE, key_block2,
+ keySize, &ssl3_keys_out->hServerKey);
+ if (crv != CKR_OK) {
+ goto key_and_mac_derive_fail;
+ }
+
+ /*
+ ** iv_block = PRF("", "IV block",
+ ** client_random + server_random);
+ ** client_write_IV[SecurityParameters.IV_size]
+ ** server_write_IV[SecurityParameters.IV_size]
+ */
+ if (IVSize) {
+ secret.data = NULL;
+ secret.len = 0;
+ keyblk.data = &key_block[i];
+ keyblk.len = 2 * IVSize;
+ status = TLS_PRF(&secret, "IV block", &crsr, &keyblk,
+ isFIPS);
+ if (status != SECSuccess) {
+ goto key_and_mac_derive_fail;
+ }
+ PORT_Memcpy(ssl3_keys_out->pIVClient, keyblk.data, IVSize);
+ PORT_Memcpy(ssl3_keys_out->pIVServer, keyblk.data + IVSize,
+ IVSize);
+ }
+ }
+ }
+
+ crv = CKR_OK;
+
+ if (0) {
+key_and_mac_derive_fail:
+ if (crv == CKR_OK)
+ crv = CKR_FUNCTION_FAILED;
+ sftk_freeSSLKeys(hSession, ssl3_keys_out);
+ }
+ MD5_DestroyContext(md5, PR_TRUE);
+ SHA1_DestroyContext(sha, PR_TRUE);
+ sftk_FreeObject(key);
+ key = NULL;
+ break;
+ }
+
+ case CKM_CONCATENATE_BASE_AND_KEY:
+ {
+ SFTKObject *newKey;
+
+ crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+ if (crv != CKR_OK) break;
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ crv = CKR_SESSION_HANDLE_INVALID;
+ break;
+ }
+
+ newKey = sftk_ObjectFromHandle(*(CK_OBJECT_HANDLE *)
+ pMechanism->pParameter,session);
+ sftk_FreeSession(session);
+ if ( newKey == NULL) {
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+
+ if (sftk_isTrue(newKey,CKA_SENSITIVE)) {
+ crv = sftk_forceAttribute(newKey,CKA_SENSITIVE,&cktrue,
+ sizeof(CK_BBOOL));
+ if (crv != CKR_OK) {
+ sftk_FreeObject(newKey);
+ break;
+ }
+ }
+
+ att2 = sftk_FindAttribute(newKey,CKA_VALUE);
+ if (att2 == NULL) {
+ sftk_FreeObject(newKey);
+ crv = CKR_KEY_HANDLE_INVALID;
+ break;
+ }
+ tmpKeySize = att->attrib.ulValueLen+att2->attrib.ulValueLen;
+ if (keySize == 0) keySize = tmpKeySize;
+ if (keySize > tmpKeySize) {
+ sftk_FreeObject(newKey);
+ sftk_FreeAttribute(att2);
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ buf = (unsigned char*)PORT_Alloc(tmpKeySize);
+ if (buf == NULL) {
+ sftk_FreeAttribute(att2);
+ sftk_FreeObject(newKey);
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+
+ PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen);
+ PORT_Memcpy(buf+att->attrib.ulValueLen,
+ att2->attrib.pValue,att2->attrib.ulValueLen);
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+ PORT_ZFree(buf,tmpKeySize);
+ sftk_FreeAttribute(att2);
+ sftk_FreeObject(newKey);
+ break;
+ }
+
+ case CKM_CONCATENATE_BASE_AND_DATA:
+ crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+ if (crv != CKR_OK) break;
+
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter;
+ tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen;
+ if (keySize == 0) keySize = tmpKeySize;
+ if (keySize > tmpKeySize) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ buf = (unsigned char*)PORT_Alloc(tmpKeySize);
+ if (buf == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+
+ PORT_Memcpy(buf,att->attrib.pValue,att->attrib.ulValueLen);
+ PORT_Memcpy(buf+att->attrib.ulValueLen,stringPtr->pData,
+ stringPtr->ulLen);
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+ PORT_ZFree(buf,tmpKeySize);
+ break;
+ case CKM_CONCATENATE_DATA_AND_BASE:
+ crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+ if (crv != CKR_OK) break;
+
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter;
+ tmpKeySize = att->attrib.ulValueLen+stringPtr->ulLen;
+ if (keySize == 0) keySize = tmpKeySize;
+ if (keySize > tmpKeySize) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ buf = (unsigned char*)PORT_Alloc(tmpKeySize);
+ if (buf == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+
+ PORT_Memcpy(buf,stringPtr->pData,stringPtr->ulLen);
+ PORT_Memcpy(buf+stringPtr->ulLen,att->attrib.pValue,
+ att->attrib.ulValueLen);
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+ PORT_ZFree(buf,tmpKeySize);
+ break;
+ case CKM_XOR_BASE_AND_DATA:
+ crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+ if (crv != CKR_OK) break;
+
+ stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter;
+ tmpKeySize = PR_MIN(att->attrib.ulValueLen,stringPtr->ulLen);
+ if (keySize == 0) keySize = tmpKeySize;
+ if (keySize > tmpKeySize) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ buf = (unsigned char*)PORT_Alloc(keySize);
+ if (buf == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+
+
+ PORT_Memcpy(buf,att->attrib.pValue,keySize);
+ for (i=0; i < (int)keySize; i++) {
+ buf[i] ^= stringPtr->pData[i];
+ }
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+ PORT_ZFree(buf,keySize);
+ break;
+
+ case CKM_EXTRACT_KEY_FROM_KEY:
+ {
+ /* the following assumes 8 bits per byte */
+ CK_ULONG extract = *(CK_EXTRACT_PARAMS *)pMechanism->pParameter;
+ CK_ULONG shift = extract & 0x7; /* extract mod 8 the fast way */
+ CK_ULONG offset = extract >> 3; /* extract div 8 the fast way */
+
+ crv = sftk_DeriveSensitiveCheck(sourceKey,key);
+ if (crv != CKR_OK) break;
+
+ if (keySize == 0) {
+ crv = CKR_TEMPLATE_INCOMPLETE;
+ break;
+ }
+ /* make sure we have enough bits in the original key */
+ if (att->attrib.ulValueLen <
+ (offset + keySize + ((shift != 0)? 1 :0)) ) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+ buf = (unsigned char*)PORT_Alloc(keySize);
+ if (buf == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+
+ /* copy the bits we need into the new key */
+ for (i=0; i < (int)keySize; i++) {
+ unsigned char *value =
+ ((unsigned char *)att->attrib.pValue)+offset+i;
+ if (shift) {
+ buf[i] = (value[0] << (shift)) | (value[1] >> (8 - shift));
+ } else {
+ buf[i] = value[0];
+ }
+ }
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,buf,keySize);
+ PORT_ZFree(buf,keySize);
+ break;
+ }
+ case CKM_MD2_KEY_DERIVATION:
+ if (keySize == 0) keySize = MD2_LENGTH;
+ if (keySize > MD2_LENGTH) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ /* now allocate the hash contexts */
+ md2 = MD2_NewContext();
+ if (md2 == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ MD2_Begin(md2);
+ MD2_Update(md2,(const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ MD2_End(md2,key_block,&outLen,MD2_LENGTH);
+ MD2_DestroyContext(md2, PR_TRUE);
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize);
+ break;
+ case CKM_MD5_KEY_DERIVATION:
+ if (keySize == 0) keySize = MD5_LENGTH;
+ if (keySize > MD5_LENGTH) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ /* now allocate the hash contexts */
+ md5 = MD5_NewContext();
+ if (md5 == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ MD5_Begin(md5);
+ MD5_Update(md5,(const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ MD5_End(md5,key_block,&outLen,MD5_LENGTH);
+ MD5_DestroyContext(md5, PR_TRUE);
+
+ crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize);
+ break;
+ case CKM_SHA1_KEY_DERIVATION:
+ if (keySize == 0) keySize = SHA1_LENGTH;
+ if (keySize > SHA1_LENGTH) {
+ crv = CKR_TEMPLATE_INCONSISTENT;
+ break;
+ }
+ /* now allocate the hash contexts */
+ sha = SHA1_NewContext();
+ if (sha == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ SHA1_Begin(sha);
+ SHA1_Update(sha,(const unsigned char*)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ SHA1_End(sha,key_block,&outLen,SHA1_LENGTH);
+ SHA1_DestroyContext(sha, PR_TRUE);
+
+ crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize);
+ break;
+
+ case CKM_DH_PKCS_DERIVE:
+ {
+ SECItem derived, dhPublic;
+ SECItem dhPrime, dhValue;
+ /* sourceKey - values for the local existing low key */
+ /* get prime and value attributes */
+ crv = sftk_Attribute2SecItem(NULL, &dhPrime, sourceKey, CKA_PRIME);
+ if (crv != SECSuccess) break;
+ crv = sftk_Attribute2SecItem(NULL, &dhValue, sourceKey, CKA_VALUE);
+ if (crv != SECSuccess) {
+ PORT_Free(dhPrime.data);
+ break;
+ }
+
+ dhPublic.data = pMechanism->pParameter;
+ dhPublic.len = pMechanism->ulParameterLen;
+
+ /* calculate private value - oct */
+ rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize);
+
+ PORT_Free(dhPrime.data);
+ PORT_Free(dhValue.data);
+
+ if (rv == SECSuccess) {
+ sftk_forceAttribute(key, CKA_VALUE, derived.data, derived.len);
+ PORT_ZFree(derived.data, derived.len);
+ } else
+ crv = CKR_HOST_MEMORY;
+
+ break;
+ }
+
+#ifdef NSS_ENABLE_ECC
+ case CKM_ECDH1_DERIVE:
+ case CKM_ECDH1_COFACTOR_DERIVE:
+ {
+ SECItem ecScalar, ecPoint;
+ SECItem tmp;
+ PRBool withCofactor = PR_FALSE;
+ unsigned char secret_hash[20];
+ unsigned char *secret;
+ unsigned char *keyData = NULL;
+ int secretlen;
+ CK_ECDH1_DERIVE_PARAMS *mechParams;
+ NSSLOWKEYPrivateKey *privKey;
+
+ /* Check mechanism parameters */
+ mechParams = (CK_ECDH1_DERIVE_PARAMS *) pMechanism->pParameter;
+ if ((pMechanism->ulParameterLen != sizeof(CK_ECDH1_DERIVE_PARAMS)) ||
+ ((mechParams->kdf == CKD_NULL) &&
+ ((mechParams->ulSharedDataLen != 0) ||
+ (mechParams->pSharedData != NULL)))) {
+ crv = CKR_MECHANISM_PARAM_INVALID;
+ break;
+ }
+
+ privKey = sftk_GetPrivKey(sourceKey, CKK_EC, &crv);
+ if (privKey == NULL) {
+ break;
+ }
+
+ /* Now we are working with a non-NULL private key */
+ SECITEM_CopyItem(NULL, &ecScalar, &privKey->u.ec.privateValue);
+
+ ecPoint.data = mechParams->pPublicData;
+ ecPoint.len = mechParams->ulPublicDataLen;
+
+ if (pMechanism->mechanism == CKM_ECDH1_COFACTOR_DERIVE) {
+ withCofactor = PR_TRUE;
+ } else {
+ /* When not using cofactor derivation, one should
+ * validate the public key to avoid small subgroup
+ * attacks.
+ */
+ if (EC_ValidatePublicKey(&privKey->u.ec.ecParams, &ecPoint)
+ != SECSuccess) {
+ crv = CKR_ARGUMENTS_BAD;
+ PORT_Free(ecScalar.data);
+ if (privKey != sourceKey->objectInfo)
+ nsslowkey_DestroyPrivateKey(privKey);
+ break;
+ }
+ }
+
+ rv = ECDH_Derive(&ecPoint, &privKey->u.ec.ecParams, &ecScalar,
+ withCofactor, &tmp);
+ PORT_Free(ecScalar.data);
+ if (privKey != sourceKey->objectInfo)
+ nsslowkey_DestroyPrivateKey(privKey);
+
+ if (rv != SECSuccess) {
+ crv = CKR_DEVICE_ERROR;
+ break;
+ }
+
+ /*
+ * tmp is the raw data created by ECDH_Derive,
+ * secret and secretlen are the values we will eventually pass as our
+ * generated key.
+ */
+ secret = tmp.data;
+ secretlen = tmp.len;
+
+ /*
+ * apply the kdf function.
+ */
+ if (mechParams->kdf == CKD_SHA1_KDF) {
+ /* Compute SHA1 hash */
+ PORT_Memset(secret_hash, 0, 20);
+ rv = SHA1_HashBuf(secret_hash, tmp.data, tmp.len);
+ if (rv != SECSuccess) {
+ PORT_ZFree(tmp.data, tmp.len);
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ secret = secret_hash;
+ secretlen = 20;
+ }
+
+ /*
+ * if keySize is supplied, then we are generating a key of a specific
+ * length. This is done by taking the least significant 'keySize'
+ * bytes from the unsigned value calculated by ECDH. Note: this may
+ * mean padding temp with extra leading zeros from what ECDH_Derive
+ * already returned (which itself may contain leading zeros).
+ */
+ if (keySize) {
+ if (secretlen < keySize) {
+ keyData = PORT_ZAlloc(keySize);
+ if (!keyData) {
+ PORT_ZFree(tmp.data, tmp.len);
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ PORT_Memcpy(&keyData[keySize-secretlen],secret,secretlen);
+ secret = keyData;
+ } else {
+ secret += (secretlen - keySize);
+ }
+ secretlen = keySize;
+ }
+
+ sftk_forceAttribute(key, CKA_VALUE, secret, secretlen);
+ PORT_ZFree(tmp.data, tmp.len);
+ if (keyData) {
+ PORT_ZFree(keyData, keySize);
+ }
+ PORT_Memset(secret_hash, 0, 20);
+
+ break;
+ }
+#endif /* NSS_ENABLE_ECC */
+
+ default:
+ crv = CKR_MECHANISM_INVALID;
+ }
+ sftk_FreeAttribute(att);
+ sftk_FreeObject(sourceKey);
+ if (crv != CKR_OK) {
+ if (key) sftk_FreeObject(key);
+ return crv;
+ }
+
+ /* link the key object into the list */
+ if (key) {
+ SFTKSessionObject *sessKey = sftk_narrowToSessionObject(key);
+ PORT_Assert(sessKey);
+ /* get the session */
+ sessKey->wasDerived = PR_TRUE;
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) {
+ sftk_FreeObject(key);
+ return CKR_HOST_MEMORY;
+ }
+
+ crv = sftk_handleObject(key,session);
+ sftk_FreeSession(session);
+ *phKey = key->handle;
+ sftk_FreeObject(key);
+ }
+ return crv;
+}
+
+
+/* NSC_GetFunctionStatus obtains an updated status of a function running
+ * in parallel with an application. */
+CK_RV NSC_GetFunctionStatus(CK_SESSION_HANDLE hSession)
+{
+ CHECK_FORK();
+
+ return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+/* NSC_CancelFunction cancels a function running in parallel */
+CK_RV NSC_CancelFunction(CK_SESSION_HANDLE hSession)
+{
+ CHECK_FORK();
+
+ return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+/* NSC_GetOperationState saves the state of the cryptographic
+ *operation in a session.
+ * NOTE: This code only works for digest functions for now. eventually need
+ * to add full flatten/resurect to our state stuff so that all types of state
+ * can be saved */
+CK_RV NSC_GetOperationState(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen)
+{
+ SFTKSessionContext *context;
+ SFTKSession *session;
+ CK_RV crv;
+ CK_ULONG pOSLen = *pulOperationStateLen;
+
+ CHECK_FORK();
+
+ /* make sure we're legal */
+ crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE, &session);
+ if (crv != CKR_OK) return crv;
+
+ *pulOperationStateLen = context->cipherInfoLen + sizeof(CK_MECHANISM_TYPE)
+ + sizeof(SFTKContextType);
+ if (pOperationState == NULL) {
+ sftk_FreeSession(session);
+ return CKR_OK;
+ } else {
+ if (pOSLen < *pulOperationStateLen) {
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ }
+ PORT_Memcpy(pOperationState,&context->type,sizeof(SFTKContextType));
+ pOperationState += sizeof(SFTKContextType);
+ PORT_Memcpy(pOperationState,&context->currentMech,
+ sizeof(CK_MECHANISM_TYPE));
+ pOperationState += sizeof(CK_MECHANISM_TYPE);
+ PORT_Memcpy(pOperationState,context->cipherInfo,context->cipherInfoLen);
+ sftk_FreeSession(session);
+ return CKR_OK;
+}
+
+
+#define sftk_Decrement(stateSize,len) \
+ stateSize = ((stateSize) > (CK_ULONG)(len)) ? \
+ ((stateSize) - (CK_ULONG)(len)) : 0;
+
+/* NSC_SetOperationState restores the state of the cryptographic
+ * operation in a session. This is coded like it can restore lots of
+ * states, but it only works for truly flat cipher structures. */
+CK_RV NSC_SetOperationState(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen,
+ CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey)
+{
+ SFTKSessionContext *context;
+ SFTKSession *session;
+ SFTKContextType type;
+ CK_MECHANISM mech;
+ CK_RV crv = CKR_OK;
+
+ CHECK_FORK();
+
+ while (ulOperationStateLen != 0) {
+ /* get what type of state we're dealing with... */
+ PORT_Memcpy(&type,pOperationState, sizeof(SFTKContextType));
+
+ /* fix up session contexts based on type */
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+ context = sftk_ReturnContextByType(session, type);
+ sftk_SetContextByType(session, type, NULL);
+ if (context) {
+ sftk_FreeContext(context);
+ }
+ pOperationState += sizeof(SFTKContextType);
+ sftk_Decrement(ulOperationStateLen,sizeof(SFTKContextType));
+
+
+ /* get the mechanism structure */
+ PORT_Memcpy(&mech.mechanism,pOperationState,sizeof(CK_MECHANISM_TYPE));
+ pOperationState += sizeof(CK_MECHANISM_TYPE);
+ sftk_Decrement(ulOperationStateLen, sizeof(CK_MECHANISM_TYPE));
+ /* should be filled in... but not necessary for hash */
+ mech.pParameter = NULL;
+ mech.ulParameterLen = 0;
+ switch (type) {
+ case SFTK_HASH:
+ crv = NSC_DigestInit(hSession,&mech);
+ if (crv != CKR_OK) break;
+ crv = sftk_GetContext(hSession, &context, SFTK_HASH, PR_TRUE,
+ NULL);
+ if (crv != CKR_OK) break;
+ PORT_Memcpy(context->cipherInfo,pOperationState,
+ context->cipherInfoLen);
+ pOperationState += context->cipherInfoLen;
+ sftk_Decrement(ulOperationStateLen,context->cipherInfoLen);
+ break;
+ default:
+ /* do sign/encrypt/decrypt later */
+ crv = CKR_SAVED_STATE_INVALID;
+ }
+ sftk_FreeSession(session);
+ if (crv != CKR_OK) break;
+ }
+ return crv;
+}
+
+/* Dual-function cryptographic operations */
+
+/* NSC_DigestEncryptUpdate continues a multiple-part digesting and encryption
+ * operation. */
+CK_RV NSC_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart,
+ pulEncryptedPartLen);
+ if (crv != CKR_OK) return crv;
+ crv = NSC_DigestUpdate(hSession,pPart,ulPartLen);
+
+ return crv;
+}
+
+
+/* NSC_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation. */
+CK_RV NSC_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen)
+{
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ crv = NSC_DecryptUpdate(hSession,pEncryptedPart, ulEncryptedPartLen,
+ pPart, pulPartLen);
+ if (crv != CKR_OK) return crv;
+ crv = NSC_DigestUpdate(hSession,pPart,*pulPartLen);
+
+ return crv;
+}
+
+
+/* NSC_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation. */
+CK_RV NSC_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ crv = NSC_EncryptUpdate(hSession,pPart,ulPartLen, pEncryptedPart,
+ pulEncryptedPartLen);
+ if (crv != CKR_OK) return crv;
+ crv = NSC_SignUpdate(hSession,pPart,ulPartLen);
+
+ return crv;
+}
+
+
+/* NSC_DecryptVerifyUpdate continues a multiple-part decryption
+ * and verify operation. */
+CK_RV NSC_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+{
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ crv = NSC_DecryptUpdate(hSession,pEncryptedData, ulEncryptedDataLen,
+ pData, pulDataLen);
+ if (crv != CKR_OK) return crv;
+ crv = NSC_VerifyUpdate(hSession, pData, *pulDataLen);
+
+ return crv;
+}
+
+/* NSC_DigestKey continues a multi-part message-digesting operation,
+ * by digesting the value of a secret key as part of the data already digested.
+ */
+CK_RV NSC_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey)
+{
+ SFTKSession *session = NULL;
+ SFTKObject *key = NULL;
+ SFTKAttribute *att;
+ CK_RV crv;
+
+ CHECK_FORK();
+
+ session = sftk_SessionFromHandle(hSession);
+ if (session == NULL) return CKR_SESSION_HANDLE_INVALID;
+
+ key = sftk_ObjectFromHandle(hKey,session);
+ sftk_FreeSession(session);
+ if (key == NULL) return CKR_KEY_HANDLE_INVALID;
+
+ /* PUT ANY DIGEST KEY RESTRICTION CHECKS HERE */
+
+ /* make sure it's a valid key for this operation */
+ if (key->objclass != CKO_SECRET_KEY) {
+ sftk_FreeObject(key);
+ return CKR_KEY_TYPE_INCONSISTENT;
+ }
+ /* get the key value */
+ att = sftk_FindAttribute(key,CKA_VALUE);
+ sftk_FreeObject(key);
+ if (!att) {
+ return CKR_KEY_HANDLE_INVALID;
+ }
+ crv = NSC_DigestUpdate(hSession,(CK_BYTE_PTR)att->attrib.pValue,
+ att->attrib.ulValueLen);
+ sftk_FreeAttribute(att);
+ return crv;
+}
diff --git a/security/nss/lib/softoken/pkcs11f.h b/security/nss/lib/softoken/pkcs11f.h
new file mode 100644
index 000000000..a6bc91f46
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11f.h
@@ -0,0 +1,937 @@
+/* ***** 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
+ * RSA Security INC.
+ * 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 ***** */
+/*
+ * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document
+ * is granted provided that it is identified as "RSA Security In.c Public-Key
+ * Cryptography Standards (PKCS)" in all material mentioning or referencing
+ * this document.
+ */
+/* This function contains pretty much everything about all the */
+/* PKCS #11 function prototypes. Because this information is */
+/* used for more than just declaring function prototypes, the */
+/* order of the functions appearing herein is important, and */
+/* should not be altered. */
+
+
+
+/* General-purpose */
+
+/* C_Initialize initializes the PKCS #11 library. */
+CK_PKCS11_FUNCTION_INFO(C_Initialize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pInitArgs /* if this is not NULL_PTR, it gets
+ * cast to CK_C_INITIALIZE_ARGS_PTR
+ * and dereferenced */
+);
+#endif
+
+
+/* C_Finalize indicates that an application is done with the
+ * PKCS #11 library. */
+CK_PKCS11_FUNCTION_INFO(C_Finalize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_VOID_PTR pReserved /* reserved. Should be NULL_PTR */
+);
+#endif
+
+
+/* C_GetInfo returns general information about PKCS #11. */
+CK_PKCS11_FUNCTION_INFO(C_GetInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_INFO_PTR pInfo /* location that receives information */
+);
+#endif
+
+
+/* C_GetFunctionList returns the function list. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FUNCTION_LIST_PTR_PTR ppFunctionList /* receives pointer to
+ * function list */
+);
+#endif
+
+
+
+/* Slot and token management */
+
+/* C_GetSlotList obtains a list of slots in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_BBOOL tokenPresent, /* only slots with tokens? */
+ CK_SLOT_ID_PTR pSlotList, /* receives array of slot IDs */
+ CK_ULONG_PTR pulCount /* receives number of slots */
+);
+#endif
+
+
+/* C_GetSlotInfo obtains information about a particular slot in
+ * the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetSlotInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the ID of the slot */
+ CK_SLOT_INFO_PTR pInfo /* receives the slot information */
+);
+#endif
+
+
+/* C_GetTokenInfo obtains information about a particular token
+ * in the system. */
+CK_PKCS11_FUNCTION_INFO(C_GetTokenInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_TOKEN_INFO_PTR pInfo /* receives the token information */
+);
+#endif
+
+
+/* C_GetMechanismList obtains a list of mechanism types
+ * supported by a token. */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismList)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of token's slot */
+ CK_MECHANISM_TYPE_PTR pMechanismList, /* gets mech. array */
+ CK_ULONG_PTR pulCount /* gets # of mechs. */
+);
+#endif
+
+
+/* C_GetMechanismInfo obtains information about a particular
+ * mechanism possibly supported by a token. */
+CK_PKCS11_FUNCTION_INFO(C_GetMechanismInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_MECHANISM_TYPE type, /* type of mechanism */
+ CK_MECHANISM_INFO_PTR pInfo /* receives mechanism info */
+);
+#endif
+
+
+/* C_InitToken initializes a token. */
+CK_PKCS11_FUNCTION_INFO(C_InitToken)
+#ifdef CK_NEED_ARG_LIST
+/* pLabel changed from CK_CHAR_PTR to CK_UTF8CHAR_PTR for v2.10 */
+(
+ CK_SLOT_ID slotID, /* ID of the token's slot */
+ CK_UTF8CHAR_PTR pPin, /* the SO's initial PIN */
+ CK_ULONG ulPinLen, /* length in bytes of the PIN */
+ CK_UTF8CHAR_PTR pLabel /* 32-byte token label (blank padded) */
+);
+#endif
+
+
+/* C_InitPIN initializes the normal user's PIN. */
+CK_PKCS11_FUNCTION_INFO(C_InitPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pPin, /* the normal user's PIN */
+ CK_ULONG ulPinLen /* length in bytes of the PIN */
+);
+#endif
+
+
+/* C_SetPIN modifies the PIN of the user who is logged in. */
+CK_PKCS11_FUNCTION_INFO(C_SetPIN)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_UTF8CHAR_PTR pOldPin, /* the old PIN */
+ CK_ULONG ulOldLen, /* length of the old PIN */
+ CK_UTF8CHAR_PTR pNewPin, /* the new PIN */
+ CK_ULONG ulNewLen /* length of the new PIN */
+);
+#endif
+
+
+
+/* Session management */
+
+/* C_OpenSession opens a session between an application and a
+ * token. */
+CK_PKCS11_FUNCTION_INFO(C_OpenSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID, /* the slot's ID */
+ CK_FLAGS flags, /* from CK_SESSION_INFO */
+ CK_VOID_PTR pApplication, /* passed to callback */
+ CK_NOTIFY Notify, /* callback function */
+ CK_SESSION_HANDLE_PTR phSession /* gets session handle */
+);
+#endif
+
+
+/* C_CloseSession closes a session between an application and a
+ * token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseSession)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CloseAllSessions closes all sessions with a token. */
+CK_PKCS11_FUNCTION_INFO(C_CloseAllSessions)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SLOT_ID slotID /* the token's slot */
+);
+#endif
+
+
+/* C_GetSessionInfo obtains information about the session. */
+CK_PKCS11_FUNCTION_INFO(C_GetSessionInfo)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_SESSION_INFO_PTR pInfo /* receives session info */
+);
+#endif
+
+
+/* C_GetOperationState obtains the state of the cryptographic operation
+ * in a session. */
+CK_PKCS11_FUNCTION_INFO(C_GetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* gets state */
+ CK_ULONG_PTR pulOperationStateLen /* gets state length */
+);
+#endif
+
+
+/* C_SetOperationState restores the state of the cryptographic
+ * operation in a session. */
+CK_PKCS11_FUNCTION_INFO(C_SetOperationState)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pOperationState, /* holds state */
+ CK_ULONG ulOperationStateLen, /* holds state length */
+ CK_OBJECT_HANDLE hEncryptionKey, /* en/decryption key */
+ CK_OBJECT_HANDLE hAuthenticationKey /* sign/verify key */
+);
+#endif
+
+
+/* C_Login logs a user into a token. */
+CK_PKCS11_FUNCTION_INFO(C_Login)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_USER_TYPE userType, /* the user type */
+ CK_UTF8CHAR_PTR pPin, /* the user's PIN */
+ CK_ULONG ulPinLen /* the length of the PIN */
+);
+#endif
+
+
+/* C_Logout logs a user out from a token. */
+CK_PKCS11_FUNCTION_INFO(C_Logout)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Object management */
+
+/* C_CreateObject creates a new object. */
+CK_PKCS11_FUNCTION_INFO(C_CreateObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* the object's template */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phObject /* gets new object's handle. */
+);
+#endif
+
+
+/* C_CopyObject copies an object, creating a new object for the
+ * copy. */
+CK_PKCS11_FUNCTION_INFO(C_CopyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new object */
+ CK_ULONG ulCount, /* attributes in template */
+ CK_OBJECT_HANDLE_PTR phNewObject /* receives handle of copy */
+);
+#endif
+
+
+/* C_DestroyObject destroys an object. */
+CK_PKCS11_FUNCTION_INFO(C_DestroyObject)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject /* the object's handle */
+);
+#endif
+
+
+/* C_GetObjectSize gets the size of an object in bytes. */
+CK_PKCS11_FUNCTION_INFO(C_GetObjectSize)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ULONG_PTR pulSize /* receives size of object */
+);
+#endif
+
+
+/* C_GetAttributeValue obtains the value of one or more object
+ * attributes. */
+CK_PKCS11_FUNCTION_INFO(C_GetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs; gets vals */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_SetAttributeValue modifies the value of one or more object
+ * attributes */
+CK_PKCS11_FUNCTION_INFO(C_SetAttributeValue)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hObject, /* the object's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* specifies attrs and values */
+ CK_ULONG ulCount /* attributes in template */
+);
+#endif
+
+
+/* C_FindObjectsInit initializes a search for token and session
+ * objects that match a template. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_ATTRIBUTE_PTR pTemplate, /* attribute values to match */
+ CK_ULONG ulCount /* attrs in search template */
+);
+#endif
+
+
+/* C_FindObjects continues a search for token and session
+ * objects that match a template, obtaining additional object
+ * handles. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjects)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_OBJECT_HANDLE_PTR phObject, /* gets obj. handles */
+ CK_ULONG ulMaxObjectCount, /* max handles to get */
+ CK_ULONG_PTR pulObjectCount /* actual # returned */
+);
+#endif
+
+
+/* C_FindObjectsFinal finishes a search for token and session
+ * objects. */
+CK_PKCS11_FUNCTION_INFO(C_FindObjectsFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Encryption and decryption */
+
+/* C_EncryptInit initializes an encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the encryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of encryption key */
+);
+#endif
+
+
+/* C_Encrypt encrypts single-part data. */
+CK_PKCS11_FUNCTION_INFO(C_Encrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pData, /* the plaintext data */
+ CK_ULONG ulDataLen, /* bytes of plaintext */
+ CK_BYTE_PTR pEncryptedData, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedDataLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptUpdate continues a multiple-part encryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext data len */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text size */
+);
+#endif
+
+
+/* C_EncryptFinal finishes a multiple-part encryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_EncryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session handle */
+ CK_BYTE_PTR pLastEncryptedPart, /* last c-text */
+ CK_ULONG_PTR pulLastEncryptedPartLen /* gets last size */
+);
+#endif
+
+
+/* C_DecryptInit initializes a decryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the decryption mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of decryption key */
+);
+#endif
+
+
+/* C_Decrypt decrypts encrypted data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Decrypt)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedData, /* ciphertext */
+ CK_ULONG ulEncryptedDataLen, /* ciphertext length */
+ CK_BYTE_PTR pData, /* gets plaintext */
+ CK_ULONG_PTR pulDataLen /* gets p-text size */
+);
+#endif
+
+
+/* C_DecryptUpdate continues a multiple-part decryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* encrypted data */
+ CK_ULONG ulEncryptedPartLen, /* input length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* p-text size */
+);
+#endif
+
+
+/* C_DecryptFinal finishes a multiple-part decryption
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pLastPart, /* gets plaintext */
+ CK_ULONG_PTR pulLastPartLen /* p-text size */
+);
+#endif
+
+
+
+/* Message digesting */
+
+/* C_DigestInit initializes a message-digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism /* the digesting mechanism */
+);
+#endif
+
+
+/* C_Digest digests data in a single part. */
+CK_PKCS11_FUNCTION_INFO(C_Digest)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* data to be digested */
+ CK_ULONG ulDataLen, /* bytes of data to digest */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets digest length */
+);
+#endif
+
+
+/* C_DigestUpdate continues a multiple-part message-digesting
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* data to be digested */
+ CK_ULONG ulPartLen /* bytes of data to be digested */
+);
+#endif
+
+
+/* C_DigestKey continues a multi-part message-digesting
+ * operation, by digesting the value of a secret key as part of
+ * the data already digested. */
+CK_PKCS11_FUNCTION_INFO(C_DigestKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_OBJECT_HANDLE hKey /* secret key to digest */
+);
+#endif
+
+
+/* C_DigestFinal finishes a multiple-part message-digesting
+ * operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pDigest, /* gets the message digest */
+ CK_ULONG_PTR pulDigestLen /* gets byte count of digest */
+);
+#endif
+
+
+
+/* Signing and MACing */
+
+/* C_SignInit initializes a signature (private key encryption)
+ * operation, where the signature is (will be) an appendix to
+ * the data, and plaintext cannot be recovered from the
+ *signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of signature key */
+);
+#endif
+
+
+/* C_Sign signs (encrypts with private key) data in a single
+ * part, where the signature is (will be) an appendix to the
+ * data, and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_Sign)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignUpdate continues a multiple-part signature operation,
+ * where the signature is (will be) an appendix to the data,
+ * and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* the data to sign */
+ CK_ULONG ulPartLen /* count of bytes to sign */
+);
+#endif
+
+
+/* C_SignFinal finishes a multiple-part signature operation,
+ * returning the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+/* C_SignRecoverInit initializes a signature operation, where
+ * the data can be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the signature mechanism */
+ CK_OBJECT_HANDLE hKey /* handle of the signature key */
+);
+#endif
+
+
+/* C_SignRecover signs data in a single operation, where the
+ * data can be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_SignRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* the data to sign */
+ CK_ULONG ulDataLen, /* count of bytes to sign */
+ CK_BYTE_PTR pSignature, /* gets the signature */
+ CK_ULONG_PTR pulSignatureLen /* gets signature length */
+);
+#endif
+
+
+
+/* Verifying signatures and MACs */
+
+/* C_VerifyInit initializes a verification operation, where the
+ * signature is an appendix to the data, and plaintext cannot
+ * cannot be recovered from the signature (e.g. DSA). */
+CK_PKCS11_FUNCTION_INFO(C_VerifyInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_Verify verifies a signature in a single-part operation,
+ * where the signature is an appendix to the data, and plaintext
+ * cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_Verify)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pData, /* signed data */
+ CK_ULONG ulDataLen, /* length of signed data */
+ CK_BYTE_PTR pSignature, /* signature */
+ CK_ULONG ulSignatureLen /* signature length*/
+);
+#endif
+
+
+/* C_VerifyUpdate continues a multiple-part verification
+ * operation, where the signature is an appendix to the data,
+ * and plaintext cannot be recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pPart, /* signed data */
+ CK_ULONG ulPartLen /* length of signed data */
+);
+#endif
+
+
+/* C_VerifyFinal finishes a multiple-part verification
+ * operation, checking the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyFinal)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen /* signature length */
+);
+#endif
+
+
+/* C_VerifyRecoverInit initializes a signature verification
+ * operation, where the data is recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecoverInit)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the verification mechanism */
+ CK_OBJECT_HANDLE hKey /* verification key */
+);
+#endif
+
+
+/* C_VerifyRecover verifies a signature in a single-part
+ * operation, where the data is recovered from the signature. */
+CK_PKCS11_FUNCTION_INFO(C_VerifyRecover)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSignature, /* signature to verify */
+ CK_ULONG ulSignatureLen, /* signature length */
+ CK_BYTE_PTR pData, /* gets signed data */
+ CK_ULONG_PTR pulDataLen /* gets signed data len */
+);
+#endif
+
+
+
+/* Dual-function cryptographic operations */
+
+/* C_DigestEncryptUpdate continues a multiple-part digesting
+ * and encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_DigestEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptDigestUpdate continues a multiple-part decryption and
+ * digesting operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptDigestUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets plaintext len */
+);
+#endif
+
+
+/* C_SignEncryptUpdate continues a multiple-part signing and
+ * encryption operation. */
+CK_PKCS11_FUNCTION_INFO(C_SignEncryptUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pPart, /* the plaintext data */
+ CK_ULONG ulPartLen, /* plaintext length */
+ CK_BYTE_PTR pEncryptedPart, /* gets ciphertext */
+ CK_ULONG_PTR pulEncryptedPartLen /* gets c-text length */
+);
+#endif
+
+
+/* C_DecryptVerifyUpdate continues a multiple-part decryption and
+ * verify operation. */
+CK_PKCS11_FUNCTION_INFO(C_DecryptVerifyUpdate)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_BYTE_PTR pEncryptedPart, /* ciphertext */
+ CK_ULONG ulEncryptedPartLen, /* ciphertext length */
+ CK_BYTE_PTR pPart, /* gets plaintext */
+ CK_ULONG_PTR pulPartLen /* gets p-text length */
+);
+#endif
+
+
+
+/* Key management */
+
+/* C_GenerateKey generates a secret key, creating a new key
+ * object. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key generation mech. */
+ CK_ATTRIBUTE_PTR pTemplate, /* template for new key */
+ CK_ULONG ulCount, /* # of attrs in template */
+ CK_OBJECT_HANDLE_PTR phKey /* gets handle of new key */
+);
+#endif
+
+
+/* C_GenerateKeyPair generates a public-key/private-key pair,
+ * creating new key objects. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateKeyPair)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session
+ * handle */
+ CK_MECHANISM_PTR pMechanism, /* key-gen
+ * mech. */
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate, /* template
+ * for pub.
+ * key */
+ CK_ULONG ulPublicKeyAttributeCount, /* # pub.
+ * attrs. */
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate, /* template
+ * for priv.
+ * key */
+ CK_ULONG ulPrivateKeyAttributeCount, /* # priv.
+ * attrs. */
+ CK_OBJECT_HANDLE_PTR phPublicKey, /* gets pub.
+ * key
+ * handle */
+ CK_OBJECT_HANDLE_PTR phPrivateKey /* gets
+ * priv. key
+ * handle */
+);
+#endif
+
+
+/* C_WrapKey wraps (i.e., encrypts) a key. */
+CK_PKCS11_FUNCTION_INFO(C_WrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_MECHANISM_PTR pMechanism, /* the wrapping mechanism */
+ CK_OBJECT_HANDLE hWrappingKey, /* wrapping key */
+ CK_OBJECT_HANDLE hKey, /* key to be wrapped */
+ CK_BYTE_PTR pWrappedKey, /* gets wrapped key */
+ CK_ULONG_PTR pulWrappedKeyLen /* gets wrapped key size */
+);
+#endif
+
+
+/* C_UnwrapKey unwraps (decrypts) a wrapped key, creating a new
+ * key object. */
+CK_PKCS11_FUNCTION_INFO(C_UnwrapKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* unwrapping mech. */
+ CK_OBJECT_HANDLE hUnwrappingKey, /* unwrapping key */
+ CK_BYTE_PTR pWrappedKey, /* the wrapped key */
+ CK_ULONG ulWrappedKeyLen, /* wrapped key len */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+/* C_DeriveKey derives a key from a base key, creating a new key
+ * object. */
+CK_PKCS11_FUNCTION_INFO(C_DeriveKey)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* session's handle */
+ CK_MECHANISM_PTR pMechanism, /* key deriv. mech. */
+ CK_OBJECT_HANDLE hBaseKey, /* base key */
+ CK_ATTRIBUTE_PTR pTemplate, /* new key template */
+ CK_ULONG ulAttributeCount, /* template length */
+ CK_OBJECT_HANDLE_PTR phKey /* gets new handle */
+);
+#endif
+
+
+
+/* Random number generation */
+
+/* C_SeedRandom mixes additional seed material into the token's
+ * random number generator. */
+CK_PKCS11_FUNCTION_INFO(C_SeedRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR pSeed, /* the seed material */
+ CK_ULONG ulSeedLen /* length of seed material */
+);
+#endif
+
+
+/* C_GenerateRandom generates random data. */
+CK_PKCS11_FUNCTION_INFO(C_GenerateRandom)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_BYTE_PTR RandomData, /* receives the random data */
+ CK_ULONG ulRandomLen /* # of bytes to generate */
+);
+#endif
+
+
+
+/* Parallel function management */
+
+/* C_GetFunctionStatus is a legacy function; it obtains an
+ * updated status of a function running in parallel with an
+ * application. */
+CK_PKCS11_FUNCTION_INFO(C_GetFunctionStatus)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+/* C_CancelFunction is a legacy function; it cancels a function
+ * running in parallel. */
+CK_PKCS11_FUNCTION_INFO(C_CancelFunction)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_SESSION_HANDLE hSession /* the session's handle */
+);
+#endif
+
+
+
+/* Functions added in for PKCS #11 Version 2.01 or later */
+
+/* C_WaitForSlotEvent waits for a slot event (token insertion,
+ * removal, etc.) to occur. */
+CK_PKCS11_FUNCTION_INFO(C_WaitForSlotEvent)
+#ifdef CK_NEED_ARG_LIST
+(
+ CK_FLAGS flags, /* blocking/nonblocking flag */
+ CK_SLOT_ID_PTR pSlot, /* location that receives the slot ID */
+ CK_VOID_PTR pRserved /* reserved. Should be NULL_PTR */
+);
+#endif
diff --git a/security/nss/lib/softoken/pkcs11i.h b/security/nss/lib/softoken/pkcs11i.h
new file mode 100644
index 000000000..3ab201562
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11i.h
@@ -0,0 +1,715 @@
+/* ***** 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 ***** */
+/*
+ * Internal data structures and functions used by pkcs11.c
+ */
+#ifndef _PKCS11I_H_
+#define _PKCS11I_H_ 1
+
+#include "nssilock.h"
+#include "seccomon.h"
+#include "secoidt.h"
+#include "lowkeyti.h"
+#include "pkcs11t.h"
+
+#include "sftkdbt.h"
+
+
+/*
+ * Configuration Defines
+ *
+ * The following defines affect the space verse speed trade offs of
+ * the PKCS #11 module. For the most part the current settings are optimized
+ * for web servers, where we want faster speed and lower lock contention at
+ * the expense of space.
+ */
+
+/*
+ * The attribute allocation strategy is static allocation:
+ * Attributes are pre-allocated as part of the session object and used from
+ * the object array.
+ */
+#define MAX_OBJS_ATTRS 45 /* number of attributes to preallocate in
+ * the object (must me the absolute max) */
+#define ATTR_SPACE 50 /* Maximum size of attribute data before extra
+ * data needs to be allocated. This is set to
+ * enough space to hold an SSL MASTER secret */
+
+#define NSC_STRICT PR_FALSE /* forces the code to do strict template
+ * matching when doing C_FindObject on token
+ * objects. This will slow down search in
+ * NSS. */
+/* default search block allocations and increments */
+#define NSC_CERT_BLOCK_SIZE 50
+#define NSC_SEARCH_BLOCK_SIZE 5
+#define NSC_SLOT_LIST_BLOCK_SIZE 10
+
+#define NSC_FIPS_MODULE 1
+#define NSC_NON_FIPS_MODULE 0
+
+/* these are data base storage hashes, not cryptographic hashes.. The define
+ * the effective size of the various object hash tables */
+/* clients care more about memory usage than lookup performance on
+ * cyrptographic objects. Clients also have less objects around to play with
+ *
+ * we eventually should make this configurable at runtime! Especially now that
+ * NSS is a shared library.
+ */
+#define SPACE_ATTRIBUTE_HASH_SIZE 32
+#define SPACE_SESSION_OBJECT_HASH_SIZE 32
+#define SPACE_SESSION_HASH_SIZE 32
+#define TIME_ATTRIBUTE_HASH_SIZE 32
+#define TIME_SESSION_OBJECT_HASH_SIZE 1024
+#define TIME_SESSION_HASH_SIZE 1024
+#define MAX_OBJECT_LIST_SIZE 800
+ /* how many objects to keep on the free list
+ * before we start freeing them */
+#define MAX_KEY_LEN 256 /* maximum symmetric key length in bytes */
+
+#define MULTIACCESS "multiaccess:"
+
+/*
+ * LOG2_BUCKETS_PER_SESSION_LOCK must be a prime number.
+ * With SESSION_HASH_SIZE=1024, LOG2 can be 9, 5, 1, or 0.
+ * With SESSION_HASH_SIZE=4096, LOG2 can be 11, 9, 5, 1, or 0.
+ *
+ * HASH_SIZE LOG2_BUCKETS_PER BUCKETS_PER_LOCK NUMBER_OF_BUCKETS
+ * 1024 9 512 2
+ * 1024 5 32 32
+ * 1024 1 2 512
+ * 1024 0 1 1024
+ * 4096 11 2048 2
+ * 4096 9 512 8
+ * 4096 5 32 128
+ * 4096 1 2 2048
+ * 4096 0 1 4096
+ */
+#define LOG2_BUCKETS_PER_SESSION_LOCK 1
+#define BUCKETS_PER_SESSION_LOCK (1 << (LOG2_BUCKETS_PER_SESSION_LOCK))
+/* NOSPREAD sessionID to hash table index macro has been slower. */
+
+/* define typedefs, double as forward declarations as well */
+typedef struct SFTKAttributeStr SFTKAttribute;
+typedef struct SFTKObjectListStr SFTKObjectList;
+typedef struct SFTKObjectFreeListStr SFTKObjectFreeList;
+typedef struct SFTKObjectListElementStr SFTKObjectListElement;
+typedef struct SFTKObjectStr SFTKObject;
+typedef struct SFTKSessionObjectStr SFTKSessionObject;
+typedef struct SFTKTokenObjectStr SFTKTokenObject;
+typedef struct SFTKSessionStr SFTKSession;
+typedef struct SFTKSlotStr SFTKSlot;
+typedef struct SFTKSessionContextStr SFTKSessionContext;
+typedef struct SFTKSearchResultsStr SFTKSearchResults;
+typedef struct SFTKHashVerifyInfoStr SFTKHashVerifyInfo;
+typedef struct SFTKHashSignInfoStr SFTKHashSignInfo;
+typedef struct SFTKSSLMACInfoStr SFTKSSLMACInfo;
+typedef struct SFTKItemTemplateStr SFTKItemTemplate;
+
+/* define function pointer typdefs for pointer tables */
+typedef void (*SFTKDestroy)(void *, PRBool);
+typedef void (*SFTKBegin)(void *);
+typedef SECStatus (*SFTKCipher)(void *,void *,unsigned int *,unsigned int,
+ void *, unsigned int);
+typedef SECStatus (*SFTKVerify)(void *,void *,unsigned int,void *,unsigned int);
+typedef void (*SFTKHash)(void *,void *,unsigned int);
+typedef void (*SFTKEnd)(void *,void *,unsigned int *,unsigned int);
+typedef void (*SFTKFree)(void *);
+
+/* Value to tell if an attribute is modifiable or not.
+ * NEVER: attribute is only set on creation.
+ * ONCOPY: attribute is set on creation and can only be changed on copy.
+ * SENSITIVE: attribute can only be changed to TRUE.
+ * ALWAYS: attribute can always be changed.
+ */
+typedef enum {
+ SFTK_NEVER = 0,
+ SFTK_ONCOPY = 1,
+ SFTK_SENSITIVE = 2,
+ SFTK_ALWAYS = 3
+} SFTKModifyType;
+
+/*
+ * Free Status Enum... tell us more information when we think we're
+ * deleting an object.
+ */
+typedef enum {
+ SFTK_DestroyFailure,
+ SFTK_Destroyed,
+ SFTK_Busy
+} SFTKFreeStatus;
+
+/*
+ * attribute values of an object.
+ */
+struct SFTKAttributeStr {
+ SFTKAttribute *next;
+ SFTKAttribute *prev;
+ PRBool freeAttr;
+ PRBool freeData;
+ /*must be called handle to make sftkqueue_find work */
+ CK_ATTRIBUTE_TYPE handle;
+ CK_ATTRIBUTE attrib;
+ unsigned char space[ATTR_SPACE];
+};
+
+
+/*
+ * doubly link list of objects
+ */
+struct SFTKObjectListStr {
+ SFTKObjectList *next;
+ SFTKObjectList *prev;
+ SFTKObject *parent;
+};
+
+struct SFTKObjectFreeListStr {
+ SFTKObject *head;
+ PZLock *lock;
+ int count;
+};
+
+/*
+ * PKCS 11 crypto object structure
+ */
+struct SFTKObjectStr {
+ SFTKObject *next;
+ SFTKObject *prev;
+ CK_OBJECT_CLASS objclass;
+ CK_OBJECT_HANDLE handle;
+ int refCount;
+ PZLock *refLock;
+ SFTKSlot *slot;
+ void *objectInfo;
+ SFTKFree infoFree;
+};
+
+struct SFTKTokenObjectStr {
+ SFTKObject obj;
+ SECItem dbKey;
+};
+
+struct SFTKSessionObjectStr {
+ SFTKObject obj;
+ SFTKObjectList sessionList;
+ PZLock *attributeLock;
+ SFTKSession *session;
+ PRBool wasDerived;
+ int nextAttr;
+ SFTKAttribute attrList[MAX_OBJS_ATTRS];
+ PRBool optimizeSpace;
+ unsigned int hashSize;
+ SFTKAttribute *head[1];
+};
+
+/*
+ * struct to deal with a temparary list of objects
+ */
+struct SFTKObjectListElementStr {
+ SFTKObjectListElement *next;
+ SFTKObject *object;
+};
+
+/*
+ * Area to hold Search results
+ */
+struct SFTKSearchResultsStr {
+ CK_OBJECT_HANDLE *handles;
+ int size;
+ int index;
+ int array_size;
+};
+
+
+/*
+ * the universal crypto/hash/sign/verify context structure
+ */
+typedef enum {
+ SFTK_ENCRYPT,
+ SFTK_DECRYPT,
+ SFTK_HASH,
+ SFTK_SIGN,
+ SFTK_SIGN_RECOVER,
+ SFTK_VERIFY,
+ SFTK_VERIFY_RECOVER
+} SFTKContextType;
+
+
+#define SFTK_MAX_BLOCK_SIZE 16
+/* currently SHA512 is the biggest hash length */
+#define SFTK_MAX_MAC_LENGTH 64
+#define SFTK_INVALID_MAC_SIZE 0xffffffff
+
+struct SFTKSessionContextStr {
+ SFTKContextType type;
+ PRBool multi; /* is multipart */
+ PRBool doPad; /* use PKCS padding for block ciphers */
+ unsigned int blockSize; /* blocksize for padding */
+ unsigned int padDataLength; /* length of the valid data in padbuf */
+ unsigned char padBuf[SFTK_MAX_BLOCK_SIZE];
+ unsigned char macBuf[SFTK_MAX_BLOCK_SIZE];
+ CK_ULONG macSize; /* size of a general block cipher mac*/
+ void *cipherInfo;
+ void *hashInfo;
+ unsigned int cipherInfoLen;
+ CK_MECHANISM_TYPE currentMech;
+ SFTKCipher update;
+ SFTKHash hashUpdate;
+ SFTKEnd end;
+ SFTKDestroy destroy;
+ SFTKDestroy hashdestroy;
+ SFTKVerify verify;
+ unsigned int maxLen;
+ SFTKObject *key;
+};
+
+/*
+ * Sessions (have objects)
+ */
+struct SFTKSessionStr {
+ SFTKSession *next;
+ SFTKSession *prev;
+ CK_SESSION_HANDLE handle;
+ int refCount;
+ PZLock *objectLock;
+ int objectIDCount;
+ CK_SESSION_INFO info;
+ CK_NOTIFY notify;
+ CK_VOID_PTR appData;
+ SFTKSlot *slot;
+ SFTKSearchResults *search;
+ SFTKSessionContext *enc_context;
+ SFTKSessionContext *hash_context;
+ SFTKSessionContext *sign_context;
+ SFTKObjectList *objects[1];
+};
+
+/*
+ * slots (have sessions and objects)
+ *
+ * The array of sessionLock's protect the session hash table (head[])
+ * as well as the reference count of session objects in that bucket
+ * (head[]->refCount), objectLock protects all elements of the slot's
+ * object hash tables (sessObjHashTable[] and tokObjHashTable), and
+ * sessionObjectHandleCount.
+ * slotLock protects the remaining protected elements:
+ * password, isLoggedIn, ssoLoggedIn, and sessionCount,
+ * and pwCheckLock serializes the key database password checks in
+ * NSC_SetPIN and NSC_Login.
+ *
+ * Each of the fields below has the following lifetime as commented
+ * next to the fields:
+ * invariant - This value is set when the slot is first created and
+ * never changed until it is destroyed.
+ * per load - This value is set when the slot is first created, or
+ * when the slot is used to open another directory. Between open and close
+ * this field does not change.
+ * variable - This value changes through the normal process of slot operation.
+ * - reset. The value of this variable is cleared during an open/close
+ * cycles.
+ * - preserved. The value of this variable is preserved over open/close
+ * cycles.
+ */
+struct SFTKSlotStr {
+ CK_SLOT_ID slotID; /* invariant */
+ PZLock *slotLock; /* invariant */
+ PZLock **sessionLock; /* invariant */
+ unsigned int numSessionLocks; /* invariant */
+ unsigned long sessionLockMask; /* invariant */
+ PZLock *objectLock; /* invariant */
+ PRLock *pwCheckLock; /* invariant */
+ PRBool present; /* variable -set */
+ PRBool hasTokens; /* per load */
+ PRBool isLoggedIn; /* variable - reset */
+ PRBool ssoLoggedIn; /* variable - reset */
+ PRBool needLogin; /* per load */
+ PRBool DB_loaded; /* per load */
+ PRBool readOnly; /* per load */
+ PRBool optimizeSpace; /* invariant */
+ SFTKDBHandle *certDB; /* per load */
+ SFTKDBHandle *keyDB; /* per load */
+ int minimumPinLen; /* per load */
+ PRInt32 sessionIDCount; /* atomically incremented */
+ /* (preserved) */
+ int sessionIDConflict; /* not protected by a lock */
+ /* (preserved) */
+ int sessionCount; /* variable - reset */
+ PRInt32 rwSessionCount; /* set by atomic operations */
+ /* (reset) */
+ int sessionObjectHandleCount;/* variable - perserved */
+ int index; /* invariant */
+ PLHashTable *tokObjHashTable; /* invariant */
+ SFTKObject **sessObjHashTable; /* variable - reset */
+ unsigned int sessObjHashSize; /* invariant */
+ SFTKSession **head; /* variable -reset */
+ unsigned int sessHashSize; /* invariant */
+ char tokDescription[33]; /* per load */
+ char updateTokDescription[33]; /* per load */
+ char slotDescription[65]; /* invariant */
+};
+
+/*
+ * special joint operations Contexts
+ */
+struct SFTKHashVerifyInfoStr {
+ SECOidTag hashOid;
+ NSSLOWKEYPublicKey *key;
+};
+
+struct SFTKHashSignInfoStr {
+ SECOidTag hashOid;
+ NSSLOWKEYPrivateKey *key;
+};
+
+/* context for the Final SSLMAC message */
+struct SFTKSSLMACInfoStr {
+ void *hashContext;
+ SFTKBegin begin;
+ SFTKHash update;
+ SFTKEnd end;
+ CK_ULONG macSize;
+ int padSize;
+ unsigned char key[MAX_KEY_LEN];
+ unsigned int keySize;
+};
+
+/*
+ * Template based on SECItems, suitable for passing as arrays
+ */
+struct SFTKItemTemplateStr {
+ CK_ATTRIBUTE_TYPE type;
+ SECItem *item;
+};
+
+/* macro for setting SFTKTemplates. */
+#define SFTK_SET_ITEM_TEMPLATE(templ, count, itemPtr, attr) \
+ templ[count].type = attr; \
+ templ[count].item = itemPtr
+
+#define SFTK_MAX_ITEM_TEMPLATE 10
+
+/*
+ * session handle modifiers
+ */
+#define SFTK_SESSION_SLOT_MASK 0xff000000L
+
+/*
+ * object handle modifiers
+ */
+#define SFTK_TOKEN_MASK 0x80000000L
+#define SFTK_TOKEN_MAGIC 0x80000000L
+#define SFTK_TOKEN_TYPE_MASK 0x70000000L
+/* keydb (high bit == 0) */
+#define SFTK_TOKEN_TYPE_PRIV 0x10000000L
+#define SFTK_TOKEN_TYPE_PUB 0x20000000L
+#define SFTK_TOKEN_TYPE_KEY 0x30000000L
+/* certdb (high bit == 1) */
+#define SFTK_TOKEN_TYPE_TRUST 0x40000000L
+#define SFTK_TOKEN_TYPE_CRL 0x50000000L
+#define SFTK_TOKEN_TYPE_SMIME 0x60000000L
+#define SFTK_TOKEN_TYPE_CERT 0x70000000L
+
+#define SFTK_TOKEN_KRL_HANDLE (SFTK_TOKEN_MAGIC|SFTK_TOKEN_TYPE_CRL|1)
+/* how big (in bytes) a password/pin we can deal with */
+#define SFTK_MAX_PIN 255
+/* minimum password/pin length (in Unicode characters) in FIPS mode */
+#define FIPS_MIN_PIN 7
+
+/* slot ID's */
+#define NETSCAPE_SLOT_ID 1
+#define PRIVATE_KEY_SLOT_ID 2
+#define FIPS_SLOT_ID 3
+
+/* slot helper macros */
+#define sftk_SlotFromSession(sp) ((sp)->slot)
+#define sftk_isToken(id) (((id) & SFTK_TOKEN_MASK) == SFTK_TOKEN_MAGIC)
+
+/* the session hash multiplier (see bug 201081) */
+#define SHMULTIPLIER 1791398085
+
+/* queueing helper macros */
+#define sftk_hash(value,size) \
+ ((PRUint32)((value) * SHMULTIPLIER) & (size-1))
+#define sftkqueue_add(element,id,head,hash_size) \
+ { int tmp = sftk_hash(id,hash_size); \
+ (element)->next = (head)[tmp]; \
+ (element)->prev = NULL; \
+ if ((head)[tmp]) (head)[tmp]->prev = (element); \
+ (head)[tmp] = (element); }
+#define sftkqueue_find(element,id,head,hash_size) \
+ for( (element) = (head)[sftk_hash(id,hash_size)]; (element) != NULL; \
+ (element) = (element)->next) { \
+ if ((element)->handle == (id)) { break; } }
+#define sftkqueue_is_queued(element,id,head,hash_size) \
+ ( ((element)->next) || ((element)->prev) || \
+ ((head)[sftk_hash(id,hash_size)] == (element)) )
+#define sftkqueue_delete(element,id,head,hash_size) \
+ if ((element)->next) (element)->next->prev = (element)->prev; \
+ if ((element)->prev) (element)->prev->next = (element)->next; \
+ else (head)[sftk_hash(id,hash_size)] = ((element)->next); \
+ (element)->next = NULL; \
+ (element)->prev = NULL; \
+
+#define sftkqueue_init_element(element) \
+ (element)->prev = NULL;
+
+#define sftkqueue_add2(element, id, index, head) \
+ { \
+ (element)->next = (head)[index]; \
+ if ((head)[index]) \
+ (head)[index]->prev = (element); \
+ (head)[index] = (element); \
+ }
+
+#define sftkqueue_find2(element, id, index, head) \
+ for ( (element) = (head)[index]; \
+ (element) != NULL; \
+ (element) = (element)->next) { \
+ if ((element)->handle == (id)) { break; } \
+ }
+
+#define sftkqueue_delete2(element, id, index, head) \
+ if ((element)->next) (element)->next->prev = (element)->prev; \
+ if ((element)->prev) (element)->prev->next = (element)->next; \
+ else (head)[index] = ((element)->next);
+
+#define sftkqueue_clear_deleted_element(element) \
+ (element)->next = NULL; \
+ (element)->prev = NULL; \
+
+
+/* sessionID (handle) is used to determine session lock bucket */
+#ifdef NOSPREAD
+/* NOSPREAD: (ID>>L2LPB) & (perbucket-1) */
+#define SFTK_SESSION_LOCK(slot,handle) \
+ ((slot)->sessionLock[((handle) >> LOG2_BUCKETS_PER_SESSION_LOCK) \
+ & (slot)->sessionLockMask])
+#else
+/* SPREAD: ID & (perbucket-1) */
+#define SFTK_SESSION_LOCK(slot,handle) \
+ ((slot)->sessionLock[(handle) & (slot)->sessionLockMask])
+#endif
+
+/* expand an attribute & secitem structures out */
+#define sftk_attr_expand(ap) (ap)->type,(ap)->pValue,(ap)->ulValueLen
+#define sftk_item_expand(ip) (ip)->data,(ip)->len
+
+typedef struct sftk_token_parametersStr {
+ CK_SLOT_ID slotID;
+ char *configdir;
+ char *certPrefix;
+ char *keyPrefix;
+ char *updatedir;
+ char *updCertPrefix;
+ char *updKeyPrefix;
+ char *updateID;
+ char *tokdes;
+ char *slotdes;
+ char *updtokdes;
+ int minPW;
+ PRBool readOnly;
+ PRBool noCertDB;
+ PRBool noKeyDB;
+ PRBool forceOpen;
+ PRBool pwRequired;
+ PRBool optimizeSpace;
+} sftk_token_parameters;
+
+typedef struct sftk_parametersStr {
+ char *configdir;
+ char *updatedir;
+ char *updateID;
+ char *secmodName;
+ char *man;
+ char *libdes;
+ PRBool readOnly;
+ PRBool noModDB;
+ PRBool noCertDB;
+ PRBool forceOpen;
+ PRBool pwRequired;
+ PRBool optimizeSpace;
+ sftk_token_parameters *tokens;
+ int token_count;
+} sftk_parameters;
+
+
+/* machine dependent path stuff used by dbinit.c and pk11db.c */
+#ifdef macintosh
+#define PATH_SEPARATOR ":"
+#define SECMOD_DB "Security Modules"
+#define CERT_DB_FMT "%sCertificates%s"
+#define KEY_DB_FMT "%sKey Database%s"
+#else
+#define PATH_SEPARATOR "/"
+#define SECMOD_DB "secmod.db"
+#define CERT_DB_FMT "%scert%s.db"
+#define KEY_DB_FMT "%skey%s.db"
+#endif
+
+SEC_BEGIN_PROTOS
+
+/* shared functions between pkcs11.c and fipstokn.c */
+extern PRBool nsf_init;
+extern CK_RV nsc_CommonInitialize(CK_VOID_PTR pReserved, PRBool isFIPS);
+extern CK_RV nsc_CommonFinalize(CK_VOID_PTR pReserved, PRBool isFIPS);
+extern CK_RV nsc_CommonGetSlotList(CK_BBOOL tokPresent,
+ CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount, int moduleIndex);
+
+/* slot initialization, reinit, shutdown and destruction */
+extern CK_RV SFTK_SlotInit(char *configdir, char *updatedir, char *updateID,
+ sftk_token_parameters *params, int moduleIndex);
+extern CK_RV SFTK_SlotReInit(SFTKSlot *slot, char *configdir,
+ char *updatedir, char *updateID,
+ sftk_token_parameters *params, int moduleIndex);
+extern CK_RV SFTK_DestroySlotData(SFTKSlot *slot);
+extern CK_RV SFTK_ShutdownSlot(SFTKSlot *slot);
+extern CK_RV sftk_CloseAllSessions(SFTKSlot *slot);
+
+
+/* internal utility functions used by pkcs11.c */
+extern SFTKAttribute *sftk_FindAttribute(SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type);
+extern void sftk_FreeAttribute(SFTKAttribute *attribute);
+extern CK_RV sftk_AddAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ void *valPtr,
+ CK_ULONG length);
+extern CK_RV sftk_Attribute2SecItem(PLArenaPool *arena, SECItem *item,
+ SFTKObject *object, CK_ATTRIBUTE_TYPE type);
+extern CK_RV sftk_MultipleAttribute2SecItem(PLArenaPool *arena,
+ SFTKObject *object, SFTKItemTemplate *templ, int count);
+extern unsigned int sftk_GetLengthInBits(unsigned char *buf,
+ unsigned int bufLen);
+extern CK_RV sftk_ConstrainAttribute(SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type, int minLength, int maxLength, int minMultiple);
+extern PRBool sftk_hasAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type);
+extern PRBool sftk_isTrue(SFTKObject *object, CK_ATTRIBUTE_TYPE type);
+extern void sftk_DeleteAttributeType(SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type);
+extern CK_RV sftk_Attribute2SecItem(PLArenaPool *arena, SECItem *item,
+ SFTKObject *object, CK_ATTRIBUTE_TYPE type);
+extern CK_RV sftk_Attribute2SSecItem(PLArenaPool *arena, SECItem *item,
+ SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type);
+extern SFTKModifyType sftk_modifyType(CK_ATTRIBUTE_TYPE type,
+ CK_OBJECT_CLASS inClass);
+extern PRBool sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass);
+extern char *sftk_getString(SFTKObject *object, CK_ATTRIBUTE_TYPE type);
+extern void sftk_nullAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type);
+extern CK_RV sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ CK_ULONG *longData);
+extern CK_RV sftk_forceAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ void *value, unsigned int len);
+extern CK_RV sftk_defaultAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ void *value, unsigned int len);
+extern unsigned int sftk_MapTrust(CK_TRUST trust, PRBool clientAuth);
+
+extern SFTKObject *sftk_NewObject(SFTKSlot *slot);
+extern CK_RV sftk_CopyObject(SFTKObject *destObject, SFTKObject *srcObject);
+extern SFTKFreeStatus sftk_FreeObject(SFTKObject *object);
+extern CK_RV sftk_DeleteObject(SFTKSession *session, SFTKObject *object);
+extern void sftk_ReferenceObject(SFTKObject *object);
+extern SFTKObject *sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle,
+ SFTKSession *session);
+extern void sftk_AddSlotObject(SFTKSlot *slot, SFTKObject *object);
+extern void sftk_AddObject(SFTKSession *session, SFTKObject *object);
+/* clear out all the existing object ID to database key mappings.
+ * used to reinit a token */
+extern CK_RV SFTK_ClearTokenKeyHashTable(SFTKSlot *slot);
+
+extern CK_RV sftk_searchObjectList(SFTKSearchResults *search,
+ SFTKObject **head, unsigned int size,
+ PZLock *lock, CK_ATTRIBUTE_PTR inTemplate,
+ int count, PRBool isLoggedIn);
+extern SFTKObjectListElement *sftk_FreeObjectListElement(
+ SFTKObjectListElement *objectList);
+extern void sftk_FreeObjectList(SFTKObjectListElement *objectList);
+extern void sftk_FreeSearch(SFTKSearchResults *search);
+extern CK_RV sftk_handleObject(SFTKObject *object, SFTKSession *session);
+
+extern SFTKSlot *sftk_SlotFromID(CK_SLOT_ID slotID, PRBool all);
+extern SFTKSlot *sftk_SlotFromSessionHandle(CK_SESSION_HANDLE handle);
+extern SFTKSession *sftk_SessionFromHandle(CK_SESSION_HANDLE handle);
+extern void sftk_FreeSession(SFTKSession *session);
+extern SFTKSession *sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify,
+ CK_VOID_PTR pApplication, CK_FLAGS flags);
+extern void sftk_update_state(SFTKSlot *slot,SFTKSession *session);
+extern void sftk_update_all_states(SFTKSlot *slot);
+extern void sftk_FreeContext(SFTKSessionContext *context);
+extern void sftk_InitFreeLists(void);
+extern void sftk_CleanupFreeLists(void);
+
+extern NSSLOWKEYPublicKey *sftk_GetPubKey(SFTKObject *object,
+ CK_KEY_TYPE key_type, CK_RV *crvp);
+extern NSSLOWKEYPrivateKey *sftk_GetPrivKey(SFTKObject *object,
+ CK_KEY_TYPE key_type, CK_RV *crvp);
+extern void sftk_FormatDESKey(unsigned char *key, int length);
+extern PRBool sftk_CheckDESKey(unsigned char *key);
+extern PRBool sftk_IsWeakKey(unsigned char *key,CK_KEY_TYPE key_type);
+
+/* mechanism allows this operation */
+extern CK_RV sftk_MechAllowsOperation(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE op);
+
+/* helper function which calls nsslowkey_FindKeyByPublicKey after safely
+ * acquiring a reference to the keydb from the slot */
+NSSLOWKEYPrivateKey *sftk_FindKeyByPublicKey(SFTKSlot *slot, SECItem *dbKey);
+
+/*
+ * narrow objects
+ */
+SFTKSessionObject * sftk_narrowToSessionObject(SFTKObject *);
+SFTKTokenObject * sftk_narrowToTokenObject(SFTKObject *);
+
+/*
+ * token object utilities
+ */
+void sftk_addHandle(SFTKSearchResults *search, CK_OBJECT_HANDLE handle);
+PRBool sftk_poisonHandle(SFTKSlot *slot, SECItem *dbkey,
+ CK_OBJECT_HANDLE handle);
+SFTKObject * sftk_NewTokenObject(SFTKSlot *slot, SECItem *dbKey,
+ CK_OBJECT_HANDLE handle);
+SFTKTokenObject *sftk_convertSessionToToken(SFTKObject *so);
+
+/****************************************
+ * implement TLS Pseudo Random Function (PRF)
+ */
+
+extern CK_RV
+sftk_TLSPRFInit(SFTKSessionContext *context,
+ SFTKObject * key,
+ CK_KEY_TYPE key_type);
+
+SEC_END_PROTOS
+
+#endif /* _PKCS11I_H_ */
diff --git a/security/nss/lib/softoken/pkcs11n.h b/security/nss/lib/softoken/pkcs11n.h
new file mode 100644
index 000000000..345e5cb94
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11n.h
@@ -0,0 +1,289 @@
+/* ***** 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):
+ * Dr Stephen Henson <stephen.henson@gemplus.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 _PKCS11N_H_
+#define _PKCS11N_H_
+
+#ifdef DEBUG
+static const char CKT_CVS_ID[] = "@(#) $RCSfile$ $Revision$ $Date$";
+#endif /* DEBUG */
+
+/*
+ * pkcs11n.h
+ *
+ * This file contains the NSS-specific type definitions for Cryptoki
+ * (PKCS#11).
+ */
+
+/*
+ * NSSCK_VENDOR_NSS
+ *
+ * Cryptoki reserves the high half of all the number spaces for
+ * vendor-defined use. I'd like to keep all of our NSS-
+ * specific values together, but not in the oh-so-obvious
+ * 0x80000001, 0x80000002, etc. area. So I've picked an offset,
+ * and constructed values for the beginnings of our spaces.
+ *
+ * Note that some "historical" Netscape values don't fall within
+ * this range.
+ */
+#define NSSCK_VENDOR_NSS 0x4E534350 /* NSCP */
+
+/*
+ * NSS-defined object classes
+ *
+ */
+#define CKO_NSS (CKO_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+#define CKO_NSS_CRL (CKO_NSS + 1)
+#define CKO_NSS_SMIME (CKO_NSS + 2)
+#define CKO_NSS_TRUST (CKO_NSS + 3)
+#define CKO_NSS_BUILTIN_ROOT_LIST (CKO_NSS + 4)
+#define CKO_NSS_NEWSLOT (CKO_NSS + 5)
+#define CKO_NSS_DELSLOT (CKO_NSS + 6)
+
+
+/*
+ * NSS-defined key types
+ *
+ */
+#define CKK_NSS (CKK_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+#define CKK_NSS_PKCS8 (CKK_NSS + 1)
+/*
+ * NSS-defined certificate types
+ *
+ */
+#define CKC_NSS (CKC_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+/* FAKE PKCS #11 defines */
+#define CKA_DIGEST 0x81000000L
+#define CKA_FLAGS_ONLY 0 /* CKA_CLASS */
+
+/*
+ * NSS-defined object attributes
+ *
+ */
+#define CKA_NSS (CKA_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+#define CKA_NSS_URL (CKA_NSS + 1)
+#define CKA_NSS_EMAIL (CKA_NSS + 2)
+#define CKA_NSS_SMIME_INFO (CKA_NSS + 3)
+#define CKA_NSS_SMIME_TIMESTAMP (CKA_NSS + 4)
+#define CKA_NSS_PKCS8_SALT (CKA_NSS + 5)
+#define CKA_NSS_PASSWORD_CHECK (CKA_NSS + 6)
+#define CKA_NSS_EXPIRES (CKA_NSS + 7)
+#define CKA_NSS_KRL (CKA_NSS + 8)
+
+#define CKA_NSS_PQG_COUNTER (CKA_NSS + 20)
+#define CKA_NSS_PQG_SEED (CKA_NSS + 21)
+#define CKA_NSS_PQG_H (CKA_NSS + 22)
+#define CKA_NSS_PQG_SEED_BITS (CKA_NSS + 23)
+#define CKA_NSS_MODULE_SPEC (CKA_NSS + 24)
+#define CKA_NSS_OVERRIDE_EXTENSIONS (CKA_NSS + 25)
+
+/*
+ * Trust attributes:
+ *
+ * If trust goes standard, these probably will too. So I'll
+ * put them all in one place.
+ */
+
+#define CKA_TRUST (CKA_NSS + 0x2000)
+
+/* "Usage" key information */
+#define CKA_TRUST_DIGITAL_SIGNATURE (CKA_TRUST + 1)
+#define CKA_TRUST_NON_REPUDIATION (CKA_TRUST + 2)
+#define CKA_TRUST_KEY_ENCIPHERMENT (CKA_TRUST + 3)
+#define CKA_TRUST_DATA_ENCIPHERMENT (CKA_TRUST + 4)
+#define CKA_TRUST_KEY_AGREEMENT (CKA_TRUST + 5)
+#define CKA_TRUST_KEY_CERT_SIGN (CKA_TRUST + 6)
+#define CKA_TRUST_CRL_SIGN (CKA_TRUST + 7)
+
+/* "Purpose" trust information */
+#define CKA_TRUST_SERVER_AUTH (CKA_TRUST + 8)
+#define CKA_TRUST_CLIENT_AUTH (CKA_TRUST + 9)
+#define CKA_TRUST_CODE_SIGNING (CKA_TRUST + 10)
+#define CKA_TRUST_EMAIL_PROTECTION (CKA_TRUST + 11)
+#define CKA_TRUST_IPSEC_END_SYSTEM (CKA_TRUST + 12)
+#define CKA_TRUST_IPSEC_TUNNEL (CKA_TRUST + 13)
+#define CKA_TRUST_IPSEC_USER (CKA_TRUST + 14)
+#define CKA_TRUST_TIME_STAMPING (CKA_TRUST + 15)
+#define CKA_TRUST_STEP_UP_APPROVED (CKA_TRUST + 16)
+
+#define CKA_CERT_SHA1_HASH (CKA_TRUST + 100)
+#define CKA_CERT_MD5_HASH (CKA_TRUST + 101)
+
+/* NSS trust stuff */
+/* XXX fgmr new ones here-- step-up, etc. */
+
+/* HISTORICAL: define used to pass in the database key for DSA private keys */
+#define CKA_NETSCAPE_DB 0xD5A0DB00L
+#define CKA_NETSCAPE_TRUST 0x80000001L
+
+/* FAKE PKCS #11 defines */
+#define CKM_FAKE_RANDOM 0x80000efeUL
+#define CKM_INVALID_MECHANISM 0xffffffffUL
+
+/*
+ * NSS-defined crypto mechanisms
+ *
+ */
+#define CKM_NSS (CKM_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+#define CKM_NSS_AES_KEY_WRAP (CKM_NSS + 1)
+#define CKM_NSS_AES_KEY_WRAP_PAD (CKM_NSS + 2)
+
+/*
+ * HISTORICAL:
+ * Do not attempt to use these. They are only used by NETSCAPE's internal
+ * PKCS #11 interface. Most of these are place holders for other mechanism
+ * and will change in the future.
+ */
+#define CKM_NETSCAPE_PBE_SHA1_DES_CBC 0x80000002UL
+#define CKM_NETSCAPE_PBE_SHA1_TRIPLE_DES_CBC 0x80000003UL
+#define CKM_NETSCAPE_PBE_SHA1_40_BIT_RC2_CBC 0x80000004UL
+#define CKM_NETSCAPE_PBE_SHA1_128_BIT_RC2_CBC 0x80000005UL
+#define CKM_NETSCAPE_PBE_SHA1_40_BIT_RC4 0x80000006UL
+#define CKM_NETSCAPE_PBE_SHA1_128_BIT_RC4 0x80000007UL
+#define CKM_NETSCAPE_PBE_SHA1_FAULTY_3DES_CBC 0x80000008UL
+#define CKM_NETSCAPE_PBE_SHA1_HMAC_KEY_GEN 0x80000009UL
+#define CKM_NETSCAPE_PBE_MD5_HMAC_KEY_GEN 0x8000000aUL
+#define CKM_NETSCAPE_PBE_MD2_HMAC_KEY_GEN 0x8000000bUL
+
+#define CKM_TLS_PRF_GENERAL 0x80000373UL
+
+/*
+ * NSS-defined return values
+ *
+ */
+#define CKR_NSS (CKM_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+#define CKR_NSS_CERTDB_FAILED (CKR_NSS + 1)
+#define CKR_NSS_KEYDB_FAILED (CKR_NSS + 2)
+
+/*
+ * Trust info
+ *
+ * This isn't part of the Cryptoki standard (yet), so I'm putting
+ * all the definitions here. Some of this would move to nssckt.h
+ * if trust info were made part of the standard. In view of this
+ * possibility, I'm putting my (NSS) values in the NSS
+ * vendor space, like everything else.
+ */
+
+typedef CK_ULONG CK_TRUST;
+
+/* The following trust types are defined: */
+#define CKT_VENDOR_DEFINED 0x80000000
+
+#define CKT_NSS (CKT_VENDOR_DEFINED|NSSCK_VENDOR_NSS)
+
+/* If trust goes standard, these'll probably drop out of vendor space. */
+#define CKT_NSS_TRUSTED (CKT_NSS + 1)
+#define CKT_NSS_TRUSTED_DELEGATOR (CKT_NSS + 2)
+#define CKT_NSS_UNTRUSTED (CKT_NSS + 3)
+#define CKT_NSS_MUST_VERIFY (CKT_NSS + 4)
+#define CKT_NSS_TRUST_UNKNOWN (CKT_NSS + 5) /* default */
+
+/*
+ * These may well remain NSS-specific; I'm only using them
+ * to cache resolution data.
+ */
+#define CKT_NSS_VALID (CKT_NSS + 10)
+#define CKT_NSS_VALID_DELEGATOR (CKT_NSS + 11)
+
+/* don't leave old programs in a lurch just yet, give them the old NETSCAPE
+ * synonym */
+#define CKO_NETSCAPE_CRL CKO_NSS_CRL
+#define CKO_NETSCAPE_SMIME CKO_NSS_SMIME
+#define CKO_NETSCAPE_TRUST CKO_NSS_TRUST
+#define CKO_NETSCAPE_BUILTIN_ROOT_LIST CKO_NSS_BUILTIN_ROOT_LIST
+#define CKO_NETSCAPE_NEWSLOT CKO_NSS_NEWSLOT
+#define CKO_NETSCAPE_DELSLOT CKO_NSS_DELSLOT
+#define CKK_NETSCAPE_PKCS8 CKK_NSS_PKCS8
+#define CKA_NETSCAPE_URL CKA_NSS_URL
+#define CKA_NETSCAPE_EMAIL CKA_NSS_EMAIL
+#define CKA_NETSCAPE_SMIME_INFO CKA_NSS_SMIME_INFO
+#define CKA_NETSCAPE_SMIME_TIMESTAMP CKA_NSS_SMIME_TIMESTAMP
+#define CKA_NETSCAPE_PKCS8_SALT CKA_NSS_PKCS8_SALT
+#define CKA_NETSCAPE_PASSWORD_CHECK CKA_NSS_PASSWORD_CHECK
+#define CKA_NETSCAPE_EXPIRES CKA_NSS_EXPIRES
+#define CKA_NETSCAPE_KRL CKA_NSS_KRL
+#define CKA_NETSCAPE_PQG_COUNTER CKA_NSS_PQG_COUNTER
+#define CKA_NETSCAPE_PQG_SEED CKA_NSS_PQG_SEED
+#define CKA_NETSCAPE_PQG_H CKA_NSS_PQG_H
+#define CKA_NETSCAPE_PQG_SEED_BITS CKA_NSS_PQG_SEED_BITS
+#define CKA_NETSCAPE_MODULE_SPEC CKA_NSS_MODULE_SPEC
+#define CKM_NETSCAPE_AES_KEY_WRAP CKM_NSS_AES_KEY_WRAP
+#define CKM_NETSCAPE_AES_KEY_WRAP_PAD CKM_NSS_AES_KEY_WRAP_PAD
+#define CKR_NETSCAPE_CERTDB_FAILED CKR_NSS_CERTDB_FAILED
+#define CKR_NETSCAPE_KEYDB_FAILED CKR_NSS_KEYDB_FAILED
+#define CKT_NETSCAPE_TRUSTED CKT_NSS_TRUSTED
+#define CKT_NETSCAPE_TRUSTED_DELEGATOR CKT_NSS_TRUSTED_DELEGATOR
+#define CKT_NETSCAPE_UNTRUSTED CKT_NSS_UNTRUSTED
+#define CKT_NETSCAPE_MUST_VERIFY CKT_NSS_MUST_VERIFY
+#define CKT_NETSCAPE_TRUST_UNKNOWN CKT_NSS_TRUST_UNKNOWN
+#define CKT_NETSCAPE_VALID CKT_NSS_VALID
+#define CKT_NETSCAPE_VALID_DELEGATOR CKT_NSS_VALID_DELEGATOR
+
+/*
+ * These are not really PKCS #11 values specifically. They are the 'loadable'
+ * module spec NSS uses. The are available for others to use as well, but not
+ * part of the formal PKCS #11 spec.
+ *
+ * The function 'FIND' returns an array of PKCS #11 initialization strings
+ * The function 'ADD' takes a PKCS #11 initialization string and stores it.
+ * The function 'DEL' takes a 'name= library=' value and deletes the associated
+ * string.
+ * The function 'RELEASE' frees the array returned by 'FIND'
+ */
+#define SECMOD_MODULE_DB_FUNCTION_FIND 0
+#define SECMOD_MODULE_DB_FUNCTION_ADD 1
+#define SECMOD_MODULE_DB_FUNCTION_DEL 2
+#define SECMOD_MODULE_DB_FUNCTION_RELEASE 3
+typedef char ** (PR_CALLBACK *SECMODModuleDBFunc)(unsigned long function,
+ char *parameters, void *moduleSpec);
+
+/* softoken slot ID's */
+#define SFTK_MIN_USER_SLOT_ID 4
+#define SFTK_MAX_USER_SLOT_ID 100
+#define SFTK_MIN_FIPS_USER_SLOT_ID 101
+#define SFTK_MAX_FIPS_USER_SLOT_ID 127
+
+
+#endif /* _PKCS11N_H_ */
diff --git a/security/nss/lib/softoken/pkcs11ni.h b/security/nss/lib/softoken/pkcs11ni.h
new file mode 100644
index 000000000..9e584a9f0
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11ni.h
@@ -0,0 +1,52 @@
+/* ***** 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
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ *
+ * 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 _PKCS11NI_H_
+#define _PKCS11NI_H_
+
+/*
+ * pkcs11ni.h
+ *
+ * This file contains softoken private exports for NSS
+ */
+
+/* softoken slot ID's */
+#define SFTK_MIN_USER_SLOT_ID 4
+#define SFTK_MAX_USER_SLOT_ID 100
+#define SFTK_MIN_FIPS_USER_SLOT_ID 101
+#define SFTK_MAX_FIPS_USER_SLOT_ID 127
+
+
+#endif /* _PKCS11NI_H_ */
diff --git a/security/nss/lib/softoken/pkcs11p.h b/security/nss/lib/softoken/pkcs11p.h
new file mode 100644
index 000000000..f1ea25805
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11p.h
@@ -0,0 +1,54 @@
+/* ***** 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 ***** */
+/*
+ * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document
+ * is granted provided that it is identified as "RSA Security Inc. Public-Key
+ * Cryptography Standards (PKCS)" in all material mentioning or referencing
+ * this document.
+ */
+/* these data types are platform/implementation dependent. */
+/*
+ * Packing was removed from the shipped RSA header files, even
+ * though it's still needed. put in a central file to help merging..
+ */
+
+#if defined(_WIN32)
+#ifdef _MSC_VER
+#pragma warning(disable:4103)
+#endif
+#pragma pack(push, cryptoki, 1)
+#endif
+
diff --git a/security/nss/lib/softoken/pkcs11t.h b/security/nss/lib/softoken/pkcs11t.h
new file mode 100644
index 000000000..104fa698a
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11t.h
@@ -0,0 +1,1774 @@
+/* ***** 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
+ * RSA Security, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/* License to copy and use this software is granted provided that it is
+ * identified as "RSA Security Inc. PKCS #11 Cryptographic Token Interface
+ * (Cryptoki)" in all material mentioning or referencing this software.
+
+ * License is also granted to make and use derivative works provided that
+ * such works are identified as "derived from the RSA Security Inc. PKCS #11
+ * Cryptographic Token Interface (Cryptoki)" in all material mentioning or
+ * referencing the derived work.
+
+ * RSA Security Inc. makes no representations concerning either the
+ * merchantability of this software or the suitability of this software for
+ * any particular purpose. It is provided "as is" without express or implied
+ * warranty of any kind.
+ */
+
+
+#ifndef _PKCS11T_H_
+#define _PKCS11T_H_ 1
+
+#define CK_TRUE 1
+#define CK_FALSE 0
+
+#include "prtypes.h"
+
+#define CK_PTR *
+#define CK_NULL_PTR 0
+#define CK_CALLBACK_FUNCTION(rtype,func) rtype (PR_CALLBACK * func)
+#define CK_DECLARE_FUNCTION(rtype,func) extern rtype func
+#define CK_DECLARE_FUNCTION_POINTER(rtype,func) rtype (PR_CALLBACK * func)
+
+#define CK_INVALID_SESSION 0
+
+/* an unsigned 8-bit value */
+typedef unsigned char CK_BYTE;
+
+/* an unsigned 8-bit character */
+typedef CK_BYTE CK_CHAR;
+
+/* an 8-bit UTF-8 character */
+typedef CK_BYTE CK_UTF8CHAR;
+
+/* a BYTE-sized Boolean flag */
+typedef CK_BYTE CK_BBOOL;
+
+/* an unsigned value, at least 32 bits long */
+typedef unsigned long int CK_ULONG;
+
+/* a signed value, the same size as a CK_ULONG */
+/* CK_LONG is new for v2.0 */
+typedef long int CK_LONG;
+
+/* at least 32 bits; each bit is a Boolean flag */
+typedef CK_ULONG CK_FLAGS;
+
+
+/* some special values for certain CK_ULONG variables */
+#define CK_UNAVAILABLE_INFORMATION (~0UL)
+#define CK_EFFECTIVELY_INFINITE 0
+
+
+typedef CK_BYTE CK_PTR CK_BYTE_PTR;
+typedef CK_CHAR CK_PTR CK_CHAR_PTR;
+typedef CK_UTF8CHAR CK_PTR CK_UTF8CHAR_PTR;
+typedef CK_ULONG CK_PTR CK_ULONG_PTR;
+typedef void CK_PTR CK_VOID_PTR;
+
+/* Pointer to a CK_VOID_PTR-- i.e., pointer to pointer to void */
+typedef CK_VOID_PTR CK_PTR CK_VOID_PTR_PTR;
+
+
+/* The following value is always invalid if used as a session */
+/* handle or object handle */
+#define CK_INVALID_HANDLE 0
+
+
+/* pack */
+#include "pkcs11p.h"
+
+typedef struct CK_VERSION {
+ CK_BYTE major; /* integer portion of version number */
+ CK_BYTE minor; /* 1/100ths portion of version number */
+} CK_VERSION;
+
+typedef CK_VERSION CK_PTR CK_VERSION_PTR;
+
+
+typedef struct CK_INFO {
+ /* manufacturerID and libraryDecription have been changed from
+ * CK_CHAR to CK_UTF8CHAR for v2.10 */
+ CK_VERSION cryptokiVersion; /* PKCS #11 interface ver */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags; /* must be zero */
+
+ /* libraryDescription and libraryVersion are new for v2.0 */
+ CK_UTF8CHAR libraryDescription[32]; /* blank padded */
+ CK_VERSION libraryVersion; /* version of library */
+} CK_INFO;
+
+typedef CK_INFO CK_PTR CK_INFO_PTR;
+
+
+/* CK_NOTIFICATION enumerates the types of notifications that
+ * PKCS #11 provides to an application */
+/* CK_NOTIFICATION has been changed from an enum to a CK_ULONG
+ * for v2.0 */
+typedef CK_ULONG CK_NOTIFICATION;
+#define CKN_SURRENDER 0
+
+
+typedef CK_ULONG CK_SLOT_ID;
+
+typedef CK_SLOT_ID CK_PTR CK_SLOT_ID_PTR;
+
+
+/* CK_SLOT_INFO provides information about a slot */
+typedef struct CK_SLOT_INFO {
+ /* slotDescription and manufacturerID have been changed from
+ * CK_CHAR to CK_UTF8CHAR for v2.10 */
+ CK_UTF8CHAR slotDescription[64]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_FLAGS flags;
+
+ /* hardwareVersion and firmwareVersion are new for v2.0 */
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+} CK_SLOT_INFO;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_TOKEN_PRESENT 0x00000001 /* a token is there */
+#define CKF_REMOVABLE_DEVICE 0x00000002 /* removable devices*/
+#define CKF_HW_SLOT 0x00000004 /* hardware slot */
+
+typedef CK_SLOT_INFO CK_PTR CK_SLOT_INFO_PTR;
+
+
+/* CK_TOKEN_INFO provides information about a token */
+typedef struct CK_TOKEN_INFO {
+ /* label, manufacturerID, and model have been changed from
+ * CK_CHAR to CK_UTF8CHAR for v2.10 */
+ CK_UTF8CHAR label[32]; /* blank padded */
+ CK_UTF8CHAR manufacturerID[32]; /* blank padded */
+ CK_UTF8CHAR model[16]; /* blank padded */
+ CK_CHAR serialNumber[16]; /* blank padded */
+ CK_FLAGS flags; /* see below */
+
+ /* ulMaxSessionCount, ulSessionCount, ulMaxRwSessionCount,
+ * ulRwSessionCount, ulMaxPinLen, and ulMinPinLen have all been
+ * changed from CK_USHORT to CK_ULONG for v2.0 */
+ CK_ULONG ulMaxSessionCount; /* max open sessions */
+ CK_ULONG ulSessionCount; /* sess. now open */
+ CK_ULONG ulMaxRwSessionCount; /* max R/W sessions */
+ CK_ULONG ulRwSessionCount; /* R/W sess. now open */
+ CK_ULONG ulMaxPinLen; /* in bytes */
+ CK_ULONG ulMinPinLen; /* in bytes */
+ CK_ULONG ulTotalPublicMemory; /* in bytes */
+ CK_ULONG ulFreePublicMemory; /* in bytes */
+ CK_ULONG ulTotalPrivateMemory; /* in bytes */
+ CK_ULONG ulFreePrivateMemory; /* in bytes */
+
+ /* hardwareVersion, firmwareVersion, and time are new for
+ * v2.0 */
+ CK_VERSION hardwareVersion; /* version of hardware */
+ CK_VERSION firmwareVersion; /* version of firmware */
+ CK_CHAR utcTime[16]; /* time */
+} CK_TOKEN_INFO;
+
+/* The flags parameter is defined as follows:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RNG 0x00000001 /* has random #
+ * generator */
+#define CKF_WRITE_PROTECTED 0x00000002 /* token is
+ * write-
+ * protected */
+#define CKF_LOGIN_REQUIRED 0x00000004 /* user must
+ * login */
+#define CKF_USER_PIN_INITIALIZED 0x00000008 /* normal user's
+ * PIN is set */
+
+/* CKF_RESTORE_KEY_NOT_NEEDED is new for v2.0. If it is set,
+ * that means that *every* time the state of cryptographic
+ * operations of a session is successfully saved, all keys
+ * needed to continue those operations are stored in the state */
+#define CKF_RESTORE_KEY_NOT_NEEDED 0x00000020
+
+/* CKF_CLOCK_ON_TOKEN is new for v2.0. If it is set, that means
+ * that the token has some sort of clock. The time on that
+ * clock is returned in the token info structure */
+#define CKF_CLOCK_ON_TOKEN 0x00000040
+
+/* CKF_PROTECTED_AUTHENTICATION_PATH is new for v2.0. If it is
+ * set, that means that there is some way for the user to login
+ * without sending a PIN through the PKCS #11 library itself */
+#define CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100
+
+/* CKF_DUAL_CRYPTO_OPERATIONS is new for v2.0. If it is true,
+ * that means that a single session with the token can perform
+ * dual simultaneous cryptographic operations (digest and
+ * encrypt; decrypt and digest; sign and encrypt; and decrypt
+ * and sign) */
+#define CKF_DUAL_CRYPTO_OPERATIONS 0x00000200
+
+/* CKF_TOKEN_INITIALIZED if new for v2.10. If it is true, the
+ * token has been initialized using C_InitializeToken or an
+ * equivalent mechanism outside the scope of PKCS #11.
+ * Calling C_InitializeToken when this flag is set will cause
+ * the token to be reinitialized. */
+#define CKF_TOKEN_INITIALIZED 0x00000400
+
+/* CKF_SECONDARY_AUTHENTICATION if new for v2.10. If it is
+ * true, the token supports secondary authentication for
+ * private key objects. This flag is deprecated in v2.11 and
+ onwards. */
+#define CKF_SECONDARY_AUTHENTICATION 0x00000800
+
+/* CKF_USER_PIN_COUNT_LOW if new for v2.10. If it is true, an
+ * incorrect user login PIN has been entered at least once
+ * since the last successful authentication. */
+#define CKF_USER_PIN_COUNT_LOW 0x00010000
+
+/* CKF_USER_PIN_FINAL_TRY if new for v2.10. If it is true,
+ * supplying an incorrect user PIN will it to become locked. */
+#define CKF_USER_PIN_FINAL_TRY 0x00020000
+
+/* CKF_USER_PIN_LOCKED if new for v2.10. If it is true, the
+ * user PIN has been locked. User login to the token is not
+ * possible. */
+#define CKF_USER_PIN_LOCKED 0x00040000
+
+/* CKF_USER_PIN_TO_BE_CHANGED if new for v2.10. If it is true,
+ * the user PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card. */
+#define CKF_USER_PIN_TO_BE_CHANGED 0x00080000
+
+/* CKF_SO_PIN_COUNT_LOW if new for v2.10. If it is true, an
+ * incorrect SO login PIN has been entered at least once since
+ * the last successful authentication. */
+#define CKF_SO_PIN_COUNT_LOW 0x00100000
+
+/* CKF_SO_PIN_FINAL_TRY if new for v2.10. If it is true,
+ * supplying an incorrect SO PIN will it to become locked. */
+#define CKF_SO_PIN_FINAL_TRY 0x00200000
+
+/* CKF_SO_PIN_LOCKED if new for v2.10. If it is true, the SO
+ * PIN has been locked. SO login to the token is not possible.
+ */
+#define CKF_SO_PIN_LOCKED 0x00400000
+
+/* CKF_SO_PIN_TO_BE_CHANGED if new for v2.10. If it is true,
+ * the SO PIN value is the default value set by token
+ * initialization or manufacturing, or the PIN has been
+ * expired by the card. */
+#define CKF_SO_PIN_TO_BE_CHANGED 0x00800000
+
+typedef CK_TOKEN_INFO CK_PTR CK_TOKEN_INFO_PTR;
+
+
+/* CK_SESSION_HANDLE is a PKCS #11-assigned value that
+ * identifies a session */
+typedef CK_ULONG CK_SESSION_HANDLE;
+
+typedef CK_SESSION_HANDLE CK_PTR CK_SESSION_HANDLE_PTR;
+
+
+/* CK_USER_TYPE enumerates the types of PKCS #11 users */
+/* CK_USER_TYPE has been changed from an enum to a CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_USER_TYPE;
+/* Security Officer */
+#define CKU_SO 0
+/* Normal user */
+#define CKU_USER 1
+/* Context specific (added in v2.20) */
+#define CKU_CONTEXT_SPECIFIC 2
+
+/* CK_STATE enumerates the session states */
+/* CK_STATE has been changed from an enum to a CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_STATE;
+#define CKS_RO_PUBLIC_SESSION 0
+#define CKS_RO_USER_FUNCTIONS 1
+#define CKS_RW_PUBLIC_SESSION 2
+#define CKS_RW_USER_FUNCTIONS 3
+#define CKS_RW_SO_FUNCTIONS 4
+
+
+/* CK_SESSION_INFO provides information about a session */
+typedef struct CK_SESSION_INFO {
+ CK_SLOT_ID slotID;
+ CK_STATE state;
+ CK_FLAGS flags; /* see below */
+
+ /* ulDeviceError was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+ CK_ULONG ulDeviceError; /* device-dependent error code */
+} CK_SESSION_INFO;
+
+/* The flags are defined in the following table:
+ * Bit Flag Mask Meaning
+ */
+#define CKF_RW_SESSION 0x00000002 /* session is r/w */
+#define CKF_SERIAL_SESSION 0x00000004 /* no parallel */
+
+typedef CK_SESSION_INFO CK_PTR CK_SESSION_INFO_PTR;
+
+
+/* CK_OBJECT_HANDLE is a token-specific identifier for an
+ * object */
+typedef CK_ULONG CK_OBJECT_HANDLE;
+
+typedef CK_OBJECT_HANDLE CK_PTR CK_OBJECT_HANDLE_PTR;
+
+
+/* CK_OBJECT_CLASS is a value that identifies the classes (or
+ * types) of objects that PKCS #11 recognizes. It is defined
+ * as follows: */
+/* CK_OBJECT_CLASS was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_OBJECT_CLASS;
+
+/* The following classes of objects are defined: */
+/* CKO_HW_FEATURE is new for v2.10 */
+/* CKO_DOMAIN_PARAMETERS is new for v2.11 */
+/* CKO_MECHANISM is new for v2.20 */
+#define CKO_DATA 0x00000000
+#define CKO_CERTIFICATE 0x00000001
+#define CKO_PUBLIC_KEY 0x00000002
+#define CKO_PRIVATE_KEY 0x00000003
+#define CKO_SECRET_KEY 0x00000004
+#define CKO_HW_FEATURE 0x00000005
+#define CKO_DOMAIN_PARAMETERS 0x00000006
+#define CKO_MECHANISM 0x00000007
+#define CKO_VENDOR_DEFINED 0x80000000
+
+typedef CK_OBJECT_CLASS CK_PTR CK_OBJECT_CLASS_PTR;
+
+/* CK_HW_FEATURE_TYPE is new for v2.10. CK_HW_FEATURE_TYPE is a
+ * value that identifies the hardware feature type of an object
+ * with CK_OBJECT_CLASS equal to CKO_HW_FEATURE. */
+typedef CK_ULONG CK_HW_FEATURE_TYPE;
+
+/* The following hardware feature types are defined */
+/* CKH_USER_INTERFACE is new for v2.20 */
+#define CKH_MONOTONIC_COUNTER 0x00000001
+#define CKH_CLOCK 0x00000002
+#define CKH_USER_INTERFACE 0x00000003
+#define CKH_VENDOR_DEFINED 0x80000000
+
+/* CK_KEY_TYPE is a value that identifies a key type */
+/* CK_KEY_TYPE was changed from CK_USHORT to CK_ULONG for v2.0 */
+typedef CK_ULONG CK_KEY_TYPE;
+
+/* the following key types are defined: */
+#define CKK_RSA 0x00000000
+#define CKK_DSA 0x00000001
+#define CKK_DH 0x00000002
+
+/* CKK_ECDSA and CKK_KEA are new for v2.0 */
+/* CKK_ECDSA is deprecated in v2.11, CKK_EC is preferred. */
+#define CKK_ECDSA 0x00000003
+#define CKK_EC 0x00000003
+#define CKK_X9_42_DH 0x00000004
+#define CKK_KEA 0x00000005
+
+#define CKK_GENERIC_SECRET 0x00000010
+#define CKK_RC2 0x00000011
+#define CKK_RC4 0x00000012
+#define CKK_DES 0x00000013
+#define CKK_DES2 0x00000014
+#define CKK_DES3 0x00000015
+
+/* all these key types are new for v2.0 */
+#define CKK_CAST 0x00000016
+#define CKK_CAST3 0x00000017
+/* CKK_CAST5 is deprecated in v2.11, CKK_CAST128 is preferred. */
+#define CKK_CAST5 0x00000018
+#define CKK_CAST128 0x00000018
+#define CKK_RC5 0x00000019
+#define CKK_IDEA 0x0000001A
+#define CKK_SKIPJACK 0x0000001B
+#define CKK_BATON 0x0000001C
+#define CKK_JUNIPER 0x0000001D
+#define CKK_CDMF 0x0000001E
+#define CKK_AES 0x0000001F
+
+/* BlowFish and TwoFish are new for v2.20 */
+#define CKK_BLOWFISH 0x00000020
+#define CKK_TWOFISH 0x00000021
+
+/* Camellia is proposed for v2.20 Amendment 3 */
+#define CKK_CAMELLIA 0x00000025
+
+#define CKK_VENDOR_DEFINED 0x80000000
+
+
+/* CK_CERTIFICATE_TYPE is a value that identifies a certificate
+ * type */
+/* CK_CERTIFICATE_TYPE was changed from CK_USHORT to CK_ULONG
+ * for v2.0 */
+typedef CK_ULONG CK_CERTIFICATE_TYPE;
+
+/* The following certificate types are defined: */
+/* CKC_X_509_ATTR_CERT is new for v2.10 */
+/* CKC_WTLS is new for v2.20 */
+#define CKC_X_509 0x00000000
+#define CKC_X_509_ATTR_CERT 0x00000001
+#define CKC_WTLS 0x00000002
+#define CKC_VENDOR_DEFINED 0x80000000
+
+
+/* CK_ATTRIBUTE_TYPE is a value that identifies an attribute
+ * type */
+/* CK_ATTRIBUTE_TYPE was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_ATTRIBUTE_TYPE;
+
+/* The CKF_ARRAY_ATTRIBUTE flag identifies an attribute which
+ consists of an array of values. */
+#define CKF_ARRAY_ATTRIBUTE 0x40000000
+
+/* The following attribute types are defined: */
+#define CKA_CLASS 0x00000000
+#define CKA_TOKEN 0x00000001
+#define CKA_PRIVATE 0x00000002
+#define CKA_LABEL 0x00000003
+#define CKA_APPLICATION 0x00000010
+#define CKA_VALUE 0x00000011
+
+/* CKA_OBJECT_ID is new for v2.10 */
+#define CKA_OBJECT_ID 0x00000012
+
+#define CKA_CERTIFICATE_TYPE 0x00000080
+#define CKA_ISSUER 0x00000081
+#define CKA_SERIAL_NUMBER 0x00000082
+
+/* CKA_AC_ISSUER, CKA_OWNER, and CKA_ATTR_TYPES are new
+ * for v2.10 */
+#define CKA_AC_ISSUER 0x00000083
+#define CKA_OWNER 0x00000084
+#define CKA_ATTR_TYPES 0x00000085
+
+/* CKA_TRUSTED is new for v2.11 */
+#define CKA_TRUSTED 0x00000086
+
+/* CKA_CERTIFICATE_CATEGORY ...
+ * CKA_CHECK_VALUE are new for v2.20 */
+#define CKA_CERTIFICATE_CATEGORY 0x00000087
+#define CKA_JAVA_MIDP_SECURITY_DOMAIN 0x00000088
+#define CKA_URL 0x00000089
+#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY 0x0000008A
+#define CKA_HASH_OF_ISSUER_PUBLIC_KEY 0x0000008B
+#define CKA_CHECK_VALUE 0x00000090
+
+#define CKA_KEY_TYPE 0x00000100
+#define CKA_SUBJECT 0x00000101
+#define CKA_ID 0x00000102
+#define CKA_SENSITIVE 0x00000103
+#define CKA_ENCRYPT 0x00000104
+#define CKA_DECRYPT 0x00000105
+#define CKA_WRAP 0x00000106
+#define CKA_UNWRAP 0x00000107
+#define CKA_SIGN 0x00000108
+#define CKA_SIGN_RECOVER 0x00000109
+#define CKA_VERIFY 0x0000010A
+#define CKA_VERIFY_RECOVER 0x0000010B
+#define CKA_DERIVE 0x0000010C
+#define CKA_START_DATE 0x00000110
+#define CKA_END_DATE 0x00000111
+#define CKA_MODULUS 0x00000120
+#define CKA_MODULUS_BITS 0x00000121
+#define CKA_PUBLIC_EXPONENT 0x00000122
+#define CKA_PRIVATE_EXPONENT 0x00000123
+#define CKA_PRIME_1 0x00000124
+#define CKA_PRIME_2 0x00000125
+#define CKA_EXPONENT_1 0x00000126
+#define CKA_EXPONENT_2 0x00000127
+#define CKA_COEFFICIENT 0x00000128
+#define CKA_PRIME 0x00000130
+#define CKA_SUBPRIME 0x00000131
+#define CKA_BASE 0x00000132
+
+/* CKA_PRIME_BITS and CKA_SUB_PRIME_BITS are new for v2.11 */
+#define CKA_PRIME_BITS 0x00000133
+#define CKA_SUBPRIME_BITS 0x00000134
+#define CKA_SUB_PRIME_BITS CKA_SUBPRIME_BITS
+/* (To retain backwards-compatibility) */
+
+#define CKA_VALUE_BITS 0x00000160
+#define CKA_VALUE_LEN 0x00000161
+
+/* CKA_EXTRACTABLE, CKA_LOCAL, CKA_NEVER_EXTRACTABLE,
+ * CKA_ALWAYS_SENSITIVE, CKA_MODIFIABLE, CKA_ECDSA_PARAMS,
+ * and CKA_EC_POINT are new for v2.0 */
+#define CKA_EXTRACTABLE 0x00000162
+#define CKA_LOCAL 0x00000163
+#define CKA_NEVER_EXTRACTABLE 0x00000164
+#define CKA_ALWAYS_SENSITIVE 0x00000165
+
+/* CKA_KEY_GEN_MECHANISM is new for v2.11 */
+#define CKA_KEY_GEN_MECHANISM 0x00000166
+
+#define CKA_MODIFIABLE 0x00000170
+
+/* CKA_ECDSA_PARAMS is deprecated in v2.11,
+ * CKA_EC_PARAMS is preferred. */
+#define CKA_ECDSA_PARAMS 0x00000180
+#define CKA_EC_PARAMS 0x00000180
+
+#define CKA_EC_POINT 0x00000181
+
+/* CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
+ * are new for v2.10. Deprecated in v2.11 and onwards. */
+#define CKA_SECONDARY_AUTH 0x00000200
+#define CKA_AUTH_PIN_FLAGS 0x00000201
+
+/* CKA_ALWAYS_AUTHENTICATE ...
+ * CKA_UNWRAP_TEMPLATE are new for v2.20 */
+#define CKA_ALWAYS_AUTHENTICATE 0x00000202
+
+#define CKA_WRAP_WITH_TRUSTED 0x00000210
+#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000211)
+#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE|0x00000212)
+
+/* CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, and CKA_HAS_RESET
+ * are new for v2.10 */
+#define CKA_HW_FEATURE_TYPE 0x00000300
+#define CKA_RESET_ON_INIT 0x00000301
+#define CKA_HAS_RESET 0x00000302
+
+/* The following attributes are new for v2.20 */
+#define CKA_PIXEL_X 0x00000400
+#define CKA_PIXEL_Y 0x00000401
+#define CKA_RESOLUTION 0x00000402
+#define CKA_CHAR_ROWS 0x00000403
+#define CKA_CHAR_COLUMNS 0x00000404
+#define CKA_COLOR 0x00000405
+#define CKA_BITS_PER_PIXEL 0x00000406
+#define CKA_CHAR_SETS 0x00000480
+#define CKA_ENCODING_METHODS 0x00000481
+#define CKA_MIME_TYPES 0x00000482
+#define CKA_MECHANISM_TYPE 0x00000500
+#define CKA_REQUIRED_CMS_ATTRIBUTES 0x00000501
+#define CKA_DEFAULT_CMS_ATTRIBUTES 0x00000502
+#define CKA_SUPPORTED_CMS_ATTRIBUTES 0x00000503
+#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE|0x00000600)
+
+#define CKA_VENDOR_DEFINED 0x80000000
+
+
+/* CK_ATTRIBUTE is a structure that includes the type, length
+ * and value of an attribute */
+typedef struct CK_ATTRIBUTE {
+ CK_ATTRIBUTE_TYPE type;
+ CK_VOID_PTR pValue;
+
+ /* ulValueLen went from CK_USHORT to CK_ULONG for v2.0 */
+ CK_ULONG ulValueLen; /* in bytes */
+} CK_ATTRIBUTE;
+
+typedef CK_ATTRIBUTE CK_PTR CK_ATTRIBUTE_PTR;
+
+
+/* CK_DATE is a structure that defines a date */
+typedef struct CK_DATE{
+ CK_CHAR year[4]; /* the year ("1900" - "9999") */
+ CK_CHAR month[2]; /* the month ("01" - "12") */
+ CK_CHAR day[2]; /* the day ("01" - "31") */
+} CK_DATE;
+
+
+/* CK_MECHANISM_TYPE is a value that identifies a mechanism
+ * type */
+/* CK_MECHANISM_TYPE was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+typedef CK_ULONG CK_MECHANISM_TYPE;
+
+/* the following mechanism types are defined: */
+#define CKM_RSA_PKCS_KEY_PAIR_GEN 0x00000000
+#define CKM_RSA_PKCS 0x00000001
+#define CKM_RSA_9796 0x00000002
+#define CKM_RSA_X_509 0x00000003
+
+/* CKM_MD2_RSA_PKCS, CKM_MD5_RSA_PKCS, and CKM_SHA1_RSA_PKCS
+ * are new for v2.0. They are mechanisms which hash and sign */
+#define CKM_MD2_RSA_PKCS 0x00000004
+#define CKM_MD5_RSA_PKCS 0x00000005
+#define CKM_SHA1_RSA_PKCS 0x00000006
+
+/* CKM_RIPEMD128_RSA_PKCS, CKM_RIPEMD160_RSA_PKCS, and
+ * CKM_RSA_PKCS_OAEP are new for v2.10 */
+#define CKM_RIPEMD128_RSA_PKCS 0x00000007
+#define CKM_RIPEMD160_RSA_PKCS 0x00000008
+#define CKM_RSA_PKCS_OAEP 0x00000009
+
+/* CKM_RSA_X9_31_KEY_PAIR_GEN, CKM_RSA_X9_31, CKM_SHA1_RSA_X9_31,
+ * CKM_RSA_PKCS_PSS, and CKM_SHA1_RSA_PKCS_PSS are new for v2.11 */
+#define CKM_RSA_X9_31_KEY_PAIR_GEN 0x0000000A
+#define CKM_RSA_X9_31 0x0000000B
+#define CKM_SHA1_RSA_X9_31 0x0000000C
+#define CKM_RSA_PKCS_PSS 0x0000000D
+#define CKM_SHA1_RSA_PKCS_PSS 0x0000000E
+
+#define CKM_DSA_KEY_PAIR_GEN 0x00000010
+#define CKM_DSA 0x00000011
+#define CKM_DSA_SHA1 0x00000012
+#define CKM_DH_PKCS_KEY_PAIR_GEN 0x00000020
+#define CKM_DH_PKCS_DERIVE 0x00000021
+
+/* CKM_X9_42_DH_KEY_PAIR_GEN, CKM_X9_42_DH_DERIVE,
+ * CKM_X9_42_DH_HYBRID_DERIVE, and CKM_X9_42_MQV_DERIVE are new for
+ * v2.11 */
+#define CKM_X9_42_DH_KEY_PAIR_GEN 0x00000030
+#define CKM_X9_42_DH_DERIVE 0x00000031
+#define CKM_X9_42_DH_HYBRID_DERIVE 0x00000032
+#define CKM_X9_42_MQV_DERIVE 0x00000033
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256_RSA_PKCS 0x00000040
+#define CKM_SHA384_RSA_PKCS 0x00000041
+#define CKM_SHA512_RSA_PKCS 0x00000042
+#define CKM_SHA256_RSA_PKCS_PSS 0x00000043
+#define CKM_SHA384_RSA_PKCS_PSS 0x00000044
+#define CKM_SHA512_RSA_PKCS_PSS 0x00000045
+
+/* CKM_SHA224 new for v2.20 amendment 3 */
+#define CKM_SHA224_RSA_PKCS 0x00000046
+#define CKM_SHA224_RSA_PKCS_PSS 0x00000047
+
+#define CKM_RC2_KEY_GEN 0x00000100
+#define CKM_RC2_ECB 0x00000101
+#define CKM_RC2_CBC 0x00000102
+#define CKM_RC2_MAC 0x00000103
+
+/* CKM_RC2_MAC_GENERAL and CKM_RC2_CBC_PAD are new for v2.0 */
+#define CKM_RC2_MAC_GENERAL 0x00000104
+#define CKM_RC2_CBC_PAD 0x00000105
+
+#define CKM_RC4_KEY_GEN 0x00000110
+#define CKM_RC4 0x00000111
+#define CKM_DES_KEY_GEN 0x00000120
+#define CKM_DES_ECB 0x00000121
+#define CKM_DES_CBC 0x00000122
+#define CKM_DES_MAC 0x00000123
+
+/* CKM_DES_MAC_GENERAL and CKM_DES_CBC_PAD are new for v2.0 */
+#define CKM_DES_MAC_GENERAL 0x00000124
+#define CKM_DES_CBC_PAD 0x00000125
+
+#define CKM_DES2_KEY_GEN 0x00000130
+#define CKM_DES3_KEY_GEN 0x00000131
+#define CKM_DES3_ECB 0x00000132
+#define CKM_DES3_CBC 0x00000133
+#define CKM_DES3_MAC 0x00000134
+
+/* CKM_DES3_MAC_GENERAL, CKM_DES3_CBC_PAD, CKM_CDMF_KEY_GEN,
+ * CKM_CDMF_ECB, CKM_CDMF_CBC, CKM_CDMF_MAC,
+ * CKM_CDMF_MAC_GENERAL, and CKM_CDMF_CBC_PAD are new for v2.0 */
+#define CKM_DES3_MAC_GENERAL 0x00000135
+#define CKM_DES3_CBC_PAD 0x00000136
+#define CKM_CDMF_KEY_GEN 0x00000140
+#define CKM_CDMF_ECB 0x00000141
+#define CKM_CDMF_CBC 0x00000142
+#define CKM_CDMF_MAC 0x00000143
+#define CKM_CDMF_MAC_GENERAL 0x00000144
+#define CKM_CDMF_CBC_PAD 0x00000145
+
+/* the following four DES mechanisms are new for v2.20 */
+#define CKM_DES_OFB64 0x00000150
+#define CKM_DES_OFB8 0x00000151
+#define CKM_DES_CFB64 0x00000152
+#define CKM_DES_CFB8 0x00000153
+
+#define CKM_MD2 0x00000200
+
+/* CKM_MD2_HMAC and CKM_MD2_HMAC_GENERAL are new for v2.0 */
+#define CKM_MD2_HMAC 0x00000201
+#define CKM_MD2_HMAC_GENERAL 0x00000202
+
+#define CKM_MD5 0x00000210
+
+/* CKM_MD5_HMAC and CKM_MD5_HMAC_GENERAL are new for v2.0 */
+#define CKM_MD5_HMAC 0x00000211
+#define CKM_MD5_HMAC_GENERAL 0x00000212
+
+#define CKM_SHA_1 0x00000220
+
+/* CKM_SHA_1_HMAC and CKM_SHA_1_HMAC_GENERAL are new for v2.0 */
+#define CKM_SHA_1_HMAC 0x00000221
+#define CKM_SHA_1_HMAC_GENERAL 0x00000222
+
+/* CKM_RIPEMD128, CKM_RIPEMD128_HMAC,
+ * CKM_RIPEMD128_HMAC_GENERAL, CKM_RIPEMD160, CKM_RIPEMD160_HMAC,
+ * and CKM_RIPEMD160_HMAC_GENERAL are new for v2.10 */
+#define CKM_RIPEMD128 0x00000230
+#define CKM_RIPEMD128_HMAC 0x00000231
+#define CKM_RIPEMD128_HMAC_GENERAL 0x00000232
+#define CKM_RIPEMD160 0x00000240
+#define CKM_RIPEMD160_HMAC 0x00000241
+#define CKM_RIPEMD160_HMAC_GENERAL 0x00000242
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256 0x00000250
+#define CKM_SHA256_HMAC 0x00000251
+#define CKM_SHA256_HMAC_GENERAL 0x00000252
+#define CKM_SHA384 0x00000260
+#define CKM_SHA384_HMAC 0x00000261
+#define CKM_SHA384_HMAC_GENERAL 0x00000262
+#define CKM_SHA512 0x00000270
+#define CKM_SHA512_HMAC 0x00000271
+#define CKM_SHA512_HMAC_GENERAL 0x00000272
+
+/* CKM_SHA224 new for v2.20 amendment 3 */
+#define CKM_SHA224 0x00000255
+#define CKM_SHA224_HMAC 0x00000256
+#define CKM_SHA224_HMAC_GENERAL 0x00000257
+
+/* All of the following mechanisms are new for v2.0 */
+/* Note that CAST128 and CAST5 are the same algorithm */
+#define CKM_CAST_KEY_GEN 0x00000300
+#define CKM_CAST_ECB 0x00000301
+#define CKM_CAST_CBC 0x00000302
+#define CKM_CAST_MAC 0x00000303
+#define CKM_CAST_MAC_GENERAL 0x00000304
+#define CKM_CAST_CBC_PAD 0x00000305
+#define CKM_CAST3_KEY_GEN 0x00000310
+#define CKM_CAST3_ECB 0x00000311
+#define CKM_CAST3_CBC 0x00000312
+#define CKM_CAST3_MAC 0x00000313
+#define CKM_CAST3_MAC_GENERAL 0x00000314
+#define CKM_CAST3_CBC_PAD 0x00000315
+#define CKM_CAST5_KEY_GEN 0x00000320
+#define CKM_CAST128_KEY_GEN 0x00000320
+#define CKM_CAST5_ECB 0x00000321
+#define CKM_CAST128_ECB 0x00000321
+#define CKM_CAST5_CBC 0x00000322
+#define CKM_CAST128_CBC 0x00000322
+#define CKM_CAST5_MAC 0x00000323
+#define CKM_CAST128_MAC 0x00000323
+#define CKM_CAST5_MAC_GENERAL 0x00000324
+#define CKM_CAST128_MAC_GENERAL 0x00000324
+#define CKM_CAST5_CBC_PAD 0x00000325
+#define CKM_CAST128_CBC_PAD 0x00000325
+#define CKM_RC5_KEY_GEN 0x00000330
+#define CKM_RC5_ECB 0x00000331
+#define CKM_RC5_CBC 0x00000332
+#define CKM_RC5_MAC 0x00000333
+#define CKM_RC5_MAC_GENERAL 0x00000334
+#define CKM_RC5_CBC_PAD 0x00000335
+#define CKM_IDEA_KEY_GEN 0x00000340
+#define CKM_IDEA_ECB 0x00000341
+#define CKM_IDEA_CBC 0x00000342
+#define CKM_IDEA_MAC 0x00000343
+#define CKM_IDEA_MAC_GENERAL 0x00000344
+#define CKM_IDEA_CBC_PAD 0x00000345
+#define CKM_GENERIC_SECRET_KEY_GEN 0x00000350
+#define CKM_CONCATENATE_BASE_AND_KEY 0x00000360
+#define CKM_CONCATENATE_BASE_AND_DATA 0x00000362
+#define CKM_CONCATENATE_DATA_AND_BASE 0x00000363
+#define CKM_XOR_BASE_AND_DATA 0x00000364
+#define CKM_EXTRACT_KEY_FROM_KEY 0x00000365
+#define CKM_SSL3_PRE_MASTER_KEY_GEN 0x00000370
+#define CKM_SSL3_MASTER_KEY_DERIVE 0x00000371
+#define CKM_SSL3_KEY_AND_MAC_DERIVE 0x00000372
+
+/* CKM_SSL3_MASTER_KEY_DERIVE_DH, CKM_TLS_PRE_MASTER_KEY_GEN,
+ * CKM_TLS_MASTER_KEY_DERIVE, CKM_TLS_KEY_AND_MAC_DERIVE, and
+ * CKM_TLS_MASTER_KEY_DERIVE_DH are new for v2.11 */
+#define CKM_SSL3_MASTER_KEY_DERIVE_DH 0x00000373
+#define CKM_TLS_PRE_MASTER_KEY_GEN 0x00000374
+#define CKM_TLS_MASTER_KEY_DERIVE 0x00000375
+#define CKM_TLS_KEY_AND_MAC_DERIVE 0x00000376
+#define CKM_TLS_MASTER_KEY_DERIVE_DH 0x00000377
+
+/* CKM_TLS_PRF is new for v2.20 */
+#define CKM_TLS_PRF 0x00000378
+
+#define CKM_SSL3_MD5_MAC 0x00000380
+#define CKM_SSL3_SHA1_MAC 0x00000381
+#define CKM_MD5_KEY_DERIVATION 0x00000390
+#define CKM_MD2_KEY_DERIVATION 0x00000391
+#define CKM_SHA1_KEY_DERIVATION 0x00000392
+
+/* CKM_SHA256/384/512 are new for v2.20 */
+#define CKM_SHA256_KEY_DERIVATION 0x00000393
+#define CKM_SHA384_KEY_DERIVATION 0x00000394
+#define CKM_SHA512_KEY_DERIVATION 0x00000395
+
+/* CKM_SHA224 new for v2.20 amendment 3 */
+#define CKM_SHA224_KEY_DERIVATION 0x00000396
+
+#define CKM_PBE_MD2_DES_CBC 0x000003A0
+#define CKM_PBE_MD5_DES_CBC 0x000003A1
+#define CKM_PBE_MD5_CAST_CBC 0x000003A2
+#define CKM_PBE_MD5_CAST3_CBC 0x000003A3
+#define CKM_PBE_MD5_CAST5_CBC 0x000003A4
+#define CKM_PBE_MD5_CAST128_CBC 0x000003A4
+#define CKM_PBE_SHA1_CAST5_CBC 0x000003A5
+#define CKM_PBE_SHA1_CAST128_CBC 0x000003A5
+#define CKM_PBE_SHA1_RC4_128 0x000003A6
+#define CKM_PBE_SHA1_RC4_40 0x000003A7
+#define CKM_PBE_SHA1_DES3_EDE_CBC 0x000003A8
+#define CKM_PBE_SHA1_DES2_EDE_CBC 0x000003A9
+#define CKM_PBE_SHA1_RC2_128_CBC 0x000003AA
+#define CKM_PBE_SHA1_RC2_40_CBC 0x000003AB
+
+/* CKM_PKCS5_PBKD2 is new for v2.10 */
+#define CKM_PKCS5_PBKD2 0x000003B0
+
+#define CKM_PBA_SHA1_WITH_SHA1_HMAC 0x000003C0
+
+/* WTLS mechanisms are new for v2.20 */
+#define CKM_WTLS_PRE_MASTER_KEY_GEN 0x000003D0
+#define CKM_WTLS_MASTER_KEY_DERIVE 0x000003D1
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC 0x000003D2
+#define CKM_WTLS_PRF 0x000003D3
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE 0x000003D4
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE 0x000003D5
+
+#define CKM_KEY_WRAP_LYNKS 0x00000400
+#define CKM_KEY_WRAP_SET_OAEP 0x00000401
+
+/* CKM_CMS_SIG is new for v2.20 */
+#define CKM_CMS_SIG 0x00000500
+
+/* Fortezza mechanisms */
+#define CKM_SKIPJACK_KEY_GEN 0x00001000
+#define CKM_SKIPJACK_ECB64 0x00001001
+#define CKM_SKIPJACK_CBC64 0x00001002
+#define CKM_SKIPJACK_OFB64 0x00001003
+#define CKM_SKIPJACK_CFB64 0x00001004
+#define CKM_SKIPJACK_CFB32 0x00001005
+#define CKM_SKIPJACK_CFB16 0x00001006
+#define CKM_SKIPJACK_CFB8 0x00001007
+#define CKM_SKIPJACK_WRAP 0x00001008
+#define CKM_SKIPJACK_PRIVATE_WRAP 0x00001009
+#define CKM_SKIPJACK_RELAYX 0x0000100a
+#define CKM_KEA_KEY_PAIR_GEN 0x00001010
+#define CKM_KEA_KEY_DERIVE 0x00001011
+#define CKM_FORTEZZA_TIMESTAMP 0x00001020
+#define CKM_BATON_KEY_GEN 0x00001030
+#define CKM_BATON_ECB128 0x00001031
+#define CKM_BATON_ECB96 0x00001032
+#define CKM_BATON_CBC128 0x00001033
+#define CKM_BATON_COUNTER 0x00001034
+#define CKM_BATON_SHUFFLE 0x00001035
+#define CKM_BATON_WRAP 0x00001036
+
+/* CKM_ECDSA_KEY_PAIR_GEN is deprecated in v2.11,
+ * CKM_EC_KEY_PAIR_GEN is preferred */
+#define CKM_ECDSA_KEY_PAIR_GEN 0x00001040
+#define CKM_EC_KEY_PAIR_GEN 0x00001040
+
+#define CKM_ECDSA 0x00001041
+#define CKM_ECDSA_SHA1 0x00001042
+
+/* CKM_ECDH1_DERIVE, CKM_ECDH1_COFACTOR_DERIVE, and CKM_ECMQV_DERIVE
+ * are new for v2.11 */
+#define CKM_ECDH1_DERIVE 0x00001050
+#define CKM_ECDH1_COFACTOR_DERIVE 0x00001051
+#define CKM_ECMQV_DERIVE 0x00001052
+
+#define CKM_JUNIPER_KEY_GEN 0x00001060
+#define CKM_JUNIPER_ECB128 0x00001061
+#define CKM_JUNIPER_CBC128 0x00001062
+#define CKM_JUNIPER_COUNTER 0x00001063
+#define CKM_JUNIPER_SHUFFLE 0x00001064
+#define CKM_JUNIPER_WRAP 0x00001065
+#define CKM_FASTHASH 0x00001070
+
+/* CKM_AES_KEY_GEN, CKM_AES_ECB, CKM_AES_CBC, CKM_AES_MAC,
+ * CKM_AES_MAC_GENERAL, CKM_AES_CBC_PAD, CKM_DSA_PARAMETER_GEN,
+ * CKM_DH_PKCS_PARAMETER_GEN, and CKM_X9_42_DH_PARAMETER_GEN are
+ * new for v2.11 */
+#define CKM_AES_KEY_GEN 0x00001080
+#define CKM_AES_ECB 0x00001081
+#define CKM_AES_CBC 0x00001082
+#define CKM_AES_MAC 0x00001083
+#define CKM_AES_MAC_GENERAL 0x00001084
+#define CKM_AES_CBC_PAD 0x00001085
+
+/* BlowFish and TwoFish are new for v2.20 */
+#define CKM_BLOWFISH_KEY_GEN 0x00001090
+#define CKM_BLOWFISH_CBC 0x00001091
+#define CKM_TWOFISH_KEY_GEN 0x00001092
+#define CKM_TWOFISH_CBC 0x00001093
+
+/* Camellia is proposed for v2.20 Amendment 3 */
+#define CKM_CAMELLIA_KEY_GEN 0x00000550
+#define CKM_CAMELLIA_ECB 0x00000551
+#define CKM_CAMELLIA_CBC 0x00000552
+#define CKM_CAMELLIA_MAC 0x00000553
+#define CKM_CAMELLIA_MAC_GENERAL 0x00000554
+#define CKM_CAMELLIA_CBC_PAD 0x00000555
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA 0x00000556
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA 0x00000557
+
+/* CKM_xxx_ENCRYPT_DATA mechanisms are new for v2.20 */
+#define CKM_DES_ECB_ENCRYPT_DATA 0x00001100
+#define CKM_DES_CBC_ENCRYPT_DATA 0x00001101
+#define CKM_DES3_ECB_ENCRYPT_DATA 0x00001102
+#define CKM_DES3_CBC_ENCRYPT_DATA 0x00001103
+#define CKM_AES_ECB_ENCRYPT_DATA 0x00001104
+#define CKM_AES_CBC_ENCRYPT_DATA 0x00001105
+
+#define CKM_DSA_PARAMETER_GEN 0x00002000
+#define CKM_DH_PKCS_PARAMETER_GEN 0x00002001
+#define CKM_X9_42_DH_PARAMETER_GEN 0x00002002
+
+#define CKM_VENDOR_DEFINED 0x80000000
+
+typedef CK_MECHANISM_TYPE CK_PTR CK_MECHANISM_TYPE_PTR;
+
+
+/* CK_MECHANISM is a structure that specifies a particular
+ * mechanism */
+typedef struct CK_MECHANISM {
+ CK_MECHANISM_TYPE mechanism;
+ CK_VOID_PTR pParameter;
+
+ /* ulParameterLen was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+ CK_ULONG ulParameterLen; /* in bytes */
+} CK_MECHANISM;
+
+typedef CK_MECHANISM CK_PTR CK_MECHANISM_PTR;
+
+
+/* CK_MECHANISM_INFO provides information about a particular
+ * mechanism */
+typedef struct CK_MECHANISM_INFO {
+ CK_ULONG ulMinKeySize;
+ CK_ULONG ulMaxKeySize;
+ CK_FLAGS flags;
+} CK_MECHANISM_INFO;
+
+/* The flags are defined as follows:
+ * Bit Flag Mask Meaning */
+#define CKF_HW 0x00000001 /* performed by HW */
+
+/* The flags CKF_ENCRYPT, CKF_DECRYPT, CKF_DIGEST, CKF_SIGN,
+ * CKG_SIGN_RECOVER, CKF_VERIFY, CKF_VERIFY_RECOVER,
+ * CKF_GENERATE, CKF_GENERATE_KEY_PAIR, CKF_WRAP, CKF_UNWRAP,
+ * and CKF_DERIVE are new for v2.0. They specify whether or not
+ * a mechanism can be used for a particular task */
+#define CKF_ENCRYPT 0x00000100
+#define CKF_DECRYPT 0x00000200
+#define CKF_DIGEST 0x00000400
+#define CKF_SIGN 0x00000800
+#define CKF_SIGN_RECOVER 0x00001000
+#define CKF_VERIFY 0x00002000
+#define CKF_VERIFY_RECOVER 0x00004000
+#define CKF_GENERATE 0x00008000
+#define CKF_GENERATE_KEY_PAIR 0x00010000
+#define CKF_WRAP 0x00020000
+#define CKF_UNWRAP 0x00040000
+#define CKF_DERIVE 0x00080000
+
+/* CKF_EC_F_P, CKF_EC_F_2M, CKF_EC_ECPARAMETERS, CKF_EC_NAMEDCURVE,
+ * CKF_EC_UNCOMPRESS, and CKF_EC_COMPRESS are new for v2.11. They
+ * describe a token's EC capabilities not available in mechanism
+ * information. */
+#define CKF_EC_F_P 0x00100000
+#define CKF_EC_F_2M 0x00200000
+#define CKF_EC_ECPARAMETERS 0x00400000
+#define CKF_EC_NAMEDCURVE 0x00800000
+#define CKF_EC_UNCOMPRESS 0x01000000
+#define CKF_EC_COMPRESS 0x02000000
+
+#define CKF_EXTENSION 0x80000000 /* FALSE for this version */
+
+typedef CK_MECHANISM_INFO CK_PTR CK_MECHANISM_INFO_PTR;
+
+
+/* CK_RV is a value that identifies the return value of a
+ * PKCS #11 function */
+/* CK_RV was changed from CK_USHORT to CK_ULONG for v2.0 */
+typedef CK_ULONG CK_RV;
+
+#define CKR_OK 0x00000000
+#define CKR_CANCEL 0x00000001
+#define CKR_HOST_MEMORY 0x00000002
+#define CKR_SLOT_ID_INVALID 0x00000003
+
+/* CKR_FLAGS_INVALID was removed for v2.0 */
+
+/* CKR_GENERAL_ERROR and CKR_FUNCTION_FAILED are new for v2.0 */
+#define CKR_GENERAL_ERROR 0x00000005
+#define CKR_FUNCTION_FAILED 0x00000006
+
+/* CKR_ARGUMENTS_BAD, CKR_NO_EVENT, CKR_NEED_TO_CREATE_THREADS,
+ * and CKR_CANT_LOCK are new for v2.01 */
+#define CKR_ARGUMENTS_BAD 0x00000007
+#define CKR_NO_EVENT 0x00000008
+#define CKR_NEED_TO_CREATE_THREADS 0x00000009
+#define CKR_CANT_LOCK 0x0000000A
+
+#define CKR_ATTRIBUTE_READ_ONLY 0x00000010
+#define CKR_ATTRIBUTE_SENSITIVE 0x00000011
+#define CKR_ATTRIBUTE_TYPE_INVALID 0x00000012
+#define CKR_ATTRIBUTE_VALUE_INVALID 0x00000013
+#define CKR_DATA_INVALID 0x00000020
+#define CKR_DATA_LEN_RANGE 0x00000021
+#define CKR_DEVICE_ERROR 0x00000030
+#define CKR_DEVICE_MEMORY 0x00000031
+#define CKR_DEVICE_REMOVED 0x00000032
+#define CKR_ENCRYPTED_DATA_INVALID 0x00000040
+#define CKR_ENCRYPTED_DATA_LEN_RANGE 0x00000041
+#define CKR_FUNCTION_CANCELED 0x00000050
+#define CKR_FUNCTION_NOT_PARALLEL 0x00000051
+
+/* CKR_FUNCTION_NOT_SUPPORTED is new for v2.0 */
+#define CKR_FUNCTION_NOT_SUPPORTED 0x00000054
+
+#define CKR_KEY_HANDLE_INVALID 0x00000060
+
+/* CKR_KEY_SENSITIVE was removed for v2.0 */
+
+#define CKR_KEY_SIZE_RANGE 0x00000062
+#define CKR_KEY_TYPE_INCONSISTENT 0x00000063
+
+/* CKR_KEY_NOT_NEEDED, CKR_KEY_CHANGED, CKR_KEY_NEEDED,
+ * CKR_KEY_INDIGESTIBLE, CKR_KEY_FUNCTION_NOT_PERMITTED,
+ * CKR_KEY_NOT_WRAPPABLE, and CKR_KEY_UNEXTRACTABLE are new for
+ * v2.0 */
+#define CKR_KEY_NOT_NEEDED 0x00000064
+#define CKR_KEY_CHANGED 0x00000065
+#define CKR_KEY_NEEDED 0x00000066
+#define CKR_KEY_INDIGESTIBLE 0x00000067
+#define CKR_KEY_FUNCTION_NOT_PERMITTED 0x00000068
+#define CKR_KEY_NOT_WRAPPABLE 0x00000069
+#define CKR_KEY_UNEXTRACTABLE 0x0000006A
+
+#define CKR_MECHANISM_INVALID 0x00000070
+#define CKR_MECHANISM_PARAM_INVALID 0x00000071
+
+/* CKR_OBJECT_CLASS_INCONSISTENT and CKR_OBJECT_CLASS_INVALID
+ * were removed for v2.0 */
+#define CKR_OBJECT_HANDLE_INVALID 0x00000082
+#define CKR_OPERATION_ACTIVE 0x00000090
+#define CKR_OPERATION_NOT_INITIALIZED 0x00000091
+#define CKR_PIN_INCORRECT 0x000000A0
+#define CKR_PIN_INVALID 0x000000A1
+#define CKR_PIN_LEN_RANGE 0x000000A2
+
+/* CKR_PIN_EXPIRED and CKR_PIN_LOCKED are new for v2.0 */
+#define CKR_PIN_EXPIRED 0x000000A3
+#define CKR_PIN_LOCKED 0x000000A4
+
+#define CKR_SESSION_CLOSED 0x000000B0
+#define CKR_SESSION_COUNT 0x000000B1
+#define CKR_SESSION_HANDLE_INVALID 0x000000B3
+#define CKR_SESSION_PARALLEL_NOT_SUPPORTED 0x000000B4
+#define CKR_SESSION_READ_ONLY 0x000000B5
+#define CKR_SESSION_EXISTS 0x000000B6
+
+/* CKR_SESSION_READ_ONLY_EXISTS and
+ * CKR_SESSION_READ_WRITE_SO_EXISTS are new for v2.0 */
+#define CKR_SESSION_READ_ONLY_EXISTS 0x000000B7
+#define CKR_SESSION_READ_WRITE_SO_EXISTS 0x000000B8
+
+#define CKR_SIGNATURE_INVALID 0x000000C0
+#define CKR_SIGNATURE_LEN_RANGE 0x000000C1
+#define CKR_TEMPLATE_INCOMPLETE 0x000000D0
+#define CKR_TEMPLATE_INCONSISTENT 0x000000D1
+#define CKR_TOKEN_NOT_PRESENT 0x000000E0
+#define CKR_TOKEN_NOT_RECOGNIZED 0x000000E1
+#define CKR_TOKEN_WRITE_PROTECTED 0x000000E2
+#define CKR_UNWRAPPING_KEY_HANDLE_INVALID 0x000000F0
+#define CKR_UNWRAPPING_KEY_SIZE_RANGE 0x000000F1
+#define CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT 0x000000F2
+#define CKR_USER_ALREADY_LOGGED_IN 0x00000100
+#define CKR_USER_NOT_LOGGED_IN 0x00000101
+#define CKR_USER_PIN_NOT_INITIALIZED 0x00000102
+#define CKR_USER_TYPE_INVALID 0x00000103
+
+/* CKR_USER_ANOTHER_ALREADY_LOGGED_IN and CKR_USER_TOO_MANY_TYPES
+ * are new to v2.01 */
+#define CKR_USER_ANOTHER_ALREADY_LOGGED_IN 0x00000104
+#define CKR_USER_TOO_MANY_TYPES 0x00000105
+
+#define CKR_WRAPPED_KEY_INVALID 0x00000110
+#define CKR_WRAPPED_KEY_LEN_RANGE 0x00000112
+#define CKR_WRAPPING_KEY_HANDLE_INVALID 0x00000113
+#define CKR_WRAPPING_KEY_SIZE_RANGE 0x00000114
+#define CKR_WRAPPING_KEY_TYPE_INCONSISTENT 0x00000115
+#define CKR_RANDOM_SEED_NOT_SUPPORTED 0x00000120
+
+/* These are new to v2.0 */
+#define CKR_RANDOM_NO_RNG 0x00000121
+
+/* These are new to v2.11 */
+#define CKR_DOMAIN_PARAMS_INVALID 0x00000130
+
+/* These are new to v2.0 */
+#define CKR_BUFFER_TOO_SMALL 0x00000150
+#define CKR_SAVED_STATE_INVALID 0x00000160
+#define CKR_INFORMATION_SENSITIVE 0x00000170
+#define CKR_STATE_UNSAVEABLE 0x00000180
+
+/* These are new to v2.01 */
+#define CKR_CRYPTOKI_NOT_INITIALIZED 0x00000190
+#define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191
+#define CKR_MUTEX_BAD 0x000001A0
+#define CKR_MUTEX_NOT_LOCKED 0x000001A1
+
+/* This is new to v2.20 */
+#define CKR_FUNCTION_REJECTED 0x00000200
+
+#define CKR_VENDOR_DEFINED 0x80000000
+
+
+/* CK_NOTIFY is an application callback that processes events */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_NOTIFY)(
+ CK_SESSION_HANDLE hSession, /* the session's handle */
+ CK_NOTIFICATION event,
+ CK_VOID_PTR pApplication /* passed to C_OpenSession */
+);
+
+
+/* CK_FUNCTION_LIST is a structure holding a PKCS #11 spec
+ * version and pointers of appropriate types to all the
+ * PKCS #11 functions */
+/* CK_FUNCTION_LIST is new for v2.0 */
+typedef struct CK_FUNCTION_LIST CK_FUNCTION_LIST;
+
+typedef CK_FUNCTION_LIST CK_PTR CK_FUNCTION_LIST_PTR;
+
+typedef CK_FUNCTION_LIST_PTR CK_PTR CK_FUNCTION_LIST_PTR_PTR;
+
+
+/* CK_CREATEMUTEX is an application callback for creating a
+ * mutex object */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_CREATEMUTEX)(
+ CK_VOID_PTR_PTR ppMutex /* location to receive ptr to mutex */
+);
+
+
+/* CK_DESTROYMUTEX is an application callback for destroying a
+ * mutex object */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_DESTROYMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_LOCKMUTEX is an application callback for locking a mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_LOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_UNLOCKMUTEX is an application callback for unlocking a
+ * mutex */
+typedef CK_CALLBACK_FUNCTION(CK_RV, CK_UNLOCKMUTEX)(
+ CK_VOID_PTR pMutex /* pointer to mutex */
+);
+
+
+/* CK_C_INITIALIZE_ARGS provides the optional arguments to
+ * C_Initialize */
+typedef struct CK_C_INITIALIZE_ARGS {
+ CK_CREATEMUTEX CreateMutex;
+ CK_DESTROYMUTEX DestroyMutex;
+ CK_LOCKMUTEX LockMutex;
+ CK_UNLOCKMUTEX UnlockMutex;
+ CK_FLAGS flags;
+ /* The official PKCS #11 spec does not have a 'LibraryParameters' field, but
+ * a reserved field. NSS needs a way to pass instance-specific information
+ * to the library (like where to find its config files, etc). This
+ * information is usually provided by the installer and passed uninterpreted
+ * by NSS to the library, though NSS does know the specifics of the softoken
+ * version of this parameter. Most compliant PKCS#11 modules expect this
+ * parameter to be NULL, and will return CKR_ARGUMENTS_BAD from
+ * C_Initialize if Library parameters is supplied. */
+ CK_CHAR_PTR *LibraryParameters;
+ /* This field is only present if the LibraryParameters is not NULL. It must
+ * be NULL in all cases */
+ CK_VOID_PTR pReserved;
+} CK_C_INITIALIZE_ARGS;
+
+/* flags: bit flags that provide capabilities of the slot
+ * Bit Flag Mask Meaning
+ */
+#define CKF_LIBRARY_CANT_CREATE_OS_THREADS 0x00000001
+#define CKF_OS_LOCKING_OK 0x00000002
+
+typedef CK_C_INITIALIZE_ARGS CK_PTR CK_C_INITIALIZE_ARGS_PTR;
+
+
+/* additional flags for parameters to functions */
+
+/* CKF_DONT_BLOCK is for the function C_WaitForSlotEvent */
+#define CKF_DONT_BLOCK 1
+
+/* CK_RSA_PKCS_OAEP_MGF_TYPE is new for v2.10.
+ * CK_RSA_PKCS_OAEP_MGF_TYPE is used to indicate the Message
+ * Generation Function (MGF) applied to a message block when
+ * formatting a message block for the PKCS #1 OAEP encryption
+ * scheme. */
+typedef CK_ULONG CK_RSA_PKCS_MGF_TYPE;
+
+typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR;
+
+/* The following MGFs are defined */
+/* CKG_MGF1_SHA256, CKG_MGF1_SHA384, and CKG_MGF1_SHA512
+ * are new for v2.20 */
+#define CKG_MGF1_SHA1 0x00000001
+#define CKG_MGF1_SHA256 0x00000002
+#define CKG_MGF1_SHA384 0x00000003
+#define CKG_MGF1_SHA512 0x00000004
+
+/* v2.20 amendment 3 */
+#define CKG_MGF1_SHA224 0x00000005
+
+/* CK_RSA_PKCS_OAEP_SOURCE_TYPE is new for v2.10.
+ * CK_RSA_PKCS_OAEP_SOURCE_TYPE is used to indicate the source
+ * of the encoding parameter when formatting a message block
+ * for the PKCS #1 OAEP encryption scheme. */
+typedef CK_ULONG CK_RSA_PKCS_OAEP_SOURCE_TYPE;
+
+typedef CK_RSA_PKCS_OAEP_SOURCE_TYPE CK_PTR CK_RSA_PKCS_OAEP_SOURCE_TYPE_PTR;
+
+/* The following encoding parameter sources are defined */
+#define CKZ_DATA_SPECIFIED 0x00000001
+
+/* CK_RSA_PKCS_OAEP_PARAMS is new for v2.10.
+ * CK_RSA_PKCS_OAEP_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_OAEP mechanism. */
+typedef struct CK_RSA_PKCS_OAEP_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_RSA_PKCS_OAEP_SOURCE_TYPE source;
+ CK_VOID_PTR pSourceData;
+ CK_ULONG ulSourceDataLen;
+} CK_RSA_PKCS_OAEP_PARAMS;
+
+typedef CK_RSA_PKCS_OAEP_PARAMS CK_PTR CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+/* CK_RSA_PKCS_PSS_PARAMS is new for v2.11.
+ * CK_RSA_PKCS_PSS_PARAMS provides the parameters to the
+ * CKM_RSA_PKCS_PSS mechanism(s). */
+typedef struct CK_RSA_PKCS_PSS_PARAMS {
+ CK_MECHANISM_TYPE hashAlg;
+ CK_RSA_PKCS_MGF_TYPE mgf;
+ CK_ULONG sLen;
+} CK_RSA_PKCS_PSS_PARAMS;
+
+typedef CK_RSA_PKCS_PSS_PARAMS CK_PTR CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+/* CK_EC_KDF_TYPE is new for v2.11. */
+typedef CK_ULONG CK_EC_KDF_TYPE;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL 0x00000001
+#define CKD_SHA1_KDF 0x00000002
+
+/* CK_ECDH1_DERIVE_PARAMS is new for v2.11.
+ * CK_ECDH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECDH1_DERIVE and CKM_ECDH1_COFACTOR_DERIVE mechanisms,
+ * where each party contributes one key pair.
+ */
+typedef struct CK_ECDH1_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_ECDH1_DERIVE_PARAMS;
+
+typedef CK_ECDH1_DERIVE_PARAMS CK_PTR CK_ECDH1_DERIVE_PARAMS_PTR;
+
+
+/* CK_ECDH2_DERIVE_PARAMS is new for v2.11.
+ * CK_ECDH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_ECMQV_DERIVE mechanism, where each party contributes two key pairs. */
+typedef struct CK_ECDH2_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_ECDH2_DERIVE_PARAMS;
+
+typedef CK_ECDH2_DERIVE_PARAMS CK_PTR CK_ECDH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_ECMQV_DERIVE_PARAMS {
+ CK_EC_KDF_TYPE kdf;
+ CK_ULONG ulSharedDataLen;
+ CK_BYTE_PTR pSharedData;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_ECMQV_DERIVE_PARAMS;
+
+typedef CK_ECMQV_DERIVE_PARAMS CK_PTR CK_ECMQV_DERIVE_PARAMS_PTR;
+
+/* Typedefs and defines for the CKM_X9_42_DH_KEY_PAIR_GEN and the
+ * CKM_X9_42_DH_PARAMETER_GEN mechanisms (new for PKCS #11 v2.11) */
+typedef CK_ULONG CK_X9_42_DH_KDF_TYPE;
+typedef CK_X9_42_DH_KDF_TYPE CK_PTR CK_X9_42_DH_KDF_TYPE_PTR;
+
+/* The following X9.42 DH key derivation functions are defined
+ (besides CKD_NULL already defined : */
+#define CKD_SHA1_KDF_ASN1 0x00000003
+#define CKD_SHA1_KDF_CONCATENATE 0x00000004
+
+/* CK_X9_42_DH1_DERIVE_PARAMS is new for v2.11.
+ * CK_X9_42_DH1_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_DERIVE key derivation mechanism, where each party
+ * contributes one key pair */
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_X9_42_DH1_DERIVE_PARAMS;
+
+typedef struct CK_X9_42_DH1_DERIVE_PARAMS CK_PTR CK_X9_42_DH1_DERIVE_PARAMS_PTR;
+
+/* CK_X9_42_DH2_DERIVE_PARAMS is new for v2.11.
+ * CK_X9_42_DH2_DERIVE_PARAMS provides the parameters to the
+ * CKM_X9_42_DH_HYBRID_DERIVE and CKM_X9_42_MQV_DERIVE key derivation
+ * mechanisms, where each party contributes two key pairs */
+typedef struct CK_X9_42_DH2_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+} CK_X9_42_DH2_DERIVE_PARAMS;
+
+typedef CK_X9_42_DH2_DERIVE_PARAMS CK_PTR CK_X9_42_DH2_DERIVE_PARAMS_PTR;
+
+typedef struct CK_X9_42_MQV_DERIVE_PARAMS {
+ CK_X9_42_DH_KDF_TYPE kdf;
+ CK_ULONG ulOtherInfoLen;
+ CK_BYTE_PTR pOtherInfo;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPrivateDataLen;
+ CK_OBJECT_HANDLE hPrivateData;
+ CK_ULONG ulPublicDataLen2;
+ CK_BYTE_PTR pPublicData2;
+ CK_OBJECT_HANDLE publicKey;
+} CK_X9_42_MQV_DERIVE_PARAMS;
+
+typedef CK_X9_42_MQV_DERIVE_PARAMS CK_PTR CK_X9_42_MQV_DERIVE_PARAMS_PTR;
+
+/* CK_KEA_DERIVE_PARAMS provides the parameters to the
+ * CKM_KEA_DERIVE mechanism */
+/* CK_KEA_DERIVE_PARAMS is new for v2.0 */
+typedef struct CK_KEA_DERIVE_PARAMS {
+ CK_BBOOL isSender;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pRandomB;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+} CK_KEA_DERIVE_PARAMS;
+
+typedef CK_KEA_DERIVE_PARAMS CK_PTR CK_KEA_DERIVE_PARAMS_PTR;
+
+
+/* CK_RC2_PARAMS provides the parameters to the CKM_RC2_ECB and
+ * CKM_RC2_MAC mechanisms. An instance of CK_RC2_PARAMS just
+ * holds the effective keysize */
+typedef CK_ULONG CK_RC2_PARAMS;
+
+typedef CK_RC2_PARAMS CK_PTR CK_RC2_PARAMS_PTR;
+
+
+/* CK_RC2_CBC_PARAMS provides the parameters to the CKM_RC2_CBC
+ * mechanism */
+typedef struct CK_RC2_CBC_PARAMS {
+ /* ulEffectiveBits was changed from CK_USHORT to CK_ULONG for
+ * v2.0 */
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+
+ CK_BYTE iv[8]; /* IV for CBC mode */
+} CK_RC2_CBC_PARAMS;
+
+typedef CK_RC2_CBC_PARAMS CK_PTR CK_RC2_CBC_PARAMS_PTR;
+
+
+/* CK_RC2_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC2_MAC_GENERAL mechanism */
+/* CK_RC2_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef struct CK_RC2_MAC_GENERAL_PARAMS {
+ CK_ULONG ulEffectiveBits; /* effective bits (1-1024) */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC2_MAC_GENERAL_PARAMS;
+
+typedef CK_RC2_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC2_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_RC5_PARAMS provides the parameters to the CKM_RC5_ECB and
+ * CKM_RC5_MAC mechanisms */
+/* CK_RC5_PARAMS is new for v2.0 */
+typedef struct CK_RC5_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+} CK_RC5_PARAMS;
+
+typedef CK_RC5_PARAMS CK_PTR CK_RC5_PARAMS_PTR;
+
+
+/* CK_RC5_CBC_PARAMS provides the parameters to the CKM_RC5_CBC
+ * mechanism */
+/* CK_RC5_CBC_PARAMS is new for v2.0 */
+typedef struct CK_RC5_CBC_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_BYTE_PTR pIv; /* pointer to IV */
+ CK_ULONG ulIvLen; /* length of IV in bytes */
+} CK_RC5_CBC_PARAMS;
+
+typedef CK_RC5_CBC_PARAMS CK_PTR CK_RC5_CBC_PARAMS_PTR;
+
+
+/* CK_RC5_MAC_GENERAL_PARAMS provides the parameters for the
+ * CKM_RC5_MAC_GENERAL mechanism */
+/* CK_RC5_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef struct CK_RC5_MAC_GENERAL_PARAMS {
+ CK_ULONG ulWordsize; /* wordsize in bits */
+ CK_ULONG ulRounds; /* number of rounds */
+ CK_ULONG ulMacLength; /* Length of MAC in bytes */
+} CK_RC5_MAC_GENERAL_PARAMS;
+
+typedef CK_RC5_MAC_GENERAL_PARAMS CK_PTR \
+ CK_RC5_MAC_GENERAL_PARAMS_PTR;
+
+
+/* CK_MAC_GENERAL_PARAMS provides the parameters to most block
+ * ciphers' MAC_GENERAL mechanisms. Its value is the length of
+ * the MAC */
+/* CK_MAC_GENERAL_PARAMS is new for v2.0 */
+typedef CK_ULONG CK_MAC_GENERAL_PARAMS;
+
+typedef CK_MAC_GENERAL_PARAMS CK_PTR CK_MAC_GENERAL_PARAMS_PTR;
+
+/* CK_DES/AES_ECB/CBC_ENCRYPT_DATA_PARAMS are new for v2.20 */
+typedef struct CK_DES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[8];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_DES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct CK_AES_CBC_ENCRYPT_DATA_PARAMS {
+ CK_BYTE iv[16];
+ CK_BYTE_PTR pData;
+ CK_ULONG length;
+} CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+
+typedef CK_AES_CBC_ENCRYPT_DATA_PARAMS CK_PTR CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_PRIVATE_WRAP mechanism */
+/* CK_SKIPJACK_PRIVATE_WRAP_PARAMS is new for v2.0 */
+typedef struct CK_SKIPJACK_PRIVATE_WRAP_PARAMS {
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pPassword;
+ CK_ULONG ulPublicDataLen;
+ CK_BYTE_PTR pPublicData;
+ CK_ULONG ulPAndGLen;
+ CK_ULONG ulQLen;
+ CK_ULONG ulRandomLen;
+ CK_BYTE_PTR pRandomA;
+ CK_BYTE_PTR pPrimeP;
+ CK_BYTE_PTR pBaseG;
+ CK_BYTE_PTR pSubprimeQ;
+} CK_SKIPJACK_PRIVATE_WRAP_PARAMS;
+
+typedef CK_SKIPJACK_PRIVATE_WRAP_PARAMS CK_PTR \
+ CK_SKIPJACK_PRIVATE_WRAP_PTR;
+
+
+/* CK_SKIPJACK_RELAYX_PARAMS provides the parameters to the
+ * CKM_SKIPJACK_RELAYX mechanism */
+/* CK_SKIPJACK_RELAYX_PARAMS is new for v2.0 */
+typedef struct CK_SKIPJACK_RELAYX_PARAMS {
+ CK_ULONG ulOldWrappedXLen;
+ CK_BYTE_PTR pOldWrappedX;
+ CK_ULONG ulOldPasswordLen;
+ CK_BYTE_PTR pOldPassword;
+ CK_ULONG ulOldPublicDataLen;
+ CK_BYTE_PTR pOldPublicData;
+ CK_ULONG ulOldRandomLen;
+ CK_BYTE_PTR pOldRandomA;
+ CK_ULONG ulNewPasswordLen;
+ CK_BYTE_PTR pNewPassword;
+ CK_ULONG ulNewPublicDataLen;
+ CK_BYTE_PTR pNewPublicData;
+ CK_ULONG ulNewRandomLen;
+ CK_BYTE_PTR pNewRandomA;
+} CK_SKIPJACK_RELAYX_PARAMS;
+
+typedef CK_SKIPJACK_RELAYX_PARAMS CK_PTR \
+ CK_SKIPJACK_RELAYX_PARAMS_PTR;
+
+
+typedef struct CK_PBE_PARAMS {
+ CK_BYTE_PTR pInitVector;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG ulPasswordLen;
+ CK_BYTE_PTR pSalt;
+ CK_ULONG ulSaltLen;
+ CK_ULONG ulIteration;
+} CK_PBE_PARAMS;
+
+typedef CK_PBE_PARAMS CK_PTR CK_PBE_PARAMS_PTR;
+
+
+/* CK_KEY_WRAP_SET_OAEP_PARAMS provides the parameters to the
+ * CKM_KEY_WRAP_SET_OAEP mechanism */
+/* CK_KEY_WRAP_SET_OAEP_PARAMS is new for v2.0 */
+typedef struct CK_KEY_WRAP_SET_OAEP_PARAMS {
+ CK_BYTE bBC; /* block contents byte */
+ CK_BYTE_PTR pX; /* extra data */
+ CK_ULONG ulXLen; /* length of extra data in bytes */
+} CK_KEY_WRAP_SET_OAEP_PARAMS;
+
+typedef CK_KEY_WRAP_SET_OAEP_PARAMS CK_PTR \
+ CK_KEY_WRAP_SET_OAEP_PARAMS_PTR;
+
+
+typedef struct CK_SSL3_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_SSL3_RANDOM_DATA;
+
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS {
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_VERSION_PTR pVersion;
+} CK_SSL3_MASTER_KEY_DERIVE_PARAMS;
+
+typedef struct CK_SSL3_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_SSL3_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hClientMacSecret;
+ CK_OBJECT_HANDLE hServerMacSecret;
+ CK_OBJECT_HANDLE hClientKey;
+ CK_OBJECT_HANDLE hServerKey;
+ CK_BYTE_PTR pIVClient;
+ CK_BYTE_PTR pIVServer;
+} CK_SSL3_KEY_MAT_OUT;
+
+typedef CK_SSL3_KEY_MAT_OUT CK_PTR CK_SSL3_KEY_MAT_OUT_PTR;
+
+
+typedef struct CK_SSL3_KEY_MAT_PARAMS {
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_BBOOL bIsExport;
+ CK_SSL3_RANDOM_DATA RandomInfo;
+ CK_SSL3_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_SSL3_KEY_MAT_PARAMS;
+
+typedef CK_SSL3_KEY_MAT_PARAMS CK_PTR CK_SSL3_KEY_MAT_PARAMS_PTR;
+
+/* CK_TLS_PRF_PARAMS is new for version 2.20 */
+typedef struct CK_TLS_PRF_PARAMS {
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_TLS_PRF_PARAMS;
+
+typedef CK_TLS_PRF_PARAMS CK_PTR CK_TLS_PRF_PARAMS_PTR;
+
+/* WTLS is new for version 2.20 */
+typedef struct CK_WTLS_RANDOM_DATA {
+ CK_BYTE_PTR pClientRandom;
+ CK_ULONG ulClientRandomLen;
+ CK_BYTE_PTR pServerRandom;
+ CK_ULONG ulServerRandomLen;
+} CK_WTLS_RANDOM_DATA;
+
+typedef CK_WTLS_RANDOM_DATA CK_PTR CK_WTLS_RANDOM_DATA_PTR;
+
+typedef struct CK_WTLS_MASTER_KEY_DERIVE_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_BYTE_PTR pVersion;
+} CK_WTLS_MASTER_KEY_DERIVE_PARAMS;
+
+typedef CK_WTLS_MASTER_KEY_DERIVE_PARAMS CK_PTR \
+ CK_WTLS_MASTER_KEY_DERIVE_PARAMS_PTR;
+
+typedef struct CK_WTLS_PRF_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_BYTE_PTR pSeed;
+ CK_ULONG ulSeedLen;
+ CK_BYTE_PTR pLabel;
+ CK_ULONG ulLabelLen;
+ CK_BYTE_PTR pOutput;
+ CK_ULONG_PTR pulOutputLen;
+} CK_WTLS_PRF_PARAMS;
+
+typedef CK_WTLS_PRF_PARAMS CK_PTR CK_WTLS_PRF_PARAMS_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_OUT {
+ CK_OBJECT_HANDLE hMacSecret;
+ CK_OBJECT_HANDLE hKey;
+ CK_BYTE_PTR pIV;
+} CK_WTLS_KEY_MAT_OUT;
+
+typedef CK_WTLS_KEY_MAT_OUT CK_PTR CK_WTLS_KEY_MAT_OUT_PTR;
+
+typedef struct CK_WTLS_KEY_MAT_PARAMS {
+ CK_MECHANISM_TYPE DigestMechanism;
+ CK_ULONG ulMacSizeInBits;
+ CK_ULONG ulKeySizeInBits;
+ CK_ULONG ulIVSizeInBits;
+ CK_ULONG ulSequenceNumber;
+ CK_BBOOL bIsExport;
+ CK_WTLS_RANDOM_DATA RandomInfo;
+ CK_WTLS_KEY_MAT_OUT_PTR pReturnedKeyMaterial;
+} CK_WTLS_KEY_MAT_PARAMS;
+
+typedef CK_WTLS_KEY_MAT_PARAMS CK_PTR CK_WTLS_KEY_MAT_PARAMS_PTR;
+
+/* CMS is new for version 2.20 */
+typedef struct CK_CMS_SIG_PARAMS {
+ CK_OBJECT_HANDLE certificateHandle;
+ CK_MECHANISM_PTR pSigningMechanism;
+ CK_MECHANISM_PTR pDigestMechanism;
+ CK_UTF8CHAR_PTR pContentType;
+ CK_BYTE_PTR pRequestedAttributes;
+ CK_ULONG ulRequestedAttributesLen;
+ CK_BYTE_PTR pRequiredAttributes;
+ CK_ULONG ulRequiredAttributesLen;
+} CK_CMS_SIG_PARAMS;
+
+typedef CK_CMS_SIG_PARAMS CK_PTR CK_CMS_SIG_PARAMS_PTR;
+
+typedef struct CK_KEY_DERIVATION_STRING_DATA {
+ CK_BYTE_PTR pData;
+ CK_ULONG ulLen;
+} CK_KEY_DERIVATION_STRING_DATA;
+
+typedef CK_KEY_DERIVATION_STRING_DATA CK_PTR \
+ CK_KEY_DERIVATION_STRING_DATA_PTR;
+
+
+/* The CK_EXTRACT_PARAMS is used for the
+ * CKM_EXTRACT_KEY_FROM_KEY mechanism. It specifies which bit
+ * of the base key should be used as the first bit of the
+ * derived key */
+/* CK_EXTRACT_PARAMS is new for v2.0 */
+typedef CK_ULONG CK_EXTRACT_PARAMS;
+
+typedef CK_EXTRACT_PARAMS CK_PTR CK_EXTRACT_PARAMS_PTR;
+
+/* CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is new for v2.10.
+ * CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE is used to
+ * indicate the Pseudo-Random Function (PRF) used to generate
+ * key bits using PKCS #5 PBKDF2. */
+typedef CK_ULONG CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE;
+
+typedef CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE CK_PTR CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE_PTR;
+
+/* The following PRFs are defined in PKCS #5 v2.0. */
+#define CKP_PKCS5_PBKD2_HMAC_SHA1 0x00000001
+
+
+/* CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is new for v2.10.
+ * CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE is used to indicate the
+ * source of the salt value when deriving a key using PKCS #5
+ * PBKDF2. */
+typedef CK_ULONG CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE;
+
+typedef CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE CK_PTR CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE_PTR;
+
+/* The following salt value sources are defined in PKCS #5 v2.0. */
+#define CKZ_SALT_SPECIFIED 0x00000001
+
+/* CK_PKCS5_PBKD2_PARAMS is new for v2.10.
+ * CK_PKCS5_PBKD2_PARAMS is a structure that provides the
+ * parameters to the CKM_PKCS5_PBKD2 mechanism. */
+typedef struct CK_PKCS5_PBKD2_PARAMS {
+ CK_PKCS5_PBKDF2_SALT_SOURCE_TYPE saltSource;
+ CK_VOID_PTR pSaltSourceData;
+ CK_ULONG ulSaltSourceDataLen;
+ CK_ULONG iterations;
+ CK_PKCS5_PBKD2_PSEUDO_RANDOM_FUNCTION_TYPE prf;
+ CK_VOID_PTR pPrfData;
+ CK_ULONG ulPrfDataLen;
+ CK_UTF8CHAR_PTR pPassword;
+ CK_ULONG_PTR ulPasswordLen;
+} CK_PKCS5_PBKD2_PARAMS;
+
+typedef CK_PKCS5_PBKD2_PARAMS CK_PTR CK_PKCS5_PBKD2_PARAMS_PTR;
+
+/* NSS Specific defines */
+
+/* defines that have been deprecated in 2.20, but maintained in our
+ * header file for backward compatibility */
+#define CKO_KG_PARAMETERS CKO_DOMAIN_PARAMETERS
+#define CKF_EC_FP CKF_EC_F_P
+/* new in v2.11 deprecated by 2.20 */
+#define CKR_KEY_PARAMS_INVALID 0x0000006B
+
+/* stuff that for historic reasons is in this header file but should have
+ * been in pkcs11n.h */
+#define CKK_INVALID_KEY_TYPE 0xffffffff
+
+#include "pkcs11n.h"
+
+/* undo packing */
+#include "pkcs11u.h"
+
+#endif
diff --git a/security/nss/lib/softoken/pkcs11u.c b/security/nss/lib/softoken/pkcs11u.c
new file mode 100644
index 000000000..0d649e26e
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11u.c
@@ -0,0 +1,2012 @@
+/* ***** 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):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * 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 ***** */
+/*
+ * Internal PKCS #11 functions. Should only be called by pkcs11.c
+ */
+#include "pkcs11.h"
+#include "pkcs11i.h"
+#include "lowkeyi.h"
+#include "secasn1.h"
+#include "blapi.h"
+#include "secerr.h"
+#include "prnetdb.h" /* for PR_ntohl */
+#include "sftkdb.h"
+
+/*
+ * ******************** Attribute Utilities *******************************
+ */
+
+/*
+ * create a new attribute with type, value, and length. Space is allocated
+ * to hold value.
+ */
+static SFTKAttribute *
+sftk_NewAttribute(SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG len)
+{
+ SFTKAttribute *attribute;
+
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+ int index;
+
+ if (so == NULL) {
+ /* allocate new attribute in a buffer */
+ PORT_Assert(0);
+ return NULL;
+ }
+ /*
+ * We attempt to keep down contention on Malloc and Arena locks by
+ * limiting the number of these calls on high traversed paths. This
+ * is done for attributes by 'allocating' them from a pool already
+ * allocated by the parent object.
+ */
+ PZ_Lock(so->attributeLock);
+ index = so->nextAttr++;
+ PZ_Unlock(so->attributeLock);
+ PORT_Assert(index < MAX_OBJS_ATTRS);
+ if (index >= MAX_OBJS_ATTRS) return NULL;
+
+ attribute = &so->attrList[index];
+ attribute->attrib.type = type;
+ attribute->freeAttr = PR_FALSE;
+ attribute->freeData = PR_FALSE;
+ if (value) {
+ if (len <= ATTR_SPACE) {
+ attribute->attrib.pValue = attribute->space;
+ } else {
+ attribute->attrib.pValue = PORT_Alloc(len);
+ attribute->freeData = PR_TRUE;
+ }
+ if (attribute->attrib.pValue == NULL) {
+ return NULL;
+ }
+ PORT_Memcpy(attribute->attrib.pValue,value,len);
+ attribute->attrib.ulValueLen = len;
+ } else {
+ attribute->attrib.pValue = NULL;
+ attribute->attrib.ulValueLen = 0;
+ }
+ attribute->attrib.type = type;
+ attribute->handle = type;
+ attribute->next = attribute->prev = NULL;
+ return attribute;
+}
+
+/*
+ * Free up all the memory associated with an attribute. Reference count
+ * must be zero to call this.
+ */
+static void
+sftk_DestroyAttribute(SFTKAttribute *attribute)
+{
+ if (attribute->freeData) {
+ if (attribute->attrib.pValue) {
+ /* clear out the data in the attribute value... it may have been
+ * sensitive data */
+ PORT_Memset(attribute->attrib.pValue, 0,
+ attribute->attrib.ulValueLen);
+ }
+ PORT_Free(attribute->attrib.pValue);
+ }
+ PORT_Free(attribute);
+}
+
+/*
+ * release a reference to an attribute structure
+ */
+void
+sftk_FreeAttribute(SFTKAttribute *attribute)
+{
+ if (attribute->freeAttr) {
+ sftk_DestroyAttribute(attribute);
+ return;
+ }
+}
+
+static SFTKAttribute *
+sftk_FindTokenAttribute(SFTKTokenObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *myattribute = NULL;
+ SFTKDBHandle *dbHandle = NULL;
+ CK_RV crv;
+
+ myattribute = (SFTKAttribute*)PORT_Alloc(sizeof(SFTKAttribute));
+ if (myattribute == NULL) {
+ goto loser;
+ }
+
+ dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
+
+ myattribute->handle = type;
+ myattribute->attrib.type = type;
+ myattribute->attrib.pValue = myattribute->space;
+ myattribute->attrib.ulValueLen = ATTR_SPACE;
+ myattribute->next = myattribute->prev = NULL;
+ myattribute->freeAttr = PR_TRUE;
+ myattribute->freeData = PR_FALSE;
+
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
+ &myattribute->attrib, 1);
+
+ /* attribute is bigger than our attribute space buffer, malloc it */
+ if (crv == CKR_BUFFER_TOO_SMALL) {
+ myattribute->attrib.pValue = NULL;
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
+ &myattribute->attrib, 1);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ myattribute->attrib.pValue = PORT_Alloc(myattribute->attrib.ulValueLen);
+ if (myattribute->attrib.pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ myattribute->freeData = PR_TRUE;
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
+ &myattribute->attrib, 1);
+ }
+loser:
+ if (dbHandle) {
+ sftk_freeDB(dbHandle);
+ }
+ if (crv != CKR_OK) {
+ if (myattribute) {
+ myattribute->attrib.ulValueLen = 0;
+ sftk_FreeAttribute(myattribute);
+ myattribute = NULL;
+ }
+ }
+ return myattribute;
+}
+
+/*
+ * look up and attribute structure from a type and Object structure.
+ * The returned attribute is referenced and needs to be freed when
+ * it is no longer needed.
+ */
+SFTKAttribute *
+sftk_FindAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) {
+ return sftk_FindTokenAttribute(sftk_narrowToTokenObject(object),type);
+ }
+
+ PZ_Lock(sessObject->attributeLock);
+ sftkqueue_find(attribute,type,sessObject->head, sessObject->hashSize);
+ PZ_Unlock(sessObject->attributeLock);
+
+ return(attribute);
+}
+
+/*
+ * Take a buffer and it's length and return it's true size in bits;
+ */
+unsigned int
+sftk_GetLengthInBits(unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int size = bufLen * 8;
+ unsigned int i;
+
+ /* Get the real length in bytes */
+ for (i=0; i < bufLen; i++) {
+ unsigned char c = *buf++;
+ if (c != 0) {
+ unsigned char m;
+ for (m=0x80; m > 0 ; m = m >> 1) {
+ if ((c & m) != 0) {
+ break;
+ }
+ size--;
+ }
+ break;
+ }
+ size-=8;
+ }
+ return size;
+}
+
+/*
+ * Constrain a big num attribute. to size and padding
+ * minLength means length of the object must be greater than equal to minLength
+ * maxLength means length of the object must be less than equal to maxLength
+ * minMultiple means that object length mod minMultiple must equal 0.
+ * all input sizes are in bits.
+ * if any constraint is '0' that constraint is not checked.
+ */
+CK_RV
+sftk_ConstrainAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ int minLength, int maxLength, int minMultiple)
+{
+ SFTKAttribute *attribute;
+ int size;
+ unsigned char *ptr;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (!attribute) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ ptr = (unsigned char *) attribute->attrib.pValue;
+ if (ptr == NULL) {
+ sftk_FreeAttribute(attribute);
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ size = sftk_GetLengthInBits(ptr, attribute->attrib.ulValueLen);
+ sftk_FreeAttribute(attribute);
+
+ if ((minLength != 0) && (size < minLength)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ if ((maxLength != 0) && (size > maxLength)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ if ((minMultiple != 0) && ((size % minMultiple) != 0)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+ return CKR_OK;
+}
+
+PRBool
+sftk_hasAttributeToken(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type)
+{
+ CK_ATTRIBUTE template;
+ CK_RV crv;
+ SFTKDBHandle *dbHandle;
+
+ dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
+ template.type = type;
+ template.pValue = NULL;
+ template.ulValueLen = 0;
+
+ crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, &template, 1);
+ sftk_freeDB(dbHandle);
+
+ /* attribute is bigger than our attribute space buffer, malloc it */
+ return (crv == CKR_OK) ? PR_TRUE : PR_FALSE;
+}
+
+/*
+ * return true if object has attribute
+ */
+PRBool
+sftk_hasAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) {
+ return sftk_hasAttributeToken(sftk_narrowToTokenObject(object), type);
+ }
+
+ PZ_Lock(sessObject->attributeLock);
+ sftkqueue_find(attribute,type,sessObject->head, sessObject->hashSize);
+ PZ_Unlock(sessObject->attributeLock);
+
+ return (PRBool)(attribute != NULL);
+}
+
+/*
+ * add an attribute to an object
+ */
+static void
+sftk_AddAttribute(SFTKObject *object,SFTKAttribute *attribute)
+{
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) return;
+ PZ_Lock(sessObject->attributeLock);
+ sftkqueue_add(attribute,attribute->handle,
+ sessObject->head, sessObject->hashSize);
+ PZ_Unlock(sessObject->attributeLock);
+}
+
+/*
+ * copy an unsigned attribute into a SECItem. Secitem is allocated in
+ * the specified arena.
+ */
+CK_RV
+sftk_Attribute2SSecItem(PLArenaPool *arena,SECItem *item,SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+
+ item->data = NULL;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen);
+ if (item->data == NULL) {
+ sftk_FreeAttribute(attribute);
+ return CKR_HOST_MEMORY;
+ }
+ PORT_Memcpy(item->data, attribute->attrib.pValue, item->len);
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+/*
+ * fetch multiple attributes into SECItems. Secitem data is allocated in
+ * the specified arena.
+ */
+CK_RV
+sftk_MultipleAttribute2SecItem(PLArenaPool *arena, SFTKObject *object,
+ SFTKItemTemplate *itemTemplate, int itemTemplateCount)
+{
+
+ CK_RV crv = CKR_OK;
+ CK_ATTRIBUTE templateSpace[SFTK_MAX_ITEM_TEMPLATE];
+ CK_ATTRIBUTE *template;
+ SFTKTokenObject *tokObject;
+ SFTKDBHandle *dbHandle = NULL;
+ int i;
+
+ tokObject = sftk_narrowToTokenObject(object);
+
+ /* session objects, just loop through the list */
+ if (tokObject == NULL) {
+ for (i=0; i < itemTemplateCount; i++) {
+ crv = sftk_Attribute2SecItem(arena,itemTemplate[i].item, object,
+ itemTemplate[i].type);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ }
+ return CKR_OK;
+ }
+
+ /* don't do any work if none is required */
+ if (itemTemplateCount == 0) {
+ return CKR_OK;
+ }
+
+ /* don't allocate the template unless we need it */
+ if (itemTemplateCount > SFTK_MAX_ITEM_TEMPLATE) {
+ template = PORT_NewArray(CK_ATTRIBUTE, itemTemplateCount);
+ } else {
+ template = templateSpace;
+ }
+
+ if (template == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
+ if (dbHandle == NULL) {
+ crv = CKR_OBJECT_HANDLE_INVALID;
+ goto loser;
+ }
+
+ /* set up the PKCS #11 template */
+ for (i=0; i < itemTemplateCount; i++) {
+ template[i].type = itemTemplate[i].type;
+ template[i].pValue = NULL;
+ template[i].ulValueLen = 0;
+ }
+
+ /* fetch the attribute lengths */
+ crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
+ template, itemTemplateCount);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* allocate space for the attributes */
+ for (i=0; i < itemTemplateCount ; i++) {
+ template[i].pValue = PORT_ArenaAlloc(arena, template[i].ulValueLen);
+ if (template[i].pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ }
+
+ /* fetch the attributes */
+ crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
+ template, itemTemplateCount);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ /* Fill in the items */
+ for (i=0; i < itemTemplateCount; i++) {
+ itemTemplate[i].item->data = template[i].pValue;
+ itemTemplate[i].item->len = template[i].ulValueLen;
+ }
+
+loser:
+ if (template != templateSpace) {
+ PORT_Free(template);
+ }
+ if (dbHandle) {
+ sftk_freeDB(dbHandle);
+ }
+
+ return crv;
+}
+
+
+/*
+ * delete an attribute from an object
+ */
+static void
+sftk_DeleteAttribute(SFTKObject *object, SFTKAttribute *attribute)
+{
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+
+ if (sessObject == NULL) {
+ return ;
+ }
+ PZ_Lock(sessObject->attributeLock);
+ if (sftkqueue_is_queued(attribute,attribute->handle,
+ sessObject->head, sessObject->hashSize)) {
+ sftkqueue_delete(attribute,attribute->handle,
+ sessObject->head, sessObject->hashSize);
+ }
+ PZ_Unlock(sessObject->attributeLock);
+}
+
+/*
+ * this is only valid for CK_BBOOL type attributes. Return the state
+ * of that attribute.
+ */
+PRBool
+sftk_isTrue(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ PRBool tok = PR_FALSE;
+
+ attribute=sftk_FindAttribute(object,type);
+ if (attribute == NULL) { return PR_FALSE; }
+ tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue);
+ sftk_FreeAttribute(attribute);
+
+ return tok;
+}
+
+/*
+ * force an attribute to null.
+ * this is for sensitive keys which are stored in the database, we don't
+ * want to keep this info around in memory in the clear.
+ */
+void
+sftk_nullAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+
+ attribute=sftk_FindAttribute(object,type);
+ if (attribute == NULL) return;
+
+ if (attribute->attrib.pValue != NULL) {
+ PORT_Memset(attribute->attrib.pValue,0,attribute->attrib.ulValueLen);
+ if (attribute->freeData) {
+ PORT_Free(attribute->attrib.pValue);
+ }
+ attribute->freeData = PR_FALSE;
+ attribute->attrib.pValue = NULL;
+ attribute->attrib.ulValueLen = 0;
+ }
+ sftk_FreeAttribute(attribute);
+}
+
+
+static CK_RV
+sftk_forceTokenAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type,
+ void *value, unsigned int len)
+{
+ CK_ATTRIBUTE attribute;
+ SFTKDBHandle *dbHandle = NULL;
+ SFTKTokenObject *to = sftk_narrowToTokenObject(object);
+ CK_RV crv;
+
+ PORT_Assert(to);
+ if (to == NULL) {
+ return CKR_DEVICE_ERROR;
+ }
+
+ dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
+
+ attribute.type = type;
+ attribute.pValue = value;
+ attribute.ulValueLen = len;
+
+ crv = sftkdb_SetAttributeValue(dbHandle, object->handle,
+ &attribute, 1);
+ sftk_freeDB(dbHandle);
+ return crv;
+}
+
+/*
+ * force an attribute to a specifc value.
+ */
+CK_RV
+sftk_forceAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type, void *value,
+ unsigned int len)
+{
+ SFTKAttribute *attribute;
+ void *att_val = NULL;
+ PRBool freeData = PR_FALSE;
+
+ PORT_Assert(object);
+ PORT_Assert(object->refCount);
+ PORT_Assert(object->slot);
+ if (!object ||
+ !object->refCount ||
+ !object->slot) {
+ return CKR_DEVICE_ERROR;
+ }
+ if (sftk_isToken(object->handle)) {
+ return sftk_forceTokenAttribute(object,type,value,len);
+ }
+ attribute=sftk_FindAttribute(object,type);
+ if (attribute == NULL) return sftk_AddAttributeType(object,type,value,len);
+
+
+ if (value) {
+ if (len <= ATTR_SPACE) {
+ att_val = attribute->space;
+ } else {
+ att_val = PORT_Alloc(len);
+ freeData = PR_TRUE;
+ }
+ if (att_val == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ if (attribute->attrib.pValue == att_val) {
+ PORT_Memset(attribute->attrib.pValue,0,
+ attribute->attrib.ulValueLen);
+ }
+ PORT_Memcpy(att_val,value,len);
+ }
+ if (attribute->attrib.pValue != NULL) {
+ if (attribute->attrib.pValue != att_val) {
+ PORT_Memset(attribute->attrib.pValue,0,
+ attribute->attrib.ulValueLen);
+ }
+ if (attribute->freeData) {
+ PORT_Free(attribute->attrib.pValue);
+ }
+ attribute->freeData = PR_FALSE;
+ attribute->attrib.pValue = NULL;
+ attribute->attrib.ulValueLen = 0;
+ }
+ if (att_val) {
+ attribute->attrib.pValue = att_val;
+ attribute->attrib.ulValueLen = len;
+ attribute->freeData = freeData;
+ }
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+/*
+ * return a null terminated string from attribute 'type'. This string
+ * is allocated and needs to be freed with PORT_Free() When complete.
+ */
+char *
+sftk_getString(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ char *label = NULL;
+
+ attribute=sftk_FindAttribute(object,type);
+ if (attribute == NULL) return NULL;
+
+ if (attribute->attrib.pValue != NULL) {
+ label = (char *) PORT_Alloc(attribute->attrib.ulValueLen+1);
+ if (label == NULL) {
+ sftk_FreeAttribute(attribute);
+ return NULL;
+ }
+
+ PORT_Memcpy(label,attribute->attrib.pValue,
+ attribute->attrib.ulValueLen);
+ label[attribute->attrib.ulValueLen] = 0;
+ }
+ sftk_FreeAttribute(attribute);
+ return label;
+}
+
+/*
+ * decode when a particular attribute may be modified
+ * SFTK_NEVER: This attribute must be set at object creation time and
+ * can never be modified.
+ * SFTK_ONCOPY: This attribute may be modified only when you copy the
+ * object.
+ * SFTK_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from
+ * CK_FALSE to CK_TRUE.
+ * SFTK_ALWAYS: This attribute can always be modified.
+ * Some attributes vary their modification type based on the class of the
+ * object.
+ */
+SFTKModifyType
+sftk_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
+{
+ /* if we don't know about it, user user defined, always allow modify */
+ SFTKModifyType mtype = SFTK_ALWAYS;
+
+ switch(type) {
+ /* NEVER */
+ case CKA_CLASS:
+ case CKA_CERTIFICATE_TYPE:
+ case CKA_KEY_TYPE:
+ case CKA_MODULUS:
+ case CKA_MODULUS_BITS:
+ case CKA_PUBLIC_EXPONENT:
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME:
+ case CKA_SUBPRIME:
+ case CKA_BASE:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ case CKA_VALUE_LEN:
+ case CKA_ALWAYS_SENSITIVE:
+ case CKA_NEVER_EXTRACTABLE:
+ case CKA_NETSCAPE_DB:
+ mtype = SFTK_NEVER;
+ break;
+
+ /* ONCOPY */
+ case CKA_TOKEN:
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ mtype = SFTK_ONCOPY;
+ break;
+
+ /* SENSITIVE */
+ case CKA_SENSITIVE:
+ case CKA_EXTRACTABLE:
+ mtype = SFTK_SENSITIVE;
+ break;
+
+ /* ALWAYS */
+ case CKA_LABEL:
+ case CKA_APPLICATION:
+ case CKA_ID:
+ case CKA_SERIAL_NUMBER:
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ case CKA_DERIVE:
+ case CKA_ENCRYPT:
+ case CKA_DECRYPT:
+ case CKA_SIGN:
+ case CKA_VERIFY:
+ case CKA_SIGN_RECOVER:
+ case CKA_VERIFY_RECOVER:
+ case CKA_WRAP:
+ case CKA_UNWRAP:
+ mtype = SFTK_ALWAYS;
+ break;
+
+ /* DEPENDS ON CLASS */
+ case CKA_VALUE:
+ mtype = (inClass == CKO_DATA) ? SFTK_ALWAYS : SFTK_NEVER;
+ break;
+
+ case CKA_SUBJECT:
+ mtype = (inClass == CKO_CERTIFICATE) ? SFTK_NEVER : SFTK_ALWAYS;
+ break;
+ default:
+ break;
+ }
+ return mtype;
+}
+
+/* decode if a particular attribute is sensitive (cannot be read
+ * back to the user of if the object is set to SENSITIVE) */
+PRBool
+sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
+{
+ switch(type) {
+ /* ALWAYS */
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ return PR_TRUE;
+
+ /* DEPENDS ON CLASS */
+ case CKA_VALUE:
+ /* PRIVATE and SECRET KEYS have SENSITIVE values */
+ return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY));
+
+ default:
+ break;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * copy an attribute into a SECItem. Secitem is allocated in the specified
+ * arena.
+ */
+CK_RV
+sftk_Attribute2SecItem(PLArenaPool *arena,SECItem *item,SFTKObject *object,
+ CK_ATTRIBUTE_TYPE type)
+{
+ int len;
+ SFTKAttribute *attribute;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+ len = attribute->attrib.ulValueLen;
+
+ if (arena) {
+ item->data = (unsigned char *) PORT_ArenaAlloc(arena,len);
+ } else {
+ item->data = (unsigned char *) PORT_Alloc(len);
+ }
+ if (item->data == NULL) {
+ sftk_FreeAttribute(attribute);
+ return CKR_HOST_MEMORY;
+ }
+ item->len = len;
+ PORT_Memcpy(item->data,attribute->attrib.pValue, len);
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+CK_RV
+sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
+ CK_ULONG *longData)
+{
+ SFTKAttribute *attribute;
+
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
+
+ if (attribute->attrib.ulValueLen != sizeof(CK_ULONG)) {
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ }
+
+ *longData = *(CK_ULONG *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+ return CKR_OK;
+}
+
+void
+sftk_DeleteAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
+{
+ SFTKAttribute *attribute;
+ attribute = sftk_FindAttribute(object, type);
+ if (attribute == NULL) return ;
+ sftk_DeleteAttribute(object,attribute);
+ sftk_FreeAttribute(attribute);
+}
+
+CK_RV
+sftk_AddAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type,void *valPtr,
+ CK_ULONG length)
+{
+ SFTKAttribute *attribute;
+ attribute = sftk_NewAttribute(object,type,valPtr,length);
+ if (attribute == NULL) { return CKR_HOST_MEMORY; }
+ sftk_AddAttribute(object,attribute);
+ return CKR_OK;
+}
+
+/*
+ * ******************** Object Utilities *******************************
+ */
+
+/* must be called holding sftk_tokenKeyLock(slot) */
+static SECItem *
+sftk_lookupTokenKeyByHandle(SFTKSlot *slot, CK_OBJECT_HANDLE handle)
+{
+ return (SECItem *)PL_HashTableLookup(slot->tokObjHashTable, (void *)handle);
+}
+
+/*
+ * use the refLock. This operations should be very rare, so the added
+ * contention on the ref lock should be lower than the overhead of adding
+ * a new lock. We use separate functions for this just in case I'm wrong.
+ */
+static void
+sftk_tokenKeyLock(SFTKSlot *slot) {
+ PZ_Lock(slot->objectLock);
+}
+
+static void
+sftk_tokenKeyUnlock(SFTKSlot *slot) {
+ PZ_Unlock(slot->objectLock);
+}
+
+static PRIntn
+sftk_freeHashItem(PLHashEntry* entry, PRIntn index, void *arg)
+{
+ SECItem *item = (SECItem *)entry->value;
+
+ SECITEM_FreeItem(item, PR_TRUE);
+ return HT_ENUMERATE_NEXT;
+}
+
+CK_RV
+SFTK_ClearTokenKeyHashTable(SFTKSlot *slot)
+{
+ sftk_tokenKeyLock(slot);
+ PORT_Assert(!slot->present);
+ PL_HashTableEnumerateEntries(slot->tokObjHashTable, sftk_freeHashItem, NULL);
+ sftk_tokenKeyUnlock(slot);
+ return CKR_OK;
+}
+
+
+/* allocation hooks that allow us to recycle old object structures */
+static SFTKObjectFreeList sessionObjectList = { NULL, NULL, 0 };
+static SFTKObjectFreeList tokenObjectList = { NULL, NULL, 0 };
+
+SFTKObject *
+sftk_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace,
+ SFTKObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject)
+{
+ SFTKObject *object;
+ int size = 0;
+
+ if (!optimizeSpace) {
+ PZ_Lock(list->lock);
+ object = list->head;
+ if (object) {
+ list->head = object->next;
+ list->count--;
+ }
+ PZ_Unlock(list->lock);
+ if (object) {
+ object->next = object->prev = NULL;
+ *hasLocks = PR_TRUE;
+ return object;
+ }
+ }
+ size = isSessionObject ? sizeof(SFTKSessionObject)
+ + hashSize *sizeof(SFTKAttribute *) : sizeof(SFTKTokenObject);
+
+ object = (SFTKObject*)PORT_ZAlloc(size);
+ if (isSessionObject && object) {
+ ((SFTKSessionObject *)object)->hashSize = hashSize;
+ }
+ *hasLocks = PR_FALSE;
+ return object;
+}
+
+static void
+sftk_PutObjectToList(SFTKObject *object, SFTKObjectFreeList *list,
+ PRBool isSessionObject) {
+
+ /* the code below is equivalent to :
+ * optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE;
+ * just faster.
+ */
+ PRBool optimizeSpace = isSessionObject &&
+ ((SFTKSessionObject *)object)->optimizeSpace;
+ if (object->refLock && !optimizeSpace
+ && (list->count < MAX_OBJECT_LIST_SIZE)) {
+ PZ_Lock(list->lock);
+ object->next = list->head;
+ list->head = object;
+ list->count++;
+ PZ_Unlock(list->lock);
+ return;
+ }
+ if (isSessionObject) {
+ SFTKSessionObject *so = (SFTKSessionObject *)object;
+ PZ_DestroyLock(so->attributeLock);
+ so->attributeLock = NULL;
+ }
+ if (object->refLock) {
+ PZ_DestroyLock(object->refLock);
+ object->refLock = NULL;
+ }
+ PORT_Free(object);
+}
+
+static SFTKObject *
+sftk_freeObjectData(SFTKObject *object) {
+ SFTKObject *next = object->next;
+
+ PORT_Free(object);
+ return next;
+}
+
+static void
+sftk_InitFreeList(SFTKObjectFreeList *list)
+{
+ list->lock = PZ_NewLock(nssILockObject);
+}
+
+void sftk_InitFreeLists(void)
+{
+ sftk_InitFreeList(&sessionObjectList);
+ sftk_InitFreeList(&tokenObjectList);
+}
+
+static void
+sftk_CleanupFreeList(SFTKObjectFreeList *list, PRBool isSessionList)
+{
+ SFTKObject *object;
+
+ if (!list->lock) {
+ return;
+ }
+ PZ_Lock(list->lock);
+ for (object= list->head; object != NULL;
+ object = sftk_freeObjectData(object)) {
+ PZ_DestroyLock(object->refLock);
+ if (isSessionList) {
+ PZ_DestroyLock(((SFTKSessionObject *)object)->attributeLock);
+ }
+ }
+ list->count = 0;
+ list->head = NULL;
+ PZ_Unlock(list->lock);
+ PZ_DestroyLock(list->lock);
+ list->lock = NULL;
+}
+
+void
+sftk_CleanupFreeLists(void)
+{
+ sftk_CleanupFreeList(&sessionObjectList, PR_TRUE);
+ sftk_CleanupFreeList(&tokenObjectList, PR_FALSE);
+}
+
+
+/*
+ * Create a new object
+ */
+SFTKObject *
+sftk_NewObject(SFTKSlot *slot)
+{
+ SFTKObject *object;
+ SFTKSessionObject *sessObject;
+ PRBool hasLocks = PR_FALSE;
+ unsigned int i;
+ unsigned int hashSize = 0;
+
+ hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE :
+ TIME_ATTRIBUTE_HASH_SIZE;
+
+ object = sftk_GetObjectFromList(&hasLocks, slot->optimizeSpace,
+ &sessionObjectList, hashSize, PR_TRUE);
+ if (object == NULL) {
+ return NULL;
+ }
+ sessObject = (SFTKSessionObject *)object;
+ sessObject->nextAttr = 0;
+
+ for (i=0; i < MAX_OBJS_ATTRS; i++) {
+ sessObject->attrList[i].attrib.pValue = NULL;
+ sessObject->attrList[i].freeData = PR_FALSE;
+ }
+ sessObject->optimizeSpace = slot->optimizeSpace;
+
+ object->handle = 0;
+ object->next = object->prev = NULL;
+ object->slot = slot;
+
+ object->refCount = 1;
+ sessObject->sessionList.next = NULL;
+ sessObject->sessionList.prev = NULL;
+ sessObject->sessionList.parent = object;
+ sessObject->session = NULL;
+ sessObject->wasDerived = PR_FALSE;
+ if (!hasLocks) object->refLock = PZ_NewLock(nssILockRefLock);
+ if (object->refLock == NULL) {
+ PORT_Free(object);
+ return NULL;
+ }
+ if (!hasLocks) sessObject->attributeLock = PZ_NewLock(nssILockAttribute);
+ if (sessObject->attributeLock == NULL) {
+ PZ_DestroyLock(object->refLock);
+ PORT_Free(object);
+ return NULL;
+ }
+ for (i=0; i < sessObject->hashSize; i++) {
+ sessObject->head[i] = NULL;
+ }
+ object->objectInfo = NULL;
+ object->infoFree = NULL;
+ return object;
+}
+
+static CK_RV
+sftk_DestroySessionObjectData(SFTKSessionObject *so)
+{
+ int i;
+
+ for (i=0; i < MAX_OBJS_ATTRS; i++) {
+ unsigned char *value = so->attrList[i].attrib.pValue;
+ if (value) {
+ PORT_Memset(value,0,so->attrList[i].attrib.ulValueLen);
+ if (so->attrList[i].freeData) {
+ PORT_Free(value);
+ }
+ so->attrList[i].attrib.pValue = NULL;
+ so->attrList[i].freeData = PR_FALSE;
+ }
+ }
+/* PZ_DestroyLock(so->attributeLock);*/
+ return CKR_OK;
+}
+
+/*
+ * free all the data associated with an object. Object reference count must
+ * be 'zero'.
+ */
+static CK_RV
+sftk_DestroyObject(SFTKObject *object)
+{
+ CK_RV crv = CKR_OK;
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+ SFTKTokenObject *to = sftk_narrowToTokenObject(object);
+
+ PORT_Assert(object->refCount == 0);
+
+ /* delete the database value */
+ if (to) {
+ if (to->dbKey.data) {
+ PORT_Free(to->dbKey.data);
+ to->dbKey.data = NULL;
+ }
+ }
+ if (so) {
+ sftk_DestroySessionObjectData(so);
+ }
+ if (object->objectInfo) {
+ (*object->infoFree)(object->objectInfo);
+ object->objectInfo = NULL;
+ object->infoFree = NULL;
+ }
+ if (so) {
+ sftk_PutObjectToList(object,&sessionObjectList,PR_TRUE);
+ } else {
+ sftk_PutObjectToList(object,&tokenObjectList,PR_FALSE);
+ }
+ return crv;
+}
+
+void
+sftk_ReferenceObject(SFTKObject *object)
+{
+ PZ_Lock(object->refLock);
+ object->refCount++;
+ PZ_Unlock(object->refLock);
+}
+
+static SFTKObject *
+sftk_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, SFTKSlot *slot)
+{
+ SFTKObject *object;
+ PRUint32 index = sftk_hash(handle, slot->sessObjHashSize);
+
+ if (sftk_isToken(handle)) {
+ return sftk_NewTokenObject(slot, NULL, handle);
+ }
+
+ PZ_Lock(slot->objectLock);
+ sftkqueue_find2(object, handle, index, slot->sessObjHashTable);
+ if (object) {
+ sftk_ReferenceObject(object);
+ }
+ PZ_Unlock(slot->objectLock);
+
+ return(object);
+}
+/*
+ * look up and object structure from a handle. OBJECT_Handles only make
+ * sense in terms of a given session. make a reference to that object
+ * structure returned.
+ */
+SFTKObject *
+sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle, SFTKSession *session)
+{
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+
+ return sftk_ObjectFromHandleOnSlot(handle,slot);
+}
+
+
+/*
+ * release a reference to an object handle
+ */
+SFTKFreeStatus
+sftk_FreeObject(SFTKObject *object)
+{
+ PRBool destroy = PR_FALSE;
+ CK_RV crv;
+
+ PZ_Lock(object->refLock);
+ if (object->refCount == 1) destroy = PR_TRUE;
+ object->refCount--;
+ PZ_Unlock(object->refLock);
+
+ if (destroy) {
+ crv = sftk_DestroyObject(object);
+ if (crv != CKR_OK) {
+ return SFTK_DestroyFailure;
+ }
+ return SFTK_Destroyed;
+ }
+ return SFTK_Busy;
+}
+
+/*
+ * add an object to a slot and session queue. These two functions
+ * adopt the object.
+ */
+void
+sftk_AddSlotObject(SFTKSlot *slot, SFTKObject *object)
+{
+ PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
+ sftkqueue_init_element(object);
+ PZ_Lock(slot->objectLock);
+ sftkqueue_add2(object, object->handle, index, slot->sessObjHashTable);
+ PZ_Unlock(slot->objectLock);
+}
+
+void
+sftk_AddObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+
+ if (so) {
+ PZ_Lock(session->objectLock);
+ sftkqueue_add(&so->sessionList,0,session->objects,0);
+ so->session = session;
+ PZ_Unlock(session->objectLock);
+ }
+ sftk_AddSlotObject(slot,object);
+ sftk_ReferenceObject(object);
+}
+
+/*
+ * delete an object from a slot and session queue
+ */
+CK_RV
+sftk_DeleteObject(SFTKSession *session, SFTKObject *object)
+{
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+ SFTKSessionObject *so = sftk_narrowToSessionObject(object);
+ SFTKTokenObject *to = sftk_narrowToTokenObject(object);
+ CK_RV crv = CKR_OK;
+ PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
+
+ /* Handle Token case */
+ if (so && so->session) {
+ SFTKSession *session = so->session;
+ PZ_Lock(session->objectLock);
+ sftkqueue_delete(&so->sessionList,0,session->objects,0);
+ PZ_Unlock(session->objectLock);
+ PZ_Lock(slot->objectLock);
+ sftkqueue_delete2(object, object->handle, index, slot->sessObjHashTable);
+ PZ_Unlock(slot->objectLock);
+ sftkqueue_clear_deleted_element(object);
+ sftk_FreeObject(object); /* free the reference owned by the queue */
+ } else {
+ SFTKDBHandle *handle = sftk_getDBForTokenObject(slot, object->handle);
+
+ PORT_Assert(to);
+ crv = sftkdb_DestroyObject(handle, object->handle);
+ sftk_freeDB(handle);
+ }
+ return crv;
+}
+
+/*
+ * Token objects don't explicitly store their attributes, so we need to know
+ * what attributes make up a particular token object before we can copy it.
+ * below are the tables by object type.
+ */
+static const CK_ATTRIBUTE_TYPE commonAttrs[] = {
+ CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_MODIFIABLE
+};
+static const CK_ULONG commonAttrsCount =
+ sizeof(commonAttrs)/sizeof(commonAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE commonKeyAttrs[] = {
+ CKA_ID, CKA_START_DATE, CKA_END_DATE, CKA_DERIVE, CKA_LOCAL, CKA_KEY_TYPE
+};
+static const CK_ULONG commonKeyAttrsCount =
+ sizeof(commonKeyAttrs)/sizeof(commonKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE secretKeyAttrs[] = {
+ CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_ENCRYPT, CKA_DECRYPT, CKA_SIGN,
+ CKA_VERIFY, CKA_WRAP, CKA_UNWRAP, CKA_VALUE
+};
+static const CK_ULONG secretKeyAttrsCount =
+ sizeof(secretKeyAttrs)/sizeof(secretKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE commonPubKeyAttrs[] = {
+ CKA_ENCRYPT, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP, CKA_SUBJECT
+};
+static const CK_ULONG commonPubKeyAttrsCount =
+ sizeof(commonPubKeyAttrs)/sizeof(commonPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE rsaPubKeyAttrs[] = {
+ CKA_MODULUS, CKA_PUBLIC_EXPONENT
+};
+static const CK_ULONG rsaPubKeyAttrsCount =
+ sizeof(rsaPubKeyAttrs)/sizeof(rsaPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dsaPubKeyAttrs[] = {
+ CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dsaPubKeyAttrsCount =
+ sizeof(dsaPubKeyAttrs)/sizeof(dsaPubKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dhPubKeyAttrs[] = {
+ CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dhPubKeyAttrsCount =
+ sizeof(dhPubKeyAttrs)/sizeof(dhPubKeyAttrs[0]);
+#ifdef NSS_ENABLE_ECC
+static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = {
+ CKA_EC_PARAMS, CKA_EC_POINT
+};
+static const CK_ULONG ecPubKeyAttrsCount =
+ sizeof(ecPubKeyAttrs)/sizeof(ecPubKeyAttrs[0]);
+#endif
+
+static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = {
+ CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT,
+ CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NETSCAPE_DB
+};
+static const CK_ULONG commonPrivKeyAttrsCount =
+ sizeof(commonPrivKeyAttrs)/sizeof(commonPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE rsaPrivKeyAttrs[] = {
+ CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
+ CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT
+};
+static const CK_ULONG rsaPrivKeyAttrsCount =
+ sizeof(rsaPrivKeyAttrs)/sizeof(rsaPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dsaPrivKeyAttrs[] = {
+ CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dsaPrivKeyAttrsCount =
+ sizeof(dsaPrivKeyAttrs)/sizeof(dsaPrivKeyAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE dhPrivKeyAttrs[] = {
+ CKA_PRIME, CKA_BASE, CKA_VALUE
+};
+static const CK_ULONG dhPrivKeyAttrsCount =
+ sizeof(dhPrivKeyAttrs)/sizeof(dhPrivKeyAttrs[0]);
+#ifdef NSS_ENABLE_ECC
+static const CK_ATTRIBUTE_TYPE ecPrivKeyAttrs[] = {
+ CKA_EC_PARAMS, CKA_VALUE
+};
+static const CK_ULONG ecPrivKeyAttrsCount =
+ sizeof(ecPrivKeyAttrs)/sizeof(ecPrivKeyAttrs[0]);
+#endif
+
+static const CK_ATTRIBUTE_TYPE certAttrs[] = {
+ CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER
+};
+static const CK_ULONG certAttrsCount =
+ sizeof(certAttrs)/sizeof(certAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE trustAttrs[] = {
+ CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
+ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_EMAIL_PROTECTION,
+ CKA_TRUST_CODE_SIGNING, CKA_TRUST_STEP_UP_APPROVED
+};
+static const CK_ULONG trustAttrsCount =
+ sizeof(trustAttrs)/sizeof(trustAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE smimeAttrs[] = {
+ CKA_SUBJECT, CKA_NETSCAPE_EMAIL, CKA_NETSCAPE_SMIME_TIMESTAMP, CKA_VALUE
+};
+static const CK_ULONG smimeAttrsCount =
+ sizeof(smimeAttrs)/sizeof(smimeAttrs[0]);
+
+static const CK_ATTRIBUTE_TYPE crlAttrs[] = {
+ CKA_SUBJECT, CKA_VALUE, CKA_NETSCAPE_URL, CKA_NETSCAPE_KRL
+};
+static const CK_ULONG crlAttrsCount =
+ sizeof(crlAttrs)/sizeof(crlAttrs[0]);
+
+/* copy an object based on it's table */
+CK_RV
+stfk_CopyTokenAttributes(SFTKObject *destObject,SFTKTokenObject *src_to,
+ const CK_ATTRIBUTE_TYPE *attrArray, CK_ULONG attrCount)
+{
+ SFTKAttribute *attribute;
+ SFTKAttribute *newAttribute;
+ CK_RV crv = CKR_OK;
+ unsigned int i;
+
+ for (i=0; i < attrCount; i++) {
+ if (!sftk_hasAttribute(destObject,attrArray[i])) {
+ attribute =sftk_FindAttribute(&src_to->obj, attrArray[i]);
+ if (!attribute) {
+ continue; /* return CKR_ATTRIBUTE_VALUE_INVALID; */
+ }
+ /* we need to copy the attribute since each attribute
+ * only has one set of link list pointers */
+ newAttribute = sftk_NewAttribute( destObject,
+ sftk_attr_expand(&attribute->attrib));
+ sftk_FreeAttribute(attribute); /* free the old attribute */
+ if (!newAttribute) {
+ return CKR_HOST_MEMORY;
+ }
+ sftk_AddAttribute(destObject,newAttribute);
+ }
+ }
+ return crv;
+}
+
+CK_RV
+stfk_CopyTokenPrivateKey(SFTKObject *destObject,SFTKTokenObject *src_to)
+{
+ CK_RV crv;
+ CK_KEY_TYPE key_type;
+ SFTKAttribute *attribute;
+
+ /* copy the common attributes for all keys first */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
+ commonKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ /* copy the common attributes for all private keys next */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonPrivKeyAttrs,
+ commonPrivKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ attribute =sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
+ PORT_Assert(attribute); /* if it wasn't here, ww should have failed
+ * copying the common attributes */
+ if (!attribute) {
+ /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
+ * the fact is, the only reason we couldn't get the attribute would
+ * be a memory error or database error (an error in the 'device').
+ * if we have a database error code, we could return it here */
+ crv = CKR_DEVICE_ERROR;
+ goto fail;
+ }
+ key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ /* finally copy the attributes for various private key types */
+ switch (key_type) {
+ case CKK_RSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPrivKeyAttrs,
+ rsaPrivKeyAttrsCount);
+ break;
+ case CKK_DSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPrivKeyAttrs,
+ dsaPrivKeyAttrsCount);
+ break;
+ case CKK_DH:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs,
+ dhPrivKeyAttrsCount);
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs,
+ ecPrivKeyAttrsCount);
+ break;
+#endif
+ default:
+ crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
+ * of token keys into our database. */
+ }
+fail:
+ return crv;
+}
+
+CK_RV
+stfk_CopyTokenPublicKey(SFTKObject *destObject,SFTKTokenObject *src_to)
+{
+ CK_RV crv;
+ CK_KEY_TYPE key_type;
+ SFTKAttribute *attribute;
+
+ /* copy the common attributes for all keys first */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
+ commonKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+
+ /* copy the common attributes for all public keys next */
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonPubKeyAttrs,
+ commonPubKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ attribute =sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
+ PORT_Assert(attribute); /* if it wasn't here, ww should have failed
+ * copying the common attributes */
+ if (!attribute) {
+ /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
+ * the fact is, the only reason we couldn't get the attribute would
+ * be a memory error or database error (an error in the 'device').
+ * if we have a database error code, we could return it here */
+ crv = CKR_DEVICE_ERROR;
+ goto fail;
+ }
+ key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
+ sftk_FreeAttribute(attribute);
+
+ /* finally copy the attributes for various public key types */
+ switch (key_type) {
+ case CKK_RSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPubKeyAttrs,
+ rsaPubKeyAttrsCount);
+ break;
+ case CKK_DSA:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPubKeyAttrs,
+ dsaPubKeyAttrsCount);
+ break;
+ case CKK_DH:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs,
+ dhPubKeyAttrsCount);
+ break;
+#ifdef NSS_ENABLE_ECC
+ case CKK_EC:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs,
+ ecPubKeyAttrsCount);
+ break;
+#endif
+ default:
+ crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
+ * of token keys into our database. */
+ }
+fail:
+ return crv;
+}
+CK_RV
+stfk_CopyTokenSecretKey(SFTKObject *destObject,SFTKTokenObject *src_to)
+{
+ CK_RV crv;
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
+ commonKeyAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ crv = stfk_CopyTokenAttributes(destObject, src_to, secretKeyAttrs,
+ secretKeyAttrsCount);
+fail:
+ return crv;
+}
+
+/*
+ * Copy a token object. We need to explicitly copy the relevant
+ * attributes since token objects don't store those attributes in
+ * the token itself.
+ */
+CK_RV
+sftk_CopyTokenObject(SFTKObject *destObject,SFTKObject *srcObject)
+{
+ SFTKTokenObject *src_to = sftk_narrowToTokenObject(srcObject);
+ CK_RV crv;
+
+ PORT_Assert(src_to);
+ if (src_to == NULL) {
+ return CKR_DEVICE_ERROR; /* internal state inconsistant */
+ }
+
+ crv = stfk_CopyTokenAttributes(destObject, src_to, commonAttrs,
+ commonAttrsCount);
+ if (crv != CKR_OK) {
+ goto fail;
+ }
+ switch (src_to->obj.objclass) {
+ case CKO_CERTIFICATE:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, certAttrs,
+ certAttrsCount);
+ break;
+ case CKO_NETSCAPE_TRUST:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, trustAttrs,
+ trustAttrsCount);
+ break;
+ case CKO_NETSCAPE_SMIME:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, smimeAttrs,
+ smimeAttrsCount);
+ break;
+ case CKO_NETSCAPE_CRL:
+ crv = stfk_CopyTokenAttributes(destObject, src_to, crlAttrs,
+ crlAttrsCount);
+ break;
+ case CKO_PRIVATE_KEY:
+ crv = stfk_CopyTokenPrivateKey(destObject,src_to);
+ break;
+ case CKO_PUBLIC_KEY:
+ crv = stfk_CopyTokenPublicKey(destObject,src_to);
+ break;
+ case CKO_SECRET_KEY:
+ crv = stfk_CopyTokenSecretKey(destObject,src_to);
+ break;
+ default:
+ crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
+ * of token keys into our database. */
+ }
+fail:
+ return crv;
+}
+
+/*
+ * copy the attributes from one object to another. Don't overwrite existing
+ * attributes. NOTE: This is a pretty expensive operation since it
+ * grabs the attribute locks for the src object for a *long* time.
+ */
+CK_RV
+sftk_CopyObject(SFTKObject *destObject,SFTKObject *srcObject)
+{
+ SFTKAttribute *attribute;
+ SFTKSessionObject *src_so = sftk_narrowToSessionObject(srcObject);
+ unsigned int i;
+
+ if (src_so == NULL) {
+ return sftk_CopyTokenObject(destObject,srcObject);
+ }
+
+ PZ_Lock(src_so->attributeLock);
+ for(i=0; i < src_so->hashSize; i++) {
+ attribute = src_so->head[i];
+ do {
+ if (attribute) {
+ if (!sftk_hasAttribute(destObject,attribute->handle)) {
+ /* we need to copy the attribute since each attribute
+ * only has one set of link list pointers */
+ SFTKAttribute *newAttribute = sftk_NewAttribute(
+ destObject,sftk_attr_expand(&attribute->attrib));
+ if (newAttribute == NULL) {
+ PZ_Unlock(src_so->attributeLock);
+ return CKR_HOST_MEMORY;
+ }
+ sftk_AddAttribute(destObject,newAttribute);
+ }
+ attribute=attribute->next;
+ }
+ } while (attribute != NULL);
+ }
+ PZ_Unlock(src_so->attributeLock);
+
+ return CKR_OK;
+}
+
+/*
+ * ******************** Search Utilities *******************************
+ */
+
+/* add an object to a search list */
+CK_RV
+AddToList(SFTKObjectListElement **list,SFTKObject *object)
+{
+ SFTKObjectListElement *newElem =
+ (SFTKObjectListElement *)PORT_Alloc(sizeof(SFTKObjectListElement));
+
+ if (newElem == NULL) return CKR_HOST_MEMORY;
+
+ newElem->next = *list;
+ newElem->object = object;
+ sftk_ReferenceObject(object);
+
+ *list = newElem;
+ return CKR_OK;
+}
+
+
+/* return true if the object matches the template */
+PRBool
+sftk_objectMatch(SFTKObject *object,CK_ATTRIBUTE_PTR theTemplate,int count)
+{
+ int i;
+
+ for (i=0; i < count; i++) {
+ SFTKAttribute *attribute = sftk_FindAttribute(object,theTemplate[i].type);
+ if (attribute == NULL) {
+ return PR_FALSE;
+ }
+ if (attribute->attrib.ulValueLen == theTemplate[i].ulValueLen) {
+ if (PORT_Memcmp(attribute->attrib.pValue,theTemplate[i].pValue,
+ theTemplate[i].ulValueLen) == 0) {
+ sftk_FreeAttribute(attribute);
+ continue;
+ }
+ }
+ sftk_FreeAttribute(attribute);
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/* search through all the objects in the queue and return the template matches
+ * in the object list.
+ */
+CK_RV
+sftk_searchObjectList(SFTKSearchResults *search,SFTKObject **head,
+ unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate,
+ int count, PRBool isLoggedIn)
+{
+ unsigned int i;
+ SFTKObject *object;
+ CK_RV crv = CKR_OK;
+
+ for(i=0; i < size; i++) {
+ /* We need to hold the lock to copy a consistant version of
+ * the linked list. */
+ PZ_Lock(lock);
+ for (object = head[i]; object != NULL; object= object->next) {
+ if (sftk_objectMatch(object,theTemplate,count)) {
+ /* don't return objects that aren't yet visible */
+ if ((!isLoggedIn) && sftk_isTrue(object,CKA_PRIVATE)) continue;
+ sftk_addHandle(search,object->handle);
+ }
+ }
+ PZ_Unlock(lock);
+ }
+ return crv;
+}
+
+/*
+ * free a single list element. Return the Next object in the list.
+ */
+SFTKObjectListElement *
+sftk_FreeObjectListElement(SFTKObjectListElement *objectList)
+{
+ SFTKObjectListElement *ol = objectList->next;
+
+ sftk_FreeObject(objectList->object);
+ PORT_Free(objectList);
+ return ol;
+}
+
+/* free an entire object list */
+void
+sftk_FreeObjectList(SFTKObjectListElement *objectList)
+{
+ SFTKObjectListElement *ol;
+
+ for (ol= objectList; ol != NULL; ol = sftk_FreeObjectListElement(ol)) {}
+}
+
+/*
+ * free a search structure
+ */
+void
+sftk_FreeSearch(SFTKSearchResults *search)
+{
+ if (search->handles) {
+ PORT_Free(search->handles);
+ }
+ PORT_Free(search);
+}
+
+/*
+ * ******************** Session Utilities *******************************
+ */
+
+/* update the sessions state based in it's flags and wether or not it's
+ * logged in */
+void
+sftk_update_state(SFTKSlot *slot,SFTKSession *session)
+{
+ if (slot->isLoggedIn) {
+ if (slot->ssoLoggedIn) {
+ session->info.state = CKS_RW_SO_FUNCTIONS;
+ } else if (session->info.flags & CKF_RW_SESSION) {
+ session->info.state = CKS_RW_USER_FUNCTIONS;
+ } else {
+ session->info.state = CKS_RO_USER_FUNCTIONS;
+ }
+ } else {
+ if (session->info.flags & CKF_RW_SESSION) {
+ session->info.state = CKS_RW_PUBLIC_SESSION;
+ } else {
+ session->info.state = CKS_RO_PUBLIC_SESSION;
+ }
+ }
+}
+
+/* update the state of all the sessions on a slot */
+void
+sftk_update_all_states(SFTKSlot *slot)
+{
+ unsigned int i;
+ SFTKSession *session;
+
+ for (i=0; i < slot->sessHashSize; i++) {
+ PZLock *lock = SFTK_SESSION_LOCK(slot,i);
+ PZ_Lock(lock);
+ for (session = slot->head[i]; session; session = session->next) {
+ sftk_update_state(slot,session);
+ }
+ PZ_Unlock(lock);
+ }
+}
+
+/*
+ * context are cipher and digest contexts that are associated with a session
+ */
+void
+sftk_FreeContext(SFTKSessionContext *context)
+{
+ if (context->cipherInfo) {
+ (*context->destroy)(context->cipherInfo,PR_TRUE);
+ }
+ if (context->hashInfo) {
+ (*context->hashdestroy)(context->hashInfo,PR_TRUE);
+ }
+ if (context->key) {
+ sftk_FreeObject(context->key);
+ context->key = NULL;
+ }
+ PORT_Free(context);
+}
+
+/*
+ * create a new nession. NOTE: The session handle is not set, and the
+ * session is not added to the slot's session queue.
+ */
+SFTKSession *
+sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication,
+ CK_FLAGS flags)
+{
+ SFTKSession *session;
+ SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
+
+ if (slot == NULL) return NULL;
+
+ session = (SFTKSession*)PORT_Alloc(sizeof(SFTKSession));
+ if (session == NULL) return NULL;
+
+ session->next = session->prev = NULL;
+ session->refCount = 1;
+ session->enc_context = NULL;
+ session->hash_context = NULL;
+ session->sign_context = NULL;
+ session->search = NULL;
+ session->objectIDCount = 1;
+ session->objectLock = PZ_NewLock(nssILockObject);
+ if (session->objectLock == NULL) {
+ PORT_Free(session);
+ return NULL;
+ }
+ session->objects[0] = NULL;
+
+ session->slot = slot;
+ session->notify = notify;
+ session->appData = pApplication;
+ session->info.flags = flags;
+ session->info.slotID = slotID;
+ session->info.ulDeviceError = 0;
+ sftk_update_state(slot,session);
+ return session;
+}
+
+
+/* free all the data associated with a session. */
+static void
+sftk_DestroySession(SFTKSession *session)
+{
+ SFTKObjectList *op,*next;
+ PORT_Assert(session->refCount == 0);
+
+ /* clean out the attributes */
+ /* since no one is referencing us, it's safe to walk the chain
+ * without a lock */
+ for (op = session->objects[0]; op != NULL; op = next) {
+ next = op->next;
+ /* paranoia */
+ op->next = op->prev = NULL;
+ sftk_DeleteObject(session,op->parent);
+ }
+ PZ_DestroyLock(session->objectLock);
+ if (session->enc_context) {
+ sftk_FreeContext(session->enc_context);
+ }
+ if (session->hash_context) {
+ sftk_FreeContext(session->hash_context);
+ }
+ if (session->sign_context) {
+ sftk_FreeContext(session->sign_context);
+ }
+ if (session->search) {
+ sftk_FreeSearch(session->search);
+ }
+ PORT_Free(session);
+}
+
+
+/*
+ * look up a session structure from a session handle
+ * generate a reference to it.
+ */
+SFTKSession *
+sftk_SessionFromHandle(CK_SESSION_HANDLE handle)
+{
+ SFTKSlot *slot = sftk_SlotFromSessionHandle(handle);
+ SFTKSession *session;
+ PZLock *lock;
+
+ if (!slot) return NULL;
+ lock = SFTK_SESSION_LOCK(slot,handle);
+
+ PZ_Lock(lock);
+ sftkqueue_find(session,handle,slot->head,slot->sessHashSize);
+ if (session) session->refCount++;
+ PZ_Unlock(lock);
+
+ return (session);
+}
+
+/*
+ * release a reference to a session handle
+ */
+void
+sftk_FreeSession(SFTKSession *session)
+{
+ PRBool destroy = PR_FALSE;
+ SFTKSlot *slot = sftk_SlotFromSession(session);
+ PZLock *lock = SFTK_SESSION_LOCK(slot,session->handle);
+
+ PZ_Lock(lock);
+ if (session->refCount == 1) destroy = PR_TRUE;
+ session->refCount--;
+ PZ_Unlock(lock);
+
+ if (destroy) sftk_DestroySession(session);
+}
+
+void
+sftk_addHandle(SFTKSearchResults *search, CK_OBJECT_HANDLE handle)
+{
+ if (search->handles == NULL) {
+ return;
+ }
+ if (search->size >= search->array_size) {
+ search->array_size += NSC_SEARCH_BLOCK_SIZE;
+ search->handles = (CK_OBJECT_HANDLE *) PORT_Realloc(search->handles,
+ sizeof(CK_OBJECT_HANDLE)* search->array_size);
+ if (search->handles == NULL) {
+ return;
+ }
+ }
+ search->handles[search->size] = handle;
+ search->size++;
+}
+
+static CK_RV
+handleToClass(SFTKSlot *slot, CK_OBJECT_HANDLE handle,
+ CK_OBJECT_CLASS *objClass)
+{
+ SFTKDBHandle *dbHandle = sftk_getDBForTokenObject(slot, handle);
+ CK_ATTRIBUTE objClassTemplate;
+ CK_RV crv;
+
+ *objClass = CKO_DATA;
+ objClassTemplate.type = CKA_CLASS;
+ objClassTemplate.pValue = objClass;
+ objClassTemplate.ulValueLen = sizeof(*objClass);
+ crv = sftkdb_GetAttributeValue(dbHandle, handle, &objClassTemplate, 1);
+ sftk_freeDB(dbHandle);
+ return crv;
+}
+
+SFTKObject *
+sftk_NewTokenObject(SFTKSlot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle)
+{
+ SFTKObject *object = NULL;
+ SFTKTokenObject *tokObject = NULL;
+ PRBool hasLocks = PR_FALSE;
+ CK_RV crv;
+
+ object = sftk_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList, 0,
+ PR_FALSE);
+ if (object == NULL) {
+ return NULL;
+ }
+ tokObject = (SFTKTokenObject *) object;
+
+ object->handle = handle;
+ /* every object must have a class, if we can't get it, the object
+ * doesn't exist */
+ crv = handleToClass(slot, handle, &object->objclass);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ object->slot = slot;
+ object->objectInfo = NULL;
+ object->infoFree = NULL;
+ if (!hasLocks) {
+ object->refLock = PZ_NewLock(nssILockRefLock);
+ }
+ if (object->refLock == NULL) {
+ goto loser;
+ }
+ object->refCount = 1;
+
+ return object;
+loser:
+ if (object) {
+ (void) sftk_DestroyObject(object);
+ }
+ return NULL;
+
+}
+
+SFTKTokenObject *
+sftk_convertSessionToToken(SFTKObject *obj)
+{
+ SECItem *key;
+ SFTKSessionObject *so = (SFTKSessionObject *)obj;
+ SFTKTokenObject *to = sftk_narrowToTokenObject(obj);
+ SECStatus rv;
+
+ sftk_DestroySessionObjectData(so);
+ PZ_DestroyLock(so->attributeLock);
+ if (to == NULL) {
+ return NULL;
+ }
+ sftk_tokenKeyLock(so->obj.slot);
+ key = sftk_lookupTokenKeyByHandle(so->obj.slot,so->obj.handle);
+ if (key == NULL) {
+ sftk_tokenKeyUnlock(so->obj.slot);
+ return NULL;
+ }
+ rv = SECITEM_CopyItem(NULL,&to->dbKey,key);
+ sftk_tokenKeyUnlock(so->obj.slot);
+ if (rv == SECFailure) {
+ return NULL;
+ }
+
+ return to;
+}
+
+SFTKSessionObject *
+sftk_narrowToSessionObject(SFTKObject *obj)
+{
+ return !sftk_isToken(obj->handle) ? (SFTKSessionObject *)obj : NULL;
+}
+
+SFTKTokenObject *
+sftk_narrowToTokenObject(SFTKObject *obj)
+{
+ return sftk_isToken(obj->handle) ? (SFTKTokenObject *)obj : NULL;
+}
+
diff --git a/security/nss/lib/softoken/pkcs11u.h b/security/nss/lib/softoken/pkcs11u.h
new file mode 100644
index 000000000..8383b471d
--- /dev/null
+++ b/security/nss/lib/softoken/pkcs11u.h
@@ -0,0 +1,52 @@
+/* ***** 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 ***** */
+/*
+ * Copyright (C) 1994-1999 RSA Security Inc. Licence to copy this document
+ * is granted provided that it is identified as "RSA Security Inc. Public-Key
+ * Cryptography Standards (PKCS)" in all material mentioning or referencing
+ * this document.
+ */
+/*
+ * reset any packing set by pkcs11p.h
+ */
+
+#if defined (_WIN32)
+#ifdef _MSC_VER
+#pragma warning(disable:4103)
+#endif
+#pragma pack(pop, cryptoki)
+#endif
+
diff --git a/security/nss/lib/softoken/rsawrapr.c b/security/nss/lib/softoken/rsawrapr.c
new file mode 100644
index 000000000..6c7fa0f30
--- /dev/null
+++ b/security/nss/lib/softoken/rsawrapr.c
@@ -0,0 +1,917 @@
+/*
+ * 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):
+ *
+ * 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 "blapi.h"
+#include "softoken.h"
+#include "sechash.h"
+
+#include "lowkeyi.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. */
+
+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 = PORT_Alloc(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)
+ PORT_Free(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;
+}
+
+/*
+ * 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 *) PORT_Alloc(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_BlockPrivate0: /* essentially unused */
+ 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) {
+ PORT_Free (block);
+ return NULL;
+ }
+ PORT_Memset (bp,
+ blockType == RSA_BlockPrivate0
+ ? RSA_BLOCK_PRIVATE0_PAD_OCTET
+ : RSA_BLOCK_PRIVATE_PAD_OCTET,
+ padLen);
+ bp += padLen;
+ *bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
+ PORT_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) {
+ PORT_Free (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) {
+ sftk_fatalError = PR_TRUE;
+ PORT_Free (block);
+ return NULL;
+ }
+ }
+ bp += padLen;
+ *bp++ = RSA_BLOCK_AFTER_PAD_OCTET;
+ PORT_Memcpy (bp, data->data, data->len);
+
+ break;
+
+ /*
+ * Blocks intended for public-key operation, using
+ * Optimal Asymmetric Encryption Padding (OAEP).
+ */
+ case RSA_BlockOAEP:
+ /*
+ * 0x00 || BT || Modified2(Salt) || Modified1(PaddedData)
+ * 1 1 OAEP_SALT_LEN OAEP_PAD_LEN + data->len [+ N]
+ *
+ * where:
+ * PaddedData is "Pad1 || ActualData [|| Pad2]"
+ * Salt is random data.
+ * Pad1 is all zeros.
+ * Pad2, if present, is random data.
+ * (The "modified" fields are all the same length as the original
+ * unmodified values; they are just xor'd with other values.)
+ *
+ * Modified1 is an XOR of PaddedData with a special octet
+ * string constructed of iterated hashing of Salt (see below).
+ * Modified2 is an XOR of Salt with the low-order octets of
+ * the hash of Modified1 (see farther below ;-).
+ *
+ * Whew!
+ */
+
+
+ /*
+ * Salt
+ */
+ rv = RNG_GenerateGlobalRandomBytes(bp, OAEP_SALT_LEN);
+ if (rv != SECSuccess) {
+ sftk_fatalError = PR_TRUE;
+ PORT_Free (block);
+ return NULL;
+ }
+ bp += OAEP_SALT_LEN;
+
+ /*
+ * Pad1
+ */
+ PORT_Memset (bp, OAEP_PAD_OCTET, OAEP_PAD_LEN);
+ bp += OAEP_PAD_LEN;
+
+ /*
+ * Data
+ */
+ PORT_Memcpy (bp, data->data, data->len);
+ bp += data->len;
+
+ /*
+ * Pad2
+ */
+ if (bp < (block + modulusLen)) {
+ rv = RNG_GenerateGlobalRandomBytes(bp, block - bp + modulusLen);
+ if (rv != SECSuccess) {
+ sftk_fatalError = PR_TRUE;
+ PORT_Free (block);
+ return NULL;
+ }
+ }
+
+ /*
+ * Now we have the following:
+ * 0x00 || BT || Salt || PaddedData
+ * (From this point on, "Pad1 || Data [|| Pad2]" is treated
+ * as the one entity PaddedData.)
+ *
+ * We need to turn PaddedData into Modified1.
+ */
+ if (oaep_xor_with_h1(block + 2 + OAEP_SALT_LEN,
+ modulusLen - 2 - OAEP_SALT_LEN,
+ block + 2, OAEP_SALT_LEN) != SECSuccess) {
+ PORT_Free (block);
+ return NULL;
+ }
+
+ /*
+ * Now we have:
+ * 0x00 || BT || Salt || Modified1(PaddedData)
+ *
+ * The remaining task is to turn Salt into Modified2.
+ */
+ if (oaep_xor_with_h2(block + 2, OAEP_SALT_LEN,
+ block + 2 + OAEP_SALT_LEN,
+ modulusLen - 2 - OAEP_SALT_LEN) != SECSuccess) {
+ PORT_Free (block);
+ return NULL;
+ }
+
+ break;
+
+ default:
+ PORT_Assert (0);
+ PORT_Free (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_BlockPrivate0:
+ 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_BlockOAEP:
+ /*
+ * 0x00 || BT || M1(Salt) || M2(Pad1||ActualData[||Pad2])
+ *
+ * The "2" below is the first octet + the second octet.
+ * (The other fields do not contain the clear values, but are
+ * the same length as the clear values.)
+ */
+ PORT_Assert (data->len <= (modulusLen - (2 + OAEP_SALT_LEN
+ + OAEP_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*)PORT_ZAlloc(modulusLen);
+ result->len = modulusLen;
+ PORT_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
+RSA_Sign(NSSLOWKEYPrivateKey *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 = nsslowkey_PrivateModulusLen(key);
+ SECItem formatted;
+ SECItem unformatted;
+
+ if (maxOutputLen < modulus_len)
+ return SECFailure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ 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);
+ if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ *output_len = modulus_len;
+
+ goto done;
+
+done:
+ if (formatted.data != NULL)
+ PORT_ZFree(formatted.data, modulus_len);
+ return rv;
+}
+
+/* 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 = nsslowkey_PublicModulusLen(key);
+ unsigned int i;
+ unsigned char * buffer;
+
+ modulus_len = nsslowkey_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 == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ goto failure;
+
+ buffer = (unsigned char *)PORT_Alloc(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 (PORT_Memcmp(buffer + modulus_len - hash_len, hash, hash_len) != 0)
+ goto loser;
+
+ PORT_Free(buffer);
+ return SECSuccess;
+
+loser:
+ PORT_Free(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 = nsslowkey_PublicModulusLen(key);
+ unsigned int i;
+ unsigned char * buffer;
+
+ if (sign_len != modulus_len)
+ goto failure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ goto failure;
+
+ buffer = (unsigned char *)PORT_Alloc(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
+ */
+ PORT_Memcpy(data,buffer + modulus_len - *data_len, *data_len);
+
+ PORT_Free(buffer);
+ return SECSuccess;
+
+loser:
+ PORT_Free(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 = nsslowkey_PublicModulusLen(key);
+ SECItem formatted;
+ SECItem unformatted;
+
+ formatted.data = NULL;
+ if (max_output_len < modulus_len)
+ goto failure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ 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;
+
+ PORT_ZFree(formatted.data, modulus_len);
+ *output_len = modulus_len;
+ return SECSuccess;
+
+failure:
+ if (formatted.data != NULL)
+ PORT_ZFree(formatted.data, modulus_len);
+ return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_DecryptBlock(NSSLOWKEYPrivateKey *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 = nsslowkey_PrivateModulusLen(key);
+ unsigned int i;
+ unsigned char * buffer;
+
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ goto failure;
+ if (input_len != modulus_len)
+ goto failure;
+
+ buffer = (unsigned char *)PORT_Alloc(modulus_len + 1);
+ if (!buffer)
+ goto failure;
+
+ rv = RSA_PrivateKeyOp(&key->u.rsa, buffer, input);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ 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;
+
+ PORT_Memcpy(output, buffer + modulus_len - *output_len, *output_len);
+
+ PORT_Free(buffer);
+ return SECSuccess;
+
+loser:
+ PORT_Free(buffer);
+failure:
+ return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+/*
+ * added to make pkcs #11 happy
+ * RAW is RSA_X_509
+ */
+SECStatus
+RSA_SignRaw(NSSLOWKEYPrivateKey *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 = nsslowkey_PrivateModulusLen(key);
+ SECItem formatted;
+ SECItem unformatted;
+
+ if (maxOutputLen < modulus_len)
+ return SECFailure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ 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);
+ if (rv != SECSuccess && PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ *output_len = modulus_len;
+
+done:
+ if (formatted.data != NULL)
+ PORT_ZFree(formatted.data, modulus_len);
+ 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 = nsslowkey_PublicModulusLen(key);
+ unsigned char * buffer;
+
+ if (sign_len != modulus_len)
+ goto failure;
+ if (hash_len > modulus_len)
+ goto failure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ goto failure;
+
+ buffer = (unsigned char *)PORT_Alloc(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 (PORT_Memcmp(buffer + (modulus_len-hash_len), hash, hash_len) != 0)
+ goto loser;
+
+ PORT_Free(buffer);
+ return SECSuccess;
+
+loser:
+ PORT_Free(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 = nsslowkey_PublicModulusLen(key);
+
+ if (sign_len != modulus_len)
+ goto failure;
+ if (max_output_len < modulus_len)
+ goto failure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ 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 = nsslowkey_PublicModulusLen(key);
+ SECItem formatted;
+ SECItem unformatted;
+
+ formatted.data = NULL;
+ if (max_output_len < modulus_len)
+ goto failure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ 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;
+
+ PORT_ZFree(formatted.data, modulus_len);
+ *output_len = modulus_len;
+ return SECSuccess;
+
+failure:
+ if (formatted.data != NULL)
+ PORT_ZFree(formatted.data, modulus_len);
+ return SECFailure;
+}
+
+/* XXX Doesn't set error code */
+SECStatus
+RSA_DecryptRaw(NSSLOWKEYPrivateKey *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 = nsslowkey_PrivateModulusLen(key);
+
+ if (modulus_len <= 0)
+ goto failure;
+ if (modulus_len > max_output_len)
+ goto failure;
+ PORT_Assert(key->keyType == NSSLOWKEYRSAKey);
+ if (key->keyType != NSSLOWKEYRSAKey)
+ goto failure;
+ if (input_len != modulus_len)
+ goto failure;
+
+ rv = RSA_PrivateKeyOp(&key->u.rsa, output, input);
+ if (rv != SECSuccess) {
+ if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) {
+ sftk_fatalError = PR_TRUE;
+ }
+ goto failure;
+ }
+
+ *output_len = modulus_len;
+ return SECSuccess;
+
+failure:
+ return SECFailure;
+}
diff --git a/security/nss/lib/softoken/sdb.c b/security/nss/lib/softoken/sdb.c
new file mode 100644
index 000000000..f492e8e97
--- /dev/null
+++ b/security/nss/lib/softoken/sdb.c
@@ -0,0 +1,2028 @@
+/* ***** 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 Red Hat, Inc.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Robert Relyea (rrelyea@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 ***** */
+/*
+ * This file implements PKCS 11 on top of our existing security modules
+ *
+ * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
+ * This implementation has two slots:
+ * slot 1 is our generic crypto support. It does not require login.
+ * It supports Public Key ops, and all they bulk ciphers and hashes.
+ * It can also support Private Key ops for imported Private keys. It does
+ * not have any token storage.
+ * slot 2 is our private key support. It requires a login before use. It
+ * can store Private Keys and Certs as token objects. Currently only private
+ * keys and their associated Certificates are saved on the token.
+ *
+ * In this implementation, session objects are only visible to the session
+ * that created or generated them.
+ */
+
+#include "sdb.h"
+#include "pkcs11t.h"
+#include "seccomon.h"
+#include <sqlite3.h>
+#include "prthread.h"
+#include "prio.h"
+#include "stdio.h"
+#include "secport.h"
+#include "prmon.h"
+#include "prenv.h"
+
+#ifdef SQLITE_UNSAFE_THREADS
+#include "prlock.h"
+/*
+ * SQLite can be compiled to be thread safe or not.
+ * turn on SQLITE_UNSAFE_THREADS if the OS does not support
+ * a thread safe version of sqlite.
+ */
+static PRLock *sqlite_lock = NULL;
+
+#define LOCK_SQLITE() PR_Lock(sqlite_lock);
+#define UNLOCK_SQLITE() PR_Unlock(sqlite_lock);
+#else
+#define LOCK_SQLITE()
+#define UNLOCK_SQLITE()
+#endif
+
+typedef enum {
+ SDB_CERT = 1,
+ SDB_KEY = 2
+} sdbDataType;
+
+/*
+ * defines controlling how long we wait to acquire locks.
+ *
+ * SDB_SQLITE_BUSY_TIMEOUT specifies how long (in milliseconds)
+ * sqlite will wait on lock. If that timeout expires, sqlite will
+ * return SQLITE_BUSY.
+ * SDB_BUSY_RETRY_TIME specifies how many seconds the sdb_ code waits
+ * after receiving a busy before retrying.
+ * SDB_MAX_BUSY_RETRIES specifies how many times the sdb_ will retry on
+ * a busy condition.
+ *
+ * SDB_SQLITE_BUSY_TIMEOUT affects all opertions, both manual
+ * (prepare/step/reset/finalize) and automatic (sqlite3_exec()).
+ * SDB_BUSY_RETRY_TIME and SDB_MAX_BUSY_RETRIES only affect manual operations
+ *
+ * total wait time for automatic operations:
+ * 1 second (SDB_SQLITE_BUSY_TIMEOUT/1000).
+ * total wait time for manual operations:
+ * (1 second + 5 seconds) * 10 = 60 seconds.
+ * (SDB_SQLITE_BUSY_TIMEOUT/1000 + SDB_BUSY_RETRY_TIME)*SDB_MAX_BUSY_RETRIES
+ */
+#define SDB_SQLITE_BUSY_TIMEOUT 1000 /* milliseconds */
+#define SDB_BUSY_RETRY_TIME 5 /* seconds */
+#define SDB_MAX_BUSY_RETRIES 10
+
+/*
+ * Note on use of sqlReadDB: Only one thread at a time may have an actual
+ * operation going on given sqlite3 * database. An operation is defined as
+ * the time from a sqlite3_prepare() until the sqlite3_finalize().
+ * Multiple sqlite3 * databases can be open and have simultaneous operations
+ * going. We use the sqlXactDB for all write operations. This database
+ * is only opened when we first create a transaction and closed when the
+ * transaction is complete. sqlReadDB is open when we first opened the database
+ * and is used for all read operation. It's use is protected by a monitor. This
+ * is because an operation can span the use of FindObjectsInit() through the
+ * call to FindObjectsFinal(). In the intermediate time it is possible to call
+ * other operations like NSC_GetAttributeValue */
+
+struct SDBPrivateStr {
+ char *sqlDBName; /* invariant, path to this database */
+ sqlite3 *sqlXactDB; /* access protected by dbMon, use protected
+ * by the transaction. Current transaction db*/
+ PRThread *sqlXactThread; /* protected by dbMon,
+ * current transaction thread */
+ sqlite3 *sqlReadDB; /* use protected by dbMon, value invariant */
+ PRIntervalTime lastUpdateTime; /* last time the cache was updated */
+ PRIntervalTime updateInterval; /* how long the cache can go before it
+ * must be updated again */
+ sdbDataType type; /* invariant, database type */
+ char *table; /* invariant, SQL table which contains the db */
+ char *cacheTable; /* invariant, SQL table cache of db */
+ PRMonitor *dbMon; /* invariant, monitor to protect
+ * sqlXact* fields, and use of the sqlReadDB */
+};
+
+typedef struct SDBPrivateStr SDBPrivate;
+
+/*
+ * known attributes
+ */
+static const CK_ATTRIBUTE_TYPE known_attributes[] = {
+ CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
+ CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
+ CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
+ CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
+ CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+ CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
+ CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
+ CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
+ CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
+ CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
+ CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS,
+ CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
+ CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
+ CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
+ CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
+ CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
+ CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
+ CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
+ CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
+ CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
+ CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
+ CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL,
+ CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP,
+ CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES,
+ CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED,
+ CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC,
+ CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
+ CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
+ CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
+ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
+ CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
+ CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
+ CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
+ CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
+};
+
+static int known_attributes_size= sizeof(known_attributes)/
+ sizeof(known_attributes[0]);
+
+/* Magic for an explicit NULL. NOTE: ideally this should be
+ * out of band data. Since it's not completely out of band, pick
+ * a value that has no meaning to any existing PKCS #11 attributes.
+ * This value is 1) not a valid string (imbedded '\0'). 2) not a U_LONG
+ * or a normal key (too short). 3) not a bool (too long). 4) not an RSA
+ * public exponent (too many bits).
+ */
+const unsigned char SQLITE_EXPLICIT_NULL[] = { 0xa5, 0x0, 0x5a };
+#define SQLITE_EXPLICIT_NULL_LEN 3
+
+/*
+ * determine when we've completed our tasks
+ */
+static int
+sdb_done(int err, int *count)
+{
+ /* allow as many rows as the database wants to give */
+ if (err == SQLITE_ROW) {
+ *count = 0;
+ return 0;
+ }
+ if (err != SQLITE_BUSY) {
+ return 1;
+ }
+ /* err == SQLITE_BUSY, Dont' retry forever in this case */
+ if (++(*count) >= SDB_MAX_BUSY_RETRIES) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ *
+ * strdup limited to 'n' bytes. (Note: len of file is assumed to be >= len)
+ *
+ * We don't have a PORT_ version of this function,
+ * I suspect it's only normally available in glib,
+ */
+static char *
+sdb_strndup(const char *file, int len)
+{
+ char *result = PORT_Alloc(len+1);
+
+ if (result == NULL) {
+ return result;
+ }
+
+ PORT_Memcpy(result, file, len);
+ result[len] = 0;
+ return result;
+}
+
+/*
+ * call back from sqlite3_exec("Pragma database_list"). Looks for the
+ * temp directory, then return the file the temp directory is stored
+ * at. */
+static int
+sdb_getTempDirCallback(void *arg, int columnCount, char **cval, char **cname)
+{
+ int i;
+ int found = 0;
+ char *file = NULL;
+ char *end, *dir;
+
+ /* we've already found the temp directory, don't look at any more records*/
+ if (*(char **)arg) {
+ return SQLITE_OK;
+ }
+
+ /* look at the columns to see if this record is the temp database,
+ * and does it say where it is stored */
+ for (i=0; i < columnCount; i++) {
+ if (PORT_Strcmp(cname[i],"name") == 0) {
+ if (PORT_Strcmp(cval[i], "temp") == 0) {
+ found++;
+ continue;
+ }
+ }
+ if (PORT_Strcmp(cname[i],"file") == 0) {
+ if (cval[i] && (*cval[i] != 0)) {
+ file = cval[i];
+ }
+ }
+ }
+
+ /* if we couldn't find it, ask for the next record */
+ if (!found || !file) {
+ return SQLITE_OK;
+ }
+
+ /* drop of the database file name and just return the directory */
+ end = PORT_Strrchr(file, '/');
+ if (!end) {
+ return SQLITE_OK;
+ }
+ dir = sdb_strndup(file, end-file);
+
+ *(char **)arg = dir;
+ return SQLITE_OK;
+}
+
+/*
+ * find out where sqlite stores the temp tables. We do this by creating
+ * a temp table, then looking for the database name that sqlite3 creates.
+ */
+static char *
+sdb_getTempDir(sqlite3 *sqlDB)
+{
+ char *tempDir = NULL;
+ int sqlerr;
+
+ /* create a temporary table */
+ sqlerr = sqlite3_exec(sqlDB, "CREATE TEMPORARY TABLE myTemp (id)",
+ NULL, 0, NULL);
+ if (sqlerr != SQLITE_OK) {
+ return NULL;
+ }
+ /* look for through the database list for the temp directory */
+ sqlerr = sqlite3_exec(sqlDB, "PRAGMA database_list",
+ sdb_getTempDirCallback, &tempDir, NULL);
+
+ /* drop the temp table we created */
+ sqlite3_exec(sqlDB, "DROP TABLE myTemp", NULL, 0, NULL);
+
+ if (sqlerr != SQLITE_OK) {
+ return NULL;
+ }
+ return tempDir;
+}
+
+
+/*
+ * Map SQL_LITE errors to PKCS #11 errors as best we can.
+ */
+static int
+sdb_mapSQLError(sdbDataType type, int sqlerr)
+{
+ switch (sqlerr) {
+ /* good matches */
+ case SQLITE_OK:
+ case SQLITE_DONE:
+ return CKR_OK;
+ case SQLITE_NOMEM:
+ return CKR_HOST_MEMORY;
+ case SQLITE_READONLY:
+ return CKR_TOKEN_WRITE_PROTECTED;
+ /* close matches */
+ case SQLITE_AUTH:
+ case SQLITE_PERM:
+ /*return CKR_USER_NOT_LOGGED_IN; */
+ case SQLITE_CANTOPEN:
+ case SQLITE_NOTFOUND:
+ /* NSS distiguishes between failure to open the cert and the key db */
+ return type == SDB_CERT ?
+ CKR_NETSCAPE_CERTDB_FAILED : CKR_NETSCAPE_KEYDB_FAILED;
+ case SQLITE_IOERR:
+ return CKR_DEVICE_ERROR;
+ default:
+ break;
+ }
+ return CKR_GENERAL_ERROR;
+}
+
+
+/*
+ * build up database name from a directory, prefix, name, version and flags.
+ */
+static char *sdb_BuildFileName(const char * directory,
+ const char *prefix, const char *type,
+ int version, int flags)
+{
+ char *dbname = NULL;
+ /* build the full dbname */
+ dbname = sqlite3_mprintf("%s/%s%s%d.db",directory, prefix, type, version);
+ return dbname;
+}
+
+
+/*
+ * find out how expensive the access system call is for non-existant files
+ * in the given directory.
+ */
+PRIntervalTime
+sdb_measureAccess(const char *directory)
+{
+ char *temp;
+ PRIntervalTime time;
+ PRIntervalTime delta;
+ PRIntervalTime next;
+ int i;
+
+ /* no directory, just return one */
+ if (directory == NULL) {
+ return 1;
+ }
+
+ /* measure 200 iterations so we have some resolution in the timer
+ * to work with. This code tries to open 200 unique files so that
+ * any caching code is defeated and we can get a reasonable idea about
+ * how well the underlying file system works */
+ time = PR_IntervalNow();
+ for (i=0; i < 200; i++) {
+ temp = sdb_BuildFileName(directory,"","._dOeSnotExist_", time+i, 0);
+ PR_Access(temp,PR_ACCESS_EXISTS);
+ sqlite3_free(temp);
+ }
+ next = PR_IntervalNow();
+ delta = next - time;
+
+ /* always return 1 or greater */
+ if (delta == 0) delta = 1;
+ return delta;
+}
+
+/*
+ * some file sytems are very slow to run sqlite3 on, particularly if the
+ * access count is pretty high. On these filesystems is faster to create
+ * a temporary database on the local filesystem and access that. This
+ * code uses a temporary table to create that cache. Temp tables are
+ * automatically cleared when the database handle it was created on
+ * Is freed.
+ */
+static const char DROP_CACHE_CMD[] = "DROP TABLE %s";
+static const char CREATE_CACHE_CMD[] =
+ "CREATE TEMPORARY TABLE %s AS SELECT * FROM %s";
+static const char CREATE_ISSUER_INDEX_CMD[] =
+ "CREATE INDEX issuer ON %s (a81)";
+static const char CREATE_SUBJECT_INDEX_CMD[] =
+ "CREATE INDEX subject ON %s (a101)";
+static const char CREATE_LABEL_INDEX_CMD[] = "CREATE INDEX label ON %s (a3)";
+static const char CREATE_ID_INDEX_CMD[] = "CREATE INDEX ckaid ON %s (a102)";
+
+static CK_RV
+sdb_buildCache(sqlite3 *sqlDB, sdbDataType type,
+ const char *cacheTable, const char *table)
+{
+ char *newStr;
+ int sqlerr = SQLITE_OK;
+
+ newStr = sqlite3_mprintf(CREATE_CACHE_CMD, cacheTable, table);
+ if (newStr == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ return sdb_mapSQLError(type, sqlerr);
+ }
+ /* failure to create the indexes is not an issue */
+ newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, cacheTable);
+ if (newStr == NULL) {
+ return CKR_OK;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, cacheTable);
+ if (newStr == NULL) {
+ return CKR_OK;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, cacheTable);
+ if (newStr == NULL) {
+ return CKR_OK;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, cacheTable);
+ if (newStr == NULL) {
+ return CKR_OK;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ return CKR_OK;
+}
+
+/*
+ * update the cache and the data records describing it.
+ * The cache is updated by dropping the temp database and recreating it.
+ */
+static CK_RV
+sdb_updateCache(SDBPrivate *sdb_p)
+{
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ char *newStr;
+
+ /* drop the old table */
+ newStr = sqlite3_mprintf(DROP_CACHE_CMD, sdb_p->cacheTable);
+ if (newStr == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ sqlerr = sqlite3_exec(sdb_p->sqlReadDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if ((sqlerr != SQLITE_OK) && (sqlerr != SQLITE_ERROR )) {
+ /* something went wrong with the drop, don't try to refresh...
+ * NOTE: SQLITE_ERROR is returned if the table doesn't exist. In
+ * that case, we just continue on and try to reload it */
+ return sdb_mapSQLError(sdb_p->type, sqlerr);
+ }
+
+
+ /* set up the new table */
+ error = sdb_buildCache(sdb_p->sqlReadDB,sdb_p->type,
+ sdb_p->cacheTable,sdb_p->table );
+ if (error == CKR_OK) {
+ /* we have a new cache! */
+ sdb_p->lastUpdateTime = PR_IntervalNow();
+ }
+ return error;
+}
+
+/*
+ * The sharing of sqlite3 handles across threads is tricky. Older versions
+ * couldn't at all, but newer ones can under strict conditions. Basically
+ * no 2 threads can use the same handle while another thread has an open
+ * stmt running. Once the sqlite3_stmt is finalized, another thread can then
+ * use the database handle.
+ *
+ * We use monitors to protect against trying to use a database before
+ * it's sqlite3_stmt is finalized. This is preferable to the opening and
+ * closing the database each operation because there is significant overhead
+ * in the open and close. Also continually opening and closing the database
+ * defeats the cache code as the cache table is lost on close (thus
+ * requiring us to have to reinitialize the cache every operation).
+ *
+ * An execption to the shared handle is transations. All writes happen
+ * through a transaction. When we are in a transaction, we must use the
+ * same database pointer for that entire transation. In this case we save
+ * the transaction database and use it for all accesses on the transaction
+ * thread. Other threads use the common database.
+ *
+ * There can only be once active transaction on the database at a time.
+ *
+ * sdb_openDBLocal() provides us with a valid database handle for whatever
+ * state we are in (reading or in a transaction), and acquires any locks
+ * appropriate to that state. It also decides when it's time to refresh
+ * the cache before we start an operation. Any database handle returned
+ * just eventually be closed with sdb_closeDBLocal().
+ *
+ * The table returned either points to the database's physical table, or
+ * to the cached shadow. Tranactions always return the physical table
+ * and read operations return either the physical table or the cache
+ * depending on whether or not the cache exists.
+ */
+static CK_RV
+sdb_openDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB, const char **table)
+{
+ *sqlDB = NULL;
+
+ PR_EnterMonitor(sdb_p->dbMon);
+
+ if (table) {
+ *table = sdb_p->table;
+ }
+
+ /* We're in a transaction, use the transaction DB */
+ if ((sdb_p->sqlXactDB) && (sdb_p->sqlXactThread == PR_GetCurrentThread())) {
+ *sqlDB =sdb_p->sqlXactDB;
+ /* only one thread can get here, safe to unlock */
+ PR_ExitMonitor(sdb_p->dbMon);
+ return CKR_OK;
+ }
+
+ /*
+ * if we are just reading from the table, we may have the table
+ * cached in a temporary table (especially if it's on a shared FS).
+ * In that case we want to see updates to the table, the the granularity
+ * is on order of human scale, not computer scale.
+ */
+ if (table && sdb_p->cacheTable) {
+ PRIntervalTime now = PR_IntervalNow();
+ if ((now - sdb_p->lastUpdateTime) > sdb_p->updateInterval) {
+ sdb_updateCache(sdb_p);
+ }
+ *table = sdb_p->cacheTable;
+ }
+
+ *sqlDB = sdb_p->sqlReadDB;
+
+ /* leave holding the lock. only one thread can actually use a given
+ * database connection at once */
+
+ return CKR_OK;
+}
+
+/* closing the local database currenly means unlocking the monitor */
+static CK_RV
+sdb_closeDBLocal(SDBPrivate *sdb_p, sqlite3 *sqlDB)
+{
+ if (sdb_p->sqlXactDB != sqlDB) {
+ /* if we weren't in a transaction, we got a lock */
+ PR_ExitMonitor(sdb_p->dbMon);
+ }
+ return CKR_OK;
+}
+
+
+/*
+ * wrapper to sqlite3_open which also sets the busy_timeout
+ */
+static int
+sdb_openDB(const char *name, sqlite3 **sqlDB, int flags)
+{
+ int sqlerr;
+ /*
+ * in sqlite3 3.5.0, there is a new open call that allows us
+ * to specify read only. Most new OS's are still on 3.3.x (including
+ * NSS's internal version and the version shipped with Firefox).
+ */
+ *sqlDB = NULL;
+ sqlerr = sqlite3_open(name, sqlDB);
+ if (sqlerr != SQLITE_OK) {
+ return sqlerr;
+ }
+
+ sqlerr = sqlite3_busy_timeout(*sqlDB, SDB_SQLITE_BUSY_TIMEOUT);
+ if (sqlerr != SQLITE_OK) {
+ sqlite3_close(*sqlDB);
+ *sqlDB = NULL;
+ return sqlerr;
+ }
+ return SQLITE_OK;
+}
+
+/* Sigh, if we created a new table since we opened the database,
+ * the database handle will not see the new table, we need to close this
+ * database and reopen it. Caller must be in a transaction or holding
+ * the dbMon. sqlDB is changed on success. */
+static int
+sdb_reopenDBLocal(SDBPrivate *sdb_p, sqlite3 **sqlDB) {
+ sqlite3 *newDB;
+ int sqlerr;
+
+ /* open a new database */
+ sqlerr = sdb_openDB(sdb_p->sqlDBName, &newDB, SDB_RDONLY);
+ if (sqlerr != SQLITE_OK) {
+ return sqlerr;
+ }
+
+ /* if we are in a transaction, we may not be holding the monitor.
+ * grab it before we update the transaction database. This is
+ * safe since are using monitors. */
+ PR_EnterMonitor(sdb_p->dbMon);
+ /* update our view of the database */
+ if (sdb_p->sqlReadDB == *sqlDB) {
+ sdb_p->sqlReadDB = newDB;
+ } else if (sdb_p->sqlXactDB == *sqlDB) {
+ sdb_p->sqlXactDB = newDB;
+ }
+ PR_ExitMonitor(sdb_p->dbMon);
+
+ /* close the old one */
+ sqlite3_close(*sqlDB);
+
+ *sqlDB = newDB;
+ return SQLITE_OK;
+}
+
+struct SDBFindStr {
+ sqlite3 *sqlDB;
+ sqlite3_stmt *findstmt;
+};
+
+
+static const char FIND_OBJECTS_CMD[] = "SELECT ALL * FROM %s WHERE %s;";
+static const char FIND_OBJECTS_ALL_CMD[] = "SELECT ALL * FROM %s;";
+CK_RV
+sdb_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *template, CK_ULONG count,
+ SDBFind **find)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ const char *table;
+ char *newStr, *findStr = NULL;
+ sqlite3_stmt *findstmt = NULL;
+ char *join="";
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int i;
+
+ LOCK_SQLITE()
+ *find = NULL;
+ error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+
+ findStr = sqlite3_mprintf("");
+ for (i=0; findStr && i < count; i++) {
+ newStr = sqlite3_mprintf("%s%sa%x=$DATA%d", findStr, join,
+ template[i].type, i);
+ join=" AND ";
+ sqlite3_free(findStr);
+ findStr = newStr;
+ }
+
+ if (findStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ if (count == 0) {
+ newStr = sqlite3_mprintf(FIND_OBJECTS_ALL_CMD, table);
+ } else {
+ newStr = sqlite3_mprintf(FIND_OBJECTS_CMD, table, findStr);
+ }
+ sqlite3_free(findStr);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &findstmt, NULL);
+ sqlite3_free(newStr);
+ for (i=0; sqlerr == SQLITE_OK && i < count; i++) {
+ const void *blobData = template[i].pValue;
+ unsigned int blobSize = template[i].ulValueLen;
+ if (blobSize == 0) {
+ blobSize = SQLITE_EXPLICIT_NULL_LEN;
+ blobData = SQLITE_EXPLICIT_NULL;
+ }
+ sqlerr = sqlite3_bind_blob(findstmt, i+1, blobData, blobSize,
+ SQLITE_TRANSIENT);
+ }
+ if (sqlerr == SQLITE_OK) {
+ *find = PORT_New(SDBFind);
+ if (*find == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ (*find)->findstmt = findstmt;
+ (*find)->sqlDB = sqlDB;
+ UNLOCK_SQLITE()
+ return CKR_OK;
+ }
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+
+loser:
+ if (findstmt) {
+ sqlite3_finalize(findstmt);
+ }
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+ UNLOCK_SQLITE()
+ return error;
+}
+
+
+CK_RV
+sdb_FindObjects(SDB *sdb, SDBFind *sdbFind, CK_OBJECT_HANDLE *object,
+ CK_ULONG arraySize, CK_ULONG *count)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3_stmt *stmt = sdbFind->findstmt;
+ int sqlerr = SQLITE_OK;
+ int retry = 0;
+
+ *count = 0;
+
+ if (arraySize == 0) {
+ return CKR_OK;
+ }
+ LOCK_SQLITE()
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ if (sqlerr == SQLITE_ROW) {
+ /* only care about the id */
+ *object++= sqlite3_column_int(stmt, 0);
+ arraySize--;
+ (*count)++;
+ }
+ } while (!sdb_done(sqlerr,&retry) && (arraySize > 0));
+
+ /* we only have some of the objects, there is probably more,
+ * set the sqlerr to an OK value so we return CKR_OK */
+ if (sqlerr == SQLITE_ROW && arraySize == 0) {
+ sqlerr = SQLITE_DONE;
+ }
+ UNLOCK_SQLITE()
+
+ return sdb_mapSQLError(sdb_p->type, sqlerr);
+}
+
+CK_RV
+sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3_stmt *stmt = sdbFind->findstmt;
+ sqlite3 *sqlDB = sdbFind->sqlDB;
+ int sqlerr = SQLITE_OK;
+
+ LOCK_SQLITE()
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlerr = sqlite3_finalize(stmt);
+ }
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+ PORT_Free(sdbFind);
+
+ UNLOCK_SQLITE()
+ return sdb_mapSQLError(sdb_p->type, sqlerr);
+}
+
+static const char GET_ATTRIBUTE_CMD[] = "SELECT ALL %s FROM %s WHERE id=$ID;";
+CK_RV
+sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ sqlite3_stmt *stmt = NULL;
+ char *getStr = NULL;
+ char *newStr = NULL;
+ const char *table = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int found = 0;
+ int retry = 0;
+ int i;
+
+
+ /* open a new db if necessary */
+ error = sdb_openDBLocal(sdb_p, &sqlDB, &table);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+
+ getStr = sqlite3_mprintf("");
+ for (i=0; getStr && i < count; i++) {
+ if (i==0) {
+ newStr = sqlite3_mprintf("a%x", template[i].type);
+ } else {
+ newStr = sqlite3_mprintf("%s, a%x", getStr, template[i].type);
+ }
+ sqlite3_free(getStr);
+ getStr = newStr;
+ }
+
+ if (getStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ newStr = sqlite3_mprintf(GET_ATTRIBUTE_CMD, getStr, table);
+ sqlite3_free(getStr);
+ getStr = NULL;
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
+ if (sqlerr != SQLITE_OK) { goto loser; }
+ sqlerr = sqlite3_bind_int(stmt, 1, object_id);
+ if (sqlerr != SQLITE_OK) { goto loser; }
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ if (sqlerr == SQLITE_ROW) {
+ for (i=0; i < count; i++) {
+ int column = i;
+ int blobSize;
+ const char *blobData;
+
+ blobSize = sqlite3_column_bytes(stmt, column);
+ blobData = sqlite3_column_blob(stmt, column);
+ if (blobData == NULL) {
+ template[i].ulValueLen = -1;
+ error = CKR_ATTRIBUTE_TYPE_INVALID;
+ continue;
+ }
+ /* If the blob equals our explicit NULL value, then the
+ * attribute is a NULL. */
+ if ((blobSize == SQLITE_EXPLICIT_NULL_LEN) &&
+ (PORT_Memcmp(blobData, SQLITE_EXPLICIT_NULL,
+ SQLITE_EXPLICIT_NULL_LEN) == 0)) {
+ blobSize = 0;
+ }
+ if (template[i].pValue) {
+ if (template[i].ulValueLen < blobSize) {
+ template[i].ulValueLen = -1;
+ error = CKR_BUFFER_TOO_SMALL;
+ continue;
+ }
+ PORT_Memcpy(template[i].pValue, blobData, blobSize);
+ }
+ template[i].ulValueLen = blobSize;
+ }
+ found = 1;
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+loser:
+ /* fix up the error if necessary */
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ if (!found && error == CKR_OK) {
+ error = CKR_OBJECT_HANDLE_INVALID;
+ }
+ }
+ if (newStr) {
+ sqlite3_free(newStr);
+ }
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ /* if we had to open a new database, free it now */
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+ return error;
+}
+
+CK_RV
+sdb_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ CK_RV crv;
+
+ if (count == 0) {
+ return CKR_OK;
+ }
+
+ LOCK_SQLITE()
+ crv = sdb_GetAttributeValueNoLock(sdb, object_id, template, count);
+ UNLOCK_SQLITE()
+ return crv;
+}
+
+static const char SET_ATTRIBUTE_CMD[] = "UPDATE %s SET %s WHERE id=$ID;";
+CK_RV
+sdb_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id,
+ const CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ sqlite3_stmt *stmt = NULL;
+ char *setStr = NULL;
+ char *newStr = NULL;
+ int sqlerr = SQLITE_OK;
+ int retry = 0;
+ CK_RV error = CKR_OK;
+ int i;
+
+ if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ if (count == 0) {
+ return CKR_OK;
+ }
+
+ LOCK_SQLITE()
+ setStr = sqlite3_mprintf("");
+ for (i=0; setStr && i < count; i++) {
+ if (i==0) {
+ sqlite3_free(setStr);
+ setStr = sqlite3_mprintf("a%x=$VALUE%d",
+ template[i].type, i);
+ continue;
+ }
+ newStr = sqlite3_mprintf("%s,a%x=$VALUE%d", setStr,
+ template[i].type, i);
+ sqlite3_free(setStr);
+ setStr = newStr;
+ }
+ newStr = NULL;
+
+ if (setStr == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ newStr = sqlite3_mprintf(SET_ATTRIBUTE_CMD, sdb_p->table, setStr);
+ sqlite3_free(setStr);
+ if (newStr == NULL) {
+ UNLOCK_SQLITE()
+ return CKR_HOST_MEMORY;
+ }
+ error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+ sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
+ if (sqlerr != SQLITE_OK) goto loser;
+ for (i=0; i < count; i++) {
+ if (template[i].ulValueLen != 0) {
+ sqlerr = sqlite3_bind_blob(stmt, i+1, template[i].pValue,
+ template[i].ulValueLen, SQLITE_STATIC);
+ } else {
+ sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL,
+ SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
+ }
+ if (sqlerr != SQLITE_OK) goto loser;
+ }
+ sqlerr = sqlite3_bind_int(stmt, i+1, object_id);
+ if (sqlerr != SQLITE_OK) goto loser;
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+loser:
+ if (newStr) {
+ sqlite3_free(newStr);
+ }
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ }
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+
+ UNLOCK_SQLITE()
+ return error;
+}
+
+/*
+ * check to see if a candidate object handle already exists.
+ */
+static PRBool
+sdb_objectExists(SDB *sdb, CK_OBJECT_HANDLE candidate)
+{
+ CK_RV crv;
+ CK_ATTRIBUTE template = { CKA_LABEL, NULL, 0 };
+
+ crv = sdb_GetAttributeValueNoLock(sdb,candidate,&template, 1);
+ if (crv == CKR_OBJECT_HANDLE_INVALID) {
+ return PR_FALSE;
+ }
+ return PR_TRUE;
+}
+
+/*
+ * if we're here, we are in a transaction, so it's safe
+ * to examine the current state of the database
+ */
+static CK_OBJECT_HANDLE
+sdb_getObjectId(SDB *sdb)
+{
+ CK_OBJECT_HANDLE candidate;
+ static CK_OBJECT_HANDLE next_obj = CK_INVALID_HANDLE;
+ int count;
+ /*
+ * get an initial object handle to use
+ */
+ if (next_obj == CK_INVALID_HANDLE) {
+ PRTime time;
+ time = PR_Now();
+
+ next_obj = (CK_OBJECT_HANDLE)(time & 0x3fffffffL);
+ }
+ candidate = next_obj++;
+ /* detect that we've looped through all the handles... */
+ for (count = 0; count < 0x40000000; count++, candidate = next_obj++) {
+ /* mask off excess bits */
+ candidate &= 0x3fffffff;
+ /* if we hit zero, go to the next entry */
+ if (candidate == CK_INVALID_HANDLE) {
+ continue;
+ }
+ /* make sure we aren't already using */
+ if (!sdb_objectExists(sdb, candidate)) {
+ /* this one is free */
+ return candidate;
+ }
+ }
+
+ /* no handle is free, fail */
+ return CK_INVALID_HANDLE;
+}
+
+static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);";
+CK_RV
+sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id,
+ const CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ sqlite3_stmt *stmt = NULL;
+ char *columnStr = NULL;
+ char *valueStr = NULL;
+ char *newStr = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ CK_OBJECT_HANDLE this_object = CK_INVALID_HANDLE;
+ int retry = 0;
+ int i;
+
+ if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ LOCK_SQLITE()
+ if ((*object_id != CK_INVALID_HANDLE) &&
+ !sdb_objectExists(sdb, *object_id)) {
+ this_object = *object_id;
+ } else {
+ this_object = sdb_getObjectId(sdb);
+ }
+ if (this_object == CK_INVALID_HANDLE) {
+ UNLOCK_SQLITE();
+ return CKR_HOST_MEMORY;
+ }
+ columnStr = sqlite3_mprintf("");
+ valueStr = sqlite3_mprintf("");
+ *object_id = this_object;
+ for (i=0; columnStr && valueStr && i < count; i++) {
+ newStr = sqlite3_mprintf("%s,a%x", columnStr, template[i].type);
+ sqlite3_free(columnStr);
+ columnStr = newStr;
+ newStr = sqlite3_mprintf("%s,$VALUE%d", valueStr, i);
+ sqlite3_free(valueStr);
+ valueStr = newStr;
+ }
+ newStr = NULL;
+ if ((columnStr == NULL) || (valueStr == NULL)) {
+ if (columnStr) {
+ sqlite3_free(columnStr);
+ }
+ if (valueStr) {
+ sqlite3_free(valueStr);
+ }
+ UNLOCK_SQLITE()
+ return CKR_HOST_MEMORY;
+ }
+ newStr = sqlite3_mprintf(CREATE_CMD, sdb_p->table, columnStr, valueStr);
+ sqlite3_free(columnStr);
+ sqlite3_free(valueStr);
+ error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+ sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
+ if (sqlerr != SQLITE_OK) goto loser;
+ sqlerr = sqlite3_bind_int(stmt, 1, *object_id);
+ if (sqlerr != SQLITE_OK) goto loser;
+ for (i=0; i < count; i++) {
+ if (template[i].ulValueLen) {
+ sqlerr = sqlite3_bind_blob(stmt, i+2, template[i].pValue,
+ template[i].ulValueLen, SQLITE_STATIC);
+ } else {
+ sqlerr = sqlite3_bind_blob(stmt, i+2, SQLITE_EXPLICIT_NULL,
+ SQLITE_EXPLICIT_NULL_LEN, SQLITE_STATIC);
+ }
+ if (sqlerr != SQLITE_OK) goto loser;
+ }
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+loser:
+ if (newStr) {
+ sqlite3_free(newStr);
+ }
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ }
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+ UNLOCK_SQLITE()
+
+ return error;
+}
+
+static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);";
+CK_RV
+sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ sqlite3_stmt *stmt = NULL;
+ char *newStr = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int retry = 0;
+
+ if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ LOCK_SQLITE()
+ error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+ newStr = sqlite3_mprintf(DESTROY_CMD, sdb_p->table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr =sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) goto loser;
+ sqlerr =sqlite3_bind_int(stmt, 1, object_id);
+ if (sqlerr != SQLITE_OK) goto loser;
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+loser:
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ }
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+
+ UNLOCK_SQLITE()
+ return error;
+}
+
+static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;";
+/*
+ * start a transaction.
+ *
+ * We need to open a new database, then store that new database into
+ * the private data structure. We open the database first, then use locks
+ * to protect storing the data to prevent deadlocks.
+ */
+CK_RV
+sdb_Begin(SDB *sdb)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ sqlite3_stmt *stmt = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int retry = 0;
+
+
+ if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+
+ LOCK_SQLITE()
+
+ /* get a new version that we will use for the entire transaction */
+ sqlerr = sdb_openDB(sdb_p->sqlDBName, &sqlDB, SDB_RDWR);
+ if (sqlerr != SQLITE_OK) {
+ goto loser;
+ }
+
+ sqlerr =sqlite3_prepare_v2(sqlDB, BEGIN_CMD, -1, &stmt, NULL);
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+loser:
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+
+ /* we are starting a new transaction,
+ * and if we succeeded, then save this database for the rest of
+ * our transaction */
+ if (error == CKR_OK) {
+ /* we hold a 'BEGIN TRANSACTION' and a sdb_p->lock. At this point
+ * sdb_p->sqlXactDB MUST be null */
+ PR_EnterMonitor(sdb_p->dbMon);
+ PORT_Assert(sdb_p->sqlXactDB == NULL);
+ sdb_p->sqlXactDB = sqlDB;
+ sdb_p->sqlXactThread = PR_GetCurrentThread();
+ PR_ExitMonitor(sdb_p->dbMon);
+ } else {
+ /* we failed to start our transaction,
+ * free any databases we opened. */
+ if (sqlDB) {
+ sqlite3_close(sqlDB);
+ }
+ }
+
+ UNLOCK_SQLITE()
+ return error;
+}
+
+/*
+ * Complete a transaction. Basically undo everything we did in begin.
+ * There are 2 flavors Abort and Commit. Basically the only differerence between
+ * these 2 are what the database will show. (no change in to former, change in
+ * the latter).
+ */
+static CK_RV
+sdb_complete(SDB *sdb, const char *cmd)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ sqlite3_stmt *stmt = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int retry = 0;
+
+
+ if ((sdb->sdb_flags & SDB_RDONLY) != 0) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ /* We must have a transation database, or we shouldn't have arrived here */
+ PR_EnterMonitor(sdb_p->dbMon);
+ PORT_Assert(sdb_p->sqlXactDB);
+ if (sdb_p->sqlXactDB == NULL) {
+ PR_ExitMonitor(sdb_p->dbMon);
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ PORT_Assert( sdb_p->sqlXactThread == PR_GetCurrentThread());
+ if ( sdb_p->sqlXactThread != PR_GetCurrentThread()) {
+ PR_ExitMonitor(sdb_p->dbMon);
+ return CKR_GENERAL_ERROR; /* shouldn't happen */
+ }
+ sqlDB = sdb_p->sqlXactDB;
+ sdb_p->sqlXactDB = NULL; /* no one else can get to this DB,
+ * safe to unlock */
+ sdb_p->sqlXactThread = NULL;
+ PR_ExitMonitor(sdb_p->dbMon);
+
+ sqlerr =sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+ /* Pending BEGIN TRANSACTIONS Can move forward at this point. */
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ /* we we have a cached DB image, update it as well */
+ if (sdb_p->cacheTable) {
+ PR_EnterMonitor(sdb_p->dbMon);
+ sdb_updateCache(sdb_p);
+ PR_ExitMonitor(sdb_p->dbMon);
+ }
+
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+
+ /* We just finished a transaction.
+ * Free the database, and remove it from the list */
+ sqlite3_close(sqlDB);
+
+ return error;
+}
+
+static const char COMMIT_CMD[] = "COMMIT TRANSACTION;";
+CK_RV
+sdb_Commit(SDB *sdb)
+{
+ CK_RV crv;
+ LOCK_SQLITE()
+ crv = sdb_complete(sdb,COMMIT_CMD);
+ UNLOCK_SQLITE()
+ return crv;
+}
+
+static const char ROLLBACK_CMD[] = "ROLLBACK TRANSACTION;";
+CK_RV
+sdb_Abort(SDB *sdb)
+{
+ CK_RV crv;
+ LOCK_SQLITE()
+ crv = sdb_complete(sdb,ROLLBACK_CMD);
+ UNLOCK_SQLITE()
+ return crv;
+}
+
+static int tableExists(sqlite3 *sqlDB, const char *tableName);
+
+static const char GET_PW_CMD[] = "SELECT ALL * FROM metaData WHERE id=$ID;";
+CK_RV
+sdb_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = sdb_p->sqlXactDB;
+ sqlite3_stmt *stmt = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int found = 0;
+ int retry = 0;
+
+ LOCK_SQLITE()
+ error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+
+ /* handle 'test' versions of the sqlite db */
+ sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
+ /* Sigh, if we created a new table since we opened the database,
+ * the database handle will not see the new table, we need to close this
+ * database and reopen it. This is safe because we are holding the lock
+ * still. */
+ if (sqlerr == SQLITE_SCHEMA) {
+ sqlerr = sdb_reopenDBLocal(sdb_p, &sqlDB);
+ if (sqlerr != SQLITE_OK) {
+ goto loser;
+ }
+ sqlerr = sqlite3_prepare_v2(sqlDB, GET_PW_CMD, -1, &stmt, NULL);
+ }
+ if (sqlerr != SQLITE_OK) goto loser;
+ sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ if (sqlerr == SQLITE_ROW) {
+ const char *blobData;
+ unsigned int len = item1->len;
+ item1->len = sqlite3_column_bytes(stmt, 1);
+ if (item1->len > len) {
+ error = CKR_BUFFER_TOO_SMALL;
+ continue;
+ }
+ blobData = sqlite3_column_blob(stmt, 1);
+ PORT_Memcpy(item1->data,blobData, item1->len);
+ if (item2) {
+ len = item2->len;
+ item2->len = sqlite3_column_bytes(stmt, 2);
+ if (item2->len > len) {
+ error = CKR_BUFFER_TOO_SMALL;
+ continue;
+ }
+ blobData = sqlite3_column_blob(stmt, 2);
+ PORT_Memcpy(item2->data,blobData, item2->len);
+ }
+ found = 1;
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+loser:
+ /* fix up the error if necessary */
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ if (!found && error == CKR_OK) {
+ error = CKR_OBJECT_HANDLE_INVALID;
+ }
+ }
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+ UNLOCK_SQLITE()
+
+ return error;
+}
+
+static const char PW_CREATE_TABLE_CMD[] =
+ "CREATE TABLE metaData (id PRIMARY KEY UNIQUE ON CONFLICT REPLACE, item1, item2);";
+static const char PW_CREATE_CMD[] =
+ "INSERT INTO metaData (id,item1,item2) VALUES($ID,$ITEM1,$ITEM2);";
+static const char MD_CREATE_CMD[] =
+ "INSERT INTO metaData (id,item1) VALUES($ID,$ITEM1);";
+CK_RV
+sdb_PutMetaData(SDB *sdb, const char *id, const SECItem *item1,
+ const SECItem *item2)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = sdb_p->sqlXactDB;
+ sqlite3_stmt *stmt = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ int retry = 0;
+ const char *cmd = PW_CREATE_CMD;
+
+ LOCK_SQLITE()
+ error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+
+ if (!tableExists(sqlDB, "metaData")) {
+ sqlerr = sqlite3_exec(sqlDB, PW_CREATE_TABLE_CMD, NULL, 0, NULL);
+ if (sqlerr != SQLITE_OK) goto loser;
+ }
+ if (item2 == NULL) {
+ cmd = MD_CREATE_CMD;
+ }
+ sqlerr = sqlite3_prepare_v2(sqlDB, cmd, -1, &stmt, NULL);
+ if (sqlerr != SQLITE_OK) goto loser;
+ sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC);
+ if (sqlerr != SQLITE_OK) goto loser;
+ sqlerr = sqlite3_bind_blob(stmt, 2, item1->data, item1->len, SQLITE_STATIC);
+ if (sqlerr != SQLITE_OK) goto loser;
+ if (item2) {
+ sqlerr = sqlite3_bind_blob(stmt, 3, item2->data,
+ item2->len, SQLITE_STATIC);
+ if (sqlerr != SQLITE_OK) goto loser;
+ }
+
+ do {
+ sqlerr = sqlite3_step(stmt);
+ if (sqlerr == SQLITE_BUSY) {
+ PR_Sleep(SDB_BUSY_RETRY_TIME);
+ }
+ } while (!sdb_done(sqlerr,&retry));
+
+loser:
+ /* fix up the error if necessary */
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ }
+
+ if (stmt) {
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+ }
+
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+ UNLOCK_SQLITE()
+
+ return error;
+}
+
+static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;";
+CK_RV
+sdb_Reset(SDB *sdb)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ sqlite3 *sqlDB = NULL;
+ char *newStr;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+
+ /* only Key databases can be reset */
+ if (sdb_p->type != SDB_KEY) {
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ LOCK_SQLITE()
+ error = sdb_openDBLocal(sdb_p, &sqlDB, NULL);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+
+ /* delete the key table */
+ newStr = sqlite3_mprintf(RESET_CMD, sdb_p->table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+
+ if (sqlerr != SQLITE_OK) goto loser;
+
+ /* delete the password entry table */
+ sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;",
+ NULL, 0, NULL);
+
+loser:
+ /* fix up the error if necessary */
+ if (error == CKR_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ }
+
+ if (sqlDB) {
+ sdb_closeDBLocal(sdb_p, sqlDB) ;
+ }
+
+ UNLOCK_SQLITE()
+ return error;
+}
+
+
+CK_RV
+sdb_Close(SDB *sdb)
+{
+ SDBPrivate *sdb_p = sdb->private;
+ int sqlerr = SQLITE_OK;
+ sdbDataType type = sdb_p->type;
+
+ sqlerr = sqlite3_close(sdb_p->sqlReadDB);
+ PORT_Free(sdb_p->sqlDBName);
+ if (sdb_p->cacheTable) {
+ sqlite3_free(sdb_p->cacheTable);
+ }
+ if (sdb_p->dbMon) {
+ PR_DestroyMonitor(sdb_p->dbMon);
+ }
+ free(sdb_p);
+ free(sdb);
+ return sdb_mapSQLError(type, sqlerr);
+}
+
+
+/*
+ * functions to support open
+ */
+
+static const char CHECK_TABLE_CMD[] = "SELECT ALL * FROM %s LIMIT 0;";
+/* return 1 if sqlDB contains table 'tableName */
+static int tableExists(sqlite3 *sqlDB, const char *tableName)
+{
+ char * cmd = sqlite3_mprintf(CHECK_TABLE_CMD, tableName);
+ int sqlerr = SQLITE_OK;
+
+ if (cmd == NULL) {
+ return 0;
+ }
+
+ sqlerr = sqlite3_exec(sqlDB, cmd, NULL, 0, 0);
+ sqlite3_free(cmd);
+
+ return (sqlerr == SQLITE_OK) ? 1 : 0;
+}
+
+/*
+ * initialize a single database
+ */
+static const char INIT_CMD[] =
+ "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)";
+static const char ALTER_CMD[] =
+ "ALTER TABLE %s ADD COLUMN a%x";
+
+CK_RV
+sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate,
+ int *newInit, int flags, PRIntervalTime accessTime, SDB **pSdb)
+{
+ int i;
+ char *initStr = NULL;
+ char *newStr;
+ int inTransaction = 0;
+ SDB *sdb = NULL;
+ SDBPrivate *sdb_p = NULL;
+ sqlite3 *sqlDB = NULL;
+ int sqlerr = SQLITE_OK;
+ CK_RV error = CKR_OK;
+ char *cacheTable = NULL;
+ PRIntervalTime now = 0;
+ PRIntervalTime tempAccess = 0;
+ char *tempDir = NULL;
+ char *env;
+ PRBool enableCache = PR_FALSE;
+
+ *pSdb = NULL;
+ *inUpdate = 0;
+
+ /* sqlite3 doesn't have a flag to specify that we want to
+ * open the database read only. If the db doesn't exist,
+ * sqlite3 will always create it.
+ */
+ LOCK_SQLITE();
+ if ((flags == SDB_RDONLY) && PR_Access(dbname, PR_ACCESS_EXISTS)) {
+ error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
+ goto loser;
+ }
+ sqlerr = sdb_openDB(dbname, &sqlDB, flags);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+
+ if (flags != SDB_RDONLY) {
+ sqlerr = sqlite3_exec(sqlDB, BEGIN_CMD, NULL, 0, NULL);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+ inTransaction = 1;
+ }
+ if (!tableExists(sqlDB,table)) {
+ *newInit = 1;
+ if (flags != SDB_CREATE) {
+ error = sdb_mapSQLError(type, SQLITE_CANTOPEN);
+ goto loser;
+ }
+ initStr = sqlite3_mprintf("");
+ for (i=0; initStr && i < known_attributes_size; i++) {
+ newStr = sqlite3_mprintf("%s, a%x",initStr, known_attributes[i]);
+ sqlite3_free(initStr);
+ initStr = newStr;
+ }
+ if (initStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+
+ newStr = sqlite3_mprintf(INIT_CMD, table, initStr);
+ sqlite3_free(initStr);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+
+ newStr = sqlite3_mprintf(CREATE_ISSUER_INDEX_CMD, table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+
+ newStr = sqlite3_mprintf(CREATE_SUBJECT_INDEX_CMD, table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+
+ newStr = sqlite3_mprintf(CREATE_LABEL_INDEX_CMD, table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+
+ newStr = sqlite3_mprintf(CREATE_ID_INDEX_CMD, table);
+ if (newStr == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ sqlerr = sqlite3_exec(sqlDB, newStr, NULL, 0, NULL);
+ sqlite3_free(newStr);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(type, sqlerr);
+ goto loser;
+ }
+ }
+ /*
+ * detect the case where we have created the database, but have
+ * not yet updated it.
+ *
+ * We only check the Key database because only the key database has
+ * a metaData table. The metaData table is created when a password
+ * is set, or in the case of update, when a password is supplied.
+ * If no key database exists, then the update would have happened immediately
+ * on noticing that the cert database didn't exist (see newInit set above).
+ */
+ if (type == SDB_KEY && !tableExists(sqlDB, "metaData")) {
+ *newInit = 1;
+ }
+
+ /* access to network filesystems are significantly slower than local ones
+ * for database operations. In those cases we need to create a cached copy
+ * of the database in a temporary location on the local disk. SQLITE
+ * already provides a way to create a temporary table and initialize it,
+ * so we use it for the cache (see sdb_buildCache for how it's done).*/
+
+ /*
+ * we decide whether or not to use the cache based on the following input.
+ *
+ * NSS_SDB_USE_CACHE environment variable is non-existant or set to
+ * anything other than "no" or "yes" ("auto", for instance).
+ * This is the normal case. NSS will measure the performance of access
+ * to the temp database versus the access to the users passed in
+ * database location. If the temp database location is "significantly"
+ * faster we will use the cache.
+ *
+ * NSS_SDB_USE_CACHE environment variable is set to "no": cache will not
+ * be used.
+ *
+ * NSS_SDB_USE_CACHE environment variable is set to "yes": cache will
+ * always be used.
+ *
+ * It is expected that most applications would use the "auto" selection,
+ * the environment variable is primarily to simplify testing, and to
+ * correct potential corner cases where */
+
+ env = PR_GetEnv("NSS_SDB_USE_CACHE");
+
+ if (env && PORT_Strcasecmp(env,"no") == 0) {
+ enableCache = PR_FALSE;
+ } else if (env && PORT_Strcasecmp(env,"yes") == 0) {
+ enableCache = PR_TRUE;
+ } else {
+ /*
+ * Use PR_Access to determine how expensive it
+ * is to check for the existance of a local file compared to the same
+ * check in the temp directory. If the temp directory is faster, cache
+ * the database there. */
+ tempDir = sdb_getTempDir(sqlDB);
+ tempAccess = sdb_measureAccess(tempDir);
+ PORT_Free(tempDir);
+ tempDir = NULL;
+
+ /* there is a cost to continually copying the database, account for
+ * that cost in the temp access time with the arbitrary factor of 4 */
+ tempAccess = tempAccess*4;
+ enableCache = (tempAccess < accessTime) ? PR_TRUE : PR_FALSE;
+ }
+
+ if (enableCache) {
+ /* try to set the temp store to memory.*/
+ sqlite3_exec(sqlDB, "PRAGMA temp_store=MEMORY", NULL, 0, NULL);
+ /* Failure to set the temp store to memory is not fatal,
+ * ignore the error */
+
+ cacheTable = sqlite3_mprintf("%sCache",table);
+ if (cacheTable == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ /* build the cache table */
+ error = sdb_buildCache(sqlDB, type, cacheTable, table);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+ /* initialize the last cache build time */
+ now = PR_IntervalNow();
+ }
+
+ sdb = (SDB *) malloc(sizeof(SDB));
+ sdb_p = (SDBPrivate *) malloc(sizeof(SDBPrivate));
+
+ /* invariant fields */
+ sdb_p->sqlDBName = PORT_Strdup(dbname);
+ sdb_p->type = type;
+ sdb_p->table = table;
+ sdb_p->cacheTable = cacheTable;
+ sdb_p->lastUpdateTime = now;
+ /* set the cache delay time. This is how long we will wait before we
+ * decide the existing cache is stale. Currently set to 10 sec */
+ sdb_p->updateInterval = PR_SecondsToInterval(10);
+ sdb_p->dbMon = PR_NewMonitor();
+ /* these fields are protected by the lock */
+ sdb_p->sqlXactDB = NULL;
+ sdb_p->sqlXactThread = NULL;
+ sdb->private = sdb_p;
+ sdb->sdb_type = SDB_SQL;
+ sdb->sdb_flags = flags | SDB_HAS_META;
+ sdb->sdb_FindObjectsInit = sdb_FindObjectsInit;
+ sdb->sdb_FindObjects = sdb_FindObjects;
+ sdb->sdb_FindObjectsFinal = sdb_FindObjectsFinal;
+ sdb->sdb_GetAttributeValue = sdb_GetAttributeValue;
+ sdb->sdb_SetAttributeValue = sdb_SetAttributeValue;
+ sdb->sdb_CreateObject = sdb_CreateObject;
+ sdb->sdb_DestroyObject = sdb_DestroyObject;
+ sdb->sdb_GetMetaData = sdb_GetMetaData;
+ sdb->sdb_PutMetaData = sdb_PutMetaData;
+ sdb->sdb_Begin = sdb_Begin;
+ sdb->sdb_Commit = sdb_Commit;
+ sdb->sdb_Abort = sdb_Abort;
+ sdb->sdb_Close = sdb_Close;
+
+ if (inTransaction) {
+ sqlerr = sqlite3_exec(sqlDB, COMMIT_CMD, NULL, 0, NULL);
+ if (sqlerr != SQLITE_OK) {
+ error = sdb_mapSQLError(sdb_p->type, sqlerr);
+ goto loser;
+ }
+ inTransaction = 0;
+ }
+
+ sdb_p->sqlReadDB = sqlDB;
+
+ *pSdb = sdb;
+ UNLOCK_SQLITE();
+ return CKR_OK;
+
+loser:
+ /* lots of stuff to do */
+ if (inTransaction) {
+ sqlite3_exec(sqlDB, ROLLBACK_CMD, NULL, 0, NULL);
+ }
+ if (sdb) {
+ free(sdb);
+ }
+ if (sdb_p) {
+ free(sdb_p);
+ }
+ if (sqlDB) {
+ sqlite3_close(sqlDB);
+ }
+ UNLOCK_SQLITE();
+ return error;
+
+}
+
+
+/* sdbopen */
+CK_RV
+s_open(const char *directory, const char *certPrefix, const char *keyPrefix,
+ int cert_version, int key_version, int flags,
+ SDB **certdb, SDB **keydb, int *newInit)
+{
+ char *cert = sdb_BuildFileName(directory, certPrefix,
+ "cert", cert_version, flags);
+ char *key = sdb_BuildFileName(directory, keyPrefix,
+ "key", key_version, flags);
+ CK_RV error = CKR_OK;
+ int inUpdate;
+ PRIntervalTime accessTime;
+
+ *certdb = NULL;
+ *keydb = NULL;
+ *newInit = 0;
+
+#ifdef SQLITE_UNSAFE_THREADS
+ if (sqlite_lock == NULL) {
+ sqlite_lock = PR_NewLock();
+ if (sqlite_lock == NULL) {
+ error = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ }
+#endif
+
+ /* how long does it take to test for a non-existant file in our working
+ * directory? Allows us to test if we may be on a network file system */
+ accessTime = sdb_measureAccess(directory);
+
+ /*
+ * open the cert data base
+ */
+ if (certdb) {
+ /* initialize Certificate database */
+ error = sdb_init(cert, "nssPublic", SDB_CERT, &inUpdate,
+ newInit, flags, accessTime, certdb);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+ }
+
+ /*
+ * open the key data base:
+ * NOTE:if we want to implement a single database, we open
+ * the same database file as the certificate here.
+ *
+ * cert an key db's have different tables, so they will not
+ * conflict.
+ */
+ if (keydb) {
+ /* initialize the Key database */
+ error = sdb_init(key, "nssPrivate", SDB_KEY, &inUpdate,
+ newInit, flags, accessTime, keydb);
+ if (error != CKR_OK) {
+ goto loser;
+ }
+ }
+
+
+loser:
+ if (cert) {
+ sqlite3_free(cert);
+ }
+ if (key) {
+ sqlite3_free(key);
+ }
+
+ if (error != CKR_OK) {
+ /* currently redundant, but could be necessary if more code is added
+ * just before loser */
+ if (keydb && *keydb) {
+ sdb_Close(*keydb);
+ }
+ if (certdb && *certdb) {
+ sdb_Close(*certdb);
+ }
+ }
+
+ return error;
+}
+
+CK_RV
+s_shutdown()
+{
+#ifdef SQLITE_UNSAFE_THREADS
+ if (sqlite_lock) {
+ PR_DestroyLock(sqlite_lock);
+ sqlite_lock = NULL;
+ }
+#endif
+ return CKR_OK;
+}
diff --git a/security/nss/lib/softoken/sdb.h b/security/nss/lib/softoken/sdb.h
new file mode 100644
index 000000000..9e041f476
--- /dev/null
+++ b/security/nss/lib/softoken/sdb.h
@@ -0,0 +1,111 @@
+/* ***** 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 Red Hat, Inc.
+ *
+ * The Initial Developer of the Original Code is
+ * Red Hat, Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2005
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Robert Relyea (rrelyea@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 ***** */
+/*
+ * This file implements PKCS 11 on top of our existing security modules
+ *
+ * For more information about PKCS 11 See PKCS 11 Token Inteface Standard.
+ * This implementation has two slots:
+ * slot 1 is our generic crypto support. It does not require login.
+ * It supports Public Key ops, and all they bulk ciphers and hashes.
+ * It can also support Private Key ops for imported Private keys. It does
+ * not have any token storage.
+ * slot 2 is our private key support. It requires a login before use. It
+ * can store Private Keys and Certs as token objects. Currently only private
+ * keys and their associated Certificates are saved on the token.
+ *
+ * In this implementation, session objects are only visible to the session
+ * that created or generated them.
+ */
+
+/*
+ * the following data structures should be moved to a 'rdb.h'.
+ */
+
+#ifndef _SDB_H
+#define _SDB_H 1
+#include "pkcs11t.h"
+#include "secitem.h"
+#include "sftkdbt.h"
+#include <sqlite3.h>
+
+#define STATIC_CMD_SIZE 2048
+
+typedef struct SDBFindStr SDBFind;
+typedef struct SDBStr SDB;
+
+struct SDBStr {
+ void *private;
+ int version;
+ SDBType sdb_type;
+ int sdb_flags;
+ void *app_private;
+ CK_RV (*sdb_FindObjectsInit)(SDB *sdb, const CK_ATTRIBUTE *template,
+ CK_ULONG count, SDBFind **find);
+ CK_RV (*sdb_FindObjects)(SDB *sdb, SDBFind *find, CK_OBJECT_HANDLE *ids,
+ CK_ULONG arraySize, CK_ULONG *count);
+ CK_RV (*sdb_FindObjectsFinal)(SDB *sdb, SDBFind *find);
+ CK_RV (*sdb_GetAttributeValue)(SDB *sdb, CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE *template, CK_ULONG count);
+ CK_RV (*sdb_SetAttributeValue)(SDB *sdb, CK_OBJECT_HANDLE object,
+ const CK_ATTRIBUTE *template, CK_ULONG count);
+ CK_RV (*sdb_CreateObject)(SDB *sdb, CK_OBJECT_HANDLE *object,
+ const CK_ATTRIBUTE *template, CK_ULONG count);
+ CK_RV (*sdb_DestroyObject)(SDB *sdb, CK_OBJECT_HANDLE object);
+ CK_RV (*sdb_GetMetaData)(SDB *sdb, const char *id,
+ SECItem *item1, SECItem *item2);
+ CK_RV (*sdb_PutMetaData)(SDB *sdb, const char *id,
+ const SECItem *item1, const SECItem *item2);
+ CK_RV (*sdb_Begin)(SDB *sdb);
+ CK_RV (*sdb_Commit)(SDB *sdb);
+ CK_RV (*sdb_Abort)(SDB *sdb);
+ CK_RV (*sdb_Reset)(SDB *sdb);
+ CK_RV (*sdb_Close)(SDB *sdb);
+};
+
+CK_RV s_open(const char *directory, const char *certPrefix,
+ const char *keyPrefix,
+ int cert_version, int key_version,
+ int flags, SDB **certdb, SDB **keydb, int *newInit);
+CK_RV s_shutdown();
+
+/* flags */
+#define SDB_RDONLY 1
+#define SDB_RDWR 2
+#define SDB_CREATE 4
+#define SDB_HAS_META 8
+
+#endif
diff --git a/security/nss/lib/softoken/sftkdb.c b/security/nss/lib/softoken/sftkdb.c
new file mode 100644
index 000000000..149ee9fa1
--- /dev/null
+++ b/security/nss/lib/softoken/sftkdb.c
@@ -0,0 +1,2410 @@
+/* ***** 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-2007
+ * 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. For the rest of NSS, only one kind of database handle exists:
+ *
+ * SFTKDBHandle
+ *
+ * There is one SFTKDBHandle for the each key database and one for each cert
+ * database. These databases are opened as associated pairs, one pair per
+ * slot. SFTKDBHandles are reference counted objects.
+ *
+ * Each SFTKDBHandle points to a low level database handle (SDB). This handle
+ * represents the underlying physical database. These objects are not
+ * reference counted, an are 'owned' by their respective SFTKDBHandles.
+ *
+ *
+ */
+#include "sftkdb.h"
+#include "sftkdbti.h"
+#include "pkcs11t.h"
+#include "pkcs11i.h"
+#include "sdb.h"
+#include "prprf.h"
+#include "secmodt.h"
+#include "pratom.h"
+#include "lgglue.h"
+#include "sftkpars.h"
+#include "secerr.h"
+
+/*
+ * We want all databases to have the same binary representation independent of
+ * endianness or length of the host architecture. In general PKCS #11 attributes
+ * are endian/length independent except those attributes that pass CK_ULONG.
+ *
+ * The following functions fixes up the CK_ULONG type attributes so that the data
+ * base sees a machine independent view. CK_ULONGs are stored as 4 byte network
+ * byte order values (big endian).
+ */
+#define BBP 8
+
+static PRBool
+sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ switch(type) {
+ case CKA_CLASS:
+ case CKA_CERTIFICATE_TYPE:
+ case CKA_CERTIFICATE_CATEGORY:
+ case CKA_KEY_TYPE:
+ case CKA_JAVA_MIDP_SECURITY_DOMAIN:
+
+ case CKA_TRUST_DIGITAL_SIGNATURE:
+ case CKA_TRUST_NON_REPUDIATION:
+ case CKA_TRUST_KEY_ENCIPHERMENT:
+ case CKA_TRUST_DATA_ENCIPHERMENT:
+ case CKA_TRUST_KEY_AGREEMENT:
+ case CKA_TRUST_KEY_CERT_SIGN:
+ case CKA_TRUST_CRL_SIGN:
+
+ case CKA_TRUST_SERVER_AUTH:
+ case CKA_TRUST_CLIENT_AUTH:
+ case CKA_TRUST_CODE_SIGNING:
+ case CKA_TRUST_EMAIL_PROTECTION:
+ case CKA_TRUST_IPSEC_END_SYSTEM:
+ case CKA_TRUST_IPSEC_TUNNEL:
+ case CKA_TRUST_IPSEC_USER:
+ case CKA_TRUST_TIME_STAMPING:
+ case CKA_TRUST_STEP_UP_APPROVED:
+ return PR_TRUE;
+ default:
+ break;
+ }
+ return PR_FALSE;
+
+}
+
+/* are the attributes private? */
+static PRBool
+sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ switch(type) {
+ case CKA_VALUE:
+ case CKA_PRIVATE_EXPONENT:
+ case CKA_PRIME_1:
+ case CKA_PRIME_2:
+ case CKA_EXPONENT_1:
+ case CKA_EXPONENT_2:
+ case CKA_COEFFICIENT:
+ return PR_TRUE;
+ default:
+ break;
+ }
+ return PR_FALSE;
+}
+
+/* These attributes must be authenticated with an hmac. */
+static PRBool
+sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type)
+{
+ switch(type) {
+ case CKA_MODULUS:
+ case CKA_PUBLIC_EXPONENT:
+ case CKA_CERT_SHA1_HASH:
+ case CKA_CERT_MD5_HASH:
+ case CKA_TRUST_SERVER_AUTH:
+ case CKA_TRUST_CLIENT_AUTH:
+ case CKA_TRUST_EMAIL_PROTECTION:
+ case CKA_TRUST_CODE_SIGNING:
+ case CKA_TRUST_STEP_UP_APPROVED:
+ case CKA_NSS_OVERRIDE_EXTENSIONS:
+ return PR_TRUE;
+ default:
+ break;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * convert a native ULONG to a database ulong. Database ulong's
+ * are all 4 byte big endian values.
+ */
+void
+sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value)
+{
+ int i;
+
+ for (i=0; i < SDB_ULONG_SIZE; i++) {
+ data[i] = (value >> (SDB_ULONG_SIZE-1-i)*BBP) & 0xff;
+ }
+}
+
+/*
+ * convert a database ulong back to a native ULONG. (reverse of the above
+ * function.
+ */
+static CK_ULONG
+sftk_SDBULong2ULong(unsigned char *data)
+{
+ int i;
+ CK_ULONG value = 0;
+
+ for (i=0; i < SDB_ULONG_SIZE; i++) {
+ value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE-1-i)*BBP);
+ }
+ return value;
+}
+
+/*
+ * fix up the input templates. Our fixed up ints are stored in data and must
+ * be freed by the caller. The new template must also be freed. If there are no
+ * CK_ULONG attributes, the orignal template is passed in as is.
+ */
+static CK_ATTRIBUTE *
+sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count,
+ unsigned char **dataOut)
+{
+ int i;
+ int ulongCount = 0;
+ unsigned char *data;
+ CK_ATTRIBUTE *ntemplate;
+
+ *dataOut = NULL;
+
+ /* first count the number of CK_ULONG attributes */
+ for (i=0; i < count; i++) {
+ /* Don't 'fixup' NULL values */
+ if (!template[i].pValue) {
+ continue;
+ }
+ if (template[i].ulValueLen == sizeof (CK_ULONG)) {
+ if ( sftkdb_isULONGAttribute(template[i].type)) {
+ ulongCount++;
+ }
+ }
+ }
+ /* no attributes to fixup, just call on through */
+ if (ulongCount == 0) {
+ return (CK_ATTRIBUTE *)template;
+ }
+
+ /* allocate space for new ULONGS */
+ data = (unsigned char *)PORT_Alloc(SDB_ULONG_SIZE*ulongCount);
+ if (!data) {
+ return NULL;
+ }
+
+ /* allocate new template */
+ ntemplate = PORT_NewArray(CK_ATTRIBUTE,count);
+ if (!ntemplate) {
+ PORT_Free(data);
+ return NULL;
+ }
+ *dataOut = data;
+ /* copy the old template, fixup the actual ulongs */
+ for (i=0; i < count; i++) {
+ ntemplate[i] = template[i];
+ /* Don't 'fixup' NULL values */
+ if (!template[i].pValue) {
+ continue;
+ }
+ if (template[i].ulValueLen == sizeof (CK_ULONG)) {
+ if ( sftkdb_isULONGAttribute(template[i].type) ) {
+ CK_ULONG value = *(CK_ULONG *) template[i].pValue;
+ sftk_ULong2SDBULong(data, value);
+ ntemplate[i].pValue = data;
+ ntemplate[i].ulValueLen = SDB_ULONG_SIZE;
+ data += SDB_ULONG_SIZE;
+ }
+ }
+ }
+ return ntemplate;
+}
+
+
+static const char SFTKDB_META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x";
+
+/*
+ * return a string describing the database type (key or cert)
+ */
+const char *
+sftkdb_TypeString(SFTKDBHandle *handle)
+{
+ return (handle->type == SFTK_KEYDB_TYPE) ? "key" : "cert";
+}
+
+/*
+ * Some attributes are signed with an Hmac and a pbe key generated from
+ * the password. This signature is stored indexed by object handle and
+ * attribute type in the meta data table in the key database.
+ *
+ * Signature entries are indexed by the string
+ * sig_[cert/key]_{ObjectID}_{Attribute}
+ *
+ * This function fetches that pkcs5 signature. Caller supplies a SECItem
+ * pre-allocated to the appropriate size if the SECItem is too small the
+ * function will fail with CKR_BUFFER_TOO_SMALL.
+ */
+static CK_RV
+sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle,
+ CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
+ SECItem *signText)
+{
+ SDB *db;
+ char id[30];
+ CK_RV crv;
+
+ db = SFTK_GET_SDB(keyHandle);
+
+ sprintf(id, SFTKDB_META_SIG_TEMPLATE,
+ sftkdb_TypeString(handle),
+ (unsigned int)objectID, (unsigned int)type);
+
+ crv = (*db->sdb_GetMetaData)(db, id, signText, NULL);
+ return crv;
+}
+
+/*
+ * Some attributes are signed with an Hmac and a pbe key generated from
+ * the password. This signature is stored indexed by object handle and
+ * attribute type in the meta data table in the key database.
+ *
+ * Signature entries are indexed by the string
+ * sig_[cert/key]_{ObjectID}_{Attribute}
+ *
+ * This function stores that pkcs5 signature.
+ */
+CK_RV
+sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget,
+ CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type,
+ SECItem *signText)
+{
+ char id[30];
+ CK_RV crv;
+
+ sprintf(id, SFTKDB_META_SIG_TEMPLATE,
+ sftkdb_TypeString(handle),
+ (unsigned int)objectID, (unsigned int)type);
+
+ crv = (*keyTarget->sdb_PutMetaData)(keyTarget, id, signText, NULL);
+ return crv;
+}
+
+/*
+ * fix up returned data. NOTE: sftkdb_fixupTemplateIn has already allocated
+ * separate data sections for the database ULONG values.
+ */
+static CK_RV
+sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID,
+ CK_ATTRIBUTE *ntemplate, int count, SFTKDBHandle *handle)
+{
+ int i;
+ CK_RV crv = CKR_OK;
+ SFTKDBHandle *keyHandle;
+ PRBool checkSig = PR_TRUE;
+ PRBool checkEnc = PR_TRUE;
+
+ PORT_Assert(handle);
+
+ /* find the key handle */
+ keyHandle = handle;
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ checkEnc = PR_FALSE;
+ keyHandle = handle->peerDB;
+ }
+
+ if ((keyHandle == NULL) ||
+ ((SFTK_GET_SDB(keyHandle)->sdb_flags & SDB_HAS_META) == 0) ||
+ (keyHandle->passwordKey.data == NULL)) {
+ checkSig = PR_FALSE;
+ }
+
+ for (i=0; i < count; i++) {
+ CK_ULONG length = template[i].ulValueLen;
+ template[i].ulValueLen = ntemplate[i].ulValueLen;
+ /* fixup ulongs */
+ if (ntemplate[i].ulValueLen == SDB_ULONG_SIZE) {
+ if (sftkdb_isULONGAttribute(template[i].type)) {
+ if (template[i].pValue) {
+ CK_ULONG value;
+ unsigned char *data;
+
+ data = (unsigned char *)ntemplate[i].pValue;
+ value = sftk_SDBULong2ULong(ntemplate[i].pValue);
+ if (length < sizeof(CK_ULONG)) {
+ template[i].ulValueLen = -1;
+ crv = CKR_BUFFER_TOO_SMALL;
+ continue;
+ }
+ PORT_Memcpy(template[i].pValue,&value,sizeof(CK_ULONG));
+ }
+ template[i].ulValueLen = sizeof(CK_ULONG);
+ }
+ }
+
+ /* if no data was retrieved, no need to process encrypted or signed
+ * attributes */
+ if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) {
+ continue;
+ }
+
+ /* fixup private attributes */
+ if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) {
+ /* we have a private attribute */
+ /* This code depends on the fact that the cipherText is bigger
+ * than the plain text */
+ SECItem cipherText;
+ SECItem *plainText;
+ SECStatus rv;
+
+ cipherText.data = ntemplate[i].pValue;
+ cipherText.len = ntemplate[i].ulValueLen;
+ PZ_Lock(handle->passwordLock);
+ if (handle->passwordKey.data == NULL) {
+ PZ_Unlock(handle->passwordLock);
+ template[i].ulValueLen = -1;
+ crv = CKR_USER_NOT_LOGGED_IN;
+ continue;
+ }
+ rv = sftkdb_DecryptAttribute(&handle->passwordKey,
+ &cipherText, &plainText);
+ PZ_Unlock(handle->passwordLock);
+ if (rv != SECSuccess) {
+ PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+ template[i].ulValueLen = -1;
+ crv = CKR_GENERAL_ERROR;
+ continue;
+ }
+ PORT_Assert(template[i].ulValueLen >= plainText->len);
+ if (template[i].ulValueLen < plainText->len) {
+ SECITEM_FreeItem(plainText,PR_TRUE);
+ PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+ template[i].ulValueLen = -1;
+ crv = CKR_GENERAL_ERROR;
+ continue;
+ }
+
+ /* copy the plain text back into the template */
+ PORT_Memcpy(template[i].pValue, plainText->data, plainText->len);
+ template[i].ulValueLen = plainText->len;
+ SECITEM_FreeItem(plainText,PR_TRUE);
+ }
+ /* make sure signed attributes are valid */
+ if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) {
+ SECStatus rv;
+ SECItem signText;
+ SECItem plainText;
+ unsigned char signData[SDB_MAX_META_DATA_LEN];
+
+ signText.data = signData;
+ signText.len = sizeof(signData);
+
+ rv = sftkdb_getAttributeSignature(handle, keyHandle,
+ objectID, ntemplate[i].type, &signText);
+ if (rv != SECSuccess) {
+ PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+ template[i].ulValueLen = -1;
+ crv = CKR_DATA_INVALID; /* better error code? */
+ continue;
+ }
+
+ plainText.data = ntemplate[i].pValue;
+ plainText.len = ntemplate[i].ulValueLen;
+
+ /*
+ * we do a second check holding the lock just in case the user
+ * loggout while we were trying to get the signature.
+ */
+ PZ_Lock(keyHandle->passwordLock);
+ if (keyHandle->passwordKey.data == NULL) {
+ /* if we are no longer logged in, no use checking the other
+ * Signatures either. */
+ checkSig = PR_FALSE;
+ PZ_Unlock(keyHandle->passwordLock);
+ continue;
+ }
+
+ rv = sftkdb_VerifyAttribute(&keyHandle->passwordKey,
+ objectID, ntemplate[i].type,
+ &plainText, &signText);
+ PZ_Unlock(keyHandle->passwordLock);
+ if (rv != SECSuccess) {
+ PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
+ template[i].ulValueLen = -1;
+ crv = CKR_SIGNATURE_INVALID; /* better error code? */
+ }
+ /* This Attribute is fine */
+ }
+ }
+ return crv;
+}
+
+/*
+ * Some attributes are signed with an HMAC and a pbe key generated from
+ * the password. This signature is stored indexed by object handle and
+ *
+ * Those attributes are:
+ * 1) Trust object hashes and trust values.
+ * 2) public key values.
+ *
+ * Certs themselves are considered properly authenticated by virtue of their
+ * signature, or their matching hash with the trust object.
+ *
+ * These signature is only checked for objects coming from shared databases.
+ * Older dbm style databases have such no signature checks. HMACs are also
+ * only checked when the token is logged in, as it requires a pbe generated
+ * from the password.
+ *
+ * Tokens which have no key database (and therefore no master password) do not
+ * have any stored signature values. Signature values are stored in the key
+ * database, since the signature data is tightly coupled to the key database
+ * password.
+ *
+ * This function takes a template of attributes that were either created or
+ * modified. These attributes are checked to see if the need to be signed.
+ * If they do, then this function signs the attributes and writes them
+ * to the meta data store.
+ *
+ * This function can fail if there are attributes that must be signed, but
+ * the token is not logged in.
+ *
+ * The caller is expected to abort any transaction he was in in the
+ * event of a failure of this function.
+ */
+static CK_RV
+sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle,
+ PRBool mayBeUpdateDB,
+ CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
+ CK_ULONG count)
+{
+ int i;
+ SFTKDBHandle *keyHandle = handle;
+ SDB *keyTarget = NULL;
+
+ PORT_Assert(handle);
+
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ keyHandle = handle->peerDB;
+ }
+
+ /* no key DB defined? then no need to sign anything */
+ if (keyHandle == NULL) {
+ return CKR_OK;
+ }
+
+ /* When we are in a middle of an update, we have an update database set,
+ * but we want to write to the real database. The bool mayBeUpdateDB is
+ * set to TRUE if it's possible that we want to write an update database
+ * rather than a primary */
+ keyTarget = (mayBeUpdateDB && keyHandle->update) ?
+ keyHandle->update : keyHandle->db;
+
+ /* skip the the database does not support meta data */
+ if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) {
+ return CKR_OK;
+ }
+
+ for (i=0; i < count; i ++) {
+ if (sftkdb_isAuthenticatedAttribute(template[i].type)) {
+ SECStatus rv;
+ SECItem *signText;
+ SECItem plainText;
+
+ plainText.data = template[i].pValue;
+ plainText.len = template[i].ulValueLen;
+ PZ_Lock(keyHandle->passwordLock);
+ if (keyHandle->passwordKey.data == NULL) {
+ PZ_Unlock(keyHandle->passwordLock);
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+ rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey,
+ objectID, template[i].type,
+ &plainText, &signText);
+ PZ_Unlock(keyHandle->passwordLock);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR; /* better error code here? */
+ }
+ rv = sftkdb_PutAttributeSignature(handle, keyTarget,
+ objectID, template[i].type, signText);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR; /* better error code here? */
+ }
+ }
+ }
+ return CKR_OK;
+}
+
+static CK_RV
+sftkdb_CreateObject(PRArenaPool *arena, SFTKDBHandle *handle,
+ SDB *db, CK_OBJECT_HANDLE *objectID,
+ CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ PRBool inTransaction = PR_FALSE;
+ CK_RV crv;
+
+ inTransaction = PR_TRUE;
+
+ crv = (*db->sdb_CreateObject)(db, objectID, template, count);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftk_signTemplate(arena, handle, (db == handle->update),
+ *objectID, template, count);
+loser:
+
+ return crv;
+}
+
+
+CK_ATTRIBUTE *
+sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object,
+ SFTKDBHandle *handle,CK_ULONG *pcount,
+ CK_RV *crv)
+{
+ int count;
+ CK_ATTRIBUTE *template;
+ int i, templateIndex;
+ SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
+ PRBool doEnc = PR_TRUE;
+
+ *crv = CKR_OK;
+
+ if (sessObject == NULL) {
+ *crv = CKR_GENERAL_ERROR; /* internal programming error */
+ return NULL;
+ }
+
+ PORT_Assert(handle);
+ /* find the key handle */
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ doEnc = PR_FALSE;
+ }
+
+ PZ_Lock(sessObject->attributeLock);
+ count = 0;
+ for (i=0; i < sessObject->hashSize; i++) {
+ SFTKAttribute *attr;
+ for (attr=sessObject->head[i]; attr; attr=attr->next) {
+ count++;
+ }
+ }
+ template = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, count);
+ if (template == NULL) {
+ PZ_Unlock(sessObject->attributeLock);
+ *crv = CKR_HOST_MEMORY;
+ return NULL;
+ }
+ templateIndex = 0;
+ for (i=0; i < sessObject->hashSize; i++) {
+ SFTKAttribute *attr;
+ for (attr=sessObject->head[i]; attr; attr=attr->next) {
+ CK_ATTRIBUTE *tp = &template[templateIndex++];
+ /* copy the attribute */
+ *tp = attr->attrib;
+
+ /* fixup ULONG s */
+ if ((tp->ulValueLen == sizeof (CK_ULONG)) &&
+ (sftkdb_isULONGAttribute(tp->type)) ) {
+ CK_ULONG value = *(CK_ULONG *) tp->pValue;
+ unsigned char *data;
+
+ tp->pValue = PORT_ArenaAlloc(arena, SDB_ULONG_SIZE);
+ data = (unsigned char *)tp->pValue;
+ if (data == NULL) {
+ *crv = CKR_HOST_MEMORY;
+ break;
+ }
+ sftk_ULong2SDBULong(data, value);
+ tp->ulValueLen = SDB_ULONG_SIZE;
+ }
+
+ /* encrypt private attributes */
+ if (doEnc && sftkdb_isPrivateAttribute(tp->type)) {
+ /* we have a private attribute */
+ SECItem *cipherText;
+ SECItem plainText;
+ SECStatus rv;
+
+ plainText.data = tp->pValue;
+ plainText.len = tp->ulValueLen;
+ PZ_Lock(handle->passwordLock);
+ if (handle->passwordKey.data == NULL) {
+ PZ_Unlock(handle->passwordLock);
+ *crv = CKR_USER_NOT_LOGGED_IN;
+ break;
+ }
+ rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey,
+ &plainText, &cipherText);
+ PZ_Unlock(handle->passwordLock);
+ if (rv == SECSuccess) {
+ tp->pValue = cipherText->data;
+ tp->ulValueLen = cipherText->len;
+ } else {
+ *crv = CKR_GENERAL_ERROR; /* better error code here? */
+ break;
+ }
+ PORT_Memset(plainText.data, 0, plainText.len);
+ }
+ }
+ }
+ PORT_Assert(templateIndex <= count);
+ PZ_Unlock(sessObject->attributeLock);
+
+ if (*crv != CKR_OK) {
+ return NULL;
+ }
+ if (pcount) {
+ *pcount = count;
+ }
+ return template;
+
+}
+
+/*
+ * return a pointer to the attribute in the give template.
+ * The return value is not const, as the caller may modify
+ * the given attribute value, but such modifications will
+ * modify the actual value in the template.
+ */
+static CK_ATTRIBUTE *
+sftkdb_getAttributeFromTemplate(CK_ATTRIBUTE_TYPE attribute,
+ CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+ CK_ULONG i;
+
+ for (i=0; i < len; i++) {
+ if (attribute == ptemplate[i].type) {
+ return &ptemplate[i];
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * fetch a template which identifies 'unique' entries based on object type
+ */
+static CK_RV
+sftkdb_getFindTemplate(CK_OBJECT_CLASS objectType, unsigned char *objTypeData,
+ CK_ATTRIBUTE *findTemplate, CK_ULONG *findCount,
+ CK_ATTRIBUTE *ptemplate, int len)
+{
+ CK_ATTRIBUTE *attr;
+ CK_ULONG count = 1;
+
+ sftk_ULong2SDBULong(objTypeData, objectType);
+ findTemplate[0].type = CKA_CLASS;
+ findTemplate[0].pValue = objTypeData;
+ findTemplate[0].ulValueLen = SDB_ULONG_SIZE;
+
+ switch (objectType) {
+ case CKO_CERTIFICATE:
+ case CKO_NSS_TRUST:
+ attr = sftkdb_getAttributeFromTemplate(CKA_ISSUER, ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[1] = *attr;
+ attr = sftkdb_getAttributeFromTemplate(CKA_SERIAL_NUMBER,
+ ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[2] = *attr;
+ count = 3;
+ break;
+
+ case CKO_PRIVATE_KEY:
+ case CKO_PUBLIC_KEY:
+ case CKO_SECRET_KEY:
+ attr = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[1] = *attr;
+ count = 2;
+ break;
+
+ case CKO_NSS_CRL:
+ attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[1] = *attr;
+ count = 2;
+ break;
+
+ case CKO_NSS_SMIME:
+ attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[1] = *attr;
+ attr = sftkdb_getAttributeFromTemplate(CKA_NSS_EMAIL, ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[2] = *attr;
+ count = 3;
+ break;
+ default:
+ attr = sftkdb_getAttributeFromTemplate(CKA_VALUE, ptemplate, len);
+ if (attr == NULL) {
+ return CKR_TEMPLATE_INCOMPLETE;
+ }
+ findTemplate[1] = *attr;
+ count = 2;
+ break;
+ }
+ *findCount = count;
+
+ return CKR_OK;
+}
+
+/*
+ * look to see if this object already exists and return it's object ID if
+ * it does.
+ */
+static CK_RV
+sftkdb_lookupObject(SDB *db, CK_OBJECT_CLASS objectType,
+ CK_OBJECT_HANDLE *id, CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+ CK_ATTRIBUTE findTemplate[3];
+ CK_ULONG count = 1;
+ CK_ULONG objCount = 0;
+ SDBFind *find = NULL;
+ unsigned char objTypeData[SDB_ULONG_SIZE];
+ CK_RV crv;
+
+ *id = CK_INVALID_HANDLE;
+ if (objectType == CKO_NSS_CRL) {
+ return CKR_OK;
+ }
+ crv = sftkdb_getFindTemplate(objectType, objTypeData,
+ findTemplate, &count, ptemplate, len);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /* use the raw find, so we get the correct database */
+ crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ (*db->sdb_FindObjects)(db, find, id, 1, &objCount);
+ (*db->sdb_FindObjectsFinal)(db, find);
+
+ if (objCount == 0) {
+ *id = CK_INVALID_HANDLE;
+ }
+ return CKR_OK;
+}
+
+/*
+ * set an attribute and sign it if necessary
+ */
+static CK_RV
+sftkdb_setAttributeValue(PRArenaPool *arena, SFTKDBHandle *handle,
+ SDB *db, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template,
+ CK_ULONG count)
+{
+ CK_RV crv;
+ crv = (*db->sdb_SetAttributeValue)(db, objectID, template, count);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ crv = sftk_signTemplate(arena, handle, db == handle->update,
+ objectID, template, count);
+ return crv;
+}
+
+/*
+ * write a softoken object out to the database.
+ */
+CK_RV
+sftkdb_write(SFTKDBHandle *handle, SFTKObject *object,
+ CK_OBJECT_HANDLE *objectID)
+{
+ CK_ATTRIBUTE *template;
+ PLArenaPool *arena;
+ CK_ULONG count;
+ CK_RV crv;
+ SDB *db;
+ PRBool inTransaction = PR_FALSE;
+ CK_OBJECT_HANDLE id;
+
+ *objectID = CK_INVALID_HANDLE;
+
+ if (handle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ db = SFTK_GET_SDB(handle);
+
+ arena = PORT_NewArena(256);
+ if (arena == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ template = sftk_ExtractTemplate(arena, object, handle, &count, &crv);
+ if (!template) {
+ goto loser;
+ }
+
+ crv = (*db->sdb_Begin)(db);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ inTransaction = PR_TRUE;
+
+ crv = sftkdb_lookupObject(db, object->objclass, &id, template, count);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ if (id == CK_INVALID_HANDLE) {
+ crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count);
+ } else {
+ /* object already exists, modify it's attributes */
+ *objectID = id;
+ crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count);
+ }
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ crv = (*db->sdb_Commit)(db);
+ inTransaction = PR_FALSE;
+
+loser:
+ if (inTransaction) {
+ (*handle->db->sdb_Abort)(handle->db);
+ /* It is trivial to show the following code cannot
+ * happen unless something is horribly wrong with our compilier or
+ * hardware */
+ PORT_Assert(crv != CKR_OK);
+ if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
+ }
+
+ if (arena) {
+ PORT_FreeArena(arena,PR_FALSE);
+ }
+ if (crv == CKR_OK) {
+ *objectID |= (handle->type | SFTK_TOKEN_TYPE);
+ }
+ return crv;
+}
+
+
+CK_RV
+sftkdb_FindObjectsInit(SFTKDBHandle *handle, const CK_ATTRIBUTE *template,
+ CK_ULONG count, SDBFind **find)
+{
+ unsigned char *data = NULL;
+ CK_ATTRIBUTE *ntemplate = NULL;
+ CK_RV crv;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ db = SFTK_GET_SDB(handle);
+
+ if (count != 0) {
+ ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
+ if (ntemplate == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ }
+
+ crv = (*db->sdb_FindObjectsInit)(db, ntemplate,
+ count, find);
+ if (data) {
+ PORT_Free(ntemplate);
+ PORT_Free(data);
+ }
+ return crv;
+}
+
+CK_RV
+sftkdb_FindObjects(SFTKDBHandle *handle, SDBFind *find,
+ CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count)
+{
+ CK_RV crv;
+ SDB *db;
+
+ if (handle == NULL) {
+ *count = 0;
+ return CKR_OK;
+ }
+ db = SFTK_GET_SDB(handle);
+
+ crv = (*db->sdb_FindObjects)(db, find, ids,
+ arraySize, count);
+ if (crv == CKR_OK) {
+ int i;
+ for (i=0; i < *count; i++) {
+ ids[i] |= (handle->type | SFTK_TOKEN_TYPE);
+ }
+ }
+ return crv;
+}
+
+CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find)
+{
+ SDB *db;
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ db = SFTK_GET_SDB(handle);
+ return (*db->sdb_FindObjectsFinal)(db, find);
+}
+
+CK_RV
+sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID,
+ CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ CK_RV crv,crv2;
+ CK_ATTRIBUTE *ntemplate;
+ unsigned char *data = NULL;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_GENERAL_ERROR;
+ }
+
+ /* short circuit common attributes */
+ if (count == 1 &&
+ (template[0].type == CKA_TOKEN ||
+ template[0].type == CKA_PRIVATE ||
+ template[0].type == CKA_SENSITIVE)) {
+ CK_BBOOL boolVal = CK_TRUE;
+
+ if (template[0].pValue == NULL) {
+ template[0].ulValueLen = sizeof(CK_BBOOL);
+ return CKR_OK;
+ }
+ if (template[0].ulValueLen < sizeof(CK_BBOOL)) {
+ template[0].ulValueLen = -1;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+
+ if ((template[0].type == CKA_PRIVATE) &&
+ (handle->type != SFTK_KEYDB_TYPE)) {
+ boolVal = CK_FALSE;
+ }
+ if ((template[0].type == CKA_SENSITIVE) &&
+ (handle->type != SFTK_KEYDB_TYPE)) {
+ boolVal = CK_FALSE;
+ }
+ *(CK_BBOOL *)template[0].pValue = boolVal;
+ template[0].ulValueLen = sizeof(CK_BBOOL);
+ return CKR_OK;
+ }
+
+ db = SFTK_GET_SDB(handle);
+ /* nothing to do */
+ if (count == 0) {
+ return CKR_OK;
+ }
+ ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
+ if (ntemplate == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+ objectID &= SFTK_OBJ_ID_MASK;
+ crv = (*db->sdb_GetAttributeValue)(db, objectID,
+ ntemplate, count);
+ crv2 = sftkdb_fixupTemplateOut(template, objectID, ntemplate,
+ count, handle);
+ if (crv == CKR_OK) crv = crv2;
+ if (data) {
+ PORT_Free(ntemplate);
+ PORT_Free(data);
+ }
+ return crv;
+
+}
+
+CK_RV
+sftkdb_SetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID,
+ const CK_ATTRIBUTE *template, CK_ULONG count)
+{
+ CK_RV crv = CKR_OK;
+ CK_ATTRIBUTE *ntemplate;
+ unsigned char *data = NULL;
+ PLArenaPool *arena = NULL;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+
+ db = SFTK_GET_SDB(handle);
+ /* nothing to do */
+ if (count == 0) {
+ return CKR_OK;
+ }
+
+ ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
+ if (ntemplate == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ arena = PORT_NewArena(256);
+ if (arena == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ objectID &= SFTK_OBJ_ID_MASK;
+ crv = (*db->sdb_Begin)(db);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = sftkdb_setAttributeValue(arena, handle, db,
+ objectID, template, count);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = (*db->sdb_Commit)(db);
+loser:
+ if (crv != CKR_OK) {
+ (*db->sdb_Abort)(db);
+ }
+ if (data) {
+ PORT_Free(ntemplate);
+ PORT_Free(data);
+ }
+ PORT_FreeArena(arena, PR_FALSE);
+ return crv;
+}
+
+CK_RV
+sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID)
+{
+ CK_RV crv = CKR_OK;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ db = SFTK_GET_SDB(handle);
+ objectID &= SFTK_OBJ_ID_MASK;
+ crv = (*db->sdb_Begin)(db);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = (*db->sdb_DestroyObject)(db, objectID);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = (*db->sdb_Commit)(db);
+loser:
+ if (crv != CKR_OK) {
+ (*db->sdb_Abort)(db);
+ }
+ return crv;
+}
+
+CK_RV
+sftkdb_CloseDB(SFTKDBHandle *handle)
+{
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ if (handle->update) {
+ (*handle->update->sdb_Close)(handle->update);
+ }
+ if (handle->db) {
+ (*handle->db->sdb_Close)(handle->db);
+ }
+ if (handle->passwordLock) {
+ PZ_DestroyLock(handle->passwordLock);
+ }
+ if (handle->updatePasswordKey) {
+ SECITEM_FreeItem(handle->updatePasswordKey, PR_TRUE);
+ }
+ if (handle->updateID) {
+ PORT_Free(handle->updateID);
+ }
+ PORT_Free(handle);
+ return CKR_OK;
+}
+
+/*
+ * reset a database to it's uninitialized state.
+ */
+static CK_RV
+sftkdb_ResetDB(SFTKDBHandle *handle)
+{
+ CK_RV crv = CKR_OK;
+ SDB *db;
+ if (handle == NULL) {
+ return CKR_TOKEN_WRITE_PROTECTED;
+ }
+ db = SFTK_GET_SDB(handle);
+ crv = (*db->sdb_Begin)(db);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = (*db->sdb_Reset)(db);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ crv = (*db->sdb_Commit)(db);
+loser:
+ if (crv != CKR_OK) {
+ (*db->sdb_Abort)(db);
+ }
+ return crv;
+}
+
+
+CK_RV
+sftkdb_Begin(SFTKDBHandle *handle)
+{
+ CK_RV crv = CKR_OK;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ db = SFTK_GET_SDB(handle);
+ if (db) {
+ crv = (*db->sdb_Begin)(db);
+ }
+ return crv;
+}
+
+CK_RV
+sftkdb_Commit(SFTKDBHandle *handle)
+{
+ CK_RV crv = CKR_OK;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ db = SFTK_GET_SDB(handle);
+ if (db) {
+ (*db->sdb_Commit)(db);
+ }
+ return crv;
+}
+
+CK_RV
+sftkdb_Abort(SFTKDBHandle *handle)
+{
+ CK_RV crv = CKR_OK;
+ SDB *db;
+
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ db = SFTK_GET_SDB(handle);
+ if (db) {
+ crv = (db->sdb_Abort)(db);
+ }
+ return crv;
+}
+
+
+/*
+ * functions to update the database from an old database
+ */
+
+/*
+ * known attributes
+ */
+static const CK_ATTRIBUTE_TYPE known_attributes[] = {
+ CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
+ CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
+ CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
+ CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
+ CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
+ CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
+ CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
+ CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
+ CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
+ CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
+ CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS,
+ CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
+ CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
+ CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
+ CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
+ CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
+ CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
+ CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
+ CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
+ CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
+ CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
+ CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL,
+ CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP,
+ CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES,
+ CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED,
+ CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC,
+ CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
+ CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
+ CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
+ CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
+ CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
+ CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
+ CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
+ CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
+};
+
+static int known_attributes_size= sizeof(known_attributes)/
+ sizeof(known_attributes[0]);
+
+static CK_RV
+sftkdb_GetObjectTemplate(SDB *source, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE *ptemplate, CK_ULONG *max)
+{
+ int i,j;
+ CK_RV crv;
+
+ if (*max < known_attributes_size) {
+ *max = known_attributes_size;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ for (i=0; i < known_attributes_size; i++) {
+ ptemplate[i].type = known_attributes[i];
+ ptemplate[i].pValue = NULL;
+ ptemplate[i].ulValueLen = 0;
+ }
+
+ crv = (*source->sdb_GetAttributeValue)(source, id,
+ ptemplate, known_attributes_size);
+
+ if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
+ return crv;
+ }
+
+ for (i=0, j=0; i < known_attributes_size; i++, j++) {
+ while (i < known_attributes_size && (ptemplate[i].ulValueLen == -1)) {
+ i++;
+ }
+ if (i >= known_attributes_size) {
+ break;
+ }
+ /* cheap optimization */
+ if (i == j) {
+ continue;
+ }
+ ptemplate[j] = ptemplate[i];
+ }
+ *max = j;
+ return CKR_OK;
+}
+
+static const char SFTKDB_META_UPDATE_TEMPLATE[] = "upd_%s_%s";
+
+/*
+ * check to see if we have already updated this database.
+ * a NULL updateID means we are trying to do an in place
+ * single database update. In that case we have already
+ * determined that an update was necessary.
+ */
+static PRBool
+sftkdb_hasUpdate(const char *typeString, SDB *db, const char *updateID)
+{
+ char *id;
+ CK_RV crv;
+ SECItem dummy = { 0, NULL, 0 };
+ unsigned char dummyData[SDB_MAX_META_DATA_LEN];
+
+ if (!updateID) {
+ return PR_FALSE;
+ }
+ id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
+ if (id == NULL) {
+ return PR_FALSE;
+ }
+ dummy.data = dummyData;
+ dummy.len = sizeof(dummyData);
+
+ crv = (*db->sdb_GetMetaData)(db, id, &dummy, NULL);
+ PR_smprintf_free(id);
+ return crv == CKR_OK ? PR_TRUE : PR_FALSE;
+}
+
+/*
+ * we just completed an update, store the update id
+ * so we don't need to do it again. If non was given,
+ * there is nothing to do.
+ */
+static CK_RV
+sftkdb_putUpdate(const char *typeString, SDB *db, const char *updateID)
+{
+ char *id;
+ CK_RV crv;
+ SECItem dummy = { 0, NULL, 0 };
+
+ /* if no id was given, nothing to do */
+ if (updateID == NULL) {
+ return CKR_OK;
+ }
+
+ dummy.data = (unsigned char *)updateID;
+ dummy.len = PORT_Strlen(updateID);
+
+ id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID);
+ if (id == NULL) {
+ return PR_FALSE;
+ }
+
+ crv = (*db->sdb_PutMetaData)(db, id, &dummy, NULL);
+ PR_smprintf_free(id);
+ return crv;
+}
+
+/*
+ * get a ULong attribute from a template:
+ * NOTE: this is a raw templated stored in database order!
+ */
+static CK_ULONG
+sftkdb_getULongFromTemplate(CK_ATTRIBUTE_TYPE type,
+ CK_ATTRIBUTE *ptemplate, CK_ULONG len)
+{
+ CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(type,
+ ptemplate, len);
+
+ if (attr && attr->pValue && attr->ulValueLen == SDB_ULONG_SIZE) {
+ return sftk_SDBULong2ULong(attr->pValue);
+ }
+ return (CK_ULONG)-1;
+}
+
+/*
+ * we need to find a unique CKA_ID.
+ * The basic idea is to just increment the lowest byte.
+ * This code also handles the following corner cases:
+ * 1) the single byte overflows. On overflow we increment the next byte up
+ * and so forth until we have overflowed the entire CKA_ID.
+ * 2) If we overflow the entire CKA_ID we expand it by one byte.
+ * 3) the CKA_ID is non-existant, we create a new one with one byte.
+ * This means no matter what CKA_ID is passed, the result of this function
+ * is always a new CKA_ID, and this function will never return the same
+ * CKA_ID the it has returned in the passed.
+ */
+static CK_RV
+sftkdb_incrementCKAID(PRArenaPool *arena, CK_ATTRIBUTE *ptemplate)
+{
+ unsigned char *buf = ptemplate->pValue;
+ CK_ULONG len = ptemplate->ulValueLen;
+
+ if (buf == NULL || len == (CK_ULONG)-1) {
+ /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */
+ len = 0;
+ } else {
+ CK_ULONG i;
+
+ /* walk from the back to front, incrementing
+ * the CKA_ID until we no longer have a carry,
+ * or have hit the front of the id. */
+ for (i=len; i != 0; i--) {
+ buf[i-1]++;
+ if (buf[i-1] != 0) {
+ /* no more carries, the increment is complete */
+ return CKR_OK;
+ }
+ }
+ /* we've now overflowed, fall through and expand the CKA_ID by
+ * one byte */
+ }
+ buf = PORT_ArenaAlloc(arena, len+1);
+ if (!buf) {
+ return CKR_HOST_MEMORY;
+ }
+ if (len > 0) {
+ PORT_Memcpy(buf, ptemplate->pValue, len);
+ }
+ buf[len] = 0;
+ ptemplate->pValue = buf;
+ ptemplate->ulValueLen = len+1;
+ return CKR_OK;
+}
+
+/*
+ * drop an attribute from a template.
+ */
+void
+sftkdb_dropAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE *ptemplate,
+ CK_ULONG *plen)
+{
+ CK_ULONG count = *plen;
+ CK_ULONG i;
+
+ for (i=0; i < count; i++) {
+ if (attr->type == ptemplate[i].type) {
+ break;
+ }
+ }
+
+ if (i == count) {
+ /* attribute not found */
+ return;
+ }
+
+ /* copy the remaining attributes up */
+ for ( i++; i < count; i++) {
+ ptemplate[i-1] = ptemplate[i];
+ }
+
+ /* decrement the template size */
+ *plen = count -1;
+}
+
+/*
+ * create some defines for the following functions to document the meaning
+ * of true/false. (make's it easier to remember what means what.
+ */
+typedef enum {
+ SFTKDB_DO_NOTHING = 0,
+ SFTKDB_ADD_OBJECT,
+ SFTKDB_MODIFY_OBJECT,
+ SFTKDB_DROP_ATTRIBUTE
+} sftkdbUpdateStatus;
+
+/*
+ * helper function to reconsile a single trust entry.
+ * Identify which trust entry we want to keep.
+ * If we don't need to do anything (the records are already equal).
+ * return SFTKDB_DO_NOTHING.
+ * If we want to use the source version,
+ * return SFTKDB_MODIFY_OBJECT
+ * If we want to use the target version,
+ * return SFTKDB_DROP_ATTRIBUTE
+ *
+ * In the end the caller will remove any attributes in the source
+ * template when SFTKDB_DROP_ATTRIBUTE is specified, then use do a
+ * set attributes with that template on the target if we received
+ * any SFTKDB_MODIFY_OBJECT returns.
+ */
+sftkdbUpdateStatus
+sftkdb_reconsileTrustEntry(PRArenaPool *arena, CK_ATTRIBUTE *target,
+ CK_ATTRIBUTE *source)
+{
+ CK_ULONG targetTrust = sftkdb_getULongFromTemplate(target->type,
+ target, 1);
+ CK_ULONG sourceTrust = sftkdb_getULongFromTemplate(target->type,
+ source, 1);
+
+ /*
+ * try to pick the best solution between the source and the
+ * target. Update the source template if we want the target value
+ * to win out. Prefer cases where we don't actually update the
+ * trust entry.
+ */
+
+ /* they are the same, everything is already kosher */
+ if (targetTrust == sourceTrust) {
+ return SFTKDB_DO_NOTHING;
+ }
+
+ /* handle the case where the source Trust attribute may be a bit
+ * flakey */
+ if (sourceTrust == (CK_ULONG)-1) {
+ /*
+ * The source Trust is invalid. We know that the target Trust
+ * must be valid here, otherwise the above
+ * targetTrust == sourceTrust check would have succeeded.
+ */
+ return SFTKDB_DROP_ATTRIBUTE;
+ }
+
+ /* target is invalid, use the source's idea of the trust value */
+ if (targetTrust == (CK_ULONG)-1) {
+ /* overwriting the target in this case is OK */
+ return SFTKDB_MODIFY_OBJECT;
+ }
+
+ /* at this point we know that both attributes exist and have the
+ * appropriate length (SDB_ULONG_SIZE). We no longer need to check
+ * ulValueLen for either attribute.
+ */
+ if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) {
+ return SFTKDB_DROP_ATTRIBUTE;
+ }
+
+ /* target has no idea, use the source's idea of the trust value */
+ if (targetTrust == CKT_NSS_TRUST_UNKNOWN) {
+ /* overwriting the target in this case is OK */
+ return SFTKDB_MODIFY_OBJECT;
+ }
+
+ /* so both the target and the source have some idea of what this
+ * trust attribute should be, and neither agree exactly.
+ * At this point, we prefer 'hard' attributes over 'soft' ones.
+ * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and
+ * CKT_NSS_UNTRUTED. Soft ones are ones which don't change the
+ * actual trust of the cert (CKT_MUST_VERIFY, CKT_NSS_VALID,
+ * CKT_NSS_VALID_DELEGATOR).
+ */
+ if ((sourceTrust == CKT_NSS_MUST_VERIFY)
+ || (sourceTrust == CKT_NSS_VALID)
+ || (sourceTrust == CKT_NSS_VALID_DELEGATOR)) {
+ return SFTKDB_DROP_ATTRIBUTE;
+ }
+ if ((targetTrust == CKT_NSS_MUST_VERIFY)
+ || (targetTrust == CKT_NSS_VALID)
+ || (targetTrust == CKT_NSS_VALID_DELEGATOR)) {
+ /* again, overwriting the target in this case is OK */
+ return SFTKDB_MODIFY_OBJECT;
+ }
+
+ /* both have hard attributes, we have a conflict, let the target win. */
+ return SFTKDB_DROP_ATTRIBUTE;
+}
+
+const CK_ATTRIBUTE_TYPE sftkdb_trustList[] =
+ { CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH,
+ CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION,
+ CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER,
+ CKA_TRUST_TIME_STAMPING };
+
+#define SFTK_TRUST_TEMPLATE_COUNT \
+ (sizeof(sftkdb_trustList)/sizeof(sftkdb_trustList[0]))
+/*
+ * Run through the list of known trust types, and reconsile each trust
+ * entry one by one. Keep track of we really need to write out the source
+ * trust object (overwriting the existing one).
+ */
+static sftkdbUpdateStatus
+sftkdb_reconsileTrust(PRArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
+{
+ CK_ATTRIBUTE trustTemplate[SFTK_TRUST_TEMPLATE_COUNT];
+ unsigned char trustData[SFTK_TRUST_TEMPLATE_COUNT*SDB_ULONG_SIZE];
+ sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
+ CK_ULONG i;
+ CK_RV crv;
+
+
+ for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) {
+ trustTemplate[i].type = sftkdb_trustList[i];
+ trustTemplate[i].pValue = &trustData[i*SDB_ULONG_SIZE];
+ trustTemplate[i].ulValueLen = SDB_ULONG_SIZE;
+ }
+ crv = (*db->sdb_GetAttributeValue)(db, id,
+ trustTemplate, SFTK_TRUST_TEMPLATE_COUNT);
+ if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
+ /* target trust has some problems, update it */
+ update = SFTKDB_MODIFY_OBJECT;
+ goto done;
+ }
+
+ for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) {
+ CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(
+ trustTemplate[i].type, ptemplate, *plen);
+ sftkdbUpdateStatus status;
+
+
+ /* if target trust value doesn't exist, nothing to merge */
+ if (trustTemplate[i].ulValueLen == (CK_ULONG)-1) {
+ /* if the source exists, then we want the source entry,
+ * go ahead and update */
+ if (attr && attr->ulValueLen != (CK_ULONG)-1) {
+ update = SFTKDB_MODIFY_OBJECT;
+ }
+ continue;
+ }
+
+ /*
+ * the source doesn't have the attribute, go to the next attribute
+ */
+ if (attr == NULL) {
+ continue;
+
+ }
+ status = sftkdb_reconsileTrustEntry(arena, &trustTemplate[i], attr);
+ if (status == SFTKDB_MODIFY_OBJECT) {
+ update = SFTKDB_MODIFY_OBJECT;
+ } else if (status == SFTKDB_DROP_ATTRIBUTE) {
+ /* drop the source copy of the attribute, we are going with
+ * the target's version */
+ sftkdb_dropAttribute(attr, ptemplate, plen);
+ }
+ }
+
+ /* finally manage stepup */
+ if (update == SFTKDB_MODIFY_OBJECT) {
+ CK_BBOOL stepUpBool = CK_FALSE;
+ /* if we are going to write from the source, make sure we don't
+ * overwrite the stepup bit if it's on*/
+ trustTemplate[0].type = CKA_TRUST_STEP_UP_APPROVED;
+ trustTemplate[0].pValue = &stepUpBool;
+ trustTemplate[0].ulValueLen = sizeof(stepUpBool);
+ crv = (*db->sdb_GetAttributeValue)(db, id, trustTemplate, 1);
+ if ((crv == CKR_OK) && (stepUpBool == CK_TRUE)) {
+ sftkdb_dropAttribute(trustTemplate, ptemplate, plen);
+ }
+ } else {
+ /* we currently aren't going to update. If the source stepup bit is
+ * on however, do an update so the target gets it as well */
+ CK_ATTRIBUTE *attr;
+
+ attr = sftkdb_getAttributeFromTemplate(CKA_TRUST_STEP_UP_APPROVED,
+ ptemplate, *plen);
+ if (attr && (attr->ulValueLen == sizeof(CK_BBOOL)) &&
+ (*(CK_BBOOL *)(attr->pValue) == CK_TRUE)) {
+ update = SFTKDB_MODIFY_OBJECT;
+ }
+ }
+
+done:
+ return update;
+}
+
+static sftkdbUpdateStatus
+sftkdb_handleIDAndName(PRArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id,
+ CK_ATTRIBUTE *ptemplate, CK_ULONG *plen)
+{
+ sftkdbUpdateStatus update = SFTKDB_DO_NOTHING;
+ CK_ATTRIBUTE *attr1, *attr2;
+ CK_ATTRIBUTE ttemplate[2] = {
+ {CKA_ID, NULL, 0},
+ {CKA_LABEL, NULL, 0}
+ };
+ CK_RV crv;
+
+ attr1 = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen);
+ attr2 = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, *plen);
+
+ /* if the source has neither an id nor label, don't bother updating */
+ if ( (!attr1 || attr1->ulValueLen == 0) &&
+ (! attr2 || attr2->ulValueLen == 0) ) {
+ return SFTKDB_DO_NOTHING;
+ }
+
+ /* the source has either an id or a label, see what the target has */
+ crv = (*db->sdb_GetAttributeValue)(db, id, ttemplate, 2);
+
+ /* if the target has neither, update from the source */
+ if ( ((ttemplate[0].ulValueLen == 0) ||
+ (ttemplate[0].ulValueLen == (CK_ULONG)-1)) &&
+ ((ttemplate[1].ulValueLen == 0) ||
+ (ttemplate[1].ulValueLen == (CK_ULONG)-1)) ) {
+ return SFTKDB_MODIFY_OBJECT;
+ }
+
+ /* check the CKA_ID */
+ if ((ttemplate[0].ulValueLen != 0) &&
+ (ttemplate[0].ulValueLen != (CK_ULONG)-1)) {
+ /* we have a CKA_ID in the target, don't overwrite
+ * the target with an empty CKA_ID from the source*/
+ if (attr1 && attr1->ulValueLen == 0) {
+ sftkdb_dropAttribute(attr1, ptemplate, plen);
+ }
+ } else if (attr1 && attr1->ulValueLen != 0) {
+ /* source has a CKA_ID, but the target doesn't, update the target */
+ update = SFTKDB_MODIFY_OBJECT;
+ }
+
+
+ /* check the nickname */
+ if ((ttemplate[1].ulValueLen != 0) &&
+ (ttemplate[1].ulValueLen != (CK_ULONG)-1)) {
+
+ /* we have a nickname in the target, and we don't have to update
+ * the CKA_ID. We are done. NOTE: if we add addition attributes
+ * in this check, this shortcut can only go on the last of them. */
+ if (update == SFTKDB_DO_NOTHING) {
+ return update;
+ }
+ /* we have a nickname in the target, don't overwrite
+ * the target with an empty nickname from the source */
+ if (attr2 && attr2->ulValueLen == 0) {
+ sftkdb_dropAttribute(attr2, ptemplate, plen);
+ }
+ } else if (attr2 && attr2->ulValueLen != 0) {
+ /* source has a nickname, but the target doesn't, update the target */
+ update = SFTKDB_MODIFY_OBJECT;
+ }
+
+ return update;
+}
+
+
+
+/*
+ * This function updates the template before we write the object out.
+ *
+ * If we are going to skip updating this object, return PR_FALSE.
+ * If it should be updated we return PR_TRUE.
+ * To help readability, these have been defined
+ * as SFTK_DONT_UPDATE and SFTK_UPDATE respectively.
+ */
+static PRBool
+sftkdb_updateObjectTemplate(PRArenaPool *arena, SDB *db,
+ CK_OBJECT_CLASS objectType,
+ CK_ATTRIBUTE *ptemplate, CK_ULONG *plen,
+ CK_OBJECT_HANDLE *targetID)
+{
+ CK_ATTRIBUTE findTemplate[3];
+ CK_ULONG count = 1;
+ CK_ULONG objCount = 0;
+ CK_OBJECT_HANDLE id;
+ SDBFind *find = NULL;
+ unsigned char objTypeData[SDB_ULONG_SIZE];
+ CK_RV crv;
+
+ crv = sftkdb_getFindTemplate(objectType, objTypeData,
+ findTemplate, &count, ptemplate, *plen);
+ if (crv != CKR_OK) {
+ return SFTKDB_DO_NOTHING;
+ }
+
+ do {
+ /* use the raw find, so we get the correct database */
+ crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find);
+ if (crv != CKR_OK) {
+ return SFTKDB_DO_NOTHING;
+ }
+ (*db->sdb_FindObjects)(db, find, &id, 1, &objCount);
+ (*db->sdb_FindObjectsFinal)(db, find);
+
+ /* This object already exists, don't update */
+ if (objCount == 1) {
+ CK_ATTRIBUTE *attr = NULL;
+ /* special post processing for attributes */
+ switch (objectType) {
+ case CKO_CERTIFICATE:
+ case CKO_PUBLIC_KEY:
+ case CKO_PRIVATE_KEY:
+ /* update target's CKA_ID and labels if they don't already
+ * exist */
+ *targetID = id;
+ return sftkdb_handleIDAndName(arena, db, id, ptemplate, plen);
+ case CKO_NSS_TRUST:
+ /* if we have conflicting trust object types,
+ * we need to reconsile them */
+ *targetID = id;
+ return sftkdb_reconsileTrust(arena, db, id, ptemplate, plen);
+ case CKO_SECRET_KEY:
+ /* secret keys in the old database are all sdr keys,
+ * unfortunately they all appear to have the same CKA_ID,
+ * even though they are truly different keys, so we always
+ * want to update these keys, but we need to
+ * give them a new CKA_ID */
+ /* NOTE: this changes ptemplate */
+ attr = sftkdb_getAttributeFromTemplate(CKA_ID,ptemplate,*plen);
+ crv = sftkdb_incrementCKAID(arena, attr);
+ /* in the extremely rare event that we needed memory and
+ * couldn't get it, don't have, just drop the key */
+ if (crv != CKR_OK) {
+ return SFTKDB_DO_NOTHING;
+ }
+ findTemplate[1] = *attr;
+ objCount = 0; /* repeat this find loop */
+ break;
+ default:
+ /* for all other objects, if we found the equivalent object,
+ * don't update it */
+ return SFTKDB_DO_NOTHING;
+ }
+ }
+ } while (objCount == 1);
+
+ /* this object doesn't exist, update it */
+ return SFTKDB_ADD_OBJECT;
+}
+
+
+#define MAX_ATTRIBUTES 500
+static CK_RV
+sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id,
+ SECItem *key)
+{
+ CK_ATTRIBUTE template[MAX_ATTRIBUTES];
+ CK_ATTRIBUTE *ptemplate;
+ CK_ULONG max_attributes = MAX_ATTRIBUTES;
+ CK_OBJECT_CLASS objectType;
+ SDB *source = handle->update;
+ SDB *target = handle->db;
+ int i;
+ CK_RV crv;
+ PLArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(256);
+ if (arena == NULL) {
+ return CKR_HOST_MEMORY;
+ }
+
+ ptemplate = &template[0];
+ id &= SFTK_OBJ_ID_MASK;
+ crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes);
+ if (crv == CKR_BUFFER_TOO_SMALL) {
+ ptemplate = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, max_attributes);
+ if (ptemplate == NULL) {
+ crv = CKR_HOST_MEMORY;
+ } else {
+ crv = sftkdb_GetObjectTemplate(source, id,
+ ptemplate, &max_attributes);
+ }
+ }
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ for (i=0; i < max_attributes; i++) {
+ ptemplate[i].pValue = PORT_ArenaAlloc(arena,ptemplate[i].ulValueLen);
+ if (ptemplate[i].pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ }
+ crv = (*source->sdb_GetAttributeValue)(source, id,
+ ptemplate, max_attributes);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ objectType = sftkdb_getULongFromTemplate(CKA_CLASS, ptemplate,
+ max_attributes);
+
+ /*
+ * Update Object updates the object template if necessary then returns
+ * whether or not we need to actually write the object out to our target
+ * database.
+ */
+ if (!handle->updateID) {
+ crv = sftkdb_CreateObject(arena, handle, target, &id,
+ ptemplate, max_attributes);
+ } else {
+ sftkdbUpdateStatus update_status;
+ update_status = sftkdb_updateObjectTemplate(arena, target,
+ objectType, ptemplate, &max_attributes, &id);
+ switch (update_status) {
+ case SFTKDB_ADD_OBJECT:
+ crv = sftkdb_CreateObject(arena, handle, target, &id,
+ ptemplate, max_attributes);
+ break;
+ case SFTKDB_MODIFY_OBJECT:
+ crv = sftkdb_setAttributeValue(arena, handle, target,
+ id, ptemplate, max_attributes);
+ break;
+ case SFTKDB_DO_NOTHING:
+ case SFTKDB_DROP_ATTRIBUTE:
+ break;
+ }
+ }
+
+loser:
+ if (arena) {
+ PORT_FreeArena(arena,PR_TRUE);
+ }
+ return crv;
+}
+
+
+#define MAX_IDS 10
+/*
+ * update a new database from an old one, now that we have the key
+ */
+CK_RV
+sftkdb_Update(SFTKDBHandle *handle, SECItem *key)
+{
+ SDBFind *find = NULL;
+ CK_ULONG idCount = MAX_IDS;
+ CK_OBJECT_HANDLE ids[MAX_IDS];
+ SECItem *updatePasswordKey = NULL;
+ CK_RV crv, crv2;
+ PRBool inTransaction = PR_FALSE;
+ int i;
+
+ if (handle == NULL) {
+ return CKR_OK;
+ }
+ if (handle->update == NULL) {
+ return CKR_OK;
+ }
+
+ /*
+ * put the whole update under a transaction. This allows us to handle
+ * any possible race conditions between with the updateID check.
+ */
+ crv = (*handle->db->sdb_Begin)(handle->db);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ inTransaction = PR_TRUE;
+
+ /* some one else has already updated this db */
+ if (sftkdb_hasUpdate(sftkdb_TypeString(handle),
+ handle->db, handle->updateID)) {
+ crv = CKR_OK;
+ goto done;
+ }
+
+ updatePasswordKey = sftkdb_GetUpdatePasswordKey(handle);
+ if (updatePasswordKey) {
+ /* pass the source DB key to the legacy code,
+ * so it can decrypt things */
+ handle->oldKey = updatePasswordKey;
+ }
+
+ /* find all the objects */
+ crv = sftkdb_FindObjectsInit(handle, NULL, 0, &find);
+
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ while ((crv == CKR_OK) && (idCount == MAX_IDS)) {
+ crv = sftkdb_FindObjects(handle, find, ids, MAX_IDS, &idCount);
+ for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
+ crv = sftkdb_mergeObject(handle, ids[i], key);
+ }
+ }
+ crv2 = sftkdb_FindObjectsFinal(handle, find);
+ if (crv == CKR_OK) crv = crv2;
+
+loser:
+ /* no longer need the old key value */
+ handle->oldKey = NULL;
+
+ /* update the password - even if we didn't update objects */
+ if (handle->type == SFTK_KEYDB_TYPE) {
+ SECItem item1, item2;
+ unsigned char data1[SDB_MAX_META_DATA_LEN];
+ unsigned char data2[SDB_MAX_META_DATA_LEN];
+
+ item1.data = data1;
+ item1.len = sizeof(data1);
+ item2.data = data2;
+ item2.len = sizeof(data2);
+
+ /* if the target db already has a password, skip this. */
+ crv = (*handle->db->sdb_GetMetaData)(handle->db, "password",
+ &item1, &item2);
+ if (crv == CKR_OK) {
+ goto done;
+ }
+
+
+ /* nope, update it from the source */
+ crv = (*handle->update->sdb_GetMetaData)(handle->update, "password",
+ &item1, &item2);
+ if (crv != CKR_OK) {
+ goto done;
+ }
+ crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1,
+ &item2);
+ if (crv != CKR_OK) {
+ goto done;
+ }
+ }
+
+done:
+ /* finally mark this up to date db up to date */
+ /* some one else has already updated this db */
+ if (crv == CKR_OK) {
+ crv = sftkdb_putUpdate(sftkdb_TypeString(handle),
+ handle->db, handle->updateID);
+ }
+
+ if (inTransaction) {
+ if (crv == CKR_OK) {
+ crv = (*handle->db->sdb_Commit)(handle->db);
+ } else {
+ (*handle->db->sdb_Abort)(handle->db);
+ }
+ }
+ if (handle->update) {
+ (*handle->update->sdb_Close)(handle->update);
+ handle->update = NULL;
+ }
+ if (handle->updateID) {
+ PORT_Free(handle->updateID);
+ handle->updateID = NULL;
+ }
+ sftkdb_FreeUpdatePasswordKey(handle);
+ if (updatePasswordKey) {
+ SECITEM_ZfreeItem(updatePasswordKey, PR_TRUE);
+ }
+ handle->updateDBIsInit = PR_FALSE;
+ return crv;
+}
+
+/******************************************************************
+ * DB handle managing functions.
+ *
+ * These functions are called by softoken to initialize, acquire,
+ * and release database handles.
+ */
+
+const char *
+sftkdb_GetUpdateID(SFTKDBHandle *handle)
+{
+ return handle->updateID;
+}
+
+/* release a database handle */
+void
+sftk_freeDB(SFTKDBHandle *handle)
+{
+ PRInt32 ref;
+
+ if (!handle) return;
+ ref = PR_AtomicDecrement(&handle->ref);
+ if (ref == 0) {
+ sftkdb_CloseDB(handle);
+ }
+ return;
+}
+
+
+/*
+ * acquire a database handle for a certificate db
+ * (database for public objects)
+ */
+SFTKDBHandle *
+sftk_getCertDB(SFTKSlot *slot)
+{
+ SFTKDBHandle *dbHandle;
+
+ PZ_Lock(slot->slotLock);
+ dbHandle = slot->certDB;
+ if (dbHandle) {
+ PR_AtomicIncrement(&dbHandle->ref);
+ }
+ PZ_Unlock(slot->slotLock);
+ return dbHandle;
+}
+
+/*
+ * acquire a database handle for a key database
+ * (database for private objects)
+ */
+SFTKDBHandle *
+sftk_getKeyDB(SFTKSlot *slot)
+{
+ SFTKDBHandle *dbHandle;
+
+ PZ_Lock(slot->slotLock);
+ dbHandle = slot->keyDB;
+ if (dbHandle) {
+ PR_AtomicIncrement(&dbHandle->ref);
+ }
+ PZ_Unlock(slot->slotLock);
+ return dbHandle;
+}
+
+/*
+ * acquire the database for a specific object. NOTE: objectID must point
+ * to a Token object!
+ */
+SFTKDBHandle *
+sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID)
+{
+ SFTKDBHandle *dbHandle;
+
+ PZ_Lock(slot->slotLock);
+ dbHandle = objectID & SFTK_KEYDB_TYPE ? slot->keyDB : slot->certDB;
+ if (dbHandle) {
+ PR_AtomicIncrement(&dbHandle->ref);
+ }
+ PZ_Unlock(slot->slotLock);
+ return dbHandle;
+}
+
+/*
+ * initialize a new database handle
+ */
+static SFTKDBHandle *
+sftk_NewDBHandle(SDB *sdb, int type)
+{
+ SFTKDBHandle *handle = PORT_New(SFTKDBHandle);
+ handle->ref = 1;
+ handle->db = sdb;
+ handle->update = NULL;
+ handle->peerDB = NULL;
+ handle->newKey = NULL;
+ handle->oldKey = NULL;
+ handle->updatePasswordKey = NULL;
+ handle->updateID = NULL;
+ handle->type = type;
+ handle->passwordKey.data = NULL;
+ handle->passwordKey.len = 0;
+ handle->passwordLock = NULL;
+ if (type == SFTK_KEYDB_TYPE) {
+ handle->passwordLock = PZ_NewLock();
+ }
+ sdb->app_private = handle;
+ return handle;
+}
+
+/*
+ * reset the key database to it's uninitialized state. This call
+ * will clear all the key entried.
+ */
+SECStatus
+sftkdb_ResetKeyDB(SFTKDBHandle *handle)
+{
+ CK_RV crv;
+
+ /* only rest the key db */
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ return SECFailure;
+ }
+ crv = sftkdb_ResetDB(handle);
+ if (crv != CKR_OK) {
+ /* set error */
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+static PRBool
+sftk_oldVersionExists(const char *dir, int version)
+{
+ int i;
+ PRStatus exists = PR_FAILURE;
+ char *file = NULL;
+
+ for (i=version; i > 1 ; i--) {
+ file = PR_smprintf("%s%d.db",dir,i);
+ if (file == NULL) {
+ continue;
+ }
+ exists = PR_Access(file, PR_ACCESS_EXISTS);
+ PR_smprintf_free(file);
+ if (exists == PR_SUCCESS) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+static PRBool
+sftk_hasLegacyDB(const char *confdir, const char *certPrefix,
+ const char *keyPrefix, int certVersion, int keyVersion)
+{
+ char *dir;
+ PRBool exists;
+
+ if (certPrefix == NULL) {
+ certPrefix = "";
+ }
+
+ if (keyPrefix == NULL) {
+ keyPrefix = "";
+ }
+
+ dir= PR_smprintf("%s/%scert", confdir, certPrefix);
+ if (dir == NULL) {
+ return PR_FALSE;
+ }
+
+ exists = sftk_oldVersionExists(dir, certVersion);
+ PR_smprintf_free(dir);
+ if (exists) {
+ return PR_TRUE;
+ }
+
+ dir= PR_smprintf("%s/%skey", confdir, keyPrefix);
+ if (dir == NULL) {
+ return PR_FALSE;
+ }
+
+ exists = sftk_oldVersionExists(dir, keyVersion);
+ PR_smprintf_free(dir);
+ return exists;
+}
+
+/*
+ * initialize certificate and key database handles as a pair.
+ *
+ * This function figures out what type of database we are opening and
+ * calls the appropriate low level function to open the database.
+ * It also figures out whether or not to setup up automatic update.
+ */
+CK_RV
+sftk_DBInit(const char *configdir, const char *certPrefix,
+ const char *keyPrefix, const char *updatedir,
+ const char *updCertPrefix, const char *updKeyPrefix,
+ const char *updateID, PRBool readOnly, PRBool noCertDB,
+ PRBool noKeyDB, PRBool forceOpen,
+ SFTKDBHandle **certDB, SFTKDBHandle **keyDB)
+{
+ const char *confdir;
+ SDBType dbType;
+ char *appName = NULL;
+ SDB *keySDB, *certSDB;
+ CK_RV crv = CKR_OK;
+ int flags = SDB_RDONLY;
+ PRBool newInit = PR_FALSE;
+ PRBool needUpdate = PR_FALSE;
+
+ if (!readOnly) {
+ flags = SDB_CREATE;
+ }
+
+ *certDB = NULL;
+ *keyDB = NULL;
+
+ if (noKeyDB && noCertDB) {
+ return CKR_OK;
+ }
+ confdir = sftk_EvaluateConfigDir(configdir, &dbType, &appName);
+
+ /*
+ * now initialize the appropriate database
+ */
+ switch (dbType) {
+ case SDB_LEGACY:
+ crv = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
+ noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
+ break;
+ case SDB_MULTIACCESS:
+ crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags,
+ noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
+ break;
+ case SDB_SQL:
+ case SDB_EXTERN: /* SHOULD open a loadable db */
+ crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags,
+ noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit);
+
+ /*
+ * if we failed to open the DB's read only, use the old ones if
+ * the exists.
+ */
+ if (crv != CKR_OK) {
+ if ((flags == SDB_RDONLY) &&
+ sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
+ /* we have legacy databases, if we failed to open the new format
+ * DB's read only, just use the legacy ones */
+ crv = sftkdbCall_open(confdir, certPrefix,
+ keyPrefix, 8, 3, flags, noCertDB? NULL : &certSDB,
+ noKeyDB ? NULL : &keySDB);
+ }
+ /* Handle the database merge case.
+ *
+ * For the merge case, we need help from the application. Only
+ * the application knows where the old database is, and what unique
+ * identifier it has associated with it.
+ *
+ * If the client supplies these values, we use them to determine
+ * if we need to update.
+ */
+ } else if (
+ /* both update params have been supplied */
+ updatedir && *updatedir && updateID && *updateID
+ /* old dbs exist? */
+ && sftk_hasLegacyDB(updatedir, updCertPrefix, updKeyPrefix, 8, 3)
+ /* and they have not yet been updated? */
+ && ((noKeyDB || !sftkdb_hasUpdate("key", keySDB, updateID))
+ || (noCertDB || !sftkdb_hasUpdate("cert", certSDB, updateID)))) {
+ /* we need to update */
+ confdir = updatedir;
+ certPrefix = updCertPrefix;
+ keyPrefix = updKeyPrefix;
+ needUpdate = PR_TRUE;
+ } else if (newInit) {
+ /* if the new format DB was also a newly created DB, and we
+ * succeeded, then need to update that new database with data
+ * from the existing legacy DB */
+ if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
+ needUpdate = PR_TRUE;
+ }
+ }
+ break;
+ default:
+ crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST
+ * return one of the types we already
+ * specified. */
+ }
+ if (crv != CKR_OK) {
+ goto done;
+ }
+ if (!noCertDB) {
+ *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE);
+ } else {
+ *certDB = NULL;
+ }
+ if (!noKeyDB) {
+ *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE);
+ } else {
+ *keyDB = NULL;
+ }
+
+ /* link them together */
+ if (*certDB) {
+ (*certDB)->peerDB = *keyDB;
+ }
+ if (*keyDB) {
+ (*keyDB)->peerDB = *certDB;
+ }
+
+ /*
+ * if we need to update, open the legacy database and
+ * mark the handle as needing update.
+ */
+ if (needUpdate) {
+ SDB *updateCert = NULL;
+ SDB *updateKey = NULL;
+ CK_RV crv2;
+
+ crv2 = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
+ noCertDB ? NULL : &updateCert, noKeyDB ? NULL : &updateKey);
+ if (crv2 == CKR_OK) {
+ if (*certDB) {
+ (*certDB)->update = updateCert;
+ (*certDB)->updateID = updateID && *updateID
+ ? PORT_Strdup(updateID) : NULL;
+ updateCert->app_private = (*certDB);
+ }
+ if (*keyDB) {
+ PRBool tokenRemoved = PR_FALSE;
+ (*keyDB)->update = updateKey;
+ (*keyDB)->updateID = updateID && *updateID ?
+ PORT_Strdup(updateID) : NULL;
+ updateKey->app_private = (*keyDB);
+ (*keyDB)->updateDBIsInit = PR_TRUE;
+ (*keyDB)->updateDBIsInit =
+ (sftkdb_HasPasswordSet(*keyDB) == SECSuccess) ?
+ PR_TRUE : PR_FALSE;
+ /* if the password on the key db is NULL, kick off our update
+ * chain of events */
+ sftkdb_CheckPassword((*keyDB), "", &tokenRemoved);
+ } else {
+ /* we don't have a key DB, update the certificate DB now */
+ sftkdb_Update(*certDB, NULL);
+ }
+ }
+ }
+done:
+ if (appName) {
+ PORT_Free(appName);
+ }
+ return forceOpen ? CKR_OK : crv;
+}
+
+CK_RV
+sftkdb_Shutdown(void)
+{
+ s_shutdown();
+ sftkdbCall_Shutdown();
+ return CKR_OK;
+}
+
diff --git a/security/nss/lib/softoken/sftkdb.h b/security/nss/lib/softoken/sftkdb.h
new file mode 100644
index 000000000..14113fb3d
--- /dev/null
+++ b/security/nss/lib/softoken/sftkdb.h
@@ -0,0 +1,119 @@
+/* ***** 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
+ * Red Hat Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * 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 ***** */
+#include "sftkdbt.h"
+#include "sdb.h"
+#include "pkcs11i.h"
+#include "pkcs11t.h"
+
+/* raw database stuff */
+CK_RV sftkdb_write(SFTKDBHandle *handle, SFTKObject *,CK_OBJECT_HANDLE *);
+CK_RV sftkdb_FindObjectsInit(SFTKDBHandle *sdb, const CK_ATTRIBUTE *template,
+ CK_ULONG count, SDBFind **find);
+CK_RV sftkdb_FindObjects(SFTKDBHandle *sdb, SDBFind *find,
+ CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count);
+CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *sdb, SDBFind *find);
+CK_RV sftkdb_GetAttributeValue(SFTKDBHandle *handle,
+ CK_OBJECT_HANDLE object_id, CK_ATTRIBUTE *template, CK_ULONG count);
+CK_RV sftkdb_SetAttributeValue(SFTKDBHandle *handle,
+ CK_OBJECT_HANDLE object_id, const CK_ATTRIBUTE *template,
+ CK_ULONG count);
+CK_RV sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id);
+CK_RV sftkdb_closeDB(SFTKDBHandle *handle);
+
+
+/* secmod.db functions */
+char ** sftkdb_ReadSecmodDB(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char *params, PRBool rw);
+SECStatus sftkdb_ReleaseSecmodDBData(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char **moduleSpecList, PRBool rw);
+SECStatus sftkdb_DeleteSecmodDB(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char *args, PRBool rw);
+SECStatus sftkdb_AddSecmodDB(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char *module, PRBool rw);
+
+/* keydb functions */
+
+SECStatus sftkdb_PWIsInitialized(SFTKDBHandle *keydb);
+SECStatus sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw,
+ PRBool *tokenRemoved);
+SECStatus sftkdb_PWCached(SFTKDBHandle *keydb);
+SECStatus sftkdb_HasPasswordSet(SFTKDBHandle *keydb);
+SECStatus sftkdb_ResetKeyDB(SFTKDBHandle *keydb);
+SECStatus sftkdb_ChangePassword(SFTKDBHandle *keydb,
+ char *oldPin, char *newPin,
+ PRBool *tokenRemoved);
+SECStatus sftkdb_ClearPassword(SFTKDBHandle *keydb);
+PRBool sftkdb_InUpdateMerge(SFTKDBHandle *keydb);
+PRBool sftkdb_NeedUpdateDBPassword(SFTKDBHandle *keydb);
+const char *sftkdb_GetUpdateID(SFTKDBHandle *keydb);
+SECItem *sftkdb_GetUpdatePasswordKey(SFTKDBHandle *keydb);
+void sftkdb_FreeUpdatePasswordKey(SFTKDBHandle *keydb);
+
+/* Utility functions */
+/*
+ * OK there are now lots of options here, lets go through them all:
+ *
+ * configdir - base directory where all the cert, key, and module datbases live.
+ * certPrefix - prefix added to the beginning of the cert database example: "
+ * "https-server1-"
+ * keyPrefix - prefix added to the beginning of the key database example: "
+ * "https-server1-"
+ * secmodName - name of the security module database (usually "secmod.db").
+ * readOnly - Boolean: true if the databases are to be openned read only.
+ * nocertdb - Don't open the cert DB and key DB's, just initialize the
+ * Volatile certdb.
+ * nomoddb - Don't open the security module DB, just initialize the
+ * PKCS #11 module.
+ * forceOpen - Continue to force initializations even if the databases cannot
+ * be opened.
+ */
+CK_RV sftk_DBInit(const char *configdir, const char *certPrefix,
+ const char *keyPrefix, const char *updatedir,
+ const char *updCertPrefix, const char *updKeyPrefix,
+ const char *updateID, PRBool readOnly, PRBool noCertDB,
+ PRBool noKeyDB, PRBool forceOpen,
+ SFTKDBHandle **certDB, SFTKDBHandle **keyDB);
+CK_RV sftkdb_Shutdown(void);
+
+SFTKDBHandle *sftk_getCertDB(SFTKSlot *slot);
+SFTKDBHandle *sftk_getKeyDB(SFTKSlot *slot);
+SFTKDBHandle *sftk_getDBForTokenObject(SFTKSlot *slot,
+ CK_OBJECT_HANDLE objectID);
+void sftk_freeDB(SFTKDBHandle *certHandle);
diff --git a/security/nss/lib/softoken/sftkdbt.h b/security/nss/lib/softoken/sftkdbt.h
new file mode 100644
index 000000000..f8cdb0639
--- /dev/null
+++ b/security/nss/lib/softoken/sftkdbt.h
@@ -0,0 +1,51 @@
+/* ***** 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
+ * Red Hat Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * 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 ***** */
+
+#ifndef SFTKDBT_H
+#define SFTKDBT_H 1
+typedef struct SFTKDBHandleStr SFTKDBHandle;
+
+#define SDB_MAX_META_DATA_LEN 256
+#define SDB_ULONG_SIZE 4
+
+typedef enum {
+ SDB_SQL,
+ SDB_EXTERN,
+ SDB_LEGACY,
+ SDB_MULTIACCESS
+} SDBType;
+
+#endif
diff --git a/security/nss/lib/softoken/sftkdbti.h b/security/nss/lib/softoken/sftkdbti.h
new file mode 100644
index 000000000..efaf842cb
--- /dev/null
+++ b/security/nss/lib/softoken/sftkdbti.h
@@ -0,0 +1,92 @@
+/* ***** 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
+ * Red Hat Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * 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 ***** */
+
+#ifndef SFTKDBTI_H
+#define SFTKDBTI_H 1
+
+/*
+ * private defines
+ */
+struct SFTKDBHandleStr {
+ SDB *db;
+ PRInt32 ref;
+ CK_OBJECT_HANDLE type;
+ SECItem passwordKey;
+ SECItem *newKey;
+ SECItem *oldKey;
+ SECItem *updatePasswordKey;
+ PZLock *passwordLock;
+ SFTKDBHandle *peerDB;
+ SDB *update;
+ char *updateID;
+ PRBool updateDBIsInit;
+};
+
+#define SFTK_KEYDB_TYPE 0x40000000
+#define SFTK_CERTDB_TYPE 0x00000000
+#define SFTK_OBJ_TYPE_MASK 0xc0000000
+#define SFTK_OBJ_ID_MASK (~SFTK_OBJ_TYPE_MASK)
+#define SFTK_TOKEN_TYPE 0x80000000
+
+/* the following is the number of id's to handle on the stack at a time,
+ * it's not an upper limit of IDS that can be stored in the database */
+#define SFTK_MAX_IDS 10
+
+#define SFTK_GET_SDB(handle) \
+ ((handle)->update ? (handle)->update : (handle)->db)
+
+SECStatus sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText,
+ SECItem **plainText);
+SECStatus sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey,
+ SECItem *plainText, SECItem **cipherText);
+SECStatus sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey,
+ CK_OBJECT_HANDLE objectID,
+ CK_ATTRIBUTE_TYPE attrType,
+ SECItem *plainText, SECItem **sigText);
+SECStatus sftkdb_VerifyAttribute(SECItem *passKey,
+ CK_OBJECT_HANDLE objectID,
+ CK_ATTRIBUTE_TYPE attrType,
+ SECItem *plainText, SECItem *sigText);
+
+void sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value);
+CK_RV sftkdb_Update(SFTKDBHandle *handle, SECItem *key);
+CK_RV sftkdb_PutAttributeSignature(SFTKDBHandle *handle,
+ SDB *keyTarget, CK_OBJECT_HANDLE objectID,
+ CK_ATTRIBUTE_TYPE type, SECItem *signText);
+
+
+
+#endif
diff --git a/security/nss/lib/softoken/sftkmod.c b/security/nss/lib/softoken/sftkmod.c
new file mode 100644
index 000000000..408270bdd
--- /dev/null
+++ b/security/nss/lib/softoken/sftkmod.c
@@ -0,0 +1,695 @@
+/* ***** 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-2007
+ * 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. For the rest of NSS, only one kind of database handle exists:
+ *
+ * SFTKDBHandle
+ *
+ * There is one SFTKDBHandle for the each key database and one for each cert
+ * database. These databases are opened as associated pairs, one pair per
+ * slot. SFTKDBHandles are reference counted objects.
+ *
+ * Each SFTKDBHandle points to a low level database handle (SDB). This handle
+ * represents the underlying physical database. These objects are not
+ * reference counted, an are 'owned' by their respective SFTKDBHandles.
+ *
+ *
+ */
+#include "sftkdb.h"
+#include "sftkpars.h"
+#include "prprf.h"
+#include "prsystem.h"
+#include "lgglue.h"
+#include "secmodt.h"
+
+/****************************************************************
+ *
+ * Secmod database.
+ *
+ * The new secmod database is simply a text file with each of the module
+ * entries. in the following form:
+ *
+ * #
+ * # This is a comment The next line is the library to load
+ * library=libmypkcs11.so
+ * name="My PKCS#11 module"
+ * params="my library's param string"
+ * nss="NSS parameters"
+ * other="parameters for other libraries and applications"
+ *
+ * library=libmynextpk11.so
+ * name="My other PKCS#11 module"
+ */
+
+static char *
+sftkdb_quote(const char *string, char quote)
+{
+ char *newString = 0;
+ int escapes = 0, size = 0;
+ const char *src;
+ char *dest;
+
+ size=2;
+ for (src=string; *src ; src++) {
+ if ((*src == quote) || (*src == '\\')) escapes++;
+ size++;
+ }
+
+ dest = newString = PORT_ZAlloc(escapes+size+1);
+ if (newString == NULL) {
+ return NULL;
+ }
+
+ *dest++=quote;
+ for (src=string; *src; src++,dest++) {
+ if ((*src == '\\') || (*src == quote)) {
+ *dest++ = '\\';
+ }
+ *dest = *src;
+ }
+ *dest=quote;
+
+ return newString;
+}
+
+/*
+ * Smart string cat functions. Automatically manage the memory.
+ * The first parameter is the source string. If it's null, we
+ * allocate memory for it. If it's not, we reallocate memory
+ * so the the concanenated string fits.
+ */
+static char *
+sftkdb_DupnCat(char *baseString, const char *str, int str_len)
+{
+ int len = (baseString ? PORT_Strlen(baseString) : 0) + 1;
+ char *newString;
+
+ len += str_len;
+ newString = (char *) PORT_Realloc(baseString,len);
+ if (newString == NULL) {
+ PORT_Free(baseString);
+ return NULL;
+ }
+ if (baseString == NULL) *newString = 0;
+ return PORT_Strncat(newString,str, str_len);
+}
+
+/* Same as sftkdb_DupnCat except it concatenates the full string, not a
+ * partial one */
+static char *
+sftkdb_DupCat(char *baseString, const char *str)
+{
+ return sftkdb_DupnCat(baseString, str, PORT_Strlen(str));
+}
+
+/* function to free up all the memory associated with a null terminated
+ * array of module specs */
+static SECStatus
+sftkdb_releaseSpecList(char **moduleSpecList)
+{
+ if (moduleSpecList) {
+ char **index;
+ for(index = moduleSpecList; *index; index++) {
+ PORT_Free(*index);
+ }
+ PORT_Free(moduleSpecList);
+ }
+ return SECSuccess;
+}
+
+#define SECMOD_STEP 10
+static SECStatus
+sftkdb_growList(char ***pModuleList, int *useCount, int last)
+{
+ char **newModuleList;
+
+ *useCount += SECMOD_STEP;
+ newModuleList = (char **)PORT_Realloc(*pModuleList,
+ *useCount*sizeof(char *));
+ if (newModuleList == NULL) {
+ return SECFailure;
+ }
+ PORT_Memset(&newModuleList[last],0, sizeof(char *)*SECMOD_STEP);
+ *pModuleList = newModuleList;
+ return SECSuccess;
+}
+
+static
+char *sftk_getOldSecmodName(const char *dbname,const char *filename)
+{
+ char *file = NULL;
+ char *dirPath = PORT_Strdup(dbname);
+ char *sep;
+
+ sep = PORT_Strrchr(dirPath,*PATH_SEPARATOR);
+#ifdef WINDOWS
+ if (!sep) {
+ sep = PORT_Strrchr(dirPath,'/');
+ }
+#endif
+ if (sep) {
+ *(sep)=0;
+ }
+ file= PR_smprintf("%s"PATH_SEPARATOR"%s", dirPath, filename);
+ PORT_Free(dirPath);
+ return file;
+}
+
+#define MAX_LINE_LENGTH 2048
+#define SFTK_DEFAULT_INTERNAL_INIT1 "library= name=\"NSS Internal PKCS #11 Module\" parameters="
+#define SFTK_DEFAULT_INTERNAL_INIT2 " NSS=\"Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={"
+#define SFTK_DEFAULT_INTERNAL_INIT3 " askpw=any timeout=30})\""
+
+#ifdef XP_UNIX
+#include <unistd.h>
+#endif
+/*
+ * Read all the existing modules in out of the file.
+ */
+char **
+sftkdb_ReadSecmodDB(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char *params, PRBool rw)
+{
+ FILE *fd = NULL;
+ char **moduleList = NULL;
+ int moduleCount = 1;
+ int useCount = SECMOD_STEP;
+ char line[MAX_LINE_LENGTH];
+ PRBool internal = PR_FALSE;
+ PRBool skipParams = PR_FALSE;
+ char *moduleString = NULL;
+ char *paramsValue=NULL;
+ PRBool failed = PR_TRUE;
+
+ if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
+ return sftkdbCall_ReadSecmodDB(appName, filename, dbname, params, rw);
+ }
+
+ moduleList = (char **) PORT_ZAlloc(useCount*sizeof(char **));
+ if (moduleList == NULL) return NULL;
+
+ /* do we really want to use streams here */
+ fd = fopen(dbname, "r");
+ if (fd == NULL) goto done;
+
+ /*
+ * the following loop takes line separated config lines and colapses
+ * the lines to a single string, escaping and quoting as necessary.
+ */
+ /* loop state variables */
+ moduleString = NULL; /* current concatenated string */
+ internal = PR_FALSE; /* is this an internal module */
+ skipParams = PR_FALSE; /* did we find an override parameter block*/
+ paramsValue = NULL; /* the current parameter block value */
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ int len = PORT_Strlen(line);
+
+ /* remove the ending newline */
+ if (len && line[len-1] == '\n') {
+ len--;
+ line[len] = 0;
+ }
+ if (*line == '#') {
+ continue;
+ }
+ if (*line != 0) {
+ /*
+ * The PKCS #11 group standard assumes blocks of strings
+ * separated by new lines, clumped by new lines. Internally
+ * we take strings separated by spaces, so we may need to escape
+ * certain spaces.
+ */
+ char *value = PORT_Strchr(line,'=');
+
+ /* there is no value, write out the stanza as is */
+ if (value == NULL || value[1] == 0) {
+ if (moduleString) {
+ moduleString = sftkdb_DupnCat(moduleString," ", 1);
+ if (moduleString == NULL) goto loser;
+ }
+ moduleString = sftkdb_DupCat(moduleString, line);
+ if (moduleString == NULL) goto loser;
+ /* value is already quoted, just write it out */
+ } else if (value[1] == '"') {
+ if (moduleString) {
+ moduleString = sftkdb_DupnCat(moduleString," ", 1);
+ if (moduleString == NULL) goto loser;
+ }
+ moduleString = sftkdb_DupCat(moduleString, line);
+ if (moduleString == NULL) goto loser;
+ /* we have an override parameter section, remember that
+ * we found this (see following comment about why this
+ * is necessary). */
+ if (PORT_Strncasecmp(line, "parameters", 10) == 0) {
+ skipParams = PR_TRUE;
+ }
+ /*
+ * The internal token always overrides it's parameter block
+ * from the passed in parameters, so wait until then end
+ * before we include the parameter block in case we need to
+ * override it. NOTE: if the parameter block is quoted with ("),
+ * this override does not happen. This allows you to override
+ * the application's parameter configuration.
+ *
+ * parameter block state is controlled by the following variables:
+ * skipParams - Bool : set to true of we have an override param
+ * block (all other blocks, either implicit or explicit are
+ * ignored).
+ * paramsValue - char * : pointer to the current param block. In
+ * the absence of overrides, paramsValue is set to the first
+ * parameter block we find. All subsequent blocks are ignored.
+ * When we find an internal token, the application passed
+ * parameters take precident.
+ */
+ } else if (PORT_Strncasecmp(line, "parameters", 10) == 0) {
+ /* already have parameters */
+ if (paramsValue) {
+ continue;
+ }
+ paramsValue = sftkdb_quote(&value[1], '"');
+ if (paramsValue == NULL) goto loser;
+ continue;
+ } else {
+ /* may need to quote */
+ char *newLine;
+ if (moduleString) {
+ moduleString = sftkdb_DupnCat(moduleString," ", 1);
+ if (moduleString == NULL) goto loser;
+ }
+ moduleString = sftkdb_DupnCat(moduleString,line,value-line+1);
+ if (moduleString == NULL) goto loser;
+ newLine = sftkdb_quote(&value[1],'"');
+ if (newLine == NULL) goto loser;
+ moduleString = sftkdb_DupCat(moduleString,newLine);
+ PORT_Free(newLine);
+ if (moduleString == NULL) goto loser;
+ }
+
+ /* check to see if it's internal? */
+ if (PORT_Strncasecmp(line, "NSS=", 4) == 0) {
+ /* This should be case insensitive! reviewers make
+ * me fix it if it's not */
+ if (PORT_Strstr(line,"internal")) {
+ internal = PR_TRUE;
+ /* override the parameters */
+ if (paramsValue) {
+ PORT_Free(paramsValue);
+ }
+ paramsValue = sftkdb_quote(params, '"');
+ }
+ }
+ continue;
+ }
+ if ((moduleString == NULL) || (*moduleString == 0)) {
+ continue;
+ }
+
+ /*
+ * if we are here, we have found a complete stanza. Now write out
+ * any param section we may have found.
+ */
+ if (paramsValue) {
+ /* we had an override */
+ if (!skipParams) {
+ moduleString = sftkdb_DupnCat(moduleString," parameters=", 12);
+ if (moduleString == NULL) goto loser;
+ moduleString = sftkdb_DupCat(moduleString, paramsValue);
+ if (moduleString == NULL) goto loser;
+ }
+ PORT_Free(paramsValue);
+ paramsValue = NULL;
+ }
+
+ if ((moduleCount+1) >= useCount) {
+ SECStatus rv;
+ rv = sftkdb_growList(&moduleList, &useCount, moduleCount+1);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ if (internal) {
+ moduleList[0] = moduleString;
+ } else {
+ moduleList[moduleCount] = moduleString;
+ moduleCount++;
+ }
+ moduleString = NULL;
+ internal = PR_FALSE;
+ skipParams = PR_FALSE;
+ }
+
+ if (moduleString) {
+ PORT_Free(moduleString);
+ moduleString = NULL;
+ }
+done:
+ /* if we couldn't open a pkcs11 database, look for the old one */
+ if (fd == NULL) {
+ char *olddbname = sftk_getOldSecmodName(dbname,filename);
+ PRStatus status;
+ char **oldModuleList;
+ int i;
+
+ /* couldn't get the old name */
+ if (!olddbname) {
+ goto bail;
+ }
+
+ /* old one doesn't exist */
+ status = PR_Access(olddbname, PR_ACCESS_EXISTS);
+ if (status != PR_SUCCESS) {
+ goto bail;
+ }
+
+ oldModuleList = sftkdbCall_ReadSecmodDB(appName, filename,
+ olddbname, params, rw);
+ /* old one had no modules */
+ if (!oldModuleList) {
+ goto bail;
+ }
+
+ /* count the modules */
+ for (i=0; oldModuleList[i]; i++) { }
+
+ /* grow the moduleList if necessary */
+ if (i >= useCount) {
+ SECStatus rv;
+ rv = sftkdb_growList(&moduleList,&useCount,moduleCount+1);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ /* write each module out, and copy it */
+ for (i=0; oldModuleList[i]; i++) {
+ if (rw) {
+ sftkdb_AddSecmodDB(dbType,appName,filename,dbname,
+ oldModuleList[i],rw);
+ }
+ if (moduleList[i]) {
+ PORT_Free(moduleList[i]);
+ }
+ moduleList[i] = PORT_Strdup(oldModuleList[i]);
+ }
+
+ /* done with the old module list */
+ sftkdbCall_ReleaseSecmodDBData(appName, filename, olddbname,
+ oldModuleList, rw);
+bail:
+ if (olddbname) {
+ PR_smprintf_free(olddbname);
+ }
+ }
+
+ if (!moduleList[0]) {
+ char * newParams;
+ moduleString = PORT_Strdup(SFTK_DEFAULT_INTERNAL_INIT1);
+ newParams = sftkdb_quote(params,'"');
+ if (newParams == NULL) goto loser;
+ moduleString = sftkdb_DupCat(moduleString, newParams);
+ PORT_Free(newParams);
+ if (moduleString == NULL) goto loser;
+ moduleString = sftkdb_DupCat(moduleString, SFTK_DEFAULT_INTERNAL_INIT2);
+ if (moduleString == NULL) goto loser;
+ moduleString = sftkdb_DupCat(moduleString, SECMOD_SLOT_FLAGS);
+ if (moduleString == NULL) goto loser;
+ moduleString = sftkdb_DupCat(moduleString, SFTK_DEFAULT_INTERNAL_INIT3);
+ if (moduleString == NULL) goto loser;
+ moduleList[0] = moduleString;
+ moduleString = NULL;
+ }
+ failed = PR_FALSE;
+
+loser:
+ /*
+ * cleanup
+ */
+ /* deal with trust cert db here */
+ if (moduleString) {
+ PORT_Free(moduleString);
+ moduleString = NULL;
+ }
+ if (paramsValue) {
+ PORT_Free(paramsValue);
+ paramsValue = NULL;
+ }
+ if (failed || (moduleList[0] == NULL)) {
+ /* This is wrong! FIXME */
+ sftkdb_releaseSpecList(moduleList);
+ moduleList = NULL;
+ failed = PR_TRUE;
+ }
+ if (fd != NULL) {
+ fclose(fd);
+ } else if (!failed && rw) {
+ /* update our internal module */
+ sftkdb_AddSecmodDB(dbType,appName,filename,dbname,moduleList[0],rw);
+ }
+ return moduleList;
+}
+
+SECStatus
+sftkdb_ReleaseSecmodDBData(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char **moduleSpecList, PRBool rw)
+{
+ if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
+ return sftkdbCall_ReleaseSecmodDBData(appName, filename, dbname,
+ moduleSpecList, rw);
+ }
+ if (moduleSpecList) {
+ sftkdb_releaseSpecList(moduleSpecList);
+ }
+ return SECSuccess;
+}
+
+
+/*
+ * Delete a module from the Data Base
+ */
+SECStatus
+sftkdb_DeleteSecmodDB(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char *args, PRBool rw)
+{
+ /* SHDB_FIXME implement */
+ FILE *fd = NULL;
+ FILE *fd2 = NULL;
+ char line[MAX_LINE_LENGTH];
+ char *dbname2 = NULL;
+ char *block = NULL;
+ char *name = NULL;
+ char *lib = NULL;
+ int name_len, lib_len;
+ PRBool skip = PR_FALSE;
+ PRBool found = PR_FALSE;
+
+ if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
+ return sftkdbCall_DeleteSecmodDB(appName, filename, dbname, args, rw);
+ }
+
+ if (!rw) {
+ return SECFailure;
+ }
+
+ dbname2 = strdup(dbname);
+ if (dbname2 == NULL) goto loser;
+ dbname2[strlen(dbname)-1]++;
+
+ /* do we really want to use streams here */
+ fd = fopen(dbname, "r");
+ if (fd == NULL) goto loser;
+ fd2 = fopen(dbname2, "w+");
+ if (fd2 == NULL) goto loser;
+
+ name = sftk_argGetParamValue("name",args);
+ if (name) {
+ name_len = PORT_Strlen(name);
+ }
+ lib = sftk_argGetParamValue("library",args);
+ if (lib) {
+ lib_len = PORT_Strlen(lib);
+ }
+
+
+ /*
+ * the following loop takes line separated config files and colapses
+ * the lines to a single string, escaping and quoting as necessary.
+ */
+ /* loop state variables */
+ block = NULL;
+ skip = PR_FALSE;
+ while (fgets(line, sizeof(line), fd) != NULL) {
+ /* If we are processing a block (we haven't hit a blank line yet */
+ if (*line != '\n') {
+ /* skip means we are in the middle of a block we are deleting */
+ if (skip) {
+ continue;
+ }
+ /* if we haven't found the block yet, check to see if this block
+ * matches our requirements */
+ if (!found && ((name && (PORT_Strncasecmp(line,"name=",5) == 0) &&
+ (PORT_Strncmp(line+5,name,name_len) == 0)) ||
+ (lib && (PORT_Strncasecmp(line,"library=",8) == 0) &&
+ (PORT_Strncmp(line+8,lib,lib_len) == 0)))) {
+
+ /* yup, we don't need to save any more data, */
+ PORT_Free(block);
+ block=NULL;
+ /* we don't need to collect more of this block */
+ skip = PR_TRUE;
+ /* we don't need to continue searching for the block */
+ found =PR_TRUE;
+ continue;
+ }
+ /* not our match, continue to collect data in this block */
+ block = sftkdb_DupCat(block,line);
+ continue;
+ }
+ /* we've collected a block of data that wasn't the module we were
+ * looking for, write it out */
+ if (block) {
+ fwrite(block, PORT_Strlen(block), 1, fd2);
+ PORT_Free(block);
+ block = NULL;
+ }
+ /* If we didn't just delete the this block, keep the blank line */
+ if (!skip) {
+ fputs(line,fd2);
+ }
+ /* we are definately not in a deleted block anymore */
+ skip = PR_FALSE;
+ }
+ fclose(fd);
+ fclose(fd2);
+ /* rename dbname2 to dbname */
+ if (found) {
+ PR_Delete(dbname);
+ PR_Rename(dbname2,dbname);
+ }
+ PORT_Free(dbname2);
+ return SECSuccess;
+
+loser:
+ if (fd != NULL) {
+ fclose(fd);
+ }
+ if (fd2 != NULL) {
+ fclose(fd2);
+ }
+ if (dbname2) {
+ PR_Delete(dbname2);
+ PORT_Free(dbname2);
+ }
+ return SECFailure;
+}
+
+/*
+ * Add a module to the Data base
+ */
+SECStatus
+sftkdb_AddSecmodDB(SDBType dbType, const char *appName,
+ const char *filename, const char *dbname,
+ char *module, PRBool rw)
+{
+ FILE *fd = NULL;
+ char *block = NULL;
+ PRBool libFound = PR_FALSE;
+
+ if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
+ return sftkdbCall_AddSecmodDB(appName, filename, dbname, module, rw);
+ }
+
+ /* can't write to a read only module */
+ if (!rw) {
+ return SECFailure;
+ }
+
+ /* remove the previous version if it exists */
+ (void) sftkdb_DeleteSecmodDB(dbType, appName, filename, dbname, module, rw);
+
+ /* do we really want to use streams here */
+ fd = fopen(dbname, "a+");
+ if (fd == NULL) {
+ return SECFailure;
+ }
+ module = sftk_argStrip(module);
+ while (*module) {
+ int count;
+ char *keyEnd = PORT_Strchr(module,'=');
+ char *value;
+
+ if (PORT_Strncmp(module, "library=", 8) == 0) {
+ libFound=PR_TRUE;
+ }
+ if (keyEnd == NULL) {
+ block = sftkdb_DupCat(block, module);
+ break;
+ }
+ value = sftk_argFetchValue(&keyEnd[1], &count);
+ block = sftkdb_DupnCat(block, module, keyEnd-module+1);
+ if (block == NULL) { goto loser; }
+ if (value) {
+ block = sftkdb_DupCat(block, sftk_argStrip(value));
+ PORT_Free(value);
+ }
+ if (block == NULL) { goto loser; }
+ block = sftkdb_DupnCat(block, "\n", 1);
+ module = keyEnd + 1 + count;
+ module = sftk_argStrip(module);
+ }
+ if (block) {
+ if (!libFound) {
+ fprintf(fd,"library=\n");
+ }
+ fwrite(block, PORT_Strlen(block), 1, fd);
+ fprintf(fd,"\n");
+ PORT_Free(block);
+ block = NULL;
+ }
+ fclose(fd);
+ return SECSuccess;
+
+loser:
+ PORT_Free(block);
+ fclose(fd);
+ return SECFailure;
+}
+
+
diff --git a/security/nss/lib/softoken/sftkpars.c b/security/nss/lib/softoken/sftkpars.c
new file mode 100644
index 000000000..c170eb3b3
--- /dev/null
+++ b/security/nss/lib/softoken/sftkpars.c
@@ -0,0 +1,644 @@
+/* ***** 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-2007
+ * 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. This file is written to abstract away how the modules are
+ * stored so we can deside that later.
+ */
+#include "sftkpars.h"
+#include "pkcs11i.h"
+#include "sdb.h"
+#include "prprf.h"
+#include "prenv.h"
+
+/*
+ * this file contains routines for parsing PKCS #11 module spec
+ * strings.
+ */
+
+#define SFTK_HANDLE_STRING_ARG(param,target,value,command) \
+ if (PORT_Strncasecmp(param,value,sizeof(value)-1) == 0) { \
+ param += sizeof(value)-1; \
+ target = sftk_argFetchValue(param,&next); \
+ param += next; \
+ command ;\
+ } else
+
+#define SFTK_HANDLE_FINAL_ARG(param) \
+ { param = sftk_argSkipParameter(param); } param = sftk_argStrip(param);
+
+static PRBool sftk_argGetPair(char c) {
+ switch (c) {
+ case '\'': return c;
+ case '\"': return c;
+ case '<': return '>';
+ case '{': return '}';
+ case '[': return ']';
+ case '(': return ')';
+ default: break;
+ }
+ return ' ';
+}
+
+static PRBool sftk_argIsBlank(char c) {
+ return isspace(c);
+}
+
+static PRBool sftk_argIsEscape(char c) {
+ return c == '\\';
+}
+
+static PRBool sftk_argIsQuote(char c) {
+ switch (c) {
+ case '\'':
+ case '\"':
+ case '<':
+ case '{': /* } end curly to keep vi bracket matching working */
+ case '(': /* ) */
+ case '[': /* ] */ return PR_TRUE;
+ default: break;
+ }
+ return PR_FALSE;
+}
+
+char *sftk_argStrip(char *c) {
+ while (*c && sftk_argIsBlank(*c)) c++;
+ return c;
+}
+
+static char *
+sftk_argFindEnd(char *string) {
+ char endChar = ' ';
+ PRBool lastEscape = PR_FALSE;
+
+ if (sftk_argIsQuote(*string)) {
+ endChar = sftk_argGetPair(*string);
+ string++;
+ }
+
+ for (;*string; string++) {
+ if (lastEscape) {
+ lastEscape = PR_FALSE;
+ continue;
+ }
+ if (sftk_argIsEscape(*string) && !lastEscape) {
+ lastEscape = PR_TRUE;
+ continue;
+ }
+ if ((endChar == ' ') && sftk_argIsBlank(*string)) break;
+ if (*string == endChar) {
+ break;
+ }
+ }
+
+ return string;
+}
+
+char *
+sftk_argFetchValue(char *string, int *pcount)
+{
+ char *end = sftk_argFindEnd(string);
+ char *retString, *copyString;
+ PRBool lastEscape = PR_FALSE;
+ int len;
+
+ len = end - string;
+ if (len == 0) {
+ *pcount = 0;
+ return NULL;
+ }
+
+ copyString = retString = (char *)PORT_Alloc(len+1);
+
+ if (*end) len++;
+ *pcount = len;
+ if (retString == NULL) return NULL;
+
+
+ if (sftk_argIsQuote(*string)) string++;
+ for (; string < end; string++) {
+ if (sftk_argIsEscape(*string) && !lastEscape) {
+ lastEscape = PR_TRUE;
+ continue;
+ }
+ lastEscape = PR_FALSE;
+ *copyString++ = *string;
+ }
+ *copyString = 0;
+ return retString;
+}
+
+static char *
+sftk_argSkipParameter(char *string)
+{
+ char *end;
+ /* look for the end of the <name>= */
+ for (;*string; string++) {
+ if (*string == '=') { string++; break; }
+ if (sftk_argIsBlank(*string)) return(string);
+ }
+
+ end = sftk_argFindEnd(string);
+ if (*end) end++;
+ return end;
+}
+
+char *
+sftk_argGetParamValue(char *paramName,char *parameters)
+{
+ char searchValue[256];
+ int paramLen = strlen(paramName);
+ char *returnValue = NULL;
+ int next;
+
+ if ((parameters == NULL) || (*parameters == 0)) return NULL;
+
+ PORT_Assert(paramLen+2 < sizeof(searchValue));
+
+ PORT_Strcpy(searchValue,paramName);
+ PORT_Strcat(searchValue,"=");
+ while (*parameters) {
+ if (PORT_Strncasecmp(parameters,searchValue,paramLen+1) == 0) {
+ parameters += paramLen+1;
+ returnValue = sftk_argFetchValue(parameters,&next);
+ break;
+ } else {
+ parameters = sftk_argSkipParameter(parameters);
+ }
+ parameters = sftk_argStrip(parameters);
+ }
+ return returnValue;
+}
+
+static char *
+sftk_argNextFlag(char *flags)
+{
+ for (; *flags ; flags++) {
+ if (*flags == ',') {
+ flags++;
+ break;
+ }
+ }
+ return flags;
+}
+
+static PRBool
+sftk_argHasFlag(char *label, char *flag, char *parameters)
+{
+ char *flags,*index;
+ int len = strlen(flag);
+ PRBool found = PR_FALSE;
+
+ flags = sftk_argGetParamValue(label,parameters);
+ if (flags == NULL) return PR_FALSE;
+
+ for (index=flags; *index; index=sftk_argNextFlag(index)) {
+ if (PORT_Strncasecmp(index,flag,len) == 0) {
+ found=PR_TRUE;
+ break;
+ }
+ }
+ PORT_Free(flags);
+ return found;
+}
+
+/*
+ * decode a number. handle octal (leading '0'), hex (leading '0x') or decimal
+ */
+static long
+sftk_argDecodeNumber(char *num)
+{
+ int radix = 10;
+ unsigned long value = 0;
+ long retValue = 0;
+ int sign = 1;
+ int digit;
+
+ if (num == NULL) return retValue;
+
+ num = sftk_argStrip(num);
+
+ if (*num == '-') {
+ sign = -1;
+ num++;
+ }
+
+ if (*num == '0') {
+ radix = 8;
+ num++;
+ if ((*num == 'x') || (*num == 'X')) {
+ radix = 16;
+ num++;
+ }
+ }
+
+
+ for ( ;*num; num++ ) {
+ if (isdigit(*num)) {
+ digit = *num - '0';
+ } else if ((*num >= 'a') && (*num <= 'f')) {
+ digit = *num - 'a' + 10;
+ } else if ((*num >= 'A') && (*num <= 'F')) {
+ digit = *num - 'A' + 10;
+ } else {
+ break;
+ }
+ if (digit >= radix) break;
+ value = value*radix + digit;
+ }
+
+ retValue = ((int) value) * sign;
+ return retValue;
+}
+
+static char *
+sftk_argGetName(char *inString, int *next)
+{
+ char *name=NULL;
+ char *string;
+ int len;
+
+ /* look for the end of the <name>= */
+ for (string = inString;*string; string++) {
+ if (*string == '=') { break; }
+ if (sftk_argIsBlank(*string)) break;
+ }
+
+ len = string - inString;
+
+ *next = len;
+ if (*string == '=') (*next) += 1;
+ if (len > 0) {
+ name = PORT_Alloc(len+1);
+ PORT_Strncpy(name,inString,len);
+ name[len] = 0;
+ }
+ return name;
+}
+
+#define FREE_CLEAR(p) if (p) { PORT_Free(p); p = NULL; }
+
+static void
+sftk_parseTokenFlags(char *tmp, sftk_token_parameters *parsed) {
+ parsed->readOnly = sftk_argHasFlag("flags","readOnly",tmp);
+ parsed->noCertDB = sftk_argHasFlag("flags","noCertDB",tmp);
+ parsed->noKeyDB = sftk_argHasFlag("flags","noKeyDB",tmp);
+ parsed->forceOpen = sftk_argHasFlag("flags","forceOpen",tmp);
+ parsed->pwRequired = sftk_argHasFlag("flags","passwordRequired",tmp);
+ parsed->optimizeSpace = sftk_argHasFlag("flags","optimizeSpace",tmp);
+ return;
+}
+
+static void
+sftk_parseFlags(char *tmp, sftk_parameters *parsed) {
+ parsed->noModDB = sftk_argHasFlag("flags","noModDB",tmp);
+ parsed->readOnly = sftk_argHasFlag("flags","readOnly",tmp);
+ /* keep legacy interface working */
+ parsed->noCertDB = sftk_argHasFlag("flags","noCertDB",tmp);
+ parsed->forceOpen = sftk_argHasFlag("flags","forceOpen",tmp);
+ parsed->pwRequired = sftk_argHasFlag("flags","passwordRequired",tmp);
+ parsed->optimizeSpace = sftk_argHasFlag("flags","optimizeSpace",tmp);
+ return;
+}
+
+static CK_RV
+sftk_parseTokenParameters(char *param, sftk_token_parameters *parsed)
+{
+ int next;
+ char *tmp;
+ char *index;
+ index = sftk_argStrip(param);
+
+ while (*index) {
+ SFTK_HANDLE_STRING_ARG(index,parsed->configdir,"configDir=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updatedir,"updateDir=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updCertPrefix,"updateCertPrefix=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updKeyPrefix,"updateKeyPrefix=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updateID,"updateID=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->certPrefix,"certPrefix=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->keyPrefix,"keyPrefix=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->tokdes,"tokenDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updtokdes,
+ "updateTokenDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->slotdes,"slotDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,tmp,"minPWLen=",
+ if(tmp) { parsed->minPW=atoi(tmp); PORT_Free(tmp); })
+ SFTK_HANDLE_STRING_ARG(index,tmp,"flags=",
+ if(tmp) { sftk_parseTokenFlags(param,parsed); PORT_Free(tmp); })
+ SFTK_HANDLE_FINAL_ARG(index)
+ }
+ return CKR_OK;
+}
+
+static void
+sftk_parseTokens(char *tokenParams, sftk_parameters *parsed)
+{
+ char *tokenIndex;
+ sftk_token_parameters *tokens = NULL;
+ int i=0,count = 0,next;
+
+ if ((tokenParams == NULL) || (*tokenParams == 0)) return;
+
+ /* first count the number of slots */
+ for (tokenIndex = sftk_argStrip(tokenParams); *tokenIndex;
+ tokenIndex = sftk_argStrip(sftk_argSkipParameter(tokenIndex))) {
+ count++;
+ }
+
+ /* get the data structures */
+ tokens = (sftk_token_parameters *)
+ PORT_ZAlloc(count*sizeof(sftk_token_parameters));
+ if (tokens == NULL) return;
+
+ for (tokenIndex = sftk_argStrip(tokenParams), i = 0;
+ *tokenIndex && i < count ; i++ ) {
+ char *name;
+ name = sftk_argGetName(tokenIndex,&next);
+ tokenIndex += next;
+
+ tokens[i].slotID = sftk_argDecodeNumber(name);
+ tokens[i].readOnly = PR_FALSE;
+ tokens[i].noCertDB = PR_FALSE;
+ tokens[i].noKeyDB = PR_FALSE;
+ if (!sftk_argIsBlank(*tokenIndex)) {
+ char *args = sftk_argFetchValue(tokenIndex,&next);
+ tokenIndex += next;
+ if (args) {
+ sftk_parseTokenParameters(args,&tokens[i]);
+ PORT_Free(args);
+ }
+ }
+ if (name) PORT_Free(name);
+ tokenIndex = sftk_argStrip(tokenIndex);
+ }
+ parsed->token_count = i;
+ parsed->tokens = tokens;
+ return;
+}
+
+CK_RV
+sftk_parseParameters(char *param, sftk_parameters *parsed, PRBool isFIPS)
+{
+ int next;
+ char *tmp;
+ char *index;
+ char *certPrefix = NULL, *keyPrefix = NULL;
+ char *tokdes = NULL, *ptokdes = NULL, *pupdtokdes = NULL;
+ char *slotdes = NULL, *pslotdes = NULL;
+ char *fslotdes = NULL, *ftokdes = NULL;
+ char *minPW = NULL;
+ index = sftk_argStrip(param);
+
+ PORT_Memset(parsed, 0, sizeof(sftk_parameters));
+
+ while (*index) {
+ SFTK_HANDLE_STRING_ARG(index,parsed->configdir,"configDir=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updatedir,"updateDir=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->updateID,"updateID=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->secmodName,"secmod=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->man,"manufacturerID=",;)
+ SFTK_HANDLE_STRING_ARG(index,parsed->libdes,"libraryDescription=",;)
+ /* constructed values, used so legacy interfaces still work */
+ SFTK_HANDLE_STRING_ARG(index,certPrefix,"certPrefix=",;)
+ SFTK_HANDLE_STRING_ARG(index,keyPrefix,"keyPrefix=",;)
+ SFTK_HANDLE_STRING_ARG(index,tokdes,"cryptoTokenDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,ptokdes,"dbTokenDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,slotdes,"cryptoSlotDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,pslotdes,"dbSlotDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,fslotdes,"FIPSSlotDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,ftokdes,"FIPSTokenDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,pupdtokdes, "updateTokenDescription=",;)
+ SFTK_HANDLE_STRING_ARG(index,minPW,"minPWLen=",;)
+
+ SFTK_HANDLE_STRING_ARG(index,tmp,"flags=",
+ if(tmp) { sftk_parseFlags(param,parsed); PORT_Free(tmp); })
+ SFTK_HANDLE_STRING_ARG(index,tmp,"tokens=",
+ if(tmp) { sftk_parseTokens(tmp,parsed); PORT_Free(tmp); })
+ SFTK_HANDLE_FINAL_ARG(index)
+ }
+ if (parsed->tokens == NULL) {
+ int count = isFIPS ? 1 : 2;
+ int index = count-1;
+ sftk_token_parameters *tokens = NULL;
+
+ tokens = (sftk_token_parameters *)
+ PORT_ZAlloc(count*sizeof(sftk_token_parameters));
+ if (tokens == NULL) {
+ goto loser;
+ }
+ parsed->tokens = tokens;
+ parsed->token_count = count;
+ tokens[index].slotID = isFIPS ? FIPS_SLOT_ID : PRIVATE_KEY_SLOT_ID;
+ tokens[index].certPrefix = certPrefix;
+ tokens[index].keyPrefix = keyPrefix;
+ tokens[index].minPW = minPW ? atoi(minPW) : 0;
+ tokens[index].readOnly = parsed->readOnly;
+ tokens[index].noCertDB = parsed->noCertDB;
+ tokens[index].noKeyDB = parsed->noCertDB;
+ tokens[index].forceOpen = parsed->forceOpen;
+ tokens[index].pwRequired = parsed->pwRequired;
+ tokens[index].optimizeSpace = parsed->optimizeSpace;
+ tokens[0].optimizeSpace = parsed->optimizeSpace;
+ certPrefix = NULL;
+ keyPrefix = NULL;
+ if (isFIPS) {
+ tokens[index].tokdes = ftokdes;
+ tokens[index].updtokdes = pupdtokdes;
+ tokens[index].slotdes = fslotdes;
+ fslotdes = NULL;
+ ftokdes = NULL;
+ pupdtokdes = NULL;
+ } else {
+ tokens[index].tokdes = ptokdes;
+ tokens[index].updtokdes = pupdtokdes;
+ tokens[index].slotdes = pslotdes;
+ tokens[0].slotID = NETSCAPE_SLOT_ID;
+ tokens[0].tokdes = tokdes;
+ tokens[0].slotdes = slotdes;
+ tokens[0].noCertDB = PR_TRUE;
+ tokens[0].noKeyDB = PR_TRUE;
+ pupdtokdes = NULL;
+ ptokdes = NULL;
+ pslotdes = NULL;
+ tokdes = NULL;
+ slotdes = NULL;
+ }
+ }
+
+loser:
+ FREE_CLEAR(certPrefix);
+ FREE_CLEAR(keyPrefix);
+ FREE_CLEAR(tokdes);
+ FREE_CLEAR(ptokdes);
+ FREE_CLEAR(pupdtokdes);
+ FREE_CLEAR(slotdes);
+ FREE_CLEAR(pslotdes);
+ FREE_CLEAR(fslotdes);
+ FREE_CLEAR(ftokdes);
+ FREE_CLEAR(minPW);
+ return CKR_OK;
+}
+
+void
+sftk_freeParams(sftk_parameters *params)
+{
+ int i;
+
+ for (i=0; i < params->token_count; i++) {
+ FREE_CLEAR(params->tokens[i].configdir);
+ FREE_CLEAR(params->tokens[i].certPrefix);
+ FREE_CLEAR(params->tokens[i].keyPrefix);
+ FREE_CLEAR(params->tokens[i].tokdes);
+ FREE_CLEAR(params->tokens[i].slotdes);
+ FREE_CLEAR(params->tokens[i].updatedir);
+ FREE_CLEAR(params->tokens[i].updCertPrefix);
+ FREE_CLEAR(params->tokens[i].updKeyPrefix);
+ FREE_CLEAR(params->tokens[i].updateID);
+ FREE_CLEAR(params->tokens[i].updtokdes);
+ }
+
+ FREE_CLEAR(params->configdir);
+ FREE_CLEAR(params->secmodName);
+ FREE_CLEAR(params->man);
+ FREE_CLEAR(params->libdes);
+ FREE_CLEAR(params->tokens);
+ FREE_CLEAR(params->updatedir);
+ FREE_CLEAR(params->updateID);
+}
+
+#define SQLDB "sql:"
+#define EXTERNDB "extern:"
+#define LEGACY "dbm:"
+const char *
+sftk_EvaluateConfigDir(const char *configdir, SDBType *dbType, char **appName)
+{
+ *appName = NULL;
+ *dbType = SDB_LEGACY;
+ if (PORT_Strncmp(configdir, MULTIACCESS, sizeof(MULTIACCESS)-1) == 0) {
+ char *cdir;
+ *dbType = SDB_MULTIACCESS;
+
+ *appName = PORT_Strdup(configdir+sizeof(MULTIACCESS)-1);
+ if (*appName == NULL) {
+ return configdir;
+ }
+ cdir = *appName;
+ while (*cdir && *cdir != ':') {
+ cdir++;
+ }
+ if (*cdir == ':') {
+ *cdir = 0;
+ cdir++;
+ }
+ configdir = cdir;
+ } else if (PORT_Strncmp(configdir, SQLDB, sizeof(SQLDB)-1) == 0) {
+ *dbType = SDB_SQL;
+ configdir = configdir + sizeof(SQLDB) -1;
+ } else if (PORT_Strncmp(configdir, EXTERNDB, sizeof(EXTERNDB)-1) == 0) {
+ *dbType = SDB_EXTERN;
+ configdir = configdir + sizeof(EXTERNDB) -1;
+ } else if (PORT_Strncmp(configdir, LEGACY, sizeof(LEGACY)-1) == 0) {
+ *dbType = SDB_LEGACY;
+ configdir = configdir + sizeof(LEGACY) -1;
+ } else {
+ /* look up the default from the environment */
+ char *defaultType = PR_GetEnv("NSS_DEFAULT_DB_TYPE");
+ if (defaultType == NULL) {
+ /* none specified, go with the legacy */
+ return configdir;
+ }
+ if (PORT_Strncmp(defaultType, SQLDB, sizeof(SQLDB)-2) == 0) {
+ *dbType = SDB_SQL;
+ } else if (PORT_Strncmp(defaultType,EXTERNDB,sizeof(EXTERNDB)-2)==0) {
+ *dbType = SDB_EXTERN;
+ } else if (PORT_Strncmp(defaultType, LEGACY, sizeof(LEGACY)-2) == 0) {
+ *dbType = SDB_LEGACY;
+ }
+ }
+ return configdir;
+}
+
+char *
+sftk_getSecmodName(char *param, SDBType *dbType, char **appName,
+ char **filename, PRBool *rw)
+{
+ int next;
+ char *configdir = NULL;
+ char *secmodName = NULL;
+ char *value = NULL;
+ char *save_params = param;
+ const char *lconfigdir;
+ param = sftk_argStrip(param);
+
+
+ while (*param) {
+ SFTK_HANDLE_STRING_ARG(param,configdir,"configDir=",;)
+ SFTK_HANDLE_STRING_ARG(param,secmodName,"secmod=",;)
+ SFTK_HANDLE_FINAL_ARG(param)
+ }
+
+ *rw = PR_TRUE;
+ if (sftk_argHasFlag("flags","readOnly",save_params)) {
+ *rw = PR_FALSE;
+ }
+
+ if (!secmodName || *secmodName == '\0') {
+ if (secmodName) PORT_Free(secmodName);
+ secmodName = PORT_Strdup(SECMOD_DB);
+ }
+
+ *filename = secmodName;
+ lconfigdir = sftk_EvaluateConfigDir(configdir, dbType, appName);
+
+ if (sftk_argHasFlag("flags","noModDB",save_params)) {
+ /* there isn't a module db, don't load the legacy support */
+ *dbType = SDB_SQL;
+ *rw = PR_FALSE;
+ }
+
+ /* only use the renamed secmod for legacy databases */
+ if ((*dbType != SDB_LEGACY) && (*dbType != SDB_MULTIACCESS)) {
+ secmodName="pkcs11.txt";
+ }
+
+ if (lconfigdir) {
+ value = PR_smprintf("%s" PATH_SEPARATOR "%s",lconfigdir,secmodName);
+ } else {
+ value = PR_smprintf("%s",secmodName);
+ }
+ if (configdir) PORT_Free(configdir);
+ return value;
+}
diff --git a/security/nss/lib/softoken/sftkpars.h b/security/nss/lib/softoken/sftkpars.h
new file mode 100644
index 000000000..0086edfba
--- /dev/null
+++ b/security/nss/lib/softoken/sftkpars.h
@@ -0,0 +1,49 @@
+/* ***** 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
+ * Red Hat Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * 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 ***** */
+#include "pkcs11i.h"
+#include "sftkdbt.h"
+
+/* parsing functions */
+char * sftk_argFetchValue(char *string, int *pcount);
+char * sftk_getSecmodName(char *param, SDBType *dbType, char **appName, char **filename,PRBool *rw);
+char *sftk_argStrip(char *c);
+CK_RV sftk_parseParameters(char *param, sftk_parameters *parsed, PRBool isFIPS);
+void sftk_freeParams(sftk_parameters *params);
+const char *sftk_EvaluateConfigDir(const char *configdir, SDBType *dbType, char **app);
+char * sftk_argGetParamValue(char *paramName,char *parameters);
+
+
+
diff --git a/security/nss/lib/softoken/sftkpwd.c b/security/nss/lib/softoken/sftkpwd.c
new file mode 100644
index 000000000..7198596a6
--- /dev/null
+++ b/security/nss/lib/softoken/sftkpwd.c
@@ -0,0 +1,1299 @@
+/* ***** 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-2007
+ * 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 ***** */
+/*
+ * The following code handles the storage of PKCS 11 modules used by the
+ * NSS. For the rest of NSS, only one kind of database handle exists:
+ *
+ * SFTKDBHandle
+ *
+ * There is one SFTKDBHandle for the each key database and one for each cert
+ * database. These databases are opened as associated pairs, one pair per
+ * slot. SFTKDBHandles are reference counted objects.
+ *
+ * Each SFTKDBHandle points to a low level database handle (SDB). This handle
+ * represents the underlying physical database. These objects are not
+ * reference counted, an are 'owned' by their respective SFTKDBHandles.
+ *
+ *
+ */
+#include "sftkdb.h"
+#include "sftkdbti.h"
+#include "pkcs11t.h"
+#include "pkcs11i.h"
+#include "sdb.h"
+#include "prprf.h"
+#include "secmodt.h"
+#include "sftkpars.h"
+#include "pratom.h"
+#include "blapi.h"
+#include "secoid.h"
+#include "sechash.h"
+#include "lowpbe.h"
+#include "secdert.h"
+#include "prsystem.h"
+#include "lgglue.h"
+#include "secerr.h"
+
+
+/******************************************************************
+ *
+ * Key DB password handling functions
+ *
+ * These functions manage the key db password (set, reset, initialize, use).
+ *
+ * The key is managed on 'this side' of the database. All private data is
+ * encrypted before it is sent to the database itself. Besides PBE's, the
+ * database management code can also mix in various fixed keys so the data
+ * in the database is no longer considered 'plain text'.
+ */
+
+
+/* take string password and turn it into a key. The key is dependent
+ * on a global salt entry acquired from the database. This salted
+ * value will be based to a pkcs5 pbe function before it is used
+ * in an actual encryption */
+static SECStatus
+sftkdb_passwordToKey(SFTKDBHandle *keydb, SECItem *salt,
+ const char *pw, SECItem *key)
+{
+ SHA1Context *cx = NULL;
+ SECStatus rv = SECFailure;
+
+ key->data = PORT_Alloc(SHA1_LENGTH);
+ if (key->data == NULL) {
+ goto loser;
+ }
+ key->len = SHA1_LENGTH;
+
+ cx = SHA1_NewContext();
+ if ( cx == NULL) {
+ goto loser;
+ }
+ SHA1_Begin(cx);
+ if (salt && salt->data ) {
+ SHA1_Update(cx, salt->data, salt->len);
+ }
+ SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw));
+ SHA1_End(cx, key->data, &key->len, key->len);
+ rv = SECSuccess;
+
+loser:
+ if (cx) {
+ SHA1_DestroyContext(cx, PR_TRUE);
+ }
+ if (rv != SECSuccess) {
+ if (key->data != NULL) {
+ PORT_ZFree(key->data,key->len);
+ }
+ key->data = NULL;
+ }
+ return rv;
+}
+
+/*
+ * Cipher text stored in the database contains 3 elements:
+ * 1) an identifier describing the encryption algorithm.
+ * 2) an entry specific salt value.
+ * 3) the encrypted value.
+ *
+ * The following data structure represents the encrypted data in a decoded
+ * (but still encrypted) form.
+ */
+typedef struct sftkCipherValueStr sftkCipherValue;
+struct sftkCipherValueStr {
+ PLArenaPool *arena;
+ SECOidTag alg;
+ NSSPKCS5PBEParameter *param;
+ SECItem salt;
+ SECItem value;
+};
+
+#define SFTK_CIPHERTEXT_VERSION 3
+
+struct SFTKDBEncryptedDataInfoStr {
+ SECAlgorithmID algorithm;
+ SECItem encryptedData;
+};
+typedef struct SFTKDBEncryptedDataInfoStr SFTKDBEncryptedDataInfo;
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+
+const SEC_ASN1Template sftkdb_EncryptedDataInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(SFTKDBEncryptedDataInfo) },
+ { SEC_ASN1_INLINE | SEC_ASN1_XTRN ,
+ offsetof(SFTKDBEncryptedDataInfo,algorithm),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OCTET_STRING,
+ offsetof(SFTKDBEncryptedDataInfo,encryptedData) },
+ { 0 }
+};
+
+/*
+ * This parses the cipherText into cipher value. NOTE: cipherValue will point
+ * to data in cipherText, if cipherText is freed, cipherValue will be invalid.
+ */
+static SECStatus
+sftkdb_decodeCipherText(SECItem *cipherText, sftkCipherValue *cipherValue)
+{
+ PLArenaPool *arena = NULL;
+ SFTKDBEncryptedDataInfo edi;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) {
+ return SECFailure;
+ }
+ cipherValue->arena = NULL;
+ cipherValue->param = NULL;
+
+ rv = SEC_QuickDERDecodeItem(arena, &edi, sftkdb_EncryptedDataInfoTemplate,
+ cipherText);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ cipherValue->alg = SECOID_GetAlgorithmTag(&edi.algorithm);
+ cipherValue->param = nsspkcs5_AlgidToParam(&edi.algorithm);
+ if (cipherValue->param == NULL) {
+ goto loser;
+ }
+ cipherValue->value = edi.encryptedData;
+ cipherValue->arena = arena;
+
+ return SECSuccess;
+loser:
+ if (cipherValue->param) {
+ nsspkcs5_DestroyPBEParameter(cipherValue->param);
+ cipherValue->param = NULL;
+ }
+ if (arena) {
+ PORT_FreeArena(arena,PR_FALSE);
+ }
+ return SECFailure;
+}
+
+
+
+/*
+ * unlike decode, Encode actually allocates a SECItem the caller must free
+ * The caller can pass an optional arena to to indicate where to place
+ * the resultant cipherText.
+ */
+static SECStatus
+sftkdb_encodeCipherText(PLArenaPool *arena, sftkCipherValue *cipherValue,
+ SECItem **cipherText)
+{
+ SFTKDBEncryptedDataInfo edi;
+ SECAlgorithmID *algid;
+ SECStatus rv;
+ PLArenaPool *localArena = NULL;
+
+
+ localArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (localArena == NULL) {
+ return SECFailure;
+ }
+
+ algid = nsspkcs5_CreateAlgorithmID(localArena, cipherValue->alg,
+ cipherValue->param);
+ if (algid == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ rv = SECOID_CopyAlgorithmID(localArena, &edi.algorithm, algid);
+ SECOID_DestroyAlgorithmID(algid, PR_TRUE);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ edi.encryptedData = cipherValue->value;
+
+ *cipherText = SEC_ASN1EncodeItem(arena, NULL, &edi,
+ sftkdb_EncryptedDataInfoTemplate);
+ if (*cipherText == NULL) {
+ rv = SECFailure;
+ }
+
+loser:
+ if (localArena) {
+ PORT_FreeArena(localArena,PR_FALSE);
+ }
+
+ return rv;
+}
+
+
+/*
+ * Use our key to decode a cipherText block from the database.
+ *
+ * plain text is allocated by nsspkcs5_CipherData and must be freed
+ * with SECITEM_FreeItem by the caller.
+ */
+SECStatus
+sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plain)
+{
+ SECStatus rv;
+ sftkCipherValue cipherValue;
+
+ /* First get the cipher type */
+ rv = sftkdb_decodeCipherText(cipherText, &cipherValue);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value,
+ PR_FALSE, NULL);
+ if (*plain == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+loser:
+ if (cipherValue.param) {
+ nsspkcs5_DestroyPBEParameter(cipherValue.param);
+ }
+ if (cipherValue.arena) {
+ PORT_FreeArena(cipherValue.arena,PR_FALSE);
+ }
+ return rv;
+}
+
+/*
+ * encrypt a block. This function returned the encrypted ciphertext which
+ * the caller must free. If the caller provides an arena, cipherText will
+ * be allocated out of that arena. This also generated the per entry
+ * salt automatically.
+ */
+SECStatus
+sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey,
+ SECItem *plainText, SECItem **cipherText)
+{
+ SECStatus rv;
+ sftkCipherValue cipherValue;
+ SECItem *cipher = NULL;
+ NSSPKCS5PBEParameter *param = NULL;
+ unsigned char saltData[HASH_LENGTH_MAX];
+
+ cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
+ cipherValue.salt.len = SHA1_LENGTH;
+ cipherValue.salt.data = saltData;
+ RNG_GenerateGlobalRandomBytes(saltData,cipherValue.salt.len);
+
+ param = nsspkcs5_NewParam(cipherValue.alg, &cipherValue.salt, 1);
+ if (param == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL);
+ if (cipher == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ cipherValue.value = *cipher;
+ cipherValue.param = param;
+
+ rv = sftkdb_encodeCipherText(arena, &cipherValue, cipherText);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+loser:
+ if (cipher) {
+ SECITEM_FreeItem(cipher, PR_TRUE);
+ }
+ if (param) {
+ nsspkcs5_DestroyPBEParameter(param);
+ }
+ return rv;
+}
+
+/*
+ * use the password and the pbe parameters to generate an HMAC for the
+ * given plain text data. This is used by sftkdb_VerifyAttribute and
+ * sftkdb_SignAttribute. Signature is returned in signData. The caller
+ * must preallocate the space in the secitem.
+ */
+static SECStatus
+sftkdb_pbehash(SECOidTag sigOid, SECItem *passKey,
+ NSSPKCS5PBEParameter *param,
+ CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType,
+ SECItem *plainText, SECItem *signData)
+{
+ SECStatus rv = SECFailure;
+ SECItem *key = NULL;
+ HMACContext *hashCx = NULL;
+ HASH_HashType hashType = HASH_AlgNULL;
+ const SECHashObject *hashObj;
+ unsigned char addressData[SDB_ULONG_SIZE];
+
+ hashType = HASH_FromHMACOid(param->encAlg);
+ if (hashType == HASH_AlgNULL) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return SECFailure;
+ }
+
+ hashObj = HASH_GetRawHashObject(hashType);
+ if (hashObj == NULL) {
+ goto loser;
+ }
+
+ key = nsspkcs5_ComputeKeyAndIV(param, passKey, NULL, PR_FALSE);
+ if (!key) {
+ goto loser;
+ }
+
+ hashCx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE);
+ if (!hashCx) {
+ goto loser;
+ }
+ HMAC_Begin(hashCx);
+ /* Tie this value to a particular object. This is most important for
+ * the trust attributes, where and attacker could copy a value for
+ * 'validCA' from another cert in the database */
+ sftk_ULong2SDBULong(addressData, objectID);
+ HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE);
+ sftk_ULong2SDBULong(addressData, attrType);
+ HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE);
+
+ HMAC_Update(hashCx, plainText->data, plainText->len);
+ rv = HMAC_Finish(hashCx, signData->data, &signData->len, signData->len);
+
+loser:
+ if (hashCx) {
+ HMAC_Destroy(hashCx, PR_TRUE);
+ }
+ if (key) {
+ SECITEM_FreeItem(key,PR_TRUE);
+ }
+ return rv;
+}
+
+/*
+ * Use our key to verify a signText block from the database matches
+ * the plainText from the database. The signText is a PKCS 5 v2 pbe.
+ * plainText is the plainText of the attribute.
+ */
+SECStatus
+sftkdb_VerifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID,
+ CK_ATTRIBUTE_TYPE attrType,
+ SECItem *plainText, SECItem *signText)
+{
+ SECStatus rv;
+ sftkCipherValue signValue;
+ SECItem signature;
+ unsigned char signData[HASH_LENGTH_MAX];
+
+
+ /* First get the cipher type */
+ rv = sftkdb_decodeCipherText(signText, &signValue);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ signature.data = signData;
+ signature.len = sizeof(signData);
+
+ rv = sftkdb_pbehash(signValue.alg, passKey, signValue.param,
+ objectID, attrType, plainText, &signature);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (SECITEM_CompareItem(&signValue.value,&signature) != 0) {
+ PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
+ rv = SECFailure;
+ }
+
+loser:
+ if (signValue.param) {
+ nsspkcs5_DestroyPBEParameter(signValue.param);
+ }
+ if (signValue.arena) {
+ PORT_FreeArena(signValue.arena,PR_FALSE);
+ }
+ return rv;
+}
+
+/*
+ * Use our key to create a signText block the plain text of an
+ * attribute. The signText is a PKCS 5 v2 pbe.
+ */
+SECStatus
+sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey,
+ CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType,
+ SECItem *plainText, SECItem **signature)
+{
+ SECStatus rv;
+ sftkCipherValue signValue;
+ NSSPKCS5PBEParameter *param = NULL;
+ unsigned char saltData[HASH_LENGTH_MAX];
+ unsigned char signData[HASH_LENGTH_MAX];
+ SECOidTag hmacAlg = SEC_OID_HMAC_SHA256; /* hash for authentication */
+ SECOidTag prfAlg = SEC_OID_HMAC_SHA256; /* hash for pb key generation */
+ HASH_HashType prfType;
+ unsigned int hmacLength;
+ unsigned int prfLength;
+
+ /* this code allows us to fetch the lengths and hashes on the fly
+ * by simply changing the OID above */
+ prfType = HASH_FromHMACOid(prfAlg);
+ PORT_Assert(prfType != HASH_AlgNULL);
+ prfLength = HASH_GetRawHashObject(prfType)->length;
+ PORT_Assert(prfLength <= HASH_LENGTH_MAX);
+
+ hmacLength = HASH_GetRawHashObject(HASH_FromHMACOid(hmacAlg))->length;
+ PORT_Assert(hmacLength <= HASH_LENGTH_MAX);
+
+ /* initialize our CipherValue structure */
+ signValue.alg = SEC_OID_PKCS5_PBMAC1;
+ signValue.salt.len = prfLength;
+ signValue.salt.data = saltData;
+ signValue.value.data = signData;
+ signValue.value.len = hmacLength;
+ RNG_GenerateGlobalRandomBytes(saltData,prfLength);
+
+ /* initialize our pkcs5 paramter */
+ param = nsspkcs5_NewParam(signValue.alg, &signValue.salt, 1);
+ if (param == NULL) {
+ rv = SECFailure;
+ goto loser;
+ }
+ param->keyID = pbeBitGenIntegrityKey;
+ /* set the PKCS 5 v2 parameters, not extractable from the
+ * data passed into nsspkcs5_NewParam */
+ param->encAlg = hmacAlg;
+ param->hashType = prfType;
+ param->keyLen = hmacLength;
+ rv = SECOID_SetAlgorithmID(param->poolp, &param->prfAlg, prfAlg, NULL);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+
+ /* calculate the mac */
+ rv = sftkdb_pbehash(signValue.alg, passKey, param, objectID, attrType,
+ plainText, &signValue.value);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ signValue.param = param;
+
+ /* write it out */
+ rv = sftkdb_encodeCipherText(arena, &signValue, signature);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+loser:
+ if (param) {
+ nsspkcs5_DestroyPBEParameter(param);
+ }
+ return rv;
+}
+
+/*
+ * safely swith the passed in key for the one caches in the keydb handle
+ *
+ * A key attached to the handle tells us the the token is logged in.
+ * We can used the key attached to the handle in sftkdb_EncryptAttribute
+ * and sftkdb_DecryptAttribute calls.
+ */
+static void
+sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey)
+{
+ unsigned char *data;
+ int len;
+
+ if (keydb->passwordLock == NULL) {
+ PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
+ return;
+ }
+
+ /* an atomic pointer set would be nice */
+ PZ_Lock(keydb->passwordLock);
+ data = keydb->passwordKey.data;
+ len = keydb->passwordKey.len;
+ keydb->passwordKey.data = passKey->data;
+ keydb->passwordKey.len = passKey->len;
+ passKey->data = data;
+ passKey->len = len;
+ PZ_Unlock(keydb->passwordLock);
+}
+
+/*
+ * returns true if we are in a middle of a merge style update.
+ */
+PRBool
+sftkdb_InUpdateMerge(SFTKDBHandle *keydb)
+{
+ return keydb->updateID ? PR_TRUE : PR_FALSE;
+}
+
+/*
+ * returns true if we are looking for the password for the user's old source
+ * database as part of a merge style update.
+ */
+PRBool
+sftkdb_NeedUpdateDBPassword(SFTKDBHandle *keydb)
+{
+ if (!sftkdb_InUpdateMerge(keydb)) {
+ return PR_FALSE;
+ }
+ if (keydb->updateDBIsInit && !keydb->updatePasswordKey) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+/*
+ * fetch an update password key from a handle.
+ */
+SECItem *
+sftkdb_GetUpdatePasswordKey(SFTKDBHandle *handle)
+{
+ SECItem *key = NULL;
+
+ /* if we're a cert db, fetch it from our peer key db */
+ if (handle->type == SFTK_CERTDB_TYPE) {
+ handle = handle->peerDB;
+ }
+
+ /* don't have one */
+ if (!handle) {
+ return NULL;
+ }
+
+ PZ_Lock(handle->passwordLock);
+ if (handle->updatePasswordKey) {
+ key = SECITEM_DupItem(handle->updatePasswordKey);
+ }
+ PZ_Unlock(handle->passwordLock);
+
+ return key;
+}
+
+/*
+ * free the update password key from a handle.
+ */
+void
+sftkdb_FreeUpdatePasswordKey(SFTKDBHandle *handle)
+{
+ SECItem *key = NULL;
+
+ /* if we're a cert db, we don't have one */
+ if (handle->type == SFTK_CERTDB_TYPE) {
+ return;
+ }
+
+ /* don't have one */
+ if (!handle) {
+ return;
+ }
+
+ PZ_Lock(handle->passwordLock);
+ if (handle->updatePasswordKey) {
+ key = handle->updatePasswordKey;
+ handle->updatePasswordKey = NULL;
+ }
+ PZ_Unlock(handle->passwordLock);
+
+ if (key) {
+ SECITEM_ZfreeItem(key, PR_TRUE);
+ }
+
+ return;
+}
+
+/*
+ * what password db we use depends heavily on the update state machine
+ *
+ * 1) no update db, return the normal database.
+ * 2) update db and no merge return the update db.
+ * 3) update db and in merge:
+ * return the update db if we need the update db's password,
+ * otherwise return our normal datbase.
+ */
+static SDB *
+sftk_getPWSDB(SFTKDBHandle *keydb)
+{
+ if (!keydb->update) {
+ return keydb->db;
+ }
+ if (!sftkdb_InUpdateMerge(keydb)) {
+ return keydb->update;
+ }
+ if (sftkdb_NeedUpdateDBPassword(keydb)) {
+ return keydb->update;
+ }
+ return keydb->db;
+}
+
+/*
+ * return success if we have a valid password entry.
+ * This is will show up outside of PKCS #11 as CKF_USER_PIN_INIT
+ * in the token flags.
+ */
+SECStatus
+sftkdb_HasPasswordSet(SFTKDBHandle *keydb)
+{
+ SECItem salt, value;
+ unsigned char saltData[SDB_MAX_META_DATA_LEN];
+ unsigned char valueData[SDB_MAX_META_DATA_LEN];
+ CK_RV crv;
+ SDB *db;
+
+ if (keydb == NULL) {
+ return SECFailure;
+ }
+
+ db = sftk_getPWSDB(keydb);
+ if (db == NULL) {
+ return SECFailure;
+ }
+
+ salt.data = saltData;
+ salt.len = sizeof(saltData);
+ value.data = valueData;
+ value.len = sizeof(valueData);
+ crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
+ return (crv == CKR_OK) ? SECSuccess : SECFailure;
+}
+
+#define SFTK_PW_CHECK_STRING "password-check"
+#define SFTK_PW_CHECK_LEN 14
+
+/*
+ * check if the supplied password is valid
+ */
+SECStatus
+sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw, PRBool *tokenRemoved)
+{
+ SECStatus rv;
+ SECItem salt, value;
+ unsigned char saltData[SDB_MAX_META_DATA_LEN];
+ unsigned char valueData[SDB_MAX_META_DATA_LEN];
+ SECItem key;
+ SECItem *result = NULL;
+ SDB *db;
+ CK_RV crv;
+
+ if (keydb == NULL) {
+ return SECFailure;
+ }
+
+ db = sftk_getPWSDB(keydb);
+ if (db == NULL) {
+ return SECFailure;
+ }
+
+ key.data = NULL;
+ key.len = 0;
+
+ if (pw == NULL) pw="";
+
+ /* get the entry from the database */
+ salt.data = saltData;
+ salt.len = sizeof(saltData);
+ value.data = valueData;
+ value.len = sizeof(valueData);
+ crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* get our intermediate key based on the entry salt value */
+ rv = sftkdb_passwordToKey(keydb, &salt, pw, &key);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* decrypt the entry value */
+ rv = sftkdb_DecryptAttribute(&key, &value, &result);
+ if (rv != SECSuccess) {
+ goto done;
+ }
+
+ /* if it's what we expect, update our key in the database handle and
+ * return Success */
+ if ((result->len == SFTK_PW_CHECK_LEN) &&
+ PORT_Memcmp(result->data, SFTK_PW_CHECK_STRING, SFTK_PW_CHECK_LEN) == 0){
+ /*
+ * We have a password, now lets handle any potential update cases..
+ *
+ * First, the normal case: no update. In this case we only need the
+ * the password for our only DB, which we now have, we switch
+ * the keys and fall through.
+ * Second regular (non-merge) update: The target DB does not yet have
+ * a password initialized, we now have the password for the source DB,
+ * so we can switch the keys and simply update the target database.
+ * Merge update case: This one is trickier.
+ * 1) If we need the source DB password, then we just got it here.
+ * We need to save that password,
+ * then we need to check to see if we need or have the target
+ * database password.
+ * If we have it (it's the same as the source), or don't need
+ * it (it's not set or is ""), we can start the update now.
+ * If we don't have it, we need the application to get it from
+ * the user. Clear our sessions out to simulate a token
+ * removal. C_GetTokenInfo will change the token description
+ * and the token will still appear to be logged out.
+ * 2) If we already have the source DB password, this password is
+ * for the target database. We can now move forward with the
+ * update, as we now have both required passwords.
+ *
+ */
+ PZ_Lock(keydb->passwordLock);
+ if (sftkdb_NeedUpdateDBPassword(keydb)) {
+ /* Squirrel this special key away.
+ * This has the side effect of turning sftkdb_NeedLegacyPW off,
+ * as well as changing which database is returned from
+ * SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword()
+ * and sftkdb_HasPasswordSet()) */
+ keydb->updatePasswordKey = SECITEM_DupItem(&key);
+ PZ_Unlock(keydb->passwordLock);
+ if (keydb->updatePasswordKey == NULL) {
+ /* PORT_Error set by SECITEM_DupItem */
+ rv = SECFailure;
+ goto done;
+ }
+
+ /* Simulate a token removal -- we need to do this any
+ * any case at this point so the token name is correct. */
+ *tokenRemoved = PR_TRUE;
+
+ /*
+ * OK, we got the update DB password, see if we need a password
+ * for the target...
+ */
+ if (sftkdb_HasPasswordSet(keydb) == SECSuccess) {
+ /* We have a password, do we know what the password is?
+ * check 1) for the password the user supplied for the
+ * update DB,
+ * and 2) for the null password.
+ *
+ * RECURSION NOTE: we are calling ourselves here. This means
+ * any updates, switchKeys, etc will have been completed
+ * if these functions return successfully, in those cases
+ * just exit returning Success. We don't recurse infinitely
+ * because we are making this call from a NeedUpdateDBPassword
+ * block and we've already set that update password at this
+ * point. */
+ rv = sftkdb_CheckPassword(keydb, pw, tokenRemoved);
+ if (rv == SECSuccess) {
+ /* source and target databases have the same password, we
+ * are good to go */
+ goto done;
+ }
+ sftkdb_CheckPassword(keydb, "", tokenRemoved);
+
+ /*
+ * Important 'NULL' code here. At this point either we
+ * succeeded in logging in with "" or we didn't.
+ *
+ * If we did succeed at login, our machine state will be set
+ * to logged in appropriately. The application will find that
+ * it's logged in as soon as it opens a new session. We have
+ * also completed the update. Life is good.
+ *
+ * If we did not succeed, well the user still successfully
+ * logged into the update database, since we faked the token
+ * removal it's just like the user logged into his smart card
+ * then removed it. the actual login work, so we report that
+ * success back to the user, but we won't actually be
+ * logged in. The application will find this out when it
+ * checks it's login state, thus triggering another password
+ * prompt so we can get the real target DB password.
+ *
+ * summary, we exit from here with SECSuccess no matter what.
+ */
+ rv = SECSuccess;
+ goto done;
+ } else {
+ /* there is no password, just fall through to update.
+ * update will write the source DB's password record
+ * into the target DB just like it would in a non-merge
+ * update case. */
+ }
+ } else {
+ PZ_Unlock(keydb->passwordLock);
+ }
+ /* load the keys, so the keydb can parse it's key set */
+ sftkdb_switchKeys(keydb, &key);
+
+ /* we need to update, do it now */
+ if (keydb->update) {
+ /* update the peer certdb if it exists */
+ if (keydb->peerDB) {
+ sftkdb_Update(keydb->peerDB, &key);
+ }
+ sftkdb_Update(keydb, &key);
+ }
+ } else {
+ rv = SECFailure;
+ /*PORT_SetError( bad password); */
+ }
+
+done:
+ if (key.data) {
+ PORT_ZFree(key.data,key.len);
+ }
+ if (result) {
+ SECITEM_FreeItem(result,PR_TRUE);
+ }
+ return rv;
+}
+
+/*
+ * return Success if the there is a cached password key.
+ */
+SECStatus
+sftkdb_PWCached(SFTKDBHandle *keydb)
+{
+ return keydb->passwordKey.data ? SECSuccess : SECFailure;
+}
+
+
+static CK_RV
+sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle,
+ CK_OBJECT_HANDLE id, SECItem *newKey)
+{
+ CK_RV crv = CKR_OK;
+ CK_RV crv2;
+ CK_ATTRIBUTE authAttrs[] = {
+ {CKA_MODULUS, NULL, 0},
+ {CKA_PUBLIC_EXPONENT, NULL, 0},
+ {CKA_CERT_SHA1_HASH, NULL, 0},
+ {CKA_CERT_MD5_HASH, NULL, 0},
+ {CKA_TRUST_SERVER_AUTH, NULL, 0},
+ {CKA_TRUST_CLIENT_AUTH, NULL, 0},
+ {CKA_TRUST_EMAIL_PROTECTION, NULL, 0},
+ {CKA_TRUST_CODE_SIGNING, NULL, 0},
+ {CKA_TRUST_STEP_UP_APPROVED, NULL, 0},
+ {CKA_NSS_OVERRIDE_EXTENSIONS, NULL, 0},
+ };
+ CK_ULONG authAttrCount = sizeof(authAttrs)/sizeof(CK_ATTRIBUTE);
+ int i, count;
+ SFTKDBHandle *keyHandle = handle;
+ SDB *keyTarget = NULL;
+
+ id &= SFTK_OBJ_ID_MASK;
+
+ if (handle->type != SFTK_KEYDB_TYPE) {
+ keyHandle = handle->peerDB;
+ }
+
+ if (keyHandle == NULL) {
+ return CKR_OK;
+ }
+
+ /* old DB's don't have meta data, finished with MACs */
+ keyTarget = SFTK_GET_SDB(keyHandle);
+ if ((keyTarget->sdb_flags &SDB_HAS_META) == 0) {
+ return CKR_OK;
+ }
+
+ /*
+ * STEP 1: find the MACed attributes of this object
+ */
+ crv2 = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount);
+ count = 0;
+ /* allocate space for the attributes */
+ for (i=0; i < authAttrCount; i++) {
+ if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){
+ continue;
+ }
+ count++;
+ authAttrs[i].pValue = PORT_ArenaAlloc(arena,authAttrs[i].ulValueLen);
+ if (authAttrs[i].pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ }
+
+ /* if count was zero, none were found, finished with MACs */
+ if (count == 0) {
+ return CKR_OK;
+ }
+
+ crv = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount);
+ /* ignore error code, we expect some possible errors */
+
+ /* GetAttributeValue just verified the old macs, safe to write
+ * them out then... */
+ for (i=0; i < authAttrCount; i++) {
+ SECItem *signText;
+ SECItem plainText;
+ SECStatus rv;
+
+ if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){
+ continue;
+ }
+
+ plainText.data = authAttrs[i].pValue;
+ plainText.len = authAttrs[i].ulValueLen;
+ rv = sftkdb_SignAttribute(arena, newKey, id,
+ authAttrs[i].type, &plainText, &signText);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ rv = sftkdb_PutAttributeSignature(handle, keyTarget, id,
+ authAttrs[i].type, signText);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ }
+
+ return CKR_OK;
+}
+
+static CK_RV
+sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
+ CK_OBJECT_HANDLE id, SECItem *newKey)
+{
+ CK_RV crv = CKR_OK;
+ CK_RV crv2;
+ CK_ATTRIBUTE *first, *last;
+ CK_ATTRIBUTE privAttrs[] = {
+ {CKA_VALUE, NULL, 0},
+ {CKA_PRIVATE_EXPONENT, NULL, 0},
+ {CKA_PRIME_1, NULL, 0},
+ {CKA_PRIME_2, NULL, 0},
+ {CKA_EXPONENT_1, NULL, 0},
+ {CKA_EXPONENT_2, NULL, 0},
+ {CKA_COEFFICIENT, NULL, 0} };
+ CK_ULONG privAttrCount = sizeof(privAttrs)/sizeof(CK_ATTRIBUTE);
+ int i, count;
+
+ /*
+ * STEP 1. Read the old attributes in the clear.
+ */
+
+ /* Get the attribute sizes.
+ * ignore the error code, we will have unknown attributes here */
+ crv2 = sftkdb_GetAttributeValue(keydb, id, privAttrs, privAttrCount);
+
+ /*
+ * find the valid block of attributes and fill allocate space for
+ * their data */
+ first = last = NULL;
+ for (i=0; i < privAttrCount; i++) {
+ /* find the block of attributes that are appropriate for this
+ * objects. There should only be once contiguous block, if not
+ * there's an error.
+ *
+ * find the first and last good entry.
+ */
+ if ((privAttrs[i].ulValueLen == -1) || (privAttrs[i].ulValueLen == 0)){
+ if (!first) continue;
+ if (!last) {
+ /* previous entry was last good entry */
+ last= &privAttrs[i-1];
+ }
+ continue;
+ }
+ if (!first) {
+ first = &privAttrs[i];
+ }
+ if (last) {
+ /* OOPS, we've found another good entry beyond the end of the
+ * last good entry, we need to fail here. */
+ crv = CKR_GENERAL_ERROR;
+ break;
+ }
+ privAttrs[i].pValue = PORT_ArenaAlloc(arena,privAttrs[i].ulValueLen);
+ if (privAttrs[i].pValue == NULL) {
+ crv = CKR_HOST_MEMORY;
+ break;
+ }
+ }
+ if (first == NULL) {
+ /* no valid entries found, return error based on crv2 */
+ return crv2;
+ }
+ if (last == NULL) {
+ last = &privAttrs[privAttrCount-1];
+ }
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ /* read the attributes */
+ count = (last-first)+1;
+ crv = sftkdb_GetAttributeValue(keydb, id, first, count);
+ if (crv != CKR_OK) {
+ return crv;
+ }
+
+ /*
+ * STEP 2: read the encrypt the attributes with the new key.
+ */
+ for (i=0; i < count; i++) {
+ SECItem plainText;
+ SECItem *result;
+ SECStatus rv;
+
+ plainText.data = first[i].pValue;
+ plainText.len = first[i].ulValueLen;
+ rv = sftkdb_EncryptAttribute(arena, newKey, &plainText, &result);
+ if (rv != SECSuccess) {
+ return CKR_GENERAL_ERROR;
+ }
+ first[i].pValue = result->data;
+ first[i].ulValueLen = result->len;
+ /* clear our sensitive data out */
+ PORT_Memset(plainText.data, 0, plainText.len);
+ }
+
+
+ /*
+ * STEP 3: write the newly encrypted attributes out directly
+ */
+ id &= SFTK_OBJ_ID_MASK;
+ keydb->newKey = newKey;
+ crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, id, first, count);
+ keydb->newKey = NULL;
+
+ return crv;
+}
+
+static CK_RV
+sftk_convertAttributes(SFTKDBHandle *handle,
+ CK_OBJECT_HANDLE id, SECItem *newKey)
+{
+ CK_RV crv = CKR_OK;
+ PLArenaPool *arena = NULL;
+
+ /* get a new arena to simplify cleanup */
+ arena = PORT_NewArena(1024);
+ if (!arena) {
+ return CKR_HOST_MEMORY;
+ }
+
+ /*
+ * first handle the MACS
+ */
+ crv = sftk_updateMacs(arena, handle, id, newKey);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+
+ if (handle->type == SFTK_KEYDB_TYPE) {
+ crv = sftk_updateEncrypted(arena, handle, id, newKey);
+ if (crv != CKR_OK) {
+ goto loser;
+ }
+ }
+
+ /* free up our mess */
+ /* NOTE: at this point we know we've cleared out any unencrypted data */
+ PORT_FreeArena(arena, PR_FALSE);
+ return CKR_OK;
+
+loser:
+ /* there may be unencrypted data, clear it out down */
+ PORT_FreeArena(arena, PR_TRUE);
+ return crv;
+}
+
+
+/*
+ * must be called with the old key active.
+ */
+CK_RV
+sftkdb_convertObjects(SFTKDBHandle *handle, CK_ATTRIBUTE *template,
+ CK_ULONG count, SECItem *newKey)
+{
+ SDBFind *find = NULL;
+ CK_ULONG idCount = SFTK_MAX_IDS;
+ CK_OBJECT_HANDLE ids[SFTK_MAX_IDS];
+ CK_RV crv, crv2;
+ int i;
+
+ crv = sftkdb_FindObjectsInit(handle, template, count, &find);
+
+ if (crv != CKR_OK) {
+ return crv;
+ }
+ while ((crv == CKR_OK) && (idCount == SFTK_MAX_IDS)) {
+ crv = sftkdb_FindObjects(handle, find, ids, SFTK_MAX_IDS, &idCount);
+ for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
+ crv = sftk_convertAttributes(handle, ids[i], newKey);
+ }
+ }
+ crv2 = sftkdb_FindObjectsFinal(handle, find);
+ if (crv == CKR_OK) crv = crv2;
+
+ return crv;
+}
+
+
+/*
+ * change the database password.
+ */
+SECStatus
+sftkdb_ChangePassword(SFTKDBHandle *keydb,
+ char *oldPin, char *newPin, PRBool *tokenRemoved)
+{
+ SECStatus rv = SECSuccess;
+ SECItem plainText;
+ SECItem newKey;
+ SECItem *result = NULL;
+ SECItem salt, value;
+ SFTKDBHandle *certdb;
+ unsigned char saltData[SDB_MAX_META_DATA_LEN];
+ unsigned char valueData[SDB_MAX_META_DATA_LEN];
+ CK_RV crv;
+ SDB *db;
+
+ if (keydb == NULL) {
+ return SECFailure;
+ }
+
+ db = SFTK_GET_SDB(keydb);
+ if (db == NULL) {
+ return SECFailure;
+ }
+
+ newKey.data = NULL;
+
+ /* make sure we have a valid old pin */
+ crv = (*keydb->db->sdb_Begin)(keydb->db);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ salt.data = saltData;
+ salt.len = sizeof(saltData);
+ value.data = valueData;
+ value.len = sizeof(valueData);
+ crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
+ if (crv == CKR_OK) {
+ rv = sftkdb_CheckPassword(keydb, oldPin, tokenRemoved);
+ if (rv == SECFailure) {
+ goto loser;
+ }
+ } else {
+ salt.len = SHA1_LENGTH;
+ RNG_GenerateGlobalRandomBytes(salt.data,salt.len);
+ }
+
+ rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+
+ /*
+ * convert encrypted entries here.
+ */
+ crv = sftkdb_convertObjects(keydb, NULL, 0, &newKey);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ /* fix up certdb macs */
+ certdb = keydb->peerDB;
+ if (certdb) {
+ CK_ATTRIBUTE objectType = { CKA_CLASS, 0, sizeof(CK_OBJECT_CLASS) };
+ CK_OBJECT_CLASS myClass = CKO_NETSCAPE_TRUST;
+
+ objectType.pValue = &myClass;
+ crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ myClass = CKO_PUBLIC_KEY;
+ crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ }
+
+
+ plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING;
+ plainText.len = SFTK_PW_CHECK_LEN;
+
+ rv = sftkdb_EncryptAttribute(NULL, &newKey, &plainText, &result);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ value.data = result->data;
+ value.len = result->len;
+ crv = (*keydb->db->sdb_PutMetaData)(keydb->db, "password", &salt, &value);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+ crv = (*keydb->db->sdb_Commit)(keydb->db);
+ if (crv != CKR_OK) {
+ rv = SECFailure;
+ goto loser;
+ }
+
+ keydb->newKey = NULL;
+
+ sftkdb_switchKeys(keydb, &newKey);
+
+loser:
+ if (newKey.data) {
+ PORT_ZFree(newKey.data,newKey.len);
+ }
+ if (result) {
+ SECITEM_FreeItem(result, PR_FALSE);
+ }
+ if (rv != SECSuccess) {
+ (*keydb->db->sdb_Abort)(keydb->db);
+ }
+
+ return rv;
+}
+
+/*
+ * loose our cached password
+ */
+SECStatus
+sftkdb_ClearPassword(SFTKDBHandle *keydb)
+{
+ SECItem oldKey;
+ oldKey.data = NULL;
+ oldKey.len = 0;
+ sftkdb_switchKeys(keydb, &oldKey);
+ if (oldKey.data) {
+ PORT_ZFree(oldKey.data, oldKey.len);
+ }
+ return SECSuccess;
+}
+
+
diff --git a/security/nss/lib/softoken/softkver.c b/security/nss/lib/softoken/softkver.c
new file mode 100644
index 000000000..931d620e1
--- /dev/null
+++ b/security/nss/lib/softoken/softkver.c
@@ -0,0 +1,56 @@
+/* ***** 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) 2002
+ * 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 ***** */
+
+/* Library identity and versioning */
+
+#include "softkver.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_softokn_rcsid[] = "$Header: NSS " SOFTOKEN_VERSION _DEBUG_STRING
+ " " __DATE__ " " __TIME__ " $";
+const char __nss_softokn_sccsid[] = "@(#)NSS " SOFTOKEN_VERSION _DEBUG_STRING
+ " " __DATE__ " " __TIME__;
diff --git a/security/nss/lib/softoken/softkver.h b/security/nss/lib/softoken/softkver.h
new file mode 100644
index 000000000..22aa8dcc6
--- /dev/null
+++ b/security/nss/lib/softoken/softkver.h
@@ -0,0 +1,66 @@
+/*
+ * Softoken version numbers
+ *
+ * ***** 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 ***** */
+
+#ifndef _SOFTKVER_H_
+#define _SOFTKVER_H_
+
+#ifdef NSS_ENABLE_ECC
+#ifdef NSS_ECC_MORE_THAN_SUITE_B
+#define SOFTOKEN_ECC_STRING " Extended ECC"
+#else
+#define SOFTOKEN_ECC_STRING " Basic ECC"
+#endif
+#else
+#define SOFTOKEN_ECC_STRING ""
+#endif
+
+/*
+ * Softoken's major version, minor version, patch level, and whether
+ * this is a beta release.
+ *
+ * The format of the version string should be
+ * "<major version>.<minor version>[.<patch level>][ <ECC>][ <Beta>]"
+ */
+#define SOFTOKEN_VERSION "3.12.2.0" SOFTOKEN_ECC_STRING
+#define SOFTOKEN_VMAJOR 3
+#define SOFTOKEN_VMINOR 12
+#define SOFTOKEN_VPATCH 2
+#define SOFTOKEN_BETA PR_FALSE
+
+#endif /* _SOFTKVER_H_ */
diff --git a/security/nss/lib/softoken/softoken.h b/security/nss/lib/softoken/softoken.h
new file mode 100644
index 000000000..35dabffaa
--- /dev/null
+++ b/security/nss/lib/softoken/softoken.h
@@ -0,0 +1,284 @@
+/*
+ * softoken.h - private data structures and prototypes for the softoken lib
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#ifndef _SOFTOKEN_H_
+#define _SOFTOKEN_H_
+
+#include "blapi.h"
+#include "lowkeyti.h"
+#include "softoknt.h"
+#include "secoidt.h"
+
+#include "pkcs11t.h" /* CK_RV Required for sftk_fipsPowerUpSelfTest(). */
+
+SEC_BEGIN_PROTOS
+
+/*
+** RSA encryption/decryption. When encrypting/decrypting the output
+** buffer must be at least the size of the public key modulus.
+*/
+
+/*
+** Format some data into a PKCS#1 encryption block, preparing the
+** data for RSA encryption.
+** "result" where the formatted block is stored (memory is allocated)
+** "modulusLen" the size of the formatted block
+** "blockType" what block type to use (SEC_RSABlock*)
+** "data" the data to format
+*/
+extern SECStatus RSA_FormatBlock(SECItem *result,
+ unsigned int modulusLen,
+ RSA_BlockType blockType,
+ SECItem *data);
+/*
+** Similar, but just returns a pointer to the allocated memory, *and*
+** will *only* format one block, even if we (in the future) modify
+** RSA_FormatBlock() to loop over multiples of modulusLen.
+*/
+extern unsigned char *RSA_FormatOneBlock(unsigned int modulusLen,
+ RSA_BlockType blockType,
+ SECItem *data);
+
+
+
+/*
+ * convenience wrappers for doing single RSA operations. They create the
+ * RSA context internally and take care of the formatting
+ * requirements. Blinding happens automagically within RSA_Sign and
+ * RSA_DecryptBlock.
+ */
+extern
+SECStatus RSA_Sign(NSSLOWKEYPrivateKey *key, unsigned char *output,
+ unsigned int *outputLen, unsigned int maxOutputLen,
+ unsigned char *input, unsigned int inputLen);
+extern
+SECStatus RSA_HashSign(SECOidTag hashOid,
+ NSSLOWKEYPrivateKey *key, unsigned char *sig,
+ unsigned int *sigLen, unsigned int maxLen,
+ unsigned char *hash, unsigned int hashLen);
+extern
+SECStatus RSA_CheckSign(NSSLOWKEYPublicKey *key, unsigned char *sign,
+ unsigned int signLength, unsigned char *hash,
+ unsigned int hashLength);
+extern
+SECStatus RSA_HashCheckSign(SECOidTag hashOid,
+ NSSLOWKEYPublicKey *key, unsigned char *sig,
+ unsigned int sigLen, unsigned char *digest,
+ unsigned int digestLen);
+extern
+SECStatus RSA_CheckSignRecover(NSSLOWKEYPublicKey *key, unsigned char *data,
+ unsigned int *data_len,unsigned int max_output_len,
+ unsigned char *sign, unsigned int sign_len);
+extern
+SECStatus RSA_EncryptBlock(NSSLOWKEYPublicKey *key, unsigned char *output,
+ unsigned int *outputLen, unsigned int maxOutputLen,
+ unsigned char *input, unsigned int inputLen);
+extern
+SECStatus RSA_DecryptBlock(NSSLOWKEYPrivateKey *key, unsigned char *output,
+ unsigned int *outputLen, unsigned int maxOutputLen,
+ unsigned char *input, unsigned int inputLen);
+
+/*
+ * added to make pkcs #11 happy
+ * RAW is RSA_X_509
+ */
+extern
+SECStatus RSA_SignRaw( NSSLOWKEYPrivateKey *key, unsigned char *output,
+ unsigned int *output_len, unsigned int maxOutputLen,
+ unsigned char *input, unsigned int input_len);
+extern
+SECStatus RSA_CheckSignRaw( NSSLOWKEYPublicKey *key, unsigned char *sign,
+ unsigned int sign_len, unsigned char *hash,
+ unsigned int hash_len);
+extern
+SECStatus RSA_CheckSignRecoverRaw( NSSLOWKEYPublicKey *key, unsigned char *data,
+ unsigned int *data_len, unsigned int max_output_len,
+ unsigned char *sign, unsigned int sign_len);
+extern
+SECStatus RSA_EncryptRaw( NSSLOWKEYPublicKey *key, unsigned char *output,
+ unsigned int *output_len,
+ unsigned int max_output_len,
+ unsigned char *input, unsigned int input_len);
+extern
+SECStatus RSA_DecryptRaw(NSSLOWKEYPrivateKey *key, unsigned char *output,
+ unsigned int *output_len,
+ unsigned int max_output_len,
+ unsigned char *input, unsigned int input_len);
+#ifdef NSS_ENABLE_ECC
+/*
+** pepare an ECParam structure from DEREncoded params
+ */
+extern SECStatus EC_FillParams(PRArenaPool *arena,
+ const SECItem *encodedParams, ECParams *params);
+extern SECStatus EC_DecodeParams(const SECItem *encodedParams,
+ ECParams **ecparams);
+extern SECStatus EC_CopyParams(PRArenaPool *arena, ECParams *dstParams,
+ const ECParams *srcParams);
+#endif
+
+
+/*
+** Prepare a buffer for padded CBC encryption, growing to the appropriate
+** boundary, filling with the appropriate padding.
+**
+** blockSize must be a power of 2.
+**
+** We add from 1 to blockSize bytes -- we *always* grow.
+** The extra bytes contain the value of the length of the padding:
+** if we have 2 bytes of padding, then the padding is "0x02, 0x02".
+**
+** NOTE: If arena is non-NULL, we re-allocate from there, otherwise
+** we assume (and use) PR memory (re)allocation.
+*/
+extern unsigned char * CBC_PadBuffer(PRArenaPool *arena, unsigned char *inbuf,
+ unsigned int inlen, unsigned int *outlen,
+ int blockSize);
+
+
+/****************************************/
+/*
+** Power-Up selftests required for FIPS and invoked only
+** under PKCS #11 FIPS mode.
+*/
+extern CK_RV sftk_fipsPowerUpSelfTest( void );
+
+/*
+** make known fixed PKCS #11 key types to their sizes in bytes
+*/
+unsigned long sftk_MapKeySize(CK_KEY_TYPE keyType);
+
+/*
+** FIPS 140-2 auditing
+*/
+extern PRBool sftk_audit_enabled;
+
+extern void sftk_LogAuditMessage(NSSAuditSeverity severity, const char *msg);
+
+extern void sftk_AuditCreateObject(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject, CK_RV rv);
+
+extern void sftk_AuditCopyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject, CK_RV rv);
+
+extern void sftk_AuditDestroyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_RV rv);
+
+extern void sftk_AuditGetObjectSize(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize,
+ CK_RV rv);
+
+extern void sftk_AuditGetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount, CK_RV rv);
+
+extern void sftk_AuditSetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount, CK_RV rv);
+
+extern void sftk_AuditCryptInit(const char *opName,
+ CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey, CK_RV rv);
+
+extern void sftk_AuditGenerateKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey, CK_RV rv);
+
+extern void sftk_AuditGenerateKeyPair(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG ulPublicKeyAttributeCount,
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG ulPrivateKeyAttributeCount,
+ CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey, CK_RV rv);
+
+extern void sftk_AuditWrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen, CK_RV rv);
+
+extern void sftk_AuditUnwrapKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey, CK_RV rv);
+
+extern void sftk_AuditDeriveKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount,
+ CK_OBJECT_HANDLE_PTR phKey, CK_RV rv);
+
+extern void sftk_AuditDigestKey(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hKey, CK_RV rv);
+
+/*
+** FIPS 140-2 Error state
+*/
+extern PRBool sftk_fatalError;
+
+/*
+** macros to check for forked child after C_Initialize
+*/
+#if defined(XP_UNIX) && !defined(NO_PTHREADS)
+
+extern PRBool forked;
+
+extern void ForkedChild(void);
+
+#define CHECK_FORK() \
+ do { if (forked) return CKR_DEVICE_ERROR; } while (0)
+
+#else
+
+#define CHECK_FORK()
+
+#endif
+
+SEC_END_PROTOS
+
+#endif /* _SOFTOKEN_H_ */
diff --git a/security/nss/lib/softoken/softokn.def b/security/nss/lib/softoken/softokn.def
new file mode 100644
index 000000000..2544d1b92
--- /dev/null
+++ b/security/nss/lib/softoken/softokn.def
@@ -0,0 +1,61 @@
+;+#
+;+# ***** 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) 2000
+;+# the Initial Developer. All Rights Reserved.
+;+#
+;+# Contributor(s):
+;+# Dr Stephen Henson <stephen.henson@gemplus.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 *****
+;+#
+;+# 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.4 { # NSS 3.4 release
+;+ global:
+LIBRARY softokn3 ;-
+EXPORTS ;-
+C_GetFunctionList; Make this function like a real PKCS #11 module as well
+FC_GetFunctionList;
+NSC_GetFunctionList;
+NSC_ModuleDBFunc;
+;+ local:
+;+ *;
+;+};
diff --git a/security/nss/lib/softoken/softokn.rc b/security/nss/lib/softoken/softokn.rc
new file mode 100644
index 000000000..860155318
--- /dev/null
+++ b/security/nss/lib/softoken/softokn.rc
@@ -0,0 +1,100 @@
+/* ***** 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) 2001
+ * 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 ***** */
+
+#include "softkver.h"
+#include <winver.h>
+
+#define MY_LIBNAME "softokn"
+#define MY_FILEDESCRIPTION "NSS PKCS #11 Library"
+
+#define STRINGIZE(x) #x
+#define STRINGIZE2(x) STRINGIZE(x)
+#define SOFTOKEN_VMAJOR_STR STRINGIZE2(SOFTOKEN_VMAJOR)
+
+#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 SOFTOKEN_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 SOFTOKEN_VMAJOR_STR
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version-information resource
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,0
+ PRODUCTVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,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", SOFTOKEN_VERSION "\0"
+ VALUE "InternalName", MY_INTERNAL_NAME "\0"
+ VALUE "OriginalFilename", MY_INTERNAL_NAME ".dll\0"
+ VALUE "ProductName", "Network Security Services\0"
+ VALUE "ProductVersion", SOFTOKEN_VERSION "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
diff --git a/security/nss/lib/softoken/softoknt.h b/security/nss/lib/softoken/softoknt.h
new file mode 100644
index 000000000..3af94e286
--- /dev/null
+++ b/security/nss/lib/softoken/softoknt.h
@@ -0,0 +1,73 @@
+/*
+ * softoknt.h - public data structures for the software token library
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#ifndef _SOFTOKNT_H_
+#define _SOFTOKNT_H_
+
+/*
+ * 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_BlockOAEP = 3, /* use OAEP padding */
+ /* XXX is this only for a public-key
+ operation? If so, add "Public" */
+ RSA_BlockRaw = 4, /* simply justify the block appropriately */
+ RSA_BlockTotal
+} RSA_BlockType;
+
+#define NSS_SOFTOKEN_DEFAULT_CHUNKSIZE 2048
+
+/*
+ * FIPS 140-2 auditing
+ */
+typedef enum {
+ NSS_AUDIT_ERROR = 3, /* errors */
+ NSS_AUDIT_WARNING = 2, /* warning messages */
+ NSS_AUDIT_INFO = 1 /* informational messages */
+} NSSAuditSeverity;
+
+#endif /* _SOFTOKNT_H_ */
diff --git a/security/nss/lib/softoken/tlsprf.c b/security/nss/lib/softoken/tlsprf.c
new file mode 100644
index 000000000..23e996d5f
--- /dev/null
+++ b/security/nss/lib/softoken/tlsprf.c
@@ -0,0 +1,215 @@
+/* tlsprf.c - TLS Pseudo Random Function (PRF) implementation
+ *
+ * ***** 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 ***** */
+/* $Id$ */
+
+#include "pkcs11i.h"
+#include "blapi.h"
+
+#define SFTK_OFFSETOF(str, memb) ((PRPtrdiff)(&(((str *)0)->memb)))
+
+static void sftk_TLSPRFNull(void *data, PRBool freeit)
+{
+ return;
+}
+
+typedef struct {
+ PRUint32 cxSize; /* size of allocated block, in bytes. */
+ PRUint32 cxBufSize; /* sizeof buffer at cxBufPtr. */
+ unsigned char *cxBufPtr; /* points to real buffer, may be cxBuf. */
+ PRUint32 cxKeyLen; /* bytes of cxBufPtr containing key. */
+ PRUint32 cxDataLen; /* bytes of cxBufPtr containing data. */
+ SECStatus cxRv; /* records failure of void functions. */
+ PRBool cxIsFIPS; /* true if conforming to FIPS 198. */
+ unsigned char cxBuf[512]; /* actual size may be larger than 512. */
+} TLSPRFContext;
+
+static void
+sftk_TLSPRFHashUpdate(TLSPRFContext *cx, const unsigned char *data,
+ unsigned int data_len)
+{
+ PRUint32 bytesUsed = cx->cxKeyLen + cx->cxDataLen;
+
+ if (cx->cxRv != SECSuccess) /* function has previously failed. */
+ return;
+ if (bytesUsed + data_len > cx->cxBufSize) {
+ /* We don't use realloc here because
+ ** (a) realloc doesn't zero out the old block, and
+ ** (b) if realloc fails, we lose the old block.
+ */
+ PRUint32 newBufSize = bytesUsed + data_len + 512;
+ unsigned char * newBuf = (unsigned char *)PORT_Alloc(newBufSize);
+ if (!newBuf) {
+ cx->cxRv = SECFailure;
+ return;
+ }
+ PORT_Memcpy(newBuf, cx->cxBufPtr, bytesUsed);
+ if (cx->cxBufPtr != cx->cxBuf) {
+ PORT_ZFree(cx->cxBufPtr, bytesUsed);
+ }
+ cx->cxBufPtr = newBuf;
+ cx->cxBufSize = newBufSize;
+ }
+ PORT_Memcpy(cx->cxBufPtr + bytesUsed, data, data_len);
+ cx->cxDataLen += data_len;
+}
+
+static void
+sftk_TLSPRFEnd(TLSPRFContext *ctx, unsigned char *hashout,
+ unsigned int *pDigestLen, unsigned int maxDigestLen)
+{
+ *pDigestLen = 0; /* tells Verify that no data has been input yet. */
+}
+
+/* Compute the PRF values from the data previously input. */
+static SECStatus
+sftk_TLSPRFUpdate(TLSPRFContext *cx,
+ unsigned char *sig, /* output goes here. */
+ unsigned int * sigLen, /* how much output. */
+ unsigned int maxLen, /* output buffer size */
+ unsigned char *hash, /* unused. */
+ unsigned int hashLen) /* unused. */
+{
+ SECStatus rv;
+ SECItem sigItem;
+ SECItem seedItem;
+ SECItem secretItem;
+
+ if (cx->cxRv != SECSuccess)
+ return cx->cxRv;
+
+ secretItem.data = cx->cxBufPtr;
+ secretItem.len = cx->cxKeyLen;
+
+ seedItem.data = cx->cxBufPtr + cx->cxKeyLen;
+ seedItem.len = cx->cxDataLen;
+
+ sigItem.data = sig;
+ sigItem.len = maxLen;
+
+ rv = TLS_PRF(&secretItem, NULL, &seedItem, &sigItem, cx->cxIsFIPS);
+ if (rv == SECSuccess && sigLen != NULL)
+ *sigLen = sigItem.len;
+ return rv;
+
+}
+
+static SECStatus
+sftk_TLSPRFVerify(TLSPRFContext *cx,
+ unsigned char *sig, /* input, for comparison. */
+ unsigned int sigLen, /* length of sig. */
+ unsigned char *hash, /* data to be verified. */
+ unsigned int hashLen) /* size of hash data. */
+{
+ unsigned char * tmp = (unsigned char *)PORT_Alloc(sigLen);
+ unsigned int tmpLen = sigLen;
+ SECStatus rv;
+
+ if (!tmp)
+ return SECFailure;
+ if (hashLen) {
+ /* hashLen is non-zero when the user does a one-step verify.
+ ** In this case, none of the data has been input yet.
+ */
+ sftk_TLSPRFHashUpdate(cx, hash, hashLen);
+ }
+ rv = sftk_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0);
+ if (rv == SECSuccess) {
+ rv = (SECStatus)(1 - !PORT_Memcmp(tmp, sig, sigLen));
+ }
+ PORT_ZFree(tmp, sigLen);
+ return rv;
+}
+
+static void
+sftk_TLSPRFHashDestroy(TLSPRFContext *cx, PRBool freeit)
+{
+ if (freeit) {
+ if (cx->cxBufPtr != cx->cxBuf)
+ PORT_ZFree(cx->cxBufPtr, cx->cxBufSize);
+ PORT_ZFree(cx, cx->cxSize);
+ }
+}
+
+CK_RV
+sftk_TLSPRFInit(SFTKSessionContext *context,
+ SFTKObject * key,
+ CK_KEY_TYPE key_type)
+{
+ SFTKAttribute * keyVal;
+ TLSPRFContext * prf_cx;
+ CK_RV crv = CKR_HOST_MEMORY;
+ PRUint32 keySize;
+ PRUint32 blockSize;
+
+ if (key_type != CKK_GENERIC_SECRET)
+ return CKR_KEY_TYPE_INCONSISTENT; /* CKR_KEY_FUNCTION_NOT_PERMITTED */
+
+ context->multi = PR_TRUE;
+
+ keyVal = sftk_FindAttribute(key, CKA_VALUE);
+ keySize = (!keyVal) ? 0 : keyVal->attrib.ulValueLen;
+ blockSize = keySize + sizeof(TLSPRFContext);
+ prf_cx = (TLSPRFContext *)PORT_Alloc(blockSize);
+ if (!prf_cx)
+ goto done;
+ prf_cx->cxSize = blockSize;
+ prf_cx->cxKeyLen = keySize;
+ prf_cx->cxDataLen = 0;
+ prf_cx->cxBufSize = blockSize - SFTK_OFFSETOF(TLSPRFContext, cxBuf);
+ prf_cx->cxRv = SECSuccess;
+ prf_cx->cxIsFIPS = (key->slot->slotID == FIPS_SLOT_ID);
+ prf_cx->cxBufPtr = prf_cx->cxBuf;
+ if (keySize)
+ PORT_Memcpy(prf_cx->cxBufPtr, keyVal->attrib.pValue, keySize);
+
+ context->hashInfo = (void *) prf_cx;
+ context->cipherInfo = (void *) prf_cx;
+ context->hashUpdate = (SFTKHash) sftk_TLSPRFHashUpdate;
+ context->end = (SFTKEnd) sftk_TLSPRFEnd;
+ context->update = (SFTKCipher) sftk_TLSPRFUpdate;
+ context->verify = (SFTKVerify) sftk_TLSPRFVerify;
+ context->destroy = (SFTKDestroy) sftk_TLSPRFNull;
+ context->hashdestroy = (SFTKDestroy) sftk_TLSPRFHashDestroy;
+ crv = CKR_OK;
+
+done:
+ if (keyVal)
+ sftk_FreeAttribute(keyVal);
+ return crv;
+}
+