So long, PBKDF2!

Password-derived keys are only as strong as the passwords they're based on. Securely leverage all of your users' authentication factors with MFKDF.

GitHub release Coverage: 100% Tests: 100%

Secure

based on argon2id

Fast

≤ 20ms overhead

Transparent

fully open-source

Flexible

modular design

MFKDF factor types: knowledge, soft tokens, USB key, out-of-brand, and intrinsic

Go beyond passwords

Most users have notoriously insecure passwords, with up to 81% of them re-using passwords across multiple accounts. MFKDF improves upon password-based key derivation by using all of a user's authentication factors (not just their password) to derive a key. MFKDF supports deriving key material from a variety of common factors, including HOTP, TOTP, and hardware tokens like YubiKey.

const derivedKey = await mfkdf.derive.key(JSON.parse(keyPolicy), {
  password: mfkdf.derive.factors.password('Tr0ub4dour'),
  hotp: mfkdf.derive.factors.hotp(365287),
  recovery: mfkdf.derive.factors.uuid('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
})

console.log(derivedKey.key.toString('hex')) // -> 34d20ced439ec2f871c96ca377f25771
PBKDF2 key entropy: 14 bits (16s); MFKDF key entropy: 194 bits (10^47 years)

Increased key entropy

All factors must be simultaneously correctly guessed to derive a key using MFKDF, meaning that they can't be individually brute-force attacked. MFKDF keys are thus exponentially harder to crack while remaining just as fast to derive on the fly as password-derived keys for users with the correct credentials.

(await mfkdf.setup.key([
  await mfkdf.setup.factors.password('Tr0ub4dour')
])).entropyBits.real // -> 16.53929514807314

(await mfkdf.setup.key([
  await mfkdf.setup.factors.password('Tr0ub4dour'),
  await mfkdf.setup.factors.hotp(),
  await mfkdf.setup.factors.hmacsha1()
])).entropyBits.real // -> 196.470863717397314
MFKDF key policy: (email AND fingerprint) OR (password AND location)

Enforce advanced policies

MFKDF is not all or nothing: factor requirements can be combined based on simple logical operators like "AND" and "OR." In fact, multi-factor derived keys can enforce arbitrarily complex authentication policies purely cryptographically, without requiring a software reference monitor or trusted hardware.

const policyBasedKey = await mfkdf.policy.setup(
  await mfkdf.policy.or(
    await mfkdf.setup.factors.uuid({ id: 'recoveryCode' }),
    await mfkdf.policy.and(
      await mfkdf.setup.factors.password('Tr0ub4dour'),
      await mfkdf.setup.factors.totp()
    )
  )
)
MFKDF recovery: updating forgotten password factor with new password

Self-service factor recovery

Password-derived keys can't be recoverred after a password is forgotten without creating a centralized point of failure (eg. a master key). Threshold-based multi-factor derived keys can be used to trustlessly recover lost factors on the client side without storing any server-side secrets.

const key = await mfkdf.derive.key(JSON.parse(keyPolicy), {
  hotp: mfkdf.derive.factors.hotp(365287),
  recoveryCode: mfkdf.derive.factors.uuid('9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d')
})

await key.recoverFactor(
  await mfkdf.setup.factors.password('myNewPassword', { id: 'password' })
) // modify key to use new password factor

We appreciate the support of:

Evaluated by USENIX Security '23 AEC: