set filename in header

This commit is contained in:
Kieran 2019-05-14 02:22:46 +08:00
parent 4a2c4edafd
commit c51f8d093a
5 changed files with 140 additions and 110 deletions

3
.gitignore vendored
View File

@ -10,4 +10,5 @@ Debug/
Release/
node_modules/
package-lock.json
out/
out/
sw.js

View File

@ -7,7 +7,7 @@
"url": "git+https://github.com/v0l/void.cat.git"
},
"scripts": {
"build": "sass src/css/style.scss dist/style.css && webpack-cli --entry ./src/js/index.js --mode production --output ./dist/bundle.js",
"build": "sass src/css/style.scss dist/style.css && webpack-cli --entry ./src/js/index.js --mode production --output ./dist/bundle.js && webpack-cli --entry ./src/js/Worker.js --mode production --output ./sw.js",
"debug": "webpack-cli --entry ./src/js/index.js --mode development --watch --output ./dist/bundle.js",
"debug-worker": "webpack-cli --entry ./src/js/Worker.js --mode development --watch --output ./sw.js"
},

View File

@ -15,7 +15,18 @@ const VoidFetch = function (event) {
let fi = await Api.GetFileInfo(hs.id);
if (fi.ok) {
let fd = new FileDownloader(fi.data, hs.key, hs.iv);
return await fd.StreamResponse();
fd.onprogress = function(x) {
if(client !== null && client !== undefined){
client.postMessage({
type: 'progress',
x
});
}
};
let resp = await fd.StreamResponse();
resp.headers.set("Content-Type", fd.fileHeader.mime != "" ? fd.fileHeader.mime : "application/octet-stream");
resp.headers.set("Content-Disposition", `inline; filename="${fd.fileHeader.name}"`);
return resp;
} else {
return Response.error();
}

View File

@ -81,126 +81,144 @@ function FileDownloader(fileinfo, key, iv) {
});
let void_download = {
SetFileHeader: function (fh) { this.fileHeader = fh; }.bind(this),
HandleProgress: this.HandleProgress.bind(this),
downloadStats: this.downloadStats,
isStart: true,
decOffset: 0,
headerLen: 0,
fileHeader: null,
hmacBytes: null,
body: response.body,
fileinfo: this.fileinfo,
aes: new AES_CBC(new Uint8Array(Utils.HexToArray(this.key)), new Uint8Array(Utils.HexToArray(this.iv)), true),
hmac: new HmacSha256(new Uint8Array(Utils.HexToArray(this.key))),
buff: new Uint8Array(),
start(controller) {
this.reader = this.body.getReader();
return this.readOnce(controller);
},
pull(controller) {
if (this.reader === undefined) {
this.reader = this.body.getReader();
}
return (async function () {
Log.I(`${this.fileinfo.FileId} Starting..`);
var isStart = true;
var decOffset = 0;
var headerLen = 0;
var fileHeader = null;
var hmacBytes = null;
while (true) {
let { done, value } = await this.reader.read();
if (done) {
if (this.buff.byteLength > 0) {
//pad the remaining data with PKCS#7
var toDecrypt = null;
let padding = 16 - (this.buff.byteLength % 16);
if(padding !== 0){
let tmpBuff = new Uint8Array(this.buff.byteLength + padding);
tmpBuff.fill(padding);
tmpBuff.set(this.buff, 0);
this.buff = null;
this.buff = tmpBuff;
}
let decBytes = this.aes.AES_Decrypt_process(this.buff);
this.hmac.process(decBytes);
controller.enqueue(decBytes);
this.buff = null;
}
let last = this.aes.AES_Decrypt_finish();
this.hmac.process(last);
this.hmac.finish();
controller.enqueue(last);
//check hmac
let h1 = Utils.ArrayToHex(hmacBytes);
let h2 = Utils.ArrayToHex(this.hmac.result)
if (h1 === h2) {
Log.I(`HMAC verify ok!`);
} else {
Log.E(`HMAC verify failed (${h1} !== ${h2})`);
//controller.cancel();
//return;
}
Log.I(`${this.fileinfo.FileId} Download complete!`);
controller.close();
return;
}
var sliceStart = 0;
var sliceEnd = value.byteLength;
//!Slice this only once!!
var toDecrypt = value;
if (isStart) {
let header = VBF.ParseStart(value.buffer);
if (header !== null) {
Log.I(`${this.fileinfo.FileId} blob header version is ${header.version} uploaded on ${header.uploaded} (Magic: ${Utils.ArrayToHex(header.magic)})`);
sliceStart = VBF.SliceToEncryptedPart(header.version, value);
} else {
throw "Invalid VBF header";
}
} else if (fileHeader != null && decOffset + toDecrypt.byteLength + headerLen + 2 >= fileHeader.len) {
sliceEnd -= 32; //hash is on the end (un-encrypted)
hmacBytes = toDecrypt.slice(sliceEnd);
}
const GetAdjustedLen = function () {
return sliceEnd - sliceStart;
};
//decrypt
//append last remaining buffer if any
if (this.buff.byteLength > 0) {
let tmpd = new Uint8Array(this.buff.byteLength + GetAdjustedLen());
tmpd.set(this.buff, 0);
tmpd.set(toDecrypt.slice(sliceStart, sliceEnd), this.buff.byteLength);
sliceEnd += this.buff.byteLength;
toDecrypt = tmpd;
this.buff = new Uint8Array();
}
let blkRem = GetAdjustedLen() % 16;
if (blkRem !== 0) {
//save any remaining data into our buffer
this.buff = toDecrypt.slice(sliceEnd - blkRem, sliceEnd);
sliceEnd -= blkRem;
}
let encBytes = toDecrypt.slice(sliceStart, sliceEnd);
let decBytes = this.aes.AES_Decrypt_process(encBytes);
decOffset += decBytes.byteLength;
//read header
if (isStart) {
headerLen = new Uint16Array(decBytes.slice(0, 2))[0];
let header = new TextDecoder('utf-8').decode(decBytes.slice(2, 2 + headerLen));
Log.I(`${this.fileinfo.FileId} got header ${header}`);
fileHeader = JSON.parse(header);
decBytes = decBytes.slice(2 + headerLen);
}
//Log.I(`${this.fileinfo.FileId} Decrypting ${toDecrypt.byteLength} bytes, got ${decBytes.byteLength} bytes`);
this.hmac.process(decBytes);
controller.enqueue(decBytes);
isStart = false;
await this.readOnce(controller);
}
}.bind(this))();
},
async readOnce(controller) {
let { done, value } = await this.reader.read();
if (done) {
if (this.buff.byteLength > 0) {
//pad the remaining data with PKCS#7
var toDecrypt = null;
let padding = 16 - (this.buff.byteLength % 16);
if (padding !== 0) {
let tmpBuff = new Uint8Array(this.buff.byteLength + padding);
tmpBuff.fill(padding);
tmpBuff.set(this.buff, 0);
this.buff = null;
this.buff = tmpBuff;
}
let decBytes = this.aes.AES_Decrypt_process(this.buff);
this.hmac.process(decBytes);
controller.enqueue(decBytes);
this.buff = null;
}
let last = this.aes.AES_Decrypt_finish();
this.hmac.process(last);
this.hmac.finish();
controller.enqueue(last);
//check hmac
let h1 = Utils.ArrayToHex(this.hmacBytes);
let h2 = Utils.ArrayToHex(this.hmac.result)
if (h1 === h2) {
Log.I(`HMAC verify ok!`);
} else {
Log.E(`HMAC verify failed (${h1} !== ${h2})`);
controller.cancel();
return;
}
Log.I(`${this.fileinfo.FileId} Download complete!`);
controller.close();
return;
}
var sliceStart = 0;
var sliceEnd = value.byteLength;
//!Slice this only once!!
var toDecrypt = value;
if (this.isStart) {
let header = VBF.ParseStart(value.buffer);
if (header !== null) {
Log.I(`${this.fileinfo.FileId} blob header version is ${header.version} uploaded on ${header.uploaded} (Magic: ${Utils.ArrayToHex(header.magic)})`);
sliceStart = VBF.SliceToEncryptedPart(header.version, value);
} else {
throw "Invalid VBF header";
}
} else if (this.fileHeader != null && this.decOffset + toDecrypt.byteLength + this.headerLen + 2 >= this.fileHeader.len) {
sliceEnd -= 32; //hash is on the end (un-encrypted)
this.hmacBytes = toDecrypt.slice(sliceEnd);
}
const GetAdjustedLen = function () {
return sliceEnd - sliceStart;
};
//decrypt
//append last remaining buffer if any
if (this.buff.byteLength > 0) {
let tmpd = new Uint8Array(this.buff.byteLength + GetAdjustedLen());
tmpd.set(this.buff, 0);
tmpd.set(toDecrypt.slice(sliceStart, sliceEnd), this.buff.byteLength);
sliceEnd += this.buff.byteLength;
toDecrypt = tmpd;
this.buff = new Uint8Array();
}
let blkRem = GetAdjustedLen() % 16;
if (blkRem !== 0) {
//save any remaining data into our buffer
this.buff = toDecrypt.slice(sliceEnd - blkRem, sliceEnd);
sliceEnd -= blkRem;
}
let encBytes = toDecrypt.slice(sliceStart, sliceEnd);
let decBytes = this.aes.AES_Decrypt_process(encBytes);
this.decOffset += decBytes.byteLength;
//read header
if (this.isStart) {
this.headerLen = new Uint16Array(decBytes.slice(0, 2))[0];
let header = new TextDecoder('utf-8').decode(decBytes.slice(2, 2 + this.headerLen));
Log.I(`${this.fileinfo.FileId} got header ${header}`);
this.fileHeader = JSON.parse(header);
this.SetFileHeader(this.fileHeader);
decBytes = decBytes.slice(2 + this.headerLen);
}
//Log.I(`${this.fileinfo.FileId} Decrypting ${toDecrypt.byteLength} bytes, got ${decBytes.byteLength} bytes`);
this.hmac.process(decBytes);
controller.enqueue(decBytes);
//report progress
let now = new Date().getTime();
let dxLoaded = decBytes.byteLength - this.downloadStats.lastLoaded;
let dxTime = now - this.downloadStats.lastProgress;
this.downloadStats.lastLoaded = decBytes.byteLength;
this.downloadStats.lastProgress = now;
this.HandleProgress('progress-speed', `${Utils.FormatBytes(dxLoaded / (dxTime / 1000.0), 2)}/s`);
this.HandleProgress('progress-download', this.decOffset / parseFloat(this.fileHeader.len));
this.isStart = false;
}
}
let sr = new ReadableStream(void_download);
return new Response(sr);
return new Response(new ReadableStream(void_download));
};
/**

View File

@ -94,7 +94,7 @@ function ViewManager() {
if ('serviceWorker' in navigator) {
let swreg = await navigator.serviceWorker.getRegistration();
if (swreg !== null) {
Log.I(`Service worker detected, using ${swreg.scope} for download..`);
Log.I(`Service worker detected, using it for download..`);
let elm_bar_label = document.querySelector('.view-download-progress div:nth-child(1)');
let elm_bar = document.querySelector('.view-download-progress div:nth-child(2)');
navigator.serviceWorker.addEventListener('message', event => {