index.js
  1  /*!
  2   * random-bytes
  3   * Copyright(c) 2016 Douglas Christopher Wilson
  4   * MIT Licensed
  5   */
  6  
  7  'use strict'
  8  
  9  /**
 10   * Module dependencies.
 11   * @private
 12   */
 13  
 14  var crypto = require('crypto')
 15  
 16  /**
 17   * Module variables.
 18   * @private
 19   */
 20  
 21  var generateAttempts = crypto.randomBytes === crypto.pseudoRandomBytes ? 1 : 3
 22  
 23  /**
 24   * Module exports.
 25   * @public
 26   */
 27  
 28  module.exports = randomBytes
 29  module.exports.sync = randomBytesSync
 30  
 31  /**
 32   * Generates strong pseudo-random bytes.
 33   *
 34   * @param {number} size
 35   * @param {function} [callback]
 36   * @return {Promise}
 37   * @public
 38   */
 39  
 40  function randomBytes(size, callback) {
 41    // validate callback is a function, if provided
 42    if (callback !== undefined && typeof callback !== 'function') {
 43      throw new TypeError('argument callback must be a function')
 44    }
 45  
 46    // require the callback without promises
 47    if (!callback && !global.Promise) {
 48      throw new TypeError('argument callback is required')
 49    }
 50  
 51    if (callback) {
 52      // classic callback style
 53      return generateRandomBytes(size, generateAttempts, callback)
 54    }
 55  
 56    return new Promise(function executor(resolve, reject) {
 57      generateRandomBytes(size, generateAttempts, function onRandomBytes(err, str) {
 58        if (err) return reject(err)
 59        resolve(str)
 60      })
 61    })
 62  }
 63  
 64  /**
 65   * Generates strong pseudo-random bytes sync.
 66   *
 67   * @param {number} size
 68   * @return {Buffer}
 69   * @public
 70   */
 71  
 72  function randomBytesSync(size) {
 73    var err = null
 74  
 75    for (var i = 0; i < generateAttempts; i++) {
 76      try {
 77        return crypto.randomBytes(size)
 78      } catch (e) {
 79        err = e
 80      }
 81    }
 82  
 83    throw err
 84  }
 85  
 86  /**
 87   * Generates strong pseudo-random bytes.
 88   *
 89   * @param {number} size
 90   * @param {number} attempts
 91   * @param {function} callback
 92   * @private
 93   */
 94  
 95  function generateRandomBytes(size, attempts, callback) {
 96    crypto.randomBytes(size, function onRandomBytes(err, buf) {
 97      if (!err) return callback(null, buf)
 98      if (!--attempts) return callback(err)
 99      setTimeout(generateRandomBytes.bind(null, size, attempts, callback), 10)
100    })
101  }