From 1f09c03d4897206e7ed4a3d90cac6c577c486aed Mon Sep 17 00:00:00 2001 From: GCHQ 77703 Date: Fri, 15 Feb 2019 14:23:16 +0000 Subject: [PATCH 001/706] Add De Bruijn Operation --- src/core/config/Categories.json | 1 + .../operations/GenerateDeBruijnSequence.mjs | 87 +++++++++++++++++++ tests/operations/index.mjs | 1 + .../tests/GenerateDeBruijnSequence.mjs | 33 +++++++ 4 files changed, 122 insertions(+) create mode 100644 src/core/operations/GenerateDeBruijnSequence.mjs create mode 100644 tests/operations/tests/GenerateDeBruijnSequence.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 8235ab10..238c7282 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -370,6 +370,7 @@ "Chi Square", "Disassemble x86", "Pseudo-Random Number Generator", + "Generate De Bruijn Sequence", "Generate UUID", "Generate TOTP", "Generate HOTP", diff --git a/src/core/operations/GenerateDeBruijnSequence.mjs b/src/core/operations/GenerateDeBruijnSequence.mjs new file mode 100644 index 00000000..647d3c7f --- /dev/null +++ b/src/core/operations/GenerateDeBruijnSequence.mjs @@ -0,0 +1,87 @@ +/** + * @author gchq77703 [gchq77703@gchq.gov.uk] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; + +/** + * Generate De Bruijn Sequence operation + */ +class GenerateDeBruijnSequence extends Operation { + + /** + * GenerateDeBruijnSequence constructor + */ + constructor() { + super(); + + this.name = "Generate De Bruijn Sequence"; + this.module = "Default"; + this.description = "Generates rolling keycode combinations given a certain alphabet size and key length."; + this.infoURL = "https://wikipedia.org/wiki/De_Bruijn_sequence"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Alphabet size (k)", + type: "number", + value: 2 + }, + { + name: "Key length (n)", + type: "number", + value: 3 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [k, n] = args; + + if (k < 2 || k > 9) { + throw new OperationError("Invalid alphabet size, required to be between 2 and 9 (inclusive)."); + } + + if (n < 2) { + throw new OperationError("Invalid key length, required to be at least 2."); + } + + if (Math.pow(k, n) > 50000) { + throw new OperationError("Too many permutations, please reduce k^n to under 50,000."); + } + + const a = []; + for (let i = 0; i < k * n; i++) a.push(0); + + const sequence = []; + + (function db(t = 1, p = 1) { + if (t > n) { + if (n % p !== 0) return; + for (let j = 1; j <= p; j++) { + sequence.push(a[j]); + } + return; + } + + a[t] = a[t - p]; + db(t + 1, p); + for (let j = a[t - p] + 1; j < k; j++) { + a[t] = j; + db(t + 1, t); + } + })(); + + return sequence.join(""); + } +} + +export default GenerateDeBruijnSequence; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index fb68ed9c..316e934c 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -45,6 +45,7 @@ import "./tests/DateTime"; import "./tests/ExtractEmailAddresses"; import "./tests/Fork"; import "./tests/FromDecimal"; +import "./tests/GenerateDeBruijnSequence"; import "./tests/Hash"; import "./tests/HaversineDistance"; import "./tests/Hexdump"; diff --git a/tests/operations/tests/GenerateDeBruijnSequence.mjs b/tests/operations/tests/GenerateDeBruijnSequence.mjs new file mode 100644 index 00000000..b68a843f --- /dev/null +++ b/tests/operations/tests/GenerateDeBruijnSequence.mjs @@ -0,0 +1,33 @@ +/** + * De Brujin Sequence tests. + * + * @author gchq77703 [gchq77703@gchq.gov.uk] + * @copyright Crown Copyright 2017 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "Small Sequence", + input: "", + expectedOutput: "00010111", + recipeConfig: [ + { + "op": "Generate De Bruijn Sequence", + "args": [2, 3] + } + ] + }, + { + name: "Long Sequence", + input: "", + expectedOutput: "0000010000200003000110001200013000210002200023000310003200033001010010200103001110011200113001210012200123001310013200133002010020200203002110021200213002210022200223002310023200233003010030200303003110031200313003210032200323003310033200333010110101201013010210102201023010310103201033011020110301111011120111301121011220112301131011320113301202012030121101212012130122101222012230123101232012330130201303013110131201313013210132201323013310133201333020210202202023020310203202033021030211102112021130212102122021230213102132021330220302211022120221302221022220222302231022320223302303023110231202313023210232202323023310233202333030310303203033031110311203113031210312203123031310313203133032110321203213032210322203223032310323203233033110331203313033210332203323033310333203333111112111131112211123111321113311212112131122211223112321123311312113131132211323113321133312122121231213212133122131222212223122321223312313123221232312332123331313213133132221322313232132331332213323133321333322222322233223232233323233233333", + recipeConfig: [ + { + "op": "Generate De Bruijn Sequence", + "args": [4, 5] + } + ] + } +]) \ No newline at end of file From 44a164ed2825ddd799b656459b89b1a4ee5a9f0a Mon Sep 17 00:00:00 2001 From: GCHQ 77703 Date: Tue, 19 Feb 2019 09:56:38 +0000 Subject: [PATCH 002/706] Fix test script linter --- tests/operations/tests/GenerateDeBruijnSequence.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/operations/tests/GenerateDeBruijnSequence.mjs b/tests/operations/tests/GenerateDeBruijnSequence.mjs index b68a843f..48e8c4ff 100644 --- a/tests/operations/tests/GenerateDeBruijnSequence.mjs +++ b/tests/operations/tests/GenerateDeBruijnSequence.mjs @@ -30,4 +30,4 @@ TestRegister.addTests([ } ] } -]) \ No newline at end of file +]); From 822a4fab86572817fcd2e6218d8c736d1e22bbf4 Mon Sep 17 00:00:00 2001 From: GCHQ 77703 Date: Tue, 19 Feb 2019 10:16:51 +0000 Subject: [PATCH 003/706] Fix operation linting --- src/core/operations/GenerateDeBruijnSequence.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/GenerateDeBruijnSequence.mjs b/src/core/operations/GenerateDeBruijnSequence.mjs index 647d3c7f..af788585 100644 --- a/src/core/operations/GenerateDeBruijnSequence.mjs +++ b/src/core/operations/GenerateDeBruijnSequence.mjs @@ -71,7 +71,7 @@ class GenerateDeBruijnSequence extends Operation { } return; } - + a[t] = a[t - p]; db(t + 1, p); for (let j = a[t - p] + 1; j < k; j++) { From 846e84d3a471513287f25d1e4071dbc5e970e272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sun, 3 Mar 2019 16:18:31 +0100 Subject: [PATCH 004/706] Add fernet encryption/decryption operation --- package-lock.json | 161 +++++++++++++++----------- package.json | 1 + src/core/config/Categories.json | 2 + src/core/operations/FernetDecrypt.mjs | 64 ++++++++++ src/core/operations/FernetEncrypt.mjs | 54 +++++++++ tests/operations/tests/Fernet.mjs | 80 +++++++++++++ 6 files changed, 292 insertions(+), 70 deletions(-) create mode 100644 src/core/operations/FernetDecrypt.mjs create mode 100644 src/core/operations/FernetEncrypt.mjs create mode 100644 tests/operations/tests/Fernet.mjs diff --git a/package-lock.json b/package-lock.json index 55ad6303..18da5b7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1631,7 +1631,7 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", "dev": true }, @@ -1716,7 +1716,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -1864,7 +1864,7 @@ }, "axios": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", "dev": true, "requires": { @@ -2334,7 +2334,7 @@ }, "browserify-aes": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { @@ -2371,7 +2371,7 @@ }, "browserify-rsa": { "version": "4.0.1", - "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { @@ -2436,7 +2436,7 @@ }, "buffer": { "version": "4.9.1", - "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { @@ -2590,7 +2590,7 @@ }, "camelcase-keys": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { @@ -2639,7 +2639,7 @@ }, "chalk": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { "ansi-styles": "^2.2.1", @@ -3172,7 +3172,7 @@ }, "create-hash": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { @@ -3185,7 +3185,7 @@ }, "create-hmac": { "version": "1.1.7", - "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { @@ -3332,7 +3332,7 @@ }, "css-select": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { @@ -3700,7 +3700,7 @@ }, "diffie-hellman": { "version": "5.0.3", - "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { @@ -3764,7 +3764,7 @@ "dependencies": { "domelementtype": { "version": "1.1.3", - "resolved": "http://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.1.3.tgz", "integrity": "sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=", "dev": true }, @@ -3969,7 +3969,7 @@ }, "entities": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", "dev": true }, @@ -4392,7 +4392,7 @@ }, "eventemitter2": { "version": "0.4.14", - "resolved": "http://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=", "dev": true }, @@ -4404,7 +4404,7 @@ }, "events": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", "dev": true }, @@ -4720,6 +4720,22 @@ "pend": "~1.2.0" } }, + "fernet": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/fernet/-/fernet-0.3.1.tgz", + "integrity": "sha512-7KnsrcpLkUsKy6aH6Ow68hrMWhvE25rTDd3370+xVGkpqZta05cUCmdJQPyLBKTsNdPUB5NumJZBgJIJ60aQqw==", + "requires": { + "crypto-js": "~3.1.2-1", + "urlsafe-base64": "1.0.0" + }, + "dependencies": { + "crypto-js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-3.1.8.tgz", + "integrity": "sha1-cV8HC/YBTyrpkqmLOSkli3E/CNU=" + } + } + }, "figgy-pudding": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", @@ -4821,7 +4837,7 @@ }, "finalhandler": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", "dev": true, "requires": { @@ -5057,7 +5073,7 @@ }, "fs-extra": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", "dev": true, "requires": { @@ -5726,7 +5742,7 @@ }, "get-stream": { "version": "3.0.0", - "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, @@ -5868,7 +5884,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -5945,7 +5961,7 @@ }, "grunt-cli": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.2.0.tgz", "integrity": "sha1-VisRnrsGndtGSs4oRVAb6Xs1tqg=", "dev": true, "requires": { @@ -5993,7 +6009,7 @@ "dependencies": { "shelljs": { "version": "0.5.3", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.5.3.tgz", "integrity": "sha1-xUmCuZbHbvDB5rWfvcWCX1txMRM=", "dev": true } @@ -6013,7 +6029,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6058,7 +6074,7 @@ }, "grunt-contrib-jshint": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-1.1.0.tgz", "integrity": "sha1-Np2QmyWTxA6L55lAshNAhQx5Oaw=", "dev": true, "requires": { @@ -6157,7 +6173,7 @@ "dependencies": { "colors": { "version": "1.1.2", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.1.2.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", "dev": true } @@ -6221,7 +6237,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -6482,7 +6498,7 @@ }, "html-webpack-plugin": { "version": "3.2.0", - "resolved": "http://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { @@ -6538,7 +6554,7 @@ }, "htmlparser2": { "version": "3.8.3", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", "dev": true, "requires": { @@ -6557,7 +6573,7 @@ }, "http-errors": { "version": "1.6.3", - "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { @@ -6607,7 +6623,7 @@ }, "http-proxy-middleware": { "version": "0.18.0", - "resolved": "http://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { @@ -7053,7 +7069,7 @@ }, "is-builtin-module": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz", "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { @@ -7614,7 +7630,7 @@ }, "jsonfile": { "version": "2.4.0", - "resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", "dev": true, "requires": { @@ -7725,7 +7741,7 @@ }, "kew": { "version": "0.7.0", - "resolved": "http://registry.npmjs.org/kew/-/kew-0.7.0.tgz", + "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", "dev": true }, @@ -7844,7 +7860,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { @@ -7857,7 +7873,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -8221,7 +8237,7 @@ }, "media-typer": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", "dev": true }, @@ -8280,7 +8296,7 @@ }, "meow": { "version": "3.7.0", - "resolved": "http://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { @@ -8501,7 +8517,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" @@ -8711,7 +8727,7 @@ }, "ncp": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-1.0.1.tgz", "integrity": "sha1-0VNn5cuHQyuhF9K/gP30Wuz7QkY=", "dev": true }, @@ -8810,7 +8826,7 @@ "dependencies": { "semver": { "version": "5.3.0", - "resolved": "http://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", "dev": true } @@ -8993,7 +9009,7 @@ "dependencies": { "colors": { "version": "0.5.1", - "resolved": "http://registry.npmjs.org/colors/-/colors-0.5.1.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-0.5.1.tgz", "integrity": "sha1-fQAj6usVTo7p/Oddy5I9DtFmd3Q=" }, "underscore": { @@ -9287,13 +9303,13 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, "os-locale": { "version": "1.4.0", - "resolved": "http://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { @@ -9302,7 +9318,7 @@ }, "os-tmpdir": { "version": "1.0.2", - "resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, @@ -9338,7 +9354,7 @@ }, "p-is-promise": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", "dev": true }, @@ -9526,7 +9542,7 @@ }, "parse-asn1": { "version": "5.1.1", - "resolved": "http://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.1.tgz", "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { @@ -9612,7 +9628,7 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, @@ -9653,7 +9669,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -9836,7 +9852,7 @@ "dependencies": { "async": { "version": "1.5.2", - "resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true } @@ -10207,7 +10223,7 @@ }, "progress": { "version": "1.1.8", - "resolved": "http://registry.npmjs.org/progress/-/progress-1.1.8.tgz", + "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=" }, "promise-inflight": { @@ -10232,13 +10248,13 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, "winston": { "version": "2.1.1", - "resolved": "http://registry.npmjs.org/winston/-/winston-2.1.1.tgz", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.1.1.tgz", "integrity": "sha1-PJNJ0ZYgf9G9/51LxD73JRDjoS4=", "dev": true, "requires": { @@ -10253,7 +10269,7 @@ "dependencies": { "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, @@ -10476,7 +10492,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true } @@ -10665,7 +10681,7 @@ "dependencies": { "jsesc": { "version": "0.5.0", - "resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", "dev": true } @@ -10716,7 +10732,7 @@ }, "htmlparser2": { "version": "3.3.0", - "resolved": "http://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.3.0.tgz", "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { @@ -10728,7 +10744,7 @@ }, "readable-stream": { "version": "1.0.34", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { @@ -10995,7 +11011,7 @@ }, "safe-regex": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { @@ -11315,7 +11331,7 @@ }, "sha.js": { "version": "2.4.11", - "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { @@ -11359,7 +11375,7 @@ }, "shelljs": { "version": "0.3.0", - "resolved": "http://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", "dev": true }, @@ -12080,7 +12096,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -12097,7 +12113,7 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", "dev": true }, @@ -12190,7 +12206,7 @@ }, "tar": { "version": "2.2.1", - "resolved": "http://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", "dev": true, "requires": { @@ -12348,7 +12364,7 @@ }, "through": { "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, @@ -12942,6 +12958,11 @@ "requires-port": "^1.0.0" } }, + "urlsafe-base64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/urlsafe-base64/-/urlsafe-base64-1.0.0.tgz", + "integrity": "sha1-I/iQaabGL0bPOh07ABac77kL4MY=" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -13008,7 +13029,7 @@ "dependencies": { "async": { "version": "0.9.2", - "resolved": "http://registry.npmjs.org/async/-/async-0.9.2.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", "dev": true }, @@ -13034,7 +13055,7 @@ }, "valid-data-url": { "version": "0.1.6", - "resolved": "http://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", + "resolved": "https://registry.npmjs.org/valid-data-url/-/valid-data-url-0.1.6.tgz", "integrity": "sha512-FXg2qXMzfAhZc0y2HzELNfUeiOjPr+52hU1DNBWiJJ2luXD+dD1R9NA48Ug5aj0ibbxroeGDc/RJv6ThiGgkDw==", "dev": true }, @@ -13050,7 +13071,7 @@ }, "validator": { "version": "9.4.1", - "resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==", "dev": true }, @@ -13736,14 +13757,14 @@ "dependencies": { "async": { "version": "1.0.0", - "resolved": "http://registry.npmjs.org/async/-/async-1.0.0.tgz", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, "colors": { "version": "1.0.3", - "resolved": "http://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true @@ -13776,7 +13797,7 @@ }, "wrap-ansi": { "version": "2.1.0", - "resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { diff --git a/package.json b/package.json index cb59db38..35901453 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "esmangle": "^1.0.1", "esprima": "^4.0.1", "exif-parser": "^0.1.12", + "fernet": "^0.3.1", "file-saver": "^2.0.0", "geodesy": "^1.1.3", "highlight.js": "^9.13.1", diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 8235ab10..2db5af51 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -73,6 +73,8 @@ "DES Decrypt", "Triple DES Encrypt", "Triple DES Decrypt", + "Fernet Encrypt", + "Fernet Decrypt", "RC2 Encrypt", "RC2 Decrypt", "RC4", diff --git a/src/core/operations/FernetDecrypt.mjs b/src/core/operations/FernetDecrypt.mjs new file mode 100644 index 00000000..76d4fd16 --- /dev/null +++ b/src/core/operations/FernetDecrypt.mjs @@ -0,0 +1,64 @@ +/** + * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import fernet from "fernet"; + +/** + * FernetDecrypt operation + */ +class FernetDecrypt extends Operation { + /** + * FernetDecrypt constructor + */ + constructor() { + super(); + + this.name = "Fernet Decrypt"; + this.module = "Default"; + this.description = "Fernet is a symmetric encryption method which makes sure that the message encrypted cannot be manipulated/read without the key. It uses URL safe encoding for the keys. Fernet uses 128-bit AES in CBC mode and PKCS7 padding, with HMAC using SHA256 for authentication. The IV is created from os.random().

Key: The key must be 32 bytes (256 bits) encoded with Base64."; + this.infoURL = "https://asecuritysite.com/encryption/fer"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Key", + "type": "string", + "value": "" + }, + ]; + this.patterns = [ + { + match: "^[A-Z\\d\\-_=]{20,}$", + flags: "i", + args: [] + }, + ]; + } + /** + * @param {String} input + * @param {Object[]} args + * @returns {String} + */ + run(input, args) { + const [secretInput] = args; + // const fernet = require("fernet"); + try { + const secret = new fernet.Secret(secretInput); + const token = new fernet.Token({ + secret: secret, + token: input, + ttl: 0 + }); + return token.decode(); + } catch (err) { + throw new OperationError(err); + } + } +} + +export default FernetDecrypt; diff --git a/src/core/operations/FernetEncrypt.mjs b/src/core/operations/FernetEncrypt.mjs new file mode 100644 index 00000000..ac8c64cb --- /dev/null +++ b/src/core/operations/FernetEncrypt.mjs @@ -0,0 +1,54 @@ +/** + * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import fernet from "fernet"; + +/** + * FernetEncrypt operation + */ +class FernetEncrypt extends Operation { + /** + * FernetEncrypt constructor + */ + constructor() { + super(); + + this.name = "Fernet Encrypt"; + this.module = "Default"; + this.description = "Fernet is a symmetric encryption method which makes sure that the message encrypted cannot be manipulated/read without the key. It uses URL safe encoding for the keys. Fernet uses 128-bit AES in CBC mode and PKCS7 padding, with HMAC using SHA256 for authentication. The IV is created from os.random().

Key: The key must be 32 bytes (256 bits) encoded with Base64."; + this.infoURL = "https://asecuritysite.com/encryption/fer"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "Key", + "type": "string", + "value": "" + }, + ]; + } + /** + * @param {String} input + * @param {Object[]} args + * @returns {String} + */ + run(input, args) { + const [secretInput] = args; + try { + const secret = new fernet.Secret(secretInput); + const token = new fernet.Token({ + secret: secret, + }); + return token.encode(input); + } catch (err) { + throw new OperationError(err); + } + } +} + +export default FernetEncrypt; diff --git a/tests/operations/tests/Fernet.mjs b/tests/operations/tests/Fernet.mjs new file mode 100644 index 00000000..0632fca9 --- /dev/null +++ b/tests/operations/tests/Fernet.mjs @@ -0,0 +1,80 @@ +/** + * Fernet tests. + * + * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "Fernet Decrypt: no input", + input: "", + expectedOutput: "Error: Invalid version", + recipeConfig: [ + { + op: "Fernet Decrypt", + args: ["MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="] + } + ], + }, + { + name: "Fernet Decrypt: no secret", + input: "gAAAAABce-Tycae8klRxhDX2uenJ-uwV8-A1XZ2HRnfOXlNzkKKfRxviNLlgtemhT_fd1Fw5P_zFUAjd69zaJBQyWppAxVV00SExe77ql8c5n62HYJOnoIU=", + expectedOutput: "Error: Secret must be 32 url-safe base64-encoded bytes.", + recipeConfig: [ + { + op: "Fernet Decrypt", + args: [""] + } + ], + }, + { + name: "Fernet Decrypt: valid arguments", + input: "gAAAAABce-Tycae8klRxhDX2uenJ-uwV8-A1XZ2HRnfOXlNzkKKfRxviNLlgtemhT_fd1Fw5P_zFUAjd69zaJBQyWppAxVV00SExe77ql8c5n62HYJOnoIU=", + expectedOutput: "This is a secret message.\n", + recipeConfig: [ + { + op: "Fernet Decrypt", + args: ["VGhpc0lzVGhpcnR5VHdvQ2hhcmFjdGVyc0xvbmdLZXk="] + } + ], + } +]); + +TestRegister.addTests([ + { + name: "Fernet Encrypt: no input", + input: "", + expectedMatch: /^gAAAAABce-[\w-]+={0,2}$/, + recipeConfig: [ + { + op: "Fernet Encrypt", + args: ["MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="] + } + ], + }, + { + name: "Fernet Encrypt: no secret", + input: "This is a secret message.\n", + expectedOutput: "Error: Secret must be 32 url-safe base64-encoded bytes.", + recipeConfig: [ + { + op: "Fernet Encrypt", + args: [""] + } + ], + }, + { + name: "Fernet Encrypt: valid arguments", + input: "This is a secret message.\n", + expectedMatch: /^gAAAAABce-[\w-]+={0,2}$/, + recipeConfig: [ + { + op: "Fernet Encrypt", + args: ["MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI="] + } + ], + } +]); From 55cac174564cf71da857f6aee0941e06635d445d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karsten=20Silkenb=C3=A4umer?= Date: Sun, 3 Mar 2019 17:19:07 +0100 Subject: [PATCH 005/706] Change author URL --- src/core/operations/FernetDecrypt.mjs | 2 +- src/core/operations/FernetEncrypt.mjs | 2 +- tests/operations/tests/Fernet.mjs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/operations/FernetDecrypt.mjs b/src/core/operations/FernetDecrypt.mjs index 76d4fd16..d68593d8 100644 --- a/src/core/operations/FernetDecrypt.mjs +++ b/src/core/operations/FernetDecrypt.mjs @@ -1,5 +1,5 @@ /** - * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */ diff --git a/src/core/operations/FernetEncrypt.mjs b/src/core/operations/FernetEncrypt.mjs index ac8c64cb..2f98449f 100644 --- a/src/core/operations/FernetEncrypt.mjs +++ b/src/core/operations/FernetEncrypt.mjs @@ -1,5 +1,5 @@ /** - * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */ diff --git a/tests/operations/tests/Fernet.mjs b/tests/operations/tests/Fernet.mjs index 0632fca9..ee9ba2f1 100644 --- a/tests/operations/tests/Fernet.mjs +++ b/tests/operations/tests/Fernet.mjs @@ -1,7 +1,7 @@ /** * Fernet tests. * - * @author Karsten Silkenbäumer [kassi@users.noreply.github.com] + * @author Karsten Silkenbäumer [github.com/kassi] * @copyright Karsten Silkenbäumer 2019 * @license Apache-2.0 */ From be2080259ec9ae7d64945fc5640188ec4b773ba6 Mon Sep 17 00:00:00 2001 From: Kyle Parrish Date: Wed, 2 Oct 2019 09:57:50 -0400 Subject: [PATCH 006/706] Add Fang URL to categories --- src/core/config/Categories.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 94f7fd30..18fc19ff 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -183,6 +183,7 @@ "Encode NetBIOS Name", "Decode NetBIOS Name", "Defang URL", + "Fang URL", "Defang IP Addresses" ] }, From cd15a8c406726bf06d55b879d271ac3f79b3ba99 Mon Sep 17 00:00:00 2001 From: Kyle Parrish Date: Wed, 2 Oct 2019 09:58:28 -0400 Subject: [PATCH 007/706] Create FangURL.mjs --- src/core/operations/FangURL.mjs | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/core/operations/FangURL.mjs diff --git a/src/core/operations/FangURL.mjs b/src/core/operations/FangURL.mjs new file mode 100644 index 00000000..5badaae7 --- /dev/null +++ b/src/core/operations/FangURL.mjs @@ -0,0 +1,77 @@ +/** + * @author arnydo [github@arnydo.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; + +/** + * FangURL operation + */ +class FangURL extends Operation { + + /** + * FangURL constructor + */ + constructor() { + super(); + + this.name = "Fang URL"; + this.module = "Default"; + this.description = "Takes a 'Defanged' Universal Resource Locator (URL) and 'Fangs' it. Meaning, it removes the alterations (defanged) that render it useless so that it can be used again."; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Escape [.]", + type: "boolean", + value: true + }, + { + name: "Escape hxxp", + type: "boolean", + value: true + }, + { + name: "Escape ://", + type: "boolean", + value: true + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [dots, http, slashes] = args; + + input = fangURL(input, dots, http, slashes); + + return input; + } + +} + + +/** + * Defangs a given URL + * + * @param {string} url + * @param {boolean} dots + * @param {boolean} http + * @param {boolean} slashes + * @returns {string} + */ +function fangURL(url, dots, http, slashes) { + if (dots) url = url.replace(/\[\.\]/g, "."); + if (http) url = url.replace(/hxxp/g, "http"); + if (slashes) url = url.replace(/\[\:\/\/\]/g, "://"); + + return url; +} + +export default FangURL; From 794e0effba5ed4193265ddc6429ba55f6dac33d4 Mon Sep 17 00:00:00 2001 From: Alan C Date: Mon, 7 Oct 2019 20:02:28 +0800 Subject: [PATCH 008/706] Add "To Float" and "From Float" operations --- package-lock.json | 6 +- package.json | 1 + src/core/config/Categories.json | 2 + src/core/operations/FromFloat.mjs | 78 ++++++++++++++ src/core/operations/ToFloat.mjs | 80 +++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/Float.mjs | 164 ++++++++++++++++++++++++++++++ 7 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 src/core/operations/FromFloat.mjs create mode 100644 src/core/operations/ToFloat.mjs create mode 100644 tests/operations/tests/Float.mjs diff --git a/package-lock.json b/package-lock.json index 11c80ca0..930dfc40 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7657,9 +7657,9 @@ "integrity": "sha512-slx8Q6oywCCSfKgPgL0sEsXtPVnSbTLWpyiDcu6msHOyKOLari1TD1qocXVCft80umnkk3/Qqh3lwoFt8T/BPQ==" }, "ieee754": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.12.tgz", - "integrity": "sha512-GguP+DRY+pJ3soyIiGPTvdiVXjZ+DbXOxGpXn3eMvNW4x4irjqXm4wHKscC+TfxSJ0yw/S1F24tqdMNsMZTiLA==" + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" }, "iferr": { "version": "0.1.5", diff --git a/package.json b/package.json index e9c33484..1283f545 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "file-saver": "^2.0.2", "geodesy": "^1.1.3", "highlight.js": "^9.15.10", + "ieee754": "^1.1.13", "jimp": "^0.6.4", "jquery": "3.4.1", "js-crc": "^0.2.0", diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 94f7fd30..939aa22e 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -14,6 +14,8 @@ "From Charcode", "To Decimal", "From Decimal", + "To Float", + "From Float", "To Binary", "From Binary", "To Octal", diff --git a/src/core/operations/FromFloat.mjs b/src/core/operations/FromFloat.mjs new file mode 100644 index 00000000..4fe5990e --- /dev/null +++ b/src/core/operations/FromFloat.mjs @@ -0,0 +1,78 @@ +/** + * @author tcode2k16 [tcode2k16@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import Utils from "../Utils.mjs"; +import ieee754 from "ieee754"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; + +/** + * From Float operation + */ +class FromFloat extends Operation { + + /** + * FromFloat constructor + */ + constructor() { + super(); + + this.name = "From Float"; + this.module = "Default"; + this.description = "Convert from EEE754 Floating Point Numbers"; + this.infoURL = "https://en.wikipedia.org/wiki/IEEE_754"; + this.inputType = "string"; + this.outputType = "byteArray"; + this.args = [ + { + "name": "Endianness", + "type": "option", + "value": [ + "Big Endian", + "Little Endian" + ] + }, + { + "name": "Size", + "type": "option", + "value": [ + "Float (4 bytes)", + "Double (8 bytes)" + ] + }, + { + "name": "Delimiter", + "type": "option", + "value": DELIM_OPTIONS + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + if (input.length === 0) return []; + + const [endianness, size, delimiterName] = args; + const delim = Utils.charRep(delimiterName || "Space"); + const byteSize = size === "Double (8 bytes)" ? 8 : 4; + const isLE = endianness === "Little Endian"; + const mLen = byteSize === 4 ? 23 : 52; + const floats = input.split(delim); + + const output = new Array(floats.length*byteSize); + for (let i = 0; i < floats.length; i++) { + ieee754.write(output, parseFloat(floats[i]), i*byteSize, isLE, mLen, byteSize); + } + return output; + } + +} + +export default FromFloat; diff --git a/src/core/operations/ToFloat.mjs b/src/core/operations/ToFloat.mjs new file mode 100644 index 00000000..b9aef638 --- /dev/null +++ b/src/core/operations/ToFloat.mjs @@ -0,0 +1,80 @@ +/** + * @author tcode2k16 [tcode2k16@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import Utils from "../Utils.mjs"; +import ieee754 from "ieee754"; +import {DELIM_OPTIONS} from "../lib/Delim.mjs"; + +/** + * To Float operation + */ +class ToFloat extends Operation { + + /** + * ToFloat constructor + */ + constructor() { + super(); + + this.name = "To Float"; + this.module = "Default"; + this.description = "Convert to EEE754 Floating Point Numbers"; + this.infoURL = "https://en.wikipedia.org/wiki/IEEE_754"; + this.inputType = "byteArray"; + this.outputType = "string"; + this.args = [ + { + "name": "Endianness", + "type": "option", + "value": [ + "Big Endian", + "Little Endian" + ] + }, + { + "name": "Size", + "type": "option", + "value": [ + "Float (4 bytes)", + "Double (8 bytes)" + ] + }, + { + "name": "Delimiter", + "type": "option", + "value": DELIM_OPTIONS + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const [endianness, size, delimiterName] = args; + const delim = Utils.charRep(delimiterName || "Space"); + const byteSize = size === "Double (8 bytes)" ? 8 : 4; + const isLE = endianness === "Little Endian"; + const mLen = byteSize === 4 ? 23 : 52; + + if (input.length % byteSize !== 0) { + throw new OperationError(`Input is not a multiple of ${byteSize}`); + } + + const output = []; + for (let i = 0; i < input.length; i+=byteSize) { + output.push(ieee754.read(input, i, isLE, mLen, byteSize)); + } + return output.join(delim); + } + +} + +export default ToFloat; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 14c7408e..b77f16a9 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -39,6 +39,7 @@ import "./tests/Crypt.mjs"; import "./tests/CSV.mjs"; import "./tests/DateTime.mjs"; import "./tests/ExtractEmailAddresses.mjs"; +import "./tests/Float.mjs"; import "./tests/Fork.mjs"; import "./tests/FromDecimal.mjs"; import "./tests/Hash.mjs"; diff --git a/tests/operations/tests/Float.mjs b/tests/operations/tests/Float.mjs new file mode 100644 index 00000000..3977834c --- /dev/null +++ b/tests/operations/tests/Float.mjs @@ -0,0 +1,164 @@ +/** + * Float tests. + * + * @author tcode2k16 [tcode2k16@gmail.com] + * + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; + + +TestRegister.addTests([ + { + name: "To Float: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "From Hex", + args: ["Auto"] + }, + { + op: "To Float", + args: ["Big Endian", "Float (4 bytes)", "Space"] + } + ], + }, + { + name: "To Float (Big Endian, 4 bytes): 0.5", + input: "3f0000003f000000", + expectedOutput: "0.5 0.5", + recipeConfig: [ + { + op: "From Hex", + args: ["Auto"] + }, + { + op: "To Float", + args: ["Big Endian", "Float (4 bytes)", "Space"] + } + ] + }, + { + name: "To Float (Little Endian, 4 bytes): 0.5", + input: "0000003f0000003f", + expectedOutput: "0.5 0.5", + recipeConfig: [ + { + op: "From Hex", + args: ["Auto"] + }, + { + op: "To Float", + args: ["Little Endian", "Float (4 bytes)", "Space"] + } + ] + }, + { + name: "To Float (Big Endian, 8 bytes): 0.5", + input: "3fe00000000000003fe0000000000000", + expectedOutput: "0.5 0.5", + recipeConfig: [ + { + op: "From Hex", + args: ["Auto"] + }, + { + op: "To Float", + args: ["Big Endian", "Double (8 bytes)", "Space"] + } + ] + }, + { + name: "To Float (Little Endian, 8 bytes): 0.5", + input: "000000000000e03f000000000000e03f", + expectedOutput: "0.5 0.5", + recipeConfig: [ + { + op: "From Hex", + args: ["Auto"] + }, + { + op: "To Float", + args: ["Little Endian", "Double (8 bytes)", "Space"] + } + ] + }, + { + name: "From Float: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "From Float", + args: ["Big Endian", "Float (4 bytes)", "Space"] + }, + { + op: "To Hex", + args: ["None"] + } + ] + }, + { + name: "From Float (Big Endian, 4 bytes): 0.5", + input: "0.5 0.5", + expectedOutput: "3f0000003f000000", + recipeConfig: [ + { + op: "From Float", + args: ["Big Endian", "Float (4 bytes)", "Space"] + }, + { + op: "To Hex", + args: ["None"] + } + ] + }, + { + name: "From Float (Little Endian, 4 bytes): 0.5", + input: "0.5 0.5", + expectedOutput: "0000003f0000003f", + recipeConfig: [ + { + op: "From Float", + args: ["Little Endian", "Float (4 bytes)", "Space"] + }, + { + op: "To Hex", + args: ["None"] + } + ] + }, + { + name: "From Float (Big Endian, 8 bytes): 0.5", + input: "0.5 0.5", + expectedOutput: "3fe00000000000003fe0000000000000", + recipeConfig: [ + { + op: "From Float", + args: ["Big Endian", "Double (8 bytes)", "Space"] + }, + { + op: "To Hex", + args: ["None"] + } + ] + }, + { + name: "From Float (Little Endian, 8 bytes): 0.5", + input: "0.5 0.5", + expectedOutput: "000000000000e03f000000000000e03f", + recipeConfig: [ + { + op: "From Float", + args: ["Little Endian", "Double (8 bytes)", "Space"] + }, + { + op: "To Hex", + args: ["None"] + } + ] + } +]); From 3546ee30a22611f6af16c00532a31eb08fdd2501 Mon Sep 17 00:00:00 2001 From: Kyle Parrish Date: Mon, 7 Oct 2019 16:09:22 -0400 Subject: [PATCH 009/706] Update escaped chars --- src/core/operations/FangURL.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/operations/FangURL.mjs b/src/core/operations/FangURL.mjs index 5badaae7..7390c1a9 100644 --- a/src/core/operations/FangURL.mjs +++ b/src/core/operations/FangURL.mjs @@ -69,7 +69,7 @@ class FangURL extends Operation { function fangURL(url, dots, http, slashes) { if (dots) url = url.replace(/\[\.\]/g, "."); if (http) url = url.replace(/hxxp/g, "http"); - if (slashes) url = url.replace(/\[\:\/\/\]/g, "://"); + if (slashes) url = url.replace(/[://]/g, "://"); return url; } From ce6d38860dcc1b76fc85ecbecde04dbceeb57416 Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 19:39:01 +0100 Subject: [PATCH 010/706] Add operation Added 'Take nth bytes' operation. --- src/core/config/Categories.json | 3 +- src/core/operations/TakeNthBytes.mjs | 78 ++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/TakeNthBytes.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index db2ab3a6..9fb63d36 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -238,7 +238,8 @@ "Escape string", "Unescape string", "Pseudo-Random Number Generator", - "Sleep" + "Sleep", + "Take nth bytes" ] }, { diff --git a/src/core/operations/TakeNthBytes.mjs b/src/core/operations/TakeNthBytes.mjs new file mode 100644 index 00000000..7dcf9c6e --- /dev/null +++ b/src/core/operations/TakeNthBytes.mjs @@ -0,0 +1,78 @@ +/** + * @author Oshawk [oshawk@protonmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Take nth bytes operation + */ +class TakeNthBytes extends Operation { + + /** + * TakeNthBytes constructor + */ + constructor() { + super(); + + this.name = "Take nth bytes"; + this.module = "Default"; + this.description = "Takes every nth byte starting with a given byte."; + this.infoURL = ""; + this.inputType = "byteArray"; + this.outputType = "byteArray"; + this.args = [ + { + name: "Take every", + type: "number", + value: 4 + }, + { + name: "Starting at", + type: "number", + value: 0 + }, + { + name: "Apply to each line", + type: "boolean", + value: false + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + let n = args[0]; + let start = args[1]; + let eachLine = args[2]; + + if (parseInt(n) !== n || n <= 0) { + throw new OperationError("'Take every' must be a positive integer.") + } + if (parseInt(start) !== start || start < 0) { + throw new OperationError("'Starting at' must be a positive or zero integer.") + } + + let offset = 0; + let output = []; + for (let i = 0; i < input.length; i++) { + if (eachLine && input[i] == 0x0a) { + offset = i + 1; + } else if (i - offset >= start && (i - (start + offset)) % n == 0) { + output.push(input[i]); + } + } + + return output; + } + +} + +export default TakeNthBytes; From 7c7d1823ca4e31139a804c3ca067806c200c530c Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 19:53:57 +0100 Subject: [PATCH 011/706] Fix formatting issue Fixed issue where new lines were truncated. --- src/core/operations/TakeNthBytes.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/operations/TakeNthBytes.mjs b/src/core/operations/TakeNthBytes.mjs index 7dcf9c6e..ffc42ff9 100644 --- a/src/core/operations/TakeNthBytes.mjs +++ b/src/core/operations/TakeNthBytes.mjs @@ -64,6 +64,7 @@ class TakeNthBytes extends Operation { let output = []; for (let i = 0; i < input.length; i++) { if (eachLine && input[i] == 0x0a) { + output.push(0x0a); offset = i + 1; } else if (i - offset >= start && (i - (start + offset)) % n == 0) { output.push(input[i]); From 502f126986cc10c2eb4df2aca46bcbe98cadeb3a Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 20:15:45 +0100 Subject: [PATCH 012/706] Fix linting Fixed linting. --- src/core/operations/TakeNthBytes.mjs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/core/operations/TakeNthBytes.mjs b/src/core/operations/TakeNthBytes.mjs index ffc42ff9..05de8869 100644 --- a/src/core/operations/TakeNthBytes.mjs +++ b/src/core/operations/TakeNthBytes.mjs @@ -49,24 +49,24 @@ class TakeNthBytes extends Operation { * @returns {byteArray} */ run(input, args) { - let n = args[0]; - let start = args[1]; - let eachLine = args[2]; + const n = args[0]; + const start = args[1]; + const eachLine = args[2]; - if (parseInt(n) !== n || n <= 0) { - throw new OperationError("'Take every' must be a positive integer.") + if (parseInt(n, 10) !== n || n <= 0) { + throw new OperationError("'Take every' must be a positive integer."); } - if (parseInt(start) !== start || start < 0) { - throw new OperationError("'Starting at' must be a positive or zero integer.") + if (parseInt(start, 10) !== start || start < 0) { + throw new OperationError("'Starting at' must be a positive or zero integer."); } - + let offset = 0; - let output = []; + const output = []; for (let i = 0; i < input.length; i++) { - if (eachLine && input[i] == 0x0a) { + if (eachLine && input[i] === 0x0a) { output.push(0x0a); offset = i + 1; - } else if (i - offset >= start && (i - (start + offset)) % n == 0) { + } else if (i - offset >= start && (i - (start + offset)) % n === 0) { output.push(input[i]); } } From 02f65379736b3a9739342f335fa84476600ea39e Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 20:16:14 +0100 Subject: [PATCH 013/706] Add tests Added tests for the 'Take nth byte' operation. --- tests/operations/tests/TakeNthBytes.mjs | 123 ++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 tests/operations/tests/TakeNthBytes.mjs diff --git a/tests/operations/tests/TakeNthBytes.mjs b/tests/operations/tests/TakeNthBytes.mjs new file mode 100644 index 00000000..22181c3d --- /dev/null +++ b/tests/operations/tests/TakeNthBytes.mjs @@ -0,0 +1,123 @@ +/** + * @author Oshawk [oshawk@protonmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; + +/** + * Take nth bytes tests + */ +TestRegister.addTests([ + { + name: "Take nth bytes: Nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Take nth bytes: Nothing (apply to each line)", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Take nth bytes: Basic single line", + input: "0123456789", + expectedOutput: "048", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Take nth bytes: Basic single line (apply to each line)", + input: "0123456789", + expectedOutput: "048", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Take nth bytes: Complex single line", + input: "0123456789", + expectedOutput: "59", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 5, false], + }, + ], + }, + { + name: "Take nth bytes: Complex single line (apply to each line)", + input: "0123456789", + expectedOutput: "59", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 5, true], + }, + ], + }, + { + name: "Take nth bytes: Basic multi line", + input: "01234\n56789", + expectedOutput: "047", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Take nth bytes: Basic multi line (apply to each line)", + input: "01234\n56789", + expectedOutput: "04\n59", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Take nth bytes: Complex multi line", + input: "01234\n56789", + expectedOutput: "\n8", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 5, false], + }, + ], + }, + { + name: "Take nth bytes: Complex multi line (apply to each line)", + input: "012345\n6789ab", + expectedOutput: "5\nb", + recipeConfig: [ + { + op: "Take nth bytes", + args: [4, 5, true], + }, + ], + } +]); From 30349dbcb90156b98365462dcd93b6dabb540b9d Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 20:44:57 +0100 Subject: [PATCH 014/706] Add operation Added 'Drop nth bytes' operation. --- src/core/config/Categories.json | 3 +- src/core/operations/DropNthBytes.mjs | 79 ++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/core/operations/DropNthBytes.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index db2ab3a6..9cfeb7fe 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -238,7 +238,8 @@ "Escape string", "Unescape string", "Pseudo-Random Number Generator", - "Sleep" + "Sleep", + "Drop nth bytes" ] }, { diff --git a/src/core/operations/DropNthBytes.mjs b/src/core/operations/DropNthBytes.mjs new file mode 100644 index 00000000..e6bac1cd --- /dev/null +++ b/src/core/operations/DropNthBytes.mjs @@ -0,0 +1,79 @@ +/** + * @author Oshawk [oshawk@protonmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * Drop nth bytes operation + */ +class DropNthBytes extends Operation { + + /** + * DropNthBytes constructor + */ + constructor() { + super(); + + this.name = "Drop nth bytes"; + this.module = "Default"; + this.description = "Drops every nth byte starting with a given byte."; + this.infoURL = ""; + this.inputType = "byteArray"; + this.outputType = "byteArray"; + this.args = [ + { + name: "Drop every", + type: "number", + value: 4 + }, + { + name: "Starting at", + type: "number", + value: 0 + }, + { + name: "Apply to each line", + type: "boolean", + value: false + } + ]; + } + + /** + * @param {byteArray} input + * @param {Object[]} args + * @returns {byteArray} + */ + run(input, args) { + const n = args[0]; + const start = args[1]; + const eachLine = args[2]; + + if (parseInt(n, 10) !== n || n <= 0) { + throw new OperationError("'Drop every' must be a positive integer."); + } + if (parseInt(start, 10) !== start || start < 0) { + throw new OperationError("'Starting at' must be a positive or zero integer."); + } + + let offset = 0; + const output = []; + for (let i = 0; i < input.length; i++) { + if (eachLine && input[i] === 0x0a) { + output.push(0x0a); + offset = i + 1; + } else if (i - offset < start || (i - (start + offset)) % n !== 0) { + output.push(input[i]); + } + } + + return output; + } + +} + +export default DropNthBytes; From b125f82784274b3d68eb0a1e2777c63bbf68f205 Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 20:59:04 +0100 Subject: [PATCH 015/706] Add tests Added tests for 'Drop nth bytes' operation. --- tests/operations/index.mjs | 1 + tests/operations/tests/DropNthBytes.mjs | 123 ++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 tests/operations/tests/DropNthBytes.mjs diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index d64a7737..046a0b79 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -91,6 +91,7 @@ import "./tests/Protobuf.mjs"; import "./tests/ParseSSHHostKey.mjs"; import "./tests/DefangIP.mjs"; import "./tests/ParseUDP.mjs"; +import "./tests/DropNthBytes.mjs"; // Cannot test operations that use the File type yet // import "./tests/SplitColourChannels.mjs"; diff --git a/tests/operations/tests/DropNthBytes.mjs b/tests/operations/tests/DropNthBytes.mjs new file mode 100644 index 00000000..00d4e0ab --- /dev/null +++ b/tests/operations/tests/DropNthBytes.mjs @@ -0,0 +1,123 @@ +/** + * @author Oshawk [oshawk@protonmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import TestRegister from "../../lib/TestRegister.mjs"; + +/** + * Drop nth bytes tests + */ +TestRegister.addTests([ + { + name: "Drop nth bytes: Nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Drop nth bytes: Nothing (apply to each line)", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Drop nth bytes: Basic single line", + input: "0123456789", + expectedOutput: "1235679", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Drop nth bytes: Basic single line (apply to each line)", + input: "0123456789", + expectedOutput: "1235679", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Drop nth bytes: Complex single line", + input: "0123456789", + expectedOutput: "01234678", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, false], + }, + ], + }, + { + name: "Drop nth bytes: Complex single line (apply to each line)", + input: "0123456789", + expectedOutput: "01234678", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, true], + }, + ], + }, + { + name: "Drop nth bytes: Basic multi line", + input: "01234\n56789", + expectedOutput: "123\n5689", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, false], + }, + ], + }, + { + name: "Drop nth bytes: Basic multi line (apply to each line)", + input: "01234\n56789", + expectedOutput: "123\n678", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 0, true], + }, + ], + }, + { + name: "Drop nth bytes: Complex multi line", + input: "01234\n56789", + expectedOutput: "012345679", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, false], + }, + ], + }, + { + name: "Drop nth bytes: Complex multi line (apply to each line)", + input: "012345\n6789ab", + expectedOutput: "01234\n6789a", + recipeConfig: [ + { + op: "Drop nth bytes", + args: [4, 5, true], + }, + ], + } +]); From 518b33643198ed6ee474b9296fa6f7ffdd6f9dfb Mon Sep 17 00:00:00 2001 From: Oshawk Date: Mon, 21 Oct 2019 21:01:33 +0100 Subject: [PATCH 016/706] Add tests to index Added tests to index. --- tests/operations/index.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index d64a7737..348e8460 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -91,6 +91,7 @@ import "./tests/Protobuf.mjs"; import "./tests/ParseSSHHostKey.mjs"; import "./tests/DefangIP.mjs"; import "./tests/ParseUDP.mjs"; +import "./tests/TakeNthBytes.mjs"; // Cannot test operations that use the File type yet // import "./tests/SplitColourChannels.mjs"; From e92ed13864d5e404fa9986e6972b61ca83a7123a Mon Sep 17 00:00:00 2001 From: n1073645 Date: Thu, 21 Nov 2019 12:53:44 +0000 Subject: [PATCH 017/706] PLIST viewer. --- src/core/operations/PLISTViewer.mjs | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/core/operations/PLISTViewer.mjs diff --git a/src/core/operations/PLISTViewer.mjs b/src/core/operations/PLISTViewer.mjs new file mode 100644 index 00000000..1d263468 --- /dev/null +++ b/src/core/operations/PLISTViewer.mjs @@ -0,0 +1,56 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; + +/** + * PLIST Viewer operation + */ +class PLISTViewer extends Operation { + + /** + * PLISTViewer constructor + */ + constructor() { + super(); + + this.name = "PLIST Viewer"; + this.module = "Other"; + this.description = "Converts PLISTXML file into a human readable format."; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + /* Example arguments. See the project wiki for full details. + { + name: "First arg", + type: "string", + value: "Don't Panic" + }, + { + name: "Second arg", + type: "number", + value: 42 + } + */ + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + // const [firstArg, secondArg] = args; + + throw new OperationError("Test"); + } + +} + +export default PLISTViewer; From 63bb19d48d06c8e780ba402f2abb0274e0ecc250 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Fri, 22 Nov 2019 08:32:46 +0000 Subject: [PATCH 018/706] Began implementing the PLIST viewer operation --- src/core/config/Categories.json | 1 + src/core/operations/PLISTViewer.mjs | 111 +++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index f663e16d..11e8f076 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -422,6 +422,7 @@ "Frequency distribution", "Index of Coincidence", "Chi Square", + "PLIST Viewer", "Disassemble x86", "Pseudo-Random Number Generator", "Generate UUID", diff --git a/src/core/operations/PLISTViewer.mjs b/src/core/operations/PLISTViewer.mjs index 1d263468..6229d336 100644 --- a/src/core/operations/PLISTViewer.mjs +++ b/src/core/operations/PLISTViewer.mjs @@ -5,7 +5,6 @@ */ import Operation from "../Operation.mjs"; -import OperationError from "../errors/OperationError.mjs"; /** * PLIST Viewer operation @@ -46,11 +45,115 @@ class PLISTViewer extends Operation { * @returns {string} */ run(input, args) { - // const [firstArg, secondArg] = args; - throw new OperationError("Test"); + const reserved = [["","",8], + ["","",6], + ["","",9], + ["","", 6], + ["","",6], + ["","",7], + ["","",6], + ["","",5], + ["",false,8], + ["",true,7]]; + + function the_viewer(input, dictionary_flag){ + var new_dict = new Array(); + var result = new Array(); + var new_key = null; + while(dictionary_flag ? input.slice(0,7) != "" : input.slice(0,8) != ""){ + reserved.forEach( function (elem, index){ + var element = elem[0]; + var endelement = elem[1]; + var length = elem[2]; + let temp = input.slice(0,length); + if(temp == element){ + input = input.slice(length); + if(temp == ""){ + var returned = the_viewer(input, true); + input = returned[1]; + if(new_key) + new_dict[new_key] = returned[0]; + else + new_dict["plist"] = returned[0]; + new_key = null; + }else if(temp == ""){ + var returned = the_viewer(input, false); + if(dictionary_flag) + new_dict[new_key] = returned[0]; + else + result.push(returned[0]); + input = returned[1]; + new_key = null; + }else if(temp == ""){ + var end = input.indexOf(endelement); + new_key = input.slice(0, end); + input = input.slice(end+length+1); + }else if(temp == "" || temp == ""){ + new_dict[new_key] = endelement; + new_key = null; + }else{ + var end = input.indexOf(endelement); + var toadd = input.slice(0, end); + if(temp == "") + toadd = parseInt(toadd); + else if(temp == "") + toadd = parseFloat(toadd); + if(dictionary_flag){ + new_dict[new_key] = toadd; + new_key = null; + }else{ + result.push(toadd); + } + input = input.slice(end+length+1); + } + } + }); + } + if(dictionary_flag){ + input = input.slice(7); + return [new_dict, input]; + }else{ + input = input.slice(8); + return [result, input]; + } + } + + let result = ""; + function print_it(input, depth) { + Object.keys(input).forEach((key, index) => { + if(typeof(input[key]) == "object") { + result += (("\t".repeat(depth)) + key + ": {\n"); + print_it(input[key], depth+1); + result += (("\t".repeat(depth)) + "}\n"); + } else { + result += (("\t".repeat(depth)) + key + " : " + input[key] + "\n"); + } + }); + } + + while (input.indexOf("/, ""); + } + while (input.indexOf("") !== -1){ + input = input.replace(/<\/plist>/, ""); + } + console.log(input); + while(input.indexOf("\n") !== -1) + input = input.replace("\n", ""); + while(input.indexOf("\t") !== -1) + input = input.replace("\t", ""); + while(input.indexOf(" ") !== -1) + input = input.replace(" ", ""); + console.log(input); + input = input.slice(input.indexOf("")+6); + //return input + var other = the_viewer(input, 1); + print_it(other[0],1); + result = "{\n" + result; + result += "}"; + return result; } - } export default PLISTViewer; From 8e1e1d56cadbb1465a323adec3ac544e9c53f3af Mon Sep 17 00:00:00 2001 From: n1073645 Date: Fri, 22 Nov 2019 15:39:43 +0000 Subject: [PATCH 019/706] Plist viewer operation added. --- src/core/operations/PLISTViewer.mjs | 156 ++++++++++------------------ 1 file changed, 55 insertions(+), 101 deletions(-) diff --git a/src/core/operations/PLISTViewer.mjs b/src/core/operations/PLISTViewer.mjs index 6229d336..939b7d1a 100644 --- a/src/core/operations/PLISTViewer.mjs +++ b/src/core/operations/PLISTViewer.mjs @@ -46,112 +46,66 @@ class PLISTViewer extends Operation { */ run(input, args) { - const reserved = [["","",8], - ["","",6], - ["","",9], - ["","", 6], - ["","",6], - ["","",7], - ["","",6], - ["","",5], - ["",false,8], - ["",true,7]]; - - function the_viewer(input, dictionary_flag){ - var new_dict = new Array(); - var result = new Array(); - var new_key = null; - while(dictionary_flag ? input.slice(0,7) != "" : input.slice(0,8) != ""){ - reserved.forEach( function (elem, index){ - var element = elem[0]; - var endelement = elem[1]; - var length = elem[2]; - let temp = input.slice(0,length); - if(temp == element){ - input = input.slice(length); - if(temp == ""){ - var returned = the_viewer(input, true); - input = returned[1]; - if(new_key) - new_dict[new_key] = returned[0]; - else - new_dict["plist"] = returned[0]; - new_key = null; - }else if(temp == ""){ - var returned = the_viewer(input, false); - if(dictionary_flag) - new_dict[new_key] = returned[0]; - else - result.push(returned[0]); - input = returned[1]; - new_key = null; - }else if(temp == ""){ - var end = input.indexOf(endelement); - new_key = input.slice(0, end); - input = input.slice(end+length+1); - }else if(temp == "" || temp == ""){ - new_dict[new_key] = endelement; - new_key = null; - }else{ - var end = input.indexOf(endelement); - var toadd = input.slice(0, end); - if(temp == "") - toadd = parseInt(toadd); - else if(temp == "") - toadd = parseFloat(toadd); - if(dictionary_flag){ - new_dict[new_key] = toadd; - new_key = null; - }else{ - result.push(toadd); - } - input = input.slice(end+length+1); - } - } - }); - } - if(dictionary_flag){ - input = input.slice(7); - return [new_dict, input]; - }else{ - input = input.slice(8); - return [result, input]; - } - } - + // Regexes are designed to transform the xml format into a reasonably more readable string format. + input = input.slice(input.indexOf("/g, "plist => ") + .replace(//g, "{") + .replace(/<\/dict>/g, "}") + .replace(//g, "[") + .replace(/<\/array>/g, "]") + .replace(/.+<\/key>/g, m => `${m.slice(5, m.indexOf(/<\/key>/g)-5)}\t=> `) + .replace(/.+<\/real>/g, m => `${m.slice(6, m.indexOf(/<\/real>/g)-6)}\n`) + .replace(/.+<\/string>/g, m => `${m.slice(8, m.indexOf(/<\/string>/g)-8)}\n`) + .replace(/.+<\/integer>/g, m => `${m.slice(9, m.indexOf(/<\/integer>/g)-9)}\n`) + .replace(//g, m => "false") + .replace(//g, m => "true") + .replace(/<\/plist>/g, "/plist") + .replace(/.+<\/date>/g, m => `${m.slice(6, m.indexOf(/<\/integer>/g)-6)}`) + .replace(/(\s|.)+?<\/data>/g, m => `${m.slice(6, m.indexOf(/<\/data>/g)-6)}`) + .replace(/[ \t\r\f\v]/g, ""); + let result = ""; - function print_it(input, depth) { - Object.keys(input).forEach((key, index) => { - if(typeof(input[key]) == "object") { - result += (("\t".repeat(depth)) + key + ": {\n"); - print_it(input[key], depth+1); - result += (("\t".repeat(depth)) + "}\n"); + + /** + * Formats the input after the regex has replaced all of the relevant parts. + * + * @param {array} input + * @param {number} depthCount + */ + function printIt(input, depthCount) { + if (!(input.length)) + return; + + // If the current position points at a larger dynamic structure. + if (input[0].indexOf("=>") !== -1) { + + // If the LHS also points at a larger structure (nested plists in a dictionary). + if (input[1].indexOf("=>") !== -1) { + result += ("\t".repeat(depthCount)) + input[0].slice(0, -2) + " => " + input[1].slice(0, -2) + " =>\n"; } else { - result += (("\t".repeat(depth)) + key + " : " + input[key] + "\n"); + result += ("\t".repeat(depthCount)) + input[0].slice(0, -2) + " => " + input[1] + "\n"; } - }); + + // Controls the tab depth for how many opening braces there have been. + if (input[1] === "{" || input[1] === "[") { + depthCount += 1; + } + input = input.slice(1); + } else { + // Controls the tab depth for how many closing braces there have been. + if (input[0] === "}" || input[0] === "]") + depthCount--; + + // Has to be here since the formatting breaks otherwise. + result += ("\t".repeat(depthCount)) + input[0] + "\n"; + if (input[0] === "{" || input[0] === "[") + depthCount++; + } + printIt(input.slice(1), depthCount); } - while (input.indexOf("/, ""); - } - while (input.indexOf("") !== -1){ - input = input.replace(/<\/plist>/, ""); - } - console.log(input); - while(input.indexOf("\n") !== -1) - input = input.replace("\n", ""); - while(input.indexOf("\t") !== -1) - input = input.replace("\t", ""); - while(input.indexOf(" ") !== -1) - input = input.replace(" ", ""); - console.log(input); - input = input.slice(input.indexOf("")+6); - //return input - var other = the_viewer(input, 1); - print_it(other[0],1); - result = "{\n" + result; - result += "}"; + input = input.split("\n").filter(e => e !== ""); + printIt(input, 0); return result; } } From 0295d0c9b47d6cd6b30492ce3b77b3741414cde9 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Mon, 25 Nov 2019 10:35:45 +0000 Subject: [PATCH 020/706] Tided up presentation of the PLIST --- src/core/operations/PLISTViewer.mjs | 71 +++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 19 deletions(-) diff --git a/src/core/operations/PLISTViewer.mjs b/src/core/operations/PLISTViewer.mjs index 939b7d1a..8232fb14 100644 --- a/src/core/operations/PLISTViewer.mjs +++ b/src/core/operations/PLISTViewer.mjs @@ -55,7 +55,7 @@ class PLISTViewer extends Operation { .replace(/<\/array>/g, "]") .replace(/.+<\/key>/g, m => `${m.slice(5, m.indexOf(/<\/key>/g)-5)}\t=> `) .replace(/.+<\/real>/g, m => `${m.slice(6, m.indexOf(/<\/real>/g)-6)}\n`) - .replace(/.+<\/string>/g, m => `${m.slice(8, m.indexOf(/<\/string>/g)-8)}\n`) + .replace(/.+<\/string>/g, m => `"${m.slice(8, m.indexOf(/<\/string>/g)-8)}"\n`) .replace(/.+<\/integer>/g, m => `${m.slice(9, m.indexOf(/<\/integer>/g)-9)}\n`) .replace(//g, m => "false") .replace(//g, m => "true") @@ -64,44 +64,77 @@ class PLISTViewer extends Operation { .replace(/(\s|.)+?<\/data>/g, m => `${m.slice(6, m.indexOf(/<\/data>/g)-6)}`) .replace(/[ \t\r\f\v]/g, ""); + /** + * Depending on the type of brace, it will increment the depth and amount of arrays accordingly. + * + * @param {string} elem + * @param {array} vals + * @param {number} offset + */ + function braces(elem, vals,offset) { + let temp = vals.indexOf(elem); + if (temp !== -1) { + depthCount += offset; + if (temp === 1) + arrCount += offset; + } + } + let result = ""; + let arrCount = 0; + let depthCount = 0; /** * Formats the input after the regex has replaced all of the relevant parts. * * @param {array} input - * @param {number} depthCount + * @param {number} index */ - function printIt(input, depthCount) { + function printIt(input, index) { if (!(input.length)) return; + let temp = ""; + const origArr = arrCount; + let currElem = input[0]; + // If the current position points at a larger dynamic structure. - if (input[0].indexOf("=>") !== -1) { + if (currElem.indexOf("=>") !== -1) { // If the LHS also points at a larger structure (nested plists in a dictionary). - if (input[1].indexOf("=>") !== -1) { - result += ("\t".repeat(depthCount)) + input[0].slice(0, -2) + " => " + input[1].slice(0, -2) + " =>\n"; - } else { - result += ("\t".repeat(depthCount)) + input[0].slice(0, -2) + " => " + input[1] + "\n"; - } + if (input[1].indexOf("=>") !== -1) + temp = currElem.slice(0, -2) + " => " + input[1].slice(0, -2) + " =>\n"; + else + temp = currElem.slice(0, -2) + " => " + input[1] + "\n"; - // Controls the tab depth for how many opening braces there have been. - if (input[1] === "{" || input[1] === "[") { - depthCount += 1; - } input = input.slice(1); } else { // Controls the tab depth for how many closing braces there have been. - if (input[0] === "}" || input[0] === "]") - depthCount--; + + braces(currElem, ["}", "]"], -1); // Has to be here since the formatting breaks otherwise. - result += ("\t".repeat(depthCount)) + input[0] + "\n"; - if (input[0] === "{" || input[0] === "[") - depthCount++; + temp = currElem + "\n"; } - printIt(input.slice(1), depthCount); + + currElem = input[0]; + + // Tab out to the correct distance. + result += ("\t".repeat(depthCount)); + + // If it is enclosed in an array show index. + if (arrCount > 0 && currElem !== "]") + result += index.toString() + " => "; + + result += temp; + + // Controls the tab depth for how many opening braces there have been. + braces(currElem, ["{", "["],1); + + // If there has been a new array then reset index. + if (arrCount > origArr) + return printIt(input.slice(1), 0); + return printIt(input.slice(1), ++index); } input = input.split("\n").filter(e => e !== ""); From d8405e5f814e17319dd293fdcddfdeeca2a43f15 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Mon, 25 Nov 2019 10:37:30 +0000 Subject: [PATCH 021/706] Linting on PLIST viewer operation. --- src/core/operations/PLISTViewer.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/operations/PLISTViewer.mjs b/src/core/operations/PLISTViewer.mjs index 8232fb14..b8a90c5b 100644 --- a/src/core/operations/PLISTViewer.mjs +++ b/src/core/operations/PLISTViewer.mjs @@ -71,8 +71,8 @@ class PLISTViewer extends Operation { * @param {array} vals * @param {number} offset */ - function braces(elem, vals,offset) { - let temp = vals.indexOf(elem); + function braces(elem, vals, offset) { + const temp = vals.indexOf(elem); if (temp !== -1) { depthCount += offset; if (temp === 1) @@ -129,7 +129,7 @@ class PLISTViewer extends Operation { result += temp; // Controls the tab depth for how many opening braces there have been. - braces(currElem, ["{", "["],1); + braces(currElem, ["{", "["], 1); // If there has been a new array then reset index. if (arrCount > origArr) From c689cf7f134df8e8309302c88e8b9bf1a22e94f8 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Thu, 9 Jan 2020 15:14:33 +0000 Subject: [PATCH 022/706] Fix #930 by allowing variable key sizes --- src/core/operations/BlowfishDecrypt.mjs | 8 ++++++-- src/core/operations/BlowfishEncrypt.mjs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/core/operations/BlowfishDecrypt.mjs b/src/core/operations/BlowfishDecrypt.mjs index 07b6a0ff..83236327 100644 --- a/src/core/operations/BlowfishDecrypt.mjs +++ b/src/core/operations/BlowfishDecrypt.mjs @@ -70,10 +70,14 @@ class BlowfishDecrypt extends Operation { inputType = args[3], outputType = args[4]; - if (key.length !== 8) { + if (key.length < 4 || key.length > 56) { throw new OperationError(`Invalid key length: ${key.length} bytes -Blowfish uses a key length of 8 bytes (64 bits).`); +Blowfish's key length needs to between 4 and 56 bytes (32-448 bits).`); + } + + if (iv.length !== 8) { + throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes`); } input = Utils.convertToByteString(input, inputType); diff --git a/src/core/operations/BlowfishEncrypt.mjs b/src/core/operations/BlowfishEncrypt.mjs index e7e558cd..ebf5e5c2 100644 --- a/src/core/operations/BlowfishEncrypt.mjs +++ b/src/core/operations/BlowfishEncrypt.mjs @@ -70,10 +70,14 @@ class BlowfishEncrypt extends Operation { inputType = args[3], outputType = args[4]; - if (key.length !== 8) { + if (key.length < 4 || key.length > 56) { throw new OperationError(`Invalid key length: ${key.length} bytes + +Blowfish's key length needs to between 4 and 56 bytes (32-448 bits).`); + } -Blowfish uses a key length of 8 bytes (64 bits).`); + if (iv.length !== 8) { + throw new OperationError(`Invalid IV length: ${iv.length} bytes. Expected 8 bytes`); } input = Utils.convertToByteString(input, inputType); From 9e17825b53b371ed1c8671472ef6585f96a29d86 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Thu, 9 Jan 2020 15:15:01 +0000 Subject: [PATCH 023/706] Add variable key size tests --- tests/operations/tests/Crypt.mjs | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/operations/tests/Crypt.mjs b/tests/operations/tests/Crypt.mjs index b56f9cf8..a6b9e2ac 100644 --- a/tests/operations/tests/Crypt.mjs +++ b/tests/operations/tests/Crypt.mjs @@ -1751,4 +1751,38 @@ DES uses a key length of 8 bytes (64 bits).`, } ], }, + { + name: "Blowfish Encrypt with variable key length: CBC, ASCII, 4 bytes", + input: "The quick brown fox jumps over the lazy dog.", + expectedOutput: "823f337a53ecf121aa9ec1b111bd5064d1d7586abbdaaa0c8fd0c6cc43c831c88bf088ee3e07287e3f36cf2e45f9c7e6", + recipeConfig: [ + { + "op": "Blowfish Encrypt", + "args": [ + {"option": "Hex", "string": "00112233"}, // Key + {"option": "Hex", "string": "0000000000000000"}, // IV + "CBC", // Mode + "Raw", // Input + "Hex" // Output + ] + } + ], + }, + { + name: "Blowfish Encrypt with variable key length: CBC, ASCII, 42 bytes", + input: "The quick brown fox jumps over the lazy dog.", + expectedOutput: "19f5a68145b34321cfba72226b0f33922ce44dd6e7869fe328db64faae156471216f12ed2a37fd0bdd7cebf867b3cff0", + recipeConfig: [ + { + "op": "Blowfish Encrypt", + "args": [ + {"option": "Hex", "string": "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdead"}, // Key + {"option": "Hex", "string": "0000000000000000"}, // IV + "CBC", // Mode + "Raw", // Input + "Hex" // Output + ] + } + ], + } ]); From 81605b2222e2a4b9b41198651da3abc9f2156082 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Sat, 11 Jan 2020 10:47:40 +0000 Subject: [PATCH 024/706] Grammar typo --- src/core/operations/BlowfishDecrypt.mjs | 2 +- src/core/operations/BlowfishEncrypt.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/operations/BlowfishDecrypt.mjs b/src/core/operations/BlowfishDecrypt.mjs index 83236327..a80fdb2b 100644 --- a/src/core/operations/BlowfishDecrypt.mjs +++ b/src/core/operations/BlowfishDecrypt.mjs @@ -73,7 +73,7 @@ class BlowfishDecrypt extends Operation { if (key.length < 4 || key.length > 56) { throw new OperationError(`Invalid key length: ${key.length} bytes -Blowfish's key length needs to between 4 and 56 bytes (32-448 bits).`); +Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`); } if (iv.length !== 8) { diff --git a/src/core/operations/BlowfishEncrypt.mjs b/src/core/operations/BlowfishEncrypt.mjs index ebf5e5c2..7d550d46 100644 --- a/src/core/operations/BlowfishEncrypt.mjs +++ b/src/core/operations/BlowfishEncrypt.mjs @@ -73,7 +73,7 @@ class BlowfishEncrypt extends Operation { if (key.length < 4 || key.length > 56) { throw new OperationError(`Invalid key length: ${key.length} bytes -Blowfish's key length needs to between 4 and 56 bytes (32-448 bits).`); +Blowfish's key length needs to be between 4 and 56 bytes (32-448 bits).`); } if (iv.length !== 8) { From 0259ed8314c0635124d7be316f9e9ec583f4cce0 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Mon, 27 Jan 2020 16:07:54 +0000 Subject: [PATCH 025/706] LS47 implemented, needs linting --- src/core/lib/LS47.mjs | 148 ++++++++++++++++++++++++++++ src/core/operations/LS47Decrypt.mjs | 58 +++++++++++ src/core/operations/LS47Encrypt.mjs | 63 ++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 src/core/lib/LS47.mjs create mode 100644 src/core/operations/LS47Decrypt.mjs create mode 100644 src/core/operations/LS47Encrypt.mjs diff --git a/src/core/lib/LS47.mjs b/src/core/lib/LS47.mjs new file mode 100644 index 00000000..a4ef10a5 --- /dev/null +++ b/src/core/lib/LS47.mjs @@ -0,0 +1,148 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import OperationError from "../errors/OperationError.mjs"; + +let letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()"; +let tiles = []; + +export function init_tiles() { + for (let i = 0; i < 49; i++) + tiles.push([letters.charAt(i), [Math.floor(i/7), i % 7]]); +} + +function rotate_down(key, col, n) { + let lines = []; + for (let i = 0; i < 7; i++) + lines.push(key.slice(i*7, (i + 1) * 7)); + let lefts = []; + let mids = []; + let rights = []; + lines.forEach((element) => { + lefts.push(element.slice(0, col)); + mids.push(element.charAt(col)); + rights.push(element.slice(col+1)); + }); + n = (7 - n % 7) % 7; + mids = mids.slice(n).concat(mids.slice(0, n)); + let result = ""; + for (let i = 0; i < 7; i++) + result += lefts[i] + mids[i] + rights[i]; + return result; +} + +function rotate_right(key, row, n) { + let mid = key.slice(row * 7, (row + 1) * 7); + n = (7 - n % 7) % 7; + return key.slice(0, 7 * row) + mid.slice(n) + mid.slice(0, n) + key.slice(7 * (row + 1)); +} + +function find_ix(letter) { + for (let i = 0; i < tiles.length; i++) + if (tiles[i][0] === letter) + return tiles[i][1]; + throw new OperationError("Letter " + letter + " is not included in LS47"); +} + +export function derive_key(password) { + let i = 0; + let k = letters; + for (const c of password) { + let [row, col] = find_ix(c); + k = rotate_down(rotate_right(k, i, col), i, row); + i = (i + 1) % 7; + } + return k; +} + +function check_key(key) { + if (key.length !== letters.length) + throw new OperationError("Wrong key size"); + let counts = new Array(); + for (let i = 0; i < letters.length; i++) + counts[letters.charAt(i)] = 0; + for (const elem of letters){ + if (letters.indexOf(elem) === -1) + throw new OperationError("Letter " + elem + " not in LS47!"); + counts[elem]++; + if (counts[elem] > 1) + throw new OperationError("Letter duplicated in the key!"); + } +} + +function find_pos (key, letter) { + let index = key.indexOf(letter); + if (index >= 0 && index < 49) + return [Math.floor(index/7), index%7]; + throw new OperationError("Letter " + letter + " is not in the key!"); +} + +function find_at_pos(key, coord) { + return key.charAt(coord[1] + (coord[0] * 7)); +} + +function add_pos(a, b) { + return [(a[0] + b[0]) % 7, (a[1] + b[1]) % 7]; +} + +function sub_pos(a, b) { + let asub = a[0] - b[0]; + let bsub = a[1] - b[1]; + return [asub - (Math.floor(asub/7) * 7), bsub - (Math.floor(bsub/7) * 7)]; +} + +function encrypt(key, plaintext) { + check_key(key); + let mp = [0, 0]; + let ciphertext = ''; + for (const p of plaintext) { + let pp = find_pos(key, p); + let mix = find_ix(find_at_pos(key, mp)); + let cp = add_pos(pp, mix); + let c = find_at_pos(key, cp); + ciphertext += c; + key = rotate_right(key, pp[0], 1); + cp = find_pos(key, c); + key = rotate_down(key, cp[1], 1); + mp = add_pos(mp, find_ix(c)); + } + return ciphertext; +} + +function decrypt(key, ciphertext) { + check_key(key); + let mp = [0,0]; + let plaintext = ''; + for (const c of ciphertext) { + let cp = find_pos(key, c); + let mix = find_ix(find_at_pos(key, mp)); + let pp = sub_pos(cp, mix); + let p = find_at_pos(key, pp); + + plaintext += p; + key = rotate_right(key, pp[0], 1); + cp = find_pos(key, c); + key = rotate_down(key, cp[1], 1); + mp = add_pos(mp, find_ix(c)); + } + return plaintext; +} + +export function encrypt_pad(key, plaintext, signature, padding_size) { + init_tiles(); + check_key(key); + let padding = ""; + for (let i = 0; i < padding_size; i++) { + padding += letters.charAt(Math.floor(Math.random() * letters.length)); + } + return encrypt(key, padding+plaintext+'---'+signature); +} + +export function decrypt_pad(key, ciphertext, padding_size) { + init_tiles(); + check_key(key); + return decrypt(key, ciphertext).slice(padding_size); +} \ No newline at end of file diff --git a/src/core/operations/LS47Decrypt.mjs b/src/core/operations/LS47Decrypt.mjs new file mode 100644 index 00000000..ffda8f93 --- /dev/null +++ b/src/core/operations/LS47Decrypt.mjs @@ -0,0 +1,58 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import * as LS47 from "../lib/LS47.mjs" + +/** + * LS47 Decrypt operation + */ +class LS47Decrypt extends Operation { + + /** + * LS47Decrypt constructor + */ + constructor() { + super(); + + this.name = "LS47 Decrypt"; + this.module = "Crypto"; + this.description = ""; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Password", + type: "string", + value: "" + }, + { + name: "Padding", + type: "number", + value: 10 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + + this.padding_size = parseInt(args[1], 10); + + LS47.init_tiles(); + + let key = LS47.derive_key(args[0]); + return LS47.decrypt_pad(key, input, this.padding_size); + } + +} + +export default LS47Decrypt; diff --git a/src/core/operations/LS47Encrypt.mjs b/src/core/operations/LS47Encrypt.mjs new file mode 100644 index 00000000..bf3b0306 --- /dev/null +++ b/src/core/operations/LS47Encrypt.mjs @@ -0,0 +1,63 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; +import * as LS47 from "../lib/LS47.mjs" + +/** + * LS47 Encrypt operation + */ +class LS47Encrypt extends Operation { + + /** + * LS47Encrypt constructor + */ + constructor() { + super(); + + this.name = "LS47 Encrypt"; + this.module = "Crypto"; + this.description = ""; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Password", + type: "string", + value: "" + }, + { + name: "Padding", + type: "number", + value: 10 + }, + { + name: "Signature", + type: "string", + value: "" + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + + this.padding_size = parseInt(args[1], 10); + + LS47.init_tiles(); + + let key = LS47.derive_key(args[0]); + return LS47.encrypt_pad(key, input, args[2], this.padding_size); + } + +} + +export default LS47Encrypt; From 5cdd062ed9c639bf387c783667d8bd86302e8acb Mon Sep 17 00:00:00 2001 From: n1073645 Date: Tue, 28 Jan 2020 09:33:32 +0000 Subject: [PATCH 026/706] Linting done --- src/core/config/Categories.json | 2 + src/core/lib/LS47.mjs | 153 ++++++++++++++++++---------- src/core/operations/LS47Decrypt.mjs | 12 +-- src/core/operations/LS47Encrypt.mjs | 14 +-- 4 files changed, 112 insertions(+), 69 deletions(-) diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 53ca796d..1b810d37 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -75,6 +75,8 @@ "DES Decrypt", "Triple DES Encrypt", "Triple DES Decrypt", + "LS47 Encrypt", + "LS47 Decrypt", "RC2 Encrypt", "RC2 Decrypt", "RC4", diff --git a/src/core/lib/LS47.mjs b/src/core/lib/LS47.mjs index a4ef10a5..b028fc4f 100644 --- a/src/core/lib/LS47.mjs +++ b/src/core/lib/LS47.mjs @@ -6,21 +6,27 @@ import OperationError from "../errors/OperationError.mjs"; -let letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()"; -let tiles = []; +const letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()"; +const tiles = []; -export function init_tiles() { +/** + * + */ +export function initTiles() { for (let i = 0; i < 49; i++) tiles.push([letters.charAt(i), [Math.floor(i/7), i % 7]]); } -function rotate_down(key, col, n) { - let lines = []; - for (let i = 0; i < 7; i++) +/** + * + */ +function rotateDown(key, col, n) { + const lines = []; + for (let i = 0; i < 7; i++) lines.push(key.slice(i*7, (i + 1) * 7)); - let lefts = []; + const lefts = []; let mids = []; - let rights = []; + const rights = []; lines.forEach((element) => { lefts.push(element.slice(0, col)); mids.push(element.charAt(col)); @@ -34,37 +40,49 @@ function rotate_down(key, col, n) { return result; } -function rotate_right(key, row, n) { - let mid = key.slice(row * 7, (row + 1) * 7); +/** + * + */ +function rotateRight(key, row, n) { + const mid = key.slice(row * 7, (row + 1) * 7); n = (7 - n % 7) % 7; return key.slice(0, 7 * row) + mid.slice(n) + mid.slice(0, n) + key.slice(7 * (row + 1)); } -function find_ix(letter) { +/** + * + */ +function findIx(letter) { for (let i = 0; i < tiles.length; i++) if (tiles[i][0] === letter) return tiles[i][1]; throw new OperationError("Letter " + letter + " is not included in LS47"); } -export function derive_key(password) { +/** + * + */ +export function deriveKey(password) { let i = 0; let k = letters; for (const c of password) { - let [row, col] = find_ix(c); - k = rotate_down(rotate_right(k, i, col), i, row); + const [row, col] = findIx(c); + k = rotateDown(rotateRight(k, i, col), i, row); i = (i + 1) % 7; } return k; } -function check_key(key) { +/** + * + */ +function checkKey(key) { if (key.length !== letters.length) throw new OperationError("Wrong key size"); - let counts = new Array(); + const counts = new Array(); for (let i = 0; i < letters.length; i++) counts[letters.charAt(i)] = 0; - for (const elem of letters){ + for (const elem of letters) { if (letters.indexOf(elem) === -1) throw new OperationError("Letter " + elem + " not in LS47!"); counts[elem]++; @@ -73,76 +91,99 @@ function check_key(key) { } } -function find_pos (key, letter) { - let index = key.indexOf(letter); +/** + * + */ +function findPos (key, letter) { + const index = key.indexOf(letter); if (index >= 0 && index < 49) return [Math.floor(index/7), index%7]; throw new OperationError("Letter " + letter + " is not in the key!"); } -function find_at_pos(key, coord) { +/** + * + */ +function findAtPos(key, coord) { return key.charAt(coord[1] + (coord[0] * 7)); } -function add_pos(a, b) { +/** + * + */ +function addPos(a, b) { return [(a[0] + b[0]) % 7, (a[1] + b[1]) % 7]; } -function sub_pos(a, b) { - let asub = a[0] - b[0]; - let bsub = a[1] - b[1]; +/** + * + */ +function subPos(a, b) { + const asub = a[0] - b[0]; + const bsub = a[1] - b[1]; return [asub - (Math.floor(asub/7) * 7), bsub - (Math.floor(bsub/7) * 7)]; } +/** + * + */ function encrypt(key, plaintext) { - check_key(key); + checkKey(key); let mp = [0, 0]; - let ciphertext = ''; + let ciphertext = ""; for (const p of plaintext) { - let pp = find_pos(key, p); - let mix = find_ix(find_at_pos(key, mp)); - let cp = add_pos(pp, mix); - let c = find_at_pos(key, cp); + const pp = findPos(key, p); + const mix = findIx(findAtPos(key, mp)); + let cp = addPos(pp, mix); + const c = findAtPos(key, cp); ciphertext += c; - key = rotate_right(key, pp[0], 1); - cp = find_pos(key, c); - key = rotate_down(key, cp[1], 1); - mp = add_pos(mp, find_ix(c)); + key = rotateRight(key, pp[0], 1); + cp = findPos(key, c); + key = rotateDown(key, cp[1], 1); + mp = addPos(mp, findIx(c)); } return ciphertext; } +/** + * + */ function decrypt(key, ciphertext) { - check_key(key); - let mp = [0,0]; - let plaintext = ''; + checkKey(key); + let mp = [0, 0]; + let plaintext = ""; for (const c of ciphertext) { - let cp = find_pos(key, c); - let mix = find_ix(find_at_pos(key, mp)); - let pp = sub_pos(cp, mix); - let p = find_at_pos(key, pp); - + let cp = findPos(key, c); + const mix = findIx(findAtPos(key, mp)); + const pp = subPos(cp, mix); + const p = findAtPos(key, pp); plaintext += p; - key = rotate_right(key, pp[0], 1); - cp = find_pos(key, c); - key = rotate_down(key, cp[1], 1); - mp = add_pos(mp, find_ix(c)); + key = rotateRight(key, pp[0], 1); + cp = findPos(key, c); + key = rotateDown(key, cp[1], 1); + mp = addPos(mp, findIx(c)); } return plaintext; } -export function encrypt_pad(key, plaintext, signature, padding_size) { - init_tiles(); - check_key(key); +/** + * + */ +export function encryptPad(key, plaintext, signature, paddingSize) { + initTiles(); + checkKey(key); let padding = ""; - for (let i = 0; i < padding_size; i++) { + for (let i = 0; i < paddingSize; i++) { padding += letters.charAt(Math.floor(Math.random() * letters.length)); } - return encrypt(key, padding+plaintext+'---'+signature); + return encrypt(key, padding+plaintext+"---"+signature); } -export function decrypt_pad(key, ciphertext, padding_size) { - init_tiles(); - check_key(key); - return decrypt(key, ciphertext).slice(padding_size); -} \ No newline at end of file +/** + * + */ +export function decryptPad(key, ciphertext, paddingSize) { + initTiles(); + checkKey(key); + return decrypt(key, ciphertext).slice(paddingSize); +} diff --git a/src/core/operations/LS47Decrypt.mjs b/src/core/operations/LS47Decrypt.mjs index ffda8f93..a5a92ebf 100644 --- a/src/core/operations/LS47Decrypt.mjs +++ b/src/core/operations/LS47Decrypt.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation.mjs"; -import * as LS47 from "../lib/LS47.mjs" +import * as LS47 from "../lib/LS47.mjs"; /** * LS47 Decrypt operation @@ -45,12 +45,12 @@ class LS47Decrypt extends Operation { */ run(input, args) { - this.padding_size = parseInt(args[1], 10); + this.paddingSize = parseInt(args[1], 10); - LS47.init_tiles(); - - let key = LS47.derive_key(args[0]); - return LS47.decrypt_pad(key, input, this.padding_size); + LS47.initTiles(); + + const key = LS47.deriveKey(args[0]); + return LS47.decryptPad(key, input, this.paddingSize); } } diff --git a/src/core/operations/LS47Encrypt.mjs b/src/core/operations/LS47Encrypt.mjs index bf3b0306..f82baaab 100644 --- a/src/core/operations/LS47Encrypt.mjs +++ b/src/core/operations/LS47Encrypt.mjs @@ -5,7 +5,7 @@ */ import Operation from "../Operation.mjs"; -import * as LS47 from "../lib/LS47.mjs" +import * as LS47 from "../lib/LS47.mjs"; /** * LS47 Encrypt operation @@ -49,13 +49,13 @@ class LS47Encrypt extends Operation { * @returns {string} */ run(input, args) { - - this.padding_size = parseInt(args[1], 10); - LS47.init_tiles(); - - let key = LS47.derive_key(args[0]); - return LS47.encrypt_pad(key, input, args[2], this.padding_size); + this.paddingSize = parseInt(args[1], 10); + + LS47.initTiles(); + + const key = LS47.deriveKey(args[0]); + return LS47.encryptPad(key, input, args[2], this.paddingSize); } } From 6fd929160d9eb5ee332af80c90e823513b0a86f1 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Tue, 28 Jan 2020 10:35:01 +0000 Subject: [PATCH 027/706] Comments and linting. --- src/core/lib/LS47.mjs | 57 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/src/core/lib/LS47.mjs b/src/core/lib/LS47.mjs index b028fc4f..6696aafc 100644 --- a/src/core/lib/LS47.mjs +++ b/src/core/lib/LS47.mjs @@ -10,7 +10,7 @@ const letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()"; const tiles = []; /** - * + * Initialises the tiles with values and positions. */ export function initTiles() { for (let i = 0; i < 49; i++) @@ -18,7 +18,12 @@ export function initTiles() { } /** + * Rotates the key "down". * + * @param {string} key + * @param {number} col + * @param {number} n + * @returns {string} */ function rotateDown(key, col, n) { const lines = []; @@ -41,7 +46,12 @@ function rotateDown(key, col, n) { } /** + * Rotates the key "right". * + * @param {string} key + * @param {number} row + * @param {number} n + * @returns {string} */ function rotateRight(key, row, n) { const mid = key.slice(row * 7, (row + 1) * 7); @@ -50,7 +60,10 @@ function rotateRight(key, row, n) { } /** + * Finds the position of a letter in the tiles. * + * @param {string} letter + * @returns {string} */ function findIx(letter) { for (let i = 0; i < tiles.length; i++) @@ -60,7 +73,10 @@ function findIx(letter) { } /** + * Derives key from the input password. * + * @param {string} password + * @returns {string} */ export function deriveKey(password) { let i = 0; @@ -74,7 +90,9 @@ export function deriveKey(password) { } /** + * Checks the key is a valid key. * + * @param {string} key */ function checkKey(key) { if (key.length !== letters.length) @@ -92,7 +110,11 @@ function checkKey(key) { } /** + * Finds the position of a letter in they key. * + * @param {letter} key + * @param {string} letter + * @returns {object} */ function findPos (key, letter) { const index = key.indexOf(letter); @@ -102,21 +124,35 @@ function findPos (key, letter) { } /** + * Returns the character at the position on the tiles. * + * @param {string} key + * @param {object} coord + * @returns {string} */ function findAtPos(key, coord) { return key.charAt(coord[1] + (coord[0] * 7)); } /** + * Returns new position by adding two positions. * + * @param {object} a + * @param {object} b + * @returns {object} */ function addPos(a, b) { return [(a[0] + b[0]) % 7, (a[1] + b[1]) % 7]; } /** + * Returns new position by subtracting two positions. + * Note: We have to manually do the remainder division, since JS does not + * operate correctly on negative numbers (e.g. -3 % 4 = -3 when it should be 1). * + * @param {object} a + * @param {object} b + * @returns {object} */ function subPos(a, b) { const asub = a[0] - b[0]; @@ -125,7 +161,11 @@ function subPos(a, b) { } /** + * Encrypts the plaintext string. * + * @param {string} key + * @param {string} plaintext + * @returns {string} */ function encrypt(key, plaintext) { checkKey(key); @@ -146,7 +186,11 @@ function encrypt(key, plaintext) { } /** + * Decrypts the ciphertext string. * + * @param {string} key + * @param {string} ciphertext + * @returns {string} */ function decrypt(key, ciphertext) { checkKey(key); @@ -167,7 +211,13 @@ function decrypt(key, ciphertext) { } /** + * Adds padding to the input. * + * @param {string} key + * @param {string} plaintext + * @param {string} signature + * @param {number} paddingSize + * @returns {string} */ export function encryptPad(key, plaintext, signature, paddingSize) { initTiles(); @@ -180,7 +230,12 @@ export function encryptPad(key, plaintext, signature, paddingSize) { } /** + * Removes padding from the ouput. * + * @param {string} key + * @param {string} ciphertext + * @param {number} paddingSize + * @returns {string} */ export function decryptPad(key, ciphertext, paddingSize) { initTiles(); From e71794d362cf8112fc940a2ae6177c84ffce3bb5 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Fri, 14 Feb 2020 12:28:12 +0000 Subject: [PATCH 028/706] Tests added for LS47 --- src/core/operations/LS47Decrypt.mjs | 4 +-- src/core/operations/LS47Encrypt.mjs | 4 +-- tests/operations/index.mjs | 1 + tests/operations/tests/LS47.mjs | 45 +++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 tests/operations/tests/LS47.mjs diff --git a/src/core/operations/LS47Decrypt.mjs b/src/core/operations/LS47Decrypt.mjs index a5a92ebf..cb92cd27 100644 --- a/src/core/operations/LS47Decrypt.mjs +++ b/src/core/operations/LS47Decrypt.mjs @@ -20,8 +20,8 @@ class LS47Decrypt extends Operation { this.name = "LS47 Decrypt"; this.module = "Crypto"; - this.description = ""; - this.infoURL = ""; + this.description = "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.\nThe LS47 alphabet consists of following characters: _abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()\nA LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption."; + this.infoURL = "https://gitea.blesmrt.net/exa/ls47/src/branch/master"; this.inputType = "string"; this.outputType = "string"; this.args = [ diff --git a/src/core/operations/LS47Encrypt.mjs b/src/core/operations/LS47Encrypt.mjs index f82baaab..51283844 100644 --- a/src/core/operations/LS47Encrypt.mjs +++ b/src/core/operations/LS47Encrypt.mjs @@ -20,8 +20,8 @@ class LS47Encrypt extends Operation { this.name = "LS47 Encrypt"; this.module = "Crypto"; - this.description = ""; - this.infoURL = ""; + this.description = "This is a slight improvement of the ElsieFour cipher as described by Alan Kaminsky. We use 7x7 characters instead of original (barely fitting) 6x6, to be able to encrypt some structured information. We also describe a simple key-expansion algorithm, because remembering passwords is popular. Similar security considerations as with ElsieFour hold.\nThe LS47 alphabet consists of following characters: _abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()\nA LS47 key is a permutation of the alphabet that is then represented in a 7x7 grid used for the encryption or decryption."; + this.infoURL = "https://gitea.blesmrt.net/exa/ls47/src/branch/master"; this.inputType = "string"; this.outputType = "string"; this.args = [ diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index bf440414..b3731727 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -96,6 +96,7 @@ import "./tests/DefangIP.mjs"; import "./tests/ParseUDP.mjs"; import "./tests/AvroToJSON.mjs"; import "./tests/Lorenz.mjs"; +import "./tests/LS47.mjs"; // Cannot test operations that use the File type yet diff --git a/tests/operations/tests/LS47.mjs b/tests/operations/tests/LS47.mjs new file mode 100644 index 00000000..40d876ee --- /dev/null +++ b/tests/operations/tests/LS47.mjs @@ -0,0 +1,45 @@ +/** + * Cartesian Product tests. + * + * @author n1073645 [n1073645@gmail.com] + * + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "LS47 Encrypt", + input: "thequickbrownfoxjumped", + expectedOutput: "(,t74ci78cp/8trx*yesu:alp1wqy", + recipeConfig: [ + { + op: "LS47 Encrypt", + args: ["helloworld", 0, "test"], + }, + ], + }, + { + name: "LS47 Decrypt", + input: "(,t74ci78cp/8trx*yesu:alp1wqy", + expectedOutput: "thequickbrownfoxjumped---test", + recipeConfig: [ + { + op: "LS47 Decrypt", + args: ["helloworld", 0], + }, + ], + }, + { + name: "LS47 Encrypt", + input: "thequickbrownfoxjumped", + expectedOutput: "Letter H is not included in LS47", + recipeConfig: [ + { + op: "LS47 Encrypt", + args: ["Helloworld", 0, "test"], + }, + ], + } +]); From e91e993fb5e7ec99db8fcb179fbd18a3f53b97bc Mon Sep 17 00:00:00 2001 From: n1073645 <57447333+n1073645@users.noreply.github.com> Date: Fri, 14 Feb 2020 13:43:30 +0000 Subject: [PATCH 029/706] Update LS47.mjs --- tests/operations/tests/LS47.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/operations/tests/LS47.mjs b/tests/operations/tests/LS47.mjs index 40d876ee..ce613923 100644 --- a/tests/operations/tests/LS47.mjs +++ b/tests/operations/tests/LS47.mjs @@ -1,5 +1,5 @@ /** - * Cartesian Product tests. + * LS47 tests. * * @author n1073645 [n1073645@gmail.com] * From 0182cdda69f7c877746084a75600d87b2cb34e19 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 16 May 2020 00:42:02 +0200 Subject: [PATCH 030/706] Base85: Fix alphabetName --- src/core/lib/Base85.mjs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/core/lib/Base85.mjs b/src/core/lib/Base85.mjs index 8da729e2..e5778132 100644 --- a/src/core/lib/Base85.mjs +++ b/src/core/lib/Base85.mjs @@ -1,3 +1,5 @@ +import Utils from "../Utils.mjs"; + /** * Base85 resources. * @@ -32,13 +34,12 @@ export const ALPHABET_OPTIONS = [ * @returns {string} */ export function alphabetName(alphabet) { - alphabet = alphabet.replace("'", "'"); - alphabet = alphabet.replace("\"", """); - alphabet = alphabet.replace("\\", "\"); + alphabet = escape(alphabet); let name; ALPHABET_OPTIONS.forEach(function(a) { - if (escape(alphabet) === escape(a.value)) name = a.name; + const expanded = Utils.expandAlphRange(a.value).join(""); + if (alphabet === escape(expanded)) name = a.name; }); return name; From 103ecff6a7465b7a46a8f452885ec99d0e45ea26 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 16 May 2020 00:42:31 +0200 Subject: [PATCH 031/706] Base85: Ignore whitespace --- src/core/operations/FromBase85.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index c874d5dc..c0d0328e 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -52,6 +52,8 @@ class FromBase85 extends Operation { if (input.length === 0) return []; + input = input.replace(/\s+/g, ""); + const matches = input.match(/<~(.+?)~>/); if (matches !== null) input = matches[1]; From 15dd9d4c93fa5bcfb1341ad8dccdb5671ae08d22 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 16 May 2020 00:42:50 +0200 Subject: [PATCH 032/706] Add magic checks for base85 --- src/core/operations/FromBase85.mjs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index c0d0328e..42f37a1c 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -33,6 +33,23 @@ class FromBase85 extends Operation { value: ALPHABET_OPTIONS }, ]; + this.checks = [ + { + pattern: "^\\s*(?:<~)?(?:(?:\\s*[!-u]){5}|\\s*z)+[!-u\\s]*(?:~>)?\\s*$", + flags: "i", + args: ["!-u"] + }, + { + pattern: "^(?:\\s*[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#])+\\s*$", + flags: "i", + args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"] + }, + { + pattern: "^(?:\\s*[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~])+\\s*$", + flags: "i", + args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"] + }, + ]; } /** From eab1be0e2c58c3d69f8b2c477e4102f601b611c7 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 20 May 2020 00:23:50 +0200 Subject: [PATCH 033/706] Magic base85: Remove 'i' flag --- src/core/operations/FromBase85.mjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index 42f37a1c..22033f99 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -36,17 +36,14 @@ class FromBase85 extends Operation { this.checks = [ { pattern: "^\\s*(?:<~)?(?:(?:\\s*[!-u]){5}|\\s*z)+[!-u\\s]*(?:~>)?\\s*$", - flags: "i", args: ["!-u"] }, { pattern: "^(?:\\s*[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#])+\\s*$", - flags: "i", args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"] }, { pattern: "^(?:\\s*[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~])+\\s*$", - flags: "i", args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"] }, ]; From 1294d764e258bb6caa739b6111bb6d79a61d394f Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 22 May 2020 03:30:15 +0200 Subject: [PATCH 034/706] Base85: Only remove start and end markers with standard/ascii85 encoding --- src/core/operations/FromBase85.mjs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index 22033f99..09ded171 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -68,8 +68,10 @@ class FromBase85 extends Operation { input = input.replace(/\s+/g, ""); - const matches = input.match(/<~(.+?)~>/); - if (matches !== null) input = matches[1]; + if (encoding === "Standard") { + const matches = input.match(/<~(.+?)~>/); + if (matches !== null) input = matches[1]; + } let i = 0; let block, blockBytes; From ee408f7add6d633b9c42a6677f5bfa75055e9ca6 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 22 May 2020 03:30:57 +0200 Subject: [PATCH 035/706] Base85: Update magic regexes to require 20 non-whitespace base85 chars --- src/core/operations/FromBase85.mjs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index 09ded171..9d73baa1 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -35,16 +35,31 @@ class FromBase85 extends Operation { ]; this.checks = [ { - pattern: "^\\s*(?:<~)?(?:(?:\\s*[!-u]){5}|\\s*z)+[!-u\\s]*(?:~>)?\\s*$", - args: ["!-u"] + pattern: + "^\\s*(?:<~)?" + // Optional whitespace and starting marker + "[\\s!-uz]*" + // Any amount of base85 characters and whitespace + "[!-uz]{20}" + // At least 20 continoues base85 characters without whitespace + "[\\s!-uz]*" + // Any amount of base85 characters and whitespace + "(?:~>)?\\s*$", // Optional ending marker and whitespace + args: ["!-u"], }, { - pattern: "^(?:\\s*[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#])+\\s*$", - args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"] + pattern: + "^" + + "[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" + + "[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{20}" + // At least 20 continoues base85 characters without whitespace + "[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" + + "$", + args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"], }, { - pattern: "^(?:\\s*[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~])+\\s*$", - args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"] + pattern: + "^" + + "[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" + + "[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{20}" + // At least 20 continoues base85 characters without whitespace + "[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" + + "$", + args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"], }, ]; } From f007c093eb3820161ab801a83e94a9da45d4f961 Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 15:06:41 +0000 Subject: [PATCH 036/706] Emulation of the WW2 SIGABA machine I have created an emulation of the SIGABA machine and have tested it against some test data from a Master's thesis by Miao Ai: https://scholarworks.sjsu.edu/cgi/viewcontent.cgi?article=1237&context=etd_projects --- src/core/lib/SIGABA.mjs | 501 +++++++++++++++++++++++++++++++++ src/core/operations/SIGABA.mjs | 293 +++++++++++++++++++ 2 files changed, 794 insertions(+) create mode 100644 src/core/lib/SIGABA.mjs create mode 100644 src/core/operations/SIGABA.mjs diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs new file mode 100644 index 00000000..c35eb3a5 --- /dev/null +++ b/src/core/lib/SIGABA.mjs @@ -0,0 +1,501 @@ +/** +Emulation of the SIGABA machine + +@author hettysymes +*/ + +/** +A set of randomised example SIGABA cipher/control rotors (these rotors are interchangeable). Cipher and control rotors can be referred to as C and R rotors respectively. +*/ + +export const CR_ROTORS = [ + {name: "Example 1", value: "SRGWANHPJZFXVIDQCEUKBYOLMT"}, + {name: "Example 2", value: "THQEFSAZVKJYULBODCPXNIMWRG"}, + {name: "Example 3", value: "XDTUYLEVFNQZBPOGIRCSMHWKAJ"}, + {name: "Example 4", value: "LOHDMCWUPSTNGVXYFJREQIKBZA"}, + {name: "Example 5", value: "ERXWNZQIJYLVOFUMSGHTCKPBDA"}, + {name: "Example 6", value: "FQECYHJIOUMDZVPSLKRTGWXBAN"}, + {name: "Example 7", value: "TBYIUMKZDJSOPEWXVANHLCFQGR"}, + {name: "Example 8", value: "QZUPDTFNYIAOMLEBWJXCGHKRSV"}, + {name: "Example 9", value: "CZWNHEMPOVXLKRSIDGJFYBTQAU"}, + {name: "Example 10", value: "ENPXJVKYQBFZTICAGMOHWRLDUS"} +]; + +/** +A set of randomised example SIGABA index rotors (may be referred to as I rotors). +*/ + +export const I_ROTORS = [ + {name: "Example 1", value: "6201348957"}, + {name: "Example 2", value: "6147253089"}, + {name: "Example 3", value: "8239647510"}, + {name: "Example 4", value: "7194835260"}, + {name: "Example 5", value: "4873205916"} +]; + +export const NUMBERS = "0123456789".split(""); + +/** +Converts a letter to uppercase (if it already isn't) + +@param {char} letter - letter to convert to upper case +@returns {char} +*/ +export function convToUpperCase(letter){ + const charCode = letter.charCodeAt(); + if (97<=charCode && charCode<=122){ + return String.fromCharCode(charCode-32); + } + return letter; +} + +/** +The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks. +*/ +export class SigabaMachine{ + /** + SigabaMachine constructor + + @param {Object[]} cipherRotors - list of CRRotors + @param {Object[]} controlRotors - list of CRRotors + @param {object[]} indexRotors - list of IRotors + */ + constructor(cipherRotors, controlRotors, indexRotors){ + this.cipherBank = new CipherBank(cipherRotors); + this.controlBank = new ControlBank(controlRotors); + this.indexBank = new IndexBank(indexRotors); + } + + /** + Steps all the correct rotors in the machine. + */ + step(){ + const controlOut = this.controlBank.goThroughControl(); + const indexOut = this.indexBank.goThroughIndex(controlOut); + this.cipherBank.step(indexOut); + } + + /** + Encrypts a letter. A space is converted to a "Z" before encryption, and a "Z" is converted to an "X". This allows spaces to be encrypted. + + @param {char} letter - letter to encrypt + @returns {char} + */ + encryptLetter(letter){ + letter = convToUpperCase(letter); + if (letter == " "){ + letter = "Z"; + } + else if (letter == "Z") { + letter = "X"; + } + const encryptedLetter = this.cipherBank.encrypt(letter); + this.step(); + return encryptedLetter; + } + + /** + Decrypts a letter. A letter decrypted as a "Z" is converted to a space before it is output, since spaces are converted to "Z"s before encryption. + + @param {char} letter - letter to decrypt + @returns {char} + */ + decryptLetter(letter){ + letter = convToUpperCase(letter); + let decryptedLetter = this.cipherBank.decrypt(letter); + if (decryptedLetter == "Z"){ + decryptedLetter = " "; + } + this.step(); + return decryptedLetter; + } + + /** + Encrypts a message of one or more letters + + @param {string} msg - message to encrypt + @returns {string} + */ + encrypt(msg){ + let ciphertext = ""; + for (const letter of msg){ + ciphertext = ciphertext.concat(this.encryptLetter(letter)); + } + return ciphertext; + } + + /** + Decrypts a message of one or more letters + + @param {string} msg - message to decrypt + @returns {string} + */ + decrypt(msg){ + let plaintext = ""; + for (const letter of msg){ + plaintext = plaintext.concat(this.decryptLetter(letter)); + } + return plaintext; + } + +} + +/** +The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation. +*/ +export class CipherBank{ + /** + CipherBank constructor + + @param {Object[]} rotors - list of CRRotors + */ + constructor(rotors){ + this.rotors = rotors; + } + + /** + Encrypts a letter through the cipher rotors (signal goes from left-to-right) + + @param {char} inputPos - the input position of the signal (letter to be encrypted) + @returns {char} + */ + encrypt(inputPos){ + for (let rotor of this.rotors){ + inputPos = rotor.crypt(inputPos, "leftToRight"); + } + return inputPos; + } + + /** + Decrypts a letter through the cipher rotors (signal goes from right-to-left) + + @param {char} inputPos - the input position of the signal (letter to be decrypted) + @returns {char} + */ + decrypt(inputPos){ + const revOrderedRotors = [...this.rotors].reverse(); + for (let rotor of revOrderedRotors){ + inputPos = rotor.crypt(inputPos, "rightToLeft"); + } + return inputPos; + } + + /** + Step the cipher rotors forward according to the inputs from the index rotors + + @param {number[]} indexInputs - the inputs from the index rotors + */ + step(indexInputs){ + const logicDict = {0: [0,9], 1:[7,8], 2:[5,6], 3:[3,4], 4:[1,2]}; + let rotorsToMove = []; + for (const key in logicDict){ + const item = logicDict[key]; + for (const i of indexInputs){ + if (item.includes(i)){ + rotorsToMove.push(this.rotors[key]); + break; + } + } + } + for (let rotor of rotorsToMove){ + rotor.step(); + } + } + +} + +/** +The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left. +*/ +export class ControlBank{ + /** + ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors. + + @param {Object[]} rotors - list of CRRotors + */ + constructor(rotors){ + this.rotors = [...rotors].reverse(); + this.numberOfMoves = 1; + } + + /** + Encrypts a letter. + + @param {char} inputPos - the input position of the signal + @returns {char} + */ + crypt(inputPos){ + for (let rotor of this.rotors){ + inputPos = rotor.crypt(inputPos, "rightToLeft"); + } + return inputPos; + } + + /** + Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I". + + @returns {number[]} + */ + getOutputs(){ + const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")]; + const logicDict = {1:"B", 2:"C", 3:"DE", 4:"FGH", 5:"IJK", 6:"LMNO", 7:"PQRST", 8:"UVWXYZ", 9:"A"}; + let numberOutputs = []; + for (let key in logicDict){ + const item = logicDict[key]; + for (let output of outputs){ + if (item.includes(output)){ + numberOutputs.push(key); + break; + } + } + } + return numberOutputs; + } + + /** + Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared. + */ + step(){ + const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3]; + this.numberOfMoves ++; + FRotor.step(); + if (this.numberOfMoves%26 == 0){ + MRotor.step(); + } + if (this.numberOfMoves%(26*26) == 0){ + SRotor.step(); + } + } + + /** + The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them. + + @returns {number[]} + */ + goThroughControl(){ + const outputs = this.getOutputs(); + this.step(); + return outputs; + } + +} + +/** +The index rotor bank consists of 5 index rotors all placed in the forwards orientation. +*/ +export class IndexBank{ + /** + IndexBank constructor + + @param {Object[]} rotors - list of IRotors + */ + constructor(rotors){ + this.rotors = rotors; + } + + /** + Encrypts a number. + + @param {number} inputPos - the input position of the signal + @returns {number} + */ + crypt(inputPos){ + for (let rotor of this.rotors){ + inputPos = rotor.crypt(inputPos); + } + return inputPos; + } + + /** + The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors. + + @param {number[]} - inputs from the control rotors + @returns {number[]} + */ + goThroughIndex(controlInputs){ + let outputs = []; + for (const inp of controlInputs){ + outputs.push(this.crypt(inp)); + } + return outputs; + } + +} + +/** +Rotor class +*/ +export class Rotor{ + /** + Rotor constructor + + @param {number[]} wireSetting - the wirings within the rotor: mapping from left-to-right, the index of the number in the list maps onto the number at that index + @param {bool} rev - true if the rotor is reversed, false if it isn't + @param {number} key - the starting position or state of the rotor + */ + constructor(wireSetting, key, rev){ + this.state = key; + this.numMapping = this.getNumMapping(wireSetting, rev); + this.posMapping = this.getPosMapping(rev); + } + + /** + Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed) + + @param {number[]} wireSetting - the wirings within the rotors + @param {bool} rev - true if reversed, false if not + @returns {number[]} + */ + getNumMapping(wireSetting, rev){ + if (rev==false){ + return wireSetting; + } + else { + const length = wireSetting.length; + let tempMapping = new Array(length); + for (let i=0; ithis.state-length; i--){ + let res = i%length; + if (res<0){ + res += length; + } + posMapping.push(res); + } + } + return posMapping; + } + + /** + Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex. + + @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt) + @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor + @returns {number} + */ + cryptNum(inputPos, direction){ + const inpNum = this.posMapping[inputPos]; + var outNum; + if (direction == "leftToRight"){ + outNum = this.numMapping[inpNum]; + } + else if (direction == "rightToLeft") { + outNum = this.numMapping.indexOf(inpNum); + } + const outPos = this.posMapping.indexOf(outNum); + return outPos; + } + + /** + Steps the rotor. The number at position 0 will be moved to position 1 etc. + */ + step(){ + const lastNum = this.posMapping.pop(); + this.posMapping.splice(0, 0, lastNum); + this.state = this.posMapping[0]; + } + +} + +/** +A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation. +*/ +export class CRRotor extends Rotor{ + + /** + CRRotor constructor + + @param {string} wireSetting - the rotor wirings (string of letters) + @param {char} key - initial state of rotor + @param {bool} rev - true if reversed, false if not + */ + constructor(wireSetting, key, rev=false){ + wireSetting = wireSetting.split("").map(CRRotor.letterToNum); + super(wireSetting, CRRotor.letterToNum(key), rev); + } + + /** + Static function which converts a letter into its number i.e. its offset from the letter "A" + + @param {char} letter - letter to convert to number + @returns {number} + */ + static letterToNum(letter){ + return letter.charCodeAt()-65; + } + + /** + Static function which converts a number (a letter's offset from "A") into its letter + + @param {number} num - number to convert to letter + @returns {char} + */ + static numToLetter(num){ + return String.fromCharCode(num+65); + } + + /** + Encrypts/decrypts a letter. + + @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.) + @param {string} direction - one of "leftToRight" and "rightToLeft" + @returns {char} + */ + crypt(inputPos, direction){ + inputPos = CRRotor.letterToNum(inputPos); + const outPos = this.cryptNum(inputPos, direction); + return CRRotor.numToLetter(outPos); + } + +} + +/** +An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption. +*/ +export class IRotor extends Rotor{ + /** + IRotor constructor + + @param {string} wireSetting - the rotor wirings (string of numbers) + @param {char} key - initial state of rotor + */ + constructor(wireSetting, key){ + wireSetting = wireSetting.split("").map(Number); + super(wireSetting, Number(key), false); + } + + /** + Encrypts a number + + @param {number} inputPos - the input position of the signal + @returns {number} + */ + crypt(inputPos){ + return this.cryptNum(inputPos, "leftToRight"); + } + +} diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs new file mode 100644 index 00000000..78f05530 --- /dev/null +++ b/src/core/operations/SIGABA.mjs @@ -0,0 +1,293 @@ +/** +Emulation of the SIGABA machine. + +@author hettysymes +*/ + +import Operation from "../Operation.mjs"; +import OperationError from "../errors/OperationError.mjs"; +import {LETTERS} from "../lib/Enigma.mjs"; +import {NUMBERS, CR_ROTORS, I_ROTORS, SigabaMachine, CRRotor, IRotor} from "../lib/SIGABA.mjs"; + +/** +Sigaba operation +*/ +class Sigaba extends Operation{ +/** +Sigaba constructor +*/ +constructor(){ +super(); + +this.name = "SIGABA"; +this.module = "SIGABA"; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. The idea behind its design was to truly randomise the motion of the rotors. In comparison, Enigma, which rotates its rotors once every key pressed, has much less randomised rotor movements. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode."; + this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "1st (left-hand) cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "1st cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "1st cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "2nd cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "2nd cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "2nd cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "3rd (middle) cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "3rd cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "3rd cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "4th cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "4th cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "4th cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "5th (right-hand) cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "5th cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "5th cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "1st (left-hand) control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "1st control rotor reversed", + type: "boolean", + value: false + }, + { + name: "1st control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "2nd control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "2nd control rotor reversed", + type: "boolean", + value: false + }, + { + name: "2nd control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "3rd (middle) control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "3rd control rotor reversed", + type: "boolean", + value: false + }, + { + name: "3rd control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "4th control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "4th control rotor reversed", + type: "boolean", + value: false + }, + { + name: "4th control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "5th (right-hand) control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "5th control rotor reversed", + type: "boolean", + value: false + }, + { + name: "5th control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "1st (left-hand) index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "1st index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "2nd index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "2nd index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "3rd (middle) index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "3rd index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "4th index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "4th index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "5th (right-hand) index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "5th index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "SIGABA mode", + type: "option", + value: ["Encrypt", "Decrypt"] + } + ]; + } + + /** + @param {string} rotor - rotor wirings + @returns {string} + */ + + parseRotorStr(rotor){ + if (rotor === ""){ + throw new OperationError(`All rotor wirings must be provided.`); + } + return rotor; + } + + run(input, args){ + const sigabaSwitch = args[40]; + const cipherRotors = []; + const controlRotors = []; + const indexRotors = []; + for (let i=0; i<5; i++){ + const rotorWiring = this.parseRotorStr(args[i*3]); + cipherRotors.push(new CRRotor(rotorWiring, args[i*3+2], args[i*3+1])); + } + for (let i=5; i<10; i++){ + const rotorWiring = this.parseRotorStr(args[i*3]); + controlRotors.push(new CRRotor(rotorWiring, args[i*3+2], args[i*3+1])); + } + for (let i=15; i<20; i++){ + const rotorWiring = this.parseRotorStr(args[i*2]); + indexRotors.push(new IRotor(rotorWiring, args[i*2+1])); + } + const sigaba = new SigabaMachine(cipherRotors, controlRotors, indexRotors); + var result; + if (sigabaSwitch === "Encrypt"){ + result = sigaba.encrypt(input); + } + else if (sigabaSwitch === "Decrypt") { + result = sigaba.decrypt(input); + } + return result; + } + +} +export default Sigaba; From 5d01b06877417120826826f823565c202a022b53 Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 15:37:07 +0000 Subject: [PATCH 037/706] Added copyright and clarified description --- src/core/lib/SIGABA.mjs | 2 ++ src/core/operations/SIGABA.mjs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index c35eb3a5..b56b3e24 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -2,6 +2,8 @@ Emulation of the SIGABA machine @author hettysymes +@copyright hettysymes 2020 +@license Apache-2.0 */ /** diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs index 78f05530..2f42c501 100644 --- a/src/core/operations/SIGABA.mjs +++ b/src/core/operations/SIGABA.mjs @@ -2,6 +2,8 @@ Emulation of the SIGABA machine. @author hettysymes +@copyright hettysymes 2020 +@license Apache-2.0 */ import Operation from "../Operation.mjs"; @@ -21,7 +23,7 @@ super(); this.name = "SIGABA"; this.module = "SIGABA"; - this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. The idea behind its design was to truly randomise the motion of the rotors. In comparison, Enigma, which rotates its rotors once every key pressed, has much less randomised rotor movements. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode."; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex than other rotor machines of its time, such as Enigma. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode."; this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; this.inputType = "string"; this.outputType = "string"; From 938385c18b5b66c691fc08ed38949491107c75de Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 16:49:04 +0000 Subject: [PATCH 038/706] Fixed grunt lint errors --- src/core/lib/SIGABA.mjs | 156 +++++----- src/core/operations/SIGABA.mjs | 501 ++++++++++++++++----------------- 2 files changed, 322 insertions(+), 335 deletions(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index b56b3e24..b69c7739 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -43,9 +43,9 @@ Converts a letter to uppercase (if it already isn't) @param {char} letter - letter to convert to upper case @returns {char} */ -export function convToUpperCase(letter){ +export function convToUpperCase(letter) { const charCode = letter.charCodeAt(); - if (97<=charCode && charCode<=122){ + if (97<=charCode && charCode<=122) { return String.fromCharCode(charCode-32); } return letter; @@ -54,7 +54,7 @@ export function convToUpperCase(letter){ /** The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks. */ -export class SigabaMachine{ +export class SigabaMachine { /** SigabaMachine constructor @@ -62,7 +62,7 @@ export class SigabaMachine{ @param {Object[]} controlRotors - list of CRRotors @param {object[]} indexRotors - list of IRotors */ - constructor(cipherRotors, controlRotors, indexRotors){ + constructor(cipherRotors, controlRotors, indexRotors) { this.cipherBank = new CipherBank(cipherRotors); this.controlBank = new ControlBank(controlRotors); this.indexBank = new IndexBank(indexRotors); @@ -71,7 +71,7 @@ export class SigabaMachine{ /** Steps all the correct rotors in the machine. */ - step(){ + step() { const controlOut = this.controlBank.goThroughControl(); const indexOut = this.indexBank.goThroughIndex(controlOut); this.cipherBank.step(indexOut); @@ -83,12 +83,11 @@ export class SigabaMachine{ @param {char} letter - letter to encrypt @returns {char} */ - encryptLetter(letter){ + encryptLetter(letter) { letter = convToUpperCase(letter); - if (letter == " "){ + if (letter === " ") { letter = "Z"; - } - else if (letter == "Z") { + } else if (letter === "Z") { letter = "X"; } const encryptedLetter = this.cipherBank.encrypt(letter); @@ -102,10 +101,10 @@ export class SigabaMachine{ @param {char} letter - letter to decrypt @returns {char} */ - decryptLetter(letter){ + decryptLetter(letter) { letter = convToUpperCase(letter); let decryptedLetter = this.cipherBank.decrypt(letter); - if (decryptedLetter == "Z"){ + if (decryptedLetter === "Z") { decryptedLetter = " "; } this.step(); @@ -118,9 +117,9 @@ export class SigabaMachine{ @param {string} msg - message to encrypt @returns {string} */ - encrypt(msg){ + encrypt(msg) { let ciphertext = ""; - for (const letter of msg){ + for (const letter of msg) { ciphertext = ciphertext.concat(this.encryptLetter(letter)); } return ciphertext; @@ -132,9 +131,9 @@ export class SigabaMachine{ @param {string} msg - message to decrypt @returns {string} */ - decrypt(msg){ + decrypt(msg) { let plaintext = ""; - for (const letter of msg){ + for (const letter of msg) { plaintext = plaintext.concat(this.decryptLetter(letter)); } return plaintext; @@ -145,13 +144,13 @@ export class SigabaMachine{ /** The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation. */ -export class CipherBank{ +export class CipherBank { /** CipherBank constructor @param {Object[]} rotors - list of CRRotors */ - constructor(rotors){ + constructor(rotors) { this.rotors = rotors; } @@ -161,8 +160,8 @@ export class CipherBank{ @param {char} inputPos - the input position of the signal (letter to be encrypted) @returns {char} */ - encrypt(inputPos){ - for (let rotor of this.rotors){ + encrypt(inputPos) { + for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos, "leftToRight"); } return inputPos; @@ -174,9 +173,9 @@ export class CipherBank{ @param {char} inputPos - the input position of the signal (letter to be decrypted) @returns {char} */ - decrypt(inputPos){ + decrypt(inputPos) { const revOrderedRotors = [...this.rotors].reverse(); - for (let rotor of revOrderedRotors){ + for (const rotor of revOrderedRotors) { inputPos = rotor.crypt(inputPos, "rightToLeft"); } return inputPos; @@ -187,19 +186,19 @@ export class CipherBank{ @param {number[]} indexInputs - the inputs from the index rotors */ - step(indexInputs){ - const logicDict = {0: [0,9], 1:[7,8], 2:[5,6], 3:[3,4], 4:[1,2]}; - let rotorsToMove = []; - for (const key in logicDict){ + step(indexInputs) { + const logicDict = {0: [0, 9], 1: [7, 8], 2: [5, 6], 3: [3, 4], 4: [1, 2]}; + const rotorsToMove = []; + for (const key in logicDict) { const item = logicDict[key]; - for (const i of indexInputs){ - if (item.includes(i)){ + for (const i of indexInputs) { + if (item.includes(i)) { rotorsToMove.push(this.rotors[key]); break; } } } - for (let rotor of rotorsToMove){ + for (const rotor of rotorsToMove) { rotor.step(); } } @@ -209,13 +208,13 @@ export class CipherBank{ /** The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left. */ -export class ControlBank{ +export class ControlBank { /** ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors. @param {Object[]} rotors - list of CRRotors */ - constructor(rotors){ + constructor(rotors) { this.rotors = [...rotors].reverse(); this.numberOfMoves = 1; } @@ -226,8 +225,8 @@ export class ControlBank{ @param {char} inputPos - the input position of the signal @returns {char} */ - crypt(inputPos){ - for (let rotor of this.rotors){ + crypt(inputPos) { + for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos, "rightToLeft"); } return inputPos; @@ -238,14 +237,14 @@ export class ControlBank{ @returns {number[]} */ - getOutputs(){ + getOutputs() { const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")]; - const logicDict = {1:"B", 2:"C", 3:"DE", 4:"FGH", 5:"IJK", 6:"LMNO", 7:"PQRST", 8:"UVWXYZ", 9:"A"}; - let numberOutputs = []; - for (let key in logicDict){ + const logicDict = {1: "B", 2: "C", 3: "DE", 4: "FGH", 5: "IJK", 6: "LMNO", 7: "PQRST", 8: "UVWXYZ", 9: "A"}; + const numberOutputs = []; + for (const key in logicDict) { const item = logicDict[key]; - for (let output of outputs){ - if (item.includes(output)){ + for (const output of outputs) { + if (item.includes(output)) { numberOutputs.push(key); break; } @@ -257,14 +256,14 @@ export class ControlBank{ /** Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared. */ - step(){ + step() { const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3]; this.numberOfMoves ++; FRotor.step(); - if (this.numberOfMoves%26 == 0){ + if (this.numberOfMoves%26 === 0) { MRotor.step(); } - if (this.numberOfMoves%(26*26) == 0){ + if (this.numberOfMoves%(26*26) === 0) { SRotor.step(); } } @@ -274,7 +273,7 @@ export class ControlBank{ @returns {number[]} */ - goThroughControl(){ + goThroughControl() { const outputs = this.getOutputs(); this.step(); return outputs; @@ -285,13 +284,13 @@ export class ControlBank{ /** The index rotor bank consists of 5 index rotors all placed in the forwards orientation. */ -export class IndexBank{ +export class IndexBank { /** IndexBank constructor @param {Object[]} rotors - list of IRotors */ - constructor(rotors){ + constructor(rotors) { this.rotors = rotors; } @@ -301,8 +300,8 @@ export class IndexBank{ @param {number} inputPos - the input position of the signal @returns {number} */ - crypt(inputPos){ - for (let rotor of this.rotors){ + crypt(inputPos) { + for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos); } return inputPos; @@ -314,9 +313,9 @@ export class IndexBank{ @param {number[]} - inputs from the control rotors @returns {number[]} */ - goThroughIndex(controlInputs){ - let outputs = []; - for (const inp of controlInputs){ + goThroughIndex(controlInputs) { + const outputs = []; + for (const inp of controlInputs) { outputs.push(this.crypt(inp)); } return outputs; @@ -327,7 +326,7 @@ export class IndexBank{ /** Rotor class */ -export class Rotor{ +export class Rotor { /** Rotor constructor @@ -335,7 +334,7 @@ export class Rotor{ @param {bool} rev - true if the rotor is reversed, false if it isn't @param {number} key - the starting position or state of the rotor */ - constructor(wireSetting, key, rev){ + constructor(wireSetting, key, rev) { this.state = key; this.numMapping = this.getNumMapping(wireSetting, rev); this.posMapping = this.getPosMapping(rev); @@ -348,14 +347,13 @@ export class Rotor{ @param {bool} rev - true if reversed, false if not @returns {number[]} */ - getNumMapping(wireSetting, rev){ - if (rev==false){ + getNumMapping(wireSetting, rev) { + if (rev===false) { return wireSetting; - } - else { + } else { const length = wireSetting.length; - let tempMapping = new Array(length); - for (let i=0; ithis.state-length; i--){ + } else { + for (let i = this.state; i > this.state-length; i--) { let res = i%length; - if (res<0){ + if (res<0) { res += length; } posMapping.push(res); @@ -399,13 +396,12 @@ export class Rotor{ @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor @returns {number} */ - cryptNum(inputPos, direction){ + cryptNum(inputPos, direction) { const inpNum = this.posMapping[inputPos]; - var outNum; - if (direction == "leftToRight"){ + let outNum; + if (direction === "leftToRight") { outNum = this.numMapping[inpNum]; - } - else if (direction == "rightToLeft") { + } else if (direction === "rightToLeft") { outNum = this.numMapping.indexOf(inpNum); } const outPos = this.posMapping.indexOf(outNum); @@ -415,7 +411,7 @@ export class Rotor{ /** Steps the rotor. The number at position 0 will be moved to position 1 etc. */ - step(){ + step() { const lastNum = this.posMapping.pop(); this.posMapping.splice(0, 0, lastNum); this.state = this.posMapping[0]; @@ -426,7 +422,7 @@ export class Rotor{ /** A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation. */ -export class CRRotor extends Rotor{ +export class CRRotor extends Rotor { /** CRRotor constructor @@ -435,7 +431,7 @@ export class CRRotor extends Rotor{ @param {char} key - initial state of rotor @param {bool} rev - true if reversed, false if not */ - constructor(wireSetting, key, rev=false){ + constructor(wireSetting, key, rev=false) { wireSetting = wireSetting.split("").map(CRRotor.letterToNum); super(wireSetting, CRRotor.letterToNum(key), rev); } @@ -446,7 +442,7 @@ export class CRRotor extends Rotor{ @param {char} letter - letter to convert to number @returns {number} */ - static letterToNum(letter){ + static letterToNum(letter) { return letter.charCodeAt()-65; } @@ -456,7 +452,7 @@ export class CRRotor extends Rotor{ @param {number} num - number to convert to letter @returns {char} */ - static numToLetter(num){ + static numToLetter(num) { return String.fromCharCode(num+65); } @@ -467,7 +463,7 @@ export class CRRotor extends Rotor{ @param {string} direction - one of "leftToRight" and "rightToLeft" @returns {char} */ - crypt(inputPos, direction){ + crypt(inputPos, direction) { inputPos = CRRotor.letterToNum(inputPos); const outPos = this.cryptNum(inputPos, direction); return CRRotor.numToLetter(outPos); @@ -478,14 +474,14 @@ export class CRRotor extends Rotor{ /** An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption. */ -export class IRotor extends Rotor{ +export class IRotor extends Rotor { /** IRotor constructor @param {string} wireSetting - the rotor wirings (string of numbers) @param {char} key - initial state of rotor */ - constructor(wireSetting, key){ + constructor(wireSetting, key) { wireSetting = wireSetting.split("").map(Number); super(wireSetting, Number(key), false); } @@ -496,7 +492,7 @@ export class IRotor extends Rotor{ @param {number} inputPos - the input position of the signal @returns {number} */ - crypt(inputPos){ + crypt(inputPos) { return this.cryptNum(inputPos, "leftToRight"); } diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs index 2f42c501..d82ee09a 100644 --- a/src/core/operations/SIGABA.mjs +++ b/src/core/operations/SIGABA.mjs @@ -7,285 +7,276 @@ Emulation of the SIGABA machine. */ import Operation from "../Operation.mjs"; -import OperationError from "../errors/OperationError.mjs"; import {LETTERS} from "../lib/Enigma.mjs"; import {NUMBERS, CR_ROTORS, I_ROTORS, SigabaMachine, CRRotor, IRotor} from "../lib/SIGABA.mjs"; /** Sigaba operation */ -class Sigaba extends Operation{ -/** -Sigaba constructor -*/ -constructor(){ -super(); +class Sigaba extends Operation { + /** + Sigaba constructor + */ + constructor() { + super(); -this.name = "SIGABA"; -this.module = "SIGABA"; - this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex than other rotor machines of its time, such as Enigma. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode."; - this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; - this.inputType = "string"; - this.outputType = "string"; - this.args = [ - { - name: "1st (left-hand) cipher rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "1st cipher rotor reversed", - type: "boolean", - value: false - }, - { - name: "1st cipher rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "2nd cipher rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "2nd cipher rotor reversed", - type: "boolean", - value: false - }, - { - name: "2nd cipher rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "3rd (middle) cipher rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "3rd cipher rotor reversed", - type: "boolean", - value: false - }, - { - name: "3rd cipher rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "4th cipher rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "4th cipher rotor reversed", - type: "boolean", - value: false - }, - { - name: "4th cipher rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "5th (right-hand) cipher rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "5th cipher rotor reversed", - type: "boolean", - value: false - }, - { - name: "5th cipher rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "1st (left-hand) control rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "1st control rotor reversed", - type: "boolean", - value: false - }, - { - name: "1st control rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "2nd control rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "2nd control rotor reversed", - type: "boolean", - value: false - }, - { - name: "2nd control rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "3rd (middle) control rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "3rd control rotor reversed", - type: "boolean", - value: false - }, - { - name: "3rd control rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "4th control rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "4th control rotor reversed", - type: "boolean", - value: false - }, - { - name: "4th control rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "5th (right-hand) control rotor", - type: "editableOption", - value: CR_ROTORS, - defaultIndex: 0 - }, - { - name: "5th control rotor reversed", - type: "boolean", - value: false - }, - { - name: "5th control rotor intial value", - type: "option", - value: LETTERS - }, - { - name: "1st (left-hand) index rotor", - type: "editableOption", - value: I_ROTORS, - defaultIndex: 0 - }, - { - name: "1st index rotor intial value", - type: "option", - value: NUMBERS - }, - { - name: "2nd index rotor", - type: "editableOption", - value: I_ROTORS, - defaultIndex: 0 - }, - { - name: "2nd index rotor intial value", - type: "option", - value: NUMBERS - }, - { - name: "3rd (middle) index rotor", - type: "editableOption", - value: I_ROTORS, - defaultIndex: 0 - }, - { - name: "3rd index rotor intial value", - type: "option", - value: NUMBERS - }, - { - name: "4th index rotor", - type: "editableOption", - value: I_ROTORS, - defaultIndex: 0 - }, - { - name: "4th index rotor intial value", - type: "option", - value: NUMBERS - }, - { - name: "5th (right-hand) index rotor", - type: "editableOption", - value: I_ROTORS, - defaultIndex: 0 - }, - { - name: "5th index rotor intial value", - type: "option", - value: NUMBERS - }, - { - name: "SIGABA mode", - type: "option", - value: ["Encrypt", "Decrypt"] - } - ]; + this.name = "SIGABA"; + this.module = "SIGABA"; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex than other rotor machines of its time, such as Enigma. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode."; + this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "1st (left-hand) cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "1st cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "1st cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "2nd cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "2nd cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "2nd cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "3rd (middle) cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "3rd cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "3rd cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "4th cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "4th cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "4th cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "5th (right-hand) cipher rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "5th cipher rotor reversed", + type: "boolean", + value: false + }, + { + name: "5th cipher rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "1st (left-hand) control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "1st control rotor reversed", + type: "boolean", + value: false + }, + { + name: "1st control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "2nd control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "2nd control rotor reversed", + type: "boolean", + value: false + }, + { + name: "2nd control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "3rd (middle) control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "3rd control rotor reversed", + type: "boolean", + value: false + }, + { + name: "3rd control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "4th control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "4th control rotor reversed", + type: "boolean", + value: false + }, + { + name: "4th control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "5th (right-hand) control rotor", + type: "editableOption", + value: CR_ROTORS, + defaultIndex: 0 + }, + { + name: "5th control rotor reversed", + type: "boolean", + value: false + }, + { + name: "5th control rotor intial value", + type: "option", + value: LETTERS + }, + { + name: "1st (left-hand) index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "1st index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "2nd index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "2nd index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "3rd (middle) index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "3rd index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "4th index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "4th index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "5th (right-hand) index rotor", + type: "editableOption", + value: I_ROTORS, + defaultIndex: 0 + }, + { + name: "5th index rotor intial value", + type: "option", + value: NUMBERS + }, + { + name: "SIGABA mode", + type: "option", + value: ["Encrypt", "Decrypt"] + } + ]; } /** - @param {string} rotor - rotor wirings + @param {string} input + @param {Object[]} args @returns {string} */ - - parseRotorStr(rotor){ - if (rotor === ""){ - throw new OperationError(`All rotor wirings must be provided.`); - } - return rotor; - } - - run(input, args){ + run(input, args) { const sigabaSwitch = args[40]; const cipherRotors = []; const controlRotors = []; const indexRotors = []; - for (let i=0; i<5; i++){ - const rotorWiring = this.parseRotorStr(args[i*3]); + for (let i=0; i<5; i++) { + const rotorWiring = args[i*3]; cipherRotors.push(new CRRotor(rotorWiring, args[i*3+2], args[i*3+1])); } - for (let i=5; i<10; i++){ - const rotorWiring = this.parseRotorStr(args[i*3]); + for (let i=5; i<10; i++) { + const rotorWiring = args[i*3]; controlRotors.push(new CRRotor(rotorWiring, args[i*3+2], args[i*3+1])); } - for (let i=15; i<20; i++){ - const rotorWiring = this.parseRotorStr(args[i*2]); + for (let i=15; i<20; i++) { + const rotorWiring = args[i*2]; indexRotors.push(new IRotor(rotorWiring, args[i*2+1])); } const sigaba = new SigabaMachine(cipherRotors, controlRotors, indexRotors); - var result; - if (sigabaSwitch === "Encrypt"){ + let result; + if (sigabaSwitch === "Encrypt") { result = sigaba.encrypt(input); - } - else if (sigabaSwitch === "Decrypt") { + } else if (sigabaSwitch === "Decrypt") { result = sigaba.decrypt(input); } return result; From e2b3389da687e74896c0d0ee8e6e89e40141ec9f Mon Sep 17 00:00:00 2001 From: hettysymes <59455170+hettysymes@users.noreply.github.com> Date: Sun, 12 Jan 2020 17:57:20 +0000 Subject: [PATCH 039/706] Added SIGABA simple test --- src/core/config/Categories.json | 3 +- tests/operations/tests/SIGABA.mjs | 67 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) mode change 100755 => 100644 src/core/config/Categories.json create mode 100644 tests/operations/tests/SIGABA.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json old mode 100755 new mode 100644 index 77e3d319..aee80ed4 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -116,7 +116,8 @@ "Multiple Bombe", "Typex", "Lorenz", - "Colossus" + "Colossus", + "SIGABA" ] }, { diff --git a/tests/operations/tests/SIGABA.mjs b/tests/operations/tests/SIGABA.mjs new file mode 100644 index 00000000..f8b19c9d --- /dev/null +++ b/tests/operations/tests/SIGABA.mjs @@ -0,0 +1,67 @@ +/** +SIGABA machine tests + +@author hettysymes +@copyright hettysymes 2020 +@license Apache-2.0 +*/ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "SIGABA: encrypt", + input: "hello world testing the sigaba machine", + expectedOutput: "ULBECJCZJBJFVUDLIXGLGIVXSYGMFRJVCERGOX", + recipeConfig: [ + { + "op": "SIGABA", + "args": [ + "BHKWECJDOVAYLFMITUGXRNSPZQ", true, "G", + "CDTAKGQOZXLVJYHSWMIBPRUNEF", false, "L", + "WAXHJZMBVDPOLTUYRCQFNSGKEI", false, "I", + "HUSCWIMJQXDALVGBFTOYZKRPNE", false, "T", + "RTLSMNKXFVWQUZGCHEJBYDAIPO", false, "B", + "GHAQBRJWDMNZTSKLOUXYPFIECV", false, "N", + "VFLGEMTCXZIQDYAKRPBONHWSUJ", true, "Q", + "ZQCAYHRJNXPFLKIOTBUSVWMGDE", false, "B", + "EZVSWPCTULGAOFDJNBIYMXKQHR", false, "J", + "ELKSGDXMVYJUZNCAROQBPWHITF", false, "R", + "3891625740", "3", + "6297135408", "1", + "2389715064", "8", + "9264351708", "6", + "9573086142", "6", + "Encrypt" + ] + } + ] + }, + { + name: "SIGABA: decrypt", + input: "helloxworldxtestingxthexsigabaxmachine", + expectedOutput: "XWCIWSAIQKNPBUKAP QXVYW RRNYAWXKRBGCQS", + recipeConfig: [ + { + "op": "SIGABA", + "args": [ + "ZECIPSQVBYKJTNRLOXUFGAWHMD", false, "C", + "IPHECDYSZTRXQUKWNVGOBLFJAM", true, "J", + "YHXUSRKIJVQWTPLAZOMDCGNEFB", true, "Z", + "TDPVSOBXULANZQYEHIGFMCRWJK", false, "W", + "THZGFXQRVBSDUICNYJWPAEMOKL", false, "F", + "KOVUTBMZQWGYDNAICSPHERXJLF", false, "F", + "DSTRLAUFXGWCEOKQPVMBZNIYJH", true, "A", + "KCULNSIXJDPEHGQYRTFZVWOBAM", false, "H", + "DZANEQLOWYRXKGUSIVJFMPBCHT", true, "M", + "MVRLHTPFWCAOKEGXZBJYIQUNSD", false, "E", + "9421765830", "3", + "3476815902", "2", + "5701842693", "7", + "4178920536", "0", + "5243709861", "1", + "Decrypt" + ] + } + ] + } +]); From 3c68ad13024b8e08f8f02e26504d6de6f019cc58 Mon Sep 17 00:00:00 2001 From: hettysymes Date: Sun, 7 Jun 2020 17:45:17 +0100 Subject: [PATCH 040/706] Modified control rotor stepping so the next control rotor steps once the previous rotor reaches "O" and added tests --- src/core/lib/SIGABA.mjs | 13 +++--- tests/operations/index.mjs | 1 + tests/operations/tests/SIGABA.mjs | 74 ++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index b69c7739..30166ad4 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -216,7 +216,6 @@ export class ControlBank { */ constructor(rotors) { this.rotors = [...rotors].reverse(); - this.numberOfMoves = 1; } /** @@ -258,14 +257,14 @@ export class ControlBank { */ step() { const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3]; - this.numberOfMoves ++; - FRotor.step(); - if (this.numberOfMoves%26 === 0) { + // 14 is the offset of "O" from "A" - the next rotor steps once the previous rotor reaches "O" + if (FRotor.state === 14) { + if (MRotor.state === 14) { + SRotor.step(); + } MRotor.step(); } - if (this.numberOfMoves%(26*26) === 0) { - SRotor.step(); - } + FRotor.step(); } /** diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 8d3cd623..832b9ddd 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -101,6 +101,7 @@ import "./tests/LuhnChecksum.mjs"; import "./tests/CipherSaber2.mjs"; import "./tests/Colossus.mjs"; import "./tests/ParseObjectIDTimestamp.mjs"; +import "./tests/SIGABA.mjs"; // Cannot test operations that use the File type yet diff --git a/tests/operations/tests/SIGABA.mjs b/tests/operations/tests/SIGABA.mjs index f8b19c9d..7bf196be 100644 --- a/tests/operations/tests/SIGABA.mjs +++ b/tests/operations/tests/SIGABA.mjs @@ -9,9 +9,9 @@ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ { - name: "SIGABA: encrypt", - input: "hello world testing the sigaba machine", - expectedOutput: "ULBECJCZJBJFVUDLIXGLGIVXSYGMFRJVCERGOX", + name: "SIGABA: encrypt test 1", + input: "HELLO WORLD TESTING THE SIGABA MACHINE", + expectedOutput: "ULBECJCZJBJFVUDWAVRGRBMPSQHOTTNVQEESKN", recipeConfig: [ { "op": "SIGABA", @@ -37,30 +37,54 @@ TestRegister.addTests([ ] }, { - name: "SIGABA: decrypt", - input: "helloxworldxtestingxthexsigabaxmachine", - expectedOutput: "XWCIWSAIQKNPBUKAP QXVYW RRNYAWXKRBGCQS", + name: "SIGABA: encrypt test 2", + input: "PCRPJZWSPNOHMWANBFBEIVZOXDQESPYDEFBNTHXLSICIRPKUATJVDUQFLZOKGHHHDUDIBRKUHVCGAGLBWVGFFXNDHKPFSPSCIIPCXUFRRHNYWIJFEJWQSGMSNJHWSLPKVXHUQUWIURHDIHIUTWGQFIYLTKEZAUESWYEKIWXUSSXWXBEHCXCUDQWKCISVPKXJVPOIJZWTUGKAORBMKBAQUZOPTSUSYZRROWQUYKNCLHVIHEGWCCONGVHEKCEXVYIPNILIXTXDELNGLJGMEQKKQJWZLPNXPOGIOSVAEAJYKWYJXXGKKPLVYAZGDCMNHMPLCYWDQSRBEMVVVZVFYJMRYGHJOTDOEQVRQOVXOGOVYGTXETFHAYELRYVDGWOFVGAOWPMHQYRZMNXVTAHWSKZLJDFVQPZGMHZWFNOBHSZHEDAEXIFCEEJYZDOEFOQWCXTKPJRUEITKHVCITCLKBUFNAFBYXELAYPBRGGGOCCAGLXXJXTSWCJHMHQPVUIBAGBDKAGEEEPKRGGICJQXSYHBNNAKGYODRAUWAEYHWCKHEQIBAONWQJYQCIFKDTOCTJMBJULWKMSNNMPXINHZQWUMJQLQKIPVZVRGYPCJJZMENWTFTUSPCSPRXHMZPCHCNQTUDCOUJHRKYQIUWWVEVVRYFDIYRQISNGPMQLNMCNMVBEWHNCUODHAGEVEUMKVZLEIKYAMPGVVSBYNRJMFCATDXTQCYXIBCXXKYEYHHYERQGQWZTWCEJBFQLRFFCIVVSZUKGLOTLNGLQNTIKTBBWVFMONUFKRLCJASEKUEEDDQDIVQMFRSJRNHYZJODFHSCJSDAIRUXOSDNFUFUFMNZYQIEGRUXKUPCHENUEZHRKYHDRJYSHLZNYRBWVXORMJMJRIRNSAJQRUMPCXUDFYRGKEAXQXJHPEWNIYIDURDGWIFEMSOFYYCFRZGMZXJNTLTJBBSZIULQSOMEVGCTCVXUHTIEHSPOPQYCJLPAJAPQPAQXE", + expectedOutput: "GMEXPPCMFGKUVGXZHVTCKXRSTJUYWNOKFVELWAHHSJBXGOEXCMLOVSIMCDMGEYMWWTFDUMCDUJEZITNPVVBGQDJEVHJXSKJAAUZWBELMSPUTXCUYPDTJCQXEBGWPWRSQLSNFMASCTJZDSFNKDDTAXLRGUPKCBNXMZPADJSFGGNYKRPYBNTYPTGVPACBEINILNACWFVKMJPGCEZFROEYYKTGYSQYMFSGVDOJJONNYEYSCCIXWLKUSJZDRVAQSNUWHMDJVDNNMPGOYRGQRSBGSPQKGCTFZQWSOXBWSQZDCRQJQAWZDPQEILGMMABIMCDPNSKAFCLPQGIRJCMGQREBEUHBYREXFABFMVZTZBDUMASVNUMHIYRSZLGNZFMVAIABLCUZLJLKKZPWEXDHYZFVSNRLCLNDRKLKSWRHQVQJRTHCNFZXDEXSLAXXOGMFVSGCJGAWOLGDMTLWSFNTCUVCCEACINRZAZZOGLEHHXLPHVKILBBJDPOOCILQKKGODSXOBDPZZDXHJLLBOBVFCHJVMUBUZZIKGCWGCYGXVEHHIJGPEQERWEZLILQNHPHALFKFMGADNELGBKILKIUETGDCBQUEOECWVFNOXTJKUYPWBNEKYSIKMVSAMBZGLIKDAOELRSTKFASEKABTUCPSFEGXXQGDFPSPVOLBHGLZSLLWCABSRKZDQQRKVCKXDGTIHPDNMPDZEXYFYKXZTPJPLYOFNLWAGKJEOHOYLMZELXIDWWNXPKEPUCKNNNHJLFYHPQNHMMCGMUPHSUSYYIVWTIMFKKKTFPGFTLTWWSQBRBMGBTZXPVULKNZIIKVTYLJFISGPTLZFTCLGNZOMVKZOIMUDGXRDDSVFRHRYWBEWHYLCUISYMRWAZZAQPJYXZQQKZLILOSHXUTQJFPTXQSREKSUDZTLGUDLUGOJMQHJRJHXCHQTKJULTWWQOXIRFRQEYBPJPEKXFIRMNATWNFBADOSIJVZYRYDBHDAEDJUVDHLDAU", recipeConfig: [ - { - "op": "SIGABA", + { "op": "SIGABA", "args": [ - "ZECIPSQVBYKJTNRLOXUFGAWHMD", false, "C", - "IPHECDYSZTRXQUKWNVGOBLFJAM", true, "J", - "YHXUSRKIJVQWTPLAZOMDCGNEFB", true, "Z", - "TDPVSOBXULANZQYEHIGFMCRWJK", false, "W", - "THZGFXQRVBSDUICNYJWPAEMOKL", false, "F", - "KOVUTBMZQWGYDNAICSPHERXJLF", false, "F", - "DSTRLAUFXGWCEOKQPVMBZNIYJH", true, "A", - "KCULNSIXJDPEHGQYRTFZVWOBAM", false, "H", - "DZANEQLOWYRXKGUSIVJFMPBCHT", true, "M", - "MVRLHTPFWCAOKEGXZBJYIQUNSD", false, "E", - "9421765830", "3", - "3476815902", "2", - "5701842693", "7", - "4178920536", "0", - "5243709861", "1", - "Decrypt" - ] + "YCHLQSUGBDIXNZKERPVJTAWFOM", true, "A", + "INPXBWETGUYSAOCHVLDMQKZJFR", false, "B", + "WNDRIOZPTAXHFJYQBMSVEKUCGL", false, "C", + "TZGHOBKRVUXLQDMPNFWCJYEIAS", false, "D", + "YWTAHRQJVLCEXUNGBIPZMSDFOK", true, "E", + "QSLRBTEKOGAICFWYVMHJNXZUDP", false, "F", + "CHJDQIGNBSAKVTUOXFWLEPRMZY", false, "G", + "CDFAJXTIMNBEQHSUGRYLWZKVPO", true, "H", + "XHFESZDNRBCGKQIJLTVMUOYAPW", false, "I", + "EZJQXMOGYTCSFRIUPVNADLHWBK", false, "J", + "7591482630", "0", + "3810592764", "1", + "4086153297", "2", + "3980526174", "3", + "6497135280", "4", + "Encrypt"] + } + ] + }, + { + name: "SIGABA: decrypt test", + input: "AKDHFWAYSLHJDKXEVMJJHGKFTQBZPJPJILOVHMBYOAGBZVLLTQUOIKXFPUFNILBDPCAELMAPSXTLMUEGSDTNUDWGZDADBFELWWHKVPRZNDATDPYEHIDMTGAGPDEZYXFSASVKSBMXVOJQXRMHDBWUNZDTIIIVKHJYPIEUHAJCNBXNLGVFADEWIKXDJZBUTGOQBCQZWYKRVEENWRWWRYDNOAPGMODTPTUJZCLUCRDILJABNTBTWUEIJSJRQBUVCOUJJDWFMNNUHXBDFYXLGUMXQEAWSVHBXQGEOOGPYRVOAJLAIYIOHHEXACDTAWWCBGQRNPERSIKHTXPXKBUNACZLFZTRBMBBDDGKNBIQMFHZROCZZBGNZSJKDRRWPEQHLCFADNPWPWSLPIFNKBWQPMARUERGWUUODXSCOJQECGHIZRFRNRSXWSFWKISHHTUFRVXLHCQWGBMRDHCYDSVNIDDRSTODCGJSSBLUYOBGEWFOVKOZBJTYCAKMZECUGLJGTSZJNBOLTMUZRRSIGGRQHLRPMGLINASSMZOBNACKUMSFNIZAUFCPFXXOOTJQWWLZOFLGZLHJCWZJCRJKVOUDLNMKQATGVTOFHACAEKFLRWRTTMVRXHYGOTYPNBMUSKDAKXFCICUOVSWXGPQOYUUWTWRPQMEQCSDJMMJKELIHGEDYKWOVHVPUAIBFGAODXODXVFIIZIGWRZSBTIGXVHFABMMOPGVMLGHQQXNOEJRDLOBGUOWSELBHERZFSBLUODMOGIBNVGVGQYDBTKLOPNKZZNGLTTGZYYXIBAHZJDCILZXKNSJDHXWTYQLFHTUINTYSBPIXOPLOQHSAHGQPYUWYNPKMRBBBYIICCBBJRKWVLBIDBBEKJCXHLPUBMIGBUFYDPOCSRUNZOKMKJHMYFJZWFNHQZOGGRTNNUVLMRLDSAJIECTYCJKBYVNAXGCMGNVFJEDSATZQDQTYRBPLZKHAXMOVJZEDKINXKBUVWXXHTYUFO", + expectedOutput: "KTSOYDGMLPMVXEAJIATXCNQFXHBNCBXIJOCQGCQBRQSBYFOOEVPVXACBMIUIRNVMJHREKRHBSXJFSMWCKTTCYXJOFSJCQECXXCHTEGPEYSMYDHCSMODUAVBNLILYUIBBIXJCXXNQPCERRSMJTPQLMOXSKTRPWOFUSWXOYRJLBIJGIOYTEAEJEGGYAGSXNHNQTETANPWEGATHSBFLHCVHVIJUAKDVGQCWUSIFFFVAJYPJAFUYDXSLGPGESOUAYXBQIIOXWTXNOXLNCGWSUKVIBMOUGNHORYLSNVNNJLKKFDUAEISOLBLCXYHMDGVBVVVIKDLTMTDVWWJBXWXROVTJBXXKXLEWTTISKIUMYSACVUGGNANMCGUMFNQUXDLTHJNYTFIQEPKQQQSSROYJOILJYQXICXACWGOHCSHENXJILOMIIFCIOUDXDCINIVKIRJCVHWXSFQXMNRBJJWTPXNJADEOPEJBLKHKXNTORIRVRLXUXXAMKMODBXNLQCVJXVOTBRHXBBVJHPFEQFCRXYRRXHXPTXXSUESUTHUGOWQYQPQFPXQPVGEIRPQNKXXMBHIPECRUWFEWJUTYIKSMJSRQIQAIAMXTGDXSJIABHIGKUPJBCHWMVYTMQNQYGDHCNMBSVTPXNFRELFXXQYIOLCDEXDXDVSINICOXRMNSPICPQMOBIDJCNBJKXFAVMUXOXHERJIBIXLMXXULDXKXXHAQDXEXIWXOEEUGKSUGCMRWJDPYCYKXTPCOXMURAJCPRXKFJAJALERWRHVMFHOGMFHXGSXQDPJCJNXRQFGHKRCYTEBJDHPCMYFEAPWSVVMMBVUJJMCAAYURHUPVQVJYDCSNMQEMNIFEXYXIIXBVRVILXAUCBDXRJHGPKPYXHPPPNVSBBCDRLVVIYPKAKYIXTJVYDGVPHXULWMADBEICNIFKWUOOHEFNANDKOXMCVBVORLQYNXLULOEGVGWNKNMOHYVRSYSOVYGAKCGAWKGAIAQNQR", + recipeConfig: [ + { "op": "SIGABA", + "args": [ + "YCHLQSUGBDIXNZKERPVJTAWFOM", true, "A", + "INPXBWETGUYSAOCHVLDMQKZJFR", false, "B", + "WNDRIOZPTAXHFJYQBMSVEKUCGL", false, "C", + "TZGHOBKRVUXLQDMPNFWCJYEIAS", false, "D", + "YWTAHRQJVLCEXUNGBIPZMSDFOK", true, "E", + "QSLRBTEKOGAICFWYVMHJNXZUDP", false, "F", + "CHJDQIGNBSAKVTUOXFWLEPRMZY", false, "G", + "CDFAJXTIMNBEQHSUGRYLWZKVPO", true, "H", + "XHFESZDNRBCGKQIJLTVMUOYAPW", false, "I", + "EZJQXMOGYTCSFRIUPVNADLHWBK", false, "J", + "7591482630", "0", + "3810592764", "1", + "4086153297", "2", + "3980526174", "3", + "6497135280", "4", + "Decrypt"] } ] } From 88947b9d42bd9e4ae085406db3f3301385da68ac Mon Sep 17 00:00:00 2001 From: hettysymes Date: Mon, 8 Jun 2020 12:27:40 +0100 Subject: [PATCH 041/706] Added operation description note and modified comment formatting --- src/core/lib/SIGABA.mjs | 338 +++++++++++++++--------------- src/core/operations/SIGABA.mjs | 31 +-- tests/operations/tests/SIGABA.mjs | 12 +- 3 files changed, 193 insertions(+), 188 deletions(-) diff --git a/src/core/lib/SIGABA.mjs b/src/core/lib/SIGABA.mjs index 30166ad4..09951c4f 100644 --- a/src/core/lib/SIGABA.mjs +++ b/src/core/lib/SIGABA.mjs @@ -1,15 +1,14 @@ /** -Emulation of the SIGABA machine - -@author hettysymes -@copyright hettysymes 2020 -@license Apache-2.0 -*/ + * Emulation of the SIGABA machine + * + * @author hettysymes + * @copyright hettysymes 2020 + * @license Apache-2.0 + */ /** -A set of randomised example SIGABA cipher/control rotors (these rotors are interchangeable). Cipher and control rotors can be referred to as C and R rotors respectively. -*/ - + * A set of randomised example SIGABA cipher/control rotors (these rotors are interchangeable). Cipher and control rotors can be referred to as C and R rotors respectively. + */ export const CR_ROTORS = [ {name: "Example 1", value: "SRGWANHPJZFXVIDQCEUKBYOLMT"}, {name: "Example 2", value: "THQEFSAZVKJYULBODCPXNIMWRG"}, @@ -24,9 +23,8 @@ export const CR_ROTORS = [ ]; /** -A set of randomised example SIGABA index rotors (may be referred to as I rotors). -*/ - + * A set of randomised example SIGABA index rotors (may be referred to as I rotors). + */ export const I_ROTORS = [ {name: "Example 1", value: "6201348957"}, {name: "Example 2", value: "6147253089"}, @@ -38,11 +36,11 @@ export const I_ROTORS = [ export const NUMBERS = "0123456789".split(""); /** -Converts a letter to uppercase (if it already isn't) - -@param {char} letter - letter to convert to upper case -@returns {char} -*/ + * Converts a letter to uppercase (if it already isn't) + * + * @param {char} letter - letter to convert to uppercase + * @returns {char} + */ export function convToUpperCase(letter) { const charCode = letter.charCodeAt(); if (97<=charCode && charCode<=122) { @@ -52,16 +50,17 @@ export function convToUpperCase(letter) { } /** -The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks. -*/ + * The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks. + */ export class SigabaMachine { - /** - SigabaMachine constructor - @param {Object[]} cipherRotors - list of CRRotors - @param {Object[]} controlRotors - list of CRRotors - @param {object[]} indexRotors - list of IRotors - */ + /** + * SigabaMachine constructor + * + * @param {Object[]} cipherRotors - list of CRRotors + * @param {Object[]} controlRotors - list of CRRotors + * @param {object[]} indexRotors - list of IRotors + */ constructor(cipherRotors, controlRotors, indexRotors) { this.cipherBank = new CipherBank(cipherRotors); this.controlBank = new ControlBank(controlRotors); @@ -69,8 +68,8 @@ export class SigabaMachine { } /** - Steps all the correct rotors in the machine. - */ + * Steps all the correct rotors in the machine. + */ step() { const controlOut = this.controlBank.goThroughControl(); const indexOut = this.indexBank.goThroughIndex(controlOut); @@ -78,11 +77,11 @@ export class SigabaMachine { } /** - Encrypts a letter. A space is converted to a "Z" before encryption, and a "Z" is converted to an "X". This allows spaces to be encrypted. - - @param {char} letter - letter to encrypt - @returns {char} - */ + * Encrypts a letter. A space is converted to a "Z" before encryption, and a "Z" is converted to an "X". This allows spaces to be encrypted. + * + * @param {char} letter - letter to encrypt + * @returns {char} + */ encryptLetter(letter) { letter = convToUpperCase(letter); if (letter === " ") { @@ -96,11 +95,11 @@ export class SigabaMachine { } /** - Decrypts a letter. A letter decrypted as a "Z" is converted to a space before it is output, since spaces are converted to "Z"s before encryption. - - @param {char} letter - letter to decrypt - @returns {char} - */ + * Decrypts a letter. A letter decrypted as a "Z" is converted to a space before it is output, since spaces are converted to "Z"s before encryption. + * + * @param {char} letter - letter to decrypt + * @returns {char} + */ decryptLetter(letter) { letter = convToUpperCase(letter); let decryptedLetter = this.cipherBank.decrypt(letter); @@ -112,11 +111,11 @@ export class SigabaMachine { } /** - Encrypts a message of one or more letters - - @param {string} msg - message to encrypt - @returns {string} - */ + * Encrypts a message of one or more letters + * + * @param {string} msg - message to encrypt + * @returns {string} + */ encrypt(msg) { let ciphertext = ""; for (const letter of msg) { @@ -126,11 +125,11 @@ export class SigabaMachine { } /** - Decrypts a message of one or more letters - - @param {string} msg - message to decrypt - @returns {string} - */ + * Decrypts a message of one or more letters + * + * @param {string} msg - message to decrypt + * @returns {string} + */ decrypt(msg) { let plaintext = ""; for (const letter of msg) { @@ -142,24 +141,25 @@ export class SigabaMachine { } /** -The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation. -*/ + * The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation. + */ export class CipherBank { - /** - CipherBank constructor - @param {Object[]} rotors - list of CRRotors - */ + /** + * CipherBank constructor + * + * @param {Object[]} rotors - list of CRRotors + */ constructor(rotors) { this.rotors = rotors; } /** - Encrypts a letter through the cipher rotors (signal goes from left-to-right) - - @param {char} inputPos - the input position of the signal (letter to be encrypted) - @returns {char} - */ + * Encrypts a letter through the cipher rotors (signal goes from left-to-right) + * + * @param {char} inputPos - the input position of the signal (letter to be encrypted) + * @returns {char} + */ encrypt(inputPos) { for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos, "leftToRight"); @@ -168,11 +168,11 @@ export class CipherBank { } /** - Decrypts a letter through the cipher rotors (signal goes from right-to-left) - - @param {char} inputPos - the input position of the signal (letter to be decrypted) - @returns {char} - */ + * Decrypts a letter through the cipher rotors (signal goes from right-to-left) + * + * @param {char} inputPos - the input position of the signal (letter to be decrypted) + * @returns {char} + */ decrypt(inputPos) { const revOrderedRotors = [...this.rotors].reverse(); for (const rotor of revOrderedRotors) { @@ -182,10 +182,10 @@ export class CipherBank { } /** - Step the cipher rotors forward according to the inputs from the index rotors - - @param {number[]} indexInputs - the inputs from the index rotors - */ + * Step the cipher rotors forward according to the inputs from the index rotors + * + * @param {number[]} indexInputs - the inputs from the index rotors + */ step(indexInputs) { const logicDict = {0: [0, 9], 1: [7, 8], 2: [5, 6], 3: [3, 4], 4: [1, 2]}; const rotorsToMove = []; @@ -206,24 +206,25 @@ export class CipherBank { } /** -The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left. -*/ + * The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left. + */ export class ControlBank { - /** - ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors. - @param {Object[]} rotors - list of CRRotors - */ + /** + * ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors. + * + * @param {Object[]} rotors - list of CRRotors + */ constructor(rotors) { this.rotors = [...rotors].reverse(); } /** - Encrypts a letter. - - @param {char} inputPos - the input position of the signal - @returns {char} - */ + * Encrypts a letter. + * + * @param {char} inputPos - the input position of the signal + * @returns {char} + */ crypt(inputPos) { for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos, "rightToLeft"); @@ -232,10 +233,10 @@ export class ControlBank { } /** - Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I". - - @returns {number[]} - */ + * Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I". + * + * @returns {number[]} + */ getOutputs() { const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")]; const logicDict = {1: "B", 2: "C", 3: "DE", 4: "FGH", 5: "IJK", 6: "LMNO", 7: "PQRST", 8: "UVWXYZ", 9: "A"}; @@ -253,8 +254,8 @@ export class ControlBank { } /** - Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared. - */ + * Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared. + */ step() { const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3]; // 14 is the offset of "O" from "A" - the next rotor steps once the previous rotor reaches "O" @@ -268,10 +269,10 @@ export class ControlBank { } /** - The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them. - - @returns {number[]} - */ + * The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them. + * + * @returns {number[]} + */ goThroughControl() { const outputs = this.getOutputs(); this.step(); @@ -281,24 +282,25 @@ export class ControlBank { } /** -The index rotor bank consists of 5 index rotors all placed in the forwards orientation. -*/ + * The index rotor bank consists of 5 index rotors all placed in the forwards orientation. + */ export class IndexBank { - /** - IndexBank constructor - @param {Object[]} rotors - list of IRotors - */ + /** + * IndexBank constructor + * + * @param {Object[]} rotors - list of IRotors + */ constructor(rotors) { this.rotors = rotors; } /** - Encrypts a number. - - @param {number} inputPos - the input position of the signal - @returns {number} - */ + * Encrypts a number. + * + * @param {number} inputPos - the input position of the signal + * @returns {number} + */ crypt(inputPos) { for (const rotor of this.rotors) { inputPos = rotor.crypt(inputPos); @@ -307,11 +309,11 @@ export class IndexBank { } /** - The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors. - - @param {number[]} - inputs from the control rotors - @returns {number[]} - */ + * The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors. + * + * @param {number[]} controlInputs - inputs from the control rotors + * @returns {number[]} + */ goThroughIndex(controlInputs) { const outputs = []; for (const inp of controlInputs) { @@ -323,16 +325,17 @@ export class IndexBank { } /** -Rotor class -*/ + * Rotor class + */ export class Rotor { - /** - Rotor constructor - @param {number[]} wireSetting - the wirings within the rotor: mapping from left-to-right, the index of the number in the list maps onto the number at that index - @param {bool} rev - true if the rotor is reversed, false if it isn't - @param {number} key - the starting position or state of the rotor - */ + /** + * Rotor constructor + * + * @param {number[]} wireSetting - the wirings within the rotor: mapping from left-to-right, the index of the number in the list maps onto the number at that index + * @param {bool} rev - true if the rotor is reversed, false if it isn't + * @param {number} key - the starting position or state of the rotor + */ constructor(wireSetting, key, rev) { this.state = key; this.numMapping = this.getNumMapping(wireSetting, rev); @@ -340,12 +343,12 @@ export class Rotor { } /** - Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed) - - @param {number[]} wireSetting - the wirings within the rotors - @param {bool} rev - true if reversed, false if not - @returns {number[]} - */ + * Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed) + * + * @param {number[]} wireSetting - the wirings within the rotors + * @param {bool} rev - true if reversed, false if not + * @returns {number[]} + */ getNumMapping(wireSetting, rev) { if (rev===false) { return wireSetting; @@ -360,11 +363,11 @@ export class Rotor { } /** - Get the position mapping (how the position numbers map onto the numbers of the rotor) - - @param {bool} rev - true if reversed, false if not - @returns {number[]} - */ + * Get the position mapping (how the position numbers map onto the numbers of the rotor) + * + * @param {bool} rev - true if reversed, false if not + * @returns {number[]} + */ getPosMapping(rev) { const length = this.numMapping.length; const posMapping = []; @@ -389,12 +392,12 @@ export class Rotor { } /** - Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex. - - @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt) - @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor - @returns {number} - */ + * Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex. + * + * @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt) + * @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor + * @returns {number} + */ cryptNum(inputPos, direction) { const inpNum = this.posMapping[inputPos]; let outNum; @@ -408,8 +411,8 @@ export class Rotor { } /** - Steps the rotor. The number at position 0 will be moved to position 1 etc. - */ + * Steps the rotor. The number at position 0 will be moved to position 1 etc. + */ step() { const lastNum = this.posMapping.pop(); this.posMapping.splice(0, 0, lastNum); @@ -419,49 +422,49 @@ export class Rotor { } /** -A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation. -*/ + * A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation. + */ export class CRRotor extends Rotor { /** - CRRotor constructor - - @param {string} wireSetting - the rotor wirings (string of letters) - @param {char} key - initial state of rotor - @param {bool} rev - true if reversed, false if not - */ + * CRRotor constructor + * + * @param {string} wireSetting - the rotor wirings (string of letters) + * @param {char} key - initial state of rotor + * @param {bool} rev - true if reversed, false if not + */ constructor(wireSetting, key, rev=false) { wireSetting = wireSetting.split("").map(CRRotor.letterToNum); super(wireSetting, CRRotor.letterToNum(key), rev); } /** - Static function which converts a letter into its number i.e. its offset from the letter "A" - - @param {char} letter - letter to convert to number - @returns {number} - */ + * Static function which converts a letter into its number i.e. its offset from the letter "A" + * + * @param {char} letter - letter to convert to number + * @returns {number} + */ static letterToNum(letter) { return letter.charCodeAt()-65; } /** - Static function which converts a number (a letter's offset from "A") into its letter - - @param {number} num - number to convert to letter - @returns {char} - */ + * Static function which converts a number (a letter's offset from "A") into its letter + * + * @param {number} num - number to convert to letter + * @returns {char} + */ static numToLetter(num) { return String.fromCharCode(num+65); } /** - Encrypts/decrypts a letter. - - @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.) - @param {string} direction - one of "leftToRight" and "rightToLeft" - @returns {char} - */ + * Encrypts/decrypts a letter. + * + * @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.) + * @param {string} direction - one of "leftToRight" and "rightToLeft" + * @returns {char} + */ crypt(inputPos, direction) { inputPos = CRRotor.letterToNum(inputPos); const outPos = this.cryptNum(inputPos, direction); @@ -471,26 +474,27 @@ export class CRRotor extends Rotor { } /** -An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption. -*/ + * An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption. + */ export class IRotor extends Rotor { - /** - IRotor constructor - @param {string} wireSetting - the rotor wirings (string of numbers) - @param {char} key - initial state of rotor - */ + /** + * IRotor constructor + * + * @param {string} wireSetting - the rotor wirings (string of numbers) + * @param {char} key - initial state of rotor + */ constructor(wireSetting, key) { wireSetting = wireSetting.split("").map(Number); super(wireSetting, Number(key), false); } /** - Encrypts a number - - @param {number} inputPos - the input position of the signal - @returns {number} - */ + * Encrypts a number + * + * @param {number} inputPos - the input position of the signal + * @returns {number} + */ crypt(inputPos) { return this.cryptNum(inputPos, "leftToRight"); } diff --git a/src/core/operations/SIGABA.mjs b/src/core/operations/SIGABA.mjs index d82ee09a..42d1a9f3 100644 --- a/src/core/operations/SIGABA.mjs +++ b/src/core/operations/SIGABA.mjs @@ -1,28 +1,29 @@ /** -Emulation of the SIGABA machine. - -@author hettysymes -@copyright hettysymes 2020 -@license Apache-2.0 -*/ + * Emulation of the SIGABA machine. + * + * @author hettysymes + * @copyright hettysymes 2020 + * @license Apache-2.0 + */ import Operation from "../Operation.mjs"; import {LETTERS} from "../lib/Enigma.mjs"; import {NUMBERS, CR_ROTORS, I_ROTORS, SigabaMachine, CRRotor, IRotor} from "../lib/SIGABA.mjs"; /** -Sigaba operation -*/ + * Sigaba operation + */ class Sigaba extends Operation { + /** - Sigaba constructor - */ + * Sigaba constructor + */ constructor() { super(); this.name = "SIGABA"; this.module = "SIGABA"; - this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex than other rotor machines of its time, such as Enigma. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode."; + this.description = "Encipher/decipher with the WW2 SIGABA machine.

SIGABA, otherwise known as ECM Mark II, was used by the United States for message encryption during WW2 up to the 1950s. It was developed in the 1930s by the US Army and Navy, and has up to this day never been broken. Consisting of 15 rotors: 5 cipher rotors and 10 rotors (5 control rotors and 5 index rotors) controlling the stepping of the cipher rotors, the rotor stepping for SIGABA is much more complex than other rotor machines of its time, such as Enigma. All example rotor wirings are random example sets.

To configure rotor wirings, for the cipher and control rotors enter a string of letters which map from A to Z, and for the index rotors enter a sequence of numbers which map from 0 to 9. Note that encryption is not the same as decryption, so first choose the desired mode.

Note: Whilst this has been tested against other software emulators, it has not been tested against hardware."; this.infoURL = "https://en.wikipedia.org/wiki/SIGABA"; this.inputType = "string"; this.outputType = "string"; @@ -251,10 +252,10 @@ class Sigaba extends Operation { } /** - @param {string} input - @param {Object[]} args - @returns {string} - */ + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ run(input, args) { const sigabaSwitch = args[40]; const cipherRotors = []; diff --git a/tests/operations/tests/SIGABA.mjs b/tests/operations/tests/SIGABA.mjs index 7bf196be..5f07ce20 100644 --- a/tests/operations/tests/SIGABA.mjs +++ b/tests/operations/tests/SIGABA.mjs @@ -1,10 +1,10 @@ /** -SIGABA machine tests - -@author hettysymes -@copyright hettysymes 2020 -@license Apache-2.0 -*/ + * SIGABA machine tests + * + * @author hettysymes + * @copyright hettysymes 2020 + * @license Apache-2.0 + */ import TestRegister from "../../lib/TestRegister.mjs"; TestRegister.addTests([ From f5a7db03cd8ab8bf9e7bbd43ec47ae9c57975a37 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 10 Jun 2020 15:50:26 +0200 Subject: [PATCH 042/706] Base85: Only require 15 continuous base85 chars --- src/core/operations/FromBase85.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/operations/FromBase85.mjs b/src/core/operations/FromBase85.mjs index 9d73baa1..3555b020 100644 --- a/src/core/operations/FromBase85.mjs +++ b/src/core/operations/FromBase85.mjs @@ -38,7 +38,7 @@ class FromBase85 extends Operation { pattern: "^\\s*(?:<~)?" + // Optional whitespace and starting marker "[\\s!-uz]*" + // Any amount of base85 characters and whitespace - "[!-uz]{20}" + // At least 20 continoues base85 characters without whitespace + "[!-uz]{15}" + // At least 15 continoues base85 characters without whitespace "[\\s!-uz]*" + // Any amount of base85 characters and whitespace "(?:~>)?\\s*$", // Optional ending marker and whitespace args: ["!-u"], @@ -47,7 +47,7 @@ class FromBase85 extends Operation { pattern: "^" + "[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" + - "[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{20}" + // At least 20 continoues base85 characters without whitespace + "[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{15}" + // At least 15 continoues base85 characters without whitespace "[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" + "$", args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"], @@ -56,7 +56,7 @@ class FromBase85 extends Operation { pattern: "^" + "[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" + - "[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{20}" + // At least 20 continoues base85 characters without whitespace + "[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{15}" + // At least 15 continoues base85 characters without whitespace "[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" + "$", args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"], From 3e3c526a625cabeae64d8f3b61f88d326e98473a Mon Sep 17 00:00:00 2001 From: n1073645 Date: Mon, 6 Jul 2020 16:35:14 +0100 Subject: [PATCH 043/706] Caesar Box Cipher Added --- src/core/config/Categories.json | 1 + src/core/operations/CaesarBoxCipher.mjs | 61 ++++++++++++++++++++++ tests/operations/index.mjs | 1 + tests/operations/tests/CaesarBoxCipher.mjs | 45 ++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 src/core/operations/CaesarBoxCipher.mjs create mode 100644 tests/operations/tests/CaesarBoxCipher.mjs diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 77e3d319..36465ced 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -91,6 +91,7 @@ "Bacon Cipher Decode", "Bifid Cipher Encode", "Bifid Cipher Decode", + "Caesar Box Cipher", "Affine Cipher Encode", "Affine Cipher Decode", "A1Z26 Cipher Encode", diff --git a/src/core/operations/CaesarBoxCipher.mjs b/src/core/operations/CaesarBoxCipher.mjs new file mode 100644 index 00000000..2e4d9830 --- /dev/null +++ b/src/core/operations/CaesarBoxCipher.mjs @@ -0,0 +1,61 @@ +/** + * @author n1073645 [n1073645@gmail.com] + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ + +import Operation from "../Operation.mjs"; + +/** + * Caesar Box Cipher operation + */ +class CaesarBoxCipher extends Operation { + + /** + * CaesarBoxCipher constructor + */ + constructor() { + super(); + + this.name = "Caesar Box Cipher"; + this.module = "Ciphers"; + this.description = ""; + this.infoURL = ""; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + name: "Box Height", + type: "number", + value: 1 + } + ]; + } + + /** + * @param {string} input + * @param {Object[]} args + * @returns {string} + */ + run(input, args) { + const tableHeight = args[0]; + const tableWidth = Math.ceil(input.length / tableHeight); + while (input.indexOf(" ") !== -1) + input = input.replace(" ", ""); + for (let i = 0; i < (tableHeight * tableWidth) - input.length; i++) { + input += "\x00"; + } + let result = ""; + for (let i = 0; i < tableHeight; i++) { + for (let j = i; j < input.length; j += tableHeight) { + if (input.charAt(j) !== "\x00") { + result += input.charAt(j); + } + } + } + return result; + } + +} + +export default CaesarBoxCipher; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index 8d3cd623..bd6cd3ed 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -101,6 +101,7 @@ import "./tests/LuhnChecksum.mjs"; import "./tests/CipherSaber2.mjs"; import "./tests/Colossus.mjs"; import "./tests/ParseObjectIDTimestamp.mjs"; +import "./tests/CaesarBoxCipher.mjs"; // Cannot test operations that use the File type yet diff --git a/tests/operations/tests/CaesarBoxCipher.mjs b/tests/operations/tests/CaesarBoxCipher.mjs new file mode 100644 index 00000000..3ccdae66 --- /dev/null +++ b/tests/operations/tests/CaesarBoxCipher.mjs @@ -0,0 +1,45 @@ +/** + * Base58 tests. + * + * @author n1073645 [n1073645@gmail.com] + * + * @copyright Crown Copyright 2020 + * @license Apache-2.0 + */ +import TestRegister from "../../lib/TestRegister.mjs"; + +TestRegister.addTests([ + { + name: "Caesar Box Cipher: nothing", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Caesar Box Cipher", + args: ["1"], + }, + ], + }, + { + name: "Caesar Box Cipher: Hello World!", + input: "Hello World!", + expectedOutput: "Hlodeor!lWl", + recipeConfig: [ + { + op: "Caesar Box Cipher", + args: ["3"], + }, + ], + }, + { + name: "Caesar Box Cipher: Hello World!", + input: "Hlodeor!lWl", + expectedOutput: "HelloWorld!", + recipeConfig: [ + { + op: "Caesar Box Cipher", + args: ["4"], + }, + ], + } +]); From 667dfd820e5e2b93a5daf4258f547d6a0a605a37 Mon Sep 17 00:00:00 2001 From: n1073645 Date: Mon, 6 Jul 2020 16:46:40 +0100 Subject: [PATCH 044/706] info url added --- src/core/operations/CaesarBoxCipher.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/operations/CaesarBoxCipher.mjs b/src/core/operations/CaesarBoxCipher.mjs index 2e4d9830..9c835b4b 100644 --- a/src/core/operations/CaesarBoxCipher.mjs +++ b/src/core/operations/CaesarBoxCipher.mjs @@ -19,8 +19,8 @@ class CaesarBoxCipher extends Operation { this.name = "Caesar Box Cipher"; this.module = "Ciphers"; - this.description = ""; - this.infoURL = ""; + this.description = "Caesar Box Encryption uses a box, a rectangle (or a square), or at least a size W caracterizing its width."; + this.infoURL = "https://www.dcode.fr/caesar-box-cipher"; this.inputType = "string"; this.outputType = "string"; this.args = [ From 6b76b7004a9d832acd4f19803c9990b18288b846 Mon Sep 17 00:00:00 2001 From: thezero Date: Sun, 14 Apr 2019 15:08:10 +0200 Subject: [PATCH 045/706] add button to hide recipe's options --- src/web/HTMLOperation.mjs | 1 + src/web/Manager.mjs | 1 + src/web/waiters/RecipeWaiter.mjs | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index fe075c48..f46b3ba8 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -83,6 +83,7 @@ class HTMLOperation {
pause not_interested + keyboard_arrow_up
 
`; diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index e1e07dfd..64dc3a35 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -135,6 +135,7 @@ class Manager { // Recipe this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe); this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe); + this.addDynamicListener(".hide-options", "click", this.recipe.hideOptClick, this.recipe); this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe); this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe); this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe); diff --git a/src/web/waiters/RecipeWaiter.mjs b/src/web/waiters/RecipeWaiter.mjs index ba0e7b11..afa3e72b 100755 --- a/src/web/waiters/RecipeWaiter.mjs +++ b/src/web/waiters/RecipeWaiter.mjs @@ -214,6 +214,30 @@ class RecipeWaiter { window.dispatchEvent(this.manager.statechange); } + /** + * Handler for hide-opt click events. + * Updates the icon status. + * + * @fires Manager#statechange + * @param {event} e + */ + hideOptClick(e) { + const icon = e.target; + + if (icon.getAttribute("hide-opt") === "false") { + icon.setAttribute("hide-opt", "true"); + icon.innerText = "keyboard_arrow_down"; + icon.classList.add("hide-options-selected"); + icon.parentNode.previousElementSibling.style.display = "none"; + } else { + icon.setAttribute("hide-opt", "false"); + icon.innerText = "keyboard_arrow_up"; + icon.classList.remove("hide-options-selected"); + icon.parentNode.previousElementSibling.style.display = "grid"; + } + + window.dispatchEvent(this.manager.statechange); + } /** * Handler for disable click events. From 3bb6a40f82e98fbc4cf45c82f75c033725862282 Mon Sep 17 00:00:00 2001 From: thezero Date: Mon, 22 Apr 2019 00:18:52 +0200 Subject: [PATCH 046/706] add button to hide all recipe options --- src/web/Manager.mjs | 1 + src/web/html/index.html | 3 +++ src/web/waiters/ControlsWaiter.mjs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 64dc3a35..493d3a19 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -120,6 +120,7 @@ class Manager { 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("hide-icon").addEventListener("click", this.controls.hideRecipeOptClick.bind(this.recipe)); document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls)); this.addMultiEventListeners("#save-texts textarea", "keyup paste", this.controls.saveTextChange, this.controls); diff --git a/src/web/html/index.html b/src/web/html/index.html index 121f0780..ad940040 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -177,6 +177,9 @@
Recipe + diff --git a/src/web/waiters/ControlsWaiter.mjs b/src/web/waiters/ControlsWaiter.mjs index 2f2705aa..b051e3ce 100755 --- a/src/web/waiters/ControlsWaiter.mjs +++ b/src/web/waiters/ControlsWaiter.mjs @@ -333,6 +333,36 @@ class ControlsWaiter { } + /** + * Hides the options for all the operations in the current recipe. + */ + hideRecipeOptClick() { + const icon = document.getElementById("hide-icon"); + + if (icon.getAttribute("hide-opt") === "false") { + icon.setAttribute("hide-opt", "true"); + icon.setAttribute("data-original-title", "Show options"); + icon.children[0].innerText = "keyboard_arrow_down"; + Array.from(document.getElementsByClassName("hide-options")).forEach(function(item){ + item.setAttribute("hide-opt", "true"); + item.innerText = "keyboard_arrow_down"; + item.classList.add("hide-options-selected"); + item.parentNode.previousElementSibling.style.display = "none"; + }); + } else { + icon.setAttribute("hide-opt", "false"); + icon.setAttribute("data-original-title", "Hide options"); + icon.children[0].innerText = "keyboard_arrow_up"; + Array.from(document.getElementsByClassName("hide-options")).forEach(function(item){ + item.setAttribute("hide-opt", "false"); + item.innerText = "keyboard_arrow_up"; + item.classList.remove("hide-options-selected"); + item.parentNode.previousElementSibling.style.display = "grid"; + }); + } + } + + /** * Populates the bug report information box with useful technical info. * From ed7baf57f0dbc04e07b1479b1e045b7c307d60c1 Mon Sep 17 00:00:00 2001 From: thezero Date: Wed, 21 Oct 2020 00:17:06 +0200 Subject: [PATCH 047/706] replace "options" with "arguments", invert global hide-icon if needed --- src/web/HTMLOperation.mjs | 2 +- src/web/Manager.mjs | 4 ++-- src/web/html/index.html | 2 +- src/web/waiters/ControlsWaiter.mjs | 26 ++++++++++++------------- src/web/waiters/RecipeWaiter.mjs | 31 +++++++++++++++++++++++------- 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/web/HTMLOperation.mjs b/src/web/HTMLOperation.mjs index f46b3ba8..285fe10e 100755 --- a/src/web/HTMLOperation.mjs +++ b/src/web/HTMLOperation.mjs @@ -83,7 +83,7 @@ class HTMLOperation {
pause not_interested - keyboard_arrow_up + keyboard_arrow_up
 
`; diff --git a/src/web/Manager.mjs b/src/web/Manager.mjs index 493d3a19..f7e08aa6 100755 --- a/src/web/Manager.mjs +++ b/src/web/Manager.mjs @@ -120,7 +120,7 @@ class Manager { 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("hide-icon").addEventListener("click", this.controls.hideRecipeOptClick.bind(this.recipe)); + document.getElementById("hide-icon").addEventListener("click", this.controls.hideRecipeArgsClick.bind(this.recipe)); document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls)); this.addMultiEventListeners("#save-texts textarea", "keyup paste", this.controls.saveTextChange, this.controls); @@ -136,7 +136,7 @@ class Manager { // Recipe this.addDynamicListener(".arg:not(select)", "input", this.recipe.ingChange, this.recipe); this.addDynamicListener(".arg[type=checkbox], .arg[type=radio], select.arg", "change", this.recipe.ingChange, this.recipe); - this.addDynamicListener(".hide-options", "click", this.recipe.hideOptClick, this.recipe); + this.addDynamicListener(".hide-args-icon", "click", this.recipe.hideArgsClick, this.recipe); this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe); this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe); this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe); diff --git a/src/web/html/index.html b/src/web/html/index.html index ad940040..b5cff9f0 100755 --- a/src/web/html/index.html +++ b/src/web/html/index.html @@ -177,7 +177,7 @@
Recipe - @@ -190,7 +190,7 @@
    -
    +
    @@ -236,9 +239,7 @@ view_compact -
    -
    -
    +