diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/softoken/legacydb/lgcreate.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-upstream.tar.xz firefox-esr-upstream.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/softoken/legacydb/lgcreate.c')
-rw-r--r-- | security/nss/lib/softoken/legacydb/lgcreate.c | 1020 |
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; +} |