/* 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; }