1 | (function() {
|
2 | var BLOCKSIZE, Crypto, nodeCrypto, sjcl;
|
3 |
|
4 | nodeCrypto = require('crypto');
|
5 |
|
6 | sjcl = require('../libs/sjcl');
|
7 |
|
8 | BLOCKSIZE = 16;
|
9 |
|
10 | /**
|
11 | * @class Crypto
|
12 | */
|
13 |
|
14 |
|
15 | Crypto = {
|
16 | /**
|
17 | * Encipher data
|
18 | * @param {String} mode The type of encryption to use.
|
19 | * @param {Buffer} plaintext The data to encrypt.
|
20 | * @param {String|Buffer} key The key to encrypt with. Can be a Buffer or
|
21 | * hex encoded string.
|
22 | * @param {String|Buffer} iv The IV. Can be a Buffer or hex encoded string.
|
23 | * @param {string} [encoding=Buffer] The format to return the encrypted
|
24 | * data at.
|
25 | * @return {Buffer|String} The encrypted data.
|
26 | */
|
27 |
|
28 | encrypt: function(mode, plaintext, key, iv, encoding) {
|
29 | var binary, buffer, cipher;
|
30 | iv = this.toBuffer(iv);
|
31 | key = this.toBuffer(key);
|
32 | cipher = nodeCrypto.createCipheriv(mode, key, iv);
|
33 | cipher.setAutoPadding(false);
|
34 | binary = cipher.update(plaintext) + cipher.final();
|
35 | buffer = new Buffer(binary, 'binary');
|
36 | if (encoding != null) {
|
37 | return buffer.toString(encoding);
|
38 | } else {
|
39 | return buffer;
|
40 | }
|
41 | },
|
42 | /**
|
43 | * Decipher encrypted data.
|
44 | * @param {String} mode The type of encryption to use.
|
45 | * @param {String|Buffer} ciphertext The data to decipher. Must be a
|
46 | * multiple of the blocksize.
|
47 | * @param {String|Buffer} key The key to decipher the data with.
|
48 | * @param {String|Buffer} iv The initialization vector to use.
|
49 | * @param {String} [encoding=Buffer] The format to return the decrypted
|
50 | * contents as.
|
51 | * @return {Buffer|String} The decrypted contents.
|
52 | */
|
53 |
|
54 | decrypt: function(mode, ciphertext, key, iv, encoding) {
|
55 | var binary, buffer, cipher;
|
56 | iv = this.toBuffer(iv);
|
57 | key = this.toBuffer(key);
|
58 | ciphertext = this.toBuffer(ciphertext);
|
59 | cipher = nodeCrypto.createDecipheriv(mode, key, iv);
|
60 | cipher.setAutoPadding(false);
|
61 | binary = cipher.update(ciphertext) + cipher.final();
|
62 | buffer = new Buffer(binary, 'binary');
|
63 | if (encoding != null) {
|
64 | return buffer.toString(encoding);
|
65 | } else {
|
66 | return buffer;
|
67 | }
|
68 | },
|
69 | /**
|
70 | * Generate keys from password using PKDF2-HMAC-SHA512.
|
71 | * @param {String} password The password.
|
72 | * @param {String|Buffer} salt The salt.
|
73 | * @param {Number} [iterations=10000] The numbers of iterations.
|
74 | * @param {Numbers} [keysize=512] The length of the derived key in bits.
|
75 | * @return {String} Returns the derived key encoded as hex.
|
76 | */
|
77 |
|
78 | pbkdf2: function(password, salt, iterations, keysize) {
|
79 | var bits, hmac, shaKey;
|
80 | if (iterations == null) {
|
81 | iterations = 10000;
|
82 | }
|
83 | if (keysize == null) {
|
84 | keysize = 512;
|
85 | }
|
86 | shaKey = "sha" + keysize;
|
87 | hmac = (function() {
|
88 |
|
89 | function hmac(key) {
|
90 | this.key = sjcl.codec.utf8String.fromBits(key);
|
91 | }
|
92 |
|
93 | hmac.prototype.encrypt = function(sjclArray) {
|
94 | var bits, buffer, byteArray, hex;
|
95 | byteArray = sjcl.codec.bytes.fromBits(sjclArray);
|
96 | buffer = new Buffer(byteArray);
|
97 | hex = nodeCrypto.createHmac(shaKey, this.key).update(buffer).digest('hex');
|
98 | bits = sjcl.codec.hex.toBits(hex);
|
99 | return bits;
|
100 | };
|
101 |
|
102 | return hmac;
|
103 |
|
104 | })();
|
105 | salt = sjcl.codec.hex.toBits(this.toHex(salt));
|
106 | bits = sjcl.misc.pbkdf2(password, salt, iterations, keysize, hmac);
|
107 | return sjcl.codec.hex.fromBits(bits);
|
108 | },
|
109 | /**
|
110 | * Cryptographically hash data using HMAC.
|
111 | * @param {String|Buffer} data The data to be hashed.
|
112 | * @param {String|Buffer} key The key to use with HMAC.
|
113 | * @param {string} mode The type of hash to use, such as sha1, sha256 or
|
114 | * sha512.
|
115 | * @return {String} The hmac digest encoded as hex.
|
116 | */
|
117 |
|
118 | hmac: function(data, key, keysize) {
|
119 | var hmac, mode;
|
120 | data = this.toBuffer(data);
|
121 | key = this.toBuffer(key);
|
122 | mode = "sha" + keysize;
|
123 | hmac = nodeCrypto.createHmac(mode, key);
|
124 | hmac.update(data);
|
125 | return hmac.digest('hex');
|
126 | },
|
127 | /**
|
128 | * Create a hash digest of data.
|
129 | * @param {String|Buffer} data The data to hash.
|
130 | * @param {String} mode The type of hash to use, such as sha1, sha256, or
|
131 | * sha512.
|
132 | * @return {String} The hash digest encoded as hex.
|
133 | */
|
134 |
|
135 | hash: function(data, keysize) {
|
136 | var hash, mode;
|
137 | data = this.toBuffer(data);
|
138 | mode = "sha" + keysize;
|
139 | hash = nodeCrypto.createHash(mode);
|
140 | hash.update(data);
|
141 | return hash.digest('hex');
|
142 | },
|
143 | /**
|
144 | * Prepend padding to data to make it fill the blocksize.
|
145 | * @param {Buffer} data The data to pad.
|
146 | * @return {Buffer} The data with padding added.
|
147 | */
|
148 |
|
149 | pad: function(data) {
|
150 | var bytesToPad, padding;
|
151 | bytesToPad = BLOCKSIZE - (data.length % BLOCKSIZE);
|
152 | padding = this.randomBytes(bytesToPad);
|
153 | return Buffer.concat([padding, data]);
|
154 | },
|
155 | /**
|
156 | * Remove padding from text.
|
157 | * @param {Numbers} plaintextLength The length of the plaintext in bytes.
|
158 | * @param {String|Buffer} data The data to remove the padding as a string
|
159 | * encoded as hex or a buffer.
|
160 | * @return {String} The data with the padding removed encoded as hex.
|
161 | */
|
162 |
|
163 | unpad: function(plaintextLength, data) {
|
164 | data = this.toHex(data);
|
165 | plaintextLength *= 2;
|
166 | return data.slice(-plaintextLength);
|
167 | },
|
168 | /**
|
169 | * Generates cryptographically strong pseudo-random data.
|
170 | * @param {Numbers} length How many bytes of data you want.
|
171 | * @return {Buffer} The random data as a Buffer.
|
172 | */
|
173 |
|
174 | randomBytes: function(length) {
|
175 | return nodeCrypto.randomBytes(length);
|
176 | },
|
177 | /**
|
178 | * Convert data to a Buffer
|
179 | * @param {String|Buffer} data The data to be converted. If a string, must
|
180 | * be encoded as hex.
|
181 | * @param {String} [encoding=hex] The format of the data to convert.
|
182 | * @return {Buffer} The data as a Buffer
|
183 | */
|
184 |
|
185 | toBuffer: function(data, encoding) {
|
186 | if (encoding == null) {
|
187 | encoding = 'hex';
|
188 | }
|
189 | if (data instanceof Buffer) {
|
190 | return data;
|
191 | }
|
192 | return new Buffer(data, encoding);
|
193 | },
|
194 | /**
|
195 | * Convert data to hex.
|
196 | * @param {String|Buffer} data The data to be converted.
|
197 | * @return {String} The data encoded as hex.
|
198 | */
|
199 |
|
200 | toHex: function(data) {
|
201 | if (data instanceof Buffer) {
|
202 | return data.toString('hex');
|
203 | }
|
204 | return data;
|
205 | },
|
206 | /**
|
207 | * Convert base64 to Buffer.
|
208 | * @param {String} data A base64 encoded string.
|
209 | * @return {Buffer} The base64 string as a Buffer.
|
210 | */
|
211 |
|
212 | fromBase64: function(data) {
|
213 | return new Buffer(data, 'base64');
|
214 | },
|
215 | /**
|
216 | * Join an array of buffers together.
|
217 | * @param {Array} buffers An array of buffers.
|
218 | * @return {Buffer} The buffers joined together.
|
219 | */
|
220 |
|
221 | concat: function(buffers) {
|
222 | return Buffer.concat(buffers);
|
223 | },
|
224 | /**
|
225 | * Parse a litte endian number.
|
226 | * @author Jim Rogers {@link http://www.jimandkatrin.com/CodeBlog/post/Parse-a-little-endian.aspx}
|
227 | * @param {String} hex The little endian number.
|
228 | * @return {Number} The little endian converted to a number.
|
229 | */
|
230 |
|
231 | parseLittleEndian: function(hex) {
|
232 | var pow, result;
|
233 | result = 0;
|
234 | pow = 0;
|
235 | while (hex.length > 0) {
|
236 | result += parseInt(hex.substring(0, 2), 16) * Math.pow(2, pow);
|
237 | hex = hex.substring(2, hex.length);
|
238 | pow += 8;
|
239 | }
|
240 | return result;
|
241 | },
|
242 | /**
|
243 | * Convert an integer into a little endian.
|
244 | * @param {Number} number The integer you want to convert.
|
245 | * @param {Boolean} [pad=true] Pad the little endian with zeroes.
|
246 | * @return {String} The little endian.
|
247 | */
|
248 |
|
249 | stringifyLittleEndian: function(number, pad) {
|
250 | var endian, i, multiplier, padding, power, remainder, value, _i;
|
251 | if (pad == null) {
|
252 | pad = true;
|
253 | }
|
254 | power = Math.floor((Math.log(number) / Math.LN2) / 8) * 8;
|
255 | multiplier = Math.pow(2, power);
|
256 | value = Math.floor(number / multiplier);
|
257 | remainder = number % multiplier;
|
258 | endian = "";
|
259 | if (remainder > 255) {
|
260 | endian += this.stringifyLittleEndian(remainder, false);
|
261 | } else if (power !== 0) {
|
262 | endian += this.dec2hex(remainder);
|
263 | }
|
264 | endian += this.dec2hex(value);
|
265 | if (pad) {
|
266 | padding = 16 - endian.length;
|
267 | for (i = _i = 0; _i < padding; i = _i += 1) {
|
268 | endian += "0";
|
269 | }
|
270 | }
|
271 | return endian;
|
272 | },
|
273 | /**
|
274 | * Turn a decimal into a hexadecimal.
|
275 | * @param {Number} dec The decimal.
|
276 | * @return {String} The hexadecimal.
|
277 | */
|
278 |
|
279 | dec2hex: function(dec) {
|
280 | var hex;
|
281 | hex = dec.toString(16);
|
282 | if (hex.length < 2) {
|
283 | hex = "0" + hex;
|
284 | }
|
285 | return hex;
|
286 | },
|
287 | /**
|
288 | * Generate a uuid.
|
289 | * @param {Number} [length=32] The length of the UUID.
|
290 | * @return {String} The UUID.
|
291 | */
|
292 |
|
293 | generateUuid: function(length) {
|
294 | if (length == null) {
|
295 | length = 32;
|
296 | }
|
297 | length /= 2;
|
298 | return this.randomBytes(length).toString('hex').toUpperCase(0);
|
299 | }
|
300 | };
|
301 |
|
302 | module.exports = Crypto;
|
303 |
|
304 | }).call(this);
|