UNPKG

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