Issue #1338 - Follow-up: Also cache the most recent PBKDF1 hash

This rewrites the caching mechanism to apply to both PBKDF1 and PBKDF2
This commit is contained in:
Kai Engert 2020-01-23 13:11:09 +01:00 • committed by Roy Tam
commit a8daf97de0

View file

@ -554,50 +554,160 @@ loser:
return A; return A;
} }
struct KDFCacheItemStr {
SECItem *hash;
SECItem *salt;
SECItem *pwItem;
HASH_HashType hashType;
int iterations;
int keyLen;
};
typedef struct KDFCacheItemStr KDFCacheItem;
/* Bug 1606992 - Cache the hash result for the common case that we're /* Bug 1606992 - Cache the hash result for the common case that we're
* asked to repeatedly compute the key for the same password item, * asked to repeatedly compute the key for the same password item,
* hash, iterations and salt. */ * hash, iterations and salt. */
static PZLock *PBE_cache_lock = NULL; static struct {
static SECItem *cached_PBKDF2_item = NULL; PZLock *lock;
static HASH_HashType cached_hashType; struct {
static int cached_iterations; KDFCacheItem common;
static int cached_keyLen; int ivLen;
static SECItem *cached_salt = NULL; PRBool faulty3DES;
static SECItem *cached_pwitem = NULL; } cacheKDF1;
struct {
KDFCacheItem common;
} cacheKDF2;
} PBECache;
void void
sftk_PBELockInit(void) sftk_PBELockInit(void)
{ {
if (!PBE_cache_lock) { if (!PBECache.lock) {
PBE_cache_lock = PZ_NewLock(nssIPBECacheLock); PBECache.lock = PZ_NewLock(nssIPBECacheLock);
} }
} }
static void static void
sftk_clearPBECacheItems(void) sftk_clearPBECommonCacheItemsLocked(KDFCacheItem *item)
{ {
if (cached_PBKDF2_item) { if (item->hash) {
SECITEM_FreeItem(cached_PBKDF2_item, PR_TRUE); SECITEM_ZfreeItem(item->hash, PR_TRUE);
cached_PBKDF2_item = NULL; item->hash = NULL;
} }
if (cached_salt) { if (item->salt) {
SECITEM_FreeItem(cached_salt, PR_TRUE); SECITEM_FreeItem(item->salt, PR_TRUE);
cached_salt = NULL; item->salt = NULL;
} }
if (cached_pwitem) { if (item->pwItem) {
SECITEM_FreeItem(cached_pwitem, PR_TRUE); SECITEM_ZfreeItem(item->pwItem, PR_TRUE);
cached_pwitem = NULL; item->pwItem = NULL;
} }
} }
sftk_setPBECommonCacheItemsKDFLocked(KDFCacheItem *cacheItem,
const SECItem *hash,
const NSSPKCS5PBEParameter *pbe_param,
const SECItem *pwItem)
{
cacheItem->hash = SECITEM_DupItem(hash);
cacheItem->hashType = pbe_param->hashType;
cacheItem->iterations = pbe_param->iter;
cacheItem->keyLen = pbe_param->keyLen;
cacheItem->salt = SECITEM_DupItem(&pbe_param->salt);
cacheItem->pwItem = SECITEM_DupItem(pwItem);
}
static void
sftk_setPBECacheKDF2(const SECItem *hash,
const NSSPKCS5PBEParameter *pbe_param,
const SECItem *pwItem)
{
PZ_Lock(PBECache.lock);
sftk_clearPBECommonCacheItemsLocked(&PBECache.cacheKDF2.common);
sftk_setPBECommonCacheItemsKDFLocked(&PBECache.cacheKDF2.common,
hash, pbe_param, pwItem);
PZ_Unlock(PBECache.lock);
}
static void
sftk_setPBECacheKDF1(const SECItem *hash,
const NSSPKCS5PBEParameter *pbe_param,
const SECItem *pwItem,
PRBool faulty3DES)
{
PZ_Lock(PBECache.lock);
sftk_clearPBECommonCacheItemsLocked(&PBECache.cacheKDF1.common);
sftk_setPBECommonCacheItemsKDFLocked(&PBECache.cacheKDF1.common,
hash, pbe_param, pwItem);
PBECache.cacheKDF1.faulty3DES = faulty3DES;
PBECache.cacheKDF1.ivLen = pbe_param->ivLen;
PZ_Unlock(PBECache.lock);
}
static PRBool
sftk_comparePBECommonCacheItemLocked(const KDFCacheItem *cacheItem,
const NSSPKCS5PBEParameter *pbe_param,
const SECItem *pwItem)
{
return (cacheItem->hash &&
cacheItem->salt &&
cacheItem->pwItem &&
pbe_param->hashType == cacheItem->hashType &&
pbe_param->iter == cacheItem->iterations &&
pbe_param->keyLen == cacheItem->keyLen &&
SECITEM_ItemsAreEqual(&pbe_param->salt, cacheItem->salt) &&
SECITEM_ItemsAreEqual(pwItem, cacheItem->pwItem));
}
static SECItem *
sftk_getPBECacheKDF2(const NSSPKCS5PBEParameter *pbe_param,
const SECItem *pwItem)
{
SECItem *result = NULL;
const KDFCacheItem *cacheItem = &PBECache.cacheKDF2.common;
PZ_Lock(PBECache.lock);
if (sftk_comparePBECommonCacheItemLocked(cacheItem, pbe_param, pwItem)) {
result = SECITEM_DupItem(cacheItem->hash);
}
PZ_Unlock(PBECache.lock);
return result;
}
static SECItem *
sftk_getPBECacheKDF1(const NSSPKCS5PBEParameter *pbe_param,
const SECItem *pwItem,
PRBool faulty3DES)
{
SECItem *result = NULL;
const KDFCacheItem *cacheItem = &PBECache.cacheKDF1.common;
PZ_Lock(PBECache.lock);
if (sftk_comparePBECommonCacheItemLocked(cacheItem, pbe_param, pwItem) &&
PBECache.cacheKDF1.faulty3DES == faulty3DES &&
PBECache.cacheKDF1.ivLen == pbe_param->ivLen) {
result = SECITEM_DupItem(cacheItem->hash);
}
PZ_Unlock(PBECache.lock);
return result;
}
void void
sftk_PBELockShutdown(void) sftk_PBELockShutdown(void)
{ {
if (PBE_cache_lock) { if (PBECache.lock) {
PZ_DestroyLock(PBE_cache_lock); PZ_DestroyLock(PBECache.lock);
PBE_cache_lock = 0; PBECache.lock = 0;
} }
sftk_clearPBECacheItems(); sftk_clearPBECommonCacheItemsLocked(&PBECache.cacheKDF1.common);
sftk_clearPBECommonCacheItemsLocked(&PBECache.cacheKDF2.common);
} }
/* /*
@ -632,7 +742,11 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
hashObj = HASH_GetRawHashObject(pbe_param->hashType); hashObj = HASH_GetRawHashObject(pbe_param->hashType);
switch (pbe_param->pbeType) { switch (pbe_param->pbeType) {
case NSSPKCS5_PBKDF1: case NSSPKCS5_PBKDF1:
hash = nsspkcs5_PBKDF1Extended(hashObj, pbe_param, pwitem, faulty3DES); hash = sftk_getPBECacheKDF1(pbe_param, pwitem, faulty3DES);
if (!hash) {
hash = nsspkcs5_PBKDF1Extended(hashObj, pbe_param, pwitem, faulty3DES);
sftk_setPBECacheKDF1(hash, pbe_param, pwitem, faulty3DES);
}
if (hash == NULL) { if (hash == NULL) {
goto loser; goto loser;
} }
@ -643,34 +757,10 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem,
break; break;
case NSSPKCS5_PBKDF2: case NSSPKCS5_PBKDF2:
PZ_Lock(PBE_cache_lock); hash = sftk_getPBECacheKDF2(pbe_param, pwitem);
if (cached_PBKDF2_item) {
if (pbe_param->hashType == cached_hashType &&
pbe_param->iter == cached_iterations &&
pbe_param->keyLen == cached_keyLen &&
cached_salt &&
SECITEM_ItemsAreEqual(&pbe_param->salt, cached_salt) &&
cached_pwitem &&
SECITEM_ItemsAreEqual(pwitem, cached_pwitem)) {
hash = SECITEM_DupItem(cached_PBKDF2_item);
} else {
sftk_clearPBECacheItems();
}
}
PZ_Unlock(PBE_cache_lock);
if (!hash) { if (!hash) {
hash = nsspkcs5_PBKDF2(hashObj, pbe_param, pwitem); hash = nsspkcs5_PBKDF2(hashObj, pbe_param, pwitem);
PZ_Lock(PBE_cache_lock); sftk_setPBECacheKDF2(hash, pbe_param, pwitem);
/* ensure no other thread was quicker than us setting the cache */
if (!cached_PBKDF2_item) {
cached_PBKDF2_item = SECITEM_DupItem(hash);
cached_hashType = pbe_param->hashType;
cached_iterations = pbe_param->iter;
cached_keyLen = pbe_param->keyLen;
cached_salt = SECITEM_DupItem(&pbe_param->salt);
cached_pwitem = SECITEM_DupItem(pwitem);
}
PZ_Unlock(PBE_cache_lock);
} }
if (getIV) { if (getIV) {
PORT_Memcpy(iv->data, pbe_param->ivData, iv->len); PORT_Memcpy(iv->data, pbe_param->ivData, iv->len);