-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypto.js
170 lines (143 loc) · 5.18 KB
/
crypto.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
const crypto = require('crypto');
const fs = require("fs");
const { waitFor, WritableBufferStream } = require("rolia-util");
const { Buffer } = require('buffer');
const { pipeline, Readable } = require('stream');
const debugging = false;
function logBuf(buf, name) {
if (debugging) {
console.log(name + ": " + buf.toString('hex'));
console.log(buf);
}
}
function genRandomBuf(size = 23) {
if (size <= 0) return null;
let buf = Buffer.alloc(size);
for (let i = 0; i < size; i++) {
buf[i] = Math.random() * 256;
}
return buf;
}
async function extractPassword(inBuf, privateKey) {
const pswdRsaBuf = inBuf.subarray(0, 65);
const pswd = crypto.privateDecrypt(privateKey, pswdRsaBuf);
return pswd;
}
async function extractPasswordFromFile(hybridFilePath, privateKey) {
var pswdRsaBuf;
// length of pswdRsaBuf is always 65
const input0 = fs.createReadStream(hybridFilePath, { start: 0, end: 64 }); // start&end inclusive. Totally 65
var reading = { done: false };
input0.on('data', chunk => {
pswdRsaBuf = chunk;
reading.done = true;
});
await waitFor(reading);
const pswd = crypto.privateDecrypt(privateKey, pswdRsaBuf);
return pswd;
}
async function hybridEncrypt(srcBuf, publicKey, salt) {
const password = genRandomBuf();
const pswdRsaBuf = crypto.publicEncrypt(publicKey, password);
logBuf(pswdRsaBuf, "pswdRsaBuf");
const key = crypto.scryptSync(password, salt, 32);
const iv = Buffer.alloc(16, 0); // Initialization vector.
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const in1 = Readable.from(srcBuf);
const out1 = new WritableBufferStream();
var jobC = { done: false };
pipeline(in1, cipher, out1, (err) => {
jobC.done = true;
if (err) throw err;
});
await waitFor(jobC);
const encBuf = out1.toBuffer();
logBuf(encBuf, "encrypted");
const hybridBuf = Buffer.concat([pswdRsaBuf, encBuf]);
logBuf(hybridBuf, "Hybrid encrypted");
return hybridBuf;
}
async function hybridDecrypt(hybridBuf, privateKey, salt) {
const password = await extractPassword(hybridBuf, privateKey);
logBuf(password, "Extracted pswd");
const key = crypto.scryptSync(password, salt, 32);
const iv = Buffer.alloc(16, 0); // Initialization vector.
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
const encBuf = hybridBuf.subarray(65);
logBuf(encBuf, "input of buf for decryption:");
const in2 = Readable.from(encBuf);
const out2 = new WritableBufferStream();
var jobD = { done: false };
pipeline(in2, decipher, out2, (err) => {
jobD.done = true;
if (err) throw err;
});
await waitFor(jobD);
const decBuf = out2.toBuffer();
logBuf(decBuf, "decrypted");
return decBuf;
}
async function hybridEncryptHex(hexStr, publicKey, salt) {
const srcBuf = Buffer.from(hexStr, 'hex');
const hybridBuf = await hybridEncrypt(srcBuf, publicKey, salt);
return hybridBuf.toString('hex');
}
async function hybridDecryptHex(hybridHex, privateKey, salt) {
const hybridBuf = Buffer.from(hybridHex, 'hex');
const decBuf = await hybridDecrypt(hybridBuf, privateKey, salt);
return decBuf.toString('hex');
}
async function hybridEncryptHexKey(hexKey, publicKey, salt) {
const hexStr = hexKey.startsWith("0x") ? hexKey.substring(2) : hexKey;
return await hybridEncryptHex(hexStr, publicKey, salt);
}
async function hybridDecryptHexKey(hybridHex, privateKey, salt) {
const decHex = await hybridDecryptHex(hybridHex, privateKey, salt);
return "0x".concat(decHex);
}
async function hybridEncryptFile(srcFilePath, fileName, tmpDir, publicKey, salt) {
const password = genRandomBuf();
const key = crypto.scryptSync(password, salt, 32);
const iv = Buffer.alloc(16, 0); // Initialization vector.
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
const tmpSubfolder = Math.random().toString().substring(2, 8) + "_enc";
fs.mkdirSync(`${tmpDir}/${tmpSubfolder}`);
const outputFilePath = `${tmpDir}/${tmpSubfolder}/${fileName}`;
const input = fs.createReadStream(srcFilePath);
const output = fs.createWriteStream(outputFilePath);
const pswdRsaBuf = crypto.publicEncrypt(publicKey, password);
output.write(pswdRsaBuf);
var encrypting = { done: false };
pipeline(input, cipher, output, (err) => {
encrypting.done = true;
if (err) throw err;
});
await waitFor(encrypting);
return outputFilePath;
}
async function hybridDecryptFile(hybridFilePath, tmpDir, privateKey, salt) {
const password = await extractPasswordFromFile(hybridFilePath, privateKey);
const key = crypto.scryptSync(password, salt, 32);
const iv = Buffer.alloc(16, 0); // Initialization vector.
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
const tmpFileName = Math.random().toString().substring(2, 5) + "_dec";
const outputFilePath = `${tmpDir}/${tmpFileName}`;
var decrypting = { done: false };
const input = fs.createReadStream(hybridFilePath, { start: 65 }); // length of bufPswdRsa is always 65
const output = fs.createWriteStream(outputFilePath);
const stream = input.pipe(decipher).pipe(output);
stream.on('finish', () => { decrypting.done = true; });
await waitFor(decrypting);
return outputFilePath;
}
module.exports = {
hybridDecryptFile,
hybridEncryptFile,
hybridDecryptHexKey,
hybridEncryptHexKey,
hybridDecryptHex,
hybridEncryptHex,
hybridDecrypt,
hybridEncrypt,
genRandomBuf
};