blob: f2b2aa63433d41490dc0b6dff3d25b29d466b68e [file] [log] [blame]
/* 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_NETSCAPE_DB, templ, count)) {
crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_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_NETSCAPE_DB, templ, count)) {
crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_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_NETSCAPE_DB, templ, count)) {
crv = lg_Attribute2SSecItem(arena, CKA_NETSCAPE_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_NETSCAPE_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;
}
/*
* 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;
}