forked from hushfile/hushfile-web
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhushfile.js
516 lines (451 loc) · 21.5 KB
/
hushfile.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
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
// set page content
function setContent(content) {
document.getElementById('content').innerHTML=content
};
// return a random password of the given length
function randomPassword(length) {
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
pass = "";
for(x=0;x<length;x++) {
i = Math.floor(Math.random() * 62);
pass += chars.charAt(i);
}
return pass;
};
// progress function for filereader on upload page
function updateProgress(evt) {
// evt is an ProgressEvent.
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
// Increase the load_progress bar length.
if (percentLoaded < 100) {
load_progress.style.width = percentLoaded + '%';
load_progress.textContent = percentLoaded + '%';
};
};
};
function getmetadata() {
var password = window.location.hash.substr(1);
// highligt no menu items
document.getElementById("upload").className="active";
document.getElementById("about").className="";
document.getElementById("faq").className="";
// create download page content
content = '<h1>hushfile.it - download file</h1>\n';
content += '<div id="metadata" style="display: none;">\n';
// create metadata div
content += '<div class="alert alert-success">\n';
content += '<p>Password accepted, metadata below. Click button to get and decrypt file.</p>\n';
content += '</div>\n';
// table with metadata
content += '<table class="table table-condensed">\n';
content += '<tr><td>Filename</td><td id="filename"> </td></tr>\n';
content += '<tr><td>Mime type</td><td id="mimetype"> </td></tr>\n';
content += '<tr><td>File size</td><td id="filesize"> </td></tr>\n';
content += '<tr><td>Uploader IP</td><td id="clientip"> </td></tr>\n';
content += '<tr style="display:none"><td>Deletepassword</td><td id="deletepassword"> </td></tr>\n';
content += '</table>\n';
content += '<button class="btn btn-large btn-primary btn-success" id="download" type="button" onclick="download();"><i class="icon-cloud-download icon-large"></i> Get and decrypt</button>\n';
content += '<button class="btn btn-large btn-primary btn-danger" id="delete" type="button" onclick="deletefile();"><i class="icon-trash icon-large"></i> Delete file</button>\n';
content += '</div>\n';
// create downloading progress div
content += '<div id="downloading" style="display: none;">\n';
content += '<p><i id="downloadingdone" class="icon-spinner icon-spin"></i> <b>Downloading...</b></p>\n';
content += '<div class="progress progress-striped" id="download_progress_bar" style="width: 20em;">\n';
content += '<div id="download_progress_bar_percent" class="downloadpercent bar bar-success">0%</div>\n';
content += '</div>\n';
content += '</div>\n';
// create decrypting div
content += '<div id="decrypting" style="display: none;">\n';
content += '<p><i id="decryptingdone" class="icon-spinner icon-spin"></i> <b>Decrypting...</b></p>\n';
content += '</div>\n';
// create user download div
content += '<div id="downloaddiv" style="display: none;"></div>\n';
// create deleting div
content += '<div id="deleting" style="display: none;">\n';
content += '<p><i id="deletingdone" class="icon-spinner icon-spin"></i> <b>Deleting...</b></p>\n';
content += '</div>\n';
// create deleteresponse div
content += '<div id="deleteresponse" style="display: none;">\n';
content += '</div>\n';
// check if it is a file id that exists
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/exists?fileid='+fileid, true);
xhr.onload = function(e) {
if (this.status == 200) {
var responseobject = JSON.parse(xhr.responseText);
if (responseobject.exists) {
// fileid exists
// create page content
setContent(content);
// download and decrypt metadata
var xhr2 = new XMLHttpRequest();
xhr2.open('GET', '/api/metadata?fileid='+fileid, true);
xhr2.onload = function(e) {
if (this.status == 200) {
// decrypt metadata
try {
metadata = CryptoJS.AES.decrypt(this.response, password).toString(CryptoJS.enc.Utf8);
} catch(err) {
setContent('<div class="alert alert-error">Unable to decrypt metadata, invalid password.</div>\n');
return;
};
if(metadata != 'undefined') {
try {
var jsonmetadata = JSON.parse(metadata);
document.getElementById('metadata').style.display="block";
document.getElementById('filename').innerHTML = jsonmetadata.filename;
document.getElementById('mimetype').innerHTML = jsonmetadata.mimetype;
document.getElementById('filesize').innerHTML = jsonmetadata.filesize;
document.getElementById('deletepassword').innerHTML = jsonmetadata.deletepassword;
} catch(err) {
setContent('<div class="alert alert-error">Unable to parse metadata, sorry.</div>\n');
// highligt no menu items
document.getElementById("upload").className="";
document.getElementById("about").className="";
document.getElementById("faq").className="";
return;
};
};
} else {
setContent('<div class="alert alert-error">Unable to download metadata, sorry.</div>\n');
// highligt no menu items
document.getElementById("upload").className="";
document.getElementById("about").className="";
document.getElementById("faq").className="";
return;
};
};
xhr2.send();
// create XHR to get IP
var ipxhr = new XMLHttpRequest();
ipxhr.open('GET', '/api/ip?fileid='+fileid, true);
ipxhr.onload = function(e) {
if (this.status == 200) {
var jsonip = JSON.parse(ipxhr.responseText);
document.getElementById('clientip').innerHTML = jsonip.uploadip;
} else {
alert("An error was encountered getting uploader ip.");
};
};
// get IP
ipxhr.send();
} else {
// fileid does not exist
setContent('<div class="alert alert-error">Invalid fileid. Expired ?</div>\n');
// highligt no menu items
document.getElementById("upload").className="";
document.getElementById("about").className="";
document.getElementById("faq").className="";
return;
};
} else if (this.status == 404) {
//fileid does not exist
setContent('<div class="alert alert-error">Invalid fileid. Expired ?</div>\n');
// highligt no menu items
document.getElementById("upload").className="";
document.getElementById("about").className="";
document.getElementById("faq").className="";
};
};
// send /exists request
xhr.send();
}
// function that handles reading file after it has been selected
function handleFileSelect(evt) {
// Reset load_progress indicator on new file selection.
load_progress.style.width = '0%';
load_progress.textContent = '0%';
document.getElementById('read_progress_div').style.display="block";
document.getElementById('encrypting').style.display="block";
document.getElementById('uploading').style.display="block";
//create filereader object
reader = new FileReader();
//register event handlers
reader.onprogress = updateProgress;
// runs after file reading completes
reader.onload = function(e) {
// Ensure that the load_progress bar displays 100% at the end.
load_progress.style.width = '100%';
load_progress.textContent = '100%';
document.getElementById('readingdone').className= 'icon-check';
document.getElementById('read_progress_div').style.color='green';
//make the next section visible
document.getElementById('encrypting').style.display="block";
document.getElementById('encryptingdone').className="icon-spinner icon-spin";
setTimeout('encrypt()',1000);
};
// get file info and show it to the user
filename = evt.target.files[0].name;
if(evt.target.files[0].type === 'undefined') {
mimetype = "application/octet-stream";
} else {
mimetype = evt.target.files[0].type;
}
filesize = evt.target.files[0].size;
document.getElementById('filename').innerHTML = filename;
document.getElementById('mimetype').innerHTML = mimetype;
document.getElementById('filesize').innerHTML = filesize;
// begin reading the file
reader.readAsArrayBuffer(evt.target.files[0]);
};
// function that encrypts the file,
// and creates and encrypts metadata
function encrypt() {
//encrypt the data
ui8a = new Uint8Array(reader.result);
wordarray = CryptoJS.enc.u8array.parse(ui8a);
cryptoobject = CryptoJS.AES.encrypt(wordarray, document.getElementById('password').value);
//generate deletepassword
deletepassword = randomPassword(40);
//encrypt the metadata
metadatajson = '{"filename": "'+filename+'", "mimetype": "'+mimetype+'", "filesize": "'+filesize+'", "deletepassword": "' + deletepassword + '"}'
metadataobject = CryptoJS.AES.encrypt(metadatajson, document.getElementById('password').value);
//done encrypting
document.getElementById('encryptingdone').className="icon-check";
document.getElementById('encrypting').style.color='green';
//make the next section visible
document.getElementById('uploading').style.display="block";
document.getElementById('uploaddone').className="icon-spinner icon-spin";
setTimeout('upload(cryptoobject,metadataobject,deletepassword)',1000);
}
function upload(cryptoobject,metadataobject,deletepassword) {
var xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload', true);
xhr.onload = function(e) {
//make sure progress is at 100%
upload_progress.style.width = '100%';
upload_progress.textContent = '100%';
//parse json reply
try {
var responseobject = JSON.parse(xhr.responseText);
if (responseobject.status=='ok') {
document.getElementById('uploaddone').className= "icon-check";
document.getElementById('uploading').style.color='green';
document.getElementById('response').style.display="block";
//get current URL
url = window.location.protocol + '://' + window.location.host + '/';
document.getElementById('response').innerHTML = '<p><i class="icon-check"></i> <b><span style="color: green;">Success! Your URL is:</span></b><br> <a class="btn btn-success" href="/'+responseobject.fileid+'#'+document.getElementById('password').value+'">'+url+responseobject.fileid+'#'+document.getElementById('password').value+'</a>';
} else {
document.getElementById('response').innerHTML = 'Something went wrong. Sorry about that. <a href="/">Try again.</a>';
}
} catch(err) {
document.getElementById('response').innerHTML = 'Something went wrong: ' + err;
};
};
// Listen to the upload progress
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
temp = Math.round((e.loaded / e.total) * 100);
upload_progress.style.width = temp + '%';
upload_progress.textContent = temp + '%';
};
};
var formData = new FormData();
formData.append('cryptofile', cryptoobject);
formData.append('metadata', metadataobject);
formData.append('deletepassword', deletepassword);
xhr.send(formData);
};
//function that deletes the file
function deletefile() {
// disable the delete button
document.getElementById('delete').className="btn btn-large btn-primary btn-success disabled";
document.getElementById('deleting').style.display="block";
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/delete?fileid='+fileid+'&deletepassword='+document.getElementById('deletepassword').innerHTML, true);
xhr.onload = function(e) {
document.getElementById('deleteresponse').style.display="block";
if (this.status == 200) {
//parse response json
var responseobject = JSON.parse(xhr.responseText);
if(responseobject.deleted) {
//file deleted OK
document.getElementById('deletingdone').className="icon-check";
document.getElementById('deleteresponse').innerHTML="<div class='alert alert-success'>File deleted successfully</div>\n";
} else {
//unable to delete file
document.getElementById('deletingdone').className="icon-warning-sign";
document.getElementById('deleteresponse').innerHTML="<div class='alert alert-error'>Unable to delete file</div>\n";
};
} else if (this.status == 401) {
document.getElementById('deletingdone').className="icon-warning-sign";
document.getElementById('deleteresponse').innerHTML="<div class='alert alert-error'>Incorrect deletepassword</div>\n";
};
};
xhr.send();
}
//function that downloads the file to the browser,
//and decrypts and creates download button
function download() {
// disable the download button
document.getElementById('download').className="btn btn-large btn-primary btn-success disabled";
// make download progress bar div visible
document.getElementById('downloading').style.display="block";
var xhr = new XMLHttpRequest();
xhr.open('GET', '/api/file?fileid='+fileid, true);
xhr.onload = function(e) {
if (this.status == 200) {
//done downloading, make downloading div green and change icon
document.getElementById('downloading').style.color='green';
document.getElementById('downloadingdone').className="icon-check";
//make the decrypting div visible
document.getElementById('decrypting').style.display="block";
// decrypt the data
decryptedwords = CryptoJS.AES.decrypt(this.response, password);
ui8a = CryptoJS.enc.u8array.stringify(decryptedwords);
fileblob = new Blob([ui8a], { type: document.getElementById('mimetype').innerHTML });
//done decrypting, change icon and make div green
document.getElementById('decryptingdone').className="icon-check";
document.getElementById('decrypting').style.color='green';
// download button
a = document.createElement("a");
a.href = window.URL.createObjectURL(fileblob);
a.download = document.getElementById('filename').innerHTML;
linkText = document.createTextNode(" Download");
i = document.createElement("i");
i.className="icon-save icon-large";
a.appendChild(i);
a.appendChild(linkText);
a.className = "btn btn-large btn-primary btn-success";
document.getElementById('downloaddiv').appendChild(a);
//make div visible
document.getElementById('downloaddiv').style.display="block";
} else {
alert("An error was encountered downloading filedata.");
};
};
// Listen to the download progress.
xhr.onprogress = function(e) {
if (e.lengthComputable) {
temp = Math.round((e.loaded / e.total) * 100);
document.getElementById('download_progress_bar_percent').style.width = temp + '%';
document.getElementById('download_progress_bar_percent').textContent = temp + '%';
};
};
xhr.send();
};
if(window.location.pathname == "/") {
// show upload page
// highligt menu item
document.getElementById("upload").className="active";
document.getElementById("about").className="";
document.getElementById("faq").className="";
// create welcome alert box
content = '<div class="alert alert-info fade in">\n';
content += '<button type="button" class="close" data-dismiss="alert">×</button>\n';
content += '<h4>Welcome!</h4>\n';
content += 'hushfile is a file sharing service where the file is <b>encrypted before upload</b>. This enables you to share files while <b>keeping them private</b> from server operators and eavesdroppers. Just pick a file and it will be <b>encrypted in your browser</b> before it is uploaded. When the process is finished you will receive a link which you can share with anyone you wish. Just <b>keep the link secret</b>, it contains the password to decrypt the file!\n';
content += '</div>\n';
// create upload form
content += '<form class="form-horizontal">\n';
content += '<div class="fileupload fileupload-new" data-provides="fileupload">\n';
content += '<div class="input-append">\n';
content += '<div class="uneditable-input span3">\n';
content += '<i class="icon-file fileupload-exists"></i>\n';
content += '<span class="fileupload-preview"></span>\n';
content += '</div>\n';
content += '<span class="btn btn-file"><span class="fileupload-new">Select file</span>\n';
content += '<span class="fileupload-exists">Change</span>\n';
content += '<input type="file" id="files" name="file">\n';
content += '</span>\n';
content += '<a href="#" class="btn fileupload-exists" data-dismiss="fileupload">Remove</a>\n';
content += '</div>\n';
content += '</div>\n';
content += '<div class="input-append" style="display: none;">\n';
content += '<input class="input-large" type="text" id="password" name="password">\n';
content += '<span class="add-on">Password</span>\n';
content += '</div>\n';
content += '</form>\n';
// create filereading progess div
content += '<div id="read_progress_div" style="display: none;">\n'
content += '<p><i id="readingdone" class="icon-check-empty"></i> <b>Reading file...</b>\n';
content += '<div class="progress progress-striped" id="read_progress_bar" style="width: 20em;">\n';
content += '<div class="loadpercent bar bar-success">0%</div>\n';
content += '</div></p>\n';
content += '<table class="table table-condensed">\n';
content += '<tr><td>Filename</td><td id="filename"> </td></tr>\n';
content += '<tr><td>Mime type</td><td id="mimetype"> </td></tr>\n';
content += '<tr><td>File size</td><td id="filesize"> </td></tr>\n';
content += '</table>\n';
content += '</div>\n';
// create encrypting div
content += '<div id="encrypting" style="display: none;">\n';
content += '<p><i id="encryptingdone" class="icon-check-empty"></i> <b>Encrypting...</b></p>\n';
content += '</div>\n';
// create uploading div
content += '<div id="uploading" style="display: none;">\n';
content += '<p><i id="uploaddone" class="icon-check-empty"></i> <b>Uploading...</b>\n';
content += '<div class="progress progress-striped" id="upload_progress_bar" style="width: 20em;">\n';
content += '<div class="uploadpercent bar bar-success">0%</div>\n';
content += '</div></p>\n';
content += '</div>\n';
// create response div
content += '<div class="alert alert-info" id="response" style="display: none;">\n';
content += '<h4>Response</h4>\n';
content += '</div>\n';
setContent(content);
var reader;
var load_progress = document.querySelector('.loadpercent');
var upload_progress = document.querySelector('.uploadpercent');
var encrypted;
var filename;
var mimetype;
var filesize;
// create random password
document.getElementById('password').value=randomPassword(40);
//wait for a file to be selected
document.getElementById('files').addEventListener('change', handleFileSelect, false);
} else if(window.location.pathname == "/faq") {
// highligt menu item
document.getElementById("upload").className="";
document.getElementById("about").className="";
document.getElementById("faq").className="active";
content = '<h1>hushfile.it Frequently Asked Questions</h1>\n';
content += '<dl>\n';
content += '<dt>Which browsers are known to work ?</dt>\n';
content += '<dd>\n';
content += '<ul>\n';
content += '<li>Google Chrome Version 26.0.1410.64</li>\n';
content += '<li>Firefox 20.0.1</li>\n';
content += '<li>Please report more working browsers to the <a href="mailto:[email protected]">author</a>!</li>\n';
content += '</ul>\n';
content += '</dd>\n';
content += '<dt>Which encryption is used ? Is it safe ?</dt>\n';
content += '<dd>\n';
content += 'The file it\'s metadata are both encrypted with AES-256 in CBC mode with PKCS7 padding. The actual encryption is performed by the <a href="http://code.google.com/p/crypto-js/" target="_blank">CryptoJS 3.1.2</a> library. From their website:\n';
content += '<blockquote>\n';
content += '<p>CryptoJS is a growing collection of standard and secure cryptographic algorithms implemented in JavaScript using best practices and patterns. They are fast, and they have a consistent and simple interface.</p>\n';
content += '</blockquote>\n';
content += '</dd>\n';
content += '</dl>\n';
setContent(content);
} else if(window.location.pathname == "/about") {
// highligt menu item
document.getElementById("upload").className="";
document.getElementById("about").className="active";
document.getElementById("faq").className="";
content = '<div class="alert alert-info">\n';
content += '<h4>Welcome</h4>\n';
content += 'hushfile is a file sharing service where the file is <b>encrypted before upload</b>. This enables you to share files while <b>keeping them private</b> from server operators and eavesdroppers. Just pick a file and it will be <b>encrypted in your browser</b> before it is uploaded. When the process is finished you will receive a link which you can share with anyone you wish. Just <b>keep the link secret</b>, it contains the password to decrypt the file!\n';
content += '<div class="alert alert-info">\n';
content += '<h4>Background</h4>\n';
content += 'The idea for hushfile came from the pastebin <a href="https://ezcrypt.it/">ezcrypt.it</a>. Ezcrypt.it is like a normal pastebin, except that it encrypts the pasted text before it is uploaded to the server. Hushfile is a file-version of ezcrypt.it, an easy way to share files with those you wish to share with, and noone else. This might seem like a subtle difference from a normal pastebin or filesharing service, but it is a <b>great</b> idea. I firmly believe that the best way to promote privacy online is to put <b>easy to use encryption</b> into the hands of the end users. People are lazy, but if a private alternative is as easy to use as a non-private one, the choice is easy.\n';
content += '</div>\n';
setContent(content);
} else {
// this is not a request for a known url, get fileid and password
var fileid = window.location.pathname.substr(1);
if(window.location.hash.substr(1)=="") {
content = '<div class="alert alert-info">Enter password:</div>\n';
content += '<input type="text" id="password">\n';
content += '<button type="button" class="btn btn-large btn-success" onclick="getmetadata();">Go</button>\n';
setContent(content);
// highligt no menu items
document.getElementById("upload").className="";
document.getElementById("about").className="";
document.getElementById("faq").className="";
} else {
getmetadata();
};
};