diff options
author | rrelyea%redhat.com <devnull@localhost> | 2007-08-09 22:36:20 +0000 |
---|---|---|
committer | rrelyea%redhat.com <devnull@localhost> | 2007-08-09 22:36:20 +0000 |
commit | 2536c68d5ab6ca467691176855f9e715ab68d8ea (patch) | |
tree | 6cf084c8d8aae6b5bc78f527e30abf9cd12d5c26 /security/nss/lib/softoken | |
parent | 84bcb3e77a502e07b3f92e64d6f4d9aacf804398 (diff) | |
download | nss-hg-2536c68d5ab6ca467691176855f9e715ab68d8ea.tar.gz |
Bug 391291 Implement Shared Database Integrity checks.
r=nelson (plus review from kengert)
Diffstat (limited to 'security/nss/lib/softoken')
-rw-r--r-- | security/nss/lib/softoken/legacydb/keydb.c | 29 | ||||
-rw-r--r-- | security/nss/lib/softoken/legacydb/lgdb.h | 5 | ||||
-rw-r--r-- | security/nss/lib/softoken/legacydb/lginit.c | 4 | ||||
-rw-r--r-- | security/nss/lib/softoken/legacydb/lowkeyti.h | 9 | ||||
-rw-r--r-- | security/nss/lib/softoken/lowpbe.c | 306 | ||||
-rw-r--r-- | security/nss/lib/softoken/lowpbe.h | 5 | ||||
-rw-r--r-- | security/nss/lib/softoken/padbuf.c | 13 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11c.c | 87 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11t.h | 15 | ||||
-rw-r--r-- | security/nss/lib/softoken/sdb.c | 115 | ||||
-rw-r--r-- | security/nss/lib/softoken/sdb.h | 14 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkdb.c | 664 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkdbt.h | 2 | ||||
-rw-r--r-- | security/nss/lib/softoken/softoken.h | 15 |
14 files changed, 1049 insertions, 234 deletions
diff --git a/security/nss/lib/softoken/legacydb/keydb.c b/security/nss/lib/softoken/legacydb/keydb.c index 51bb402c8..d64d9080e 100644 --- a/security/nss/lib/softoken/legacydb/keydb.c +++ b/security/nss/lib/softoken/legacydb/keydb.c @@ -1355,7 +1355,7 @@ loser: * check to see if the user has a password */ static SECStatus -nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle,SDBPasswordEntry *entry) +nsslowkey_GetPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry) { DBT checkkey; /*, checkdata; */ NSSLOWKEYDBKey *dbkey = NULL; @@ -1427,7 +1427,7 @@ loser: * check to see if the user has a password */ static SECStatus -nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle,SDBPasswordEntry *entry) +nsslowkey_PutPWCheckEntry(NSSLOWKEYDBHandle *handle,NSSLOWKEYPasswordEntry *entry) { DBT checkkey; NSSLOWKEYDBKey *dbkey = NULL; @@ -2224,33 +2224,50 @@ keydb_Close(NSSLOWKEYDBHandle *kdb) */ CK_RV -lg_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry) +lg_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2) { NSSLOWKEYDBHandle *keydb; + NSSLOWKEYPasswordEntry entry; SECStatus rv; keydb = lg_getKeyDB(sdb); if (keydb == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } - rv = nsslowkey_GetPWCheckEntry(keydb, entry); + if (PORT_Strcmp(id,"password") != 0) { + /* shouldn't happen */ + return CKR_GENERAL_ERROR; /* no extra data stored */ + } + rv = nsslowkey_GetPWCheckEntry(keydb, &entry); if (rv != SECSuccess) { return CKR_GENERAL_ERROR; } + item1->len = entry.salt.len; + PORT_Memcpy(item1->data, entry.salt.data, item1->len); + item2->len = entry.value.len; + PORT_Memcpy(item2->data, entry.value.data, item2->len); return CKR_OK; } CK_RV -lg_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry) +lg_PutMetaData(SDB *sdb, const char *id, + const SECItem *item1, const SECItem *item2) { NSSLOWKEYDBHandle *keydb; + NSSLOWKEYPasswordEntry entry; SECStatus rv; keydb = lg_getKeyDB(sdb); if (keydb == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } - rv = nsslowkey_PutPWCheckEntry(keydb, entry); + if (PORT_Strcmp(id,"password") != 0) { + /* shouldn't happen */ + return CKR_GENERAL_ERROR; /* no extra data stored */ + } + entry.salt = *item1; + entry.value = *item2; + rv = nsslowkey_PutPWCheckEntry(keydb, &entry); if (rv != SECSuccess) { return CKR_GENERAL_ERROR; } diff --git a/security/nss/lib/softoken/legacydb/lgdb.h b/security/nss/lib/softoken/legacydb/lgdb.h index 2369acd94..207b7aecf 100644 --- a/security/nss/lib/softoken/legacydb/lgdb.h +++ b/security/nss/lib/softoken/legacydb/lgdb.h @@ -189,8 +189,9 @@ CK_RV lg_Reset(SDB *sdb); CK_RV lg_Begin(SDB *sdb); CK_RV lg_Commit(SDB *sdb); CK_RV lg_Abort(SDB *sdb); -CK_RV lg_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry); -CK_RV lg_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry); +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); SEC_END_PROTOS diff --git a/security/nss/lib/softoken/legacydb/lginit.c b/security/nss/lib/softoken/legacydb/lginit.c index 060579b91..3964f6ee7 100644 --- a/security/nss/lib/softoken/legacydb/lginit.c +++ b/security/nss/lib/softoken/legacydb/lginit.c @@ -546,8 +546,8 @@ lg_init(SDB **pSdb, int flags, NSSLOWCERTCertDBHandle *certdbPtr, sdb->sdb_SetAttributeValue = lg_SetAttributeValue; sdb->sdb_CreateObject = lg_CreateObject; sdb->sdb_DestroyObject = lg_DestroyObject; - sdb->sdb_GetPWEntry = lg_GetPWEntry; - sdb->sdb_PutPWEntry = lg_PutPWEntry; + sdb->sdb_GetMetaData = lg_GetMetaData; + sdb->sdb_PutMetaData = lg_PutMetaData; sdb->sdb_Begin = lg_Begin; sdb->sdb_Commit = lg_Commit; sdb->sdb_Abort = lg_Abort; diff --git a/security/nss/lib/softoken/legacydb/lowkeyti.h b/security/nss/lib/softoken/legacydb/lowkeyti.h index 57f8105e3..7fa74da72 100644 --- a/security/nss/lib/softoken/legacydb/lowkeyti.h +++ b/security/nss/lib/softoken/legacydb/lowkeyti.h @@ -158,4 +158,13 @@ struct NSSLOWKEYPrivateKeyStr { }; typedef struct NSSLOWKEYPrivateKeyStr NSSLOWKEYPrivateKey; + +typedef struct NSSLOWKEYPasswordEntryStr NSSLOWKEYPasswordEntry; +struct NSSLOWKEYPasswordEntryStr { + SECItem salt; + SECItem value; + unsigned char data[128]; +}; + + #endif /* _LOWKEYTI_H_ */ diff --git a/security/nss/lib/softoken/lowpbe.c b/security/nss/lib/softoken/lowpbe.c index 6b4605b4e..778834156 100644 --- a/security/nss/lib/softoken/lowpbe.c +++ b/security/nss/lib/softoken/lowpbe.c @@ -74,6 +74,42 @@ static const SEC_ASN1Template NSSPKCS5PKCS12V2PBEParameterTemplate[] = { 0 } }; + +/* PKCS5 v2 */ + +struct nsspkcs5V2PBEParameterStr { + SECAlgorithmID keyParams; /* parameters of the key generation */ + SECAlgorithmID algParams; /* paramters for the encryption or mac op */ +}; + +typedef struct nsspkcs5V2PBEParameterStr nsspkcs5V2PBEParameter; +#define PBKDF2 + +#ifdef PBKDF2 +static const SEC_ASN1Template NSSPKCS5V2PBES2ParameterTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(nsspkcs5V2PBEParameter) }, + { SEC_ASN1_INLINE, offsetof(nsspkcs5V2PBEParameter, keyParams), + SECOID_AlgorithmIDTemplate }, + { SEC_ASN1_INLINE, offsetof(nsspkcs5V2PBEParameter, algParams), + SECOID_AlgorithmIDTemplate }, + { 0 } +}; + +static const SEC_ASN1Template NSSPKCS5V2PBEParameterTemplate[] = +{ + { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(NSSPKCS5PBEParameter) }, + /* this is really a choice, but since we don't understand any other + *choice, just inline it. */ + { SEC_ASN1_OCTET_STRING, offsetof(NSSPKCS5PBEParameter, salt) }, + { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, iteration) }, + { SEC_ASN1_INTEGER, offsetof(NSSPKCS5PBEParameter, keyLength) }, + { SEC_ASN1_INLINE, offsetof(NSSPKCS5PBEParameter, prfAlg), + SECOID_AlgorithmIDTemplate }, + { 0 } +}; +#endif + SECStatus nsspkcs5_HashBuf(const SECHashObject *hashObj, unsigned char *dest, unsigned char *src, int len) @@ -297,28 +333,16 @@ nsspkcs5_PBKDF1Extended(const SECHashObject *hashObj, /* * PBDKDF2 is PKCS #5 v2.0 it's currently not used by NSS */ -/* - * We This is safe because hLen for all our - * HMAC algorithms are multiples of 4. - */ static void -xorbytes(unsigned char *dest, unsigned char *src, int len) +do_xor(unsigned char *dest, unsigned char *src, int len) { -#ifdef PARANOIA + /* use byt xor, not all platforms are happy about inaligned + * integer fetches */ while (len--) { *dest = *dest ^ *src; dest++; src++; } -#else - PRUInt32 dest32 = (PRUInt32 *)dest; - PRUInt32 src32 = (PRUInt32 *)dest; - while (len -= sizeof(PRUInt32)) { - *dest32 = *dest32 ^ *src32; - dest++; - src++; - } -#endif } static SECStatus @@ -327,33 +351,31 @@ nsspkcs5_PBKFD2_F(const SECHashObject *hashobj, SECItem *pwitem, SECItem *salt, { int j; HMACContext *cx = NULL; - unsigned int hLen = hashObject->length + unsigned int hLen = hashobj->length; SECStatus rv = SECFailure; unsigned char *last = NULL; - int lastLength = salt->len + 4; + unsigned int lastLength = salt->len + 4; + unsigned int lastBufLength; cx=HMAC_Create(hashobj,pwitem->data,pwitem->len,PR_TRUE); if (cx == NULL) { goto loser; } PORT_Memset(T,0,hLen); - realLastLength= MAX(lastLength,hLen); - last = PORT_Alloc(realLastLength); + lastBufLength = PR_MAX(lastLength, hLen); + last = PORT_Alloc(lastBufLength); if (last == NULL) { goto loser; } - PORT_Memcpy(last,salt.data,salt.len); + PORT_Memcpy(last,salt->data,salt->len); last[salt->len ] = (i >> 24) & 0xff; last[salt->len+1] = (i >> 16) & 0xff; last[salt->len+2] = (i >> 8) & 0xff; last[salt->len+3] = i & 0xff; /* NOTE: we need at least one iteration to return success! */ - for (j=0; j < interations; j++) { - rv =HMAC_Begin(cx); - if (rv !=SECSuccess) { - break; - } + for (j=0; j < iterations; j++) { + HMAC_Begin(cx); HMAC_Update(cx,last,lastLength); rv =HMAC_Finish(cx,last,&lastLength,hLen); if (rv !=SECSuccess) { @@ -363,26 +385,29 @@ nsspkcs5_PBKFD2_F(const SECHashObject *hashobj, SECItem *pwitem, SECItem *salt, } loser: if (cx) { - HMAC_DestroyContext(cx, PR_TRUE); + HMAC_Destroy(cx, PR_TRUE); } if (last) { - PORT_ZFree(last,reaLastLength); + PORT_ZFree(last,lastBufLength); } return rv; } static SECItem * -nsspkcs5_PBKFD2(const SECHashObject *hashObj, NSSPKCS5PBEParameter *pbe_param, +nsspkcs5_PBKDF2(const SECHashObject *hashobj, NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem) { + int iterations = pbe_param->iter; + int bytesNeeded = pbe_param->keyLen; unsigned int dkLen = bytesNeeded; - unsigned int hLen = hashObject->length + unsigned int hLen = hashobj->length; unsigned int l = (dkLen+hLen-1) / hLen; + unsigned int i; unsigned char *rp; - SECItem *result; - SECItem *salt = pbe_param->salt; - int interations = pbe_param->iter; - int bytesNeeded = pbe_param->keyLen; + unsigned char *T = NULL; + SECItem *result = NULL;; + SECItem *salt = &pbe_param->salt; + SECStatus rv = SECFailure; result = SECITEM_AllocItem(NULL,NULL,l*hLen); if (result == NULL) { @@ -394,7 +419,7 @@ nsspkcs5_PBKFD2(const SECHashObject *hashObj, NSSPKCS5PBEParameter *pbe_param, goto loser; } - for (i=0,rp=results->data; i < l ; i++, rp +=hLen) { + for (i=0,rp=result->data; i < l ; i++, rp +=hLen) { rv = nsspkcs5_PBKFD2_F(hashobj,pwitem,salt,iterations,i,T); if (rv != SECSuccess) { break; @@ -404,10 +429,10 @@ nsspkcs5_PBKFD2(const SECHashObject *hashObj, NSSPKCS5PBEParameter *pbe_param, loser: if (T) { - PORT_ZFree(T); + PORT_ZFree(T,hLen); } if (rv != SECSuccess) { - SECITEM_FreeITEM(result,PR_TRUE); + SECITEM_FreeItem(result,PR_TRUE); result = NULL; } else { result->len = dkLen; @@ -580,7 +605,7 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, return NULL; } - if ((pbe_param->ivLen) && (iv->data == NULL)) { + if (iv && (pbe_param->ivLen) && (iv->data == NULL)) { getIV = PR_TRUE; iv->data = (unsigned char *)PORT_Alloc(pbe_param->ivLen); if (iv->data == NULL) { @@ -596,15 +621,18 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, if (hash == NULL) { goto loser; } - PORT_Assert(hash->len >= key->len+iv->len); + PORT_Assert(hash->len >= key->len+(getIV ? iv->len : 0)); if (getIV) { PORT_Memcpy(iv->data, hash->data+(hash->len - iv->len),iv->len); - } + } + break; #ifdef PBKDF2 case NSSPKCS5_PBKDF2: hash = nsspkcs5_PBKDF2(hashObj,pbe_param,pwitem); - PORT_Assert(!getIV); + if (getIV) { + PORT_Memcpy(iv->data, pbe_param->ivData, iv->len); + } break; #endif case NSSPKCS5_PKCS12_V2: @@ -715,6 +743,19 @@ finish_des: pbe_param->ivLen = 0; pbe_param->encAlg = SEC_OID_RC4; break; + +#ifdef PBKDF2 + case SEC_OID_PKCS5_PBKDF2: + case SEC_OID_PKCS5_PBES2: + case SEC_OID_PKCS5_PBMAC1: + /* everything else will be filled in by the template */ + pbe_param->ivLen = 0; + pbe_param->pbeType = NSSPKCS5_PBKDF2; + pbe_param->encAlg = SEC_OID_PKCS5_PBKDF2; + pbe_param->keyLen = 0; /* needs to be set by caller after return */ + break; +#endif + default: return SECFailure; } @@ -767,12 +808,36 @@ loser: return pbe_param; } +/* + * find the hash type needed to implement a specific HMAC. + * OID definitions are from pkcs 5 v2.0 and 2.1 + */ +HASH_HashType +HASH_FromHMACOid(SECOidTag hmac) +{ + switch (hmac) { + case SEC_OID_HMAC_SHA1: + return HASH_AlgSHA1; + case SEC_OID_HMAC_SHA256: + return HASH_AlgSHA256; + case SEC_OID_HMAC_SHA384: + return HASH_AlgSHA384; + case SEC_OID_HMAC_SHA512: + return HASH_AlgSHA512; + case SEC_OID_HMAC_SHA224: + default: + break; + } + return HASH_AlgNULL; +} + /* decode the algid and generate a PKCS 5 parameter from it */ NSSPKCS5PBEParameter * nsspkcs5_AlgidToParam(SECAlgorithmID *algid) { NSSPKCS5PBEParameter *pbe_param = NULL; + nsspkcs5V2PBEParameter pbev2_param; SECOidTag algorithm; SECStatus rv = SECFailure; @@ -801,8 +866,42 @@ nsspkcs5_AlgidToParam(SECAlgorithmID *algid) rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, NSSPKCS5PKCS12V2PBEParameterTemplate, &algid->parameters); break; +#ifdef PBKDF2 case NSSPKCS5_PBKDF2: + PORT_Memset(&pbev2_param,0, sizeof(pbev2_param)); + /* just the PBE */ + if (algorithm == SEC_OID_PKCS5_PBKDF2) { + rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, + NSSPKCS5V2PBEParameterTemplate, &algid->parameters); + } else { + /* PBE data an others */ + rv = SEC_ASN1DecodeItem(pbe_param->poolp, &pbev2_param, + NSSPKCS5V2PBES2ParameterTemplate, &algid->parameters); + if (rv != SECSuccess) { + break; + } + pbe_param->encAlg = SECOID_GetAlgorithmTag(&pbev2_param.algParams); + rv = SEC_ASN1DecodeItem(pbe_param->poolp, pbe_param, + NSSPKCS5V2PBEParameterTemplate, + &pbev2_param.keyParams.parameters); + if (rv != SECSuccess) { + break; + } + pbe_param->keyLen = DER_GetInteger(&pbe_param->keyLength); + } + /* we we are encrypting, save any iv's */ + if (algorithm == SEC_OID_PKCS5_PBES2) { + pbe_param->ivLen = pbev2_param.algParams.parameters.len; + pbe_param->ivData = pbev2_param.algParams.parameters.data; + } + pbe_param->hashType = + HASH_FromHMACOid(SECOID_GetAlgorithmTag(&pbe_param->prfAlg)); + if (pbe_param->hashType == HASH_AlgNULL) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + rv = SECFailure; + } break; +#endif } loser: @@ -853,8 +952,8 @@ sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, if(encrypt != PR_FALSE) { void *dummy; - dummy = DES_PadBuffer(NULL, dup_src->data, - dup_src->len, &dup_src->len); + dummy = CBC_PadBuffer(NULL, dup_src->data, + dup_src->len, &dup_src->len, 8 /* DES_BLOCK_SIZE */); if(dummy == NULL) { SECITEM_FreeItem(dup_src, PR_TRUE); return NULL; @@ -911,6 +1010,85 @@ sec_pkcs5_des(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, return dest; } +/* perform aes encryption/decryption if an error occurs, NULL is returned + */ +static SECItem * +sec_pkcs5_aes(SECItem *key, SECItem *iv, SECItem *src, PRBool triple_des, + PRBool encrypt) +{ + SECItem *dest; + SECItem *dup_src; + SECStatus rv = SECFailure; + int pad; + + if((src == NULL) || (key == NULL) || (iv == NULL)) + 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 *)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); + + if(ctxt != NULL) { + rv = (encrypt ? AES_Encrypt : AES_Decrypt)( + ctxt, dest->data, &dest->len, + dup_src->len + 64, dup_src->data, dup_src->len); + + /* 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); + } + } + } + + if(rv == SECFailure) { + if(dest != NULL) { + SECITEM_FreeItem(dest, PR_TRUE); + } + dest = NULL; + } + + if(dup_src != NULL) { + SECITEM_FreeItem(dup_src, PR_TRUE); + } + + return dest; +} + /* perform rc2 encryption/decryption if an error occurs, NULL is returned */ static SECItem * @@ -934,8 +1112,8 @@ sec_pkcs5_rc2(SECItem *key, SECItem *iv, SECItem *src, PRBool dummy, if(encrypt != PR_FALSE) { void *dummy; - dummy = DES_PadBuffer(NULL, dup_src->data, - dup_src->len, &dup_src->len); + dummy = CBC_PadBuffer(NULL, dup_src->data, + dup_src->len, &dup_src->len, 8 /* RC2_BLOCK_SIZE */); if(dummy == NULL) { SECITEM_FreeItem(dup_src, PR_TRUE); return NULL; @@ -1062,6 +1240,12 @@ nsspkcs5_CipherData(NSSPKCS5PBEParameter *pbe_param, SECItem *pwitem, } switch(pbe_param->encAlg) { + /* PKCS 5 v2 only */ + case SEC_OID_AES_128_CBC: + case SEC_OID_AES_192_CBC: + case SEC_OID_AES_256_CBC: + cryptof = sec_pkcs5_aes; + break; case SEC_OID_DES_EDE3_CBC: cryptof = sec_pkcs5_des; tripleDES = PR_TRUE; @@ -1125,6 +1309,8 @@ nsspkcs5_CreateAlgorithmID(PRArenaPool *arena, SECOidTag algorithm, { SECAlgorithmID *algid, *ret_algid = NULL; SECItem der_param; + nsspkcs5V2PBEParameter pkcs5v2_param; + SECStatus rv = SECFailure; void *dummy = NULL; @@ -1157,6 +1343,38 @@ nsspkcs5_CreateAlgorithmID(PRArenaPool *arena, SECOidTag algorithm, dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param, NSSPKCS5PKCS12V2PBEParameterTemplate); break; +#ifdef PBKDF2 + case NSSPKCS5_PBKDF2: + if (pbe_param->keyLength.data == NULL) { + dummy = SEC_ASN1EncodeInteger(pbe_param->poolp, + &pbe_param->keyLength, pbe_param->keyLen); + if (dummy == NULL) { + goto loser; + } + } + PORT_Memset(&pkcs5v2_param, 0, sizeof(pkcs5v2_param)); + dummy = SEC_ASN1EncodeItem(arena, &der_param, pbe_param, + NSSPKCS5V2PBEParameterTemplate); + if (dummy == NULL) { + break; + } + dummy = NULL; + rv = SECOID_SetAlgorithmID(arena, &pkcs5v2_param.keyParams, + SEC_OID_PKCS5_PBKDF2, &der_param); + if (rv != SECSuccess) { + break; + } + der_param.data = pbe_param->ivData; + der_param.len = pbe_param->ivLen; + rv = SECOID_SetAlgorithmID(arena, &pkcs5v2_param.algParams, + pbe_param->encAlg, pbe_param->ivLen ? &der_param : NULL); + if (rv != SECSuccess) { + break; + } + dummy = SEC_ASN1EncodeItem(arena, &der_param, &pkcs5v2_param, + NSSPKCS5V2PBES2ParameterTemplate); + break; +#endif default: break; } diff --git a/security/nss/lib/softoken/lowpbe.h b/security/nss/lib/softoken/lowpbe.h index 32e1e1169..974b2fa4f 100644 --- a/security/nss/lib/softoken/lowpbe.h +++ b/security/nss/lib/softoken/lowpbe.h @@ -65,13 +65,16 @@ struct NSSPKCS5PBEParameterStr { PRArenaPool *poolp; SECItem salt; /* octet string */ SECItem iteration; /* integer */ + SECItem keyLength; /* integer */ /* used locally */ int iter; int keyLen; int ivLen; + unsigned char *ivData; HASH_HashType hashType; NSSPKCS5PBEType pbeType; + SECAlgorithmID prfAlg; PBEBitGenID keyID; SECOidTag encAlg; PRBool is2KeyDES; @@ -130,6 +133,8 @@ nsspkcs5_ComputeKeyAndIV(NSSPKCS5PBEParameter *, SECItem *pwitem, extern void nsspkcs5_DestroyPBEParameter(NSSPKCS5PBEParameter *param); +HASH_HashType HASH_FromHMACOid(SECOidTag oid); + SEC_END_PROTOS #endif diff --git a/security/nss/lib/softoken/padbuf.c b/security/nss/lib/softoken/padbuf.c index c37a0469e..8c43fa287 100644 --- a/security/nss/lib/softoken/padbuf.c +++ b/security/nss/lib/softoken/padbuf.c @@ -38,15 +38,16 @@ #include "secerr.h" /* - * Prepare a buffer for DES encryption, growing to the appropriate boundary, - * filling with the appropriate padding. + * Prepare a buffer for any padded CBC encryption algorithm, growing to the + * appropriate boundary and filling with the appropriate padding. + * blockSize must be a power of 2. * * NOTE: If arena is non-NULL, we re-allocate from there, otherwise * we assume (and use) XP memory (re)allocation. */ unsigned char * -DES_PadBuffer(PRArenaPool *arena, unsigned char *inbuf, unsigned int inlen, - unsigned int *outlen) +CBC_PadBuffer(PRArenaPool *arena, unsigned char *inbuf, unsigned int inlen, + unsigned int *outlen, int blockSize) { unsigned char *outbuf; unsigned int des_len; @@ -54,11 +55,11 @@ DES_PadBuffer(PRArenaPool *arena, unsigned char *inbuf, unsigned int inlen, unsigned char des_pad_len; /* - * We need from 1 to DES_KEY_LENGTH bytes -- we *always* grow. + * We need from 1 to blockSize bytes -- we *always* grow. * The extra bytes contain the value of the length of the padding: * if we have 2 bytes of padding, then the padding is "0x02, 0x02". */ - des_len = (inlen + DES_KEY_LENGTH) & ~(DES_KEY_LENGTH - 1); + des_len = (inlen + blockSize) & ~(blockSize - 1); if (arena != NULL) { outbuf = (unsigned char*)PORT_ArenaGrow (arena, inbuf, inlen, des_len); diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c index df149a9ea..70ae8693d 100644 --- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -2597,14 +2597,20 @@ nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism, { SECItem *pbe_key = NULL, iv, pwitem; CK_PBE_PARAMS *pbe_params = NULL; + CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL; *key_length = 0; iv.data = NULL; iv.len = 0; - pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; - - pwitem.data = (unsigned char *)pbe_params->pPassword; - pwitem.len = (unsigned int)pbe_params->ulPasswordLen; + if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { + pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; + pwitem.data = (unsigned char *)pbkd2_params->pPassword; + pwitem.len = (unsigned int)pbkd2_params->ulPasswordLen; + } else { + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + pwitem.data = (unsigned char *)pbe_params->pPassword; + pwitem.len = (unsigned int)pbe_params->ulPasswordLen; + } pbe_key = nsspkcs5_ComputeKeyAndIV(pkcs5_pbe, &pwitem, &iv, faulty3DES); if (pbe_key == NULL) { return CKR_HOST_MEMORY; @@ -2616,7 +2622,7 @@ nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism, pbe_key = NULL; if (iv.data) { - if (pbe_params->pInitVector != NULL) { + if (pbe_params && pbe_params->pInitVector != NULL) { PORT_Memcpy(pbe_params->pInitVector, iv.data, iv.len); } PORT_Free(iv.data); @@ -2698,11 +2704,6 @@ loser: return crv; } - - - - - static CK_RV nsc_SetupBulkKeyGen(CK_MECHANISM_TYPE mechanism, CK_KEY_TYPE *key_type, @@ -2823,16 +2824,19 @@ nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe) *pbe = params; return CKR_OK; } + /* maybe this should be table driven? */ static CK_RV nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, - CK_KEY_TYPE *key_type) + CK_KEY_TYPE *key_type, CK_ULONG *key_length) { CK_RV crv = CKR_OK; SECOidData *oid; - CK_PBE_PARAMS *pbe_params; - NSSPKCS5PBEParameter *params; + CK_PBE_PARAMS *pbe_params = NULL; + NSSPKCS5PBEParameter *params = NULL; + CK_PKCS5_PBKD2_PARAMS *pbkd2_params = NULL; SECItem salt; + CK_ULONG iteration = 0; *pbe = NULL; @@ -2841,28 +2845,66 @@ nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, return CKR_MECHANISM_INVALID; } - pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; - salt.data = (unsigned char *)pbe_params->pSalt; - salt.len = (unsigned int)pbe_params->ulSaltLen; - - params=nsspkcs5_NewParam(oid->offset, &salt, pbe_params->ulIteration); + if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { + pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; + if (pbkd2_params->saltSource != CKZ_SALT_SPECIFIED) { + return CKR_MECHANISM_PARAM_INVALID; + } + salt.data = (unsigned char *)pbkd2_params->pSaltSourceData; + salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen; + iteration = pbkd2_params->iterations; + } else { + pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; + salt.data = (unsigned char *)pbe_params->pSalt; + salt.len = (unsigned int)pbe_params->ulSaltLen; + iteration = pbe_params->ulIteration; + } + params=nsspkcs5_NewParam(oid->offset, &salt, iteration); if (params == NULL) { return CKR_MECHANISM_INVALID; } - switch (params->encAlg) { case SEC_OID_DES_CBC: *key_type = CKK_DES; + *key_length = params->keyLen; break; case SEC_OID_DES_EDE3_CBC: *key_type = params->is2KeyDES ? CKK_DES2 : CKK_DES3; + *key_length = params->keyLen; break; case SEC_OID_RC2_CBC: *key_type = CKK_RC2; + *key_length = params->keyLen; break; case SEC_OID_RC4: *key_type = CKK_RC4; + *key_length = params->keyLen; + break; + case SEC_OID_PKCS5_PBKDF2: + /* sigh, PKCS #11 currently only defines SHA1 for the KDF hash type. + * we do the check here because this where we would handle multiple + * hash types in the future */ + if (pbkd2_params == NULL || + pbkd2_params->prf != CKP_PKCS5_PBKD2_HMAC_SHA1) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + /* key type must already be set */ + if (*key_type == CKK_INVALID_KEY_TYPE) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + /* PBKDF2 needs to calculate the key length from the other parameters + */ + if (*key_length == 0) { + *key_length = sftk_MapKeySize(*key_type); + } + if (*key_length == 0) { + crv = CKR_TEMPLATE_INCOMPLETE; + break; + } + params->keyLen = *key_length; break; default: crv = CKR_MECHANISM_INVALID; @@ -2920,6 +2962,11 @@ CK_RV NSC_GenerateKey(CK_SESSION_HANDLE hSession, key_length = *(CK_ULONG *)pTemplate[i].pValue; continue; } + /* some algorithms need keytype specified */ + if (pTemplate[i].type == CKA_KEY_TYPE) { + key_type = *(CK_ULONG *)pTemplate[i].pValue; + continue; + } crv = sftk_AddAttributeType(key,sftk_attr_expand(&pTemplate[i])); if (crv != CKR_OK) break; @@ -2982,7 +3029,7 @@ CK_RV NSC_GenerateKey(CK_SESSION_HANDLE hSession, case CKM_PBE_MD5_DES_CBC: case CKM_PBE_MD2_DES_CBC: key_gen_type = nsc_pbe; - crv = nsc_SetupPBEKeyGen(pMechanism,&pbe_param, &key_type); + crv = nsc_SetupPBEKeyGen(pMechanism,&pbe_param, &key_type, &key_length); break; case CKM_DSA_PARAMETER_GEN: key_gen_type = nsc_param; diff --git a/security/nss/lib/softoken/pkcs11t.h b/security/nss/lib/softoken/pkcs11t.h index 7f8009e40..a828743c3 100644 --- a/security/nss/lib/softoken/pkcs11t.h +++ b/security/nss/lib/softoken/pkcs11t.h @@ -661,6 +661,10 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_SHA384_RSA_PKCS_PSS 0x00000044 #define CKM_SHA512_RSA_PKCS_PSS 0x00000045 +/* CKM_SHA224 new for v2.20 amendment 3 */ +#define CKM_SHA224_RSA_PKCS 0x00000046 +#define CKM_SHA224_RSA_PKCS_PSS 0x00000047 + #define CKM_RC2_KEY_GEN 0x00000100 #define CKM_RC2_ECB 0x00000101 #define CKM_RC2_CBC 0x00000102 @@ -744,6 +748,11 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_SHA512_HMAC 0x00000271 #define CKM_SHA512_HMAC_GENERAL 0x00000272 +/* CKM_SHA224 new for v2.20 amendment 3 */ +#define CKM_SHA224 0x00000255 +#define CKM_SHA224_HMAC 0x00000256 +#define CKM_SHA224_HMAC_GENERAL 0x00000257 + /* All of the following mechanisms are new for v2.0 */ /* Note that CAST128 and CAST5 are the same algorithm */ #define CKM_CAST_KEY_GEN 0x00000300 @@ -815,6 +824,9 @@ typedef CK_ULONG CK_MECHANISM_TYPE; #define CKM_SHA384_KEY_DERIVATION 0x00000394 #define CKM_SHA512_KEY_DERIVATION 0x00000395 +/* CKM_SHA224 new for v2.20 amendment 3 */ +#define CKM_SHA224_KEY_DERIVATION 0x00000396 + #define CKM_PBE_MD2_DES_CBC 0x000003A0 #define CKM_PBE_MD5_DES_CBC 0x000003A1 #define CKM_PBE_MD5_CAST_CBC 0x000003A2 @@ -1235,6 +1247,9 @@ typedef CK_RSA_PKCS_MGF_TYPE CK_PTR CK_RSA_PKCS_MGF_TYPE_PTR; #define CKG_MGF1_SHA384 0x00000003 #define CKG_MGF1_SHA512 0x00000004 +/* v2.20 amendment 3 */ +#define CKG_MGF1_SHA224 0x00000005 + /* CK_RSA_PKCS_OAEP_SOURCE_TYPE is new for v2.10. * CK_RSA_PKCS_OAEP_SOURCE_TYPE is used to indicate the source * of the encoding parameter when formatting a message block diff --git a/security/nss/lib/softoken/sdb.c b/security/nss/lib/softoken/sdb.c index 2642a991d..a258c555d 100644 --- a/security/nss/lib/softoken/sdb.c +++ b/security/nss/lib/softoken/sdb.c @@ -282,8 +282,8 @@ struct SDBFindStr { }; -#define FIND_OBJECTS_CMD "SELECT ALL * FROM %s WHERE %s;" -#define FIND_OBJECTS_ALL_CMD "SELECT ALL * FROM %s;" +static const char FIND_OBJECTS_CMD[] = "SELECT ALL * FROM %s WHERE %s;"; +static const char FIND_OBJECTS_ALL_CMD[] = "SELECT ALL * FROM %s;"; CK_RV sdb_FindObjectsInit(SDB *sdb, const CK_ATTRIBUTE *template, CK_ULONG count, SDBFind **find) @@ -420,7 +420,7 @@ sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind) return sdb_mapSQLError(sdb_p->type, sqlerr); } -#define GET_ATTRIBUTE_CMD "SELECT ALL %s FROM %s WHERE id=$ID;" +static const char GET_ATTRIBUTE_CMD[] = "SELECT ALL %s FROM %s WHERE id=$ID;"; CK_RV sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, CK_ATTRIBUTE *template, CK_ULONG count) @@ -547,7 +547,7 @@ sdb_GetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, return crv; } -#define SET_ATTRIBUTE_CMD "UPDATE %s SET %s WHERE id=$ID;" +static const char SET_ATTRIBUTE_CMD[] = "UPDATE %s SET %s WHERE id=$ID;"; CK_RV sdb_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, const CK_ATTRIBUTE *template, CK_ULONG count) @@ -562,7 +562,7 @@ sdb_SetAttributeValue(SDB *sdb, CK_OBJECT_HANDLE object_id, CK_RV error = CKR_OK; int i; - if (sdb->sdb_flags == SDB_RDONLY) { + if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED; } @@ -697,7 +697,7 @@ sdb_getObjectId(SDB *sdb) return CK_INVALID_HANDLE; } -#define CREATE_CMD "INSERT INTO %s (id%s) VALUES($ID%s);" +static const char CREATE_CMD[] = "INSERT INTO %s (id%s) VALUES($ID%s);"; CK_RV sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, const CK_ATTRIBUTE *template, CK_ULONG count) @@ -714,7 +714,7 @@ sdb_CreateObject(SDB *sdb, CK_OBJECT_HANDLE *object_id, int retry = 0; int i; - if (sdb->sdb_flags == SDB_RDONLY) { + if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED; } @@ -801,7 +801,7 @@ loser: return error; } -#define DESTROY_CMD "DELETE FROM %s WHERE (id=$ID);" +static const char DESTROY_CMD[] = "DELETE FROM %s WHERE (id=$ID);"; CK_RV sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) { @@ -813,7 +813,7 @@ sdb_DestroyObject(SDB *sdb, CK_OBJECT_HANDLE object_id) CK_RV error = CKR_OK; int retry = 0; - if (sdb->sdb_flags == SDB_RDONLY) { + if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED; } @@ -858,7 +858,7 @@ loser: return error; } -#define BEGIN_CMD "BEGIN IMMEDIATE TRANSACTION;" +static const char BEGIN_CMD[] = "BEGIN IMMEDIATE TRANSACTION;"; /* * start a transaction. * @@ -877,7 +877,7 @@ sdb_Begin(SDB *sdb) int retry = 0; - if (sdb->sdb_flags == SDB_RDONLY) { + if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED; } @@ -957,7 +957,7 @@ sdb_complete(SDB *sdb, const char *cmd) int retry = 0; - if (sdb->sdb_flags == SDB_RDONLY) { + if ((sdb->sdb_flags & SDB_RDONLY) != 0) { return CKR_TOKEN_WRITE_PROTECTED; } @@ -1011,7 +1011,7 @@ sdb_complete(SDB *sdb, const char *cmd) return error; } -#define COMMIT_CMD "COMMIT TRANSACTION;" +static const char COMMIT_CMD[] = "COMMIT TRANSACTION;"; CK_RV sdb_Commit(SDB *sdb) { @@ -1022,7 +1022,7 @@ sdb_Commit(SDB *sdb) return crv; } -#define ROLLBACK_CMD "ROLLBACK TRANSACTION;" +static const char ROLLBACK_CMD[] = "ROLLBACK TRANSACTION;"; CK_RV sdb_Abort(SDB *sdb) { @@ -1033,9 +1033,11 @@ sdb_Abort(SDB *sdb) return crv; } -#define GET_PW_CMD "SELECT ALL * FROM password WHERE id='password';" +static int tableExists(sqlite3 *sqlDB, const char *tableName); + +static const char GET_PW_CMD[] = "SELECT ALL * FROM metaData WHERE id=$ID;"; CK_RV -sdb_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry) +sdb_GetMetaData(SDB *sdb, const char *id, SECItem *item1, SECItem *item2) { SDBPrivate *sdb_p = sdb->private; sqlite3 *sqlDB = sdb_p->sqlXactDB; @@ -1045,7 +1047,7 @@ sdb_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry) int found = 0; int retry = 0; - /* only Key databases have password entries */ + /* currently only Key databases have meta data entries */ if (sdb_p->type != SDB_KEY) { return CKR_OBJECT_HANDLE_INVALID; } @@ -1056,7 +1058,10 @@ sdb_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry) goto loser; } + /* handle 'test' versions of the sqlite db */ sqlerr = sqlite3_prepare(sqlDB, GET_PW_CMD, -1, &stmt, NULL); + if (sqlerr != SQLITE_OK) goto loser; + sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC); do { sqlerr = sqlite3_step(stmt); if (sqlerr == SQLITE_BUSY) { @@ -1064,22 +1069,22 @@ sdb_GetPWEntry(SDB *sdb, SDBPasswordEntry *entry) } if (sqlerr == SQLITE_ROW) { const char *blobData; - entry->salt.data = entry->data; - entry->salt.len = sqlite3_column_bytes(stmt, 1); - if (entry->salt.len > sizeof(entry->data)) { + item1->len = sqlite3_column_bytes(stmt, 1); + if (item1->len > SDB_MAX_META_DATA_LEN) { error = CKR_BUFFER_TOO_SMALL; continue; } blobData = sqlite3_column_blob(stmt, 1); - PORT_Memcpy(entry->salt.data,blobData, entry->salt.len); - entry->value.data = &entry->data[entry->salt.len]; - entry->value.len = sqlite3_column_bytes(stmt, 2); - if ((entry->value.len+entry->salt.len) > sizeof(entry->data)) { - error = CKR_BUFFER_TOO_SMALL; - continue; + PORT_Memcpy(item1->data,blobData, item1->len); + if (item2) { + item2->len = sqlite3_column_bytes(stmt, 2); + if (item2->len > SDB_MAX_META_DATA_LEN) { + error = CKR_BUFFER_TOO_SMALL; + continue; + } + blobData = sqlite3_column_blob(stmt, 2); + PORT_Memcpy(item2->data,blobData, item2->len); } - blobData = sqlite3_column_blob(stmt, 2); - PORT_Memcpy(entry->value.data,blobData, entry->value.len); found = 1; } } while (!sdb_done(sqlerr,&retry)); @@ -1106,13 +1111,15 @@ loser: return error; } -static int tableExists(sqlite3 *sqlDB, const char *tableName); -#define PW_CREATE_TABLE_CMD \ - "CREATE TABLE password (id PRIMARY KEY UNIQUE ON CONFLICT REPLACE, salt, value);" -#define PW_CREATE_CMD \ - "INSERT INTO password (id,salt,value) VALUES('password',$SALT,$VALUE);" +static const char PW_CREATE_TABLE_CMD[] = + "CREATE TABLE metaData (id PRIMARY KEY UNIQUE ON CONFLICT REPLACE, item1, item2);"; +static const char PW_CREATE_CMD[] = + "INSERT INTO metaData (id,item1,item2) VALUES($ID,$ITEM1,$ITEM2);"; +static const char MD_CREATE_CMD[] = + "INSERT INTO metaData (id,item1) VALUES($ID,$ITEM1);"; CK_RV -sdb_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry) +sdb_PutMetaData(SDB *sdb, const char *id, const SECItem *item1, + const SECItem *item2) { SDBPrivate *sdb_p = sdb->private; sqlite3 *sqlDB = sdb_p->sqlXactDB; @@ -1120,6 +1127,7 @@ sdb_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry) int sqlerr = SQLITE_OK; CK_RV error = CKR_OK; int retry = 0; + const char *cmd = PW_CREATE_CMD; /* only Key databases have password entries */ if (sdb_p->type != SDB_KEY) { @@ -1132,18 +1140,24 @@ sdb_PutPWEntry(SDB *sdb, SDBPasswordEntry *entry) goto loser; } - if (!tableExists(sqlDB, "password")) { + if (!tableExists(sqlDB, "metaData")) { sqlerr = sqlite3_exec(sqlDB, PW_CREATE_TABLE_CMD, NULL, 0, NULL); if (sqlerr != SQLITE_OK) goto loser; } - sqlerr = sqlite3_prepare(sqlDB, PW_CREATE_CMD, -1, &stmt, NULL); + if (item2 == NULL) { + cmd = MD_CREATE_CMD; + } + sqlerr = sqlite3_prepare(sqlDB, cmd, -1, &stmt, NULL); if (sqlerr != SQLITE_OK) goto loser; - sqlerr = sqlite3_bind_blob(stmt, 1, entry->salt.data, - entry->salt.len, SQLITE_STATIC); + sqlerr = sqlite3_bind_text(stmt, 1, id, PORT_Strlen(id), SQLITE_STATIC); if (sqlerr != SQLITE_OK) goto loser; - sqlerr = sqlite3_bind_blob(stmt, 2, entry->value.data, - entry->value.len, SQLITE_STATIC); + sqlerr = sqlite3_bind_blob(stmt, 2, item1->data, item1->len, SQLITE_STATIC); if (sqlerr != SQLITE_OK) goto loser; + if (item2) { + sqlerr = sqlite3_bind_blob(stmt, 3, item2->data, + item2->len, SQLITE_STATIC); + if (sqlerr != SQLITE_OK) goto loser; + } do { sqlerr = sqlite3_step(stmt); @@ -1171,7 +1185,7 @@ loser: return error; } -#define RESET_CMD "DROP TABLE IF EXISTS %s;" +static const char RESET_CMD[] = "DROP TABLE IF EXISTS %s;"; CK_RV sdb_Reset(SDB *sdb) { @@ -1204,7 +1218,7 @@ sdb_Reset(SDB *sdb) if (sqlerr != SQLITE_OK) goto loser; /* delete the password entry table */ - sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS password;", + sqlerr = sqlite3_exec(sqlDB, "DROP TABLE IF EXISTS metaData;", NULL, 0, NULL); loser: @@ -1241,7 +1255,7 @@ sdb_Close(SDB *sdb) * functions to support open */ -#define CHECK_TABLE_CMD "SELECT ALL * FROM %s LIMIT 0;" +static const char CHECK_TABLE_CMD[] = "SELECT ALL * FROM %s LIMIT 0;"; /* return 1 if sqlDB contains table 'tableName */ static int tableExists(sqlite3 *sqlDB, const char *tableName) { @@ -1261,11 +1275,10 @@ static int tableExists(sqlite3 *sqlDB, const char *tableName) /* * initialize a single database */ -#define INIT_CMD \ - "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)" -#define ALTER_CMD \ - "ALTER TABLE %s ADD COLUMN a%x" - +static const char INIT_CMD[] = + "CREATE TABLE %s (id PRIMARY KEY UNIQUE ON CONFLICT ABORT%s)"; +static const char ALTER_CMD[] = + "ALTER TABLE %s ADD COLUMN a%x"; CK_RV sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, @@ -1357,7 +1370,7 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, sdb_p->sqlXactThread = NULL; sdb->private = sdb_p; sdb->sdb_type = SDB_SQL; - sdb->sdb_flags = flags; + sdb->sdb_flags = flags | SDB_HAS_META; sdb->sdb_FindObjectsInit = sdb_FindObjectsInit; sdb->sdb_FindObjects = sdb_FindObjects; sdb->sdb_FindObjectsFinal = sdb_FindObjectsFinal; @@ -1365,8 +1378,8 @@ sdb_init(char *dbname, char *table, sdbDataType type, int *inUpdate, sdb->sdb_SetAttributeValue = sdb_SetAttributeValue; sdb->sdb_CreateObject = sdb_CreateObject; sdb->sdb_DestroyObject = sdb_DestroyObject; - sdb->sdb_GetPWEntry = sdb_GetPWEntry; - sdb->sdb_PutPWEntry = sdb_PutPWEntry; + sdb->sdb_GetMetaData = sdb_GetMetaData; + sdb->sdb_PutMetaData = sdb_PutMetaData; sdb->sdb_Begin = sdb_Begin; sdb->sdb_Commit = sdb_Commit; sdb->sdb_Abort = sdb_Abort; diff --git a/security/nss/lib/softoken/sdb.h b/security/nss/lib/softoken/sdb.h index 818bc4bf1..9e041f476 100644 --- a/security/nss/lib/softoken/sdb.h +++ b/security/nss/lib/softoken/sdb.h @@ -66,7 +66,6 @@ typedef struct SDBFindStr SDBFind; typedef struct SDBStr SDB; -typedef struct SDBPasswordEntryStr SDBPasswordEntry; struct SDBStr { void *private; @@ -86,8 +85,10 @@ struct SDBStr { CK_RV (*sdb_CreateObject)(SDB *sdb, CK_OBJECT_HANDLE *object, const CK_ATTRIBUTE *template, CK_ULONG count); CK_RV (*sdb_DestroyObject)(SDB *sdb, CK_OBJECT_HANDLE object); - CK_RV (*sdb_GetPWEntry)(SDB *sdb, SDBPasswordEntry *entry); - CK_RV (*sdb_PutPWEntry)(SDB *sdb, SDBPasswordEntry *entry); + CK_RV (*sdb_GetMetaData)(SDB *sdb, const char *id, + SECItem *item1, SECItem *item2); + CK_RV (*sdb_PutMetaData)(SDB *sdb, const char *id, + const SECItem *item1, const SECItem *item2); CK_RV (*sdb_Begin)(SDB *sdb); CK_RV (*sdb_Commit)(SDB *sdb); CK_RV (*sdb_Abort)(SDB *sdb); @@ -95,12 +96,6 @@ struct SDBStr { CK_RV (*sdb_Close)(SDB *sdb); }; -struct SDBPasswordEntryStr { - SECItem salt; - SECItem value; - unsigned char data[128]; -}; - CK_RV s_open(const char *directory, const char *certPrefix, const char *keyPrefix, int cert_version, int key_version, @@ -111,5 +106,6 @@ CK_RV s_shutdown(); #define SDB_RDONLY 1 #define SDB_RDWR 2 #define SDB_CREATE 4 +#define SDB_HAS_META 8 #endif diff --git a/security/nss/lib/softoken/sftkdb.c b/security/nss/lib/softoken/sftkdb.c index ebb0e5160..e079b8a96 100644 --- a/security/nss/lib/softoken/sftkdb.c +++ b/security/nss/lib/softoken/sftkdb.c @@ -64,6 +64,7 @@ #include "secdert.h" #include "prsystem.h" #include "lgglue.h" +#include "secerr.h" /* * private defines @@ -85,10 +86,18 @@ struct SFTKDBHandleStr { #define SFTK_OBJ_ID_MASK (~SFTK_OBJ_TYPE_MASK) #define SFTK_TOKEN_TYPE 0x80000000 -static SECStatus sftkdb_decrypt(SECItem *passKey, SECItem *cipherText, +static SECStatus sftkdb_decryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plainText); -static SECStatus sftkdb_encrypt(PLArenaPool *arena, SECItem *passKey, +static SECStatus sftkdb_encryptAttribute(PLArenaPool *arena, SECItem *passKey, SECItem *plainText, SECItem **cipherText); +static SECStatus sftkdb_signAttribute(PLArenaPool *arena, SECItem *passKey, + CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE attrType, + SECItem *plainText, SECItem **sigText); +static SECStatus sftkdb_verifyAttribute(SECItem *passKey, + CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE attrType, + SECItem *plainText, SECItem *sigText); /* @@ -104,7 +113,7 @@ static SECStatus sftkdb_encrypt(PLArenaPool *arena, SECItem *passKey, #define BBP 8 static PRBool -sftkdb_isULONG(CK_ATTRIBUTE_TYPE type) +sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type) { switch(type) { case CKA_CLASS: @@ -140,7 +149,7 @@ sftkdb_isULONG(CK_ATTRIBUTE_TYPE type) /* are the attributes private? */ static PRBool -sftkdb_isPrivate(CK_ATTRIBUTE_TYPE type) +sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type) { switch(type) { case CKA_VALUE: @@ -157,6 +166,42 @@ sftkdb_isPrivate(CK_ATTRIBUTE_TYPE type) return PR_FALSE; } +/* These attributes must be authenticated with an hmac. */ +static PRBool +sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type) +{ + switch(type) { + case CKA_MODULUS: + case CKA_PUBLIC_EXPONENT: + case CKA_CERT_SHA1_HASH: + case CKA_CERT_MD5_HASH: + case CKA_TRUST_SERVER_AUTH: + case CKA_TRUST_CLIENT_AUTH: + case CKA_TRUST_EMAIL_PROTECTION: + case CKA_TRUST_CODE_SIGNING: + case CKA_TRUST_STEP_UP_APPROVED: + case CKA_NSS_OVERRIDE_EXTENSIONS: + return PR_TRUE; + default: + break; + } + return PR_FALSE; +} + +/* + * convert a native ULONG to a database ulong. Database ulong's + * are all 4 byte big endian values. + */ +static void +sftk_uLong2DBULong(unsigned char *data, CK_ULONG value) +{ + int i; + + for (i=0; i < DB_ULONG_SIZE; i++) { + data[i] = (value >> (DB_ULONG_SIZE-1-i)*BBP) & 0xff; + } +} + /* * fix up the input templates. Our fixed up ints are stored in data and must * be freed by the caller. The new template must also be freed. If there are no @@ -166,7 +211,7 @@ static CK_ATTRIBUTE * sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, unsigned char **dataOut) { - int i,j; + int i; int ulongCount = 0; unsigned char *data; CK_ATTRIBUTE *ntemplate; @@ -180,7 +225,7 @@ sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, continue; } if (template[i].ulValueLen == sizeof (CK_ULONG)) { - if ( sftkdb_isULONG(template[i].type)) { + if ( sftkdb_isULONGAttribute(template[i].type)) { ulongCount++; } } @@ -211,11 +256,9 @@ sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, continue; } if (template[i].ulValueLen == sizeof (CK_ULONG)) { - if ( sftkdb_isULONG(template[i].type) ) { + if ( sftkdb_isULONGAttribute(template[i].type) ) { CK_ULONG value = *(CK_ULONG *) template[i].pValue; - for (j=0; j < DB_ULONG_SIZE; j++) { - data[j] = (value >> (DB_ULONG_SIZE-1-j)*BBP) & 0xff; - } + sftk_uLong2DBULong(data, value); ntemplate[i].pValue = data; ntemplate[i].ulValueLen = DB_ULONG_SIZE; data += DB_ULONG_SIZE; @@ -225,6 +268,66 @@ sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, return ntemplate; } +#define GET_SDB(handle) ((handle)->update ? (handle)->update : (handle)->db) + +static const char SFTKDB_META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x"; + +/* + * Some attributes are signed with an Hmac and a pbe key generated from + * the password. This signature is stored indexed by object handle and + * attribute type in the meta data table in the key database. + * + * Signature entries are indexed by the string + * sig_[cert/key]_{ObjectID}_{Attribute} + * + * This function fetches that pkcs5 signature. Caller supplies a SECItem + * pre-allocated to the appropriate size if the SECItem is too small the + * function will fail with CKR_BUFFER_TOO_SMALL. + */ +static CK_RV +sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle, + CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type, + SECItem *signText) +{ + SDB *db; + char id[30]; + CK_RV crv; + + db = GET_SDB(keyHandle); + + sprintf(id, SFTKDB_META_SIG_TEMPLATE, + handle->type == SFTK_KEYDB_TYPE ? "key":"cert", + (unsigned int)objectID, (unsigned int)type); + + crv = (*db->sdb_GetMetaData)(db, id, signText, NULL); + return crv; +} + +/* + * Some attributes are signed with an Hmac and a pbe key generated from + * the password. This signature is stored indexed by object handle and + * attribute type in the meta data table in the key database. + * + * Signature entries are indexed by the string + * sig_[cert/key]_{ObjectID}_{Attribute} + * + * This function stores that pkcs5 signature. + */ +static CK_RV +sftkdb_putAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget, + CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type, + SECItem *signText) +{ + char id[30]; + CK_RV crv; + + sprintf(id, SFTKDB_META_SIG_TEMPLATE, + handle->type == SFTK_KEYDB_TYPE ? "key":"cert", + (unsigned int)objectID, (unsigned int)type); + + crv = (*keyTarget->sdb_PutMetaData)(keyTarget, id, signText, NULL); + return crv; +} /* @@ -232,18 +335,36 @@ sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, * separate data sections for the database ULONG values. */ static CK_RV -sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_ATTRIBUTE *ntemplate, - int count, SFTKDBHandle *handle) +sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE *ntemplate, int count, SFTKDBHandle *handle) { int i,j; CK_RV crv = CKR_OK; + SFTKDBHandle *keyHandle; + PRBool checkSig = PR_TRUE; + PRBool checkEnc = PR_TRUE; + + PORT_Assert(handle); + + /* find the key handle */ + keyHandle = handle; + if (handle->type != SFTK_KEYDB_TYPE) { + checkEnc = PR_FALSE; + keyHandle = handle->peerDB; + } + + if ((keyHandle == NULL) || + ((GET_SDB(keyHandle)->sdb_flags & SDB_HAS_META) == 0) || + (keyHandle->passwordKey.data == NULL)) { + checkSig = PR_FALSE; + } for (i=0; i < count; i++) { CK_ULONG length = template[i].ulValueLen; template[i].ulValueLen = ntemplate[i].ulValueLen; /* fixup ulongs */ if (ntemplate[i].ulValueLen == DB_ULONG_SIZE) { - if (sftkdb_isULONG(template[i].type)) { + if (sftkdb_isULONGAttribute(template[i].type)) { if (template[i].pValue) { CK_ULONG value = 0; unsigned char *data; @@ -262,10 +383,15 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_ATTRIBUTE *ntemplate, template[i].ulValueLen = sizeof(CK_ULONG); } } + + /* if no data was retrieved, no need to process encrypted or signed + * attributes */ + if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) { + continue; + } + /* fixup private attributes */ - if ((handle != NULL) && (handle->type == SFTK_KEYDB_TYPE) && - (template[i].pValue != NULL) && (template[i].ulValueLen != -1) - && sftkdb_isPrivate(ntemplate[i].type)) { + if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) { /* we have a private attribute */ /* This code depends on the fact that the cipherText is bigger * than the plain text */ @@ -282,7 +408,8 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_ATTRIBUTE *ntemplate, crv = CKR_USER_NOT_LOGGED_IN; continue; } - rv = sftkdb_decrypt(&handle->passwordKey, &cipherText, &plainText); + rv = sftkdb_decryptAttribute(&handle->passwordKey, + &cipherText, &plainText); PZ_Unlock(handle->passwordLock); if (rv != SECSuccess) { PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); @@ -304,12 +431,154 @@ sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_ATTRIBUTE *ntemplate, template[i].ulValueLen = plainText->len; SECITEM_FreeItem(plainText,PR_TRUE); } + /* make sure signed attributes are valid */ + if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) { + SECStatus rv; + SECItem signText; + SECItem plainText; + unsigned char signData[SDB_MAX_META_DATA_LEN]; + + signText.data = signData; + signText.len = sizeof(signData); + + rv = sftkdb_getAttributeSignature(handle, keyHandle, + objectID, ntemplate[i].type, &signText); + if (rv != SECSuccess) { + PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); + template[i].ulValueLen = -1; + crv = CKR_DATA_INVALID; /* better error code? */ + continue; + } + + plainText.data = ntemplate[i].pValue; + plainText.len = ntemplate[i].ulValueLen; + + /* + * we do a second check holding the lock just in case the user + * loggout while we were trying to get the signature. + */ + PZ_Lock(keyHandle->passwordLock); + if (keyHandle->passwordKey.data == NULL) { + /* if we are no longer logged in, no use checking the other + * Signatures either. */ + checkSig = PR_FALSE; + PZ_Unlock(keyHandle->passwordLock); + continue; + } + + rv = sftkdb_verifyAttribute(&keyHandle->passwordKey, + objectID, ntemplate[i].type, + &plainText, &signText); + PZ_Unlock(keyHandle->passwordLock); + if (rv != SECSuccess) { + PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); + template[i].ulValueLen = -1; + crv = CKR_SIGNATURE_INVALID; /* better error code? */ + } + /* This Attribute is fine */ + } } return crv; } +/* + * Some attributes are signed with an HMAC and a pbe key generated from + * the password. This signature is stored indexed by object handle and + * + * Those attributes are: + * 1) Trust object hashes and trust values. + * 2) public key values. + * + * Certs themselves are considered properly authenticated by virtue of their + * signature, or their matching hash with the trust object. + * + * These signature is only checked for objects coming from shared databases. + * Older dbm style databases have such no signature checks. HMACs are also + * only checked when the token is logged in, as it requires a pbe generated + * from the password. + * + * Tokens which have no key database (and therefore no master password) do not + * have any stored signature values. Signature values are stored in the key + * database, since the signature data is tightly coupled to the key database + * password. + * + * This function takes a template of attributes that were either created or + * modified. These attributes are checked to see if the need to be signed. + * If they do, then this function signs the attributes and writes them + * to the meta data store. + * + * This function can fail if there are attributes that must be signed, but + * the token is not logged in. + * + * The caller is expected to abort any transaction he was in in the + * event of a failure of this function. + */ static CK_RV -sftkdb_CreateObject(SFTKDBHandle *handle, SDB *db, CK_OBJECT_HANDLE *objectID, +sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle, + PRBool mayBeUpdateDB, + CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE *template, + CK_ULONG count) +{ + int i; + SFTKDBHandle *keyHandle = handle; + SDB *keyTarget = NULL; + + PORT_Assert(handle); + + if (handle->type != SFTK_KEYDB_TYPE) { + keyHandle = handle->peerDB; + } + + /* no key DB defined? then no need to sign anything */ + if (keyHandle == NULL) { + return CKR_OK; + } + + /* When we are in a middle of an update, we have an update database set, + * but we want to write to the real database. The bool mayBeUpdateDB is + * set to TRUE if it's possible that we want to write an update database + * rather than a primary */ + keyTarget = (mayBeUpdateDB && keyHandle->update) ? + keyHandle->update : keyHandle->db; + + /* skip the the database does not support meta data */ + if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) { + return CKR_OK; + } + + for (i=0; i < count; i ++) { + if (sftkdb_isAuthenticatedAttribute(template[i].type)) { + SECStatus rv; + SECItem *signText; + SECItem plainText; + + plainText.data = template[i].pValue; + plainText.len = template[i].ulValueLen; + PZ_Lock(keyHandle->passwordLock); + if (keyHandle->passwordKey.data == NULL) { + PZ_Unlock(keyHandle->passwordLock); + return CKR_USER_NOT_LOGGED_IN; + } + rv = sftkdb_signAttribute(arena, &keyHandle->passwordKey, + objectID, template[i].type, + &plainText, &signText); + PZ_Unlock(keyHandle->passwordLock); + if (rv != SECSuccess) { + return CKR_GENERAL_ERROR; /* better error code here? */ + } + rv = sftkdb_putAttributeSignature(handle, keyTarget, + objectID, template[i].type, signText); + if (rv != SECSuccess) { + return CKR_GENERAL_ERROR; /* better error code here? */ + } + } + } + return CKR_OK; +} + +static CK_RV +sftkdb_CreateObject(PRArenaPool *arena, SFTKDBHandle *handle, + SDB *db, CK_OBJECT_HANDLE *objectID, CK_ATTRIBUTE *template, CK_ULONG count) { PRBool inTransaction = PR_FALSE; @@ -324,6 +593,11 @@ sftkdb_CreateObject(SFTKDBHandle *handle, SDB *db, CK_OBJECT_HANDLE *objectID, if (crv != CKR_OK) { goto loser; } + crv = sftk_signTemplate(arena, handle, (db == handle->update), + *objectID, template, count); + if (crv != CKR_OK) { + goto loser; + } crv = (*db->sdb_Commit)(db); inTransaction = PR_FALSE; @@ -339,6 +613,7 @@ loser: return crv; } + CK_ATTRIBUTE * sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, SFTKDBHandle *handle,CK_ULONG *pcount, @@ -348,6 +623,7 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, CK_ATTRIBUTE *template; int i, templateIndex; SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object); + PRBool doEnc = PR_TRUE; *crv = CKR_OK; @@ -356,6 +632,12 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, return NULL; } + PORT_Assert(handle); + /* find the key handle */ + if (handle->type != SFTK_KEYDB_TYPE) { + doEnc = PR_FALSE; + } + PZ_Lock(sessObject->attributeLock); count = 0; for (i=0; i < sessObject->hashSize; i++) { @@ -380,7 +662,7 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, /* fixup ULONG s */ if ((tp->ulValueLen == sizeof (CK_ULONG)) && - (sftkdb_isULONG(tp->type)) ) { + (sftkdb_isULONGAttribute(tp->type)) ) { CK_ULONG value = *(CK_ULONG *) tp->pValue; unsigned char *data; int j; @@ -398,8 +680,7 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, } /* encrypt private attributes */ - if ((handle != NULL) && (handle->type == SFTK_KEYDB_TYPE) && - sftkdb_isPrivate(tp->type)) { + if (doEnc && sftkdb_isPrivateAttribute(tp->type)) { /* we have a private attribute */ SECItem *cipherText; @@ -414,8 +695,8 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, *crv = CKR_USER_NOT_LOGGED_IN; break; } - rv = sftkdb_encrypt(arena, &handle->passwordKey, &plainText, - &cipherText); + rv = sftkdb_encryptAttribute(arena, &handle->passwordKey, + &plainText, &cipherText); PZ_Unlock(handle->passwordLock); if (rv == SECSuccess) { tp->pValue = cipherText->data; @@ -441,8 +722,6 @@ sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, } -#define GET_SDB(handle) ((handle)->update ? (handle)->update : (handle)->db) - CK_RV sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, CK_OBJECT_HANDLE *objectID) @@ -470,7 +749,7 @@ sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, goto loser; } - crv = sftkdb_CreateObject(handle, db, objectID, template, count); + crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count); loser: if (arena) { @@ -550,7 +829,7 @@ CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find) } CK_RV -sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, +sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE *template, CK_ULONG count) { CK_RV crv,crv2; @@ -600,10 +879,11 @@ sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, if (ntemplate == NULL) { return CKR_HOST_MEMORY; } - object_id &= SFTK_OBJ_ID_MASK; - crv = (*db->sdb_GetAttributeValue)(db, object_id, + objectID &= SFTK_OBJ_ID_MASK; + crv = (*db->sdb_GetAttributeValue)(db, objectID, ntemplate, count); - crv2 = sftkdb_fixupTemplateOut(template, ntemplate, count, handle); + crv2 = sftkdb_fixupTemplateOut(template, objectID, ntemplate, + count, handle); if (crv == CKR_OK) crv = crv2; if (data) { PORT_Free(ntemplate); @@ -614,12 +894,13 @@ sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, } CK_RV -sftkdb_SetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, +sftkdb_SetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template, CK_ULONG count) { CK_RV crv = CKR_OK; CK_ATTRIBUTE *ntemplate; unsigned char *data = NULL; + PLArenaPool *arena = NULL; SDB *db; if (handle == NULL) { @@ -631,17 +912,27 @@ sftkdb_SetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id, if (count == 0) { return CKR_OK; } + ntemplate = sftkdb_fixupTemplateIn(template, count, &data); if (ntemplate == NULL) { return CKR_HOST_MEMORY; } - object_id &= SFTK_OBJ_ID_MASK; + + arena = PORT_NewArena(256); + if (arena == NULL) { + return CKR_HOST_MEMORY; + } + + objectID &= SFTK_OBJ_ID_MASK; crv = (*db->sdb_Begin)(db); if (crv != CKR_OK) { goto loser; } - crv = (*db->sdb_SetAttributeValue)(db, object_id, - ntemplate, count); + crv = (*db->sdb_SetAttributeValue)(db, objectID, ntemplate, count); + if (crv != CKR_OK) { + goto loser; + } + crv = sftk_signTemplate(arena, handle, PR_TRUE, objectID, ntemplate, count); if (crv != CKR_OK) { goto loser; } @@ -654,11 +945,12 @@ loser: PORT_Free(ntemplate); PORT_Free(data); } + PORT_FreeArena(arena, PR_FALSE); return crv; } CK_RV -sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id) +sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID) { CK_RV crv = CKR_OK; SDB *db; @@ -667,12 +959,12 @@ sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id) return CKR_TOKEN_WRITE_PROTECTED; } db = GET_SDB(handle); - object_id &= SFTK_OBJ_ID_MASK; + objectID &= SFTK_OBJ_ID_MASK; crv = (*db->sdb_Begin)(db); if (crv != CKR_OK) { goto loser; } - crv = (*db->sdb_DestroyObject)(db, object_id); + crv = (*db->sdb_DestroyObject)(db, objectID); if (crv != CKR_OK) { goto loser; } @@ -1432,7 +1724,7 @@ loser: * value will be based to a pkcs5 pbe function before it is used * in an actual encryption */ static SECStatus -sftkdb_passwordToKey(SFTKDBHandle *keydb, SDBPasswordEntry *entry, +sftkdb_passwordToKey(SFTKDBHandle *keydb, SECItem *salt, const char *pw, SECItem *key) { SHA1Context *cx = NULL; @@ -1449,8 +1741,8 @@ sftkdb_passwordToKey(SFTKDBHandle *keydb, SDBPasswordEntry *entry, goto loser; } SHA1_Begin(cx); - if (entry && entry->salt.data ) { - SHA1_Update(cx, entry->salt.data, entry->salt.len); + if (salt && salt->data ) { + SHA1_Update(cx, salt->data, salt->len); } SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw)); SHA1_End(cx, key->data, &key->len, key->len); @@ -1621,7 +1913,7 @@ loser: * with SECITEM_FreeItem by the caller. */ static SECStatus -sftkdb_decrypt(SECItem *passKey, SECItem *cipherText, SECItem **plain) +sftkdb_decryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plain) { SECStatus rv; sftkCipherValue cipherValue; @@ -1658,8 +1950,8 @@ loser: * salt automatically. */ static SECStatus -sftkdb_encrypt(PLArenaPool *arena, SECItem *passKey, SECItem *plainText, - SECItem **cipherText) +sftkdb_encryptAttribute(PLArenaPool *arena, SECItem *passKey, + SECItem *plainText, SECItem **cipherText) { SECStatus rv; sftkCipherValue cipherValue; @@ -1700,6 +1992,188 @@ loser: return rv; } +/* + * use the password and the pbe parameters to generate an HMAC for the + * given plain text data. This is used by sftkdb_verifyAttribute and + * sftkdb_signAttribute. Signature is returned in signData. The caller + * must preallocate the space in the secitem. + */ +static SECStatus +sftkdb_pbehash(SECOidTag sigOid, SECItem *passKey, + NSSPKCS5PBEParameter *param, + CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, + SECItem *plainText, SECItem *signData) +{ + SECStatus rv = SECFailure; + SECItem *key = NULL; + HMACContext *hashCx = NULL; + HASH_HashType hashType = HASH_AlgNULL; + const SECHashObject *hashObj; + unsigned char addressData[DB_ULONG_SIZE]; + + hashType = HASH_FromHMACOid(param->encAlg); + if (hashType == HASH_AlgNULL) { + PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); + return SECFailure; + } + + hashObj = HASH_GetRawHashObject(hashType); + if (hashObj == NULL) { + goto loser; + } + + key = nsspkcs5_ComputeKeyAndIV(param, passKey, NULL, PR_FALSE); + if (!key) { + goto loser; + } + + hashCx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE); + if (!hashCx) { + goto loser; + } + HMAC_Begin(hashCx); + /* Tie this value to a particular object. This is most important for + * the trust attributes, where and attacker could copy a value for + * 'validCA' from another cert in the database */ + sftk_uLong2DBULong(addressData, objectID); + HMAC_Update(hashCx, addressData, DB_ULONG_SIZE); + sftk_uLong2DBULong(addressData, attrType); + HMAC_Update(hashCx, addressData, DB_ULONG_SIZE); + + HMAC_Update(hashCx, plainText->data, plainText->len); + rv = HMAC_Finish(hashCx, signData->data, &signData->len, signData->len); + +loser: + if (hashCx) { + HMAC_Destroy(hashCx, PR_TRUE); + } + if (key) { + SECITEM_FreeItem(key,PR_TRUE); + } + return rv; +} + +/* + * Use our key to verify a signText block from the database matches + * the plainText from the database. The signText is a PKCS 5 v2 pbe. + * plainText is the plainText of the attribute. + */ +static SECStatus +sftkdb_verifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID, + CK_ATTRIBUTE_TYPE attrType, + SECItem *plainText, SECItem *signText) +{ + SECStatus rv; + sftkCipherValue signValue; + SECItem signature; + unsigned char signData[HASH_LENGTH_MAX]; + + + /* First get the cipher type */ + rv = sftkdb_decodeCipherText(signText, &signValue); + if (rv != SECSuccess) { + goto loser; + } + signature.data = signData; + signature.len = sizeof(signData); + + rv = sftkdb_pbehash(signValue.alg, passKey, signValue.param, + objectID, attrType, plainText, &signature); + if (rv != SECSuccess) { + goto loser; + } + if (SECITEM_CompareItem(&signValue.value,&signature) != 0) { + PORT_SetError(SEC_ERROR_BAD_SIGNATURE); + rv = SECFailure; + } + +loser: + if (signValue.param) { + nsspkcs5_DestroyPBEParameter(signValue.param); + } + if (signValue.arena) { + PORT_FreeArena(signValue.arena,PR_FALSE); + } + return rv; +} + +/* + * Use our key to create a signText block the plain text of an + * attribute. The signText is a PKCS 5 v2 pbe. + */ +static SECStatus +sftkdb_signAttribute(PLArenaPool *arena, SECItem *passKey, + CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, + SECItem *plainText, SECItem **signature) +{ + SECStatus rv; + sftkCipherValue signValue; + NSSPKCS5PBEParameter *param = NULL; + unsigned char saltData[HASH_LENGTH_MAX]; + unsigned char signData[HASH_LENGTH_MAX]; + SECOidTag hmacAlg = SEC_OID_HMAC_SHA256; /* hash for authentication */ + SECOidTag prfAlg = SEC_OID_HMAC_SHA256; /* hash for pb key generation */ + HASH_HashType prfType; + unsigned int hmacLength; + unsigned int prfLength; + + /* this code allows us to fetch the lengths and hashes on the fly + * by simply changing the OID above */ + prfType = HASH_FromHMACOid(prfAlg); + PORT_Assert(prfType != HASH_AlgNULL); + prfLength = HASH_GetRawHashObject(prfType)->length; + PORT_Assert(prfLength <= HASH_LENGTH_MAX); + + hmacLength = HASH_GetRawHashObject(HASH_FromHMACOid(hmacAlg))->length; + PORT_Assert(hmacLength <= HASH_LENGTH_MAX); + + /* initialize our CipherValue structure */ + signValue.alg = SEC_OID_PKCS5_PBMAC1; + signValue.salt.len = prfLength; + signValue.salt.data = saltData; + signValue.value.data = signData; + signValue.value.len = hmacLength; + RNG_GenerateGlobalRandomBytes(saltData,prfLength); + + /* initialize our pkcs5 paramter */ + param = nsspkcs5_NewParam(signValue.alg, &signValue.salt, 1); + if (param == NULL) { + rv = SECFailure; + goto loser; + } + param->keyID = pbeBitGenIntegrityKey; + /* set the PKCS 5 v2 parameters, not extractable from the + * data passed into nsspkcs5_NewParam */ + param->encAlg = hmacAlg; + param->hashType = prfType; + param->keyLen = hmacLength; + rv = SECOID_SetAlgorithmID(param->poolp, ¶m->prfAlg, prfAlg, NULL); + if (rv != SECSuccess) { + goto loser; + } + + + /* calculate the mac */ + rv = sftkdb_pbehash(signValue.alg, passKey, param, objectID, attrType, + plainText, &signValue.value); + if (rv != SECSuccess) { + goto loser; + } + signValue.param = param; + + /* write it out */ + rv = sftkdb_encodeCipherText(arena, &signValue, signature); + if (rv != SECSuccess) { + goto loser; + } + +loser: + if (param) { + nsspkcs5_DestroyPBEParameter(param); + } + return rv; +} + /* * stub files for legacy db's to be able to encrypt and decrypt @@ -1733,7 +2207,7 @@ sftkdb_encrypt_stub(PRArenaPool *arena, SDB *sdb, SECItem *plainText, return SECFailure; } - rv = sftkdb_encrypt(arena, + rv = sftkdb_encryptAttribute(arena, handle->newKey?handle->newKey:&handle->passwordKey, plainText, cipherText); PZ_Unlock(handle->passwordLock); @@ -1771,7 +2245,7 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText) /* PORT_SetError */ return SECFailure; } - rv = sftkdb_decrypt(&handle->passwordKey, cipherText, plainText); + rv = sftkdb_decryptAttribute(&handle->passwordKey, cipherText, plainText); PZ_Unlock(handle->passwordLock); return rv; @@ -1781,8 +2255,8 @@ sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText) * safely swith the passed in key for the one caches in the keydb handle * * A key attached to the handle tells us the the token is logged in. - * We can used the key attached to the handle in sftkdb_encrypt - * and sftkdb_decrypt calls. + * We can used the key attached to the handle in sftkdb_encryptAttribute + * and sftkdb_decryptAttribute calls. */ static void sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey) @@ -1831,11 +2305,11 @@ static const CK_ATTRIBUTE_TYPE known_attributes[] = { CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE, CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES, - CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL, - CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP, - CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES, - CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED, - CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC, + CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL, + CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP, + CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES, + CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED, + CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC, CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION, CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT, CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN, @@ -1919,12 +2393,18 @@ sftkdb_copyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, SECItem *key) SDB *target = handle->db; int i; CK_RV crv; + PLArenaPool *arena = NULL; + + arena = PORT_NewArena(256); + if (arena == NULL) { + return CKR_HOST_MEMORY; + } ptemplate = &template[0]; id &= SFTK_OBJ_ID_MASK; crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes); if (crv == CKR_BUFFER_TOO_SMALL) { - ptemplate = PORT_NewArray(CK_ATTRIBUTE, max_attributes); + ptemplate = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, max_attributes); if (ptemplate == NULL) { crv = CKR_HOST_MEMORY; } else { @@ -1937,7 +2417,7 @@ sftkdb_copyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, SECItem *key) } for (i=0; i < max_attributes; i++) { - ptemplate[i].pValue = PORT_Alloc(ptemplate[i].ulValueLen); + ptemplate[i].pValue = PORT_ArenaAlloc(arena,ptemplate[i].ulValueLen); if (ptemplate[i].pValue == NULL) { crv = CKR_HOST_MEMORY; goto loser; @@ -1949,22 +2429,12 @@ sftkdb_copyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, SECItem *key) goto loser; } - crv = sftkdb_CreateObject(handle, target, &id, ptemplate, max_attributes); - if (ptemplate && ptemplate != template) { - PORT_Free(ptemplate); - } + crv = sftkdb_CreateObject(arena, handle, target, &id, + ptemplate, max_attributes); loser: - if (ptemplate) { - for (i=0; i < max_attributes; i++) { - if (ptemplate[i].pValue) { - PORT_Memset(ptemplate[i].pValue, 0, ptemplate[i].ulValueLen); - PORT_Free(ptemplate[i].pValue); - } - } - if (ptemplate != template) { - PORT_Free(ptemplate); - } + if (arena) { + PORT_FreeArena(arena,PR_TRUE); } return crv; } @@ -2009,17 +2479,24 @@ sftkdb_update(SFTKDBHandle *handle, SECItem *key) loser: /* update Meta data - even if we didn't update objects */ if (handle->type == SFTK_KEYDB_TYPE) { - SDBPasswordEntry entry; + SECItem item1, item2; + unsigned char data1[SDB_MAX_META_DATA_LEN]; + unsigned char data2[SDB_MAX_META_DATA_LEN]; + crv = (*handle->db->sdb_Begin)(handle->db); if (crv != CKR_OK) { goto loser2; } inTransaction = PR_TRUE; - crv = (*handle->update->sdb_GetPWEntry)(handle->update, & entry); + item1.data = data1; + item2.data = data2; + crv = (*handle->update->sdb_GetMetaData)(handle->update, "password", + &item1, &item2); if (crv != CKR_OK) { goto loser2; } - crv = (*handle->db->sdb_PutPWEntry)(handle->db, &entry); + crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1, + &item2); if (crv != CKR_OK) { goto loser2; } @@ -2045,7 +2522,9 @@ loser2: SECStatus sftkdb_HasPasswordSet(SFTKDBHandle *keydb) { - SDBPasswordEntry entry; + SECItem item1, item2; + unsigned char data1[SDB_MAX_META_DATA_LEN]; + unsigned char data2[SDB_MAX_META_DATA_LEN]; CK_RV crv; SDB *db; @@ -2058,7 +2537,9 @@ sftkdb_HasPasswordSet(SFTKDBHandle *keydb) return SECFailure; } - crv = (*db->sdb_GetPWEntry)(db, &entry); + item1.data = data1; + item2.data = data2; + crv = (*db->sdb_GetMetaData)(db, "password", &item1, &item2); return (crv == CKR_OK) ? SECSuccess : SECFailure; } @@ -2072,7 +2553,9 @@ SECStatus sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw) { SECStatus rv; - SDBPasswordEntry entry; + SECItem salt, value; + unsigned char data1[SDB_MAX_META_DATA_LEN]; + unsigned char data2[SDB_MAX_META_DATA_LEN]; SECItem key; SECItem *result = NULL; SDB *db; @@ -2093,20 +2576,22 @@ sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw) if (pw == NULL) pw=""; /* get the entry from the database */ - crv = (*db->sdb_GetPWEntry)(db, &entry); + salt.data = data1; + value.data = data2; + crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value); if (crv != CKR_OK) { rv = SECFailure; goto loser; } /* get our intermediate key based on the entry salt value */ - rv = sftkdb_passwordToKey(keydb, &entry, pw, &key); + rv = sftkdb_passwordToKey(keydb, &salt, pw, &key); if (rv != SECSuccess) { goto loser; } /* decrypt the entry value */ - rv = sftkdb_decrypt(&key, &entry.value, &result); + rv = sftkdb_decryptAttribute(&key, &value, &result); if (rv != SECSuccess) { goto loser; } @@ -2247,7 +2732,7 @@ sftk_convertPrivateAttributes(SFTKDBHandle *keydb, CK_OBJECT_HANDLE id, plainText.data = first[i].pValue; plainText.len = first[i].ulValueLen; - rv = sftkdb_encrypt(arena, newKey, &plainText, &result); + rv = sftkdb_encryptAttribute(arena, newKey, &plainText, &result); if (rv != SECSuccess) { goto loser; } @@ -2332,7 +2817,9 @@ sftkdb_ChangePassword(SFTKDBHandle *keydb, char *oldPin, char *newPin) SECItem plainText; SECItem newKey; SECItem *result = NULL; - SDBPasswordEntry entry; + SECItem salt, value; + unsigned char data1[SDB_MAX_META_DATA_LEN]; + unsigned char data2[SDB_MAX_META_DATA_LEN]; CK_RV crv; SDB *db; @@ -2353,19 +2840,20 @@ sftkdb_ChangePassword(SFTKDBHandle *keydb, char *oldPin, char *newPin) rv = SECFailure; goto loser; } - crv = (*db->sdb_GetPWEntry)(db, &entry); + salt.data = data1; + value.data = data2; + crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value); if (crv == CKR_OK) { rv = sftkdb_CheckPassword(keydb, oldPin); if (rv == SECFailure) { goto loser; } } else { - entry.salt.data = entry.data; - entry.salt.len = SALT_LENGTH; - RNG_GenerateGlobalRandomBytes(entry.data,entry.salt.len); + salt.len = SALT_LENGTH; + RNG_GenerateGlobalRandomBytes(salt.data,salt.len); } - rv = sftkdb_passwordToKey(keydb, &entry, newPin, &newKey); + rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey); if (rv != SECSuccess) { goto loser; } @@ -2383,13 +2871,13 @@ sftkdb_ChangePassword(SFTKDBHandle *keydb, char *oldPin, char *newPin) plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING; plainText.len = SFTK_PW_CHECK_LEN; - rv = sftkdb_encrypt(NULL, &newKey, &plainText, &result); + rv = sftkdb_encryptAttribute(NULL, &newKey, &plainText, &result); if (rv != SECSuccess) { goto loser; } - entry.value.data = result->data; - entry.value.len = result->len; - crv = (*keydb->db->sdb_PutPWEntry)(keydb->db, &entry); + value.data = result->data; + value.len = result->len; + crv = (*keydb->db->sdb_PutMetaData)(keydb->db, "password", &salt, &value); if (crv != CKR_OK) { rv = SECFailure; goto loser; diff --git a/security/nss/lib/softoken/sftkdbt.h b/security/nss/lib/softoken/sftkdbt.h index f9d7c9eaa..3d59514d2 100644 --- a/security/nss/lib/softoken/sftkdbt.h +++ b/security/nss/lib/softoken/sftkdbt.h @@ -7,6 +7,8 @@ #define SFTKDBT_H 1 typedef struct SFTKDBHandleStr SFTKDBHandle; +#define SDB_MAX_META_DATA_LEN 256 + typedef enum { SDB_SQL, SDB_EXTERN, diff --git a/security/nss/lib/softoken/softoken.h b/security/nss/lib/softoken/softoken.h index 6078c40b2..c96c75f2f 100644 --- a/security/nss/lib/softoken/softoken.h +++ b/security/nss/lib/softoken/softoken.h @@ -155,18 +155,21 @@ extern SECStatus EC_CopyParams(PRArenaPool *arena, ECParams *dstParams, /* -** Prepare a buffer for DES encryption, growing to the appropriate boundary, -** filling with the appropriate padding. -** We add from 1 to DES_KEY_LENGTH bytes -- we *always* grow. +** Prepare a buffer for padded CBC encryption, growing to the appropriate +** boundary, filling with the appropriate padding. +** +** blockSize must be a power of 2. +** +** We add from 1 to blockSize bytes -- we *always* grow. ** The extra bytes contain the value of the length of the padding: ** if we have 2 bytes of padding, then the padding is "0x02, 0x02". ** ** NOTE: If arena is non-NULL, we re-allocate from there, otherwise ** we assume (and use) PR memory (re)allocation. -** Maybe this belongs in util? */ -extern unsigned char * DES_PadBuffer(PRArenaPool *arena, unsigned char *inbuf, - unsigned int inlen, unsigned int *outlen); +extern unsigned char * CBC_PadBuffer(PRArenaPool *arena, unsigned char *inbuf, + unsigned int inlen, unsigned int *outlen, + int blockSize); /****************************************/ |