MFKDF
JavaScript Implementation of a Next-Generation Multi-Factor Key Derivation Function (MFKDF2)
Install / Use
/learn @multifactor/MFKDFREADME
<img src="https://raw.githubusercontent.com/multifactor/MFKDF/main/logo.png" height="64">
Next-Generation Multi-Factor Key Derivation Function (MFKDF2)
Site | Docs | Demo | Videos | Contributing | Security | Multifactor | Paper | Author
The Next-Generation Multi-Factor Key Derivation Function (MFKDF2) is a function that takes multiple inputs and outputs a string of bytes that can be used as a cryptographic key. It serves the same purpose as a password-based key derivation function (PBKDF), but is stronger than password-based key derivation due to its support for multiple authentication factors, including HOTP, TOTP, and hardware tokens like YubiKey. MFKDF2 also enables self-service account recovery via K-of-N (secret-sharing style) key derivation, eliminating the need for central recovery keys, and supports arbitrarily complex key derivation policies. It builds on the now-deprecated original MFKDF.
Contents
Introduction
Password-based key derivation functions (eg. PBKDF2) are used to derive cryptographic keys from a password. Doing so allows users to encrypt secrets on the client side without having to worry about key management. But most users have notoriously insecure passwords, with up to 81% of them re-using passwords across multiple accounts. Even when multi-factor authentication is used to protect an account with a weak password, and password-derived keys are only as secure as the passwords they're based on.
The multi-factor key derivation function (MFKDF) improves upon password-based key derivation by using all of a user's authentication factors, not just their password, to derive a key. This library provides four key advantages over current password-based key derivation techniques:
- Beyond passwords: supports deriving key material from a variety of common factors, including HOTP, TOTP, and hardware tokens like YubiKey.
- Increased entropy: all factors must be simultaneously correct to derive a key, exponentially increasing the difficulty of brute-force attacks.
- Self-service recovery: threshold keys can be used to recover lost factors on the client side without creating a centralized point of failure.
- Authentication policies: multi-factor derived keys can cryptographically enforce arbitrarily complex authentication policies.
Getting Started
Download MFKDF.js
There are three ways to add mfkdf.js to your project: self-hosted, using a CDN, or using NPM (recommended).
Option 1: Self-Hosted
First download the latest release on GitHub, then add mfkdf.js or mfkdf.min.js to your page like so:
<script src="mfkdf.min.js"></script>
Option 2: CDN
You can automatically include the latest version of mfkdf.min.js in your page like so:
<script src="https://cdn.jsdelivr.net/gh/multifactor/mfkdf/mfkdf.min.js"></script>
Note that this may automatically update to include breaking changes in the future. Therefore, it is recommended that you get the latest single-version tag with SRI from jsDelivr instead.
Option 3: NPM (recommended)
Add MFKDF to your NPM project:
npm install mfkdf
Require MFKDF like so:
const mfkdf = require('mfkdf');
Migrating
MFKDF2 retains as much backwards-compatibility as possible with the original MFKDF API, but makes the following breaking changes compared to the original MFKDF:
- Removed ISO key-based authentication, we recommend use of MFCHF2 instead
- Removed support for enveloped secrets and keys, we recommend deriving sub-keys or using external secret storage
- Removed support for KDFs other than argon2id; any argon2 params higher than (but not lower than) OWASP defaults are supported
- Removed support for custom key sizes; derived keys are always 256 bits, and can be stretched or truncated from there
Additionally, we've made a number of major security and feature improvements, including:
- A number of security improvements, including share encryption, policy integrity, and per-factor salting
- Key derivation parameters can be hardened over time without changing the key
- Support for Passkeys as a factor via the WebAuthn PRF extension
- Support for deriving passwords from an MFKDF2-derived key (via MFDPG2)
- Optional support for timing oracles to harden TOTP factor construction
In general, MFKDF2 is more opinionated than the original MFKDF, with the goal of being more secure by default and making insecure design decisions harder, at the cost of some flexibility. It also focuses on key derivation has less anscillary features, offloading cryptographic use of derived keys to external libraries in order to improve this library's auditability and reduce its attack surface. As a result, it also removes many problematic dependencies from the original MFKDF library.
Multi-Factor Key Derivation
Setup Key
Before you can derive a multi-factor derived key, you must setup a "key policy," which is essentially just a JSON document which specifies how a key is derived and ensures the key is the same every time (as long as the factors are correct). Setting up this policy yourself is difficult and potentially dangerous if insecure configuration options are chosen; therefore, the setup.key utility is provided with safe defaults. You can use it like so:
// setup 16 byte 3-factor multi-factor derived key with a password, HOTP code, and UUID code
const setup = await mfkdf.setup.key([
await mfkdf.setup.factors.password('password'),
await mfkdf.setup.factors.hotp({ secret: Buffer.from('abcdefghijklmnopqrst') }),
await mfkdf.setup.factors.uuid({ uuid: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' })
])
Every factor in a multi-factor derived key must have a unique ID. If you use multiple factors of the same type, make sure to specify an ID like so:
const result = await mfkdf.setup.key([
await mfkdf.setup.factors.password('Tr0ub4dour', { id: 'password1' }),
await mfkdf.setup.factors.password('abcdefgh', { id: 'password2' })
])
Setup returns an MFKDFDerivedKey object. Therefore, you can now access the derived key directly:
setup.key.toString('hex') // -> 34d2…5771
Some of the factors you setup may have their own outputs at this stage. You can access them like so:
console.log(setup.outputs)
// -> {
// password: { strength: { ... } },
// hotp: { uri: 'otpauth://...', ... },
// uuid: { uuid: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' }
// }
You can also save the resulting key policy for later use like so:
// save key policy
const policy = JSON.stringify(setup.policy)
Derive Key
Later, you can derive the same key using the saved key policy and established factors:
// derive key using the 3 factors
const derive = await mfkdf.derive.key(JSON.parse(policy), {
password: mfkdf.derive.factors.password('password'),
hotp: mfkdf.derive.factors.hotp(241063),
uuid: mfkdf.derive.factors.uuid('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
})
Derive also returns an MFKDFDerivedKey object. Therefore, you can again access the derived key directly like so:
// key should be the same if correct factors are provided
derive.key.toString('hex') // -> 34d2…5771
Some factors (like TOTP and HOTP) cause the key policy to change every time it is derived. Thus, don't forget to save the new key policy after deriving it:
// save new key policy
const newPolicy = JSON.stringify(derive.policy)
Factors
The following basic MFKDF factors are currently supported:
| Factor | Setup | Derive | | --------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | Password | setup.factors.password | derive.factors.password | | UUID | setup.factors.uuid | [derive.factors.uuid]
Related Skills
node-connect
340.5kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.2kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
340.5kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.2kCommit, push, and open a PR
