Issues (994)

src/Crypto/Cipher.js (1 issue)

Severity
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
The variable clearText seems to be never used. Consider removing it.
Loading history...
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