1 | /*global console*/ |
||
2 | "use strict"; |
||
3 | |||
4 | const crypto = require("crypto"); |
||
5 | const BufferList = require("bl"); |
||
6 | |||
7 | /** |
||
8 | * CrossPlatform Cipher |
||
9 | * This cross platform Cipher uses AES 256 for encryption. This library can |
||
10 | * be used for encryptoion and de-cryption of string on iOS, Android, Windows |
||
11 | * and Node platform. |
||
12 | * Features: |
||
13 | * 1. 256 bit AES encryption |
||
14 | * 2. Random IV generation. |
||
15 | * 3. Provision for SHA256 hashing of key. |
||
16 | */ |
||
17 | class Cipher { |
||
18 | constructor() { |
||
19 | this._maxKeySize = 32; |
||
20 | this._maxIVSize = 16; |
||
21 | this._algorithm = "AES-256-CBC"; |
||
22 | this._charset = "utf8"; |
||
23 | this._encoding = "base64"; |
||
24 | this._hashAlgo = "sha256"; |
||
25 | this._digestEncoding = "hex"; |
||
26 | |||
27 | this._characterMatrixForRandomIVStringGeneration = [ |
||
28 | "A", |
||
29 | "B", |
||
30 | "C", |
||
31 | "D", |
||
32 | "E", |
||
33 | "F", |
||
34 | "G", |
||
35 | "H", |
||
36 | "I", |
||
37 | "J", |
||
38 | "K", |
||
39 | "L", |
||
40 | "M", |
||
41 | "N", |
||
42 | "O", |
||
43 | "P", |
||
44 | "Q", |
||
45 | "R", |
||
46 | "S", |
||
47 | "T", |
||
48 | "U", |
||
49 | "V", |
||
50 | "W", |
||
51 | "X", |
||
52 | "Y", |
||
53 | "Z", |
||
54 | "a", |
||
55 | "b", |
||
56 | "c", |
||
57 | "d", |
||
58 | "e", |
||
59 | "f", |
||
60 | "g", |
||
61 | "h", |
||
62 | "i", |
||
63 | "j", |
||
64 | "k", |
||
65 | "l", |
||
66 | "m", |
||
67 | "n", |
||
68 | "o", |
||
69 | "p", |
||
70 | "q", |
||
71 | "r", |
||
72 | "s", |
||
73 | "t", |
||
74 | "u", |
||
75 | "v", |
||
76 | "w", |
||
77 | "x", |
||
78 | "y", |
||
79 | "z", |
||
80 | "0", |
||
81 | "1", |
||
82 | "2", |
||
83 | "3", |
||
84 | "4", |
||
85 | "5", |
||
86 | "6", |
||
87 | "7", |
||
88 | "8", |
||
89 | "9", |
||
90 | "-", |
||
91 | "_", |
||
92 | ]; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * private function: _encryptDecrypt |
||
97 | * encryptes or decrypts to or from text or encrypted text given an iv and key |
||
98 | * @param {string} text can be plain text or encrypted text |
||
99 | * @param {string} key the key used to encrypt or decrypt |
||
100 | * @param {string} initVector the initialization vector to encrypt or |
||
101 | * decrypt |
||
102 | * @param {bool} isEncrypt true = encryption, false = decryption |
||
103 | * @return {string} encryted text or plain text |
||
104 | */ |
||
105 | _encryptDecrypt(text, key, initVector, isEncrypt) { |
||
106 | if (!text || !key) { |
||
107 | throw ( |
||
108 | "cryptLib._encryptDecrypt: -> key and plain or encrypted text " + |
||
109 | "required" |
||
110 | ); |
||
111 | } |
||
112 | |||
113 | let ivBl = new BufferList(), |
||
114 | keyBl = new BufferList(), |
||
115 | keyCharArray = key.split(""), |
||
116 | ivCharArray = [], |
||
117 | encryptor, |
||
118 | decryptor, |
||
119 | clearText; |
||
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||
120 | |||
121 | if (initVector && initVector.length > 0) { |
||
122 | ivCharArray = initVector.split(""); |
||
123 | } |
||
124 | |||
125 | for (let i = 0; i < this._maxIVSize; i++) { |
||
126 | ivBl.append(ivCharArray.shift() || [null]); |
||
127 | } |
||
128 | |||
129 | for (let i = 0; i < this._maxKeySize; i++) { |
||
130 | keyBl.append(keyCharArray.shift() || [null]); |
||
131 | } |
||
132 | |||
133 | if (isEncrypt) { |
||
134 | encryptor = crypto.createCipheriv( |
||
135 | this._algorithm, |
||
136 | keyBl.toString(), |
||
137 | ivBl.toString() |
||
138 | ); |
||
139 | encryptor.setEncoding(this._encoding); |
||
140 | encryptor.write(text); |
||
141 | encryptor.end(); |
||
142 | return encryptor.read(); |
||
143 | } |
||
144 | |||
145 | decryptor = crypto.createDecipheriv( |
||
146 | this._algorithm, |
||
147 | keyBl.toString(), |
||
148 | ivBl.toString() |
||
149 | ); |
||
150 | let dec = decryptor.update(text, this._encoding, this._charset); |
||
151 | dec += decryptor.final(this._charset); |
||
152 | return dec; |
||
153 | } |
||
154 | |||
155 | /** |
||
156 | * private function: _isCorrectLength |
||
157 | * checks if length is preset and is a whole number and > 0 |
||
158 | * @param {int} length |
||
159 | * @return {bool} |
||
160 | */ |
||
161 | _isCorrectLength(length) { |
||
162 | return length && /^\d+$/.test(length) && parseInt(length, 10) !== 0; |
||
163 | } |
||
164 | |||
165 | /** |
||
166 | * generates random initaliztion vector given a length |
||
167 | * @param {int} length the length of the iv to be generated |
||
168 | */ |
||
169 | generateRandomIV(length) { |
||
170 | if (!this._isCorrectLength(length)) { |
||
171 | throw "cryptLib.generateRandomIV() -> needs length or in wrong format"; |
||
172 | } |
||
173 | |||
174 | let randomBytes = crypto.randomBytes(length), |
||
175 | _iv = []; |
||
176 | |||
177 | for (let i = 0; i < length; i++) { |
||
178 | let ptr = |
||
179 | randomBytes[i] % |
||
180 | this._characterMatrixForRandomIVStringGeneration.length; |
||
181 | _iv[i] = this._characterMatrixForRandomIVStringGeneration[ptr]; |
||
182 | } |
||
183 | return _iv.join(""); |
||
184 | } |
||
185 | |||
186 | generateRandomIV16() { |
||
187 | let randomBytes = crypto.randomBytes(16), |
||
188 | _iv = []; |
||
189 | |||
190 | for (let i = 0; i < 16; i++) { |
||
191 | let ptr = |
||
192 | randomBytes[i] % |
||
193 | this._characterMatrixForRandomIVStringGeneration.length; |
||
194 | _iv[i] = this._characterMatrixForRandomIVStringGeneration[ptr]; |
||
195 | } |
||
196 | return _iv.join(""); |
||
197 | } |
||
198 | |||
199 | /** |
||
200 | * Creates a hash of a key using SHA-256 algorithm |
||
201 | * @param {string} key the key that will be hashed |
||
202 | * @param {int} length the length of the SHA-256 hash |
||
203 | * @return {string} the output hash generated given a key and length |
||
204 | */ |
||
205 | getHashSha256(key, length) { |
||
206 | if (!key) { |
||
207 | throw "cryptLib.getHashSha256() -> needs key"; |
||
208 | } |
||
209 | |||
210 | if (!this._isCorrectLength(length)) { |
||
211 | throw "cryptLib.getHashSha256() -> needs length or in wrong format"; |
||
212 | } |
||
213 | |||
214 | return crypto |
||
215 | .createHash(this._hashAlgo) |
||
216 | .update(key) |
||
217 | .digest(this._digestEncoding) |
||
218 | .substring(0, length); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * encryptes plain text given a key and initialization vector |
||
223 | * @param {string} text can be plain text or encrypted text |
||
224 | * @param {string} key the key used to encrypt or decrypt |
||
225 | * @param {string} initVector the initialization vector to encrypt or |
||
226 | * decrypt |
||
227 | * @return {string} encryted text or plain text |
||
228 | */ |
||
229 | encrypt(plainText, key, initVector) { |
||
230 | return this._encryptDecrypt(plainText, key, initVector, true); |
||
231 | } |
||
232 | |||
233 | /** |
||
234 | * decrypts encrypted text given a key and initialization vector |
||
235 | * @param {string} text can be plain text or encrypted text |
||
236 | * @param {string} key the key used to encrypt or decrypt |
||
237 | * @param {string} initVector the initialization vector to encrypt or |
||
238 | * decrypt |
||
239 | * @return {string} encryted text or plain text |
||
240 | */ |
||
241 | decrypt(encryptedText, key, initVector) { |
||
242 | return this._encryptDecrypt(encryptedText, key, initVector, false); |
||
243 | } |
||
244 | |||
245 | encryptPlainTextWithRandomIV(plainText, key) { |
||
246 | return this._encryptDecrypt( |
||
247 | this.generateRandomIV16() + plainText, |
||
248 | this.getHashSha256(key, 32), |
||
249 | this.generateRandomIV16(), |
||
250 | true |
||
251 | ); |
||
252 | } |
||
253 | |||
254 | decryptCipherTextWithRandomIV(cipherText, key) { |
||
255 | let out = this._encryptDecrypt( |
||
256 | cipherText, |
||
257 | this.getHashSha256(key, 32), |
||
258 | this.generateRandomIV16(), |
||
259 | false |
||
260 | ); |
||
261 | return out.substring(16, out.length); |
||
262 | } |
||
263 | } |
||
264 | |||
265 | module.exports = new Cipher(); |
||
266 |