UNPKG

3.88 kBJavaScriptView Raw
1(function() {
2 var Crypto, OPDATA01_HEADER, Opdata;
3
4 Crypto = require('./crypto');
5
6 OPDATA01_HEADER = '6F70646174613031';
7
8 /**
9 * @class Opdata
10 */
11
12
13 Opdata = (function() {
14 /**
15 * Opdata object
16 * @constructor
17 * @param {String} type Can be either 'item', 'itemKey' or 'profileKey'
18 * @param {String|Buffer} encryptionKey The encryption key
19 * @param {String|Buffer} hmacKey The hmac key
20 */
21
22 function Opdata(encryptionKey, hmacKey) {
23 this.encryptionKey = Crypto.toBuffer(encryptionKey);
24 this.hmacKey = Crypto.toBuffer(hmacKey);
25 if (this.encryptionKey.length !== 32) {
26 console.log(this.encryptionKey.toString('hex'));
27 throw new Error("Encryption key must be 32 bytes.");
28 }
29 if (this.hmacKey.length !== 32) {
30 console.log(this.hmacKey.toString('hex'));
31 throw new Error("HMAC Key must be 32 bytes");
32 }
33 }
34
35 /**
36 * Decrypt an object
37 * @param {String} type Can be either 'item', 'itemKey' or 'profileKey'
38 * @param {String|Buffer} object The encrypted opdata object
39 * @return {String} The decrypted object
40 */
41
42
43 Opdata.prototype.decrypt = function(type, object) {
44 var ciphertext, dataToHmac, expectedHmac, iv, keys, length, objectHmac, plaintext, rawtext;
45 object = Crypto.toHex(object);
46 if (type !== 'itemKey' && object.slice(0, 16).toUpperCase() !== OPDATA01_HEADER) {
47 console.error('Not an opdata01 object');
48 return false;
49 }
50 if (type === 'itemKey') {
51 iv = Crypto.toBuffer(object.slice(0, 32));
52 ciphertext = Crypto.toBuffer(object.slice(32, -64));
53 } else {
54 length = Crypto.parseLittleEndian(object.slice(16, 32));
55 iv = Crypto.toBuffer(object.slice(32, 64));
56 ciphertext = Crypto.toBuffer(object.slice(64, -64));
57 }
58 dataToHmac = Crypto.toBuffer(object.slice(0, -64));
59 expectedHmac = object.slice(-64);
60 objectHmac = Crypto.hmac(dataToHmac, this.hmacKey, 'sha256');
61 if (objectHmac !== expectedHmac) {
62 console.error('Hmac does not match');
63 return false;
64 }
65 rawtext = Crypto.decrypt('aes-256-cbc', ciphertext, this.encryptionKey, iv, 'hex');
66 if (type !== 'itemKey') {
67 plaintext = Crypto.unpad(length, rawtext);
68 }
69 switch (type) {
70 case 'item':
71 return Crypto.toBuffer(plaintext).toString('utf8');
72 case 'itemKey':
73 return [rawtext.slice(0, 64), rawtext.slice(64)];
74 case 'profileKey':
75 keys = Crypto.hash(plaintext, 'sha512');
76 return [keys.slice(0, 64), keys.slice(64)];
77 }
78 };
79
80 /**
81 * Encrypt plaintext as object
82 * @param {String} type Can be either 'item', 'itemKey' or 'profileKey'
83 * @param {Buffer} plaintext The data to be encrypted
84 * @return {Buffer} The encrypted opdata object
85 */
86
87
88 Opdata.prototype.encrypt = function(type, plaintext) {
89 var ciphertext, dataToHmac, endian, header, hmac, iv, paddedtext;
90 iv = Crypto.randomBytes(16);
91 if (type === 'itemKey') {
92 paddedtext = plaintext;
93 } else {
94 paddedtext = Crypto.concat([iv, Crypto.pad(plaintext)]);
95 }
96 ciphertext = Crypto.encrypt('aes-256-cbc', paddedtext, this.encryptionKey, iv);
97 if (type === 'itemKey') {
98 dataToHmac = Crypto.concat([iv, ciphertext]);
99 } else {
100 header = Crypto.toBuffer(OPDATA01_HEADER);
101 endian = Crypto.stringifyLittleEndian(plaintext.length);
102 endian = Crypto.toBuffer(endian);
103 dataToHmac = Crypto.concat([header, endian, iv, ciphertext]);
104 }
105 hmac = Crypto.hmac(dataToHmac, this.hmacKey, 'sha256');
106 hmac = Crypto.toBuffer(hmac);
107 return Crypto.concat([dataToHmac, hmac]);
108 };
109
110 return Opdata;
111
112 })();
113
114 module.exports = Opdata;
115
116}).call(this);