Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 | 1x 13x 2x 11x 3x 8x 8x 1x 7x 7x 7x 7x 7x 224x 224x 7x 1x 2x 2x 2x 1x | /**
* @file Multi-Factor Derived Key Threshold Hints
* @copyright Multifactor, Inc. 2022–2025
*
* @description
* Add factor hints to a multi-factor derived key
*
* @author Vivek Nair (https://nair.me) <vivek@nair.me>
*/
const { decrypt, hkdf } = require('../../crypt')
/**
* Get a (probabilistic) hint for a factor to (usually) help verify which factor is wrong.
* Makes the key slightly easier to brute-force (about 2^bits times easier), so be careful.
*
* @example
* const setup = await mfkdf.setup.key([
* await mfkdf.setup.factors.password("password1", {
* id: "password1",
* }),
* ]);
*
* const hint = await setup.getHint("password1", 7); // -> 1011000
*
* const derived = await mfkdf.derive.key(setup.policy, {
* password1: mfkdf.derive.factors.password("password1"),
* });
*
* const hint2 = await derived.getHint("password1", 7); // -> 1011000
* hint2.should.equal(hint);
*
* @param {string} factor - Factor ID to add hint for
* @param {string} [bits=7] - Bits of entropy to reveal (default: 7 bits; more is risky)
* @returns {Buffer} Derived sub-key
* @author Vivek Nair (https://nair.me) <vivek@nair.me>
* @since 2.0.0
* @memberOf MFKDFDerivedKey
* @async
*/
async function getHint (factor, bits = 7) {
if (typeof factor !== 'string' || factor.length === 0) {
throw new TypeError('factor id must be a non-empty string')
}
if (typeof bits !== 'number' || bits < 1 || bits > 256) {
throw new TypeError('bits must be a number between 1 and 256')
}
// get factor data
const factorData = this.policy.factors.find((f) => f.id === factor)
if (!factorData) {
throw new RangeError('factor id not found in policy')
}
const pad = Buffer.from(factorData.secret, 'base64')
const secretKey = Buffer.from(
await hkdf(
'sha256',
this.key,
Buffer.from(factorData.salt, 'base64'),
'mfkdf2:factor:secret:' + factorData.id,
32
)
)
const factorMaterial = decrypt(pad, secretKey)
const buffer = Buffer.from(
await hkdf(
'sha256',
factorMaterial,
Buffer.from(factorData.salt, 'base64'),
'mfkdf2:factor:hint:' + factorData.id,
32
)
)
const binaryString = [...buffer]
.map((byte) => byte.toString(2).padStart(8, '0'))
.reduce((acc, bits) => acc + bits, '')
return binaryString.slice(-1 * bits)
}
module.exports.getHint = getHint
/**
* Add a (probabilistic) hint for a factor to (usually) help verify which factor is wrong.
* Permanently adds the hint to the key policy, and throws an error when the factor is wrong.
* Makes the key slightly easier to brute-force (about 2^bits times easier), so be careful.
* Overrides the existing hint if one already exists.
*
* @example
* const setup = await mfkdf.setup.key(
* [
* await mfkdf.setup.factors.password('password1', {
* id: 'password1'
* })
* ],
* {
* integrity: false
* }
* )
*
* await setup.addHint('password1')
*
* await mfkdf.derive
* .key(
* setup.policy,
* {
* password1: mfkdf.derive.factors.password('password2')
* },
* false
* )
* .should.be.rejectedWith(RangeError)
*
* @param {string} factor - Factor ID to add hint for
* @param {string} [bits=7] - Bits of entropy to reveal (default: 7 bits; more is risky)
* @returns {Buffer} Derived sub-key
* @author Vivek Nair (https://nair.me) <vivek@nair.me>
* @since 2.0.0
* @memberOf MFKDFDerivedKey
* @async
*/
async function addHint (factor, bits = 7) {
const hint = await this.getHint(factor, bits)
const factorData = this.policy.factors.find((f) => f.id === factor)
factorData.hint = hint
}
module.exports.addHint = addHint
|