PK11SymKey * PK11_Derive_osw(PK11SymKey *base, CK_MECHANISM_TYPE mechanism , SECItem *param, CK_MECHANISM_TYPE target , CK_ATTRIBUTE_TYPE operation, int keysize) { SECOidTag oid; PK11Context *ctx; unsigned char dkey[HMAC_BUFSIZE]; SECItem dkey_param; SECStatus status; unsigned int len=0; CK_EXTRACT_PARAMS bs; chunk_t dkey_chunk; if( ((mechanism == CKM_SHA256_KEY_DERIVATION) || (mechanism == CKM_SHA384_KEY_DERIVATION)|| (mechanism == CKM_SHA512_KEY_DERIVATION)) && (param == NULL) && (keysize ==0)) { switch (mechanism) { case CKM_SHA256_KEY_DERIVATION: oid = SEC_OID_SHA256; break; case CKM_SHA384_KEY_DERIVATION: oid = SEC_OID_SHA384; break; case CKM_SHA512_KEY_DERIVATION: oid = SEC_OID_SHA512; break; default: DBG(DBG_CRYPT, DBG_log("PK11_Derive_osw: Invalid NSS mechanism ")); break; /*should not reach here*/ } ctx = PK11_CreateDigestContext(oid); PR_ASSERT(ctx!=NULL); status=PK11_DigestBegin(ctx); PR_ASSERT(status == SECSuccess); status=PK11_DigestKey(ctx, base); PR_ASSERT(status == SECSuccess); PK11_DigestFinal(ctx, dkey, &len, sizeof dkey); PK11_DestroyContext(ctx, PR_TRUE); dkey_chunk.ptr = dkey; dkey_chunk.len = len; PK11SymKey *tkey1 = pk11_derive_wrapper_osw(base, CKM_CONCATENATE_DATA_AND_BASE, dkey_chunk, CKM_EXTRACT_KEY_FROM_KEY, CKA_DERIVE, 0); PR_ASSERT(tkey1!=NULL); bs=0; dkey_param.data = (unsigned char*)&bs; dkey_param.len = sizeof (bs); PK11SymKey *tkey2 = PK11_Derive(tkey1, CKM_EXTRACT_KEY_FROM_KEY, &dkey_param, target, operation, len); PR_ASSERT(tkey2!=NULL); if(tkey1!=NULL) { PK11_FreeSymKey(tkey1); } return tkey2; } else { return PK11_Derive(base, mechanism, param, target, operation, keysize); } }
PK11SymKey *pk11_derive_wrapper_osw(PK11SymKey *base, CK_MECHANISM_TYPE mechanism , chunk_t data, CK_MECHANISM_TYPE target , CK_ATTRIBUTE_TYPE operation, int keySize) { CK_KEY_DERIVATION_STRING_DATA string; SECItem param; string.pData = data.ptr; string.ulLen = data.len; param.data = (unsigned char*)&string; param.len = sizeof(string); return PK11_Derive(base, mechanism, ¶m, target, operation, keySize); }
SECStatus tls13_HkdfExtract(PK11SymKey *ikm1, PK11SymKey *ikm2in, SSLHashType baseHash, PK11SymKey **prkp) { CK_NSS_HKDFParams params; SECItem paramsi; SECStatus rv; SECItem *salt; PK11SymKey *prk; static const PRUint8 zeroKeyBuf[HASH_LENGTH_MAX]; PK11SymKey *zeroKey = NULL; PK11SlotInfo *slot = NULL; PK11SymKey *ikm2; params.bExtract = CK_TRUE; params.bExpand = CK_FALSE; params.pInfo = NULL; params.ulInfoLen = 0UL; if (ikm1) { /* TODO([email protected]): This violates the PKCS#11 key boundary * but is imposed on us by the present HKDF interface. */ rv = PK11_ExtractKeyValue(ikm1); if (rv != SECSuccess) return rv; salt = PK11_GetKeyData(ikm1); if (!salt) return SECFailure; params.pSalt = salt->data; params.ulSaltLen = salt->len; PORT_Assert(salt->len > 0); } else { /* Per documentation for CKM_NSS_HKDF_*: * * If the optional salt is given, it is used; otherwise, the salt is * set to a sequence of zeros equal in length to the HMAC output. */ params.pSalt = NULL; params.ulSaltLen = 0UL; } paramsi.data = (unsigned char *)¶ms; paramsi.len = sizeof(params); PORT_Assert(kTlsHkdfInfo[baseHash].pkcs11Mech); PORT_Assert(kTlsHkdfInfo[baseHash].hashSize); PORT_Assert(kTlsHkdfInfo[baseHash].hash == baseHash); /* A zero ikm2 is a key of hash-length 0s. */ if (!ikm2in) { SECItem zeroItem = { siBuffer, (unsigned char *)zeroKeyBuf, kTlsHkdfInfo[baseHash].hashSize }; slot = PK11_GetInternalSlot(); if (!slot) { return SECFailure; } zeroKey = PK11_ImportSymKey(slot, kTlsHkdfInfo[baseHash].pkcs11Mech, PK11_OriginUnwrap, CKA_DERIVE, &zeroItem, NULL); if (!zeroKey) return SECFailure; ikm2 = zeroKey; } else { ikm2 = ikm2in; } PORT_Assert(ikm2); PRINT_BUF(50, (NULL, "HKDF Extract: IKM1/Salt", params.pSalt, params.ulSaltLen)); PRINT_KEY(50, (NULL, "HKDF Extract: IKM2", ikm2)); prk = PK11_Derive(ikm2, kTlsHkdfInfo[baseHash].pkcs11Mech, ¶msi, kTlsHkdfInfo[baseHash].pkcs11Mech, CKA_DERIVE, kTlsHkdfInfo[baseHash].hashSize); if (zeroKey) PK11_FreeSymKey(zeroKey); if (slot) PK11_FreeSlot(slot); if (!prk) return SECFailure; PRINT_KEY(50, (NULL, "HKDF Extract", prk)); *prkp = prk; return SECSuccess; }
// Convert an opaque key handle aKeyHandle back into a Private Key object, using // the long-lived aPersistentKey mixed with aAppParam and the AES Key Wrap // algorithm. static UniqueSECKEYPrivateKey PrivateKeyFromKeyHandle(const UniquePK11SlotInfo& aSlot, const UniquePK11SymKey& aPersistentKey, uint8_t* aKeyHandle, uint32_t aKeyHandleLen, uint8_t* aAppParam, uint32_t aAppParamLen, const nsNSSShutDownPreventionLock&) { MOZ_ASSERT(aSlot); MOZ_ASSERT(aPersistentKey); MOZ_ASSERT(aKeyHandle); MOZ_ASSERT(aAppParam); MOZ_ASSERT(aAppParamLen == SHA256_LENGTH); if (NS_WARN_IF(!aSlot || !aPersistentKey || !aKeyHandle || !aAppParam || aAppParamLen != SHA256_LENGTH)) { return nullptr; } // As we only support one key format ourselves (right now), fail early if // we aren't that length if (NS_WARN_IF(aKeyHandleLen != kVersion1KeyHandleLen)) { return nullptr; } if (NS_WARN_IF(aKeyHandle[0] != SoftTokenHandle::Version1)) { // Unrecognized version return nullptr; } uint8_t saltLen = aKeyHandle[1]; uint8_t* saltPtr = aKeyHandle + 2; if (NS_WARN_IF(saltLen != kSaltByteLen)) { return nullptr; } // Prepare the HKDF (https://tools.ietf.org/html/rfc5869) CK_NSS_HKDFParams hkdfParams = { true, saltPtr, saltLen, true, aAppParam, aAppParamLen }; SECItem kdfParams = { siBuffer, (unsigned char*)&hkdfParams, sizeof(hkdfParams) }; // Derive a wrapping key from aPersistentKey, the salt, and the aAppParam. // CKM_AES_KEY_GEN and CKA_WRAP are key type and usage attributes of the // derived symmetric key and don't matter because we ignore them anyway. UniquePK11SymKey wrapKey(PK11_Derive(aPersistentKey.get(), CKM_NSS_HKDF_SHA256, &kdfParams, CKM_AES_KEY_GEN, CKA_WRAP, kWrappingKeyByteLen)); if (NS_WARN_IF(!wrapKey.get())) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to derive a wrapping key, NSS error #%d", PORT_GetError())); return nullptr; } uint8_t wrappedLen = aKeyHandleLen - saltLen - 2; uint8_t* wrappedPtr = aKeyHandle + saltLen + 2; ScopedAutoSECItem wrappedKeyItem(wrappedLen); memcpy(wrappedKeyItem.data, wrappedPtr, wrappedKeyItem.len); ScopedAutoSECItem pubKey(kPublicKeyLen); UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, /* default IV */ nullptr )); CK_ATTRIBUTE_TYPE usages[] = { CKA_SIGN }; int usageCount = 1; UniqueSECKEYPrivateKey unwrappedKey( PK11_UnwrapPrivKey(aSlot.get(), wrapKey.get(), CKM_NSS_AES_KEY_WRAP_PAD, param.get(), &wrappedKeyItem, /* no nickname */ nullptr, /* discard pubkey */ &pubKey, /* not permanent */ false, /* non-exportable */ true, CKK_EC, usages, usageCount, /* wincx */ nullptr)); if (NS_WARN_IF(!unwrappedKey)) { // Not our key. MOZ_LOG(gNSSTokenLog, LogLevel::Debug, ("Could not unwrap key handle, NSS Error #%d", PORT_GetError())); return nullptr; } return unwrappedKey; }
// Convert a Private Key object into an opaque key handle, using AES Key Wrap // with the long-lived aPersistentKey mixed with aAppParam to convert aPrivKey. // The key handle's format is version || saltLen || salt || wrappedPrivateKey static UniqueSECItem KeyHandleFromPrivateKey(const UniquePK11SlotInfo& aSlot, const UniquePK11SymKey& aPersistentKey, uint8_t* aAppParam, uint32_t aAppParamLen, const UniqueSECKEYPrivateKey& aPrivKey, const nsNSSShutDownPreventionLock&) { MOZ_ASSERT(aSlot); MOZ_ASSERT(aPersistentKey); MOZ_ASSERT(aAppParam); MOZ_ASSERT(aPrivKey); if (NS_WARN_IF(!aSlot || !aPersistentKey || !aPrivKey || !aAppParam)) { return nullptr; } // Generate a random salt uint8_t saltParam[kSaltByteLen]; SECStatus srv = PK11_GenerateRandomOnSlot(aSlot.get(), saltParam, sizeof(saltParam)); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to generate a salt, NSS error #%d", PORT_GetError())); return nullptr; } // Prepare the HKDF (https://tools.ietf.org/html/rfc5869) CK_NSS_HKDFParams hkdfParams = { true, saltParam, sizeof(saltParam), true, aAppParam, aAppParamLen }; SECItem kdfParams = { siBuffer, (unsigned char*)&hkdfParams, sizeof(hkdfParams) }; // Derive a wrapping key from aPersistentKey, the salt, and the aAppParam. // CKM_AES_KEY_GEN and CKA_WRAP are key type and usage attributes of the // derived symmetric key and don't matter because we ignore them anyway. UniquePK11SymKey wrapKey(PK11_Derive(aPersistentKey.get(), CKM_NSS_HKDF_SHA256, &kdfParams, CKM_AES_KEY_GEN, CKA_WRAP, kWrappingKeyByteLen)); if (NS_WARN_IF(!wrapKey.get())) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to derive a wrapping key, NSS error #%d", PORT_GetError())); return nullptr; } UniqueSECItem wrappedKey(::SECITEM_AllocItem(/* default arena */ nullptr, /* no buffer */ nullptr, kWrappedKeyBufLen)); if (NS_WARN_IF(!wrappedKey)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory")); return nullptr; } UniqueSECItem param(PK11_ParamFromIV(CKM_NSS_AES_KEY_WRAP_PAD, /* default IV */ nullptr )); srv = PK11_WrapPrivKey(aSlot.get(), wrapKey.get(), aPrivKey.get(), CKM_NSS_AES_KEY_WRAP_PAD, param.get(), wrappedKey.get(), /* wincx */ nullptr); if (NS_WARN_IF(srv != SECSuccess)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to wrap U2F key, NSS error #%d", PORT_GetError())); return nullptr; } // Concatenate the salt and the wrapped Private Key together mozilla::dom::CryptoBuffer keyHandleBuf; if (NS_WARN_IF(!keyHandleBuf.SetCapacity(wrappedKey.get()->len + sizeof(saltParam) + 2, mozilla::fallible))) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory")); return nullptr; } // It's OK to ignore the return values here because we're writing into // pre-allocated space keyHandleBuf.AppendElement(SoftTokenHandle::Version1, mozilla::fallible); keyHandleBuf.AppendElement(sizeof(saltParam), mozilla::fallible); keyHandleBuf.AppendElements(saltParam, sizeof(saltParam), mozilla::fallible); keyHandleBuf.AppendSECItem(wrappedKey.get()); UniqueSECItem keyHandle(::SECITEM_AllocItem(nullptr, nullptr, 0)); if (NS_WARN_IF(!keyHandle)) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory")); return nullptr; } if (NS_WARN_IF(!keyHandleBuf.ToSECItem(/* default arena */ nullptr, keyHandle.get()))) { MOZ_LOG(gNSSTokenLog, LogLevel::Warning, ("Failed to allocate memory")); return nullptr; } return keyHandle; }
/* MUST BE THREAD-SAFE */ static PK11SymKey *calc_dh_shared(const chunk_t g, /* converted to SECItem */ /*const*/ SECKEYPrivateKey *privk, /* NSS doesn't do const */ const struct oakley_group_desc *group, const SECKEYPublicKey *local_pubk) { struct timeval tv0; SECKEYPublicKey *remote_pubk; SECItem nss_g; PK11SymKey *dhshared; PRArenaPool *arena; SECStatus status; unsigned int dhshared_len; DBG(DBG_CRYPT, DBG_log("Started DH shared-secret computation in NSS:")); gettimeofday(&tv0, NULL); arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); passert(arena != NULL); remote_pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); remote_pubk->arena = arena; remote_pubk->keyType = dhKey; remote_pubk->pkcs11Slot = NULL; remote_pubk->pkcs11ID = CK_INVALID_HANDLE; nss_g.data = g.ptr; nss_g.len = (unsigned int)g.len; nss_g.type = siBuffer; status = SECITEM_CopyItem(remote_pubk->arena, &remote_pubk->u.dh.prime, &local_pubk->u.dh.prime); passert(status == SECSuccess); status = SECITEM_CopyItem(remote_pubk->arena, &remote_pubk->u.dh.base, &local_pubk->u.dh.base); passert(status == SECSuccess); status = SECITEM_CopyItem(remote_pubk->arena, &remote_pubk->u.dh.publicValue, &nss_g); passert(status == SECSuccess); dhshared = PK11_PubDerive(privk, remote_pubk, PR_FALSE, NULL, NULL, CKM_DH_PKCS_DERIVE, CKM_CONCATENATE_DATA_AND_BASE, CKA_DERIVE, group->bytes, lsw_return_nss_password_file_info()); passert(dhshared != NULL); dhshared_len = PK11_GetKeyLength(dhshared); if (group->bytes > dhshared_len) { DBG(DBG_CRYPT, DBG_log("Dropped %lu leading zeros", group->bytes - dhshared_len)); chunk_t zeros; PK11SymKey *newdhshared; CK_KEY_DERIVATION_STRING_DATA string_params; SECItem params; zeros = hmac_pads(0x00, group->bytes - dhshared_len); params.data = (unsigned char *)&string_params; params.len = sizeof(string_params); string_params.pData = zeros.ptr; string_params.ulLen = zeros.len; newdhshared = PK11_Derive(dhshared, CKM_CONCATENATE_DATA_AND_BASE, ¶ms, CKM_CONCATENATE_DATA_AND_BASE, CKA_DERIVE, 0); passert(newdhshared != NULL); PK11_FreeSymKey(dhshared); dhshared = newdhshared; freeanychunk(zeros); } else { DBG(DBG_CRYPT, DBG_log("Dropped no leading zeros %d", dhshared_len)); } /* nss_symkey_log(dhshared, "dhshared"); */ DBG(DBG_CRYPT, { struct timeval tv1; unsigned long tv_diff; gettimeofday(&tv1, NULL); tv_diff = (tv1.tv_sec - tv0.tv_sec) * 1000000 + (tv1.tv_usec - tv0.tv_usec); DBG_log("calc_dh_shared(): time elapsed (%s): %ld usec", enum_show(&oakley_group_names, group->group), tv_diff); });
/* MUST BE THREAD-SAFE */ PK11SymKey *calc_dh_shared(const chunk_t g, /* converted to SECItem */ /*const*/ SECKEYPrivateKey *privk, /* NSS doesn't do const */ const struct oakley_group_desc *group, const SECKEYPublicKey *local_pubk, const char **story) { SECStatus status; DBG(DBG_CRYPT, DBG_log("Started DH shared-secret computation in NSS:")); PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); passert(arena != NULL); SECKEYPublicKey *remote_pubk = (SECKEYPublicKey *) PORT_ArenaZAlloc(arena, sizeof(SECKEYPublicKey)); remote_pubk->arena = arena; remote_pubk->keyType = dhKey; remote_pubk->pkcs11Slot = NULL; remote_pubk->pkcs11ID = CK_INVALID_HANDLE; SECItem nss_g = { .data = g.ptr, .len = (unsigned int)g.len, .type = siBuffer }; status = SECITEM_CopyItem(remote_pubk->arena, &remote_pubk->u.dh.prime, &local_pubk->u.dh.prime); passert(status == SECSuccess); status = SECITEM_CopyItem(remote_pubk->arena, &remote_pubk->u.dh.base, &local_pubk->u.dh.base); passert(status == SECSuccess); status = SECITEM_CopyItem(remote_pubk->arena, &remote_pubk->u.dh.publicValue, &nss_g); passert(status == SECSuccess); PK11SymKey *dhshared = PK11_PubDerive(privk, remote_pubk, PR_FALSE, NULL, NULL, CKM_DH_PKCS_DERIVE, CKM_CONCATENATE_DATA_AND_BASE, CKA_DERIVE, group->bytes, lsw_return_nss_password_file_info()); if (dhshared != NULL) { unsigned int shortfall = group->bytes - PK11_GetKeyLength(dhshared); if (shortfall > 0) { /* * We've got to pad the result with [shortfall] 0x00 bytes. * The chance of shortfall being n should be 1 in 256^n. * So really zauto ought to be big enough for the zeros. * If it isn't, we allocate space on the heap * (this will likely never be executed). */ DBG(DBG_CRYPT, DBG_log("restoring %u DHshared leading zeros", shortfall)); unsigned char zauto[10]; unsigned char *z = shortfall <= sizeof(zauto) ? zauto : alloc_bytes(shortfall, "DH shortfall"); memset(z, 0x00, shortfall); CK_KEY_DERIVATION_STRING_DATA string_params = { .pData = z, .ulLen = shortfall }; SECItem params = { .data = (unsigned char *)&string_params, .len = sizeof(string_params) }; PK11SymKey *newdhshared = PK11_Derive(dhshared, CKM_CONCATENATE_DATA_AND_BASE, ¶ms, CKM_CONCATENATE_DATA_AND_BASE, CKA_DERIVE, 0); passert(newdhshared != NULL); if (z != zauto) pfree(z); free_any_symkey("dhshared", &dhshared); dhshared = newdhshared; } } *story = enum_name(&oakley_group_names, group->group); SECKEY_DestroyPublicKey(remote_pubk); return dhshared; }