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