bcrypt.js 7.0 KB

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