// 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; }
SECStatus crmf_encrypted_value_unwrap_priv_key(PLArenaPool *poolp, CRMFEncryptedValue *encValue, SECKEYPrivateKey *privKey, SECKEYPublicKey *newPubKey, SECItem *nickname, PK11SlotInfo *slot, unsigned char keyUsage, SECKEYPrivateKey **unWrappedKey, void *wincx) { PK11SymKey *wrappingKey = NULL; CK_MECHANISM_TYPE wrapMechType; SECOidTag oidTag; SECItem *params = NULL, *publicValue = NULL; int keySize, origLen; CK_KEY_TYPE keyType; CK_ATTRIBUTE_TYPE *usage = NULL; CK_ATTRIBUTE_TYPE rsaUsage[] = { CKA_UNWRAP, CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER }; CK_ATTRIBUTE_TYPE dsaUsage[] = { CKA_SIGN }; CK_ATTRIBUTE_TYPE dhUsage[] = { CKA_DERIVE }; int usageCount = 0; oidTag = SECOID_GetAlgorithmTag(encValue->symmAlg); wrapMechType = crmf_get_pad_mech_from_tag(oidTag); keySize = crmf_get_key_size_from_mech(wrapMechType); wrappingKey = PK11_PubUnwrapSymKey(privKey, &encValue->encSymmKey, wrapMechType, CKA_UNWRAP, keySize); if (wrappingKey == NULL) { goto loser; } /* Make the length a byte length instead of bit length*/ params = (encValue->symmAlg != NULL) ? crmf_decode_params(&encValue->symmAlg->parameters) : NULL; origLen = encValue->encValue.len; encValue->encValue.len = CRMF_BITS_TO_BYTES(origLen); publicValue = crmf_get_public_value(newPubKey, NULL); switch (newPubKey->keyType) { default: case rsaKey: keyType = CKK_RSA; switch (keyUsage & (KU_KEY_ENCIPHERMENT | KU_DIGITAL_SIGNATURE)) { case KU_KEY_ENCIPHERMENT: usage = rsaUsage; usageCount = 2; break; case KU_DIGITAL_SIGNATURE: usage = &rsaUsage[2]; usageCount = 2; break; case KU_KEY_ENCIPHERMENT | KU_DIGITAL_SIGNATURE: case 0: /* default to everything */ usage = rsaUsage; usageCount = 4; break; } break; case dhKey: keyType = CKK_DH; usage = dhUsage; usageCount = sizeof(dhUsage) / sizeof(dhUsage[0]); break; case dsaKey: keyType = CKK_DSA; usage = dsaUsage; usageCount = sizeof(dsaUsage) / sizeof(dsaUsage[0]); break; } PORT_Assert(usage != NULL); PORT_Assert(usageCount != 0); *unWrappedKey = PK11_UnwrapPrivKey(slot, wrappingKey, wrapMechType, params, &encValue->encValue, nickname, publicValue, PR_TRUE, PR_TRUE, keyType, usage, usageCount, wincx); encValue->encValue.len = origLen; if (*unWrappedKey == NULL) { goto loser; } SECITEM_FreeItem(publicValue, PR_TRUE); if (params != NULL) { SECITEM_FreeItem(params, PR_TRUE); } PK11_FreeSymKey(wrappingKey); return SECSuccess; loser: *unWrappedKey = NULL; return SECFailure; }