view usr/src/lib/libkmf/plugins/kmf_nss/common/nss_spi.c @ 3692:b717db492822

6525220 KMF_ValidateCert() and KMF_FindCertInCRL() should take a subject certificate as input
author hylee
date Wed, 21 Feb 2007 15:26:17 -0800
parents 8ddeb2ace8aa
children 79eeec53e95c
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * NSS keystore wrapper
 *
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <synch.h>

#include <kmfapiP.h>
#include <ber_der.h>
#include <oidsalg.h>
/* NSS related headers */

#include <mps/nss.h>
#include <mps/cert.h>
#include <mps/certdb.h>
#include <mps/secoid.h>
#include <mps/secder.h>
#include <mps/secerr.h>
#include <mps/cryptohi.h>
#include <mps/keyhi.h>
#include <mps/keythi.h>
#include <mps/pk11func.h>
#include <mps/pk11pqg.h>
#include <mps/pkcs12.h>
#include <mps/p12plcy.h>
#include <mps/prerror.h>

#define	NSS_OK		0

mutex_t init_lock = DEFAULTMUTEX;
static int nss_initialized = 0;

KMF_RETURN
NSS_ConfigureKeystore(KMF_HANDLE_T, KMF_CONFIG_PARAMS *);

KMF_RETURN
NSS_FindCert(KMF_HANDLE_T,
	KMF_FINDCERT_PARAMS *params,
	KMF_X509_DER_CERT *kmf_cert,
	uint32_t *num_certs);

void
NSS_FreeKMFCert(KMF_HANDLE_T, KMF_X509_DER_CERT *);

KMF_RETURN
NSS_StoreCert(KMF_HANDLE_T, KMF_STORECERT_PARAMS *params,
		KMF_DATA * pcert);

KMF_RETURN
NSS_ImportCert(KMF_HANDLE_T, KMF_IMPORTCERT_PARAMS *params);

KMF_RETURN
NSS_DeleteCert(KMF_HANDLE_T, KMF_DELETECERT_PARAMS *params);

KMF_RETURN
NSS_CreateKeypair(KMF_HANDLE_T, KMF_CREATEKEYPAIR_PARAMS *,
	KMF_KEY_HANDLE *, KMF_KEY_HANDLE *);

KMF_RETURN
NSS_EncodePubKeyData(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_DATA *);

KMF_RETURN
NSS_SignData(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_OID *,
	KMF_DATA *, KMF_DATA *);

KMF_RETURN
NSS_ImportCRL(KMF_HANDLE_T, KMF_IMPORTCRL_PARAMS *params);

KMF_RETURN
NSS_DeleteCRL(KMF_HANDLE_T, KMF_DELETECRL_PARAMS *params);

KMF_RETURN
NSS_FindCRL(KMF_HANDLE_T, KMF_FINDCRL_PARAMS *params,
	char **CRLNameList, int *CRLCount);

KMF_RETURN
NSS_FindKey(KMF_HANDLE_T, KMF_FINDKEY_PARAMS *,
	KMF_KEY_HANDLE *, uint32_t *);

KMF_RETURN
NSS_FindCertInCRL(KMF_HANDLE_T, KMF_FINDCERTINCRL_PARAMS *params);

KMF_RETURN
NSS_GetErrorString(KMF_HANDLE_T, char **);

KMF_RETURN
NSS_DeleteKey(KMF_HANDLE_T, KMF_DELETEKEY_PARAMS *,
	KMF_KEY_HANDLE *, boolean_t);

KMF_RETURN
NSS_GetPrikeyByCert(KMF_HANDLE_T, KMF_CRYPTOWITHCERT_PARAMS *, KMF_DATA *,
	KMF_KEY_HANDLE *, KMF_KEY_ALG);

KMF_RETURN
NSS_DecryptData(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_OID *,
	KMF_DATA *, KMF_DATA *);

KMF_RETURN
NSS_ExportP12(KMF_HANDLE_T,
	KMF_EXPORTP12_PARAMS *,
	int, KMF_X509_DER_CERT *,
	int, KMF_KEY_HANDLE *,
	char *);

KMF_RETURN
NSS_StorePrivateKey(KMF_HANDLE_T, KMF_STOREKEY_PARAMS *, KMF_RAW_KEY_DATA *);

KMF_RETURN
NSS_CreateSymKey(KMF_HANDLE_T, KMF_CREATESYMKEY_PARAMS *, KMF_KEY_HANDLE *);

KMF_RETURN
NSS_GetSymKeyValue(KMF_HANDLE_T, KMF_KEY_HANDLE *, KMF_RAW_SYM_KEY *);

KMF_RETURN
NSS_SetTokenPin(KMF_HANDLE_T, KMF_SETPIN_PARAMS *, KMF_CREDENTIAL *);

static
KMF_PLUGIN_FUNCLIST nss_plugin_table =
{
	1,				/* Version */
	NSS_ConfigureKeystore,
	NSS_FindCert,
	NSS_FreeKMFCert,
	NSS_StoreCert,
	NSS_ImportCert,
	NSS_ImportCRL,
	NSS_DeleteCert,
	NSS_DeleteCRL,
	NSS_CreateKeypair,
	NSS_FindKey,
	NSS_EncodePubKeyData,
	NSS_SignData,
	NSS_DeleteKey,
	NULL    /* ListCRL */,
	NSS_FindCRL,
	NSS_FindCertInCRL,
	NSS_GetErrorString,
	NSS_GetPrikeyByCert,
	NSS_DecryptData,
	NSS_ExportP12,
	NSS_StorePrivateKey,
	NSS_CreateSymKey,
	NSS_GetSymKeyValue,
	NSS_SetTokenPin,
	NULL /* Finalize */
};

/* additions for importing and exporting PKCS 12 files */
typedef struct p12uContextStr {
	char		*filename;	/* name of file */
	PRFileDesc	*file;		/* pointer to file */
	PRBool		error;		/* error occurred? */
	int		errorValue;	/* which error occurred? */
} p12uContext;

#define	SET_ERROR(h, c) h->lasterr.kstype = KMF_KEYSTORE_NSS; \
	h->lasterr.errcode = c;

KMF_PLUGIN_FUNCLIST *
KMF_Plugin_Initialize()
{
	SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
	SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
	SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
	SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
	SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
	SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);

	return (&nss_plugin_table);
}

static char *
/*ARGSUSED*/
nss_getpassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
	if (retry)
		return (NULL);
	if (arg != NULL)
		return ((char *)strdup(arg));
	else
		return (NULL);
}

