bcrypt.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. 'use strict';
  2. var nodePreGyp = require('@mapbox/node-pre-gyp');
  3. var path = require('path');
  4. var binding_path = nodePreGyp.find(path.resolve(path.join(__dirname, './package.json')));
  5. var bindings = require(binding_path);
  6. var crypto = require('crypto');
  7. var promises = require('./promises');
  8. /// generate a salt (sync)
  9. /// @param {Number} [rounds] number of rounds (default 10)
  10. /// @return {String} salt
  11. module.exports.genSaltSync = function genSaltSync(rounds, minor) {
  12. // default 10 rounds
  13. if (!rounds) {
  14. rounds = 10;
  15. } else if (typeof rounds !== 'number') {
  16. throw new Error('rounds must be a number');
  17. }
  18. if(!minor) {
  19. minor = 'b';
  20. } else if(minor !== 'b' && minor !== 'a') {
  21. throw new Error('minor must be either "a" or "b"');
  22. }
  23. return bindings.gen_salt_sync(minor, rounds, crypto.randomBytes(16));
  24. };
  25. /// generate a salt
  26. /// @param {Number} [rounds] number of rounds (default 10)
  27. /// @param {Function} cb callback(err, salt)
  28. module.exports.genSalt = function genSalt(rounds, minor, cb) {
  29. var error;
  30. // if callback is first argument, then use defaults for others
  31. if (typeof arguments[0] === 'function') {
  32. // have to set callback first otherwise arguments are overriden
  33. cb = arguments[0];
  34. rounds = 10;
  35. minor = 'b';
  36. // callback is second argument
  37. } else if (typeof arguments[1] === 'function') {
  38. // have to set callback first otherwise arguments are overriden
  39. cb = arguments[1];
  40. minor = 'b';
  41. }
  42. if (!cb) {
  43. return promises.promise(genSalt, this, [rounds, minor]);
  44. }
  45. // default 10 rounds
  46. if (!rounds) {
  47. rounds = 10;
  48. } else if (typeof rounds !== 'number') {
  49. // callback error asynchronously
  50. error = new Error('rounds must be a number');
  51. return process.nextTick(function() {
  52. cb(error);
  53. });
  54. }
  55. if(!minor) {
  56. minor = 'b'
  57. } else if(minor !== 'b' && minor !== 'a') {
  58. error = new Error('minor must be either "a" or "b"');
  59. return process.nextTick(function() {
  60. cb(error);
  61. });
  62. }
  63. crypto.randomBytes(16, function(error, randomBytes) {
  64. if (error) {
  65. cb(error);
  66. return;
  67. }
  68. bindings.gen_salt(minor, rounds, randomBytes, cb);
  69. });
  70. };
  71. /// hash data using a salt
  72. /// @param {String|Buffer} data the data to encrypt
  73. /// @param {String} salt the salt to use when hashing
  74. /// @return {String} hash
  75. module.exports.hashSync = function hashSync(data, salt) {
  76. if (data == null || salt == null) {
  77. throw new Error('data and salt arguments required');
  78. }
  79. if (!(typeof data === 'string' || data instanceof Buffer) || (typeof salt !== 'string' && typeof salt !== 'number')) {
  80. throw new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
  81. }
  82. if (typeof salt === 'number') {
  83. salt = module.exports.genSaltSync(salt);
  84. }
  85. return bindings.encrypt_sync(data, salt);
  86. };
  87. /// hash data using a salt
  88. /// @param {String|Buffer} data the data to encrypt
  89. /// @param {String} salt the salt to use when hashing
  90. /// @param {Function} cb callback(err, hash)
  91. module.exports.hash = function hash(data, salt, cb) {
  92. var error;
  93. if (typeof data === 'function') {
  94. error = new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
  95. return process.nextTick(function() {
  96. data(error);
  97. });
  98. }
  99. if (typeof salt === 'function') {
  100. error = new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
  101. return process.nextTick(function() {
  102. salt(error);
  103. });
  104. }
  105. // cb exists but is not a function
  106. // return a rejecting promise
  107. if (cb && typeof cb !== 'function') {
  108. return promises.reject(new Error('cb must be a function or null to return a Promise'));
  109. }
  110. if (!cb) {
  111. return promises.promise(hash, this, [data, salt]);
  112. }
  113. if (data == null || salt == null) {
  114. error = new Error('data and salt arguments required');
  115. return process.nextTick(function() {
  116. cb(error);
  117. });
  118. }
  119. if (!(typeof data === 'string' || data instanceof Buffer) || (typeof salt !== 'string' && typeof salt !== 'number')) {
  120. error = new Error('data must be a string or Buffer and salt must either be a salt string or a number of rounds');
  121. return process.nextTick(function() {
  122. cb(error);
  123. });
  124. }
  125. if (typeof salt === 'number') {
  126. return module.exports.genSalt(salt, function(err, salt) {
  127. return bindings.encrypt(data, salt, cb);
  128. });
  129. }
  130. return bindings.encrypt(data, salt, cb);
  131. };
  132. /// compare raw data to hash
  133. /// @param {String|Buffer} data the data to hash and compare
  134. /// @param {String} hash expected hash
  135. /// @return {bool} true if hashed data matches hash
  136. module.exports.compareSync = function compareSync(data, hash) {
  137. if (data == null || hash == null) {
  138. throw new Error('data and hash arguments required');
  139. }
  140. if (!(typeof data === 'string' || data instanceof Buffer) || typeof hash !== 'string') {
  141. throw new Error('data must be a string or Buffer and hash must be a string');
  142. }
  143. return bindings.compare_sync(data, hash);
  144. };
  145. /// compare raw data to hash
  146. /// @param {String|Buffer} data the data to hash and compare
  147. /// @param {String} hash expected hash
  148. /// @param {Function} cb callback(err, matched) - matched is true if hashed data matches hash
  149. module.exports.compare = function compare(data, hash, cb) {
  150. var error;
  151. if (typeof data === 'function') {
  152. error = new Error('data and hash arguments required');
  153. return process.nextTick(function() {
  154. data(error);
  155. });
  156. }
  157. if (typeof hash === 'function') {
  158. error = new Error('data and hash arguments required');
  159. return process.nextTick(function() {
  160. hash(error);
  161. });
  162. }
  163. // cb exists but is not a function
  164. // return a rejecting promise
  165. if (cb && typeof cb !== 'function') {
  166. return promises.reject(new Error('cb must be a function or null to return a Promise'));
  167. }
  168. if (!cb) {
  169. return promises.promise(compare, this, [data, hash]);
  170. }
  171. if (data == null || hash == null) {
  172. error = new Error('data and hash arguments required');
  173. return process.nextTick(function() {
  174. cb(error);
  175. });
  176. }
  177. if (!(typeof data === 'string' || data instanceof Buffer) || typeof hash !== 'string') {
  178. error = new Error('data and hash must be strings');
  179. return process.nextTick(function() {
  180. cb(error);
  181. });
  182. }
  183. return bindings.compare(data, hash, cb);
  184. };
  185. /// @param {String} hash extract rounds from this hash
  186. /// @return {Number} the number of rounds used to encrypt a given hash
  187. module.exports.getRounds = function getRounds(hash) {
  188. if (hash == null) {
  189. throw new Error('hash argument required');
  190. }
  191. if (typeof hash !== 'string') {
  192. throw new Error('hash must be a string');
  193. }
  194. return bindings.get_rounds(hash);
  195. };