/
CryptoAdapter.js
276 lines (233 loc) · 9.43 KB
/
CryptoAdapter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
'use strict';
(function() {
var EncryptionType = {
NONE: 0,
REQUEST: 1, // the request is encrypted
REQUEST_RESPONSE_VAL: 2 // the request + the response value (only value!) is encrypted
};
// Require and check for any modules
//////////////////////////////////////////////////////////////////////
var CryptoJS = require('crypto-js');
var Commands = require('./Commands');
var JSEncryptWrapper = require('node-jsencrypt');
var Debug = require('./Debug');
//////////////////////////////////////////////////////////////////////
var CryptoAdapter = {
HASH_ALGORITHM: {
SHA1: "SHA1",
SHA256: "SHA256"
}
};
/**
* Creates a Hmac-SHA1 hash of the provided message
* @param {string} message text which should be hashed
* @param {'utf8'|'hex'} messageEncoding encoding of the message
* @param key which is used to create the hash
* @param {'utf8'|'hex'} keyEncoding encoding of the key
* @param {'utf8'|'hex'} hashEncoding encoding of he result
* @returns {string|*} resulting hash
*/
CryptoAdapter.HmacSHA1 = function HmacSHA1(message, messageEncoding, key, keyEncoding, hashEncoding) {
var msg = getEncoding(messageEncoding).parse(message);
var k = getEncoding(keyEncoding).parse(key);
var hash = CryptoJS.HmacSHA1(msg, k);
return hash.toString(getEncoding(hashEncoding || 'utf8'));
};
CryptoAdapter.SHA1 = function SHA1(message) {
return CryptoJS.SHA1(message).toString();
};
/**
* Creates a Hmac-SHA1 hash of the provided message
* @param {string} message text which should be hashed
* @param {'utf8'|'hex'} messageEncoding encoding of the message
* @param key which is used to create the hash
* @param {'utf8'|'hex'} keyEncoding encoding of the key
* @param {'utf8'|'hex'} hashEncoding encoding of he result
* @returns {string|*} resulting hash
*/
CryptoAdapter.HmacSHA256 = function HmacSHA256(message, messageEncoding, key, keyEncoding, hashEncoding) {
var msg = getEncoding(messageEncoding).parse(message);
var k = getEncoding(keyEncoding).parse(key);
var hash = CryptoJS.HmacSHA256(msg, k);
return hash.toString(getEncoding(hashEncoding || 'utf8'));
};
CryptoAdapter.SHA256 = function SHA256(message) {
return CryptoJS.SHA256(message).toString();
};
CryptoAdapter.encrypt = function encryptAES(message, key) {
var ct = CryptoJS.AES.encrypt(message, getKey(key));
return ct.toString();
};
CryptoAdapter.decrypt = function decryptAES(ct, key) {
var decrypted = CryptoJS.AES.decrypt(ct, getKey(key));
return decrypted.toString(getEncoding('utf8'));
};
/**
* Hashes the given payload using an MD5 algorithm.
* @param payload the payload to hash
* @returns {*} the hexadecimal hash
*/
CryptoAdapter.md5Hex = function md5Hex(payload) {
var hash = CryptoJS.MD5(payload);
return hash.toString();
};
/**
* Creates a random alphanumeric string with the given length
* @param len how long the seed should be.
* @returns {string} the random seed, exactly len characters long.
*/
CryptoAdapter.createSeed = function createSeed(len) {
var chars = "0123456789abcdefghijklmnopqrstuvwxyz";
var result = '';
for (var i = len; i > 0; --i) result += chars[Math.floor(Math.random() * chars.length)];
return result;
};
var getKey = function getKey(id) {
var key = getKey.cachedKey;
if (!key) {
// TODO-goelzda Do we realy need this?
//var devId = VendorHub.DeviceInfo.getPlatformInfoObj().uuid; // this is a string
/*var devId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
return v.toString(16);
}); // this is a string*/
var hexId = getEncoding('hex').parse(id);
key = hexId.toString();
getKey.cachedKey = key;
}
return key;
};
/**
* abstracts the available encoding types of the lib
* @param encoding which should be normalized
* @returns {*} correct encoding for the lib
*/
var getEncoding = function(encoding) {
encoding = (encoding).toLowerCase();
if (encoding === "utf8" || encoding === "ascii") {
return CryptoJS.enc.Utf8;
} else if (encoding === "hex") {
return CryptoJS.enc.Hex;
} else {
console.error("Invalid encoding: " + encoding + " not supported!");
return {
parse: function() { } // empty parse function so nothing will break
};
}
};
// RSA
/**
* RSA encrypts the plaintext with the given public key
* @param plaintext
* @param publicKey
* @returns {string} base64
*/
CryptoAdapter.rsaEncrypt = function rsaEncrypt(plaintext, publicKey) {
var encrypt = new JSEncryptWrapper();
encrypt.setPublicKey(publicKey);
return encrypt.encrypt(plaintext);
};
// AES
CryptoAdapter.generateAesKey = function generateAesKey(key) {
var salt = CryptoJS.lib.WordArray.random(128/8);
return CryptoJS.PBKDF2(getKey(key), salt, { keySize: 256/32, iterations: 50 }).toString(CryptoJS.enc.Hex);
};
CryptoAdapter.generateSalt = function generateSalt() {
return CryptoJS.lib.WordArray.random(2).toString(CryptoJS.enc.Hex);
};
CryptoAdapter.generateAesIV = function generateAesIV() {
return CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);
};
/**
* AES encrypts the plaintext with the key and iv
* @param plaintext
* @param key_hex
* @param iv_hex
* @returns {object} CryptoJS
*/
CryptoAdapter.aesEncrypt = function aesEncrypt(plaintext, key_hex, iv_hex) {
return CryptoJS.AES.encrypt(
plaintext,
CryptoJS.enc.Hex.parse(key_hex),
{
iv: CryptoJS.enc.Hex.parse(iv_hex),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
}
);
};
/**
* AES decrypts the base64 ciphertext with the key and iv
* @param ciphertext_base64
* @param key_hex
* @param iv_hex
* @returns {string} utf8
*/
CryptoAdapter.aesDecrypt = function aesDecrypt(ciphertext_base64, key_hex, iv_hex) {
ciphertext_base64 = ciphertext_base64.replace(/\n/, "");
var ciphertext_hex = checkBlockSize(b64ToHex(ciphertext_base64), 16); // the blockSize is 16
var cipherParams = CryptoJS.lib.CipherParams.create({
ciphertext: CryptoJS.enc.Hex.parse(ciphertext_hex)
});
var decrypted = CryptoJS.AES.decrypt(
cipherParams,
CryptoJS.enc.Hex.parse(key_hex),
{
iv: CryptoJS.enc.Hex.parse(iv_hex),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.ZeroPadding
}
);
return decrypted.toString(CryptoJS.enc.Utf8);
};
/**
* Will return a command that contains is aes encrypted and contains the payload provided. It also ensures the proper
* encryption prefix is used (full or command only encryption).
* @param payload
* @param key
* @param iv
* @param encryptionType
* @returns {*|string}
*/
CryptoAdapter.getLxAesEncryptedCmd = function getLxAesEncryptedCmd(payload, key, iv, encryptionType) {
Debug.Encryption && console.log("LxCrypto", "getLxAesEncryptedCmd: " + payload);
var encrypted,
ciphertext,
ciphertextBase64,
format,
command;
// AES encryption
encrypted = CryptoAdapter.aesEncrypt(payload, key, iv);
ciphertext = encrypted.ciphertext;
ciphertextBase64 = ciphertext.toString(CryptoJS.enc.Base64);
Debug.Encryption && console.log("LxCrypto", " ciphertext base64: " + ciphertextBase64);
Debug.Encryption && console.log("LxCrypto", " ciphertext hex: " + ciphertext.toString(CryptoJS.enc.Hex));
format = Commands.ENCRYPTION.COMMAND;
if (encryptionType === EncryptionType.REQUEST_RESPONSE_VAL) {
format = Commands.ENCRYPTION.COMMAND_AND_RESPONSE;
}
command = Commands.format(format, encodeURIComponent(ciphertextBase64));
Debug.Encryption && console.log("LxCrypto", " ecnryptedCmd: " + command);
return command;
};
/**
* checks blockSize and fills up with 0x00 if the hex string has an incorrect length
* Bug in old Miniserver Versions!
* https://www.wrike.com/open.htm?id=143296929
* @param hexStr
* @param blockSize
* @returns hexStr
*/
var checkBlockSize = function checkBlockSize(hexStr, blockSize) {
if (hexStr.length % blockSize > 0) {
hexStr = hexStr + new Array(blockSize - hexStr.length % blockSize + 1).join('0');
}
return hexStr;
};
var b64ToHex = function (b64) {
return CryptoJS.enc.Base64.parse(b64).toString(CryptoJS.enc.Hex);
};
//////////////////////////////////////////////////////////////////////
module.exports = CryptoAdapter;
//////////////////////////////////////////////////////////////////////
}).call(this);