// 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;
}
Exemplo n.º 2
0
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;
}