diff options
Diffstat (limited to 'security/nss/lib/softoken')
-rw-r--r-- | security/nss/lib/softoken/fipstest.c | 5 | ||||
-rw-r--r-- | security/nss/lib/softoken/fipstokn.c | 28 | ||||
-rw-r--r-- | security/nss/lib/softoken/legacydb/lgattr.c | 2 | ||||
-rw-r--r-- | security/nss/lib/softoken/lgglue.c | 13 | ||||
-rw-r--r-- | security/nss/lib/softoken/lowkey.c | 27 | ||||
-rw-r--r-- | security/nss/lib/softoken/manifest.mn | 1 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11.c | 147 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11c.c | 731 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11i.h | 132 | ||||
-rw-r--r-- | security/nss/lib/softoken/pkcs11u.c | 43 | ||||
-rw-r--r-- | security/nss/lib/softoken/sdb.c | 106 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkdb.c | 10 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkdb.h | 2 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkdbti.h | 1 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkhmac.c | 289 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkike.c | 1294 | ||||
-rw-r--r-- | security/nss/lib/softoken/sftkpwd.c | 307 | ||||
-rw-r--r-- | security/nss/lib/softoken/softkver.h | 6 | ||||
-rw-r--r-- | security/nss/lib/softoken/softoken.gyp | 3 | ||||
-rw-r--r-- | security/nss/lib/softoken/tlsprf.c | 2 |
20 files changed, 2716 insertions, 433 deletions
diff --git a/security/nss/lib/softoken/fipstest.c b/security/nss/lib/softoken/fipstest.c index 0cca74d6e..e765cde4c 100644 --- a/security/nss/lib/softoken/fipstest.c +++ b/security/nss/lib/softoken/fipstest.c @@ -12,6 +12,7 @@ #include "lowkeyi.h" #include "secoid.h" #include "secerr.h" +#include "pkcs11i.h" /* * different platforms have different ways of calling and initial entry point @@ -626,6 +627,10 @@ sftk_startup_tests(void) * the token */ return; } + rv = sftk_fips_IKE_PowerUpSelfTests(); + if (rv != SECSuccess) { + return; + } sftk_self_tests_success = PR_TRUE; } diff --git a/security/nss/lib/softoken/fipstokn.c b/security/nss/lib/softoken/fipstokn.c index ca7d7998a..166f19de2 100644 --- a/security/nss/lib/softoken/fipstokn.c +++ b/security/nss/lib/softoken/fipstokn.c @@ -645,17 +645,37 @@ FC_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin, CHECK_FORK(); - if ((rv = sftk_fipsCheck()) == CKR_OK && - (rv = sftk_newPinCheck(pNewPin, usNewLen)) == CKR_OK) { + rv = sftk_fipsCheck(); + if (rv != CKR_OK) { + goto loser; + } + + if (isLevel2 || usNewLen > 0) { + rv = sftk_newPinCheck(pNewPin, usNewLen); + if (rv != CKR_OK) { + goto loser; + } rv = NSC_SetPIN(hSession, pOldPin, usOldLen, pNewPin, usNewLen); - if ((rv == CKR_OK) && - (sftk_SlotIDFromSessionHandle(hSession) == FIPS_SLOT_ID)) { + if (rv != CKR_OK) { + goto loser; + } + if (sftk_SlotIDFromSessionHandle(hSession) == FIPS_SLOT_ID) { /* if we set the password in level1 we now go * to level2. NOTE: we don't allow the user to * go from level2 to level1 */ isLevel2 = PR_TRUE; } + } else { + /* here both old and new passwords are empty, but we need to + * call NSC_SetPIN to force rekey the database entries */ + PORT_Assert(usNewLen == 0); + rv = NSC_SetPIN(hSession, pOldPin, usOldLen, pNewPin, usNewLen); + if (rv != CKR_OK) { + goto loser; + } } + +loser: if (sftk_audit_enabled) { char msg[128]; NSSAuditSeverity severity = (rv == CKR_OK) ? NSS_AUDIT_INFO : NSS_AUDIT_ERROR; diff --git a/security/nss/lib/softoken/legacydb/lgattr.c b/security/nss/lib/softoken/legacydb/lgattr.c index 3d77bd056..c1865a38e 100644 --- a/security/nss/lib/softoken/legacydb/lgattr.c +++ b/security/nss/lib/softoken/legacydb/lgattr.c @@ -1069,7 +1069,7 @@ lg_FindTrustAttribute(LGObjectCache *obj, CK_ATTRIBUTE_TYPE type, NSSLOWCERTCertificate *cert; unsigned char hash[SHA1_LENGTH]; unsigned int trustFlags; - CK_RV crv; + CK_RV crv = CKR_CANCEL; switch (type) { case CKA_PRIVATE: diff --git a/security/nss/lib/softoken/lgglue.c b/security/nss/lib/softoken/lgglue.c index 67f17943b..8cfb4ec56 100644 --- a/security/nss/lib/softoken/lgglue.c +++ b/security/nss/lib/softoken/lgglue.c @@ -194,12 +194,15 @@ sftkdb_encrypt_stub(PLArenaPool *arena, SDB *sdb, SECItem *plainText, /* PORT_SetError */ return SECFailure; } - if (handle->newKey) { - key = handle->newKey; - iterationCount = handle->newDefaultIterationCount; + key = handle->newKey ? handle->newKey : &handle->passwordKey; + if (sftk_isLegacyIterationCountAllowed()) { + if (handle->newKey) { + iterationCount = handle->newDefaultIterationCount; + } else { + iterationCount = handle->defaultIterationCount; + } } else { - key = &handle->passwordKey; - iterationCount = handle->defaultIterationCount; + iterationCount = 1; } rv = sftkdb_EncryptAttribute(arena, key, iterationCount, diff --git a/security/nss/lib/softoken/lowkey.c b/security/nss/lib/softoken/lowkey.c index a28a3a55e..e9bbff1d9 100644 --- a/security/nss/lib/softoken/lowkey.c +++ b/security/nss/lib/softoken/lowkey.c @@ -261,6 +261,7 @@ NSSLOWKEYPublicKey * nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk) { NSSLOWKEYPublicKey *pubk; + SECItem publicValue; PLArenaPool *arena; arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); @@ -301,6 +302,19 @@ nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk) pubk->arena = arena; pubk->keyType = privk->keyType; + /* if the public key value doesn't exist, calculate it */ + if (privk->u.dsa.publicValue.len == 0) { + rv = DH_Derive(&privk->u.dsa.params.base, &privk->u.dsa.params.prime, + &privk->u.dsa.privateValue, &publicValue, 0); + if (rv != SECSuccess) { + break; + } + rv = SECITEM_CopyItem(privk->arena, &privk->u.dsa.publicValue, &publicValue); + SECITEM_FreeItem(&publicValue, PR_FALSE); + if (rv != SECSuccess) { + break; + } + } rv = SECITEM_CopyItem(arena, &pubk->u.dsa.publicValue, &privk->u.dsa.publicValue); if (rv != SECSuccess) @@ -327,6 +341,19 @@ nsslowkey_ConvertToPublicKey(NSSLOWKEYPrivateKey *privk) pubk->arena = arena; pubk->keyType = privk->keyType; + /* if the public key value doesn't exist, calculate it */ + if (privk->u.dh.publicValue.len == 0) { + rv = DH_Derive(&privk->u.dh.base, &privk->u.dh.prime, + &privk->u.dh.privateValue, &publicValue, 0); + if (rv != SECSuccess) { + break; + } + rv = SECITEM_CopyItem(privk->arena, &privk->u.dh.publicValue, &publicValue); + SECITEM_FreeItem(&publicValue, PR_FALSE); + if (rv != SECSuccess) { + break; + } + } rv = SECITEM_CopyItem(arena, &pubk->u.dh.publicValue, &privk->u.dh.publicValue); if (rv != SECSuccess) diff --git a/security/nss/lib/softoken/manifest.mn b/security/nss/lib/softoken/manifest.mn index 256d443ea..f25b97730 100644 --- a/security/nss/lib/softoken/manifest.mn +++ b/security/nss/lib/softoken/manifest.mn @@ -46,6 +46,7 @@ CSRCS = \ sdb.c \ sftkdb.c \ sftkhmac.c \ + sftkike.c \ sftkpars.c \ sftkpwd.c \ softkver.c \ diff --git a/security/nss/lib/softoken/pkcs11.c b/security/nss/lib/softoken/pkcs11.c index 7f0adf82f..7f3a01746 100644 --- a/security/nss/lib/softoken/pkcs11.c +++ b/security/nss/lib/softoken/pkcs11.c @@ -324,10 +324,14 @@ static const struct mechanismList mechanisms[] = { { CKM_AES_CBC, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE }, { CKM_AES_MAC, { 16, 32, CKF_SN_VR }, PR_TRUE }, { CKM_AES_MAC_GENERAL, { 16, 32, CKF_SN_VR }, PR_TRUE }, + { CKM_AES_CMAC, { 16, 32, CKF_SN_VR }, PR_TRUE }, + { CKM_AES_CMAC_GENERAL, { 16, 32, CKF_SN_VR }, PR_TRUE }, { CKM_AES_CBC_PAD, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE }, { CKM_AES_CTS, { 16, 32, CKF_EN_DE }, PR_TRUE }, { CKM_AES_CTR, { 16, 32, CKF_EN_DE }, PR_TRUE }, { CKM_AES_GCM, { 16, 32, CKF_EN_DE }, PR_TRUE }, + { CKM_AES_XCBC_MAC_96, { 16, 16, CKF_SN_VR }, PR_TRUE }, + { CKM_AES_XCBC_MAC, { 16, 16, CKF_SN_VR }, PR_TRUE }, /* ------------------------- Camellia Operations --------------------- */ { CKM_CAMELLIA_KEY_GEN, { 16, 32, CKF_GENERATE }, PR_TRUE }, { CKM_CAMELLIA_ECB, { 16, 32, CKF_EN_DE_WR_UN }, PR_TRUE }, @@ -346,6 +350,7 @@ static const struct mechanismList mechanisms[] = { /* ------------------------- ChaCha20 Operations ---------------------- */ { CKM_NSS_CHACHA20_KEY_GEN, { 32, 32, CKF_GENERATE }, PR_TRUE }, { CKM_NSS_CHACHA20_POLY1305, { 32, 32, CKF_EN_DE }, PR_TRUE }, + { CKM_NSS_CHACHA20_CTR, { 32, 32, CKF_EN_DE }, PR_TRUE }, #endif /* NSS_DISABLE_CHACHAPOLY */ /* ------------------------- Hashing Operations ----------------------- */ { CKM_MD2, { 0, 0, CKF_DIGEST }, PR_FALSE }, @@ -509,7 +514,11 @@ static const struct mechanismList mechanisms[] = { { CKM_NSS_JPAKE_FINAL_SHA512, { 0, 0, CKF_DERIVE }, PR_TRUE }, /* -------------------- Constant Time TLS MACs ----------------------- */ { CKM_NSS_HMAC_CONSTANT_TIME, { 0, 0, CKF_DIGEST }, PR_TRUE }, - { CKM_NSS_SSL3_MAC_CONSTANT_TIME, { 0, 0, CKF_DIGEST }, PR_TRUE } + { CKM_NSS_SSL3_MAC_CONSTANT_TIME, { 0, 0, CKF_DIGEST }, PR_TRUE }, + /* --------------------IPSEC ----------------------- */ + { CKM_NSS_IKE_PRF_PLUS_DERIVE, { 8, 255 * 64, CKF_DERIVE }, PR_TRUE }, + { CKM_NSS_IKE_PRF_DERIVE, { 8, 64, CKF_DERIVE }, PR_TRUE }, + { CKM_NSS_IKE1_PRF_DERIVE, { 8, 64, CKF_DERIVE }, PR_TRUE } }; static const CK_ULONG mechanismCount = sizeof(mechanisms) / sizeof(mechanisms[0]); @@ -1815,8 +1824,6 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type, break; /* key was not DER encoded, no need to unwrap */ } - PORT_Assert(pubKey->u.ec.ecParams.name != ECCurve25519); - /* handle the encoded case */ if ((pubKey->u.ec.publicValue.data[0] == SEC_ASN1_OCTET_STRING) && pubKey->u.ec.publicValue.len > keyLen) { @@ -1827,7 +1834,13 @@ sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type, SEC_ASN1_GET(SEC_OctetStringTemplate), &pubKey->u.ec.publicValue); /* nope, didn't decode correctly */ - if ((rv != SECSuccess) || (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED) || (publicValue.len != keyLen)) { + if ((rv != SECSuccess) || (publicValue.len != keyLen)) { + crv = CKR_ATTRIBUTE_VALUE_INVALID; + break; + } + /* we don't handle compressed points except in the case of ECCurve25519 */ + if ((pubKey->u.ec.ecParams.fieldID.type != ec_field_plain) && + (publicValue.data[0] != EC_POINT_FORM_UNCOMPRESSED)) { crv = CKR_ATTRIBUTE_VALUE_INVALID; break; } @@ -2195,6 +2208,124 @@ sftk_GetPrivKey(SFTKObject *object, CK_KEY_TYPE key_type, CK_RV *crvp) return priv; } +/* populate a public key object from a lowpublic keys structure */ +CK_RV +sftk_PutPubKey(SFTKObject *publicKey, SFTKObject *privateKey, CK_KEY_TYPE keyType, NSSLOWKEYPublicKey *pubKey) +{ + CK_OBJECT_CLASS classType = CKO_PUBLIC_KEY; + CK_BBOOL cktrue = CK_TRUE; + CK_RV crv = CKR_OK; + sftk_DeleteAttributeType(publicKey, CKA_CLASS); + sftk_DeleteAttributeType(publicKey, CKA_KEY_TYPE); + sftk_DeleteAttributeType(publicKey, CKA_VALUE); + + switch (keyType) { + case CKK_RSA: + sftk_DeleteAttributeType(publicKey, CKA_MODULUS); + sftk_DeleteAttributeType(publicKey, CKA_PUBLIC_EXPONENT); + /* format the keys */ + /* fill in the RSA dependent paramenters in the public key */ + crv = sftk_AddAttributeType(publicKey, CKA_MODULUS, + sftk_item_expand(&pubKey->u.rsa.modulus)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_PUBLIC_EXPONENT, + sftk_item_expand(&pubKey->u.rsa.publicExponent)); + break; + case CKK_DSA: + sftk_DeleteAttributeType(publicKey, CKA_PRIME); + sftk_DeleteAttributeType(publicKey, CKA_SUBPRIME); + sftk_DeleteAttributeType(publicKey, CKA_BASE); + crv = sftk_AddAttributeType(publicKey, CKA_PRIME, + sftk_item_expand(&pubKey->u.dsa.params.prime)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_SUBPRIME, + sftk_item_expand(&pubKey->u.dsa.params.subPrime)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_BASE, + sftk_item_expand(&pubKey->u.dsa.params.base)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_VALUE, + sftk_item_expand(&pubKey->u.dsa.publicValue)); + break; + case CKK_DH: + sftk_DeleteAttributeType(publicKey, CKA_PRIME); + sftk_DeleteAttributeType(publicKey, CKA_BASE); + crv = sftk_AddAttributeType(publicKey, CKA_PRIME, + sftk_item_expand(&pubKey->u.dh.prime)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_BASE, + sftk_item_expand(&pubKey->u.dh.base)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_VALUE, + sftk_item_expand(&pubKey->u.dh.publicValue)); + break; + case CKK_EC: + sftk_DeleteAttributeType(publicKey, CKA_EC_PARAMS); + sftk_DeleteAttributeType(publicKey, CKA_EC_POINT); + crv = sftk_AddAttributeType(publicKey, CKA_EC_PARAMS, + sftk_item_expand(&pubKey->u.ec.ecParams.DEREncoding)); + if (crv != CKR_OK) { + break; + } + crv = sftk_AddAttributeType(publicKey, CKA_EC_POINT, + sftk_item_expand(&pubKey->u.ec.publicValue)); + break; + default: + return CKR_KEY_TYPE_INCONSISTENT; + } + if (crv != CKR_OK) { + return crv; + } + crv = sftk_AddAttributeType(publicKey, CKA_CLASS, &classType, + sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) { + return crv; + } + crv = sftk_AddAttributeType(publicKey, CKA_KEY_TYPE, &keyType, + sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) { + return crv; + } + /* now handle the operator attributes */ + if (sftk_isTrue(privateKey, CKA_DECRYPT)) { + crv = sftk_forceAttribute(publicKey, CKA_ENCRYPT, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + return crv; + } + } + if (sftk_isTrue(privateKey, CKA_SIGN)) { + crv = sftk_forceAttribute(publicKey, CKA_VERIFY, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + return crv; + } + } + if (sftk_isTrue(privateKey, CKA_SIGN_RECOVER)) { + crv = sftk_forceAttribute(publicKey, CKA_VERIFY_RECOVER, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + return crv; + } + } + if (sftk_isTrue(privateKey, CKA_DERIVE)) { + crv = sftk_forceAttribute(publicKey, CKA_DERIVE, &cktrue, sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + return crv; + } + } + return crv; +} + /* **************************** Symetric Key utils ************************ */ @@ -3153,7 +3284,7 @@ nsc_CommonFinalize(CK_VOID_PTR pReserved, PRBool isFIPS) * this call doesn't force freebl to be reloaded. */ BL_SetForkState(PR_FALSE); -#ifndef NSS_TEST_BUILD +#ifndef NSS_STATIC_SOFTOKEN /* unload freeBL shared library from memory. This may only decrement the * OS refcount if it's been loaded multiple times, eg. by libssl */ BL_Unload(); @@ -3777,7 +3908,10 @@ NSC_SetPIN(CK_SESSION_HANDLE hSession, CK_CHAR_PTR pOldPin, crv = CKR_PIN_LEN_RANGE; goto loser; } - if (ulNewLen < (CK_ULONG)slot->minimumPinLen) { + /* check the length of new pin, unless both old and new passwords + * are empty */ + if ((ulNewLen != 0 || ulOldLen != 0) && + ulNewLen < (CK_ULONG)slot->minimumPinLen) { crv = CKR_PIN_LEN_RANGE; goto loser; } @@ -3935,7 +4069,6 @@ NSC_CloseSession(CK_SESSION_HANDLE hSession) session = NULL; } - sftk_FreeSession(session); return CKR_OK; } diff --git a/security/nss/lib/softoken/pkcs11c.c b/security/nss/lib/softoken/pkcs11c.c index 4837961f1..6bc5cc939 100644 --- a/security/nss/lib/softoken/pkcs11c.c +++ b/security/nss/lib/softoken/pkcs11c.c @@ -30,6 +30,7 @@ #include "lowpbe.h" /* We do PBE below */ #include "pkcs11t.h" #include "secoid.h" +#include "cmac.h" #include "alghmac.h" #include "softoken.h" #include "secasn1.h" @@ -39,7 +40,7 @@ #include "prenv.h" #define __PASTE(x, y) x##y - +#define BAD_PARAM_CAST(pMech, typeSize) (!pMech->pParameter || pMech->ulParameterLen < typeSize) /* * we renamed all our internal functions, get the correct * definitions for them... @@ -96,42 +97,10 @@ sftk_Space(void *data, PRBool freeit) /* * map all the SEC_ERROR_xxx error codes that may be returned by freebl - * functions to CKR_xxx. return CKR_DEVICE_ERROR by default for backward - * compatibility. + * functions to CKR_xxx. Most of the mapping is done in + * sftk_mapCryptError (now in pkcs11u.c). The next two functions adjust + * that mapping based for different contexts (Decrypt or Verify). */ -static CK_RV -sftk_MapCryptError(int error) -{ - switch (error) { - case SEC_ERROR_INVALID_ARGS: - case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */ - return CKR_ARGUMENTS_BAD; - case SEC_ERROR_INPUT_LEN: - return CKR_DATA_LEN_RANGE; - case SEC_ERROR_OUTPUT_LEN: - return CKR_BUFFER_TOO_SMALL; - case SEC_ERROR_LIBRARY_FAILURE: - return CKR_GENERAL_ERROR; - case SEC_ERROR_NO_MEMORY: - return CKR_HOST_MEMORY; - case SEC_ERROR_BAD_SIGNATURE: - return CKR_SIGNATURE_INVALID; - case SEC_ERROR_INVALID_KEY: - return CKR_KEY_SIZE_RANGE; - case SEC_ERROR_BAD_KEY: /* an EC public key that fails validation */ - return CKR_KEY_SIZE_RANGE; /* the closest error code */ - case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM: - return CKR_TEMPLATE_INCONSISTENT; - case SEC_ERROR_UNSUPPORTED_KEYALG: - return CKR_MECHANISM_INVALID; - case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE: - return CKR_DOMAIN_PARAMS_INVALID; - /* key pair generation failed after max number of attempts */ - case SEC_ERROR_NEED_RANDOM: - return CKR_FUNCTION_FAILED; - } - return CKR_DEVICE_ERROR; -} /* used by Decrypt and UnwrapKey (indirectly) */ static CK_RV @@ -760,6 +729,32 @@ sftk_ChaCha20Poly1305_Decrypt(const SFTKChaCha20Poly1305Info *ctx, sizeof(ctx->nonce), ad, ctx->adLen); } +static SECStatus +sftk_ChaCha20Ctr(const SFTKChaCha20CtrInfo *ctx, + unsigned char *output, unsigned int *outputLen, + unsigned int maxOutputLen, + const unsigned char *input, unsigned int inputLen) +{ + if (maxOutputLen < inputLen) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + ChaCha20_Xor(output, input, inputLen, ctx->key, + ctx->nonce, ctx->counter); + *outputLen = inputLen; + return SECSuccess; +} + +static void +sftk_ChaCha20Ctr_DestroyContext(SFTKChaCha20CtrInfo *ctx, + PRBool freeit) +{ + memset(ctx, 0, sizeof(*ctx)); + if (freeit) { + PORT_Free(ctx); + } +} + /** NSC_CryptInit initializes an encryption/Decryption operation. * * Always called by NSC_EncryptInit, NSC_DecryptInit, NSC_WrapKey,NSC_UnwrapKey. @@ -788,6 +783,10 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, PRBool useNewKey = PR_FALSE; int t; + if (!pMechanism) { + return CKR_MECHANISM_PARAM_INVALID; + } + crv = sftk_MechAllowsOperation(pMechanism->mechanism, mechUsage); if (crv != CKR_OK) return crv; @@ -901,6 +900,11 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, crv = CKR_KEY_HANDLE_INVALID; break; } + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC2_CBC_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } rc2_param = (CK_RC2_CBC_PARAMS *)pMechanism->pParameter; effectiveKeyLength = (rc2_param->ulEffectiveBits + 7) / 8; context->cipherInfo = @@ -930,6 +934,11 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, crv = CKR_KEY_HANDLE_INVALID; break; } + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_CBC_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } rc5_param = (CK_RC5_CBC_PARAMS *)pMechanism->pParameter; context->blockSize = rc5_param->ulWordsize * 2; rc5Key.data = (unsigned char *)att->attrib.pValue; @@ -993,6 +1002,10 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, crv = CKR_KEY_TYPE_INCONSISTENT; break; } + if (pMechanism->ulParameterLen < 8) { + crv = CKR_DOMAIN_PARAMS_INVALID; + break; + } t = NSS_DES_CBC; goto finish_des; case CKM_DES3_ECB: @@ -1010,6 +1023,10 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, crv = CKR_KEY_TYPE_INCONSISTENT; break; } + if (pMechanism->ulParameterLen < 8) { + crv = CKR_DOMAIN_PARAMS_INVALID; + break; + } t = NSS_DES_EDE3_CBC; finish_des: context->blockSize = 8; @@ -1127,6 +1144,13 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, case CKM_AES_CTS: case CKM_AES_CTR: case CKM_AES_GCM: + if ((pMechanism->mechanism == CKM_AES_GCM && BAD_PARAM_CAST(pMechanism, sizeof(CK_GCM_PARAMS))) || + (pMechanism->mechanism == CKM_AES_CTR && BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CTR_PARAMS))) || + ((pMechanism->mechanism == CKM_AES_CBC || pMechanism->mechanism == CKM_AES_CTS) && BAD_PARAM_CAST(pMechanism, AES_BLOCK_SIZE))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + if (pMechanism->mechanism == CKM_AES_GCM) { context->multi = PR_FALSE; } @@ -1180,6 +1204,48 @@ sftk_CryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, context->destroy = (SFTKDestroy)sftk_ChaCha20Poly1305_DestroyContext; break; + case CKM_NSS_CHACHA20_CTR: + if (key_type != CKK_NSS_CHACHA20) { + crv = CKR_KEY_TYPE_INCONSISTENT; + break; + } + if (pMechanism->pParameter == NULL || pMechanism->ulParameterLen != 16) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + + att = sftk_FindAttribute(key, CKA_VALUE); + if (att == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + break; + } + SFTKChaCha20CtrInfo *ctx = PORT_ZNew(SFTKChaCha20CtrInfo); + if (!ctx) { + sftk_FreeAttribute(att); + crv = CKR_HOST_MEMORY; + break; + } + if (att->attrib.ulValueLen != sizeof(ctx->key)) { + sftk_FreeAttribute(att); + PORT_Free(ctx); + crv = CKR_KEY_HANDLE_INVALID; + break; + } + memcpy(ctx->key, att->attrib.pValue, att->attrib.ulValueLen); + sftk_FreeAttribute(att); + + /* The counter is little endian. */ + PRUint8 *param = pMechanism->pParameter; + int i = 0; + for (; i < 4; ++i) { + ctx->counter |= param[i] << (i * 8); + } + memcpy(ctx->nonce, param + 4, 12); + context->cipherInfo = ctx; + context->update = (SFTKCipher)sftk_ChaCha20Ctr; + context->destroy = (SFTKDestroy)sftk_ChaCha20Ctr_DestroyContext; + break; + case CKM_NSS_AES_KEY_WRAP_PAD: context->doPad = PR_TRUE; /* fall thru */ @@ -1311,8 +1377,11 @@ NSC_EncryptUpdate(CK_SESSION_HANDLE hSession, /* do it: NOTE: this assumes buf size in is >= buf size out! */ rv = (*context->update)(context->cipherInfo, pEncryptedPart, &outlen, maxout, pPart, ulPartLen); + if (rv != SECSuccess) { + return sftk_MapCryptError(PORT_GetError()); + } *pulEncryptedPartLen = (CK_ULONG)(outlen + padoutlen); - return (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); + return CKR_OK; } /* NSC_EncryptFinal finishes a multiple-part encryption operation. */ @@ -1392,26 +1461,29 @@ NSC_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, return crv; if (!pEncryptedData) { - *pulEncryptedDataLen = context->rsa ? context->maxLen : ulDataLen + 2 * context->blockSize; - goto finish; + outlen = context->rsa ? context->maxLen : ulDataLen + 2 * context->blockSize; + goto done; } if (context->doPad) { if (context->multi) { + CK_ULONG updateLen = maxoutlen; CK_ULONG finalLen; /* padding is fairly complicated, have the update and final * code deal with it */ sftk_FreeSession(session); crv = NSC_EncryptUpdate(hSession, pData, ulDataLen, pEncryptedData, - pulEncryptedDataLen); - if (crv != CKR_OK) - *pulEncryptedDataLen = 0; - maxoutlen -= *pulEncryptedDataLen; - pEncryptedData += *pulEncryptedDataLen; + &updateLen); + if (crv != CKR_OK) { + updateLen = 0; + } + maxoutlen -= updateLen; + pEncryptedData += updateLen; finalLen = maxoutlen; crv2 = NSC_EncryptFinal(hSession, pEncryptedData, &finalLen); - if (crv2 == CKR_OK) - *pulEncryptedDataLen += finalLen; + if (crv == CKR_OK && crv2 == CKR_OK) { + *pulEncryptedDataLen = updateLen + finalLen; + } return crv == CKR_OK ? crv2 : crv; } /* doPad without multi means that padding must be done on the first @@ -1437,14 +1509,15 @@ NSC_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, rv = (*context->update)(context->cipherInfo, pEncryptedData, &outlen, maxoutlen, pText.data, pText.len); crv = (rv == SECSuccess) ? CKR_OK : sftk_MapCryptError(PORT_GetError()); - *pulEncryptedDataLen = (CK_ULONG)outlen; if (pText.data != pData) PORT_ZFree(pText.data, pText.len); fail: sftk_TerminateOp(session, SFTK_ENCRYPT, context); -finish: +done: sftk_FreeSession(session); - + if (crv == CKR_OK) { + *pulEncryptedDataLen = (CK_ULONG)outlen; + } return crv; } @@ -1533,8 +1606,73 @@ NSC_DecryptUpdate(CK_SESSION_HANDLE hSession, /* do it: NOTE: this assumes buf size in is >= buf size out! */ rv = (*context->update)(context->cipherInfo, pPart, &outlen, maxout, pEncryptedPart, ulEncryptedPartLen); + if (rv != SECSuccess) { + return sftk_MapDecryptError(PORT_GetError()); + } *pulPartLen = (CK_ULONG)(outlen + padoutlen); - return (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); + return CKR_OK; +} + +/* From ssl3con.c: Constant-time helper macro that copies the MSB of x to all + * other bits. */ +#define DUPLICATE_MSB_TO_ALL(x) ((unsigned int)((int)(x) >> (sizeof(int) * 8 - 1))) +/* CK_RVToMask returns, in constant time, a mask value of + * all ones if rv == CKR_OK. Otherwise it returns zero. */ +static unsigned int +CK_RVToMask(CK_RV rv) +{ + unsigned int good; + /* rv ^ CKR_OK is zero iff rv == CKR_OK. Subtracting one results + * in the MSB being set to one iff it was zero before. */ + good = rv ^ CKR_OK; + good--; + return DUPLICATE_MSB_TO_ALL(good); +} +/* Constant-time helper macro that selects l or r depending on all-1 or all-0 + * mask m */ +#define CT_SEL(m, l, r) (((m) & (l)) | (~(m) & (r))) +/* Constant-time helper macro that returns all-1s if x is not 0; and all-0s + * otherwise. */ +#define CT_NOT_ZERO(x) (DUPLICATE_MSB_TO_ALL(((x) | (0 - x)))) + +/* sftk_CheckCBCPadding checks, in constant time, the padding validity and + * accordingly sets the pad length. */ +static CK_RV +sftk_CheckCBCPadding(CK_BYTE_PTR pLastPart, + unsigned int blockSize, unsigned int *outPadSize) +{ + PORT_Assert(outPadSize); + + unsigned int padSize = (unsigned int)pLastPart[blockSize - 1]; + + /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/ + unsigned int goodPad = DUPLICATE_MSB_TO_ALL(~(blockSize - padSize)); + /* padSize should not be 0 */ + goodPad &= CT_NOT_ZERO(padSize); + + unsigned int i; + for (i = 0; i < blockSize; i++) { + /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/ + unsigned int loopMask = DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i)); + /* Get the padding value (should be padSize) from buffer */ + unsigned int padVal = pLastPart[blockSize - 1 - i]; + /* Update goodPad only if i < padSize */ + goodPad &= CT_SEL(loopMask, ~(padVal ^ padSize), goodPad); + } + + /* If any of the final padding bytes had the wrong value, one or more + * of the lower eight bits of |goodPad| will be cleared. We AND the + * bottom 8 bits together and duplicate the result to all the bits. */ + goodPad &= goodPad >> 4; + goodPad &= goodPad >> 2; + goodPad &= goodPad >> 1; + goodPad <<= sizeof(goodPad) * 8 - 1; + goodPad = DUPLICATE_MSB_TO_ALL(goodPad); + + /* Set outPadSize to padSize or 0 */ + *outPadSize = CT_SEL(goodPad, padSize, 0); + /* Return OK if the pad is valid */ + return CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID); } /* NSC_DecryptFinal finishes a multiple-part decryption operation. */ @@ -1575,24 +1713,10 @@ NSC_DecryptFinal(CK_SESSION_HANDLE hSession, if (rv != SECSuccess) { crv = sftk_MapDecryptError(PORT_GetError()); } else { - unsigned int padSize = - (unsigned int)pLastPart[context->blockSize - 1]; - if ((padSize > context->blockSize) || (padSize == 0)) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - unsigned int i; - unsigned int badPadding = 0; /* used as a boolean */ - for (i = 0; i < padSize; i++) { - badPadding |= - (unsigned int)pLastPart[context->blockSize - 1 - i] ^ - padSize; - } - if (badPadding) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - *pulLastPartLen = outlen - padSize; - } - } + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(&pLastPart[outlen - context->blockSize], context->blockSize, &padSize); + /* Update pulLastPartLen, in constant time, if crv is OK */ + *pulLastPartLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulLastPartLen); } } } @@ -1625,52 +1749,48 @@ NSC_Decrypt(CK_SESSION_HANDLE hSession, return crv; if (!pData) { - *pulDataLen = ulEncryptedDataLen + context->blockSize; - goto finish; + *pulDataLen = (CK_ULONG)(ulEncryptedDataLen + context->blockSize); + goto done; } if (context->doPad && context->multi) { + CK_ULONG updateLen = maxoutlen; CK_ULONG finalLen; /* padding is fairly complicated, have the update and final * code deal with it */ sftk_FreeSession(session); crv = NSC_DecryptUpdate(hSession, pEncryptedData, ulEncryptedDataLen, - pData, pulDataLen); - if (crv != CKR_OK) - *pulDataLen = 0; - maxoutlen -= *pulDataLen; - pData += *pulDataLen; + pData, &updateLen); + if (crv == CKR_OK) { + maxoutlen -= updateLen; + pData += updateLen; + } finalLen = maxoutlen; crv2 = NSC_DecryptFinal(hSession, pData, &finalLen); - if (crv2 == CKR_OK) - *pulDataLen += finalLen; - return crv == CKR_OK ? crv2 : crv; + if (crv == CKR_OK) { + *pulDataLen = CT_SEL(CK_RVToMask(crv2), updateLen + finalLen, *pulDataLen); + return crv2; + } else { + return crv; + } } rv = (*context->update)(context->cipherInfo, pData, &outlen, maxoutlen, pEncryptedData, ulEncryptedDataLen); /* XXX need to do MUCH better error mapping than this. */ crv = (rv == SECSuccess) ? CKR_OK : sftk_MapDecryptError(PORT_GetError()); - if (rv == SECSuccess && context->doPad) { - unsigned int padding = pData[outlen - 1]; - if (padding > context->blockSize || !padding) { - crv = CKR_ENCRYPTED_DATA_INVALID; + if (rv == SECSuccess) { + if (context->doPad) { + unsigned int padSize = 0; + crv = sftk_CheckCBCPadding(&pData[outlen - context->blockSize], context->blockSize, &padSize); + /* Update pulDataLen, in constant time, if crv is OK */ + *pulDataLen = CT_SEL(CK_RVToMask(crv), outlen - padSize, *pulDataLen); } else { - unsigned int i; - unsigned int badPadding = 0; /* used as a boolean */ - for (i = 0; i < padding; i++) { - badPadding |= (unsigned int)pData[outlen - 1 - i] ^ padding; - } - if (badPadding) { - crv = CKR_ENCRYPTED_DATA_INVALID; - } else { - outlen -= padding; - } + *pulDataLen = (CK_ULONG)outlen; } } - *pulDataLen = (CK_ULONG)outlen; sftk_TerminateOp(session, SFTK_DECRYPT, context); -finish: +done: sftk_FreeSession(session); return crv; } @@ -1880,58 +2000,59 @@ static SECStatus sftk_HMACCmp(CK_ULONG *copyLen, unsigned char *sig, unsigned int sigLen, unsigned char *hash, unsigned int hashLen) { - return (PORT_Memcmp(sig, hash, *copyLen) == 0) ? SECSuccess : SECFailure; + return (NSS_SecureMemcmp(sig, hash, *copyLen) == 0) ? SECSuccess : SECFailure; } /* - * common HMAC initalization routine + * common HMAC + CMAC initialization routine */ static CK_RV -sftk_doHMACInit(SFTKSessionContext *context, HASH_HashType hash, - SFTKObject *key, CK_ULONG mac_size) +sftk_doMACInit(CK_MECHANISM_TYPE mech, SFTKSessionContext *session, + SFTKObject *key, CK_ULONG mac_size) { - SFTKAttribute *keyval; - HMACContext *HMACcontext; + CK_RV crv; + sftk_MACCtx *context; CK_ULONG *intpointer; - const SECHashObject *hashObj = HASH_GetRawHashObject(hash); PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID); - /* required by FIPS 198 Section 4 */ - if (isFIPS && (mac_size < 4 || mac_size < hashObj->length / 2)) { - return CKR_BUFFER_TOO_SMALL; + /* Set up the initial context. */ + crv = sftk_MAC_Create(mech, key, &context); + if (crv != CKR_OK) { + return crv; } - keyval = sftk_FindAttribute(key, CKA_VALUE); - if (keyval == NULL) - return CKR_KEY_SIZE_RANGE; + session->hashInfo = context; + session->multi = PR_TRUE; - HMACcontext = HMAC_Create(hashObj, - (const unsigned char *)keyval->attrib.pValue, - keyval->attrib.ulValueLen, isFIPS); - context->hashInfo = HMACcontext; - context->multi = PR_TRUE; - sftk_FreeAttribute(keyval); - if (context->hashInfo == NULL) { - if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) { - return CKR_KEY_SIZE_RANGE; - } - return CKR_HOST_MEMORY; + /* Required by FIPS 198 Section 4. Delay this check until after the MAC + * has been initialized to steal the output size of the MAC. */ + if (isFIPS && (mac_size < 4 || mac_size < context->mac_size / 2)) { + sftk_MAC_Destroy(context, PR_TRUE); + return CKR_BUFFER_TOO_SMALL; } - context->hashUpdate = (SFTKHash)HMAC_Update; - context->end = (SFTKEnd)HMAC_Finish; - context->hashdestroy = (SFTKDestroy)HMAC_Destroy; + /* Configure our helper functions appropriately. Note that these casts + * ignore the return values. */ + session->hashUpdate = (SFTKHash)sftk_MAC_Update; + session->end = (SFTKEnd)sftk_MAC_Finish; + session->hashdestroy = (SFTKDestroy)sftk_MAC_Destroy; + intpointer = PORT_New(CK_ULONG); if (intpointer == NULL) { + sftk_MAC_Destroy(context, PR_TRUE); return CKR_HOST_MEMORY; } *intpointer = mac_size; - context->cipherInfo = intpointer; - context->destroy = (SFTKDestroy)sftk_Space; - context->update = (SFTKCipher)sftk_SignCopy; - context->verify = (SFTKVerify)sftk_HMACCmp; - context->maxLen = hashObj->length; - HMAC_Begin(HMACcontext); + session->cipherInfo = intpointer; + + /* Since we're only "hashing", copy the result from session->end to the + * caller using sftk_SignCopy. */ + session->update = (SFTKCipher)sftk_SignCopy; + session->verify = (SFTKVerify)sftk_HMACCmp; + session->destroy = (SFTKDestroy)sftk_Space; + + session->maxLen = context->mac_size; + return CKR_OK; } @@ -1996,7 +2117,7 @@ sftk_SSLMACVerify(SFTKSSLMACInfo *info, unsigned char *sig, unsigned int sigLen, info->update(info->hashContext, ssl_pad_2, info->padSize); info->update(info->hashContext, hash, hashLen); info->end(info->hashContext, tmpBuf, &out, SFTK_MAX_MAC_LENGTH); - return (PORT_Memcmp(sig, tmpBuf, info->macSize) == 0) ? SECSuccess : SECFailure; + return (NSS_SecureMemcmp(sig, tmpBuf, info->macSize) == 0) ? SECSuccess : SECFailure; } /* @@ -2080,13 +2201,20 @@ sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_RC5_MAC_GENERAL_PARAMS *rc5_mac; #endif unsigned char ivBlock[SFTK_MAX_BLOCK_SIZE]; + unsigned char k2[SFTK_MAX_BLOCK_SIZE]; + unsigned char k3[SFTK_MAX_BLOCK_SIZE]; SFTKSessionContext *context; CK_RV crv; unsigned int blockSize; + PRBool isXCBC = PR_FALSE; + + if (!pMechanism) { + return CKR_MECHANISM_PARAM_INVALID; + } switch (pMechanism->mechanism) { case CKM_RC2_MAC_GENERAL: - if (!pMechanism->pParameter) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC2_MAC_GENERAL_PARAMS))) { return CKR_MECHANISM_PARAM_INVALID; } mac_bytes = @@ -2106,12 +2234,18 @@ sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, break; #if NSS_SOFTOKEN_DOES_RC5 case CKM_RC5_MAC_GENERAL: + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_MAC_GENERAL_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } mac_bytes = ((CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter)->ulMacLength; /* fall through */ case CKM_RC5_MAC: /* this works because ulEffectiveBits is in the same place in both the * CK_RC5_MAC_GENERAL_PARAMS and CK_RC5_CBC_PARAMS */ + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_RC5_MAC_GENERAL_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } rc5_mac = (CK_RC5_MAC_GENERAL_PARAMS *)pMechanism->pParameter; rc5_params.ulWordsize = rc5_mac->ulWordsize; rc5_params.ulRounds = rc5_mac->ulRounds; @@ -2186,6 +2320,26 @@ sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, cbc_mechanism.pParameter = &ivBlock; cbc_mechanism.ulParameterLen = blockSize; break; + case CKM_AES_XCBC_MAC_96: + case CKM_AES_XCBC_MAC: + /* The only difference between CKM_AES_XCBC_MAC + * and CKM_AES_XCBC_MAC_96 is the size of the returned mac. */ + mac_bytes = pMechanism->mechanism == CKM_AES_XCBC_MAC_96 ? 12 : 16; + blockSize = 16; + PORT_Memset(ivBlock, 0, blockSize); + cbc_mechanism.mechanism = CKM_AES_CBC; + cbc_mechanism.pParameter = &ivBlock; + cbc_mechanism.ulParameterLen = blockSize; + /* is XCBC requires extra processing at the end of the operation */ + isXCBC = PR_TRUE; + /* The input key is used to generate k1, k2, and k3. k2 and k3 + * are used at the end in the pad step. k1 replaces the input + * key in the aes cbc mac */ + crv = sftk_aes_xcbc_new_keys(hSession, hKey, &hKey, k2, k3); + if (crv != CKR_OK) { + return crv; + } + break; default: return CKR_FUNCTION_NOT_SUPPORTED; } @@ -2195,24 +2349,43 @@ sftk_InitCBCMac(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, if (mac_bytes == SFTK_INVALID_MAC_SIZE) mac_bytes = blockSize >> 1; else { - if (mac_bytes > blockSize) - return CKR_MECHANISM_PARAM_INVALID; + if (mac_bytes > blockSize) { + crv = CKR_MECHANISM_PARAM_INVALID; + goto fail; + } } crv = sftk_CryptInit(hSession, &cbc_mechanism, hKey, CKA_ENCRYPT, /* CBC mech is able to ENCRYPT, not SIGN/VERIFY */ keyUsage, contextType, PR_TRUE); if (crv != CKR_OK) - return crv; + goto fail; crv = sftk_GetContext(hSession, &context, contextType, PR_TRUE, NULL); /* this shouldn't happen! */ PORT_Assert(crv == CKR_OK); if (crv != CKR_OK) - return crv; + goto fail; context->blockSize = blockSize; context->macSize = mac_bytes; + context->isXCBC = isXCBC; + if (isXCBC) { + /* save the xcbc specific parameters */ + PORT_Memcpy(context->k2, k2, blockSize); + PORT_Memcpy(context->k3, k3, blockSize); + PORT_Memset(k2, 0, blockSize); + PORT_Memset(k3, 0, blockSize); + /* get rid of the temp key now that the context has been created */ + NSC_DestroyObject(hSession, hKey); + } return CKR_OK; +fail: + if (isXCBC) { + PORT_Memset(k2, 0, blockSize); + PORT_Memset(k3, 0, blockSize); + NSC_DestroyObject(hSession, hKey); /* get rid of our temp key */ + } + return crv; } /* @@ -2629,18 +2802,19 @@ NSC_SignInit(CK_SESSION_HANDLE hSession, break; -#define INIT_HMAC_MECH(mmm) \ - case CKM_##mmm##_HMAC_GENERAL: \ - PORT_Assert(pMechanism->pParameter); \ - if (!pMechanism->pParameter) { \ - crv = CKR_MECHANISM_PARAM_INVALID; \ - break; \ - } \ - crv = sftk_doHMACInit(context, HASH_Alg##mmm, key, \ - *(CK_ULONG *)pMechanism->pParameter); \ - break; \ - case CKM_##mmm##_HMAC: \ - crv = sftk_doHMACInit(context, HASH_Alg##mmm, key, mmm##_LENGTH); \ +#define INIT_HMAC_MECH(mmm) \ + case CKM_##mmm##_HMAC_GENERAL: \ + PORT_Assert(pMechanism->pParameter); \ + if (!pMechanism->pParameter) { \ + crv = CKR_MECHANISM_PARAM_INVALID; \ + break; \ + } \ + crv = sftk_doMACInit(pMechanism->mechanism, context, key, \ + *(CK_ULONG *)pMechanism->pParameter); \ + break; \ + case CKM_##mmm##_HMAC: \ + crv = sftk_doMACInit(pMechanism->mechanism, context, key, \ + mmm##_LENGTH); \ break; INIT_HMAC_MECH(MD2) @@ -2652,17 +2826,27 @@ NSC_SignInit(CK_SESSION_HANDLE hSession, case CKM_SHA_1_HMAC_GENERAL: PORT_Assert(pMechanism->pParameter); - if (!pMechanism->pParameter) { + if (!pMechanism->pParameter || pMechanism->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) { crv = CKR_MECHANISM_PARAM_INVALID; break; } - crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, - *(CK_ULONG *)pMechanism->pParameter); + crv = sftk_doMACInit(pMechanism->mechanism, context, key, + *(CK_ULONG *)pMechanism->pParameter); break; case CKM_SHA_1_HMAC: - crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, SHA1_LENGTH); + crv = sftk_doMACInit(pMechanism->mechanism, context, key, SHA1_LENGTH); + break; + case CKM_AES_CMAC_GENERAL: + PORT_Assert(pMechanism->pParameter); + if (!pMechanism->pParameter || pMechanism->ulParameterLen != sizeof(CK_MAC_GENERAL_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_doMACInit(pMechanism->mechanism, context, key, *(CK_ULONG *)pMechanism->pParameter); + break; + case CKM_AES_CMAC: + crv = sftk_doMACInit(pMechanism->mechanism, context, key, AES_BLOCK_SIZE); break; - case CKM_SSL3_MD5_MAC: PORT_Assert(pMechanism->pParameter); if (!pMechanism->pParameter) { @@ -2828,6 +3012,13 @@ sftk_MACFinal(SFTKSessionContext *ctx) { unsigned int padLen = ctx->padDataLength; /* pad and proceed the residual */ + if (ctx->isXCBC) { + CK_RV crv = sftk_xcbc_mac_pad(ctx->padBuf, padLen, ctx->blockSize, + ctx->k2, ctx->k3); + if (crv != CKR_OK) + return crv; + return sftk_MACBlock(ctx, ctx->padBuf); + } if (padLen) { /* shd clr ctx->padLen to make sftk_MACFinal idempotent */ PORT_Memset(ctx->padBuf + padLen, 0, ctx->blockSize - padLen); @@ -2866,7 +3057,7 @@ sftk_MACUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, blkSize - context->padDataLength; /* not enough data even for one block */ - if (ulPartLen < minInput) { + if (ulPartLen <= minInput) { PORT_Memcpy(residual, pPart, ulPartLen); context->padDataLength += ulPartLen; goto cleanup; @@ -2880,7 +3071,7 @@ sftk_MACUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, goto terminate; } /* MACing full blocks */ - while (ulPartLen >= blkSize) { + while (ulPartLen > blkSize) { if (CKR_OK != (crv = sftk_MACBlock(context, pPart))) goto terminate; ulPartLen -= blkSize; @@ -3329,11 +3520,11 @@ NSC_VerifyInit(CK_SESSION_HANDLE hSession, crv = CKR_MECHANISM_PARAM_INVALID; break; } - crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, - *(CK_ULONG *)pMechanism->pParameter); + crv = sftk_doMACInit(pMechanism->mechanism, context, key, + *(CK_ULONG *)pMechanism->pParameter); break; case CKM_SHA_1_HMAC: - crv = sftk_doHMACInit(context, HASH_AlgSHA1, key, SHA1_LENGTH); + crv = sftk_doMACInit(pMechanism->mechanism, context, key, SHA1_LENGTH); break; case CKM_SSL3_MD5_MAC: @@ -3459,7 +3650,7 @@ NSC_VerifyFinal(CK_SESSION_HANDLE hSession, /* must be block cipher MACing */ crv = CKR_SIGNATURE_LEN_RANGE; } else if (CKR_OK == (crv = sftk_MACFinal(context))) { - if (PORT_Memcmp(pSignature, context->macBuf, ulSignatureLen)) + if (NSS_SecureMemcmp(pSignature, context->macBuf, ulSignatureLen)) crv = CKR_SIGNATURE_INVALID; } @@ -3659,11 +3850,17 @@ nsc_pbe_key_gen(NSSPKCS5PBEParameter *pkcs5_pbe, CK_MECHANISM_PTR pMechanism, iv.len = 0; if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PKCS5_PBKD2_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; pwitem.data = (unsigned char *)pbkd2_params->pPassword; /* was this a typo in the PKCS #11 spec? */ pwitem.len = *pbkd2_params->ulPasswordLen; } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PBE_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; pwitem.data = (unsigned char *)pbe_params->pPassword; pwitem.len = pbe_params->ulPasswordLen; @@ -3963,6 +4160,10 @@ nsc_SetupHMACKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe) PORT_FreeArena(arena, PR_TRUE); return CKR_HOST_MEMORY; } + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PBE_PARAMS))) { + PORT_FreeArena(arena, PR_TRUE); + return CKR_MECHANISM_PARAM_INVALID; + } params->poolp = arena; params->ivLen = 0; @@ -4042,10 +4243,10 @@ nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, } if (pMechanism->mechanism == CKM_PKCS5_PBKD2) { - pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; - if (pbkd2_params == NULL) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PKCS5_PBKD2_PARAMS))) { return CKR_MECHANISM_PARAM_INVALID; } + pbkd2_params = (CK_PKCS5_PBKD2_PARAMS *)pMechanism->pParameter; switch (pbkd2_params->prf) { case CKP_PKCS5_PBKD2_HMAC_SHA1: hashType = HASH_AlgSHA1; @@ -4072,6 +4273,9 @@ nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, salt.len = (unsigned int)pbkd2_params->ulSaltSourceDataLen; iteration = pbkd2_params->iterations; } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_PBE_PARAMS))) { + return CKR_MECHANISM_PARAM_INVALID; + } pbe_params = (CK_PBE_PARAMS *)pMechanism->pParameter; salt.data = (unsigned char *)pbe_params->pSalt; salt.len = (unsigned int)pbe_params->ulSaltLen; @@ -4118,11 +4322,12 @@ nsc_SetupPBEKeyGen(CK_MECHANISM_PTR pMechanism, NSSPKCS5PBEParameter **pbe, break; default: crv = CKR_MECHANISM_INVALID; - nsspkcs5_DestroyPBEParameter(params); break; } if (crv == CKR_OK) { *pbe = params; + } else { + nsspkcs5_DestroyPBEParameter(params); } return crv; } @@ -4187,12 +4392,12 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSession, } crv = sftk_AddAttributeType(key, sftk_attr_expand(&pTemplate[i])); - if (crv != CKR_OK) + if (crv != CKR_OK) { break; + } } if (crv != CKR_OK) { - sftk_FreeObject(key); - return crv; + goto loser; } /* make sure we don't have any class, key_type, or value fields */ @@ -4305,8 +4510,10 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSession, } if (crv != CKR_OK) { - sftk_FreeObject(key); - return crv; + if (pbe_param) { + nsspkcs5_DestroyPBEParameter(pbe_param); + } + goto loser; } /* if there was no error, @@ -4324,6 +4531,10 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSession, break; case nsc_ssl: rsa_pms = (SSL3RSAPreMasterSecret *)buf; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_VERSION))) { + crv = CKR_MECHANISM_PARAM_INVALID; + goto loser; + } version = (CK_VERSION *)pMechanism->pParameter; rsa_pms->client_version[0] = version->major; rsa_pms->client_version[1] = version->minor; @@ -4342,6 +4553,10 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSession, crv = nsc_parameter_gen(key_type, key); break; case nsc_jpake: + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_NSS_JPAKERound1Params))) { + crv = CKR_MECHANISM_PARAM_INVALID; + goto loser; + } crv = jpake_Round1(hashType, (CK_NSS_JPAKERound1Params *)pMechanism->pParameter, key); @@ -4349,34 +4564,30 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSession, } if (crv != CKR_OK) { - sftk_FreeObject(key); - return crv; + goto loser; } /* Add the class, key_type, and value */ crv = sftk_AddAttributeType(key, CKA_CLASS, &objclass, sizeof(CK_OBJECT_CLASS)); if (crv != CKR_OK) { - sftk_FreeObject(key); - return crv; + goto loser; } crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &key_type, sizeof(CK_KEY_TYPE)); if (crv != CKR_OK) { - sftk_FreeObject(key); - return crv; + goto loser; } if (key_length != 0) { crv = sftk_AddAttributeType(key, CKA_VALUE, buf, key_length); if (crv != CKR_OK) { - sftk_FreeObject(key); - return crv; + goto loser; } } /* get the session */ session = sftk_SessionFromHandle(hSession); if (session == NULL) { - sftk_FreeObject(key); - return CKR_SESSION_HANDLE_INVALID; + crv = CKR_SESSION_HANDLE_INVALID; + goto loser; } /* @@ -4393,6 +4604,7 @@ NSC_GenerateKey(CK_SESSION_HANDLE hSession, if (crv == CKR_OK) { *phKey = key->handle; } +loser: sftk_FreeObject(key); return crv; } @@ -4645,6 +4857,13 @@ sftk_PairwiseConsistencyCheck(CK_SESSION_HANDLE hSession, return crv; } + /* detect trivial signing transforms */ + if ((signature_length >= pairwise_digest_length) && + (PORT_Memcmp(known_digest, signature + (signature_length - pairwise_digest_length), pairwise_digest_length) == 0)) { + PORT_Free(signature); + return CKR_DEVICE_ERROR; + } + /* Verify the known hash using the public key. */ crv = NSC_VerifyInit(hSession, &mech, publicKey->handle); if (crv != CKR_OK) { @@ -4838,6 +5057,7 @@ NSC_GenerateKeyPair(CK_SESSION_HANDLE hSession, bitSize = sftk_GetLengthInBits(pubExp.data, pubExp.len); if (bitSize < 2) { crv = CKR_ATTRIBUTE_VALUE_INVALID; + PORT_Free(pubExp.data); break; } crv = sftk_AddAttributeType(privateKey, CKA_PUBLIC_EXPONENT, @@ -5498,8 +5718,8 @@ NSC_WrapKey(CK_SESSION_HANDLE hSession, } key = sftk_ObjectFromHandle(hKey, session); - sftk_FreeSession(session); if (key == NULL) { + sftk_FreeSession(session); return CKR_KEY_HANDLE_INVALID; } @@ -5605,7 +5825,7 @@ NSC_WrapKey(CK_SESSION_HANDLE hSession, break; } sftk_FreeObject(key); - + sftk_FreeSession(session); return sftk_mapWrap(crv); } @@ -6414,7 +6634,6 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, CK_KEY_TYPE keyType = CKK_GENERIC_SECRET; CK_OBJECT_CLASS classType = CKO_SECRET_KEY; CK_KEY_DERIVATION_STRING_DATA *stringPtr; - CK_MECHANISM_TYPE mechanism = pMechanism->mechanism; PRBool isTLS = PR_FALSE; PRBool isDH = PR_FALSE; HASH_HashType tlsPrfHash = HASH_AlgNULL; @@ -6432,6 +6651,11 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if (!slot) { return CKR_SESSION_HANDLE_INVALID; } + if (!pMechanism) { + return CKR_MECHANISM_PARAM_INVALID; + } + CK_MECHANISM_TYPE mechanism = pMechanism->mechanism; + /* * now lets create an object to hang the attributes off of */ @@ -6476,6 +6700,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, extractValue = PR_FALSE; classType = CKO_PRIVATE_KEY; break; + case CKM_NSS_PUB_FROM_PRIV: + extractValue = PR_FALSE; + classType = CKO_PUBLIC_KEY; + break; case CKM_NSS_JPAKE_FINAL_SHA1: /* fall through */ case CKM_NSS_JPAKE_FINAL_SHA256: /* fall through */ case CKM_NSS_JPAKE_FINAL_SHA384: /* fall through */ @@ -6517,6 +6745,73 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, } 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; + + crv = sftk_GetULongAttribute(sourceKey, CKA_KEY_TYPE, &keyType); + if (crv != CKR_OK) { + break; + } + + /* privKey is stored in sourceKey and will be destroyed when + * the sourceKey is freed. */ + privKey = sftk_GetPrivKey(sourceKey, keyType, &crv); + if (privKey == NULL) { + break; + } + pubKey = nsslowkey_ConvertToPublicKey(privKey); + if (pubKey == NULL) { + error = PORT_GetError(); + crv = sftk_MapCryptError(error); + break; + } + crv = sftk_PutPubKey(key, sourceKey, keyType, pubKey); + nsslowkey_DestroyPublicKey(pubKey); + break; + } + case CKM_NSS_IKE_PRF_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE_PRF_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike_prf(hSession, att, + (CK_NSS_IKE_PRF_DERIVE_PARAMS *)pMechanism->pParameter, key); + break; + case CKM_NSS_IKE1_PRF_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE1_PRF_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike1_prf(hSession, att, + (CK_NSS_IKE1_PRF_DERIVE_PARAMS *)pMechanism->pParameter, + key, keySize); + break; + case CKM_NSS_IKE1_APP_B_PRF_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_MECHANISM_TYPE)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike1_appendix_b_prf(hSession, att, + (CK_MECHANISM_TYPE *)pMechanism->pParameter, + key, keySize); + break; + case CKM_NSS_IKE_PRF_PLUS_DERIVE: + if (pMechanism->ulParameterLen != + sizeof(CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS)) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } + crv = sftk_ike_prf_plus(hSession, att, + (CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *)pMechanism->pParameter, + key, keySize); + break; /* * generate the master secret */ @@ -6534,6 +6829,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if ((mechanism == CKM_TLS12_MASTER_KEY_DERIVE) || (mechanism == CKM_TLS12_MASTER_KEY_DERIVE_DH)) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_TLS12_MASTER_KEY_DERIVE_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } CK_TLS12_MASTER_KEY_DERIVE_PARAMS *tls12_master = (CK_TLS12_MASTER_KEY_DERIVE_PARAMS *)pMechanism->pParameter; tlsPrfHash = GetHashTypeFromMechanism(tls12_master->prfHashMechanism); @@ -6801,6 +7100,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; if (mechanism == CKM_TLS12_KEY_AND_MAC_DERIVE) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_TLS12_KEY_MAT_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } CK_TLS12_KEY_MAT_PARAMS *tls12_keys = (CK_TLS12_KEY_MAT_PARAMS *)pMechanism->pParameter; tlsPrfHash = GetHashTypeFromMechanism(tls12_keys->prfHashMechanism); @@ -6844,6 +7147,13 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, crv = CKR_HOST_MEMORY; break; } + + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_SSL3_KEY_MAT_PARAMS))) { + MD5_DestroyContext(md5, PR_TRUE); + SHA1_DestroyContext(sha, PR_TRUE); + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } ssl3_keys = (CK_SSL3_KEY_MAT_PARAMS *)pMechanism->pParameter; PORT_Memcpy(srcrdata, @@ -7038,6 +7348,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, CK_ULONG len; if (mechanism == CKM_DES3_ECB_ENCRYPT_DATA) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter; mode = NSS_DES_EDE3; @@ -7087,10 +7401,18 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if (mechanism == CKM_AES_ECB_ENCRYPT_DATA) { mode = NSS_AES; iv = NULL; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; data = stringPtr->pData; len = stringPtr->ulLen; } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *)pMechanism->pParameter; mode = NSS_AES_CBC; @@ -7123,6 +7445,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, CK_ULONG len; if (mechanism == CKM_CAMELLIA_ECB_ENCRYPT_DATA) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter; aesEncryptPtr = NULL; @@ -7131,6 +7457,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, len = stringPtr->ulLen; iv = NULL; } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = NULL; aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *) pMechanism->pParameter; @@ -7164,6 +7494,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, CK_ULONG len; if (mechanism == CKM_SEED_ECB_ENCRYPT_DATA) { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } mode = NSS_SEED; stringPtr = (CK_KEY_DERIVATION_STRING_DATA *) pMechanism->pParameter; @@ -7172,6 +7506,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, len = stringPtr->ulLen; iv = NULL; } else { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_AES_CBC_ENCRYPT_DATA_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } mode = NSS_SEED_CBC; aesEncryptPtr = (CK_AES_CBC_ENCRYPT_DATA_PARAMS *) pMechanism->pParameter; @@ -7263,6 +7601,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if (crv != CKR_OK) break; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; tmpKeySize = att->attrib.ulValueLen + stringPtr->ulLen; if (keySize == 0) @@ -7289,6 +7631,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if (crv != CKR_OK) break; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; tmpKeySize = att->attrib.ulValueLen + stringPtr->ulLen; if (keySize == 0) @@ -7315,6 +7661,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if (crv != CKR_OK) break; + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_KEY_DERIVATION_STRING_DATA))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } stringPtr = (CK_KEY_DERIVATION_STRING_DATA *)pMechanism->pParameter; tmpKeySize = PR_MIN(att->attrib.ulValueLen, stringPtr->ulLen); if (keySize == 0) @@ -7339,6 +7689,10 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, break; case CKM_EXTRACT_KEY_FROM_KEY: { + if (BAD_PARAM_CAST(pMechanism, sizeof(CK_EXTRACT_PARAMS))) { + crv = CKR_MECHANISM_PARAM_INVALID; + break; + } /* the following assumes 8 bits per byte */ CK_ULONG extract = *(CK_EXTRACT_PARAMS *)pMechanism->pParameter; CK_ULONG shift = extract & 0x7; /* extract mod 8 the fast way */ @@ -7479,14 +7833,14 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, case CKM_DH_PKCS_DERIVE: { SECItem derived, dhPublic; - SECItem dhPrime, dhValue; + SECItem dhPrime, dhSubPrime, dhValue; /* sourceKey - values for the local existing low key */ /* get prime and value attributes */ crv = sftk_Attribute2SecItem(NULL, &dhPrime, sourceKey, CKA_PRIME); - if (crv != SECSuccess) + if (crv != CKR_OK) break; crv = sftk_Attribute2SecItem(NULL, &dhValue, sourceKey, CKA_VALUE); - if (crv != SECSuccess) { + if (crv != CKR_OK) { PORT_Free(dhPrime.data); break; } @@ -7494,6 +7848,20 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, dhPublic.data = pMechanism->pParameter; dhPublic.len = pMechanism->ulParameterLen; + /* If the caller bothered to provide Q, use Q to validate + * the public key. */ + crv = sftk_Attribute2SecItem(NULL, &dhSubPrime, sourceKey, CKA_SUBPRIME); + if (crv == CKR_OK) { + rv = KEA_Verify(&dhPublic, &dhPrime, &dhSubPrime); + PORT_Free(dhSubPrime.data); + if (rv != SECSuccess) { + crv = CKR_ARGUMENTS_BAD; + PORT_Free(dhPrime.data); + PORT_Free(dhValue.data); + break; + } + } + /* calculate private value - oct */ rv = DH_Derive(&dhPublic, &dhPrime, &dhValue, &derived, keySize); @@ -7503,6 +7871,7 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, if (rv == SECSuccess) { sftk_forceAttribute(key, CKA_VALUE, derived.data, derived.len); PORT_ZFree(derived.data, derived.len); + crv = CKR_OK; } else crv = CKR_HOST_MEMORY; @@ -7668,7 +8037,7 @@ NSC_DeriveKey(CK_SESSION_HANDLE hSession, const SECHashObject *rawHash; unsigned hashLen; CK_BYTE hashbuf[HASH_LENGTH_MAX]; - CK_BYTE *prk; /* psuedo-random key */ + CK_BYTE *prk; /* psuedo-random key */ CK_ULONG prkLen; CK_BYTE *okm; /* output keying material */ unsigned allocated_space = 0; /* If we need more work space, track it */ diff --git a/security/nss/lib/softoken/pkcs11i.h b/security/nss/lib/softoken/pkcs11i.h index 1ba0bdb82..51127a32a 100644 --- a/security/nss/lib/softoken/pkcs11i.h +++ b/security/nss/lib/softoken/pkcs11i.h @@ -17,6 +17,9 @@ #include "chacha20poly1305.h" #include "hasht.h" +#include "alghmac.h" +#include "cmac.h" + /* * Configuration Defines * @@ -106,6 +109,7 @@ typedef struct SFTKOAEPEncryptInfoStr SFTKOAEPEncryptInfo; typedef struct SFTKOAEPDecryptInfoStr SFTKOAEPDecryptInfo; typedef struct SFTKSSLMACInfoStr SFTKSSLMACInfo; typedef struct SFTKChaCha20Poly1305InfoStr SFTKChaCha20Poly1305Info; +typedef struct SFTKChaCha20CtrInfoStr SFTKChaCha20CtrInfo; typedef struct SFTKItemTemplateStr SFTKItemTemplate; /* define function pointer typdefs for pointer tables */ @@ -253,12 +257,15 @@ struct SFTKSessionContextStr { PRBool multi; /* is multipart */ PRBool rsa; /* is rsa */ PRBool doPad; /* use PKCS padding for block ciphers */ + PRBool isXCBC; /* xcbc, use special handling in final */ unsigned int blockSize; /* blocksize for padding */ unsigned int padDataLength; /* length of the valid data in padbuf */ /** latest incomplete block of data for block cipher */ unsigned char padBuf[SFTK_MAX_BLOCK_SIZE]; /** result of MAC'ing of latest full block of data with block cipher */ unsigned char macBuf[SFTK_MAX_BLOCK_SIZE]; + unsigned char k2[SFTK_MAX_BLOCK_SIZE]; + unsigned char k3[SFTK_MAX_BLOCK_SIZE]; CK_ULONG macSize; /* size of a general block cipher mac*/ void *cipherInfo; void *hashInfo; @@ -408,6 +415,14 @@ struct SFTKChaCha20Poly1305InfoStr { unsigned int adLen; }; +/* SFTKChaCha20BlockInfoStr the key, nonce and counter for a + * ChaCha20 block operation. */ +struct SFTKChaCha20CtrInfoStr { + PRUint8 key[32]; + PRUint8 nonce[12]; + PRUint32 counter; +}; + /* * Template based on SECItems, suitable for passing as arrays */ @@ -446,7 +461,7 @@ struct SFTKItemTemplateStr { #define SFTK_TOKEN_KRL_HANDLE (SFTK_TOKEN_MAGIC | SFTK_TOKEN_TYPE_CRL | 1) /* how big (in bytes) a password/pin we can deal with */ -#define SFTK_MAX_PIN 255 +#define SFTK_MAX_PIN 500 /* minimum password/pin length (in Unicode characters) in FIPS mode */ #define FIPS_MIN_PIN 7 @@ -583,6 +598,73 @@ typedef struct sftk_parametersStr { #define CERT_DB_FMT "%scert%s.db" #define KEY_DB_FMT "%skey%s.db" +struct sftk_MACConstantTimeCtxStr { + const SECHashObject *hash; + unsigned char mac[64]; + unsigned char secret[64]; + unsigned int headerLength; + unsigned int secretLength; + unsigned int totalLength; + unsigned char header[75]; +}; +typedef struct sftk_MACConstantTimeCtxStr sftk_MACConstantTimeCtx; + +struct sftk_MACCtxStr { + /* This is a common MAC context that supports both HMAC and CMAC + * operations. This also presents a unified set of semantics: + * + * - Everything except Destroy returns a CK_RV, indicating success + * or failure. (This handles the difference between HMAC's and CMAC's + * interfaces, since the underlying AES _might_ fail with CMAC). + * + * - The underlying MAC is started on Init(...), so Update(...) can + * called right away. (This handles the difference between HMAC and + * CMAC in their *_Init(...) functions). + * + * - Calling semantics: + * + * - One of sftk_MAC_{Create,Init,InitRaw}(...) to set up the MAC + * context, checking the return code. + * - sftk_MAC_Update(...) as many times as necessary to process + * input data, checking the return code. + * - sftk_MAC_Finish(...) to get the output of the MAC; result_len + * may be NULL if the caller knows the expected output length, + * checking the return code. If result_len is NULL, this will + * PR_ASSERT(...) that the actual returned length was equal to + * max_result_len. + * + * Note: unlike HMAC_Finish(...), this allows the caller to specify + * a return value less than return length, to align with + * CMAC_Finish(...)'s semantics. This will force an additional + * stack allocation of size SFTK_MAX_MAC_LENGTH. + * - sftk_MAC_Reset(...) if the caller wishes to compute a new MAC + * with the same key, checking the return code. + * - sftk_MAC_Destroy(...) when the caller frees its associated + * memory, passing PR_TRUE if sftk_MAC_Create(...) was called, + * and PR_FALSE otherwise. + */ + + CK_MECHANISM_TYPE mech; + unsigned int mac_size; + + union { + HMACContext *hmac; + CMACContext *cmac; + + /* Functions to update when adding a new MAC or a new hash: + * + * - sftk_MAC_Init + * - sftk_MAC_Update + * - sftk_MAC_Finish + * - sftk_MAC_Reset + */ + void *raw; + } mac; + + void (*destroy_func)(void *ctx, PRBool free_it); +}; +typedef struct sftk_MACCtxStr sftk_MACCtx; + SEC_BEGIN_PROTOS /* shared functions between pkcs11.c and fipstokn.c */ @@ -604,6 +686,7 @@ extern CK_RV SFTK_ShutdownSlot(SFTKSlot *slot); extern CK_RV sftk_CloseAllSessions(SFTKSlot *slot, PRBool logout); /* internal utility functions used by pkcs11.c */ +extern CK_RV sftk_MapCryptError(int error); extern SFTKAttribute *sftk_FindAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type); extern void sftk_FreeAttribute(SFTKAttribute *attribute); @@ -682,10 +765,36 @@ extern NSSLOWKEYPublicKey *sftk_GetPubKey(SFTKObject *object, CK_KEY_TYPE key_type, CK_RV *crvp); extern NSSLOWKEYPrivateKey *sftk_GetPrivKey(SFTKObject *object, CK_KEY_TYPE key_type, CK_RV *crvp); +extern CK_RV sftk_PutPubKey(SFTKObject *publicKey, SFTKObject *privKey, CK_KEY_TYPE keyType, + NSSLOWKEYPublicKey *pubKey); extern void sftk_FormatDESKey(unsigned char *key, int length); extern PRBool sftk_CheckDESKey(unsigned char *key); extern PRBool sftk_IsWeakKey(unsigned char *key, CK_KEY_TYPE key_type); +/* ike and xcbc helpers */ +extern CK_RV sftk_ike_prf(CK_SESSION_HANDLE hSession, + const SFTKAttribute *inKey, + const CK_NSS_IKE_PRF_DERIVE_PARAMS *params, SFTKObject *outKey); +extern CK_RV sftk_ike1_prf(CK_SESSION_HANDLE hSession, + const SFTKAttribute *inKey, + const CK_NSS_IKE1_PRF_DERIVE_PARAMS *params, SFTKObject *outKey, + unsigned int keySize); +extern CK_RV sftk_ike1_appendix_b_prf(CK_SESSION_HANDLE hSession, + const SFTKAttribute *inKey, + const CK_MECHANISM_TYPE *params, SFTKObject *outKey, + unsigned int keySize); +extern CK_RV sftk_ike_prf_plus(CK_SESSION_HANDLE hSession, + const SFTKAttribute *inKey, + const CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *params, SFTKObject *outKey, + unsigned int keySize); +extern CK_RV sftk_aes_xcbc_new_keys(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hKey, CK_OBJECT_HANDLE_PTR phKey, + unsigned char *k2, unsigned char *k3); +extern CK_RV sftk_xcbc_mac_pad(unsigned char *padBuf, unsigned int bufLen, + int blockSize, const unsigned char *k2, + const unsigned char *k3); +extern SECStatus sftk_fips_IKE_PowerUpSelfTests(void); + /* mechanism allows this operation */ extern CK_RV sftk_MechAllowsOperation(CK_MECHANISM_TYPE type, CK_ATTRIBUTE_TYPE op); @@ -727,17 +836,6 @@ extern CK_RV jpake_Final(HASH_HashType hashType, SFTKObject *sourceKey, SFTKObject *key); /* Constant time MAC functions (hmacct.c) */ - -struct sftk_MACConstantTimeCtxStr { - const SECHashObject *hash; - unsigned char mac[64]; - unsigned char secret[64]; - unsigned int headerLength; - unsigned int secretLength; - unsigned int totalLength; - unsigned char header[75]; -}; -typedef struct sftk_MACConstantTimeCtxStr sftk_MACConstantTimeCtx; sftk_MACConstantTimeCtx *sftk_HMACConstantTime_New( CK_MECHANISM_PTR mech, SFTKObject *key); sftk_MACConstantTimeCtx *sftk_SSLv3MACConstantTime_New( @@ -759,6 +857,16 @@ sftk_TLSPRFInit(SFTKSessionContext *context, HASH_HashType hash_alg, unsigned int out_len); +/* PKCS#11 MAC implementation. See sftk_MACCtxStr declaration above for + * calling semantics for these functions. */ +CK_RV sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx); +CK_RV sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key); +CK_RV sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS); +CK_RV sftk_MAC_Update(sftk_MACCtx *ctx, CK_BYTE_PTR data, unsigned int data_len); +CK_RV sftk_MAC_Finish(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len); +CK_RV sftk_MAC_Reset(sftk_MACCtx *ctx); +void sftk_MAC_Destroy(sftk_MACCtx *ctx, PRBool free_it); + SEC_END_PROTOS #endif /* _PKCS11I_H_ */ diff --git a/security/nss/lib/softoken/pkcs11u.c b/security/nss/lib/softoken/pkcs11u.c index 8cb5d2e70..1f66e6f61 100644 --- a/security/nss/lib/softoken/pkcs11u.c +++ b/security/nss/lib/softoken/pkcs11u.c @@ -15,6 +15,47 @@ #include "softoken.h" /* + * ******************** Error mapping ******************************* + */ +/* + * map all the SEC_ERROR_xxx error codes that may be returned by freebl + * functions to CKR_xxx. return CKR_DEVICE_ERROR by default for backward + * compatibility. + */ +CK_RV +sftk_MapCryptError(int error) +{ + switch (error) { + case SEC_ERROR_INVALID_ARGS: + case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */ + return CKR_ARGUMENTS_BAD; + case SEC_ERROR_INPUT_LEN: + return CKR_DATA_LEN_RANGE; + case SEC_ERROR_OUTPUT_LEN: + return CKR_BUFFER_TOO_SMALL; + case SEC_ERROR_LIBRARY_FAILURE: + return CKR_GENERAL_ERROR; + case SEC_ERROR_NO_MEMORY: + return CKR_HOST_MEMORY; + case SEC_ERROR_BAD_SIGNATURE: + return CKR_SIGNATURE_INVALID; + case SEC_ERROR_INVALID_KEY: + return CKR_KEY_SIZE_RANGE; + case SEC_ERROR_BAD_KEY: /* an EC public key that fails validation */ + return CKR_KEY_SIZE_RANGE; /* the closest error code */ + case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM: + return CKR_TEMPLATE_INCONSISTENT; + case SEC_ERROR_UNSUPPORTED_KEYALG: + return CKR_MECHANISM_INVALID; + case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE: + return CKR_DOMAIN_PARAMS_INVALID; + /* key pair generation failed after max number of attempts */ + case SEC_ERROR_NEED_RANDOM: + return CKR_FUNCTION_FAILED; + } + return CKR_DEVICE_ERROR; +} +/* * ******************** Attribute Utilities ******************************* */ @@ -100,7 +141,7 @@ sftk_DestroyAttribute(SFTKAttribute *attribute) void sftk_FreeAttribute(SFTKAttribute *attribute) { - if (attribute->freeAttr) { + if (attribute && attribute->freeAttr) { sftk_DestroyAttribute(attribute); return; } diff --git a/security/nss/lib/softoken/sdb.c b/security/nss/lib/softoken/sdb.c index fb897d68c..c84476100 100644 --- a/security/nss/lib/softoken/sdb.c +++ b/security/nss/lib/softoken/sdb.c @@ -159,7 +159,7 @@ static const CK_ATTRIBUTE_TYPE known_attributes[] = { CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING, CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH, CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS, - CKA_PUBLIC_KEY_INFO + CKA_PUBLIC_KEY_INFO, CKA_NSS_SERVER_DISTRUST_AFTER, CKA_NSS_EMAIL_DISTRUST_AFTER }; static int known_attributes_size = sizeof(known_attributes) / @@ -858,7 +858,6 @@ sdb_FindObjectsFinal(SDB *sdb, SDBFind *sdbFind) return sdb_mapSQLError(sdb_p->type, sqlerr); } -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) @@ -866,8 +865,6 @@ sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, SDBPrivate *sdb_p = sdb->private; sqlite3 *sqlDB = NULL; sqlite3_stmt *stmt = NULL; - char *getStr = NULL; - char *newStr = NULL; const char *table = NULL; int sqlerr = SQLITE_OK; CK_RV error = CKR_OK; @@ -875,59 +872,79 @@ sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, int retry = 0; unsigned int i; + if (count == 0) { + error = CKR_OBJECT_HANDLE_INVALID; + goto loser; + } + /* open a new db if necessary */ error = sdb_openDBLocal(sdb_p, &sqlDB, &table); if (error != CKR_OK) { goto loser; } + char *columns = NULL; for (i = 0; i < count; i++) { - getStr = sqlite3_mprintf("a%x", template[i].type); - - if (getStr == NULL) { - error = CKR_HOST_MEMORY; - goto loser; + char *newColumns; + if (columns) { + newColumns = sqlite3_mprintf("%s, a%x", columns, template[i].type); + sqlite3_free(columns); + columns = NULL; + } else { + newColumns = sqlite3_mprintf("a%x", template[i].type); } - - newStr = sqlite3_mprintf(GET_ATTRIBUTE_CMD, getStr, table); - sqlite3_free(getStr); - getStr = NULL; - if (newStr == NULL) { + if (!newColumns) { error = CKR_HOST_MEMORY; goto loser; } + columns = newColumns; + } - sqlerr = sqlite3_prepare_v2(sqlDB, newStr, -1, &stmt, NULL); - sqlite3_free(newStr); - newStr = NULL; - if (sqlerr == SQLITE_ERROR) { - template[i].ulValueLen = -1; - error = CKR_ATTRIBUTE_TYPE_INVALID; - continue; - } else if (sqlerr != SQLITE_OK) { - goto loser; - } + PORT_Assert(columns); - sqlerr = sqlite3_bind_int(stmt, 1, object_id); - if (sqlerr != SQLITE_OK) { - goto loser; - } + char *statement = sqlite3_mprintf("SELECT DISTINCT %s FROM %s where id=$ID LIMIT 1;", + columns, table); + sqlite3_free(columns); + columns = NULL; + if (!statement) { + error = CKR_HOST_MEMORY; + goto loser; + } - do { - sqlerr = sqlite3_step(stmt); - if (sqlerr == SQLITE_BUSY) { - PR_Sleep(SDB_BUSY_RETRY_TIME); - } - if (sqlerr == SQLITE_ROW) { + sqlerr = sqlite3_prepare_v2(sqlDB, statement, -1, &stmt, NULL); + sqlite3_free(statement); + statement = NULL; + if (sqlerr != SQLITE_OK) { + goto loser; + } + + // NB: indices in sqlite3_bind_int are 1-indexed + sqlerr = sqlite3_bind_int(stmt, 1, object_id); + if (sqlerr != SQLITE_OK) { + goto loser; + } + + do { + sqlerr = sqlite3_step(stmt); + if (sqlerr == SQLITE_BUSY) { + PR_Sleep(SDB_BUSY_RETRY_TIME); + } + if (sqlerr == SQLITE_ROW) { + PORT_Assert(!found); + for (i = 0; i < count; i++) { unsigned int blobSize; const char *blobData; - blobSize = sqlite3_column_bytes(stmt, 0); - blobData = sqlite3_column_blob(stmt, 0); + // NB: indices in sqlite_column_{bytes,blob} are 0-indexed + blobSize = sqlite3_column_bytes(stmt, i); + blobData = sqlite3_column_blob(stmt, i); if (blobData == NULL) { + /* PKCS 11 requires that get attributes process all the + * attributes in the template, marking the attributes with + * issues with -1. Mark the error but continue */ template[i].ulValueLen = -1; error = CKR_ATTRIBUTE_TYPE_INVALID; - break; + continue; } /* If the blob equals our explicit NULL value, then the * attribute is a NULL. */ @@ -938,20 +955,21 @@ sdb_GetAttributeValueNoLock(SDB *sdb, CK_OBJECT_HANDLE object_id, } if (template[i].pValue) { if (template[i].ulValueLen < blobSize) { + /* like CKR_ATTRIBUTE_TYPE_INVALID, continue processing */ template[i].ulValueLen = -1; error = CKR_BUFFER_TOO_SMALL; - break; + continue; } PORT_Memcpy(template[i].pValue, blobData, blobSize); } template[i].ulValueLen = blobSize; - found = 1; } - } while (!sdb_done(sqlerr, &retry)); - sqlite3_reset(stmt); - sqlite3_finalize(stmt); - stmt = NULL; - } + found = 1; + } + } while (!sdb_done(sqlerr, &retry)); + sqlite3_reset(stmt); + sqlite3_finalize(stmt); + stmt = NULL; loser: /* fix up the error if necessary */ diff --git a/security/nss/lib/softoken/sftkdb.c b/security/nss/lib/softoken/sftkdb.c index 21796bbbb..61cb2ce8b 100644 --- a/security/nss/lib/softoken/sftkdb.c +++ b/security/nss/lib/softoken/sftkdb.c @@ -2451,7 +2451,7 @@ sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID) * initialize a new database handle */ static SFTKDBHandle * -sftk_NewDBHandle(SDB *sdb, int type) +sftk_NewDBHandle(SDB *sdb, int type, PRBool legacy) { SFTKDBHandle *handle = PORT_New(SFTKDBHandle); handle->ref = 1; @@ -2463,6 +2463,7 @@ sftk_NewDBHandle(SDB *sdb, int type) handle->updatePasswordKey = NULL; handle->updateID = NULL; handle->type = type; + handle->usesLegacyStorage = legacy; handle->passwordKey.data = NULL; handle->passwordKey.len = 0; handle->passwordLock = NULL; @@ -2622,6 +2623,7 @@ sftk_DBInit(const char *configdir, const char *certPrefix, PRBool newInit = PR_FALSE; PRBool needUpdate = PR_FALSE; char *nconfdir = NULL; + PRBool legacy = PR_TRUE; if (!readOnly) { flags = SDB_CREATE; @@ -2654,12 +2656,14 @@ sftk_DBInit(const char *configdir, const char *certPrefix, case NSS_DB_TYPE_EXTERN: /* SHOULD open a loadable db */ crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags, noCertDB ? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit); + legacy = PR_FALSE; /* * if we failed to open the DB's read only, use the old ones if * the exists. */ if (crv != CKR_OK) { + legacy = PR_TRUE; if ((flags & SDB_RDONLY) == SDB_RDONLY) { nconfdir = sftk_legacyPathFromSDBPath(confdir); } @@ -2713,12 +2717,12 @@ sftk_DBInit(const char *configdir, const char *certPrefix, goto done; } if (!noCertDB) { - *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE); + *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE, legacy); } else { *certDB = NULL; } if (!noKeyDB) { - *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE); + *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE, legacy); } else { *keyDB = NULL; } diff --git a/security/nss/lib/softoken/sftkdb.h b/security/nss/lib/softoken/sftkdb.h index e30f3a237..bce39dc92 100644 --- a/security/nss/lib/softoken/sftkdb.h +++ b/security/nss/lib/softoken/sftkdb.h @@ -70,3 +70,5 @@ SFTKDBHandle *sftk_getKeyDB(SFTKSlot *slot); SFTKDBHandle *sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID); void sftk_freeDB(SFTKDBHandle *certHandle); + +PRBool sftk_isLegacyIterationCountAllowed(void); diff --git a/security/nss/lib/softoken/sftkdbti.h b/security/nss/lib/softoken/sftkdbti.h index 22655de56..a3a19414e 100644 --- a/security/nss/lib/softoken/sftkdbti.h +++ b/security/nss/lib/softoken/sftkdbti.h @@ -23,6 +23,7 @@ struct SFTKDBHandleStr { SDB *update; char *updateID; PRBool updateDBIsInit; + PRBool usesLegacyStorage; }; #define SFTK_KEYDB_TYPE 0x40000000 diff --git a/security/nss/lib/softoken/sftkhmac.c b/security/nss/lib/softoken/sftkhmac.c index be6344c70..617e6fd4e 100644 --- a/security/nss/lib/softoken/sftkhmac.c +++ b/security/nss/lib/softoken/sftkhmac.c @@ -9,12 +9,14 @@ #include "softoken.h" #include "hmacct.h" -/* MACMechanismToHash converts a PKCS#11 MAC mechanism into a freebl hash +/* HMACMechanismToHash converts a PKCS#11 MAC mechanism into a freebl hash * type. */ static HASH_HashType -MACMechanismToHash(CK_MECHANISM_TYPE mech) +HMACMechanismToHash(CK_MECHANISM_TYPE mech) { switch (mech) { + case CKM_MD2_HMAC: + return HASH_AlgMD2; case CKM_MD5_HMAC: case CKM_SSL3_MD5_MAC: return HASH_AlgMD5; @@ -48,7 +50,7 @@ SetupMAC(CK_MECHANISM_PTR mech, SFTKObject *key) return NULL; } - alg = MACMechanismToHash(params->macAlg); + alg = HMACMechanismToHash(params->macAlg); if (alg == HASH_AlgNULL) { return NULL; } @@ -188,3 +190,284 @@ sftk_MACConstantTime_DestroyContext(void *pctx, PRBool free) { PORT_Free(pctx); } + +CK_RV +sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx) +{ + CK_RV ret; + + if (ret_ctx == NULL || key == NULL) { + return CKR_HOST_MEMORY; + } + + *ret_ctx = PORT_New(sftk_MACCtx); + if (*ret_ctx == NULL) { + return CKR_HOST_MEMORY; + } + + ret = sftk_MAC_Init(*ret_ctx, mech, key); + if (ret != CKR_OK) { + sftk_MAC_Destroy(*ret_ctx, PR_TRUE); + } + + return ret; +} + +CK_RV +sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key) +{ + SFTKAttribute *keyval = NULL; + PRBool isFIPS = (key->slot->slotID == FIPS_SLOT_ID); + CK_RV ret = CKR_OK; + + /* Find the actual value of the key. */ + keyval = sftk_FindAttribute(key, CKA_VALUE); + if (keyval == NULL) { + ret = CKR_KEY_SIZE_RANGE; + goto done; + } + + ret = sftk_MAC_InitRaw(ctx, mech, + (const unsigned char *)keyval->attrib.pValue, + keyval->attrib.ulValueLen, isFIPS); + +done: + sftk_FreeAttribute(keyval); + return ret; +} + +CK_RV +sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS) +{ + const SECHashObject *hashObj = NULL; + CK_RV ret = CKR_OK; + + if (ctx == NULL) { + return CKR_HOST_MEMORY; + } + + /* Clear the context before use. */ + PORT_Memset(ctx, 0, sizeof(*ctx)); + + /* Save the mech. */ + ctx->mech = mech; + + /* Initialize the correct MAC context. */ + switch (mech) { + case CKM_MD2_HMAC: + case CKM_MD5_HMAC: + case CKM_SHA_1_HMAC: + case CKM_SHA224_HMAC: + case CKM_SHA256_HMAC: + case CKM_SHA384_HMAC: + case CKM_SHA512_HMAC: + hashObj = HASH_GetRawHashObject(HMACMechanismToHash(mech)); + + /* Because we condition above only on hashes we know to be valid, + * hashObj should never be NULL. This assert is only useful when + * adding a new hash function (for which only partial support has + * been added); thus there is no need to turn it into an if and + * avoid the NULL dereference on the following line. */ + PR_ASSERT(hashObj != NULL); + ctx->mac_size = hashObj->length; + + goto hmac; + case CKM_AES_CMAC: + ctx->mac.cmac = CMAC_Create(CMAC_AES, key, key_len); + ctx->destroy_func = (void (*)(void *, PRBool))(&CMAC_Destroy); + + /* Copy the behavior of sftk_doCMACInit here. */ + if (ctx->mac.cmac == NULL) { + if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) { + ret = CKR_KEY_SIZE_RANGE; + goto done; + } + + ret = CKR_HOST_MEMORY; + goto done; + } + + ctx->mac_size = AES_BLOCK_SIZE; + + goto done; + default: + ret = CKR_MECHANISM_PARAM_INVALID; + goto done; + } + +hmac: + ctx->mac.hmac = HMAC_Create(hashObj, key, key_len, isFIPS); + ctx->destroy_func = (void (*)(void *, PRBool))(&HMAC_Destroy); + + /* Copy the behavior of sftk_doHMACInit here. */ + if (ctx->mac.hmac == NULL) { + if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) { + ret = CKR_KEY_SIZE_RANGE; + goto done; + } + ret = CKR_HOST_MEMORY; + goto done; + } + + /* Semantics: HMAC and CMAC should behave the same. Begin HMAC now. */ + HMAC_Begin(ctx->mac.hmac); + +done: + /* Handle a failure: ctx->mac.raw should be NULL, but make sure + * destroy_func isn't set. */ + if (ret != CKR_OK) { + ctx->destroy_func = NULL; + } + + return ret; +} + +CK_RV +sftk_MAC_Reset(sftk_MACCtx *ctx) +{ + /* Useful for resetting the state of MAC prior to calling update again + * + * This lets the caller keep a single MAC instance and re-use it as long + * as the key stays the same. */ + switch (ctx->mech) { + case CKM_MD2_HMAC: + case CKM_MD5_HMAC: + case CKM_SHA_1_HMAC: + case CKM_SHA224_HMAC: + case CKM_SHA256_HMAC: + case CKM_SHA384_HMAC: + case CKM_SHA512_HMAC: + HMAC_Begin(ctx->mac.hmac); + break; + case CKM_AES_CMAC: + if (CMAC_Begin(ctx->mac.cmac) != SECSuccess) { + return CKR_FUNCTION_FAILED; + } + break; + default: + /* This shouldn't happen -- asserting indicates partial support + * for a new MAC type. */ + PR_ASSERT(PR_FALSE); + return CKR_FUNCTION_FAILED; + } + + return CKR_OK; +} + +CK_RV +sftk_MAC_Update(sftk_MACCtx *ctx, CK_BYTE_PTR data, unsigned int data_len) +{ + switch (ctx->mech) { + case CKM_MD2_HMAC: + case CKM_MD5_HMAC: + case CKM_SHA_1_HMAC: + case CKM_SHA224_HMAC: + case CKM_SHA256_HMAC: + case CKM_SHA384_HMAC: + case CKM_SHA512_HMAC: + /* HMAC doesn't indicate failure in the return code. */ + HMAC_Update(ctx->mac.hmac, data, data_len); + break; + case CKM_AES_CMAC: + /* CMAC indicates failure in the return code, however this is + * unlikely to occur. */ + if (CMAC_Update(ctx->mac.cmac, data, data_len) != SECSuccess) { + return CKR_FUNCTION_FAILED; + } + break; + default: + /* This shouldn't happen -- asserting indicates partial support + * for a new MAC type. */ + PR_ASSERT(PR_FALSE); + return CKR_FUNCTION_FAILED; + } + return CKR_OK; +} + +CK_RV +sftk_MAC_Finish(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len) +{ + unsigned int actual_result_len; + + switch (ctx->mech) { + case CKM_MD2_HMAC: + case CKM_MD5_HMAC: + case CKM_SHA_1_HMAC: + case CKM_SHA224_HMAC: + case CKM_SHA256_HMAC: + case CKM_SHA384_HMAC: + case CKM_SHA512_HMAC: + /* HMAC doesn't indicate failure in the return code. Additionally, + * unlike CMAC, it doesn't support partial results. This means that we + * need to allocate a buffer if max_result_len < ctx->mac_size. */ + if (max_result_len >= ctx->mac_size) { + /* Split this into two calls to avoid an unnecessary stack + * allocation and memcpy when possible. */ + HMAC_Finish(ctx->mac.hmac, result, &actual_result_len, max_result_len); + } else { + uint8_t tmp_buffer[SFTK_MAX_MAC_LENGTH]; + + /* Assumption: buffer is large enough to hold this HMAC's + * output. */ + PR_ASSERT(SFTK_MAX_MAC_LENGTH >= ctx->mac_size); + + HMAC_Finish(ctx->mac.hmac, tmp_buffer, &actual_result_len, SFTK_MAX_MAC_LENGTH); + + if (actual_result_len > max_result_len) { + /* This should always be true since: + * + * (SFTK_MAX_MAC_LENGTH >= ctx->mac_size = + * actual_result_len) > max_result_len, + * + * but guard this truncation just in case. */ + actual_result_len = max_result_len; + } + + PORT_Memcpy(result, tmp_buffer, actual_result_len); + } + break; + case CKM_AES_CMAC: + /* CMAC indicates failure in the return code, however this is + * unlikely to occur. */ + if (CMAC_Finish(ctx->mac.cmac, result, &actual_result_len, max_result_len) != SECSuccess) { + return CKR_FUNCTION_FAILED; + } + break; + default: + /* This shouldn't happen -- asserting indicates partial support + * for a new MAC type. */ + PR_ASSERT(PR_FALSE); + return CKR_FUNCTION_FAILED; + } + + if (result_len) { + /* When result length is passed, inform the caller of its value. */ + *result_len = actual_result_len; + } else if (max_result_len == ctx->mac_size) { + /* Validate that the amount requested was what was actually given; the + * caller assumes that what they passed was the output size of the + * underlying MAC and that they got all the bytes the asked for. */ + PR_ASSERT(actual_result_len == max_result_len); + } + + return CKR_OK; +} + +void +sftk_MAC_Destroy(sftk_MACCtx *ctx, PRBool free_it) +{ + if (ctx == NULL) { + return; + } + + if (ctx->mac.raw != NULL && ctx->destroy_func != NULL) { + ctx->destroy_func(ctx->mac.raw, PR_TRUE); + } + + /* Clean up the struct so we don't double free accidentally. */ + PORT_Memset(ctx, 0, sizeof(sftk_MACCtx)); + + if (free_it == PR_TRUE) { + PORT_Free(ctx); + } +} diff --git a/security/nss/lib/softoken/sftkike.c b/security/nss/lib/softoken/sftkike.c new file mode 100644 index 000000000..7d5370bd4 --- /dev/null +++ b/security/nss/lib/softoken/sftkike.c @@ -0,0 +1,1294 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * This file implements PKCS 11 on top of our existing security modules + * + * For more information about PKCS 11 See PKCS 11 Token Inteface Standard. + * This implementation has two slots: + * slot 1 is our generic crypto support. It does not require login. + * It supports Public Key ops, and all they bulk ciphers and hashes. + * It can also support Private Key ops for imported Private keys. It does + * not have any token storage. + * slot 2 is our private key support. It requires a login before use. It + * can store Private Keys and Certs as token objects. Currently only private + * keys and their associated Certificates are saved on the token. + * + * In this implementation, session objects are only visible to the session + * that created or generated them. + */ +#include "seccomon.h" +#include "secitem.h" +#include "secport.h" +#include "blapi.h" +#include "pkcs11.h" +#include "pkcs11i.h" +#include "pkcs1sig.h" +#include "lowkeyi.h" +#include "secder.h" +#include "secdig.h" +#include "lowpbe.h" /* We do PBE below */ +#include "pkcs11t.h" +#include "secoid.h" +#include "alghmac.h" +#include "softoken.h" +#include "secasn1.h" +#include "secerr.h" + +#include "prprf.h" +#include "prenv.h" + +/* + * A common prfContext to handle both hmac and aes xcbc + * hash contexts have non-null hashObj and hmac, aes + * contexts have non-null aes */ +typedef struct prfContextStr { + HASH_HashType hashType; + const SECHashObject *hashObj; + HMACContext *hmac; + AESContext *aes; + unsigned int nextChar; + unsigned char padBuf[AES_BLOCK_SIZE]; + unsigned char macBuf[AES_BLOCK_SIZE]; + unsigned char k1[AES_BLOCK_SIZE]; + unsigned char k2[AES_BLOCK_SIZE]; + unsigned char k3[AES_BLOCK_SIZE]; +} prfContext; + +/* iv full of zeros used in several places in aes xcbc */ +static const unsigned char iv_zero[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * Generate AES XCBC keys from the AES MAC key. + * k1 is used in the actual mac. + * k2 and k3 are used in the final pad step. + */ +static CK_RV +sftk_aes_xcbc_get_keys(const unsigned char *keyValue, unsigned int keyLen, + unsigned char *k1, unsigned char *k2, unsigned char *k3) +{ + SECStatus rv; + CK_RV crv; + unsigned int tmpLen; + AESContext *aes_context = NULL; + unsigned char newKey[AES_BLOCK_SIZE]; + + /* AES XCBC keys. k1, k2, and k3 are derived by encrypting + * k1data, k2data, and k3data with the mac key. + */ + static const unsigned char k1data[] = { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 + }; + static const unsigned char k2data[] = { + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02 + }; + static const unsigned char k3data[] = { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 + }; + + /* k1_0 = aes_ecb(0, k1data) */ + static const unsigned char k1_0[] = { + 0xe1, 0x4d, 0x5d, 0x0e, 0xe2, 0x77, 0x15, 0xdf, + 0x08, 0xb4, 0x15, 0x2b, 0xa2, 0x3d, 0xa8, 0xe0 + + }; + /* k2_0 = aes_ecb(0, k2data) */ + static const unsigned char k2_0[] = { + 0x5e, 0xba, 0x73, 0xf8, 0x91, 0x42, 0xc5, 0x48, + 0x80, 0xf6, 0x85, 0x94, 0x37, 0x3c, 0x5c, 0x37 + }; + /* k3_0 = aes_ecb(0, k3data) */ + static const unsigned char k3_0[] = { + 0x8d, 0x34, 0xef, 0xcb, 0x3b, 0xd5, 0x45, 0xca, + 0x06, 0x2a, 0xec, 0xdf, 0xef, 0x7c, 0x0b, 0xfa + }; + + /* first make sure out input key is the correct length + * rfc 4434. If key is shorter, pad with zeros to the + * the right. If key is longer newKey = aes_xcbc(0, key, keyLen). + */ + if (keyLen < AES_BLOCK_SIZE) { + PORT_Memcpy(newKey, keyValue, keyLen); + PORT_Memset(&newKey[keyLen], 0, AES_BLOCK_SIZE - keyLen); + keyValue = newKey; + } else if (keyLen > AES_BLOCK_SIZE) { + /* calculate our new key = aes_xcbc(0, key, keyLen). Because the + * key above is fixed (0), we can precalculate k1, k2, and k3. + * if this code ever needs to be more generic (support any xcbc + * function rather than just aes, we would probably want to just + * recurse here using our prf functions. This would be safe because + * the recurse case would have keyLen == blocksize and thus skip + * this conditional. + */ + aes_context = AES_CreateContext(k1_0, iv_zero, NSS_AES_CBC, + PR_TRUE, AES_BLOCK_SIZE, AES_BLOCK_SIZE); + /* we know the following loop will execute at least once */ + while (keyLen > AES_BLOCK_SIZE) { + rv = AES_Encrypt(aes_context, newKey, &tmpLen, AES_BLOCK_SIZE, + keyValue, AES_BLOCK_SIZE); + if (rv != SECSuccess) { + goto fail; + } + keyValue += AES_BLOCK_SIZE; + keyLen -= AES_BLOCK_SIZE; + } + PORT_Memcpy(newKey, keyValue, keyLen); + sftk_xcbc_mac_pad(newKey, keyLen, AES_BLOCK_SIZE, k2_0, k3_0); + rv = AES_Encrypt(aes_context, newKey, &tmpLen, AES_BLOCK_SIZE, + newKey, AES_BLOCK_SIZE); + if (rv != SECSuccess) { + goto fail; + } + keyValue = newKey; + AES_DestroyContext(aes_context, PR_TRUE); + } + /* the length of the key in keyValue is known to be AES_BLOCK_SIZE, + * either because it was on input, or it was shorter and extended, or + * because it was mac'd down using aes_xcbc_prf. + */ + aes_context = AES_CreateContext(keyValue, iv_zero, + NSS_AES, PR_TRUE, AES_BLOCK_SIZE, AES_BLOCK_SIZE); + if (aes_context == NULL) { + goto fail; + } + rv = AES_Encrypt(aes_context, k1, &tmpLen, AES_BLOCK_SIZE, + k1data, sizeof(k1data)); + if (rv != SECSuccess) { + goto fail; + } + rv = AES_Encrypt(aes_context, k2, &tmpLen, AES_BLOCK_SIZE, + k2data, sizeof(k2data)); + if (rv != SECSuccess) { + goto fail; + } + rv = AES_Encrypt(aes_context, k3, &tmpLen, AES_BLOCK_SIZE, + k3data, sizeof(k3data)); + if (rv != SECSuccess) { + goto fail; + } + AES_DestroyContext(aes_context, PR_TRUE); + PORT_Memset(newKey, 0, AES_BLOCK_SIZE); + return CKR_OK; +fail: + crv = sftk_MapCryptError(PORT_GetError()); + if (aes_context) { + AES_DestroyContext(aes_context, PR_TRUE); + } + PORT_Memset(k1, 0, AES_BLOCK_SIZE); + PORT_Memset(k2, 0, AES_BLOCK_SIZE); + PORT_Memset(k3, 0, AES_BLOCK_SIZE); + PORT_Memset(newKey, 0, AES_BLOCK_SIZE); + return crv; +} + +/* encode the final pad block of aes xcbc, padBuf is modified */ +CK_RV +sftk_xcbc_mac_pad(unsigned char *padBuf, unsigned int bufLen, int blockSize, + const unsigned char *k2, const unsigned char *k3) +{ + int i; + if (bufLen == blockSize) { + for (i = 0; i < blockSize; i++) { + padBuf[i] ^= k2[i]; + } + } else { + padBuf[bufLen++] = 0x80; + for (i = bufLen; i < blockSize; i++) { + padBuf[i] = 0x00; + } + for (i = 0; i < blockSize; i++) { + padBuf[i] ^= k3[i]; + } + } + return CKR_OK; +} + +/* Map the mechanism to the underlying hash. If the type is not a hash + * or HMAC, return HASH_AlgNULL. This can happen legitimately if + * we are doing AES XCBC */ +static HASH_HashType +sftk_map_hmac_to_hash(CK_MECHANISM_TYPE type) +{ + switch (type) { + case CKM_SHA_1_HMAC: + case CKM_SHA_1: + return HASH_AlgSHA1; + case CKM_MD5_HMAC: + case CKM_MD5: + return HASH_AlgMD5; + case CKM_MD2_HMAC: + case CKM_MD2: + return HASH_AlgMD2; + case CKM_SHA224_HMAC: + case CKM_SHA224: + return HASH_AlgSHA224; + case CKM_SHA256_HMAC: + case CKM_SHA256: + return HASH_AlgSHA256; + case CKM_SHA384_HMAC: + case CKM_SHA384: + return HASH_AlgSHA384; + case CKM_SHA512_HMAC: + case CKM_SHA512: + return HASH_AlgSHA512; + } + return HASH_AlgNULL; +} + +/* + * Generally setup the context based on the mechanism. + * If the mech is HMAC, context->hashObj should be set + * Otherwise it is assumed to be AES XCBC. prf_setup + * checks these assumptions and will return an error + * if they are not met. NOTE: this function does not allocate + * anything, so there is no requirement to free context after + * prf_setup like there is if you call prf_init. + */ +static CK_RV +prf_setup(prfContext *context, CK_MECHANISM_TYPE mech) +{ + context->hashType = sftk_map_hmac_to_hash(mech); + context->hashObj = NULL; + context->hmac = NULL; + context->aes = NULL; + if (context->hashType != HASH_AlgNULL) { + context->hashObj = HASH_GetRawHashObject(context->hashType); + if (context->hashObj == NULL) { + return CKR_GENERAL_ERROR; + } + return CKR_OK; + } else if (mech == CKM_AES_XCBC_MAC) { + return CKR_OK; + } + return CKR_MECHANISM_PARAM_INVALID; +} + +/* return the underlying prf length for this context. This will + * function once the context is setup */ +static CK_RV +prf_length(prfContext *context) +{ + if (context->hashObj) { + return context->hashObj->length; + } + return AES_BLOCK_SIZE; /* AES */ +} + +/* set up the key for the prf. prf_update or prf_final should not be called if + * prf_init has not been called first. Once prf_init returns hmac and + * aes contexts should set and valid. + */ +static CK_RV +prf_init(prfContext *context, const unsigned char *keyValue, + unsigned int keyLen) +{ + CK_RV crv; + + context->hmac = NULL; + if (context->hashObj) { + context->hmac = HMAC_Create(context->hashObj, + keyValue, keyLen, PR_FALSE); + if (context->hmac == NULL) { + return sftk_MapCryptError(PORT_GetError()); + } + HMAC_Begin(context->hmac); + } else { + crv = sftk_aes_xcbc_get_keys(keyValue, keyLen, context->k1, + context->k2, context->k3); + if (crv != CKR_OK) + return crv; + context->nextChar = 0; + context->aes = AES_CreateContext(context->k1, iv_zero, NSS_AES_CBC, + PR_TRUE, sizeof(context->k1), AES_BLOCK_SIZE); + if (context->aes == NULL) { + crv = sftk_MapCryptError(PORT_GetError()); + PORT_Memset(context->k1, 0, sizeof(context->k1)); + PORT_Memset(context->k2, 0, sizeof(context->k2)); + PORT_Memset(context->k3, 0, sizeof(context->k2)); + return crv; + } + } + return CKR_OK; +} + +/* + * process input to the prf + */ +static CK_RV +prf_update(prfContext *context, const unsigned char *buf, unsigned int len) +{ + unsigned int tmpLen; + SECStatus rv; + + if (context->hmac) { + HMAC_Update(context->hmac, buf, len); + } else { + /* AES MAC XCBC*/ + /* We must keep the last block back so that it can be processed in + * final. This is why we only check that nextChar + len > blocksize, + * rather than checking that nextChar + len >= blocksize */ + while (context->nextChar + len > AES_BLOCK_SIZE) { + if (context->nextChar != 0) { + /* first handle fill in any partial blocks in the buffer */ + unsigned int left = AES_BLOCK_SIZE - context->nextChar; + /* note: left can be zero */ + PORT_Memcpy(context->padBuf + context->nextChar, buf, left); + /* NOTE: AES MAC XCBC xors the data with the previous block + * We don't do that step here because our AES_Encrypt mode + * is CBC, which does the xor automatically */ + rv = AES_Encrypt(context->aes, context->macBuf, &tmpLen, + sizeof(context->macBuf), context->padBuf, + sizeof(context->padBuf)); + if (rv != SECSuccess) { + return sftk_MapCryptError(PORT_GetError()); + } + context->nextChar = 0; + len -= left; + buf += left; + } else { + /* optimization. if we have complete blocks to write out + * (and will still have leftover blocks for padbuf in the end). + * we can mac directly out of our buffer without first copying + * them to padBuf */ + rv = AES_Encrypt(context->aes, context->macBuf, &tmpLen, + sizeof(context->macBuf), buf, AES_BLOCK_SIZE); + if (rv != SECSuccess) { + return sftk_MapCryptError(PORT_GetError()); + } + len -= AES_BLOCK_SIZE; + buf += AES_BLOCK_SIZE; + } + } + PORT_Memcpy(context->padBuf + context->nextChar, buf, len); + context->nextChar += len; + } + return CKR_OK; +} + +/* + * free the data associated with the prf. Clear any possible CSPs + * This can safely be called on any context after prf_setup. It can + * also be called an an already freed context. + * A free context can be reused by calling prf_init again without + * the need to call prf_setup. + */ +static void +prf_free(prfContext *context) +{ + if (context->hmac) { + HMAC_Destroy(context->hmac, PR_TRUE); + context->hmac = NULL; + } + if (context->aes) { + PORT_Memset(context->k1, 0, sizeof(context->k1)); + PORT_Memset(context->k2, 0, sizeof(context->k2)); + PORT_Memset(context->k3, 0, sizeof(context->k2)); + PORT_Memset(context->padBuf, 0, sizeof(context->padBuf)); + PORT_Memset(context->macBuf, 0, sizeof(context->macBuf)); + AES_DestroyContext(context->aes, PR_TRUE); + context->aes = NULL; + } +} + +/* + * extract the final prf value. On success, this has the side effect of + * also freeing the context data and clearing the keys + */ +static CK_RV +prf_final(prfContext *context, unsigned char *buf, unsigned int len) +{ + unsigned int tmpLen; + SECStatus rv; + + if (context->hmac) { + unsigned int outLen; + HMAC_Finish(context->hmac, buf, &outLen, len); + if (outLen != len) { + return CKR_GENERAL_ERROR; + } + } else { + /* prf_update had guarrenteed that the last full block is still in + * the padBuf if the input data is a multiple of the blocksize. This + * allows sftk_xcbc_mac_pad to process that pad buf accordingly */ + CK_RV crv = sftk_xcbc_mac_pad(context->padBuf, context->nextChar, + AES_BLOCK_SIZE, context->k2, context->k3); + if (crv != CKR_OK) { + return crv; + } + rv = AES_Encrypt(context->aes, context->macBuf, &tmpLen, + sizeof(context->macBuf), context->padBuf, AES_BLOCK_SIZE); + if (rv != SECSuccess) { + return sftk_MapCryptError(PORT_GetError()); + } + PORT_Memcpy(buf, context->macBuf, len); + } + prf_free(context); + return CKR_OK; +} + +/* + * There are four flavors of ike prf functions here. + * ike_prf is used in both ikeV1 and ikeV2 to generate + * an initial key that all the other keys are generated with. + * + * These functions are called from NSC_DeriveKey with the inKey value + * already looked up, and it expects the CKA_VALUE for outKey to be set. + * + * Depending on usage it returns either: + * 1. prf(Ni|Nr, inKey); (bDataAsKey=TRUE, bRekey=FALSE) + * 2. prf(inKey, Ni|Nr); (bDataAsKkey=FALSE, bRekey=FALSE) + * 3. prf(inKey, newKey | Ni | Nr); (bDataAsKey=FALSE, bRekey=TRUE) + * The resulting output key is always the length of the underlying prf + * (as returned by prf_length()). + * The combination of bDataAsKey=TRUE and bRekey=TRUE is not allowed + * + * Case 1 is used in + * a. ikev2 (rfc5996) inKey is called g^ir, the output is called SKEYSEED + * b. ikev1 (rfc2409) inKey is called g^ir, the output is called SKEYID + * Case 2 is used in ikev1 (rfc2409) inkey is called pre-shared-key, output + * is called SKEYID + * Case 3 is used in ikev2 (rfc5996) rekey case, inKey is SK_d, newKey is + * g^ir (new), the output is called SKEYSEED + */ +CK_RV +sftk_ike_prf(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey, + const CK_NSS_IKE_PRF_DERIVE_PARAMS *params, SFTKObject *outKey) +{ + SFTKAttribute *newKeyValue = NULL; + SFTKObject *newKeyObj = NULL; + unsigned char outKeyData[HASH_LENGTH_MAX]; + unsigned char *newInKey = NULL; + unsigned int newInKeySize; + unsigned int macSize; + CK_RV crv = CKR_OK; + prfContext context; + + crv = prf_setup(&context, params->prfMechanism); + if (crv != CKR_OK) { + return crv; + } + macSize = prf_length(&context); + if ((params->bDataAsKey) && (params->bRekey)) { + return CKR_ARGUMENTS_BAD; + } + if (params->bRekey) { + /* lookup the value of new key from the session and key handle */ + SFTKSession *session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + newKeyObj = sftk_ObjectFromHandle(params->hNewKey, session); + sftk_FreeSession(session); + if (newKeyObj == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + newKeyValue = sftk_FindAttribute(newKeyObj, CKA_VALUE); + if (newKeyValue == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + } + if (params->bDataAsKey) { + /* The key is Ni || Np, so we need to concatenate them together first */ + newInKeySize = params->ulNiLen + params->ulNrLen; + newInKey = PORT_Alloc(newInKeySize); + if (newInKey == NULL) { + crv = CKR_HOST_MEMORY; + goto fail; + } + PORT_Memcpy(newInKey, params->pNi, params->ulNiLen); + PORT_Memcpy(newInKey + params->ulNiLen, params->pNr, params->ulNrLen); + crv = prf_init(&context, newInKey, newInKeySize); + if (crv != CKR_OK) { + goto fail; + } + /* key as the data */ + crv = prf_update(&context, inKey->attrib.pValue, + inKey->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + } else { + 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); + if (crv != CKR_OK) { + goto fail; + } + } + crv = prf_update(&context, params->pNi, params->ulNiLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, params->pNr, params->ulNrLen); + if (crv != CKR_OK) { + goto fail; + } + } + crv = prf_final(&context, outKeyData, macSize); + if (crv != CKR_OK) { + goto fail; + } + + crv = sftk_forceAttribute(outKey, CKA_VALUE, outKeyData, macSize); +fail: + if (newInKey) { + PORT_Free(newInKey); + } + if (newKeyValue) { + sftk_FreeAttribute(newKeyValue); + } + if (newKeyObj) { + sftk_FreeObject(newKeyObj); + } + PORT_Memset(outKeyData, 0, macSize); + prf_free(&context); + return crv; +} + +/* + * The second flavor of ike prf is ike1_prf. + * + * It is used by ikeV1 to generate the various session keys used in the + * connection. It uses the initial key, an optional previous key, and a one byte + * key number to generate a unique key for each of the various session + * functions (encryption, decryption, mac). These keys expect a key size + * (as they may vary in length based on usage). If no length is provided, + * it will default to the length of the prf. + * + * This function returns either: + * prf(inKey, gxyKey || CKYi || CKYr || key_number) + * or + * prf(inKey, prevkey || gxyKey || CKYi || CKYr || key_number) + * depending on the stats of bHasPrevKey + * + * This is defined in rfc2409. For each of the following keys. + * inKey is SKEYID, gxyKey is g^xy + * for outKey = SKEYID_d, bHasPrevKey = false, key_number = 0 + * for outKey = SKEYID_a, prevKey= SKEYID_d, key_number = 1 + * for outKey = SKEYID_e, prevKey= SKEYID_a, key_number = 2 + */ +CK_RV +sftk_ike1_prf(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey, + const CK_NSS_IKE1_PRF_DERIVE_PARAMS *params, SFTKObject *outKey, + unsigned int keySize) +{ + SFTKAttribute *gxyKeyValue = NULL; + SFTKObject *gxyKeyObj = NULL; + SFTKAttribute *prevKeyValue = NULL; + SFTKObject *prevKeyObj = NULL; + SFTKSession *session; + unsigned char outKeyData[HASH_LENGTH_MAX]; + unsigned int macSize; + CK_RV crv; + prfContext context; + + crv = prf_setup(&context, params->prfMechanism); + if (crv != CKR_OK) { + return crv; + } + macSize = prf_length(&context); + if (keySize > macSize) { + return CKR_KEY_SIZE_RANGE; + } + if (keySize == 0) { + keySize = macSize; + } + + /* lookup the two keys from their passed in handles */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + gxyKeyObj = sftk_ObjectFromHandle(params->hKeygxy, session); + if (params->bHasPrevKey) { + prevKeyObj = sftk_ObjectFromHandle(params->hPrevKey, session); + } + sftk_FreeSession(session); + if ((gxyKeyObj == NULL) || ((params->bHasPrevKey) && + (prevKeyObj == NULL))) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + gxyKeyValue = sftk_FindAttribute(gxyKeyObj, CKA_VALUE); + if (gxyKeyValue == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + if (prevKeyObj) { + prevKeyValue = sftk_FindAttribute(prevKeyObj, CKA_VALUE); + if (prevKeyValue == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + } + + /* outKey = prf(inKey, [prevKey|] gxyKey | CKYi | CKYr | keyNumber) */ + crv = prf_init(&context, inKey->attrib.pValue, inKey->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + if (prevKeyValue) { + crv = prf_update(&context, prevKeyValue->attrib.pValue, + prevKeyValue->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + } + crv = prf_update(&context, gxyKeyValue->attrib.pValue, + gxyKeyValue->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, params->pCKYi, params->ulCKYiLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, params->pCKYr, params->ulCKYrLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, ¶ms->keyNumber, 1); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_final(&context, outKeyData, macSize); + if (crv != CKR_OK) { + goto fail; + } + + crv = sftk_forceAttribute(outKey, CKA_VALUE, outKeyData, keySize); +fail: + if (gxyKeyValue) { + sftk_FreeAttribute(gxyKeyValue); + } + if (prevKeyValue) { + sftk_FreeAttribute(prevKeyValue); + } + if (gxyKeyObj) { + sftk_FreeObject(gxyKeyObj); + } + if (prevKeyObj) { + sftk_FreeObject(prevKeyObj); + } + PORT_Memset(outKeyData, 0, macSize); + prf_free(&context); + return crv; +} + +/* + * The third flavor of ike prf is ike1_appendix_b. + * + * It is used by ikeV1 to generate longer key material from skeyid_e. + * Unlike ike1_prf, if no length is provided, this function + * will generate a KEY_RANGE_ERROR. + * + * This function returns (from rfc2409 appendix b): + * Ka = K1 | K2 | K3 | K4 |... Kn + * where: + * K1 = prf(K, 0x00) + * K2 = prf(K, K1) + * K3 = prf(K, K2) + * K4 = prf(K, K3) + * . + * Kn = prf(K, K(n-1)) + * K = inKey + */ +CK_RV +sftk_ike1_appendix_b_prf(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey, + const CK_MECHANISM_TYPE *mech, SFTKObject *outKey, unsigned int keySize) +{ + unsigned char *outKeyData = NULL; + unsigned char *thisKey = NULL; + unsigned char *lastKey = NULL; + unsigned int macSize; + unsigned int outKeySize; + unsigned int genKeySize; + CK_RV crv; + prfContext context; + + crv = prf_setup(&context, *mech); + if (crv != CKR_OK) { + return crv; + } + + macSize = prf_length(&context); + + if (keySize == 0) { + keySize = macSize; + } + + if (keySize <= inKey->attrib.ulValueLen) { + return sftk_forceAttribute(outKey, CKA_VALUE, + inKey->attrib.pValue, keySize); + } + outKeySize = PR_ROUNDUP(keySize, macSize); + outKeyData = PORT_Alloc(outKeySize); + if (outKeyData == NULL) { + crv = CKR_HOST_MEMORY; + goto fail; + } + + /* + * this loop generates on block of the prf, basically + * kn = prf(key, Kn-1) + * Kn is thisKey, Kn-1 is lastKey + * key is inKey + */ + thisKey = outKeyData; + for (genKeySize = 0; genKeySize <= keySize; genKeySize += macSize) { + crv = prf_init(&context, inKey->attrib.pValue, inKey->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + if (lastKey == NULL) { + const unsigned char zero = 0; + crv = prf_update(&context, &zero, 1); + } else { + crv = prf_update(&context, lastKey, macSize); + } + if (crv != CKR_OK) { + goto fail; + } + crv = prf_final(&context, thisKey, macSize); + if (crv != CKR_OK) { + goto fail; + } + lastKey = thisKey; + thisKey += macSize; + } + crv = sftk_forceAttribute(outKey, CKA_VALUE, outKeyData, keySize); +fail: + if (outKeyData) { + PORT_ZFree(outKeyData, outKeySize); + } + prf_free(&context); + return crv; +} + +/* + * The final flavor of ike prf is ike_prf_plus + * + * It is used by ikeV2 to generate the various session keys used in the + * connection. It uses the initial key and a feedback version of the prf + * to generate sufficient bytes to cover all the session keys. The application + * will then use CK_EXTRACT_KEY_FROM_KEY to pull out the various subkeys. + * This function expects a key size to be set by the application to cover + * all the keys. Unlike ike1_prf, if no length is provided, this function + * will generate a KEY_RANGE_ERROR + * + * This function returns (from rfc5996): + * prfplus = T1 | T2 | T3 | T4 |... Tn + * where: + * T1 = prf(K, S | 0x01) + * T2 = prf(K, T1 | S | 0x02) + * T3 = prf(K, T3 | S | 0x03) + * T4 = prf(K, T4 | S | 0x04) + * . + * Tn = prf(K, T(n-1) | n) + * K = inKey, S = seedKey | seedData + */ + +CK_RV +sftk_ike_prf_plus(CK_SESSION_HANDLE hSession, const SFTKAttribute *inKey, + const CK_NSS_IKE_PRF_PLUS_DERIVE_PARAMS *params, SFTKObject *outKey, + unsigned int keySize) +{ + SFTKAttribute *seedValue = NULL; + SFTKObject *seedKeyObj = NULL; + unsigned char *outKeyData = NULL; + unsigned int outKeySize; + unsigned char *thisKey; + unsigned char *lastKey = NULL; + unsigned char currentByte = 0; + unsigned int getKeySize; + unsigned int macSize; + CK_RV crv; + prfContext context; + + if (keySize == 0) { + return CKR_KEY_SIZE_RANGE; + } + + crv = prf_setup(&context, params->prfMechanism); + if (crv != CKR_OK) { + return crv; + } + /* pull in optional seedKey */ + if (params->bHasSeedKey) { + SFTKSession *session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + return CKR_SESSION_HANDLE_INVALID; + } + seedKeyObj = sftk_ObjectFromHandle(params->hSeedKey, session); + sftk_FreeSession(session); + if (seedKeyObj == NULL) { + return CKR_KEY_HANDLE_INVALID; + } + seedValue = sftk_FindAttribute(seedKeyObj, CKA_VALUE); + if (seedValue == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + } else if (params->ulSeedDataLen == 0) { + crv = CKR_ARGUMENTS_BAD; + goto fail; + } + macSize = prf_length(&context); + outKeySize = PR_ROUNDUP(keySize, macSize); + outKeyData = PORT_Alloc(outKeySize); + if (outKeyData == NULL) { + crv = CKR_HOST_MEMORY; + goto fail; + } + + /* + * this loop generates on block of the prf, basically + * Tn = prf(key, Tn-1 | S | n) + * Tn is thisKey, Tn-2 is lastKey, S is seedKey || seedData, + * key is inKey. currentByte = n-1 on entry. + */ + thisKey = outKeyData; + for (getKeySize = 0; getKeySize < keySize; getKeySize += macSize) { + /* if currentByte is 255, we'll overflow when we increment it below. + * This can only happen if keysize > 255*macSize. In that case + * the application has asked for too much key material, so return + * an error */ + if (currentByte == 255) { + crv = CKR_KEY_SIZE_RANGE; + goto fail; + } + crv = prf_init(&context, inKey->attrib.pValue, + inKey->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + + if (lastKey) { + crv = prf_update(&context, lastKey, macSize); + if (crv != CKR_OK) { + goto fail; + } + } + /* prf the key first */ + if (seedValue) { + crv = prf_update(&context, seedValue->attrib.pValue, + seedValue->attrib.ulValueLen); + if (crv != CKR_OK) { + goto fail; + } + } + /* then prf the data */ + if (params->ulSeedDataLen != 0) { + crv = prf_update(&context, params->pSeedData, + params->ulSeedDataLen); + if (crv != CKR_OK) { + goto fail; + } + } + currentByte++; + crv = prf_update(&context, ¤tByte, 1); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_final(&context, thisKey, macSize); + if (crv != CKR_OK) { + goto fail; + } + lastKey = thisKey; + thisKey += macSize; + } + crv = sftk_forceAttribute(outKey, CKA_VALUE, outKeyData, keySize); +fail: + if (outKeyData) { + PORT_ZFree(outKeyData, outKeySize); + } + if (seedValue) { + sftk_FreeAttribute(seedValue); + } + if (seedKeyObj) { + sftk_FreeObject(seedKeyObj); + } + prf_free(&context); + return crv; +} + +/* sftk_aes_xcbc_new_keys: + * + * aes xcbc creates 3 new keys from the input key. The first key will be the + * base key of the underlying cbc. The sign code hooks directly into encrypt + * so we'll have to create a full PKCS #11 key with handle for that key. The + * caller needs to delete the key when it's through setting up the context. + * + * The other two keys will be stored in the sign context until we need them + * at the end. + */ +CK_RV +sftk_aes_xcbc_new_keys(CK_SESSION_HANDLE hSession, + CK_OBJECT_HANDLE hKey, CK_OBJECT_HANDLE_PTR phKey, + unsigned char *k2, unsigned char *k3) +{ + SFTKObject *key = NULL; + SFTKSession *session = NULL; + SFTKObject *inKeyObj = NULL; + SFTKAttribute *inKeyValue = NULL; + CK_KEY_TYPE key_type = CKK_AES; + CK_OBJECT_CLASS objclass = CKO_SECRET_KEY; + CK_BBOOL ck_true = CK_TRUE; + CK_RV crv = CKR_OK; + SFTKSlot *slot = sftk_SlotFromSessionHandle(hSession); + unsigned char buf[AES_BLOCK_SIZE]; + + if (!slot) { + return CKR_SESSION_HANDLE_INVALID; + } + + /* get the session */ + session = sftk_SessionFromHandle(hSession); + if (session == NULL) { + crv = CKR_SESSION_HANDLE_INVALID; + goto fail; + } + + inKeyObj = sftk_ObjectFromHandle(hKey, session); + if (inKeyObj == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + + inKeyValue = sftk_FindAttribute(inKeyObj, CKA_VALUE); + if (inKeyValue == NULL) { + crv = CKR_KEY_HANDLE_INVALID; + goto fail; + } + + crv = sftk_aes_xcbc_get_keys(inKeyValue->attrib.pValue, + inKeyValue->attrib.ulValueLen, buf, k2, k3); + + if (crv != CKR_OK) { + goto fail; + } + + /* + * now lets create an object to hang the attributes off of + */ + key = sftk_NewObject(slot); /* fill in the handle later */ + if (key == NULL) { + crv = CKR_HOST_MEMORY; + goto fail; + } + + /* 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); + sftk_DeleteAttributeType(key, CKA_SIGN); + + /* Add the class, key_type, and value */ + crv = sftk_AddAttributeType(key, CKA_CLASS, &objclass, sizeof(CK_OBJECT_CLASS)); + if (crv != CKR_OK) { + goto fail; + } + crv = sftk_AddAttributeType(key, CKA_KEY_TYPE, &key_type, sizeof(CK_KEY_TYPE)); + if (crv != CKR_OK) { + goto fail; + } + crv = sftk_AddAttributeType(key, CKA_SIGN, &ck_true, sizeof(CK_BBOOL)); + if (crv != CKR_OK) { + goto fail; + } + crv = sftk_AddAttributeType(key, CKA_VALUE, buf, AES_BLOCK_SIZE); + if (crv != CKR_OK) { + goto fail; + } + + /* + * finish filling in the key and link it with our global system. + */ + crv = sftk_handleObject(key, session); + if (crv != CKR_OK) { + goto fail; + } + *phKey = key->handle; +fail: + if (session) { + sftk_FreeSession(session); + } + + if (inKeyValue) { + sftk_FreeAttribute(inKeyValue); + } + if (inKeyObj) { + sftk_FreeObject(inKeyObj); + } + if (key) { + sftk_FreeObject(key); + } + /* clear our CSPs */ + PORT_Memset(buf, 0, sizeof(buf)); + if (crv != CKR_OK) { + PORT_Memset(k2, 0, AES_BLOCK_SIZE); + PORT_Memset(k3, 0, AES_BLOCK_SIZE); + } + return crv; +} + +/* + * Helper function that tests a single prf test vector + */ +static SECStatus +prf_test(CK_MECHANISM_TYPE mech, + const unsigned char *inKey, unsigned int inKeyLen, + const unsigned char *plainText, unsigned int plainTextLen, + const unsigned char *expectedResult, unsigned int expectedResultLen) +{ + PRUint8 ike_computed_mac[HASH_LENGTH_MAX]; + prfContext context; + unsigned int macSize; + CK_RV crv; + + crv = prf_setup(&context, mech); + if (crv != CKR_OK) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + macSize = prf_length(&context); + crv = prf_init(&context, inKey, inKeyLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, plainText, plainTextLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_final(&context, ike_computed_mac, macSize); + if (crv != CKR_OK) { + goto fail; + } + + if (macSize != expectedResultLen) { + goto fail; + } + if (PORT_Memcmp(expectedResult, ike_computed_mac, macSize) != 0) { + goto fail; + } + + /* only do the alignment if the plaintext is long enough */ + if (plainTextLen <= macSize) { + return SECSuccess; + } + prf_free(&context); + /* do it again, but this time tweak with the alignment */ + crv = prf_init(&context, inKey, inKeyLen); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, plainText, 1); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, &plainText[1], macSize); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_update(&context, &plainText[1 + macSize], plainTextLen - (macSize + 1)); + if (crv != CKR_OK) { + goto fail; + } + crv = prf_final(&context, ike_computed_mac, macSize); + if (crv != CKR_OK) { + goto fail; + } + if (PORT_Memcmp(expectedResult, ike_computed_mac, macSize) != 0) { + goto fail; + } + prf_free(&context); + return SECSuccess; +fail: + prf_free(&context); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; +} + +/* + * FIPS Power up Self Tests for IKE. This is in this function so it + * can access the private prf_ functions here. It's called out of fipstest.c + */ +SECStatus +sftk_fips_IKE_PowerUpSelfTests(void) +{ + /* PRF known test vectors */ + static const PRUint8 ike_xcbc_known_key[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + static const PRUint8 ike_xcbc_known_plain_text[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f + }; + static const PRUint8 ike_xcbc_known_mac[] = { + 0xd2, 0xa2, 0x46, 0xfa, 0x34, 0x9b, 0x68, 0xa7, + 0x99, 0x98, 0xa4, 0x39, 0x4f, 0xf7, 0xa2, 0x63 + }; + /* test 2 uses the same key as test 1 */ + static const PRUint8 ike_xcbc_known_plain_text_2[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13 + }; + static const PRUint8 ike_xcbc_known_mac_2[] = { + 0x47, 0xf5, 0x1b, 0x45, 0x64, 0x96, 0x62, 0x15, + 0xb8, 0x98, 0x5c, 0x63, 0x05, 0x5e, 0xd3, 0x08 + }; + static const PRUint8 ike_xcbc_known_key_3[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09 + }; + /* test 3 uses the same plaintest as test 2 */ + static const PRUint8 ike_xcbc_known_mac_3[] = { + 0x0f, 0xa0, 0x87, 0xaf, 0x7d, 0x86, 0x6e, 0x76, + 0x53, 0x43, 0x4e, 0x60, 0x2f, 0xdd, 0xe8, 0x35 + }; + static const PRUint8 ike_xcbc_known_key_4[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0xed, 0xcb + }; + /* test 4 uses the same plaintest as test 2 */ + static const PRUint8 ike_xcbc_known_mac_4[] = { + 0x8c, 0xd3, 0xc9, 0x3a, 0xe5, 0x98, 0xa9, 0x80, + 0x30, 0x06, 0xff, 0xb6, 0x7c, 0x40, 0xe9, 0xe4 + }; + static const PRUint8 ike_sha1_known_key[] = { + 0x59, 0x98, 0x2b, 0x5b, 0xa5, 0x7e, 0x62, 0xc0, + 0x46, 0x0d, 0xef, 0xc7, 0x1e, 0x18, 0x64, 0x63 + }; + static const PRUint8 ike_sha1_known_plain_text[] = { + 0x1c, 0x07, 0x32, 0x1a, 0x9a, 0x7e, 0x41, 0xcd, + 0x88, 0x0c, 0xa3, 0x7a, 0xdb, 0x10, 0xc7, 0x3b, + 0xf0, 0x0e, 0x7a, 0xe3, 0xcf, 0xc6, 0xfd, 0x8b, + 0x51, 0xbc, 0xe2, 0xb9, 0x90, 0xe6, 0xf2, 0x01 + }; + static const PRUint8 ike_sha1_known_mac[] = { + 0x0c, 0x2a, 0xf3, 0x42, 0x97, 0x15, 0x62, 0x1d, + 0x2a, 0xad, 0xc9, 0x94, 0x5a, 0x90, 0x26, 0xfa, + 0xc7, 0x91, 0xe2, 0x4b + }; + static const PRUint8 ike_sha256_known_key[] = { + 0x9d, 0xa2, 0xd5, 0x8f, 0x57, 0xf0, 0x39, 0xf9, + 0x20, 0x4e, 0x0d, 0xd0, 0xef, 0x04, 0xf3, 0x72 + }; + static const PRUint8 ike_sha256_known_plain_text[] = { + 0x33, 0xf1, 0x7a, 0xfc, 0xb6, 0x13, 0x4c, 0xbf, + 0x1c, 0xab, 0x59, 0x87, 0x7d, 0x42, 0xdb, 0x35, + 0x82, 0x22, 0x6e, 0xff, 0x74, 0xdd, 0x37, 0xeb, + 0x8b, 0x75, 0xe6, 0x75, 0x64, 0x5f, 0xc1, 0x69 + }; + static const PRUint8 ike_sha256_known_mac[] = { + 0x80, 0x4b, 0x4a, 0x1e, 0x0e, 0xc5, 0x93, 0xcf, + 0xb6, 0xe4, 0x54, 0x52, 0x41, 0x49, 0x39, 0x6d, + 0xe2, 0x34, 0xd0, 0xda, 0xe2, 0x9f, 0x34, 0xa8, + 0xfd, 0xb5, 0xf9, 0xaf, 0xe7, 0x6e, 0xa6, 0x52 + }; + static const PRUint8 ike_sha384_known_key[] = { + 0xce, 0xc8, 0x9d, 0x84, 0x5a, 0xdd, 0x83, 0xef, + 0xce, 0xbd, 0x43, 0xab, 0x71, 0xd1, 0x7d, 0xb9 + }; + static const PRUint8 ike_sha384_known_plain_text[] = { + 0x17, 0x24, 0xdb, 0xd8, 0x93, 0x52, 0x37, 0x64, + 0xbf, 0xef, 0x8c, 0x6f, 0xa9, 0x27, 0x85, 0x6f, + 0xcc, 0xfb, 0x77, 0xae, 0x25, 0x43, 0x58, 0xcc, + 0xe2, 0x9c, 0x27, 0x69, 0xa3, 0x29, 0x15, 0xc1 + }; + static const PRUint8 ike_sha384_known_mac[] = { + 0x6e, 0x45, 0x14, 0x61, 0x0b, 0xf8, 0x2d, 0x0a, + 0xb7, 0xbf, 0x02, 0x60, 0x09, 0x6f, 0x61, 0x46, + 0xa1, 0x53, 0xc7, 0x12, 0x07, 0x1a, 0xbb, 0x63, + 0x3c, 0xed, 0x81, 0x3c, 0x57, 0x21, 0x56, 0xc7, + 0x83, 0xe3, 0x68, 0x74, 0xa6, 0x5a, 0x64, 0x69, + 0x0c, 0xa7, 0x01, 0xd4, 0x0d, 0x56, 0xea, 0x18 + }; + static const PRUint8 ike_sha512_known_key[] = { + 0xac, 0xad, 0xc6, 0x31, 0x4a, 0x69, 0xcf, 0xcd, + 0x4e, 0x4a, 0xd1, 0x77, 0x18, 0xfe, 0xa7, 0xce + }; + static const PRUint8 ike_sha512_known_plain_text[] = { + 0xb1, 0x5a, 0x9c, 0xfc, 0xe8, 0xc8, 0xd7, 0xea, + 0xb8, 0x79, 0xd6, 0x24, 0x30, 0x29, 0xd4, 0x01, + 0x88, 0xd3, 0xb7, 0x40, 0x87, 0x5a, 0x6a, 0xc6, + 0x2f, 0x56, 0xca, 0xc4, 0x37, 0x7e, 0x2e, 0xdd + }; + static const PRUint8 ike_sha512_known_mac[] = { + 0xf0, 0x5a, 0xa0, 0x36, 0xdf, 0xce, 0x45, 0xa5, + 0x58, 0xd4, 0x04, 0x18, 0xde, 0xa9, 0x80, 0x96, + 0xe5, 0x19, 0xbc, 0x78, 0x41, 0xe3, 0xdb, 0x3d, + 0xd9, 0x36, 0x58, 0xd1, 0x18, 0xc3, 0xe8, 0x3b, + 0x50, 0x2f, 0x39, 0x8e, 0xcb, 0x13, 0x61, 0xec, + 0x77, 0xd3, 0x8a, 0x88, 0x55, 0xef, 0xff, 0x40, + 0x7f, 0x6f, 0x77, 0x2e, 0x5d, 0x65, 0xb5, 0x8e, + 0xb1, 0x13, 0x40, 0x96, 0xe8, 0x47, 0x8d, 0x2b + }; + SECStatus rv; + + rv = prf_test(CKM_AES_XCBC_MAC, + ike_xcbc_known_key, sizeof(ike_xcbc_known_key), + ike_xcbc_known_plain_text, sizeof(ike_xcbc_known_plain_text), + ike_xcbc_known_mac, sizeof(ike_xcbc_known_mac)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_AES_XCBC_MAC, + ike_xcbc_known_key, sizeof(ike_xcbc_known_key), + ike_xcbc_known_plain_text_2, sizeof(ike_xcbc_known_plain_text_2), + ike_xcbc_known_mac_2, sizeof(ike_xcbc_known_mac_2)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_AES_XCBC_MAC, + ike_xcbc_known_key_3, sizeof(ike_xcbc_known_key_3), + ike_xcbc_known_plain_text_2, sizeof(ike_xcbc_known_plain_text_2), + ike_xcbc_known_mac_3, sizeof(ike_xcbc_known_mac_3)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_AES_XCBC_MAC, + ike_xcbc_known_key_4, sizeof(ike_xcbc_known_key_4), + ike_xcbc_known_plain_text_2, sizeof(ike_xcbc_known_plain_text_2), + ike_xcbc_known_mac_4, sizeof(ike_xcbc_known_mac_4)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_SHA_1_HMAC, + ike_sha1_known_key, sizeof(ike_sha1_known_key), + ike_sha1_known_plain_text, sizeof(ike_sha1_known_plain_text), + ike_sha1_known_mac, sizeof(ike_sha1_known_mac)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_SHA256_HMAC, + ike_sha256_known_key, sizeof(ike_sha256_known_key), + ike_sha256_known_plain_text, + sizeof(ike_sha256_known_plain_text), + ike_sha256_known_mac, sizeof(ike_sha256_known_mac)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_SHA384_HMAC, + ike_sha384_known_key, sizeof(ike_sha384_known_key), + ike_sha384_known_plain_text, + sizeof(ike_sha384_known_plain_text), + ike_sha384_known_mac, sizeof(ike_sha384_known_mac)); + if (rv != SECSuccess) + return rv; + rv = prf_test(CKM_SHA512_HMAC, + ike_sha512_known_key, sizeof(ike_sha512_known_key), + ike_sha512_known_plain_text, + sizeof(ike_sha512_known_plain_text), + ike_sha512_known_mac, sizeof(ike_sha512_known_mac)); + return rv; +} diff --git a/security/nss/lib/softoken/sftkpwd.c b/security/nss/lib/softoken/sftkpwd.c index b6d098a07..f2acf7664 100644 --- a/security/nss/lib/softoken/sftkpwd.c +++ b/security/nss/lib/softoken/sftkpwd.c @@ -34,13 +34,40 @@ #include "secerr.h" #include "softoken.h" -const int NSS_DEFAULT_ITERATION_COUNT = -#ifdef DEBUG - 25 -#else - 500 -#endif - ; +static const int NSS_MP_PBE_ITERATION_COUNT = 10000; + +static int +getPBEIterationCount(void) +{ + int c = NSS_MP_PBE_ITERATION_COUNT; + + char *val = getenv("NSS_MIN_MP_PBE_ITERATION_COUNT"); + if (val) { + int minimum = atoi(val); + if (c < minimum) { + c = minimum; + } + } + + val = getenv("NSS_MAX_MP_PBE_ITERATION_COUNT"); + if (val) { + int maximum = atoi(val); + if (c > maximum) { + c = maximum; + } + } + + return c; +} + +PRBool +sftk_isLegacyIterationCountAllowed(void) +{ + static const char *legacyCountEnvVar = + "NSS_ALLOW_LEGACY_DBM_ITERATION_COUNT"; + char *iterEnv = getenv(legacyCountEnvVar); + return (iterEnv && strcmp("0", iterEnv) != 0); +} /****************************************************************** * @@ -244,6 +271,7 @@ sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, if (rv != SECSuccess) { goto loser; } + /* fprintf(stderr, "sftkdb_DecryptAttribute iteration: %d\n", cipherValue.param->iter); */ *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value, PR_FALSE, NULL); @@ -682,7 +710,7 @@ sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key, /* * check to see if we have the NULL password set. * We special case the NULL password so that if you have no password set, you - * don't do thousands of hash rounds. This allows us to startup and get + * don't do thousands of hash rounds. This allows us to startup and get * webpages without slowdown in normal mode. */ SECStatus @@ -823,11 +851,14 @@ sftkdb_finishPasswordCheck(SFTKDBHandle *keydb, SECItem *key, const char *pw, { SECItem *result = NULL; SECStatus rv; - int iterationCount = NSS_DEFAULT_ITERATION_COUNT; + int iterationCount = getPBEIterationCount(); if (*pw == 0) { iterationCount = 1; + } else if (keydb->usesLegacyStorage && !sftk_isLegacyIterationCountAllowed()) { + iterationCount = 1; } + /* decrypt the entry value */ rv = sftkdb_DecryptAttribute(key, value, &result); if (rv != SECSuccess) { @@ -974,92 +1005,78 @@ static CK_RV sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle, CK_OBJECT_HANDLE id, SECItem *newKey, int iterationCount) { - CK_ATTRIBUTE authAttrs[] = { - { CKA_MODULUS, NULL, 0 }, - { CKA_PUBLIC_EXPONENT, NULL, 0 }, - { CKA_CERT_SHA1_HASH, NULL, 0 }, - { CKA_CERT_MD5_HASH, NULL, 0 }, - { CKA_TRUST_SERVER_AUTH, NULL, 0 }, - { CKA_TRUST_CLIENT_AUTH, NULL, 0 }, - { CKA_TRUST_EMAIL_PROTECTION, NULL, 0 }, - { CKA_TRUST_CODE_SIGNING, NULL, 0 }, - { CKA_TRUST_STEP_UP_APPROVED, NULL, 0 }, - { CKA_NSS_OVERRIDE_EXTENSIONS, NULL, 0 }, - }; - CK_ULONG authAttrCount = sizeof(authAttrs) / sizeof(CK_ATTRIBUTE); - unsigned int i, count; SFTKDBHandle *keyHandle = handle; SDB *keyTarget = NULL; - - id &= SFTK_OBJ_ID_MASK; - if (handle->type != SFTK_KEYDB_TYPE) { keyHandle = handle->peerDB; } - if (keyHandle == NULL) { return CKR_OK; } - - /* old DB's don't have meta data, finished with MACs */ + // Old DBs don't have metadata, so we can return early here. keyTarget = SFTK_GET_SDB(keyHandle); if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) { return CKR_OK; } - /* - * STEP 1: find the MACed attributes of this object - */ - (void)sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount); - count = 0; - /* allocate space for the attributes */ - for (i = 0; i < authAttrCount; i++) { - if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)) { - continue; - } - count++; - authAttrs[i].pValue = PORT_ArenaAlloc(arena, authAttrs[i].ulValueLen); - if (authAttrs[i].pValue == NULL) { - break; - } - } - - /* if count was zero, none were found, finished with MACs */ - if (count == 0) { - return CKR_OK; - } - - (void)sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount); - /* ignore error code, we expect some possible errors */ + id &= SFTK_OBJ_ID_MASK; - /* GetAttributeValue just verified the old macs, safe to write - * them out then... */ - for (i = 0; i < authAttrCount; i++) { - SECItem *signText; - SECItem plainText; - SECStatus rv; + CK_ATTRIBUTE_TYPE authAttrTypes[] = { + CKA_MODULUS, + CKA_PUBLIC_EXPONENT, + CKA_CERT_SHA1_HASH, + CKA_CERT_MD5_HASH, + CKA_TRUST_SERVER_AUTH, + CKA_TRUST_CLIENT_AUTH, + CKA_TRUST_EMAIL_PROTECTION, + CKA_TRUST_CODE_SIGNING, + CKA_TRUST_STEP_UP_APPROVED, + CKA_NSS_OVERRIDE_EXTENSIONS, + }; + const CK_ULONG authAttrTypeCount = sizeof(authAttrTypes) / sizeof(authAttrTypes[0]); - if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)) { + // We don't know what attributes this object has, so we update them one at a + // time. + unsigned int i; + for (i = 0; i < authAttrTypeCount; i++) { + CK_ATTRIBUTE authAttr = { authAttrTypes[i], NULL, 0 }; + CK_RV rv = sftkdb_GetAttributeValue(handle, id, &authAttr, 1); + if (rv != CKR_OK) { continue; } - - if (authAttrs[i].ulValueLen == sizeof(CK_ULONG) && - sftkdb_isULONGAttribute(authAttrs[i].type)) { - CK_ULONG value = *(CK_ULONG *)authAttrs[i].pValue; - sftk_ULong2SDBULong(authAttrs[i].pValue, value); - authAttrs[i].ulValueLen = SDB_ULONG_SIZE; + if ((authAttr.ulValueLen == -1) || (authAttr.ulValueLen == 0)) { + continue; } - - plainText.data = authAttrs[i].pValue; - plainText.len = authAttrs[i].ulValueLen; - rv = sftkdb_SignAttribute(arena, newKey, iterationCount, id, - authAttrs[i].type, &plainText, &signText); - if (rv != SECSuccess) { + authAttr.pValue = PORT_ArenaAlloc(arena, authAttr.ulValueLen); + if (authAttr.pValue == NULL) { + return CKR_HOST_MEMORY; + } + rv = sftkdb_GetAttributeValue(handle, id, &authAttr, 1); + if (rv != CKR_OK) { + return rv; + } + if ((authAttr.ulValueLen == -1) || (authAttr.ulValueLen == 0)) { return CKR_GENERAL_ERROR; } - rv = sftkdb_PutAttributeSignature(handle, keyTarget, id, - authAttrs[i].type, signText); - if (rv != SECSuccess) { + // GetAttributeValue just verified the old macs, so it is safe to write + // them out now. + if (authAttr.ulValueLen == sizeof(CK_ULONG) && + sftkdb_isULONGAttribute(authAttr.type)) { + CK_ULONG value = *(CK_ULONG *)authAttr.pValue; + sftk_ULong2SDBULong(authAttr.pValue, value); + authAttr.ulValueLen = SDB_ULONG_SIZE; + } + SECItem *signText; + SECItem plainText; + plainText.data = authAttr.pValue; + plainText.len = authAttr.ulValueLen; + if (sftkdb_SignAttribute(arena, newKey, iterationCount, id, + authAttr.type, &plainText, + &signText) != SECSuccess) { + return CKR_GENERAL_ERROR; + } + if (sftkdb_PutAttributeSignature(handle, keyTarget, id, authAttr.type, + signText) != SECSuccess) { return CKR_GENERAL_ERROR; } } @@ -1071,112 +1088,66 @@ static CK_RV sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb, CK_OBJECT_HANDLE id, SECItem *newKey, int iterationCount) { - CK_RV crv = CKR_OK; - CK_RV crv2; - CK_ATTRIBUTE *first, *last; - CK_ATTRIBUTE privAttrs[] = { - { CKA_VALUE, NULL, 0 }, - { CKA_PRIVATE_EXPONENT, NULL, 0 }, - { CKA_PRIME_1, NULL, 0 }, - { CKA_PRIME_2, NULL, 0 }, - { CKA_EXPONENT_1, NULL, 0 }, - { CKA_EXPONENT_2, NULL, 0 }, - { CKA_COEFFICIENT, NULL, 0 } + CK_ATTRIBUTE_TYPE privAttrTypes[] = { + CKA_VALUE, + CKA_PRIVATE_EXPONENT, + CKA_PRIME_1, + CKA_PRIME_2, + CKA_EXPONENT_1, + CKA_EXPONENT_2, + CKA_COEFFICIENT, }; - CK_ULONG privAttrCount = sizeof(privAttrs) / sizeof(CK_ATTRIBUTE); - unsigned int i, count; + const CK_ULONG privAttrCount = sizeof(privAttrTypes) / sizeof(privAttrTypes[0]); - /* - * STEP 1. Read the old attributes in the clear. - */ - - /* Get the attribute sizes. - * ignore the error code, we will have unknown attributes here */ - crv2 = sftkdb_GetAttributeValue(keydb, id, privAttrs, privAttrCount); - - /* - * find the valid block of attributes and fill allocate space for - * their data */ - first = last = NULL; + // We don't know what attributes this object has, so we update them one at a + // time. + unsigned int i; for (i = 0; i < privAttrCount; i++) { - /* find the block of attributes that are appropriate for this - * objects. There should only be once contiguous block, if not - * there's an error. - * - * find the first and last good entry. - */ - if ((privAttrs[i].ulValueLen == -1) || (privAttrs[i].ulValueLen == 0)) { - if (!first) - continue; - if (!last) { - /* previous entry was last good entry */ - last = &privAttrs[i - 1]; - } + // Read the old attribute in the clear. + CK_ATTRIBUTE privAttr = { privAttrTypes[i], NULL, 0 }; + CK_RV crv = sftkdb_GetAttributeValue(keydb, id, &privAttr, 1); + if (crv != CKR_OK) { continue; } - if (!first) { - first = &privAttrs[i]; + if ((privAttr.ulValueLen == -1) || (privAttr.ulValueLen == 0)) { + continue; } - if (last) { - /* OOPS, we've found another good entry beyond the end of the - * last good entry, we need to fail here. */ - crv = CKR_GENERAL_ERROR; - break; + privAttr.pValue = PORT_ArenaAlloc(arena, privAttr.ulValueLen); + if (privAttr.pValue == NULL) { + return CKR_HOST_MEMORY; } - privAttrs[i].pValue = PORT_ArenaAlloc(arena, privAttrs[i].ulValueLen); - if (privAttrs[i].pValue == NULL) { - crv = CKR_HOST_MEMORY; - break; + crv = sftkdb_GetAttributeValue(keydb, id, &privAttr, 1); + if (crv != CKR_OK) { + return crv; + } + if ((privAttr.ulValueLen == -1) || (privAttr.ulValueLen == 0)) { + return CKR_GENERAL_ERROR; } - } - if (first == NULL) { - /* no valid entries found, return error based on crv2 */ - return crv2; - } - if (last == NULL) { - last = &privAttrs[privAttrCount - 1]; - } - if (crv != CKR_OK) { - return crv; - } - /* read the attributes */ - count = (last - first) + 1; - crv = sftkdb_GetAttributeValue(keydb, id, first, count); - if (crv != CKR_OK) { - return crv; - } - - /* - * STEP 2: read the encrypt the attributes with the new key. - */ - for (i = 0; i < count; i++) { SECItem plainText; SECItem *result; - SECStatus rv; - - plainText.data = first[i].pValue; - plainText.len = first[i].ulValueLen; - rv = sftkdb_EncryptAttribute(arena, newKey, iterationCount, - &plainText, &result); - if (rv != SECSuccess) { + plainText.data = privAttr.pValue; + plainText.len = privAttr.ulValueLen; + if (sftkdb_EncryptAttribute(arena, newKey, iterationCount, + &plainText, &result) != SECSuccess) { return CKR_GENERAL_ERROR; } - first[i].pValue = result->data; - first[i].ulValueLen = result->len; - /* clear our sensitive data out */ + privAttr.pValue = result->data; + privAttr.ulValueLen = result->len; + // Clear sensitive data. PORT_Memset(plainText.data, 0, plainText.len); - } - /* - * STEP 3: write the newly encrypted attributes out directly - */ - id &= SFTK_OBJ_ID_MASK; - keydb->newKey = newKey; - keydb->newDefaultIterationCount = iterationCount; - crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, id, first, count); - keydb->newKey = NULL; + // Write the newly encrypted attributes out directly. + CK_OBJECT_HANDLE newId = id & SFTK_OBJ_ID_MASK; + keydb->newKey = newKey; + keydb->newDefaultIterationCount = iterationCount; + crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, newId, &privAttr, 1); + keydb->newKey = NULL; + if (crv != CKR_OK) { + return crv; + } + } - return crv; + return CKR_OK; } static CK_RV @@ -1266,7 +1237,7 @@ sftkdb_ChangePassword(SFTKDBHandle *keydb, SFTKDBHandle *certdb; unsigned char saltData[SDB_MAX_META_DATA_LEN]; unsigned char valueData[SDB_MAX_META_DATA_LEN]; - int iterationCount = NSS_DEFAULT_ITERATION_COUNT; + int iterationCount = getPBEIterationCount(); CK_RV crv; SDB *db; @@ -1304,6 +1275,8 @@ sftkdb_ChangePassword(SFTKDBHandle *keydb, if (newPin && *newPin == 0) { iterationCount = 1; + } else if (keydb->usesLegacyStorage && !sftk_isLegacyIterationCountAllowed()) { + iterationCount = 1; } rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey); diff --git a/security/nss/lib/softoken/softkver.h b/security/nss/lib/softoken/softkver.h index 3c1842f6e..fce11cd6d 100644 --- a/security/nss/lib/softoken/softkver.h +++ b/security/nss/lib/softoken/softkver.h @@ -17,10 +17,10 @@ * The format of the version string should be * "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]" */ -#define SOFTOKEN_VERSION "3.41.4" SOFTOKEN_ECC_STRING +#define SOFTOKEN_VERSION "3.48" SOFTOKEN_ECC_STRING #define SOFTOKEN_VMAJOR 3 -#define SOFTOKEN_VMINOR 41 -#define SOFTOKEN_VPATCH 4 +#define SOFTOKEN_VMINOR 48 +#define SOFTOKEN_VPATCH 0 #define SOFTOKEN_VBUILD 0 #define SOFTOKEN_BETA PR_FALSE diff --git a/security/nss/lib/softoken/softoken.gyp b/security/nss/lib/softoken/softoken.gyp index ba917cfc8..8b6751182 100644 --- a/security/nss/lib/softoken/softoken.gyp +++ b/security/nss/lib/softoken/softoken.gyp @@ -10,7 +10,7 @@ 'target_name': 'softokn_static', 'type': 'static_library', 'defines': [ - 'NSS_TEST_BUILD', + 'NSS_STATIC_SOFTOKEN', ], 'dependencies': [ 'softokn_base', @@ -58,6 +58,7 @@ 'sdb.c', 'sftkdb.c', 'sftkhmac.c', + 'sftkike.c', 'sftkpars.c', 'sftkpwd.c', 'softkver.c', diff --git a/security/nss/lib/softoken/tlsprf.c b/security/nss/lib/softoken/tlsprf.c index 05e246887..b96733b1f 100644 --- a/security/nss/lib/softoken/tlsprf.c +++ b/security/nss/lib/softoken/tlsprf.c @@ -129,7 +129,7 @@ sftk_TLSPRFVerify(TLSPRFContext *cx, } rv = sftk_TLSPRFUpdate(cx, tmp, &tmpLen, sigLen, NULL, 0); if (rv == SECSuccess) { - rv = (SECStatus)(1 - !PORT_Memcmp(tmp, sig, sigLen)); + rv = (SECStatus)(1 - !NSS_SecureMemcmp(tmp, sig, sigLen)); } PORT_ZFree(tmp, sigLen); return rv; |