Index: mozilla/security/nss/lib/softoken/pkcs11c.c |
=================================================================== |
--- mozilla/security/nss/lib/softoken/pkcs11c.c (revision 158129) |
+++ mozilla/security/nss/lib/softoken/pkcs11c.c (working copy) |
@@ -24,7 +24,6 @@ |
#include "pkcs11.h" |
#include "pkcs11i.h" |
#include "lowkeyi.h" |
-#include "sechash.h" |
#include "secder.h" |
#include "secdig.h" |
#include "lowpbe.h" /* We do PBE below */ |
@@ -2057,7 +2056,7 @@ |
context->update = (SFTKCipher) nsc_DSA_Sign_Stub; |
context->destroy = (privKey == key->objectInfo) ? |
(SFTKDestroy) sftk_Null:(SFTKDestroy)sftk_FreePrivKey; |
- context->maxLen = DSA_SIGNATURE_LEN; |
+ context->maxLen = DSA_MAX_SIGNATURE_LEN; |
break; |
@@ -2905,14 +2904,36 @@ |
return CKR_OK; |
} |
+ |
+/* |
+ * this is coded for "full" support. These selections will be limitted to |
+ * the official subset by freebl. |
+ */ |
+static unsigned int |
+sftk_GetSubPrimeFromPrime(unsigned int primeBits) |
+{ |
+ if (primeBits <= 1024) { |
+ return 160; |
+ } else if (primeBits <= 2048) { |
+ return 224; |
+ } else if (primeBits <= 3072) { |
+ return 256; |
+ } else if (primeBits <= 7680) { |
+ return 384; |
+ } else { |
+ return 512; |
+ } |
+} |
+ |
static CK_RV |
nsc_parameter_gen(CK_KEY_TYPE key_type, SFTKObject *key) |
{ |
SFTKAttribute *attribute; |
CK_ULONG counter; |
unsigned int seedBits = 0; |
+ unsigned int subprimeBits = 0; |
unsigned int primeBits; |
- unsigned int j; |
+ unsigned int j = 8; /* default to 1024 bits */ |
CK_RV crv = CKR_OK; |
PQGParams *params = NULL; |
PQGVerify *vfy = NULL; |
@@ -2924,9 +2945,11 @@ |
} |
primeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; |
sftk_FreeAttribute(attribute); |
- j = PQG_PBITS_TO_INDEX(primeBits); |
- if (j == (unsigned int)-1) { |
- return CKR_ATTRIBUTE_VALUE_INVALID; |
+ if (primeBits < 1024) { |
+ j = PQG_PBITS_TO_INDEX(primeBits); |
+ if (j == (unsigned int)-1) { |
+ return CKR_ATTRIBUTE_VALUE_INVALID; |
+ } |
} |
attribute = sftk_FindAttribute(key, CKA_NETSCAPE_PQG_SEED_BITS); |
@@ -2935,15 +2958,35 @@ |
sftk_FreeAttribute(attribute); |
} |
+ attribute = sftk_FindAttribute(key, CKA_SUBPRIME_BITS); |
+ if (attribute != NULL) { |
+ subprimeBits = (unsigned int) *(CK_ULONG *)attribute->attrib.pValue; |
+ sftk_FreeAttribute(attribute); |
+ } |
+ |
sftk_DeleteAttributeType(key,CKA_PRIME_BITS); |
+ sftk_DeleteAttributeType(key,CKA_SUBPRIME_BITS); |
sftk_DeleteAttributeType(key,CKA_NETSCAPE_PQG_SEED_BITS); |
- if (seedBits == 0) { |
- rv = PQG_ParamGen(j, ¶ms, &vfy); |
+ /* use the old PQG interface if we have old input data */ |
+ if ((primeBits < 1024) || ((primeBits == 1024) && (subprimeBits == 0))) { |
+ if (seedBits == 0) { |
+ rv = PQG_ParamGen(j, ¶ms, &vfy); |
+ } else { |
+ rv = PQG_ParamGenSeedLen(j,seedBits/8, ¶ms, &vfy); |
+ } |
} else { |
- rv = PQG_ParamGenSeedLen(j,seedBits/8, ¶ms, &vfy); |
+ if (subprimeBits == 0) { |
+ subprimeBits = sftk_GetSubPrimeFromPrime(primeBits); |
+ } |
+ if (seedBits == 0) { |
+ seedBits = primeBits; |
+ } |
+ rv = PQG_ParamGenV2(primeBits, subprimeBits, seedBits/8, ¶ms, &vfy); |
} |
+ |
+ |
if (rv != SECSuccess) { |
if (PORT_GetError() == SEC_ERROR_LIBRARY_FAILURE) { |
sftk_fatalError = PR_TRUE; |
@@ -3459,6 +3502,7 @@ |
CK_MECHANISM mech = {0, NULL, 0}; |
CK_ULONG modulusLen; |
+ CK_ULONG subPrimeLen; |
PRBool isEncryptable = PR_FALSE; |
PRBool canSignVerify = PR_FALSE; |
PRBool isDerivable = PR_FALSE; |
@@ -3472,10 +3516,12 @@ |
unsigned char *text_compared; |
CK_ULONG bytes_encrypted; |
CK_ULONG bytes_compared; |
+ CK_ULONG pairwise_digest_length = PAIRWISE_DIGEST_LENGTH; |
/* Variables used for Signature/Verification functions. */ |
- /* always uses SHA-1 digest */ |
- unsigned char *known_digest = (unsigned char *)"Mozilla Rules World!"; |
+ /* Must be at least 256 bits for DSA2 digest */ |
+ unsigned char *known_digest = (unsigned char *) |
+ "Mozilla Rules the World through NSS!"; |
unsigned char *signature; |
CK_ULONG signature_length; |
@@ -3492,6 +3538,19 @@ |
modulusLen--; |
} |
sftk_FreeAttribute(attribute); |
+ } else if (keyType == CKK_DSA) { |
+ SFTKAttribute *attribute; |
+ |
+ /* Get subprime length of private key. */ |
+ attribute = sftk_FindAttribute(privateKey, CKA_SUBPRIME); |
+ if (attribute == NULL) { |
+ return CKR_DEVICE_ERROR; |
+ } |
+ subPrimeLen = attribute->attrib.ulValueLen; |
+ if (subPrimeLen > 1 && *(unsigned char *)attribute->attrib.pValue == 0) { |
+ subPrimeLen--; |
+ } |
+ sftk_FreeAttribute(attribute); |
} |
/**************************************************/ |
@@ -3617,7 +3676,8 @@ |
mech.mechanism = CKM_RSA_PKCS; |
break; |
case CKK_DSA: |
- signature_length = DSA_SIGNATURE_LEN; |
+ signature_length = DSA_MAX_SIGNATURE_LEN; |
+ pairwise_digest_length = subPrimeLen; |
mech.mechanism = CKM_DSA; |
break; |
#ifdef NSS_ENABLE_ECC |
@@ -3645,7 +3705,7 @@ |
crv = NSC_Sign(hSession, |
known_digest, |
- PAIRWISE_DIGEST_LENGTH, |
+ pairwise_digest_length, |
signature, |
&signature_length); |
if (crv != CKR_OK) { |
@@ -3662,7 +3722,7 @@ |
crv = NSC_Verify(hSession, |
known_digest, |
- PAIRWISE_DIGEST_LENGTH, |
+ pairwise_digest_length, |
signature, |
signature_length); |
@@ -3945,9 +4005,12 @@ |
break; |
} |
+ /* |
+ * these are checked by DSA_NewKey |
+ */ |
bitSize = sftk_GetLengthInBits(pqgParam.subPrime.data, |
pqgParam.subPrime.len); |
- if (bitSize != DSA_Q_BITS) { |
+ if ((bitSize < DSA_MIN_Q_BITS) || (bitSize > DSA_MAX_Q_BITS)) { |
crv = CKR_TEMPLATE_INCOMPLETE; |
PORT_Free(pqgParam.prime.data); |
PORT_Free(pqgParam.subPrime.data); |
@@ -3963,7 +4026,7 @@ |
break; |
} |
bitSize = sftk_GetLengthInBits(pqgParam.base.data,pqgParam.base.len); |
- if ((bitSize < 1) || (bitSize > DSA_MAX_P_BITS)) { |
+ if ((bitSize < 2) || (bitSize > DSA_MAX_P_BITS)) { |
crv = CKR_TEMPLATE_INCOMPLETE; |
PORT_Free(pqgParam.prime.data); |
PORT_Free(pqgParam.subPrime.data); |
@@ -5065,6 +5128,113 @@ |
return 0; |
} |
+/* Inputs: |
+ * key_len: Length of derived key to be generated. |
+ * SharedSecret: a shared secret that is the output of a key agreement primitive. |
+ * SharedInfo: (Optional) some data shared by the entities computing the secret key. |
+ * SharedInfoLen: the length in octets of SharedInfo |
+ * Hash: The hash function to be used in the KDF |
+ * HashLen: the length in octets of the output of Hash |
+ * Output: |
+ * key: Pointer to a buffer containing derived key, if return value is SECSuccess. |
+ */ |
+static CK_RV sftk_compute_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, SECItem *SharedSecret, |
+ CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen, |
+ SECStatus Hash(unsigned char *, const unsigned char *, uint32), |
+ CK_ULONG HashLen) |
+{ |
+ unsigned char *buffer = NULL, *output_buffer = NULL; |
+ uint32 buffer_len, max_counter, i; |
+ SECStatus rv; |
+ |
+ /* Check that key_len isn't too long. The maximum key length could be |
+ * greatly increased if the code below did not limit the 4-byte counter |
+ * to a maximum value of 255. */ |
+ if (key_len > 254 * HashLen) |
+ return SEC_ERROR_INVALID_ARGS; |
+ |
+ if (SharedInfo == NULL) |
+ SharedInfoLen = 0; |
+ |
+ buffer_len = SharedSecret->len + 4 + SharedInfoLen; |
+ buffer = (CK_BYTE *)PORT_Alloc(buffer_len); |
+ if (buffer == NULL) { |
+ rv = SEC_ERROR_NO_MEMORY; |
+ goto loser; |
+ } |
+ |
+ max_counter = key_len/HashLen; |
+ if (key_len > max_counter * HashLen) |
+ max_counter++; |
+ |
+ output_buffer = (CK_BYTE *)PORT_Alloc(max_counter * HashLen); |
+ if (output_buffer == NULL) { |
+ rv = SEC_ERROR_NO_MEMORY; |
+ goto loser; |
+ } |
+ |
+ /* Populate buffer with SharedSecret || Counter || [SharedInfo] |
+ * where Counter is 0x00000001 */ |
+ PORT_Memcpy(buffer, SharedSecret->data, SharedSecret->len); |
+ buffer[SharedSecret->len] = 0; |
+ buffer[SharedSecret->len + 1] = 0; |
+ buffer[SharedSecret->len + 2] = 0; |
+ buffer[SharedSecret->len + 3] = 1; |
+ if (SharedInfo) { |
+ PORT_Memcpy(&buffer[SharedSecret->len + 4], SharedInfo, SharedInfoLen); |
+ } |
+ |
+ for(i=0; i < max_counter; i++) { |
+ rv = Hash(&output_buffer[i * HashLen], buffer, buffer_len); |
+ if (rv != SECSuccess) |
+ goto loser; |
+ |
+ /* Increment counter (assumes max_counter < 255) */ |
+ buffer[SharedSecret->len + 3]++; |
+ } |
+ |
+ PORT_ZFree(buffer, buffer_len); |
+ if (key_len < max_counter * HashLen) { |
+ PORT_Memset(output_buffer + key_len, 0, max_counter * HashLen - key_len); |
+ } |
+ *key = output_buffer; |
+ |
+ return SECSuccess; |
+ |
+ loser: |
+ if (buffer) { |
+ PORT_ZFree(buffer, buffer_len); |
+ } |
+ if (output_buffer) { |
+ PORT_ZFree(output_buffer, max_counter * HashLen); |
+ } |
+ return rv; |
+} |
+ |
+static CK_RV sftk_ANSI_X9_63_kdf(CK_BYTE **key, CK_ULONG key_len, |
+ SECItem *SharedSecret, |
+ CK_BYTE_PTR SharedInfo, CK_ULONG SharedInfoLen, |
+ CK_EC_KDF_TYPE kdf) |
+{ |
+ if (kdf == CKD_SHA1_KDF) |
+ return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, |
+ SharedInfoLen, SHA1_HashBuf, SHA1_LENGTH); |
+ else if (kdf == CKD_SHA224_KDF) |
+ return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, |
+ SharedInfoLen, SHA224_HashBuf, SHA224_LENGTH); |
+ else if (kdf == CKD_SHA256_KDF) |
+ return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, |
+ SharedInfoLen, SHA256_HashBuf, SHA256_LENGTH); |
+ else if (kdf == CKD_SHA384_KDF) |
+ return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, |
+ SharedInfoLen, SHA384_HashBuf, SHA384_LENGTH); |
+ else if (kdf == CKD_SHA512_KDF) |
+ return sftk_compute_ANSI_X9_63_kdf(key, key_len, SharedSecret, SharedInfo, |
+ SharedInfoLen, SHA512_HashBuf, SHA512_LENGTH); |
+ else |
+ return SEC_ERROR_INVALID_ALGORITHM; |
+} |
+ |
/* |
* SSL Key generation given pre master secret |
*/ |
@@ -5893,17 +6063,8 @@ |
crv = CKR_TEMPLATE_INCONSISTENT; |
break; |
} |
- /* now allocate the hash contexts */ |
- md5 = MD5_NewContext(); |
- if (md5 == NULL) { |
- crv = CKR_HOST_MEMORY; |
- break; |
- } |
- MD5_Begin(md5); |
- MD5_Update(md5,(const unsigned char*)att->attrib.pValue, |
+ MD5_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, |
att->attrib.ulValueLen); |
- MD5_End(md5,key_block,&outLen,MD5_LENGTH); |
- MD5_DestroyContext(md5, PR_TRUE); |
crv = sftk_forceAttribute (key,CKA_VALUE,key_block,keySize); |
break; |
@@ -5913,21 +6074,60 @@ |
crv = CKR_TEMPLATE_INCONSISTENT; |
break; |
} |
- /* now allocate the hash contexts */ |
- sha = SHA1_NewContext(); |
- if (sha == NULL) { |
- crv = CKR_HOST_MEMORY; |
+ SHA1_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, |
+ att->attrib.ulValueLen); |
+ |
+ crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); |
+ break; |
+ |
+ case CKM_SHA224_KEY_DERIVATION: |
+ if (keySize == 0) keySize = SHA224_LENGTH; |
+ if (keySize > SHA224_LENGTH) { |
+ crv = CKR_TEMPLATE_INCONSISTENT; |
break; |
} |
- SHA1_Begin(sha); |
- SHA1_Update(sha,(const unsigned char*)att->attrib.pValue, |
+ SHA224_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, |
att->attrib.ulValueLen); |
- SHA1_End(sha,key_block,&outLen,SHA1_LENGTH); |
- SHA1_DestroyContext(sha, PR_TRUE); |
crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); |
break; |
+ case CKM_SHA256_KEY_DERIVATION: |
+ if (keySize == 0) keySize = SHA256_LENGTH; |
+ if (keySize > SHA256_LENGTH) { |
+ crv = CKR_TEMPLATE_INCONSISTENT; |
+ break; |
+ } |
+ SHA256_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, |
+ att->attrib.ulValueLen); |
+ |
+ crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); |
+ break; |
+ |
+ case CKM_SHA384_KEY_DERIVATION: |
+ if (keySize == 0) keySize = SHA384_LENGTH; |
+ if (keySize > SHA384_LENGTH) { |
+ crv = CKR_TEMPLATE_INCONSISTENT; |
+ break; |
+ } |
+ SHA384_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, |
+ att->attrib.ulValueLen); |
+ |
+ crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); |
+ break; |
+ |
+ case CKM_SHA512_KEY_DERIVATION: |
+ if (keySize == 0) keySize = SHA512_LENGTH; |
+ if (keySize > SHA512_LENGTH) { |
+ crv = CKR_TEMPLATE_INCONSISTENT; |
+ break; |
+ } |
+ SHA512_HashBuf(key_block,(const unsigned char*)att->attrib.pValue, |
+ att->attrib.ulValueLen); |
+ |
+ crv = sftk_forceAttribute(key,CKA_VALUE,key_block,keySize); |
+ break; |
+ |
case CKM_DH_PKCS_DERIVE: |
{ |
SECItem derived, dhPublic; |
@@ -5967,7 +6167,6 @@ |
SECItem ecScalar, ecPoint; |
SECItem tmp; |
PRBool withCofactor = PR_FALSE; |
- unsigned char secret_hash[20]; |
unsigned char *secret; |
unsigned char *keyData = NULL; |
int secretlen, curveLen, pubKeyLen; |
@@ -6053,28 +6252,30 @@ |
break; |
} |
- /* |
- * tmp is the raw data created by ECDH_Derive, |
- * secret and secretlen are the values we will eventually pass as our |
- * generated key. |
- */ |
- secret = tmp.data; |
- secretlen = tmp.len; |
/* |
* apply the kdf function. |
*/ |
- if (mechParams->kdf == CKD_SHA1_KDF) { |
- /* Compute SHA1 hash */ |
- PORT_Memset(secret_hash, 0, 20); |
- rv = SHA1_HashBuf(secret_hash, tmp.data, tmp.len); |
+ if (mechParams->kdf == CKD_NULL) { |
+ /* |
+ * tmp is the raw data created by ECDH_Derive, |
+ * secret and secretlen are the values we will |
+ * eventually pass as our generated key. |
+ */ |
+ secret = tmp.data; |
+ secretlen = tmp.len; |
+ } else { |
+ secretlen = keySize; |
+ rv = sftk_ANSI_X9_63_kdf(&secret, keySize, |
+ &tmp, mechParams->pSharedData, |
+ mechParams->ulSharedDataLen, mechParams->kdf); |
+ PORT_ZFree(tmp.data, tmp.len); |
if (rv != SECSuccess) { |
- PORT_ZFree(tmp.data, tmp.len); |
crv = CKR_HOST_MEMORY; |
break; |
- } |
- secret = secret_hash; |
- secretlen = 20; |
+ } |
+ tmp.data = secret; |
+ tmp.len = secretlen; |
} |
/* |
@@ -6105,8 +6306,6 @@ |
if (keyData) { |
PORT_ZFree(keyData, keySize); |
} |
- PORT_Memset(secret_hash, 0, 20); |
- |
break; |
ec_loser: |