summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/softoken/legacydb/lgcreate.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/softoken/legacydb/lgcreate.c1020
1 files changed, 1020 insertions, 0 deletions
diff --git a/security/nss/lib/softoken/legacydb/lgcreate.c b/security/nss/lib/softoken/legacydb/lgcreate.c
new file mode 100644
index 0000000000..3ed50a4255
--- /dev/null
+++ b/security/nss/lib/softoken/legacydb/lgcreate.c
@@ -0,0 +1,1020 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "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_NSS_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_NSS_TRUSTED:
+ return CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED;
+ case CKT_NSS_TRUSTED_DELEGATOR:
+ return CERTDB_VALID_CA | trustCA;
+ case CKT_NSS_MUST_VERIFY_TRUST:
+ return CERTDB_MUST_VERIFY;
+ case CKT_NSS_NOT_TRUSTED:
+ return CERTDB_TERMINAL_RECORD;
+ case CKT_NSS_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_NSS_TRUST_UNKNOWN;
+ CK_TRUST clientTrust = CKT_NSS_TRUST_UNKNOWN;
+ CK_TRUST emailTrust = CKT_NSS_TRUST_UNKNOWN;
+ CK_TRUST signTrust = CKT_NSS_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_NSS_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_NSS_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_NSS_URL, templ, count);
+ isKRL = lg_isTrue(CKA_NSS_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;
+ SECItem pubKey2Space = { siBuffer, NULL, 0 };
+ PLArenaPool *arena = NULL;
+ NSSLOWKEYDBHandle *keyHandle = NULL;
+
+ switch (key_type) {
+ case CKK_RSA:
+ pubKeyAttr = CKA_MODULUS;
+ break;
+ case CKK_EC:
+ pubKeyAttr = CKA_EC_POINT;
+ break;
+ 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;
+
+ 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;
+ }
+ }
+
+ 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*/);
+ if (priv == NULL && pubKey == &pubKey2Space) {
+ /* no match on the decoded key, match the original pubkey */
+ pubKey = &pubKeySpace;
+ priv = nsslowkey_FindKeyByPublicKey(keyHandle, pubKey,
+ sdb /*password*/);
+ }
+ 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;
+ }
+ lg_nsslowkey_DestroyPrivateKey(priv);
+ crv = CKR_OK;
+
+ *handle = lg_mkHandle(sdb, pubKey, LG_TOKEN_TYPE_PUB);
+
+done:
+ PORT_Free(pubKeySpace.data);
+ if (arena) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ 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_NSS_DB, templ, count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NSS_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_NSS_DB, templ, count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NSS_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;
+
+ 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_NSS_DB, templ, count)) {
+ crv = lg_Attribute2SSecItem(arena, CKA_NSS_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;
+
+ 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_NSS_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);
+ lg_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 */
+ privKey->u.rsa.modulus.data =
+ (unsigned char *)PORT_ArenaAlloc(arena, pubkey->len);
+ if (privKey->u.rsa.modulus.data == NULL) {
+ crv = CKR_HOST_MEMORY;
+ goto loser;
+ }
+ privKey->u.rsa.modulus.len = pubkey->len;
+ PORT_Memcpy(privKey->u.rsa.modulus.data, pubkey->data, pubkey->len);
+
+ /* The public exponent is set to 0 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)
+ lg_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;
+}
+
+/*
+ * return the 'next' key handle
+ */
+CK_RV
+lg_GetNewObjectID(SDB *sdb, CK_OBJECT_HANDLE *handle)
+{
+ /* the upper level needs the Object ID early to populate any
+ * signature attributes. The legacy can't really return a new
+ * handle without the full object template (chicken and egg issue).
+ * Fortunately we can just return a bogus handle because the legacy
+ * database doesn't support meta data and can't store any of the signed
+ * attributes anyway */
+ *handle = CK_INVALID_HANDLE;
+ return CKR_OK;
+}
+
+/*
+ * 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_NSS_TRUST:
+ crv = lg_createTrustObject(sdb, handle, templ, count);
+ break;
+ case CKO_NSS_CRL:
+ crv = lg_createCrlObject(sdb, handle, templ, count);
+ break;
+ case CKO_NSS_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;
+}