All files / src/policy derive.js

100% Statements 21/21
100% Branches 10/10
100% Functions 2/2
100% Lines 19/19

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                    1x 1x 1x 1x     47x 47x   47x 88x 34x 32x     54x 34x         47x                                                                       17x 17x 16x   15x   15x   1x  
/**
 * @file MFKDF Policy Derivation
 * @copyright Multifactor 2022 All Rights Reserved
 *
 * @description
 * Derive key from policy and given factors
 *
 * @author Vivek Nair (https://nair.me) <[email protected]>
 */
 
const validate = require('./validate').validate
const evaluate = require('./evaluate').evaluate
const stack = require('../derive/factors/stack').stack
const deriveKey = require('../derive/key').key
 
function expand (policy, factors) {
  const parsedFactors = {}
  const ids = Object.keys(factors)
 
  for (const factor of policy.factors) {
    if (factor.type === 'stack') {
      if (evaluate(factor.params, ids)) {
        parsedFactors[factor.id] = stack(expand(factor.params, factors))
      }
    } else {
      if (ids.includes(factor.id)) {
        parsedFactors[factor.id] = factors[factor.id]
      }
    }
  }
 
  return parsedFactors
}
 
/**
 * Derive a policy-based multi-factor derived key
 *
 * @example
 * // setup key that can be derived from passwordA AND (passwordB OR passwordC)
 * const setup = await mfkdf.policy.setup(
 *   await mfkdf.policy.and(
 *     await mfkdf.setup.factors.password('passwordA', { id: 'passwordA' }),
 *     await mfkdf.policy.or(
 *       await mfkdf.setup.factors.password('passwordB', { id: 'passwordB' }),
 *       await mfkdf.setup.factors.password('passwordC', { id: 'passwordC' })
 *     )
 *   ), { size: 8 }
 * )
 *
 * // derive key with passwordA and passwordC (or passwordA and passwordB)
 * const derive = await mfkdf.policy.derive(setup.policy, {
 *   passwordA: mfkdf.derive.factors.password('passwordA'),
 *   passwordC: mfkdf.derive.factors.password('passwordC'),
 * })
 *
 * setup.key.toString('hex') // -> e16a227944a65263
 * derive.key.toString('hex') // -> e16a227944a65263
 *
 * @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.16.0
 * @async
 * @memberOf policy
 */
async function derive (policy, factors) {
  const ids = Object.keys(factors)
  if (!validate(policy)) throw new TypeError('policy contains duplicate ids')
  if (!evaluate(policy, ids)) throw new RangeError('insufficient factors to derive key')
 
  const expanded = expand(policy, factors)
 
  return await deriveKey(policy, expanded)
}
module.exports.derive = derive