forked from Kieran/void.cat
set filename in header
This commit is contained in:
parent
4a2c4edafd
commit
c51f8d093a
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,4 +10,5 @@ Debug/
|
|||||||
Release/
|
Release/
|
||||||
node_modules/
|
node_modules/
|
||||||
package-lock.json
|
package-lock.json
|
||||||
out/
|
out/
|
||||||
|
sw.js
|
@ -7,7 +7,7 @@
|
|||||||
"url": "git+https://github.com/v0l/void.cat.git"
|
"url": "git+https://github.com/v0l/void.cat.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"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": "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"
|
"debug-worker": "webpack-cli --entry ./src/js/Worker.js --mode development --watch --output ./sw.js"
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,18 @@ const VoidFetch = function (event) {
|
|||||||
let fi = await Api.GetFileInfo(hs.id);
|
let fi = await Api.GetFileInfo(hs.id);
|
||||||
if (fi.ok) {
|
if (fi.ok) {
|
||||||
let fd = new FileDownloader(fi.data, hs.key, hs.iv);
|
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 {
|
} else {
|
||||||
return Response.error();
|
return Response.error();
|
||||||
}
|
}
|
||||||
|
@ -81,126 +81,144 @@ function FileDownloader(fileinfo, key, iv) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let void_download = {
|
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,
|
body: response.body,
|
||||||
fileinfo: this.fileinfo,
|
fileinfo: this.fileinfo,
|
||||||
aes: new AES_CBC(new Uint8Array(Utils.HexToArray(this.key)), new Uint8Array(Utils.HexToArray(this.iv)), true),
|
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))),
|
hmac: new HmacSha256(new Uint8Array(Utils.HexToArray(this.key))),
|
||||||
buff: new Uint8Array(),
|
buff: new Uint8Array(),
|
||||||
|
start(controller) {
|
||||||
|
this.reader = this.body.getReader();
|
||||||
|
return this.readOnce(controller);
|
||||||
|
},
|
||||||
pull(controller) {
|
pull(controller) {
|
||||||
if (this.reader === undefined) {
|
|
||||||
this.reader = this.body.getReader();
|
|
||||||
}
|
|
||||||
return (async function () {
|
return (async function () {
|
||||||
Log.I(`${this.fileinfo.FileId} Starting..`);
|
Log.I(`${this.fileinfo.FileId} Starting..`);
|
||||||
var isStart = true;
|
|
||||||
var decOffset = 0;
|
|
||||||
var headerLen = 0;
|
|
||||||
var fileHeader = null;
|
|
||||||
var hmacBytes = null;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
let { done, value } = await this.reader.read();
|
await this.readOnce(controller);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}.bind(this))();
|
}.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(new ReadableStream(void_download));
|
||||||
return new Response(sr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,7 +94,7 @@ function ViewManager() {
|
|||||||
if ('serviceWorker' in navigator) {
|
if ('serviceWorker' in navigator) {
|
||||||
let swreg = await navigator.serviceWorker.getRegistration();
|
let swreg = await navigator.serviceWorker.getRegistration();
|
||||||
if (swreg !== null) {
|
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_label = document.querySelector('.view-download-progress div:nth-child(1)');
|
||||||
let elm_bar = document.querySelector('.view-download-progress div:nth-child(2)');
|
let elm_bar = document.querySelector('.view-download-progress div:nth-child(2)');
|
||||||
navigator.serviceWorker.addEventListener('message', event => {
|
navigator.serviceWorker.addEventListener('message', event => {
|
||||||
|
Loading…
Reference in New Issue
Block a user