1
0
mirror of synced 2025-01-31 12:03:42 +01:00

Merge remote-tracking branch 'upstream/master'

This commit is contained in:
toby 2017-02-09 11:31:15 -05:00
commit d26cf4ca9d
60 changed files with 1191 additions and 876 deletions

37
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,37 @@
# Contributing
Take a look through the [Wiki pages](https://github.com/gchq/CyberChef/wiki) for guides on [compiling CyberChef](https://github.com/gchq/CyberChef/wiki/Getting-started) and [adding new operations](https://github.com/gchq/CyberChef/wiki/Adding-a-new-operation).
There are lots of opportunities to contribute to CyberChef. If you want ideas, take a look at any [Issues](https://github.com/gchq/CyberChef/issues) tagged with '[help wanted](https://github.com/gchq/CyberChef/labels/help%20wanted)'.
Before your contributions can be accepted, you must:
- Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0)
- Push your changes to your fork.
- Submit a pull request.
## Coding conventions
* Indentation: Each block should consist of 4 spaces
* Object/namespace identifiers: CamelCase
* Function/variable names: camelCase
* Constants: UNDERSCORE_UPPER_CASE
* Source code encoding: UTF-8 (without BOM)
* All source files must end with a newline
* Line endings: UNIX style (\n)
## Design Principals
1. If at all possible, all operations and features should be client-side and not rely on connections to an external server. This increases the utility of CyberChef on closed networks and in virtual machines that are not connected to the Internet. Calls to external APIs may be accepted if there is no other option, but not for critical components.
2. Latency should be kept to a minimum to enhance the user experience. This means that all operation code should sit on the client, rather than being loaded dynamically from a server.
3. Use Vanilla JS if at all possible to reduce the number of libraries required and relied upon. Frameworks like jQuery, although included, should not be used unless absolutely necessary.
4. Minimise the use of large libraries, especially for niche operations that won't be used very often - these will be downloaded by everyone using the app, whether they use that operation or not (due to principal 2).
With these principals in mind, any changes or additions to CyberChef should keep it:
- Standalone
- Efficient
- As small as possible

25
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,25 @@
<!-- Prefix the title above with one of the following: -->
<!-- Bug report: -->
<!-- Operation request: -->
<!-- Feature request: -->
<!-- Misc: -->
### Summary
<!-- If you're describing a bug, tell us what's wrong -->
<!-- If you're suggesting a change/improvement, tell us what it is and how it should work -->
### Example
<!-- If describing a bug, tell us what happens instead of the expected behavior -->
<!-- Include a link that triggers the bug if possible -->
<!-- If you are requesting a new operation, include example input and output -->
### Possible solutions
<!-- Not obligatory, but suggest a fix/reason for the bug, or ideas for how to -->
<!-- implement the addition or change, including links to relevant resources -->
### Environment
<!-- Include any relevant details about the environment you experienced the bug in -->
<!-- This information is displayed in the About/Support pane -->
* CyberChef compile time:
* User-Agent:
* [Link to reproduce]()

View File

@ -181,6 +181,7 @@ module.exports = function(grunt) {
var templateOptions = {
data: {
compileTime: grunt.template.today("dd/mm/yyyy HH:MM:ss") + " UTC",
compileMsg: grunt.option("compile-msg") || grunt.option("msg") || "",
codebaseStats: grunt.file.read("src/static/stats.txt").split("\n").join("<br>")
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -274,7 +274,7 @@
</div>
<div class="modal-body">
<img class="about-img-left" src="images/cyberchef-128x128.png" />
<p class="subtext">Compile time: <%= grunt.template.today("dd/mm/yyyy HH:MM:ss") %> UTC</p>
<p class="subtext">Compile time: <%= compileTime %></p>
<p>&copy Crown Copyright 2016.</p>
<p>Licenced under the Apache Licence, Version 2.0.</p>
<br>
@ -285,6 +285,10 @@
<img src='images/help-16x16.png' />
FAQs
</a></li>
<li role='presentation'><a href='#report-bug' aria-controls='messages' role='tab' data-toggle='tab'>
<img src='images/bug-16x16.png' />
Report a bug
</a></li>
<li role='presentation'><a href='#stats' aria-controls='messages' role='tab' data-toggle='tab'>
<img src='images/stats-16x16.png' />
Stats
@ -333,6 +337,14 @@
<p><a href='?recipe=%5B%7B"op"%3A"Fork"%2C"args"%3A%5B"%5C%5Cn"%2C"%5C%5Cn"%5D%7D%2C%7B"op"%3A"From%20UNIX%20Timestamp"%2C"args"%3A%5B"Seconds%20(s)"%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA%3D%3D'>Click here</a> for an example.</p>
</div>
</div>
<div role='tabpanel' class='tab-pane' id='report-bug'>
<br>
<p>If you find a bug in CyberChef, please raise an issue in our GitHub repository explaining it in as much detail as possible. Copy and include the following information if relevant.</p>
<br>
<pre id='report-bug-info'></pre>
<br>
<a class="btn btn-primary" href="https://github.com/gchq/CyberChef/issues/new" role="button">Raise issue on GitHub</a>
</div>
<div role='tabpanel' class='tab-pane' id='stats'>
<br>
<p>If you're a nerd like me, you might find statistics really fun! Here's some about the CyberChef code base:</p>

View File

@ -45,9 +45,7 @@
"comma-spacing": "error",
"comma-style": "error",
"computed-property-spacing": "error",
"no-trailing-spaces": ["warn", {
"skipBlankLines": true
}],
"no-trailing-spaces": "warn",
"eol-last": "error",
"func-call-spacing": "error",
"indent": ["error", 4, {
@ -62,7 +60,27 @@
"properties": "always"
}],
"semi": ["error", "always"],
"unicode-bom": "error"
"unicode-bom": "error",
"require-jsdoc": ["error", {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true,
"ArrowFunctionExpression": true
}
}],
"keyword-spacing": ["error", {
"before": true,
"after": true
}],
"no-multiple-empty-lines": ["warn", {
"max": 2,
"maxEOF": 1,
"maxBOF": 0
}],
"no-whitespace-before-property": "error",
"operator-linebreak": ["error", "after"],
"space-in-parens": "error"
},
"globals": {
/* core/* */

View File

@ -84,6 +84,9 @@ var Categories = [
"Vigenère Decode",
"To Morse Code",
"From Morse Code",
"Affine Cipher Encode",
"Affine Cipher Decode",
"Atbash Cipher",
"Substitute",
"Derive PBKDF2 key",
"Derive EVP key",

View File

@ -1360,6 +1360,55 @@ var OperationConfig = {
}
]
},
"Affine Cipher Encode": {
description: "The Affine cipher is a type of monoalphabetic substitution cipher, wherein each letter in an alphabet is mapped to its numeric equivalent, encrypted using simple mathematical function, <code>(ax + b) % 26</code>, and converted back to a letter.",
run: Cipher.runAffineEnc,
highlight: true,
highlightReverse: true,
inputType: "string",
outputType: "string",
args: [
{
name: "a",
type: "number",
value: Cipher.AFFINE_A
},
{
name: "b",
type: "number",
value: Cipher.AFFINE_B
}
]
},
"Affine Cipher Decode": {
description: "The Affine cipher is a type of monoalphabetic substitution cipher. To decrypt, each letter in an alphabet is mapped to its numeric equivalent, decrypted by a mathematical function, and converted back to a letter.",
run: Cipher.runAffineDec,
highlight: true,
highlightReverse: true,
inputType: "string",
outputType: "string",
args: [
{
name: "a",
type: "number",
value: Cipher.AFFINE_A
},
{
name: "b",
type: "number",
value: Cipher.AFFINE_B
}
]
},
"Atbash Cipher": {
description: "Atbash is a mono-alphabetic substitution cipher originally used to encode the Hebrew alphabet. It has been modified here for use with the Latin alphabet.",
run: Cipher.runAtbash,
highlight: true,
highlightReverse: true,
inputType: "string",
outputType: "string",
args: []
},
"Rotate right": {
description: "Rotates each byte to the right by the number of bits specified. Currently only supports 8-bit values.",
run: Rotate.runRotr,

View File

@ -113,7 +113,7 @@ Chef.prototype.silentBake = function(recipeConfig) {
try {
recipe.execute(dish);
} catch(err) {
} catch (err) {
// Suppress all errors
}
return new Date().getTime() - startTime;

View File

@ -70,7 +70,7 @@ var FlowControl = {
var dish = new Dish(inputs[i], inputType);
try {
progress = recipe.execute(dish, 0);
} catch(err) {
} catch (err) {
if (!ignoreErrors) {
throw err;
}

View File

@ -922,8 +922,8 @@ var Utils = {
* @returns {Object}
*/
extend: function(a, b){
for(var key in b)
if(b.hasOwnProperty(key))
for (var key in b)
if (b.hasOwnProperty(key))
a[key] = b[key];
return a;
},
@ -990,6 +990,52 @@ var Utils = {
},
* Actual modulo function, since % is actually the remainder function in JS.
*
* @author Matt C [matt@artemisbot.pw]
* @param {number} x
* @param {number} y
* @returns {number}
*/
mod: function (x, y) {
return ((x % y) + y) % y;
},
/**
* Finds the greatest common divisor of two numbers.
*
* @author Matt C [matt@artemisbot.pw]
* @param {number} x
* @param {number} y
* @returns {number}
*/
gcd: function(x, y) {
if (!y) {
return x;
}
return Utils.gcd(y, x % y);
},
/**
* Finds the modular inverse of two values.
*
* @author Matt C [matt@artemisbot.pw]
* @param {number} x
* @param {number} y
* @returns {number}
*/
modInv: function(x, y) {
x %= y;
for (var i = 1; i < y; i++) {
if ((x * i) % 26 === 1) {
return i;
}
}
},
/**
* A mapping of names of delimiter characters to their symbols.
* @constant
@ -1183,7 +1229,6 @@ String.prototype.count = function(chr) {
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Library overrides ///////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -153,7 +153,7 @@ var Checksum = {
var csum = 0;
for (var i = 0; i < input.length; i++) {
if(i % 2 === 0) {
if (i % 2 === 0) {
csum += (input[i] << 8);
} else {
csum += input[i];

View File

@ -460,7 +460,7 @@ var Cipher = {
msgIndex = alphabet.indexOf(input[i]);
// Subtract indexes from each other, add 26 just in case the value is negative,
// modulo to remove if neccessary
output += alphabet[(msgIndex - keyIndex + alphabet.length ) % 26];
output += alphabet[(msgIndex - keyIndex + alphabet.length) % 26];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
chr = key[(i - fail) % key.length].toLowerCase();
keyIndex = alphabet.indexOf(chr);
@ -476,6 +476,106 @@ var Cipher = {
},
/**
* @constant
* @default
*/
AFFINE_A: 1,
/**
* @constant
* @default
*/
AFFINE_B: 0,
/**
* Affine Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAffineEnc: function (input, args) {
var alphabet = "abcdefghijklmnopqrstuvwxyz",
a = args[0],
b = args[1],
output = "";
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
return "The values of a and b can only be integers.";
}
for (var i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Uses the affine function ax+b % m = y (where m is length of the alphabet)
output += alphabet[((a * alphabet.indexOf(input[i])) + b) % 26];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
// Same as above, accounting for uppercase
output += alphabet[((a * alphabet.indexOf(input[i].toLowerCase())) + b) % 26].toUpperCase();
} else {
// Non-alphabetic characters
output += input[i];
}
}
return output;
},
/**
* Affine Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAffineDec: function (input, args) {
var alphabet = "abcdefghijklmnopqrstuvwxyz",
a = args[0],
b = args[1],
output = "",
aModInv;
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
return "The values of a and b can only be integers.";
}
if (Utils.gcd(a, 26) !== 1) {
return "The value of a must be coprime to 26.";
}
// Calculates modular inverse of a
aModInv = Utils.modInv(a, 26);
for (var i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Uses the affine decode function (y-b * A') % m = x (where m is length of the alphabet and A' is modular inverse)
output += alphabet[Utils.mod((alphabet.indexOf(input[i]) - b) * aModInv, 26)];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
// Same as above, accounting for uppercase
output += alphabet[Utils.mod((alphabet.indexOf(input[i].toLowerCase()) - b) * aModInv, 26)].toUpperCase();
} else {
// Non-alphabetic characters
output += input[i];
}
}
return output;
},
/**
* Atbash Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.pw]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runAtbash: function (input, args) {
return Cipher.runAffineEnc(input, [25, 25]);
},
/**
* @constant
* @default

View File

@ -244,7 +244,7 @@ var Code = {
var i = 0,
level = 0;
while (i < code.length) {
switch(code[i]) {
switch (code[i]) {
case "{":
level++;
break;
@ -296,6 +296,9 @@ var Code = {
return code;
/**
* Replaces a matched token with a placeholder value.
*/
function preserveToken(str, match, t) {
preservedTokens[t] = match[0];
return str.substring(0, match.index) +

View File

@ -140,7 +140,7 @@ var DateTime = {
try {
date = moment.tz(input, inputFormat, inputTimezone);
if (!date || date.format() === "Invalid date") throw Error;
} catch(err) {
} catch (err) {
return "Invalid format.\n\n" + DateTime.FORMAT_EXAMPLES;
}
@ -164,7 +164,7 @@ var DateTime = {
try {
date = moment.tz(input, inputFormat, inputTimezone);
if (!date || date.format() === "Invalid date") throw Error;
} catch(err) {
} catch (err) {
return "Invalid format.\n\n" + DateTime.FORMAT_EXAMPLES;
}

View File

@ -243,7 +243,6 @@ var HTML = {
},
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_colorSpace.
@ -309,7 +308,7 @@ var HTML = {
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max) {
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;

View File

@ -639,6 +639,9 @@ var IP = {
return result;
/**
* Converts a list of 4 numeric strings in the range 0-255 to a list of numbers.
*/
function parseBlocks(blocks) {
if (blocks.length !== 4)
throw "More than 4 blocks.";
@ -703,6 +706,9 @@ var IP = {
}
return ipv6;
/**
* Converts a list of 3-8 numeric hex strings in the range 0-65535 to a list of numbers.
*/
function parseBlocks(blocks) {
if (blocks.length < 3 || blocks.length > 8)
throw "Badly formatted IPv6 address.";

View File

@ -122,7 +122,7 @@ var JS = {
AST = escodegen.attachComments(AST, AST.comments, AST.tokens);
result = escodegen.generate(AST, options);
} catch(e) {
} catch (e) {
// Leave original error so the user can see the detail
throw "Unable to parse JavaScript.<br>" + e.message;
}

View File

@ -89,7 +89,7 @@ var MorseCode = {
words = Array.prototype.map.call(words, function(word) {
var letters = Array.prototype.map.call(word, function(character) {
var letter = character.toUpperCase();
if(typeof MorseCode.MORSE_TABLE[letter] == "undefined") {
if (typeof MorseCode.MORSE_TABLE[letter] == "undefined") {
return "";
}
@ -106,7 +106,7 @@ var MorseCode = {
input = input.replace(
/<dash>|<dot>|<ld>|<wd>/g,
function(match) {
switch(match) {
switch (match) {
case "<dash>": return dash;
case "<dot>": return dot;
case "<ld>": return letterDelim;
@ -131,14 +131,14 @@ var MorseCode = {
var reverseTable = function() {
reversedTable = {};
for(var letter in MorseCode.MORSE_TABLE) {
for (var letter in MorseCode.MORSE_TABLE) {
var signal = MorseCode.MORSE_TABLE[letter];
reversedTable[signal] = letter;
}
};
return function(input, args) {
if(reversedTable === null) {
if (reversedTable === null) {
reverseTable();
}

View File

@ -86,7 +86,7 @@ var SeqUtils = {
var regex = new RegExp(search, "gi"),
matches = input.match(regex);
return matches.length;
} catch(err) {
} catch (err) {
return 0;
}
} else if (search) {

View File

@ -42,7 +42,7 @@ var URL_ = {
var data = input.replace(/\+/g, "%20");
try {
return decodeURIComponent(data);
} catch(err) {
} catch (err) {
return unescape(data);
}
},

View File

@ -1,3 +1,5 @@
/* globals moment */
/**
* Waiter to handle events related to the CyberChef controls (i.e. Bake, Step, Save, Load etc.)
*
@ -154,12 +156,13 @@ ControlsWaiter.prototype.initialiseSaveLink = function(recipeConfig) {
* @param {boolean} includeRecipe - Whether to include the recipe in the URL.
* @param {boolean} includeInput - Whether to include the input in the URL.
* @param {Object[]} [recipeConfig] - The recipe configuration object array.
* @param {string} [baseURL] - The CyberChef URL, set to the current URL if not included
* @returns {string}
*/
ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput, recipeConfig) {
ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput, recipeConfig, baseURL) {
recipeConfig = recipeConfig || this.app.getRecipeConfig();
var link = window.location.protocol + "//" +
var link = baseURL || window.location.protocol + "//" +
window.location.host +
window.location.pathname,
recipeStr = JSON.stringify(recipeConfig),
@ -189,7 +192,7 @@ ControlsWaiter.prototype.saveTextChange = function() {
try {
var recipeConfig = JSON.parse(document.getElementById("save-text").value);
this.initialiseSaveLink(recipeConfig);
} catch(err) {}
} catch (err) {}
};
@ -333,7 +336,20 @@ ControlsWaiter.prototype.loadButtonClick = function() {
this.app.setRecipeConfig(recipeConfig);
$("#rec-list [data-toggle=popover]").popover();
} catch(e) {
} catch (e) {
this.app.alert("Invalid recipe", "danger", 2000);
}
};
/**
* Populates the bug report information box with useful technical info.
*/
ControlsWaiter.prototype.supportButtonClick = function() {
var reportBugInfo = document.getElementById("report-bug-info"),
saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
reportBugInfo.innerHTML = "* CyberChef compile time: <%= compileTime %>\n" +
"* User-Agent: \n" + navigator.userAgent + "\n" +
"* [Link to reproduce](" + saveLink + ")\n\n";
};

View File

@ -362,13 +362,13 @@ HTMLApp.prototype.loadURIParams = function() {
try {
var recipeConfig = JSON.parse(this.queryString.recipe);
this.setRecipeConfig(recipeConfig);
} catch(err) {}
} catch (err) {}
} else if (this.queryString.op) {
// If there's no recipe, look for single operations
this.manager.recipe.clearRecipe();
try {
this.manager.recipe.addOperation(this.queryString.op);
} catch(err) {
} catch (err) {
// If no exact match, search for nearest match and add that
var matchedOps = this.manager.ops.filterOperations(this.queryString.op, false);
if (matchedOps.length) {
@ -388,7 +388,7 @@ HTMLApp.prototype.loadURIParams = function() {
try {
var inputData = Utils.fromBase64(this.queryString.input);
this.setInput(inputData);
} catch(err) {}
} catch (err) {}
}
// Restore auto-bake state

View File

@ -86,6 +86,7 @@ Manager.prototype.initialiseEventListeners = function() {
document.getElementById("load-delete-button").addEventListener("click", this.controls.loadDeleteClick.bind(this.controls));
document.getElementById("load-name").addEventListener("change", this.controls.loadNameChange.bind(this.controls));
document.getElementById("load-button").addEventListener("click", this.controls.loadButtonClick.bind(this.controls));
document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls));
this.addMultiEventListener("#save-text", "keyup paste", this.controls.saveTextChange, this.controls);
// Operations

View File

@ -227,7 +227,7 @@ SeasonalWaiter.treeWalk = (function() {
while (node && node !== parent) {
if (allNodes || node.nodeType === 1) {
if (fn(node) === false) {
return(false);
return false;
}
}
// If it's an element &&

View File

@ -43,7 +43,7 @@ var main = function() {
// Fix issues with browsers that don't support console.log()
window.console = console || {log: function() {}, error: function() {}};
window.compileTime = moment.tz("<%= grunt.template.today() %>", "ddd MMM D YYYY HH:mm:ss", "UTC").valueOf();
window.compileTime = moment.tz("<%= compileTime %>", "DD/MM/YYYY HH:mm:ss z", "UTC").valueOf();
window.compileMessage = "<%= compileMsg %>";
document.addEventListener("DOMContentLoaded", main, false);

View File

@ -1,9 +1,9 @@
212 source files
115060 lines
115305 lines
4.3M size
142 JavaScript source files
105900 lines
106125 lines
3.8M size
83 third party JavaScript source files
@ -11,11 +11,11 @@
3.0M size
59 first party JavaScript source files
19642 lines
740K size
19867 lines
748K size
3.4M uncompressed JavaScript size
3.5M uncompressed JavaScript size
1.9M compressed JavaScript size
15 categories
167 operations
170 operations