/* 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 "lowkeyi.h" #include "secasn1.h" #include "secder.h" #include "secoid.h" #include "blapi.h" #include "secitem.h" #include "pcert.h" #include "mcom_db.h" #include "secerr.h" #include "keydbi.h" #include "lgdb.h" /* * Record keys for keydb */ #define SALT_STRING "global-salt" #define VERSION_STRING "Version" #define KEYDB_PW_CHECK_STRING "password-check" #define KEYDB_PW_CHECK_LEN 14 #define KEYDB_FAKE_PW_CHECK_STRING "fake-password-check" #define KEYDB_FAKE_PW_CHECK_LEN 19 /* Size of the global salt for key database */ #define SALT_LENGTH 16 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) const SEC_ASN1Template nsslowkey_EncryptedPrivateKeyInfoTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLOWKEYEncryptedPrivateKeyInfo) }, { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(NSSLOWKEYEncryptedPrivateKeyInfo, algorithm), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, { SEC_ASN1_OCTET_STRING, offsetof(NSSLOWKEYEncryptedPrivateKeyInfo, encryptedData) }, { 0 } }; const SEC_ASN1Template nsslowkey_PointerToEncryptedPrivateKeyInfoTemplate[] = { { SEC_ASN1_POINTER, 0, nsslowkey_EncryptedPrivateKeyInfoTemplate } }; /* ====== Default key databse encryption algorithm ====== */ static void sec_destroy_dbkey(NSSLOWKEYDBKey *dbkey) { if (dbkey && dbkey->arena) { PORT_FreeArena(dbkey->arena, PR_FALSE); } } static void free_dbt(DBT *dbt) { if (dbt) { PORT_Free(dbt->data); PORT_Free(dbt); } return; } static int keydb_Get(NSSLOWKEYDBHandle *db, DBT *key, DBT *data, unsigned int flags); static int keydb_Put(NSSLOWKEYDBHandle *db, DBT *key, DBT *data, unsigned int flags); static int keydb_Sync(NSSLOWKEYDBHandle *db, unsigned int flags); static int keydb_Del(NSSLOWKEYDBHandle *db, DBT *key, unsigned int flags); static int keydb_Seq(NSSLOWKEYDBHandle *db, DBT *key, DBT *data, unsigned int flags); static void keydb_Close(NSSLOWKEYDBHandle *db); /* * format of key database entries for version 3 of database: * byte offset field * ----------- ----- * 0 version * 1 salt-len * 2 nn-len * 3.. salt-data * ... nickname * ... encrypted-key-data */ static DBT * encode_dbkey(NSSLOWKEYDBKey *dbkey, unsigned char version) { DBT *bufitem = NULL; unsigned char *buf; int nnlen; char *nn; bufitem = (DBT *)PORT_ZAlloc(sizeof(DBT)); if (bufitem == NULL) { goto loser; } if (dbkey->nickname) { nn = dbkey->nickname; nnlen = PORT_Strlen(nn) + 1; } else { nn = ""; nnlen = 1; } /* compute the length of the record */ /* 1 + 1 + 1 == version number header + salt length + nn len */ bufitem->size = dbkey->salt.len + nnlen + dbkey->derPK.len + 1 + 1 + 1; bufitem->data = (void *)PORT_ZAlloc(bufitem->size); if (bufitem->data == NULL) { goto loser; } buf = (unsigned char *)bufitem->data; /* set version number */ buf[0] = version; /* set length of salt */ PORT_Assert(dbkey->salt.len < 256); buf[1] = dbkey->salt.len; /* set length of nickname */ PORT_Assert(nnlen < 256); buf[2] = nnlen; /* copy salt */ if (dbkey->salt.len > 0) { PORT_Memcpy(&buf[3], dbkey->salt.data, dbkey->salt.len); } /* copy nickname */ PORT_Memcpy(&buf[3 + dbkey->salt.len], nn, nnlen); /* copy encrypted key */ PORT_Memcpy(&buf[3 + dbkey->salt.len + nnlen], dbkey->derPK.data, dbkey->derPK.len); return (bufitem); loser: if (bufitem) { free_dbt(bufitem); } return (NULL); } static NSSLOWKEYDBKey * decode_dbkey(DBT *bufitem, int expectedVersion) { NSSLOWKEYDBKey *dbkey; PLArenaPool *arena = NULL; unsigned char *buf; int version; int keyoff; int nnlen; int saltoff; buf = (unsigned char *)bufitem->data; version = buf[0]; if (version != expectedVersion) { goto loser; } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { goto loser; } dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey)); if (dbkey == NULL) { goto loser; } dbkey->arena = arena; dbkey->salt.data = NULL; dbkey->derPK.data = NULL; dbkey->salt.len = buf[1]; dbkey->salt.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->salt.len); if (dbkey->salt.data == NULL) { goto loser; } saltoff = 2; keyoff = 2 + dbkey->salt.len; if (expectedVersion >= 3) { nnlen = buf[2]; if (nnlen) { dbkey->nickname = (char *)PORT_ArenaZAlloc(arena, nnlen + 1); if (dbkey->nickname) { PORT_Memcpy(dbkey->nickname, &buf[keyoff + 1], nnlen); } } keyoff += (nnlen + 1); saltoff = 3; } PORT_Memcpy(dbkey->salt.data, &buf[saltoff], dbkey->salt.len); dbkey->derPK.len = bufitem->size - keyoff; dbkey->derPK.data = (unsigned char *)PORT_ArenaZAlloc(arena, dbkey->derPK.len); if (dbkey->derPK.data == NULL) { goto loser; } PORT_Memcpy(dbkey->derPK.data, &buf[keyoff], dbkey->derPK.len); return (dbkey); loser: if (arena) { PORT_FreeArena(arena, PR_FALSE); } return (NULL); } static NSSLOWKEYDBKey * get_dbkey(NSSLOWKEYDBHandle *handle, DBT *index) { NSSLOWKEYDBKey *dbkey; DBT entry; int ret; /* get it from the database */ ret = keydb_Get(handle, index, &entry, 0); if (ret) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return NULL; } /* set up dbkey struct */ dbkey = decode_dbkey(&entry, handle->version); return (dbkey); } static SECStatus put_dbkey(NSSLOWKEYDBHandle *handle, DBT *index, NSSLOWKEYDBKey *dbkey, PRBool update) { DBT *keydata = NULL; int status; keydata = encode_dbkey(dbkey, handle->version); if (keydata == NULL) { goto loser; } /* put it in the database */ if (update) { status = keydb_Put(handle, index, keydata, 0); } else { status = keydb_Put(handle, index, keydata, R_NOOVERWRITE); } if (status) { goto loser; } /* sync the database */ status = keydb_Sync(handle, 0); if (status) { goto loser; } free_dbt(keydata); return (SECSuccess); loser: if (keydata) { free_dbt(keydata); } return (SECFailure); } SECStatus nsslowkey_TraverseKeys(NSSLOWKEYDBHandle *handle, SECStatus (*keyfunc)(DBT *k, DBT *d, void *pdata), void *udata) { DBT data; DBT key; SECStatus status; int ret; if (handle == NULL) { return (SECFailure); } ret = keydb_Seq(handle, &key, &data, R_FIRST); if (ret) { return (SECFailure); } do { /* skip version record */ if (data.size > 1) { if (key.size == (sizeof(SALT_STRING) - 1)) { if (PORT_Memcmp(key.data, SALT_STRING, key.size) == 0) { continue; } } /* skip password check */ if (key.size == KEYDB_PW_CHECK_LEN) { if (PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING, KEYDB_PW_CHECK_LEN) == 0) { continue; } } status = (*keyfunc)(&key, &data, udata); if (status != SECSuccess) { return (status); } } } while (keydb_Seq(handle, &key, &data, R_NEXT) == 0); return (SECSuccess); } #ifdef notdef typedef struct keyNode { struct keyNode *next; DBT key; } keyNode; typedef struct { PLArenaPool *arena; keyNode *head; } keyList; static SECStatus sec_add_key_to_list(DBT *key, DBT *data, void *arg) { keyList *keylist; keyNode *node; void *keydata; keylist = (keyList *)arg; /* allocate the node struct */ node = (keyNode *)PORT_ArenaZAlloc(keylist->arena, sizeof(keyNode)); if (node == NULL) { return (SECFailure); } /* allocate room for key data */ keydata = PORT_ArenaZAlloc(keylist->arena, key->size); if (keydata == NULL) { return (SECFailure); } /* link node into list */ node->next = keylist->head; keylist->head = node; /* copy key into node */ PORT_Memcpy(keydata, key->data, key->size); node->key.size = key->size; node->key.data = keydata; return (SECSuccess); } #endif static SECItem * decodeKeyDBGlobalSalt(DBT *saltData) { SECItem *saltitem; saltitem = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); if (saltitem == NULL) { return (NULL); } saltitem->data = (unsigned char *)PORT_ZAlloc(saltData->size); if (saltitem->data == NULL) { PORT_Free(saltitem); return (NULL); } saltitem->len = saltData->size; PORT_Memcpy(saltitem->data, saltData->data, saltitem->len); return (saltitem); } static SECItem * GetKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle) { DBT saltKey; DBT saltData; int ret; saltKey.data = SALT_STRING; saltKey.size = sizeof(SALT_STRING) - 1; ret = keydb_Get(handle, &saltKey, &saltData, 0); if (ret) { return (NULL); } return (decodeKeyDBGlobalSalt(&saltData)); } static SECStatus StoreKeyDBGlobalSalt(NSSLOWKEYDBHandle *handle, SECItem *salt) { DBT saltKey; DBT saltData; int status; saltKey.data = SALT_STRING; saltKey.size = sizeof(SALT_STRING) - 1; saltData.data = (void *)salt->data; saltData.size = salt->len; /* put global salt into the database now */ status = keydb_Put(handle, &saltKey, &saltData, 0); if (status) { return (SECFailure); } return (SECSuccess); } static SECStatus makeGlobalVersion(NSSLOWKEYDBHandle *handle) { unsigned char version; DBT versionData; DBT versionKey; int status; version = NSSLOWKEY_DB_FILE_VERSION; versionData.data = &version; versionData.size = 1; versionKey.data = VERSION_STRING; versionKey.size = sizeof(VERSION_STRING) - 1; /* put version string into the database now */ status = keydb_Put(handle, &versionKey, &versionData, 0); if (status) { return (SECFailure); } handle->version = version; return (SECSuccess); } static SECStatus makeGlobalSalt(NSSLOWKEYDBHandle *handle) { DBT saltKey; DBT saltData; unsigned char saltbuf[16]; int status; saltKey.data = SALT_STRING; saltKey.size = sizeof(SALT_STRING) - 1; saltData.data = (void *)saltbuf; saltData.size = sizeof(saltbuf); RNG_GenerateGlobalRandomBytes(saltbuf, sizeof(saltbuf)); /* put global salt into the database now */ status = keydb_Put(handle, &saltKey, &saltData, 0); if (status) { return (SECFailure); } return (SECSuccess); } static SECStatus encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg, SECItem *encCheck); static unsigned char nsslowkey_version(NSSLOWKEYDBHandle *handle) { DBT versionKey; DBT versionData; int ret; versionKey.data = VERSION_STRING; versionKey.size = sizeof(VERSION_STRING) - 1; if (handle->db == NULL) { return 255; } /* lookup version string in database */ ret = keydb_Get(handle, &versionKey, &versionData, 0); /* error accessing the database */ if (ret < 0) { return 255; } if (ret >= 1) { return 0; } return *((unsigned char *)versionData.data); } static PRBool seckey_HasAServerKey(NSSLOWKEYDBHandle *handle) { DBT key; DBT data; int ret; PRBool found = PR_FALSE; ret = keydb_Seq(handle, &key, &data, R_FIRST); if (ret) { return PR_FALSE; } do { /* skip version record */ if (data.size > 1) { /* skip salt */ if (key.size == (sizeof(SALT_STRING) - 1)) { if (PORT_Memcmp(key.data, SALT_STRING, key.size) == 0) { continue; } } /* skip pw check entry */ if (key.size == KEYDB_PW_CHECK_LEN) { if (PORT_Memcmp(key.data, KEYDB_PW_CHECK_STRING, KEYDB_PW_CHECK_LEN) == 0) { continue; } } /* keys stored by nickname will have 0 as the last byte of the * db key. Other keys must be stored by modulus. We will not * update those because they are left over from a keygen that * never resulted in a cert. */ if (((unsigned char *)key.data)[key.size - 1] != 0) { continue; } if (PORT_Strcmp(key.data, "Server-Key") == 0) { found = PR_TRUE; break; } } } while (keydb_Seq(handle, &key, &data, R_NEXT) == 0); return found; } /* forward declare local create function */ static NSSLOWKEYDBHandle *nsslowkey_NewHandle(DB *dbHandle); /* * currently updates key database from v2 to v3 */ static SECStatus nsslowkey_UpdateKeyDBPass1(NSSLOWKEYDBHandle *handle) { SECStatus rv; DBT checkKey; DBT checkData; DBT saltKey; DBT saltData; DBT key; DBT data; unsigned char version; NSSLOWKEYDBKey *dbkey = NULL; NSSLOWKEYDBHandle *update = NULL; SECItem *oldSalt = NULL; int ret; SECItem checkitem; if (handle->updatedb == NULL) { return SECSuccess; } /* create a full DB Handle for our update so we * can use the correct locks for the db primatives */ update = nsslowkey_NewHandle(handle->updatedb); if (update == NULL) { return SECSuccess; } /* update has now inherited the database handle */ handle->updatedb = NULL; /* * check the version record */ version = nsslowkey_version(update); if (version != 2) { goto done; } saltKey.data = SALT_STRING; saltKey.size = sizeof(SALT_STRING) - 1; ret = keydb_Get(update, &saltKey, &saltData, 0); if (ret) { /* no salt in old db, so it is corrupted */ goto done; } oldSalt = decodeKeyDBGlobalSalt(&saltData); if (oldSalt == NULL) { /* bad salt in old db, so it is corrupted */ goto done; } /* * look for a pw check entry */ checkKey.data = KEYDB_PW_CHECK_STRING; checkKey.size = KEYDB_PW_CHECK_LEN; ret = keydb_Get(update, &checkKey, &checkData, 0); if (ret) { /* * if we have a key, but no KEYDB_PW_CHECK_STRING, then this must * be an old server database, and it does have a password associated * with it. Put a fake entry in so we can identify this db when we do * get the password for it. */ if (seckey_HasAServerKey(update)) { DBT fcheckKey; DBT fcheckData; /* * include a fake string */ fcheckKey.data = KEYDB_FAKE_PW_CHECK_STRING; fcheckKey.size = KEYDB_FAKE_PW_CHECK_LEN; fcheckData.data = "1"; fcheckData.size = 1; /* put global salt into the new database now */ ret = keydb_Put(handle, &saltKey, &saltData, 0); if (ret) { goto done; } ret = keydb_Put(handle, &fcheckKey, &fcheckData, 0); if (ret) { goto done; } } else { goto done; } } else { /* put global salt into the new database now */ ret = keydb_Put(handle, &saltKey, &saltData, 0); if (ret) { goto done; } dbkey = decode_dbkey(&checkData, 2); if (dbkey == NULL) { goto done; } checkitem = dbkey->derPK; dbkey->derPK.data = NULL; /* format the new pw check entry */ rv = encodePWCheckEntry(NULL, &dbkey->derPK, SEC_OID_RC4, &checkitem); if (rv != SECSuccess) { goto done; } rv = put_dbkey(handle, &checkKey, dbkey, PR_TRUE); if (rv != SECSuccess) { goto done; } /* free the dbkey */ sec_destroy_dbkey(dbkey); dbkey = NULL; } /* now traverse the database */ ret = keydb_Seq(update, &key, &data, R_FIRST); if (ret) { goto done; } do { /* skip version record */ if (data.size > 1) { /* skip salt */ if (key.size == (sizeof(SALT_STRING) - 1)) { if (PORT_Memcmp(key.data, SALT_STRING, key.size) == 0) { continue; } } /* skip pw check entry */ if (key.size == checkKey.size) { if (PORT_Memcmp(key.data, checkKey.data, key.size) == 0) { continue; } } /* keys stored by nickname will have 0 as the last byte of the * db key. Other keys must be stored by modulus. We will not * update those because they are left over from a keygen that * never resulted in a cert. */ if (((unsigned char *)key.data)[key.size - 1] != 0) { continue; } dbkey = decode_dbkey(&data, 2); if (dbkey == NULL) { continue; } /* This puts the key into the new database with the same * index (nickname) that it had before. The second pass * of the update will have the password. It will decrypt * and re-encrypt the entries using a new algorithm. */ dbkey->nickname = (char *)key.data; rv = put_dbkey(handle, &key, dbkey, PR_FALSE); dbkey->nickname = NULL; sec_destroy_dbkey(dbkey); } } while (keydb_Seq(update, &key, &data, R_NEXT) == 0); dbkey = NULL; done: /* sync the database */ ret = keydb_Sync(handle, 0); nsslowkey_CloseKeyDB(update); if (oldSalt) { SECITEM_FreeItem(oldSalt, PR_TRUE); } if (dbkey) { sec_destroy_dbkey(dbkey); } return (SECSuccess); } static SECStatus openNewDB(const char *appName, const char *prefix, const char *dbname, NSSLOWKEYDBHandle *handle, NSSLOWKEYDBNameFunc namecb, void *cbarg) { SECStatus rv = SECFailure; int status = RDB_FAIL; char *updname = NULL; DB *updatedb = NULL; PRBool updated = PR_FALSE; int ret; if (appName) { handle->db = rdbopen(appName, prefix, "key", NO_CREATE, &status); } else { handle->db = dbopen(dbname, NO_CREATE, 0600, DB_HASH, 0); } /* if create fails then we lose */ if (handle->db == NULL) { return (status == RDB_RETRY) ? SECWouldBlock : SECFailure; } /* force a transactional read, which will verify that one and only one * process attempts the update. */ if (nsslowkey_version(handle) == NSSLOWKEY_DB_FILE_VERSION) { /* someone else has already updated the database for us */ db_InitComplete(handle->db); return SECSuccess; } /* * if we are creating a multiaccess database, see if there is a * local database we can update from. */ if (appName) { NSSLOWKEYDBHandle *updateHandle; updatedb = dbopen(dbname, NO_RDONLY, 0600, DB_HASH, 0); if (!updatedb) { goto noupdate; } /* nsslowkey_version needs a full handle because it calls * the kdb_Get() function, which needs to lock. */ updateHandle = nsslowkey_NewHandle(updatedb); if (!updateHandle) { updatedb->close(updatedb); goto noupdate; } handle->version = nsslowkey_version(updateHandle); if (handle->version != NSSLOWKEY_DB_FILE_VERSION) { nsslowkey_CloseKeyDB(updateHandle); goto noupdate; } /* copy the new DB from the old one */ db_Copy(handle->db, updatedb); nsslowkey_CloseKeyDB(updateHandle); db_InitComplete(handle->db); return SECSuccess; } noupdate: /* update the version number */ rv = makeGlobalVersion(handle); if (rv != SECSuccess) { goto loser; } /* * try to update from v2 db */ updname = (*namecb)(cbarg, 2); if (updname != NULL) { handle->updatedb = dbopen(updname, NO_RDONLY, 0600, DB_HASH, 0); PORT_Free(updname); if (handle->updatedb) { /* * Try to update the db using a null password. If the db * doesn't have a password, then this will work. If it does * have a password, then this will fail and we will do the * update later */ rv = nsslowkey_UpdateKeyDBPass1(handle); if (rv == SECSuccess) { updated = PR_TRUE; } } } /* we are using the old salt if we updated from an old db */ if (!updated) { rv = makeGlobalSalt(handle); if (rv != SECSuccess) { goto loser; } } /* sync the database */ ret = keydb_Sync(handle, 0); if (ret) { rv = SECFailure; goto loser; } rv = SECSuccess; loser: db_InitComplete(handle->db); return rv; } static DB * openOldDB(const char *appName, const char *prefix, const char *dbname, PRBool openflags) { DB *db = NULL; if (appName) { db = rdbopen(appName, prefix, "key", openflags, NULL); } else { db = dbopen(dbname, openflags, 0600, DB_HASH, 0); } return db; } /* check for correct version number */ static PRBool verifyVersion(NSSLOWKEYDBHandle *handle) { int version = nsslowkey_version(handle); handle->version = version; if (version != NSSLOWKEY_DB_FILE_VERSION) { if (handle->db) { keydb_Close(handle); handle->db = NULL; } } return handle->db != NULL; } static NSSLOWKEYDBHandle * nsslowkey_NewHandle(DB *dbHandle) { NSSLOWKEYDBHandle *handle; handle = (NSSLOWKEYDBHandle *)PORT_ZAlloc(sizeof(NSSLOWKEYDBHandle)); if (handle == NULL) { PORT_SetError(SEC_ERROR_NO_MEMORY); return NULL; } handle->appname = NULL; handle->dbname = NULL; handle->global_salt = NULL; handle->updatedb = NULL; handle->db = dbHandle; handle->ref = 1; handle->lock = PZ_NewLock(nssILockKeyDB); return handle; } NSSLOWKEYDBHandle * nsslowkey_OpenKeyDB(PRBool readOnly, const char *appName, const char *prefix, NSSLOWKEYDBNameFunc namecb, void *cbarg) { NSSLOWKEYDBHandle *handle = NULL; SECStatus rv; int openflags; char *dbname = NULL; handle = nsslowkey_NewHandle(NULL); openflags = readOnly ? NO_RDONLY : NO_RDWR; dbname = (*namecb)(cbarg, NSSLOWKEY_DB_FILE_VERSION); if (dbname == NULL) { goto loser; } handle->appname = appName ? PORT_Strdup(appName) : NULL; handle->dbname = (appName == NULL) ? PORT_Strdup(dbname) : (prefix ? PORT_Strdup(prefix) : NULL); handle->readOnly = readOnly; handle->db = openOldDB(appName, prefix, dbname, openflags); if (handle->db) { verifyVersion(handle); if (handle->version == 255) { goto loser; } } /* if first open fails, try to create a new DB */ if (handle->db == NULL) { if (readOnly) { goto loser; } rv = openNewDB(appName, prefix, dbname, handle, namecb, cbarg); /* two processes started to initialize the database at the same time. * The multiprocess code blocked the second one, then had it retry to * see if it can just open the database normally */ if (rv == SECWouldBlock) { handle->db = openOldDB(appName, prefix, dbname, openflags); verifyVersion(handle); if (handle->db == NULL) { goto loser; } } else if (rv != SECSuccess) { goto loser; } } handle->global_salt = GetKeyDBGlobalSalt(handle); if (dbname) PORT_Free(dbname); return handle; loser: if (dbname) PORT_Free(dbname); PORT_SetError(SEC_ERROR_BAD_DATABASE); nsslowkey_CloseKeyDB(handle); return NULL; } /* * Close the database */ void nsslowkey_CloseKeyDB(NSSLOWKEYDBHandle *handle) { if (handle != NULL) { if (handle->db != NULL) { keydb_Close(handle); } if (handle->updatedb) { handle->updatedb->close(handle->updatedb); } if (handle->dbname) PORT_Free(handle->dbname); if (handle->appname) PORT_Free(handle->appname); if (handle->global_salt) { SECITEM_FreeItem(handle->global_salt, PR_TRUE); } if (handle->lock != NULL) { SKIP_AFTER_FORK(PZ_DestroyLock(handle->lock)); } PORT_Free(handle); } } /* Get the key database version */ int nsslowkey_GetKeyDBVersion(NSSLOWKEYDBHandle *handle) { PORT_Assert(handle != NULL); return handle->version; } /* * Delete a private key that was stored in the database */ SECStatus nsslowkey_DeleteKey(NSSLOWKEYDBHandle *handle, const SECItem *pubkey) { DBT namekey; int ret; if (handle == NULL) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return (SECFailure); } /* set up db key and data */ namekey.data = pubkey->data; namekey.size = pubkey->len; /* delete it from the database */ ret = keydb_Del(handle, &namekey, 0); if (ret) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return (SECFailure); } /* sync the database */ ret = keydb_Sync(handle, 0); if (ret) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return (SECFailure); } return (SECSuccess); } /* * Store a key in the database, indexed by its public key modulus.(value!) */ SECStatus nsslowkey_StoreKeyByPublicKey(NSSLOWKEYDBHandle *handle, NSSLOWKEYPrivateKey *privkey, SECItem *pubKeyData, char *nickname, SDB *sdb) { return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData, nickname, sdb, PR_FALSE); } SECStatus nsslowkey_UpdateNickname(NSSLOWKEYDBHandle *handle, NSSLOWKEYPrivateKey *privkey, SECItem *pubKeyData, char *nickname, SDB *sdb) { return nsslowkey_StoreKeyByPublicKeyAlg(handle, privkey, pubKeyData, nickname, sdb, PR_TRUE); } /* see if the symetric CKA_ID already Exists. */ PRBool nsslowkey_KeyForIDExists(NSSLOWKEYDBHandle *handle, SECItem *id) { DBT namekey; DBT dummy; int status; namekey.data = (char *)id->data; namekey.size = id->len; status = keydb_Get(handle, &namekey, &dummy, 0); if (status) { return PR_FALSE; } return PR_TRUE; } /* see if the public key for this cert is in the database filed * by modulus */ PRBool nsslowkey_KeyForCertExists(NSSLOWKEYDBHandle *handle, NSSLOWCERTCertificate *cert) { NSSLOWKEYPublicKey *pubkey = NULL; DBT namekey; DBT dummy; int status; /* get cert's public key */ pubkey = nsslowcert_ExtractPublicKey(cert); if (pubkey == NULL) { return PR_FALSE; } /* TNH - make key from NSSLOWKEYPublicKey */ switch (pubkey->keyType) { case NSSLOWKEYRSAKey: namekey.data = pubkey->u.rsa.modulus.data; namekey.size = pubkey->u.rsa.modulus.len; break; case NSSLOWKEYDSAKey: namekey.data = pubkey->u.dsa.publicValue.data; namekey.size = pubkey->u.dsa.publicValue.len; break; case NSSLOWKEYDHKey: namekey.data = pubkey->u.dh.publicValue.data; namekey.size = pubkey->u.dh.publicValue.len; break; case NSSLOWKEYECKey: namekey.data = pubkey->u.ec.publicValue.data; namekey.size = pubkey->u.ec.publicValue.len; break; default: /* XXX We don't do Fortezza or DH yet. */ return PR_FALSE; } if (handle->version != 3) { unsigned char buf[SHA1_LENGTH]; SHA1_HashBuf(buf, namekey.data, namekey.size); /* NOTE: don't use pubkey after this! it's now thrashed */ PORT_Memcpy(namekey.data, buf, sizeof(buf)); namekey.size = sizeof(buf); } status = keydb_Get(handle, &namekey, &dummy, 0); /* some databases have the key stored as a signed value */ if (status) { unsigned char *buf = (unsigned char *)PORT_Alloc(namekey.size + 1); if (buf) { PORT_Memcpy(&buf[1], namekey.data, namekey.size); buf[0] = 0; namekey.data = buf; namekey.size++; status = keydb_Get(handle, &namekey, &dummy, 0); PORT_Free(buf); } } lg_nsslowkey_DestroyPublicKey(pubkey); if (status) { return PR_FALSE; } return PR_TRUE; } typedef struct NSSLowPasswordDataParamStr { SECItem salt; SECItem iter; } NSSLowPasswordDataParam; static const SEC_ASN1Template NSSLOWPasswordParamTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSLowPasswordDataParam) }, { SEC_ASN1_OCTET_STRING, offsetof(NSSLowPasswordDataParam, salt) }, { SEC_ASN1_INTEGER, offsetof(NSSLowPasswordDataParam, iter) }, { 0 } }; struct LGEncryptedDataInfoStr { SECAlgorithmID algorithm; SECItem encryptedData; }; typedef struct LGEncryptedDataInfoStr LGEncryptedDataInfo; const SEC_ASN1Template lg_EncryptedDataInfoTemplate[] = { { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(LGEncryptedDataInfo) }, { SEC_ASN1_INLINE | SEC_ASN1_XTRN, offsetof(LGEncryptedDataInfo, algorithm), SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, { SEC_ASN1_OCTET_STRING, offsetof(LGEncryptedDataInfo, encryptedData) }, { 0 } }; static SECItem * nsslowkey_EncodePW(SECOidTag alg, const SECItem *salt, SECItem *data) { NSSLowPasswordDataParam param; LGEncryptedDataInfo edi; PLArenaPool *arena; unsigned char one = 1; SECItem *epw = NULL; SECItem *encParam; int iterLen = 0; int saltLen; SECStatus rv; param.salt = *salt; param.iter.type = siBuffer; /* encode as signed integer */ param.iter.data = &one; param.iter.len = 1; edi.encryptedData = *data; iterLen = salt->len > 1 ? salt->data[salt->len - 1] : 2; saltLen = (salt->len - iterLen) - 1; /* if the resulting saltLen is a sha hash length, then assume that * the iteration count is tacked on the end of the buffer */ if ((saltLen == SHA1_LENGTH) || (saltLen == SHA256_LENGTH) || (saltLen == SHA384_LENGTH) || (saltLen == SHA224_LENGTH) || (saltLen == SHA512_LENGTH)) { param.iter.data = &salt->data[saltLen]; param.iter.len = iterLen; param.salt.len = saltLen; } arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { return NULL; } encParam = SEC_ASN1EncodeItem(arena, NULL, ¶m, NSSLOWPasswordParamTemplate); if (encParam == NULL) { goto loser; } rv = SECOID_SetAlgorithmID(arena, &edi.algorithm, alg, encParam); if (rv != SECSuccess) { goto loser; } epw = SEC_ASN1EncodeItem(NULL, NULL, &edi, lg_EncryptedDataInfoTemplate); loser: PORT_FreeArena(arena, PR_FALSE); return epw; } static SECItem * nsslowkey_DecodePW(const SECItem *derData, SECOidTag *alg, SECItem *salt) { NSSLowPasswordDataParam param; LGEncryptedDataInfo edi; PLArenaPool *arena; SECItem *pwe = NULL; SECStatus rv; salt->data = NULL; param.iter.type = siBuffer; /* decode as signed integer */ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { return NULL; } rv = SEC_QuickDERDecodeItem(arena, &edi, lg_EncryptedDataInfoTemplate, derData); if (rv != SECSuccess) { goto loser; } *alg = SECOID_GetAlgorithmTag(&edi.algorithm); rv = SEC_QuickDERDecodeItem(arena, ¶m, NSSLOWPasswordParamTemplate, &edi.algorithm.parameters); if (rv != SECSuccess) { goto loser; } /* if the iteration count isn't one, tack it at the end of the salt */ if (!((param.iter.len == 1) && (param.iter.data[0] == 1))) { int total_len = param.salt.len + param.iter.len + 1; salt->data = PORT_Alloc(total_len); if (salt->data == NULL) { goto loser; } PORT_Memcpy(salt->data, param.salt.data, param.salt.len); PORT_Memcpy(&salt->data[param.salt.len], param.iter.data, param.iter.len); salt->data[total_len - 1] = param.iter.len; salt->len = total_len; } else { rv = SECITEM_CopyItem(NULL, salt, ¶m.salt); if (rv != SECSuccess) { goto loser; } } pwe = SECITEM_DupItem(&edi.encryptedData); loser: if (!pwe && salt->data) { PORT_Free(salt->data); salt->data = NULL; } PORT_FreeArena(arena, PR_FALSE); return pwe; } /* * check to see if the user has a password */ static SECStatus nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle, NSSLOWKEYPasswordEntry *entry) { DBT checkkey; /*, checkdata; */ NSSLOWKEYDBKey *dbkey = NULL; SECItem *global_salt = NULL; SECItem *item = NULL; SECItem entryData, oid; SECItem none = { siBuffer, NULL, 0 }; SECStatus rv = SECFailure; SECOidTag algorithm; if (handle == NULL) { /* PORT_SetError */ return (SECFailure); } global_salt = GetKeyDBGlobalSalt(handle); if (!global_salt) { global_salt = &none; } if (global_salt->len > sizeof(entry->data)) { /* PORT_SetError */ goto loser; } PORT_Memcpy(entry->data, global_salt->data, global_salt->len); entry->salt.data = entry->data; entry->salt.len = global_salt->len; entry->value.data = &entry->data[entry->salt.len]; checkkey.data = KEYDB_PW_CHECK_STRING; checkkey.size = KEYDB_PW_CHECK_LEN; dbkey = get_dbkey(handle, &checkkey); if (dbkey == NULL) { /* handle 'FAKE' check here */ goto loser; } oid.len = dbkey->derPK.data[0]; oid.data = &dbkey->derPK.data[1]; if (dbkey->derPK.len < (KEYDB_PW_CHECK_LEN + 1 + oid.len)) { goto loser; } algorithm = SECOID_FindOIDTag(&oid); entryData.type = siBuffer; entryData.len = dbkey->derPK.len - (oid.len + 1); entryData.data = &dbkey->derPK.data[oid.len + 1]; item = nsslowkey_EncodePW(algorithm, &dbkey->salt, &entryData); if (!item || (item->len + entry->salt.len) > sizeof(entry->data)) { goto loser; } PORT_Memcpy(entry->value.data, item->data, item->len); entry->value.len = item->len; rv = SECSuccess; loser: if (item) { SECITEM_FreeItem(item, PR_TRUE); } if (dbkey) { sec_destroy_dbkey(dbkey); } if (global_salt != &none) { SECITEM_FreeItem(global_salt, PR_TRUE); } return rv; } /* * check to see if the user has a password */ static SECStatus nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle, NSSLOWKEYPasswordEntry *entry) { DBT checkkey; NSSLOWKEYDBKey *dbkey = NULL; SECItem *item = NULL; SECItem salt; SECOidTag algid = SEC_OID_UNKNOWN; SECStatus rv = SECFailure; PLArenaPool *arena; int ret; if (handle == NULL) { /* PORT_SetError */ return (SECFailure); } checkkey.data = KEYDB_PW_CHECK_STRING; checkkey.size = KEYDB_PW_CHECK_LEN; salt.data = NULL; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { return SECFailure; } item = nsslowkey_DecodePW(&entry->value, &algid, &salt); if (item == NULL) { goto loser; } dbkey = PORT_ArenaZNew(arena, NSSLOWKEYDBKey); if (dbkey == NULL) { goto loser; } dbkey->arena = arena; rv = SECITEM_CopyItem(arena, &dbkey->salt, &salt); if (rv != SECSuccess) { goto loser; } rv = encodePWCheckEntry(arena, &dbkey->derPK, algid, item); if (rv != SECSuccess) { goto loser; } rv = put_dbkey(handle, &checkkey, dbkey, PR_TRUE); if (rv != SECSuccess) { goto loser; } if (handle->global_salt) { SECITEM_FreeItem(handle->global_salt, PR_TRUE); handle->global_salt = NULL; } rv = StoreKeyDBGlobalSalt(handle, &entry->salt); if (rv != SECSuccess) { goto loser; } ret = keydb_Sync(handle, 0); if (ret) { rv = SECFailure; goto loser; } handle->global_salt = GetKeyDBGlobalSalt(handle); loser: if (item) { SECITEM_FreeItem(item, PR_TRUE); } if (arena) { PORT_FreeArena(arena, PR_TRUE); } if (salt.data) { PORT_Free(salt.data); } return rv; } #ifdef EC_DEBUG #define SEC_PRINT(str1, str2, num, sitem) \ printf("pkcs11c.c:%s:%s (keytype=%d) [len=%d]\n", \ str1, str2, num, sitem->len); \ for (i = 0; i < sitem->len; i++) { \ printf("%02x:", sitem->data[i]); \ } \ printf("\n") #else #define SEC_PRINT(a, b, c, d) #endif /* EC_DEBUG */ SECStatus seckey_encrypt_private_key(PLArenaPool *permarena, NSSLOWKEYPrivateKey *pk, SDB *sdbpw, SECItem *result) { NSSLOWKEYPrivateKeyInfo *pki = NULL; SECStatus rv = SECFailure; PLArenaPool *temparena = NULL; SECItem *der_item = NULL; SECItem *cipherText = NULL; SECItem *dummy = NULL; #ifdef EC_DEBUG SECItem *fordebug = NULL; #endif int savelen; temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); if (temparena == NULL) goto loser; /* allocate structures */ pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena, sizeof(NSSLOWKEYPrivateKeyInfo)); der_item = (SECItem *)PORT_ArenaZAlloc(temparena, sizeof(SECItem)); if ((pki == NULL) || (der_item == NULL)) goto loser; /* setup private key info */ dummy = SEC_ASN1EncodeInteger(temparena, &(pki->version), NSSLOWKEY_PRIVATE_KEY_INFO_VERSION); if (dummy == NULL) goto loser; /* Encode the key, and set the algorithm (with params) */ switch (pk->keyType) { case NSSLOWKEYRSAKey: lg_prepare_low_rsa_priv_key_for_asn1(pk); dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, lg_nsslowkey_RSAPrivateKeyTemplate); if (dummy == NULL) { rv = SECFailure; goto loser; } rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), SEC_OID_PKCS1_RSA_ENCRYPTION, 0); if (rv == SECFailure) { goto loser; } break; case NSSLOWKEYDSAKey: lg_prepare_low_dsa_priv_key_for_asn1(pk); dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, lg_nsslowkey_DSAPrivateKeyTemplate); if (dummy == NULL) { rv = SECFailure; goto loser; } lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params); dummy = SEC_ASN1EncodeItem(temparena, NULL, &pk->u.dsa.params, lg_nsslowkey_PQGParamsTemplate); if (dummy == NULL) { rv = SECFailure; goto loser; } rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), SEC_OID_ANSIX9_DSA_SIGNATURE, dummy); if (rv == SECFailure) { goto loser; } break; case NSSLOWKEYDHKey: lg_prepare_low_dh_priv_key_for_asn1(pk); dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, lg_nsslowkey_DHPrivateKeyTemplate); if (dummy == NULL) { rv = SECFailure; goto loser; } rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), SEC_OID_X942_DIFFIE_HELMAN_KEY, dummy); if (rv == SECFailure) { goto loser; } break; case NSSLOWKEYECKey: lg_prepare_low_ec_priv_key_for_asn1(pk); /* Public value is encoded as a bit string so adjust length * to be in bits before ASN encoding and readjust * immediately after. * * Since the SECG specification recommends not including the * parameters as part of ECPrivateKey, we zero out the curveOID * length before encoding and restore it later. */ pk->u.ec.publicValue.len <<= 3; savelen = pk->u.ec.ecParams.curveOID.len; pk->u.ec.ecParams.curveOID.len = 0; dummy = SEC_ASN1EncodeItem(temparena, &(pki->privateKey), pk, lg_nsslowkey_ECPrivateKeyTemplate); pk->u.ec.ecParams.curveOID.len = savelen; pk->u.ec.publicValue.len >>= 3; if (dummy == NULL) { rv = SECFailure; goto loser; } dummy = &pk->u.ec.ecParams.DEREncoding; /* At this point dummy should contain the encoded params */ rv = SECOID_SetAlgorithmID(temparena, &(pki->algorithm), SEC_OID_ANSIX962_EC_PUBLIC_KEY, dummy); if (rv == SECFailure) { goto loser; } #ifdef EC_DEBUG fordebug = &(pki->privateKey); SEC_PRINT("seckey_encrypt_private_key()", "PrivateKey", pk->keyType, fordebug); #endif break; default: /* We don't support DH or Fortezza private keys yet */ PORT_Assert(PR_FALSE); break; } /* setup encrypted private key info */ dummy = SEC_ASN1EncodeItem(temparena, der_item, pki, lg_nsslowkey_PrivateKeyInfoTemplate); SEC_PRINT("seckey_encrypt_private_key()", "PrivateKeyInfo", pk->keyType, der_item); if (dummy == NULL) { rv = SECFailure; goto loser; } rv = lg_util_encrypt(temparena, sdbpw, dummy, &cipherText); if (rv != SECSuccess) { goto loser; } rv = SECITEM_CopyItem(permarena, result, cipherText); loser: if (temparena != NULL) PORT_FreeArena(temparena, PR_TRUE); return rv; } static SECStatus seckey_put_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, SDB *sdbpw, NSSLOWKEYPrivateKey *pk, char *nickname, PRBool update) { NSSLOWKEYDBKey *dbkey = NULL; PLArenaPool *arena = NULL; SECStatus rv = SECFailure; if ((keydb == NULL) || (index == NULL) || (sdbpw == NULL) || (pk == NULL)) return SECFailure; arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); if (arena == NULL) return SECFailure; dbkey = (NSSLOWKEYDBKey *)PORT_ArenaZAlloc(arena, sizeof(NSSLOWKEYDBKey)); if (dbkey == NULL) goto loser; dbkey->arena = arena; dbkey->nickname = nickname; rv = seckey_encrypt_private_key(arena, pk, sdbpw, &dbkey->derPK); if (rv != SECSuccess) goto loser; rv = put_dbkey(keydb, index, dbkey, update); /* let success fall through */ loser: if (arena != NULL) PORT_FreeArena(arena, PR_TRUE); return rv; } /* * Store a key in the database, indexed by its public key modulus. * Note that the nickname is optional. It was only used by keyutil. */ SECStatus nsslowkey_StoreKeyByPublicKeyAlg(NSSLOWKEYDBHandle *handle, NSSLOWKEYPrivateKey *privkey, SECItem *pubKeyData, char *nickname, SDB *sdbpw, PRBool update) { DBT namekey; SECStatus rv; if (handle == NULL) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return (SECFailure); } /* set up db key and data */ namekey.data = pubKeyData->data; namekey.size = pubKeyData->len; /* encrypt the private key */ rv = seckey_put_private_key(handle, &namekey, sdbpw, privkey, nickname, update); return (rv); } static NSSLOWKEYPrivateKey * seckey_decrypt_private_key(SECItem *epki, SDB *sdbpw) { NSSLOWKEYPrivateKey *pk = NULL; NSSLOWKEYPrivateKeyInfo *pki = NULL; SECStatus rv = SECFailure; PLArenaPool *temparena = NULL, *permarena = NULL; SECItem *dest = NULL; #ifdef EC_DEBUG SECItem *fordebug = NULL; #endif if ((epki == NULL) || (sdbpw == NULL)) goto loser; temparena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); permarena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE); if ((temparena == NULL) || (permarena == NULL)) goto loser; /* allocate temporary items */ pki = (NSSLOWKEYPrivateKeyInfo *)PORT_ArenaZAlloc(temparena, sizeof(NSSLOWKEYPrivateKeyInfo)); /* allocate permanent arena items */ pk = (NSSLOWKEYPrivateKey *)PORT_ArenaZAlloc(permarena, sizeof(NSSLOWKEYPrivateKey)); if ((pk == NULL) || (pki == NULL)) goto loser; pk->arena = permarena; rv = lg_util_decrypt(sdbpw, epki, &dest); if (rv != SECSuccess) { goto loser; } if (dest != NULL) { SECItem newPrivateKey; SECItem newAlgParms; SEC_PRINT("seckey_decrypt_private_key()", "PrivateKeyInfo", -1, dest); rv = SEC_QuickDERDecodeItem(temparena, pki, lg_nsslowkey_PrivateKeyInfoTemplate, dest); if (rv == SECSuccess) { switch (SECOID_GetAlgorithmTag(&pki->algorithm)) { case SEC_OID_X500_RSA_ENCRYPTION: case SEC_OID_PKCS1_RSA_ENCRYPTION: pk->keyType = NSSLOWKEYRSAKey; lg_prepare_low_rsa_priv_key_for_asn1(pk); if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey, &pki->privateKey)) break; rv = SEC_QuickDERDecodeItem(permarena, pk, lg_nsslowkey_RSAPrivateKeyTemplate, &newPrivateKey); if (rv == SECSuccess) { break; } /* Try decoding with the alternative template, but only allow * a zero-length modulus for a secret key object. * See bug 715073. */ rv = SEC_QuickDERDecodeItem(permarena, pk, lg_nsslowkey_RSAPrivateKeyTemplate2, &newPrivateKey); /* A publicExponent of 0 is the defining property of a secret * key disguised as an RSA key. When decoding with the * alternative template, only accept a secret key with an * improperly encoded modulus and a publicExponent of 0. */ if (rv == SECSuccess) { if (pk->u.rsa.modulus.len == 2 && pk->u.rsa.modulus.data[0] == SEC_ASN1_INTEGER && pk->u.rsa.modulus.data[1] == 0 && pk->u.rsa.publicExponent.len == 1 && pk->u.rsa.publicExponent.data[0] == 0) { /* Fix the zero-length integer by setting it to 0. */ pk->u.rsa.modulus.data = pk->u.rsa.publicExponent.data; pk->u.rsa.modulus.len = pk->u.rsa.publicExponent.len; } else { PORT_SetError(SEC_ERROR_BAD_DER); rv = SECFailure; } } break; case SEC_OID_ANSIX9_DSA_SIGNATURE: pk->keyType = NSSLOWKEYDSAKey; lg_prepare_low_dsa_priv_key_for_asn1(pk); if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey, &pki->privateKey)) break; rv = SEC_QuickDERDecodeItem(permarena, pk, lg_nsslowkey_DSAPrivateKeyTemplate, &newPrivateKey); if (rv != SECSuccess) goto loser; lg_prepare_low_pqg_params_for_asn1(&pk->u.dsa.params); if (SECSuccess != SECITEM_CopyItem(permarena, &newAlgParms, &pki->algorithm.parameters)) break; rv = SEC_QuickDERDecodeItem(permarena, &pk->u.dsa.params, lg_nsslowkey_PQGParamsTemplate, &newAlgParms); break; case SEC_OID_X942_DIFFIE_HELMAN_KEY: pk->keyType = NSSLOWKEYDHKey; lg_prepare_low_dh_priv_key_for_asn1(pk); if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey, &pki->privateKey)) break; rv = SEC_QuickDERDecodeItem(permarena, pk, lg_nsslowkey_DHPrivateKeyTemplate, &newPrivateKey); break; case SEC_OID_ANSIX962_EC_PUBLIC_KEY: pk->keyType = NSSLOWKEYECKey; lg_prepare_low_ec_priv_key_for_asn1(pk); #ifdef EC_DEBUG fordebug = &pki->privateKey; SEC_PRINT("seckey_decrypt_private_key()", "PrivateKey", pk->keyType, fordebug); #endif if (SECSuccess != SECITEM_CopyItem(permarena, &newPrivateKey, &pki->privateKey)) break; rv = SEC_QuickDERDecodeItem(permarena, pk, lg_nsslowkey_ECPrivateKeyTemplate, &newPrivateKey); if (rv != SECSuccess) goto loser; lg_prepare_low_ecparams_for_asn1(&pk->u.ec.ecParams); rv = SECITEM_CopyItem(permarena, &pk->u.ec.ecParams.DEREncoding, &pki->algorithm.parameters); if (rv != SECSuccess) goto loser; /* Fill out the rest of EC params */ rv = LGEC_FillParams(permarena, &pk->u.ec.ecParams.DEREncoding, &pk->u.ec.ecParams); if (rv != SECSuccess) goto loser; if (pk->u.ec.publicValue.len != 0) { pk->u.ec.publicValue.len >>= 3; } break; default: rv = SECFailure; break; } } else if (PORT_GetError() == SEC_ERROR_BAD_DER) { PORT_SetError(SEC_ERROR_BAD_PASSWORD); goto loser; } } /* let success fall through */ loser: if (temparena != NULL) PORT_FreeArena(temparena, PR_TRUE); if (dest != NULL) SECITEM_ZfreeItem(dest, PR_TRUE); if (rv != SECSuccess) { if (permarena != NULL) PORT_FreeArena(permarena, PR_TRUE); pk = NULL; } return pk; } static NSSLOWKEYPrivateKey * seckey_decode_encrypted_private_key(NSSLOWKEYDBKey *dbkey, SDB *sdbpw) { if ((dbkey == NULL) || (sdbpw == NULL)) { return NULL; } return seckey_decrypt_private_key(&(dbkey->derPK), sdbpw); } static NSSLOWKEYPrivateKey * seckey_get_private_key(NSSLOWKEYDBHandle *keydb, DBT *index, char **nickname, SDB *sdbpw) { NSSLOWKEYDBKey *dbkey = NULL; NSSLOWKEYPrivateKey *pk = NULL; if ((keydb == NULL) || (index == NULL) || (sdbpw == NULL)) { return NULL; } dbkey = get_dbkey(keydb, index); if (dbkey == NULL) { goto loser; } if (nickname) { if (dbkey->nickname && (dbkey->nickname[0] != 0)) { *nickname = PORT_Strdup(dbkey->nickname); } else { *nickname = NULL; } } pk = seckey_decode_encrypted_private_key(dbkey, sdbpw); /* let success fall through */ loser: if (dbkey != NULL) { sec_destroy_dbkey(dbkey); } return pk; } /* * Find a key in the database, indexed by its public key modulus * This is used to find keys that have been stored before their * certificate arrives. Once the certificate arrives the key * is looked up by the public modulus in the certificate, and the * re-stored by its nickname. */ NSSLOWKEYPrivateKey * nsslowkey_FindKeyByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus, SDB *sdbpw) { DBT namekey; NSSLOWKEYPrivateKey *pk = NULL; if (handle == NULL) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return NULL; } /* set up db key */ namekey.data = modulus->data; namekey.size = modulus->len; pk = seckey_get_private_key(handle, &namekey, NULL, sdbpw); /* no need to free dbkey, since its on the stack, and the data it * points to is owned by the database */ return (pk); } char * nsslowkey_FindKeyNicknameByPublicKey(NSSLOWKEYDBHandle *handle, SECItem *modulus, SDB *sdbpw) { DBT namekey; NSSLOWKEYPrivateKey *pk = NULL; char *nickname = NULL; if (handle == NULL) { PORT_SetError(SEC_ERROR_BAD_DATABASE); return NULL; } /* set up db key */ namekey.data = modulus->data; namekey.size = modulus->len; pk = seckey_get_private_key(handle, &namekey, &nickname, sdbpw); if (pk) { lg_nsslowkey_DestroyPrivateKey(pk); } /* no need to free dbkey, since its on the stack, and the data it * points to is owned by the database */ return (nickname); } /* ===== ENCODING ROUTINES ===== */ static SECStatus encodePWCheckEntry(PLArenaPool *arena, SECItem *entry, SECOidTag alg, SECItem *encCheck) { SECOidData *oidData; oidData = SECOID_FindOIDByTag(alg); if (oidData == NULL) { return SECFailure; } entry->len = 1 + oidData->oid.len + encCheck->len; if (arena) { entry->data = (unsigned char *)PORT_ArenaAlloc(arena, entry->len); } else { entry->data = (unsigned char *)PORT_Alloc(entry->len); } if (entry->data == NULL) { return SECFailure; } /* first length of oid */ entry->data[0] = (unsigned char)oidData->oid.len; /* next oid itself */ PORT_Memcpy(&entry->data[1], oidData->oid.data, oidData->oid.len); /* finally the encrypted check string */ PORT_Memcpy(&entry->data[1 + oidData->oid.len], encCheck->data, encCheck->len); return SECSuccess; } #define MAX_DB_SIZE 0xffff /* * Clear out all the keys in the existing database */ static SECStatus nsslowkey_ResetKeyDB(NSSLOWKEYDBHandle *handle) { SECStatus rv; int errors = 0; if (handle->db == NULL) { return (SECSuccess); } if (handle->readOnly) { /* set an error code */ return SECFailure; } if (handle->appname == NULL && handle->dbname == NULL) { return SECFailure; } keydb_Close(handle); if (handle->appname) { handle->db = rdbopen(handle->appname, handle->dbname, "key", NO_CREATE, NULL); } else { handle->db = dbopen(handle->dbname, NO_CREATE, 0600, DB_HASH, 0); } if (handle->db == NULL) { /* set an error code */ return SECFailure; } rv = makeGlobalVersion(handle); if (rv != SECSuccess) { errors++; goto done; } if (handle->global_salt) { rv = StoreKeyDBGlobalSalt(handle, handle->global_salt); } else { rv = makeGlobalSalt(handle); if (rv == SECSuccess) { handle->global_salt = GetKeyDBGlobalSalt(handle); } } if (rv != SECSuccess) { errors++; } done: /* sync the database */ (void)keydb_Sync(handle, 0); db_InitComplete(handle->db); return (errors == 0 ? SECSuccess : SECFailure); } static int keydb_Get(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags) { int ret; PRLock *kdbLock = kdb->lock; DB *db = kdb->db; PORT_Assert(kdbLock != NULL); PZ_Lock(kdbLock); ret = (*db->get)(db, key, data, flags); (void)PZ_Unlock(kdbLock); return (ret); } static int keydb_Put(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags) { int ret = 0; PRLock *kdbLock = kdb->lock; DB *db = kdb->db; PORT_Assert(kdbLock != NULL); PZ_Lock(kdbLock); ret = (*db->put)(db, key, data, flags); (void)PZ_Unlock(kdbLock); return (ret); } static int keydb_Sync(NSSLOWKEYDBHandle *kdb, unsigned int flags) { int ret; PRLock *kdbLock = kdb->lock; DB *db = kdb->db; PORT_Assert(kdbLock != NULL); PZ_Lock(kdbLock); ret = (*db->sync)(db, flags); (void)PZ_Unlock(kdbLock); return (ret); } static int keydb_Del(NSSLOWKEYDBHandle *kdb, DBT *key, unsigned int flags) { int ret; PRLock *kdbLock = kdb->lock; DB *db = kdb->db; PORT_Assert(kdbLock != NULL); PZ_Lock(kdbLock); ret = (*db->del)(db, key, flags); (void)PZ_Unlock(kdbLock); return (ret); } static int keydb_Seq(NSSLOWKEYDBHandle *kdb, DBT *key, DBT *data, unsigned int flags) { int ret; PRLock *kdbLock = kdb->lock; DB *db = kdb->db; PORT_Assert(kdbLock != NULL); PZ_Lock(kdbLock); ret = (*db->seq)(db, key, data, flags); (void)PZ_Unlock(kdbLock); return (ret); } static void keydb_Close(NSSLOWKEYDBHandle *kdb) { PRLock *kdbLock = kdb->lock; DB *db = kdb->db; PORT_Assert(kdbLock != NULL); SKIP_AFTER_FORK(PZ_Lock(kdbLock)); (*db->close)(db); SKIP_AFTER_FORK(PZ_Unlock(kdbLock)); return; } /* * SDB Entry Points for the Key DB */ CK_RV lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2) { NSSLOWKEYDBHandle *keydb; NSSLOWKEYPasswordEntry entry; SECStatus rv; keydb = lg_getKeyDB(sdb); if (keydb == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } if (PORT_Strcmp(id, "password") != 0) { /* shouldn't happen */ return CKR_GENERAL_ERROR; /* no extra data stored */ } rv = nsslowkey_GetPWCheckEntry(keydb, &entry); if (rv != SECSuccess) { return CKR_GENERAL_ERROR; } item1->len = entry.salt.len; PORT_Memcpy(item1->data, entry.salt.data, item1->len); item2->len = entry.value.len; PORT_Memcpy(item2->data, entry.value.data, item2->len); return CKR_OK; } CK_RV lg_PutMetaData(SDB *sdb, const char *id, const SECItem *item1, const SECItem *item2) { NSSLOWKEYDBHandle *keydb; NSSLOWKEYPasswordEntry entry; SECStatus rv; keydb = lg_getKeyDB(sdb); if (keydb == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } if (PORT_Strcmp(id, "password") != 0) { /* shouldn't happen */ return CKR_GENERAL_ERROR; /* no extra data stored */ } entry.salt = *item1; entry.value = *item2; rv = nsslowkey_PutPWCheckEntry(keydb, &entry); if (rv != SECSuccess) { return CKR_GENERAL_ERROR; } return CKR_OK; } CK_RV lg_DestroyMetaData(SDB *db, const char *id) { return CKR_GENERAL_ERROR; /* no extra data stored */ } CK_RV lg_Reset(SDB *sdb) { NSSLOWKEYDBHandle *keydb; SECStatus rv; keydb = lg_getKeyDB(sdb); if (keydb == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } rv = nsslowkey_ResetKeyDB(keydb); if (rv != SECSuccess) { return CKR_GENERAL_ERROR; } return CKR_OK; }