CyberChef/src/js/operations/Compress.js

567 lines
17 KiB
JavaScript
Raw Normal View History

2016-11-28 11:42:58 +01:00
/* globals Zlib, bzip2 */
/**
* Compression operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
var Compress = {
/**
* @constant
* @default
*/
COMPRESSION_TYPE: ["Dynamic Huffman Coding", "Fixed Huffman Coding", "None (Store)"],
/**
* @constant
* @default
*/
INFLATE_BUFFER_TYPE: ["Adaptive", "Block"],
/**
* @constant
* @default
*/
COMPRESSION_METHOD: ["Deflate", "None (Store)"],
/**
* @constant
* @default
*/
OS: ["MSDOS", "Unix", "Macintosh"],
/**
* @constant
* @default
*/
RAW_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding" : Zlib.RawDeflate.CompressionType.FIXED,
"Dynamic Huffman Coding" : Zlib.RawDeflate.CompressionType.DYNAMIC,
"None (Store)" : Zlib.RawDeflate.CompressionType.NONE,
},
/**
* Raw Deflate operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runRawDeflate: function(input, args) {
2016-11-28 11:42:58 +01:00
var deflate = new Zlib.RawDeflate(input, {
compressionType: Compress.RAW_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
},
/**
* @constant
* @default
*/
INFLATE_INDEX: 0,
/**
* @constant
* @default
*/
INFLATE_BUFFER_SIZE: 0,
/**
* @constant
* @default
*/
INFLATE_RESIZE: false,
/**
* @constant
* @default
*/
INFLATE_VERIFY: false,
/**
* @constant
* @default
*/
RAW_BUFFER_TYPE_LOOKUP: {
"Adaptive" : Zlib.RawInflate.BufferType.ADAPTIVE,
"Block" : Zlib.RawInflate.BufferType.BLOCK,
2016-11-28 11:42:58 +01:00
},
/**
* Raw Inflate operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runRawInflate: function(input, args) {
2016-11-28 11:42:58 +01:00
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
2016-11-28 11:42:58 +01:00
var inflate = new Zlib.RawInflate(input, {
index: args[0],
bufferSize: args[1],
bufferType: Compress.RAW_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
}),
result = Array.prototype.slice.call(inflate.decompress());
// Raw Inflate somethimes messes up and returns nonsense like this:
// ]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]....]...
// e.g. Input data of [8b, 1d, dc, 44]
// Look for the first two square brackets:
2016-12-14 17:39:17 +01:00
if (result.length > 158 && result[0] === 93 && result[5] === 93) {
2016-11-28 11:42:58 +01:00
// If the first two square brackets are there, check that the others
// are also there. If they are, throw an error. If not, continue.
var valid = false;
for (var i = 0; i < 155; i += 5) {
2016-12-14 17:39:17 +01:00
if (result[i] !== 93) {
2016-11-28 11:42:58 +01:00
valid = true;
}
}
if (!valid) {
throw "Error: Unable to inflate data";
}
}
// Trust me, this is the easiest way...
return result;
},
/**
* @constant
* @default
*/
ZLIB_COMPRESSION_TYPE_LOOKUP: {
"Fixed Huffman Coding" : Zlib.Deflate.CompressionType.FIXED,
"Dynamic Huffman Coding" : Zlib.Deflate.CompressionType.DYNAMIC,
"None (Store)" : Zlib.Deflate.CompressionType.NONE,
},
/**
* Zlib Deflate operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runZlibDeflate: function(input, args) {
2016-11-28 11:42:58 +01:00
var deflate = new Zlib.Deflate(input, {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
});
return Array.prototype.slice.call(deflate.compress());
},
/**
* @constant
* @default
*/
ZLIB_BUFFER_TYPE_LOOKUP: {
"Adaptive" : Zlib.Inflate.BufferType.ADAPTIVE,
"Block" : Zlib.Inflate.BufferType.BLOCK,
},
/**
* Zlib Inflate operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runZlibInflate: function(input, args) {
2016-11-28 11:42:58 +01:00
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
2016-11-28 11:42:58 +01:00
var inflate = new Zlib.Inflate(input, {
2016-12-14 17:39:17 +01:00
index: args[0],
bufferSize: args[1],
bufferType: Compress.ZLIB_BUFFER_TYPE_LOOKUP[args[2]],
resize: args[3],
verify: args[4]
});
2016-11-28 11:42:58 +01:00
return Array.prototype.slice.call(inflate.decompress());
},
/**
* @constant
* @default
*/
GZIP_CHECKSUM: false,
/**
* Gzip operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runGzip: function(input, args) {
2016-11-28 11:42:58 +01:00
var filename = args[1],
comment = args[2],
options = {
deflateOptions: {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[0]]
},
flags: {
fhcrc: args[3]
}
};
if (filename.length) {
options.flags.fname = true;
options.filename = filename;
}
if (comment.length) {
options.flags.fcommenct = true;
options.comment = comment;
}
var gzip = new Zlib.Gzip(input, options);
return Array.prototype.slice.call(gzip.compress());
},
/**
* Gunzip operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runGunzip: function(input, args) {
2016-11-28 11:42:58 +01:00
// Deal with character encoding issues
input = Utils.strToByteArray(Utils.byteArrayToUtf8(input));
2016-11-28 11:42:58 +01:00
var gunzip = new Zlib.Gunzip(input);
return Array.prototype.slice.call(gunzip.decompress());
},
/**
* @constant
* @default
*/
PKZIP_FILENAME: "file.txt",
/**
* @constant
* @default
*/
ZIP_COMPRESSION_METHOD_LOOKUP: {
"Deflate" : Zlib.Zip.CompressionMethod.DEFLATE,
"None (Store)" : Zlib.Zip.CompressionMethod.STORE
},
/**
* @constant
* @default
*/
ZIP_OS_LOOKUP: {
"MSDOS" : Zlib.Zip.OperatingSystem.MSDOS,
"Unix" : Zlib.Zip.OperatingSystem.UNIX,
"Macintosh" : Zlib.Zip.OperatingSystem.MACINTOSH
},
/**
* Zip operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {byteArray}
2016-11-28 11:42:58 +01:00
*/
runPkzip: function(input, args) {
var password = Utils.strToByteArray(args[2]),
2016-11-28 11:42:58 +01:00
options = {
filename: Utils.strToByteArray(args[0]),
comment: Utils.strToByteArray(args[1]),
2016-11-28 11:42:58 +01:00
compressionMethod: Compress.ZIP_COMPRESSION_METHOD_LOOKUP[args[3]],
os: Compress.ZIP_OS_LOOKUP[args[4]],
deflateOption: {
compressionType: Compress.ZLIB_COMPRESSION_TYPE_LOOKUP[args[5]]
},
},
zip = new Zlib.Zip();
if (password.length)
zip.setPassword(password);
zip.addFile(input, options);
return Array.prototype.slice.call(zip.compress());
},
/**
* @constant
* @default
*/
PKUNZIP_VERIFY: false,
/**
* Unzip operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {string}
*/
runPkunzip: function(input, args) {
2016-11-28 11:42:58 +01:00
var options = {
password: Utils.strToByteArray(args[0]),
2016-11-28 11:42:58 +01:00
verify: args[1]
},
file = "",
unzip = new Zlib.Unzip(input, options),
filenames = unzip.getFilenames(),
output = "<div style='padding: 5px;'>" + filenames.length + " file(s) found</div>\n";
output += "<div class='panel-group' id='zip-accordion' role='tablist' aria-multiselectable='true'>";
window.uzip = unzip;
for (var i = 0; i < filenames.length; i++) {
file = Utils.byteArrayToUtf8(unzip.decompress(filenames[i]));
2016-11-28 11:42:58 +01:00
output += "<div class='panel panel-default'>" +
"<div class='panel-heading' role='tab' id='heading" + i + "'>" +
"<h4 class='panel-title'>" +
"<a class='collapsed' role='button' data-toggle='collapse' data-parent='#zip-accordion' href='#collapse" + i +
"' aria-expanded='true' aria-controls='collapse" + i + "'>" +
filenames[i] + "<span class='pull-right'>" + file.length.toLocaleString() + " bytes</span></a></h4></div>" +
"<div id='collapse" + i + "' class='panel-collapse collapse' role='tabpanel' aria-labelledby='heading" + i + "'>" +
"<div class='panel-body'>" +
Utils.escapeHtml(file) + "</div></div></div>";
2016-11-28 11:42:58 +01:00
}
return output + "</div>";
},
/**
* Bzip2 Decompress operation.
*
* @param {byteArray} input
2016-11-28 11:42:58 +01:00
* @param {Object[]} args
* @returns {string}
*/
runBzip2Decompress: function(input, args) {
2016-11-28 11:42:58 +01:00
var compressed = new Uint8Array(input),
bzip2Reader,
2016-11-28 11:42:58 +01:00
plain = "";
bzip2Reader = bzip2.array(compressed);
plain = bzip2.simple(bzip2Reader);
2016-11-28 11:42:58 +01:00
return plain;
},
/**
* @constant
* @default
*/
TAR_FILENAME: "file.txt",
/**
* Tar unpack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runTar: function(input, args) {
2017-02-09 05:35:54 +01:00
var zeroFillBytes = function(string, bytes) {
var paddedString = new Array(bytes);
paddedString.fill(0);
string = string.toString().slice(0, bytes);
Array.prototype.map.call(string, function(chr, i) {
paddedString[i] = chr;
});
return paddedString;
};
var padLeft = function(string, length, padChar) {
while(string.length < length) {
string = padChar + string;
}
return string;
};
var Tarball = function() {
this.bytes = new Array(512);
this.position = 0;
};
Tarball.prototype.writeBytes = function(bytes) {
var self = this;
if(this.bytes.length - this.position < 512) {
var filler = new Array(512);
filler.fill(0);
this.bytes = this.bytes.concat(filler);
}
Array.prototype.forEach.call(bytes, function(b, i) {
if(typeof b.charCodeAt !== "undefined") {
b = b.charCodeAt();
}
self.bytes[self.position] = b;
self.position += 1;
});
};
Tarball.prototype.writeEndBlocks = function() {
2017-02-09 05:52:59 +01:00
var filler = new Array(512 * 2);
2017-02-09 05:35:54 +01:00
filler.fill(0);
this.writeBytes(filler);
};
var fileSize = padLeft(input.length.toString(8), 11, "0");
var currentUnixTimestamp = Math.floor(Date.now() / 1000);
var lastModTime = padLeft(currentUnixTimestamp.toString(8), 11, "0");
var file = {
fileName: zeroFillBytes(args[0], 100),
fileMode: zeroFillBytes("0000664", 8),
ownerUID: zeroFillBytes("0", 8),
ownerGID: zeroFillBytes("0", 8),
size: zeroFillBytes(fileSize, 12),
lastModTime: zeroFillBytes(lastModTime, 12),
checksum: " ",
type: "0",
linkedFileName: zeroFillBytes("", 100),
USTARFormat: zeroFillBytes("ustar", 6),
version: "00",
ownerUserName: zeroFillBytes("", 32),
ownerGroupName: zeroFillBytes("", 32),
deviceMajor: zeroFillBytes("", 8),
deviceMinor: zeroFillBytes("", 8),
fileNamePrefix: zeroFillBytes("", 155),
};
var checksum = 0;
for(var key in file) {
var bytes = file[key];
Array.prototype.forEach.call(bytes, function(b) {
if(typeof b.charCodeAt !== "undefined") {
checksum += b.charCodeAt();
} else {
checksum += b;
}
});
}
checksum = zeroFillBytes(padLeft(checksum.toString(8), 7, "0"), 8);
file.checksum = checksum;
var tarball = new Tarball();
tarball.writeBytes(file.fileName);
tarball.writeBytes(file.fileMode);
tarball.writeBytes(file.ownerUID);
tarball.writeBytes(file.ownerGID);
tarball.writeBytes(file.size);
tarball.writeBytes(file.lastModTime);
tarball.writeBytes(file.checksum);
tarball.writeBytes(file.type);
tarball.writeBytes(file.linkedFileName);
tarball.writeBytes(file.USTARFormat);
tarball.writeBytes(file.version);
tarball.writeBytes(file.ownerUserName);
tarball.writeBytes(file.ownerGroupName);
tarball.writeBytes(file.deviceMajor);
tarball.writeBytes(file.deviceMinor);
tarball.writeBytes(file.fileNamePrefix);
tarball.writeBytes(zeroFillBytes("", 12));
tarball.writeBytes(input);
tarball.writeEndBlocks();
return tarball.bytes;
},
/**
* Untar unpack operation.
*
* @author tlwr [toby@toby.codes]
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runUntar: function(input, args) {
var Stream = function(input) {
this.bytes = input;
this.position = 0;
};
Stream.prototype.readString = function(numBytes) {
var result = "";
for(var i = this.position; i < this.position + numBytes; i++) {
var currentByte = this.bytes[i];
if(currentByte === 0) break;
result += String.fromCharCode(currentByte);
}
this.position += numBytes;
return result;
};
Stream.prototype.readInt = function(numBytes, base) {
var string = this.readString(numBytes);
return parseInt(string, base);
};
Stream.prototype.hasMore = function() {
return this.position < this.bytes.length;
};
var stream = new Stream(input),
files = [];
while(stream.hasMore()) {
var dataPosition = stream.position + 512;
var file = {
fileName: stream.readString(100),
fileMode: stream.readString(8),
ownerUID: stream.readString(8),
ownerGID: stream.readString(8),
size: parseInt(stream.readString(12), 8), // Octal
lastModTime: new Date(1000 * stream.readInt(12, 8)), // Octal
checksum: stream.readString(8),
type: stream.readString(1),
linkedFileName: stream.readString(100),
USTARFormat: stream.readString(6).indexOf("ustar") >= 0,
};
if(file.USTARFormat) {
file.version = stream.readString(2);
file.ownerUserName = stream.readString(32);
file.ownerGroupName = stream.readString(32);
file.deviceMajor = stream.readString(8);
file.deviceMinor = stream.readString(8);
file.filenamePrefix = stream.readString(155);
}
stream.position = dataPosition;
if(file.type === "0") {
// File
files.push(file);
var endPosition = stream.position + file.size;
if(file.size % 512 !== 0) {
endPosition += 512 - (file.size % 512);
}
file.contents = "";
while(stream.position < endPosition) {
file.contents += stream.readString(512);
}
} else if(file.type === "5") {
// Directory
files.push(file);
} else {
// Symlink or empty bytes
}
}
return Utils.displayFilesAsHTML(files);
},
2016-11-28 11:42:58 +01:00
};