static KMF_RETURN
nss_authenticate(KMF_HANDLE_T handle,
	PK11SlotInfo *nss_slot, KMF_CREDENTIAL *cred)
{

	SECStatus nssrv = SECSuccess;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;

	/* If a password was given, try to login to the slot */
	if (cred == NULL || cred->cred == NULL || cred->credlen == 0 ||
		nss_slot == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	if (PK11_IsLoggedIn(nss_slot, NULL)) {
		return (KMF_OK);
	}

	PK11_SetPasswordFunc(nss_getpassword);
	nssrv = PK11_Authenticate(nss_slot, PR_TRUE,
		(void *)cred->cred);

	if (nssrv != SECSuccess) {
		SET_ERROR(kmfh, nssrv);
		PK11_FreeSlot(nss_slot);
		return (KMF_ERR_AUTH_FAILED);
	}

	return (KMF_OK);
}

static SECStatus
Init_NSS_DBs(const char *configdir,
	const char *certPrefix,
	const char *keyPrefix,
	const char *secmodName)
{
	SECStatus rv = NSS_OK;

	(void) mutex_lock(&init_lock);

	/* If another thread already did it, return OK. */
	if (nss_initialized) {
		(void) mutex_unlock(&init_lock);
		return (SECSuccess);
	}

	rv = NSS_Initialize((configdir && strlen(configdir)) ?
		configdir : "./", certPrefix,
		keyPrefix, secmodName ? secmodName : "secmod.db",
		NSS_INIT_COOPERATE);
	if (rv != SECSuccess) {
		goto end;
	}

	nss_initialized++;
end:
	(void) mutex_unlock(&init_lock);
	return (rv);
}

/*
 * When it is called the first time, it will intialize NSS.  Once the NSS
 * is initialized, this function returns KMF_KEYSTORE_ALREADY_INITIALIZED
 * if it is called again.
 */
KMF_RETURN
NSS_ConfigureKeystore(KMF_HANDLE_T handle, KMF_CONFIG_PARAMS *params)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;

	if (params == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	(void) mutex_lock(&init_lock);
	if (nss_initialized == 0) {
		SECStatus err;

		(void) mutex_unlock(&init_lock);
		err = Init_NSS_DBs(params->nssconfig.configdir,
		    params->nssconfig.certPrefix,
		    params->nssconfig.keyPrefix,
		    params->nssconfig.secModName);
		if (err != SECSuccess) {
			SET_ERROR(kmfh, err);
			return (KMF_ERR_INTERNAL);
		}
	} else {
		rv = KMF_KEYSTORE_ALREADY_INITIALIZED;
		(void) mutex_unlock(&init_lock);
	}

	return (rv);
}


/*
 * This function sets up the slot to be used for other operations.
 * This function is basically called by every NSS SPI function.
 * For those functions that can only be performed in the internal slot, the
 * boolean "internal_slot_only" argument needs to be TRUE.
 * A slot pointer will be returned when this function is executed successfully.
 */
static KMF_RETURN
Do_NSS_Init(
	void *handle,
	KMF_NSS_PARAMS nss_opts,
	boolean_t internal_slot_only,
	PK11SlotInfo **nss_slot)
{
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;

	if (!nss_initialized)
		return (KMF_ERR_PLUGIN_INIT);

	/*
	 * NSS Is already initialized, but we need to find
	 * the right slot.
	 */
	if (nss_opts.slotlabel == NULL ||
	    strcmp(nss_opts.slotlabel, "internal") == 0) {
		*nss_slot = PK11_GetInternalKeySlot();
	} else if (internal_slot_only == TRUE)  {
		return (KMF_ERR_SLOTNAME);
	} else {
		*nss_slot = PK11_FindSlotByName(nss_opts.slotlabel);
	}

	if (*nss_slot == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		return (KMF_ERR_SLOTNAME);
	}

	/*
	 * If the token was not yet initialized, return an error.
	 */
	if (PK11_NeedUserInit(*nss_slot)) {
		return (KMF_ERR_UNINITIALIZED_TOKEN);
	}

	return (KMF_OK);
}

static KMF_RETURN
nss2kmf_cert(CERTCertificate *nss_cert, KMF_X509_DER_CERT *kmf_cert)
{
	kmf_cert->kmf_private.keystore_type = KMF_KEYSTORE_NSS;
	kmf_cert->kmf_private.flags = KMF_FLAG_CERT_VALID;

	kmf_cert->certificate.Length = nss_cert->derCert.len;

	if ((kmf_cert->certificate.Data = malloc(nss_cert->derCert.len)) ==
		NULL) {
		kmf_cert->certificate.Length = 0;
		return (KMF_ERR_MEMORY);
	}
	(void) memcpy(kmf_cert->certificate.Data, nss_cert->derCert.data,
		nss_cert->derCert.len);
	if (nss_cert->nickname != NULL)
		kmf_cert->kmf_private.label =
			(char *)strdup(nss_cert->nickname);
	return (KMF_OK);
}

static KMF_RETURN
nss_getcert_by_label(KMF_HANDLE *kmfh,
	char *name, KMF_X509_DER_CERT *kmf_cert,
	uint32_t *num_certs, KMF_CERT_VALIDITY find_criteria)
{
	KMF_RETURN rv = KMF_OK;
	CERTCertificate *nss_cert;
	SECCertTimeValidity validity;

	nss_cert = PK11_FindCertFromNickname(name, NULL);
	if (nss_cert == NULL) {
		*num_certs = 0;
		SET_ERROR(kmfh, PORT_GetError());
		*num_certs = 0;
		return (KMF_ERR_CERT_NOT_FOUND);
	} else {
		*num_certs = 1;
	}

	switch (find_criteria) {
	case KMF_ALL_CERTS:
		break;
	case KMF_NONEXPIRED_CERTS:
		validity = CERT_CheckCertValidTimes(nss_cert, PR_Now(),
			PR_FALSE);
		if (validity != secCertTimeValid) {
			/* this is an invalid cert, reject it */
			*num_certs = 0;
			CERT_DestroyCertificate(nss_cert);
			return (KMF_OK);
		}
		break;
	case KMF_EXPIRED_CERTS:
		validity = CERT_CheckCertValidTimes(nss_cert, PR_Now(),
			PR_FALSE);
		if (validity == secCertTimeValid) {
			/* this is a valid cert, reject it in this case. */
			*num_certs = 0;
			CERT_DestroyCertificate(nss_cert);
			return (KMF_OK);
		}
		break;
	default:
		return (KMF_ERR_BAD_PARAMETER);
	}

	if (kmf_cert != NULL)
		rv = nss2kmf_cert(nss_cert, kmf_cert);

	/* We copied the data we need, so cleanup the internal record */
	CERT_DestroyCertificate(nss_cert);

	if (rv != KMF_OK)
		*num_certs = 0;

	return (rv);
}

static KMF_RETURN
nss_find_matching_certs(PK11SlotInfo *slot,
	char *issuer, char *subject, KMF_BIGINT *serial,
	CERTCertList **certlist, KMF_CERT_VALIDITY find_criteria)
{
	KMF_RETURN rv = KMF_OK;
	SECStatus ret;
	CERTCertList *list;
	CERTCertListNode *node;
	KMF_X509_NAME issuerDN, subjectDN;
	boolean_t findIssuer = FALSE;
	boolean_t findSubject = FALSE;
	boolean_t findSerial = FALSE;

	if (issuer != NULL && strlen(issuer)) {
		rv = KMF_DNParser(issuer,  &issuerDN);
		if (rv != KMF_OK)
			return (rv);
		findIssuer = TRUE;
	}
	if (subject != NULL && strlen(subject)) {
		rv = KMF_DNParser(subject, &subjectDN);
		if (rv != KMF_OK)
			return (rv);
		findSubject = TRUE;
	}
	if (serial != 0 && serial->val != NULL && serial->len > 0)
		findSerial = TRUE;

	list = PK11_ListCertsInSlot(slot);
	if (list) {
		node = CERT_LIST_HEAD(list);
		while (!CERT_LIST_END(node, list)) {
			KMF_X509_NAME cmpDN;
			KMF_DATA der;
			boolean_t match;
			CERTCertListNode *freenode;

			if (findIssuer) {
				der.Data = node->cert->derIssuer.data;
				der.Length = node->cert->derIssuer.len;
				rv = DerDecodeName(&der, &cmpDN);
				if (rv == KMF_OK) {
					match = !KMF_CompareRDNs(&issuerDN,
						&cmpDN);
					KMF_FreeDN(&cmpDN);
					if (!match)
						goto delete_and_cont;
				} else {
					goto delete_and_cont;
				}
			}
			if (findSubject) {
				der.Data = node->cert->derSubject.data;
				der.Length = node->cert->derSubject.len;
				rv = DerDecodeName(&der, &cmpDN);
				if (rv == KMF_OK) {
					match = !KMF_CompareRDNs(&subjectDN,
						&cmpDN);
					KMF_FreeDN(&cmpDN);
					if (!match)
						goto delete_and_cont;
				} else {
					goto delete_and_cont;
				}
			}
			if (findSerial) {
				SECItem *sernum;

				sernum = &node->cert->serialNumber;

				if (serial->len != sernum->len)
					goto delete_and_cont;

				if (memcmp(sernum->data, serial->val,
					serial->len))
					goto delete_and_cont;
			}

			/* select the certs using find criteria */
			switch (find_criteria) {
			case KMF_ALL_CERTS:
				break;
			case KMF_NONEXPIRED_CERTS:
				ret = CERT_CertTimesValid(node->cert);
				if (ret == SECFailure) {
					/* this is an invalid cert */
					goto skip;
				}
				break;

			case KMF_EXPIRED_CERTS:
				ret = CERT_CertTimesValid(node->cert);
				if (ret != SECFailure) {
					/* this is a valid cert */
					goto skip;
				}
				break;
			}
skip:
			node = CERT_LIST_NEXT(node);
			continue;
delete_and_cont:
			freenode = node;
			node = CERT_LIST_NEXT(node);
			CERT_RemoveCertListNode(freenode);
		}
	}

	if (rv == KMF_OK && certlist != NULL) {
		*certlist = list;
	} else {
		CERT_DestroyCertList(list);
	}
	return (rv);
}

static KMF_RETURN
convertCertList(void *kmfhandle,
	CERTCertList *nsscerts, KMF_X509_DER_CERT *kmfcerts,
	uint32_t *numcerts)
{
	KMF_RETURN rv = KMF_OK;
	CERTCertListNode *node;

	*numcerts = 0;

	for (node = CERT_LIST_HEAD(nsscerts);
		!CERT_LIST_END(node, nsscerts) && rv == KMF_OK;
		node = CERT_LIST_NEXT(node), (*numcerts)++) {
		if (kmfcerts != NULL)
			rv = nss2kmf_cert(node->cert, &kmfcerts[*numcerts]);
	}

	/*
	 * If we failed, delete any certs allocated so far.
	 */
	if (rv != KMF_OK) {
		int i;
		for (i = 0; i < *numcerts; i++)
			KMF_FreeKMFCert(kmfhandle, &kmfcerts[i]);
	}
	return (rv);
}

KMF_RETURN
NSS_FindCert(KMF_HANDLE_T handle, KMF_FINDCERT_PARAMS *params,
	KMF_X509_DER_CERT *kmfcerts,
	uint32_t *num_certs)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	PK11SlotInfo *nss_slot = NULL;
	CERTCertList *certlist = NULL;

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, FALSE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	*num_certs = 0;
	if (params->certLabel) {
		rv = nss_getcert_by_label(kmfh,
			params->certLabel,
			kmfcerts, num_certs, params->find_cert_validity);
	} else {
		rv = nss_find_matching_certs(nss_slot,
			params->issuer, params->subject, params->serial,
			&certlist, params->find_cert_validity);

		if (rv == KMF_OK && certlist != NULL) {
			rv = convertCertList(handle,
				certlist, kmfcerts, num_certs);
			CERT_DestroyCertList(certlist);
		}
	}

	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	if (rv == KMF_OK && *num_certs == 0)
		rv = KMF_ERR_CERT_NOT_FOUND;

	return (rv);
}

void
/*ARGSUSED*/
NSS_FreeKMFCert(KMF_HANDLE_T handle,
	KMF_X509_DER_CERT *kmf_cert)
{
	if (kmf_cert != NULL) {
		if (kmf_cert->certificate.Data != NULL) {
			free(kmf_cert->certificate.Data);
			kmf_cert->certificate.Data = NULL;
			kmf_cert->certificate.Length = 0;
		}
		if (kmf_cert->kmf_private.label != NULL) {
			free(kmf_cert->kmf_private.label);
			kmf_cert->kmf_private.label = NULL;
		}
	}
}

