@@ -18,7 +18,8 @@ namespace Teknik.Areas.Upload.Controllers | |||
{ | |||
return View(new UploadViewModel()); | |||
} | |||
// GET: Upload/Upload | |||
// User did not supply key | |||
[HttpGet] | |||
[AllowAnonymous] | |||
public ActionResult Download(string url) | |||
@@ -26,6 +27,14 @@ namespace Teknik.Areas.Upload.Controllers | |||
return View(new UploadViewModel()); | |||
} | |||
// User supplied key | |||
[HttpGet] | |||
[AllowAnonymous] | |||
public ActionResult Download(string url, string key) | |||
{ | |||
return View(new UploadViewModel()); | |||
} | |||
[HttpPost] | |||
[AllowAnonymous] | |||
[ValidateAntiForgeryToken] | |||
@@ -34,7 +43,7 @@ namespace Teknik.Areas.Upload.Controllers | |||
Models.Upload upload = Uploader.SaveFile(data, fileType, iv); | |||
if (upload != null) | |||
{ | |||
return Json(new { result = new { name = upload.Url, url = Url.SubRouteUrl("upload", "Upload.Download", new { file = upload.Url }) } }, "text/plain"); | |||
return Json(new { result = new { name = upload.Url, url = Url.SubRouteUrl("upload", "Upload.Download.Key", new { file = upload.Url, key = "{key}" }), keyVar = "{key}" } }, "text/plain"); | |||
} | |||
return Json(new { error = "Unable to upload file" }); | |||
} |
@@ -1,58 +1,82 @@ | |||
self.addEventListener('message', function (e) { | |||
var data = e.data; | |||
importScripts(data.script); | |||
importScripts(e.data.script); | |||
switch (data.cmd) { | |||
switch (e.data.cmd) { | |||
case 'encrypt': | |||
//var startByte = 0; | |||
//var endByte = 0; | |||
//var prog = []; | |||
var bytes = new Uint8Array(e.data.file); | |||
//var bytes = new Uint8Array(data.file); | |||
var startByte = 0; | |||
var endByte = 0; | |||
var prog = []; | |||
//// Create aes encryptor object | |||
//var aesEncryptor = CryptoJS.algo.AES.createEncryptor(data.key, { iv: data.iv }); | |||
var key = CryptoJS.enc.Utf8.parse(e.data.key); | |||
var iv = CryptoJS.enc.Utf8.parse(e.data.iv); | |||
// Create aes encryptor object | |||
var aesEncryptor = CryptoJS.algo.AES.createEncryptor(key, { iv: iv, mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.NoPadding }); | |||
//while (startByte <= (bytes.length - 1)) { | |||
// // Set the end byte | |||
// endByte = startByte + data.chunkSize; | |||
// if (endByte > bytes.length - 1) | |||
// { | |||
// endByte = bytes.length - 1; | |||
// } | |||
while (startByte <= (bytes.length - 1)) { | |||
// Set the end byte | |||
endByte = startByte + e.data.chunkSize; | |||
if (endByte > bytes.length - 1) | |||
{ | |||
endByte = bytes.length - 1; | |||
} | |||
// Grab current set of bytes | |||
var curBytes = bytes.subarray(startByte, endByte); | |||
//var b64encoded = btoa(String.fromCharCode.apply(null, curBytes)); | |||
var wordArray = CryptoJS.lib.WordArray.create(curBytes) | |||
// encrypt the passed in file data | |||
var enc = aesEncryptor.process(wordArray); | |||
// Convert and add to current array buffer | |||
var encStr = enc.toString(CryptoJS.enc.Base64); // to string | |||
prog.pushArray(_base64ToArray(encStr)); | |||
// // Grab current set of bytes | |||
// var curBytes = bytes.subarray(startByte, endByte); | |||
// var wordArray = CryptoJS.lib.WordArray.create(curBytes) | |||
// Send an update on progress | |||
var objData = | |||
{ | |||
cmd: 'progress', | |||
processed: endByte, | |||
total: bytes.length - 1 | |||
}; | |||
// // encrypt the passed in file data and add it to bits[] | |||
// prog.push(aesEncryptor.process(wordArray)); | |||
self.postMessage(objData); | |||
// // Set the next start as the current end | |||
// startByte = endByte + 1; | |||
//}//then finalize | |||
//prog.push(aesEncryptor.finalize()); | |||
// Set the next start as the current end | |||
startByte = endByte + 1; | |||
} | |||
//throw JSON.stringify({ data: prog, start: startByte, end: endByte, len: bytes.length }) | |||
var wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(data.file)); | |||
//then finalize | |||
var encFinal = aesEncryptor.finalize(); | |||
var finalStr = encFinal.toString(CryptoJS.enc.Base64); // to final string | |||
prog.pushArray(_base64ToArray(finalStr)); | |||
var objData = | |||
{ | |||
cmd: 'progress', | |||
processed: bytes.length - 1, | |||
total: bytes.length - 1 | |||
}; | |||
var encWords = CryptoJS.AES.encrypt(wordArray, data.key, { iv: data.iv, mode: CryptoJS.mode.CBC }); | |||
// convert array to ArrayBuffer | |||
var arBuf = _arrayToArrayBuffer(prog); | |||
//throw JSON.stringify({ data: wordArray }); | |||
//throw JSON.stringify({ dataLength: prog.length, len: bytes.length, finalLength: arBuf.byteLength }) | |||
var dcBase64String = encWords.toString(); // to Base64-String | |||
//var encByteArray = wordToByteArray(encWords.words); | |||
// patch it all back together for the trip home | |||
// Now package it into a mesage to send home | |||
var objData = | |||
{ | |||
encrypted: str2ab(dcBase64String) | |||
cmd: 'finish', | |||
encrypted: arBuf | |||
}; | |||
self.postMessage(objData, [objData.encrypted]); | |||
break; | |||
case 'decrypt': | |||
// decrypt the passed in file data | |||
var decrypted = CryptoJS.AES.decrypt(data.file, data.key, { iv: data.iv }); | |||
var decrypted = CryptoJS.AES.decrypt(e.data.file, e.data.key, { iv: e.data.iv }); | |||
var fileText = decrypted.toString(); | |||
@@ -61,36 +85,23 @@ | |||
} | |||
}, false); | |||
function wordToByteArray(wordArray) { | |||
var byteArray = [], word, i, j; | |||
for (i = 0; i < wordArray.length; ++i) { | |||
word = wordArray[i]; | |||
for (j = 3; j >= 0; --j) { | |||
byteArray.push((word >> 8 * j) & 0xFF); | |||
} | |||
} | |||
return byteArray; | |||
function _arrayToArrayBuffer(array) { | |||
var len = array.length; | |||
var bytes = new Uint8Array(len); | |||
bytes.set(array, 0); | |||
return bytes.buffer; | |||
} | |||
function _base64ToArrayBuffer(base64) { | |||
function _base64ToArray(base64) { | |||
var binary_string = atob(base64); | |||
var len = binary_string.length; | |||
var bytes = new Uint8Array(len); | |||
for (var i = 0; i < len; i++) { | |||
bytes[i] = binary_string.charCodeAt(i); | |||
} | |||
return bytes.buffer; | |||
return bytes; | |||
} | |||
function ab2str(buf) { | |||
return String.fromCharCode.apply(null, new Uint16Array(buf)); | |||
} | |||
function str2ab(str) { | |||
var buf = new ArrayBuffer(str.length); // 2 bytes for each char | |||
var bufView = new Uint16Array(buf); | |||
for (var i = 0, strLen = str.length; i < strLen; i++) { | |||
bufView[i] = str.charCodeAt(i); | |||
} | |||
return buf; | |||
} | |||
Array.prototype.pushArray = function (arr) { | |||
this.push.apply(this, arr); | |||
}; |
@@ -3,13 +3,31 @@ | |||
$("#upload-links").html(''); | |||
}); | |||
function linkUploadDelete(selector) { | |||
function linkSaveKey(selector, uploadID, key, fileID) { | |||
$(selector).click(function () { | |||
$.ajax({ | |||
type: "POST", | |||
url: saveKeyToServerURL, | |||
data: AddAntiForgeryToken({ uploadID: uploadID, key: key }), | |||
success: function (html) { | |||
if (html.result) { | |||
} | |||
else { | |||
$("#top_msg").css('display', 'inline', 'important'); | |||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + html.error + '</div>'); | |||
} | |||
} | |||
}); | |||
return false; | |||
}); | |||
} | |||
function linkUploadDelete(selector, uploadID) { | |||
$(selector).click(function () { | |||
ID = encodeURIComponent($(this).attr('id')); | |||
$.ajax({ | |||
type: "POST", | |||
url: generateDeleteKeyURL, | |||
data: AddAntiForgeryToken({ uploadID: ID }), | |||
data: AddAntiForgeryToken({ uploadID: uploadID }), | |||
success: function (html) { | |||
if (html.result) { | |||
bootbox.dialog({ | |||
@@ -28,6 +46,15 @@ function linkUploadDelete(selector) { | |||
}); | |||
} | |||
function linkRemove(selector, fileID) { | |||
$(selector).click(function () { | |||
$('#link-' + fileID).remove(); | |||
return false; | |||
}); | |||
} | |||
var fileCount = 0; | |||
Dropzone.options.TeknikUpload = { | |||
paramName: "file", // The name that will be used to transfer the file | |||
maxFilesize: maxUploadSize, // MB | |||
@@ -37,53 +64,39 @@ Dropzone.options.TeknikUpload = { | |||
previewTemplate: function () { }, | |||
addedfile: function (file) { | |||
// Create the UI element for the new item | |||
var short_name = file.name.hashCode(); | |||
var fileID = fileCount; | |||
fileCount++; | |||
// save ID to the file object | |||
file.ID = fileID; | |||
$("#upload-links").css('display', 'inline', 'important'); | |||
$("#upload-links").prepend(' \ | |||
<div class="row link-' + short_name + '" id="link-' + short_name + '"> \ | |||
<div class="col-sm-12 text-center"> \ | |||
'+ file.name + ' \ | |||
</div> \ | |||
<div class="progress-' + short_name + '"> \ | |||
<div class="progress-bar progress-bar-success" id="progressBar-' + short_name + '" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%">0%</div> \ | |||
<div class="panel panel-default" id="link-' + fileID + '"> \ | |||
<div class="panel-heading text-center" id="link-header-' + fileID + '">'+ file.name + '</div> \ | |||
<div class="panel-body" id="link-panel-' + fileID + '"> \ | |||
<div class="row"> \ | |||
<div class="col-sm-12 text-center" id="upload-link-' + fileID + '"></div> \ | |||
</div> \ | |||
<div class="row"> \ | |||
<div class="col-sm-12 text-center"> \ | |||
<div class="progress" id="progress-' + fileID + '"> \ | |||
<div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" style="width: 0%">0%</div> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
'); | |||
// Encrypt the file | |||
encryptFile(file, uploadFile); | |||
$("#upload_message").css('display', 'none', 'important'); | |||
}, | |||
init: function() { | |||
this.on("removedfile", function(file) { | |||
var name = file.name.hashCode(); | |||
$('.link-'+name).remove(); | |||
}); | |||
this.on("reset", function(file, responseText) { | |||
$("#upload_message").css('display', 'inline', 'important'); | |||
$(".progress").children('.progress-bar').css('width', '0%'); | |||
$(".progress").children('.progress-bar').html('0%'); | |||
}); | |||
this.on("error", function(file, errorMessage) { | |||
this.removeFile(file); | |||
$("#top_msg").css('display', 'inline', 'important'); | |||
$("#top_msg").html('<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>'+errorMessage+'</div>'); | |||
}); | |||
this.on("totaluploadprogress", function(progress, totalBytes, totalBytesSent) { | |||
$(".progress").children('.progress-bar').css('width', (progress.toFixed(2) * (3/5)) + 40 +'%'); | |||
$(".progress").children('.progress-bar').html(progress.toFixed(2)+'% Uploaded'); | |||
}); | |||
this.on("queuecomplete", function() { | |||
$(".progress").children('.progress-bar').html('Complete'); | |||
}); | |||
this.removeFile(file); | |||
} | |||
}; | |||
// Function to encrypt a file, overide the file's data attribute with the encrypted value, and then call a callback function if supplied | |||
function encryptFile(file, callback) { | |||
var filetype = file.type; | |||
var filename = file.name; | |||
var shortName = file.name.hashCode(); | |||
var fileID = file.ID; | |||
// Start the file reader | |||
var reader = new FileReader(); | |||
@@ -92,30 +105,34 @@ function encryptFile(file, callback) { | |||
reader.onload = (function (callback) { | |||
return function (e) { | |||
// Create random key and iv | |||
var keyStr = randomString(16, '#aA'); | |||
var ivStr = randomString(16, '#aA'); | |||
var key = CryptoJS.enc.Utf8.parse(keyStr); | |||
var iv = CryptoJS.enc.Utf8.parse(ivStr); | |||
// Display encryption message | |||
$(".progress-" + shortName).children('.progress-bar').css('width', '20%'); | |||
$(".progress-" + shortName).children('.progress-bar').html('Encrypting...'); | |||
var keyStr = randomString(24, '#aA'); | |||
var ivStr = randomString(24, '#aA'); | |||
var worker = new Worker(encScriptSrc); | |||
worker.addEventListener('message', function (e) { | |||
if (callback != null) { | |||
// Finish | |||
callback(e.data.encrypted, keyStr, ivStr, filetype, filename); | |||
switch (e.data.cmd) | |||
{ | |||
case 'progress': | |||
var percentComplete = Math.round(e.data.processed * 100 / e.data.total); | |||
$("#progress-" + fileID).children('.progress-bar').css('width', (percentComplete * (2 / 5)) + 20 + '%'); | |||
$("#progress-" + fileID).children('.progress-bar').html(percentComplete + '% Encrypted'); | |||
break; | |||
case 'finish': | |||
if (callback != null) { | |||
// Finish | |||
callback(e.data.encrypted, keyStr, ivStr, filetype, fileID); | |||
} | |||
break; | |||
} | |||
}); | |||
worker.onerror = function (err) { | |||
alert(err); | |||
// An error occured | |||
$(".progress-" + shortName).children('.progress-bar').css('width', '100%'); | |||
$(".progress-" + shortName).children('.progress-bar').html('Error Occured'); | |||
$("#progress-" + fileID).children('.progress-bar').css('width', '100%'); | |||
$("#progress-" + fileID).children('.progress-bar').removeClass('progress-bar-success'); | |||
$("#progress-" + fileID).children('.progress-bar').addClass('progress-bar-danger'); | |||
$("#progress-" + fileID).children('.progress-bar').html('Error Occured'); | |||
} | |||
// Execute worker with data | |||
@@ -123,24 +140,31 @@ function encryptFile(file, callback) { | |||
{ | |||
cmd: 'encrypt', | |||
script: aesScriptSrc, | |||
key: key, | |||
iv: iv, | |||
chunkSize: 1024, | |||
key: keyStr, | |||
iv: ivStr, | |||
chunkSize: chunkSize, | |||
file: e.target.result | |||
}; | |||
worker.postMessage(objData, [objData.file]); | |||
}; | |||
})(callback); | |||
reader.onprogress = function (data) { | |||
if (data.lengthComputable) { | |||
var progress = parseInt(((data.loaded / data.total) * 100), 10); | |||
$('#progress-' + fileID).children('.progress-bar').css('width', (progress / 5) + '%'); | |||
$('#progress-' + fileID).children('.progress-bar').html(progress + '% Loaded'); | |||
} | |||
} | |||
// Start async read | |||
var blob = file.slice(0, file.size); | |||
reader.readAsArrayBuffer(blob); | |||
} | |||
function uploadFile(data, key, iv, filetype, filename) | |||
function uploadFile(data, key, iv, filetype, fileID) | |||
{ | |||
$("#key").val(key); | |||
$("#iv").val(iv); | |||
$('#key-' + fileID).val(key); | |||
var blob = new Blob([data]); | |||
// Now we need to upload the file | |||
var fd = new FormData(); | |||
@@ -150,51 +174,61 @@ function uploadFile(data, key, iv, filetype, filename) | |||
fd.append('__RequestVerificationToken', $('#__AjaxAntiForgeryForm input[name=__RequestVerificationToken]').val()); | |||
var xhr = new XMLHttpRequest(); | |||
xhr.upload.addEventListener("progress", uploadProgress.bind(null, filename), false); | |||
xhr.addEventListener("load", uploadComplete.bind(null, filename), false); | |||
xhr.addEventListener("error", uploadFailed, false); | |||
xhr.addEventListener("abort", uploadCanceled, false); | |||
xhr.upload.addEventListener("progress", uploadProgress.bind(null, fileID), false); | |||
xhr.addEventListener("load", uploadComplete.bind(null, fileID, key), false); | |||
xhr.addEventListener("error", uploadFailed.bind(null, fileID), false); | |||
xhr.addEventListener("abort", uploadCanceled.bind(null, fileID), false); | |||
xhr.open("POST", uploadFileURL); | |||
xhr.send(fd); | |||
} | |||
function uploadProgress(filename, evt) { | |||
var shortName = filename.hashCode(); | |||
function uploadProgress(fileID, evt) { | |||
if (evt.lengthComputable) { | |||
var percentComplete = Math.round(evt.loaded * 100 / evt.total); | |||
$(".progress-" + shortName).children('.progress-bar').css('width', (percentComplete * (3 / 5)) + 40 + '%'); | |||
$(".progress-" + shortName).children('.progress-bar').html(percentComplete + '% Uploaded'); | |||
} | |||
else { | |||
document.getElementById('progressNumber').innerHTML = 'unable to compute'; | |||
$('#progress-' + fileID).children('.progress-bar').css('width', (percentComplete * (2 / 5)) + 60 + '%'); | |||
$('#progress-' + fileID).children('.progress-bar').html(percentComplete + '% Uploaded'); | |||
} | |||
} | |||
function uploadComplete(filename, evt) { | |||
function uploadComplete(fileID, key, evt) { | |||
obj = JSON.parse(evt.target.responseText); | |||
var name = obj.result.name; | |||
var fullName = obj.result.url; | |||
var shortName = filename.hashCode(); | |||
$('.progress-' + shortName).children('.progress-bar').css('width', '100%'); | |||
$('.progress-' + shortName).children('.progress-bar').html('Complete'); | |||
$('.links-' + shortName).append(' \ | |||
<div class="col-sm-6"> \ | |||
' + filename + ' \ | |||
</div> \ | |||
<div class="col-sm-3"> \ | |||
<a href="' + fullName + '" target="_blank" class="alert-link">' + fullName + '</a> \ | |||
</div> \ | |||
<div class="col-sm-3"> \ | |||
<button type="button" class="btn btn-default btn-xs generate-delete-link-' + name + '" id="' + name + '">Generate Deletion URL</button> \ | |||
var fullName = decodeURIComponent(obj.result.url); | |||
var keyVar = decodeURIComponent(obj.result.keyVar); | |||
fullName = fullName.replace(keyVar, key); | |||
$('#progress-' + fileID).children('.progress-bar').css('width', '100%'); | |||
$('#progress-' + fileID).children('.progress-bar').html('Complete'); | |||
$('#upload-link-' + fileID).html('<p><a href="' + fullName + '" target="_blank" class="alert-link">' + fullName + '</a></p>'); | |||
$('#link-' + fileID).append(' \ | |||
<div class="panel-footer"> \ | |||
<div class="row"> \ | |||
<div class="col-sm-4 text-center"> \ | |||
<button type="button" class="btn btn-default btn-sm" id="save-key-link-' + fileID + '">Save Key On Server</button> \ | |||
</div> \ | |||
<div class="col-sm-4 text-center"> \ | |||
<button type="button" class="btn btn-default btn-sm" id="generate-delete-link-' + fileID + '">Generate Deletion URL</button> \ | |||
</div> \ | |||
<div class="col-sm-4 text-center"> \ | |||
<button type="button" class="btn btn-default btn-sm" id="remove-link-' + fileID + '">Remove</button> \ | |||
</div> \ | |||
</div> \ | |||
</div> \ | |||
'); | |||
linkUploadDelete('.generate-delete-link-' + name + ''); | |||
linkSaveKey('#save-key-link-' + fileID + '', name, key, fileID); | |||
linkUploadDelete('#generate-delete-link-' + fileID + '', name); | |||
linkRemove('#remove-link-' + fileID + '', fileID); | |||
} | |||
function uploadFailed(evt) { | |||
alert("There was an error attempting to upload the file."); | |||
function uploadFailed(fileID, evt) { | |||
$('#progress-' + fileID).children('.progress-bar').css('width', '100%'); | |||
$("#progress-" + fileID).children('.progress-bar').removeClass('progress-bar-success'); | |||
$("#progress-" + fileID).children('.progress-bar').addClass('progress-bar-danger'); | |||
$('#progress-' + fileID).children('.progress-bar').html('Upload Failed'); | |||
} | |||
function uploadCanceled(evt) { | |||
alert("The upload has been canceled by the user or the browser dropped the connection."); | |||
function uploadCanceled(fileID, evt) { | |||
$('#progress-' + fileID).children('.progress-bar').css('width', '100%'); | |||
$("#progress-" + fileID).children('.progress-bar').removeClass('progress-bar-success'); | |||
$("#progress-" + fileID).children('.progress-bar').addClass('progress-bar-warning'); | |||
$('#progress-' + fileID).children('.progress-bar').html('Upload Canceled'); | |||
} |
@@ -29,10 +29,17 @@ namespace Teknik.Areas.Upload | |||
new { controller = "Upload", action = "Download", url = string.Empty }, | |||
new[] { typeof(Controllers.UploadController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
"Upload.Download.Key", | |||
"dev", | |||
"Upload/{file}/{key}", | |||
new { controller = "Upload", action = "Download", url = string.Empty }, | |||
new[] { typeof(Controllers.UploadController).Namespace } | |||
); | |||
context.MapSubdomainRoute( | |||
"Upload.Delete", | |||
"dev", | |||
"Upload/{url}/{deleteKey}", | |||
"Upload/{file}/{key}", | |||
new { controller = "Upload", action = "Delete", url = string.Empty, deleteKey = string.Empty }, | |||
new[] { typeof(Controllers.UploadController).Namespace } | |||
); | |||
@@ -105,13 +112,15 @@ namespace Teknik.Areas.Upload | |||
"~/Scripts/Dropzone/dropzone.js", | |||
"~/Areas/Upload/Scripts/Upload.js", | |||
"~/Scripts/bootbox/bootbox.min.js", | |||
"~/Scripts/Crypto-js/aes.js", | |||
"~/Scripts/Crypto-js/lib-typedarray.js")); | |||
"~/Scripts/Crypto-js/aes.js")); | |||
BundleTable.Bundles.Add(new ScriptBundle("~/bundles/cryptoWorker").Include( | |||
"~/Areas/Upload/Scripts/EncryptionWorker.js")); | |||
BundleTable.Bundles.Add(new ScriptBundle("~/bundles/crypto").Include( | |||
"~/Scripts/Crypto-js/aes.js", | |||
"~/Scripts/Crypto-js/lib-typedarray.js")); | |||
"~/Scripts/Crypto-js/enc-base64.js", | |||
"~/Scripts/Crypto-js/mode-ctr.js", | |||
"~/Scripts/Crypto-js/lib-typedarrays.js", | |||
"~/Scripts/Crypto-js/pad-nopadding.js")); | |||
// Register Style Bundles | |||
BundleTable.Bundles.Add(new StyleBundle("~/Content/upload").Include( |
@@ -4,9 +4,10 @@ | |||
var encScriptSrc = '@Scripts.Url("~/bundles/cryptoWorker")'; | |||
var aesScriptSrc = '@Scripts.Url("~/bundles/crypto")'; | |||
var generateDeleteKeyURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action= "GenerateDeleteKey" })'; | |||
var saveKeyToServerURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action= "SaveFileKey" })'; | |||
var uploadFileURL = '@Url.SubRouteUrl("upload", "Upload.Action", new { action = "Upload" })'; | |||
var uploadURL = '@Url.SubRouteUrl("upload", "Upload.Download")'; | |||
var maxUploadSize = @(Model.Config.UploadConfig.MaxUploadSize / 100000); | |||
var chunkSize = @(Model.Config.UploadConfig.ChunkSize); | |||
</script> | |||
@Styles.Render("~/Content/upload") | |||
@@ -15,8 +16,6 @@ | |||
<div class="row text-center"> | |||
<form action="@Url.SubRouteUrl("upload", "Upload.Action", new { action = "Upload" })" class="dropzone" id="TeknikUpload" name="TeknikUpload" enctype="multipart/form-data"> | |||
@Html.AntiForgeryToken() | |||
<input name="key" id="key" type="hidden" /> | |||
<input name="iv" id="iv" type="hidden" /> | |||
<div class="dz-message text-center" id="upload_message"> | |||
<div class="row"> | |||
<div class="col-sm-12"> |
@@ -15,6 +15,8 @@ namespace Teknik.Configuration | |||
public string FileExtension { get; set; } | |||
public int UrlLength { get; set; } | |||
public bool IncludeExtension { get; set; } | |||
// The size of the chunk that the file will be encrypted/decrypted in (bytes) | |||
public int ChunkSize { get; set; } | |||
public UploadConfig() | |||
{ | |||
@@ -28,6 +30,7 @@ namespace Teknik.Configuration | |||
FileExtension = "enc"; | |||
UrlLength = 6; | |||
IncludeExtension = true; | |||
ChunkSize = 1024; | |||
} | |||
} | |||
} |
@@ -0,0 +1,109 @@ | |||
/* | |||
CryptoJS v3.1.2 | |||
code.google.com/p/crypto-js | |||
(c) 2009-2013 by Jeff Mott. All rights reserved. | |||
code.google.com/p/crypto-js/wiki/License | |||
*/ | |||
(function () { | |||
// Shortcuts | |||
var C = CryptoJS; | |||
var C_lib = C.lib; | |||
var WordArray = C_lib.WordArray; | |||
var C_enc = C.enc; | |||
/** | |||
* Base64 encoding strategy. | |||
*/ | |||
var Base64 = C_enc.Base64 = { | |||
/** | |||
* Converts a word array to a Base64 string. | |||
* | |||
* @param {WordArray} wordArray The word array. | |||
* | |||
* @return {string} The Base64 string. | |||
* | |||
* @static | |||
* | |||
* @example | |||
* | |||
* var base64String = CryptoJS.enc.Base64.stringify(wordArray); | |||
*/ | |||
stringify: function (wordArray) { | |||
// Shortcuts | |||
var words = wordArray.words; | |||
var sigBytes = wordArray.sigBytes; | |||
var map = this._map; | |||
// Clamp excess bits | |||
wordArray.clamp(); | |||
// Convert | |||
var base64Chars = []; | |||
for (var i = 0; i < sigBytes; i += 3) { | |||
var byte1 = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; | |||
var byte2 = (words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; | |||
var byte3 = (words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; | |||
var triplet = (byte1 << 16) | (byte2 << 8) | byte3; | |||
for (var j = 0; (j < 4) && (i + j * 0.75 < sigBytes); j++) { | |||
base64Chars.push(map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); | |||
} | |||
} | |||
// Add padding | |||
var paddingChar = map.charAt(64); | |||
if (paddingChar) { | |||
while (base64Chars.length % 4) { | |||
base64Chars.push(paddingChar); | |||
} | |||
} | |||
return base64Chars.join(''); | |||
}, | |||
/** | |||
* Converts a Base64 string to a word array. | |||
* | |||
* @param {string} base64Str The Base64 string. | |||
* | |||
* @return {WordArray} The word array. | |||
* | |||
* @static | |||
* | |||
* @example | |||
* | |||
* var wordArray = CryptoJS.enc.Base64.parse(base64String); | |||
*/ | |||
parse: function (base64Str) { | |||
// Shortcuts | |||
var base64StrLength = base64Str.length; | |||
var map = this._map; | |||
// Ignore padding | |||
var paddingChar = map.charAt(64); | |||
if (paddingChar) { | |||
var paddingIndex = base64Str.indexOf(paddingChar); | |||
if (paddingIndex != -1) { | |||
base64StrLength = paddingIndex; | |||
} | |||
} | |||
// Convert | |||
var words = []; | |||
var nBytes = 0; | |||
for (var i = 0; i < base64StrLength; i++) { | |||
if (i % 4) { | |||
var bits1 = map.indexOf(base64Str.charAt(i - 1)) << ((i % 4) * 2); | |||
var bits2 = map.indexOf(base64Str.charAt(i)) >>> (6 - (i % 4) * 2); | |||
words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); | |||
nBytes++; | |||
} | |||
} | |||
return WordArray.create(words, nBytes); | |||
}, | |||
_map: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' | |||
}; | |||
}()); |
@@ -0,0 +1,135 @@ | |||
/* | |||
CryptoJS v3.1.2 | |||
code.google.com/p/crypto-js | |||
(c) 2009-2013 by Jeff Mott. All rights reserved. | |||
code.google.com/p/crypto-js/wiki/License | |||
*/ | |||
(function () { | |||
// Shortcuts | |||
var C = CryptoJS; | |||
var C_lib = C.lib; | |||
var WordArray = C_lib.WordArray; | |||
var C_enc = C.enc; | |||
/** | |||
* UTF-16 BE encoding strategy. | |||
*/ | |||
var Utf16BE = C_enc.Utf16 = C_enc.Utf16BE = { | |||
/** | |||
* Converts a word array to a UTF-16 BE string. | |||
* | |||
* @param {WordArray} wordArray The word array. | |||
* | |||
* @return {string} The UTF-16 BE string. | |||
* | |||
* @static | |||
* | |||
* @example | |||
* | |||
* var utf16String = CryptoJS.enc.Utf16.stringify(wordArray); | |||
*/ | |||
stringify: function (wordArray) { | |||
// Shortcuts | |||
var words = wordArray.words; | |||
var sigBytes = wordArray.sigBytes; | |||
// Convert | |||
var utf16Chars = []; | |||
for (var i = 0; i < sigBytes; i += 2) { | |||
var codePoint = (words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff; | |||
utf16Chars.push(String.fromCharCode(codePoint)); | |||
} | |||
return utf16Chars.join(''); | |||
}, | |||
/** | |||
* Converts a UTF-16 BE string to a word array. | |||
* | |||
* @param {string} utf16Str The UTF-16 BE string. | |||
* | |||
* @return {WordArray} The word array. | |||
* | |||
* @static | |||
* | |||
* @example | |||
* | |||
* var wordArray = CryptoJS.enc.Utf16.parse(utf16String); | |||
*/ | |||
parse: function (utf16Str) { | |||
// Shortcut | |||
var utf16StrLength = utf16Str.length; | |||
// Convert | |||
var words = []; | |||
for (var i = 0; i < utf16StrLength; i++) { | |||
words[i >>> 1] |= utf16Str.charCodeAt(i) << (16 - (i % 2) * 16); | |||
} | |||
return WordArray.create(words, utf16StrLength * 2); | |||
} | |||
}; | |||
/** | |||
* UTF-16 LE encoding strategy. | |||
*/ | |||
C_enc.Utf16LE = { | |||
/** | |||
* Converts a word array to a UTF-16 LE string. | |||
* | |||
* @param {WordArray} wordArray The word array. | |||
* | |||
* @return {string} The UTF-16 LE string. | |||
* | |||
* @static | |||
* | |||
* @example | |||
* | |||
* var utf16Str = CryptoJS.enc.Utf16LE.stringify(wordArray); | |||
*/ | |||
stringify: function (wordArray) { | |||
// Shortcuts | |||
var words = wordArray.words; | |||
var sigBytes = wordArray.sigBytes; | |||
// Convert | |||
var utf16Chars = []; | |||
for (var i = 0; i < sigBytes; i += 2) { | |||
var codePoint = swapEndian((words[i >>> 2] >>> (16 - (i % 4) * 8)) & 0xffff); | |||
utf16Chars.push(String.fromCharCode(codePoint)); | |||
} | |||
return utf16Chars.join(''); | |||
}, | |||
/** | |||
* Converts a UTF-16 LE string to a word array. | |||
* | |||
* @param {string} utf16Str The UTF-16 LE string. | |||
* | |||
* @return {WordArray} The word array. | |||
* | |||
* @static | |||
* | |||
* @example | |||
* | |||
* var wordArray = CryptoJS.enc.Utf16LE.parse(utf16Str); | |||
*/ | |||
parse: function (utf16Str) { | |||
// Shortcut | |||
var utf16StrLength = utf16Str.length; | |||
// Convert | |||
var words = []; | |||
for (var i = 0; i < utf16StrLength; i++) { | |||
words[i >>> 1] |= swapEndian(utf16Str.charCodeAt(i) << (16 - (i % 2) * 16)); | |||
} | |||
return WordArray.create(words, utf16StrLength * 2); | |||
} | |||
}; | |||
function swapEndian(word) { | |||
return ((word << 8) & 0xff00ff00) | ((word >>> 8) & 0x00ff00ff); | |||
} | |||
}()); |
@@ -0,0 +1,44 @@ | |||
/* | |||
CryptoJS v3.1.2 | |||
code.google.com/p/crypto-js | |||
(c) 2009-2013 by Jeff Mott. All rights reserved. | |||
code.google.com/p/crypto-js/wiki/License | |||
*/ | |||
/** | |||
* Counter block mode. | |||
*/ | |||
CryptoJS.mode.CTR = (function () { | |||
var CTR = CryptoJS.lib.BlockCipherMode.extend(); | |||
var Encryptor = CTR.Encryptor = CTR.extend({ | |||
processBlock: function (words, offset) { | |||
// Shortcuts | |||
var cipher = this._cipher | |||
var blockSize = cipher.blockSize; | |||
var iv = this._iv; | |||
var counter = this._counter; | |||
// Generate keystream | |||
if (iv) { | |||
counter = this._counter = iv.slice(0); | |||
// Remove IV for subsequent blocks | |||
this._iv = undefined; | |||
} | |||
var keystream = counter.slice(0); | |||
cipher.encryptBlock(keystream, 0); | |||
// Increment counter | |||
counter[blockSize - 1] = (counter[blockSize - 1] + 1) | 0 | |||
// Encrypt | |||
for (var i = 0; i < blockSize; i++) { | |||
words[offset + i] ^= keystream[i]; | |||
} | |||
} | |||
}); | |||
CTR.Decryptor = Encryptor; | |||
return CTR; | |||
}()); |
@@ -0,0 +1,16 @@ | |||
/* | |||
CryptoJS v3.1.2 | |||
code.google.com/p/crypto-js | |||
(c) 2009-2013 by Jeff Mott. All rights reserved. | |||
code.google.com/p/crypto-js/wiki/License | |||
*/ | |||
/** | |||
* A noop padding strategy. | |||
*/ | |||
CryptoJS.pad.NoPadding = { | |||
pad: function () { | |||
}, | |||
unpad: function () { | |||
} | |||
}; |
@@ -289,7 +289,11 @@ | |||
<Content Include="Scripts\bootstrap.js" /> | |||
<Content Include="Scripts\bootstrap.min.js" /> | |||
<Content Include="Scripts\common.js" /> | |||
<Content Include="Scripts\Crypto-js\enc-base64.js" /> | |||
<Content Include="Scripts\Crypto-js\enc-utf16.js" /> | |||
<Content Include="Scripts\Crypto-js\lib-typedarrays.js" /> | |||
<Content Include="Scripts\Crypto-js\mode-ctr.js" /> | |||
<Content Include="Scripts\Crypto-js\pad-nopadding.js" /> | |||
<Content Include="Scripts\Dropzone\dropzone.js" /> | |||
<Content Include="Scripts\jquery-2.1.4.js" /> | |||
<Content Include="Scripts\jquery-2.1.4.min.js" /> |