UNPKG

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