diff options
Diffstat (limited to 'mozilla/security/nss/lib/softoken')
66 files changed, 50877 insertions, 0 deletions
diff --git a/mozilla/security/nss/lib/softoken/Makefile b/mozilla/security/nss/lib/softoken/Makefile new file mode 100644 index 0000000..c7f7b2a --- /dev/null +++ b/mozilla/security/nss/lib/softoken/Makefile @@ -0,0 +1,100 @@ +#! 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) # +####################################################################### + +ifdef NSS_DISABLE_DBM +DIRS= dummy +endif + +####################################################################### +# (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/mozilla/security/nss/lib/softoken/config.mk b/mozilla/security/nss/lib/softoken/config.mk new file mode 100644 index 0000000..3a35944 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/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) + +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 \ + -l$(SQLITE_LIB_NAME) \ + -L$(NSSUTIL_LIB_DIR) \ + -lnssutil3 \ + -L$(NSPR_LIB_DIR) \ + -lplc4 \ + -lplds4 \ + -lnspr4 \ + $(NULL) +else # ! NS_USE_GCC + +EXTRA_SHARED_LIBS += \ + $(DIST)/lib/$(SQLITE_LIB_NAME).lib \ + $(NSSUTIL_LIB_DIR)/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 \ + $(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 \ + -l$(SQLITE_LIB_NAME) \ + -L$(NSSUTIL_LIB_DIR) \ + -lnssutil3 \ + -L$(NSPR_LIB_DIR) \ + -lplc4 \ + -lplds4 \ + -lnspr4 \ + $(NULL) +endif + +ifeq ($(OS_TARGET),AIX) +OS_LIBS += -lpthread +endif + +ifeq ($(OS_TARGET),SunOS) +OS_LIBS += -lbsm +endif + +ifeq ($(OS_TARGET),WINCE) +DEFINES += -DDBM_USING_NSPR +endif diff --git a/mozilla/security/nss/lib/softoken/ecdecode.c b/mozilla/security/nss/lib/softoken/ecdecode.c new file mode 100644 index 0000000..c037dc5 --- /dev/null +++ b/mozilla/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, ¶ms->fieldID.u.prime, + curveParams->irr)); + } else { + CHECK_OK(hexString2SECItem(params->arena, ¶ms->fieldID.u.poly, + curveParams->irr)); + } + CHECK_OK(hexString2SECItem(params->arena, ¶ms->curve.a, + curveParams->curvea)); + CHECK_OK(hexString2SECItem(params->arena, ¶ms->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, ¶ms->base, genenc)); + CHECK_OK(hexString2SECItem(params->arena, ¶ms->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/mozilla/security/nss/lib/softoken/fipsaudt.c b/mozilla/security/nss/lib/softoken/fipsaudt.c new file mode 100644 index 0000000..573a17c --- /dev/null +++ b/mozilla/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, NSS_AUDIT_LOAD_KEY, 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, NSS_AUDIT_COPY_KEY, 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, NSS_AUDIT_DESTROY_KEY, 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, NSS_AUDIT_ACCESS_KEY, 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, NSS_AUDIT_ACCESS_KEY, 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, NSS_AUDIT_CHANGE_KEY, 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, NSS_AUDIT_CRYPT, 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, NSS_AUDIT_GENERATE_KEY, 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, NSS_AUDIT_GENERATE_KEY, 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, NSS_AUDIT_WRAP_KEY, 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, NSS_AUDIT_UNWRAP_KEY, 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, NSS_AUDIT_DERIVE_KEY, 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, NSS_AUDIT_DIGEST_KEY, msg); +} diff --git a/mozilla/security/nss/lib/softoken/fipstest.c b/mozilla/security/nss/lib/softoken/fipstest.c new file mode 100644 index 0000000..a9ca120 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/fipstest.c @@ -0,0 +1,2155 @@ +/* + * 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: fipstest.c,v 1.27 2009/06/19 23:05:48 rrelyea%redhat.com Exp $ */ + +#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 256 /* 2048-bits */ +#define FIPS_RSA_COEFFICIENT_LENGTH 128 /* 1024-bits */ +#define FIPS_RSA_PRIME0_LENGTH 128 /* 1024-bits */ +#define FIPS_RSA_PRIME1_LENGTH 128 /* 1024-bits */ +#define FIPS_RSA_EXPONENT0_LENGTH 128 /* 1024-bits */ +#define FIPS_RSA_EXPONENT1_LENGTH 128 /* 1024-bits */ +#define FIPS_RSA_PRIVATE_EXPONENT_LENGTH 256 /* 2048-bits */ +#define FIPS_RSA_ENCRYPT_LENGTH 256 /* 2048-bits */ +#define FIPS_RSA_DECRYPT_LENGTH 256 /* 2048-bits */ +#define FIPS_RSA_SIGNATURE_LENGTH 256 /* 2048-bits */ +#define FIPS_RSA_MODULUS_LENGTH 256 /* 2048-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 (2048-bits). */ + static const PRUint8 rsa_modulus[FIPS_RSA_MODULUS_LENGTH] = { + 0xb8, 0x15, 0x00, 0x33, 0xda, 0x0c, 0x9d, 0xa5, + 0x14, 0x8c, 0xde, 0x1f, 0x23, 0x07, 0x54, 0xe2, + 0xc6, 0xb9, 0x51, 0x04, 0xc9, 0x65, 0x24, 0x6e, + 0x0a, 0x46, 0x34, 0x5c, 0x37, 0x86, 0x6b, 0x88, + 0x24, 0x27, 0xac, 0xa5, 0x02, 0x79, 0xfb, 0xed, + 0x75, 0xc5, 0x3f, 0x6e, 0xdf, 0x05, 0x5f, 0x0f, + 0x20, 0x70, 0xa0, 0x5b, 0x85, 0xdb, 0xac, 0xb9, + 0x5f, 0x02, 0xc2, 0x64, 0x1e, 0x84, 0x5b, 0x3e, + 0xad, 0xbf, 0xf6, 0x2e, 0x51, 0xd6, 0xad, 0xf7, + 0xa7, 0x86, 0x75, 0x86, 0xec, 0xa7, 0xe1, 0xf7, + 0x08, 0xbf, 0xdc, 0x56, 0xb1, 0x3b, 0xca, 0xd8, + 0xfc, 0x51, 0xdf, 0x9a, 0x2a, 0x37, 0x06, 0xf2, + 0xd1, 0x6b, 0x9a, 0x5e, 0x2a, 0xe5, 0x20, 0x57, + 0x35, 0x9f, 0x1f, 0x98, 0xcf, 0x40, 0xc7, 0xd6, + 0x98, 0xdb, 0xde, 0xf5, 0x64, 0x53, 0xf7, 0x9d, + 0x45, 0xf3, 0xd6, 0x78, 0xb9, 0xe3, 0xa3, 0x20, + 0xcd, 0x79, 0x43, 0x35, 0xef, 0xd7, 0xfb, 0xb9, + 0x80, 0x88, 0x27, 0x2f, 0x63, 0xa8, 0x67, 0x3d, + 0x4a, 0xfa, 0x06, 0xc6, 0xd2, 0x86, 0x0b, 0xa7, + 0x28, 0xfd, 0xe0, 0x1e, 0x93, 0x4b, 0x17, 0x2e, + 0xb0, 0x11, 0x6f, 0xc6, 0x2b, 0x98, 0x0f, 0x15, + 0xe3, 0x87, 0x16, 0x7a, 0x7c, 0x67, 0x3e, 0x12, + 0x2b, 0xf8, 0xbe, 0x48, 0xc1, 0x97, 0x47, 0xf4, + 0x1f, 0x81, 0x80, 0x12, 0x28, 0xe4, 0x7b, 0x1e, + 0xb7, 0x00, 0xa4, 0xde, 0xaa, 0xfb, 0x0f, 0x77, + 0x84, 0xa3, 0xd6, 0xb2, 0x03, 0x48, 0xdd, 0x53, + 0x8b, 0x46, 0x41, 0x28, 0x52, 0xc4, 0x53, 0xf0, + 0x1c, 0x95, 0xd9, 0x36, 0xe0, 0x0f, 0x26, 0x46, + 0x9c, 0x61, 0x0e, 0x80, 0xca, 0x86, 0xaf, 0x39, + 0x95, 0xe5, 0x60, 0x43, 0x61, 0x3e, 0x2b, 0xb4, + 0xe8, 0xbd, 0x8d, 0x77, 0x62, 0xf5, 0x32, 0x43, + 0x2f, 0x4b, 0x65, 0x82, 0x14, 0xdd, 0x29, 0x5b}; + + /* 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 2048-bits), */ + /* (private prime0 is 1024-bits), */ + /* (private prime1 is 1024-bits), */ + /* (private prime exponent0 is 1024-bits), */ + /* (private prime exponent1 is 1024-bits), */ + /* and (private coefficient is 1024-bits). */ + static const PRUint8 rsa_version[] = { 0x00 }; + + static const PRUint8 rsa_private_exponent[FIPS_RSA_PRIVATE_EXPONENT_LENGTH] + = {0x29, 0x08, 0x05, 0x53, 0x89, 0x76, 0xe6, 0x6c, + 0xb5, 0x77, 0xf0, 0xca, 0xdf, 0xf3, 0xf2, 0x67, + 0xda, 0x03, 0xd4, 0x9b, 0x4c, 0x88, 0xce, 0xe5, + 0xf8, 0x44, 0x4d, 0xc7, 0x80, 0x58, 0xe5, 0xff, + 0x22, 0x8f, 0xf5, 0x5b, 0x92, 0x81, 0xbe, 0x35, + 0xdf, 0xda, 0x67, 0x99, 0x3e, 0xfc, 0xe3, 0x83, + 0x6b, 0xa7, 0xaf, 0x16, 0xb7, 0x6f, 0x8f, 0xc0, + 0x81, 0xfd, 0x0b, 0x77, 0x65, 0x95, 0xfb, 0x00, + 0xad, 0x99, 0xec, 0x35, 0xc6, 0xe8, 0x23, 0x3e, + 0xe0, 0x88, 0x88, 0x09, 0xdb, 0x16, 0x50, 0xb7, + 0xcf, 0xab, 0x74, 0x61, 0x9e, 0x7f, 0xc5, 0x67, + 0x38, 0x56, 0xc7, 0x90, 0x85, 0x78, 0x5e, 0x84, + 0x21, 0x49, 0xea, 0xce, 0xb2, 0xa0, 0xff, 0xe4, + 0x70, 0x7f, 0x57, 0x7b, 0xa8, 0x36, 0xb8, 0x54, + 0x8d, 0x1d, 0xf5, 0x44, 0x9d, 0x68, 0x59, 0xf9, + 0x24, 0x6e, 0x85, 0x8f, 0xc3, 0x5f, 0x8a, 0x2c, + 0x94, 0xb7, 0xbc, 0x0e, 0xa5, 0xef, 0x93, 0x06, + 0x38, 0xcd, 0x07, 0x0c, 0xae, 0xb8, 0x44, 0x1a, + 0xd8, 0xe7, 0xf5, 0x9a, 0x1e, 0x9c, 0x18, 0xc7, + 0x6a, 0xc2, 0x7f, 0x28, 0x01, 0x4f, 0xb4, 0xb8, + 0x90, 0x97, 0x5a, 0x43, 0x38, 0xad, 0xe8, 0x95, + 0x68, 0x83, 0x1a, 0x1b, 0x10, 0x07, 0xe6, 0x02, + 0x52, 0x1f, 0xbf, 0x76, 0x6b, 0x46, 0xd6, 0xfb, + 0xc3, 0xbe, 0xb5, 0xac, 0x52, 0x53, 0x01, 0x1c, + 0xf3, 0xc5, 0xeb, 0x64, 0xf2, 0x1e, 0xc4, 0x38, + 0xe9, 0xaa, 0xd9, 0xc3, 0x72, 0x51, 0xa5, 0x44, + 0x58, 0x69, 0x0b, 0x1b, 0x98, 0x7f, 0xf2, 0x23, + 0xff, 0xeb, 0xf0, 0x75, 0x24, 0xcf, 0xc5, 0x1e, + 0xb8, 0x6a, 0xc5, 0x2f, 0x4f, 0x23, 0x50, 0x7d, + 0x15, 0x9d, 0x19, 0x7a, 0x0b, 0x82, 0xe0, 0x21, + 0x5b, 0x5f, 0x9d, 0x50, 0x2b, 0x83, 0xe4, 0x48, + 0xcc, 0x39, 0xe5, 0xfb, 0x13, 0x7b, 0x6f, 0x81 }; + + static const PRUint8 rsa_prime0[FIPS_RSA_PRIME0_LENGTH] = { + 0xe4, 0xbf, 0x21, 0x62, 0x9b, 0xa9, 0x77, 0x40, + 0x8d, 0x2a, 0xce, 0xa1, 0x67, 0x5a, 0x4c, 0x96, + 0x45, 0x98, 0x67, 0xbd, 0x75, 0x22, 0x33, 0x6f, + 0xe6, 0xcb, 0x77, 0xde, 0x9e, 0x97, 0x7d, 0x96, + 0x8c, 0x5e, 0x5d, 0x34, 0xfb, 0x27, 0xfc, 0x6d, + 0x74, 0xdb, 0x9d, 0x2e, 0x6d, 0xf6, 0xea, 0xfc, + 0xce, 0x9e, 0xda, 0xa7, 0x25, 0xa2, 0xf4, 0x58, + 0x6d, 0x0a, 0x3f, 0x01, 0xc2, 0xb4, 0xab, 0x38, + 0xc1, 0x14, 0x85, 0xb6, 0xfa, 0x94, 0xc3, 0x85, + 0xf9, 0x3c, 0x2e, 0x96, 0x56, 0x01, 0xe7, 0xd6, + 0x14, 0x71, 0x4f, 0xfb, 0x4c, 0x85, 0x52, 0xc4, + 0x61, 0x1e, 0xa5, 0x1e, 0x96, 0x13, 0x0d, 0x8f, + 0x66, 0xae, 0xa0, 0xcd, 0x7d, 0x25, 0x66, 0x19, + 0x15, 0xc2, 0xcf, 0xc3, 0x12, 0x3c, 0xe8, 0xa4, + 0x52, 0x4c, 0xcb, 0x28, 0x3c, 0xc4, 0xbf, 0x95, + 0x33, 0xe3, 0x81, 0xea, 0x0c, 0x6c, 0xa2, 0x05}; + static const PRUint8 rsa_prime1[FIPS_RSA_PRIME1_LENGTH] = { + 0xce, 0x03, 0x94, 0xf4, 0xa9, 0x2c, 0x1e, 0x06, + 0xe7, 0x40, 0x30, 0x01, 0xf7, 0xbb, 0x68, 0x8c, + 0x27, 0xd2, 0x15, 0xe3, 0x28, 0x49, 0x5b, 0xa8, + 0xc1, 0x9a, 0x42, 0x7e, 0x31, 0xf9, 0x08, 0x34, + 0x81, 0xa2, 0x0f, 0x04, 0x61, 0x34, 0xe3, 0x36, + 0x92, 0xb1, 0x09, 0x2b, 0xe9, 0xef, 0x84, 0x88, + 0xbe, 0x9c, 0x98, 0x60, 0xa6, 0x60, 0x84, 0xe9, + 0x75, 0x6f, 0xcc, 0x81, 0xd1, 0x96, 0xef, 0xdd, + 0x2e, 0xca, 0xc4, 0xf5, 0x42, 0xfb, 0x13, 0x2b, + 0x57, 0xbf, 0x14, 0x5e, 0xc2, 0x7f, 0x77, 0x35, + 0x29, 0xc4, 0xe5, 0xe0, 0xf9, 0x6d, 0x15, 0x4a, + 0x42, 0x56, 0x1c, 0x3e, 0x0c, 0xc5, 0xce, 0x70, + 0x08, 0x63, 0x1e, 0x73, 0xdb, 0x7e, 0x74, 0x05, + 0x32, 0x01, 0xc6, 0x36, 0x32, 0x75, 0x6b, 0xed, + 0x9d, 0xfe, 0x7c, 0x7e, 0xa9, 0x57, 0xb4, 0xe9, + 0x22, 0xe4, 0xe7, 0xfe, 0x36, 0x07, 0x9b, 0xdf}; + static const PRUint8 rsa_exponent0[FIPS_RSA_EXPONENT0_LENGTH] = { + 0x04, 0x5a, 0x3a, 0xa9, 0x64, 0xaa, 0xd9, 0xd1, + 0x09, 0x9e, 0x99, 0xe5, 0xea, 0x50, 0x86, 0x8a, + 0x89, 0x72, 0x77, 0xee, 0xdb, 0xee, 0xb5, 0xa9, + 0xd8, 0x6b, 0x60, 0xb1, 0x84, 0xb4, 0xff, 0x37, + 0xc1, 0x1d, 0xfe, 0x8a, 0x06, 0x89, 0x61, 0x3d, + 0x37, 0xef, 0x01, 0xd3, 0xa3, 0x56, 0x02, 0x6c, + 0xa3, 0x05, 0xd4, 0xc5, 0x3f, 0x6b, 0x15, 0x59, + 0x25, 0x61, 0xff, 0x86, 0xea, 0x0c, 0x84, 0x01, + 0x85, 0x72, 0xfd, 0x84, 0x58, 0xca, 0x41, 0xda, + 0x27, 0xbe, 0xe4, 0x68, 0x09, 0xe4, 0xe9, 0x63, + 0x62, 0x6a, 0x31, 0x8a, 0x67, 0x8f, 0x55, 0xde, + 0xd4, 0xb6, 0x3f, 0x90, 0x10, 0x6c, 0xf6, 0x62, + 0x17, 0x23, 0x15, 0x7e, 0x33, 0x76, 0x65, 0xb5, + 0xee, 0x7b, 0x11, 0x76, 0xf5, 0xbe, 0xe0, 0xf2, + 0x57, 0x7a, 0x8c, 0x97, 0x0c, 0x68, 0xf5, 0xf8, + 0x41, 0xcf, 0x7f, 0x66, 0x53, 0xac, 0x31, 0x7d}; + static const PRUint8 rsa_exponent1[FIPS_RSA_EXPONENT1_LENGTH] = { + 0x93, 0x54, 0x14, 0x6e, 0x73, 0x9d, 0x4d, 0x4b, + 0xfa, 0x8c, 0xf8, 0xc8, 0x2f, 0x76, 0x22, 0xea, + 0x38, 0x80, 0x11, 0x8f, 0x05, 0xfc, 0x90, 0x44, + 0x3b, 0x50, 0x2a, 0x45, 0x3d, 0x4f, 0xaf, 0x02, + 0x7d, 0xc2, 0x7b, 0xa2, 0xd2, 0x31, 0x94, 0x5c, + 0x2e, 0xc3, 0xd4, 0x9f, 0x47, 0x09, 0x37, 0x6a, + 0xe3, 0x85, 0xf1, 0xa3, 0x0c, 0xd8, 0xf1, 0xb4, + 0x53, 0x7b, 0xc4, 0x71, 0x02, 0x86, 0x42, 0xbb, + 0x96, 0xff, 0x03, 0xa3, 0xb2, 0x67, 0x03, 0xea, + 0x77, 0x31, 0xfb, 0x4b, 0x59, 0x24, 0xf7, 0x07, + 0x59, 0xfb, 0xa9, 0xba, 0x1e, 0x26, 0x58, 0x97, + 0x66, 0xa1, 0x56, 0x49, 0x39, 0xb1, 0x2c, 0x55, + 0x0a, 0x6a, 0x78, 0x18, 0xba, 0xdb, 0xcf, 0xf4, + 0xf7, 0x32, 0x35, 0xa2, 0x04, 0xab, 0xdc, 0xa7, + 0x6d, 0xd9, 0xd5, 0x06, 0x6f, 0xec, 0x7d, 0x40, + 0x4c, 0xe8, 0x0e, 0xd0, 0xc9, 0xaa, 0xdf, 0x59}; + static const PRUint8 rsa_coefficient[FIPS_RSA_COEFFICIENT_LENGTH] = { + 0x17, 0xd7, 0xf5, 0x0a, 0xf0, 0x68, 0x97, 0x96, + 0xc4, 0x29, 0x18, 0x77, 0x9a, 0x1f, 0xe3, 0xf3, + 0x12, 0x13, 0x0f, 0x7e, 0x7b, 0xb9, 0xc1, 0x91, + 0xf9, 0xc7, 0x08, 0x56, 0x5c, 0xa4, 0xbc, 0x83, + 0x71, 0xf9, 0x78, 0xd9, 0x2b, 0xec, 0xfe, 0x6b, + 0xdc, 0x2f, 0x63, 0xc9, 0xcd, 0x50, 0x14, 0x5b, + 0xd3, 0x6e, 0x85, 0x4d, 0x0c, 0xa2, 0x0b, 0xa0, + 0x09, 0xb6, 0xca, 0x34, 0x9c, 0xc2, 0xc1, 0x4a, + 0xb0, 0xbc, 0x45, 0x93, 0xa5, 0x7e, 0x99, 0xb5, + 0xbd, 0xe4, 0x69, 0x29, 0x08, 0x28, 0xd2, 0xcd, + 0xab, 0x24, 0x78, 0x48, 0x41, 0x26, 0x0b, 0x37, + 0xa3, 0x43, 0xd1, 0x95, 0x1a, 0xd6, 0xee, 0x22, + 0x1c, 0x00, 0x0b, 0xc2, 0xb7, 0xa4, 0xa3, 0x21, + 0xa9, 0xcd, 0xe4, 0x69, 0xd3, 0x45, 0x02, 0xb1, + 0xb7, 0x3a, 0xbf, 0x51, 0x35, 0x1b, 0x78, 0xc2, + 0xcf, 0x0c, 0x0d, 0x60, 0x09, 0xa9, 0x44, 0x02}; + + /* 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" + "blocks SHA256, SHA384 and " + "SHA512 RSA Signature KAT tests. " + "Known plaintext message utilized" + "for RSA Encryption & Decryption" + "blocks SHA256, SHA384 and " + "SHA512 RSA Signature KAT tests."}; + + /* RSA Known Ciphertext (2048-bits). */ + static const PRUint8 rsa_known_ciphertext[] = { + 0x04, 0x12, 0x46, 0xe3, 0x6a, 0xee, 0xde, 0xdd, + 0x49, 0xa1, 0xd9, 0x83, 0xf7, 0x35, 0xf9, 0x70, + 0x88, 0x03, 0x2d, 0x01, 0x8b, 0xd1, 0xbf, 0xdb, + 0xe5, 0x1c, 0x85, 0xbe, 0xb5, 0x0b, 0x48, 0x45, + 0x7a, 0xf0, 0xa0, 0xe3, 0xa2, 0xbb, 0x4b, 0xf6, + 0x27, 0xd0, 0x1b, 0x12, 0xe3, 0x77, 0x52, 0x34, + 0x9e, 0x8e, 0x03, 0xd2, 0xf8, 0x79, 0x6e, 0x39, + 0x79, 0x53, 0x3c, 0x44, 0x14, 0x94, 0xbb, 0x8d, + 0xaa, 0x14, 0x44, 0xa0, 0x7b, 0xa5, 0x8c, 0x93, + 0x5f, 0x99, 0xa4, 0xa3, 0x6e, 0x7a, 0x38, 0x40, + 0x78, 0xfa, 0x36, 0x91, 0x5e, 0x9a, 0x9c, 0xba, + 0x1e, 0xd4, 0xf9, 0xda, 0x4b, 0x0f, 0xa8, 0xa3, + 0x1c, 0xf3, 0x3a, 0xd1, 0xa5, 0xb4, 0x51, 0x16, + 0xed, 0x4b, 0xcf, 0xec, 0x93, 0x7b, 0x90, 0x21, + 0xbc, 0x3a, 0xf4, 0x0b, 0xd1, 0x3a, 0x2b, 0xba, + 0xa6, 0x7d, 0x5b, 0x53, 0xd8, 0x64, 0xf9, 0x29, + 0x7b, 0x7f, 0x77, 0x3e, 0x51, 0x4c, 0x9a, 0x94, + 0xd2, 0x4b, 0x4a, 0x8d, 0x61, 0x74, 0x97, 0xae, + 0x53, 0x6a, 0xf4, 0x90, 0xc2, 0x2c, 0x49, 0xe2, + 0xfa, 0xeb, 0x91, 0xc5, 0xe5, 0x83, 0x13, 0xc9, + 0x44, 0x4b, 0x95, 0x2c, 0x57, 0x70, 0x15, 0x5c, + 0x64, 0x8d, 0x1a, 0xfd, 0x2a, 0xc7, 0xb2, 0x9c, + 0x5c, 0x99, 0xd3, 0x4a, 0xfd, 0xdd, 0xf6, 0x82, + 0x87, 0x8c, 0x5a, 0xc4, 0xa8, 0x0d, 0x2a, 0xef, + 0xc3, 0xa2, 0x7e, 0x8e, 0x67, 0x9f, 0x6f, 0x63, + 0xdb, 0xbb, 0x1d, 0x31, 0xc4, 0xbb, 0xbc, 0x13, + 0x3f, 0x54, 0xc6, 0xf6, 0xc5, 0x28, 0x32, 0xab, + 0x96, 0x42, 0x10, 0x36, 0x40, 0x92, 0xbb, 0x57, + 0x55, 0x38, 0xf5, 0x43, 0x7e, 0x43, 0xc4, 0x65, + 0x47, 0x64, 0xaa, 0x0f, 0x4c, 0xe9, 0x49, 0x16, + 0xec, 0x6a, 0x50, 0xfd, 0x14, 0x49, 0xca, 0xdb, + 0x44, 0x54, 0xca, 0xbe, 0xa3, 0x0e, 0x5f, 0xef}; + + /* RSA Known Signed Hash (2048-bits). */ + static const PRUint8 rsa_known_sha256_signature[] = { + 0x8c, 0x2d, 0x2e, 0xfb, 0x37, 0xb5, 0x6f, 0x38, + 0x9f, 0x06, 0x5a, 0xf3, 0x8c, 0xa0, 0xd0, 0x7a, + 0xde, 0xcf, 0xf9, 0x14, 0x95, 0x59, 0xd3, 0x5f, + 0x51, 0x5d, 0x5d, 0xad, 0xd8, 0x71, 0x33, 0x50, + 0x1d, 0x03, 0x3b, 0x3a, 0x32, 0x00, 0xb4, 0xde, + 0x7f, 0xe4, 0xb1, 0xe5, 0x6b, 0x83, 0xf4, 0x80, + 0x10, 0x3b, 0xb8, 0x8a, 0xdb, 0xe8, 0x0a, 0x42, + 0x9e, 0x8d, 0xd7, 0xbe, 0xed, 0xde, 0x5a, 0x3d, + 0xc6, 0xdb, 0xfe, 0x49, 0x6a, 0xe9, 0x1e, 0x75, + 0x66, 0xf1, 0x3f, 0x9e, 0x3f, 0xff, 0x05, 0x65, + 0xde, 0xca, 0x62, 0x62, 0xf3, 0xec, 0x53, 0x09, + 0xa0, 0x37, 0xd5, 0x66, 0x62, 0x72, 0x14, 0xb6, + 0x51, 0x32, 0x67, 0x50, 0xc1, 0xe1, 0x2f, 0x9e, + 0x98, 0x4e, 0x53, 0x96, 0x55, 0x4b, 0xc4, 0x92, + 0xc3, 0xb4, 0x80, 0xf0, 0x35, 0xc9, 0x00, 0x4b, + 0x5c, 0x85, 0x92, 0xb1, 0xe8, 0x6e, 0xa5, 0x51, + 0x38, 0x9f, 0xc9, 0x11, 0xb6, 0x14, 0xdf, 0x34, + 0x64, 0x40, 0x82, 0x82, 0xde, 0x16, 0x69, 0x93, + 0x89, 0x4e, 0x5c, 0x32, 0xf2, 0x0a, 0x4e, 0x9e, + 0xbd, 0x63, 0x99, 0x4f, 0xf3, 0x15, 0x90, 0xc2, + 0xfe, 0x6f, 0xb7, 0xf4, 0xad, 0xd4, 0x8e, 0x0b, + 0xd2, 0xf5, 0x22, 0xd2, 0x71, 0x65, 0x13, 0xf7, + 0x82, 0x7b, 0x75, 0xb6, 0xc1, 0xb4, 0x45, 0xbd, + 0x8f, 0x95, 0xcf, 0x5b, 0x95, 0x32, 0xef, 0x18, + 0x5f, 0xd3, 0xdf, 0x7e, 0x22, 0xdd, 0x25, 0xeb, + 0xe1, 0xbf, 0x3b, 0x9a, 0x55, 0x75, 0x4f, 0x3c, + 0x38, 0x67, 0x57, 0x04, 0x04, 0x57, 0x27, 0xf6, + 0x34, 0x0e, 0x57, 0x8a, 0x7c, 0xff, 0x7d, 0xca, + 0x8c, 0x06, 0xf8, 0x9d, 0xdb, 0xe4, 0xd8, 0x19, + 0xdd, 0x4d, 0xfd, 0x8f, 0xa0, 0x06, 0x53, 0xe8, + 0x33, 0x00, 0x70, 0x3f, 0x6b, 0xc3, 0xbd, 0x9a, + 0x78, 0xb5, 0xa9, 0xef, 0x6d, 0xda, 0x67, 0x92}; + + /* RSA Known Signed Hash (2048-bits). */ + static const PRUint8 rsa_known_sha384_signature[] = { + 0x20, 0x2d, 0x21, 0x3a, 0xaa, 0x1e, 0x05, 0x15, + 0x5c, 0xca, 0x84, 0x86, 0xc0, 0x15, 0x81, 0xdf, + 0xd4, 0x06, 0x9f, 0xe0, 0xc1, 0xed, 0xef, 0x0f, + 0xfe, 0xb3, 0xc3, 0xbb, 0x28, 0xa5, 0x56, 0xbf, + 0xe3, 0x11, 0x5c, 0xc2, 0xc0, 0x0b, 0xfa, 0xfa, + 0x3d, 0xd3, 0x06, 0x20, 0xe2, 0xc9, 0xe4, 0x66, + 0x28, 0xb7, 0xc0, 0x3b, 0x3c, 0x96, 0xc6, 0x49, + 0x3b, 0xcf, 0x86, 0x49, 0x31, 0xaf, 0x5b, 0xa3, + 0xec, 0x63, 0x10, 0xdf, 0xda, 0x2f, 0x68, 0xac, + 0x7b, 0x3a, 0x49, 0xfa, 0xe6, 0x0d, 0xfe, 0x37, + 0x17, 0x56, 0x8e, 0x5c, 0x48, 0x97, 0x43, 0xf7, + 0xa0, 0xbc, 0xe3, 0x4b, 0x42, 0xde, 0x58, 0x1d, + 0xd9, 0x5d, 0xb3, 0x08, 0x35, 0xbd, 0xa4, 0xe1, + 0x80, 0xc3, 0x64, 0xab, 0x21, 0x97, 0xad, 0xfb, + 0x71, 0xee, 0xa3, 0x3d, 0x9c, 0xaa, 0xfa, 0x16, + 0x60, 0x46, 0x32, 0xda, 0x44, 0x2e, 0x10, 0x92, + 0x20, 0xd8, 0x98, 0x80, 0x84, 0x75, 0x5b, 0x70, + 0x91, 0x00, 0x33, 0x19, 0x69, 0xc9, 0x2a, 0xec, + 0x3d, 0xe5, 0x5f, 0x0f, 0x9a, 0xa7, 0x97, 0x1f, + 0x79, 0xc3, 0x1d, 0x65, 0x74, 0x62, 0xc5, 0xa1, + 0x23, 0x65, 0x4b, 0x84, 0xa1, 0x03, 0x98, 0xf3, + 0xf1, 0x02, 0x24, 0xca, 0xe5, 0xd4, 0xc8, 0xa2, + 0x30, 0xad, 0x72, 0x7d, 0x29, 0x60, 0x1a, 0x8e, + 0x6f, 0x23, 0xa4, 0xda, 0x68, 0xa4, 0x45, 0x9c, + 0x39, 0x70, 0x44, 0x18, 0x4b, 0x73, 0xfe, 0xf8, + 0x33, 0x53, 0x1d, 0x7e, 0x93, 0x93, 0xac, 0xc7, + 0x1e, 0x6e, 0x6b, 0xfd, 0x9e, 0xba, 0xa6, 0x71, + 0x70, 0x47, 0x6a, 0xd6, 0x82, 0x32, 0xa2, 0x6e, + 0x20, 0x72, 0xb0, 0xba, 0xec, 0x91, 0xbb, 0x6b, + 0xcc, 0x84, 0x0a, 0x33, 0x2b, 0x8a, 0x8d, 0xeb, + 0x71, 0xcd, 0xca, 0x67, 0x1b, 0xad, 0x10, 0xd4, + 0xce, 0x4f, 0xc0, 0x29, 0xec, 0xfa, 0xed, 0xfa}; + + /* RSA Known Signed Hash (2048-bits). */ + static const PRUint8 rsa_known_sha512_signature[] = { + 0x35, 0x0e, 0x74, 0x9d, 0xeb, 0xc7, 0x67, 0x31, + 0x9f, 0xff, 0x0b, 0xbb, 0x5e, 0x66, 0xb4, 0x2f, + 0xbf, 0x72, 0x60, 0x4f, 0xe9, 0xbd, 0xec, 0xc8, + 0x17, 0x79, 0x5f, 0x39, 0x83, 0xb4, 0x54, 0x2e, + 0x01, 0xb9, 0xd3, 0x20, 0x47, 0xcb, 0xd4, 0x42, + 0xf2, 0x6e, 0x36, 0xc1, 0x97, 0xad, 0xef, 0x8e, + 0xe6, 0x51, 0xee, 0x5e, 0x9e, 0x88, 0xb4, 0x9d, + 0xda, 0x3e, 0x77, 0x4b, 0xe8, 0xae, 0x48, 0x53, + 0x2c, 0xc4, 0xd3, 0x25, 0x6b, 0x23, 0xb7, 0x54, + 0x3c, 0x95, 0x8f, 0xfb, 0x6f, 0x6d, 0xc5, 0x56, + 0x39, 0x69, 0x28, 0x0e, 0x74, 0x9b, 0x31, 0xe8, + 0x76, 0x77, 0x2b, 0xc1, 0x44, 0x89, 0x81, 0x93, + 0xfc, 0xf6, 0xec, 0x5f, 0x8f, 0x89, 0xfc, 0x1d, + 0xa4, 0x53, 0x58, 0x8c, 0xe9, 0xc0, 0xc0, 0x26, + 0xe6, 0xdf, 0x6d, 0x27, 0xb1, 0x8e, 0x3e, 0xb6, + 0x47, 0xe1, 0x02, 0x96, 0xc2, 0x5f, 0x7f, 0x3d, + 0xc5, 0x6c, 0x2f, 0xea, 0xaa, 0x5e, 0x39, 0xfc, + 0x77, 0xca, 0x00, 0x02, 0x5c, 0x64, 0x7c, 0xce, + 0x7d, 0x63, 0x82, 0x05, 0xed, 0xf7, 0x5b, 0x55, + 0x58, 0xc0, 0xeb, 0x76, 0xd7, 0x95, 0x55, 0x37, + 0x85, 0x7d, 0x17, 0xad, 0xd2, 0x11, 0xfd, 0x97, + 0x48, 0xb5, 0xc2, 0x5e, 0xc7, 0x62, 0xc0, 0xe0, + 0x68, 0xa8, 0x61, 0x14, 0x41, 0xca, 0x25, 0x3a, + 0xec, 0x48, 0x54, 0x22, 0x83, 0x2b, 0x69, 0x54, + 0xfd, 0xc8, 0x99, 0x9a, 0xee, 0x37, 0x03, 0xa3, + 0x8f, 0x0f, 0x32, 0xb0, 0xaa, 0x74, 0x39, 0x04, + 0x7c, 0xd9, 0xc2, 0x8f, 0xbe, 0xf2, 0xc4, 0xbe, + 0xdd, 0x7a, 0x7a, 0x7f, 0x72, 0xd3, 0x80, 0x59, + 0x18, 0xa0, 0xa1, 0x2d, 0x6f, 0xa3, 0xa9, 0x48, + 0xed, 0x20, 0xa6, 0xea, 0xaa, 0x10, 0x83, 0x98, + 0x0c, 0x13, 0x69, 0x6e, 0xcd, 0x31, 0x6b, 0xd0, + 0x66, 0xa6, 0x5e, 0x30, 0x0c, 0x82, 0xd5, 0x81}; + + 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_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 entropy[] = { + 0x8e,0x9c,0x0d,0x25,0x75,0x22,0x04,0xf9, + 0xc5,0x79,0x10,0x8b,0x23,0x79,0x37,0x14, + 0x9f,0x2c,0xc7,0x0b,0x39,0xf8,0xee,0xef, + 0x95,0x0c,0x97,0x59,0xfc,0x0a,0x85,0x41, + 0x76,0x9d,0x6d,0x67,0x00,0x4e,0x19,0x12, + 0x02,0x16,0x53,0xea,0xf2,0x73,0xd7,0xd6, + 0x7f,0x7e,0xc8,0xae,0x9c,0x09,0x99,0x7d, + 0xbb,0x9e,0x48,0x7f,0xbb,0x96,0x46,0xb3, + 0x03,0x75,0xf8,0xc8,0x69,0x45,0x3f,0x97, + 0x5e,0x2e,0x48,0xe1,0x5d,0x58,0x97,0x4c }; + static const PRUint8 rng_known_result[] = { + 0x16,0xe1,0x8c,0x57,0x21,0xd8,0xf1,0x7e, + 0x5a,0xa0,0x16,0x0b,0x7e,0xa6,0x25,0xb4, + 0x24,0x19,0xdb,0x54,0xfa,0x35,0x13,0x66, + 0xbb,0xaa,0x2a,0x1b,0x22,0x33,0x2e,0x4a, + 0x14,0x07,0x9d,0x52,0xfc,0x73,0x61,0x48, + 0xac,0xc1,0x22,0xfc,0xa4,0xfc,0xac,0xa4, + 0xdb,0xda,0x5b,0x27,0x33,0xc4,0xb3 }; + static const PRUint8 reseed_entropy[] = { + 0xc6,0x0b,0x0a,0x30,0x67,0x07,0xf4,0xe2, + 0x24,0xa7,0x51,0x6f,0x5f,0x85,0x3e,0x5d, + 0x67,0x97,0xb8,0x3b,0x30,0x9c,0x7a,0xb1, + 0x52,0xc6,0x1b,0xc9,0x46,0xa8,0x62,0x79 }; + static const PRUint8 additional_input[] = { + 0x86,0x82,0x28,0x98,0xe7,0xcb,0x01,0x14, + 0xae,0x87,0x4b,0x1d,0x99,0x1b,0xc7,0x41, + 0x33,0xff,0x33,0x66,0x40,0x95,0x54,0xc6, + 0x67,0x4d,0x40,0x2a,0x1f,0xf9,0xeb,0x65 }; + static const PRUint8 rng_reseed_result[] = { + 0x02,0x0c,0xc6,0x17,0x86,0x49,0xba,0xc4, + 0x7b,0x71,0x35,0x05,0xf0,0xdb,0x4a,0xc2, + 0x2c,0x38,0xc1,0xa4,0x42,0xe5,0x46,0x4a, + 0x7d,0xf0,0xbe,0x47,0x88,0xb8,0x0e,0xc6, + 0x25,0x2b,0x1d,0x13,0xef,0xa6,0x87,0x96, + 0xa3,0x7d,0x5b,0x80,0xc2,0x38,0x76,0x61, + 0xc7,0x80,0x5d,0x0f,0x05,0x76,0x85 }; + 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 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; + PR_STATIC_ASSERT(sizeof(rng_known_result) >= sizeof(rng_reseed_result)); + PRUint8 result[sizeof(rng_known_result)]; + PRUint8 DSAX[FIPS_DSA_SUBPRIME_LENGTH]; + + /********************************************/ + /* Generate random bytes with a known seed. */ + /********************************************/ + rng_status = PRNGTEST_Instantiate(entropy, sizeof entropy, + NULL, 0, NULL, 0); + if (rng_status != SECSuccess) { + return ( CKR_DEVICE_ERROR ); + } + rng_status = PRNGTEST_Generate(result, sizeof rng_known_result, NULL, 0); + if ( ( rng_status != SECSuccess) || + ( PORT_Memcmp( result, rng_known_result, + sizeof rng_known_result ) != 0 ) ) { + PRNGTEST_Uninstantiate(); + return ( CKR_DEVICE_ERROR ); + } + rng_status = PRNGTEST_Reseed(reseed_entropy, sizeof reseed_entropy, + additional_input, sizeof additional_input); + if (rng_status != SECSuccess) { + PRNGTEST_Uninstantiate(); + return ( CKR_DEVICE_ERROR ); + } + rng_status = PRNGTEST_Generate(result, sizeof rng_reseed_result, NULL, 0); + if ( ( rng_status != SECSuccess) || + ( PORT_Memcmp( result, rng_reseed_result, + sizeof rng_reseed_result ) != 0 ) ) { + PRNGTEST_Uninstantiate(); + return ( CKR_DEVICE_ERROR ); + } + rng_status = PRNGTEST_Uninstantiate(); + if (rng_status != SECSuccess) { + 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/mozilla/security/nss/lib/softoken/fipstokn.c b/mozilla/security/nss/lib/softoken/fipstokn.c new file mode 100644 index 0000000..96fc6f2 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/fipstokn.c @@ -0,0 +1,1607 @@ +/* ***** 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_CRYPTO_TEST_USER +#define AUDIT_CRYPTO_TEST_USER 2400 /* Crypto test results */ +#define AUDIT_CRYPTO_PARAM_CHANGE_USER 2401 /* Crypto attribute change */ +#define AUDIT_CRYPTO_LOGIN 2402 /* Logged in as crypto officer */ +#define AUDIT_CRYPTO_LOGOUT 2403 /* Logged out from crypto */ +#define AUDIT_CRYPTO_KEY_USER 2404 /* Create,delete,negotiate */ +#define AUDIT_CRYPTO_FAILURE_USER 2405 /* Fail decrypt,encrypt,randomize */ +#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; +} + +#ifdef LINUX + +int +sftk_mapLinuxAuditType(NSSAuditSeverity severity, NSSAuditType auditType) +{ + switch (auditType) { + case NSS_AUDIT_ACCESS_KEY: + case NSS_AUDIT_CHANGE_KEY: + case NSS_AUDIT_COPY_KEY: + case NSS_AUDIT_DERIVE_KEY: + case NSS_AUDIT_DESTROY_KEY: + case NSS_AUDIT_DIGEST_KEY: + case NSS_AUDIT_GENERATE_KEY: + case NSS_AUDIT_LOAD_KEY: + case NSS_AUDIT_UNWRAP_KEY: + case NSS_AUDIT_WRAP_KEY: + return AUDIT_CRYPTO_KEY_USER; + case NSS_AUDIT_CRYPT: + return (severity == NSS_AUDIT_ERROR) ? AUDIT_CRYPTO_FAILURE_USER : + AUDIT_CRYPTO_KEY_USER; + case NSS_AUDIT_FIPS_STATE: + case NSS_AUDIT_INIT_PIN: + case NSS_AUDIT_INIT_TOKEN: + case NSS_AUDIT_SET_PIN: + return AUDIT_CRYPTO_PARAM_CHANGE_USER; + case NSS_AUDIT_SELF_TEST: + return AUDIT_CRYPTO_TEST_USER; + case NSS_AUDIT_LOGIN: + return AUDIT_CRYPTO_LOGIN; + case NSS_AUDIT_LOGOUT: + return AUDIT_CRYPTO_LOGOUT; + /* we skip the fault case here so we can get compiler + * warnings if new 'NSSAuditType's are added without + * added them to this list, defaults fall through */ + } + /* default */ + return AUDIT_CRYPTO_PARAM_CHANGE_USER; +} +#endif + + +/********************************************************************** + * + * 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, NSSAuditType auditType, + 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 linuxAuditType; + 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; + } + linuxAuditType = sftk_mapLinuxAuditType(severity, auditType); + if (audit_log_user_message_func) { + audit_log_user_message_func(audit_fd, linuxAuditType, message, + NULL, NULL, NULL, result); + } else { + audit_send_user_message_func(audit_fd, linuxAuditType, 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; + + sftk_ForkReset(pReserved, &crv); + + 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, NSS_AUDIT_SELF_TEST, 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; + + if (sftk_ForkReset(pReserved, &crv)) { + return crv; + } + + 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, NSS_AUDIT_INIT_TOKEN, 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, NSS_AUDIT_INIT_PIN, 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, NSS_AUDIT_SET_PIN, 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, NSS_AUDIT_LOGIN, 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, NSS_AUDIT_LOGOUT, 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, NSS_AUDIT_SELF_TEST, 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/mozilla/security/nss/lib/softoken/jpakesftk.c b/mozilla/security/nss/lib/softoken/jpakesftk.c new file mode 100644 index 0000000..9f4a012 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/jpakesftk.c @@ -0,0 +1,396 @@ +/* ***** 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 Mozilla Fonudation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * 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 "seccomon.h" +#include "secerr.h" +#include "blapi.h" +#include "pkcs11i.h" +#include "softoken.h" + +static CK_RV +jpake_mapStatus(SECStatus rv, CK_RV invalidArgsMapping) { + int err; + if (rv == SECSuccess) + return CKR_OK; + err = PORT_GetError(); + switch (err) { + /* XXX: SEC_ERROR_INVALID_ARGS might be caused by invalid template + parameters. */ + case SEC_ERROR_INVALID_ARGS: return invalidArgsMapping; + case SEC_ERROR_BAD_SIGNATURE: return CKR_SIGNATURE_INVALID; + case SEC_ERROR_NO_MEMORY: return CKR_HOST_MEMORY; + } + return CKR_FUNCTION_FAILED; +} + +/* If key is not NULL then the gx value will be stored as an attribute with + the type given by the gxAttrType parameter. */ +static CK_RV +jpake_Sign(PLArenaPool * arena, const PQGParams * pqg, HASH_HashType hashType, + const SECItem * signerID, const SECItem * x, + CK_NSS_JPAKEPublicValue * out) +{ + SECItem gx, gv, r; + CK_RV crv; + + PORT_Assert(arena != NULL); + + gx.data = NULL; + gv.data = NULL; + r.data = NULL; + crv = jpake_mapStatus(JPAKE_Sign(arena, pqg, hashType, signerID, x, NULL, + NULL, &gx, &gv, &r), + CKR_MECHANISM_PARAM_INVALID); + if (crv == CKR_OK) { + if (out->pGX != NULL && out->ulGXLen >= gx.len || + out->pGV != NULL && out->ulGVLen >= gv.len || + out->pR != NULL && out->ulRLen >= r.len) { + PORT_Memcpy(out->pGX, gx.data, gx.len); + PORT_Memcpy(out->pGV, gv.data, gv.len); + PORT_Memcpy(out->pR, r.data, r.len); + out->ulGXLen = gx.len; + out->ulGVLen = gv.len; + out->ulRLen = r.len; + } else { + crv = CKR_MECHANISM_PARAM_INVALID; + } + } + return crv; +} + +static CK_RV +jpake_Verify(PLArenaPool * arena, const PQGParams * pqg, + HASH_HashType hashType, const SECItem * signerID, + const CK_BYTE * peerIDData, CK_ULONG peerIDLen, + const CK_NSS_JPAKEPublicValue * publicValueIn) +{ + SECItem peerID, gx, gv, r; + peerID.data = (unsigned char *) peerIDData; peerID.len = peerIDLen; + gx.data = publicValueIn->pGX; gx.len = publicValueIn->ulGXLen; + gv.data = publicValueIn->pGV; gv.len = publicValueIn->ulGVLen; + r.data = publicValueIn->pR; r.len = publicValueIn->ulRLen; + return jpake_mapStatus(JPAKE_Verify(arena, pqg, hashType, signerID, &peerID, + &gx, &gv, &r), + CKR_MECHANISM_PARAM_INVALID); +} + +#define NUM_ELEM(x) (sizeof (x) / sizeof (x)[0]) + +/* Ensure that the key is of the given type. */ +static CK_RV +jpake_ensureKeyType(SFTKObject * key, CK_KEY_TYPE keyType) +{ + CK_RV crv; + SFTKAttribute * keyTypeAttr = sftk_FindAttribute(key, CKA_KEY_TYPE); + crv = keyTypeAttr != NULL && + *(CK_KEY_TYPE *)keyTypeAttr->attrib.pValue == keyType + ? CKR_OK + : CKR_TEMPLATE_INCONSISTENT; + if (keyTypeAttr != NULL) + sftk_FreeAttribute(keyTypeAttr); + return crv; +} + +/* If the template has the key type set, ensure that it was set to the correct + * value. If the template did not have the key type set, set it to the + * correct value. + */ +static CK_RV +jpake_enforceKeyType(SFTKObject * key, CK_KEY_TYPE keyType) { + CK_RV crv; + SFTKAttribute * keyTypeAttr = sftk_FindAttribute(key, CKA_KEY_TYPE); + if (keyTypeAttr != NULL) { + crv = *(CK_KEY_TYPE *)keyTypeAttr->attrib.pValue == keyType + ? CKR_OK + : CKR_TEMPLATE_INCONSISTENT; + sftk_FreeAttribute(keyTypeAttr); + } else { + crv = sftk_forceAttribute(key, CKA_KEY_TYPE, &keyType, sizeof keyType); + } + return crv; +} + +static CK_RV +jpake_MultipleSecItem2Attribute(SFTKObject * key, const SFTKItemTemplate * attrs, + size_t attrsCount) +{ + size_t i; + + for (i = 0; i < attrsCount; ++i) { + CK_RV crv = sftk_forceAttribute(key, attrs[i].type, attrs[i].item->data, + attrs[i].item->len); + if (crv != CKR_OK) + return crv; + } + return CKR_OK; +} + +CK_RV +jpake_Round1(HASH_HashType hashType, CK_NSS_JPAKERound1Params * params, + SFTKObject * key) +{ + CK_RV crv; + PQGParams pqg; + PLArenaPool * arena; + SECItem signerID; + SFTKItemTemplate templateAttrs[] = { + { CKA_PRIME, &pqg.prime }, + { CKA_SUBPRIME, &pqg.subPrime }, + { CKA_BASE, &pqg.base }, + { CKA_NSS_JPAKE_SIGNERID, &signerID } + }; + SECItem x2, gx1, gx2; + const SFTKItemTemplate generatedAttrs[] = { + { CKA_NSS_JPAKE_X2, &x2 }, + { CKA_NSS_JPAKE_GX1, &gx1 }, + { CKA_NSS_JPAKE_GX2, &gx2 }, + }; + SECItem x1; + + PORT_Assert(params != NULL); + PORT_Assert(key != NULL); + + arena = PORT_NewArena(NSS_SOFTOKEN_DEFAULT_CHUNKSIZE); + if (arena == NULL) + crv = CKR_HOST_MEMORY; + + crv = sftk_MultipleAttribute2SecItem(arena, key, templateAttrs, + NUM_ELEM(templateAttrs)); + + if (crv == CKR_OK && (signerID.data == NULL || signerID.len == 0)) + crv = CKR_TEMPLATE_INCOMPLETE; + + /* generate x1, g^x1 and the proof of knowledge of x1 */ + if (crv == CKR_OK) { + x1.data = NULL; + crv = jpake_mapStatus(DSA_NewRandom(arena, &pqg.subPrime, &x1), + CKR_TEMPLATE_INCONSISTENT); + } + if (crv == CKR_OK) + crv = jpake_Sign(arena, &pqg, hashType, &signerID, &x1, ¶ms->gx1); + + /* generate x2, g^x2 and the proof of knowledge of x2 */ + if (crv == CKR_OK) { + x2.data = NULL; + crv = jpake_mapStatus(DSA_NewRandom(arena, &pqg.subPrime, &x2), + CKR_TEMPLATE_INCONSISTENT); + } + if (crv == CKR_OK) + crv = jpake_Sign(arena, &pqg, hashType, &signerID, &x2, ¶ms->gx2); + + /* Save the values needed for round 2 into CKA_VALUE */ + if (crv == CKR_OK) { + gx1.data = params->gx1.pGX; + gx1.len = params->gx1.ulGXLen; + gx2.data = params->gx2.pGX; + gx2.len = params->gx2.ulGXLen; + crv = jpake_MultipleSecItem2Attribute(key, generatedAttrs, + NUM_ELEM(generatedAttrs)); + } + + PORT_FreeArena(arena, PR_TRUE); + return crv; +} + +CK_RV +jpake_Round2(HASH_HashType hashType, CK_NSS_JPAKERound2Params * params, + SFTKObject * sourceKey, SFTKObject * key) +{ + CK_RV crv; + PLArenaPool * arena; + PQGParams pqg; + SECItem signerID, x2, gx1, gx2; + SFTKItemTemplate sourceAttrs[] = { + { CKA_PRIME, &pqg.prime }, + { CKA_SUBPRIME, &pqg.subPrime }, + { CKA_BASE, &pqg.base }, + { CKA_NSS_JPAKE_SIGNERID, &signerID }, + { CKA_NSS_JPAKE_X2, &x2 }, + { CKA_NSS_JPAKE_GX1, &gx1 }, + { CKA_NSS_JPAKE_GX2, &gx2 }, + }; + SECItem x2s, gx3, gx4; + const SFTKItemTemplate copiedAndGeneratedAttrs[] = { + { CKA_NSS_JPAKE_SIGNERID, &signerID }, + { CKA_PRIME, &pqg.prime }, + { CKA_SUBPRIME, &pqg.subPrime }, + { CKA_NSS_JPAKE_X2, &x2 }, + { CKA_NSS_JPAKE_X2S, &x2s }, + { CKA_NSS_JPAKE_GX1, &gx1 }, + { CKA_NSS_JPAKE_GX2, &gx2 }, + { CKA_NSS_JPAKE_GX3, &gx3 }, + { CKA_NSS_JPAKE_GX4, &gx4 } + }; + SECItem peerID; + + PORT_Assert(params != NULL); + PORT_Assert(sourceKey != NULL); + PORT_Assert(key != NULL); + + arena = PORT_NewArena(NSS_SOFTOKEN_DEFAULT_CHUNKSIZE); + if (arena == NULL) + crv = CKR_HOST_MEMORY; + + /* TODO: check CKK_NSS_JPAKE_ROUND1 */ + + crv = sftk_MultipleAttribute2SecItem(arena, sourceKey, sourceAttrs, + NUM_ELEM(sourceAttrs)); + + /* Get the peer's ID out of the template and sanity-check it. */ + if (crv == CKR_OK) + crv = sftk_Attribute2SecItem(arena, &peerID, key, + CKA_NSS_JPAKE_PEERID); + if (crv == CKR_OK && (peerID.data == NULL || peerID.len == 0)) + crv = CKR_TEMPLATE_INCOMPLETE; + if (crv == CKR_OK && SECITEM_CompareItem(&signerID, &peerID) == SECEqual) + crv = CKR_TEMPLATE_INCONSISTENT; + + /* Verify zero-knowledge proofs for g^x3 and g^x4 */ + if (crv == CKR_OK) + crv = jpake_Verify(arena, &pqg, hashType, &signerID, + peerID.data, peerID.len, ¶ms->gx3); + if (crv == CKR_OK) + crv = jpake_Verify(arena, &pqg, hashType, &signerID, + peerID.data, peerID.len, ¶ms->gx4); + + /* Calculate the base and x2s for A=base^x2s */ + if (crv == CKR_OK) { + SECItem s; + s.data = params->pSharedKey; + s.len = params->ulSharedKeyLen; + gx3.data = params->gx3.pGX; + gx3.len = params->gx3.ulGXLen; + gx4.data = params->gx4.pGX; + gx4.len = params->gx4.ulGXLen; + pqg.base.data = NULL; + x2s.data = NULL; + crv = jpake_mapStatus(JPAKE_Round2(arena, &pqg.prime, &pqg.subPrime, + &gx1, &gx3, &gx4, &pqg.base, + &x2, &s, &x2s), + CKR_MECHANISM_PARAM_INVALID); + } + + /* Generate A=base^x2s and its zero-knowledge proof. */ + if (crv == CKR_OK) + crv = jpake_Sign(arena, &pqg, hashType, &signerID, &x2s, ¶ms->A); + + /* Copy P and Q from the ROUND1 key to the ROUND2 key and save the values + needed for the final key material derivation into CKA_VALUE. */ + if (crv == CKR_OK) + crv = sftk_forceAttribute(key, CKA_PRIME, pqg.prime.data, + pqg.prime.len); + if (crv == CKR_OK) + crv = sftk_forceAttribute(key, CKA_SUBPRIME, pqg.subPrime.data, + pqg.subPrime.len); + if (crv == CKR_OK) { + crv = jpake_MultipleSecItem2Attribute(key, copiedAndGeneratedAttrs, + NUM_ELEM(copiedAndGeneratedAttrs)); + } + + if (crv == CKR_OK) + crv = jpake_enforceKeyType(key, CKK_NSS_JPAKE_ROUND2); + + PORT_FreeArena(arena, PR_TRUE); + return crv; +} + +CK_RV +jpake_Final(HASH_HashType hashType, const CK_NSS_JPAKEFinalParams * param, + SFTKObject * sourceKey, SFTKObject * key) +{ + PLArenaPool * arena; + SECItem K; + PQGParams pqg; + CK_RV crv; + SECItem peerID, signerID, x2s, x2, gx1, gx2, gx3, gx4; + SFTKItemTemplate sourceAttrs[] = { + { CKA_NSS_JPAKE_PEERID, &peerID }, + { CKA_NSS_JPAKE_SIGNERID, &signerID }, + { CKA_PRIME, &pqg.prime }, + { CKA_SUBPRIME, &pqg.subPrime }, + { CKA_NSS_JPAKE_X2, &x2 }, + { CKA_NSS_JPAKE_X2S, &x2s }, + { CKA_NSS_JPAKE_GX1, &gx1 }, + { CKA_NSS_JPAKE_GX2, &gx2 }, + { CKA_NSS_JPAKE_GX3, &gx3 }, + { CKA_NSS_JPAKE_GX4, &gx4 } + }; + + PORT_Assert(param != NULL); + PORT_Assert(sourceKey != NULL); + PORT_Assert(key != NULL); + + arena = PORT_NewArena(NSS_SOFTOKEN_DEFAULT_CHUNKSIZE); + if (arena == NULL) + crv = CKR_HOST_MEMORY; + + /* TODO: verify key type CKK_NSS_JPAKE_ROUND2 */ + + crv = sftk_MultipleAttribute2SecItem(arena, sourceKey, sourceAttrs, + NUM_ELEM(sourceAttrs)); + + /* Calculate base for B=base^x4s */ + if (crv == CKR_OK) { + pqg.base.data = NULL; + crv = jpake_mapStatus(JPAKE_Round2(arena, &pqg.prime, &pqg.subPrime, + &gx1, &gx2, &gx3, &pqg.base, + NULL, NULL, NULL), + CKR_MECHANISM_PARAM_INVALID); + } + + /* Verify zero-knowledge proof for B */ + if (crv == CKR_OK) + crv = jpake_Verify(arena, &pqg, hashType, &signerID, + peerID.data, peerID.len, ¶m->B); + if (crv == CKR_OK) { + SECItem B; + B.data = param->B.pGX; + B.len = param->B.ulGXLen; + K.data = NULL; + crv = jpake_mapStatus(JPAKE_Final(arena, &pqg.prime, &pqg.subPrime, + &x2, &gx4, &x2s, &B, &K), + CKR_MECHANISM_PARAM_INVALID); + } + + /* Save key material into CKA_VALUE. */ + if (crv == CKR_OK) + crv = sftk_forceAttribute(key, CKA_VALUE, K.data, K.len); + + if (crv == CKR_OK) + crv = jpake_enforceKeyType(key, CKK_GENERIC_SECRET); + + PORT_FreeArena(arena, PR_TRUE); + return crv; +} diff --git a/mozilla/security/nss/lib/softoken/legacydb/Makefile b/mozilla/security/nss/lib/softoken/legacydb/Makefile new file mode 100644 index 0000000..78a7c78 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/cdbhdl.h b/mozilla/security/nss/lib/softoken/legacydb/cdbhdl.h new file mode 100644 index 0000000..0323693 --- /dev/null +++ b/mozilla/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: cdbhdl.h,v 1.2 2007/06/13 00:24:57 rrelyea%redhat.com Exp $ + */ +#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/mozilla/security/nss/lib/softoken/legacydb/config.mk b/mozilla/security/nss/lib/softoken/legacydb/config.mk new file mode 100644 index 0000000..eb18479 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/config.mk @@ -0,0 +1,98 @@ +# +# ***** 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) + +EXTRA_LIBS += $(CRYPTOLIB) + +ifndef NSS_DISABLE_DBM +EXTRA_LIBS += $(DIST)/lib/$(LIB_PREFIX)dbm.$(LIB_SUFFIX) +endif + +# 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 \ + -L$(NSSUTIL_LIB_DIR) \ + -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 \ + -L$(NSSUTIL_LIB_DIR) \ + -lnssutil3 \ + -L$(NSPR_LIB_DIR) \ + -lplc4 \ + -lplds4 \ + -lnspr4 \ + $(NULL) +endif + +ifeq ($(OS_TARGET),SunOS) +OS_LIBS += -lbsm +endif + +ifeq ($(OS_TARGET),WINCE) +DEFINES += -DDBM_USING_NSPR +endif diff --git a/mozilla/security/nss/lib/softoken/legacydb/dbmshim.c b/mozilla/security/nss/lib/softoken/legacydb/dbmshim.c new file mode 100644 index 0000000..edbd98e --- /dev/null +++ b/mozilla/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: dbmshim.c,v 1.2 2007/06/13 00:24:57 rrelyea%redhat.com Exp $ + */ +#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/mozilla/security/nss/lib/softoken/legacydb/keydb.c b/mozilla/security/nss/lib/softoken/legacydb/keydb.c new file mode 100644 index 0000000..8fe9fb5 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/keydb.c @@ -0,0 +1,2272 @@ +/* ***** 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: keydb.c,v 1.11.22.1 2010/08/07 05:49:16 wtc%google.com Exp $ */ + +#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) { + SKIP_AFTER_FORK(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 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.type = siBuffer; /* encode as signed integer */ + 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, ¶m, + 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; + param.iter.type = siBuffer; /* decode as signed integer */ + + 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, ¶m, NSSLOWPasswordParamTemplate, + &edi.algorithm.parameters); + if (rv != SECSuccess) { + goto loser; + } + rv = SECITEM_CopyItem(NULL, salt, ¶m.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); + SKIP_AFTER_FORK(PZ_Lock(kdbLock)); + + (* db->close)(db); + + SKIP_AFTER_FORK(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/mozilla/security/nss/lib/softoken/legacydb/keydbi.h b/mozilla/security/nss/lib/softoken/legacydb/keydbi.h new file mode 100644 index 0000000..4885613 --- /dev/null +++ b/mozilla/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: keydbi.h,v 1.2 2007/06/13 00:24:57 rrelyea%redhat.com Exp $ */ + +#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/mozilla/security/nss/lib/softoken/legacydb/lgattr.c b/mozilla/security/nss/lib/softoken/legacydb/lgattr.c new file mode 100644 index 0000000..3446448 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/lgattr.c @@ -0,0 +1,1824 @@ +/* ***** 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" +#include "secasn1.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: + if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) { + return lg_CopyAttributeSigned(attribute, type, + key->u.ec.publicValue.data, + key->u.ec.publicValue.len); + } else { + SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL, + &(key->u.ec.publicValue), + SEC_ASN1_GET(SEC_OctetStringTemplate)); + CK_RV crv; + if (!pubValue) { + return CKR_HOST_MEMORY; + } + crv = lg_CopyAttributeSigned(attribute, type, + pubValue->data, + pubValue->len); + SECITEM_FreeItem(pubValue, PR_TRUE); + return crv; + } + 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/mozilla/security/nss/lib/softoken/legacydb/lgcreate.c b/mozilla/security/nss/lib/softoken/legacydb/lgcreate.c new file mode 100644 index 0000000..ab6c652 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/lgcreate.c @@ -0,0 +1,1011 @@ +/* ***** 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 "secasn1.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 = CKR_OK; + NSSLOWKEYPrivateKey *priv; + SECItem pubKeySpace = {siBuffer, NULL, 0}; + SECItem *pubKey; +#ifdef NSS_ENABLE_ECC + SECItem pubKey2Space = {siBuffer, NULL, 0}; + PRArenaPool *arena = NULL; +#endif /* NSS_ENABLE_ECC */ + 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; + } + + + pubKey = &pubKeySpace; + crv = lg_Attribute2SSecItem(NULL,pubKeyAttr,templ,count,pubKey); + if (crv != CKR_OK) return crv; + +#ifdef NSS_ENABLE_ECC + if (key_type == CKK_EC) { + SECStatus rv; + /* + * for ECC, use the decoded key first. + */ + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + crv = CKR_HOST_MEMORY; + goto done; + } + rv= SEC_QuickDERDecodeItem(arena, &pubKey2Space, + SEC_ASN1_GET(SEC_OctetStringTemplate), + pubKey); + if (rv != SECSuccess) { + /* decode didn't work, just try the pubKey */ + PORT_FreeArena(arena, PR_FALSE); + arena = NULL; + } else { + /* try the decoded pub key first */ + pubKey = &pubKey2Space; + } + } +#endif /* NSS_ENABLE_ECC */ + + PORT_Assert(pubKey->data); + if (pubKey->data == NULL) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + goto done; + } + keyHandle = lg_getKeyDB(sdb); + if (keyHandle == NULL) { + crv = CKR_TOKEN_WRITE_PROTECTED; + goto done; + } + 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*/); +#ifdef NSS_ENABLE_ECC + if (priv == NULL && pubKey == &pubKey2Space) { + /* no match on the decoded key, match the original pubkey */ + pubKey = &pubKeySpace; + priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey, + sdb /*password*/); + } +#endif + if (priv == NULL) { + /* the legacy database can only 'store' public keys which already + * have their corresponding private keys in the database */ + crv = CKR_ATTRIBUTE_VALUE_INVALID; + goto done; + } + nsslowkey_DestroyPrivateKey(priv); + crv = CKR_OK; + + *handle = lg_mkHandle(sdb, pubKey, LG_TOKEN_TYPE_PUB); + +done: + PORT_Free(pubKeySpace.data); +#ifdef NSS_ENABLE_ECC + if (arena) + PORT_FreeArena(arena, PR_FALSE); +#endif + + return crv; +} + +/* 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/mozilla/security/nss/lib/softoken/legacydb/lgdb.h b/mozilla/security/nss/lib/softoken/legacydb/lgdb.h new file mode 100644 index 0000000..ed6f129 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/lgdb.h @@ -0,0 +1,216 @@ +/* ***** 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 + +#ifndef XP_UNIX + +#define NO_CHECK_FORK + +#endif + +#ifndef NO_CHECK_FORK + +extern PRBool parentForkedAfterC_Initialize; +#define SKIP_AFTER_FORK(x) if (!parentForkedAfterC_Initialize) x + +#else + +#define SKIP_AFTER_FORK(x) x + +#endif /* NO_CHECK_FORK */ + +#endif /* _LGDB_H_ */ + diff --git a/mozilla/security/nss/lib/softoken/legacydb/lgdestroy.c b/mozilla/security/nss/lib/softoken/legacydb/lgdestroy.c new file mode 100644 index 0000000..37db2e5 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/lgfind.c b/mozilla/security/nss/lib/softoken/legacydb/lgfind.c new file mode 100644 index 0000000..29aab3d --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/lgfind.c @@ -0,0 +1,946 @@ +/* ***** 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; + break; + } + 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; + break; + } + 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; + break; + } + 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_CERTIFICATE_TYPE,&pTemplate[i], + 1,&certType); + if (crv != CKR_OK) { + classFlags = 0; + break; + } + 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; + break; + } + 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/mozilla/security/nss/lib/softoken/legacydb/lginit.c b/mozilla/security/nss/lib/softoken/legacydb/lginit.c new file mode 100644 index 0000000..6236b78 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/lginit.c @@ -0,0 +1,674 @@ +/* + * 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: lginit.c,v 1.14.22.1 2011/01/06 19:55:02 wtc%google.com Exp $ */ + +#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; + SKIP_AFTER_FORK(PR_Lock(lgdb_p->dbLock)); +} + +void +lg_DBUnlock(SDB *sdb) +{ + LGPrivate *lgdb_p = (LGPrivate *)sdb->private; + SKIP_AFTER_FORK(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; +} + +PRBool parentForkedAfterC_Initialize; + +void lg_SetForkState(PRBool forked) +{ + parentForkedAfterC_Initialize = forked; +} + +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) { + SKIP_AFTER_FORK(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->private = lgdb_p; + sdb->version = 0; + sdb->sdb_type = SDB_LEGACY; + sdb->sdb_flags = flags; + sdb->app_private = NULL; + 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; + sdb->sdb_SetForkState = lg_SetForkState; + + *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(PRBool forked) +{ + lg_SetForkState(forked); + nsslowcert_DestroyFreeLists(); + nsslowcert_DestroyGlobalLocks(); + SECOID_Shutdown(); + lg_SetForkState(PR_FALSE); + return CKR_OK; +} + diff --git a/mozilla/security/nss/lib/softoken/legacydb/lgutil.c b/mozilla/security/nss/lib/softoken/legacydb/lgutil.c new file mode 100644 index 0000000..f474b5f --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/lowcert.c b/mozilla/security/nss/lib/softoken/legacydb/lowcert.c new file mode 100644 index 0000000..703faeb --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/lowcert.c @@ -0,0 +1,831 @@ +/* ***** 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: lowcert.c,v 1.5 2009/04/12 01:31:45 nelson%bolyard.com Exp $ + */ + +#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, + ¬Before->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, + ¬After->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, ¬BeforeA, ¬AfterA); + if ( rv != SECSuccess ) { + return(PR_FALSE); + } + + rv = nsslowcert_GetCertTimes(certb, ¬BeforeB, ¬AfterB); + 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; + } + if (len > NSS_MAX_LEGACY_DB_KEY_SIZE) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + 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/mozilla/security/nss/lib/softoken/legacydb/lowkey.c b/mozilla/security/nss/lib/softoken/legacydb/lowkey.c new file mode 100644 index 0000000..5ee64d1 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/lowkeyi.h b/mozilla/security/nss/lib/softoken/legacydb/lowkeyi.h new file mode 100644 index 0000000..a40ff95 --- /dev/null +++ b/mozilla/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: lowkeyi.h,v 1.2 2007/06/13 00:24:57 rrelyea%redhat.com Exp $ */ + +#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/mozilla/security/nss/lib/softoken/legacydb/lowkeyti.h b/mozilla/security/nss/lib/softoken/legacydb/lowkeyti.h new file mode 100644 index 0000000..7fa74da --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/manifest.mn b/mozilla/security/nss/lib/softoken/legacydb/manifest.mn new file mode 100644 index 0000000..a761c92 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/manifest.mn @@ -0,0 +1,63 @@ +# +# ***** 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))\" + +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/mozilla/security/nss/lib/softoken/legacydb/nssdbm.def b/mozilla/security/nss/lib/softoken/legacydb/nssdbm.def new file mode 100644 index 0000000..bf7f7a5 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/nssdbm.rc b/mozilla/security/nss/lib/softoken/legacydb/nssdbm.rc new file mode 100644 index 0000000..c6944a3 --- /dev/null +++ b/mozilla/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,SOFTOKEN_VBUILD + PRODUCTVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,SOFTOKEN_VBUILD + 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/mozilla/security/nss/lib/softoken/legacydb/pcert.h b/mozilla/security/nss/lib/softoken/legacydb/pcert.h new file mode 100644 index 0000000..68956ab --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/legacydb/pcertdb.c b/mozilla/security/nss/lib/softoken/legacydb/pcertdb.c new file mode 100644 index 0000000..74c8f9d --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/pcertdb.c @@ -0,0 +1,5389 @@ +/* ***** 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: pcertdb.c,v 1.11 2009/04/13 17:23:15 nelson%bolyard.com Exp $ + */ +#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); + + SKIP_AFTER_FORK(PZ_Lock(freeListLock)); + return; +} + +/* + * Free the cert reference count lock + */ +static void +nsslowcert_UnlockFreeList(void) +{ + PRStatus prstat = PR_SUCCESS; + + PORT_Assert(freeListLock != NULL); + + SKIP_AFTER_FORK(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 = PR_SUCCESS; + + PORT_Assert(dbLock != NULL); + SKIP_AFTER_FORK(PZ_Lock(dbLock)); + + (* db->close)(db); + + SKIP_AFTER_FORK(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] = (PRUint8)( entry->trust.sslFlags >> 8 ); + buf[1] = (PRUint8)( entry->trust.sslFlags ); + buf[2] = (PRUint8)( entry->trust.emailFlags >> 8 ); + buf[3] = (PRUint8)( entry->trust.emailFlags ); + buf[4] = (PRUint8)( entry->trust.objectSigningFlags >> 8 ); + buf[5] = (PRUint8)( entry->trust.objectSigningFlags ); + buf[6] = (PRUint8)( entry->derCert.len >> 8 ); + buf[7] = (PRUint8)( entry->derCert.len ); + buf[8] = (PRUint8)( nnlen >> 8 ); + buf[9] = (PRUint8)( nnlen ); + + 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 (len > NSS_MAX_LEGACY_DB_KEY_SIZE) + goto loser; + 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; + if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE) + goto loser; + 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] ); + lenoff = dbentry->len - ( entry->derCert.len + nnlen + headerlen ); + if ( lenoff ) { + if ( lenoff < 0 || (lenoff & 0xffff) != 0 ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + goto loser; + } + /* The cert size exceeded 64KB. Reconstruct the correct length. */ + entry->derCert.len += lenoff; + } + + /* 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] = (PRUint8)( entry->derCrl.len >> 8 ); + buf[1] = (PRUint8)( entry->derCrl.len ); + buf[2] = (PRUint8)( nnlen >> 8 ); + buf[3] = (PRUint8)( nnlen ); + + 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 urlLen; + int lenDiff; + + /* 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] ); + urlLen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] ); + lenDiff = dbentry->len - + (entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN); + if (lenDiff) { + if (lenDiff < 0 || (lenDiff & 0xffff) != 0) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + goto loser; + } + /* CRL entry is greater than 64 K. Hack to make this continue to work */ + entry->derCrl.len += lenDiff; + } + + /* copy the der CRL */ + 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 (urlLen != 0) { + entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen); + 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], + urlLen); + } + + 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] = (PRUint8)( entry->subjectName.len >> 8 ); + buf[1] = (PRUint8)( entry->subjectName.len ); + 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; + if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE) + goto loser; + 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) +{ + int lenDiff; + + /* 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] ); + lenDiff = dbentry->len - + (entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN); + if (lenDiff) { + if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + goto loser; + } + /* The entry size exceeded 64KB. Reconstruct the correct length. */ + entry->subjectName.len += lenDiff; + } + + /* 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] = (PRUint8)( entry->subjectName.len >> 8 ); + buf[1] = (PRUint8)( entry->subjectName.len ); + buf[2] = (PRUint8)( entry->smimeOptions.len >> 8 ); + buf[3] = (PRUint8)( entry->smimeOptions.len ); + buf[4] = (PRUint8)( entry->optionsDate.len >> 8 ); + buf[5] = (PRUint8)( entry->optionsDate.len ); + + /* 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; + if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE) + goto loser; + 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) +{ + int lenDiff; + + /* 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] ); + lenDiff = dbentry->len - (entry->subjectName.len + + entry->smimeOptions.len + + entry->optionsDate.len + + DB_SMIME_ENTRY_HEADER_LEN); + if (lenDiff) { + if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + goto loser; + } + /* The entry size exceeded 64KB. Reconstruct the correct length. */ + entry->subjectName.len += lenDiff; + } + + /* 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 = entry->certKeys; + SECItem *keyIDs = entry->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++ ) { + if (keyIDs[i].len > 0xffff || + (certKeys[i].len > 0xffff)) { + PORT_SetError(SEC_ERROR_INPUT_LEN); + goto loser; + } + len += certKeys[i].len; + len += 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] = (PRUint8)( ncerts >> 8 ); + buf[1] = (PRUint8)( ncerts ); + buf[2] = (PRUint8)( nnlen >> 8 ); + buf[3] = (PRUint8)( nnlen ); + /* v7 email field is NULL in v8 */ + buf[4] = 0; + buf[5] = 0; + + PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen); + tmpbuf = &buf[keyidoff]; + for ( i = 0; i < ncerts; i++ ) { + tmpbuf[0] = (PRUint8)( certKeys[i].len >> 8 ); + tmpbuf[1] = (PRUint8)( certKeys[i].len ); + tmpbuf += 2; + } + for ( i = 0; i < ncerts; i++ ) { + tmpbuf[0] = (PRUint8)( keyIDs[i].len >> 8 ); + tmpbuf[1] = (PRUint8)( keyIDs[i].len ); + tmpbuf += 2; + } + + for ( i = 0; i < ncerts; i++ ) { + PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len); + tmpbuf += certKeys[i].len; + } + for ( i = 0; i < ncerts; i++ ) { + PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len); + tmpbuf += keyIDs[i].len; + } + + if (entry->emailAddrs) { + tmpbuf[0] = (PRUint8)( entry->nemailAddrs >> 8 ); + tmpbuf[1] = (PRUint8)( entry->nemailAddrs ); + tmpbuf += 2; + for (i=0; i < entry->nemailAddrs; i++) { + int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1; + tmpbuf[0] = (PRUint8)( nameLen >> 8 ); + tmpbuf[1] = (PRUint8)( nameLen ); + 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; + if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE) + goto loser; + 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) +{ + PRArenaPool *arena = entry->common.arena; + unsigned char *tmpbuf; + unsigned char *end; + void *mark = PORT_ArenaMark(arena); + unsigned int eaddrlen; + unsigned int i; + unsigned int keyidoff; + unsigned int len; + unsigned int ncerts = 0; + unsigned int nnlen; + SECStatus rv; + + 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] ); + keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen; + len = keyidoff + (4 * ncerts); + if ( dbentry->len < len) { + PORT_SetError(SEC_ERROR_BAD_DATABASE); + goto loser; + } + + entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts); + entry->keyIDs = PORT_ArenaNewArray(arena, 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 = PORT_ArenaNewArray(arena, char *, 2); + 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. + */ + tmpbuf = &dbentry->data[keyidoff]; + for ( i = 0; i < ncerts; i++ ) { + unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1]; + entry->certKeys[i].len = itemlen; + len += itemlen; + tmpbuf += 2; + } + for ( i = 0; i < ncerts; i++ ) { + unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1] ; + entry->keyIDs[i].len = itemlen; + len += itemlen; + tmpbuf += 2; + } + + /* is encoded entry large enough ? */ + if ( len > dbentry->len ){ + PORT_SetError(SEC_ERROR_BAD_DATABASE); + goto loser; + } + + for ( i = 0; i < ncerts; i++ ) { + unsigned int kLen = entry->certKeys[i].len; + entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen); + if ( entry->certKeys[i].data == NULL ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen); + tmpbuf += kLen; + } + for ( i = 0; i < ncerts; i++ ) { + unsigned int iLen = entry->keyIDs[i].len; + entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen); + if ( entry->keyIDs[i].data == NULL ) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen); + tmpbuf += iLen; + } + + end = dbentry->data + dbentry->len; + if ((eaddrlen == 0) && (end - tmpbuf > 1)) { + /* read in the additional email addresses */ + entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1]; + tmpbuf += 2; + if (end - tmpbuf < 2 * (int)entry->nemailAddrs) + goto loser; + entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs); + if (entry->emailAddrs == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + for (i=0; i < entry->nemailAddrs; i++) { + int nameLen; + if (end - tmpbuf < 2) { + goto loser; + } + nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1]; + tmpbuf += 2; + if (end - tmpbuf < nameLen) { + goto loser; + } + entry->emailAddrs[i] = PORT_ArenaAlloc(arena,nameLen); + if (entry->emailAddrs == NULL) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen); + tmpbuf += nameLen; + } + if (tmpbuf != end) + goto loser; + } + PORT_ArenaUnmark(arena, mark); + return(SECSuccess); + +loser: + PORT_ArenaRelease(arena, mark); /* discard above allocations */ + 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 = SECSuccess; + 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); + } + /* here, ret is zero and rv is SECSuccess. + * Below here, ret is a count of successful calls to the callback function. + */ + 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 ) { + ++ret; + } + } + } while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 ); + /* If any callbacks succeeded, or no calls to callbacks were made, + * then report success. Otherwise, report failure. + */ + return (ret ? SECSuccess : rv); +} +/* + * 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(); + SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock)); + freeListLock = NULL; +} + +void +nsslowcert_DestroyGlobalLocks(void) +{ + if (dbLock) { + SKIP_AFTER_FORK(PZ_DestroyLock(dbLock)); + dbLock = NULL; + } + if (certRefCountLock) { + SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock)); + certRefCountLock = NULL; + } + if (certTrustLock) { + SKIP_AFTER_FORK(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/mozilla/security/nss/lib/softoken/legacydb/pcertt.h b/mozilla/security/nss/lib/softoken/legacydb/pcertt.h new file mode 100644 index 0000000..6862d36 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/legacydb/pcertt.h @@ -0,0 +1,452 @@ +/* ***** 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: pcertt.h,v 1.3 2009/04/12 01:31:46 nelson%bolyard.com Exp $ + */ +#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 */ + +#define NSS_MAX_LEGACY_DB_KEY_SIZE (60 * 1024) + +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/mozilla/security/nss/lib/softoken/legacydb/pk11db.c b/mozilla/security/nss/lib/softoken/legacydb/pk11db.c new file mode 100644 index 0000000..f85a0a6 --- /dev/null +++ b/mozilla/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,¶m,&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/mozilla/security/nss/lib/softoken/lgglue.c b/mozilla/security/nss/lib/softoken/lgglue.c new file mode 100644 index 0000000..3047e40 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/lgglue.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): + * + * 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" +#include "softoken.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 const char *LEGACY_LIB_NAME = + SHLIB_PREFIX"nssdbm"SHLIB_VERSION"."SHLIB_SUFFIX; +/* + * 2 bools to tell us if we've check the legacy library successfully or + * not. Initialize on startup to false by the C BSS segment; + */ +static PRBool legacy_glue_libCheckFailed; /* set if we failed the check */ +static PRBool legacy_glue_libCheckSucceeded; /* set if we passed the check */ +static PRLibrary *legacy_glue_lib = NULL; +static SECStatus +sftkdbLoad_Legacy(PRBool isFIPS) +{ + PRLibrary *lib = NULL; + LGSetCryptFunc setCryptFunction = NULL; + + if (legacy_glue_lib) { + /* this check is necessary because it's possible we loaded the + * legacydb to read secmod.db, which told us whether we were in + * FIPS mode or not. */ + if (isFIPS && !legacy_glue_libCheckSucceeded) { + if (legacy_glue_libCheckFailed || + !BLAPI_SHVerify(LEGACY_LIB_NAME,(PRFuncPtr)legacy_glue_open)) { + legacy_glue_libCheckFailed = PR_TRUE; + /* don't clobber legacy glue to avoid race. just let it + * get cleared in shutdown */ + return SECFailure; + } + legacy_glue_libCheckSucceeded = PR_TRUE; + } + return SECSuccess; + } + + lib = sftkdb_LoadLibrary(LEGACY_LIB_NAME); + 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; + } + + /* verify the loaded library if we are in FIPS mode */ + if (isFIPS) { + if (!BLAPI_SHVerify(LEGACY_LIB_NAME,(PRFuncPtr)legacy_glue_open)) { + PR_UnloadLibrary(lib); + return SECFailure; + } + legacy_glue_libCheckSucceeded = PR_TRUE; + } + + 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, PRBool isFIPS, + SDB **certDB, SDB **keyDB) +{ + SECStatus rv; + + rv = sftkdbLoad_Legacy(isFIPS); + 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(PR_FALSE); + 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(PR_FALSE); + 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(PR_FALSE); + 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(PR_FALSE); + 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) { +#ifdef NO_FORK_CHECK + PRBool parentForkedAfterC_Initialize = PR_FALSE; +#endif + crv = (*legacy_glue_shutdown)(parentForkedAfterC_Initialize); + } + 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; + legacy_glue_libCheckFailed = PR_FALSE; + legacy_glue_libCheckSucceeded = PR_FALSE; + return crv; +} + + diff --git a/mozilla/security/nss/lib/softoken/lgglue.h b/mozilla/security/nss/lib/softoken/lgglue.h new file mode 100644 index 0000000..091ff82 --- /dev/null +++ b/mozilla/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)(PRBool forked); +typedef void (*LGSetForkStateFunc)(PRBool); +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, PRBool isFIPS, + 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/mozilla/security/nss/lib/softoken/lowkey.c b/mozilla/security/nss/lib/softoken/lowkey.c new file mode 100644 index 0000000..4174cef --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/lowkeyi.h b/mozilla/security/nss/lib/softoken/lowkeyi.h new file mode 100644 index 0000000..ec6f0e9 --- /dev/null +++ b/mozilla/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: lowkeyi.h,v 1.11 2007/06/13 00:24:56 rrelyea%redhat.com Exp $ */ + +#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/mozilla/security/nss/lib/softoken/lowkeyti.h b/mozilla/security/nss/lib/softoken/lowkeyti.h new file mode 100644 index 0000000..15bb973 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/lowpbe.c b/mozilla/security/nss/lib/softoken/lowpbe.c new file mode 100644 index 0000000..f918398 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/lowpbe.h b/mozilla/security/nss/lib/softoken/lowpbe.h new file mode 100644 index 0000000..974b2fa --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/manifest.mn b/mozilla/security/nss/lib/softoken/manifest.mn new file mode 100644 index 0000000..d70470d --- /dev/null +++ b/mozilla/security/nss/lib/softoken/manifest.mn @@ -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) 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 SQLITE_INCLUDE_DIR +INCLUDES += -I$(SQLITE_INCLUDE_DIR) +endif + +EXPORTS = \ + secmodt.h \ + $(NULL) + +PRIVATE_EXPORTS = \ + lgglue.h \ + lowkeyi.h \ + lowkeyti.h \ + pk11pars.h \ + pkcs11ni.h \ + softoken.h \ + softoknt.h \ + softkver.h \ + sdb.h \ + sftkdbt.h \ + pk11init.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 \ + jpakesftk.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/mozilla/security/nss/lib/softoken/padbuf.c b/mozilla/security/nss/lib/softoken/padbuf.c new file mode 100644 index 0000000..8c43fa2 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/pk11init.h b/mozilla/security/nss/lib/softoken/pk11init.h new file mode 100644 index 0000000..d91a0d3 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/pk11init.h @@ -0,0 +1,55 @@ +/* ***** 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 header file included in pk11wrap dir, or in softoken + */ +#ifndef _PK11_INIT_H_ +#define _PK11_INIT_H_ 1 + +/* hold slot default flags until we initialize a slot. This structure is only + * useful between the time we define a module (either by hand or from the + * database) and the time the module is loaded. Not reference counted */ +struct PK11PreSlotInfoStr { + CK_SLOT_ID slotID; /* slot these flags are for */ + unsigned long defaultFlags; /* bit mask of default implementation this slot + * provides */ + int askpw; /* slot specific password bits */ + long timeout; /* slot specific timeout value */ + char hasRootCerts; /* is this the root cert PKCS #11 module? */ + char hasRootTrust; /* is this the root cert PKCS #11 module? */ +}; + +#endif /* _PK11_INIT_H_ 1 */ diff --git a/mozilla/security/nss/lib/softoken/pk11pars.h b/mozilla/security/nss/lib/softoken/pk11pars.h new file mode 100644 index 0000000..dea16e7 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/pk11pars.h @@ -0,0 +1,872 @@ +/* ***** 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(SEED,SECMOD_SEED_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((unsigned char )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/mozilla/security/nss/lib/softoken/pkcs11.c b/mozilla/security/nss/lib/softoken/pkcs11.c new file mode 100644 index 0000000..7a2425c --- /dev/null +++ b/mozilla/security/nss/lib/softoken/pkcs11.c @@ -0,0 +1,4631 @@ +/* ***** 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 "prtypes.h" +#include "nspr.h" +#include "softkver.h" +#include "secoid.h" +#include "sftkdb.h" +#include "sftkpars.h" +#include "ec.h" +#include "secasn1.h" + +PRBool parentForkedAfterC_Initialize; + +#ifndef NO_FORK_CHECK + +PRBool sftkForkCheckDisabled; + +#if defined(CHECK_FORK_PTHREAD) || defined(CHECK_FORK_MIXED) +PRBool forked = PR_FALSE; +#endif + +#if defined(CHECK_FORK_GETPID) || defined(CHECK_FORK_MIXED) +#include <unistd.h> +pid_t myPid; +#endif + +#ifdef CHECK_FORK_MIXED +#include <sys/systeminfo.h> +PRBool usePthread_atfork; +#endif + +#endif + +/* + * ******************** 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}, + /* ------------------------- SEED Operations --------------------------- */ + {CKM_SEED_KEY_GEN, {16, 16, CKF_GENERATE}, PR_TRUE}, + {CKM_SEED_ECB, {16, 16, CKF_EN_DE_WR_UN}, PR_TRUE}, + {CKM_SEED_CBC, {16, 16, CKF_EN_DE_WR_UN}, PR_TRUE}, + {CKM_SEED_MAC, {16, 16, CKF_SN_VR}, PR_TRUE}, + {CKM_SEED_MAC_GENERAL, {16, 16, CKF_SN_VR}, PR_TRUE}, + {CKM_SEED_CBC_PAD, {16, 16, 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}, + /* ------------------------- HKDF Operations -------------------------- */ + {CKM_NSS_HKDF_SHA1, {1, 128, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_HKDF_SHA256, {1, 128, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_HKDF_SHA384, {1, 128, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_HKDF_SHA512, {1, 128, CKF_DERIVE}, PR_TRUE}, + /* ------------------------- 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}, + /* --------------------------- J-PAKE -------------------------------- */ + {CKM_NSS_JPAKE_ROUND1_SHA1, {0, 0, CKF_GENERATE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND1_SHA256, {0, 0, CKF_GENERATE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND1_SHA384, {0, 0, CKF_GENERATE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND1_SHA512, {0, 0, CKF_GENERATE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND2_SHA1, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND2_SHA256, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND2_SHA384, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_ROUND2_SHA512, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_FINAL_SHA1, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_FINAL_SHA256, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_FINAL_SHA384, {0, 0, CKF_DERIVE}, PR_TRUE}, + {CKM_NSS_JPAKE_FINAL_SHA512, {0, 0, CKF_DERIVE}, PR_TRUE} +}; +static const CK_ULONG mechanismCount = sizeof(mechanisms)/sizeof(mechanisms[0]); + +/* sigh global so fipstokn can read it */ +PRBool nsc_init = PR_FALSE; + +#if defined(CHECK_FORK_PTHREAD) || defined(CHECK_FORK_MIXED) + +#include <pthread.h> + +static 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, PR_FALSE); + } + 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_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; + } + 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; + } + 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); + +static SECStatus +sftk_fillRSAPrivateKey(SFTKObject *object); + +/* + * 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; + PRBool createObjectInfo = PR_TRUE; + int missing_rsa_mod_component = 0; + int missing_rsa_exp_component = 0; + int missing_rsa_crt_component = 0; + + SECItem mod; + CK_RV crv; + + switch (key_type) { + case CKK_RSA: + if ( !sftk_hasAttribute(object, CKA_MODULUS)) { + missing_rsa_mod_component++; + } + if ( !sftk_hasAttribute(object, CKA_PUBLIC_EXPONENT)) { + missing_rsa_exp_component++; + } + if ( !sftk_hasAttribute(object, CKA_PRIVATE_EXPONENT)) { + missing_rsa_exp_component++; + } + if ( !sftk_hasAttribute(object, CKA_PRIME_1)) { + missing_rsa_mod_component++; + } + if ( !sftk_hasAttribute(object, CKA_PRIME_2)) { + missing_rsa_mod_component++; + } + if ( !sftk_hasAttribute(object, CKA_EXPONENT_1)) { + missing_rsa_crt_component++; + } + if ( !sftk_hasAttribute(object, CKA_EXPONENT_2)) { + missing_rsa_crt_component++; + } + if ( !sftk_hasAttribute(object, CKA_COEFFICIENT)) { + missing_rsa_crt_component++; + } + if (missing_rsa_mod_component || missing_rsa_exp_component || + missing_rsa_crt_component) { + /* we are missing a component, see if we have enough to rebuild + * the rest */ + int have_exp = 2- missing_rsa_exp_component; + int have_component = 5- + (missing_rsa_exp_component+missing_rsa_mod_component); + SECStatus rv; + + if ((have_exp == 0) || (have_component < 3)) { + /* nope, not enough to reconstruct the private key */ + return CKR_TEMPLATE_INCOMPLETE; + } + /*fill in the missing parameters */ + rv = sftk_fillRSAPrivateKey(object); + if (rv != SECSuccess) { + 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 */ + case CKK_NSS_JPAKE_ROUND1: + if (!sftk_hasAttribute(object, CKA_PRIME || + !sftk_hasAttribute(object, CKA_SUBPRIME) || + !sftk_hasAttribute(object, CKA_BASE))) { + return CKR_TEMPLATE_INCOMPLETE; + } + /* fall through */ + case CKK_NSS_JPAKE_ROUND2: + /* CKA_NSS_JPAKE_SIGNERID and CKA_NSS_JPAKE_PEERID are checked in + the J-PAKE code. */ + encrypt = sign = recover = wrap = CK_FALSE; + derive = CK_TRUE; + createObjectInfo = PR_FALSE; + break; + 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 if (createObjectInfo) { + 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; + case CKK_AES: + attribute = sftk_FindAttribute(object,CKA_VALUE); + /* shouldn't happen */ + if (attribute == NULL) + return CKR_TEMPLATE_INCOMPLETE; + if (attribute->attrib.ulValueLen != 16 && + attribute->attrib.ulValueLen != 24 && + attribute->attrib.ulValueLen != 32) { + sftk_FreeAttribute(attribute); + return CKR_KEY_SIZE_RANGE; + } + crv = sftk_forceAttribute(object, CKA_VALUE_LEN, + &attribute->attrib.ulValueLen, sizeof(CK_ULONG)); + 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 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(¶ms,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); + if (crv == CKR_OK) { + int keyLen,curveLen; + + curveLen = (pubKey->u.ec.ecParams.fieldID.size +7)/8; + keyLen = (2*curveLen)+1; + + /* special note: We can't just use the first byte to determine + * between these 2 cases because both EC_POINT_FORM_UNCOMPRESSED + * and SEC_ASN1_OCTET_STRING are 0x04 */ + + /* handle the non-DER encoded case (UNCOMPRESSED only) */ + if (pubKey->u.ec.publicValue.data[0] == EC_POINT_FORM_UNCOMPRESSED + && pubKey->u.ec.publicValue.len == keyLen) { + break; /* key was not DER encoded, no need to unwrap */ + } + + /* if we ever support compressed, handle it here */ + + /* handle the encoded case */ + if ((pubKey->u.ec.publicValue.data[0] == SEC_ASN1_OCTET_STRING) + && pubKey->u.ec.publicValue.len > keyLen) { + SECItem publicValue; + SECStatus rv; + + rv = SEC_QuickDERDecodeItem(arena, &publicValue, + SEC_ASN1_GET(SEC_OctetStringTemplate), + &pubKey->u.ec.publicValue); + /* nope, didn't decode correctly */ + if ((rv != SECSuccess) + || (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) + || (publicValue.len != keyLen)) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + break; + } + /* replace our previous with the decoded key */ + pubKey->u.ec.publicValue = publicValue; + break; + } + crv = CKR_ATTRIBUTE_VALUE_INVALID; + } + 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; +} + +/* + * we have a partial rsa private key, fill in the rest + */ +static SECStatus +sftk_fillRSAPrivateKey(SFTKObject *object) +{ + RSAPrivateKey tmpKey = { 0 }; + SFTKAttribute *modulus = NULL; + SFTKAttribute *prime1 = NULL; + SFTKAttribute *prime2 = NULL; + SFTKAttribute *privateExponent = NULL; + SFTKAttribute *publicExponent = NULL; + SECStatus rv; + CK_RV crv; + + /* first fill in the components that we have. Populate only uses + * the non-crt components, so only fill those in */ + tmpKey.arena = NULL; + modulus = sftk_FindAttribute(object, CKA_MODULUS); + if (modulus) { + tmpKey.modulus.data = modulus->attrib.pValue; + tmpKey.modulus.len = modulus->attrib.ulValueLen; + } + prime1 = sftk_FindAttribute(object, CKA_PRIME_1); + if (prime1) { + tmpKey.prime1.data = prime1->attrib.pValue; + tmpKey.prime1.len = prime1->attrib.ulValueLen; + } + prime2 = sftk_FindAttribute(object, CKA_PRIME_2); + if (prime2) { + tmpKey.prime2.data = prime2->attrib.pValue; + tmpKey.prime2.len = prime2->attrib.ulValueLen; + } + privateExponent = sftk_FindAttribute(object, CKA_PRIVATE_EXPONENT); + if (privateExponent) { + tmpKey.privateExponent.data = privateExponent->attrib.pValue; + tmpKey.privateExponent.len = privateExponent->attrib.ulValueLen; + } + publicExponent = sftk_FindAttribute(object, CKA_PUBLIC_EXPONENT); + if (publicExponent) { + tmpKey.publicExponent.data = publicExponent->attrib.pValue; + tmpKey.publicExponent.len = publicExponent->attrib.ulValueLen; + } + + /* + * populate requires one exponent plus 2 other components to work. + * we expected our caller to check that first. If that didn't happen, + * populate will simply return an error here. + */ + rv = RSA_PopulatePrivateKey(&tmpKey); + if (rv != SECSuccess) { + goto loser; + } + + /* now that we have a fully populated key, set all our attribute values */ + rv = SECFailure; + crv = sftk_forceAttribute(object,CKA_MODULUS, + sftk_item_expand(&tmpKey.modulus)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_PUBLIC_EXPONENT, + sftk_item_expand(&tmpKey.publicExponent)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_PRIVATE_EXPONENT, + sftk_item_expand(&tmpKey.privateExponent)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_PRIME_1, + sftk_item_expand(&tmpKey.prime1)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_PRIME_2, + sftk_item_expand(&tmpKey.prime2)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_EXPONENT_1, + sftk_item_expand(&tmpKey.exponent1)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_EXPONENT_2, + sftk_item_expand(&tmpKey.exponent2)); + if (crv != CKR_OK) goto loser; + crv = sftk_forceAttribute(object,CKA_COEFFICIENT, + sftk_item_expand(&tmpKey.coefficient)); + if (crv != CKR_OK) goto loser; + rv = SECSuccess; + + /* we're done (one way or the other), clean up all our stuff */ +loser: + if (tmpKey.arena) { + PORT_FreeArena(tmpKey.arena,PR_TRUE); + } + if (modulus) { + sftk_FreeAttribute(modulus); + } + if (prime1) { + sftk_FreeAttribute(prime1); + } + if (prime2) { + sftk_FreeAttribute(prime2); + } + if (privateExponent) { + sftk_FreeAttribute(privateExponent); + } + if (publicExponent) { + sftk_FreeAttribute(publicExponent); + } + return rv; +} + + + + + + + +/* 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, + moduleIndex == NSC_FIPS_MODULE, + &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, PRBool logout) +{ + SFTKSession *session; + unsigned int i; + SFTKDBHandle *handle; + + /* first log out the card */ + /* special case - if we are in a middle of upgrade, we want to close the + * sessions to fake a token removal to tell the upper level code we have + * switched from one database to another, but we don't want to + * explicity logout in case we can continue the upgrade with the + * existing password if possible. + */ + if (logout) { + handle = sftk_getKeyDB(slot); + SKIP_AFTER_FORK(PZ_Lock(slot->slotLock)); + slot->isLoggedIn = PR_FALSE; + if (slot->needLogin && handle) { + sftkdb_ClearPassword(handle); + } + SKIP_AFTER_FORK(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 { + SKIP_AFTER_FORK(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; + SKIP_AFTER_FORK(PZ_Unlock(lock)); + SKIP_AFTER_FORK(PZ_Lock(slot->slotLock)); + --slot->sessionCount; + SKIP_AFTER_FORK(PZ_Unlock(slot->slotLock)); + if (session->info.flags & CKF_RW_SESSION) { + PR_AtomicDecrement(&slot->rwSessionCount); + } + } else { + SKIP_AFTER_FORK(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; + SKIP_AFTER_FORK(PZ_Lock(slot->slotLock)); + certHandle = slot->certDB; + slot->certDB = NULL; + keyHandle = slot->keyDB; + slot->keyDB = NULL; + SKIP_AFTER_FORK(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, PR_TRUE); + } + + /* 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 */ + SKIP_AFTER_FORK(PZ_DestroyLock(slot->slotLock)); + slot->slotLock = NULL; + if (slot->sessionLock) { + for (i=0; i < slot->numSessionLocks; i++) { + if (slot->sessionLock[i]) { + SKIP_AFTER_FORK(PZ_DestroyLock(slot->sessionLock[i])); + slot->sessionLock[i] = NULL; + } + } + PORT_Free(slot->sessionLock); + slot->sessionLock = NULL; + } + if (slot->objectLock) { + SKIP_AFTER_FORK(PZ_DestroyLock(slot->objectLock)); + slot->objectLock = NULL; + } + if (slot->pwCheckLock) { + SKIP_AFTER_FORK(PR_DestroyLock(slot->pwCheckLock)); + slot->pwCheckLock = NULL; + } + PORT_Free(slot); + return CKR_OK; +} + +#ifndef NO_FORK_CHECK + +static CK_RV ForkCheck(void) +{ + CHECK_FORK(); + return CKR_OK; +} + +#endif + +/* + * handle the SECMOD.db + */ +char ** +NSC_ModuleDBFunc(unsigned long function,char *parameters, void *args) +{ + char *secmod = NULL; + char *appName = NULL; + char *filename = NULL; +#ifdef NSS_DISABLE_DBM + SDBType dbType = SDB_SQL; +#else + SDBType dbType = SDB_LEGACY; +#endif + PRBool rw; + static char *success="Success"; + char **rvstr = NULL; + +#ifndef NO_FORK_CHECK + if (CKR_OK != ForkCheck()) 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); + } + + ENABLE_FORK_CHECK(); + + 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; + } + rv = BL_Init(); /* initialize freebl engine */ + if (rv != SECSuccess) { + crv = CKR_DEVICE_ERROR; + return crv; + } + + /* 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, ¶mStrings, 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, NSS_AUDIT_FIPS_STATE, + "enabled FIPS mode"); + } else { + sftk_LogAuditMessage(NSS_AUDIT_INFO, NSS_AUDIT_FIPS_STATE, + "disabled FIPS mode"); + } + } + } + + for (i=0; i < paramStrings.token_count; i++) { + crv = SFTK_SlotInit(paramStrings.configdir, + paramStrings.updatedir, paramStrings.updateID, + ¶mStrings.tokens[i], moduleIndex); + if (crv != CKR_OK) { + nscFreeAllSlots(moduleIndex); + break; + } + } +loser: + sftk_freeParams(¶mStrings); + } + if (CKR_OK == crv) { + sftk_InitFreeLists(); + } + +#ifndef NO_FORK_CHECK + if (CKR_OK == crv) { +#if defined(CHECK_FORK_MIXED) + /* Before Solaris 10, fork handlers are not unregistered at dlclose() + * time. So, we only use pthread_atfork on Solaris 10 and later. For + * earlier versions, we use PID checks. + */ + char buf[200]; + int major = 0, minor = 0; + + long rv = sysinfo(SI_RELEASE, buf, sizeof(buf)); + if (rv > 0 && rv < sizeof(buf)) { + if (2 == sscanf(buf, "%d.%d", &major, &minor)) { + /* Are we on Solaris 10 or greater ? */ + if (major >5 || (5 == major && minor >= 10)) { + /* we are safe to use pthread_atfork */ + usePthread_atfork = PR_TRUE; + } + } + } + if (usePthread_atfork) { + pthread_atfork(NULL, NULL, ForkedChild); + } else { + myPid = getpid(); + } + +#elif defined(CHECK_FORK_PTHREAD) + pthread_atfork(NULL, NULL, ForkedChild); +#elif defined(CHECK_FORK_GETPID) + myPid = getpid(); +#else +#error Incorrect fork check method. +#endif + } +#endif + return crv; +} + +CK_RV NSC_Initialize(CK_VOID_PTR pReserved) +{ + CK_RV crv; + + sftk_ForkReset(pReserved, &crv); + + 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) +{ + /* propagate the fork status to freebl and util */ + BL_SetForkState(parentForkedAfterC_Initialize); + UTIL_SetForkState(parentForkedAfterC_Initialize); + + nscFreeAllSlots(isFIPS ? NSC_FIPS_MODULE : NSC_NON_FIPS_MODULE); + + /* don't muck with the globals if 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(); + + /* reset fork status in freebl. We must do this before BL_Unload so that + * this call doesn't force freebl to be reloaded. */ + BL_SetForkState(PR_FALSE); + + /* unload freeBL shared library from memory. This may only decrement the + * OS refcount if it's been loaded multiple times, eg. by libssl */ + BL_Unload(); + + /* clean up the default OID table */ + SECOID_Shutdown(); + + /* reset fork status in util */ + UTIL_SetForkState(PR_FALSE); + + nsc_init = PR_FALSE; + +#ifdef CHECK_FORK_MIXED + if (!usePthread_atfork) { + myPid = 0; /* allow CHECK_FORK in the next softoken initialization to + * succeed */ + } else { + forked = PR_FALSE; /* allow reinitialization */ + } +#elif defined(CHECK_FORK_GETPID) + myPid = 0; /* allow reinitialization */ +#elif defined (CHECK_FORK_PTHREAD) + forked = PR_FALSE; /* allow reinitialization */ +#endif + return CKR_OK; +} + +/* Hard-reset the entire softoken PKCS#11 module if the parent process forked + * while it was initialized. */ +PRBool sftk_ForkReset(CK_VOID_PTR pReserved, CK_RV* crv) +{ +#ifndef NO_FORK_CHECK + if (PARENT_FORKED()) { + parentForkedAfterC_Initialize = PR_TRUE; + if (nsc_init) { + /* finalize non-FIPS token */ + *crv = nsc_CommonFinalize(pReserved, PR_FALSE); + PORT_Assert(CKR_OK == *crv); + nsc_init = (PRBool) !(*crv == CKR_OK); + } + if (nsf_init) { + /* finalize FIPS token */ + *crv = nsc_CommonFinalize(pReserved, PR_TRUE); + PORT_Assert(CKR_OK == *crv); + nsf_init = (PRBool) !(*crv == CKR_OK); + } + parentForkedAfterC_Initialize = PR_FALSE; + return PR_TRUE; + } +#endif + return PR_FALSE; +} + +/* NSC_Finalize indicates that an application is done with the + * Cryptoki library.*/ +CK_RV NSC_Finalize (CK_VOID_PTR pReserved) +{ + CK_RV crv; + + /* reset entire PKCS#11 module upon fork */ + if (sftk_ForkReset(pReserved, &crv)) { + return crv; + } + + 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, PR_FALSE); + } + 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, PR_FALSE); + } + 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); + /* Reset login flags. */ + if (ulNewLen == 0) { + PRBool tokenRemoved = PR_FALSE; + PZ_Lock(slot->slotLock); + slot->isLoggedIn = PR_FALSE; + slot->ssoLoggedIn = PR_FALSE; + PZ_Unlock(slot->slotLock); + + rv = sftkdb_CheckPassword(handle, "", &tokenRemoved); + if (tokenRemoved) { + sftk_CloseAllSessions(slot, PR_FALSE); + } + } + sftk_update_all_states(slot); + sftk_freeDB(handle); + 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 (slot->needLogin && 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; + +#ifndef NO_CHECK_FORK + /* skip fork check if we are being called from C_Initialize or C_Finalize */ + if (!parentForkedAfterC_Initialize) { + CHECK_FORK(); + } +#endif + + slot = sftk_SlotFromID(slotID, PR_FALSE); + if (slot == NULL) return CKR_SLOT_ID_INVALID; + + return sftk_CloseAllSessions(slot, PR_TRUE); +} + + + +/* 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; + if (!slot->needLogin) { + return ulPinLen ? CKR_PIN_INCORRECT : CKR_OK; + } + 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, PR_FALSE); + } + 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 (slot->needLogin && 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, ¶mStrings, 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, + ¶mStrings.tokens[0], moduleIndex); + } else { + crv = SFTK_SlotInit(paramStrings.configdir, + paramStrings.updatedir, paramStrings.updateID, + ¶mStrings.tokens[0], moduleIndex); + } + if (crv != CKR_OK) { + goto loser; + } +loser: + sftk_freeParams(¶mStrings); + 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_ULONG 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_ULONG 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 void +sftk_pruneSearch(CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount, + PRBool *searchCertDB, PRBool *searchKeyDB) { + CK_ULONG i; + + *searchCertDB = PR_TRUE; + *searchKeyDB = PR_TRUE; + for (i = 0; i < ulCount; i++) { + if (pTemplate[i].type == CKA_CLASS && pTemplate[i].pValue != NULL) { + CK_OBJECT_CLASS class = *((CK_OBJECT_CLASS*)pTemplate[i].pValue); + if (class == CKO_PRIVATE_KEY || class == CKO_SECRET_KEY) { + *searchCertDB = PR_FALSE; + } else { + *searchKeyDB = PR_FALSE; + } + break; + } + } +} + +static CK_RV +sftk_searchTokenList(SFTKSlot *slot, SFTKSearchResults *search, + CK_ATTRIBUTE *pTemplate, CK_ULONG ulCount, + PRBool *tokenOnly, PRBool isLoggedIn) +{ + CK_RV crv = CKR_OK; + CK_RV crv2; + PRBool searchCertDB; + PRBool searchKeyDB; + + sftk_pruneSearch(pTemplate, ulCount, &searchCertDB, &searchKeyDB); + + if (searchCertDB) { + SFTKDBHandle *certHandle = sftk_getCertDB(slot); + crv = sftk_searchDatabase(certHandle, search, pTemplate, ulCount); + crv2 = sftk_emailhack(slot, certHandle, search, pTemplate, ulCount); + if (crv == CKR_OK) crv = crv2; + sftk_freeDB(certHandle); + } + + if (crv == CKR_OK && isLoggedIn && searchKeyDB) { + 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/mozilla/security/nss/lib/softoken/pkcs11c.c b/mozilla/security/nss/lib/softoken/pkcs11c.c new file mode 100644 index 0000000..8847c2f --- /dev/null +++ b/mozilla/security/nss/lib/softoken/pkcs11c.c @@ -0,0 +1,6471 @@ +/* ***** 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); +} + +/* + * map all the SEC_ERROR_xxx error codes that may be returned by freebl + * functions to CKR_xxx. return CKR_DEVICE_ERROR by default for backward + * compatibility. + */ +static CK_RV +sftk_MapCryptError(int error) +{ + switch (error) { + case SEC_ERROR_INVALID_ARGS: + case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */ + return CKR_ARGUMENTS_BAD; + case SEC_ERROR_INPUT_LEN: + return CKR_DATA_LEN_RANGE; + case SEC_ERROR_OUTPUT_LEN: + return CKR_BUFFER_TOO_SMALL; + case SEC_ERROR_LIBRARY_FAILURE: + return CKR_GENERAL_ERROR; + case SEC_ERROR_NO_MEMORY: + return CKR_HOST_MEMORY; + case SEC_ERROR_BAD_SIGNATURE: + return CKR_SIGNATURE_INVALID; + case SEC_ERROR_INVALID_KEY: + return CKR_KEY_SIZE_RANGE; + case SEC_ERROR_BAD_KEY: /* an EC public key that fails validation */ + return CKR_KEY_SIZE_RANGE; /* the closest error code */ + case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM: + return CKR_TEMPLATE_INCONSISTENT; + /* EC functions set this error if NSS_ENABLE_ECC is not defined */ + case SEC_ERROR_UNSUPPORTED_KEYALG: + return CKR_MECHANISM_INVALID; + case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE: + return CKR_DOMAIN_PARAMS_INVALID; + /* key pair generation failed after max number of attempts */ + case SEC_ERROR_NEED_RANDOM: + return CKR_FUNCTION_FAILED; + } + return CKR_DEVICE_ERROR; +} + +/* used by Decrypt and UnwrapKey (indirectly) */ +static CK_RV +sftk_MapDecryptError(int error) +{ + switch (error) { + case SEC_ERROR_BAD_DATA: + return CKR_ENCRYPTED_DATA_INVALID; + default: + return sftk_MapCryptError(error); + } +} + +/* + * return CKR_SIGNATURE_INVALID instead of CKR_DEVICE_ERROR by default for + * backward compatibilty. + */ +static CK_RV +sftk_MapVerifyError(int error) +{ + CK_RV crv = sftk_MapCryptError(error); + if (crv == CKR_DEVICE_ERROR) + crv = CKR_SIGNATURE_INVALID; + return crv; +} + + +/* + * 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 sftk_MapCryptError(PORT_GetError()); + + /* 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 sftk_MapCryptError(PORT_GetError()); + + /* 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 ************************ + */ + + +/* + * 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_SEED_CBC_PAD: + context->doPad = PR_TRUE; + /* fall thru */ + case CKM_SEED_CBC: + if (!pMechanism->pParameter || + pMechanism->ulParameterLen != 16) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + /* fall thru */ + case CKM_SEED_ECB: + context->blockSize = 16; + if (key_type != CKK_SEED) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + att = sftk_FindAttribute(key,CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + context->cipherInfo = SEED_CreateContext( + (unsigned char*)att->attrib.pValue, + (unsigned char*)pMechanism->pParameter, + pMechanism->mechanism == CKM_SEED_ECB ? NSS_SEED : NSS_SEED_CBC, + isEncrypt); + sftk_FreeAttribute(att); + if (context->cipherInfo == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + context->update = (SFTKCipher)(isEncrypt ? SEED_Encrypt : SEED_Decrypt); + context->destroy = (SFTKDestroy) SEED_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); + return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); +} + + +/* 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 : sftk_MapCryptError(PORT_GetError()); +} + +/* 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 : sftk_MapCryptError(PORT_GetError()); + *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 sftk_MapDecryptError(PORT_GetError()); + 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 : sftk_MapDecryptError(PORT_GetError()); +} + + +/* 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 : sftk_MapDecryptError(PORT_GetError()); +} + +/* 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 : sftk_MapDecryptError(PORT_GetError()); + 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_SEED_MAC_GENERAL: + mac_bytes = *(CK_ULONG *)pMechanism->pParameter; + /* fall through */ + case CKM_SEED_MAC: + blockSize = 16; + PORT_Memset(ivBlock,0,blockSize); + cbc_mechanism.mechanism = CKM_SEED_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 sftk_MapCryptError(PORT_GetError()); + } + + /* 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 sftk_MapCryptError(PORT_GetError()); + /* 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 : sftk_MapCryptError(PORT_GetError()); +} + +/* 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 : sftk_MapCryptError(PORT_GetError()); +} + + +/* + ************** 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 : sftk_MapVerifyError(PORT_GetError()); + +} + + +/* 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 : sftk_MapVerifyError(PORT_GetError()); + +} + +/* + ************** 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 : sftk_MapVerifyError(PORT_GetError()); +} + +/* + **************************** 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 : sftk_MapCryptError(PORT_GetError()); +} + +/* 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); + /* + * This may fail with SEC_ERROR_NEED_RANDOM, which means the RNG isn't + * seeded with enough entropy. + */ + return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); +} + +/* + **************************** 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, ¶ms, &vfy); + } else { + rv = PQG_ParamGenSeedLen(j,seedBits/8, ¶ms, &vfy); + } + + if (rv != SECSuccess) { + if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { + sftk_fatalError = PR_TRUE; + } + return sftk_MapCryptError(PORT_GetError()); + } + 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_SEED_KEY_GEN: + *key_type = CKK_SEED; + *key_length = 16; + 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,¶ms->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, nsc_jpake} 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; + HASH_HashType hashType; + + 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_SEED_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; + case CKM_NSS_JPAKE_ROUND1_SHA1: hashType = HASH_AlgSHA1; goto jpake1; + case CKM_NSS_JPAKE_ROUND1_SHA256: hashType = HASH_AlgSHA256; goto jpake1; + case CKM_NSS_JPAKE_ROUND1_SHA384: hashType = HASH_AlgSHA384; goto jpake1; + case CKM_NSS_JPAKE_ROUND1_SHA512: hashType = HASH_AlgSHA512; goto jpake1; +jpake1: + key_gen_type = nsc_jpake; + key_type = CKK_NSS_JPAKE_ROUND1; + objclass = CKO_PRIVATE_KEY; + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound1Params)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (sftk_isTrue(key, CKA_TOKEN)) { + crv = CKR_TEMPLATE_INCONSISTENT; + } + 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; + case nsc_jpake: + crv = jpake_Round1(hashType, + (CK_NSS_JPAKERound1Params *) pMechanism->pParameter, + 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 = sftk_MapCryptError(PORT_GetError()); + 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 = sftk_MapCryptError(PORT_GetError()); + 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 = sftk_MapCryptError(PORT_GetError()); + 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 = sftk_MapCryptError(PORT_GetError()); + 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 = sftk_MapCryptError(PORT_GetError()); + break; + } + + if (getenv("NSS_USE_DECODED_CKA_EC_POINT")) { + crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, + sftk_item_expand(&ecPriv->publicValue)); + } else { + SECItem *pubValue = SEC_ASN1EncodeItem(NULL, NULL, + &ecPriv->publicValue, + SEC_ASN1_GET(SEC_OctetStringTemplate)); + if (!pubValue) { + crv = CKR_ARGUMENTS_BAD; + goto ecgn_done; + } + crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, + sftk_item_expand(pubValue)); + SECITEM_FreeItem(pubValue, PR_TRUE); + } + 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, NSS_AUDIT_SELF_TEST, 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 = NULL; + SFTKAttribute * att2 = NULL; + 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; + HASH_HashType hashType; + PRBool extractValue = PR_TRUE; + + 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); + } + + switch (pMechanism->mechanism) { + case CKM_NSS_JPAKE_ROUND2_SHA1: /* fall through */ + case CKM_NSS_JPAKE_ROUND2_SHA256: /* fall through */ + case CKM_NSS_JPAKE_ROUND2_SHA384: /* fall through */ + case CKM_NSS_JPAKE_ROUND2_SHA512: + extractValue = PR_FALSE; + classType = CKO_PRIVATE_KEY; + break; + case CKM_NSS_JPAKE_FINAL_SHA1: /* fall through */ + case CKM_NSS_JPAKE_FINAL_SHA256: /* fall through */ + case CKM_NSS_JPAKE_FINAL_SHA384: /* fall through */ + case CKM_NSS_JPAKE_FINAL_SHA512: + extractValue = PR_FALSE; + /* fall through */ + default: + 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; + } + + if (extractValue) { + /* 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, curveLen, pubKeyLen; + CK_ECDH1_DERIVE_PARAMS *mechParams; + NSSLOWKEYPrivateKey *privKey; + PLArenaPool *arena = NULL; + + /* 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; + + curveLen = (privKey->u.ec.ecParams.fieldID.size +7)/8; + pubKeyLen = (2*curveLen) + 1; + + /* if the len is too small, can't be a valid point */ + if (ecPoint.len < pubKeyLen) { + goto ec_loser; + } + /* if the len is too large, must be an encoded point (length is + * equal case just falls through */ + if (ecPoint.len > pubKeyLen) { + SECItem newPoint; + + arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (arena == NULL) { + goto ec_loser; + } + + rv = SEC_QuickDERDecodeItem(arena, &newPoint, + SEC_ASN1_GET(SEC_OctetStringTemplate), + &ecPoint); + if (rv != SECSuccess) { + goto ec_loser; + } + ecPoint = newPoint; + } + + 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) { + goto ec_loser; + } + } + + rv = ECDH_Derive(&ecPoint, &privKey->u.ec.ecParams, &ecScalar, + withCofactor, &tmp); + PORT_Free(ecScalar.data); + ecScalar.data = NULL; + if (privKey != sourceKey->objectInfo) { + nsslowkey_DestroyPrivateKey(privKey); + privKey=NULL; + } + if (arena) { + PORT_FreeArena(arena,PR_FALSE); + arena=NULL; + } + + if (rv != SECSuccess) { + crv = sftk_MapCryptError(PORT_GetError()); + 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; + +ec_loser: + crv = CKR_ARGUMENTS_BAD; + PORT_Free(ecScalar.data); + if (privKey != sourceKey->objectInfo) + nsslowkey_DestroyPrivateKey(privKey); + if (arena) { + PORT_FreeArena(arena, PR_FALSE); + } + break; + + } +#endif /* NSS_ENABLE_ECC */ + + /* See RFC 5869 and CK_NSS_HKDFParams for documentation. */ + case CKM_NSS_HKDF_SHA1: hashType = HASH_AlgSHA1; goto hkdf; + case CKM_NSS_HKDF_SHA256: hashType = HASH_AlgSHA256; goto hkdf; + case CKM_NSS_HKDF_SHA384: hashType = HASH_AlgSHA384; goto hkdf; + case CKM_NSS_HKDF_SHA512: hashType = HASH_AlgSHA512; goto hkdf; +hkdf: { + const CK_NSS_HKDFParams * params = + (const CK_NSS_HKDFParams *) pMechanism->pParameter; + const SECHashObject * rawHash; + unsigned hashLen; + CK_BYTE buf[HASH_LENGTH_MAX]; + /* const */ CK_BYTE * prk; /* psuedo-random key */ + CK_ULONG prkLen; + const CK_BYTE * okm; /* output keying material */ + + rawHash = HASH_GetRawHashObject(hashType); + if (rawHash == NULL || rawHash->length > sizeof buf) { + crv = CKR_FUNCTION_FAILED; + break; + } + hashLen = rawHash->length; + + if (pMechanism->ulParameterLen != sizeof(CK_NSS_HKDFParams) || + !params || (!params->bExpand && !params->bExtract) || + (params->bExtract && params->ulSaltLen > 0 && !params->pSalt) || + (params->bExpand && params->ulInfoLen > 0 && !params->pInfo)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (keySize == 0 || keySize > sizeof key_block || + (!params->bExpand && keySize > hashLen) || + (params->bExpand && keySize > 255 * hashLen)) { + crv = CKR_TEMPLATE_INCONSISTENT; + break; + } + crv = sftk_DeriveSensitiveCheck(sourceKey, key); + if (crv != CKR_OK) + break; + + /* HKDF-Extract(salt, base key value) */ + if (params->bExtract) { + CK_BYTE * salt; + CK_ULONG saltLen; + HMACContext * hmac; + unsigned int bufLen; + + salt = params->pSalt; + saltLen = params->ulSaltLen; + if (salt == NULL) { + saltLen = hashLen; + salt = buf; + memset(salt, 0, saltLen); + } + hmac = HMAC_Create(rawHash, salt, saltLen, isFIPS); + if (!hmac) { + crv = CKR_HOST_MEMORY; + break; + } + HMAC_Begin(hmac); + HMAC_Update(hmac, (const unsigned char*) att->attrib.pValue, + att->attrib.ulValueLen); + HMAC_Finish(hmac, buf, &bufLen, sizeof(buf)); + HMAC_Destroy(hmac, PR_TRUE); + PORT_Assert(bufLen == rawHash->length); + prk = buf; + prkLen = bufLen; + } else { + /* PRK = base key value */ + prk = (CK_BYTE*) att->attrib.pValue; + prkLen = att->attrib.ulValueLen; + } + + /* HKDF-Expand */ + if (!params->bExpand) { + okm = prk; + } else { + /* T(1) = HMAC-Hash(prk, "" | info | 0x01) + * T(n) = HMAC-Hash(prk, T(n-1) | info | n + * key material = T(1) | ... | T(n) + */ + HMACContext * hmac; + CK_BYTE i; + unsigned iterations = PR_ROUNDUP(keySize, hashLen) / hashLen; + hmac = HMAC_Create(rawHash, prk, prkLen, isFIPS); + if (hmac == NULL) { + crv = CKR_HOST_MEMORY; + break; + } + for (i = 1; i <= iterations; ++i) { + unsigned len; + HMAC_Begin(hmac); + if (i > 1) { + HMAC_Update(hmac, key_block + ((i-2) * hashLen), hashLen); + } + if (params->ulInfoLen != 0) { + HMAC_Update(hmac, params->pInfo, params->ulInfoLen); + } + HMAC_Update(hmac, &i, 1); + HMAC_Finish(hmac, key_block + ((i-1) * hashLen), &len, + hashLen); + PORT_Assert(len == hashLen); + } + HMAC_Destroy(hmac, PR_TRUE); + okm = key_block; + } + /* key material = prk */ + crv = sftk_forceAttribute(key, CKA_VALUE, okm, keySize); + break; + } /* end of CKM_NSS_HKDF_* */ + + case CKM_NSS_JPAKE_ROUND2_SHA1: hashType = HASH_AlgSHA1; goto jpake2; + case CKM_NSS_JPAKE_ROUND2_SHA256: hashType = HASH_AlgSHA256; goto jpake2; + case CKM_NSS_JPAKE_ROUND2_SHA384: hashType = HASH_AlgSHA384; goto jpake2; + case CKM_NSS_JPAKE_ROUND2_SHA512: hashType = HASH_AlgSHA512; goto jpake2; +jpake2: + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKERound2Params)) + crv = CKR_MECHANISM_PARAM_INVALID; + if (crv == CKR_OK && sftk_isTrue(key, CKA_TOKEN)) + crv = CKR_TEMPLATE_INCONSISTENT; + if (crv == CKR_OK) + crv = sftk_DeriveSensitiveCheck(sourceKey, key); + if (crv == CKR_OK) + crv = jpake_Round2(hashType, + (CK_NSS_JPAKERound2Params *) pMechanism->pParameter, + sourceKey, key); + break; + + case CKM_NSS_JPAKE_FINAL_SHA1: hashType = HASH_AlgSHA1; goto jpakeFinal; + case CKM_NSS_JPAKE_FINAL_SHA256: hashType = HASH_AlgSHA256; goto jpakeFinal; + case CKM_NSS_JPAKE_FINAL_SHA384: hashType = HASH_AlgSHA384; goto jpakeFinal; + case CKM_NSS_JPAKE_FINAL_SHA512: hashType = HASH_AlgSHA512; goto jpakeFinal; +jpakeFinal: + if (pMechanism->pParameter == NULL || + pMechanism->ulParameterLen != sizeof(CK_NSS_JPAKEFinalParams)) + crv = CKR_MECHANISM_PARAM_INVALID; + /* We purposely do not do the derive sensitivity check; we want to be + able to derive non-sensitive keys while allowing the ROUND1 and + ROUND2 keys to be sensitive (which they always are, since they are + in the CKO_PRIVATE_KEY class). The caller must include CKA_SENSITIVE + in the template in order for the resultant keyblock key to be + sensitive. + */ + if (crv == CKR_OK) + crv = jpake_Final(hashType, + (CK_NSS_JPAKEFinalParams *) pMechanism->pParameter, + sourceKey, key); + break; + + default: + crv = CKR_MECHANISM_INVALID; + } + if (att) { + 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/mozilla/security/nss/lib/softoken/pkcs11i.h b/mozilla/security/nss/lib/softoken/pkcs11i.h new file mode 100644 index 0000000..aead245 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/pkcs11i.h @@ -0,0 +1,731 @@ +/* ***** 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" +#include "hasht.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 PRBool sftk_ForkReset(CK_VOID_PTR pReserved, CK_RV* crv); +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, PRBool logout); + + +/* 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); + + +/* J-PAKE (jpakesftk.c) */ +extern +CK_RV jpake_Round1(HASH_HashType hashType, + CK_NSS_JPAKERound1Params * params, + SFTKObject * key); +extern +CK_RV jpake_Round2(HASH_HashType hashType, + CK_NSS_JPAKERound2Params * params, + SFTKObject * sourceKey, SFTKObject * key); +extern +CK_RV jpake_Final(HASH_HashType hashType, + const CK_NSS_JPAKEFinalParams * params, + SFTKObject * sourceKey, SFTKObject * key); + +/**************************************** + * 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/mozilla/security/nss/lib/softoken/pkcs11ni.h b/mozilla/security/nss/lib/softoken/pkcs11ni.h new file mode 100644 index 0000000..9e584a9 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/pkcs11u.c b/mozilla/security/nss/lib/softoken/pkcs11u.c new file mode 100644 index 0000000..7f44873 --- /dev/null +++ b/mozilla/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" +#include "softoken.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 = CKR_HOST_MEMORY; + + 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, &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) { + SKIP_AFTER_FORK(PZ_Lock(slot->objectLock)); +} + +static void +sftk_tokenKeyUnlock(SFTKSlot *slot) { + SKIP_AFTER_FORK(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; + } + SKIP_AFTER_FORK(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; + SKIP_AFTER_FORK(PZ_Unlock(list->lock)); + SKIP_AFTER_FORK(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/mozilla/security/nss/lib/softoken/rsawrapr.c b/mozilla/security/nss/lib/softoken/rsawrapr.c new file mode 100644 index 0000000..a40c265 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/rsawrapr.c @@ -0,0 +1,942 @@ +/* + * 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: rsawrapr.c,v 1.11.70.1 2011/04/07 22:54:48 wtc%google.com Exp $ */ + +#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, j; + 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. + * + * Build the block left to right. + * Fill the entire block from Pad to the end with random bytes. + * Use the bytes after Pad as a supply of extra random bytes from + * which to find replacements for the zero bytes in Pad. + * If we need more than that, refill the bytes after Pad with + * new random bytes as necessary. + */ + 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; + } + j = modulusLen - 2; + rv = RNG_GenerateGlobalRandomBytes(bp, j); + if (rv == SECSuccess) { + for (i = 0; i < padLen; ) { + unsigned char repl; + /* Pad with non-zero random data. */ + if (bp[i] != RSA_BLOCK_AFTER_PAD_OCTET) { + ++i; + continue; + } + if (j <= padLen) { + rv = RNG_GenerateGlobalRandomBytes(bp + padLen, + modulusLen - (2 + padLen)); + if (rv != SECSuccess) + break; + j = modulusLen - 2; + } + do { + repl = bp[--j]; + } while (repl == RSA_BLOCK_AFTER_PAD_OCTET && j > padLen); + if (repl != RSA_BLOCK_AFTER_PAD_OCTET) { + bp[i++] = repl; + } + } + } + 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/mozilla/security/nss/lib/softoken/sdb.c b/mozilla/security/nss/lib/softoken/sdb.c new file mode 100644 index 0000000..3c1b3ec --- /dev/null +++ b/mozilla/security/nss/lib/softoken/sdb.c @@ -0,0 +1,2065 @@ +/* ***** 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) + * Meena Vyas (meena.vyas@oracle.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" +#include "prsystem.h" /* for PR_GetDirectorySeparator() */ +#include "sys/stat.h" +#if defined (_WIN32) +#include <io.h> +#endif + +#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; + char dirsep; + + /* 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 */ + dirsep = PR_GetDirectorySeparator(); + end = PORT_Strrchr(file, dirsep); + 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 CK_RV +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. Return the number of operations done in 33 ms. + */ +static PRUint32 +sdb_measureAccess(const char *directory) +{ + PRUint32 i; + PRIntervalTime time; + PRIntervalTime delta; + PRIntervalTime duration = PR_MillisecondsToInterval(33); + + /* no directory, just return one */ + if (directory == NULL) { + return 1; + } + + /* measure number of Access operations that can be done in 33 milliseconds + * (1/30'th of a second), or 10000 operations, which ever comes first. + */ + time = PR_IntervalNow(); + for (i=0; i < 10000u; i++) { + char *temp; + PRIntervalTime next; + + temp = sdb_BuildFileName(directory,"","._dOeSnotExist_", time+i, 0); + PR_Access(temp,PR_ACCESS_EXISTS); + sqlite3_free(temp); + next = PR_IntervalNow(); + delta = next - time; + if (delta >= duration) + break; + } + + /* always return 1 or greater */ + return i ? i : 1u; +} + +/* + * 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_reset(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; + } + + for (i=0; i < count; i++) { + getStr = sqlite3_mprintf("a%x", template[i].type); + + 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); + sqlite3_free(newStr); + newStr = NULL; + if (sqlerr == SQLITE_ERROR) { + template[i].ulValueLen = -1; + error = CKR_ATTRIBUTE_TYPE_INVALID; + continue; + } else 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) { + int blobSize; + const char *blobData; + + blobSize = sqlite3_column_bytes(stmt, 0); + blobData = sqlite3_column_blob(stmt, 0); + if (blobData == NULL) { + template[i].ulValueLen = -1; + error = CKR_ATTRIBUTE_TYPE_INVALID; + break; + } + /* 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; + break; + } + PORT_Memcpy(template[i].pValue, blobData, blobSize); + } + template[i].ulValueLen = blobSize; + found = 1; + } + } while (!sdb_done(sqlerr,&retry)); + sqlite3_reset(stmt); + sqlite3_finalize(stmt); + stmt = NULL; + } + +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 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; + + 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; + } + + 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; +} + +void sdb_SetForkState(PRBool forked) +{ + /* XXXright now this is a no-op. The global fork state in the softokn3 + * shared library is already taken care of at the PKCS#11 level. + * If and when we add fork state to the sqlite shared library and extern + * interface, we will need to set it and reset it from here */ +} + +/* + * 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, PRUint32 accessOps, 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; + char *env; + PRBool enableCache = PR_FALSE; + PRBool create; + + *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(); + create = (PR_Access(dbname, PR_ACCESS_EXISTS) != PR_SUCCESS); + if ((flags == SDB_RDONLY) && create) { + 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; + } + /* sql created the file, but it doesn't set appropriate modes for + * a database */ + if (create) { + /* NO NSPR call for this? :( */ +#ifndef WINCE + chmod (dbname, 0600); +#endif + } + + 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 { + char *tempDir = NULL; + PRUint32 tempOps = 0; + /* + * 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); + if (tempDir) { + tempOps = sdb_measureAccess(tempDir); + PORT_Free(tempDir); + + /* There is a cost to continually copying the database. + * Account for that cost with the arbitrary factor of 10 */ + enableCache = (PRBool)(tempOps > accessOps * 10); + } + } + + 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->version = 0; + sdb->sdb_type = SDB_SQL; + sdb->sdb_flags = flags | SDB_HAS_META; + sdb->app_private = NULL; + 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_Reset = sdb_Reset; + sdb->sdb_Close = sdb_Close; + sdb->sdb_SetForkState = sdb_SetForkState; + + 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; + PRUint32 accessOps; + + if (certdb) + *certdb = NULL; + if (keydb) + *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 */ + accessOps = sdb_measureAccess(directory); + + /* + * open the cert data base + */ + if (certdb) { + /* initialize Certificate database */ + error = sdb_init(cert, "nssPublic", SDB_CERT, &inUpdate, + newInit, flags, accessOps, 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, accessOps, 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/mozilla/security/nss/lib/softoken/sdb.h b/mozilla/security/nss/lib/softoken/sdb.h new file mode 100644 index 0000000..517777e --- /dev/null +++ b/mozilla/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" + +#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); + void (*sdb_SetForkState)(PRBool forked); +}; + +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/mozilla/security/nss/lib/softoken/secmodt.h b/mozilla/security/nss/lib/softoken/secmodt.h new file mode 100644 index 0000000..59fdca0 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/secmodt.h @@ -0,0 +1,503 @@ +/* ***** 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 _SECMODT_H_ +#define _SECMODT_H_ 1 + +#include "nssrwlkt.h" +#include "nssilckt.h" +#include "secoid.h" +#include "secasn1.h" +#include "pkcs11t.h" + +/* find a better home for these... */ +extern const SEC_ASN1Template SECKEY_PointerToEncryptedPrivateKeyInfoTemplate[]; +extern SEC_ASN1TemplateChooser NSS_Get_SECKEY_PointerToEncryptedPrivateKeyInfoTemplate; +extern const SEC_ASN1Template SECKEY_EncryptedPrivateKeyInfoTemplate[]; +extern SEC_ASN1TemplateChooser NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate; +extern const SEC_ASN1Template SECKEY_PrivateKeyInfoTemplate[]; +extern SEC_ASN1TemplateChooser NSS_Get_SECKEY_PrivateKeyInfoTemplate; +extern const SEC_ASN1Template SECKEY_PointerToPrivateKeyInfoTemplate[]; +extern SEC_ASN1TemplateChooser NSS_Get_SECKEY_PointerToPrivateKeyInfoTemplate; + +/* PKCS11 needs to be included */ +typedef struct SECMODModuleStr SECMODModule; +typedef struct SECMODModuleListStr SECMODModuleList; +typedef NSSRWLock SECMODListLock; +typedef struct PK11SlotInfoStr PK11SlotInfo; /* defined in secmodti.h */ +typedef struct PK11PreSlotInfoStr PK11PreSlotInfo; /* defined in secmodti.h */ +typedef struct PK11SymKeyStr PK11SymKey; /* defined in secmodti.h */ +typedef struct PK11ContextStr PK11Context; /* defined in secmodti.h */ +typedef struct PK11SlotListStr PK11SlotList; +typedef struct PK11SlotListElementStr PK11SlotListElement; +typedef struct PK11RSAGenParamsStr PK11RSAGenParams; +typedef unsigned long SECMODModuleID; +typedef struct PK11DefaultArrayEntryStr PK11DefaultArrayEntry; +typedef struct PK11GenericObjectStr PK11GenericObject; +typedef void (*PK11FreeDataFunc)(void *); + +struct SECMODModuleStr { + PLArenaPool *arena; + PRBool internal; /* true of internally linked modules, false + * for the loaded modules */ + PRBool loaded; /* Set to true if module has been loaded */ + PRBool isFIPS; /* Set to true if module is finst internal */ + char *dllName; /* name of the shared library which implements + * this module */ + char *commonName; /* name of the module to display to the user */ + void *library; /* pointer to the library. opaque. used only by + * pk11load.c */ + void *functionList; /* The PKCS #11 function table */ + PZLock *refLock; /* only used pk11db.c */ + int refCount; /* Module reference count */ + PK11SlotInfo **slots; /* array of slot points attached to this mod*/ + int slotCount; /* count of slot in above array */ + PK11PreSlotInfo *slotInfo; /* special info about slots default settings */ + int slotInfoCount; /* count */ + SECMODModuleID moduleID; /* ID so we can find this module again */ + PRBool isThreadSafe; + unsigned long ssl[2]; /* SSL cipher enable flags */ + char *libraryParams; /* Module specific parameters */ + void *moduleDBFunc; /* function to return module configuration data*/ + SECMODModule *parent; /* module that loaded us */ + PRBool isCritical; /* This module must load successfully */ + PRBool isModuleDB; /* this module has lists of PKCS #11 modules */ + PRBool moduleDBOnly; /* this module only has lists of PKCS #11 modules */ + int trustOrder; /* order for this module's certificate trust rollup */ + int cipherOrder; /* order for cipher operations */ + unsigned long evControlMask; /* control the running and shutdown of slot + * events (SECMOD_WaitForAnyTokenEvent) */ + CK_VERSION cryptokiVersion; /* version of this library */ +}; + +/* evControlMask flags */ +/* + * These bits tell the current state of a SECMOD_WaitForAnyTokenEvent. + * + * SECMOD_WAIT_PKCS11_EVENT - we're waiting in the PKCS #11 module in + * C_WaitForSlotEvent(). + * SECMOD_WAIT_SIMULATED_EVENT - we're waiting in the NSS simulation code + * which polls for token insertion and removal events. + * SECMOD_END_WAIT - SECMOD_CancelWait has been called while the module is + * waiting in SECMOD_WaitForAnyTokenEvent. SECMOD_WaitForAnyTokenEvent + * should return immediately to it's caller. + */ +#define SECMOD_END_WAIT 0x01 +#define SECMOD_WAIT_SIMULATED_EVENT 0x02 +#define SECMOD_WAIT_PKCS11_EVENT 0x04 + +struct SECMODModuleListStr { + SECMODModuleList *next; + SECMODModule *module; +}; + +struct PK11SlotListStr { + PK11SlotListElement *head; + PK11SlotListElement *tail; + PZLock *lock; +}; + +struct PK11SlotListElementStr { + PK11SlotListElement *next; + PK11SlotListElement *prev; + PK11SlotInfo *slot; + int refCount; +}; + +struct PK11RSAGenParamsStr { + int keySizeInBits; + unsigned long pe; +}; + +typedef enum { + PK11CertListUnique = 0, /* get one instance of all certs */ + PK11CertListUser = 1, /* get all instances of user certs */ + PK11CertListRootUnique = 2, /* get one instance of CA certs without a private key. + * deprecated. Use PK11CertListCAUnique + */ + PK11CertListCA = 3, /* get all instances of CA certs */ + PK11CertListCAUnique = 4, /* get one instance of CA certs */ + PK11CertListUserUnique = 5, /* get one instance of user certs */ + PK11CertListAll = 6 /* get all instances of all certs */ +} PK11CertListType; + +/* + * Entry into the Array which lists all the legal bits for the default flags + * in the slot, their definition, and the PKCS #11 mechanism the represent + * Always Statically allocated. + */ +struct PK11DefaultArrayEntryStr { + char *name; + unsigned long flag; + unsigned long mechanism; /* this is a long so we don't include the + * whole pkcs 11 world to use this header */ +}; + + +#define SECMOD_RSA_FLAG 0x00000001L +#define SECMOD_DSA_FLAG 0x00000002L +#define SECMOD_RC2_FLAG 0x00000004L +#define SECMOD_RC4_FLAG 0x00000008L +#define SECMOD_DES_FLAG 0x00000010L +#define SECMOD_DH_FLAG 0x00000020L +#define SECMOD_FORTEZZA_FLAG 0x00000040L +#define SECMOD_RC5_FLAG 0x00000080L +#define SECMOD_SHA1_FLAG 0x00000100L +#define SECMOD_MD5_FLAG 0x00000200L +#define SECMOD_MD2_FLAG 0x00000400L +#define SECMOD_SSL_FLAG 0x00000800L +#define SECMOD_TLS_FLAG 0x00001000L +#define SECMOD_AES_FLAG 0x00002000L +#define SECMOD_SHA256_FLAG 0x00004000L +#define SECMOD_SHA512_FLAG 0x00008000L /* also for SHA384 */ +#define SECMOD_CAMELLIA_FLAG 0x00010000L /* = PUBLIC_MECH_CAMELLIA_FLAG */ +#define SECMOD_SEED_FLAG 0x00020000L +/* reserved bit for future, do not use */ +#define SECMOD_RESERVED_FLAG 0X08000000L +#define SECMOD_FRIENDLY_FLAG 0x10000000L +#define SECMOD_RANDOM_FLAG 0x80000000L + +/* need to make SECMOD and PK11 prefixes consistant. */ +#define PK11_OWN_PW_DEFAULTS 0x20000000L +#define PK11_DISABLE_FLAG 0x40000000L + +/* + * PK11AttrFlags + * + * A 32-bit bitmask of PK11_ATTR_XXX flags + */ +typedef PRUint32 PK11AttrFlags; + +/* + * PK11_ATTR_XXX + * + * The following PK11_ATTR_XXX bitflags are used to specify + * PKCS #11 object attributes that have Boolean values. Some NSS + * functions have a "PK11AttrFlags attrFlags" parameter whose value + * is the logical OR of these bitflags. NSS use these bitflags on + * private keys or secret keys. Some of these bitflags also apply + * to the public keys associated with the private keys. + * + * For each PKCS #11 object attribute, we need two bitflags to + * specify not only "true" and "false" but also "default". For + * example, PK11_ATTR_PRIVATE and PK11_ATTR_PUBLIC control the + * CKA_PRIVATE attribute. If PK11_ATTR_PRIVATE is set, we add + * { CKA_PRIVATE, &cktrue, sizeof(CK_BBOOL) } + * to the template. If PK11_ATTR_PUBLIC is set, we add + * { CKA_PRIVATE, &ckfalse, sizeof(CK_BBOOL) } + * to the template. If neither flag is set, we don't add any + * CKA_PRIVATE entry to the template. + */ + +/* + * Attributes for PKCS #11 storage objects, which include not only + * keys but also certificates and domain parameters. + */ + +/* + * PK11_ATTR_TOKEN + * PK11_ATTR_SESSION + * + * These two flags determine whether the object is a token or + * session object. + * + * These two flags are related and cannot both be set. + * If the PK11_ATTR_TOKEN flag is set, the object is a token + * object. If the PK11_ATTR_SESSION flag is set, the object is + * a session object. If neither flag is set, the object is *by + * default* a session object. + * + * These two flags specify the value of the PKCS #11 CKA_TOKEN + * attribute. + */ +#define PK11_ATTR_TOKEN 0x00000001L +#define PK11_ATTR_SESSION 0x00000002L + +/* + * PK11_ATTR_PRIVATE + * PK11_ATTR_PUBLIC + * + * These two flags determine whether the object is a private or + * public object. A user may not access a private object until the + * user has authenticated to the token. + * + * These two flags are related and cannot both be set. + * If the PK11_ATTR_PRIVATE flag is set, the object is a private + * object. If the PK11_ATTR_PUBLIC flag is set, the object is a + * public object. If neither flag is set, it is token-specific + * whether the object is private or public. + * + * These two flags specify the value of the PKCS #11 CKA_PRIVATE + * attribute. NSS only uses this attribute on private and secret + * keys, so public keys created by NSS get the token-specific + * default value of the CKA_PRIVATE attribute. + */ +#define PK11_ATTR_PRIVATE 0x00000004L +#define PK11_ATTR_PUBLIC 0x00000008L + +/* + * PK11_ATTR_MODIFIABLE + * PK11_ATTR_UNMODIFIABLE + * + * These two flags determine whether the object is modifiable or + * read-only. + * + * These two flags are related and cannot both be set. + * If the PK11_ATTR_MODIFIABLE flag is set, the object can be + * modified. If the PK11_ATTR_UNMODIFIABLE flag is set, the object + * is read-only. If neither flag is set, the object is *by default* + * modifiable. + * + * These two flags specify the value of the PKCS #11 CKA_MODIFIABLE + * attribute. + */ +#define PK11_ATTR_MODIFIABLE 0x00000010L +#define PK11_ATTR_UNMODIFIABLE 0x00000020L + +/* Attributes for PKCS #11 key objects. */ + +/* + * PK11_ATTR_SENSITIVE + * PK11_ATTR_INSENSITIVE + * + * These two flags are related and cannot both be set. + * If the PK11_ATTR_SENSITIVE flag is set, the key is sensitive. + * If the PK11_ATTR_INSENSITIVE flag is set, the key is not + * sensitive. If neither flag is set, it is token-specific whether + * the key is sensitive or not. + * + * If a key is sensitive, certain attributes of the key cannot be + * revealed in plaintext outside the token. + * + * This flag specifies the value of the PKCS #11 CKA_SENSITIVE + * attribute. Although the default value of the CKA_SENSITIVE + * attribute for secret keys is CK_FALSE per PKCS #11, some FIPS + * tokens set the default value to CK_TRUE because only CK_TRUE + * is allowed. So in practice the default value of this attribute + * is token-specific, hence the need for two bitflags. + */ +#define PK11_ATTR_SENSITIVE 0x00000040L +#define PK11_ATTR_INSENSITIVE 0x00000080L + +/* + * PK11_ATTR_EXTRACTABLE + * PK11_ATTR_UNEXTRACTABLE + * + * These two flags are related and cannot both be set. + * If the PK11_ATTR_EXTRACTABLE flag is set, the key is extractable + * and can be wrapped. If the PK11_ATTR_UNEXTRACTABLE flag is set, + * the key is not extractable, and certain attributes of the key + * cannot be revealed in plaintext outside the token (just like a + * sensitive key). If neither flag is set, it is token-specific + * whether the key is extractable or not. + * + * These two flags specify the value of the PKCS #11 CKA_EXTRACTABLE + * attribute. + */ +#define PK11_ATTR_EXTRACTABLE 0x00000100L +#define PK11_ATTR_UNEXTRACTABLE 0x00000200L + +/* Cryptographic module types */ +#define SECMOD_EXTERNAL 0 /* external module */ +#define SECMOD_INTERNAL 1 /* internal default module */ +#define SECMOD_FIPS 2 /* internal fips module */ + +/* default module configuration strings */ +#define SECMOD_SLOT_FLAGS "slotFlags=[RSA,DSA,DH,RC2,RC4,DES,RANDOM,SHA1,MD5,MD2,SSL,TLS,AES,Camellia,SEED,SHA256,SHA512]" + +#define SECMOD_MAKE_NSS_FLAGS(fips,slot) \ +"Flags=internal,critical"fips" slotparams=("#slot"={"SECMOD_SLOT_FLAGS"})" + +#define SECMOD_INT_NAME "NSS Internal PKCS #11 Module" +#define SECMOD_INT_FLAGS SECMOD_MAKE_NSS_FLAGS("",1) +#define SECMOD_FIPS_NAME "NSS Internal FIPS PKCS #11 Module" +#define SECMOD_FIPS_FLAGS SECMOD_MAKE_NSS_FLAGS(",fips",3) + +/* + * What is the origin of a given Key. Normally this doesn't matter, but + * the fortezza code needs to know if it needs to invoke the SSL3 fortezza + * hack. + */ +typedef enum { + PK11_OriginNULL = 0, /* There is not key, it's a null SymKey */ + PK11_OriginDerive = 1, /* Key was derived from some other key */ + PK11_OriginGenerated = 2, /* Key was generated (also PBE keys) */ + PK11_OriginFortezzaHack = 3,/* Key was marked for fortezza hack */ + PK11_OriginUnwrap = 4 /* Key was unwrapped or decrypted */ +} PK11Origin; + +/* PKCS #11 disable reasons */ +typedef enum { + PK11_DIS_NONE = 0, + PK11_DIS_USER_SELECTED = 1, + PK11_DIS_COULD_NOT_INIT_TOKEN = 2, + PK11_DIS_TOKEN_VERIFY_FAILED = 3, + PK11_DIS_TOKEN_NOT_PRESENT = 4 +} PK11DisableReasons; + +/* types of PKCS #11 objects + * used to identify which NSS data structure is + * passed to the PK11_Raw* functions. Types map as follows: + * PK11_TypeGeneric PK11GenericObject * + * PK11_TypePrivKey SECKEYPrivateKey * + * PK11_TypePubKey SECKEYPublicKey * + * PK11_TypeSymKey PK11SymKey * + * PK11_TypeCert CERTCertificate * (currently not used). + */ +typedef enum { + PK11_TypeGeneric = 0, + PK11_TypePrivKey = 1, + PK11_TypePubKey = 2, + PK11_TypeCert = 3, + PK11_TypeSymKey = 4 +} PK11ObjectType; + + + +/* function pointer type for password callback function. + * This type is passed in to PK11_SetPasswordFunc() + */ +typedef char *(PR_CALLBACK *PK11PasswordFunc)(PK11SlotInfo *slot, PRBool retry, void *arg); +typedef PRBool (PR_CALLBACK *PK11VerifyPasswordFunc)(PK11SlotInfo *slot, void *arg); +typedef PRBool (PR_CALLBACK *PK11IsLoggedInFunc)(PK11SlotInfo *slot, void *arg); + +/* + * Special strings the password callback function can return only if + * the slot is an protected auth path slot. + */ +#define PK11_PW_RETRY "RETRY" /* an failed attempt to authenticate + * has already been made, just retry + * the operation */ +#define PK11_PW_AUTHENTICATED "AUTH" /* a successful attempt to authenticate + * has completed. Continue without + * another call to C_Login */ +/* All other non-null values mean that that NSS could call C_Login to force + * the authentication. The following define is to aid applications in + * documenting that is what it's trying to do */ +#define PK11_PW_TRY "TRY" /* Default: a prompt has been presented + * to the user, initiate a C_Login + * to authenticate the token */ + +/* + * PKCS #11 key structures + */ + +/* +** Attributes +*/ +struct SECKEYAttributeStr { + SECItem attrType; + SECItem **attrValue; +}; +typedef struct SECKEYAttributeStr SECKEYAttribute; + +/* +** A PKCS#8 private key info object +*/ +struct SECKEYPrivateKeyInfoStr { + PLArenaPool *arena; + SECItem version; + SECAlgorithmID algorithm; + SECItem privateKey; + SECKEYAttribute **attributes; +}; +typedef struct SECKEYPrivateKeyInfoStr SECKEYPrivateKeyInfo; + +/* +** A PKCS#8 private key info object +*/ +struct SECKEYEncryptedPrivateKeyInfoStr { + PLArenaPool *arena; + SECAlgorithmID algorithm; + SECItem encryptedData; +}; +typedef struct SECKEYEncryptedPrivateKeyInfoStr SECKEYEncryptedPrivateKeyInfo; + +/* + * token removal detection + */ +typedef enum { + PK11TokenNotRemovable = 0, + PK11TokenPresent = 1, + PK11TokenChanged = 2, + PK11TokenRemoved = 3 +} PK11TokenStatus; + +typedef enum { + PK11TokenRemovedOrChangedEvent = 0, + PK11TokenPresentEvent = 1 +} PK11TokenEvent; + +/* + * CRL Import Flags + */ +#define CRL_IMPORT_DEFAULT_OPTIONS 0x00000000 +#define CRL_IMPORT_BYPASS_CHECKS 0x00000001 + + +/* + * Merge Error Log + */ +typedef struct PK11MergeLogStr PK11MergeLog; +typedef struct PK11MergeLogNodeStr PK11MergeLogNode; + +/* These need to be global, leave some open fields so we can 'expand' + * these without breaking binary compatibility */ +struct PK11MergeLogNodeStr { + PK11MergeLogNode *next; /* next entry in the list */ + PK11MergeLogNode *prev; /* last entry in the list */ + PK11GenericObject *object; /* object that failed */ + int error; /* what the error was */ + CK_RV reserved1; + unsigned long reserved2; /* future flags */ + unsigned long reserved3; /* future scalar */ + void *reserved4; /* future pointer */ + void *reserved5; /* future expansion pointer */ +}; + +struct PK11MergeLogStr { + PK11MergeLogNode *head; + PK11MergeLogNode *tail; + PLArenaPool *arena; + int version; + unsigned long reserved1; + unsigned long reserved2; + unsigned long reserved3; + void *reserverd4; + void *reserverd5; +}; + + +#endif /*_SECMODT_H_ */ diff --git a/mozilla/security/nss/lib/softoken/sftkdb.c b/mozilla/security/nss/lib/softoken/sftkdb.c new file mode 100644 index 0000000..c68ec4c --- /dev/null +++ b/mozilla/security/nss/lib/softoken/sftkdb.c @@ -0,0 +1,2769 @@ +/* ***** 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" +#include "softoken.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_CERTIFICATE_CATEGORY: + case CKA_CERTIFICATE_TYPE: + case CKA_CLASS: + case CKA_JAVA_MIDP_SECURITY_DOMAIN: + case CKA_KEY_GEN_MECHANISM: + case CKA_KEY_TYPE: + case CKA_MECHANISM_TYPE: + case CKA_MODULUS_BITS: + case CKA_PRIME_BITS: + case CKA_SUBPRIME_BITS: + case CKA_VALUE_BITS: + case CKA_VALUE_LEN: + + 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; + CK_RV crv; + SFTKDBHandle *keyHandle = handle; + SDB *keyTarget = NULL; + PRBool usingPeerDB = PR_FALSE; + PRBool inPeerDBTransaction = PR_FALSE; + + PORT_Assert(handle); + + if (handle->type != SFTK_KEYDB_TYPE) { + keyHandle = handle->peerDB; + usingPeerDB = PR_TRUE; + } + + /* no key DB defined? then no need to sign anything */ + if (keyHandle == NULL) { + crv = CKR_OK; + goto loser; + } + + /* 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) { + crv = CKR_OK; + goto loser; + } + + /* If we had to switch databases, we need to initialize a transaction. */ + if (usingPeerDB) { + crv = (*keyTarget->sdb_Begin)(keyTarget); + if (crv != CKR_OK) { + goto loser; + } + inPeerDBTransaction = PR_TRUE; + } + + 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); + crv = CKR_USER_NOT_LOGGED_IN; + goto loser; + } + rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey, + objectID, template[i].type, + &plainText, &signText); + PZ_Unlock(keyHandle->passwordLock); + if (rv != SECSuccess) { + crv = CKR_GENERAL_ERROR; /* better error code here? */ + goto loser; + } + rv = sftkdb_PutAttributeSignature(handle, keyTarget, + objectID, template[i].type, signText); + if (rv != SECSuccess) { + crv = CKR_GENERAL_ERROR; /* better error code here? */ + goto loser; + } + } + } + crv = CKR_OK; + + /* If necessary, commit the transaction */ + if (inPeerDBTransaction) { + crv = (*keyTarget->sdb_Commit)(keyTarget); + if (crv != CKR_OK) { + goto loser; + } + inPeerDBTransaction = PR_FALSE; + } + +loser: + if (inPeerDBTransaction) { + /* The transaction must have failed. Abort. */ + (*keyTarget->sdb_Abort)(keyTarget); + PORT_Assert(crv != CKR_OK); + if (crv == CKR_OK) crv = CKR_GENERAL_ERROR; + } + return crv; +} + +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; +} + +static const CK_ATTRIBUTE * +sftkdb_getAttributeFromConstTemplate(CK_ATTRIBUTE_TYPE attribute, + const 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; + } + if (attr->ulValueLen == 0) { + /* key is too generic to determine that it's unique, usually + * happens in the key gen case */ + return CKR_OBJECT_HANDLE_INVALID; + } + + 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 its 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_OBJECT_HANDLE_INVALID) { + /* key is too generic to determine that it's unique, usually + * happens in the key gen case, tell the caller to go ahead + * and just create it */ + return CKR_OK; + } + 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; +} + + +/* + * check to see if this template conflicts with others in our current database. + */ +static CK_RV +sftkdb_checkConflicts(SDB *db, CK_OBJECT_CLASS objectType, + const CK_ATTRIBUTE *ptemplate, CK_ULONG len, + CK_OBJECT_HANDLE sourceID) +{ + CK_ATTRIBUTE findTemplate[2]; + unsigned char objTypeData[SDB_ULONG_SIZE]; + /* we may need to allocate some temporaries. Keep track of what was + * allocated so we can free it in the end */ + unsigned char *temp1 = NULL; + unsigned char *temp2 = NULL; + CK_ULONG objCount = 0; + SDBFind *find = NULL; + CK_OBJECT_HANDLE id; + const CK_ATTRIBUTE *attr, *attr2; + CK_RV crv; + CK_ATTRIBUTE subject; + + /* Currently the only conflict is with nicknames pointing to the same + * subject when creating or modifying a certificate. */ + /* If the object is not a cert, no problem. */ + if (objectType != CKO_CERTIFICATE) { + return CKR_OK; + } + /* if not setting a nickname then there's still no problem */ + attr = sftkdb_getAttributeFromConstTemplate(CKA_LABEL, ptemplate, len); + if ((attr == NULL) || (attr->ulValueLen == 0)) { + return CKR_OK; + } + /* fetch the subject of the source. For creation and merge, this should + * be found in the template */ + attr2 = sftkdb_getAttributeFromConstTemplate(CKA_SUBJECT, ptemplate, len); + if (sourceID == CK_INVALID_HANDLE) { + if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen < 0)) { + crv = CKR_TEMPLATE_INCOMPLETE; + goto done; + } + } else if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen <= 0)) { + /* sourceID is set if we are trying to modify an existing entry instead + * of creating a new one. In this case the subject may not be (probably + * isn't) in the template, we have to read it from the database */ + subject.type = CKA_SUBJECT; + subject.pValue = NULL; + subject.ulValueLen = 0; + crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1); + if (crv != CKR_OK) { + goto done; + } + if ((CK_LONG)subject.ulValueLen < 0) { + crv = CKR_DEVICE_ERROR; /* closest pkcs11 error to corrupted DB */ + goto done; + } + temp1 = subject.pValue = PORT_Alloc(++subject.ulValueLen); + if (temp1 == NULL) { + crv = CKR_HOST_MEMORY; + goto done; + } + crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1); + if (crv != CKR_OK) { + goto done; + } + attr2 = &subject; + } + + /* check for another cert in the database with the same nickname */ + sftk_ULong2SDBULong(objTypeData, objectType); + findTemplate[0].type = CKA_CLASS; + findTemplate[0].pValue = objTypeData; + findTemplate[0].ulValueLen = SDB_ULONG_SIZE; + findTemplate[1] = *attr; + + crv = (*db->sdb_FindObjectsInit)(db, findTemplate, 2, &find); + if (crv != CKR_OK) { + goto done; + } + (*db->sdb_FindObjects)(db, find, &id, 1, &objCount); + (*db->sdb_FindObjectsFinal)(db, find); + + /* object count == 0 means no conflicting certs found, + * go on with the operation */ + if (objCount == 0) { + crv = CKR_OK; + goto done; + } + + /* There is a least one cert that shares the nickname, make sure it also + * matches the subject. */ + findTemplate[0] = *attr2; + /* we know how big the source subject was. Use that length to create the + * space for the target. If it's not enough space, then it means the + * source subject is too big, and therefore not a match. GetAttributeValue + * will return CKR_BUFFER_TOO_SMALL. Otherwise it should be exactly enough + * space (or enough space to be able to compare the result. */ + temp2 = findTemplate[0].pValue = PORT_Alloc(++findTemplate[0].ulValueLen); + if (temp2 == NULL) { + crv = CKR_HOST_MEMORY; + goto done; + } + crv = (*db->sdb_GetAttributeValue)(db, id, findTemplate, 1); + if (crv != CKR_OK) { + if (crv == CKR_BUFFER_TOO_SMALL) { + /* if our buffer is too small, then the Subjects clearly do + * not match */ + crv = CKR_ATTRIBUTE_VALUE_INVALID; + goto loser; + } + /* otherwise we couldn't get the value, just fail */ + goto done; + } + + /* Ok, we have both subjects, make sure they are the same. + * Compare the subjects */ + if ((findTemplate[0].ulValueLen != attr2->ulValueLen) || + (attr2->ulValueLen > 0 && + PORT_Memcmp(findTemplate[0].pValue, attr2->pValue, attr2->ulValueLen) + != 0)) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + goto loser; + } + crv = CKR_OK; + +done: + /* If we've failed for some other reason than a conflict, make sure we + * return an error code other than CKR_ATTRIBUTE_VALUE_INVALID. + * (NOTE: neither sdb_FindObjectsInit nor sdb_GetAttributeValue should + * return CKR_ATTRIBUTE_VALUE_INVALID, so the following is paranoia). + */ + if (crv == CKR_ATTRIBUTE_VALUE_INVALID) { + crv = CKR_GENERAL_ERROR; /* clearly a programming error */ + } + + /* exit point if we found a conflict */ +loser: + PORT_Free(temp1); + PORT_Free(temp2); + return crv; +} + +/* + * try to update the template to fix any errors. This is only done + * during update. + * + * NOTE: we must update the template or return an error, or the update caller + * will loop forever! + * + * Two copies of the source code for this algorithm exist in NSS. + * Changes must be made in both copies. + * The other copy is in pk11_IncrementNickname() in pk11wrap/pk11merge.c. + * + */ +static CK_RV +sftkdb_resolveConflicts(PRArenaPool *arena, CK_OBJECT_CLASS objectType, + CK_ATTRIBUTE *ptemplate, CK_ULONG *plen) +{ + CK_ATTRIBUTE *attr; + char *nickname, *newNickname; + int end, digit; + + /* sanity checks. We should never get here with these errors */ + if (objectType != CKO_CERTIFICATE) { + return CKR_GENERAL_ERROR; /* shouldn't happen */ + } + attr = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen); + if ((attr == NULL) || (attr->ulValueLen == 0)) { + return CKR_GENERAL_ERROR; /* shouldn't happen */ + } + + /* update the nickname */ + /* is there a number at the end of the nickname already? + * if so just increment that number */ + nickname = (char *)attr->pValue; + + /* does nickname end with " #n*" ? */ + for (end = attr->ulValueLen - 1; + end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0'; + end--) /* just scan */ ; + if (attr->ulValueLen >= 3 && + end < (attr->ulValueLen - 1) /* at least one digit */ && + nickname[end] == '#' && + nickname[end - 1] == ' ') { + /* Already has a suitable suffix string */ + } else { + /* ... append " #2" to the name */ + static const char num2[] = " #2"; + newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + sizeof(num2)); + if (!newNickname) { + return CKR_HOST_MEMORY; + } + PORT_Memcpy(newNickname, nickname, attr->ulValueLen); + PORT_Memcpy(&newNickname[attr->ulValueLen], num2, sizeof(num2)); + attr->pValue = newNickname; /* modifies ptemplate */ + attr->ulValueLen += 3; /* 3 is strlen(num2) */ + return CKR_OK; + } + + for (end = attr->ulValueLen - 1; + end >= 0 && (digit = nickname[end]) <= '9' && digit >= '0'; + end--) { + if (digit < '9') { + nickname[end]++; + return CKR_OK; + } + nickname[end] = '0'; + } + + /* we overflowed, insert a new '1' for a carry in front of the number */ + newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + 1); + if (!newNickname) { + return CKR_HOST_MEMORY; + } + /* PORT_Memcpy should handle len of '0' */ + PORT_Memcpy(newNickname, nickname, ++end); + newNickname[end] = '1'; + PORT_Memset(&newNickname[end+1],'0',attr->ulValueLen - end); + attr->pValue = newNickname; + attr->ulValueLen++; + 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); + + /* + * we have opened a new database, but we have not yet updated it. We are + * still running pointing to the old database (so the application can + * still read). We don't want to write to the old database at this point, + * however, since it leads to user confusion. So at this point we simply + * require a user login. Let NSS know this so it can prompt the user. + */ + if (db == handle->update) { + return CKR_USER_NOT_LOGGED_IN; + } + + 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; + + /* + * We want to make the base database as free from object specific knowledge + * as possible. To maintain compatibility, keep some of the desirable + * object specific semantics of the old database. + * + * These were 2 fold: + * 1) there were certain conflicts (like trying to set the same nickname + * on two different subjects) that would return an error. + * 2) Importing the 'same' object would silently update that object. + * + * The following 2 functions mimic the desirable effects of these two + * semantics without pushing any object knowledge to the underlying database + * code. + */ + + /* make sure we don't have attributes that conflict with the existing DB */ + crv = sftkdb_checkConflicts(db, object->objclass, template, count, + CK_INVALID_HANDLE); + if (crv != CKR_OK) { + goto loser; + } + /* Find any copies that match this particular object */ + 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) { + (*db->sdb_Abort)(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, SFTKObject *object, + const CK_ATTRIBUTE *template, CK_ULONG count) +{ + CK_ATTRIBUTE *ntemplate; + unsigned char *data = NULL; + PLArenaPool *arena = NULL; + SDB *db; + CK_RV crv = CKR_OK; + CK_OBJECT_HANDLE objectID = (object->handle & SFTK_OBJ_ID_MASK); + PRBool inTransaction = PR_FALSE; + + if (handle == NULL) { + return CKR_TOKEN_WRITE_PROTECTED; + } + + db = SFTK_GET_SDB(handle); + /* nothing to do */ + if (count == 0) { + return CKR_OK; + } + /* + * we have opened a new database, but we have not yet updated it. We are + * still running pointing to the old database (so the application can + * still read). We don't want to write to the old database at this point, + * however, since it leads to user confusion. So at this point we simply + * require a user login. Let NSS know this so it can prompt the user. + */ + if (db == handle->update) { + return CKR_USER_NOT_LOGGED_IN; + } + + ntemplate = sftkdb_fixupTemplateIn(template, count, &data); + if (ntemplate == NULL) { + return CKR_HOST_MEMORY; + } + + /* make sure we don't have attributes that conflict with the existing DB */ + crv = sftkdb_checkConflicts(db, object->objclass, template, count, objectID); + if (crv != CKR_OK) { + goto loser; + } + + arena = PORT_NewArena(256); + if (arena == NULL) { + crv = CKR_HOST_MEMORY; + goto loser; + } + + crv = (*db->sdb_Begin)(db); + if (crv != CKR_OK) { + goto loser; + } + inTransaction = PR_TRUE; + 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 && inTransaction) { + (*db->sdb_Abort)(db); + } + if (data) { + PORT_Free(ntemplate); + PORT_Free(data); + } + if (arena) { + 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) +{ +#ifdef NO_FORK_CHECK + PRBool parentForkedAfterC_Initialize = PR_FALSE; +#endif + if (handle == NULL) { + return CKR_OK; + } + if (handle->update) { + if (handle->db->sdb_SetForkState) { + (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize); + } + (*handle->update->sdb_Close)(handle->update); + } + if (handle->db) { + if (handle->db->sdb_SetForkState) { + (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize); + } + (*handle->db->sdb_Close)(handle->db); + } + if (handle->passwordLock) { + SKIP_AFTER_FORK(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 reconcile 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_reconcileTrustEntry(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 reconcile 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_reconcileTrust(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_reconcileTrustEntry(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) +{ + PRBool done; /* should we repeat the loop? */ + CK_OBJECT_HANDLE id; + CK_RV crv = CKR_OK; + + do { + crv = sftkdb_checkConflicts(db, objectType, ptemplate, + *plen, CK_INVALID_HANDLE); + if (crv != CKR_ATTRIBUTE_VALUE_INVALID) { + break; + } + crv = sftkdb_resolveConflicts(arena, objectType, ptemplate, plen); + } while (crv == CKR_OK); + + if (crv != CKR_OK) { + return SFTKDB_DO_NOTHING; + } + + do { + done = PR_TRUE; + crv = sftkdb_lookupObject(db, objectType, &id, ptemplate, *plen); + if (crv != CKR_OK) { + return SFTKDB_DO_NOTHING; + } + + /* This object already exists, merge it, don't update */ + if (id != CK_INVALID_HANDLE) { + 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 reconcile them */ + *targetID = id; + return sftkdb_reconcileTrust(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 = attr ? sftkdb_incrementCKAID(arena, attr) + : CKR_HOST_MEMORY; + /* in the extremely rare event that we needed memory and + * couldn't get it, just drop the key */ + if (crv != CKR_OK) { + return SFTKDB_DO_NOTHING; + } + done = PR_FALSE; /* 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 (!done); + + /* 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; + + SKIP_AFTER_FORK(PZ_Lock(slot->slotLock)); + dbHandle = slot->keyDB; + if (dbHandle) { + PR_AtomicIncrement(&dbHandle->ref); + } + SKIP_AFTER_FORK(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(nssILockAttribute); + } + 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, PRBool isFIPS, + 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, + isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB); + break; + case SDB_MULTIACCESS: + crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags, + isFIPS, 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, isFIPS, + 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, + isFIPS, 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/mozilla/security/nss/lib/softoken/sftkdb.h b/mozilla/security/nss/lib/softoken/sftkdb.h new file mode 100644 index 0000000..2806d6f --- /dev/null +++ b/mozilla/security/nss/lib/softoken/sftkdb.h @@ -0,0 +1,118 @@ +/* ***** 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, SFTKObject *object, + 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, PRBool isFIPS, + 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/mozilla/security/nss/lib/softoken/sftkdbt.h b/mozilla/security/nss/lib/softoken/sftkdbt.h new file mode 100644 index 0000000..f8cdb06 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/sftkdbti.h b/mozilla/security/nss/lib/softoken/sftkdbti.h new file mode 100644 index 0000000..efaf842 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/sftkmod.c b/mozilla/security/nss/lib/softoken/sftkmod.c new file mode 100644 index 0000000..c89b7da --- /dev/null +++ b/mozilla/security/nss/lib/softoken/sftkmod.c @@ -0,0 +1,734 @@ +/* ***** 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" +#if defined (_WIN32) +#include <io.h> +#endif + +/**************************************************************** + * + * 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; +} + +#ifdef XP_UNIX +#include <unistd.h> +#endif +#include <fcntl.h> + +#ifndef WINCE +/* same as fopen, except it doesn't use umask, but explicit */ +FILE * +lfopen(const char *name, const char *mode, int flags) +{ + int fd; + FILE *file; + + fd = open(name, flags, 0600); + if (fd < 0) { + return NULL; + } + file = fdopen(fd, mode); + if (!file) { + close(fd); + } + /* file inherits fd */ + return file; +} +#endif + +#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})\"" + +/* + * 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; +#ifdef WINCE + fd2 = fopen(dbname2, "w+"); +#else + fd2 = lfopen(dbname2, "w+", O_CREAT|O_RDWR|O_TRUNC); +#endif + 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); + if (found) { + /* rename dbname2 to dbname */ + PR_Delete(dbname); + PR_Rename(dbname2,dbname); + } else { + PR_Delete(dbname2); + } + PORT_Free(dbname2); + PORT_Free(lib); + PORT_Free(name); + return SECSuccess; + +loser: + if (fd != NULL) { + fclose(fd); + } + if (fd2 != NULL) { + fclose(fd2); + } + if (dbname2) { + PR_Delete(dbname2); + PORT_Free(dbname2); + } + PORT_Free(lib); + PORT_Free(name); + 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); + +#ifdef WINCE + fd = fopen(dbname, "a+"); +#else + fd = lfopen(dbname, "a+", O_CREAT|O_RDWR|O_APPEND); +#endif + 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; + } + block = sftkdb_DupnCat(block, module, keyEnd-module+1); + if (block == NULL) { goto loser; } + value = sftk_argFetchValue(&keyEnd[1], &count); + 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/mozilla/security/nss/lib/softoken/sftkpars.c b/mozilla/security/nss/lib/softoken/sftkpars.c new file mode 100644 index 0000000..c9df469 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/sftkpars.c @@ -0,0 +1,650 @@ +/* ***** 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; \ + if (target) \ + PORT_Free(target); \ + 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((unsigned char )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 = NULL; + 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); tmp = NULL; }) + SFTK_HANDLE_STRING_ARG(index,tmp,"flags=", + if(tmp) { sftk_parseTokenFlags(param,parsed); PORT_Free(tmp); tmp = NULL; }) + 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 = NULL; + 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); tmp = NULL; }) + SFTK_HANDLE_STRING_ARG(index,tmp,"tokens=", + if(tmp) { sftk_parseTokens(tmp,parsed); PORT_Free(tmp); tmp = NULL; }) + 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; +#ifdef NSS_DISABLE_DBM + *dbType = SDB_SQL; +#else + *dbType = SDB_LEGACY; +#endif + 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/mozilla/security/nss/lib/softoken/sftkpars.h b/mozilla/security/nss/lib/softoken/sftkpars.h new file mode 100644 index 0000000..0086edf --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/sftkpwd.c b/mozilla/security/nss/lib/softoken/sftkpwd.c new file mode 100644 index 0000000..9d56f1b --- /dev/null +++ b/mozilla/security/nss/lib/softoken/sftkpwd.c @@ -0,0 +1,1309 @@ +/* ***** 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" +#include "softoken.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, ¶m->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 */ + SKIP_AFTER_FORK(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; + SKIP_AFTER_FORK(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; + + /* don't have one */ + if (!handle) { + return; + } + + /* if we're a cert db, we don't have one */ + if (handle->type == SFTK_CERTDB_TYPE) { + 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); + + /* If no password is set, we can update right away */ + if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update + && crv != CKR_OK) { + /* update the peer certdb if it exists */ + if (keydb->peerDB) { + sftkdb_Update(keydb->peerDB, NULL); + } + sftkdb_Update(keydb, NULL); + } + 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->db->sdb_flags & SDB_RDONLY) == 0) && 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; +} + +/* + * lose 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/mozilla/security/nss/lib/softoken/softkver.c b/mozilla/security/nss/lib/softoken/softkver.c new file mode 100644 index 0000000..931d620 --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/softkver.h b/mozilla/security/nss/lib/softoken/softkver.h new file mode 100644 index 0000000..071bdab --- /dev/null +++ b/mozilla/security/nss/lib/softoken/softkver.h @@ -0,0 +1,67 @@ +/* + * 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, build number, + * and whether this is a beta release. + * + * The format of the version string should be + * "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]" + */ +#define SOFTOKEN_VERSION "3.12.11.0" SOFTOKEN_ECC_STRING +#define SOFTOKEN_VMAJOR 3 +#define SOFTOKEN_VMINOR 12 +#define SOFTOKEN_VPATCH 11 +#define SOFTOKEN_VBUILD 0 +#define SOFTOKEN_BETA PR_FALSE + +#endif /* _SOFTKVER_H_ */ diff --git a/mozilla/security/nss/lib/softoken/softoken.h b/mozilla/security/nss/lib/softoken/softoken.h new file mode 100644 index 0000000..6375f9a --- /dev/null +++ b/mozilla/security/nss/lib/softoken/softoken.h @@ -0,0 +1,385 @@ +/* + * 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: softoken.h,v 1.23 2009/02/26 06:57:15 nelson%bolyard.com Exp $ */ + +#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, + NSSAuditType, 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 process after C_Initialize +*/ +#if defined(XP_UNIX) && !defined(NO_CHECK_FORK) + +#ifdef DEBUG + +#define FORK_ASSERT() \ + { \ + char* forkAssert = getenv("NSS_STRICT_NOFORK"); \ + if ( (!forkAssert) || (0 == strcmp(forkAssert, "1")) ) { \ + PORT_Assert(0); \ + } \ + } + +#else + +#define FORK_ASSERT() + +#endif + +/* we have 3 methods of implementing the fork checks : + * - Solaris "mixed" method + * - pthread_atfork method + * - getpid method + */ + +#if !defined (CHECK_FORK_MIXED) && !defined(CHECK_FORK_PTHREAD) && \ + !defined (CHECK_FORK_GETPID) + +/* Choose fork check method automatically unless specified + * This section should be updated as more platforms get pthread fixes + * to unregister fork handlers in dlclose. + */ + +#ifdef SOLARIS + +/* Solaris 8, s9 use PID checks, s10 uses pthread_atfork */ + +#define CHECK_FORK_MIXED + +#elif defined(LINUX) + +#define CHECK_FORK_PTHREAD + +#else + +/* Other Unix platforms use only PID checks. Even if pthread_atfork is + * available, the behavior of dlclose isn't guaranteed by POSIX to + * unregister the fork handler. */ + +#define CHECK_FORK_GETPID + +#endif + +#endif + +#if defined(CHECK_FORK_MIXED) + +extern PRBool usePthread_atfork; +#include <unistd.h> +extern pid_t myPid; +extern PRBool forked; + +#define PARENT_FORKED() (usePthread_atfork ? forked : (myPid && myPid != getpid())) + +#elif defined(CHECK_FORK_PTHREAD) + +extern PRBool forked; + +#define PARENT_FORKED() forked + +#elif defined(CHECK_FORK_GETPID) + +#include <unistd.h> +extern pid_t myPid; + +#define PARENT_FORKED() (myPid && myPid != getpid()) + +#endif + +extern PRBool parentForkedAfterC_Initialize; +extern PRBool sftkForkCheckDisabled; + +#define CHECK_FORK() \ + do { \ + if (!sftkForkCheckDisabled && PARENT_FORKED()) { \ + FORK_ASSERT(); \ + return CKR_DEVICE_ERROR; \ + } \ + } while (0) + +#define SKIP_AFTER_FORK(x) if (!parentForkedAfterC_Initialize) x + +#define ENABLE_FORK_CHECK() \ + { \ + char* doForkCheck = getenv("NSS_STRICT_NOFORK"); \ + if ( doForkCheck && !strcmp(doForkCheck, "DISABLED") ) { \ + sftkForkCheckDisabled = PR_TRUE; \ + } \ + } + + +#else + +/* non-Unix platforms, or fork check disabled */ + +#define CHECK_FORK() +#define SKIP_AFTER_FORK(x) x +#define ENABLE_FORK_CHECK() + +#ifndef NO_FORK_CHECK +#define NO_FORK_CHECK +#endif + +#endif + + +SEC_END_PROTOS + +#endif /* _SOFTOKEN_H_ */ diff --git a/mozilla/security/nss/lib/softoken/softokn.def b/mozilla/security/nss/lib/softoken/softokn.def new file mode 100644 index 0000000..2544d1b --- /dev/null +++ b/mozilla/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/mozilla/security/nss/lib/softoken/softokn.rc b/mozilla/security/nss/lib/softoken/softokn.rc new file mode 100644 index 0000000..9596808 --- /dev/null +++ b/mozilla/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,SOFTOKEN_VBUILD + PRODUCTVERSION SOFTOKEN_VMAJOR,SOFTOKEN_VMINOR,SOFTOKEN_VPATCH,SOFTOKEN_VBUILD + 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/mozilla/security/nss/lib/softoken/softoknt.h b/mozilla/security/nss/lib/softoken/softoknt.h new file mode 100644 index 0000000..0ec8992 --- /dev/null +++ b/mozilla/security/nss/lib/softoken/softoknt.h @@ -0,0 +1,94 @@ +/* + * 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: softoknt.h,v 1.6 2009/08/03 16:58:28 christophe.ravel.bugs%sun.com Exp $ */ + +#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; + +typedef enum { + NSS_AUDIT_ACCESS_KEY = 0, + NSS_AUDIT_CHANGE_KEY, + NSS_AUDIT_COPY_KEY, + NSS_AUDIT_CRYPT, + NSS_AUDIT_DERIVE_KEY, + NSS_AUDIT_DESTROY_KEY, + NSS_AUDIT_DIGEST_KEY, + NSS_AUDIT_FIPS_STATE, + NSS_AUDIT_GENERATE_KEY, + NSS_AUDIT_INIT_PIN, + NSS_AUDIT_INIT_TOKEN, + NSS_AUDIT_LOAD_KEY, + NSS_AUDIT_LOGIN, + NSS_AUDIT_LOGOUT, + NSS_AUDIT_SELF_TEST, + NSS_AUDIT_SET_PIN, + NSS_AUDIT_UNWRAP_KEY, + NSS_AUDIT_WRAP_KEY +} NSSAuditType; + +#endif /* _SOFTOKNT_H_ */ diff --git a/mozilla/security/nss/lib/softoken/tlsprf.c b/mozilla/security/nss/lib/softoken/tlsprf.c new file mode 100644 index 0000000..2c100d9 --- /dev/null +++ b/mozilla/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: tlsprf.c,v 1.6 2005/08/06 09:27:28 nelsonb%netscape.com Exp $ */ + +#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; +} + |