diff --git a/lib/freebl/stubs.c b/lib/freebl/stubs.c --- a/lib/freebl/stubs.c +++ b/lib/freebl/stubs.c @@ -485,17 +485,17 @@ PR_GetLibraryFilePathname_stub(const cha extern int PORT_GetError_stub(void) { STUB_SAFE_CALL0(PORT_GetError_Util); return errno; } extern void -PORT_SafeZero(void *p, size_t n) +PORT_SafeZero_stub(void *p, size_t n) { STUB_SAFE_CALL2(PORT_SafeZero, p, n); /* just use a generic call in the case where we are running * standalone freebl */ if (p != NULL) { volatile unsigned char *__vl = (unsigned char *)p; size_t __nl = n; while (__nl--) diff --git a/lib/freebl/stubs.h b/lib/freebl/stubs.h --- a/lib/freebl/stubs.h +++ b/lib/freebl/stubs.h @@ -22,17 +22,17 @@ #define PORT_Alloc PORT_Alloc_stub #define PORT_ArenaAlloc PORT_ArenaAlloc_stub #define PORT_ArenaZAlloc PORT_ArenaZAlloc_stub #define PORT_Free PORT_Free_stub #define PORT_FreeArena PORT_FreeArena_stub #define PORT_GetError PORT_GetError_stub #define PORT_NewArena PORT_NewArena_stub -#define PORT_SaveZero PORT_SaveZero_stub +#define PORT_SafeZero PORT_SafeZero_stub #define PORT_SetError PORT_SetError_stub #define PORT_ZAlloc PORT_ZAlloc_stub #define PORT_ZFree PORT_ZFree_stub #define PORT_ZAllocAligned PORT_ZAllocAligned_stub #define PORT_ZAllocAlignedOffset PORT_ZAllocAlignedOffset_stub #define SECITEM_AllocItem SECITEM_AllocItem_stub #define SECITEM_CompareItem SECITEM_CompareItem_stub diff --git a/lib/freebl/unix_fips140_3.c b/lib/freebl/unix_fips140_3.c --- a/lib/freebl/unix_fips140_3.c +++ b/lib/freebl/unix_fips140_3.c @@ -25,17 +25,17 @@ RNG_SystemInfoForRNG(void) { PRUint8 bytes[SYSTEM_RNG_SEED_COUNT]; size_t numBytes = RNG_SystemRNG(bytes, SYSTEM_RNG_SEED_COUNT); if (!numBytes) { /* error is set */ return; } RNG_RandomUpdate(bytes, numBytes); - PORT_SaveZero(bytes, sizeof(bytes)); + PORT_SafeZero(bytes, sizeof(bytes)); } static unsigned int rng_grndFlags = 0; static PRCallOnceType rng_KernelFips; static PRStatus rng_getKernelFips() { diff --git a/lib/softoken/pkcs11c.c b/lib/softoken/pkcs11c.c --- a/lib/softoken/pkcs11c.c +++ b/lib/softoken/pkcs11c.c @@ -535,17 +535,17 @@ sftk_InitGeneric(SFTKSession *session, C context->cipherInfo = NULL; context->hashInfo = NULL; context->doPad = PR_FALSE; context->padDataLength = 0; context->key = key; context->blockSize = 0; context->maxLen = 0; context->isFIPS = sftk_operationIsFIPS(session->slot, pMechanism, - operation, key); + operation, key, 0); *contextPtr = context; return CKR_OK; } static int sftk_aes_mode(CK_MECHANISM_TYPE mechanism) { switch (mechanism) { @@ -4794,16 +4794,17 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSessi goto loser; } /* make sure we don't have any class, key_type, or value fields */ sftk_DeleteAttributeType(key, CKA_CLASS); sftk_DeleteAttributeType(key, CKA_KEY_TYPE); sftk_DeleteAttributeType(key, CKA_VALUE); + /* Now Set up the parameters to generate the key (based on mechanism) */ key_gen_type = nsc_bulk; /* bulk key by default */ switch (pMechanism->mechanism) { case CKM_CDMF_KEY_GEN: case CKM_DES_KEY_GEN: case CKM_DES2_KEY_GEN: case CKM_DES3_KEY_GEN: checkWeak = PR_TRUE; @@ -4990,16 +4991,20 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSessi crv = CKR_SESSION_HANDLE_INVALID; goto loser; } /* * handle the base object stuff */ crv = sftk_handleObject(key, session); + /* we need to do this check at the end, so we can check the generated key length against + * fips requirements */ + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_NSS_GENERATE, key, 0); + session->lastOpWasFIPS = key->isFIPS; sftk_FreeSession(session); if (crv == CKR_OK && sftk_isTrue(key, CKA_SENSITIVE)) { crv = sftk_forceAttribute(key, CKA_ALWAYS_SENSITIVE, &cktrue, sizeof(CK_BBOOL)); } if (crv == CKR_OK && !sftk_isTrue(key, CKA_EXTRACTABLE)) { crv = sftk_forceAttribute(key, CKA_NEVER_EXTRACTABLE, &cktrue, sizeof(CK_BBOOL)); } if (crv == CKR_OK) { @@ -6077,18 +6082,18 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS } /* * handle the base object cleanup for the private Key * If we have any problems, we destroy the public Key we've * created and linked. */ crv = sftk_handleObject(publicKey, session); - sftk_FreeSession(session); if (crv != CKR_OK) { + sftk_FreeSession(session); sftk_FreeObject(publicKey); NSC_DestroyObject(hSession, privateKey->handle); sftk_FreeObject(privateKey); return crv; } if (sftk_isTrue(privateKey, CKA_SENSITIVE)) { crv = sftk_forceAttribute(privateKey, CKA_ALWAYS_SENSITIVE, &cktrue, sizeof(CK_BBOOL)); @@ -6120,22 +6125,29 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hS (PRUint32)hSession, (PRUint32)pMechanism->mechanism, (PRUint32)crv); sftk_LogAuditMessage(NSS_AUDIT_ERROR, NSS_AUDIT_SELF_TEST, msg); } } } if (crv != CKR_OK) { + sftk_FreeSession(session); NSC_DestroyObject(hSession, publicKey->handle); sftk_FreeObject(publicKey); NSC_DestroyObject(hSession, privateKey->handle); sftk_FreeObject(privateKey); return crv; } + /* we need to do this check at the end to make sure the generated key meets the key length requirements */ + privateKey->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_NSS_GENERATE_KEY_PAIR, privateKey, 0); + publicKey->isFIPS = privateKey->isFIPS; + session->lastOpWasFIPS = privateKey->isFIPS; + + sftk_FreeSession(session); *phPrivateKey = privateKey->handle; *phPublicKey = publicKey->handle; sftk_FreeObject(publicKey); sftk_FreeObject(privateKey); return CKR_OK; } @@ -7321,30 +7333,35 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_ return CKR_MECHANISM_PARAM_INVALID; } if ((params->bExpand && keySize == 0) || (!params->bExpand && keySize > hashLen) || (params->bExpand && keySize > 255 * hashLen)) { return CKR_TEMPLATE_INCONSISTENT; } + if (!params->bExpand) { + keySize = hashLen; + } + /* sourceKey is NULL if we are called from the POST, skip the * sensitiveCheck */ if (sourceKey != NULL) { crv = sftk_DeriveSensitiveCheck(sourceKey, key, canBeData); if (crv != CKR_OK) return crv; } /* HKDF-Extract(salt, base key value) */ if (params->bExtract) { CK_BYTE *salt; CK_ULONG saltLen; HMACContext *hmac; unsigned int bufLen; + SFTKSource saltKeySource = SFTK_SOURCE_DEFAULT; switch (params->ulSaltType) { case CKF_HKDF_SALT_NULL: saltLen = hashLen; salt = hashbuf; memset(salt, 0, saltLen); break; case CKF_HKDF_SALT_DATA: @@ -7369,31 +7386,57 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_ /* if the base key is not fips, but the salt key is, the * resulting key can be fips */ if (isFIPS && (key->isFIPS == 0) && (saltKey->isFIPS == 1)) { CK_MECHANISM mech; mech.mechanism = CKM_HKDF_DERIVE; mech.pParameter = params; mech.ulParameterLen = sizeof(*params); key->isFIPS = sftk_operationIsFIPS(saltKey->slot, &mech, - CKA_DERIVE, saltKey); + CKA_DERIVE, saltKey, + keySize*PR_BITS_PER_BYTE); } + saltKeySource = saltKey->source; saltKey_att = sftk_FindAttribute(saltKey, CKA_VALUE); if (saltKey_att == NULL) { sftk_FreeObject(saltKey); return CKR_KEY_HANDLE_INVALID; } /* save the resulting salt */ salt = saltKey_att->attrib.pValue; saltLen = saltKey_att->attrib.ulValueLen; break; default: return CKR_MECHANISM_PARAM_INVALID; break; } + /* only TLS style usage is FIPS approved, + * turn off the FIPS indicator for other usages */ + if (isFIPS && key && sourceKey) { + PRBool fipsOK = PR_FALSE; + /* case one: mix the kea with a previous or default + * salt */ + if ((sourceKey->source == SFTK_SOURCE_KEA) && + (saltKeySource == SFTK_SOURCE_HKDF_EXPAND) && + (saltLen == rawHash->length)) { + fipsOK = PR_TRUE; + } + /* case two: restart, remix the previous secret as a salt */ + if ((sourceKey->objclass == CKO_DATA) && + (NSS_SecureMemcmpZero(sourceKeyBytes, sourceKeyLen) == 0) && + (sourceKeyLen == rawHash->length) && + (saltKeySource == SFTK_SOURCE_HKDF_EXPAND) && + (saltLen == rawHash->length)) { + fipsOK = PR_TRUE; + } + if (!fipsOK) { + key->isFIPS = PR_FALSE; + } + } + if (key) key->source = SFTK_SOURCE_HKDF_EXTRACT; hmac = HMAC_Create(rawHash, salt, saltLen, isFIPS); if (saltKey_att) { sftk_FreeAttribute(saltKey_att); } if (saltKey) { sftk_FreeObject(saltKey); } @@ -7411,26 +7454,50 @@ sftk_HKDF(CK_HKDF_PARAMS_PTR params, CK_ /* PRK = base key value */ prk = sourceKeyBytes; prkLen = sourceKeyLen; } /* HKDF-Expand */ if (!params->bExpand) { okm = prk; - keySize = genLen = hashLen; + genLen = hashLen; } else { /* T(1) = HMAC-Hash(prk, "" | info | 0x01) * T(n) = HMAC-Hash(prk, T(n-1) | info | n * key material = T(1) | ... | T(n) */ HMACContext *hmac; CK_BYTE bi; unsigned iterations; + /* only TLS style usage is FIPS approved, + * turn off the FIPS indicator for other usages */ + if (isFIPS && key && key->isFIPS && sourceKey) { + unsigned char *info=¶ms->pInfo[3]; + /* only one case, + * 1) Expand only + * 2) with a key whose source was + * SFTK_SOURCE_HKDF_EXPAND or SFTK_SOURCE_HKDF_EXTRACT + * 3) source key length == rawHash->length + * 4) Info has tls or dtls + * If any of those conditions aren't met, then we turn + * off the fips indicator */ + if (params->bExtract || + ((sourceKey->source != SFTK_SOURCE_HKDF_EXTRACT) && + (sourceKey->source != SFTK_SOURCE_HKDF_EXPAND)) || + (sourceKeyLen != rawHash->length) || + (params->ulInfoLen < 7) || + ((PORT_Memcmp(info,"tls",3) != 0) && + (PORT_Memcmp(info,"dtls",4) != 0))) { + key->isFIPS = PR_FALSE; + } + } + if (key) key->source = SFTK_SOURCE_HKDF_EXPAND; + genLen = PR_ROUNDUP(keySize, hashLen); iterations = genLen / hashLen; if (genLen > sizeof(keyBlock)) { keyBlockAlloc = PORT_Alloc(genLen); if (keyBlockAlloc == NULL) { return CKR_HOST_MEMORY; } @@ -7635,17 +7702,18 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession /* get the value of the base key */ att = sftk_FindAttribute(sourceKey, CKA_VALUE); if (att == NULL) { sftk_FreeObject(key); sftk_FreeObject(sourceKey); return CKR_KEY_HANDLE_INVALID; } } - key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_DERIVE, sourceKey); + key->isFIPS = sftk_operationIsFIPS(slot, pMechanism, CKA_DERIVE, sourceKey, + keySize*PR_BITS_PER_BYTE); switch (mechanism) { /* get a public key from a private key. nsslowkey_ConvertToPublickey() * will generate the public portion if it doesn't already exist. */ case CKM_NSS_PUB_FROM_PRIV: { NSSLOWKEYPrivateKey *privKey; NSSLOWKEYPublicKey *pubKey; int error; @@ -8797,16 +8865,17 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession /* calculate private value - oct */ rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize); SECITEM_ZfreeItem(&dhPrime, PR_FALSE); SECITEM_ZfreeItem(&dhValue, PR_FALSE); if (rv == SECSuccess) { + key->source = SFTK_SOURCE_KEA; sftk_forceAttribute(key, CKA_VALUE, derived.data, derived.len); SECITEM_ZfreeItem(&derived, PR_FALSE); crv = CKR_OK; } else crv = CKR_HOST_MEMORY; break; } @@ -8894,16 +8963,17 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession * 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; + key->isFIPS = PR_FALSE; crv = sftk_ANSI_X9_63_kdf(&secret, keySize, &tmp, mechParams->pSharedData, mechParams->ulSharedDataLen, mechParams->kdf); PORT_ZFree(tmp.data, tmp.len); if (crv != CKR_OK) { break; } tmp.data = secret; @@ -8927,16 +8997,17 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession } PORT_Memcpy(&keyData[keySize - secretlen], secret, secretlen); secret = keyData; } else { secret += (secretlen - keySize); } secretlen = keySize; } + key->source = SFTK_SOURCE_KEA; sftk_forceAttribute(key, CKA_VALUE, secret, secretlen); PORT_ZFree(tmp.data, tmp.len); if (keyData) { PORT_ZFree(keyData, keySize); } break; diff --git a/lib/softoken/pkcs11i.h b/lib/softoken/pkcs11i.h --- a/lib/softoken/pkcs11i.h +++ b/lib/softoken/pkcs11i.h @@ -147,16 +147,26 @@ typedef enum { */ typedef enum { SFTK_DestroyFailure, SFTK_Destroyed, SFTK_Busy } SFTKFreeStatus; /* + * Source of various objects + */ +typedef enum { + SFTK_SOURCE_DEFAULT=0, + SFTK_SOURCE_KEA, + SFTK_SOURCE_HKDF_EXPAND, + SFTK_SOURCE_HKDF_EXTRACT +} SFTKSource; + +/* * attribute values of an object. */ struct SFTKAttributeStr { SFTKAttribute *next; SFTKAttribute *prev; PRBool freeAttr; PRBool freeData; /*must be called handle to make sftkqueue_find work */ @@ -189,16 +199,17 @@ struct SFTKObjectStr { CK_OBJECT_CLASS objclass; CK_OBJECT_HANDLE handle; int refCount; PZLock *refLock; SFTKSlot *slot; void *objectInfo; SFTKFree infoFree; PRBool isFIPS; + SFTKSource source; }; struct SFTKTokenObjectStr { SFTKObject obj; SECItem dbKey; }; struct SFTKSessionObjectStr { @@ -963,15 +974,16 @@ char **NSC_ModuleDBFunc(unsigned long fu const SECItem *sftk_VerifyDH_Prime(SECItem *dhPrime, SECItem *generator, PRBool isFIPS); /* check if dhSubPrime claims dhPrime is a safe prime. */ SECStatus sftk_IsSafePrime(SECItem *dhPrime, SECItem *dhSubPrime, PRBool *isSafe); /* map an operation Attribute to a Mechanism flag */ CK_FLAGS sftk_AttributeToFlags(CK_ATTRIBUTE_TYPE op); /* check the FIPS table to determine if this current operation is allowed by * FIPS security policy */ PRBool sftk_operationIsFIPS(SFTKSlot *slot, CK_MECHANISM *mech, - CK_ATTRIBUTE_TYPE op, SFTKObject *source); + CK_ATTRIBUTE_TYPE op, SFTKObject *source, + CK_ULONG targetKeySize); /* add validation objects to the slot */ CK_RV sftk_CreateValidationObjects(SFTKSlot *slot); SEC_END_PROTOS #endif /* _PKCS11I_H_ */ diff --git a/lib/softoken/pkcs11u.c b/lib/softoken/pkcs11u.c --- a/lib/softoken/pkcs11u.c +++ b/lib/softoken/pkcs11u.c @@ -1098,16 +1098,17 @@ sftk_NewObject(SFTKSlot *slot) sessObject->attrList[i].freeData = PR_FALSE; } sessObject->optimizeSpace = slot->optimizeSpace; object->handle = 0; object->next = object->prev = NULL; object->slot = slot; object->isFIPS = sftk_isFIPS(slot->slotID); + object->source = SFTK_SOURCE_DEFAULT; object->refCount = 1; sessObject->sessionList.next = NULL; sessObject->sessionList.prev = NULL; sessObject->sessionList.parent = object; sessObject->session = NULL; sessObject->wasDerived = PR_FALSE; if (!hasLocks) @@ -1683,16 +1684,17 @@ fail: CK_RV sftk_CopyObject(SFTKObject *destObject, SFTKObject *srcObject) { SFTKAttribute *attribute; SFTKSessionObject *src_so = sftk_narrowToSessionObject(srcObject); unsigned int i; destObject->isFIPS = srcObject->isFIPS; + destObject->source = srcObject->source; if (src_so == NULL) { return sftk_CopyTokenObject(destObject, srcObject); } PZ_Lock(src_so->attributeLock); for (i = 0; i < src_so->hashSize; i++) { attribute = src_so->head[i]; do { @@ -2068,16 +2070,17 @@ sftk_NewTokenObject(SFTKSlot *slot, SECI /* every object must have a class, if we can't get it, the object * doesn't exist */ crv = handleToClass(slot, handle, &object->objclass); if (crv != CKR_OK) { goto loser; } object->slot = slot; object->isFIPS = sftk_isFIPS(slot->slotID); + object->source = SFTK_SOURCE_DEFAULT; object->objectInfo = NULL; object->infoFree = NULL; if (!hasLocks) { object->refLock = PZ_NewLock(nssILockRefLock); } if (object->refLock == NULL) { goto loser; } @@ -2234,16 +2237,25 @@ sftk_AttributeToFlags(CK_ATTRIBUTE_TYPE break; case CKA_DERIVE: flags = CKF_DERIVE; break; /* fake attribute to select digesting */ case CKA_DIGEST: flags = CKF_DIGEST; break; + /* fake attribute to select key gen */ + case CKA_NSS_GENERATE: + flags = CKF_GENERATE; + break; + /* fake attribute to select key pair gen */ + case CKA_NSS_GENERATE_KEY_PAIR: + flags = CKF_GENERATE_KEY_PAIR; + break; + /* fake attributes to to handle MESSAGE* flags */ case CKA_NSS_MESSAGE | CKA_ENCRYPT: flags = CKF_MESSAGE_ENCRYPT; break; case CKA_NSS_MESSAGE | CKA_DECRYPT: flags = CKF_MESSAGE_DECRYPT; break; case CKA_NSS_MESSAGE | CKA_SIGN: flags = CKF_MESSAGE_SIGN; @@ -2319,20 +2331,20 @@ sftk_quickGetECCCurveOid(SFTKObject *sou } /* This function currently only returns valid lengths for * FIPS approved ECC curves. If we want to make this generic * in the future, that Curve determination can be done in * the sftk_handleSpecial. Since it's currently only used * in FIPS indicators, it's currently only compiled with * the FIPS indicator code */ -static int +static CK_ULONG sftk_getKeyLength(SFTKObject *source) { - CK_KEY_TYPE keyType = CK_INVALID_HANDLE; + CK_KEY_TYPE keyType = CKK_INVALID_KEY_TYPE; CK_ATTRIBUTE_TYPE keyAttribute; CK_ULONG keyLength = 0; SFTKAttribute *attribute; CK_RV crv; /* If we don't have a key, then it doesn't have a length. * this may be OK (say we are hashing). The mech info will * sort this out because algorithms which expect no keys @@ -2342,17 +2354,17 @@ sftk_getKeyLength(SFTKObject *source) } crv = sftk_GetULongAttribute(source, CKA_KEY_TYPE, &keyType); if (crv != CKR_OK) { /* sometimes we're passed a data object, in that case the * key length is CKA_VALUE, which is the default */ keyType = CKK_INVALID_KEY_TYPE; } - if (keyType == CKK_EC) { + if (keyType == CKK_EC || keyType == CKK_EC_EDWARDS || keyType == CKK_EC_MONTGOMERY) { SECOidTag curve = sftk_quickGetECCCurveOid(source); switch (curve) { case SEC_OID_CURVE25519: /* change when we start algorithm testing on curve25519 */ return 0; case SEC_OID_SECG_EC_SECP256R1: return 256; case SEC_OID_SECG_EC_SECP384R1: @@ -2384,24 +2396,65 @@ sftk_getKeyLength(SFTKObject *source) attribute = sftk_FindAttribute(source, keyAttribute); if (attribute) { keyLength = attribute->attrib.ulValueLen * 8; sftk_FreeAttribute(attribute); } return keyLength; } +PRBool +sftk_checkFIPSHash(CK_MECHANISM_TYPE hash, PRBool allowSmall, PRBool allowCMAC) +{ + switch (hash) { + case CKM_AES_CMAC: + return allowCMAC; + case CKM_SHA_1: + case CKM_SHA_1_HMAC: + case CKM_SHA224: + case CKM_SHA224_HMAC: + return allowSmall; + case CKM_SHA256: + case CKM_SHA256_HMAC: + case CKM_SHA384: + case CKM_SHA384_HMAC: + case CKM_SHA512: + case CKM_SHA512_HMAC: + return PR_TRUE; + } + return PR_FALSE; +} + +PRBool +sftk_checkKeyLength(CK_ULONG keyLength, CK_ULONG min, + CK_ULONG max, CK_ULONG step) +{ + if (keyLength > max) { + return PR_FALSE; + } + if (keyLength < min ) { + return PR_FALSE; + } + if (((keyLength - min) % step) != 0) { + return PR_FALSE; + } + return PR_TRUE; +} + /* * handle specialized FIPS semantics that are too complicated to * handle with just a table. NOTE: this means any additional semantics * would have to be coded here before they can be added to the table */ static PRBool sftk_handleSpecial(SFTKSlot *slot, CK_MECHANISM *mech, - SFTKFIPSAlgorithmList *mechInfo, SFTKObject *source) + SFTKFIPSAlgorithmList *mechInfo, SFTKObject *source, + CK_ULONG keyLength, CK_ULONG targetKeyLength) { + PRBool allowSmall = PR_FALSE; + PRBool allowCMAC = PR_FALSE; switch (mechInfo->special) { case SFTKFIPSDH: { SECItem dhPrime; SECItem dhBase; SECItem dhGenerator; PRBool fipsOk = PR_FALSE; const SECItem *dhSubPrime; CK_RV crv = sftk_Attribute2SecItem(NULL, &dhPrime, @@ -2451,32 +2504,97 @@ sftk_handleSpecial(SFTKSlot *slot, CK_ME } /* we use the existing hash utilities to find the length of * the hash */ hashObj = HASH_GetRawHashObject(sftk_GetHashTypeFromMechanism( pss->hashAlg)); if (hashObj == NULL) { return PR_FALSE; } + /* cap the salt for legacy keys */ + if ((keyLength <= 1024) && (pss->sLen > 63)) { + return PR_FALSE; + } + /* cap the salt for based on the hash */ if (pss->sLen > hashObj->length) { return PR_FALSE; } + /* Our code makes sure pss->hashAlg matches the explicit + * hash in the mechanism, and only mechanisms with approved + * hashes are included, so no need to check pss->hashAlg + * here */ + return PR_TRUE; + } + case SFTKFIPSPBKDF2: { + /* PBKDF2 must have the following addition restrictions + * (independent of keysize). + * 1. iteration count must be at least 1000. + * 2. salt must be at least 128 bits (16 bytes). + * 3. password must match the length specified in the SP + */ + CK_PKCS5_PBKD2_PARAMS *pbkdf2 = (CK_PKCS5_PBKD2_PARAMS *) + mech->pParameter; + if (mech->ulParameterLen != sizeof(*pbkdf2)) { + return PR_FALSE; + } + if (pbkdf2->iterations < 1000) { + return PR_FALSE; + } + if (pbkdf2->ulSaltSourceDataLen < 16) { + return PR_FALSE; + } + if (*(pbkdf2->ulPasswordLen) < SFTKFIPS_PBKDF2_MIN_PW_LEN) { + return PR_FALSE; + } return PR_TRUE; } + /* check the hash mechanisms to make sure they themselves are FIPS */ + case SFTKFIPSChkHashSp800: + allowCMAC = PR_TRUE; + case SFTKFIPSChkHash: + allowSmall = PR_TRUE; + case SFTKFIPSChkHashTls: + if (mech->ulParameterLen < mechInfo->offset +sizeof(CK_ULONG)) { + return PR_FALSE; + } + return sftk_checkFIPSHash(*(CK_ULONG *)(((char *)mech->pParameter) + + mechInfo->offset), allowSmall, allowCMAC); + case SFTKFIPSTlsKeyCheck: + if (mech->mechanism != CKM_NSS_TLS_KEY_AND_MAC_DERIVE_SHA256) { + /* unless the mechnism has a built-in hash, check the hash */ + if (mech->ulParameterLen < mechInfo->offset +sizeof(CK_ULONG)) { + return PR_FALSE; + } + if (!sftk_checkFIPSHash(*(CK_ULONG *)(((char *)mech->pParameter) + + mechInfo->offset), PR_FALSE, PR_FALSE)) { + return PR_FALSE; + } + } + return sftk_checkKeyLength(targetKeyLength, 112, 512, 1); + case SFTKFIPSRSAOAEP:; + CK_RSA_PKCS_OAEP_PARAMS *rsaoaep = (CK_RSA_PKCS_OAEP_PARAMS *) + mech->pParameter; + + HASH_HashType hash_msg = sftk_GetHashTypeFromMechanism(rsaoaep->hashAlg); + HASH_HashType hash_pad = sftk_GetHashTypeFromMechanism(rsaoaep->mgf); + /* message hash and mask generation function must be the same */ + if (hash_pad != hash_msg) return PR_FALSE; + + return sftk_checkFIPSHash(rsaoaep->hashAlg, PR_FALSE, PR_FALSE); default: break; } /* if we didn't understand the special processing, mark it non-fips */ return PR_FALSE; } #endif PRBool sftk_operationIsFIPS(SFTKSlot *slot, CK_MECHANISM *mech, CK_ATTRIBUTE_TYPE op, - SFTKObject *source) + SFTKObject *source, CK_ULONG targetKeyLength) { #ifndef NSS_HAS_FIPS_INDICATORS return PR_FALSE; #else int i; CK_FLAGS opFlags; CK_ULONG keyLength; @@ -2498,23 +2616,25 @@ sftk_operationIsFIPS(SFTKSlot *slot, CK_ } keyLength = sftk_getKeyLength(source); /* check against our algorithm array */ for (i = 0; i < SFTK_NUMBER_FIPS_ALGORITHMS; i++) { SFTKFIPSAlgorithmList *mechs = &sftk_fips_mechs[i]; /* if we match the number of records exactly, then we are an * approved algorithm in the approved mode with an approved key */ - if (((mech->mechanism == mechs->type) && - (opFlags == (mechs->info.flags & opFlags)) && - (keyLength <= mechs->info.ulMaxKeySize) && - (keyLength >= mechs->info.ulMinKeySize) && - ((keyLength - mechs->info.ulMinKeySize) % mechs->step) == 0) && - ((mechs->special == SFTKFIPSNone) || - sftk_handleSpecial(slot, mech, mechs, source))) { + if ((mech->mechanism == mechs->type) && + (opFlags == (mechs->info.flags & opFlags)) && + sftk_checkKeyLength(keyLength, mechs->info.ulMinKeySize, + mechs->info.ulMaxKeySize, mechs->step) && + ((targetKeyLength == 0) || (mechs->special == SFTKFIPSTlsKeyCheck) + || sftk_checkKeyLength(targetKeyLength, mechs->info.ulMinKeySize, + mechs->info.ulMaxKeySize, mechs->step)) && + ((mechs->special == SFTKFIPSNone) || + sftk_handleSpecial(slot, mech, mechs, source, keyLength, targetKeyLength))) { return PR_TRUE; } } return PR_FALSE; #endif } /* diff --git a/lib/softoken/sftkike.c b/lib/softoken/sftkike.c --- a/lib/softoken/sftkike.c +++ b/lib/softoken/sftkike.c @@ -511,16 +511,22 @@ sftk_ike_prf(CK_SESSION_HANDLE hSession, } /* key as the data */ crv = prf_update(&context, inKey->attrib.pValue, inKey->attrib.ulValueLen); if (crv != CKR_OK) { goto fail; } } else { + /* ikev1 isn't validated, if we use this function in ikev1 mode, + * mark the resulting key as not FIPS */ + if (!params->bRekey) { + outKey->isFIPS = PR_FALSE; + } + crv = prf_init(&context, inKey->attrib.pValue, inKey->attrib.ulValueLen); if (crv != CKR_OK) { goto fail; } if (newKeyValue) { crv = prf_update(&context, newKeyValue->attrib.pValue, newKeyValue->attrib.ulValueLen); diff --git a/lib/softoken/sftkmessage.c b/lib/softoken/sftkmessage.c --- a/lib/softoken/sftkmessage.c +++ b/lib/softoken/sftkmessage.c @@ -178,16 +178,48 @@ sftk_CryptMessage(CK_SESSION_HANDLE hSes CHECK_FORK(); /* make sure we're legal */ crv = sftk_GetContext(hSession, &context, contextType, PR_TRUE, NULL); if (crv != CKR_OK) return crv; + if (context->isFIPS && (contextType == SFTK_MESSAGE_ENCRYPT)) { + if ((pParameter == NULL) || (ulParameterLen != sizeof(CK_GCM_MESSAGE_PARAMS))) { + context->isFIPS = PR_FALSE; + } else { + CK_GCM_MESSAGE_PARAMS *p = (CK_GCM_MESSAGE_PARAMS *)pParameter; + switch (p->ivGenerator) { + default: + case CKG_NO_GENERATE: + context->isFIPS = PR_FALSE; + break; + case CKG_GENERATE_RANDOM: + if ((p->ulIvLen < 96/PR_BITS_PER_BYTE) || + (p->ulIvFixedBits != 0)) { + context->isFIPS = PR_FALSE; + } + break; + case CKG_GENERATE_COUNTER_XOR: + if ((p->ulIvLen != 96/PR_BITS_PER_BYTE) || + (p->ulIvFixedBits != 32)) { + context->isFIPS = PR_FALSE; + } + break; + case CKG_GENERATE_COUNTER: + if ((p->ulIvFixedBits < 32) || + ((p->ulIvLen*PR_BITS_PER_BYTE - p->ulIvFixedBits) < 32)) { + context->isFIPS = PR_FALSE; + } + break; + } + } + } + if (!pOuttext) { *pulOuttextLen = ulIntextLen; return CKR_OK; } rv = (*context->aeadUpdate)(context->cipherInfo, pOuttext, &outlen, maxout, pIntext, ulIntextLen, pParameter, ulParameterLen, pAssociatedData, ulAssociatedDataLen); diff --git a/lib/util/pkcs11n.h b/lib/util/pkcs11n.h --- a/lib/util/pkcs11n.h +++ b/lib/util/pkcs11n.h @@ -148,16 +148,18 @@ /* * NSS-defined certificate types * */ #define CKC_NSS (CKC_VENDOR_DEFINED | NSSCK_VENDOR_NSS) /* FAKE PKCS #11 defines */ #define CKA_DIGEST 0x81000000L +#define CKA_NSS_GENERATE 0x81000001L +#define CKA_NSS_GENERATE_KEY_PAIR 0x81000002L #define CKA_NSS_MESSAGE 0x82000000L #define CKA_NSS_MESSAGE_MASK 0xff000000L #define CKA_FLAGS_ONLY 0 /* CKA_CLASS */ /* * NSS-defined object attributes * */