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 | 1x 1x 1x 1x 1x 1x 1x 1x 164x 164x 164x 163x 161x 161x 161x 161x 324x 253x 252x 3x 249x 248x 248x 248x 248x 251x 251x 251x 71x 71x 322x 156x 156x 156x 156x 317x 250x 156x 156x 1x | /** * @file Multi-factor Key Derivation * @copyright Multifactor 2022 All Rights Reserved * * @description * Derive a multi-factor derived key * * @author Vivek Nair (https://nair.me) <[email protected]> */ const Ajv = require('ajv') const policySchema = require('./policy.json') const combine = require('../secrets/combine').combine const recover = require('../secrets/recover').recover const kdf = require('../kdf').kdf const { hkdf } = require('@panva/hkdf') const xor = require('buffer-xor') const MFKDFDerivedKey = require('../classes/MFKDFDerivedKey') /** * Derive a key from multiple factors of input * * @example * // setup 16 byte 2-of-3-factor multi-factor derived key with a password, HOTP code, and UUID recovery code * const setup = await mfkdf.setup.key([ * await mfkdf.setup.factors.password('password'), * await mfkdf.setup.factors.hotp({ secret: Buffer.from('hello world') }), * await mfkdf.setup.factors.uuid({ id: 'recovery', uuid: '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d' }) * ], {threshold: 2, size: 16}) * * // derive key using 2 of the 3 factors * const derive = await mfkdf.derive.key(setup.policy, { * password: mfkdf.derive.factors.password('password'), * hotp: mfkdf.derive.factors.hotp(365287) * }) * * setup.key.toString('hex') // -> 34d20ced439ec2f871c96ca377f25771 * derive.key.toString('hex') // -> 34d20ced439ec2f871c96ca377f25771 * * @param {Object} policy - The key policy for the key being derived * @param {Object.<string, MFKDFFactor>} factors - Factors used to derive this key * @returns {MFKDFDerivedKey} A multi-factor derived key object * @author Vivek Nair (https://nair.me) <[email protected]> * @since 0.9.0 * @async * @memberOf derive */ async function key (policy, factors) { const ajv = new Ajv() const valid = ajv.validate(policySchema, policy) if (!valid) throw new TypeError('invalid key policy', ajv.errors) if (Object.keys(factors).length < policy.threshold) throw new RangeError('insufficient factors provided to derive key') const shares = [] const newFactors = [] const outputs = {} for (const factor of policy.factors) { if (factors[factor.id] && typeof factors[factor.id] === 'function') { const material = await factors[factor.id](factor.params) let share if (material.type === 'persisted') { share = material.data } else { if (material.type !== factor.type) throw new TypeError('wrong factor material function used for this factor type') const pad = Buffer.from(factor.pad, 'base64') let stretched = Buffer.from(await hkdf('sha512', material.data, '', '', policy.size)) if (Buffer.byteLength(pad) > policy.size) stretched = Buffer.concat([Buffer.alloc(Buffer.byteLength(pad) - policy.size), stretched]) share = xor(pad, stretched) } shares.push(share) if (material.output) outputs[factor.id] = await material.output() newFactors.push(material.params) } else { shares.push(null) newFactors.push(null) } } if (shares.filter(x => Buffer.isBuffer(x)).length < policy.threshold) throw new RangeError('insufficient factors provided to derive key') const secret = combine(shares, policy.threshold, policy.factors.length) const key = await kdf(secret, Buffer.from(policy.salt, 'base64'), policy.size, policy.kdf) const newPolicy = JSON.parse(JSON.stringify(policy)) for (const [index, factor] of newFactors.entries()) { if (typeof factor === 'function') { newPolicy.factors[index].params = await factor({ key }) } } const originalShares = recover(shares, policy.threshold, policy.factors.length) return new MFKDFDerivedKey(newPolicy, key, secret, originalShares, outputs) } module.exports.key = key |