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