KMF_RETURN
NSS_StoreCert(KMF_HANDLE_T handle, KMF_STORECERT_PARAMS *params,
	KMF_DATA *pcert)
{
	KMF_RETURN ret = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	SECStatus nss_rv;
	CERTCertificate *nss_cert = NULL;
	CERTCertTrust *nss_trust = NULL;
	PK11SlotInfo *nss_slot = NULL;
	CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB();

	if (pcert == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	/* NSS only support DER format */
	if (params == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	ret = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, FALSE, &nss_slot);
	if (ret != KMF_OK) {
		return (ret);
	}

	nss_cert = CERT_DecodeCertFromPackage((char *)pcert->Data,
	    pcert->Length);
	if (nss_cert == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto out;
	}

	nss_rv = PK11_ImportCert(nss_slot, nss_cert, CK_INVALID_HANDLE,
	    params->certLabel, 0);
	if (nss_rv) {
		SET_ERROR(kmfh, nss_rv);
		ret = KMF_ERR_BAD_CERT_FORMAT;
		goto out;
	}

	if (params->ks_opt_u.nss_opts.trustflag != NULL &&
		strlen(params->ks_opt_u.nss_opts.trustflag)) {
		nss_trust = (CERTCertTrust *) malloc(sizeof (CERTCertTrust));
		if (nss_trust == NULL) {
			ret = KMF_ERR_MEMORY;
				goto out;
		}
		nss_rv = CERT_DecodeTrustString(nss_trust,
			params->ks_opt_u.nss_opts.trustflag);
		if (nss_rv) {
			SET_ERROR(kmfh, nss_rv);
			ret = KMF_ERR_BAD_PARAMETER;
			goto out;
		}

		nss_rv = CERT_ChangeCertTrust(certHandle, nss_cert, nss_trust);
		if (nss_rv) {
			SET_ERROR(kmfh, nss_rv);
			ret = KMF_ERR_BAD_PARAMETER;
		}
	}

out:
	if (nss_trust != NULL) {
		free(nss_trust);
	}

	if (nss_cert != NULL) {
		CERT_DestroyCertificate(nss_cert);
	}

	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	return (ret);
}


KMF_RETURN
NSS_ImportCert(KMF_HANDLE_T handle, KMF_IMPORTCERT_PARAMS *params)
{
	KMF_RETURN ret = KMF_OK;
	KMF_STORECERT_PARAMS scparams;
	KMF_DATA cert = {NULL, 0};
	KMF_DATA cert_der = {NULL, 0};
	KMF_DATA *cptr = NULL;
	KMF_ENCODE_FORMAT format;

	if (params == NULL || params->certfile == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	/*
	 * Check if the input cert file is a valid certificate and
	 * auto-detect the file format of it.
	 */
	ret = KMF_IsCertFile(handle, params->certfile, &format);
	if (ret != KMF_OK)
		return (ret);

	ret = KMF_ReadInputFile(handle, params->certfile, &cert);
	if (ret != KMF_OK) {
		return (ret);
	}

	/*
	 * If the imported cert is in PEM format, convert it to
	 * DER format in order to store it in NSS token.
	 */
	if (format == KMF_FORMAT_PEM) {
		int derlen;
		ret = KMF_Pem2Der(cert.Data, cert.Length,
		    &cert_der.Data, &derlen);
		if (ret != KMF_OK) {
			goto cleanup;
		}
		cert_der.Length = (size_t)derlen;
		cptr = &cert_der;
	} else {
		cptr = &cert;
	}

	(void) memset(&scparams, 0, sizeof (scparams));
	scparams.kstype = params->kstype;
	scparams.certLabel = params->certLabel;
	scparams.nssparms = params->nssparms;

	ret = NSS_StoreCert(handle, &scparams, cptr);

	if (format == KMF_FORMAT_PEM) {
		KMF_FreeData(&cert_der);
	}

cleanup:
	KMF_FreeData(&cert);

	return (ret);
}

KMF_RETURN
NSS_DeleteCert(KMF_HANDLE_T handle, KMF_DELETECERT_PARAMS *params)
{
	KMF_RETURN rv = KMF_OK;
	int nssrv;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	CERTCertificate *cert = NULL;
	PK11SlotInfo *nss_slot = NULL;

	/* check params */
	if (params == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts,
		FALSE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	if (params->certLabel) {
		cert = PK11_FindCertFromNickname(params->certLabel, NULL);
		if (cert == NULL) {
			return (KMF_ERR_CERT_NOT_FOUND);
		}

		switch (params->find_cert_validity) {
		case KMF_ALL_CERTS:
			break;
		case KMF_NONEXPIRED_CERTS:
			nssrv = CERT_CertTimesValid(cert);
			if (nssrv == SECFailure) {
				/* this is an invalid cert - skip it */
				goto out;
			}
			break;
		case KMF_EXPIRED_CERTS:
			nssrv = CERT_CertTimesValid(cert);
			if (nssrv != SECFailure) {
				/* this is a valid cert - skip it */
				goto out;
			}
			break;
		}
		/* delete it from database */
		nssrv = SEC_DeletePermCertificate(cert);
		if (nssrv) {
			SET_ERROR(kmfh, nssrv);
			rv = KMF_ERR_INTERNAL;
		}
	} else {
		CERTCertListNode *node;
		CERTCertList *certlist = NULL;

		rv = nss_find_matching_certs(nss_slot,
			params->issuer, params->subject, params->serial,
			&certlist, params->find_cert_validity);

		for (node = CERT_LIST_HEAD(certlist);
			!CERT_LIST_END(node, certlist) && rv == KMF_OK;
			node = CERT_LIST_NEXT(node)) {

			nssrv = SEC_DeletePermCertificate(node->cert);
			if (nssrv) {
				SET_ERROR(kmfh, nssrv);
				rv = KMF_ERR_INTERNAL;
			}
		}

		if (rv == KMF_OK && certlist != NULL) {
			CERT_DestroyCertList(certlist);
		} else if (rv == KMF_OK && certlist == NULL) {
			rv = KMF_ERR_CERT_NOT_FOUND;
		}
	}
out:
	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	if (cert != NULL) {
		CERT_DestroyCertificate(cert);
	}

	return (rv);
}

static void
InitRandom(char *filename)
{
	char buf[2048];
	int fd;
	PRInt32 count;

	fd = open(filename, O_RDONLY);
	if (!fd)
		return;

	count = read(fd, buf, sizeof (buf));
	if (count > 0) {
		PK11_RandomUpdate(buf, count);
	}

	(void) close(fd);
}

KMF_RETURN
NSS_CreateKeypair(KMF_HANDLE_T handle,
	KMF_CREATEKEYPAIR_PARAMS *params,
	KMF_KEY_HANDLE *privkey,
	KMF_KEY_HANDLE *pubkey)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	PK11RSAGenParams	rsaparams;
	void	*nssparams;
	CK_MECHANISM_TYPE mechanism;
	ulong_t publicExponent = 0x010001;
	PK11SlotInfo *nss_slot = NULL;
	SECKEYPrivateKey *NSSprivkey = NULL;
	SECKEYPublicKey *NSSpubkey = NULL;
	PQGParams *pqgParams = NULL;


	if (params == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, FALSE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	rv = nss_authenticate(handle, nss_slot, &params->cred);
	if (rv != KMF_OK) {
		return (rv);
	}

	/* Get some random bits */
	InitRandom("/dev/urandom");
	if (params->keytype == KMF_RSA) {
		rsaparams.keySizeInBits = params->keylength;
		/*
		 * NSS only allows for a 4 byte exponent.
		 * Ignore the exponent parameter if it is too big.
		 */
		if (params->rsa_exponent.len > 0 &&
		    params->rsa_exponent.len <= sizeof (publicExponent) &&
		    params->rsa_exponent.val != NULL) {
			(void) memcpy(&publicExponent,
				params->rsa_exponent.val,
				params->rsa_exponent.len);
		}
		rsaparams.pe = publicExponent;
		mechanism = CKM_RSA_PKCS_KEY_PAIR_GEN;
		nssparams = &rsaparams;
	} else if (params->keytype == KMF_DSA) {
		PQGVerify *pqgVerify = NULL;
		int ks;
		SECStatus	nssrv, passed;

		mechanism = CKM_DSA_KEY_PAIR_GEN;

		ks = PQG_PBITS_TO_INDEX(params->keylength);
		nssrv = PK11_PQG_ParamGen(ks, &pqgParams, &pqgVerify);
		if (nssrv != SECSuccess) {
			SET_ERROR(kmfh, rv);
			PK11_PQG_DestroyVerify(pqgVerify);
			rv = KMF_ERR_KEYGEN_FAILED;
			goto cleanup;
		}

		nssrv = PK11_PQG_VerifyParams(pqgParams, pqgVerify, &passed);
		if (nssrv != SECSuccess || passed != SECSuccess) {
			SET_ERROR(kmfh, rv);
			rv = KMF_ERR_KEYGEN_FAILED;
		}

		PK11_PQG_DestroyVerify(pqgVerify);

		if (rv != KMF_OK) {
			SET_ERROR(kmfh, PORT_GetError());
			goto cleanup;
		}

		nssparams = pqgParams;
	} else {
		rv = KMF_ERR_BAD_PARAMETER;
		goto cleanup;
	}

	NSSprivkey = PK11_GenerateKeyPair(nss_slot,
		mechanism, nssparams, &NSSpubkey,
		PR_TRUE, /* isPermanent */
		PR_TRUE, /* isSensitive */
		(void *)params->cred.cred);

	if (NSSprivkey == NULL || NSSpubkey == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_KEYGEN_FAILED;
	} else {
		if (params->keylabel != NULL &&
			strlen(params->keylabel)) {
			(void) PK11_SetPrivateKeyNickname(NSSprivkey,
				params->keylabel);
			(void) PK11_SetPublicKeyNickname(NSSpubkey,
				params->keylabel);
		}
		/* Now, convert it to a KMF_KEY object for the framework */
		if (privkey != NULL) {
			privkey->kstype = KMF_KEYSTORE_NSS;
			privkey->keyalg = params->keytype;
			privkey->keyclass = KMF_ASYM_PRI;
			privkey->keylabel =
				PK11_GetPrivateKeyNickname(NSSprivkey);
			privkey->keyp = (void *)NSSprivkey;
		}
		if (pubkey != NULL) {
			pubkey->kstype = KMF_KEYSTORE_NSS;
			pubkey->keyalg = params->keytype;
			pubkey->keyp = (void *)NSSpubkey;
			pubkey->keyclass = KMF_ASYM_PUB;
			pubkey->keylabel =
				PK11_GetPublicKeyNickname(NSSpubkey);
		}
		rv = KMF_OK;
	}
cleanup:
	if (rv != KMF_OK) {
		if (NSSpubkey)
			PK11_DeleteTokenPublicKey(NSSpubkey);
		if (NSSprivkey)
			PK11_DeleteTokenPrivateKey(NSSprivkey, PR_TRUE);

		privkey->keyp = NULL;
		pubkey->keyp = NULL;
	}

	if (pqgParams != NULL)
		PK11_PQG_DestroyParams(pqgParams);


	if (nss_slot != NULL)
		PK11_FreeSlot(nss_slot);

	return (rv);
}

KMF_RETURN
NSS_SignData(KMF_HANDLE_T handle, KMF_KEY_HANDLE *key,
	KMF_OID *AlgOID, KMF_DATA *tobesigned,
	KMF_DATA *output)
{
	KMF_RETURN		ret = KMF_OK;
	KMF_ALGORITHM_INDEX		AlgId;
	SECOidTag		signAlgTag;
	SECKEYPrivateKey	*NSSprivkey = NULL;
	SECStatus		rv;
	SECItem			signed_data;
	KMF_HANDLE		*kmfh = (KMF_HANDLE *)handle;

	signed_data.data = 0;
	if (key == NULL || AlgOID == NULL ||
	    tobesigned == NULL || output == NULL ||
	    tobesigned->Data == NULL ||
	    output->Data == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	/* Map the OID to a NSS algorithm */
	AlgId = X509_AlgorithmOidToAlgId(AlgOID);
	if (AlgId == KMF_ALGID_NONE)
		return (KMF_ERR_BAD_PARAMETER);

	NSSprivkey = (SECKEYPrivateKey *)key->keyp;

	if (AlgId == KMF_ALGID_MD5WithRSA)
		signAlgTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION;
	else if (AlgId == KMF_ALGID_MD2WithRSA)
		signAlgTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION;
	else if (AlgId == KMF_ALGID_SHA1WithRSA)
		signAlgTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION;
	else if (AlgId == KMF_ALGID_SHA1WithDSA)
		signAlgTag = SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST;
	else
		return (KMF_ERR_BAD_PARAMETER);

	rv = SEC_SignData(&signed_data, tobesigned->Data,
	    tobesigned->Length, NSSprivkey, signAlgTag);

	if (rv != 0) {
		SET_ERROR(kmfh, rv);
		return (KMF_ERR_INTERNAL);
	}

	if (signed_data.len <= output->Length) {
		(void) memcpy(output->Data, signed_data.data, signed_data.len);
		output->Length = signed_data.len;
	} else {
		output->Length = 0;
		ret = KMF_ERR_BAD_PARAMETER;
	}
	free(signed_data.data);

	return (ret);
}

KMF_RETURN
NSS_EncodePubKeyData(KMF_HANDLE_T handle, KMF_KEY_HANDLE *keyp,
	KMF_DATA *encoded)
{
	KMF_RETURN ret = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	SECItem *rvitem;
	CERTSubjectPublicKeyInfo *spki = NULL;

	if (keyp == NULL || encoded == NULL || keyp->keyp == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	spki = SECKEY_CreateSubjectPublicKeyInfo(keyp->keyp);
	if (spki == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		return (KMF_ERR_MEMORY);
	}

	rvitem = SEC_ASN1EncodeItem(NULL, NULL, spki,
		CERT_SubjectPublicKeyInfoTemplate);

	if (rvitem != NULL) {
		encoded->Data = malloc(rvitem->len);
		if (encoded->Data == NULL) {
			ret = KMF_ERR_MEMORY;
		} else {
			(void) memcpy(encoded->Data, rvitem->data, rvitem->len);
			encoded->Length = rvitem->len;
		}
		SECITEM_FreeItem(rvitem, TRUE);
	} else {
		SET_ERROR(kmfh, PORT_GetError());
		encoded->Data = NULL;
		encoded->Length = 0;
		ret = KMF_ERR_ENCODING;
	}
	SECKEY_DestroySubjectPublicKeyInfo(spki);

	return (ret);
}

KMF_RETURN
NSS_DeleteKey(KMF_HANDLE_T handle, KMF_DELETEKEY_PARAMS *params,
	KMF_KEY_HANDLE *key, boolean_t delete_token)
{
	KMF_RETURN rv = KMF_OK;
	PK11SlotInfo *nss_slot = NULL;

	/*
	 * "delete_token" means to clear it from the token storage as well
	 * as from memory.
	 */
	if (key == NULL || key->keyp == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	if (delete_token) {
		SECStatus nssrv = SECSuccess;
		if (key->keyclass != KMF_ASYM_PUB &&
			key->keyclass != KMF_ASYM_PRI &&
			key->keyclass != KMF_SYMMETRIC)
			return (KMF_ERR_BAD_KEY_CLASS);

		if (params == NULL)
			return (KMF_ERR_BAD_PARAMETER);
		rv = Do_NSS_Init(handle,
			params->ks_opt_u.nss_opts, FALSE, &nss_slot);
		if (rv != KMF_OK) {
			return (rv);
		}
		rv = nss_authenticate(handle, nss_slot, &params->cred);
		if (rv != KMF_OK) {
			return (rv);
		}

		if (key->keyclass == KMF_ASYM_PUB) {
			nssrv = PK11_DeleteTokenPublicKey(
				(SECKEYPublicKey *)key->keyp);
		} else if (key->keyclass == KMF_ASYM_PRI) {
			nssrv = PK11_DeleteTokenPrivateKey(
				(SECKEYPrivateKey *)key->keyp, PR_TRUE);
		} else if (key->keyclass == KMF_SYMMETRIC) {
			nssrv = PK11_DeleteTokenSymKey(
					(PK11SymKey *) key->keyp);
			if (nssrv == SECSuccess)
				PK11_FreeSymKey(
					(PK11SymKey *) key->keyp);
		}
		if (nssrv != SECSuccess) {
			SET_ERROR(handle, PORT_GetError());
			rv = KMF_ERR_INTERNAL;
		}
	} else {
		if (key->keyclass == KMF_ASYM_PUB) {
			SECKEY_DestroyPublicKey((SECKEYPublicKey *)key->keyp);
		} else if (key->keyclass == KMF_ASYM_PRI) {
			SECKEY_DestroyPrivateKey((SECKEYPrivateKey *)key->keyp);
		} else if (key->keyclass == KMF_SYMMETRIC) {
			PK11_FreeSymKey((PK11SymKey *) key->keyp);
		} else {
			return (KMF_ERR_BAD_KEY_CLASS);
		}
	}
	key->keyp = NULL;

	return (rv);
}

KMF_RETURN
NSS_ImportCRL(KMF_HANDLE_T handle, KMF_IMPORTCRL_PARAMS *params)
{
	KMF_RETURN ret = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	PK11SlotInfo *nss_slot = NULL;
	CERTSignedCrl *nss_crl = NULL;
	KMF_ENCODE_FORMAT format;
	int importOptions;
	SECItem crlDER;
	KMF_DATA crl1;
	KMF_DATA crl2;

	if (params == NULL || params->ks_opt_u.nss_opts.crlfile == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	/*
	 * Check if the input CRL file is a valid CRL file and auto-detect
	 * the encoded format of the file.
	 */
	ret = KMF_IsCRLFile(handle, params->ks_opt_u.nss_opts.crlfile,
	    &format);
	if (ret != KMF_OK)
		return (ret);

	ret = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, TRUE, &nss_slot);
	if (ret != KMF_OK) {
		return (ret);
	}

	/* set importOptions */
	if (params->ks_opt_u.nss_opts.crl_check == B_FALSE) {
		importOptions = CRL_IMPORT_DEFAULT_OPTIONS |
		    CRL_IMPORT_BYPASS_CHECKS;
	} else {
		importOptions = CRL_IMPORT_DEFAULT_OPTIONS;
	}


	/* Read in the CRL file */
	crl1.Data = NULL;
	crl2.Data = NULL;
	ret = KMF_ReadInputFile(handle, params->ks_opt_u.nss_opts.crlfile,
	    &crl1);
	if (ret != KMF_OK) {
		return (ret);
	}

	/* If the input CRL is in PEM format, convert it to DER first. */
	if (format == KMF_FORMAT_PEM) {
		int len;
		ret = KMF_Pem2Der(crl1.Data, crl1.Length,
		    &crl2.Data, &len);
		if (ret != KMF_OK) {
			goto out;
		}
		crl2.Length = (size_t)len;
	}

	crlDER.data = format == KMF_FORMAT_ASN1 ? crl1.Data : crl2.Data;
	crlDER.len = format == KMF_FORMAT_ASN1 ? crl1.Length : crl2.Length;

	nss_crl = PK11_ImportCRL(nss_slot, &crlDER, NULL, SEC_CRL_TYPE,
	    NULL, importOptions, NULL, CRL_DECODE_DEFAULT_OPTIONS);

	if (nss_crl == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		ret = KMF_ERR_BAD_CRLFILE;
		goto out;
	}

out:
	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	if (crl1.Data != NULL) {
		free(crl1.Data);
	}

	if (crl2.Data != NULL) {
		free(crl2.Data);
	}

	if (nss_crl != NULL) {
		SEC_DestroyCrl(nss_crl);
	}

	return (ret);
}

KMF_RETURN
NSS_DeleteCRL(KMF_HANDLE_T handle, KMF_DELETECRL_PARAMS *params)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	CERTSignedCrl *crl = NULL;
	CERTCertificate *cert = NULL;
	PK11SlotInfo *nss_slot = NULL;
	CERTCrlHeadNode *crlList = NULL;
	CERTCrlNode *crlNode = NULL;
	PRArenaPool *arena = NULL;
	CERTName *name = NULL;
	CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB();

	/* check params */
	if (params == NULL ||
	    (params->ks_opt_u.nss_opts.crl_issuerName == NULL &&
	    params->ks_opt_u.nss_opts.crl_subjName == NULL) ||
	    (params->ks_opt_u.nss_opts.crl_issuerName != NULL &&
	    params->ks_opt_u.nss_opts.crl_subjName != NULL)) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, TRUE,
		&nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	/* Find the CRL based on the deletion criteria. */
	if (params->ks_opt_u.nss_opts.crl_issuerName != NULL) {
		/*
		 * If the deletion is based on the issuer's certificate
		 * nickname, we will get the issuer's cert first, then
		 * get the CRL from the cert.
		 */
		cert = CERT_FindCertByNicknameOrEmailAddr(certHandle,
		    params->ks_opt_u.nss_opts.crl_issuerName);
		if (!cert) {
			SET_ERROR(kmfh, PORT_GetError());
			rv = KMF_ERR_CERT_NOT_FOUND;
			goto out;
		}

		crl = SEC_FindCrlByName(certHandle, &cert->derSubject,
		    SEC_CRL_TYPE);
		if (crl == NULL) {
			SET_ERROR(kmfh, PORT_GetError());
			rv = KMF_ERR_CRL_NOT_FOUND;
			goto out;
		}
	} else {
		/*
		 * If the deletion is based on the CRL's subject name, we will
		 * get all the CRLs from the internal database and search
		 * for the CRL with the same subject name.
		 */
		boolean_t found = B_FALSE;
		int nssrv;

		nssrv = SEC_LookupCrls(certHandle, &crlList, SEC_CRL_TYPE);
		if (nssrv) {
			SET_ERROR(kmfh, nssrv);
			rv = KMF_ERR_CRL_NOT_FOUND;
			goto out;
		}

		if (crlList == NULL) {
			SET_ERROR(kmfh, PORT_GetError());
			rv = KMF_ERR_CRL_NOT_FOUND;
			goto out;
		}

		/* Allocate space for name */
		arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
		if (arena == NULL) {
			rv = KMF_ERR_MEMORY;
			goto out;
		}

		name = PORT_ArenaZAlloc(arena, sizeof (*name));
		if (name == NULL) {
			rv = KMF_ERR_MEMORY;
			goto out;
		}
		name->arena = arena;

		crlNode  = crlList->first;
		while (crlNode && !found) {
			char *asciiname = NULL;
			SECItem* issuer;

			name = &crlNode->crl->crl.name;
			if (!name) {
				SET_ERROR(kmfh, PORT_GetError());
				rv = KMF_ERR_CRL_NOT_FOUND;
				break;
			}

			asciiname = CERT_NameToAscii(name);
			if (asciiname == NULL) {
				SET_ERROR(kmfh, PORT_GetError());
				rv = KMF_ERR_CRL_NOT_FOUND;
				break;
			}

			if (strcmp(params->ks_opt_u.nss_opts.crl_subjName,
			    asciiname) == 0) {
				found = B_TRUE;
				issuer = &crlNode->crl->crl.derName;
				crl = SEC_FindCrlByName(certHandle, issuer,
				    SEC_CRL_TYPE);
				if (crl == NULL) {
					/* We found a cert but no CRL */
					SET_ERROR(kmfh,  PORT_GetError());
					rv = KMF_ERR_CRL_NOT_FOUND;
				}
			}
			PORT_Free(asciiname);
			crlNode = crlNode->next;
		}

		if (rv) {
			goto out;
		}
	}

	if (crl) {
		(void) SEC_DeletePermCRL(crl);
	}

out:
	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	if (crlList != NULL) {
		PORT_FreeArena(crlList->arena, PR_FALSE);
	}

	if (arena != NULL) {
		PORT_FreeArena(arena, PR_FALSE);
	}

	if (cert != NULL) {
		CERT_DestroyCertificate(cert);
	}

	if (crl != NULL) {
		SEC_DestroyCrl(crl);
	}

	return (rv);
}


KMF_RETURN
NSS_FindCRL(KMF_HANDLE_T handle, KMF_FINDCRL_PARAMS *params,
	char **CRLNameList, int *CRLCount)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	PK11SlotInfo *nss_slot = NULL;
	CERTCrlHeadNode *crlList = NULL;
	CERTCrlNode *crlNode = NULL;
	PRArenaPool *arena = NULL;
	CERTName *name = NULL;
	SECStatus nssrv;
	char *asciiname = NULL;
	int crl_num;
	int i;
	CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB();

	if (CRLCount == NULL || params == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	*CRLCount = 0;

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, TRUE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	/* Look up Crls */
	nssrv = SEC_LookupCrls(certHandle, &crlList, SEC_CRL_TYPE);
	if (nssrv) {
		SET_ERROR(kmfh, rv);
		rv = KMF_ERR_CRL_NOT_FOUND;
		goto out;
	}

	/* Allocate space for name first */
	arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
	if (arena == NULL) {
	    rv = KMF_ERR_MEMORY;
	    goto out;
	}

	name = PORT_ArenaZAlloc(arena, sizeof (*name));
	if (name == NULL) {
		rv = KMF_ERR_MEMORY;
		goto out;
	}
	name->arena = arena;

	/*
	 * Loop thru the crlList and create a crl list with CRL's subject name.
	 */
	crlNode  = crlList->first;
	crl_num = 0;
	while (crlNode) {
		char *subj_name;

		/* Get the CRL subject name */
		name = &crlNode->crl->crl.name;
		if (!name) {
			SET_ERROR(kmfh, PORT_GetError());
			rv = KMF_ERR_CRL_NOT_FOUND;
			break;
		}


		if (CRLNameList != NULL) {
			asciiname = CERT_NameToAscii(name);
			if (asciiname == NULL) {
				SET_ERROR(kmfh, PORT_GetError());
				rv = KMF_ERR_CRL_NOT_FOUND;
				break;
			}
			subj_name = strdup(asciiname);
			PORT_Free(asciiname);
			if (subj_name == NULL) {
				rv = KMF_ERR_MEMORY;
				break;
			}
			CRLNameList[crl_num] = subj_name;
		}

		crl_num++;
		crlNode = crlNode->next;
	}

	if (rv == KMF_OK) {
		/* success */
		*CRLCount = crl_num;
	}

out:
	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	if (crlList != NULL) {
		PORT_FreeArena(crlList->arena, PR_FALSE);
	}

	if (arena != NULL) {
		PORT_FreeArena(arena, PR_FALSE);
	}

	/* If failed, free memory allocated for the returning rlist */
	if (rv && (CRLNameList != NULL)) {
		for (i = 0; i < crl_num; i++) {
			free(CRLNameList[i]);
		}
	}

	return (rv);
}


KMF_RETURN
NSS_FindCertInCRL(KMF_HANDLE_T handle, KMF_FINDCERTINCRL_PARAMS *params)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	PK11SlotInfo *nss_slot = NULL;
	CERTCertificate *cert = NULL;
	CERTSignedCrl *crl = NULL;
	CERTCrlEntry *entry;
	boolean_t match = B_FALSE;
	int i;
	CERTCertDBHandle *certHandle = CERT_GetDefaultCertDB();

	/* check params */
	if (params == NULL ||
	    (params->ks_opt_u.nss_opts.certLabel == NULL &&
	    params->ks_opt_u.nss_opts.certificate == NULL)) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, TRUE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	/* Find the certificate first */
	if (params->ks_opt_u.nss_opts.certLabel != NULL) {
		cert = CERT_FindCertByNicknameOrEmailAddr(certHandle,
		    params->ks_opt_u.nss_opts.certLabel);
	} else {
		SECItem derCert = { NULL, 0};

		derCert.data = params->ks_opt_u.nss_opts.certificate->Data;
		derCert.len =  params->ks_opt_u.nss_opts.certificate->Length;
		cert = CERT_FindCertByDERCert(certHandle, &derCert);
	}

	if (!cert) {
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_CERT_NOT_FOUND;
		goto out;
	}

	/* Find the CRL with the same issuer as the given certificate. */
	crl = SEC_FindCrlByName(certHandle, &cert->derIssuer, SEC_CRL_TYPE);
	if (crl == NULL) {
		/*
		 * Could not find the CRL issued by the same issuer. This
		 * usually means that the CRL is not installed in the DB.
		 */
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_CRL_NOT_FOUND;
		goto out;

	}

	/* Check if the certificate's serialNumber is revoked in the CRL */
	i = 0;
	while ((entry = (crl->crl).entries[i++]) != NULL) {
		if (SECITEM_CompareItem(&(cert->serialNumber),
		    &(entry->serialNumber)) == SECEqual) {
			match = B_TRUE;
			break;
		}
	}

	if (!match) {
		rv = KMF_ERR_NOT_REVOKED;
	}

out:
	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	if (cert != NULL) {
		CERT_DestroyCertificate(cert);
	}

	if (crl != NULL) {
		SEC_DestroyCrl(crl);
	}

	return (rv);
}

KMF_RETURN
NSS_GetErrorString(KMF_HANDLE_T handle, char **msgstr)
{
	KMF_RETURN ret = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	char *str;

	/* Get the error string in the default language */
	str = (char *)PR_ErrorToName((PRErrorCode)kmfh->lasterr.errcode);

	if (str != NULL) {
		*msgstr = (char *)strdup(str);
		if ((*msgstr) == NULL)
			ret = KMF_ERR_MEMORY;
	} else {
		*msgstr = NULL;
	}

	return (ret);
}

KMF_RETURN
NSS_GetPrikeyByCert(KMF_HANDLE_T handle, KMF_CRYPTOWITHCERT_PARAMS *params,
	KMF_DATA *SignerCertData, KMF_KEY_HANDLE *key,
	KMF_KEY_ALG keytype)
{
	CERTCertificate *nss_cert = NULL;
	SECKEYPrivateKey* privkey = NULL;
	PK11SlotInfo *nss_slot = NULL;
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;

	rv = Do_NSS_Init(handle,
		params->nssparms, FALSE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	rv = nss_authenticate(handle, nss_slot, &params->cred);
	if (rv != KMF_OK) {
		return (rv);
	}

	nss_cert = CERT_DecodeCertFromPackage((char *)SignerCertData->Data,
	    SignerCertData->Length);

	if (nss_cert == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		return (KMF_ERR_BAD_CERT_FORMAT);
	}

	privkey = PK11_FindPrivateKeyFromCert(nss_slot, nss_cert, NULL);
	if (privkey == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		return (KMF_ERR_KEY_NOT_FOUND);
	}

	key->kstype = KMF_KEYSTORE_NSS;
	key->keyclass = KMF_ASYM_PRI;
	key->keyalg = keytype;
	key->keyp = (void *)privkey;
	key->keylabel = PK11_GetPrivateKeyNickname(privkey);

	CERT_DestroyCertificate(nss_cert);

	return (KMF_OK);

}

KMF_RETURN
NSS_DecryptData(KMF_HANDLE_T handle, KMF_KEY_HANDLE *key,
	KMF_OID *AlgOID, KMF_DATA *ciphertext,
	KMF_DATA *output)
{
	KMF_RETURN		ret = KMF_OK;
	SECKEYPrivateKey	*NSSprivkey = NULL;
	SECStatus		rv;
	KMF_HANDLE		*kmfh = (KMF_HANDLE *)handle;
	unsigned int in_len = 0, out_len = 0;
	unsigned int total_decrypted = 0, modulus_len = 0;
	uint8_t *in_data, *out_data;
	int i, blocks;


	if (key == NULL || AlgOID == NULL ||
	    ciphertext == NULL || output == NULL ||
	    ciphertext->Data == NULL ||
	    output->Data == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	NSSprivkey = (SECKEYPrivateKey *)key->keyp;
	modulus_len = PK11_GetPrivateModulusLen(NSSprivkey);

	blocks = ciphertext->Length/modulus_len;
	out_data = output->Data;
	in_data = ciphertext->Data;
	out_len = modulus_len - 11;
	in_len = modulus_len;

	for (i = 0; i < blocks; i++) {
		rv = PK11_PrivDecryptPKCS1(NSSprivkey, out_data,
		    &out_len, ciphertext->Length, in_data, in_len);

		if (rv != 0) {
			SET_ERROR(kmfh, rv);
			return (KMF_ERR_INTERNAL);
		}

		out_data += out_len;
		total_decrypted += out_len;
		in_data += in_len;
	}

	output->Length = total_decrypted;

	return (ret);
}

static KMF_KEY_ALG
pk11keytype2kmf(CK_KEY_TYPE type)
{
	switch (type) {
	case CKK_RSA:
		return (KMF_RSA);
	case CKK_DSA:
		return (KMF_RSA);
	case CKK_AES:
		return (KMF_AES);
	case CKK_RC4:
		return (KMF_RC4);
	case CKK_DES:
		return (KMF_DES);
	case CKK_DES3:
		return (KMF_DES3);
	default:
		/* not supported */
		return (KMF_KEYALG_NONE);
	}
}

KMF_RETURN
NSS_FindKey(KMF_HANDLE_T handle, KMF_FINDKEY_PARAMS *parms,
	KMF_KEY_HANDLE *keys, uint32_t *numkeys)
{
	KMF_RETURN rv;
	SECKEYPrivateKeyList *prilist;
	SECKEYPrivateKeyListNode *prinode;
	SECKEYPublicKeyList *publist;
	SECKEYPublicKeyListNode *pubnode;
	PK11SlotInfo *nss_slot = NULL;
	PK11SymKey *symlist;
	int count;

	if (handle == NULL || parms == NULL || numkeys == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	rv = Do_NSS_Init(handle,
		parms->ks_opt_u.nss_opts, FALSE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	rv = nss_authenticate(handle, nss_slot, &parms->cred);
	if (rv != KMF_OK) {
		return (rv);
	}

	*numkeys = 0;
	if (parms->keyclass == KMF_ASYM_PUB) {
		publist = PK11_ListPublicKeysInSlot(nss_slot, parms->findLabel);
		if (publist == NULL) {
			rv = KMF_ERR_KEY_NOT_FOUND;
			goto cleanup;
		}
	} else if (parms->keyclass == KMF_ASYM_PRI) {
		prilist = PK11_ListPrivKeysInSlot(nss_slot,
			parms->findLabel, NULL);
		if (prilist == NULL) {
			rv = KMF_ERR_KEY_NOT_FOUND;
			goto cleanup;
		}
	} else if (parms->keyclass == KMF_SYMMETRIC) {
		symlist = PK11_ListFixedKeysInSlot(nss_slot, parms->findLabel,
		    NULL);
		if (symlist == NULL) {
			rv = KMF_ERR_KEY_NOT_FOUND;
			goto cleanup;
		}
	} else {
		rv = KMF_ERR_BAD_KEY_CLASS;
		goto cleanup;
	}

	if (parms->keyclass == KMF_ASYM_PUB) {
		for (count = 0, pubnode = PUBKEY_LIST_HEAD(publist);
			!PUBKEY_LIST_END(pubnode, publist);
			pubnode = PUBKEY_LIST_NEXT(pubnode), count++) {
			if (keys != NULL) {
				keys[count].kstype = KMF_KEYSTORE_NSS;
				keys[count].keyclass = KMF_ASYM_PUB;
				keys[count].keyp = (void *)pubnode->key;
				keys[count].keylabel =
					PK11_GetPublicKeyNickname(
						pubnode->key);

				if (pubnode->key->keyType == rsaKey)
					keys[count].keyalg = KMF_RSA;
				else if (pubnode->key->keyType == dsaKey)
					keys[count].keyalg = KMF_DSA;
			}
		}
		*numkeys = count;
	} else if (parms->keyclass == KMF_ASYM_PRI) {
		for (count = 0, prinode = PRIVKEY_LIST_HEAD(prilist);
			!PRIVKEY_LIST_END(prinode, prilist);
			prinode = PRIVKEY_LIST_NEXT(prinode), count++) {
			if (keys != NULL) {
				keys[count].kstype = KMF_KEYSTORE_NSS;
				keys[count].keyclass = KMF_ASYM_PRI;
				keys[count].keyp = (void *)prinode->key;
				keys[count].keylabel =
					PK11_GetPrivateKeyNickname(
						prinode->key);

				if (prinode->key->keyType == rsaKey)
					keys[count].keyalg = KMF_RSA;
				else if (prinode->key->keyType == dsaKey)
					keys[count].keyalg = KMF_DSA;
			}
		}
		*numkeys = count;
	} else if (parms->keyclass == KMF_SYMMETRIC) {
		count = 0;
		while (symlist) {
			PK11SymKey *symkey = symlist;
			CK_KEY_TYPE type;
			KMF_KEY_ALG keyalg;

			type = PK11_GetSymKeyType(symkey);
			keyalg = pk11keytype2kmf(type);

			/*
			 * If keytype is specified in the searching parameter,
			 * check the keytype and skip the key if its keytype
			 * doesn't match.
			 */
			symlist = PK11_GetNextSymKey(symkey);
			if (parms->keytype != KMF_KEYALG_NONE &&
			    parms->keytype != keyalg) {
				continue;
			}

			if (keys != NULL) {
				keys[count].kstype = KMF_KEYSTORE_NSS;
				keys[count].keyclass = KMF_SYMMETRIC;
				keys[count].keyp = (void *) symkey;
				keys[count].keylabel =
				    PK11_GetSymKeyNickname(symkey);
				keys[count].keyalg = keyalg;
			} else {
				PK11_FreeSymKey(symkey);
			}
			count++;
		}
		*numkeys = count;
	}

cleanup:
	if (nss_slot != NULL) {
		PK11_FreeSlot(nss_slot);
	}

	return (rv);
}

static SECStatus
p12u_SwapUnicodeBytes(SECItem *uniItem)
{
	unsigned int i;
	unsigned char a;
	if ((uniItem == NULL) || (uniItem->len % 2)) {
		return (SECFailure);
		}
	for (i = 0; i < uniItem->len; i += 2) {
		a = uniItem->data[i];
		uniItem->data[i] = uniItem->data[i+1];
		uniItem->data[i+1] = a;
	}
	return (SECSuccess);
}

static PRBool
p12u_ucs2_ascii_conversion_function(
	PRBool		toUnicode,
	unsigned char	*inBuf,
	unsigned int	inBufLen,
	unsigned char	*outBuf,
	unsigned int	maxOutBufLen,
	unsigned int	*outBufLen,
	PRBool		swapBytes)
{
	SECItem it = { 0 };
	SECItem *dup = NULL;
	PRBool ret;

	it.data = inBuf;
	it.len = inBufLen;
	dup = SECITEM_DupItem(&it);
	/*
	 * If converting Unicode to ASCII, swap bytes before conversion
	 * as neccessary.
	 */
	if (!toUnicode && swapBytes) {
		if (p12u_SwapUnicodeBytes(dup) != SECSuccess) {
			SECITEM_ZfreeItem(dup, PR_TRUE);
			return (PR_FALSE);
		}
	}
	/* Perform the conversion. */
	ret = PORT_UCS2_UTF8Conversion(toUnicode, dup->data, dup->len,
		outBuf, maxOutBufLen, outBufLen);
	if (dup)
		SECITEM_ZfreeItem(dup, PR_TRUE);

	return (ret);
}

static PRBool
p12u_OpenFile(p12uContext *p12ctx, PRBool fileRead)
{
	if (!p12ctx || !p12ctx->filename) {
		return (PR_FALSE);
	}

	if (fileRead) {
		p12ctx->file = PR_Open(p12ctx->filename,
			PR_RDONLY, 0400);
	} else {
		p12ctx->file = PR_Open(p12ctx->filename,
			PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 0600);
	}

	if (!p12ctx->file) {
		p12ctx->error = PR_TRUE;
		return (PR_FALSE);
	}

	return (PR_TRUE);
}

static void
p12u_DestroyContext(p12uContext **ppCtx, PRBool removeFile)
{
	if (!ppCtx || !(*ppCtx)) {
		return;
	}

	if ((*ppCtx)->file != NULL) {
		PR_Close((*ppCtx)->file);
	}

	if ((*ppCtx)->filename != NULL) {
		if (removeFile) {
			PR_Delete((*ppCtx)->filename);
		}
		free((*ppCtx)->filename);
	}

	free(*ppCtx);
	*ppCtx = NULL;
}

static p12uContext *
p12u_InitContext(PRBool fileImport, char *filename)
{
	p12uContext *p12ctx;

	p12ctx = PORT_ZNew(p12uContext);
	if (!p12ctx) {
		return (NULL);
	}

	p12ctx->error = PR_FALSE;
	p12ctx->errorValue = 0;
	p12ctx->filename = strdup(filename);

	if (!p12u_OpenFile(p12ctx, fileImport)) {
		p12u_DestroyContext(&p12ctx, PR_FALSE);
		return (NULL);
	}

	return (p12ctx);
}

static void
p12u_WriteToExportFile(void *arg, const char *buf, unsigned long len)
{
	p12uContext *p12cxt = arg;
	int writeLen;

	if (!p12cxt || (p12cxt->error == PR_TRUE)) {
		return;
	}

	if (p12cxt->file == NULL) {
		p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
		p12cxt->error = PR_TRUE;
		return;
	}

	writeLen = PR_Write(p12cxt->file, (unsigned char *)buf, (int32)len);

	if (writeLen != (int)len) {
		PR_Close(p12cxt->file);
		free(p12cxt->filename);
		p12cxt->filename = NULL;
		p12cxt->file = NULL;
		p12cxt->errorValue = SEC_ERROR_PKCS12_UNABLE_TO_WRITE;
		p12cxt->error = PR_TRUE;
	}
}

#define	HANDLE_NSS_ERROR(r) {\
	SET_ERROR(kmfh, PORT_GetError()); \
	rv = r; \
	goto out; }

static KMF_RETURN
add_cert_to_bag(SEC_PKCS12ExportContext *p12ecx,
	CERTCertificate *cert, SECItem *pwitem)
{
	KMF_RETURN rv = KMF_OK;
	SEC_PKCS12SafeInfo *keySafe = NULL, *certSafe = NULL;

	keySafe = SEC_PKCS12CreateUnencryptedSafe(p12ecx);
	if (PK11_IsFIPS()) {
		certSafe = keySafe;
	} else {
		certSafe = SEC_PKCS12CreatePasswordPrivSafe(p12ecx, pwitem,
			SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC);
	}

	if (!certSafe || !keySafe) {
		rv = KMF_ERR_INTERNAL;
		goto out;
	}

	if (SEC_PKCS12AddCertAndKey(p12ecx, certSafe, NULL, cert,
		CERT_GetDefaultCertDB(), keySafe, NULL, PR_TRUE, pwitem,
		SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC)
		!= SECSuccess) {
		rv = KMF_ERR_INTERNAL;
	}
out:
	return (rv);
}

/*ARGSUSED*/
KMF_RETURN
NSS_ExportP12(KMF_HANDLE_T handle,
	KMF_EXPORTP12_PARAMS *params,
	int numcerts, KMF_X509_DER_CERT *certs,
	int numkeys, KMF_KEY_HANDLE *keylist,
	char *filename)
{
	KMF_RETURN rv;
	KMF_HANDLE *kmfh = (KMF_HANDLE  *)handle;
	SEC_PKCS12ExportContext *p12ecx = NULL;
	p12uContext *p12ctx = NULL;
	CERTCertList *certlist = NULL;
	CERTCertificate *nsscert = NULL;
	CERTCertListNode* node = NULL;
	PK11SlotInfo	*slot = NULL;
	SECItem pwitem = {NULL, 0};

	rv = Do_NSS_Init(handle,
		params->nssparms, FALSE, &slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	rv = nss_authenticate(handle, slot, &params->cred);
	if (rv != KMF_OK) {
		return (rv);
	}

	/*
	 * Find the certificate(s) first.
	 */
	if (params->certLabel) {
		nsscert = PK11_FindCertFromNickname(params->certLabel,
			NULL);
		if (nsscert == NULL) {
			HANDLE_NSS_ERROR(KMF_ERR_CERT_NOT_FOUND)
		}
	} else {
		rv = nss_find_matching_certs(slot,
			params->issuer,
			params->subject,
			params->serial,
			&certlist, 0);

		if (rv == KMF_OK && certlist == NULL) {
			return (KMF_ERR_CERT_NOT_FOUND);
		}
		if (rv != KMF_OK)
			return (rv);
	}

	/*
	 * The KMF_CREDENTIAL holds the password to use for
	 * encrypting the PKCS12 key information.
	 */
	pwitem.data = (uchar_t *)params->p12cred.cred;
	pwitem.len = params->p12cred.credlen;

	p12ctx = p12u_InitContext(PR_FALSE, filename);
	if (!p12ctx) {
		HANDLE_NSS_ERROR(KMF_ERR_OPEN_FILE)
	}

	PORT_SetUCS2_ASCIIConversionFunction(
		p12u_ucs2_ascii_conversion_function);

	p12ecx = SEC_PKCS12CreateExportContext(NULL, NULL,
		slot, NULL);
	if (!p12ecx) {
		HANDLE_NSS_ERROR(KMF_ERR_OPEN_FILE)
	}

	if (SEC_PKCS12AddPasswordIntegrity(p12ecx, &pwitem, SEC_OID_SHA1)
		!= SECSuccess) {
		HANDLE_NSS_ERROR(KMF_ERR_INTERNAL)
	}

	/*
	 * NSS actually supports storing a list of keys and certs
	 * in the PKCS#12 PDU.  Nice feature.
	 */
	if (certlist != NULL) {
		for (node = CERT_LIST_HEAD(certlist);
			!CERT_LIST_END(node, certlist) && rv == KMF_OK;
			node = CERT_LIST_NEXT(node)) {

			rv = add_cert_to_bag(p12ecx, node->cert, &pwitem);
		}
	} else if (nsscert != NULL) {
		rv = add_cert_to_bag(p12ecx, nsscert, &pwitem);
	}

	if (SEC_PKCS12Encode(p12ecx, p12u_WriteToExportFile, p12ctx)
		!= SECSuccess) {
		HANDLE_NSS_ERROR(KMF_ERR_ENCODING)
	}
out:
	if (nsscert)
		CERT_DestroyCertificate(nsscert);

	if (certlist)
		CERT_DestroyCertList(certlist);

	if (p12ctx)
		p12u_DestroyContext(&p12ctx, PR_FALSE);

	if (p12ecx)
		SEC_PKCS12DestroyExportContext(p12ecx);

	return (rv);
}

#define	SETATTR(t, n, atype, value, size) \
	t[n].type = atype; \
	t[n].pValue = (CK_BYTE *)value; \
	t[n].ulValueLen = (CK_ULONG)size;

KMF_RETURN
NSS_StorePrivateKey(KMF_HANDLE_T handle, KMF_STOREKEY_PARAMS *params,
	KMF_RAW_KEY_DATA *rawkey)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	SECStatus	ckrv = SECSuccess;
	PK11SlotInfo	*slot = NULL;
	CERTCertificate *nss_cert = NULL;
	SECKEYPrivateKeyInfo rpk;
	SECItem		nickname;
	KMF_DATA	derkey = { NULL, 0 };
	uchar_t		ver = 0;

	if (!kmfh)
		return (KMF_ERR_UNINITIALIZED); /* Plugin Not Initialized */

	if (params == NULL || params->certificate == NULL || rawkey == NULL)
		return (KMF_ERR_BAD_PARAMETER);

	rv = Do_NSS_Init(handle,
		params->nssparms, FALSE, &slot);

	if (rv != KMF_OK)
		return (rv);

	rv = nss_authenticate(handle, slot, &params->cred);
	if (rv != KMF_OK) {
		return (rv);
	}

	/*
	 * Decode the cert into an NSS CERT object so we can access the
	 * SPKI and KeyUsage data later.
	 */
	nss_cert = CERT_DecodeCertFromPackage((char *)params->certificate->Data,
		params->certificate->Length);

	if (nss_cert == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_BAD_CERT_FORMAT;
		goto cleanup;
	}

	(void) memset(&rpk, 0, sizeof (rpk));

	rpk.arena = NULL;
	rpk.version.type = siUnsignedInteger;
	rpk.version.data = &ver;
	rpk.version.len = 1;
	if (rawkey->keytype == KMF_RSA) {

		rv = DerEncodeRSAPrivateKey(&derkey, &rawkey->rawdata.rsa);
		if (rv != KMF_OK)
			goto cleanup;

		rpk.algorithm = nss_cert->subjectPublicKeyInfo.algorithm;
		rpk.privateKey.data = derkey.Data;
		rpk.privateKey.len = derkey.Length;
		rpk.attributes = NULL;


	} else if (rawkey->keytype == KMF_DSA) {
		rv = DerEncodeDSAPrivateKey(&derkey, &rawkey->rawdata.dsa);
		if (rv != KMF_OK)
			goto cleanup;

		rpk.algorithm = nss_cert->subjectPublicKeyInfo.algorithm;
		rpk.privateKey.data = derkey.Data;
		rpk.privateKey.len = derkey.Length;
		rpk.attributes = NULL;

	} else {
		return (KMF_ERR_BAD_PARAMETER);
	}

	nickname.data = (uchar_t *)params->label;
	nickname.len = (params->label ? strlen(params->label) : 0);

	ckrv = PK11_ImportPrivateKeyInfo(slot, &rpk,
		&nickname, &nss_cert->subjectPublicKeyInfo.subjectPublicKey,
		TRUE, TRUE, nss_cert->keyUsage, NULL);

	if (ckrv != CKR_OK) {
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_INTERNAL;
	}

cleanup:
	if (nss_cert != NULL) {
		CERT_DestroyCertificate(nss_cert);
	}
	KMF_FreeData(&derkey);
	return (rv);
}

KMF_RETURN
NSS_CreateSymKey(KMF_HANDLE_T handle,
	KMF_CREATESYMKEY_PARAMS *params,
	KMF_KEY_HANDLE *symkey)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	PK11SlotInfo *nss_slot = NULL;
	PK11SymKey *nsskey = NULL;
	CK_MECHANISM_TYPE keyType;
	SECStatus nssrv;
	int keySize;

	if (params == NULL || symkey == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	switch (params->keytype) {
	case KMF_AES:
		keyType = CKM_AES_KEY_GEN;
		keySize = params->keylength;
		if (keySize == 0 || (keySize % 8) != 0)
			return (KMF_ERR_BAD_KEY_SIZE);
		break;
	case KMF_RC4:
		keyType = CKM_RC4_KEY_GEN;
		keySize = params->keylength;
		if (keySize == 0 || (keySize % 8) != 0)
			return (KMF_ERR_BAD_KEY_SIZE);
		break;
	case KMF_DES:
		keyType = CKM_DES_KEY_GEN;
		keySize = 0; /* required by PK11_TokenKeyGen()  */
		break;
	case KMF_DES3:
		keyType = CKM_DES3_KEY_GEN;
		keySize = 0; /* required by PK11_TokenKeyGen() */
		break;
	default:
		rv = KMF_ERR_BAD_KEY_TYPE;
		goto out;
	}

	rv = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts, FALSE, &nss_slot);
	if (rv != KMF_OK) {
		return (rv);
	}

	rv = nss_authenticate(handle, nss_slot, &params->cred);
	if (rv != KMF_OK) {
		return (rv);
	}

	nsskey = PK11_TokenKeyGen(nss_slot, keyType, NULL, keySize,  NULL,
	    PR_TRUE, (void *)params->cred.cred);
	if (nsskey == NULL) {
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_KEYGEN_FAILED;
		goto out;
	}

	nssrv = PK11_SetSymKeyNickname(nsskey, params->keylabel);
	if (nssrv != SECSuccess) {
		SET_ERROR(kmfh, PORT_GetError());
		rv = KMF_ERR_KEYGEN_FAILED;
		goto out;
	}

	symkey->kstype = KMF_KEYSTORE_NSS;
	symkey->keyalg = params->keytype;
	symkey->keyclass = KMF_SYMMETRIC;
	symkey->israw = FALSE;
	symkey->keyp = (void *)nsskey;

out:
	if (nss_slot != NULL)
		PK11_FreeSlot(nss_slot);

	if (rv != KMF_OK && nsskey != NULL) {
		PK11_DeleteTokenSymKey(nsskey);
		PK11_FreeSymKey(nsskey);
	}
	return (rv);
}

KMF_RETURN
NSS_GetSymKeyValue(KMF_HANDLE_T handle, KMF_KEY_HANDLE *symkey,
	KMF_RAW_SYM_KEY *rkey)
{
	KMF_RETURN rv = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	SECItem *value = NULL;
	PK11SymKey *nsskey;
	SECStatus nss_rv;

	if (kmfh == NULL)
		return (KMF_ERR_UNINITIALIZED);

	if (symkey == NULL || rkey == NULL)
		return (KMF_ERR_BAD_PARAMETER);
	else if (symkey->keyclass != KMF_SYMMETRIC)
		return (KMF_ERR_BAD_KEY_CLASS);

	if (symkey->israw) {
		KMF_RAW_KEY_DATA *rawkey = (KMF_RAW_KEY_DATA *)symkey->keyp;

		if (rawkey == NULL ||
		    rawkey->rawdata.sym.keydata.val == NULL ||
		    rawkey->rawdata.sym.keydata.len == 0)
			return (KMF_ERR_BAD_KEYHANDLE);

		rkey->keydata.len = rawkey->rawdata.sym.keydata.len;
		if ((rkey->keydata.val = malloc(rkey->keydata.len)) == NULL)
			return (KMF_ERR_MEMORY);
		(void) memcpy(rkey->keydata.val,
			rawkey->rawdata.sym.keydata.val, rkey->keydata.len);
	} else {
		nsskey = (PK11SymKey *)(symkey->keyp);
		if (nsskey == NULL)
			return (KMF_ERR_BAD_KEYHANDLE);

		nss_rv = PK11_ExtractKeyValue(nsskey);
		if (nss_rv != SECSuccess) {
			SET_ERROR(kmfh, PORT_GetError());
			rv = KMF_ERR_GETKEYVALUE_FAILED;
			goto out;
		}

		value = PK11_GetKeyData(nsskey);
		if (value == NULL) {
			SET_ERROR(kmfh, PORT_GetError());
			rv = KMF_ERR_GETKEYVALUE_FAILED;
			goto out;
		}

		if (value->len == 0 || value->data == NULL) {
			rv = KMF_ERR_GETKEYVALUE_FAILED;
			goto out;
		}

		rkey->keydata.val = malloc(value->len);
		if (rkey->keydata.val == NULL) {
			rv = KMF_ERR_MEMORY;
			goto out;
		}
		(void) memcpy(rkey->keydata.val, value->data, value->len);
		rkey->keydata.len = value->len;
		(void) memset(value->data, 0, value->len);
	}
out:
	if (value != NULL)
		SECITEM_FreeItem(value, PR_TRUE);
	return (rv);
}

KMF_RETURN
NSS_SetTokenPin(KMF_HANDLE_T handle, KMF_SETPIN_PARAMS *params,
	KMF_CREDENTIAL *newpin)
{
	KMF_RETURN ret = KMF_OK;
	KMF_HANDLE *kmfh = (KMF_HANDLE *)handle;
	int rv;
	PK11SlotInfo *nss_slot = NULL;

	if (handle == NULL || params == NULL || newpin == NULL) {
		return (KMF_ERR_BAD_PARAMETER);
	}

	ret = Do_NSS_Init(handle,
		params->ks_opt_u.nss_opts,
		FALSE, &nss_slot);
	/* If it was uninitialized, set it */
	if (ret == KMF_ERR_UNINITIALIZED_TOKEN) {
		rv = PK11_InitPin(nss_slot, NULL, newpin->cred);
		if (rv != SECSuccess) {
			SET_ERROR(kmfh, PORT_GetError());
			ret = KMF_ERR_AUTH_FAILED;
		} else {
			ret = KMF_OK;
		}
	} else if (ret == KMF_OK) {
		ret = nss_authenticate(handle, nss_slot, &params->cred);
		if (ret != KMF_OK) {
			return (ret);
		}
		rv = PK11_ChangePW(nss_slot,
			params->cred.cred, newpin->cred);
		if (rv != SECSuccess) {
			SET_ERROR(kmfh, PORT_GetError());
			ret = KMF_ERR_AUTH_FAILED;
		}
	}

	return (ret);
}