From 4703a817f8b90cc3423620e5c7606bdbcedbcf2e Mon Sep 17 00:00:00 2001 From: Robert Relyea Date: Mon, 25 Nov 2019 11:28:16 -0800 Subject: [PATCH] Bug 1585189 - Changed the algorithm used to encrypt NSS database entries, from 3DES to AES256. Our NSS DB uses 3DES internally to encrypt their entries. This patch changes the default algorithm for AES256 to increase the security. This patch also adds code to use AES Wrap in the future. It also adds an integrity check to the AES256 CBC. The change only affects sqlite databases. bob Differential Revision: https://phabricator.services.mozilla.com/D54589 --- lib/softoken/legacydb/keydb.c | 6 + lib/softoken/legacydb/lgcreate.c | 16 ++ lib/softoken/legacydb/lgdb.h | 2 + lib/softoken/legacydb/lginit.c | 4 +- lib/softoken/lgglue.c | 9 +- lib/softoken/lowpbe.c | 347 ++++++++++++++++++++++++------- lib/softoken/lowpbe.h | 1 + lib/softoken/pkcs11c.c | 74 +------ lib/softoken/pkcs11i.h | 15 ++ lib/softoken/pkcs11u.c | 53 ++++- lib/softoken/sdb.c | 46 +++- lib/softoken/sdb.h | 4 + lib/softoken/sftkdb.c | 260 ++++++++++++++++++++--- lib/softoken/sftkdb.h | 3 +- lib/softoken/sftkdbti.h | 33 ++- lib/softoken/sftkpwd.c | 127 +++++++++-- lib/util/pkcs11n.h | 1 + 17 files changed, 796 insertions(+), 205 deletions(-) diff --git a/lib/softoken/legacydb/keydb.c b/lib/softoken/legacydb/keydb.c index c8c4d07ba8..daa0bbd97a 100644 --- a/lib/softoken/legacydb/keydb.c +++ b/lib/softoken/legacydb/keydb.c @@ -2251,6 +2251,12 @@ lg_PutMetaData(SDB *sdb, const char *id, 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) { diff --git a/lib/softoken/legacydb/lgcreate.c b/lib/softoken/legacydb/lgcreate.c index f2b2aa6343..19c4d4df2b 100644 --- a/lib/softoken/legacydb/lgcreate.c +++ b/lib/softoken/legacydb/lgcreate.c @@ -960,6 +960,22 @@ lg_createKeyObject(SDB *sdb, CK_OBJECT_CLASS objclass, 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. diff --git a/lib/softoken/legacydb/lgdb.h b/lib/softoken/legacydb/lgdb.h index ee80f4b775..c28e8a3687 100644 --- a/lib/softoken/legacydb/lgdb.h +++ b/lib/softoken/legacydb/lgdb.h @@ -150,6 +150,8 @@ CK_RV lg_Abort(SDB *sdb); CK_RV lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2); CK_RV lg_PutMetaData(SDB *sdb, const char *id, const SECItem *item1, const SECItem *item2); +CK_RV lg_DestroyMetaData(SDB *sdb, const char *id); +CK_RV lg_GetNewObjectID(SDB *sdb, CK_OBJECT_HANDLE *object_id); SEC_END_PROTOS diff --git a/lib/softoken/legacydb/lginit.c b/lib/softoken/legacydb/lginit.c index 4f0b53f521..e3048709bb 100644 --- a/lib/softoken/legacydb/lginit.c +++ b/lib/softoken/legacydb/lginit.c @@ -519,7 +519,7 @@ lg_init(SDB **pSdb, int flags, NSSLOWCERTCertDBHandle *certdbPtr, } sdb->private = lgdb_p; - sdb->version = 0; + sdb->version = 1; sdb->sdb_flags = flags; sdb->app_private = NULL; sdb->sdb_FindObjectsInit = lg_FindObjectsInit; @@ -531,12 +531,14 @@ lg_init(SDB **pSdb, int flags, NSSLOWCERTCertDBHandle *certdbPtr, sdb->sdb_DestroyObject = lg_DestroyObject; sdb->sdb_GetMetaData = lg_GetMetaData; sdb->sdb_PutMetaData = lg_PutMetaData; + sdb->sdb_DestroyMetaData = lg_DestroyMetaData; sdb->sdb_Begin = lg_Begin; sdb->sdb_Commit = lg_Commit; sdb->sdb_Abort = lg_Abort; sdb->sdb_Reset = lg_Reset; sdb->sdb_Close = lg_Close; sdb->sdb_SetForkState = lg_SetForkState; + sdb->sdb_GetNewObjectID = lg_GetNewObjectID; *pSdb = sdb; return CKR_OK; diff --git a/lib/softoken/lgglue.c b/lib/softoken/lgglue.c index 8cfb4ec561..461e9a3cd2 100644 --- a/lib/softoken/lgglue.c +++ b/lib/softoken/lgglue.c @@ -205,7 +205,8 @@ sftkdb_encrypt_stub(PLArenaPool *arena, SDB *sdb, SECItem *plainText, iterationCount = 1; } - rv = sftkdb_EncryptAttribute(arena, key, iterationCount, + rv = sftkdb_EncryptAttribute(arena, handle, sdb, key, iterationCount, + CK_INVALID_HANDLE, CKT_INVALID_TYPE, plainText, cipherText); PZ_Unlock(handle->passwordLock); @@ -227,7 +228,7 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText) return SECFailure; } - /* if we aren't th handle, try the other handle */ + /* if we aren't the key handle, try the other handle */ oldKey = handle->oldKey; if (handle->type != SFTK_KEYDB_TYPE) { handle = handle->peerDB; @@ -244,7 +245,9 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText) /* PORT_SetError */ return SECFailure; } - rv = sftkdb_DecryptAttribute(oldKey ? oldKey : &handle->passwordKey, + rv = sftkdb_DecryptAttribute(NULL, oldKey ? oldKey : &handle->passwordKey, + CK_INVALID_HANDLE, + CKT_INVALID_TYPE, cipherText, plainText); PZ_Unlock(handle->passwordLock); diff --git a/lib/softoken/lowpbe.c b/lib/softoken/lowpbe.c index 4a101c68c4..5669e1bd68 100644 --- a/lib/softoken/lowpbe.c +++ b/lib/softoken/lowpbe.c @@ -18,9 +18,13 @@ #include "alghmac.h" #include "softoken.h" #include "secerr.h" +#include "pkcs11i.h" SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) +/* how much a crypto encrypt/decryption may expand a buffer */ +#define MAX_CRYPTO_EXPANSION 64 + /* template for PKCS 5 PBE Parameter. This template has been expanded * based upon the additions in PKCS 12. This should eventually be moved * if RSA updates PKCS 5. @@ -643,11 +647,49 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, return NULL; } +#define MAX_IV_LENGTH 64 +/* get a random IV into the parameters */ +static SECStatus +nsspkcs5_SetIVParam(NSSPKCS5PBEParameter *pbe_param, int ivLen) +{ + SECStatus rv; + SECItem derIV; + SECItem iv; + SECItem *dummy = NULL; + unsigned char ivData[MAX_IV_LENGTH]; + + PORT_Assert(ivLen <= MAX_IV_LENGTH); + + /* Because of a bug in the decode section, the IV's not are expected + * to be der encoded, but still need to parse as if they were der data. + * because we want to be compatible with existing versions of nss that + * have that bug, create an IV that looks like der data. That still + * leaves 14 bytes of entropy in the IV */ + rv = RNG_GenerateGlobalRandomBytes(ivData, ivLen - 2); + if (rv != SECSuccess) { + return SECFailure; + } + derIV.data = NULL; + derIV.len = 0; + iv.data = ivData; + iv.len = ivLen - 2; + dummy = SEC_ASN1EncodeItem(pbe_param->poolp, &derIV, &iv, + SEC_ASN1_GET(SEC_OctetStringTemplate)); + if (dummy == NULL) { + return SECFailure; + } + pbe_param->ivData = derIV.data; + pbe_param->ivLen = derIV.len; + PORT_Assert(pbe_param->ivLen == ivLen); + return SECSuccess; +} + static SECStatus nsspkcs5_FillInParam(SECOidTag algorithm, HASH_HashType hashType, NSSPKCS5PBEParameter *pbe_param) { PRBool skipType = PR_FALSE; + SECStatus rv; pbe_param->keyLen = 5; pbe_param->ivLen = 8; @@ -720,11 +762,68 @@ nsspkcs5_FillInParam(SECOidTag algorithm, HASH_HashType hashType, pbe_param->encAlg = SEC_OID_PKCS5_PBKDF2; pbe_param->keyLen = 0; /* needs to be set by caller after return */ break; + /* AES uses PBKDF2 */ + case SEC_OID_AES_128_CBC: + rv = nsspkcs5_SetIVParam(pbe_param, 16); + if (rv != SECSuccess) { + return rv; + } + pbe_param->ivLen = 16; + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = algorithm; + pbe_param->keyLen = 128 / 8; + break; + case SEC_OID_AES_192_CBC: + rv = nsspkcs5_SetIVParam(pbe_param, 16); + if (rv != SECSuccess) { + return rv; + } + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = algorithm; + pbe_param->keyLen = 192 / 8; + break; + case SEC_OID_AES_256_CBC: + rv = nsspkcs5_SetIVParam(pbe_param, 16); + if (rv != SECSuccess) { + return rv; + } + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = algorithm; + pbe_param->keyLen = 256 / 8; + break; + case SEC_OID_AES_128_KEY_WRAP: + pbe_param->ivLen = 0; + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = algorithm; + pbe_param->keyLen = 128 / 8; + break; + case SEC_OID_AES_192_KEY_WRAP: + pbe_param->ivLen = 0; + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = algorithm; + pbe_param->keyLen = 192 / 8; + break; + case SEC_OID_AES_256_KEY_WRAP: + pbe_param->ivLen = 0; + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = algorithm; + pbe_param->keyLen = 256 / 8; + break; default: return SECFailure; } - + if (pbe_param->pbeType == NSSPKCS5_PBKDF2) { + SECOidTag prfAlg = HASH_HMACOidFromHash(pbe_param->hashType); + if (prfAlg == SEC_OID_UNKNOWN) { + return SECFailure; + } + rv = SECOID_SetAlgorithmID(pbe_param->poolp, &pbe_param->prfAlg, + prfAlg, NULL); + if (rv != SECSuccess) { + return rv; + } + } return SECSuccess; } @@ -797,6 +896,29 @@ HASH_FromHMACOid(SECOidTag hmac) return HASH_AlgNULL; } +SECOidTag +HASH_HMACOidFromHash(HASH_HashType hashType) +{ + switch (hashType) { + case HASH_AlgSHA1: + return SEC_OID_HMAC_SHA1; + case HASH_AlgSHA256: + return SEC_OID_HMAC_SHA256; + case HASH_AlgSHA384: + return SEC_OID_HMAC_SHA384; + case HASH_AlgSHA512: + return SEC_OID_HMAC_SHA512; + case HASH_AlgSHA224: + return SEC_OID_HMAC_SHA224; + case HASH_AlgMD2: + case HASH_AlgMD5: + case HASH_AlgTOTAL: + default: + break; + } + return SEC_OID_UNKNOWN; +} + /* decode the algid and generate a PKCS 5 parameter from it */ NSSPKCS5PBEParameter * @@ -901,11 +1023,16 @@ sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, { SECItem *dest; SECItem *dup_src; + CK_RV crv = CKR_DEVICE_ERROR; + int error; SECStatus rv = SECFailure; - int pad; + DESContext *ctxt; + unsigned int pad; - if ((src == NULL) || (key == NULL) || (iv == NULL)) + if ((src == NULL) || (key == NULL) || (iv == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; + } dup_src = SECITEM_DupItem(src); if (dup_src == NULL) { @@ -916,7 +1043,7 @@ sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, void *dummy; dummy = CBC_PadBuffer(NULL, dup_src->data, - dup_src->len, &dup_src->len, 8 /* DES_BLOCK_SIZE */); + dup_src->len, &dup_src->len, DES_BLOCK_SIZE); if (dummy == NULL) { SECITEM_FreeItem(dup_src, PR_TRUE); return NULL; @@ -924,42 +1051,33 @@ sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, dup_src->data = (unsigned char *)dummy; } - dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); - if (dest != NULL) { - /* allocate with over flow */ - dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64); - if (dest->data != NULL) { - DESContext *ctxt; - ctxt = DES_CreateContext(key->data, iv->data, - (triple_des ? NSS_DES_EDE3_CBC : NSS_DES_CBC), - encrypt); + dest = SECITEM_AllocItem(NULL, NULL, dup_src->len + MAX_CRYPTO_EXPANSION); + if (dest == NULL) { + goto loser; + } + ctxt = DES_CreateContext(key->data, iv->data, + (triple_des ? NSS_DES_EDE3_CBC : NSS_DES_CBC), + encrypt); + if (ctxt == NULL) { + goto loser; + } + rv = (encrypt ? DES_Encrypt : DES_Decrypt)( + ctxt, dest->data, &dest->len, + dest->len, dup_src->data, dup_src->len); - if (ctxt != NULL) { - rv = (encrypt ? DES_Encrypt : DES_Decrypt)( - ctxt, dest->data, &dest->len, - dup_src->len + 64, dup_src->data, dup_src->len); + crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; + error = PORT_GetError(); - /* remove padding -- assumes 64 bit blocks */ - if ((encrypt == PR_FALSE) && (rv == SECSuccess)) { - pad = dest->data[dest->len - 1]; - if ((pad > 0) && (pad <= 8)) { - if (dest->data[dest->len - pad] != pad) { - rv = SECFailure; - PORT_SetError(SEC_ERROR_BAD_PASSWORD); - } else { - dest->len -= pad; - } - } else { - rv = SECFailure; - PORT_SetError(SEC_ERROR_BAD_PASSWORD); - } - } - DES_DestroyContext(ctxt, PR_TRUE); - } - } + /* remove padding */ + if ((encrypt == PR_FALSE) && (rv == SECSuccess)) { + crv = sftk_CheckCBCPadding(dest->data, dest->len, DES_BLOCK_SIZE, &pad); + dest->len = CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len); + PORT_SetError(CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD)); } + DES_DestroyContext(ctxt, PR_TRUE); - if (rv == SECFailure) { +loser: + if (crv != CKR_OK) { if (dest != NULL) { SECITEM_FreeItem(dest, PR_TRUE); } @@ -981,11 +1099,16 @@ sec_pkcs5_aes(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, { SECItem *dest; SECItem *dup_src; + CK_RV crv = CKR_DEVICE_ERROR; + int error; SECStatus rv = SECFailure; - int pad; + AESContext *ctxt; + unsigned int pad; - if ((src == NULL) || (key == NULL) || (iv == NULL)) + if ((src == NULL) || (key == NULL) || (iv == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; + } dup_src = SECITEM_DupItem(src); if (dup_src == NULL) { @@ -1004,41 +1127,108 @@ sec_pkcs5_aes(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, dup_src->data = (unsigned char *)dummy; } - dest = (SECItem *)PORT_ZAlloc(sizeof(SECItem)); - if (dest != NULL) { - /* allocate with over flow */ - dest->data = (unsigned char *)PORT_ZAlloc(dup_src->len + 64); - if (dest->data != NULL) { - AESContext *ctxt; - ctxt = AES_CreateContext(key->data, iv->data, - NSS_AES_CBC, encrypt, key->len, 16); + dest = SECITEM_AllocItem(NULL, NULL, dup_src->len + MAX_CRYPTO_EXPANSION); + if (dest == NULL) { + goto loser; + } + ctxt = AES_CreateContext(key->data, iv->data, NSS_AES_CBC, + encrypt, key->len, AES_BLOCK_SIZE); + if (ctxt == NULL) { + goto loser; + } + rv = (encrypt ? AES_Encrypt : AES_Decrypt)( + ctxt, dest->data, &dest->len, + dest->len, dup_src->data, dup_src->len); - if (ctxt != NULL) { - rv = (encrypt ? AES_Encrypt : AES_Decrypt)( - ctxt, dest->data, &dest->len, - dup_src->len + 64, dup_src->data, dup_src->len); + crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; + error = PORT_GetError(); - /* remove padding -- assumes 64 bit blocks */ - if ((encrypt == PR_FALSE) && (rv == SECSuccess)) { - pad = dest->data[dest->len - 1]; - if ((pad > 0) && (pad <= 16)) { - if (dest->data[dest->len - pad] != pad) { - rv = SECFailure; - PORT_SetError(SEC_ERROR_BAD_PASSWORD); - } else { - dest->len -= pad; - } - } else { - rv = SECFailure; - PORT_SetError(SEC_ERROR_BAD_PASSWORD); - } - } - AES_DestroyContext(ctxt, PR_TRUE); - } + /* remove padding */ + if ((encrypt == PR_FALSE) && (rv == SECSuccess)) { + crv = sftk_CheckCBCPadding(dest->data, dest->len, AES_BLOCK_SIZE, &pad); + dest->len = CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len); + PORT_SetError(CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD)); + } + AES_DestroyContext(ctxt, PR_TRUE); + +loser: + if (crv != CKR_OK) { + if (dest != NULL) { + SECITEM_FreeItem(dest, PR_TRUE); } + dest = NULL; + } + + if (dup_src != NULL) { + SECITEM_FreeItem(dup_src, PR_TRUE); } - if (rv == SECFailure) { + return dest; +} + +/* perform aes encryption/decryption if an error occurs, NULL is returned + */ +static SECItem * +sec_pkcs5_aes_key_wrap(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, + PRBool encrypt) +{ + SECItem *dest; + SECItem *dup_src; + CK_RV crv = CKR_DEVICE_ERROR; + int error; + SECStatus rv = SECFailure; + AESKeyWrapContext *ctxt; + unsigned int pad; + + if ((src == NULL) || (key == NULL) || (iv == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return NULL; + } + + dup_src = SECITEM_DupItem(src); + if (dup_src == NULL) { + return NULL; + } + + if (encrypt != PR_FALSE) { + void *dummy; + + dummy = CBC_PadBuffer(NULL, dup_src->data, + dup_src->len, &dup_src->len, AES_BLOCK_SIZE); + if (dummy == NULL) { + SECITEM_FreeItem(dup_src, PR_TRUE); + return NULL; + } + dup_src->data = (unsigned char *)dummy; + } + + dest = SECITEM_AllocItem(NULL, NULL, dup_src->len + MAX_CRYPTO_EXPANSION); + if (dest == NULL) { + goto loser; + } + ctxt = AESKeyWrap_CreateContext(key->data, iv->data, encrypt, + key->len); + + if (ctxt == NULL) { + goto loser; + } + rv = (encrypt ? AESKeyWrap_Encrypt : AESKeyWrap_Decrypt)( + ctxt, dest->data, &dest->len, + dest->len, dup_src->data, dup_src->len); + + crv = (rv == SECSuccess) ? CKR_OK : CKR_DEVICE_ERROR; + error = PORT_GetError(); + + /* remove padding */ + if ((encrypt == PR_FALSE) && (rv == SECSuccess)) { + crv = sftk_CheckCBCPadding(dest->data, dest->len, AES_BLOCK_SIZE, &pad); + dest->len = CT_SEL(sftk_CKRVToMask(crv), dest->len - pad, dest->len); + PORT_SetError(CT_SEL(sftk_CKRVToMask(crv), error, SEC_ERROR_BAD_PASSWORD)); + } + AESKeyWrap_DestroyContext(ctxt, PR_TRUE); + +loser: + if (crv != CKR_OK) { if (dest != NULL) { SECITEM_FreeItem(dest, PR_TRUE); } @@ -1064,6 +1254,7 @@ sec_pkcs5_rc2(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy, int pad; if ((src == NULL) || (key == NULL) || (iv == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } @@ -1138,6 +1329,7 @@ sec_pkcs5_rc4(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy_op, SECStatus rv = SECFailure; if ((src == NULL) || (key == NULL) || (iv == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } @@ -1192,6 +1384,7 @@ nsspkcs5_CipherData(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, } if ((pwitem == NULL) || (src == NULL)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } @@ -1203,6 +1396,11 @@ nsspkcs5_CipherData(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, switch (pbe_param->encAlg) { /* PKCS 5 v2 only */ + case SEC_OID_AES_128_KEY_WRAP: + case SEC_OID_AES_192_KEY_WRAP: + case SEC_OID_AES_256_KEY_WRAP: + cryptof = sec_pkcs5_aes_key_wrap; + break; case SEC_OID_AES_128_CBC: case SEC_OID_AES_192_CBC: case SEC_OID_AES_256_CBC: @@ -1240,8 +1438,8 @@ nsspkcs5_CipherData(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, * The case can only happen on decrypted of a * SEC_OID_DES_EDE3_CBD. */ - if ((dest == NULL) && (encrypt == PR_FALSE) && - (pbe_param->encAlg == SEC_OID_DES_EDE3_CBC)) { + if ((pbe_param->encAlg == SEC_OID_DES_EDE3_CBC) && + (dest == NULL) && (encrypt == PR_FALSE)) { dest = (*cryptof)(key, &iv, src, PR_FALSE, encrypt); if (update && (dest != NULL)) *update = PR_TRUE; @@ -1331,10 +1529,19 @@ nsspkcs5_CreateAlgorithmID(PLArenaPool *arena, SECOidTag algorithm, rv = SECOID_SetAlgorithmID(arena, &pkcs5v2_param.algParams, pbe_param->encAlg, pbe_param->ivLen ? &der_param : NULL); if (rv != SECSuccess) { + dummy = NULL; break; } + der_param.data = NULL; + der_param.len = 0; dummy = SEC_ASN1EncodeItem(arena, &der_param, &pkcs5v2_param, NSSPKCS5V2PBES2ParameterTemplate); + /* If the algorithm was set to some encryption oid, set it + * to PBES2 */ + if ((algorithm != SEC_OID_PKCS5_PBKDF2) && + (algorithm != SEC_OID_PKCS5_PBMAC1)) { + algorithm = SEC_OID_PKCS5_PBES2; + } break; default: break; diff --git a/lib/softoken/lowpbe.h b/lib/softoken/lowpbe.h index 2080138184..3ace0795a8 100644 --- a/lib/softoken/lowpbe.h +++ b/lib/softoken/lowpbe.h @@ -101,6 +101,7 @@ extern void nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *param); HASH_HashType HASH_FromHMACOid(SECOidTag oid); +SECOidTag HASH_HMACOidFromHash(HASH_HashType); SEC_END_PROTOS diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c index a9cf0cd9cf..c3e15c4f43 100644 --- a/lib/softoken/pkcs11c.c +++ b/lib/softoken/pkcs11c.c @@ -1613,68 +1613,6 @@ NSC_DecryptUpdate(CK_SESSION_HANDLE hSession, return CKR_OK; } -/* From ssl3con.c: Constant-time helper macro that copies the MSB of x to all - * other bits. */ -#define DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1))) -/* CK_RVToMask returns, in constant time, a mask value of - * all ones if rv == CKR_OK. Otherwise it returns zero. */ -static unsigned int -CK_RVToMask(CK_RV rv) -{ - unsigned int good; - /* rv ^ CKR_OK is zero iff rv == CKR_OK. Subtracting one results - * in the MSB being set to one iff it was zero before. */ - good = rv ^ CKR_OK; - good--; - return DUPLICATE_MSB_TO_ALL(good); -} -/* Constant-time helper macro that selects l or r depending on all-1 or all-0 - * mask m */ -#define CT_SEL(m, l, r) (((m) & (l)) | (~(m) & (r))) -/* Constant-time helper macro that returns all-1s if x is not 0; and all-0s - * otherwise. */ -#define CT_NOT_ZERO(x) (DUPLICATE_MSB_TO_ALL(((x) | (0 - x)))) - -/* sftk_CheckCBCPadding checks, in constant time, the padding validity and - * accordingly sets the pad length. */ -static CK_RV -sftk_CheckCBCPadding(CK_BYTE_PTR pLastPart, - unsigned int blockSize, unsigned int *outPadSize) -{ - PORT_Assert(outPadSize); - - unsigned int padSize = (unsigned int)pLastPart[blockSize - 1]; - - /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/ - unsigned int goodPad = DUPLICATE_MSB_TO_ALL(~(blockSize - padSize)); - /* padSize should not be 0 */ - goodPad &= CT_NOT_ZERO(padSize); - - unsigned int i; - for (i = 0; i < blockSize; i++) { - /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/ - unsigned int loopMask = DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i)); - /* Get the padding value (should be padSize) from buffer */ - unsigned int padVal = pLastPart[blockSize - 1 - i]; - /* Update goodPad only if i < padSize */ - goodPad &= CT_SEL(loopMask, ~(padVal ^ padSize), goodPad); - } - - /* If any of the final padding bytes had the wrong value, one or more - * of the lower eight bits of |goodPad| will be cleared. We AND the - * bottom 8 bits together and duplicate the result to all the bits. */ - goodPad &= goodPad >> 4; - goodPad &= goodPad >> 2; - goodPad &= goodPad >> 1; - goodPad <<= sizeof(goodPad) * 8 - 1; - goodPad = DUPLICATE_MSB_TO_ALL(goodPad); - - /* Set outPadSize to padSize or 0 */ - *outPadSize = CT_SEL(goodPad, padSize, 0); - /* Return OK if the pad is valid */ - return CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); -} - /* NSC_DecryptFinal finishes a multiple-part decryption operation. */ CK_RV NSC_DecryptFinal(CK_SESSION_HANDLE hSession, @@ -1714,9 +1652,10 @@ NSC_DecryptFinal(CK_SESSION_HANDLE hSession, crv = sftk_MapDecryptError(PORT_GetError()); } else { unsigned int padSize = 0; - crv = sftk_CheckCBCPadding(&pLastPart[outlen - context->blockSize], context->blockSize, &padSize); + crv = sftk_CheckCBCPadding(pLastPart, outlen, + context->blockSize, &padSize); /* Update pulLastPartLen, in constant time, if crv is OK */ - *pulLastPartLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulLastPartLen); + *pulLastPartLen = CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulLastPartLen); } } } @@ -1768,7 +1707,7 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, finalLen = maxoutlen; crv2 = NSC_DecryptFinal(hSession, pData, &finalLen); if (crv == CKR_OK) { - *pulDataLen = CT_SEL(CK_RVToMask(crv2), updateLen + finalLen, *pulDataLen); + *pulDataLen = CT_SEL(sftk_CKRVToMask(crv2), updateLen + finalLen, *pulDataLen); return crv2; } else { return crv; @@ -1782,9 +1721,10 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, if (rv == SECSuccess) { if (context->doPad) { unsigned int padSize = 0; - crv = sftk_CheckCBCPadding(&pData[outlen - context->blockSize], context->blockSize, &padSize); + crv = sftk_CheckCBCPadding(pData, outlen, context->blockSize, + &padSize); /* Update pulDataLen, in constant time, if crv is OK */ - *pulDataLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulDataLen); + *pulDataLen = CT_SEL(sftk_CKRVToMask(crv), outlen - padSize, *pulDataLen); } else { *pulDataLen = (CK_ULONG)outlen; } diff --git a/lib/softoken/pkcs11i.h b/lib/softoken/pkcs11i.h index 51127a32a3..e5cda130c9 100644 --- a/lib/softoken/pkcs11i.h +++ b/lib/softoken/pkcs11i.h @@ -71,6 +71,17 @@ * before we start freeing them */ #define MAX_KEY_LEN 256 /* maximum symmetric key length in bytes */ +/* From ssl3con.c: Constant-time helper macro that copies the MSB of x to all + * * other bits. */ +#define CT_DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1))) + +/* Constant-time helper macro that selects l or r depending on all-1 or all-0 + * * mask m */ +#define CT_SEL(m, l, r) (((m) & (l)) | (~(m) & (r))) +/* Constant-time helper macro that returns all-1s if x is not 0; and all-0s + * * otherwise. */ +#define CT_NOT_ZERO(x) (CT_DUPLICATE_MSB_TO_ALL(((x) | (0 - x)))) + /* * LOG2_BUCKETS_PER_SESSION_LOCK must be a prime number. * With SESSION_HASH_SIZE=1024, LOG2 can be 9, 5, 1, or 0. @@ -867,6 +878,10 @@ CK_RV sftk_MAC_Finish(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result CK_RV sftk_MAC_Reset(sftk_MACCtx *ctx); void sftk_MAC_Destroy(sftk_MACCtx *ctx, PRBool free_it); +/* constant time helpers */ +unsigned int sftk_CKRVToMask(CK_RV rv); +CK_RV sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen, + unsigned int blockSize, unsigned int *outPadSize); SEC_END_PROTOS #endif /* _PKCS11I_H_ */ diff --git a/lib/softoken/pkcs11u.c b/lib/softoken/pkcs11u.c index 1f66e6f615..804e2abacd 100644 --- a/lib/softoken/pkcs11u.c +++ b/lib/softoken/pkcs11u.c @@ -1249,7 +1249,7 @@ sftk_DeleteObject(SFTKSession *session, SFTKObject *object) SFTKTokenObject *to = sftk_narrowToTokenObject(object); PORT_Assert(to); #endif - crv = sftkdb_DestroyObject(handle, object->handle); + crv = sftkdb_DestroyObject(handle, object->handle, object->objclass); sftk_freeDB(handle); } return crv; @@ -2010,3 +2010,54 @@ sftk_narrowToTokenObject(SFTKObject *obj) { return sftk_isToken(obj->handle) ? (SFTKTokenObject *)obj : NULL; } + +/* Constant time helper functions */ + +/* sftk_CKRVToMask returns, in constant time, a mask value of + * all ones if rv == CKR_OK. Otherwise it returns zero. */ +unsigned int +sftk_CKRVToMask(CK_RV rv) +{ + PR_STATIC_ASSERT(CKR_OK == 0); + return ~CT_NOT_ZERO(rv); +} + +/* sftk_CheckCBCPadding checks, in constant time, the padding validity and + * accordingly sets the pad length. */ +CK_RV +sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen, + unsigned int blockSize, unsigned int *outPadSize) +{ + PORT_Assert(outPadSize); + + unsigned int padSize = (unsigned int)pBuf[bufLen - 1]; + + /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/ + unsigned int goodPad = CT_DUPLICATE_MSB_TO_ALL(~(blockSize - padSize)); + /* padSize should not be 0 */ + goodPad &= CT_NOT_ZERO(padSize); + + unsigned int i; + for (i = 0; i < blockSize; i++) { + /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/ + unsigned int loopMask = CT_DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i)); + /* Get the padding value (should be padSize) from buffer */ + unsigned int padVal = pBuf[bufLen - 1 - i]; + /* Update goodPad only if i < padSize */ + goodPad &= CT_SEL(loopMask, ~(padVal ^ padSize), goodPad); + } + + /* If any of the final padding bytes had the wrong value, one or more + * of the lower eight bits of |goodPad| will be cleared. We AND the + * bottom 8 bits together and duplicate the result to all the bits. */ + goodPad &= goodPad >> 4; + goodPad &= goodPad >> 2; + goodPad &= goodPad >> 1; + goodPad <<= sizeof(goodPad) * 8 - 1; + goodPad = CT_DUPLICATE_MSB_TO_ALL(goodPad); + + /* Set outPadSize to padSize or 0 */ + *outPadSize = CT_SEL(goodPad, padSize, 0); + /* Return OK if the pad is valid */ + return CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); +} diff --git a/lib/softoken/sdb.c b/lib/softoken/sdb.c index c844761001..21e6c1b82d 100644 --- a/lib/softoken/sdb.c +++ b/lib/softoken/sdb.c @@ -1161,6 +1161,19 @@ sdb_getObjectId(SDB *sdb) return CK_INVALID_HANDLE; } +CK_RV +sdb_GetNewObjectID(SDB *sdb, CK_OBJECT_HANDLE *object) +{ + CK_OBJECT_HANDLE id; + + id = sdb_getObjectId(sdb); + if (id == CK_INVALID_HANDLE) { + return CKR_DEVICE_MEMORY; /* basically we ran out of resources */ + } + *object = id; + return CKR_OK; +} + static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);"; CK_RV sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, @@ -1268,10 +1281,13 @@ sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, return error; } +/* + * Generic destroy that can destroy metadata or objects + */ static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);"; - CK_RV -sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) +sdb_destroyAnyObject(SDB *sdb, const char *table, + CK_OBJECT_HANDLE object_id, const char *string_id) { SDBPrivate *sdb_p = sdb->private; sqlite3 *sqlDB = NULL; @@ -1290,7 +1306,7 @@ sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) if (error != CKR_OK) { goto loser; } - newStr = sqlite3_mprintf(DESTROY_CMD, sdb_p->table); + newStr = sqlite3_mprintf(DESTROY_CMD, table); if (newStr == NULL) { error = CKR_HOST_MEMORY; goto loser; @@ -1299,7 +1315,12 @@ sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) sqlite3_free(newStr); if (sqlerr != SQLITE_OK) goto loser; - sqlerr = sqlite3_bind_int(stmt, 1, object_id); + if (string_id == NULL) { + sqlerr = sqlite3_bind_int(stmt, 1, object_id); + } else { + sqlerr = sqlite3_bind_text(stmt, 1, string_id, + PORT_Strlen(string_id), SQLITE_STATIC); + } if (sqlerr != SQLITE_OK) goto loser; @@ -1328,6 +1349,19 @@ sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) return error; } +CK_RV +sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) +{ + SDBPrivate *sdb_p = sdb->private; + return sdb_destroyAnyObject(sdb, sdb_p->table, object_id, NULL); +} + +CK_RV +sdb_DestroyMetaData(SDB *sdb, const char *id) +{ + return sdb_destroyAnyObject(sdb, "metaData", 0, id); +} + static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;"; /* @@ -2044,7 +2078,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, sdb_p->sqlXactDB = NULL; sdb_p->sqlXactThread = NULL; sdb->private = sdb_p; - sdb->version = 0; + sdb->version = 1; sdb->sdb_flags = inFlags | SDB_HAS_META; sdb->app_private = NULL; sdb->sdb_FindObjectsInit = sdb_FindObjectsInit; @@ -2056,12 +2090,14 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, sdb->sdb_DestroyObject = sdb_DestroyObject; sdb->sdb_GetMetaData = sdb_GetMetaData; sdb->sdb_PutMetaData = sdb_PutMetaData; + sdb->sdb_DestroyMetaData = sdb_DestroyMetaData; sdb->sdb_Begin = sdb_Begin; sdb->sdb_Commit = sdb_Commit; sdb->sdb_Abort = sdb_Abort; sdb->sdb_Reset = sdb_Reset; sdb->sdb_Close = sdb_Close; sdb->sdb_SetForkState = sdb_SetForkState; + sdb->sdb_GetNewObjectID = sdb_GetNewObjectID; if (inTransaction) { sqlerr = sqlite3_exec(sqlDB, COMMIT_CMD, NULL, 0, NULL); diff --git a/lib/softoken/sdb.h b/lib/softoken/sdb.h index 8ff254bf71..931be826ce 100644 --- a/lib/softoken/sdb.h +++ b/lib/softoken/sdb.h @@ -75,6 +75,10 @@ struct SDBStr { CK_RV(*sdb_Close) (SDB *sdb); void (*sdb_SetForkState)(PRBool forked); + CK_RV(*sdb_GetNewObjectID) + (SDB *db, CK_OBJECT_HANDLE *object); + CK_RV(*sdb_DestroyMetaData) + (SDB *db, const char *id); }; CK_RV s_open(const char *directory, const char *certPrefix, diff --git a/lib/softoken/sftkdb.c b/lib/softoken/sftkdb.c index 61cb2ce8b9..e7c9f93872 100644 --- a/lib/softoken/sftkdb.c +++ b/lib/softoken/sftkdb.c @@ -245,21 +245,44 @@ sftkdb_TypeString(SFTKDBHandle *handle) * function will fail with CKR_BUFFER_TOO_SMALL. */ static CK_RV -sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle, +sftkdb_getRawAttributeSignature(SFTKDBHandle *handle, SDB *db, + CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE type, + SECItem *signText) +{ + char id[30]; + CK_RV crv; + + sprintf(id, SFTKDB_META_SIG_TEMPLATE, + sftkdb_TypeString(handle), + (unsigned int)objectID, (unsigned int)type); + + crv = (*db->sdb_GetMetaData)(db, id, signText, NULL); + return crv; +} + +CK_RV +sftkdb_GetAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle, CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type, SECItem *signText) { - SDB *db; + SDB *db = SFTK_GET_SDB(keyHandle); + return sftkdb_getRawAttributeSignature(handle, db, objectID, type, signText); +} + +CK_RV +sftkdb_DestroyAttributeSignature(SFTKDBHandle *handle, SDB *db, + CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE type) +{ char id[30]; CK_RV crv; - db = SFTK_GET_SDB(keyHandle); - sprintf(id, SFTKDB_META_SIG_TEMPLATE, sftkdb_TypeString(handle), (unsigned int)objectID, (unsigned int)type); - crv = (*db->sdb_GetMetaData)(db, id, signText, NULL); + crv = (*db->sdb_DestroyMetaData)(db, id); return crv; } @@ -363,7 +386,10 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID, crv = CKR_USER_NOT_LOGGED_IN; continue; } - rv = sftkdb_DecryptAttribute(&handle->passwordKey, + rv = sftkdb_DecryptAttribute(handle, + &handle->passwordKey, + objectID, + ntemplate[i].type, &cipherText, &plainText); PZ_Unlock(handle->passwordLock); if (rv != SECSuccess) { @@ -389,6 +415,7 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID, /* make sure signed attributes are valid */ if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) { SECStatus rv; + CK_RV local_crv; SECItem signText; SECItem plainText; unsigned char signData[SDB_MAX_META_DATA_LEN]; @@ -396,12 +423,17 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID, signText.data = signData; signText.len = sizeof(signData); - rv = sftkdb_getAttributeSignature(handle, keyHandle, - objectID, ntemplate[i].type, &signText); - if (rv != SECSuccess) { + /* Use a local variable so that we don't clobber any already + * set error. This function returns either CKR_OK or the last + * found error in the template */ + local_crv = sftkdb_GetAttributeSignature(handle, keyHandle, + objectID, + ntemplate[i].type, + &signText); + if (local_crv != CKR_OK) { PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); template[i].ulValueLen = -1; - crv = CKR_DATA_INVALID; /* better error code? */ + crv = local_crv; continue; } @@ -421,7 +453,8 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID, continue; } - rv = sftkdb_VerifyAttribute(&keyHandle->passwordKey, + rv = sftkdb_VerifyAttribute(keyHandle, + &keyHandle->passwordKey, objectID, ntemplate[i].type, &plainText, &signText); PZ_Unlock(keyHandle->passwordLock); @@ -529,7 +562,8 @@ sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle, crv = CKR_USER_NOT_LOGGED_IN; goto loser; } - rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey, + rv = sftkdb_SignAttribute(arena, keyHandle, keyTarget, + &keyHandle->passwordKey, keyHandle->defaultIterationCount, objectID, template[i].type, &plainText, &signText); @@ -538,10 +572,9 @@ sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle, crv = CKR_GENERAL_ERROR; /* better error code here? */ goto loser; } - rv = sftkdb_PutAttributeSignature(handle, keyTarget, - objectID, template[i].type, signText); - if (rv != SECSuccess) { - crv = CKR_GENERAL_ERROR; /* better error code here? */ + crv = sftkdb_PutAttributeSignature(handle, keyTarget, objectID, + template[i].type, signText); + if (crv != CKR_OK) { goto loser; } } @@ -586,10 +619,56 @@ sftkdb_CreateObject(PLArenaPool *arena, SFTKDBHandle *handle, return crv; } +static CK_RV +sftkdb_fixupSignatures(SFTKDBHandle *handle, + SDB *db, CK_OBJECT_HANDLE oldID, CK_OBJECT_HANDLE newID, + CK_ATTRIBUTE *ptemplate, CK_ULONG max_attributes) +{ + unsigned int i; + CK_RV crv = CKR_OK; + + /* if we don't have a meta table, we didn't write any signature objects */ + if ((db->sdb_flags & SDB_HAS_META) == 0) { + return CKR_OK; + } + for (i = 0; i < max_attributes; i++) { + CK_ATTRIBUTE *att = &ptemplate[i]; + CK_ATTRIBUTE_TYPE type = att->type; + if (sftkdb_isPrivateAttribute(type)) { + /* move the signature from one object handle to another and delete + * the old entry */ + SECItem signature; + unsigned char signData[SDB_MAX_META_DATA_LEN]; + + signature.data = signData; + signature.len = sizeof(signData); + crv = sftkdb_getRawAttributeSignature(handle, db, oldID, type, + &signature); + if (crv != CKR_OK) { + /* NOTE: if we ever change our default write from AES_CBC + * to AES_KW, We'll need to change this to a continue as + * we won't need the integrity record for AES_KW */ + break; + } + crv = sftkdb_PutAttributeSignature(handle, db, newID, type, + &signature); + if (crv != CKR_OK) { + break; + } + /* now get rid of the old one */ + crv = sftkdb_DestroyAttributeSignature(handle, db, oldID, type); + if (crv != CKR_OK) { + break; + } + } + } + return crv; +} + CK_ATTRIBUTE * sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, - SFTKDBHandle *handle, CK_ULONG *pcount, - CK_RV *crv) + SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID, + SDB *db, CK_ULONG *pcount, CK_RV *crv) { unsigned int count; CK_ATTRIBUTE *template; @@ -663,8 +742,11 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, *crv = CKR_USER_NOT_LOGGED_IN; break; } - rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey, + rv = sftkdb_EncryptAttribute(arena, handle, db, + &handle->passwordKey, handle->defaultIterationCount, + objectID, + tp->type, &plainText, &cipherText); PZ_Unlock(handle->passwordLock); if (rv == SECSuccess) { @@ -1111,7 +1193,7 @@ sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, CK_RV crv; SDB *db; PRBool inTransaction = PR_FALSE; - CK_OBJECT_HANDLE id; + CK_OBJECT_HANDLE id, candidateID; *objectID = CK_INVALID_HANDLE; @@ -1136,16 +1218,21 @@ sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, return CKR_HOST_MEMORY; } - template = sftk_ExtractTemplate(arena, object, handle, &count, &crv); - if (!template) { + crv = (*db->sdb_Begin)(db); + if (crv != CKR_OK) { goto loser; } + inTransaction = PR_TRUE; - crv = (*db->sdb_Begin)(db); + crv = (*db->sdb_GetNewObjectID)(db, &candidateID); if (crv != CKR_OK) { goto loser; } - inTransaction = PR_TRUE; + + template = sftk_ExtractTemplate(arena, object, handle, candidateID, db, &count, &crv); + if (!template) { + goto loser; + } /* * We want to make the base database as free from object specific knowledge @@ -1174,16 +1261,23 @@ sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, goto loser; } if (id == CK_INVALID_HANDLE) { + *objectID = candidateID; crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count); } else { /* object already exists, modify it's attributes */ *objectID = id; + /* The object ID changed from our candidate, we need to move any + * signature attribute signatures to the new object ID. */ + crv = sftkdb_fixupSignatures(handle, db, candidateID, id, + template, count); + if (crv != CKR_OK) { + goto loser; + } crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count); } if (crv != CKR_OK) { goto loser; } - crv = (*db->sdb_Commit)(db); inTransaction = PR_FALSE; @@ -1413,7 +1507,8 @@ sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object, } CK_RV -sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID) +sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID, + CK_OBJECT_CLASS objclass) { CK_RV crv = CKR_OK; SDB *db; @@ -1423,6 +1518,7 @@ sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID) } db = SFTK_GET_SDB(handle); objectID &= SFTK_OBJ_ID_MASK; + crv = (*db->sdb_Begin)(db); if (crv != CKR_OK) { goto loser; @@ -1431,6 +1527,51 @@ sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID) if (crv != CKR_OK) { goto loser; } + /* if the database supports meta data, delete any old signatures + * that we may have added */ + if ((db->sdb_flags & SDB_HAS_META) == SDB_HAS_META) { + SDB *keydb = db; + if (handle->type == SFTK_KEYDB_TYPE) { + /* delete any private attribute signatures that might exist */ + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_VALUE); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_PRIVATE_EXPONENT); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_PRIME_1); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_PRIME_2); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_EXPONENT_1); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_EXPONENT_2); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_COEFFICIENT); + } else { + keydb = SFTK_GET_SDB(handle->peerDB); + } + /* now destroy any authenticated attributes that may exist */ + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_MODULUS); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_PUBLIC_EXPONENT); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_CERT_SHA1_HASH); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_CERT_MD5_HASH); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_TRUST_SERVER_AUTH); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_TRUST_CLIENT_AUTH); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_TRUST_EMAIL_PROTECTION); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_TRUST_CODE_SIGNING); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_TRUST_STEP_UP_APPROVED); + (void)sftkdb_DestroyAttributeSignature(handle, keydb, objectID, + CKA_NSS_OVERRIDE_EXTENSIONS); + } crv = (*db->sdb_Commit)(db); loser: if (crv != CKR_OK) { @@ -2144,6 +2285,56 @@ sftkdb_updateObjectTemplate(PLArenaPool *arena, SDB *db, return SFTKDB_ADD_OBJECT; } +static CK_RV +sftkdb_updateIntegrity(PLArenaPool *arena, SFTKDBHandle *handle, + SDB *source, CK_OBJECT_HANDLE sourceID, + SDB *target, CK_OBJECT_HANDLE targetID, + CK_ATTRIBUTE *ptemplate, CK_ULONG max_attributes) +{ + unsigned int i; + CK_RV global_crv = CKR_OK; + + /* if the target doesn't have META data, don't need to do anything */ + if ((target->sdb_flags & SDB_HAS_META) == 0) { + return CKR_OK; + } + /* if the source doesn't have meta data, then the record won't require + * integrity */ + if ((source->sdb_flags & SDB_HAS_META) == 0) { + return CKR_OK; + } + for (i = 0; i < max_attributes; i++) { + CK_ATTRIBUTE *att = &ptemplate[i]; + CK_ATTRIBUTE_TYPE type = att->type; + if (sftkdb_isPrivateAttribute(type)) { + /* copy integrity signatures associated with this record (if any) */ + SECItem signature; + unsigned char signData[SDB_MAX_META_DATA_LEN]; + CK_RV crv; + + signature.data = signData; + signature.len = sizeof(signData); + crv = sftkdb_getRawAttributeSignature(handle, source, sourceID, type, + &signature); + if (crv != CKR_OK) { + /* old databases don't have signature IDs because they are + * 3DES encrypted. Since we know not to look for integrity + * for 3DES records it's OK not to find one here. A new record + * will be created when we reencrypt using AES CBC */ + continue; + } + crv = sftkdb_PutAttributeSignature(handle, target, targetID, type, + &signature); + if (crv != CKR_OK) { + /* we had a signature in the source db, but we couldn't store + * it in the target, remember the error so we can report it. */ + global_crv = crv; + } + } + } + return global_crv; +} + #define MAX_ATTRIBUTES 500 static CK_RV sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, @@ -2156,6 +2347,7 @@ sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, SDB *source = handle->update; SDB *target = handle->db; unsigned int i; + CK_OBJECT_HANDLE newID = CK_INVALID_HANDLE; CK_RV crv; PLArenaPool *arena = NULL; @@ -2202,20 +2394,20 @@ sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, * database. */ if (!handle->updateID) { - crv = sftkdb_CreateObject(arena, handle, target, &id, + crv = sftkdb_CreateObject(arena, handle, target, &newID, ptemplate, max_attributes); } else { sftkdbUpdateStatus update_status; update_status = sftkdb_updateObjectTemplate(arena, target, - objectType, ptemplate, &max_attributes, &id); + objectType, ptemplate, &max_attributes, &newID); switch (update_status) { case SFTKDB_ADD_OBJECT: - crv = sftkdb_CreateObject(arena, handle, target, &id, + crv = sftkdb_CreateObject(arena, handle, target, &newID, ptemplate, max_attributes); break; case SFTKDB_MODIFY_OBJECT: crv = sftkdb_setAttributeValue(arena, handle, target, - id, ptemplate, max_attributes); + newID, ptemplate, max_attributes); break; case SFTKDB_DO_NOTHING: case SFTKDB_DROP_ATTRIBUTE: @@ -2223,6 +2415,13 @@ sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, } } + /* if keyDB copy any meta data hashes to target, Update for the new + * object ID */ + if (crv == CKR_OK) { + crv = sftkdb_updateIntegrity(arena, handle, source, id, target, newID, + ptemplate, max_attributes); + } + loser: if (arena) { PORT_FreeArena(arena, PR_TRUE); @@ -2251,7 +2450,6 @@ sftkdb_Update(SFTKDBHandle *handle, SECItem *key) if (handle->update == NULL) { return CKR_OK; } - /* * put the whole update under a transaction. This allows us to handle * any possible race conditions between with the updateID check. diff --git a/lib/softoken/sftkdb.h b/lib/softoken/sftkdb.h index bce39dc920..a8c289d4d7 100644 --- a/lib/softoken/sftkdb.h +++ b/lib/softoken/sftkdb.h @@ -17,7 +17,8 @@ CK_RV sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, CK_ATTRIBUTE *template, CK_ULONG count); CK_RV sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object, const CK_ATTRIBUTE *template, CK_ULONG count); -CK_RV sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id); +CK_RV sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, + CK_OBJECT_CLASS objclass); CK_RV sftkdb_closeDB(SFTKDBHandle *handle); /* keydb functions */ diff --git a/lib/softoken/sftkdbti.h b/lib/softoken/sftkdbti.h index a3a19414e0..eea52c81d3 100644 --- a/lib/softoken/sftkdbti.h +++ b/lib/softoken/sftkdbti.h @@ -39,16 +39,26 @@ struct SFTKDBHandleStr { #define SFTK_GET_SDB(handle) \ ((handle)->update ? (handle)->update : (handle)->db) -SECStatus sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, - SECItem **plainText); -SECStatus sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey, - int iterationCount, SECItem *plainText, - SECItem **cipherText); -SECStatus sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey, +SECStatus sftkdb_DecryptAttribute(SFTKDBHandle *handle, + SECItem *passKey, + CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE attrType, + SECItem *cipherText, SECItem **plainText); +SECStatus sftkdb_EncryptAttribute(PLArenaPool *arena, + SFTKDBHandle *handle, SDB *db, + SECItem *passKey, + int iterationCount, + CK_OBJECT_HANDLE id, + CK_ATTRIBUTE_TYPE attrType, + SECItem *plainText, SECItem **cipherText); +SECStatus sftkdb_SignAttribute(PLArenaPool *arena, + SFTKDBHandle *handle, SDB *db, + SECItem *passKey, int iterationCount, CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, SECItem *plainText, SECItem **sigText); -SECStatus sftkdb_VerifyAttribute(SECItem *passKey, +SECStatus sftkdb_VerifyAttribute(SFTKDBHandle *handle, + SECItem *passKey, CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, SECItem *plainText, SECItem *sigText); @@ -59,5 +69,14 @@ CK_RV sftkdb_Update(SFTKDBHandle *handle, SECItem *key); CK_RV sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget, CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type, SECItem *signText); +CK_RV sftkdb_GetAttributeSignature(SFTKDBHandle *handle, + SFTKDBHandle *keyHandle, + CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE type, + SECItem *signText); +CK_RV +sftkdb_DestroyAttributeSignature(SFTKDBHandle *handle, SDB *db, + CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE type); #endif diff --git a/lib/softoken/sftkpwd.c b/lib/softoken/sftkpwd.c index f2acf76647..b629169f80 100644 --- a/lib/softoken/sftkpwd.c +++ b/lib/softoken/sftkpwd.c @@ -260,18 +260,19 @@ sftkdb_encodeCipherText(PLArenaPool *arena, sftkCipherValue *cipherValue, * with SECITEM_FreeItem by the caller. */ SECStatus -sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, - SECItem **plain) +sftkdb_DecryptAttribute(SFTKDBHandle *handle, SECItem *passKey, + CK_OBJECT_HANDLE id, CK_ATTRIBUTE_TYPE type, + SECItem *cipherText, SECItem **plain) { SECStatus rv; sftkCipherValue cipherValue; /* First get the cipher type */ + *plain = NULL; rv = sftkdb_decodeCipherText(cipherText, &cipherValue); if (rv != SECSuccess) { goto loser; } - /* fprintf(stderr, "sftkdb_DecryptAttribute iteration: %d\n", cipherValue.param->iter); */ *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value, PR_FALSE, NULL); @@ -280,6 +281,33 @@ sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, goto loser; } + /* If we are using aes 256, we need to check authentication as well.*/ + if ((type != CKT_INVALID_TYPE) && (cipherValue.alg == SEC_OID_AES_256_CBC)) { + SECItem signature; + unsigned char signData[SDB_MAX_META_DATA_LEN]; + + /* if we get here from the old legacy db, there is clearly an + * error, don't return the plaintext */ + if (handle == NULL) { + rv = SECFailure; + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + goto loser; + } + + signature.data = signData; + signature.len = sizeof(signData); + rv = sftkdb_GetAttributeSignature(handle, handle, id, type, + &signature); + if (rv != SECSuccess) { + goto loser; + } + rv = sftkdb_VerifyAttribute(handle, passKey, CK_INVALID_HANDLE, type, + *plain, &signature); + if (rv != SECSuccess) { + goto loser; + } + } + loser: if (cipherValue.param) { nsspkcs5_DestroyPBEParameter(cipherValue.param); @@ -287,9 +315,36 @@ sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, if (cipherValue.arena) { PORT_FreeArena(cipherValue.arena, PR_FALSE); } + /* Item decrypted, but failed integrity, clear it out */ + if (*plain && rv != SECSuccess) { + SECITEM_ZfreeItem(*plain, PR_TRUE); + *plain = NULL; + } return rv; } +/* If the database can't store the integrity check, it's a non-FIPS database + * and we use the old encryption scheme for it */ +static PRBool +sftkdb_useLegacyEncryption(SFTKDBHandle *handle, SDB *db) +{ + if ((handle == NULL) || (db == NULL)) { + /* this is the case where the legacy db is calling back to us to + * encrypt or decrypt attributes inside the lower level db code. + * This is because the legacy db stored keys as pkcs #8 encrypted + * blobs rather than individual encrypted attributes */ + return PR_TRUE; + } + /* currently, only the legacy db can't store meta data, but if we + * add a new db that also can't store meta data, then it to wouldn't + * be able to do the integrity checks. In both cases use the old encryption + * algorithms. */ + if ((db->sdb_flags & SDB_HAS_META) == 0) { + return PR_TRUE; + } + return PR_FALSE; +} + /* * encrypt a block. This function returned the encrypted ciphertext which * the caller must free. If the caller provides an arena, cipherText will @@ -297,22 +352,32 @@ sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, * salt automatically. */ SECStatus -sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey, - int iterationCount, SECItem *plainText, - SECItem **cipherText) +sftkdb_EncryptAttribute(PLArenaPool *arena, SFTKDBHandle *handle, SDB *db, + SECItem *passKey, int iterationCount, + CK_OBJECT_HANDLE id, CK_ATTRIBUTE_TYPE type, + SECItem *plainText, SECItem **cipherText) { SECStatus rv; sftkCipherValue cipherValue; SECItem *cipher = NULL; NSSPKCS5PBEParameter *param = NULL; unsigned char saltData[HASH_LENGTH_MAX]; + SECItem *signature = NULL; + HASH_HashType hashType = HASH_AlgNULL; - cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; - cipherValue.salt.len = SHA1_LENGTH; + if (sftkdb_useLegacyEncryption(handle, db)) { + cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; + cipherValue.salt.len = SHA1_LENGTH; + hashType = HASH_AlgSHA1; + } else { + cipherValue.alg = SEC_OID_AES_256_CBC; + cipherValue.salt.len = SHA256_LENGTH; + hashType = HASH_AlgSHA256; + } cipherValue.salt.data = saltData; RNG_GenerateGlobalRandomBytes(saltData, cipherValue.salt.len); - param = nsspkcs5_NewParam(cipherValue.alg, HASH_AlgSHA1, &cipherValue.salt, + param = nsspkcs5_NewParam(cipherValue.alg, hashType, &cipherValue.salt, iterationCount); if (param == NULL) { rv = SECFailure; @@ -331,7 +396,26 @@ sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey, goto loser; } + /* If we are using aes 256, we need to add authentication as well */ + if ((type != CKT_INVALID_TYPE) && + (cipherValue.param->encAlg == SEC_OID_AES_256_CBC)) { + rv = sftkdb_SignAttribute(arena, handle, db, passKey, iterationCount, + CK_INVALID_HANDLE, type, plainText, + &signature); + if (rv != SECSuccess) { + goto loser; + } + rv = sftkdb_PutAttributeSignature(handle, db, id, type, + signature); + if (rv != SECSuccess) { + goto loser; + } + } + loser: + if ((arena == NULL) && signature) { + SECITEM_FreeItem(cipher, PR_TRUE); + } if (cipher) { SECITEM_FreeItem(cipher, PR_TRUE); } @@ -408,7 +492,8 @@ sftkdb_pbehash(SECOidTag sigOid, SECItem *passKey, * plainText is the plainText of the attribute. */ SECStatus -sftkdb_VerifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID, +sftkdb_VerifyAttribute(SFTKDBHandle *handle, + SECItem *passKey, CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, SECItem *plainText, SECItem *signText) { @@ -450,8 +535,9 @@ sftkdb_VerifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID, * attribute. The signText is a PKCS 5 v2 pbe. */ SECStatus -sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey, - int iterationCount, CK_OBJECT_HANDLE objectID, +sftkdb_SignAttribute(PLArenaPool *arena, SFTKDBHandle *keyDB, SDB *db, + SECItem *passKey, int iterationCount, + CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, SECItem *plainText, SECItem **signature) { @@ -860,7 +946,8 @@ sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key, const char *pw, } /* decrypt the entry value */ - rv = sftkdb_DecryptAttribute(key, value, &result); + rv = sftkdb_DecryptAttribute(keydb, key, CK_INVALID_HANDLE, + CKT_INVALID_TYPE, value, &result); if (rv != SECSuccess) { goto done; } @@ -1070,9 +1157,9 @@ sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle, SECItem plainText; plainText.data = authAttr.pValue; plainText.len = authAttr.ulValueLen; - if (sftkdb_SignAttribute(arena, newKey, iterationCount, id, - authAttr.type, &plainText, - &signText) != SECSuccess) { + if (sftkdb_SignAttribute(arena, handle, keyTarget, newKey, + iterationCount, id, authAttr.type, + &plainText, &signText) != SECSuccess) { return CKR_GENERAL_ERROR; } if (sftkdb_PutAttributeSignature(handle, keyTarget, id, authAttr.type, @@ -1127,7 +1214,8 @@ sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb, SECItem *result; plainText.data = privAttr.pValue; plainText.len = privAttr.ulValueLen; - if (sftkdb_EncryptAttribute(arena, newKey, iterationCount, + if (sftkdb_EncryptAttribute(arena, keydb, keydb->db, newKey, + iterationCount, id, privAttr.type, &plainText, &result) != SECSuccess) { return CKR_GENERAL_ERROR; } @@ -1317,8 +1405,9 @@ sftkdb_ChangePassword(SFTKDBHandle *keydb, plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING; plainText.len = SFTK_PW_CHECK_LEN; - rv = sftkdb_EncryptAttribute(NULL, &newKey, iterationCount, - &plainText, &result); + rv = sftkdb_EncryptAttribute(NULL, keydb, keydb->db, &newKey, + iterationCount, CK_INVALID_HANDLE, + CKT_INVALID_TYPE, &plainText, &result); if (rv != SECSuccess) { goto loser; } diff --git a/lib/util/pkcs11n.h b/lib/util/pkcs11n.h index 7fbfb780c2..1eea8b255e 100644 --- a/lib/util/pkcs11n.h +++ b/lib/util/pkcs11n.h @@ -138,6 +138,7 @@ /* FAKE PKCS #11 defines */ #define CKM_FAKE_RANDOM 0x80000efeUL #define CKM_INVALID_MECHANISM 0xffffffffUL +#define CKT_INVALID_TYPE 0xffffffffUL /* * NSS-defined crypto mechanisms