2
0
mirror of https://github.com/gchq/CyberChef synced 2025-12-05 23:53:27 +00:00

Compare commits

...

359 Commits

Author SHA1 Message Date
n1474335
98f4fe4c2b 8.6.2 2018-08-29 19:06:02 +00:00
n1474335
0d63b3cbae JSON output is now indented by default. Closes #350. 2018-08-29 19:05:58 +00:00
n1474335
7061c05f77 8.6.1 2018-08-29 18:58:01 +00:00
n1474335
9b9a182f9f Merge branch 'klaxon1-feature/improve-parse-ip-range' 2018-08-29 18:57:08 +00:00
n1474335
2d9e7fcc6d Added full stop to operation description 2018-08-29 18:56:50 +00:00
n1474335
56946a66aa Merge branch 'feature/improve-parse-ip-range' of https://github.com/klaxon1/CyberChef into klaxon1-feature/improve-parse-ip-range 2018-08-29 18:48:29 +00:00
n1474335
c9242e32fe Auto-generated configs and modules are now cleaned when starting a new dev or prod task 2018-08-29 18:21:46 +00:00
n1474335
22e8883934 8.6.0 2018-08-29 18:04:55 +00:00
n1474335
552a18d89a Updated CHANGELOG 2018-08-29 18:04:49 +00:00
n1474335
6b725e9114 Merge branch 'GCHQ77703-master' 2018-08-29 18:00:06 +00:00
n1474335
415beaa0b0 Tidied up Geohash operations 2018-08-29 17:59:48 +00:00
n1474335
e9fe227ed7 Merge branch 'master' of https://github.com/GCHQ77703/CyberChef into GCHQ77703-master 2018-08-29 17:48:23 +00:00
n1474335
c1be109592 8.5.1 2018-08-29 17:36:05 +00:00
n1474335
88e603bbf1 Merge branch 'PenguinGeorge-cascadexor-new' 2018-08-29 17:35:13 +00:00
n1474335
c7b2095bb4 Updated package-lock.json 2018-08-29 17:34:47 +00:00
n1474335
7396117d89 Merge branch 'cascadexor-new' of https://github.com/PenguinGeorge/CyberChef into PenguinGeorge-cascadexor-new 2018-08-29 17:31:59 +00:00
Klaxon
2820660264 fix regex issues and ESlint errors and warnings
fix comment

fix ESlint errors and warnings

fix regex

add author
2018-08-28 23:07:53 +10:00
Klaxon
86145dbf67 add tests 2018-08-28 23:07:48 +10:00
Klaxon
135b17186e update description 2018-08-28 23:07:40 +10:00
Klaxon
ce494339ef add parse IPv6 list 2018-08-28 23:07:32 +10:00
Klaxon
dd5af7eb10 add parse IPv4 list 2018-08-28 23:07:04 +10:00
GCHQ 77703
91133172d5 Remove subdomain from Wikipedia 2018-08-26 18:51:52 +01:00
GCHQ 77703
001f3f30cd Remove multi-line operations 2018-08-26 18:06:02 +01:00
GCHQ 77703
a1b1059ad1 Revert package-lock.json changes 2018-08-26 17:39:33 +01:00
GCHQ 77703
69a0122fea Fix "From Geohash" test 2018-08-26 17:20:50 +01:00
GCHQ 77703
3d505b4248 Add ToGeohash and FromGeohash operations 2018-08-26 14:27:15 +01:00
George J
70d4e3394c Updated XOR to include Cascade scheme 2018-08-24 22:33:24 +01:00
n1474335
3905c01a0d 8.5.0 2018-08-24 01:07:56 +01:00
n1474335
2a49af1ec3 'To Braille' and 'From Braille' operations added. Closes #255 2018-08-24 01:07:51 +01:00
n1474335
3d4f54e8bc 8.4.3 2018-08-24 00:32:57 +01:00
n1474335
61f2f2d2e3 'Show Base64 offsets' operation show supports base64 input. Closes #276 2018-08-24 00:32:52 +01:00
n1474335
00058bd5c7 8.4.2 2018-08-23 23:24:05 +01:00
n1474335
383fe50fc9 Support for negative values in 'Drop bytes' and 'Take bytes'. Closes #266 2018-08-23 23:23:00 +01:00
n1474335
e4fdadc573 8.4.1 2018-08-23 22:56:46 +01:00
n1474335
032f8808ef Fixed typo in Z85 alphabet 2018-08-23 22:56:18 +01:00
n1474335
10cf0d13c2 8.4.0 2018-08-23 22:10:23 +01:00
n1474335
6c9fea97ef Updated CHANGELOG 2018-08-23 22:10:15 +01:00
n1474335
4c2d612bdd Merge branch 'PenguinGeorge-ascii85-new' 2018-08-23 22:05:44 +01:00
n1474335
8e9fece77d Tidied up Base85 ops 2018-08-23 22:05:31 +01:00
n1474335
c818370123 Merge branch 'ascii85-new' of https://github.com/PenguinGeorge/CyberChef into PenguinGeorge-ascii85-new 2018-08-23 21:46:41 +01:00
n1474335
bf2643802a 8.3.1 2018-08-23 21:42:04 +01:00
n1474335
1ad079fbd4 'Find/Replace', 'Filter' and 'Register' now used XRegExp 2018-08-23 21:41:57 +01:00
George J
2d9e8773f5 Updated Base85 operations for latest CyberChef version 2018-08-22 20:24:32 +01:00
n1474335
5aa13f2428 Changelog improved 2018-08-22 03:26:02 +01:00
n1474335
38f5fcde86 8.3.0 2018-08-21 19:29:51 +01:00
n1474335
f99ab87ca1 Merge branch 'artemisbot-features/messagepack' 2018-08-21 19:29:39 +01:00
n1474335
d3d230a76d Tweaked MessagePack operations 2018-08-21 19:29:19 +01:00
n1474335
e7c6a05e9f Merge branch 'features/messagepack' of https://github.com/artemisbot/CyberChef into artemisbot-features/messagepack 2018-08-21 19:22:10 +01:00
n1474335
17ec5c2683 8.2.0 2018-08-21 19:11:15 +01:00
n1474335
8d1839b9e2 Updated CHANGELOG.md 2018-08-21 19:10:35 +01:00
n1474335
23213a4ac5 Merge branch 'PenguinGeorge-info-links' 2018-08-21 19:08:08 +01:00
n1474335
e81c81351d Added more infoLinks and updated newOperation script to include prompt for them. 2018-08-21 19:07:13 +01:00
Matt C
36e16d9925 Fixed linting issues 2018-08-20 19:14:19 +01:00
Matt C
84eaaf4819 Tests now work
Also they'll work in the node API too now
2018-08-20 19:08:01 +01:00
n1474335
757096d97a Added image licencing info 2018-08-20 01:20:02 +01:00
n1474335
2393eac218 Removed src/test.mjs 2018-08-20 00:50:59 +01:00
n1474335
745d21d1bc 8.1.4 2018-08-20 00:26:49 +01:00
n1474335
cb34518a89 Recipes with a single backslash argument are now parsed correctly. Fixes #319 2018-08-20 00:26:44 +01:00
Matt C
821dd9c48c Added messagepack tests but they don't work
and i'm too burnt out to figure out why
2018-08-20 00:20:04 +01:00
n1474335
740f3924df Pressing return on a highlighted search result now correctly adds it to the recipe in Firefox. 2018-08-20 00:10:57 +01:00
n1474335
f052ed9b00 Compile message no longer overflows on small screens 2018-08-20 00:04:49 +01:00
n1474335
2507362741 8.1.3 2018-08-19 23:08:02 +01:00
n1474335
481a4266b0 Reset progress on statechange events. Fixes #329. 2018-08-19 23:07:53 +01:00
n1474335
92d9abf43a 8.1.2 2018-08-19 22:58:35 +01:00
n1474335
5bcdd99870 'Change IP format' operation now uses the correct arguments. Fixes #332 2018-08-19 22:58:31 +01:00
n1474335
e11bb38625 8.1.1 2018-08-19 22:50:54 +01:00
n1474335
affe057cab Added 'Decimal' option for toggleStrings. Closes #337. 2018-08-19 22:50:49 +01:00
n1474335
c1b2fc9400 Updated CHANGELOG.md 2018-08-19 17:39:49 +01:00
n1474335
782e0f3475 8.1.0 2018-08-19 17:37:19 +01:00
n1474335
6be7ac89d4 Merge branch 'sevzero-esm' 2018-08-19 17:37:03 +01:00
n1474335
ad2424cfdd Tidied up 'Dechunk HTTP Response' operation 2018-08-19 17:36:40 +01:00
n1474335
b9703e2bb6 8.0.2 2018-08-19 17:14:36 +01:00
n1474335
198b67104f Merge branch 'Cynser-fix-date-time' 2018-08-19 17:14:20 +01:00
n1474335
8dab0eac58 Removed excess newline 2018-08-19 17:14:07 +01:00
Cynser
8b78383732 Newline at end of files 2018-08-16 03:31:09 +01:00
Cynser
f1c31ea966 Added some tests for the Translate DateTime Format operation 2018-08-16 03:28:11 +01:00
Cynser
1a1fad7433 Fixed DateTime format arguments 2018-08-16 03:01:19 +01:00
d98762625
ab248a0209 Merge pull request #333 from d98762625/update-cla-doc
Update CLA info in README
2018-08-14 12:09:19 +01:00
d98762625
6155dbb6e9 Update CLA info in README 2018-08-14 09:35:41 +01:00
n1474335
f7310369e6 8.0.1 2018-08-09 19:17:35 +01:00
n1474335
9b7f4e824a Improvements and basic tests for Magic op and addition of deep cloning for dishes 2018-08-09 19:17:24 +01:00
Matt C
0c06e64051 Added MessagePack operations 2018-08-09 11:09:28 +01:00
n1474335
2b6c280858 8.0.0 2018-08-06 08:17:36 +01:00
n1474335
6de74b8211 Merge branch 'esm' 2018-08-06 08:16:51 +01:00
n1474335
3e5b3900bc CSS tweak to dropdown toggle height 2018-08-06 07:53:30 +01:00
n1474335
0453bd098b Removed phantomjs-prebuilt from direct dependencies 2018-08-06 07:39:25 +01:00
n1474335
4dc6dac885 Added CHANGELOG.md 2018-08-06 07:35:30 +01:00
n1474335
8da9af8989 Updated dependencies 2018-08-04 19:32:30 +01:00
n1474335
bd34f6bb6d Merge branch 'Dachande663-operation-haversine' into esm 2018-08-03 21:19:14 +01:00
n1474335
ff59a9d130 Moved Haversine Distance operation in categories 2018-08-03 21:18:53 +01:00
n1474335
63e0e4eaeb Merge branch 'operation-haversine' of https://github.com/Dachande663/CyberChef into Dachande663-operation-haversine 2018-08-03 21:12:54 +01:00
n1474335
c9635bee06 Merge branch 'feature-bootstrap4' into esm 2018-08-03 20:22:27 +01:00
n1474335
1b2d09c1e5 Merge branch 'esm' into feature-bootstrap4 2018-08-03 20:21:59 +01:00
n1474335
a947e06140 Added option to turn off background auto magic 2018-08-03 20:18:19 +01:00
n1474335
53b92ae8ef Further CSS tweaks 2018-08-03 19:58:44 +01:00
Luke Lanchester
87b77215e4 Added Haversine distance operation
This operation calculates the great circle distance between two GPS co-ordinates, returning the value
2018-08-03 18:20:38 +01:00
n1474335
2a55f1317a Fixed themes for MD 2018-08-02 13:21:02 +01:00
n1474335
c3a353837f Linted 2018-07-27 15:54:49 +00:00
n1474335
035b6dfa95 Added font and icon 2018-07-27 15:52:32 +00:00
n1474335
33b2fd8d69 Improved magic and staleness indicator animations 2018-07-27 15:18:08 +00:00
n1474335
541e4ff8cd Added functionality to Magic button and improved the icon 2018-07-27 13:37:38 +00:00
n1474335
ba5c503c48 Added Magic button and tweaked margins for Windows 2018-07-26 17:47:14 +00:00
n1474335
651ca6cf5d Fixed all HTML operations 2018-07-15 13:25:44 +01:00
n1474335
ab44100312 Changed table-condensed to table-sm 2018-07-13 13:03:13 +00:00
n1474335
a39e2c165d Fixed input and output cards 2018-07-13 12:10:24 +00:00
n1474335
d182261ff3 Switched to unbordered args 2018-07-13 10:30:24 +00:00
n1474335
052c32e2ce Arguments are now bordered white boxes again 2018-07-13 00:03:04 +01:00
n1474335
dcf3152dee Operation cursor now indicates that it can be dragged 2018-06-20 00:27:40 +01:00
n1474335
4338e2626b Replaced Bootstrap alert with Material Design snackbar 2018-06-20 00:18:59 +01:00
n1474335
67dffbec32 Actually fixed flow control operation argument text colour 2018-06-19 01:06:10 +01:00
n1474335
7504f89666 Fixed flow control operation argument text colour 2018-06-19 01:03:55 +01:00
n1474335
ba79144036 Operation arguments are now arranged using CSS Grid 2018-06-19 00:55:08 +01:00
n1474335
974ab29e36 Switched toggle string dropdown sides and removed form-control padding 2018-06-17 23:52:00 +01:00
n1474335
fa523e0d4a Changed banner icons to Material Design 2018-06-17 14:09:52 +01:00
n1474335
ab9bc7e390 Fixed boolean arg checkboxes 2018-06-17 13:49:32 +01:00
n1474335
4147ec719b Converted modals to Bootstrap 4 Material Design 2018-06-17 12:44:12 +01:00
n1474335
4ca14ed0ce 'From Hexdump' now supports an alternative style of Wireshark hexdumps. Test added. 2018-06-13 13:00:49 +00:00
n1474335
7d410fcdc9 Converted op-icons to material design and improved controls. 2018-06-10 14:55:15 +01:00
n1474335
2bacd6424d Converted operation arguments to material design 2018-06-10 12:03:55 +01:00
n1474335
27b0505ede Merged ESM into feature-bootstrap4 2018-06-09 10:48:35 +01:00
n1474335
1ce6e32086 Merged ESM into feature-bootstrap4. Started work on text inputs. 2018-06-09 10:43:36 +01:00
n1474335
40a1da10e3 Merge branch 'esm' of github.com:gchq/CyberChef into esm 2018-06-03 17:33:39 +01:00
n1474335
1ef4f71d8b ESM: Added BackgroundWorkerWaiter for running Magic on output in the background 2018-06-03 17:33:13 +01:00
n1474335
a0f034b843 ESM: Fixed magic errors 2018-06-01 12:43:36 +00:00
n1474335
37c8d1b7ea ESM: Fixed OpModules import to make inline version function correctly 2018-05-29 16:22:04 +00:00
n1474335
6a4bc3c9df ESM: Fixed PGP ops and added tests 2018-05-29 16:00:24 +00:00
n1474335
78c1c4d680 ESM: Added builder script to generate new operations. npm run newop 2018-05-29 14:18:39 +00:00
n1474335
60340fc8ae ESM: Removed legacy files 2018-05-29 01:20:44 +01:00
n1474335
be0fc7591d Merge branch 'picapi-ip-convert' into esm 2018-05-29 00:48:45 +01:00
n1474335
7f9a2eeb6b ESM: Tidied up IP operations 2018-05-29 00:48:30 +01:00
n1474335
40b29d770a Merge branch 'artemisbot-esmconversion' into esm 2018-05-28 15:47:50 -07:00
n1474335
3f3a3e0016 ESM: Tidied up recently added operations 2018-05-28 15:42:43 -07:00
Matt C
6a561185df ESM: Ported RegEx operations 2018-05-28 00:39:03 +01:00
Matt C
049656ec6b ESM: Ported PublicKey operations 2018-05-27 23:53:43 +01:00
Matt C
709630f39b Ported Image operations + some cleanup 2018-05-27 23:27:11 +01:00
Matt C
eb3a2502f5 Ported rest of Code ops & enabled/fixed some tests 2018-05-27 22:07:09 +01:00
n1474335
ade056e881 Merge branch 'artemisbot-esmconversion' into esm 2018-05-27 16:13:43 +01:00
n1474335
6768038a2f ESM: Tidied up recently ported ops 2018-05-27 16:13:18 +01:00
Matt C
905bc6699e ESM: Ported case converters, generic beautifier and syntax highlighting 2018-05-26 18:04:53 +01:00
sevzero
ed84614389 Fixed issues raised by Travis 2018-05-24 13:39:14 +00:00
sevzero
67bd2605c0 Updated variable scope in legacy HTTP.js file 2018-05-24 13:21:50 +00:00
sevzero
83c145c2ac Reduced variable scopes 2018-05-24 13:17:46 +00:00
sevzero
53bf52c989 Added decoder for chunked HTTP encoding
This decoder will join up a HTTP response sent using chunked transfer encoding, raised in issue #168.

This is useful when attempting to extract files or gzipped responses sent using chunked transfer encoding, particularly when combined with the gunzip operation.
2018-05-24 13:09:04 +00:00
Matt C
176e83a79f Converted JS operations
Deleted legacy files, neatened args in other ported ops
2018-05-23 20:36:29 +01:00
Matt C
95f81ad740 Ported Bitwise operations
also enabled bitshift tests
2018-05-23 18:59:57 +01:00
Matt C
9ffab374db Converted PBKDF2 and RC2, enabled tests, deleted legacy Cipher file
Also made DESDecrypt test pass
2018-05-23 18:31:26 +01:00
Matt C
46b8b2fa7e Converted DES and TripleDES ops 2018-05-23 18:23:03 +01:00
Matt C
189e077247 Ported blowfish operations and library to ESM modules 2018-05-23 16:54:12 +01:00
n1474335
c29ea53405 ESM: Ported Punycode, HTTP and PRNG operations 2018-05-21 19:08:24 +00:00
n1474335
0d1e5311dc ESM: Changed thrown errors to OperationErrors 2018-05-21 18:34:52 +00:00
n1474335
cefe3fc542 ESM: Ported Bzip2, Diff and Tar operations 2018-05-21 18:23:05 +00:00
n1474335
749b0510e7 ESM: Ported BSON, ToTable, Filetime and XKCD operations 2018-05-21 17:37:32 +00:00
n1474335
eed28f67d5 ESM: Ported UUID, OTP, Numberwang and PHP operations 2018-05-21 12:35:11 +00:00
n1474335
739e06d7d3 Merge branch 'd98762625-port-flowcontrol' into esm 2018-05-21 11:45:28 +00:00
n1474335
28b24b725f ESM: Tidied up FlowControl ops 2018-05-21 11:39:10 +00:00
n1474335
1472f82205 Merge branch 'port-flowcontrol' of https://github.com/d98762625/CyberChef into d98762625-port-flowcontrol 2018-05-21 11:27:00 +00:00
n1474335
4990a1f9f1 ESM: Added file exists check to npm postinstall script. 2018-05-21 11:25:13 +00:00
d98762625
10556f528f update comments 2018-05-21 11:12:58 +01:00
d98762625
8ff6596657 add other flowcontrol ops. Update tests 2018-05-21 10:58:35 +01:00
d98762625
046e1ebad9 Merge branch 'esm' into port-flowcontrol 2018-05-21 09:02:58 +01:00
d98762625
9f52689fde Merge branch 'esm' of github.com:gchq/CyberChef into esm 2018-05-21 09:02:21 +01:00
n1474335
093a7c8c8a ESM: Config files are now initialised correctly. 2018-05-20 17:05:59 +01:00
n1474335
ee519c7352 Merged esm branch into feature-magic. Ported FileType ops. 2018-05-20 16:49:42 +01:00
d98762625
ec0ecf5151 add comments 2018-05-18 12:52:16 +01:00
d98762625
bfb405c4a6 Add Jump 2018-05-18 12:50:23 +01:00
d98762625
bca73b496f add Merge (without Fork). Add flowcontrol setter to Operation 2018-05-18 12:38:37 +01:00
d98762625
72d943aca2 Add register 2018-05-18 12:24:31 +01:00
d98762625
0f6ee68731 edit setter in Register 2018-05-18 12:23:58 +01:00
d98762625
3f08fa3b23 update package-lock 2018-05-18 11:40:24 +01:00
n1474335
1dddcb4345 ESM: Ported various tests for completed operations 2018-05-17 15:34:00 +00:00
n1474335
3fd1f4e6d9 ESM: Ported all Hash and Checksum operations 2018-05-17 15:11:34 +00:00
Callum Fraser
3ba12ae9ac Made variables non-private, and followed convention for the references to the IP lib. 2018-05-16 22:32:46 +01:00
Callum Fraser
ea36687205 Merge branch 'esm' of https://github.com/gchq/CyberChef into ip-convert 2018-05-16 21:52:40 +01:00
Callum Fraser
c17e897674 Ported the ParseIPv4Header operation. 2018-05-16 21:51:51 +01:00
Callum Fraser
b7bdd4eed6 Ported the GroupIPAddresses operation. 2018-05-16 21:51:24 +01:00
Callum Fraser
792218df9c Ported the ChangeIPFormat operation. 2018-05-16 21:51:04 +01:00
Callum Fraser
161f89c038 Ported the TCPIPChecksum operation.
Main content has been moved to the IP lib file, as it is used in multiple files.
Changed the name, due to it originally containing a backslash, which caused issues with the script.
2018-05-16 21:50:48 +01:00
Callum Fraser
1920e9c7fb Added more content to the Ip.mjs file, required for other functions. 2018-05-16 21:37:48 +01:00
n1474335
5362508a99 ESM: Ported HTML, Unicode, Quoted Printable and Endian operations 2018-05-16 17:10:50 +00:00
n1474335
f26d175cad ESM: Ported Base58, Base and BCD operations 2018-05-16 16:25:05 +00:00
n1474335
84df055888 ESM: Ported MS and Entropy operations 2018-05-16 11:39:30 +01:00
n1474335
ebcc5bd9c8 ESM: Added generateConfig calls to relevant grunt tasks 2018-05-16 10:25:29 +01:00
n1474335
b760c2f1a0 ESM: Fixed OperationError detection and tidied up ops. 2018-05-16 10:17:49 +01:00
n1474335
acb8a342a7 Merge branch 'esmconversion' of https://github.com/artemisbot/CyberChef into esm 2018-05-16 09:28:24 +01:00
Callum Fraser
654596ea79 Ported "Parse IPv6 Address" 2018-05-15 23:48:33 +01:00
Callum Fraser
8d2adfaae7 Updated the Ip core file with more required functions/variables 2018-05-15 23:48:18 +01:00
Callum Fraser
5b6d57fd47 Ported "Parse IP Range". 2018-05-15 23:31:03 +01:00
Callum Fraser
39ba83eefb Created a Ip for now which contains all the helper functions, they may be moved into their corrosponding files in the future 2018-05-15 23:30:35 +01:00
Matt C
03f435915b Imported OperationError to TranslateDateTimeFormat 2018-05-15 18:50:04 +01:00
n1474335
07715bd167 ESM: Rewritten src/web/ in ESM format. 2018-05-15 17:36:45 +00:00
Matt C
2e4f5b7070 Changed all error returns to OperationErrors 2018-05-15 18:01:04 +01:00
Matt C
b3ee251ee3 ESM: Port Extract.js module 2018-05-15 16:30:17 +01:00
n1474335
c90acd24f5 ESM: Added author tag to Sum op 2018-05-15 15:21:50 +00:00
n1474335
e41eb3d8a2 Merge branch 'port-arithmetic' of https://github.com/d98762625/CyberChef into esm 2018-05-15 15:16:56 +00:00
Matt C
3c214ce17c Deleted PGP file 2018-05-15 16:07:51 +01:00
Matt C
285e512483 Actually made these ops work 2018-05-15 16:04:57 +01:00
n1474335
b8d3b33963 ESM: Ported CharEnc operations 2018-05-15 15:03:41 +00:00
d98762625
3bbfc130d4 create arithmetic specific delimiter options 2018-05-15 14:59:28 +01:00
d98762625
6ddc1b1c9c export Arithmetic funcs individually. Use existing Delim 2018-05-15 14:32:39 +01:00
d98762625
f79dd29ed3 port standard deviation ops 2018-05-15 10:47:06 +01:00
d98762625
1198094d3b port median operation 2018-05-15 10:42:51 +01:00
d98762625
2716be397c port mean operation 2018-05-15 10:40:26 +01:00
d98762625
9de93022d6 port divide operation 2018-05-15 10:33:58 +01:00
d98762625
bf28410812 port multiply operation 2018-05-15 10:24:35 +01:00
d98762625
30288c6237 add Subtract op 2018-05-15 10:19:06 +01:00
Matt C
2b0c327001 Ported x86 Disassembler & PGP ops 2018-05-15 10:15:31 +01:00
d98762625
4fe34a4839 port Sum operation 2018-05-15 10:12:59 +01:00
Matt C
b8d39f49b2 Convert URL operations
Delete legacy URL module
2018-05-14 22:15:28 +01:00
Matt C
c9e9499106 Merge remote-tracking branch 'upstream/esm' into esmconversion 2018-05-14 20:39:13 +01:00
n1474335
a7d763287e ESM: Ported AES operations 2018-05-14 18:23:16 +00:00
n1474335
61832a9e2a ESM: whitespace tidying 2018-05-14 18:07:17 +00:00
n1474335
cbb5e7c611 Merge branch 'esmconversion' of https://github.com/artemisbot/CyberChef into esm 2018-05-14 17:49:57 +00:00
n1474335
bad45f19d6 ESM: Ported DateTime operations 2018-05-14 17:48:57 +00:00
Matt C
e6f19c3dfd Fixed linebreaks in test index (thanks VSCode) 2018-05-14 18:33:16 +01:00
Matt C
24e4e268dc Converted RC4, RC4Drop and Derive EVP 2018-05-14 18:30:52 +01:00
n1474335
10005ce104 ESM: Ported OS operations 2018-05-14 17:16:42 +00:00
Matt C
4008dbf38a Merge remote-tracking branch 'upstream/esm' into esmconversion
Also removed substitute operation from legacy module
2018-05-14 18:09:21 +01:00
n1474335
cefe5bbaa8 ESM: Ported Convert operations 2018-05-14 17:03:23 +00:00
n1474335
d327dd47b2 ESM: Ported SeqUtils operations 2018-05-14 16:46:54 +00:00
n1474335
66c768fe31 ESM: Ported Tidy operations. Updated portOperation script to attempt to find the run function and list related constants. 2018-05-14 15:55:17 +00:00
n1474335
037e2f3771 ESM: Ported StrUtils and NetBIOS operations. 2018-05-14 14:31:04 +00:00
n1474335
a98d37e61c Merged master into esm branch 2018-05-14 11:58:01 +00:00
George J
77a0238406 Fixes syntax error 2018-05-13 13:27:52 +01:00
Matt C
2d6a56343b Converted substitute operation, added tests & moved to OperationError 2018-05-11 16:32:19 +01:00
Matt C
350d10d98b Added toggleString support 2018-05-11 10:03:06 +01:00
n1474335
dcc28438ff 7.11.1 2018-05-10 15:34:23 +00:00
n1474335
df7c1721f5 PGP ops no longer require a key to be in date 2018-05-10 15:34:10 +00:00
Matt C
6bec68021c Converted Vignere, added more tests and cleaned stuff up 2018-05-09 21:13:09 +01:00
Matt C
789ec94eff Converted Bifid & moved over tests 2018-05-09 20:28:28 +01:00
Matt C
f87666f659 Converted Affine/Atbash operations to mjs & added tests 2018-05-09 20:18:33 +01:00
n1474335
6987e6b1b9 ESM: Ported Bit shift, MAC address and Morse Code operations. 2018-05-07 12:12:58 +01:00
n1474335
0ca0e7427d Merge branch 'esm' of github.com:gchq/CyberChef into esm 2018-05-07 11:35:35 +01:00
n1474335
be61419b80 ESM: Added remaining ByteRepr operations. 2018-05-06 13:18:41 +01:00
n1474335
1f877817f4 ESM: Added portOperation.mjs script. Added To and From Hexdump operations. 2018-05-06 12:24:01 +01:00
George J
c7f13ff67f Added 'More Info' links to the descriptions of many operations for #265 2018-05-05 00:43:42 +01:00
n1474335
403296cc59 7.11.0 2018-05-04 16:11:50 +00:00
n1474335
addb2b4448 Merge branch 'JustAnotherMark-totable-operation' 2018-05-04 16:10:39 +00:00
n1474335
8556bdcdeb Tidied up 'To Table' operation, adding better CSV parsing support. 2018-05-04 16:10:22 +00:00
n1474335
8fc5f59647 Merge branch 'totable-operation' of https://github.com/JustAnotherMark/CyberChef into JustAnotherMark-totable-operation 2018-05-01 15:41:51 +00:00
n1474335
f7729c0fd2 7.10.3 2018-04-30 17:55:26 +00:00
n1474335
e2376c7c71 'BSON serialise' errors are now thrown correctly 2018-04-30 17:55:21 +00:00
n1474335
2cefd3b941 Updated dependencies 2018-04-30 17:52:43 +00:00
n1474335
4cff1c8fa9 Merge branch 'd98762625-op-errors' into esm 2018-04-30 17:26:11 +00:00
n1474335
0dbc7d4fd1 Tidied up OperationError code. 2018-04-30 17:25:13 +00:00
n1474335
68ab2da866 7.10.2 2018-04-30 16:51:24 +00:00
n1474335
72f7f0b70c Utils.fromHex function now defaults to automatically strip all delimiters. Fixes #295 2018-04-30 16:51:04 +00:00
n1474335
d18a7df3bc 7.10.1 2018-04-29 21:45:07 +01:00
n1474335
f81012ef6e CRC Checksum operations now send their input as ArrayBuffers. Tests added. Fixes #293 2018-04-29 21:44:54 +01:00
d98762625
cc20ad9567 Add OperationError error type and use for errors to be printed to the output panel 2018-04-27 10:59:53 +01:00
Mark Jones
411bba53a8 Fix code style issues raised by linting. 2018-04-26 13:00:35 +01:00
Mark Jones
e2af3c78e7 Added ToTable operation to output data as ASCII or HTML tables. 2018-04-26 00:33:19 +01:00
n1474335
a8aa1bc5e8 Added File and JSON Dish types and updated types for compression ops. 2018-04-21 13:41:42 +01:00
n1474335
76a066ab74 The raw, unpresented dish is now returned to the app after baking, where it can be retrieved as various different data types. 2018-04-21 12:25:48 +01:00
n1474335
30aa4e05ef ESM: 'grunt test' now generates configs before running 2018-04-11 18:08:42 +00:00
n1474335
4e38b09e18 ESM: Config scripts now run synchronously before webpack builds start 2018-04-11 17:58:40 +00:00
n1474335
23f69bd21d Merge branch 'd98762625-set-operations' into esm 2018-04-11 17:31:19 +00:00
n1474335
dc5ce31087 ESM: .gitignore now ignores generated files 2018-04-11 17:30:15 +00:00
n1474335
e99331f305 ESM: Tidied up Set operations 2018-04-11 17:29:02 +00:00
d98762625
955a082614 add lint command to package.json. Remove old conflict remnants 2018-04-09 15:38:44 +01:00
d98762625
bbc580e71b Quick fix for empty recipe error. Changed deflate back to compression module 2018-04-09 15:21:09 +01:00
d98762625
76f27dbcdb pull from upstream 2018-04-09 11:23:18 +01:00
d98762625
543dce5721 remove setOps operation 2018-04-09 11:19:05 +01:00
d98762625
adc4f78e99 Add other set operations 2018-04-09 11:13:23 +01:00
d98762625
852c95a994 add Set Difference operation 2018-04-09 10:23:05 +01:00
n1474335
b7ed1becba ESM: Added new List<File> Dish type. Added present() method for displaying operation output in a nice way. Testing required. 2018-04-06 18:11:13 +00:00
d98762625
03ecaa81f7 move Set Intersection into its own operation class 2018-04-06 16:52:59 +01:00
d98762625
5f93c667a2 Pull SetUnion into its own operation 2018-04-06 16:27:24 +01:00
n1474335
cb66508b8a jsesc is now transpiled with babel 2018-04-06 15:44:49 +01:00
n1474335
ae55fde591 Merge branch 'artemisbot-esm' into esm 2018-04-06 12:41:13 +00:00
n1474335
fad4713a90 ESM: Tidied up Rotate operations 2018-04-06 12:40:39 +00:00
n1474335
c56038a1e2 Added error message for loading errors. Closes #254 2018-04-05 15:58:19 +00:00
d98762625
f491461a57 inital move of two ops 2018-04-04 17:37:19 +01:00
d98762625
7ce1bf1048 merge esm 2018-04-04 16:00:37 +01:00
Matt C
af51090ebb Added rotate ESM tests 2018-04-03 23:29:47 +01:00
Matt C
7e86f02e4e Deleted legacy Rotate module 2018-04-03 22:51:50 +01:00
Matt C
4988ead918 Rotate module converted to ESM
4 Ops:
- ROT-13
- ROT-47
- Rotate left
- Rotate right
+ module containing common functions
2018-04-03 22:50:26 +01:00
n1474335
083d2d1cc4 ESM: Added Zlib ops and created a Zlib library. 2018-04-02 20:46:55 +01:00
n1474335
fbb3a02315 ESM: The operations index is now generated automatically 2018-04-02 19:24:25 +01:00
n1474335
eeb1d0a891 ESM: Added Hex ops and created a Hex library. 2018-04-02 18:06:48 +01:00
n1474335
0011e9caa8 Added .editorconfig 2018-04-02 17:32:17 +01:00
n1474335
041cd9fb8e ESM: Added remaining Base64 ops and created a Base64 library. Added the prefer-const eslint rule. 2018-04-02 17:10:51 +01:00
n1474335
81e62a6c22 Merge branch 'master' of github.com:gchq/CyberChef 2018-03-29 00:00:09 +01:00
n1474335
c501720cdd 7.10.0 2018-03-28 23:58:52 +01:00
n1474335
92b5aa08f3 Updated dependencies 2018-03-28 23:58:43 +01:00
n1474335
1097170a68 Updated dependencies 2018-03-28 23:56:33 +01:00
n1474335
e6e2b169dc Merge branch 'tlwr-feature-add-pgp-kbpgp' 2018-03-28 23:27:01 +01:00
n1474335
e41145427e Tidied up PGP operations and added progress callback 2018-03-28 23:26:48 +01:00
n1474335
caadf8e762 Added tags to X.509 operation description 2018-03-28 10:22:22 +00:00
n1474335
1143c23ad9 Merge branch 'feature-add-pgp-kbpgp' of https://github.com/tlwr/CyberChef into tlwr-feature-add-pgp-kbpgp 2018-03-27 00:07:20 +01:00
n1474335
9b4fc3d3aa Converted the core to ES modules 2018-03-26 23:14:23 +01:00
n1474335
c1bb93eec1 7.9.0 2018-03-26 22:43:58 +01:00
n1474335
1ae9c82e29 Merge branch 'feature-updates' 2018-03-26 22:43:48 +01:00
n1474335
715ca1c292 Added Bcrypt, Scrypt, BSON and string operations along with many new tests. 2018-03-26 22:25:36 +01:00
d98762625
e403adb9a1 fix more linting errors 2018-03-25 17:51:36 +01:00
d98762625
f3610e7c95 fix lint errors 2018-03-25 17:44:10 +01:00
d98762625
951568ce22 use bind for partial application of power set function 2018-03-25 17:27:14 +01:00
d98762625
e8bb9e264d more comments 2018-03-25 17:10:55 +01:00
d98762625
208cb05c74 reuse difference function for symmetric difference 2018-03-25 17:03:05 +01:00
d98762625
2c68be3193 add comments 2018-03-25 16:42:33 +01:00
n1474335
2f5b0533d8 Added note to 'From UNIX Timestamp' op regarding date formats. 2018-03-23 20:08:53 +00:00
n1474335
5ece79c74d 7.8.1 2018-03-23 20:02:03 +00:00
n1474335
12c226f874 Updated DisassembleX86-64 library to fix issue with call instrution. Closes #246. 2018-03-23 20:01:56 +00:00
d98762625
20e54a8ecf add tests for setOperations 2018-03-22 18:11:24 +00:00
71846
7d15bfe58a initial functionality commit 2018-03-16 14:42:55 +00:00
n1474335
567474ce00 Removed confusing delimiters from fuzzy hash comparison ops 2018-03-04 18:49:05 +00:00
n1474335
209a5b7274 7.8.0 2018-03-04 18:45:23 +00:00
n1474335
e962c9594b Merge branch 'feature-fuzzy-hashes' 2018-03-04 18:45:15 +00:00
n1474335
242bad09ea Added SSDEEP and CTPH operations 2018-03-04 18:41:41 +00:00
n1474335
8dd223f1cb 7.7.8 2018-03-04 17:51:39 +00:00
n1474335
d15662dea4 Merge branch 'bug-hashes' 2018-03-04 17:49:36 +00:00
n1474335
f47a408755 Fix for UTF-8/binary handling in hashing operations. Added tests to prevent future breakages. Closes #249. 2018-03-04 17:39:53 +00:00
n1474335
bcc8804495 Updated buttons, fonts and collapse 2018-03-02 16:30:20 +00:00
n1474335
5a7bb092c7 Fixed popovers in Bootstrap v4 2018-03-02 15:32:27 +00:00
n1474335
d166dda229 Started updating Bootstrap to v4 2018-03-02 14:04:58 +00:00
n1474335
8360c9e9f9 Fixed noindex meta tag for standalone version 2018-03-02 11:15:37 +00:00
n1474335
328142dac7 Added 's' to the standalone version string 2018-03-01 20:32:09 +00:00
n1474335
372b2378a8 7.7.7 2018-03-01 19:45:47 +00:00
n1474335
574207c626 Updated many dependencies including Webpack (v4) and crypto-api (v0.8) 2018-03-01 19:45:34 +00:00
n1474335
ead64dcb47 7.7.6 2018-02-28 16:40:22 +00:00
n1474335
da473de9f0 Switched from uas_parser.js to ua-parser-js library to improve library management and remove ReDos vulnerabilities. Also improved performance of two native regexes. Thanks to James Davis for raising awareness. 2018-02-28 16:40:15 +00:00
n1474335
a177e2ab7e 7.7.5 2018-02-21 11:24:46 +00:00
n1474335
8cef6db482 Added IPv6 interface IDs to the 'Format MAC address' operation 2018-02-21 11:24:38 +00:00
n1474335
79a3128491 Changed fragment to query in sitemap 2018-02-20 17:39:46 +00:00
n1474335
cb1fe80214 Added robots meta tag to inline version to prevent indexing. 2018-02-20 17:34:19 +00:00
n1474335
19f2e6dae0 Fixed robots.txt copy 2018-02-20 17:15:06 +00:00
n1474335
74394a773d Fixed URL in sitemap 2018-02-20 17:09:02 +00:00
n1474335
d491e95d1c 7.7.4 2018-02-20 17:04:47 +00:00
n1474335
881523ce54 Merge branch 'sitemap' 2018-02-20 17:04:34 +00:00
n1474335
502a9fbb92 Added dynamically generated sitemap and robots.txt file to prevent crawling of inline version 2018-02-20 16:52:27 +00:00
n1474335
56d33ea487 Magic operation now calculates the entropy of each option and displays tooltips explaining the properties. 2018-02-19 17:25:28 +00:00
n1474335
559741fd07 Fixed a few small bugs 2018-02-15 18:46:17 +00:00
n1474335
b3c52a8601 Magic operation now brute forces character encodings. Linted. 2018-02-15 17:38:39 +00:00
n1474335
27ec4aa923 Magic operation now recognises useful operations such as 'Render Image' even though their output cannot be analysed 2018-02-15 13:39:55 +00:00
n1474335
1760ab2305 Recipe errors are now ignored in the Magic operation 2018-02-14 17:00:14 +00:00
n1474335
99ade42e9a Added 'Intensive mode' to the Magic operation, where it brute-forces various simple encodings like XOR or bit rotates. 2018-02-14 16:08:59 +00:00
n1474335
544d78f461 The Magic operation now only checks the most commonly used Internet languages by default, to lower false positives and improve performance. 2018-02-14 13:08:03 +00:00
n1474335
2bc563b693 Added support for 238 languages to the Magic operation. 2018-02-10 17:53:59 +00:00
n1474335
23bdfd04a2 Magic operation now shows matching ops even if they are not run. 2018-02-10 15:31:50 +00:00
n1474335
6624f25a64 Magic operation now detects UTF8 and gives a probability score for each language 2018-02-10 15:10:53 +00:00
n1474335
865ee6a720 Magic operation tidying 2018-01-23 01:15:13 +00:00
n1474335
6947d2a7f3 Magic operation now displays an ordered table of the most likely decodings. 2018-01-22 23:34:24 +00:00
n1474335
28abd00d82 Added speculative execution of recipes to determine the most likely decodings. 2018-01-22 22:06:26 +00:00
n1474335
57314b77e5 Merge branch 'master' into feature-magic 2018-01-22 20:04:51 +00:00
n1474335
b035f6c410 Added detection patterns for X.509 certs, Morse Code, Tar, images and BCD. 2018-01-22 19:57:41 +00:00
n1474335
615a020469 Added detection patterns for UNIX timestamps, Zlib deflate, Gzip, Zip and Bzip2. 2018-01-22 17:50:00 +00:00
n1474335
48f8ca693d Added detection patterns for Octal, Binary, Decimal, Hexdumps, HTML Entities, URL encoding, escaped Unicode, and Quoted Printable encoding. 2018-01-14 18:26:06 +00:00
n1474335
a1624a9215 Added detection patterns for non-standard Base64 alphabets, Base58 and Base32. 2018-01-14 17:28:56 +00:00
n1474335
fc2828fee3 Added Magic operation with the ability to detect language, file type and some encoding types. 2018-01-14 16:07:39 +00:00
Toby Lorne
aa3f781e18 Merge pull request #3 from artemisbot/features/add-pgp-kbpgp
Update to master & add verify/sign
2018-01-13 12:33:28 +00:00
Matt C
50a3cc57ad Fixed missing newline 2018-01-12 18:18:52 +00:00
Matt C
7554cbda72 Added PGP Sign/Verify operations 2018-01-12 16:52:15 +00:00
Matt C
6a67fe09de Added passphrase support to importing private key 2018-01-12 12:03:46 +00:00
Matt C
f07263ca2a Fix decrypt operation 2018-01-12 11:45:16 +00:00
Matt C
bfbefb7318 Merge branch 'master' into features/add-pgp-kbpgp 2018-01-11 22:57:31 +00:00
Toby Lorne
db8955d90d WIP: add encrypt and decrypt operations
Currently the encrypt operation works only to my public key and not to
keys generated by the generate key pair operation. Probably something
wrong with the generate operation.
2017-12-24 17:44:32 +00:00
Toby Lorne
5a5ce1101b Merge pull request #2 from artemisbot/features/add-pgp-kbpgp
Promisified generation of key pair
2017-12-23 17:33:42 +00:00
Matt C
670566b7eb Promisified generation of key pair 2017-12-21 14:23:31 +00:00
tlwr
dcd8f98e8c Fix linting in PGP operations 2017-11-26 20:13:49 +00:00
tlwr
60c8da7bbb Add operation "Generate PGP Key Pair"
Have not yet found a nice way of working with the kbpgp API as it is
very callback heavy. Probably just my rusty javascript.
2017-11-25 16:00:33 +00:00
533 changed files with 43814 additions and 57063 deletions

View File

@@ -5,10 +5,15 @@
"chrome": 40,
"firefox": 35,
"edge": 14,
"node": "6.5",
"node": "6.5"
},
"modules": false,
"useBuiltIns": true
}]
],
"plugins": [
["babel-plugin-transform-builtin-extend", {
"globals": ["Error"]
}]
]
}

14
.editorconfig Normal file
View File

@@ -0,0 +1,14 @@
# top-most EditorConfig file
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
indent_size = 4
[{package.json,.travis.yml}]
indent_style = space
indent_size = 2

View File

@@ -1,2 +1 @@
src/core/lib/**
src/core/config/MetaConfig.js
src/core/vendor/**

View File

@@ -1,6 +1,6 @@
{
"parserOptions": {
"ecmaVersion": 8,
"ecmaVersion": 9,
"ecmaFeatures": {
"impliedStrict": true
},
@@ -84,12 +84,12 @@
"no-whitespace-before-property": "error",
"operator-linebreak": ["error", "after"],
"space-in-parens": "error",
"no-var": "error"
"no-var": "error",
"prefer-const": "error"
},
"globals": {
"$": false,
"jQuery": false,
"moment": false,
"log": false,
"COMPILE_TIME": false,

5
.gitignore vendored
View File

@@ -6,4 +6,7 @@ docs/*
!docs/*.conf.json
!docs/*.ico
.vscode
src/core/config/MetaConfig.js
src/core/config/modules/*
src/core/config/OperationConfig.json
src/core/operations/index.mjs

View File

@@ -1,6 +1,6 @@
language: node_js
node_js:
- "8.4"
- node
install: npm install
before_script:
- npm install -g grunt
@@ -11,6 +11,7 @@ script:
- grunt node
- grunt prod --msg="$COMPILE_MSG"
before_deploy:
- grunt exec:sitemap
- grunt copy:ghPages
deploy:
- provider: pages

88
CHANGELOG.md Normal file
View File

@@ -0,0 +1,88 @@
# Changelog
All notable changes to CyberChef will be documented in this file.
### [8.6.0] - 2018-08-29
- 'To Geohash' and 'From Geohash' operations added [@GCHQ77703] | [#344]
### [8.5.0] - 2018-08-23
- 'To Braille' and 'From Braille' operations added [@n1474335] | [#255]
### [8.4.0] - 2018-08-23
- 'To Base85' and 'From Base85' operations added [@PenguinGeorge] | [#340]
### [8.3.0] - 2018-08-21
- 'To MessagePack' and 'From MessagePack' operations added [@artemisbot] | [#338]
### [8.2.0] - 2018-08-21
- Information links added to most operations, accessible in the description popover [@PenguinGeorge] | [#298]
### [8.1.0] - 2018-08-19
- 'Dechunk HTTP response' operation added [@sevzero] | [#311]
## [8.0.0] - 2018-08-05
- Codebase rewritten using [ES modules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) and [classes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes) [@n1474335] [@d98762625] [@artemisbot] [@picapi] | [#284]
- Operation architecture restructured to make adding new operations a lot simpler [@n1474335] | [#284]
- A script has been added to aid in the creation of new operations by running `npm run newop` [@n1474335] | [#284]
- 'Magic' operation added - [automated detection of encoded data](https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic) [@n1474335] | [#239]
- UI updated to use [Bootstrap Material Design](https://fezvrasta.github.io/bootstrap-material-design/) [@n1474335] | [#248]
- `JSON`, `File` and `List<File>` Dish types added [@n1474335] | [#284]
- `OperationError` type added for better handling of errors thrown by operations [@d98762625] | [#296]
- A `present()` method has been added, allowing operations to pass machine-friendly data to subsequent operations whilst presenting human-friendly data to the user [@n1474335] | [#284]
- Set operations added [@d98762625] | [#281]
- 'To Table' operation added [@JustAnotherMark] | [#294]
- 'Haversine distance' operation added [@Dachande663] | [#325]
- Started keeping a changelog [@n1474335]
## [7.0.0] - 2017-12-28
- Added support for loading, processing and downloading files up to 500MB [@n1474335] | [#224]
## [6.0.0] - 2017-09-19
- Threading support added. All recipe processing moved into a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) to increase performance and allowing long-running operations to be cancelled [@n1474335] | [#173]
- Module system created so that operations relying on large libraries can be downloaded separately as required, reducing the initial loading time for the app [@n1474335] | [#173]
## [5.0.0] - 2017-03-30
- Webpack build process configured with Babel transpilation and ES6 imports and exports [@n1474335] | [#95]
## [4.0.0] - 2016-11-28
- Initial open source commit [@n1474335] | [b1d73a72](https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306)
[8.6.0]: https://github.com/gchq/CyberChef/releases/tag/v8.6.0
[8.5.0]: https://github.com/gchq/CyberChef/releases/tag/v8.5.0
[8.4.0]: https://github.com/gchq/CyberChef/releases/tag/v8.4.0
[8.3.0]: https://github.com/gchq/CyberChef/releases/tag/v8.3.0
[8.2.0]: https://github.com/gchq/CyberChef/releases/tag/v8.2.0
[8.1.0]: https://github.com/gchq/CyberChef/releases/tag/v8.1.0
[8.0.0]: https://github.com/gchq/CyberChef/releases/tag/v8.0.0
[7.0.0]: https://github.com/gchq/CyberChef/releases/tag/v7.0.0
[6.0.0]: https://github.com/gchq/CyberChef/releases/tag/v6.0.0
[5.0.0]: https://github.com/gchq/CyberChef/releases/tag/v5.0.0
[4.0.0]: https://github.com/gchq/CyberChef/commit/b1d73a725dc7ab9fb7eb789296efd2b7e4b08306
[@n1474335]: https://github.com/n1474335
[@d98762625]: https://github.com/d98762625
[@GCHQ77703]: https://github.com/GCHQ77703
[@artemisbot]: https://github.com/artemisbot
[@picapi]: https://github.com/picapi
[@Dachande663]: https://github.com/Dachande663
[@JustAnotherMark]: https://github.com/JustAnotherMark
[@sevzero]: https://github.com/sevzero
[@PenguinGeorge]: https://github.com/PenguinGeorge
[#95]: https://github.com/gchq/CyberChef/pull/299
[#173]: https://github.com/gchq/CyberChef/pull/173
[#224]: https://github.com/gchq/CyberChef/pull/224
[#239]: https://github.com/gchq/CyberChef/pull/239
[#248]: https://github.com/gchq/CyberChef/pull/248
[#255]: https://github.com/gchq/CyberChef/issues/255
[#281]: https://github.com/gchq/CyberChef/pull/281
[#284]: https://github.com/gchq/CyberChef/pull/284
[#294]: https://github.com/gchq/CyberChef/pull/294
[#296]: https://github.com/gchq/CyberChef/pull/296
[#298]: https://github.com/gchq/CyberChef/pull/298
[#311]: https://github.com/gchq/CyberChef/pull/311
[#325]: https://github.com/gchq/CyberChef/pull/325
[#338]: https://github.com/gchq/CyberChef/pull/338
[#340]: https://github.com/gchq/CyberChef/pull/340
[#344]: https://github.com/gchq/CyberChef/pull/344

View File

@@ -4,7 +4,8 @@ const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const NodeExternals = require("webpack-node-externals");
const Inliner = require("web-resource-inliner");
const fs = require("fs");
const glob = require("glob");
const path = require("path");
/**
* Grunt configuration for building the app in various formats.
@@ -21,15 +22,15 @@ module.exports = function (grunt) {
// Tasks
grunt.registerTask("dev",
"A persistent task which creates a development build whenever source files are modified.",
["clean:dev", "concurrent:dev"]);
["clean:dev", "clean:config", "exec:generateConfig", "concurrent:dev"]);
grunt.registerTask("node",
"Compiles CyberChef into a single NodeJS module.",
["clean:node", "webpack:metaConf", "webpack:node", "chmod:build"]);
["clean:node", "clean:config", "exec:generateConfig", "webpack:node", "chmod:build"]);
grunt.registerTask("test",
"A task which runs all the tests in test/tests.",
["clean:test", "webpack:metaConf", "webpack:tests", "execute:test"]);
["exec:generateConfig", "exec:tests"]);
grunt.registerTask("docs",
"Compiles documentation in the /docs directory.",
@@ -37,7 +38,7 @@ module.exports = function (grunt) {
grunt.registerTask("prod",
"Creates a production-ready build. Use the --msg flag to add a compile message.",
["eslint", "clean:prod", "webpack:metaConf", "webpack:web", "inline", "chmod"]);
["eslint", "clean:prod", "clean:config", "exec:generateConfig", "webpack:web", "inline", "chmod"]);
grunt.registerTask("default",
"Lints the code base",
@@ -45,7 +46,7 @@ module.exports = function (grunt) {
grunt.registerTask("inline",
"Compiles a production build of CyberChef into a single, portable web page.",
["webpack:webInline", "runInliner", "clean:inlineScripts"]);
["exec:generateConfig", "webpack:webInline", "runInliner", "clean:inlineScripts"]);
grunt.registerTask("runInliner", runInliner);
@@ -60,9 +61,9 @@ module.exports = function (grunt) {
grunt.loadNpmTasks("grunt-jsdoc");
grunt.loadNpmTasks("grunt-contrib-clean");
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-chmod");
grunt.loadNpmTasks("grunt-exec");
grunt.loadNpmTasks("grunt-execute");
grunt.loadNpmTasks("grunt-accessibility");
grunt.loadNpmTasks("grunt-concurrent");
@@ -118,12 +119,12 @@ module.exports = function (grunt) {
* Generates an entry list for all the modules.
*/
function listEntryModules() {
const path = "./src/core/config/modules/";
let entryModules = {};
const entryModules = {};
fs.readdirSync(path).forEach(file => {
if (file !== "Default.js" && file !== "OpModules.js")
entryModules[file.split(".js")[0]] = path + file;
glob.sync("./src/core/config/modules/*.mjs").forEach(file => {
const basename = path.basename(file);
if (basename !== "Default.mjs" && basename !== "OpModules.mjs")
entryModules[basename.split(".mjs")[0]] = path.resolve(file);
});
return entryModules;
@@ -131,10 +132,10 @@ module.exports = function (grunt) {
grunt.initConfig({
clean: {
dev: ["build/dev/*", "src/core/config/MetaConfig.js"],
prod: ["build/prod/*", "src/core/config/MetaConfig.js"],
test: ["build/test/*", "src/core/config/MetaConfig.js"],
node: ["build/node/*", "src/core/config/MetaConfig.js"],
dev: ["build/dev/*"],
prod: ["build/prod/*"],
node: ["build/node/*"],
config: ["src/core/config/OperationConfig.json", "src/core/config/modules/*", "src/code/operations/index.mjs"],
docs: ["docs/*", "!docs/*.conf.json", "!docs/*.ico", "!docs/*.png"],
inlineScripts: ["build/prod/scripts.js"],
},
@@ -143,10 +144,10 @@ module.exports = function (grunt) {
configFile: "./.eslintrc.json"
},
configs: ["Gruntfile.js"],
core: ["src/core/**/*.js", "!src/core/lib/**/*", "!src/core/config/MetaConfig.js"],
web: ["src/web/**/*.js"],
node: ["src/node/**/*.js"],
tests: ["test/**/*.js"],
core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*", "!src/core/operations/legacy/**/*"],
web: ["src/web/**/*.{js,mjs}"],
node: ["src/node/**/*.{js,mjs}"],
tests: ["test/**/*.{js,mjs}"],
},
jsdoc: {
options: {
@@ -159,17 +160,11 @@ module.exports = function (grunt) {
all: {
src: [
"src/**/*.js",
"!src/core/lib/**/*",
"!src/core/config/MetaConfig.js"
"src/**/*.mjs",
"!src/core/vendor/**/*"
],
}
},
concurrent: {
options: {
logConcurrentOutput: true
},
dev: ["webpack:metaConfDev", "webpack-dev-server:start"]
},
accessibility: {
options: {
accessibilityLevel: "WCAG2A",
@@ -184,61 +179,23 @@ module.exports = function (grunt) {
},
webpack: {
options: webpackConfig,
metaConf: {
target: "node",
entry: [
"babel-polyfill",
"./src/core/config/OperationConfig.js"
],
output: {
filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",
library: "MetaConfig",
libraryTarget: "commonjs2",
libraryExport: "default"
},
externals: [NodeExternals()],
},
metaConfDev: {
target: "node",
entry: [
"babel-polyfill",
"./src/core/config/OperationConfig.js"
],
output: {
filename: "MetaConfig.js",
path: __dirname + "/src/core/config/",
library: "MetaConfig",
libraryTarget: "commonjs2",
libraryExport: "default"
},
externals: [NodeExternals()],
watch: true
},
web: {
mode: "production",
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
main: "./src/web/index.js",
sitemap: "./src/web/static/sitemap.js"
}, moduleEntryPoints),
output: {
path: __dirname + "/build/prod"
},
resolve: {
alias: {
"./config/modules/OpModules.js": "./config/modules/Default.js"
"./config/modules/OpModules": "./config/modules/Default"
}
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new webpack.optimize.UglifyJsPlugin({
compress: {
"screw_ie8": true,
"dead_code": true,
"unused": true,
"warnings": false
},
comments: false,
}),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/web/html/index.html",
@@ -255,6 +212,7 @@ module.exports = function (grunt) {
]
},
webInline: {
mode: "production",
target: "web",
entry: "./src/web/index.js",
output: {
@@ -262,21 +220,14 @@ module.exports = function (grunt) {
path: __dirname + "/build/prod"
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new webpack.optimize.UglifyJsPlugin({
compress: {
"screw_ie8": true,
"dead_code": true,
"unused": true,
"warnings": false
},
comments: false,
}),
new webpack.DefinePlugin(Object.assign({}, BUILD_CONSTANTS, {
INLINE: "true"
})),
new HtmlWebpackPlugin({
filename: "cyberchef.htm",
template: "./src/web/html/index.html",
compileTime: compileTime,
version: pkg.version,
version: pkg.version + "s",
inline: true,
minify: {
removeComments: true,
@@ -288,8 +239,9 @@ module.exports = function (grunt) {
]
},
tests: {
mode: "development",
target: "node",
entry: "./test/index.js",
entry: "./test/index.mjs",
externals: [NodeExternals()],
output: {
filename: "index.js",
@@ -300,8 +252,9 @@ module.exports = function (grunt) {
]
},
node: {
mode: "production",
target: "node",
entry: "./src/node/index.js",
entry: "./src/node/index.mjs",
externals: [NodeExternals()],
output: {
filename: "CyberChef.js",
@@ -326,18 +279,20 @@ module.exports = function (grunt) {
children: false,
chunks: false,
modules: false,
warningsFilter: /source-map/,
entrypoints: false,
warningsFilter: [/source-map/, /dependency is an expression/],
}
},
start: {
webpack: {
mode: "development",
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.js": "./config/modules/Default.js"
"./config/modules/OpModules": "./config/modules/Default"
}
},
plugins: [
@@ -377,7 +332,7 @@ module.exports = function (grunt) {
expand: true,
src: "docs/**",
dest: "build/prod/"
}
},
]
}
},
@@ -395,6 +350,18 @@ module.exports = function (grunt) {
src: ["docs/**/*", "docs/"]
}
},
watch: {
config: {
files: ["src/core/operations/**/*", "!src/core/operations/index.mjs"],
tasks: ["exec:generateConfig"]
}
},
concurrent: {
dev: ["watch:config", "webpack-dev-server:start"],
options: {
logConcurrentOutput: true
}
},
exec: {
repoSize: {
command: [
@@ -406,9 +373,23 @@ module.exports = function (grunt) {
cleanGit: {
command: "git gc --prune=now --aggressive"
},
},
execute: {
test: "build/test/index.js"
sitemap: {
command: "node build/prod/sitemap.js > build/prod/sitemap.xml"
},
generateConfig: {
command: [
"echo '\n--- Regenerating config files. ---'",
"mkdir -p src/core/config/modules",
"echo 'export default {};\n' > src/core/config/modules/OpModules.mjs",
"echo '[]\n' > src/core/config/OperationConfig.json",
"node --experimental-modules src/core/config/scripts/generateOpsIndex.mjs",
"node --experimental-modules src/core/config/scripts/generateConfig.mjs",
"echo '--- Config scripts finished. ---\n'"
].join(";")
},
tests: {
command: "node --experimental-modules test/index.mjs"
}
},
});
};

View File

@@ -12,7 +12,7 @@
CyberChef is a simple, intuitive web app for carrying out all manner of "cyber" operations within a web browser. These operations include simple encoding like XOR or Base64, more complex encryption like AES, DES and Blowfish, creating binary and hexdumps, compression and decompression of data, calculating hashes and checksums, IPv6 and X.509 parsing, changing character encodings, and much more.
The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years. Every effort has been made to structure the code in a readable and extendable format, however it should be noted that the analyst is not a professional developer.
The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years.
## Live demo
@@ -43,16 +43,19 @@ You can use as many operations as you like in simple or complex ways. Some examp
- [Carry out different operations on data of different types][8]
- [Use parts of the input as arguments to operations][9]
- [Perform AES decryption, extracting the IV from the beginning of the cipher stream][10]
- [Automagically detect several layers of nested encoding][12]
## Features
- Drag and drop
- Operations can be dragged in and out of the recipe list, or reorganised.
- Files can be dragged over the input box to load them directly into the browser.
- Files up to 500MB can be dragged over the input box to load them directly into the browser.
- Auto Bake
- Whenever you modify the input or the recipe, CyberChef will automatically "bake" for you and produce the output immediately.
- This can be turned off and operated manually if it is affecting performance (if the input is very large, for instance).
- Automated encoding detection
- CyberChef uses [a number of techniques](https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic) to attempt to automatically detect which encodings your data is under. If it finds a suitable operation which can make sense of your data, it displays the 'magic' icon in the Output field which you can click to decode your data.
- Breakpoints
- You can set breakpoints on any operation in your recipe to pause execution before running it.
- You can also step through the recipe one operation at a time to see what the data looks like at each stage.
@@ -81,11 +84,12 @@ CyberChef is built to support
## Contributing
Contributing a new operation to CyberChef is super easy! There is a quickstart script which will walk you through the process. If you can write basic JavaScript, you can write a CyberChef operation.
An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the project [wiki pages](https://github.com/gchq/CyberChef/wiki).
- Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0)
- Push your changes to your fork.
- Submit a pull request.
- Submit a pull request. If you are doing this for the first time, you will be prompted to sign the [GCHQ Contributor Licence Agreement](https://cla-assistant.io/gchq/CyberChef) via the CLA assistant on the pull request. This will also ask whether you are happy for GCHQ to contact you about a token of thanks for your contribution, or about job opportunities at GCHQ.
## Licencing
@@ -104,3 +108,4 @@ CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/lice
[9]: https://gchq.github.io/CyberChef/#recipe=Register('key%3D(%5B%5C%5Cda-f%5D*)',true,false)Find_/_Replace(%7B'option':'Regex','string':'.*data%3D(.*)'%7D,'$1',true,false,true)RC4(%7B'option':'Hex','string':'$R0'%7D,'Hex','Latin1')&input=aHR0cDovL21hbHdhcmV6LmJpei9iZWFjb24ucGhwP2tleT0wZTkzMmE1YyZkYXRhPThkYjdkNWViZTM4NjYzYTU0ZWNiYjMzNGUzZGIxMQ
[10]: https://gchq.github.io/CyberChef/#recipe=Register('(.%7B32%7D)',true,false)Drop_bytes(0,32,false)AES_Decrypt(%7B'option':'Hex','string':'1748e7179bd56570d51fa4ba287cc3e5'%7D,%7B'option':'Hex','string':'$R0'%7D,'CTR','Hex','Raw',%7B'option':'Hex','string':''%7D)&input=NTFlMjAxZDQ2MzY5OGVmNWY3MTdmNzFmNWI0NzEyYWYyMGJlNjc0YjNiZmY1M2QzODU0NjM5NmVlNjFkYWFjNDkwOGUzMTljYTNmY2Y3MDg5YmZiNmIzOGVhOTllNzgxZDI2ZTU3N2JhOWRkNmYzMTFhMzk0MjBiODk3OGU5MzAxNGIwNDJkNDQ3MjZjYWVkZjU0MzZlYWY2NTI0MjljMGRmOTRiNTIxNjc2YzdjMmNlODEyMDk3YzI3NzI3M2M3YzcyY2Q4OWFlYzhkOWZiNGEyNzU4NmNjZjZhYTBhZWUyMjRjMzRiYTNiZmRmN2FlYjFkZGQ0Nzc2MjJiOTFlNzJjOWU3MDlhYjYwZjhkYWY3MzFlYzBjYzg1Y2UwZjc0NmZmMTU1NGE1YTNlYzI5MWNhNDBmOWU2MjlhODcyNTkyZDk4OGZkZDgzNDUzNGFiYTc5YzFhZDE2NzY3NjlhN2MwMTBiZjA0NzM5ZWNkYjY1ZDk1MzAyMzcxZDYyOWQ5ZTM3ZTdiNGEzNjFkYTQ2OGYxZWQ1MzU4OTIyZDJlYTc1MmRkMTFjMzY2ZjMwMTdiMTRhYTAxMWQyYWYwM2M0NGY5NTU3OTA5OGExNWUzY2Y5YjQ0ODZmOGZmZTljMjM5ZjM0ZGU3MTUxZjZjYTY1MDBmZTRiODUwYzNmMWMwMmU4MDFjYWYzYTI0NDY0NjE0ZTQyODAxNjE1YjhmZmFhMDdhYzgyNTE0OTNmZmRhN2RlNWRkZjMzNjg4ODBjMmI5NWIwMzBmNDFmOGYxNTA2NmFkZDA3MWE2NmNmNjBlNWY0NmYzYTIzMGQzOTdiNjUyOTYzYTIxYTUzZg
[11]: https://gchq.github.io/CyberChef/#recipe=XOR(%7B'option':'Hex','string':'3a'%7D,'Standard',false)To_Hexdump(16,false,false)&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4
[12]: https://gchq.github.io/CyberChef/#recipe=Magic(3,false,false)&input=V1VhZ3dzaWFlNm1QOGdOdENDTFVGcENwQ0IyNlJtQkRvREQ4UGFjZEFtekF6QlZqa0syUXN0RlhhS2hwQzZpVVM3UkhxWHJKdEZpc29SU2dvSjR3aGptMWFybTg2NHFhTnE0UmNmVW1MSHJjc0FhWmM1VFhDWWlmTmRnUzgzZ0RlZWpHWDQ2Z2FpTXl1QlY2RXNrSHQxc2NnSjg4eDJ0TlNvdFFEd2JHWTFtbUNvYjJBUkdGdkNLWU5xaU45aXBNcTFaVTFtZ2tkYk51R2NiNzZhUnRZV2hDR1VjOGc5M1VKdWRoYjhodHNoZVpud1RwZ3FoeDgzU1ZKU1pYTVhVakpUMnptcEM3dVhXdHVtcW9rYmRTaTg4WXRrV0RBYzFUb291aDJvSDRENGRkbU5LSldVRHBNd21uZ1VtSzE0eHdtb21jY1BRRTloTTE3MkFQblNxd3hkS1ExNzJSa2NBc3lzbm1qNWdHdFJtVk5OaDJzMzU5d3I2bVMyUVJQ

11675
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "7.7.3",
"version": "8.6.2",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -30,87 +30,109 @@
"main": "build/node/CyberChef.js",
"bugs": "https://github.com/gchq/CyberChef/issues",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.1",
"css-loader": "^0.28.9",
"eslint": "^4.17.0",
"exports-loader": "^0.6.4",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^1.1.6",
"grunt": ">=1.0.1",
"grunt-accessibility": "~5.0.0",
"autoprefixer": "^9.1.0",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"bootstrap": "^4.1.3",
"colors": "^1.3.1",
"css-loader": "^1.0.0",
"eslint": "^5.3.0",
"exports-loader": "^0.7.0",
"extract-text-webpack-plugin": "^4.0.0-alpha0",
"file-loader": "^1.1.11",
"grunt": "^1.0.3",
"grunt-accessibility": "~6.0.0",
"grunt-chmod": "~1.1.1",
"grunt-concurrent": "^2.3.1",
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-copy": "~1.0.0",
"grunt-eslint": "^20.1.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^21.0.0",
"grunt-exec": "~3.0.0",
"grunt-execute": "^0.2.2",
"grunt-jsdoc": "^2.2.1",
"grunt-webpack": "^3.0.2",
"html-webpack-plugin": "^2.30.1",
"imports-loader": "^0.7.1",
"grunt-webpack": "^3.1.2",
"html-webpack-plugin": "^3.2.0",
"imports-loader": "^0.8.0",
"ink-docstrap": "^1.3.2",
"jsdoc-babel": "^0.3.0",
"less": "^2.7.3",
"less-loader": "^4.0.5",
"postcss-css-variables": "^0.8.0",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.10",
"style-loader": "^0.19.1",
"url-loader": "^0.6.2",
"val-loader": "^1.1.0",
"js-to-mjs": "^0.2.0",
"jsdoc-babel": "^0.4.0",
"node-sass": "^4.9.2",
"postcss-css-variables": "^0.9.0",
"postcss-import": "^12.0.0",
"postcss-loader": "^2.1.6",
"prompt": "^1.0.0",
"sass-loader": "^7.1.0",
"sitemap": "^1.13.0",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"web-resource-inliner": "^4.2.1",
"webpack": "^3.10.0",
"webpack-dev-server": "^2.11.1",
"webpack-node-externals": "^1.6.0",
"worker-loader": "^1.1.0"
"webpack": "^4.16.4",
"webpack-dev-server": "^3.1.5",
"webpack-node-externals": "^1.7.2",
"worker-loader": "^2.0.0"
},
"dependencies": {
"arrive": "^2.4.1",
"babel-plugin-transform-builtin-extend": "1.1.2",
"babel-polyfill": "^6.26.0",
"bignumber.js": "^6.0.0",
"bootstrap": "^3.3.7",
"bootstrap-colorpicker": "^2.5.2",
"bootstrap-switch": "^3.3.4",
"crypto-api": "^0.7.5",
"bcryptjs": "^2.4.3",
"bignumber.js": "^7.2.1",
"bootstrap-colorpicker": "^2.5.3",
"bootstrap-material-design": "^4.1.1",
"bson": "^3.0.2",
"chi-squared": "^1.1.0",
"crypto-api": "^0.8.0",
"crypto-js": "^3.1.9-1",
"diff": "^3.4.0",
"escodegen": "^1.9.0",
"ctph.js": "0.0.5",
"diff": "^3.5.0",
"es6-promisify": "^6.0.0",
"escodegen": "^1.11.0",
"esmangle": "^1.0.1",
"esprima": "^4.0.0",
"esprima": "^4.0.1",
"exif-parser": "^0.1.12",
"file-saver": "^1.3.3",
"file-saver": "^1.3.8",
"highlight.js": "^9.12.0",
"jquery": "^3.3.1",
"js-crc": "^0.2.0",
"js-sha3": "^0.7.0",
"jsbn": "^1.1.0",
"jsesc": "^2.5.1",
"jsonpath": "^1.0.0",
"jsrsasign": "8.0.4",
"lodash": "^4.17.4",
"jsrsasign": "8.0.12",
"kbpgp": "^2.0.77",
"lodash": "^4.17.10",
"loglevel": "^1.6.1",
"loglevel-message-prefix": "^3.0.0",
"moment": "^2.20.1",
"moment-timezone": "^0.5.14",
"node-forge": "^0.7.1",
"moment": "^2.22.2",
"moment-timezone": "^0.5.21",
"ngeohash": "^0.6.0",
"node-forge": "^0.7.5",
"node-md6": "^0.1.0",
"nwmatcher": "^1.4.3",
"notepack.io": "^2.1.3",
"nwmatcher": "^1.4.4",
"otp": "^0.1.3",
"sladex-blowfish": "^0.8.1",
"popper.js": "^1.14.4",
"scryptsy": "^2.0.0",
"snackbarjs": "^1.1.0",
"sortablejs": "^1.7.0",
"split.js": "^1.3.5",
"ssdeep.js": "0.0.2",
"ua-parser-js": "^0.7.18",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
"xmldom": "^0.1.27",
"xpath": "0.0.27",
"xregexp": "^4.0.0",
"xregexp": "^4.2.0",
"zlibjs": "^0.3.1"
},
"scripts": {
"start": "grunt dev",
"build": "grunt prod",
"test": "grunt test",
"docs": "grunt docs"
"docs": "grunt docs",
"lint": "grunt lint",
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs",
"postinstall": "[ -f node_modules/crypto-api/src/crypto-api.mjs ] || npx j2m node_modules/crypto-api/src/crypto-api.js"
}
}

View File

@@ -1,165 +0,0 @@
import Dish from "./Dish.js";
import Recipe from "./Recipe.js";
/**
* The main controller for CyberChef.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
*/
const Chef = function() {
this.dish = new Dish();
};
/**
* Runs the recipe over the input.
*
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
* @param {Object[]} recipeConfig - The recipe configuration object
* @param {Object} options - The options object storing various user choices
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
* @param {number} progress - The position in the recipe to start from
* @param {number} [step] - Whether to only execute one operation in the recipe
*
* @returns {Object} response
* @returns {string} response.result - The output of the recipe
* @returns {string} response.type - The data type of the result
* @returns {number} response.progress - The position that we have got to in the recipe
* @returns {number} response.duration - The number of ms it took to execute the recipe
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
*/
Chef.prototype.bake = async function(input, recipeConfig, options, progress, step) {
log.debug("Chef baking");
const startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
containsFc = recipe.containsFlowControl(),
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
let error = false;
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Clean up progress
if (progress >= recipeConfig.length) {
progress = 0;
}
if (step) {
// Unset breakpoint on this step
recipe.setBreakpoint(progress, false);
// Set breakpoint on next step
recipe.setBreakpoint(progress + 1, true);
}
// If stepping with flow control, we have to start from the beginning
// but still want to skip all previous breakpoints
if (progress > 0 && containsFc) {
recipe.removeBreaksUpTo(progress);
progress = 0;
}
// If starting from scratch, load data
if (progress === 0) {
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
this.dish.set(input, type);
}
try {
progress = await recipe.execute(this.dish, progress);
} catch (err) {
log.error(err);
error = {
displayStr: err.displayStr,
};
progress = err.progress;
}
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
// The threshold is specified in KiB.
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
const returnType = this.dish.size() > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
return {
result: this.dish.type === Dish.HTML ?
this.dish.get(Dish.HTML, notUTF8) :
this.dish.get(returnType, notUTF8),
type: Dish.enumLookup(this.dish.type),
progress: progress,
duration: new Date().getTime() - startTime,
error: error
};
};
/**
* When a browser tab is unfocused and the browser has to run lots of dynamic content in other tabs,
* it swaps out the memory for that tab. If the CyberChef tab has been unfocused for more than a
* minute, we run a silent bake which will force the browser to load and cache all the relevant
* JavaScript code needed to do a real bake.
*
* This will stop baking taking a long time when the CyberChef browser tab has been unfocused for a
* long time and the browser has swapped out all its memory.
*
* The output will not be modified (hence "silent" bake).
*
* This will only actually execute the recipe if auto-bake is enabled, otherwise it will just load
* the recipe, ingredients and dish.
*
* @param {Object[]} recipeConfig - The recipe configuration object
* @returns {number} The time it took to run the silent bake in milliseconds.
*/
Chef.prototype.silentBake = function(recipeConfig) {
log.debug("Running silent bake");
let startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
dish = new Dish("", Dish.STRING);
try {
recipe.execute(dish);
} catch (err) {
// Suppress all errors
}
return new Date().getTime() - startTime;
};
/**
* Calculates highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
* @returns {Object}
*/
Chef.prototype.calculateHighlights = function(recipeConfig, direction, pos) {
const recipe = new Recipe(recipeConfig);
const highlights = recipe.generateHighlightList();
if (!highlights) return false;
for (let i = 0; i < highlights.length; i++) {
// Remove multiple highlights before processing again
pos = [pos[0]];
const func = direction === "forward" ? highlights[i].f : highlights[i].b;
if (typeof func == "function") {
pos = func(pos, highlights[i].args);
}
}
return {
pos: pos,
direction: direction
};
};
export default Chef;

198
src/core/Chef.mjs Executable file
View File

@@ -0,0 +1,198 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Dish from "./Dish";
import Recipe from "./Recipe";
import log from "loglevel";
/**
* The main controller for CyberChef.
*/
class Chef {
/**
* Chef constructor
*/
constructor() {
this.dish = new Dish();
}
/**
* Runs the recipe over the input.
*
* @param {string|ArrayBuffer} input - The input data as a string or ArrayBuffer
* @param {Object[]} recipeConfig - The recipe configuration object
* @param {Object} options - The options object storing various user choices
* @param {boolean} options.attempHighlight - Whether or not to attempt highlighting
* @param {number} progress - The position in the recipe to start from
* @param {number} [step] - Whether to only execute one operation in the recipe
*
* @returns {Object} response
* @returns {string} response.result - The output of the recipe
* @returns {string} response.type - The data type of the result
* @returns {number} response.progress - The position that we have got to in the recipe
* @returns {number} response.duration - The number of ms it took to execute the recipe
* @returns {number} response.error - The error object thrown by a failed operation (false if no error)
*/
async bake(input, recipeConfig, options, progress, step) {
log.debug("Chef baking");
const startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
containsFc = recipe.containsFlowControl(),
notUTF8 = options && options.hasOwnProperty("treatAsUtf8") && !options.treatAsUtf8;
let error = false;
if (containsFc && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Clean up progress
if (progress >= recipeConfig.length) {
progress = 0;
}
if (step) {
// Unset breakpoint on this step
recipe.setBreakpoint(progress, false);
// Set breakpoint on next step
recipe.setBreakpoint(progress + 1, true);
}
// If the previously run operation presented a different value to its
// normal output, we need to recalculate it.
if (recipe.lastOpPresented(progress)) {
progress = 0;
}
// If stepping with flow control, we have to start from the beginning
// but still want to skip all previous breakpoints
if (progress > 0 && containsFc) {
recipe.removeBreaksUpTo(progress);
progress = 0;
}
// If starting from scratch, load data
if (progress === 0) {
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
this.dish.set(input, type);
}
try {
progress = await recipe.execute(this.dish, progress);
} catch (err) {
log.error(err);
error = {
displayStr: err.displayStr,
};
progress = err.progress;
}
// Depending on the size of the output, we may send it back as a string or an ArrayBuffer.
// This can prevent unnecessary casting as an ArrayBuffer can be easily downloaded as a file.
// The threshold is specified in KiB.
const threshold = (options.ioDisplayThreshold || 1024) * 1024;
const returnType = this.dish.size > threshold ? Dish.ARRAY_BUFFER : Dish.STRING;
// Create a raw version of the dish, unpresented
const rawDish = this.dish.clone();
// Present the raw result
await recipe.present(this.dish);
return {
dish: rawDish,
result: this.dish.type === Dish.HTML ?
await this.dish.get(Dish.HTML, notUTF8) :
await this.dish.get(returnType, notUTF8),
type: Dish.enumLookup(this.dish.type),
progress: progress,
duration: new Date().getTime() - startTime,
error: error
};
}
/**
* When a browser tab is unfocused and the browser has to run lots of dynamic content in other tabs,
* it swaps out the memory for that tab. If the CyberChef tab has been unfocused for more than a
* minute, we run a silent bake which will force the browser to load and cache all the relevant
* JavaScript code needed to do a real bake.
*
* This will stop baking taking a long time when the CyberChef browser tab has been unfocused for a
* long time and the browser has swapped out all its memory.
*
* The output will not be modified (hence "silent" bake).
*
* This will only actually execute the recipe if auto-bake is enabled, otherwise it will just load
* the recipe, ingredients and dish.
*
* @param {Object[]} recipeConfig - The recipe configuration object
* @returns {number} The time it took to run the silent bake in milliseconds.
*/
silentBake(recipeConfig) {
log.debug("Running silent bake");
const startTime = new Date().getTime(),
recipe = new Recipe(recipeConfig),
dish = new Dish();
try {
recipe.execute(dish);
} catch (err) {
// Suppress all errors
}
return new Date().getTime() - startTime;
}
/**
* Calculates highlight offsets if possible.
*
* @param {Object[]} recipeConfig
* @param {string} direction
* @param {Object} pos - The position object for the highlight.
* @param {number} pos.start - The start offset.
* @param {number} pos.end - The end offset.
* @returns {Object}
*/
calculateHighlights(recipeConfig, direction, pos) {
const recipe = new Recipe(recipeConfig);
const highlights = recipe.generateHighlightList();
if (!highlights) return false;
for (let i = 0; i < highlights.length; i++) {
// Remove multiple highlights before processing again
pos = [pos[0]];
const func = direction === "forward" ? highlights[i].f : highlights[i].b;
if (typeof func == "function") {
pos = func(pos, highlights[i].args);
}
}
return {
pos: pos,
direction: direction
};
}
/**
* Translates the dish to a specified type and returns it.
*
* @param {Dish} dish
* @param {string} type
* @returns {Dish}
*/
async getDishAs(dish, type) {
const newDish = new Dish(dish);
return await newDish.get(type);
}
}
export default Chef;

View File

@@ -7,9 +7,9 @@
*/
import "babel-polyfill";
import Chef from "./Chef.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/Default.js";
import Chef from "./Chef";
import OperationConfig from "./config/OperationConfig.json";
import OpModules from "./config/modules/OpModules";
// Add ">" to the start of all log messages in the Chef Worker
import loglevelMessagePrefix from "loglevel-message-prefix";
@@ -60,6 +60,9 @@ self.addEventListener("message", function(e) {
case "silentBake":
silentBake(r.data);
break;
case "getDishAs":
getDishAs(r.data);
break;
case "docURL":
// Used to set the URL of the current document so that scripts can be
// imported into an inline worker.
@@ -88,7 +91,7 @@ self.addEventListener("message", function(e) {
*/
async function bake(data) {
// Ensure the relevant modules are loaded
loadRequiredModules(data.recipeConfig);
self.loadRequiredModules(data.recipeConfig);
try {
const response = await self.chef.bake(
@@ -101,12 +104,16 @@ async function bake(data) {
self.postMessage({
action: "bakeComplete",
data: response
data: Object.assign(response, {
id: data.id
})
});
} catch (err) {
self.postMessage({
action: "bakeError",
data: err
data: Object.assign(err, {
id: data.id
})
});
}
}
@@ -126,18 +133,16 @@ function silentBake(data) {
/**
* Checks that all required modules are loaded and loads them if not.
*
* @param {Object} recipeConfig
* Translates the dish to a given type.
*/
function loadRequiredModules(recipeConfig) {
recipeConfig.forEach(op => {
let module = self.OperationConfig[op.op].module;
async function getDishAs(data) {
const value = await self.chef.getDishAs(data.dish, data.type);
if (!OpModules.hasOwnProperty(module)) {
log.info("Loading module " + module);
self.sendStatusMessage("Loading module " + module);
self.importScripts(self.docURL + "/" + module + ".js");
self.postMessage({
action: "dishReturned",
data: {
value: value,
id: data.id
}
});
}
@@ -162,6 +167,25 @@ function calculateHighlights(recipeConfig, direction, pos) {
}
/**
* Checks that all required modules are loaded and loads them if not.
*
* @param {Object} recipeConfig
*/
self.loadRequiredModules = function(recipeConfig) {
recipeConfig.forEach(op => {
const module = self.OperationConfig[op.op].module;
if (!OpModules.hasOwnProperty(module)) {
log.info(`Loading ${module} module`);
self.sendStatusMessage(`Loading ${module} module`);
self.importScripts(`${self.docURL}/${module}.js`);
self.sendStatusMessage("");
}
});
};
/**
* Send status update to the app.
*

View File

@@ -1,274 +0,0 @@
import Utils from "./Utils.js";
import BigNumber from "bignumber.js";
/**
* The data being operated on by each operation.
*
* @author n1474335 [n1474335@gmail.com]
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
* @param {number} type - The data type of value, see Dish enums.
*/
const Dish = function(value, type) {
this.value = value || typeof value === "string" ? value : null;
this.type = type || Dish.BYTE_ARRAY;
};
/**
* Dish data type enum for byte arrays.
* @readonly
* @enum
*/
Dish.BYTE_ARRAY = 0;
/**
* Dish data type enum for strings.
* @readonly
* @enum
*/
Dish.STRING = 1;
/**
* Dish data type enum for numbers.
* @readonly
* @enum
*/
Dish.NUMBER = 2;
/**
* Dish data type enum for HTML.
* @readonly
* @enum
*/
Dish.HTML = 3;
/**
* Dish data type enum for ArrayBuffers.
* @readonly
* @enum
*/
Dish.ARRAY_BUFFER = 4;
/**
* Dish data type enum for BigNumbers.
* @readonly
* @enum
*/
Dish.BIG_NUMBER = 5;
/**
* Returns the data type enum for the given type string.
*
* @static
* @param {string} typeStr - The name of the data type.
* @returns {number} The data type enum value.
*/
Dish.typeEnum = function(typeStr) {
switch (typeStr.toLowerCase()) {
case "bytearray":
case "byte array":
return Dish.BYTE_ARRAY;
case "string":
return Dish.STRING;
case "number":
return Dish.NUMBER;
case "html":
return Dish.HTML;
case "arraybuffer":
case "array buffer":
return Dish.ARRAY_BUFFER;
case "bignumber":
case "big number":
return Dish.BIG_NUMBER;
default:
throw "Invalid data type string. No matching enum.";
}
};
/**
* Returns the data type string for the given type enum.
*
* @static
* @param {number} typeEnum - The enum value of the data type.
* @returns {string} The data type as a string.
*/
Dish.enumLookup = function(typeEnum) {
switch (typeEnum) {
case Dish.BYTE_ARRAY:
return "byteArray";
case Dish.STRING:
return "string";
case Dish.NUMBER:
return "number";
case Dish.HTML:
return "html";
case Dish.ARRAY_BUFFER:
return "ArrayBuffer";
case Dish.BIG_NUMBER:
return "BigNumber";
default:
throw "Invalid data type enum. No matching type.";
}
};
/**
* Sets the data value and type and then validates them.
*
* @param {byteArray|string|number|ArrayBuffer|BigNumber} value - The value of the input data.
* @param {number} type - The data type of value, see Dish enums.
*/
Dish.prototype.set = function(value, type) {
log.debug("Dish type: " + Dish.enumLookup(type));
this.value = value;
this.type = type;
if (!this.valid()) {
const sample = Utils.truncate(JSON.stringify(this.value), 13);
throw "Data is not a valid " + Dish.enumLookup(type) + ": " + sample;
}
};
/**
* Returns the value of the data in the type format specified.
*
* @param {number} type - The data type of value, see Dish enums.
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
* @returns {byteArray|string|number|ArrayBuffer|BigNumber} The value of the output data.
*/
Dish.prototype.get = function(type, notUTF8) {
if (this.type !== type) {
this.translate(type, notUTF8);
}
return this.value;
};
/**
* Translates the data to the given type format.
*
* @param {number} toType - The data type of value, see Dish enums.
* @param {boolean} [notUTF8] - Do not treat strings as UTF8.
*/
Dish.prototype.translate = function(toType, notUTF8) {
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
// Convert data to intermediate byteArray type
switch (this.type) {
case Dish.STRING:
this.value = this.value ? Utils.strToByteArray(this.value) : [];
break;
case Dish.NUMBER:
this.value = typeof this.value == "number" ? Utils.strToByteArray(this.value.toString()) : [];
break;
case Dish.HTML:
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
break;
case Dish.ARRAY_BUFFER:
// Array.from() would be nicer here, but it's slightly slower
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
break;
case Dish.BIG_NUMBER:
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
break;
default:
break;
}
this.type = Dish.BYTE_ARRAY;
// Convert from byteArray to toType
switch (toType) {
case Dish.STRING:
case Dish.HTML:
this.value = this.value ? byteArrayToStr(this.value) : "";
this.type = Dish.STRING;
break;
case Dish.NUMBER:
this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
this.type = Dish.NUMBER;
break;
case Dish.ARRAY_BUFFER:
this.value = new Uint8Array(this.value).buffer;
this.type = Dish.ARRAY_BUFFER;
break;
case Dish.BIG_NUMBER:
try {
this.value = new BigNumber(byteArrayToStr(this.value));
} catch (err) {
this.value = new BigNumber(NaN);
}
this.type = Dish.BIG_NUMBER;
break;
default:
break;
}
};
/**
* Validates that the value is the type that has been specified.
* May have to disable parts of BYTE_ARRAY validation if it effects performance.
*
* @returns {boolean} Whether the data is valid or not.
*/
Dish.prototype.valid = function() {
switch (this.type) {
case Dish.BYTE_ARRAY:
if (!(this.value instanceof Array)) {
return false;
}
// Check that every value is a number between 0 - 255
for (let i = 0; i < this.value.length; i++) {
if (typeof this.value[i] !== "number" ||
this.value[i] < 0 ||
this.value[i] > 255) {
return false;
}
}
return true;
case Dish.STRING:
case Dish.HTML:
return typeof this.value === "string";
case Dish.NUMBER:
return typeof this.value === "number";
case Dish.ARRAY_BUFFER:
return this.value instanceof ArrayBuffer;
case Dish.BIG_NUMBER:
return this.value instanceof BigNumber;
default:
return false;
}
};
/**
* Determines how much space the Dish takes up.
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
* we measure how many bytes are taken up when the number is written as a string.
*
* @returns {number}
*/
Dish.prototype.size = function() {
switch (this.type) {
case Dish.BYTE_ARRAY:
case Dish.STRING:
case Dish.HTML:
return this.value.length;
case Dish.NUMBER:
case Dish.BIG_NUMBER:
return this.value.toString().length;
case Dish.ARRAY_BUFFER:
return this.value.byteLength;
default:
return -1;
}
};
export default Dish;

425
src/core/Dish.mjs Executable file
View File

@@ -0,0 +1,425 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "./Utils";
import BigNumber from "bignumber.js";
import log from "loglevel";
/**
* The data being operated on by each operation.
*/
class Dish {
/**
* Dish constructor
*
* @param {Dish} [dish=null] - A dish to clone
*/
constructor(dish=null) {
this.value = [];
this.type = Dish.BYTE_ARRAY;
if (dish &&
dish.hasOwnProperty("value") &&
dish.hasOwnProperty("type")) {
this.set(dish.value, dish.type);
}
}
/**
* Returns the data type enum for the given type string.
*
* @param {string} typeStr - The name of the data type.
* @returns {number} The data type enum value.
*/
static typeEnum(typeStr) {
switch (typeStr.toLowerCase()) {
case "bytearray":
case "byte array":
return Dish.BYTE_ARRAY;
case "string":
return Dish.STRING;
case "number":
return Dish.NUMBER;
case "html":
return Dish.HTML;
case "arraybuffer":
case "array buffer":
return Dish.ARRAY_BUFFER;
case "bignumber":
case "big number":
return Dish.BIG_NUMBER;
case "json":
return Dish.JSON;
case "file":
return Dish.FILE;
case "list<file>":
return Dish.LIST_FILE;
default:
throw "Invalid data type string. No matching enum.";
}
}
/**
* Returns the data type string for the given type enum.
*
* @param {number} typeEnum - The enum value of the data type.
* @returns {string} The data type as a string.
*/
static enumLookup(typeEnum) {
switch (typeEnum) {
case Dish.BYTE_ARRAY:
return "byteArray";
case Dish.STRING:
return "string";
case Dish.NUMBER:
return "number";
case Dish.HTML:
return "html";
case Dish.ARRAY_BUFFER:
return "ArrayBuffer";
case Dish.BIG_NUMBER:
return "BigNumber";
case Dish.JSON:
return "JSON";
case Dish.FILE:
return "File";
case Dish.LIST_FILE:
return "List<File>";
default:
throw "Invalid data type enum. No matching type.";
}
}
/**
* Sets the data value and type and then validates them.
*
* @param {*} value
* - The value of the input data.
* @param {number} type
* - The data type of value, see Dish enums.
*/
set(value, type) {
if (typeof type === "string") {
type = Dish.typeEnum(type);
}
log.debug("Dish type: " + Dish.enumLookup(type));
this.value = value;
this.type = type;
if (!this.valid()) {
const sample = Utils.truncate(JSON.stringify(this.value), 13);
throw "Data is not a valid " + Dish.enumLookup(type) + ": " + sample;
}
}
/**
* Returns the value of the data in the type format specified.
*
* @param {number} type - The data type of value, see Dish enums.
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
* @returns {*} - The value of the output data.
*/
async get(type, notUTF8=false) {
if (typeof type === "string") {
type = Dish.typeEnum(type);
}
if (this.type !== type) {
await this._translate(type, notUTF8);
}
return this.value;
}
/**
* Translates the data to the given type format.
*
* @param {number} toType - The data type of value, see Dish enums.
* @param {boolean} [notUTF8=false] - Do not treat strings as UTF8.
*/
async _translate(toType, notUTF8=false) {
log.debug(`Translating Dish from ${Dish.enumLookup(this.type)} to ${Dish.enumLookup(toType)}`);
const byteArrayToStr = notUTF8 ? Utils.byteArrayToChars : Utils.byteArrayToUtf8;
// Convert data to intermediate byteArray type
switch (this.type) {
case Dish.STRING:
this.value = this.value ? Utils.strToByteArray(this.value) : [];
break;
case Dish.NUMBER:
this.value = typeof this.value === "number" ? Utils.strToByteArray(this.value.toString()) : [];
break;
case Dish.HTML:
this.value = this.value ? Utils.strToByteArray(Utils.unescapeHtml(Utils.stripHtmlTags(this.value, true))) : [];
break;
case Dish.ARRAY_BUFFER:
// Array.from() would be nicer here, but it's slightly slower
this.value = Array.prototype.slice.call(new Uint8Array(this.value));
break;
case Dish.BIG_NUMBER:
this.value = this.value instanceof BigNumber ? Utils.strToByteArray(this.value.toFixed()) : [];
break;
case Dish.JSON:
this.value = this.value ? Utils.strToByteArray(JSON.stringify(this.value, null, 4)) : [];
break;
case Dish.FILE:
this.value = await Utils.readFile(this.value);
this.value = Array.prototype.slice.call(this.value);
break;
case Dish.LIST_FILE:
this.value = await Promise.all(this.value.map(async f => Utils.readFile(f)));
this.value = this.value.map(b => Array.prototype.slice.call(b));
this.value = [].concat.apply([], this.value);
break;
default:
break;
}
this.type = Dish.BYTE_ARRAY;
// Convert from byteArray to toType
switch (toType) {
case Dish.STRING:
case Dish.HTML:
this.value = this.value ? byteArrayToStr(this.value) : "";
this.type = Dish.STRING;
break;
case Dish.NUMBER:
this.value = this.value ? parseFloat(byteArrayToStr(this.value)) : 0;
this.type = Dish.NUMBER;
break;
case Dish.ARRAY_BUFFER:
this.value = new Uint8Array(this.value).buffer;
this.type = Dish.ARRAY_BUFFER;
break;
case Dish.BIG_NUMBER:
try {
this.value = new BigNumber(byteArrayToStr(this.value));
} catch (err) {
this.value = new BigNumber(NaN);
}
this.type = Dish.BIG_NUMBER;
break;
case Dish.JSON:
this.value = JSON.parse(byteArrayToStr(this.value));
this.type = Dish.JSON;
break;
case Dish.FILE:
this.value = new File(this.value, "unknown");
break;
case Dish.LIST_FILE:
this.value = [new File(this.value, "unknown")];
this.type = Dish.LIST_FILE;
break;
default:
break;
}
}
/**
* Validates that the value is the type that has been specified.
* May have to disable parts of BYTE_ARRAY validation if it effects performance.
*
* @returns {boolean} Whether the data is valid or not.
*/
valid() {
switch (this.type) {
case Dish.BYTE_ARRAY:
if (!(this.value instanceof Array)) {
return false;
}
// Check that every value is a number between 0 - 255
for (let i = 0; i < this.value.length; i++) {
if (typeof this.value[i] !== "number" ||
this.value[i] < 0 ||
this.value[i] > 255) {
return false;
}
}
return true;
case Dish.STRING:
case Dish.HTML:
return typeof this.value === "string";
case Dish.NUMBER:
return typeof this.value === "number";
case Dish.ARRAY_BUFFER:
return this.value instanceof ArrayBuffer;
case Dish.BIG_NUMBER:
return this.value instanceof BigNumber;
case Dish.JSON:
// All values can be serialised in some manner, so we return true in all cases
return true;
case Dish.FILE:
return this.value instanceof File;
case Dish.LIST_FILE:
return this.value instanceof Array &&
this.value.reduce((acc, curr) => acc && curr instanceof File, true);
default:
return false;
}
}
/**
* Determines how much space the Dish takes up.
* Numbers in JavaScript are 64-bit floating point, however for the purposes of the Dish,
* we measure how many bytes are taken up when the number is written as a string.
*
* @returns {number}
*/
get size() {
switch (this.type) {
case Dish.BYTE_ARRAY:
case Dish.STRING:
case Dish.HTML:
return this.value.length;
case Dish.NUMBER:
case Dish.BIG_NUMBER:
return this.value.toString().length;
case Dish.ARRAY_BUFFER:
return this.value.byteLength;
case Dish.JSON:
return JSON.stringify(this.value).length;
case Dish.FILE:
return this.value.size;
case Dish.LIST_FILE:
return this.value.reduce((acc, curr) => acc + curr.size, 0);
default:
return -1;
}
}
/**
* Returns a deep clone of the current Dish.
*
* @returns {Dish}
*/
clone() {
const newDish = new Dish();
switch (this.type) {
case Dish.STRING:
case Dish.HTML:
case Dish.NUMBER:
case Dish.BIG_NUMBER:
// These data types are immutable so it is acceptable to copy them by reference
newDish.set(
this.value,
this.type
);
break;
case Dish.BYTE_ARRAY:
case Dish.JSON:
// These data types are mutable so they need to be copied by value
newDish.set(
JSON.parse(JSON.stringify(this.value)),
this.type
);
break;
case Dish.ARRAY_BUFFER:
// Slicing an ArrayBuffer returns a new ArrayBuffer with a copy its contents
newDish.set(
this.value.slice(0),
this.type
);
break;
case Dish.FILE:
// A new file can be created by copying over all the values from the original
newDish.set(
new File([this.value], this.value.name, {
"type": this.value.type,
"lastModified": this.value.lastModified
}),
this.type
);
break;
case Dish.LIST_FILE:
newDish.set(
this.value.map(f =>
new File([f], f.name, {
"type": f.type,
"lastModified": f.lastModified
})
),
this.type
);
break;
default:
throw new Error("Cannot clone Dish, unknown type");
}
return newDish;
}
}
/**
* Dish data type enum for byte arrays.
* @readonly
* @enum
*/
Dish.BYTE_ARRAY = 0;
/**
* Dish data type enum for strings.
* @readonly
* @enum
*/
Dish.STRING = 1;
/**
* Dish data type enum for numbers.
* @readonly
* @enum
*/
Dish.NUMBER = 2;
/**
* Dish data type enum for HTML.
* @readonly
* @enum
*/
Dish.HTML = 3;
/**
* Dish data type enum for ArrayBuffers.
* @readonly
* @enum
*/
Dish.ARRAY_BUFFER = 4;
/**
* Dish data type enum for BigNumbers.
* @readonly
* @enum
*/
Dish.BIG_NUMBER = 5;
/**
* Dish data type enum for JSON.
* @readonly
* @enum
*/
Dish.JSON = 6;
/**
* Dish data type enum for lists of files.
* @readonly
* @enum
*/
Dish.FILE = 7;
/**
* Dish data type enum for lists of files.
* @readonly
* @enum
*/
Dish.LIST_FILE = 8;
export default Dish;

View File

@@ -1,289 +0,0 @@
import Recipe from "./Recipe.js";
import Dish from "./Dish.js";
/**
* Flow Control operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const FlowControl = {
/**
* Fork operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
runFork: async function(state) {
let opList = state.opList,
inputType = opList[state.progress].inputType,
outputType = opList[state.progress].outputType,
input = state.dish.get(inputType),
ings = opList[state.progress].getIngValues(),
splitDelim = ings[0],
mergeDelim = ings[1],
ignoreErrors = ings[2],
subOpList = [],
inputs = [],
i;
if (input)
inputs = input.split(splitDelim);
// Create subOpList for each tranche to operate on
// (all remaining operations unless we encounter a Merge)
for (i = state.progress + 1; i < opList.length; i++) {
if (opList[i].name === "Merge" && !opList[i].isDisabled()) {
break;
} else {
subOpList.push(opList[i]);
}
}
let recipe = new Recipe(),
output = "",
progress = 0;
state.forkOffset += state.progress + 1;
recipe.addOperations(subOpList);
// Take a deep(ish) copy of the ingredient values
const ingValues = subOpList.map(op => JSON.parse(JSON.stringify(op.getIngValues())));
// Run recipe over each tranche
for (i = 0; i < inputs.length; i++) {
log.debug(`Entering tranche ${i + 1} of ${inputs.length}`);
// Baseline ing values for each tranche so that registers are reset
subOpList.forEach((op, i) => {
op.setIngValues(JSON.parse(JSON.stringify(ingValues[i])));
});
const dish = new Dish(inputs[i], inputType);
try {
progress = await recipe.execute(dish, 0, state);
} catch (err) {
if (!ignoreErrors) {
throw err;
}
progress = err.progress + 1;
}
output += dish.get(outputType) + mergeDelim;
}
state.dish.set(output, outputType);
state.progress += progress;
return state;
},
/**
* Merge operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
runMerge: function(state) {
// No need to actually do anything here. The fork operation will
// merge when it sees this operation.
return state;
},
/**
* Register operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
runRegister: function(state) {
const ings = state.opList[state.progress].getIngValues(),
extractorStr = ings[0],
i = ings[1],
m = ings[2];
let modifiers = "";
if (i) modifiers += "i";
if (m) modifiers += "m";
const extractor = new RegExp(extractorStr, modifiers),
input = state.dish.get(Dish.STRING),
registers = input.match(extractor);
if (!registers) return state;
if (ENVIRONMENT_IS_WORKER()) {
self.setRegisters(state.forkOffset + state.progress, state.numRegisters, registers.slice(1));
}
/**
* Replaces references to registers (e.g. $R0) with the contents of those registers.
*
* @param {string} str
* @returns {string}
*/
function replaceRegister(str) {
// Replace references to registers ($Rn) with contents of registers
return str.replace(/(\\*)\$R(\d{1,2})/g, (match, slashes, regNum) => {
const index = parseInt(regNum, 10) + 1;
if (index <= state.numRegisters || index >= state.numRegisters + registers.length)
return match;
if (slashes.length % 2 !== 0) return match.slice(1); // Remove escape
return slashes + registers[index - state.numRegisters];
});
}
// Step through all subsequent ops and replace registers in args with extracted content
for (let i = state.progress + 1; i < state.opList.length; i++) {
if (state.opList[i].isDisabled()) continue;
let args = state.opList[i].getIngValues();
args = args.map(arg => {
if (typeof arg !== "string" && typeof arg !== "object") return arg;
if (typeof arg === "object" && arg.hasOwnProperty("string")) {
arg.string = replaceRegister(arg.string);
return arg;
}
return replaceRegister(arg);
});
state.opList[i].setIngValues(args);
}
state.numRegisters += registers.length - 1;
return state;
},
/**
* Jump operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @param {number} state.numJumps - The number of jumps taken so far.
* @returns {Object} The updated state of the recipe.
*/
runJump: function(state) {
const ings = state.opList[state.progress].getIngValues(),
label = ings[0],
maxJumps = ings[1],
jmpIndex = FlowControl._getLabelIndex(label, state);
if (state.numJumps >= maxJumps || jmpIndex === -1) {
log.debug("Maximum jumps reached or label cannot be found");
return state;
}
state.progress = jmpIndex;
state.numJumps++;
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
return state;
},
/**
* Conditional Jump operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @param {number} state.numJumps - The number of jumps taken so far.
* @returns {Object} The updated state of the recipe.
*/
runCondJump: function(state) {
const ings = state.opList[state.progress].getIngValues(),
dish = state.dish,
regexStr = ings[0],
invert = ings[1],
label = ings[2],
maxJumps = ings[3],
jmpIndex = FlowControl._getLabelIndex(label, state);
if (state.numJumps >= maxJumps || jmpIndex === -1) {
log.debug("Maximum jumps reached or label cannot be found");
return state;
}
if (regexStr !== "") {
let strMatch = dish.get(Dish.STRING).search(regexStr) > -1;
if (!invert && strMatch || invert && !strMatch) {
state.progress = jmpIndex;
state.numJumps++;
log.debug(`Jumping to label '${label}' at position ${jmpIndex} (jumps = ${state.numJumps})`);
}
}
return state;
},
/**
* Return operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
runReturn: function(state) {
state.progress = state.opList.length;
return state;
},
/**
* Comment operation.
*
* @param {Object} state - The current state of the recipe.
* @param {number} state.progress - The current position in the recipe.
* @param {Dish} state.dish - The Dish being operated on.
* @param {Operation[]} state.opList - The list of operations in the recipe.
* @returns {Object} The updated state of the recipe.
*/
runComment: function(state) {
return state;
},
/**
* Returns the index of a label.
*
* @private
* @param {Object} state
* @param {string} name
* @returns {number}
*/
_getLabelIndex: function(name, state) {
for (let o = 0; o < state.opList.length; o++) {
let operation = state.opList[o];
if (operation.name === "Label"){
let ings = operation.getIngValues();
if (name === ings[0]) {
return o;
}
}
}
return -1;
},
};
export default FlowControl;

View File

@@ -1,92 +0,0 @@
import Utils from "./Utils.js";
/**
* The arguments to operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {Object} ingredientConfig
*/
const Ingredient = function(ingredientConfig) {
this.name = "";
this.type = "";
this.value = null;
if (ingredientConfig) {
this._parseConfig(ingredientConfig);
}
};
/**
* Reads and parses the given config.
*
* @private
* @param {Object} ingredientConfig
*/
Ingredient.prototype._parseConfig = function(ingredientConfig) {
this.name = ingredientConfig.name;
this.type = ingredientConfig.type;
};
/**
* Returns the value of the Ingredient as it should be displayed in a recipe config.
*
* @returns {*}
*/
Ingredient.prototype.getConfig = function() {
return this.value;
};
/**
* Sets the value of the Ingredient.
*
* @param {*} value
*/
Ingredient.prototype.setValue = function(value) {
this.value = Ingredient.prepare(value, this.type);
};
/**
* Most values will be strings when they are entered. This function converts them to the correct
* type.
*
* @static
* @param {*} data
* @param {string} type - The name of the data type.
*/
Ingredient.prepare = function(data, type) {
let number;
switch (type) {
case "binaryString":
case "binaryShortString":
case "editableOption":
return Utils.parseEscapedChars(data);
case "byteArray":
if (typeof data == "string") {
data = data.replace(/\s+/g, "");
return Utils.fromHex(data);
} else {
return data;
}
case "number":
number = parseFloat(data);
if (isNaN(number)) {
const sample = Utils.truncate(data.toString(), 10);
throw "Invalid ingredient value. Not a number: " + sample;
}
return number;
default:
return data;
}
};
export default Ingredient;

118
src/core/Ingredient.mjs Executable file
View File

@@ -0,0 +1,118 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "./Utils";
import {fromHex} from "./lib/Hex";
/**
* The arguments to operations.
*/
class Ingredient {
/**
* Ingredient constructor
*
* @param {Object} ingredientConfig
*/
constructor(ingredientConfig) {
this.name = "";
this.type = "";
this._value = null;
this.disabled = false;
this.hint = "";
this.toggleValues = [];
this.target = null;
if (ingredientConfig) {
this._parseConfig(ingredientConfig);
}
}
/**
* Reads and parses the given config.
*
* @private
* @param {Object} ingredientConfig
*/
_parseConfig(ingredientConfig) {
this.name = ingredientConfig.name;
this.type = ingredientConfig.type;
this.defaultValue = ingredientConfig.value;
this.disabled = !!ingredientConfig.disabled;
this.hint = ingredientConfig.hint || false;
this.toggleValues = ingredientConfig.toggleValues;
this.target = typeof ingredientConfig.target !== "undefined" ? ingredientConfig.target : null;
}
/**
* Returns the value of the Ingredient as it should be displayed in a recipe config.
*
* @returns {*}
*/
get config() {
return this._value;
}
/**
* Sets the value of the Ingredient.
*
* @param {*} value
*/
set value(value) {
this._value = Ingredient.prepare(value, this.type);
}
/**
* Gets the value of the Ingredient.
*
* @returns {*}
*/
get value() {
return this._value;
}
/**
* Most values will be strings when they are entered. This function converts them to the correct
* type.
*
* @param {*} data
* @param {string} type - The name of the data type.
*/
static prepare(data, type) {
let number;
switch (type) {
case "binaryString":
case "binaryShortString":
case "editableOption":
return Utils.parseEscapedChars(data);
case "byteArray":
if (typeof data == "string") {
data = data.replace(/\s+/g, "");
return fromHex(data);
} else {
return data;
}
case "number":
number = parseFloat(data);
if (isNaN(number)) {
const sample = Utils.truncate(data.toString(), 10);
throw "Invalid ingredient value. Not a number: " + sample;
}
return number;
default:
return data;
}
}
}
export default Ingredient;

View File

@@ -1,174 +0,0 @@
import Dish from "./Dish.js";
import Ingredient from "./Ingredient.js";
import OperationConfig from "./config/MetaConfig.js";
import OpModules from "./config/modules/OpModules.js";
/**
* The Operation specified by the user to be run.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {string} operationName
*/
const Operation = function(operationName) {
this.name = operationName;
this.module = "";
this.description = "";
this.inputType = -1;
this.outputType = -1;
this.run = null;
this.highlight = null;
this.highlightReverse = null;
this.breakpoint = false;
this.disabled = false;
this.ingList = [];
if (OperationConfig.hasOwnProperty(this.name)) {
this._parseConfig(OperationConfig[this.name]);
}
};
/**
* Reads and parses the given config.
*
* @private
* @param {Object} operationConfig
*/
Operation.prototype._parseConfig = function(operationConfig) {
this.module = operationConfig.module;
this.description = operationConfig.description;
this.inputType = Dish.typeEnum(operationConfig.inputType);
this.outputType = Dish.typeEnum(operationConfig.outputType);
this.highlight = operationConfig.highlight;
this.highlightReverse = operationConfig.highlightReverse;
this.flowControl = operationConfig.flowControl;
this.run = OpModules[this.module][this.name];
for (let a = 0; a < operationConfig.args.length; a++) {
const ingredientConfig = operationConfig.args[a];
const ingredient = new Ingredient(ingredientConfig);
this.addIngredient(ingredient);
}
if (this.highlight === "func") {
this.highlight = OpModules[this.module][`${this.name}-highlight`];
}
if (this.highlightReverse === "func") {
this.highlightReverse = OpModules[this.module][`${this.name}-highlightReverse`];
}
};
/**
* Returns the value of the Operation as it should be displayed in a recipe config.
*
* @returns {Object}
*/
Operation.prototype.getConfig = function() {
const ingredientConfig = [];
for (let o = 0; o < this.ingList.length; o++) {
ingredientConfig.push(this.ingList[o].getConfig());
}
const operationConfig = {
"op": this.name,
"args": ingredientConfig
};
return operationConfig;
};
/**
* Adds a new Ingredient to this Operation.
*
* @param {Ingredient} ingredient
*/
Operation.prototype.addIngredient = function(ingredient) {
this.ingList.push(ingredient);
};
/**
* Set the Ingredient values for this Operation.
*
* @param {Object[]} ingValues
*/
Operation.prototype.setIngValues = function(ingValues) {
for (let i = 0; i < ingValues.length; i++) {
this.ingList[i].setValue(ingValues[i]);
}
};
/**
* Get the Ingredient values for this Operation.
*
* @returns {Object[]}
*/
Operation.prototype.getIngValues = function() {
const ingValues = [];
for (let i = 0; i < this.ingList.length; i++) {
ingValues.push(this.ingList[i].value);
}
return ingValues;
};
/**
* Set whether this Operation has a breakpoint.
*
* @param {boolean} value
*/
Operation.prototype.setBreakpoint = function(value) {
this.breakpoint = !!value;
};
/**
* Returns true if this Operation has a breakpoint set.
*
* @returns {boolean}
*/
Operation.prototype.isBreakpoint = function() {
return this.breakpoint;
};
/**
* Set whether this Operation is disabled.
*
* @param {boolean} value
*/
Operation.prototype.setDisabled = function(value) {
this.disabled = !!value;
};
/**
* Returns true if this Operation is disabled.
*
* @returns {boolean}
*/
Operation.prototype.isDisabled = function() {
return this.disabled;
};
/**
* Returns true if this Operation is a flow control.
*
* @returns {boolean}
*/
Operation.prototype.isFlowControl = function() {
return this.flowControl;
};
export default Operation;

294
src/core/Operation.mjs Executable file
View File

@@ -0,0 +1,294 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Dish from "./Dish";
import Ingredient from "./Ingredient";
/**
* The Operation specified by the user to be run.
*/
class Operation {
/**
* Operation constructor
*/
constructor() {
// Private fields
this._inputType = -1;
this._outputType = -1;
this._presentType = -1;
this._breakpoint = false;
this._disabled = false;
this._flowControl = false;
this._ingList = [];
// Public fields
this.name = "";
this.module = "";
this.description = "";
this.infoURL = null;
}
/**
* Interface for operation runner
*
* @param {*} input
* @param {Object[]} args
* @returns {*}
*/
run(input, args) {
return input;
}
/**
* Interface for forward highlighter
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return false;
}
/**
* Interface for reverse highlighter
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return false;
}
/**
* Method to be called when displaying the result of an operation in a human-readable
* format. This allows operations to return usable data from their run() method and
* only format them when this method is called.
*
* The default action is to return the data unchanged, but child classes can override
* this behaviour.
*
* @param {*} data - The result of the run() function
* @param {Object[]} args - The operation's arguments
* @returns {*} - A human-readable version of the data
*/
present(data, args) {
return data;
}
/**
* Sets the input type as a Dish enum.
*
* @param {string} typeStr
*/
set inputType(typeStr) {
this._inputType = Dish.typeEnum(typeStr);
}
/**
* Gets the input type as a readable string.
*
* @returns {string}
*/
get inputType() {
return Dish.enumLookup(this._inputType);
}
/**
* Sets the output type as a Dish enum.
*
* @param {string} typeStr
*/
set outputType(typeStr) {
this._outputType = Dish.typeEnum(typeStr);
if (this._presentType < 0) this._presentType = this._outputType;
}
/**
* Gets the output type as a readable string.
*
* @returns {string}
*/
get outputType() {
return Dish.enumLookup(this._outputType);
}
/**
* Sets the presentation type as a Dish enum.
*
* @param {string} typeStr
*/
set presentType(typeStr) {
this._presentType = Dish.typeEnum(typeStr);
}
/**
* Gets the presentation type as a readable string.
*
* @returns {string}
*/
get presentType() {
return Dish.enumLookup(this._presentType);
}
/**
* Sets the args for the current operation.
*
* @param {Object[]} conf
*/
set args(conf) {
conf.forEach(arg => {
const ingredient = new Ingredient(arg);
this.addIngredient(ingredient);
});
}
/**
* Gets the args for the current operation.
*
* @param {Object[]} conf
*/
get args() {
return this._ingList.map(ing => {
const conf = {
name: ing.name,
type: ing.type,
value: ing.defaultValue
};
if (ing.toggleValues) conf.toggleValues = ing.toggleValues;
if (ing.hint) conf.hint = ing.hint;
if (ing.disabled) conf.disabled = ing.disabled;
if (ing.target) conf.target = ing.target;
return conf;
});
}
/**
* Returns the value of the Operation as it should be displayed in a recipe config.
*
* @returns {Object}
*/
get config() {
return {
"op": this.name,
"args": this._ingList.map(ing => ing.config)
};
}
/**
* Adds a new Ingredient to this Operation.
*
* @param {Ingredient} ingredient
*/
addIngredient(ingredient) {
this._ingList.push(ingredient);
}
/**
* Set the Ingredient values for this Operation.
*
* @param {Object[]} ingValues
*/
set ingValues(ingValues) {
ingValues.forEach((val, i) => {
this._ingList[i].value = val;
});
}
/**
* Get the Ingredient values for this Operation.
*
* @returns {Object[]}
*/
get ingValues() {
return this._ingList.map(ing => ing.value);
}
/**
* Set whether this Operation has a breakpoint.
*
* @param {boolean} value
*/
set breakpoint(value) {
this._breakpoint = !!value;
}
/**
* Returns true if this Operation has a breakpoint set.
*
* @returns {boolean}
*/
get breakpoint() {
return this._breakpoint;
}
/**
* Set whether this Operation is disabled.
*
* @param {boolean} value
*/
set disabled(value) {
this._disabled = !!value;
}
/**
* Returns true if this Operation is disabled.
*
* @returns {boolean}
*/
get disabled() {
return this._disabled;
}
/**
* Returns true if this Operation is a flow control.
*
* @returns {boolean}
*/
get flowControl() {
return this._flowControl;
}
/**
* Set whether this Operation is a flowcontrol op.
*
* @param {boolean} value
*/
set flowControl(value) {
this._flowControl = !!value;
}
}
export default Operation;

View File

@@ -1,264 +0,0 @@
import Operation from "./Operation.js";
/**
* The Recipe controls a list of Operations and the Dish they operate on.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @class
* @param {Object} recipeConfig
*/
const Recipe = function(recipeConfig) {
this.opList = [];
if (recipeConfig) {
this._parseConfig(recipeConfig);
}
};
/**
* Reads and parses the given config.
*
* @private
* @param {Object} recipeConfig
*/
Recipe.prototype._parseConfig = function(recipeConfig) {
for (let c = 0; c < recipeConfig.length; c++) {
const operationName = recipeConfig[c].op;
const operation = new Operation(operationName);
operation.setIngValues(recipeConfig[c].args);
operation.setBreakpoint(recipeConfig[c].breakpoint);
operation.setDisabled(recipeConfig[c].disabled);
this.addOperation(operation);
}
};
/**
* Returns the value of the Recipe as it should be displayed in a recipe config.
*
* @returns {*}
*/
Recipe.prototype.getConfig = function() {
const recipeConfig = [];
for (let o = 0; o < this.opList.length; o++) {
recipeConfig.push(this.opList[o].getConfig());
}
return recipeConfig;
};
/**
* Adds a new Operation to this Recipe.
*
* @param {Operation} operation
*/
Recipe.prototype.addOperation = function(operation) {
this.opList.push(operation);
};
/**
* Adds a list of Operations to this Recipe.
*
* @param {Operation[]} operations
*/
Recipe.prototype.addOperations = function(operations) {
this.opList = this.opList.concat(operations);
};
/**
* Set a breakpoint on a specified Operation.
*
* @param {number} position - The index of the Operation
* @param {boolean} value
*/
Recipe.prototype.setBreakpoint = function(position, value) {
try {
this.opList[position].setBreakpoint(value);
} catch (err) {
// Ignore index error
}
};
/**
* Remove breakpoints on all Operations in the Recipe up to the specified position. Used by Flow
* Control Fork operation.
*
* @param {number} pos
*/
Recipe.prototype.removeBreaksUpTo = function(pos) {
for (let i = 0; i < pos; i++) {
this.opList[i].setBreakpoint(false);
}
};
/**
* Returns true if there is an Flow Control Operation in this Recipe.
*
* @returns {boolean}
*/
Recipe.prototype.containsFlowControl = function() {
for (let i = 0; i < this.opList.length; i++) {
if (this.opList[i].isFlowControl()) return true;
}
return false;
};
/**
* Returns the index of the last Operation index that will be executed, taking into account disabled
* Operations and breakpoints.
*
* @param {number} [startIndex=0] - The index to start searching from
* @returns (number}
*/
Recipe.prototype.lastOpIndex = function(startIndex) {
let i = startIndex + 1 || 0,
op;
for (; i < this.opList.length; i++) {
op = this.opList[i];
if (op.isDisabled()) return i-1;
if (op.isBreakpoint()) return i-1;
}
return i-1;
};
/**
* Executes each operation in the recipe over the given Dish.
*
* @param {Dish} dish
* @param {number} [startFrom=0] - The index of the Operation to start executing from
* @param {number} [forkState={}] - If this is a forked recipe, the state of the recipe up to this point
* @returns {number} - The final progress through the recipe
*/
Recipe.prototype.execute = async function(dish, startFrom = 0, forkState = {}) {
let op, input, output,
numJumps = 0,
numRegisters = forkState.numRegisters || 0;
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
for (let i = startFrom; i < this.opList.length; i++) {
op = this.opList[i];
log.debug(`[${i}] ${op.name} ${JSON.stringify(op.getIngValues())}`);
if (op.isDisabled()) {
log.debug("Operation is disabled, skipping");
continue;
}
if (op.isBreakpoint()) {
log.debug("Pausing at breakpoint");
return i;
}
try {
input = dish.get(op.inputType);
log.debug("Executing operation");
if (op.isFlowControl()) {
// Package up the current state
let state = {
"progress": i,
"dish": dish,
"opList": this.opList,
"numJumps": numJumps,
"numRegisters": numRegisters,
"forkOffset": forkState.forkOffset || 0
};
state = await op.run(state);
i = state.progress;
numJumps = state.numJumps;
numRegisters = state.numRegisters;
} else {
output = await op.run(input, op.getIngValues());
dish.set(output, op.outputType);
}
} catch (err) {
const e = typeof err == "string" ? { message: err } : err;
e.progress = i;
if (e.fileName) {
e.displayStr = op.name + " - " + e.name + " in " +
e.fileName + " on line " + e.lineNumber +
".<br><br>Message: " + (e.displayStr || e.message);
} else {
e.displayStr = op.name + " - " + (e.displayStr || e.message);
}
throw e;
}
}
log.debug("Recipe complete");
return this.opList.length;
};
/**
* Returns the recipe configuration in string format.
*
* @returns {string}
*/
Recipe.prototype.toString = function() {
return JSON.stringify(this.getConfig());
};
/**
* Creates a Recipe from a given configuration string.
*
* @param {string} recipeStr
*/
Recipe.prototype.fromString = function(recipeStr) {
const recipeConfig = JSON.parse(recipeStr);
this._parseConfig(recipeConfig);
};
/**
* Generates a list of all the highlight functions assigned to operations in the recipe, if the
* entire recipe supports highlighting.
*
* @returns {Object[]} highlights
* @returns {function} highlights[].f
* @returns {function} highlights[].b
* @returns {Object[]} highlights[].args
*/
Recipe.prototype.generateHighlightList = function() {
const highlights = [];
for (let i = 0; i < this.opList.length; i++) {
let op = this.opList[i];
if (op.isDisabled()) continue;
// If any breakpoints are set, do not attempt to highlight
if (op.isBreakpoint()) return false;
// If any of the operations do not support highlighting, fail immediately.
if (op.highlight === false || op.highlight === undefined) return false;
highlights.push({
f: op.highlight,
b: op.highlightReverse,
args: op.getIngValues()
});
}
return highlights;
};
export default Recipe;

290
src/core/Recipe.mjs Executable file
View File

@@ -0,0 +1,290 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
// import Operation from "./Operation.js";
import OpModules from "./config/modules/OpModules";
import OperationConfig from "./config/OperationConfig.json";
import OperationError from "./errors/OperationError";
import log from "loglevel";
/**
* The Recipe controls a list of Operations and the Dish they operate on.
*/
class Recipe {
/**
* Recipe constructor
*
* @param {Object} recipeConfig
*/
constructor(recipeConfig) {
this.opList = [];
if (recipeConfig) {
this._parseConfig(recipeConfig);
}
}
/**
* Reads and parses the given config.
*
* @private
* @param {Object} recipeConfig
*/
_parseConfig(recipeConfig) {
for (let c = 0; c < recipeConfig.length; c++) {
const operationName = recipeConfig[c].op;
const opConf = OperationConfig[operationName];
const opObj = OpModules[opConf.module][operationName];
const operation = new opObj();
operation.ingValues = recipeConfig[c].args;
operation.breakpoint = recipeConfig[c].breakpoint;
operation.disabled = recipeConfig[c].disabled;
this.addOperation(operation);
}
}
/**
* Returns the value of the Recipe as it should be displayed in a recipe config.
*
* @returns {Object[]}
*/
get config() {
return this.opList.map(op => op.config);
}
/**
* Adds a new Operation to this Recipe.
*
* @param {Operation} operation
*/
addOperation(operation) {
this.opList.push(operation);
}
/**
* Adds a list of Operations to this Recipe.
*
* @param {Operation[]} operations
*/
addOperations(operations) {
this.opList = this.opList.concat(operations);
}
/**
* Set a breakpoint on a specified Operation.
*
* @param {number} position - The index of the Operation
* @param {boolean} value
*/
setBreakpoint(position, value) {
try {
this.opList[position].breakpoint = value;
} catch (err) {
// Ignore index error
}
}
/**
* Remove breakpoints on all Operations in the Recipe up to the specified position. Used by Flow
* Control Fork operation.
*
* @param {number} pos
*/
removeBreaksUpTo(pos) {
for (let i = 0; i < pos; i++) {
this.opList[i].breakpoint = false;
}
}
/**
* Returns true if there is an Flow Control Operation in this Recipe.
*
* @returns {boolean}
*/
containsFlowControl() {
return this.opList.reduce((acc, curr) => {
return acc || curr.flowControl;
}, false);
}
/**
* Executes each operation in the recipe over the given Dish.
*
* @param {Dish} dish
* @param {number} [startFrom=0]
* - The index of the Operation to start executing from
* @param {number} [forkState={}]
* - If this is a forked recipe, the state of the recipe up to this point
* @returns {number}
* - The final progress through the recipe
*/
async execute(dish, startFrom=0, forkState={}) {
let op, input, output,
numJumps = 0,
numRegisters = forkState.numRegisters || 0;
if (startFrom === 0) this.lastRunOp = null;
log.debug(`[*] Executing recipe of ${this.opList.length} operations, starting at ${startFrom}`);
for (let i = startFrom; i < this.opList.length; i++) {
op = this.opList[i];
log.debug(`[${i}] ${op.name} ${JSON.stringify(op.ingValues)}`);
if (op.disabled) {
log.debug("Operation is disabled, skipping");
continue;
}
if (op.breakpoint) {
log.debug("Pausing at breakpoint");
return i;
}
try {
input = await dish.get(op.inputType);
log.debug("Executing operation");
if (op.flowControl) {
// Package up the current state
let state = {
"progress": i,
"dish": dish,
"opList": this.opList,
"numJumps": numJumps,
"numRegisters": numRegisters,
"forkOffset": forkState.forkOffset || 0
};
state = await op.run(state);
i = state.progress;
numJumps = state.numJumps;
numRegisters = state.numRegisters;
} else {
output = await op.run(input, op.ingValues);
dish.set(output, op.outputType);
}
this.lastRunOp = op;
} catch (err) {
// Return expected errors as output
if (err instanceof OperationError ||
(err.type && err.type === "OperationError")) {
// Cannot rely on `err instanceof OperationError` here as extending
// native types is not fully supported yet.
dish.set(err.message, "string");
return i;
} else {
const e = typeof err == "string" ? { message: err } : err;
e.progress = i;
if (e.fileName) {
e.displayStr = `${op.name} - ${e.name} in ${e.fileName} on line ` +
`${e.lineNumber}.<br><br>Message: ${e.displayStr || e.message}`;
} else {
e.displayStr = `${op.name} - ${e.displayStr || e.message}`;
}
throw e;
}
}
}
log.debug("Recipe complete");
return this.opList.length;
}
/**
* Present the results of the final operation.
*
* @param {Dish} dish
*/
async present(dish) {
if (!this.lastRunOp) return;
const output = await this.lastRunOp.present(
await dish.get(this.lastRunOp.outputType),
this.lastRunOp.ingValues
);
dish.set(output, this.lastRunOp.presentType);
}
/**
* Returns the recipe configuration in string format.
*
* @returns {string}
*/
toString() {
return JSON.stringify(this.config);
}
/**
* Creates a Recipe from a given configuration string.
*
* @param {string} recipeStr
*/
fromString(recipeStr) {
const recipeConfig = JSON.parse(recipeStr);
this._parseConfig(recipeConfig);
}
/**
* Generates a list of all the highlight functions assigned to operations in the recipe, if the
* entire recipe supports highlighting.
*
* @returns {Object[]} highlights
* @returns {function} highlights[].f
* @returns {function} highlights[].b
* @returns {Object[]} highlights[].args
*/
generateHighlightList() {
const highlights = [];
for (let i = 0; i < this.opList.length; i++) {
const op = this.opList[i];
if (op.disabled) continue;
// If any breakpoints are set, do not attempt to highlight
if (op.breakpoint) return false;
// If any of the operations do not support highlighting, fail immediately.
if (op.highlight === false || op.highlight === undefined) return false;
highlights.push({
f: op.highlight,
b: op.highlightReverse,
args: op.ingValues
});
}
return highlights;
}
/**
* Determines whether the previous operation has a different presentation type to its normal output.
*
* @param {number} progress
* @returns {boolean}
*/
lastOpPresented(progress) {
if (progress < 1) return false;
return this.opList[progress-1].presentType !== this.opList[progress-1].outputType;
}
}
export default Recipe;

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,11 @@
/**
* Type definition for a CatConf.
*
* @typedef {Object} CatConf
* @property {string} name - The display name for the category
* @property {string[]} ops - A list of the operations to be included in this category
*/
/**
* Categories of operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @constant
* @type {CatConf[]}
*/
const Categories = [
[
{
name: "Favourites",
ops: []
"name": "Favourites",
"ops": []
},
{
name: "Data format",
ops: [
"name": "Data format",
"ops": [
"To Hexdump",
"From Hexdump",
"To Hex",
@@ -44,6 +25,8 @@ const Categories = [
"From Base32",
"To Base58",
"From Base58",
"To Base85",
"From Base85",
"To Base",
"From Base",
"To BCD",
@@ -52,6 +35,7 @@ const Categories = [
"From HTML Entity",
"URL Encode",
"URL Decode",
"Escape Unicode Characters",
"Unescape Unicode Characters",
"To Quoted Printable",
"From Quoted Printable",
@@ -66,11 +50,15 @@ const Categories = [
"Encode text",
"Decode text",
"Swap endianness",
"To MessagePack",
"From MessagePack",
"To Braille",
"From Braille"
]
},
{
name: "Encryption / Encoding",
ops: [
"name": "Encryption / Encoding",
"ops": [
"AES Encrypt",
"AES Decrypt",
"Blowfish Encrypt",
@@ -99,23 +87,36 @@ const Categories = [
"Substitute",
"Derive PBKDF2 key",
"Derive EVP key",
"Pseudo-Random Number Generator",
"Bcrypt",
"Scrypt",
"Pseudo-Random Number Generator"
]
},
{
name: "Public Key",
ops: [
"name": "Public Key",
"ops": [
"Parse X.509 certificate",
"Parse ASN.1 hex string",
"PEM to Hex",
"Hex to PEM",
"Hex to Object Identifier",
"Object Identifier to Hex",
"Generate PGP Key Pair",
"PGP Encrypt",
"PGP Decrypt",
"PGP Encrypt and Sign",
"PGP Decrypt and Verify"
]
},
{
name: "Arithmetic / Logic",
ops: [
"name": "Arithmetic / Logic",
"ops": [
"Set Union",
"Set Intersection",
"Set Difference",
"Symmetric Difference",
"Cartesian Product",
"Power Set",
"XOR",
"XOR Brute Force",
"OR",
@@ -134,14 +135,15 @@ const Categories = [
"Bit shift right",
"Rotate left",
"Rotate right",
"ROT13",
"ROT13"
]
},
{
name: "Networking",
ops: [
"name": "Networking",
"ops": [
"HTTP request",
"Strip HTTP headers",
"Dechunk HTTP response",
"Parse User Agent",
"Parse IP range",
"Parse IPv6 address",
@@ -153,20 +155,20 @@ const Categories = [
"Change IP format",
"Group IP addresses",
"Encode NetBIOS Name",
"Decode NetBIOS Name",
"Decode NetBIOS Name"
]
},
{
name: "Language",
ops: [
"name": "Language",
"ops": [
"Encode text",
"Decode text",
"Unescape Unicode Characters",
"Unescape Unicode Characters"
]
},
{
name: "Utils",
ops: [
"name": "Utils",
"ops": [
"Diff",
"Remove whitespace",
"Remove null bytes",
@@ -174,6 +176,7 @@ const Categories = [
"To Lower case",
"Add line numbers",
"Remove line numbers",
"To Table",
"Reverse",
"Sort",
"Unique",
@@ -201,12 +204,12 @@ const Categories = [
"Escape string",
"Unescape string",
"Pseudo-Random Number Generator",
"Sleep",
"Sleep"
]
},
{
name: "Date / Time",
ops: [
"name": "Date / Time",
"ops": [
"Parse DateTime",
"Translate DateTime Format",
"From UNIX Timestamp",
@@ -214,12 +217,12 @@ const Categories = [
"Windows Filetime to UNIX Timestamp",
"UNIX Timestamp to Windows Filetime",
"Extract dates",
"Sleep",
"Sleep"
]
},
{
name: "Extractors",
ops: [
"name": "Extractors",
"ops": [
"Strings",
"Extract IP addresses",
"Extract email addresses",
@@ -232,12 +235,12 @@ const Categories = [
"XPath expression",
"JPath expression",
"CSS selector",
"Extract EXIF",
"Extract EXIF"
]
},
{
name: "Compression",
ops: [
"name": "Compression",
"ops": [
"Raw Deflate",
"Raw Inflate",
"Zlib Deflate",
@@ -248,12 +251,12 @@ const Categories = [
"Unzip",
"Bzip2 Decompress",
"Tar",
"Untar",
"Untar"
]
},
{
name: "Hashing",
ops: [
"name": "Hashing",
"ops": [
"Analyse hash",
"Generate all hashes",
"MD2",
@@ -270,7 +273,15 @@ const Categories = [
"HAS-160",
"Whirlpool",
"Snefru",
"SSDEEP",
"CTPH",
"Compare SSDEEP hashes",
"Compare CTPH hashes",
"HMAC",
"Bcrypt",
"Bcrypt compare",
"Bcrypt parse",
"Scrypt",
"Fletcher-8 Checksum",
"Fletcher-16 Checksum",
"Fletcher-32 Checksum",
@@ -279,11 +290,13 @@ const Categories = [
"CRC-16 Checksum",
"CRC-32 Checksum",
"TCP/IP Checksum",
"To Geohash",
"From Geohash"
]
},
{
name: "Code tidy",
ops: [
"name": "Code tidy",
"ops": [
"Syntax highlighter",
"Generic Code Beautify",
"JavaScript Parser",
@@ -307,11 +320,15 @@ const Categories = [
"To Snake case",
"To Camel case",
"To Kebab case",
"BSON serialise",
"BSON deserialise",
"To MessagePack",
"From MessagePack"
]
},
{
name: "Other",
ops: [
"name": "Other",
"ops": [
"Entropy",
"Frequency distribution",
"Chi Square",
@@ -322,16 +339,18 @@ const Categories = [
"Generate UUID",
"Generate TOTP",
"Generate HOTP",
"Haversine distance",
"Render Image",
"Remove EXIF",
"Extract EXIF",
"Numberwang",
"XKCD Random Number",
"XKCD Random Number"
]
},
{
name: "Flow control",
ops: [
"name": "Flow control",
"ops": [
"Magic",
"Fork",
"Merge",
"Register",
@@ -341,7 +360,5 @@ const Categories = [
"Return",
"Comment"
]
},
];
export default Categories;
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
import CharEnc from "../../operations/CharEnc.js";
/**
* CharEnc module.
*
* Libraries:
* - cptable
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.CharEnc = {
"Encode text": CharEnc.runEncode,
"Decode text": CharEnc.runDecode,
};
export default OpModules;

View File

@@ -1,44 +0,0 @@
import Cipher from "../../operations/Cipher.js";
/**
* Ciphers module.
*
* Libraries:
* - CryptoJS
* - Blowfish
* - Forge
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Ciphers = {
"AES Encrypt": Cipher.runAesEnc,
"AES Decrypt": Cipher.runAesDec,
"Blowfish Encrypt": Cipher.runBlowfishEnc,
"Blowfish Decrypt": Cipher.runBlowfishDec,
"DES Encrypt": Cipher.runDesEnc,
"DES Decrypt": Cipher.runDesDec,
"Triple DES Encrypt": Cipher.runTripleDesEnc,
"Triple DES Decrypt": Cipher.runTripleDesDec,
"Derive PBKDF2 key": Cipher.runPbkdf2,
"Derive EVP key": Cipher.runEvpkdf,
"RC4": Cipher.runRc4,
"RC4 Drop": Cipher.runRc4drop,
"RC2 Encrypt": Cipher.runRc2Enc,
"RC2 Decrypt": Cipher.runRc2Dec,
"Vigenère Encode": Cipher.runVigenereEnc,
"Vigenère Decode": Cipher.runVigenereDec,
"Bifid Cipher Encode": Cipher.runBifidEnc,
"Bifid Cipher Decode": Cipher.runBifidDec,
"Affine Cipher Encode": Cipher.runAffineEnc,
"Affine Cipher Decode": Cipher.runAffineDec,
"Atbash Cipher": Cipher.runAtbash,
"Substitute": Cipher.runSubstitute,
"Pseudo-Random Number Generator": Cipher.runPRNG,
};
export default OpModules;

View File

@@ -1,44 +0,0 @@
import JS from "../../operations/JS.js";
import Code from "../../operations/Code.js";
/**
* Code module.
*
* Libraries:
* - lodash
* - vkbeautify
* - xmldom
* - xpath
* - jpath
* - highlight.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Code = {
"JavaScript Parser": JS.runParse,
"JavaScript Beautify": JS.runBeautify,
"JavaScript Minify": JS.runMinify,
"Syntax highlighter": Code.runSyntaxHighlight,
"Generic Code Beautify": Code.runGenericBeautify,
"JSON Beautify": Code.runJsonBeautify,
"JSON Minify": Code.runJsonMinify,
"XML Beautify": Code.runXmlBeautify,
"XML Minify": Code.runXmlMinify,
"SQL Beautify": Code.runSqlBeautify,
"SQL Minify": Code.runSqlMinify,
"CSS Beautify": Code.runCssBeautify,
"CSS Minify": Code.runCssMinify,
"XPath expression": Code.runXpath,
"CSS selector": Code.runCSSQuery,
"To Snake case": Code.runToSnakeCase,
"To Camel case": Code.runToCamelCase,
"To Kebab case": Code.runToKebabCase,
"JPath expression": Code.runJpath,
};
export default OpModules;

View File

@@ -1,32 +0,0 @@
import Compress from "../../operations/Compress.js";
/**
* Compression module.
*
* Libraries:
* - zlib.js
* - bzip2.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Compression = {
"Raw Deflate": Compress.runRawDeflate,
"Raw Inflate": Compress.runRawInflate,
"Zlib Deflate": Compress.runZlibDeflate,
"Zlib Inflate": Compress.runZlibInflate,
"Gzip": Compress.runGzip,
"Gunzip": Compress.runGunzip,
"Zip": Compress.runPkzip,
"Unzip": Compress.runPkunzip,
"Bzip2 Decompress": Compress.runBzip2Decompress,
"Tar": Compress.runTar,
"Untar": Compress.runUntar,
};
export default OpModules;

View File

@@ -1,197 +0,0 @@
import FlowControl from "../../FlowControl.js";
import Arithmetic from "../../operations/Arithmetic.js";
import Base from "../../operations/Base.js";
import Base58 from "../../operations/Base58.js";
import Base64 from "../../operations/Base64.js";
import BCD from "../../operations/BCD.js";
import BitwiseOp from "../../operations/BitwiseOp.js";
import ByteRepr from "../../operations/ByteRepr.js";
import Convert from "../../operations/Convert.js";
import DateTime from "../../operations/DateTime.js";
import Endian from "../../operations/Endian.js";
import Entropy from "../../operations/Entropy.js";
import Filetime from "../../operations/Filetime.js";
import FileType from "../../operations/FileType.js";
import Hexdump from "../../operations/Hexdump.js";
import HTML from "../../operations/HTML.js";
import MAC from "../../operations/MAC.js";
import MorseCode from "../../operations/MorseCode.js";
import MS from "../../operations/MS.js";
import NetBIOS from "../../operations/NetBIOS.js";
import Numberwang from "../../operations/Numberwang.js";
import OS from "../../operations/OS.js";
import OTP from "../../operations/OTP.js";
import PHP from "../../operations/PHP.js";
import QuotedPrintable from "../../operations/QuotedPrintable.js";
import Rotate from "../../operations/Rotate.js";
import SeqUtils from "../../operations/SeqUtils.js";
import StrUtils from "../../operations/StrUtils.js";
import Tidy from "../../operations/Tidy.js";
import Unicode from "../../operations/Unicode.js";
import UUID from "../../operations/UUID.js";
import XKCD from "../../operations/XKCD.js";
/**
* Default module.
*
* The Default module is for operations that are expected to be very commonly used or
* do not require any libraries. This module is loaded into the app at compile time.
*
* Libraries:
* - Utils.js
* - otp
* - crypto
* - bignumber.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Default = {
"To Hexdump": Hexdump.runTo,
"From Hexdump": Hexdump.runFrom,
"To Hex": ByteRepr.runToHex,
"From Hex": ByteRepr.runFromHex,
"To Octal": ByteRepr.runToOct,
"From Octal": ByteRepr.runFromOct,
"To Charcode": ByteRepr.runToCharcode,
"From Charcode": ByteRepr.runFromCharcode,
"To Decimal": ByteRepr.runToDecimal,
"From Decimal": ByteRepr.runFromDecimal,
"To Binary": ByteRepr.runToBinary,
"From Binary": ByteRepr.runFromBinary,
"To Hex Content": ByteRepr.runToHexContent,
"From Hex Content": ByteRepr.runFromHexContent,
"To Base64": Base64.runTo,
"From Base64": Base64.runFrom,
"Show Base64 offsets": Base64.runOffsets,
"To Base32": Base64.runTo32,
"From Base32": Base64.runFrom32,
"To Base58": Base58.runTo,
"From Base58": Base58.runFrom,
"To Base": Base.runTo,
"From Base": Base.runFrom,
"To BCD": BCD.runToBCD,
"From BCD": BCD.runFromBCD,
"To HTML Entity": HTML.runToEntity,
"From HTML Entity": HTML.runFromEntity,
"Strip HTML tags": HTML.runStripTags,
"Parse colour code": HTML.runParseColourCode,
"Unescape Unicode Characters": Unicode.runUnescape,
"To Quoted Printable": QuotedPrintable.runTo,
"From Quoted Printable": QuotedPrintable.runFrom,
"Swap endianness": Endian.runSwapEndianness,
"ROT13": Rotate.runRot13,
"ROT47": Rotate.runRot47,
"Rotate left": Rotate.runRotl,
"Rotate right": Rotate.runRotr,
"Bit shift left": BitwiseOp.runBitShiftLeft,
"Bit shift right": BitwiseOp.runBitShiftRight,
"XOR": BitwiseOp.runXor,
"XOR Brute Force": BitwiseOp.runXorBrute,
"OR": BitwiseOp.runOr,
"NOT": BitwiseOp.runNot,
"AND": BitwiseOp.runAnd,
"ADD": BitwiseOp.runAdd,
"SUB": BitwiseOp.runSub,
"To Morse Code": MorseCode.runTo,
"From Morse Code": MorseCode.runFrom,
"Format MAC addresses": MAC.runFormat,
"Encode NetBIOS Name": NetBIOS.runEncodeName,
"Decode NetBIOS Name": NetBIOS.runDecodeName,
"Offset checker": StrUtils.runOffsetChecker,
"To Upper case": StrUtils.runUpper,
"To Lower case": StrUtils.runLower,
"Split": StrUtils.runSplit,
"Filter": StrUtils.runFilter,
"Escape string": StrUtils.runEscape,
"Unescape string": StrUtils.runUnescape,
"Head": StrUtils.runHead,
"Tail": StrUtils.runTail,
"Hamming Distance": StrUtils.runHamming,
"Remove whitespace": Tidy.runRemoveWhitespace,
"Remove null bytes": Tidy.runRemoveNulls,
"Drop bytes": Tidy.runDropBytes,
"Take bytes": Tidy.runTakeBytes,
"Pad lines": Tidy.runPad,
"Reverse": SeqUtils.runReverse,
"Sort": SeqUtils.runSort,
"Unique": SeqUtils.runUnique,
"Count occurrences": SeqUtils.runCount,
"Add line numbers": SeqUtils.runAddLineNumbers,
"Remove line numbers": SeqUtils.runRemoveLineNumbers,
"Expand alphabet range": SeqUtils.runExpandAlphRange,
"Convert distance": Convert.runDistance,
"Convert area": Convert.runArea,
"Convert mass": Convert.runMass,
"Convert speed": Convert.runSpeed,
"Convert data units": Convert.runDataSize,
"Parse UNIX file permissions": OS.runParseUnixPerms,
"Parse DateTime": DateTime.runParse,
"Translate DateTime Format": DateTime.runTranslateFormat,
"From UNIX Timestamp": DateTime.runFromUnixTimestamp,
"To UNIX Timestamp": DateTime.runToUnixTimestamp,
"Sleep": DateTime.runSleep,
"Microsoft Script Decoder": MS.runDecodeScript,
"Entropy": Entropy.runEntropy,
"Frequency distribution": Entropy.runFreqDistrib,
"Chi Square": Entropy.runChiSq,
"Detect File Type": FileType.runDetect,
"Scan for Embedded Files": FileType.runScanForEmbeddedFiles,
"Generate UUID": UUID.runGenerateV4,
"Numberwang": Numberwang.run,
"Generate TOTP": OTP.runTOTP,
"Generate HOTP": OTP.runHOTP,
"Fork": FlowControl.runFork,
"Merge": FlowControl.runMerge,
"Register": FlowControl.runRegister,
"Label": FlowControl.runComment,
"Jump": FlowControl.runJump,
"Conditional Jump": FlowControl.runCondJump,
"Return": FlowControl.runReturn,
"Comment": FlowControl.runComment,
"PHP Deserialize": PHP.runDeserialize,
"Sum": Arithmetic.runSum,
"Subtract": Arithmetic.runSub,
"Multiply": Arithmetic.runMulti,
"Divide": Arithmetic.runDiv,
"Mean": Arithmetic.runMean,
"Median": Arithmetic.runMedian,
"Standard Deviation": Arithmetic.runStdDev,
"Windows Filetime to UNIX Timestamp": Filetime.runFromFiletimeToUnix,
"UNIX Timestamp to Windows Filetime": Filetime.runToFiletimeFromUnix,
"XKCD Random Number": XKCD.runRandomNumber,
/*
Highlighting functions.
This is a temporary solution as highlighting should be entirely
overhauled at some point.
*/
"From Base64-highlight": Base64.highlightFrom,
"From Base64-highlightReverse": Base64.highlightTo,
"To Base64-highlight": Base64.highlightTo,
"To Base64-highlightReverse": Base64.highlightFrom,
"From Hex-highlight": ByteRepr.highlightFrom,
"From Hex-highlightReverse": ByteRepr.highlightTo,
"To Hex-highlight": ByteRepr.highlightTo,
"To Hex-highlightReverse": ByteRepr.highlightFrom,
"From Charcode-highlight": ByteRepr.highlightFrom,
"From Charcode-highlightReverse": ByteRepr.highlightTo,
"To Charcode-highlight": ByteRepr.highlightTo,
"To Charcode-highlightReverse": ByteRepr.highlightFrom,
"From Binary-highlight": ByteRepr.highlightFromBinary,
"From Binary-highlightReverse": ByteRepr.highlightToBinary,
"To Binary-highlight": ByteRepr.highlightToBinary,
"To Binary-highlightReverse": ByteRepr.highlightFromBinary,
"From Hexdump-highlight": Hexdump.highlightFrom,
"From Hexdump-highlightReverse": Hexdump.highlightTo,
"To Hexdump-highlight": Hexdump.highlightTo,
"To Hexdump-highlightReverse": Hexdump.highlightFrom,
};
export default OpModules;

View File

@@ -1,20 +0,0 @@
import Diff from "../../operations/Diff.js";
/**
* Diff module.
*
* Libraries:
* - JsDIff
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Diff = {
"Diff": Diff.runDiff,
};
export default OpModules;

View File

@@ -1,21 +0,0 @@
import Punycode from "../../operations/Punycode.js";
/**
* Encodings module.
*
* Libraries:
* - punycode
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Encodings = {
"To Punycode": Punycode.runToAscii,
"From Punycode": Punycode.runToUnicode,
};
export default OpModules;

View File

@@ -1,22 +0,0 @@
import HTTP from "../../operations/HTTP.js";
/**
* HTTP module.
*
* Libraries:
* - UAS_parser
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.HTTP = {
"HTTP request": HTTP.runHTTPRequest,
"Strip HTTP headers": HTTP.runStripHeaders,
"Parse User Agent": HTTP.runParseUserAgent,
};
export default OpModules;

View File

@@ -1,48 +0,0 @@
import Checksum from "../../operations/Checksum.js";
import Hash from "../../operations/Hash.js";
/**
* Hashing module.
*
* Libraries:
* - CryptoApi
* - node-md6
* - js-sha3
* - ./Checksum.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Hashing = {
"Analyse hash": Hash.runAnalyse,
"Generate all hashes": Hash.runAll,
"MD2": Hash.runMD2,
"MD4": Hash.runMD4,
"MD5": Hash.runMD5,
"MD6": Hash.runMD6,
"SHA0": Hash.runSHA0,
"SHA1": Hash.runSHA1,
"SHA2": Hash.runSHA2,
"SHA3": Hash.runSHA3,
"Keccak": Hash.runKeccak,
"Shake": Hash.runShake,
"RIPEMD": Hash.runRIPEMD,
"HAS-160": Hash.runHAS,
"Whirlpool": Hash.runWhirlpool,
"Snefru": Hash.runSnefru,
"HMAC": Hash.runHMAC,
"Fletcher-8 Checksum": Checksum.runFletcher8,
"Fletcher-16 Checksum": Checksum.runFletcher16,
"Fletcher-32 Checksum": Checksum.runFletcher32,
"Fletcher-64 Checksum": Checksum.runFletcher64,
"Adler-32 Checksum": Checksum.runAdler32,
"CRC-16 Checksum": Checksum.runCRC16,
"CRC-32 Checksum": Checksum.runCRC32,
"TCP/IP Checksum": Checksum.runTCPIP,
};
export default OpModules;

View File

@@ -1,25 +0,0 @@
import Image from "../../operations/Image.js";
/**
* Image module.
*
* Libraries:
* - exif-parser
* - remove-exif
* - ./FileType.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Image = {
"Extract EXIF": Image.runExtractEXIF,
"Remove EXIF": Image.runRemoveEXIF,
"Render Image": Image.runRenderImage,
};
export default OpModules;

View File

@@ -1,25 +0,0 @@
import IP from "../../operations/IP.js";
/**
* JSBN module.
*
* Libraries:
* - jsbn
* - ./Checksum.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.JSBN = {
"Parse IP range": IP.runParseIpRange,
"Parse IPv6 address": IP.runParseIPv6,
"Parse IPv4 header": IP.runParseIPv4Header,
"Change IP format": IP.runChangeIpFormat,
"Group IP addresses": IP.runGroupIps,
};
export default OpModules;

View File

@@ -1,43 +0,0 @@
/**
* Imports all modules for builds which do not load modules separately.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import OpModules from "./Default.js";
import CharEncModule from "./CharEnc.js";
import CipherModule from "./Ciphers.js";
import CodeModule from "./Code.js";
import CompressionModule from "./Compression.js";
import DiffModule from "./Diff.js";
import EncodingModule from "./Encodings.js";
import HashingModule from "./Hashing.js";
import HTTPModule from "./HTTP.js";
import ImageModule from "./Image.js";
import JSBNModule from "./JSBN.js";
import PublicKeyModule from "./PublicKey.js";
import RegexModule from "./Regex.js";
import ShellcodeModule from "./Shellcode.js";
import URLModule from "./URL.js";
Object.assign(
OpModules,
CharEncModule,
CipherModule,
CodeModule,
CompressionModule,
DiffModule,
EncodingModule,
HashingModule,
HTTPModule,
ImageModule,
JSBNModule,
PublicKeyModule,
RegexModule,
ShellcodeModule,
URLModule
);
export default OpModules;

View File

@@ -1,25 +0,0 @@
import PublicKey from "../../operations/PublicKey.js";
/**
* PublicKey module.
*
* Libraries:
* - jsrsasign
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.PublicKey = {
"Parse X.509 certificate": PublicKey.runParseX509,
"Parse ASN.1 hex string": PublicKey.runParseAsn1HexString,
"PEM to Hex": PublicKey.runPemToHex,
"Hex to PEM": PublicKey.runHexToPem,
"Hex to Object Identifier": PublicKey.runHexToObjectIdentifier,
"Object Identifier to Hex": PublicKey.runObjectIdentifierToHex,
};
export default OpModules;

View File

@@ -1,30 +0,0 @@
import Extract from "../../operations/Extract.js";
import Regex from "../../operations/Regex.js";
/**
* Regex module.
*
* Libraries:
* - XRegExp
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Regex = {
"Regular expression": Regex.runRegex,
"Find / Replace": Regex.runFindReplace,
"Strings": Extract.runStrings,
"Extract IP addresses": Extract.runIp,
"Extract email addresses": Extract.runEmail,
"Extract MAC addresses": Extract.runMac,
"Extract URLs": Extract.runUrls,
"Extract domains": Extract.runDomains,
"Extract file paths": Extract.runFilePaths,
"Extract dates": Extract.runDates,
};
export default OpModules;

View File

@@ -1,20 +0,0 @@
import Shellcode from "../../operations/Shellcode.js";
/**
* Shellcode module.
*
* Libraries:
* - DisassembleX86-64.js
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.Shellcode = {
"Disassemble x86": Shellcode.runDisassemble,
};
export default OpModules;

View File

@@ -1,23 +0,0 @@
import URL_ from "../../operations/URL.js";
/**
* URL module.
*
* Libraries:
* - Utils.js
* - url
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
let OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.URL = {
"URL Encode": URL_.runTo,
"URL Decode": URL_.runFrom,
"Parse URI": URL_.runParse,
};
export default OpModules;

View File

@@ -0,0 +1,149 @@
/**
* This script automatically generates OperationConfig.json, containing metadata
* for each operation in the src/core/operations directory.
* It also generates modules in the src/core/config/modules directory to separate
* out operations into logical collections.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/*eslint no-console: ["off"] */
import path from "path";
import fs from "fs";
import process from "process";
import * as Ops from "../../operations/index";
const dir = path.join(process.cwd() + "/src/core/config/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: generateConfig.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/generateConfig.mjs");
process.exit(1);
}
const operationConfig = {},
modules = {};
/**
* Generate operation config and module lists.
*/
for (const opObj in Ops) {
const op = new Ops[opObj]();
operationConfig[op.name] = {
module: op.module,
description: op.description,
infoURL: op.infoURL,
inputType: op.inputType,
outputType: op.presentType,
flowControl: op.flowControl,
args: op.args
};
if (op.hasOwnProperty("patterns")) {
operationConfig[op.name].patterns = op.patterns;
}
if (!modules.hasOwnProperty(op.module))
modules[op.module] = {};
modules[op.module][op.name] = opObj;
}
/**
* Write OperationConfig.
*/
fs.writeFileSync(
path.join(dir, "OperationConfig.json"),
JSON.stringify(operationConfig, null, 4)
);
console.log("Written OperationConfig.json");
/**
* Write modules.
*/
if (!fs.existsSync(path.join(dir, "modules/"))) {
fs.mkdirSync(path.join(dir, "modules/"));
}
for (const module in modules) {
let code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateConfig.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
for (const opName in modules[module]) {
const objName = modules[module][opName];
code += `import ${objName} from "../../operations/${objName}";\n`;
}
code += `
const OpModules = typeof self === "undefined" ? {} : self.OpModules || {};
OpModules.${module} = {
`;
for (const opName in modules[module]) {
const objName = modules[module][opName];
code += ` "${opName}": ${objName},\n`;
}
code += `};
export default OpModules;
`;
fs.writeFileSync(
path.join(dir, `modules/${module}.mjs`),
code
);
console.log(`Written ${module} module`);
}
/**
* Write OpModules wrapper.
*/
let opModulesCode = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateConfig.mjs
*
* Imports all modules for builds which do not load modules separately.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
for (const module in modules) {
opModulesCode += `import ${module}Module from "./${module}";\n`;
}
opModulesCode += `
const OpModules = {};
Object.assign(
OpModules,
`;
for (const module in modules) {
opModulesCode += ` ${module}Module,\n`;
}
opModulesCode += `);
export default OpModules;
`;
fs.writeFileSync(
path.join(dir, "modules/OpModules.mjs"),
opModulesCode
);
console.log("Written OpModules.mjs");

View File

@@ -0,0 +1,60 @@
/**
* This script automatically generates src/core/operations/index.mjs, containing
* imports for all operations in src/core/operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/*eslint no-console: ["off"] */
import path from "path";
import fs from "fs";
import process from "process";
const dir = path.join(process.cwd() + "/src/core/config/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: generateOpsIndex.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/generateOpsIndex.mjs");
process.exit(1);
}
// Find all operation files
const opObjs = [];
fs.readdirSync(path.join(dir, "../operations")).forEach(file => {
if (!file.endsWith(".mjs") || file === "index.mjs") return;
opObjs.push(file.split(".mjs")[0]);
});
// Construct index file
let code = `/**
* THIS FILE IS AUTOMATICALLY GENERATED BY src/core/config/scripts/generateOpsIndex.mjs
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright ${new Date().getUTCFullYear()}
* @license Apache-2.0
*/
`;
opObjs.forEach(obj => {
code += `import ${obj} from "./${obj}";\n`;
});
code += `
export {
`;
opObjs.forEach(obj => {
code += ` ${obj},\n`;
});
code += "};\n";
// Write file
fs.writeFileSync(
path.join(dir, "../operations/index.mjs"),
code
);
console.log("Written operation index.");

View File

@@ -0,0 +1,230 @@
/**
* Interactive script for generating a new operation template.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/*eslint no-console: ["off"] */
import prompt from "prompt";
import colors from "colors";
import process from "process";
import fs from "fs";
import path from "path";
import EscapeString from "../../operations/EscapeString";
const dir = path.join(process.cwd() + "/src/core/operations/");
if (!fs.existsSync(dir)) {
console.log("\nCWD: " + process.cwd());
console.log("Error: newOperation.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/newOperation.mjs");
process.exit(1);
}
const ioTypes = ["string", "byteArray", "number", "html", "ArrayBuffer", "BigNumber", "JSON", "File", "List<File>"];
const schema = {
properties: {
opName: {
description: "The operation name should be short but descriptive.",
example: "URL Decode",
prompt: "Operation name",
type: "string",
pattern: /^[\w\s-/().]+$/,
required: true,
message: "Operation names should consist of letters, numbers or the following symbols: _-/()."
},
module: {
description: `Modules are used to group operations that rely on large libraries. Any operation that is not in the Default module will be loaded in dynamically when it is first called. All operations in the same module will also be loaded at this time. This system prevents the CyberChef web app from getting too bloated and taking a long time to load initially.
If your operation does not rely on a library, just leave this blank and it will be added to the Default module. If it relies on the same library as other operations, enter the name of the module those operations are in. If it relies on a new large library, enter a new module name (capitalise the first letter).`,
example: "Crypto",
prompt: "Module",
type: "string",
pattern: /^[A-Z][A-Za-z\d]+$/,
message: "Module names should start with a capital letter and not contain any spaces or symbols.",
default: "Default"
},
description: {
description: "The description should explain what the operation is and how it works. It can describe how the arguments should be entered and give examples of expected input and output. HTML markup is supported. Use <code> tags for examples. The description is scanned during searches, so include terms that are likely to be searched for when someone is looking for your operation.",
example: "Converts URI/URL percent-encoded characters back to their raw values.<br><br>e.g. <code>%3d</code> becomes <code>=</code>",
prompt: "Description",
type: "string"
},
infoURL: {
description: "An optional URL for an external site can be added to give more information about the operation. Wikipedia links are often suitable. If linking to Wikipedia, use an international link (e.g. https://wikipedia.org/...) rather than a localised link (e.g. https://en.wikipedia.org/...).",
example: "https://wikipedia.org/wiki/Percent-encoding",
prompt: "Information URL",
type: "string",
},
inputType: {
description: `The input type defines how the input data will be presented to your operation. Check the project wiki for a full description of each type. The options are: ${ioTypes.join(", ")}.`,
example: "string",
prompt: "Input type",
type: "string",
pattern: new RegExp(`^(${ioTypes.join("|")})$`),
required: true,
message: `The input type should be one of: ${ioTypes.join(", ")}.`
},
outputType: {
description: `The output type tells CyberChef what sort of data you are returning from your operation. Check the project wiki for a full description of each type. The options are: ${ioTypes.join(", ")}.`,
example: "string",
prompt: "Output type",
type: "string",
pattern: new RegExp(`^(${ioTypes.join("|")})$`),
required: true,
message: `The output type should be one of: ${ioTypes.join(", ")}.`
},
highlight: {
description: "If your operation does not change the length of the input in any way, we can enable highlighting. If it does change the length in a predictable way, we may still be able to enable highlighting and calculate the correct offsets. If this is not possible, we will disable highlighting for this operation.",
example: "true/false",
prompt: "Enable highlighting",
type: "boolean",
default: "false",
message: "Enter true or false to specify if highlighting should be enabled."
},
authorName: {
description: "Your name or username will be added to the @author tag for this operation.",
example: "n1474335",
prompt: "Username",
type: "string"
},
authorEmail: {
description: "Your email address will also be added to the @author tag for this operation.",
example: "n1474335@gmail.com",
prompt: "Email",
type: "string"
}
}
};
// Build schema
for (const prop in schema.properties) {
const p = schema.properties[prop];
p.description = "\n" + colors.white(p.description) + colors.cyan("\nExample: " + p.example) + "\n" + colors.green(p.prompt);
}
console.log("\n\nThis script will generate a new operation template based on the information you provide. These values can be changed manually later.".yellow);
prompt.message = "";
prompt.delimiter = ":".green;
prompt.start();
prompt.get(schema, (err, result) => {
if (err) {
console.log("\nExiting build script.");
process.exit(0);
}
const moduleName = result.opName.replace(/\w\S*/g, txt => {
return txt.charAt(0).toUpperCase() + txt.substr(1);
}).replace(/[\s-()/./]/g, "");
const template = `/**
* @author ${result.authorName} [${result.authorEmail}]
* @copyright Crown Copyright ${(new Date()).getFullYear()}
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* ${result.opName} operation
*/
class ${moduleName} extends Operation {
/**
* ${moduleName} constructor
*/
constructor() {
super();
this.name = "${result.opName}";
this.module = "${result.module}";
this.description = "${(new EscapeString).run(result.description, ["Special chars", "Double"])}";
this.infoURL = "${result.infoURL}";
this.inputType = "${result.inputType}";
this.outputType = "${result.outputType}";
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 {${result.inputType}} input
* @param {Object[]} args
* @returns {${result.outputType}}
*/
run(input, args) {
// const [firstArg, secondArg] = args;
throw new OperationError("Test");
}
${result.highlight ? `
/**
* Highlight ${result.opName}
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight ${result.opName} in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
` : ""}
}
export default ${moduleName};
`;
//console.log(template);
const filename = path.join(dir, `./${moduleName}.mjs`);
if (fs.existsSync(filename)) {
console.log(`${filename} already exists. It has NOT been overwritten.`.red);
console.log("Choose a different operation name to avoid conflicts.");
process.exit(0);
}
fs.writeFileSync(filename, template);
console.log(`\nOperation template written to ${colors.green(filename)}`);
console.log(`\nNext steps:
1. Add your operation to ${colors.green("src/core/config/Categories.json")}
2. Write your operation code.
3. Write tests in ${colors.green("test/tests/operations/")}
4. Run ${colors.cyan("npm run lint")} and ${colors.cyan("npm run test")}
5. Submit a Pull Request to get your operation added to the official CyberChef repository.`);
});

View File

@@ -0,0 +1,26 @@
/**
* Custom error type for handling operation input errors.
* i.e. where the operation can handle the error and print a message to the screen.
*
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
class OperationError extends Error {
/**
* Standard error constructor. Adds no new behaviour.
*
* @param args - Standard error args
*/
constructor(...args) {
super(...args);
this.type = "OperationError";
if (Error.captureStackTrace) {
Error.captureStackTrace(this, OperationError);
}
}
}
export default OperationError;

139
src/core/lib/Arithmetic.mjs Normal file
View File

@@ -0,0 +1,139 @@
/**
* @author bwhitn [brian.m.whitney@outlook.com]
* @author d98762625 [d98762625@gmailcom]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Utils from "../Utils";
import BigNumber from "bignumber.js";
/**
* Converts a string array to a number array.
*
* @param {string[]} input
* @param {string} delim
* @returns {BigNumber[]}
*/
export function createNumArray(input, delim) {
delim = Utils.charRep(delim || "Space");
const splitNumbers = input.split(delim);
const numbers = [];
let num;
splitNumbers.map((number) => {
try {
num = BigNumber(number.trim());
if (!num.isNaN()) {
numbers.push(num);
}
} catch (err) {
// This line is not a valid number
}
});
return numbers;
}
/**
* Adds an array of numbers and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function sum(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.plus(curr));
}
}
/**
* Subtracts an array of numbers and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function sub(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.minus(curr));
}
}
/**
* Multiplies an array of numbers and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function multi(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.times(curr));
}
}
/**
* Divides an array of numbers and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function div(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.div(curr));
}
}
/**
* Computes mean of a number array and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function mean(data) {
if (data.length > 0) {
return sum(data).div(data.length);
}
}
/**
* Computes median of a number array and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function median(data) {
if ((data.length % 2) === 0 && data.length > 0) {
data.sort(function(a, b){
return a.minus(b);
});
const first = data[Math.floor(data.length / 2)];
const second = data[Math.floor(data.length / 2) - 1];
return mean([first, second]);
} else {
return data[Math.floor(data.length / 2)];
}
}
/**
* Computes standard deviation of a number array and returns the value.
*
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
export function stdDev(data) {
if (data.length > 0) {
const avg = mean(data);
let devSum = new BigNumber(0);
data.map((datum) => {
devSum = devSum.plus(datum.minus(avg).pow(2));
});
return devSum.div(data.length).sqrt();
}
}

48
src/core/lib/BCD.mjs Executable file
View File

@@ -0,0 +1,48 @@
/**
* Binary Code Decimal resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
/**
* BCD encoding schemes.
*/
export const ENCODING_SCHEME = [
"8 4 2 1",
"7 4 2 1",
"4 2 2 1",
"2 4 2 1",
"8 4 -2 -1",
"Excess-3",
"IBM 8 4 2 1",
];
/**
* Lookup table for the binary value of each digit representation.
*
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
* but unfortunately it's much easier (if less elegant) to use lookup tables
* when supporting multiple encoding schemes.
*
* "Practicality beats purity" - PEP 20
*
* In some schemes it is possible to represent the same value in multiple ways.
* For instance, in 4 2 2 1 encoding, 0100 and 0010 both represent 2. Support
* has not yet been added for this.
*/
export const ENCODING_LOOKUP = {
"8 4 2 1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"7 4 2 1": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10],
"4 2 2 1": [0, 1, 4, 5, 8, 9, 12, 13, 14, 15],
"2 4 2 1": [0, 1, 2, 3, 4, 11, 12, 13, 14, 15],
"8 4 -2 -1": [0, 7, 6, 5, 4, 11, 10, 9, 8, 15],
"Excess-3": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
"IBM 8 4 2 1": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9],
};
/**
* BCD formats.
*/
export const FORMAT = ["Nibbles", "Bytes", "Raw"];

22
src/core/lib/Base58.mjs Executable file
View File

@@ -0,0 +1,22 @@
/**
* Base58 resources.
*
* @author tlwr [toby@toby.codes]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
/**
* Base58 alphabet options.
*/
export const ALPHABET_OPTIONS = [
{
name: "Bitcoin",
value: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
},
{
name: "Ripple",
value: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
},
];

142
src/core/lib/Base64.mjs Executable file
View File

@@ -0,0 +1,142 @@
/**
* Base64 functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
/**
* Base64's the input byte array using the given alphabet, returning a string.
*
* @param {byteArray|Uint8Array|string} data
* @param {string} [alphabet="A-Za-z0-9+/="]
* @returns {string}
*
* @example
* // returns "SGVsbG8="
* toBase64([72, 101, 108, 108, 111]);
*
* // returns "SGVsbG8="
* toBase64("Hello");
*/
export function toBase64(data, alphabet="A-Za-z0-9+/=") {
if (!data) return "";
if (typeof data == "string") {
data = Utils.strToByteArray(data);
}
alphabet = Utils.expandAlphRange(alphabet).join("");
let output = "",
chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
while (i < data.length) {
chr1 = data[i++];
chr2 = data[i++];
chr3 = data[i++];
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) +
alphabet.charAt(enc3) + alphabet.charAt(enc4);
}
return output;
}
/**
* UnBase64's the input string using the given alphabet, returning a byte array.
*
* @param {byteArray} data
* @param {string} [alphabet="A-Za-z0-9+/="]
* @param {string} [returnType="string"] - Either "string" or "byteArray"
* @param {boolean} [removeNonAlphChars=true]
* @returns {byteArray}
*
* @example
* // returns "Hello"
* fromBase64("SGVsbG8=");
*
* // returns [72, 101, 108, 108, 111]
* fromBase64("SGVsbG8=", null, "byteArray");
*/
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
if (!data) {
return returnType === "string" ? "" : [];
}
alphabet = alphabet || "A-Za-z0-9+/=";
alphabet = Utils.expandAlphRange(alphabet).join("");
const output = [];
let chr1, chr2, chr3,
enc1, enc2, enc3, enc4,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, "");
}
while (i < data.length) {
enc1 = alphabet.indexOf(data.charAt(i++));
enc2 = alphabet.indexOf(data.charAt(i++) || "=");
enc3 = alphabet.indexOf(data.charAt(i++) || "=");
enc4 = alphabet.indexOf(data.charAt(i++) || "=");
enc2 = enc2 === -1 ? 64 : enc2;
enc3 = enc3 === -1 ? 64 : enc3;
enc4 = enc4 === -1 ? 64 : enc4;
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(chr1);
if (enc3 !== 64) {
output.push(chr2);
}
if (enc4 !== 64) {
output.push(chr3);
}
}
return returnType === "string" ? Utils.byteArrayToUtf8(output) : output;
}
/**
* Base64 alphabets.
*/
export const ALPHABET_OPTIONS = [
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
{name: "Filename safe: A-Za-z0-9+-=", value: "A-Za-z0-9+\\-="},
{name: "itoa64: ./0-9A-Za-z=", value: "./0-9A-Za-z="},
{name: "XML: A-Za-z0-9_.", value: "A-Za-z0-9_."},
{name: "y64: A-Za-z0-9._-", value: "A-Za-z0-9._-"},
{name: "z64: 0-9a-zA-Z+/=", value: "0-9a-zA-Z+/="},
{name: "Radix-64: 0-9A-Za-z+/=", value: "0-9A-Za-z+/="},
{name: "Uuencoding: [space]-_", value: " -_"},
{name: "Xxencoding: +-0-9A-Za-z", value: "+\\-0-9A-Za-z"},
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
];

45
src/core/lib/Base85.mjs Normal file
View File

@@ -0,0 +1,45 @@
/**
* Base85 resources.
*
* @author PenguinGeorge [george@penguingeorge.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/**
* Base85 alphabet options.
*/
export const ALPHABET_OPTIONS = [
{
name: "Standard",
value: "!-u",
},
{
name: "Z85 (ZeroMQ)",
value: "0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#",
},
{
name: "IPv6",
value: "0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|~}",
}
];
/**
* Returns the name of the alphabet, when given the alphabet.
*
* @param {string} alphabet
* @returns {string}
*/
export function alphabetName(alphabet) {
alphabet = alphabet.replace("'", "&apos;");
alphabet = alphabet.replace("\"", "&quot;");
alphabet = alphabet.replace("\\", "&bsol;");
let name;
ALPHABET_OPTIONS.forEach(function(a) {
if (escape(alphabet) === escape(a.value)) name = a.name;
});
return name;
}

118
src/core/lib/BitwiseOp.mjs Normal file
View File

@@ -0,0 +1,118 @@
/**
* Bitwise operation resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/**
* Runs bitwise operations across the input data.
*
* @param {byteArray} input
* @param {byteArray} key
* @param {function} func - The bitwise calculation to carry out
* @param {boolean} nullPreserving
* @param {string} scheme
* @returns {byteArray}
*/
export function bitOp (input, key, func, nullPreserving, scheme) {
if (!key || !key.length) key = [0];
const result = [];
let x = null,
k = null,
o = null;
for (let i = 0; i < input.length; i++) {
k = key[i % key.length];
if (scheme === "Cascade") k = input[i + 1] || 0;
o = input[i];
x = nullPreserving && (o === 0 || o === k) ? o : func(o, k);
result.push(x);
if (scheme &&
scheme !== "Standard" &&
!(nullPreserving && (o === 0 || o === k))) {
switch (scheme) {
case "Input differential":
key[i % key.length] = x;
break;
case "Output differential":
key[i % key.length] = o;
break;
}
}
}
return result;
}
/**
* XOR bitwise calculation.
*
* @param {number} operand
* @param {number} key
* @returns {number}
*/
export function xor(operand, key) {
return operand ^ key;
}
/**
* NOT bitwise calculation.
*
* @param {number} operand
* @returns {number}
*/
export function not(operand, _) {
return ~operand & 0xff;
}
/**
* AND bitwise calculation.
*
* @param {number} operand
* @param {number} key
* @returns {number}
*/
export function and(operand, key) {
return operand & key;
}
/**
* OR bitwise calculation.
*
* @param {number} operand
* @param {number} key
* @returns {number}
*/
export function or(operand, key) {
return operand | key;
}
/**
* ADD bitwise calculation.
*
* @param {number} operand
* @param {number} key
* @returns {number}
*/
export function add(operand, key) {
return (operand + key) % 256;
}
/**
* SUB bitwise calculation.
*
* @param {number} operand
* @param {number} key
* @returns {number}
*/
export function sub(operand, key) {
const result = operand - key;
return (result < 0) ? 256 + result : result;
}

15
src/core/lib/Braille.mjs Normal file
View File

@@ -0,0 +1,15 @@
/**
* Braille resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/**
* Braille lookup table.
*/
export const BRAILLE_LOOKUP = {
ascii: " A1B'K2L@CIF/MSP\"E3H9O6R^DJG>NTQ,*5<-U8V.%[$+X!&;:4\\0Z7(_?W]#Y)=",
dot6: "⠀⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿"
};

204
src/core/lib/CanvasComponents.mjs Executable file
View File

@@ -0,0 +1,204 @@
/**
* Various components for drawing diagrams on an HTML5 canvas.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
/**
* Draws a line from one point to another
*
* @param ctx
* @param startX
* @param startY
* @param endX
* @param endY
*/
export function drawLine(ctx, startX, startY, endX, endY) {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.closePath();
ctx.stroke();
}
/**
* Draws a bar chart on the canvas.
*
* @param canvas
* @param scores
* @param xAxisLabel
* @param yAxisLabel
* @param numXLabels
* @param numYLabels
* @param fontSize
*/
export function drawBarChart(canvas, scores, xAxisLabel, yAxisLabel, numXLabels, numYLabels, fontSize) {
fontSize = fontSize || 15;
if (!numXLabels || numXLabels > Math.round(canvas.width / 50)) {
numXLabels = Math.round(canvas.width / 50);
}
if (!numYLabels || numYLabels > Math.round(canvas.width / 50)) {
numYLabels = Math.round(canvas.height / 50);
}
// Graph properties
const ctx = canvas.getContext("2d"),
leftPadding = canvas.width * 0.08,
rightPadding = canvas.width * 0.03,
topPadding = canvas.height * 0.08,
bottomPadding = canvas.height * 0.2,
graphHeight = canvas.height - topPadding - bottomPadding,
graphWidth = canvas.width - leftPadding - rightPadding,
base = topPadding + graphHeight,
ceil = topPadding;
ctx.font = fontSize + "px Arial";
// Draw axis
ctx.lineWidth = "1.0";
ctx.strokeStyle = "#444";
drawLine(ctx, leftPadding, base, graphWidth + leftPadding, base); // x
drawLine(ctx, leftPadding, base, leftPadding, ceil); // y
// Bar properties
const barPadding = graphWidth * 0.003,
barWidth = (graphWidth - (barPadding * scores.length)) / scores.length,
max = Math.max.apply(Math, scores);
let currX = leftPadding + barPadding;
// Draw bars
ctx.fillStyle = "green";
for (let i = 0; i < scores.length; i++) {
const h = scores[i] / max * graphHeight;
ctx.fillRect(currX, base - h, barWidth, h);
currX += barWidth + barPadding;
}
// Mark x axis
ctx.fillStyle = "black";
ctx.textAlign = "center";
currX = leftPadding + barPadding;
if (numXLabels >= scores.length) {
// Mark every score
for (let i = 0; i <= scores.length; i++) {
ctx.fillText(i, currX, base + (bottomPadding * 0.3));
currX += barWidth + barPadding;
}
} else {
// Mark some scores
for (let i = 0; i <= numXLabels; i++) {
const val = Math.ceil((scores.length / numXLabels) * i);
currX = (graphWidth / numXLabels) * i + leftPadding;
ctx.fillText(val, currX, base + (bottomPadding * 0.3));
}
}
// Mark y axis
ctx.textAlign = "right";
let currY;
if (numYLabels >= max) {
// Mark every increment
for (let i = 0; i <= max; i++) {
currY = base - (i / max * graphHeight) + fontSize / 3;
ctx.fillText(i, leftPadding * 0.8, currY);
}
} else {
// Mark some increments
for (let i = 0; i <= numYLabels; i++) {
const val = Math.ceil((max / numYLabels) * i);
currY = base - (val / max * graphHeight) + fontSize / 3;
ctx.fillText(val, leftPadding * 0.8, currY);
}
}
// Label x axis
if (xAxisLabel) {
ctx.textAlign = "center";
ctx.fillText(xAxisLabel, graphWidth / 2 + leftPadding, base + bottomPadding * 0.8);
}
// Label y axis
if (yAxisLabel) {
ctx.save();
const x = leftPadding * 0.3,
y = graphHeight / 2 + topPadding;
ctx.translate(x, y);
ctx.rotate(-Math.PI / 2);
ctx.textAlign = "center";
ctx.fillText(yAxisLabel, 0, 0);
ctx.restore();
}
}
/**
* Draws a scale bar on the canvas.
*
* @param canvas
* @param score
* @param max
* @param markings
*/
export function drawScaleBar(canvas, score, max, markings) {
// Bar properties
const ctx = canvas.getContext("2d"),
leftPadding = canvas.width * 0.01,
rightPadding = canvas.width * 0.01,
topPadding = canvas.height * 0.1,
bottomPadding = canvas.height * 0.35,
barHeight = canvas.height - topPadding - bottomPadding,
barWidth = canvas.width - leftPadding - rightPadding;
// Scale properties
const proportion = score / max;
// Draw bar outline
ctx.strokeRect(leftPadding, topPadding, barWidth, barHeight);
// Shade in up to proportion
const grad = ctx.createLinearGradient(leftPadding, 0, barWidth + leftPadding, 0);
grad.addColorStop(0, "green");
grad.addColorStop(0.5, "gold");
grad.addColorStop(1, "red");
ctx.fillStyle = grad;
ctx.fillRect(leftPadding, topPadding, barWidth * proportion, barHeight);
// Add markings
let x0, y0, x1, y1;
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.font = "13px Arial";
for (let i = 0; i < markings.length; i++) {
// Draw min line down
x0 = barWidth / max * markings[i].min + leftPadding;
y0 = topPadding + barHeight + (bottomPadding * 0.1);
x1 = x0;
y1 = topPadding + barHeight + (bottomPadding * 0.3);
drawLine(ctx, x0, y0, x1, y1);
// Draw max line down
x0 = barWidth / max * markings[i].max + leftPadding;
x1 = x0;
drawLine(ctx, x0, y0, x1, y1);
// Join min and max lines
x0 = barWidth / max * markings[i].min + leftPadding;
y0 = topPadding + barHeight + (bottomPadding * 0.3);
x1 = barWidth / max * markings[i].max + leftPadding;
y1 = y0;
drawLine(ctx, x0, y0, x1, y1);
// Add label
if (markings[i].max >= max * 0.9) {
ctx.textAlign = "right";
x0 = x1;
} else if (markings[i].max <= max * 0.1) {
ctx.textAlign = "left";
} else {
x0 = x0 + (x1 - x0) / 2;
}
y0 = topPadding + barHeight + (bottomPadding * 0.8);
ctx.fillText(markings[i].label, x0, y0);
}
}

58
src/core/lib/ChrEnc.mjs Normal file
View File

@@ -0,0 +1,58 @@
/**
* Character encoding resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
/**
* Character encoding format mappings.
*/
export const IO_FORMAT = {
"UTF-8 (65001)": 65001,
"UTF-7 (65000)": 65000,
"UTF16LE (1200)": 1200,
"UTF16BE (1201)": 1201,
"UTF16 (1201)": 1201,
"IBM EBCDIC International (500)": 500,
"IBM EBCDIC US-Canada (37)": 37,
"Windows-874 Thai (874)": 874,
"Japanese Shift-JIS (932)": 932,
"Simplified Chinese GBK (936)": 936,
"Korean (949)": 949,
"Traditional Chinese Big5 (950)": 950,
"Windows-1250 Central European (1250)": 1250,
"Windows-1251 Cyrillic (1251)": 1251,
"Windows-1252 Latin (1252)": 1252,
"Windows-1253 Greek (1253)": 1253,
"Windows-1254 Turkish (1254)": 1254,
"Windows-1255 Hebrew (1255)": 1255,
"Windows-1256 Arabic (1256)": 1256,
"Windows-1257 Baltic (1257)": 1257,
"Windows-1258 Vietnam (1258)": 1258,
"US-ASCII (20127)": 20127,
"Simplified Chinese GB2312 (20936)": 20936,
"KOI8-R Russian Cyrillic (20866)": 20866,
"KOI8-U Ukrainian Cyrillic (21866)": 21866,
"ISO-8859-1 Latin 1 Western European (28591)": 28591,
"ISO-8859-2 Latin 2 Central European (28592)": 28592,
"ISO-8859-3 Latin 3 South European (28593)": 28593,
"ISO-8859-4 Latin 4 North European (28594)": 28594,
"ISO-8859-5 Latin/Cyrillic (28595)": 28595,
"ISO-8859-6 Latin/Arabic (28596)": 28596,
"ISO-8859-7 Latin/Greek (28597)": 28597,
"ISO-8859-8 Latin/Hebrew (28598)": 28598,
"ISO-8859-9 Latin 5 Turkish (28599)": 28599,
"ISO-8859-10 Latin 6 Nordic (28600)": 28600,
"ISO-8859-11 Latin/Thai (28601)": 28601,
"ISO-8859-13 Latin 7 Baltic Rim (28603)": 28603,
"ISO-8859-14 Latin 8 Celtic (28604)": 28604,
"ISO-8859-15 Latin 9 (28605)": 28605,
"ISO-8859-16 Latin 10 (28606)": 28606,
"ISO-2022 JIS Japanese (50222)": 50222,
"EUC Japanese (51932)": 51932,
"EUC Korean (51949)": 51949,
"Simplified Chinese GB18030 (54936)": 54936,
};

82
src/core/lib/Ciphers.mjs Normal file
View File

@@ -0,0 +1,82 @@
/**
* Cipher functions.
*
* @author Matt C [matt@artemisbot.uk]
* @author n1474335 [n1474335@gmail.com]
*
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
*/
import OperationError from "../errors/OperationError";
import CryptoJS from "crypto-js";
/**
* Affine Cipher Encode operation.
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
export function affineEncode(input, args) {
const alphabet = "abcdefghijklmnopqrstuvwxyz",
a = args[0],
b = args[1];
let output = "";
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
throw new OperationError("The values of a and b can only be integers.");
}
for (let i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Uses the affine function ax+b % m = y (where m is length of the alphabet)
output += alphabet[((a * alphabet.indexOf(input[i])) + b) % 26];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
// Same as above, accounting for uppercase
output += alphabet[((a * alphabet.indexOf(input[i].toLowerCase())) + b) % 26].toUpperCase();
} else {
// Non-alphabetic characters
output += input[i];
}
}
return output;
}
/**
* Generates a polybius square for the given keyword
*
* @private
* @author Matt C [matt@artemisbot.uk]
* @param {string} keyword - Must be upper case
* @returns {string}
*/
export function genPolybiusSquare (keyword) {
const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ",
polArray = `${keyword}${alpha}`.split("").unique(),
polybius = [];
for (let i = 0; i < 5; i++) {
polybius[i] = polArray.slice(i*5, i*5 + 5);
}
return polybius;
}
/**
* A mapping of string formats to their classes in the CryptoJS library.
*
* @private
* @constant
*/
export const format = {
"Hex": CryptoJS.enc.Hex,
"Base64": CryptoJS.enc.Base64,
"UTF8": CryptoJS.enc.Utf8,
"UTF16": CryptoJS.enc.Utf16,
"UTF16LE": CryptoJS.enc.Utf16LE,
"UTF16BE": CryptoJS.enc.Utf16BE,
"Latin1": CryptoJS.enc.Latin1,
};

29
src/core/lib/Code.mjs Normal file
View File

@@ -0,0 +1,29 @@
/**
* Code resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/**
* This tries to rename variable names in a code snippet according to a function.
*
* @param {string} input
* @param {function} replacer - This function will be fed the token which should be renamed.
* @returns {string}
*/
export function replaceVariableNames(input, replacer) {
const tokenRegex = /\\"|"(?:\\"|[^"])*"|(\b[a-z0-9\-_]+\b)/ig;
return input.replace(tokenRegex, (...args) => {
const match = args[0],
quotes = args[1];
if (!quotes) {
return match;
} else {
return replacer(match);
}
});
}

313
src/core/lib/DateTime.mjs Normal file
View File

@@ -0,0 +1,313 @@
/**
* DateTime resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
/**
* DateTime units.
*/
export const UNITS = ["Seconds (s)", "Milliseconds (ms)", "Microseconds (μs)", "Nanoseconds (ns)"];
/**
* DateTime formats.
*/
export const DATETIME_FORMATS = [
{
name: "Standard date and time",
value: "DD/MM/YYYY HH:mm:ss"
},
{
name: "American-style date and time",
value: "MM/DD/YYYY HH:mm:ss"
},
{
name: "International date and time",
value: "YYYY-MM-DD HH:mm:ss"
},
{
name: "Verbose date and time",
value: "dddd Do MMMM YYYY HH:mm:ss Z z"
},
{
name: "UNIX timestamp (seconds)",
value: "X"
},
{
name: "UNIX timestamp offset (milliseconds)",
value: "x"
},
{
name: "Automatic",
value: ""
},
];
/**
* MomentJS DateTime formatting examples.
*/
export const FORMAT_EXAMPLES = `Format string tokens:
<table class="table table-striped table-hover table-sm table-bordered" style="font-family: sans-serif">
<thead class="thead-dark">
<tr>
<th>Category</th>
<th>Token</th>
<th>Output</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Month</b></td>
<td>M</td>
<td>1 2 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>Mo</td>
<td>1st 2nd ... 11th 12th</td>
</tr>
<tr>
<td></td>
<td>MM</td>
<td>01 02 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>MMM</td>
<td>Jan Feb ... Nov Dec</td>
</tr>
<tr>
<td></td>
<td>MMMM</td>
<td>January February ... November December</td>
</tr>
<tr>
<td><b>Quarter</b></td>
<td>Q</td>
<td>1 2 3 4</td>
</tr>
<tr>
<td><b>Day of Month</b></td>
<td>D</td>
<td>1 2 ... 30 31</td>
</tr>
<tr>
<td></td>
<td>Do</td>
<td>1st 2nd ... 30th 31st</td>
</tr>
<tr>
<td></td>
<td>DD</td>
<td>01 02 ... 30 31</td>
</tr>
<tr>
<td><b>Day of Year</b></td>
<td>DDD</td>
<td>1 2 ... 364 365</td>
</tr>
<tr>
<td></td>
<td>DDDo</td>
<td>1st 2nd ... 364th 365th</td>
</tr>
<tr>
<td></td>
<td>DDDD</td>
<td>001 002 ... 364 365</td>
</tr>
<tr>
<td><b>Day of Week</b></td>
<td>d</td>
<td>0 1 ... 5 6</td>
</tr>
<tr>
<td></td>
<td>do</td>
<td>0th 1st ... 5th 6th</td>
</tr>
<tr>
<td></td>
<td>dd</td>
<td>Su Mo ... Fr Sa</td>
</tr>
<tr>
<td></td>
<td>ddd</td>
<td>Sun Mon ... Fri Sat</td>
</tr>
<tr>
<td></td>
<td>dddd</td>
<td>Sunday Monday ... Friday Saturday</td>
</tr>
<tr>
<td><b>Day of Week (Locale)</b></td>
<td>e</td>
<td>0 1 ... 5 6</td>
</tr>
<tr>
<td><b>Day of Week (ISO)</b></td>
<td>E</td>
<td>1 2 ... 6 7</td>
</tr>
<tr>
<td><b>Week of Year</b></td>
<td>w</td>
<td>1 2 ... 52 53</td>
</tr>
<tr>
<td></td>
<td>wo</td>
<td>1st 2nd ... 52nd 53rd</td>
</tr>
<tr>
<td></td>
<td>ww</td>
<td>01 02 ... 52 53</td>
</tr>
<tr>
<td><b>Week of Year (ISO)</b></td>
<td>W</td>
<td>1 2 ... 52 53</td>
</tr>
<tr>
<td></td>
<td>Wo</td>
<td>1st 2nd ... 52nd 53rd</td>
</tr>
<tr>
<td></td>
<td>WW</td>
<td>01 02 ... 52 53</td>
</tr>
<tr>
<td><b>Year</b></td>
<td>YY</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>YYYY</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>Week Year</b></td>
<td>gg</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>gggg</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>Week Year (ISO)</b></td>
<td>GG</td>
<td>70 71 ... 29 30</td>
</tr>
<tr>
<td></td>
<td>GGGG</td>
<td>1970 1971 ... 2029 2030</td>
</tr>
<tr>
<td><b>AM/PM</b></td>
<td>A</td>
<td>AM PM</td>
</tr>
<tr>
<td></td>
<td>a</td>
<td>am pm</td>
</tr>
<tr>
<td><b>Hour</b></td>
<td>H</td>
<td>0 1 ... 22 23</td>
</tr>
<tr>
<td></td>
<td>HH</td>
<td>00 01 ... 22 23</td>
</tr>
<tr>
<td></td>
<td>h</td>
<td>1 2 ... 11 12</td>
</tr>
<tr>
<td></td>
<td>hh</td>
<td>01 02 ... 11 12</td>
</tr>
<tr>
<td><b>Minute</b></td>
<td>m</td>
<td>0 1 ... 58 59</td>
</tr>
<tr>
<td></td>
<td>mm</td>
<td>00 01 ... 58 59</td>
</tr>
<tr>
<td><b>Second</b></td>
<td>s</td>
<td>0 1 ... 58 59</td>
</tr>
<tr>
<td></td>
<td>ss</td>
<td>00 01 ... 58 59</td>
</tr>
<tr>
<td><b>Fractional Second</b></td>
<td>S</td>
<td>0 1 ... 8 9</td>
</tr>
<tr>
<td></td>
<td>SS</td>
<td>00 01 ... 98 99</td>
</tr>
<tr>
<td></td>
<td>SSS</td>
<td>000 001 ... 998 999</td>
</tr>
<tr>
<td></td>
<td>SSSS ... SSSSSSSSS</td>
<td>000[0..] 001[0..] ... 998[0..] 999[0..]</td>
</tr>
<tr>
<td><b>Timezone</b></td>
<td>z or zz</td>
<td>EST CST ... MST PST</td>
</tr>
<tr>
<td></td>
<td>Z</td>
<td>-07:00 -06:00 ... +06:00 +07:00</td>
</tr>
<tr>
<td></td>
<td>ZZ</td>
<td>-0700 -0600 ... +0600 +0700</td>
</tr>
<tr>
<td><b>Unix Timestamp</b></td>
<td>X</td>
<td>1360013296</td>
</tr>
<tr>
<td><b>Unix Millisecond Timestamp</b></td>
<td>x</td>
<td>1360013296123</td>
</tr>
</tbody>
</table>`;

37
src/core/lib/Decimal.mjs Normal file
View File

@@ -0,0 +1,37 @@
/**
* Decimal functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Utils from "../Utils";
/**
* Convert a string of decimal values into a byte array.
*
* @param {string} data
* @param {string} [delim]
* @returns {byteArray}
*
* @example
* // returns [10,20,30]
* fromDecimal("10 20 30");
*
* // returns [10,20,30]
* fromDecimal("10:20:30", "Colon");
*/
export function fromDecimal(data, delim="Auto") {
delim = Utils.charRep(delim);
const output = [];
let byteStr = data.split(delim);
if (byteStr[byteStr.length-1] === "")
byteStr = byteStr.slice(0, byteStr.length-1);
for (let i = 0; i < byteStr.length; i++) {
output[i] = parseInt(byteStr[i], 10);
}
return output;
}

74
src/core/lib/Delim.mjs Normal file
View File

@@ -0,0 +1,74 @@
/**
* Various delimiters
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/**
* Generic sequence delimiters.
*/
export const DELIM_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"];
/**
* Binary sequence delimiters.
*/
export const BIN_DELIM_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"];
/**
* Letter sequence delimiters.
*/
export const LETTER_DELIM_OPTIONS = ["Space", "Line feed", "CRLF", "Forward slash", "Backslash", "Comma", "Semi-colon", "Colon"];
/**
* Word sequence delimiters.
*/
export const WORD_DELIM_OPTIONS = ["Line feed", "CRLF", "Forward slash", "Backslash", "Comma", "Semi-colon", "Colon"];
/**
* Input sequence delimiters.
*/
export const INPUT_DELIM_OPTIONS = ["Line feed", "CRLF", "Space", "Comma", "Semi-colon", "Colon", "Nothing (separate chars)"];
/**
* Armithmetic sequence delimiters
*/
export const ARITHMETIC_DELIM_OPTIONS = ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"];
/**
* Hash delimiters
*/
export const HASH_DELIM_OPTIONS = ["Line feed", "CRLF", "Space", "Comma"];
/**
* IP delimiters
*/
export const IP_DELIM_OPTIONS = ["Line feed", "CRLF", "Space", "Comma", "Semi-colon"];
/**
* Split delimiters.
*/
export const SPLIT_DELIM_OPTIONS = [
{name: "Comma", value: ","},
{name: "Space", value: " "},
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (separate chars)", value: ""}
];
/**
* Join delimiters.
*/
export const JOIN_DELIM_OPTIONS = [
{name: "Line feed", value: "\\n"},
{name: "CRLF", value: "\\r\\n"},
{name: "Space", value: " "},
{name: "Comma", value: ","},
{name: "Semi-colon", value: ";"},
{name: "Colon", value: ":"},
{name: "Nothing (join chars)", value: ""}
];

41
src/core/lib/Extract.mjs Normal file
View File

@@ -0,0 +1,41 @@
/**
* Identifier extraction functions
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
*/
/**
* Runs search operations across the input data using regular expressions.
*
* @param {string} input
* @param {RegExp} searchRegex
* @param {RegExp} removeRegex - A regular expression defining results to remove from the
* final list
* @param {boolean} includeTotal - Whether or not to include the total number of results
* @returns {string}
*/
export function search (input, searchRegex, removeRegex, includeTotal) {
let output = "",
total = 0,
match;
while ((match = searchRegex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
if (match.index === searchRegex.lastIndex) {
searchRegex.lastIndex++;
}
if (removeRegex && removeRegex.test(match[0]))
continue;
total++;
output += match[0] + "\n";
}
if (includeTotal)
output = "Total found: " + total + "\n\n" + output;
return output;
}

View File

@@ -0,0 +1,20 @@
/**
* Flow control functions
*
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
/**
* Returns the index of a label.
*
* @param {Object} state - The current state of the recipe.
* @param {string} name - The label name to look for.
* @returns {number}
*/
export function getLabelIndex(name, state) {
return state.opList.findIndex((operation) => {
return (operation.name === "Label") && (name === operation.ingValues[0]);
});
}

28
src/core/lib/Hash.mjs Normal file
View File

@@ -0,0 +1,28 @@
/**
* Hashing resources.
*
* @author n1474335 [n1474335@gmail.com]
*
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
import CryptoApi from "crypto-api/src/crypto-api";
/**
* Generic hash function.
*
* @param {string} name
* @param {ArrayBuffer} input
* @param {Object} [options={}]
* @returns {string}
*/
export function runHash(name, input, options={}) {
const msg = Utils.arrayBufferToStr(input, false),
hasher = CryptoApi.getHasher(name, options);
hasher.update(msg);
return CryptoApi.encoder.toHex(hasher.finalize());
}

109
src/core/lib/Hex.mjs Normal file
View File

@@ -0,0 +1,109 @@
/**
* Hexadecimal functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
/**
* Convert a byte array into a hex string.
*
* @param {Uint8Array|byteArray} data
* @param {string} [delim=" "]
* @param {number} [padding=2]
* @returns {string}
*
* @example
* // returns "0a 14 1e"
* toHex([10,20,30]);
*
* // returns "0a:14:1e"
* toHex([10,20,30], ":");
*/
export function toHex(data, delim=" ", padding=2) {
if (!data) return "";
let output = "";
for (let i = 0; i < data.length; i++) {
output += data[i].toString(16).padStart(padding, "0") + delim;
}
// Add \x or 0x to beginning
if (delim === "0x") output = "0x" + output;
if (delim === "\\x") output = "\\x" + output;
if (delim.length)
return output.slice(0, -delim.length);
else
return output;
}
/**
* Convert a byte array into a hex string as efficiently as possible with no options.
*
* @param {byteArray} data
* @returns {string}
*
* @example
* // returns "0a141e"
* toHex([10,20,30]);
*/
export function toHexFast(data) {
if (!data) return "";
const output = [];
for (let i = 0; i < data.length; i++) {
output.push((data[i] >>> 4).toString(16));
output.push((data[i] & 0x0f).toString(16));
}
return output.join("");
}
/**
* Convert a hex string into a byte array.
*
* @param {string} data
* @param {string} [delim]
* @param {number} [byteLen=2]
* @returns {byteArray}
*
* @example
* // returns [10,20,30]
* fromHex("0a 14 1e");
*
* // returns [10,20,30]
* fromHex("0a:14:1e", "Colon");
*/
export function fromHex(data, delim="Auto", byteLen=2) {
if (delim !== "None") {
const delimRegex = delim === "Auto" ? /[^a-f\d]/gi : Utils.regexRep(delim);
data = data.replace(delimRegex, "");
}
const output = [];
for (let i = 0; i < data.length; i += byteLen) {
output.push(parseInt(data.substr(i, byteLen), 16));
}
return output;
}
/**
* To Hexadecimal delimiters.
*/
export const TO_HEX_DELIM_OPTIONS = ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"];
/**
* From Hexadecimal delimiters.
*/
export const FROM_HEX_DELIM_OPTIONS = ["Auto"].concat(TO_HEX_DELIM_OPTIONS);

676
src/core/lib/IP.mjs Normal file
View File

@@ -0,0 +1,676 @@
/**
* IP resources.
*
* @author picapi
* @author n1474335 [n1474335@gmail.com]
* @author Klaxon [klaxon@veyr.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
/**
* Parses an IPv4 CIDR range (e.g. 192.168.0.0/24) and displays information about it.
*
* @param {RegExp} cidr
* @param {boolean} includeNetworkInfo
* @param {boolean} enumerateAddresses
* @param {boolean} allowLargeList
* @returns {string}
*/
export function ipv4CidrRange(cidr, includeNetworkInfo, enumerateAddresses, allowLargeList) {
const network = strToIpv4(cidr[1]),
cidrRange = parseInt(cidr[2], 10);
let output = "";
if (cidrRange < 0 || cidrRange > 31) {
return "IPv4 CIDR must be less than 32";
}
const mask = ~(0xFFFFFFFF >>> cidrRange),
ip1 = network & mask,
ip2 = ip1 | ~mask;
if (includeNetworkInfo) {
output += "Network: " + ipv4ToStr(network) + "\n";
output += "CIDR: " + cidrRange + "\n";
output += "Mask: " + ipv4ToStr(mask) + "\n";
output += "Range: " + ipv4ToStr(ip1) + " - " + ipv4ToStr(ip2) + "\n";
output += "Total addresses in range: " + (((ip2 - ip1) >>> 0) + 1) + "\n\n";
}
if (enumerateAddresses) {
if (cidrRange >= 16 || allowLargeList) {
output += generateIpv4Range(ip1, ip2).join("\n");
} else {
output += _LARGE_RANGE_ERROR;
}
}
return output;
}
/**
* Parses an IPv6 CIDR range (e.g. ff00::/48) and displays information about it.
*
* @param {RegExp} cidr
* @param {boolean} includeNetworkInfo
* @returns {string}
*/
export function ipv6CidrRange(cidr, includeNetworkInfo) {
let output = "";
const network = strToIpv6(cidr[1]),
cidrRange = parseInt(cidr[cidr.length-1], 10);
if (cidrRange < 0 || cidrRange > 127) {
return "IPv6 CIDR must be less than 128";
}
const ip1 = new Array(8),
ip2 = new Array(8),
total = new Array(128);
const mask = genIpv6Mask(cidrRange);
let totalDiff = "";
for (let i = 0; i < 8; i++) {
ip1[i] = network[i] & mask[i];
ip2[i] = ip1[i] | (~mask[i] & 0x0000FFFF);
totalDiff = (ip2[i] - ip1[i]).toString(2);
if (totalDiff !== "0") {
for (let n = 0; n < totalDiff.length; n++) {
total[i*16 + 16-(totalDiff.length-n)] = totalDiff[n];
}
}
}
if (includeNetworkInfo) {
output += "Network: " + ipv6ToStr(network) + "\n";
output += "Shorthand: " + ipv6ToStr(network, true) + "\n";
output += "CIDR: " + cidrRange + "\n";
output += "Mask: " + ipv6ToStr(mask) + "\n";
output += "Range: " + ipv6ToStr(ip1) + " - " + ipv6ToStr(ip2) + "\n";
output += "Total addresses in range: " + (parseInt(total.join(""), 2) + 1) + "\n\n";
}
return output;
}
/**
* Parses an IPv4 hyphenated range (e.g. 192.168.0.0 - 192.168.0.255) and displays information
* about it.
*
* @param {RegExp} range
* @param {boolean} includeNetworkInfo
* @param {boolean} enumerateAddresses
* @param {boolean} allowLargeList
* @returns {string}
*/
export function ipv4HyphenatedRange(range, includeNetworkInfo, enumerateAddresses, allowLargeList) {
const ip1 = strToIpv4(range[0].split("-")[0].trim()),
ip2 = strToIpv4(range[0].split("-")[1].trim());
let output = "";
// Calculate mask
let diff = ip1 ^ ip2,
cidr = 32,
mask = 0;
while (diff !== 0) {
diff >>= 1;
cidr--;
mask = (mask << 1) | 1;
}
mask = ~mask >>> 0;
const network = ip1 & mask,
subIp1 = network & mask,
subIp2 = subIp1 | ~mask;
if (includeNetworkInfo) {
output += `Minimum subnet required to hold this range:
\tNetwork: ${ipv4ToStr(network)}
\tCIDR: ${cidr}
\tMask: ${ipv4ToStr(mask)}
\tSubnet range: ${ipv4ToStr(subIp1)} - ${ipv4ToStr(subIp2)}
\tTotal addresses in subnet: ${(((subIp2 - subIp1) >>> 0) + 1)}
Range: ${ipv4ToStr(ip1)} - ${ipv4ToStr(ip2)}
Total addresses in range: ${(((ip2 - ip1) >>> 0) + 1)}
`;
}
if (enumerateAddresses) {
if (((ip2 - ip1) >>> 0) <= 65536 || allowLargeList) {
output += generateIpv4Range(ip1, ip2).join("\n");
} else {
output += _LARGE_RANGE_ERROR;
}
}
return output;
}
/**
* Parses an IPv6 hyphenated range (e.g. ff00:: - ffff::) and displays information about it.
*
* @param {RegExp} range
* @param {boolean} includeNetworkInfo
* @returns {string}
*/
export function ipv6HyphenatedRange(range, includeNetworkInfo) {
const ip1 = strToIpv6(range[0].split("-")[0].trim()),
ip2 = strToIpv6(range[0].split("-")[1].trim()),
total = new Array(128).fill();
let output = "",
t = "",
i;
for (i = 0; i < 8; i++) {
t = (ip2[i] - ip1[i]).toString(2);
if (t !== "0") {
for (let n = 0; n < t.length; n++) {
total[i*16 + 16-(t.length-n)] = t[n];
}
}
}
if (includeNetworkInfo) {
output += "Range: " + ipv6ToStr(ip1) + " - " + ipv6ToStr(ip2) + "\n";
output += "Shorthand range: " + ipv6ToStr(ip1, true) + " - " + ipv6ToStr(ip2, true) + "\n";
output += "Total addresses in range: " + (parseInt(total.join(""), 2) + 1) + "\n\n";
}
return output;
}
/**
* Parses a list of IPv4 addresses separated by a new line (\n) and displays information
* about it.
*
* @param {RegExp} list
* @param {boolean} includeNetworkInfo
* @param {boolean} enumerateAddresses
* @param {boolean} allowLargeList
* @returns {string}
*/
export function ipv4ListedRange(match, includeNetworkInfo, enumerateAddresses, allowLargeList) {
let ipv4List = match[0].split("\n");
ipv4List = ipv4List.filter(Boolean);
const ipv4CidrList = ipv4List.filter(function(a) {
return a.includes("/");
});
for (let i = 0; i < ipv4CidrList.length; i++) {
const network = strToIpv4(ipv4CidrList[i].split("/")[0]);
const cidrRange = parseInt(ipv4CidrList[i].split("/")[1], 10);
if (cidrRange < 0 || cidrRange > 31) {
return "IPv4 CIDR must be less than 32";
}
const mask = ~(0xFFFFFFFF >>> cidrRange),
cidrIp1 = network & mask,
cidrIp2 = cidrIp1 | ~mask;
ipv4List.splice(ipv4List.indexOf(ipv4CidrList[i]), 1);
ipv4List.push(ipv4ToStr(cidrIp1), ipv4ToStr(cidrIp2));
}
ipv4List = ipv4List.sort(ipv4Compare);
const ip1 = ipv4List[0];
const ip2 = ipv4List[ipv4List.length - 1];
const range = [ip1 + " - " + ip2];
return ipv4HyphenatedRange(range, includeNetworkInfo, enumerateAddresses, allowLargeList);
}
/**
* Parses a list of IPv6 addresses separated by a new line (\n) and displays information
* about it.
*
* @param {RegExp} list
* @param {boolean} includeNetworkInfo
* @returns {string}
*/
export function ipv6ListedRange(match, includeNetworkInfo) {
let ipv6List = match[0].split("\n");
ipv6List = ipv6List.filter(function(str) {
return str.trim();
});
for (let i =0; i < ipv6List.length; i++){
ipv6List[i] = ipv6List[i].trim();
}
const ipv6CidrList = ipv6List.filter(function(a) {
return a.includes("/");
});
for (let i = 0; i < ipv6CidrList.length; i++) {
const network = strToIpv6(ipv6CidrList[i].split("/")[0]);
const cidrRange = parseInt(ipv6CidrList[i].split("/")[1], 10);
if (cidrRange < 0 || cidrRange > 127) {
return "IPv6 CIDR must be less than 128";
}
const cidrIp1 = new Array(8),
cidrIp2 = new Array(8);
const mask = genIpv6Mask(cidrRange);
for (let j = 0; j < 8; j++) {
cidrIp1[j] = network[j] & mask[j];
cidrIp2[j] = cidrIp1[j] | (~mask[j] & 0x0000FFFF);
}
ipv6List.splice(ipv6List.indexOf(ipv6CidrList[i]), 1);
ipv6List.push(ipv6ToStr(cidrIp1), ipv6ToStr(cidrIp2));
}
ipv6List = ipv6List.sort(ipv6Compare);
const ip1 = ipv6List[0];
const ip2 = ipv6List[ipv6List.length - 1];
const range = [ip1 + " - " + ip2];
return ipv6HyphenatedRange(range, includeNetworkInfo);
}
/**
* Converts an IPv4 address from string format to numerical format.
*
* @param {string} ipStr
* @returns {number}
*
* @example
* // returns 168427520
* strToIpv4("10.10.0.0");
*/
export function strToIpv4(ipStr) {
const blocks = ipStr.split("."),
numBlocks = parseBlocks(blocks);
let result = 0;
result += numBlocks[0] << 24;
result += numBlocks[1] << 16;
result += numBlocks[2] << 8;
result += numBlocks[3];
return result;
/**
* Converts a list of 4 numeric strings in the range 0-255 to a list of numbers.
*/
function parseBlocks(blocks) {
if (blocks.length !== 4)
throw new OperationError("More than 4 blocks.");
const numBlocks = [];
for (let i = 0; i < 4; i++) {
numBlocks[i] = parseInt(blocks[i], 10);
if (numBlocks[i] < 0 || numBlocks[i] > 255)
throw new OperationError("Block out of range.");
}
return numBlocks;
}
}
/**
* Converts an IPv4 address from numerical format to string format.
*
* @param {number} ipInt
* @returns {string}
*
* @example
* // returns "10.10.0.0"
* ipv4ToStr(168427520);
*/
export function ipv4ToStr(ipInt) {
const blockA = (ipInt >> 24) & 255,
blockB = (ipInt >> 16) & 255,
blockC = (ipInt >> 8) & 255,
blockD = ipInt & 255;
return blockA + "." + blockB + "." + blockC + "." + blockD;
}
/**
* Converts an IPv6 address from string format to numerical array format.
*
* @param {string} ipStr
* @returns {number[]}
*
* @example
* // returns [65280, 0, 0, 0, 0, 0, 4369, 8738]
* strToIpv6("ff00::1111:2222");
*/
export function strToIpv6(ipStr) {
let j = 0;
const blocks = ipStr.split(":"),
numBlocks = parseBlocks(blocks),
ipv6 = new Array(8);
for (let i = 0; i < 8; i++) {
if (isNaN(numBlocks[j])) {
ipv6[i] = 0;
if (i === (8-numBlocks.slice(j).length)) j++;
} else {
ipv6[i] = numBlocks[j];
j++;
}
}
return ipv6;
/**
* Converts a list of 3-8 numeric hex strings in the range 0-65535 to a list of numbers.
*/
function parseBlocks(blocks) {
if (blocks.length < 3 || blocks.length > 8)
throw new OperationError("Badly formatted IPv6 address.");
const numBlocks = [];
for (let i = 0; i < blocks.length; i++) {
numBlocks[i] = parseInt(blocks[i], 16);
if (numBlocks[i] < 0 || numBlocks[i] > 65535)
throw new OperationError("Block out of range.");
}
return numBlocks;
}
}
/**
* Converts an IPv6 address from numerical array format to string format.
*
* @param {number[]} ipv6
* @param {boolean} compact - Whether or not to return the address in shorthand or not
* @returns {string}
*
* @example
* // returns "ff00::1111:2222"
* ipv6ToStr([65280, 0, 0, 0, 0, 0, 4369, 8738], true);
*
* // returns "ff00:0000:0000:0000:0000:0000:1111:2222"
* ipv6ToStr([65280, 0, 0, 0, 0, 0, 4369, 8738], false);
*/
export function ipv6ToStr(ipv6, compact) {
let output = "",
i = 0;
if (compact) {
let start = -1,
end = -1,
s = 0,
e = -1;
for (i = 0; i < 8; i++) {
if (ipv6[i] === 0 && e === (i-1)) {
e = i;
} else if (ipv6[i] === 0) {
s = i; e = i;
}
if (e >= 0 && (e-s) > (end - start)) {
start = s;
end = e;
}
}
for (i = 0; i < 8; i++) {
if (i !== start) {
output += Utils.hex(ipv6[i], 1) + ":";
} else {
output += ":";
i = end;
if (end === 7) output += ":";
}
}
if (output[0] === ":")
output = ":" + output;
} else {
for (i = 0; i < 8; i++) {
output += Utils.hex(ipv6[i], 4) + ":";
}
}
return output.slice(0, output.length-1);
}
/**
* Generates a list of IPv4 addresses in string format between two given numerical values.
*
* @param {number} ip
* @param {number} endIp
* @returns {string[]}
*
* @example
* // returns ["0.0.0.1", "0.0.0.2", "0.0.0.3"]
* IP.generateIpv4Range(1, 3);
*/
export function generateIpv4Range(ip, endIp) {
const range = [];
if (endIp >= ip) {
for (; ip <= endIp; ip++) {
range.push(ipv4ToStr(ip));
}
} else {
range[0] = "Second IP address smaller than first.";
}
return range;
}
/**
* Generates an IPv6 subnet mask given a CIDR value.
*
* @param {number} cidr
* @returns {number[]}
*/
export function genIpv6Mask(cidr) {
const mask = new Array(8);
let shift;
for (let i = 0; i < 8; i++) {
if (cidr > ((i+1)*16)) {
mask[i] = 0x0000FFFF;
} else {
shift = cidr-(i*16);
if (shift < 0) shift = 0;
mask[i] = ~((0x0000FFFF >>> shift) | 0xFFFF0000);
}
}
return mask;
}
/**
* Comparison operation for sorting of IPv4 addresses.
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function ipv4Compare(a, b) {
return strToIpv4(a) - strToIpv4(b);
}
/**
* Comparison operation for sorting of IPv6 addresses.
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function ipv6Compare(a, b) {
const a_ = strToIpv6(a),
b_ = strToIpv6(b);
for (let i = 0; i < a_.length; i++){
if (a_[i] !== b_[i]){
return a_[i] - b_[i];
}
}
return 0;
}
const _LARGE_RANGE_ERROR = "The specified range contains more than 65,536 addresses. Running this query could crash your browser. If you want to run it, select the \"Allow large queries\" option. You are advised to turn off \"Auto Bake\" whilst editing large ranges.";
/**
* A regular expression that matches an IPv4 address
*/
export const IPV4_REGEX = /^\s*((?:\d{1,3}\.){3}\d{1,3})\s*$/;
/**
* A regular expression that matches an IPv6 address
*/
export const IPV6_REGEX = /^\s*(((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\4)::|:\b|(?![\dA-F])))|(?!\3\4)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4}))\s*$/i;
/**
* Lookup table for Internet Protocols.
* Taken from https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
*/
export const protocolLookup = {
0: {keyword: "HOPOPT", protocol: "IPv6 Hop-by-Hop Option"},
1: {keyword: "ICMP", protocol: "Internet Control Message"},
2: {keyword: "IGMP", protocol: "Internet Group Management"},
3: {keyword: "GGP", protocol: "Gateway-to-Gateway"},
4: {keyword: "IPv4", protocol: "IPv4 encapsulation"},
5: {keyword: "ST", protocol: "Stream"},
6: {keyword: "TCP", protocol: "Transmission Control"},
7: {keyword: "CBT", protocol: "CBT"},
8: {keyword: "EGP", protocol: "Exterior Gateway Protocol"},
9: {keyword: "IGP", protocol: "any private interior gateway (used by Cisco for their IGRP)"},
10: {keyword: "BBN-RCC-MON", protocol: "BBN RCC Monitoring"},
11: {keyword: "NVP-II", protocol: "Network Voice Protocol"},
12: {keyword: "PUP", protocol: "PUP"},
13: {keyword: "ARGUS (deprecated)", protocol: "ARGUS"},
14: {keyword: "EMCON", protocol: "EMCON"},
15: {keyword: "XNET", protocol: "Cross Net Debugger"},
16: {keyword: "CHAOS", protocol: "Chaos"},
17: {keyword: "UDP", protocol: "User Datagram"},
18: {keyword: "MUX", protocol: "Multiplexing"},
19: {keyword: "DCN-MEAS", protocol: "DCN Measurement Subsystems"},
20: {keyword: "HMP", protocol: "Host Monitoring"},
21: {keyword: "PRM", protocol: "Packet Radio Measurement"},
22: {keyword: "XNS-IDP", protocol: "XEROX NS IDP"},
23: {keyword: "TRUNK-1", protocol: "Trunk-1"},
24: {keyword: "TRUNK-2", protocol: "Trunk-2"},
25: {keyword: "LEAF-1", protocol: "Leaf-1"},
26: {keyword: "LEAF-2", protocol: "Leaf-2"},
27: {keyword: "RDP", protocol: "Reliable Data Protocol"},
28: {keyword: "IRTP", protocol: "Internet Reliable Transaction"},
29: {keyword: "ISO-TP4", protocol: "ISO Transport Protocol Class 4"},
30: {keyword: "NETBLT", protocol: "Bulk Data Transfer Protocol"},
31: {keyword: "MFE-NSP", protocol: "MFE Network Services Protocol"},
32: {keyword: "MERIT-INP", protocol: "MERIT Internodal Protocol"},
33: {keyword: "DCCP", protocol: "Datagram Congestion Control Protocol"},
34: {keyword: "3PC", protocol: "Third Party Connect Protocol"},
35: {keyword: "IDPR", protocol: "Inter-Domain Policy Routing Protocol"},
36: {keyword: "XTP", protocol: "XTP"},
37: {keyword: "DDP", protocol: "Datagram Delivery Protocol"},
38: {keyword: "IDPR-CMTP", protocol: "IDPR Control Message Transport Proto"},
39: {keyword: "TP++", protocol: "TP++ Transport Protocol"},
40: {keyword: "IL", protocol: "IL Transport Protocol"},
41: {keyword: "IPv6", protocol: "IPv6 encapsulation"},
42: {keyword: "SDRP", protocol: "Source Demand Routing Protocol"},
43: {keyword: "IPv6-Route", protocol: "Routing Header for IPv6"},
44: {keyword: "IPv6-Frag", protocol: "Fragment Header for IPv6"},
45: {keyword: "IDRP", protocol: "Inter-Domain Routing Protocol"},
46: {keyword: "RSVP", protocol: "Reservation Protocol"},
47: {keyword: "GRE", protocol: "Generic Routing Encapsulation"},
48: {keyword: "DSR", protocol: "Dynamic Source Routing Protocol"},
49: {keyword: "BNA", protocol: "BNA"},
50: {keyword: "ESP", protocol: "Encap Security Payload"},
51: {keyword: "AH", protocol: "Authentication Header"},
52: {keyword: "I-NLSP", protocol: "Integrated Net Layer Security TUBA"},
53: {keyword: "SWIPE (deprecated)", protocol: "IP with Encryption"},
54: {keyword: "NARP", protocol: "NBMA Address Resolution Protocol"},
55: {keyword: "MOBILE", protocol: "IP Mobility"},
56: {keyword: "TLSP", protocol: "Transport Layer Security Protocol using Kryptonet key management"},
57: {keyword: "SKIP", protocol: "SKIP"},
58: {keyword: "IPv6-ICMP", protocol: "ICMP for IPv6"},
59: {keyword: "IPv6-NoNxt", protocol: "No Next Header for IPv6"},
60: {keyword: "IPv6-Opts", protocol: "Destination Options for IPv6"},
61: {keyword: "", protocol: "any host internal protocol"},
62: {keyword: "CFTP", protocol: "CFTP"},
63: {keyword: "", protocol: "any local network"},
64: {keyword: "SAT-EXPAK", protocol: "SATNET and Backroom EXPAK"},
65: {keyword: "KRYPTOLAN", protocol: "Kryptolan"},
66: {keyword: "RVD", protocol: "MIT Remote Virtual Disk Protocol"},
67: {keyword: "IPPC", protocol: "Internet Pluribus Packet Core"},
68: {keyword: "", protocol: "any distributed file system"},
69: {keyword: "SAT-MON", protocol: "SATNET Monitoring"},
70: {keyword: "VISA", protocol: "VISA Protocol"},
71: {keyword: "IPCV", protocol: "Internet Packet Core Utility"},
72: {keyword: "CPNX", protocol: "Computer Protocol Network Executive"},
73: {keyword: "CPHB", protocol: "Computer Protocol Heart Beat"},
74: {keyword: "WSN", protocol: "Wang Span Network"},
75: {keyword: "PVP", protocol: "Packet Video Protocol"},
76: {keyword: "BR-SAT-MON", protocol: "Backroom SATNET Monitoring"},
77: {keyword: "SUN-ND", protocol: "SUN ND PROTOCOL-Temporary"},
78: {keyword: "WB-MON", protocol: "WIDEBAND Monitoring"},
79: {keyword: "WB-EXPAK", protocol: "WIDEBAND EXPAK"},
80: {keyword: "ISO-IP", protocol: "ISO Internet Protocol"},
81: {keyword: "VMTP", protocol: "VMTP"},
82: {keyword: "SECURE-VMTP", protocol: "SECURE-VMTP"},
83: {keyword: "VINES", protocol: "VINES"},
84: {keyword: "TTP", protocol: "Transaction Transport Protocol"},
85: {keyword: "NSFNET-IGP", protocol: "NSFNET-IGP"},
86: {keyword: "DGP", protocol: "Dissimilar Gateway Protocol"},
87: {keyword: "TCF", protocol: "TCF"},
88: {keyword: "EIGRP", protocol: "EIGRP"},
89: {keyword: "OSPFIGP", protocol: "OSPFIGP"},
90: {keyword: "Sprite-RPC", protocol: "Sprite RPC Protocol"},
91: {keyword: "LARP", protocol: "Locus Address Resolution Protocol"},
92: {keyword: "MTP", protocol: "Multicast Transport Protocol"},
93: {keyword: "AX.25", protocol: "AX.25 Frames"},
94: {keyword: "IPIP", protocol: "IP-within-IP Encapsulation Protocol"},
95: {keyword: "MICP (deprecated)", protocol: "Mobile Internetworking Control Pro."},
96: {keyword: "SCC-SP", protocol: "Semaphore Communications Sec. Pro."},
97: {keyword: "ETHERIP", protocol: "Ethernet-within-IP Encapsulation"},
98: {keyword: "ENCAP", protocol: "Encapsulation Header"},
99: {keyword: "", protocol: "any private encryption scheme"},
100: {keyword: "GMTP", protocol: "GMTP"},
101: {keyword: "IFMP", protocol: "Ipsilon Flow Management Protocol"},
102: {keyword: "PNNI", protocol: "PNNI over IP"},
103: {keyword: "PIM", protocol: "Protocol Independent Multicast"},
104: {keyword: "ARIS", protocol: "ARIS"},
105: {keyword: "SCPS", protocol: "SCPS"},
106: {keyword: "QNX", protocol: "QNX"},
107: {keyword: "A/N", protocol: "Active Networks"},
108: {keyword: "IPComp", protocol: "IP Payload Compression Protocol"},
109: {keyword: "SNP", protocol: "Sitara Networks Protocol"},
110: {keyword: "Compaq-Peer", protocol: "Compaq Peer Protocol"},
111: {keyword: "IPX-in-IP", protocol: "IPX in IP"},
112: {keyword: "VRRP", protocol: "Virtual Router Redundancy Protocol"},
113: {keyword: "PGM", protocol: "PGM Reliable Transport Protocol"},
114: {keyword: "", protocol: "any 0-hop protocol"},
115: {keyword: "L2TP", protocol: "Layer Two Tunneling Protocol"},
116: {keyword: "DDX", protocol: "D-II Data Exchange (DDX)"},
117: {keyword: "IATP", protocol: "Interactive Agent Transfer Protocol"},
118: {keyword: "STP", protocol: "Schedule Transfer Protocol"},
119: {keyword: "SRP", protocol: "SpectraLink Radio Protocol"},
120: {keyword: "UTI", protocol: "UTI"},
121: {keyword: "SMP", protocol: "Simple Message Protocol"},
122: {keyword: "SM (deprecated)", protocol: "Simple Multicast Protocol"},
123: {keyword: "PTP", protocol: "Performance Transparency Protocol"},
124: {keyword: "ISIS over IPv4", protocol: ""},
125: {keyword: "FIRE", protocol: ""},
126: {keyword: "CRTP", protocol: "Combat Radio Transport Protocol"},
127: {keyword: "CRUDP", protocol: "Combat Radio User Datagram"},
128: {keyword: "SSCOPMCE", protocol: ""},
129: {keyword: "IPLT", protocol: ""},
130: {keyword: "SPS", protocol: "Secure Packet Shield"},
131: {keyword: "PIPE", protocol: "Private IP Encapsulation within IP"},
132: {keyword: "SCTP", protocol: "Stream Control Transmission Protocol"},
133: {keyword: "FC", protocol: "Fibre Channel"},
134: {keyword: "RSVP-E2E-IGNORE", protocol: ""},
135: {keyword: "Mobility Header", protocol: ""},
136: {keyword: "UDPLite", protocol: ""},
137: {keyword: "MPLS-in-IP", protocol: ""},
138: {keyword: "manet", protocol: "MANET Protocols"},
139: {keyword: "HIP", protocol: "Host Identity Protocol"},
140: {keyword: "Shim6", protocol: "Shim6 Protocol"},
141: {keyword: "WESP", protocol: "Wrapped Encapsulating Security Payload"},
142: {keyword: "ROHC", protocol: "Robust Header Compression"},
253: {keyword: "", protocol: "Use for experimentation and testing"},
254: {keyword: "", protocol: "Use for experimentation and testing"},
255: {keyword: "Reserved", protocol: ""}
};

1560
src/core/lib/Magic.mjs Normal file

File diff suppressed because it is too large Load Diff

117
src/core/lib/PGP.mjs Normal file
View File

@@ -0,0 +1,117 @@
/**
* PGP functions.
*
* @author tlwr [toby@toby.codes]
* @author Matt C [matt@artemisbot.uk]
* @author n1474335 [n1474335@gmail.com]
*
* @copyright Crown Copyright 2018
* @license Apache-2.0
*
*/
import OperationError from "../errors/OperationError";
import kbpgp from "kbpgp";
import * as es6promisify from "es6-promisify";
const promisify = es6promisify.default ? es6promisify.default.promisify : es6promisify.promisify;
/**
* Progress callback
*/
export const ASP = kbpgp.ASP({
"progress_hook": info => {
let msg = "";
switch (info.what) {
case "guess":
msg = "Guessing a prime";
break;
case "fermat":
msg = "Factoring prime using Fermat's factorization method";
break;
case "mr":
msg = "Performing Miller-Rabin primality test";
break;
case "passed_mr":
msg = "Passed Miller-Rabin primality test";
break;
case "failed_mr":
msg = "Failed Miller-Rabin primality test";
break;
case "found":
msg = "Prime found";
break;
default:
msg = `Stage: ${info.what}`;
}
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(msg);
}
});
/**
* Get size of subkey
*
* @param {number} keySize
* @returns {number}
*/
export function getSubkeySize(keySize) {
return {
1024: 1024,
2048: 1024,
4096: 2048,
256: 256,
384: 256,
}[keySize];
}
/**
* Import private key and unlock if necessary
*
* @param {string} privateKey
* @param {string} [passphrase]
* @returns {Object}
*/
export async function importPrivateKey(privateKey, passphrase) {
try {
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
armored: privateKey,
opts: {
"no_check_keys": true
}
});
if (key.is_pgp_locked()) {
if (passphrase) {
await promisify(key.unlock_pgp.bind(key))({
passphrase
});
} else {
throw new OperationError("Did not provide passphrase with locked private key.");
}
}
return key;
} catch (err) {
throw new OperationError(`Could not import private key: ${err}`);
}
}
/**
* Import public key
*
* @param {string} publicKey
* @returns {Object}
*/
export async function importPublicKey (publicKey) {
try {
const key = await promisify(kbpgp.KeyManager.import_from_armored_pgp)({
armored: publicKey,
opts: {
"no_check_keys": true
}
});
return key;
} catch (err) {
throw new OperationError(`Could not import public key: ${err}`);
}
}

View File

@@ -0,0 +1,72 @@
/**
* Public key resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import { toHex, fromHex } from "./Hex";
/**
* Formats Distinguished Name (DN) strings.
*
* @param {string} dnStr
* @param {number} indent
* @returns {string}
*/
export function formatDnStr (dnStr, indent) {
const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
let output = "",
maxKeyLen = 0,
key,
value,
i,
str;
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
maxKeyLen = key.length > maxKeyLen ? key.length : maxKeyLen;
}
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
key = fields[i].split("=")[0];
value = fields[i].split("=")[1];
str = key.padEnd(maxKeyLen, " ") + " = " + value + "\n";
output += str.padStart(indent + str.length, " ");
}
return output.slice(0, -1);
}
/**
* Formats byte strings by adding line breaks and delimiters.
*
* @param {string} byteStr
* @param {number} length - Line width
* @param {number} indent
* @returns {string}
*/
export function formatByteStr (byteStr, length, indent) {
byteStr = toHex(fromHex(byteStr), ":");
length = length * 3;
let output = "";
for (let i = 0; i < byteStr.length; i += length) {
const str = byteStr.slice(i, i + length) + "\n";
if (i === 0) {
output += str;
} else {
output += str.padStart(indent + str.length, " ");
}
}
return output.slice(0, output.length-1);
}

103
src/core/lib/Rotate.mjs Normal file
View File

@@ -0,0 +1,103 @@
/**
* Bit rotation functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @todo Support for UTF16
*/
/**
* Runs rotation operations across the input data.
*
* @param {byteArray} data
* @param {number} amount
* @param {function} algo - The rotation operation to carry out
* @returns {byteArray}
*/
export function rot(data, amount, algo) {
const result = [];
for (let i = 0; i < data.length; i++) {
let b = data[i];
for (let j = 0; j < amount; j++) {
b = algo(b);
}
result.push(b);
}
return result;
}
/**
* Rotate right bitwise op.
*
* @param {byte} b
* @returns {byte}
*/
export function rotr(b) {
const bit = (b & 1) << 7;
return (b >> 1) | bit;
}
/**
* Rotate left bitwise op.
*
* @param {byte} b
* @returns {byte}
*/
export function rotl(b) {
const bit = (b >> 7) & 1;
return ((b << 1) | bit) & 0xFF;
}
/**
* Rotates a byte array to the right by a specific amount as a whole, so that bits are wrapped
* from the end of the array to the beginning.
*
* @param {byteArray} data
* @param {number} amount
* @returns {byteArray}
*/
export function rotrCarry(data, amount) {
const result = [];
let carryBits = 0,
newByte;
amount = amount % 8;
for (let i = 0; i < data.length; i++) {
const oldByte = data[i] >>> 0;
newByte = (oldByte >> amount) | carryBits;
carryBits = (oldByte & (Math.pow(2, amount)-1)) << (8-amount);
result.push(newByte);
}
result[0] |= carryBits;
return result;
}
/**
* Rotates a byte array to the left by a specific amount as a whole, so that bits are wrapped
* from the beginning of the array to the end.
*
* @param {byteArray} data
* @param {number} amount
* @returns {byteArray}
*/
export function rotlCarry(data, amount) {
const result = [];
let carryBits = 0,
newByte;
amount = amount % 8;
for (let i = data.length-1; i >= 0; i--) {
const oldByte = data[i];
newByte = ((oldByte << amount) | carryBits) & 0xFF;
carryBits = (oldByte >> (8-amount)) & (Math.pow(2, amount)-1);
result[i] = (newByte);
}
result[data.length-1] = result[data.length-1] | carryBits;
return result;
}

19
src/core/lib/Zlib.mjs Normal file
View File

@@ -0,0 +1,19 @@
/**
* Zlib exports.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import zlibAndGzip from "zlibjs/bin/zlib_and_gzip.min";
const Zlib = zlibAndGzip.Zlib;
export const COMPRESSION_TYPE = ["Dynamic Huffman Coding", "Fixed Huffman Coding", "None (Store)"];
export const INFLATE_BUFFER_TYPE = ["Adaptive", "Block"];
export const ZLIB_COMPRESSION_TYPE_LOOKUP = {
"Fixed Huffman Coding": Zlib.Deflate.CompressionType.FIXED,
"Dynamic Huffman Coding": Zlib.Deflate.CompressionType.DYNAMIC,
"None (Store)": Zlib.Deflate.CompressionType.NONE,
};

View File

@@ -1,186 +0,0 @@
"use strict";
/**
* Various components for drawing diagrams on an HTML5 canvas.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @constant
* @namespace
*/
const CanvasComponents = {
drawLine: function(ctx, startX, startY, endX, endY) {
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(endX, endY);
ctx.closePath();
ctx.stroke();
},
drawBarChart: function(canvas, scores, xAxisLabel, yAxisLabel, numXLabels, numYLabels, fontSize) {
fontSize = fontSize || 15;
if (!numXLabels || numXLabels > Math.round(canvas.width / 50)) {
numXLabels = Math.round(canvas.width / 50);
}
if (!numYLabels || numYLabels > Math.round(canvas.width / 50)) {
numYLabels = Math.round(canvas.height / 50);
}
// Graph properties
var ctx = canvas.getContext("2d"),
leftPadding = canvas.width * 0.08,
rightPadding = canvas.width * 0.03,
topPadding = canvas.height * 0.08,
bottomPadding = canvas.height * 0.15,
graphHeight = canvas.height - topPadding - bottomPadding,
graphWidth = canvas.width - leftPadding - rightPadding,
base = topPadding + graphHeight,
ceil = topPadding;
ctx.font = fontSize + "px Arial";
// Draw axis
ctx.lineWidth = "1.0";
ctx.strokeStyle = "#444";
CanvasComponents.drawLine(ctx, leftPadding, base, graphWidth + leftPadding, base); // x
CanvasComponents.drawLine(ctx, leftPadding, base, leftPadding, ceil); // y
// Bar properties
var barPadding = graphWidth * 0.003,
barWidth = (graphWidth - (barPadding * scores.length)) / scores.length,
currX = leftPadding + barPadding,
max = Math.max.apply(Math, scores);
// Draw bars
ctx.fillStyle = "green";
for (var i = 0; i < scores.length; i++) {
var h = scores[i] / max * graphHeight;
ctx.fillRect(currX, base - h, barWidth, h);
currX += barWidth + barPadding;
}
// Mark x axis
ctx.fillStyle = "black";
ctx.textAlign = "center";
currX = leftPadding + barPadding;
if (numXLabels >= scores.length) {
// Mark every score
for (i = 0; i <= scores.length; i++) {
ctx.fillText(i, currX, base + (bottomPadding * 0.3));
currX += barWidth + barPadding;
}
} else {
// Mark some scores
for (i = 0; i <= numXLabels; i++) {
var val = Math.ceil((scores.length / numXLabels) * i);
currX = (graphWidth / numXLabels) * i + leftPadding;
ctx.fillText(val, currX, base + (bottomPadding * 0.3));
}
}
// Mark y axis
ctx.textAlign = "right";
var currY;
if (numYLabels >= max) {
// Mark every increment
for (i = 0; i <= max; i++) {
currY = base - (i / max * graphHeight) + fontSize / 3;
ctx.fillText(i, leftPadding * 0.8, currY);
}
} else {
// Mark some increments
for (i = 0; i <= numYLabels; i++) {
val = Math.ceil((max / numYLabels) * i);
currY = base - (val / max * graphHeight) + fontSize / 3;
ctx.fillText(val, leftPadding * 0.8, currY);
}
}
// Label x axis
if (xAxisLabel) {
ctx.textAlign = "center";
ctx.fillText(xAxisLabel, graphWidth / 2 + leftPadding, base + bottomPadding * 0.8);
}
// Label y axis
if (yAxisLabel) {
ctx.save();
var x = leftPadding * 0.3,
y = graphHeight / 2 + topPadding;
ctx.translate(x, y);
ctx.rotate(-Math.PI / 2);
ctx.textAlign = "center";
ctx.fillText(yAxisLabel, 0, 0);
ctx.restore();
}
},
drawScaleBar: function(canvas, score, max, markings) {
// Bar properties
var ctx = canvas.getContext("2d"),
leftPadding = canvas.width * 0.01,
rightPadding = canvas.width * 0.01,
topPadding = canvas.height * 0.1,
bottomPadding = canvas.height * 0.3,
barHeight = canvas.height - topPadding - bottomPadding,
barWidth = canvas.width - leftPadding - rightPadding;
// Scale properties
var proportion = score / max;
// Draw bar outline
ctx.strokeRect(leftPadding, topPadding, barWidth, barHeight);
// Shade in up to proportion
var grad = ctx.createLinearGradient(leftPadding, 0, barWidth + leftPadding, 0);
grad.addColorStop(0, "green");
grad.addColorStop(0.5, "gold");
grad.addColorStop(1, "red");
ctx.fillStyle = grad;
ctx.fillRect(leftPadding, topPadding, barWidth * proportion, barHeight);
// Add markings
var x0, y0, x1, y1;
ctx.fillStyle = "black";
ctx.textAlign = "center";
ctx.font = "13px Arial";
for (var i = 0; i < markings.length; i++) {
// Draw min line down
x0 = barWidth / max * markings[i].min + leftPadding;
y0 = topPadding + barHeight + (bottomPadding * 0.1);
x1 = x0;
y1 = topPadding + barHeight + (bottomPadding * 0.3);
CanvasComponents.drawLine(ctx, x0, y0, x1, y1);
// Draw max line down
x0 = barWidth / max * markings[i].max + leftPadding;
x1 = x0;
CanvasComponents.drawLine(ctx, x0, y0, x1, y1);
// Join min and max lines
x0 = barWidth / max * markings[i].min + leftPadding;
y0 = topPadding + barHeight + (bottomPadding * 0.3);
x1 = barWidth / max * markings[i].max + leftPadding;
y1 = y0;
CanvasComponents.drawLine(ctx, x0, y0, x1, y1);
// Add label
if (markings[i].max >= max * 0.9) {
ctx.textAlign = "right";
x0 = x1;
} else if (markings[i].max <= max * 0.1) {
ctx.textAlign = "left";
} else {
x0 = x0 + (x1 - x0) / 2;
}
y0 = topPadding + barHeight + (bottomPadding * 0.8);
ctx.fillText(markings[i].label, x0, y0);
}
},
};
export default CanvasComponents;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import { bitOp, add } from "../lib/BitwiseOp";
/**
* ADD operation
*/
class ADD extends Operation {
/**
* ADD constructor
*/
constructor() {
super();
this.name = "ADD";
this.module = "Default";
this.description = "ADD the input with the given key (e.g. <code>fe023da5</code>), MOD 255";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bitwise_operators";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return bitOp(input, key, add);
}
/**
* Highlight ADD
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight ADD in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default ADD;

View File

@@ -0,0 +1,109 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import forge from "node-forge/dist/forge.min.js";
import OperationError from "../errors/OperationError";
/**
* AES Decrypt operation
*/
class AESDecrypt extends Operation {
/**
* AESDecrypt constructor
*/
constructor() {
super();
this.name = "AES Decrypt";
this.module = "Ciphers";
this.description = "Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.<br><br><b>Key:</b> The following algorithms will be used based on the size of the key:<ul><li>16 bytes = AES-128</li><li>24 bytes = AES-192</li><li>32 bytes = AES-256</li></ul><br><br><b>IV:</b> The Initialization Vector should be 16 bytes long. If not entered, it will default to 16 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.<br><br><b>GCM Tag:</b> This field is ignored unless 'GCM' mode is used.";
this.infoURL = "https://wikipedia.org/wiki/Advanced_Encryption_Standard";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "IV",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Hex", "Raw"]
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "GCM Tag",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if cannot decrypt input or invalid key length
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4],
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
if ([16, 24, 32].indexOf(key.length) < 0) {
throw new OperationError(`Invalid key length: ${key.length} bytes
The following algorithms will be used based on the size of the key:
16 bytes = AES-128
24 bytes = AES-192
32 bytes = AES-256`);
}
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
decipher.start({
iv: iv,
tag: gcmTag
});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();
if (result) {
return outputType === "Hex" ? decipher.output.toHex() : decipher.output.getBytes();
} else {
throw new OperationError("Unable to decrypt input with these parameters.");
}
}
}
export default AESDecrypt;

View File

@@ -0,0 +1,107 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import forge from "node-forge/dist/forge.min.js";
import OperationError from "../errors/OperationError";
/**
* AES Encrypt operation
*/
class AESEncrypt extends Operation {
/**
* AESEncrypt constructor
*/
constructor() {
super();
this.name = "AES Encrypt";
this.module = "Ciphers";
this.description = "Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.<br><br><b>Key:</b> The following algorithms will be used based on the size of the key:<ul><li>16 bytes = AES-128</li><li>24 bytes = AES-192</li><li>32 bytes = AES-256</li></ul>You can generate a password-based key using one of the KDF operations.<br><br><b>IV:</b> The Initialization Vector should be 16 bytes long. If not entered, it will default to 16 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.infoURL = "https://wikipedia.org/wiki/Advanced_Encryption_Standard";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "IV",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "Output",
"type": "option",
"value": ["Hex", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid key length
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
if ([16, 24, 32].indexOf(key.length) < 0) {
throw new OperationError(`Invalid key length: ${key.length} bytes
The following algorithms will be used based on the size of the key:
16 bytes = AES-128
24 bytes = AES-192
32 bytes = AES-256`);
}
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("AES-" + mode, key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer(input));
cipher.finish();
if (outputType === "Hex") {
if (mode === "GCM") {
return cipher.output.toHex() + "\n\n" +
"Tag: " + cipher.mode.tag.toHex();
}
return cipher.output.toHex();
} else {
if (mode === "GCM") {
return cipher.output.getBytes() + "\n\n" +
"Tag: " + cipher.mode.tag.getBytes();
}
return cipher.output.getBytes();
}
}
}
export default AESEncrypt;

View File

@@ -0,0 +1,77 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import { bitOp, and } from "../lib/BitwiseOp";
/**
* AND operation
*/
class AND extends Operation {
/**
* AND constructor
*/
constructor() {
super();
this.name = "AND";
this.module = "Default";
this.description = "AND the input with the given key.<br>e.g. <code>fe023da5</code>";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#AND";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "Decimal", "Base64", "UTF8", "Latin1"]
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return bitOp(input, key, and);
}
/**
* Highlight AND
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight AND in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AND;

View File

@@ -0,0 +1,46 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Add line numbers operation
*/
class AddLineNumbers extends Operation {
/**
* AddLineNumbers constructor
*/
constructor() {
super();
this.name = "Add line numbers";
this.module = "Default";
this.description = "Adds line numbers to the output.";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const lines = input.split("\n"),
width = lines.length.toString().length;
let output = "";
for (let n = 0; n < lines.length; n++) {
output += (n+1).toString().padStart(width, " ") + " " + lines[n] + "\n";
}
return output.slice(0, output.length-1);
}
}
export default AddLineNumbers;

View File

@@ -0,0 +1,53 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
/**
* Adler-32 Checksum operation
*/
class Adler32Checksum extends Operation {
/**
* Adler32Checksum constructor
*/
constructor() {
super();
this.name = "Adler-32 Checksum";
this.module = "Hashing";
this.description = "Adler-32 is a checksum algorithm which was invented by Mark Adler in 1995, and is a modification of the Fletcher checksum. Compared to a cyclic redundancy check of the same length, it trades reliability for speed (preferring the latter).<br><br>Adler-32 is more reliable than Fletcher-16, and slightly less reliable than Fletcher-32.";
this.infoURL = "https://wikipedia.org/wiki/Adler-32";
this.inputType = "byteArray";
this.outputType = "string";
this.args = [];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const MOD_ADLER = 65521;
let a = 1,
b = 0;
for (let i = 0; i < input.length; i++) {
a += input[i];
b += a;
}
a %= MOD_ADLER;
b %= MOD_ADLER;
return Utils.hex(((b << 16) | a) >>> 0, 8);
}
}
export default Adler32Checksum;

View File

@@ -0,0 +1,106 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
/**
* Affine Cipher Decode operation
*/
class AffineCipherDecode extends Operation {
/**
* AffineCipherDecode constructor
*/
constructor() {
super();
this.name = "Affine Cipher Decode";
this.module = "Ciphers";
this.description = "The Affine cipher is a type of monoalphabetic substitution cipher. To decrypt, each letter in an alphabet is mapped to its numeric equivalent, decrypted by a mathematical function, and converted back to a letter.";
this.infoURL = "https://wikipedia.org/wiki/Affine_cipher";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "a",
"type": "number",
"value": 1
},
{
"name": "b",
"type": "number",
"value": 0
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if a or b values are invalid
*/
run(input, args) {
const alphabet = "abcdefghijklmnopqrstuvwxyz",
[a, b] = args,
aModInv = Utils.modInv(a, 26); // Calculates modular inverse of a
let output = "";
if (!/^\+?(0|[1-9]\d*)$/.test(a) || !/^\+?(0|[1-9]\d*)$/.test(b)) {
throw new OperationError("The values of a and b can only be integers.");
}
if (Utils.gcd(a, 26) !== 1) {
throw new OperationError("The value of `a` must be coprime to 26.");
}
for (let i = 0; i < input.length; i++) {
if (alphabet.indexOf(input[i]) >= 0) {
// Uses the affine decode function (y-b * A') % m = x (where m is length of the alphabet and A' is modular inverse)
output += alphabet[Utils.mod((alphabet.indexOf(input[i]) - b) * aModInv, 26)];
} else if (alphabet.indexOf(input[i].toLowerCase()) >= 0) {
// Same as above, accounting for uppercase
output += alphabet[Utils.mod((alphabet.indexOf(input[i].toLowerCase()) - b) * aModInv, 26)].toUpperCase();
} else {
// Non-alphabetic characters
output += input[i];
}
}
return output;
}
/**
* Highlight Affine Cipher Decode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Affine Cipher Decode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AffineCipherDecode;

View File

@@ -0,0 +1,78 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import { affineEncode } from "../lib/Ciphers";
/**
* Affine Cipher Encode operation
*/
class AffineCipherEncode extends Operation {
/**
* AffineCipherEncode constructor
*/
constructor() {
super();
this.name = "Affine Cipher Encode";
this.module = "Ciphers";
this.description = "The Affine cipher is a type of monoalphabetic substitution cipher, wherein each letter in an alphabet is mapped to its numeric equivalent, encrypted using simple mathematical function, <code>(ax + b) % 26</code>, and converted back to a letter.";
this.infoURL = "https://wikipedia.org/wiki/Affine_cipher";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "a",
"type": "number",
"value": 1
},
{
"name": "b",
"type": "number",
"value": 0
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return affineEncode(input, args);
}
/**
* Highlight Affine Cipher Encode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Affine Cipher Encode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AffineCipherEncode;

View File

@@ -0,0 +1,184 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
/**
* Analyse hash operation
*/
class AnalyseHash extends Operation {
/**
* AnalyseHash constructor
*/
constructor() {
super();
this.name = "Analyse hash";
this.module = "Hashing";
this.description = "Tries to determine information about a given hash and suggests which algorithm may have been used to generate it based on its length.";
this.infoURL = "https://wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
input = input.replace(/\s/g, "");
let output = "",
possibleHashFunctions = [];
const byteLength = input.length / 2,
bitLength = byteLength * 8;
if (!/^[a-f0-9]+$/i.test(input)) {
throw new OperationError("Invalid hash");
}
output += "Hash length: " + input.length + "\n" +
"Byte length: " + byteLength + "\n" +
"Bit length: " + bitLength + "\n\n" +
"Based on the length, this hash could have been generated by one of the following hashing functions:\n";
switch (bitLength) {
case 4:
possibleHashFunctions = [
"Fletcher-4",
"Luhn algorithm",
"Verhoeff algorithm",
];
break;
case 8:
possibleHashFunctions = [
"Fletcher-8",
];
break;
case 16:
possibleHashFunctions = [
"BSD checksum",
"CRC-16",
"SYSV checksum",
"Fletcher-16"
];
break;
case 32:
possibleHashFunctions = [
"CRC-32",
"Fletcher-32",
"Adler-32",
];
break;
case 64:
possibleHashFunctions = [
"CRC-64",
"RIPEMD-64",
"SipHash",
];
break;
case 128:
possibleHashFunctions = [
"MD5",
"MD4",
"MD2",
"HAVAL-128",
"RIPEMD-128",
"Snefru",
"Tiger-128",
];
break;
case 160:
possibleHashFunctions = [
"SHA-1",
"SHA-0",
"FSB-160",
"HAS-160",
"HAVAL-160",
"RIPEMD-160",
"Tiger-160",
];
break;
case 192:
possibleHashFunctions = [
"Tiger",
"HAVAL-192",
];
break;
case 224:
possibleHashFunctions = [
"SHA-224",
"SHA3-224",
"ECOH-224",
"FSB-224",
"HAVAL-224",
];
break;
case 256:
possibleHashFunctions = [
"SHA-256",
"SHA3-256",
"BLAKE-256",
"ECOH-256",
"FSB-256",
"GOST",
"Grøstl-256",
"HAVAL-256",
"PANAMA",
"RIPEMD-256",
"Snefru",
];
break;
case 320:
possibleHashFunctions = [
"RIPEMD-320",
];
break;
case 384:
possibleHashFunctions = [
"SHA-384",
"SHA3-384",
"ECOH-384",
"FSB-384",
];
break;
case 512:
possibleHashFunctions = [
"SHA-512",
"SHA3-512",
"BLAKE-512",
"ECOH-512",
"FSB-512",
"Grøstl-512",
"JH",
"MD6",
"Spectral Hash",
"SWIFFT",
"Whirlpool",
];
break;
case 1024:
possibleHashFunctions = [
"Fowler-Noll-Vo",
];
break;
default:
possibleHashFunctions = [
"Unknown"
];
break;
}
return output + possibleHashFunctions.join("\n");
}
}
export default AnalyseHash;

View File

@@ -1,253 +0,0 @@
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
* Math operations on numbers.
*
* @author bwhitn [brian.m.whitney@outlook.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Arithmetic = {
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Line feed", "Space", "Comma", "Semi-colon", "Colon", "CRLF"],
/**
* Splits a string based on a delimiter and calculates the sum of numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSum: function(input, args) {
const val = Arithmetic._sum(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and subtracts all the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runSub: function(input, args) {
let val = Arithmetic._sub(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and multiplies the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMulti: function(input, args) {
let val = Arithmetic._multi(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and divides the numbers.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runDiv: function(input, args) {
let val = Arithmetic._div(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and computes the mean (average).
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMean: function(input, args) {
let val = Arithmetic._mean(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Splits a string based on a delimiter and finds the median.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runMedian: function(input, args) {
let val = Arithmetic._median(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* splits a string based on a delimiter and computes the standard deviation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runStdDev: function(input, args) {
let val = Arithmetic._stdDev(Arithmetic._createNumArray(input, args[0]));
return val instanceof BigNumber ? val : new BigNumber(NaN);
},
/**
* Converts a string array to a number array.
*
* @private
* @param {string[]} input
* @param {string} delim
* @returns {BigNumber[]}
*/
_createNumArray: function(input, delim) {
delim = Utils.charRep[delim || "Space"];
let splitNumbers = input.split(delim),
numbers = [],
num;
for (let i = 0; i < splitNumbers.length; i++) {
try {
num = BigNumber(splitNumbers[i].trim());
if (!num.isNaN()) {
numbers.push(num);
}
} catch (err) {
// This line is not a valid number
}
}
return numbers;
},
/**
* Adds an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sum: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.plus(curr));
}
},
/**
* Subtracts an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_sub: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.minus(curr));
}
},
/**
* Multiplies an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_multi: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.times(curr));
}
},
/**
* Divides an array of numbers and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_div: function(data) {
if (data.length > 0) {
return data.reduce((acc, curr) => acc.div(curr));
}
},
/**
* Computes mean of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_mean: function(data) {
if (data.length > 0) {
return Arithmetic._sum(data).div(data.length);
}
},
/**
* Computes median of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_median: function (data) {
if ((data.length % 2) === 0 && data.length > 0) {
let first, second;
data.sort(function(a, b){
return a.minus(b);
});
first = data[Math.floor(data.length / 2)];
second = data[Math.floor(data.length / 2) - 1];
return Arithmetic._mean([first, second]);
} else {
return data[Math.floor(data.length / 2)];
}
},
/**
* Computes standard deviation of a number array and returns the value.
*
* @private
* @param {BigNumber[]} data
* @returns {BigNumber}
*/
_stdDev: function (data) {
if (data.length > 0) {
let avg = Arithmetic._mean(data);
let devSum = new BigNumber(0);
for (let i = 0; i < data.length; i++) {
devSum = devSum.plus(data[i].minus(avg).pow(2));
}
return devSum.div(data.length).sqrt();
}
},
};
export default Arithmetic;

View File

@@ -0,0 +1,67 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import { affineEncode } from "../lib/Ciphers";
/**
* Atbash Cipher operation
*/
class AtbashCipher extends Operation {
/**
* AtbashCipher constructor
*/
constructor() {
super();
this.name = "Atbash Cipher";
this.module = "Ciphers";
this.description = "Atbash is a mono-alphabetic substitution cipher originally used to encode the Hebrew alphabet. It has been modified here for use with the Latin alphabet.";
this.infoURL = "https://wikipedia.org/wiki/Atbash";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
return affineEncode(input, [25, 25]);
}
/**
* Highlight Atbash Cipher
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Atbash Cipher in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default AtbashCipher;

View File

@@ -1,215 +0,0 @@
import Utils from "../Utils.js";
import BigNumber from "bignumber.js";
/**
* Binary-Coded Decimal operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const BCD = {
/**
* @constant
* @default
*/
ENCODING_SCHEME: [
"8 4 2 1",
"7 4 2 1",
"4 2 2 1",
"2 4 2 1",
"8 4 -2 -1",
"Excess-3",
"IBM 8 4 2 1",
],
/**
* Lookup table for the binary value of each digit representation.
*
* I wrote a very nice algorithm to generate 8 4 2 1 encoding programatically,
* but unfortunately it's much easier (if less elegant) to use lookup tables
* when supporting multiple encoding schemes.
*
* "Practicality beats purity" - PEP 20
*
* In some schemes it is possible to represent the same value in multiple ways.
* For instance, in 4 2 2 1 encoding, 0100 and 0010 both represent 2. Support
* has not yet been added for this.
*
* @constant
*/
ENCODING_LOOKUP: {
"8 4 2 1": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"7 4 2 1": [0, 1, 2, 3, 4, 5, 6, 8, 9, 10],
"4 2 2 1": [0, 1, 4, 5, 8, 9, 12, 13, 14, 15],
"2 4 2 1": [0, 1, 2, 3, 4, 11, 12, 13, 14, 15],
"8 4 -2 -1": [0, 7, 6, 5, 4, 11, 10, 9, 8, 15],
"Excess-3": [3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
"IBM 8 4 2 1": [10, 1, 2, 3, 4, 5, 6, 7, 8, 9],
},
/**
* @default
* @constant
*/
FORMAT: ["Nibbles", "Bytes", "Raw"],
/**
* To BCD operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {string}
*/
runToBCD: function(input, args) {
if (input.isNaN())
return "Invalid input";
if (!input.integerValue(BigNumber.ROUND_DOWN).isEqualTo(input))
return "Fractional values are not supported by BCD";
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
outputFormat = args[3];
// Split input number up into separate digits
const digits = input.toFixed().split("");
if (digits[0] === "-" || digits[0] === "+") {
digits.shift();
}
let nibbles = [];
digits.forEach(d => {
const n = parseInt(d, 10);
nibbles.push(encoding[n]);
});
if (signed) {
if (packed && digits.length % 2 === 0) {
// If there are an even number of digits, we add a leading 0 so
// that the sign nibble doesn't sit in its own byte, leading to
// ambiguity around whether the number ends with a 0 or not.
nibbles.unshift(encoding[0]);
}
nibbles.push(input > 0 ? 12 : 13);
// 12 ("C") for + (credit)
// 13 ("D") for - (debit)
}
let bytes = [];
if (packed) {
let encoded = 0,
little = false;
nibbles.forEach(n => {
encoded ^= little ? n : (n << 4);
if (little) {
bytes.push(encoded);
encoded = 0;
}
little = !little;
});
if (little) bytes.push(encoded);
} else {
bytes = nibbles;
// Add null high nibbles
nibbles = nibbles.map(n => {
return [0, n];
}).reduce((a, b) => {
return a.concat(b);
});
}
// Output
switch (outputFormat) {
case "Nibbles":
return nibbles.map(n => {
return n.toString(2).padStart(4, "0");
}).join(" ");
case "Bytes":
return bytes.map(b => {
return b.toString(2).padStart(8, "0");
}).join(" ");
case "Raw":
default:
return Utils.byteArrayToChars(bytes);
}
},
/**
* From BCD operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runFromBCD: function(input, args) {
const encoding = BCD.ENCODING_LOOKUP[args[0]],
packed = args[1],
signed = args[2],
inputFormat = args[3];
let nibbles = [],
output = "",
byteArray;
// Normalise the input
switch (inputFormat) {
case "Nibbles":
case "Bytes":
input = input.replace(/\s/g, "");
for (let i = 0; i < input.length; i += 4) {
nibbles.push(parseInt(input.substr(i, 4), 2));
}
break;
case "Raw":
default:
byteArray = Utils.strToByteArray(input);
byteArray.forEach(b => {
nibbles.push(b >>> 4);
nibbles.push(b & 15);
});
break;
}
if (!packed) {
// Discard each high nibble
for (let i = 0; i < nibbles.length; i++) {
nibbles.splice(i, 1);
}
}
if (signed) {
const sign = nibbles.pop();
if (sign === 13 ||
sign === 11) {
// Negative
output += "-";
}
}
nibbles.forEach(n => {
if (isNaN(n)) throw "Invalid input";
let val = encoding.indexOf(n);
if (val < 0) throw `Value ${Utils.bin(n, 4)} not in encoding scheme`;
output += val.toString();
});
return new BigNumber(output);
},
};
export default BCD;

View File

@@ -0,0 +1,51 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import bsonjs from "bson";
import OperationError from "../errors/OperationError";
/**
* BSON deserialise operation
*/
class BSONDeserialise extends Operation {
/**
* BSONDeserialise constructor
*/
constructor() {
super();
this.name = "BSON deserialise";
this.module = "BSON";
this.description = "BSON is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database. It is a binary form for representing simple data structures, associative arrays (called objects or documents in MongoDB), and various data types of specific interest to MongoDB. The name 'BSON' is based on the term JSON and stands for 'Binary JSON'.<br><br>Input data should be in a raw bytes format.";
this.infoURL = "https://wikipedia.org/wiki/BSON";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
if (!input.byteLength) return "";
const bson = new bsonjs();
try {
const data = bson.deserialize(new Buffer(input));
return JSON.stringify(data, null, 2);
} catch (err) {
throw new OperationError(err.toString());
}
}
}
export default BSONDeserialise;

View File

@@ -0,0 +1,51 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import bsonjs from "bson";
import OperationError from "../errors/OperationError";
/**
* BSON serialise operation
*/
class BSONSerialise extends Operation {
/**
* BSONSerialise constructor
*/
constructor() {
super();
this.name = "BSON serialise";
this.module = "BSON";
this.description = "BSON is a computer data interchange format used mainly as a data storage and network transfer format in the MongoDB database. It is a binary form for representing simple data structures, associative arrays (called objects or documents in MongoDB), and various data types of specific interest to MongoDB. The name 'BSON' is based on the term JSON and stands for 'Binary JSON'.<br><br>Input data should be valid JSON.";
this.infoURL = "https://wikipedia.org/wiki/BSON";
this.inputType = "string";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
if (!input) return new ArrayBuffer();
const bson = new bsonjs();
try {
const data = JSON.parse(input);
return bson.serialize(data).buffer;
} catch (err) {
throw new OperationError(err.toString());
}
}
}
export default BSONSerialise;

View File

@@ -1,68 +0,0 @@
import BigNumber from "bignumber.js";
/**
* Numerical base operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Base = {
/**
* @constant
* @default
*/
DEFAULT_RADIX: 36,
/**
* To Base operation.
*
* @param {BigNumber} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
if (!input) {
throw ("Error: Input must be a number");
}
const radix = args[0] || Base.DEFAULT_RADIX;
if (radix < 2 || radix > 36) {
throw "Error: Radix argument must be between 2 and 36";
}
return input.toString(radix);
},
/**
* From Base operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {BigNumber}
*/
runFrom: function(input, args) {
const radix = args[0] || Base.DEFAULT_RADIX;
if (radix < 2 || radix > 36) {
throw "Error: Radix argument must be between 2 and 36";
}
let number = input.replace(/\s/g, "").split("."),
result = new BigNumber(number[0], radix) || 0;
if (number.length === 1) return result;
// Fractional part
for (let i = 0; i < number[1].length; i++) {
const digit = new BigNumber(number[1][i], radix);
result += digit.div(Math.pow(radix, i+1));
}
return result;
},
};
export default Base;

View File

@@ -1,137 +0,0 @@
import Utils from "../Utils.js";
/**
* Base58 operations.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*
* @namespace
*/
const Base58 = {
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{
name: "Bitcoin",
value: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",
},
{
name: "Ripple",
value: "rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz",
},
],
/**
* @constant
* @default
*/
REMOVE_NON_ALPH_CHARS: true,
/**
* To Base58 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
let alphabet = args[0] || Base58.ALPHABET_OPTIONS[0].value,
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw ("Error: alphabet must be of length 58");
}
if (input.length === 0) return "";
input.forEach(function(b) {
let carry = (result[0] << 8) + b;
result[0] = carry % 58;
carry = (carry / 58) | 0;
for (let i = 1; i < result.length; i++) {
carry += result[i] << 8;
result[i] = carry % 58;
carry = (carry / 58) | 0;
}
while (carry > 0) {
result.push(carry % 58);
carry = (carry / 58) | 0;
}
});
result = result.map(function(b) {
return alphabet[b];
}).reverse().join("");
while (result.length < input.length) {
result = alphabet[0] + result;
}
return result;
},
/**
* From Base58 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base58.ALPHABET_OPTIONS[0].value,
removeNonAlphaChars = args[1] === undefined ? true : args[1],
result = [0];
alphabet = Utils.expandAlphRange(alphabet).join("");
if (alphabet.length !== 58 ||
[].unique.call(alphabet).length !== 58) {
throw ("Alphabet must be of length 58");
}
if (input.length === 0) return [];
[].forEach.call(input, function(c, charIndex) {
const index = alphabet.indexOf(c);
if (index === -1) {
if (removeNonAlphaChars) {
return;
} else {
throw ("Char '" + c + "' at position " + charIndex + " not in alphabet");
}
}
let carry = result[0] * 58 + index;
result[0] = carry & 0xFF;
carry = carry >> 8;
for (let i = 1; i < result.length; i++) {
carry += result[i] * 58;
result[i] = carry & 0xFF;
carry = carry >> 8;
}
while (carry > 0) {
result.push(carry & 0xFF);
carry = carry >> 8;
}
});
return result.reverse();
},
};
export default Base58;

View File

@@ -1,347 +0,0 @@
import Utils from "../Utils.js";
/**
* Base64 operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const Base64 = {
/**
* @constant
* @default
*/
ALPHABET: "A-Za-z0-9+/=",
/**
* @constant
* @default
*/
ALPHABET_OPTIONS: [
{name: "Standard: A-Za-z0-9+/=", value: "A-Za-z0-9+/="},
{name: "URL safe: A-Za-z0-9-_", value: "A-Za-z0-9-_"},
{name: "Filename safe: A-Za-z0-9+-=", value: "A-Za-z0-9+\\-="},
{name: "itoa64: ./0-9A-Za-z=", value: "./0-9A-Za-z="},
{name: "XML: A-Za-z0-9_.", value: "A-Za-z0-9_."},
{name: "y64: A-Za-z0-9._-", value: "A-Za-z0-9._-"},
{name: "z64: 0-9a-zA-Z+/=", value: "0-9a-zA-Z+/="},
{name: "Radix-64: 0-9A-Za-z+/=", value: "0-9A-Za-z+/="},
{name: "Uuencoding: [space]-_", value: " -_"},
{name: "Xxencoding: +-0-9A-Za-z", value: "+\\-0-9A-Za-z"},
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
],
/**
* To Base64 operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runTo: function(input, args) {
const alphabet = args[0] || Base64.ALPHABET;
return Utils.toBase64(new Uint8Array(input), alphabet);
},
/**
* @constant
* @default
*/
REMOVE_NON_ALPH_CHARS: true,
/**
* From Base64 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom: function(input, args) {
let alphabet = args[0] || Base64.ALPHABET,
removeNonAlphChars = args[1];
return Utils.fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
},
/**
* @constant
* @default
*/
BASE32_ALPHABET: "A-Z2-7=",
/**
* To Base32 operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runTo32: function(input, args) {
if (!input) return "";
let alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
output = "",
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
while (i < input.length) {
chr1 = input[i++];
chr2 = input[i++];
chr3 = input[i++];
chr4 = input[i++];
chr5 = input[i++];
enc1 = chr1 >> 3;
enc2 = ((chr1 & 7) << 2) | (chr2 >> 6);
enc3 = (chr2 >> 1) & 31;
enc4 = ((chr2 & 1) << 4) | (chr3 >> 4);
enc5 = ((chr3 & 15) << 1) | (chr4 >> 7);
enc6 = (chr4 >> 2) & 31;
enc7 = ((chr4 & 3) << 3) | (chr5 >> 5);
enc8 = chr5 & 31;
if (isNaN(chr2)) {
enc3 = enc4 = enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr3)) {
enc5 = enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr4)) {
enc6 = enc7 = enc8 = 32;
} else if (isNaN(chr5)) {
enc8 = 32;
}
output += alphabet.charAt(enc1) + alphabet.charAt(enc2) + alphabet.charAt(enc3) +
alphabet.charAt(enc4) + alphabet.charAt(enc5) + alphabet.charAt(enc6) +
alphabet.charAt(enc7) + alphabet.charAt(enc8);
}
return output;
},
/**
* From Base32 operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFrom32: function(input, args) {
if (!input) return [];
let alphabet = args[0] ?
Utils.expandAlphRange(args[0]).join("") : "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",
removeNonAlphChars = args[0];
let output = [],
chr1, chr2, chr3, chr4, chr5,
enc1, enc2, enc3, enc4, enc5, enc6, enc7, enc8,
i = 0;
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[\]\\\-^]/g, "\\$&") + "]", "g");
input = input.replace(re, "");
}
while (i < input.length) {
enc1 = alphabet.indexOf(input.charAt(i++));
enc2 = alphabet.indexOf(input.charAt(i++) || "=");
enc3 = alphabet.indexOf(input.charAt(i++) || "=");
enc4 = alphabet.indexOf(input.charAt(i++) || "=");
enc5 = alphabet.indexOf(input.charAt(i++) || "=");
enc6 = alphabet.indexOf(input.charAt(i++) || "=");
enc7 = alphabet.indexOf(input.charAt(i++) || "=");
enc8 = alphabet.indexOf(input.charAt(i++) || "=");
chr1 = (enc1 << 3) | (enc2 >> 2);
chr2 = ((enc2 & 3) << 6) | (enc3 << 1) | (enc4 >> 4);
chr3 = ((enc4 & 15) << 4) | (enc5 >> 1);
chr4 = ((enc5 & 1) << 7) | (enc6 << 2) | (enc7 >> 3);
chr5 = ((enc7 & 7) << 5) | enc8;
output.push(chr1);
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
}
return output;
},
/**
* @constant
* @default
*/
SHOW_IN_BINARY: false,
/**
* @constant
* @default
*/
OFFSETS_SHOW_VARIABLE: true,
/**
* Show Base64 offsets operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {html}
*/
runOffsets: function(input, args) {
let alphabet = args[0] || Base64.ALPHABET,
showVariable = args[1],
offset0 = Utils.toBase64(input, alphabet),
offset1 = Utils.toBase64([0].concat(input), alphabet),
offset2 = Utils.toBase64([0, 0].concat(input), alphabet),
len0 = offset0.indexOf("="),
len1 = offset1.indexOf("="),
len2 = offset2.indexOf("="),
script = "<script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>",
staticSection = "",
padding = "";
if (input.length < 1) {
return "Please enter a string.";
}
// Highlight offset 0
if (len0 % 4 === 2) {
staticSection = offset0.slice(0, -3);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else {
staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset0 = staticSection;
}
// Highlight offset 1
padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2);
if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else {
staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset1 = staticSection;
}
// Highlight offset 2
padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3);
if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" +
"<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else {
staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2)) + "'>" +
staticSection + "</span>";
}
if (!showVariable) {
offset2 = staticSection;
}
return (showVariable ? "Characters highlighted in <span class='hl5'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hl3'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 +
"\nOffset 1: " + offset1 +
"\nOffset 2: " + offset2 +
script :
offset0 + "\n" + offset1 + "\n" + offset2);
},
/**
* Highlight to Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3 * 4);
pos[0].end = Math.ceil(pos[0].end / 3 * 4);
return pos;
},
/**
* Highlight from Base64
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
pos[0].start = Math.ceil(pos[0].start / 4 * 3);
pos[0].end = Math.floor(pos[0].end / 4 * 3);
return pos;
},
};
export default Base64;

View File

@@ -0,0 +1,55 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import bcrypt from "bcryptjs";
/**
* Bcrypt operation
*/
class Bcrypt extends Operation {
/**
* Bcrypt constructor
*/
constructor() {
super();
this.name = "Bcrypt";
this.module = "Hashing";
this.description = "bcrypt is a password hashing function designed by Niels Provos and David Mazi\xe8res, based on the Blowfish cipher, and presented at USENIX in 1999. Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count (rounds) can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.<br><br>Enter the password in the input to generate its hash.";
this.infoURL = "https://wikipedia.org/wiki/Bcrypt";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Rounds",
"type": "number",
"value": 10
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const rounds = args[0];
const salt = await bcrypt.genSalt(rounds);
return await bcrypt.hash(input, salt, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});
}
}
export default Bcrypt;

View File

@@ -0,0 +1,56 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import bcrypt from "bcryptjs";
/**
* Bcrypt compare operation
*/
class BcryptCompare extends Operation {
/**
* BcryptCompare constructor
*/
constructor() {
super();
this.name = "Bcrypt compare";
this.module = "Hashing";
this.description = "Tests whether the input matches the given bcrypt hash. To test multiple possible passwords, use the 'Fork' operation.";
this.infoURL = "https://wikipedia.org/wiki/Bcrypt";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Hash",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
const hash = args[0];
const match = await bcrypt.compare(input, hash, null, p => {
// Progress callback
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage(`Progress: ${(p * 100).toFixed(0)}%`);
});
return match ? "Match: " + input : "No match";
}
}
export default BcryptCompare;

View File

@@ -0,0 +1,49 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import bcrypt from "bcryptjs";
/**
* Bcrypt parse operation
*/
class BcryptParse extends Operation {
/**
* BcryptParse constructor
*/
constructor() {
super();
this.name = "Bcrypt parse";
this.module = "Hashing";
this.description = "Parses a bcrypt hash to determine the number of rounds used, the salt, and the password hash.";
this.infoURL = "https://wikipedia.org/wiki/Bcrypt";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
async run(input, args) {
try {
return `Rounds: ${bcrypt.getRounds(input)}
Salt: ${bcrypt.getSalt(input)}
Password hash: ${input.split(bcrypt.getSalt(input))[1]}
Full hash: ${input}`;
} catch (err) {
throw new OperationError("Error: " + err.toString());
}
}
}
export default BcryptParse;

View File

@@ -0,0 +1,125 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import { genPolybiusSquare } from "../lib/Ciphers";
import OperationError from "../errors/OperationError";
/**
* Bifid Cipher Decode operation
*/
class BifidCipherDecode extends Operation {
/**
* BifidCipherDecode constructor
*/
constructor() {
super();
this.name = "Bifid Cipher Decode";
this.module = "Ciphers";
this.description = "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.";
this.infoURL = "https://wikipedia.org/wiki/Bifid_cipher";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Keyword",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if invalid key
*/
run(input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ",
structure = [];
let output = "",
count = 0,
trans = "";
if (!/^[A-Z]+$/.test(keywordStr) && keyword.length > 0)
throw new OperationError("The key must consist only of letters in the English alphabet");
const polybius = genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach((letter) => {
const alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0;
let polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
trans += `${i}${polInd}`;
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
structure.forEach(pos => {
if (typeof pos === "boolean") {
const coords = [trans[count], trans[count+trans.length/2]];
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
}
/**
* Highlight Bifid Cipher Decode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bifid Cipher Decode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BifidCipherDecode;

View File

@@ -0,0 +1,130 @@
/**
* @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Operation from "../Operation";
import OperationError from "../errors/OperationError";
import { genPolybiusSquare } from "../lib/Ciphers";
/**
* Bifid Cipher Encode operation
*/
class BifidCipherEncode extends Operation {
/**
* BifidCipherEncode constructor
*/
constructor() {
super();
this.name = "Bifid Cipher Encode";
this.module = "Ciphers";
this.description = "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.";
this.infoURL = "https://wikipedia.org/wiki/Bifid_cipher";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Keyword",
"type": "string",
"value": ""
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*
* @throws {OperationError} if key is invalid
*/
run(input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ",
xCo = [],
yCo = [],
structure = [];
let output = "",
count = 0;
if (!/^[A-Z]+$/.test(keywordStr) && keyword.length > 0)
throw new OperationError("The key must consist only of letters in the English alphabet");
const polybius = genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach(letter => {
const alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0;
let polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
xCo.push(polInd);
yCo.push(i);
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
const trans = `${yCo.join("")}${xCo.join("")}`;
structure.forEach(pos => {
if (typeof pos === "boolean") {
const coords = trans.substr(2*count, 2).split("");
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
}
/**
* Highlight Bifid Cipher Encode
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bifid Cipher Encode in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BifidCipherEncode;

View File

@@ -0,0 +1,76 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Bit shift left operation
*/
class BitShiftLeft extends Operation {
/**
* BitShiftLeft constructor
*/
constructor() {
super();
this.name = "Bit shift left";
this.module = "Default";
this.description = "Shifts the bits in each byte towards the left by the specified amount.";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Amount",
"type": "number",
"value": 1
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const amount = args[0];
return input.map(b => {
return (b << amount) & 0xff;
});
}
/**
* Highlight Bit shift left
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bit shift left in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BitShiftLeft;

View File

@@ -0,0 +1,83 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
/**
* Bit shift right operation
*/
class BitShiftRight extends Operation {
/**
* BitShiftRight constructor
*/
constructor() {
super();
this.name = "Bit shift right";
this.module = "Default";
this.description = "Shifts the bits in each byte towards the right by the specified amount.<br><br><i>Logical shifts</i> replace the leftmost bits with zeros.<br><i>Arithmetic shifts</i> preserve the most significant bit (MSB) of the original byte keeping the sign the same (positive or negative).";
this.infoURL = "https://wikipedia.org/wiki/Bitwise_operation#Bit_shifts";
this.inputType = "byteArray";
this.outputType = "byteArray";
this.args = [
{
"name": "Amount",
"type": "number",
"value": 1
},
{
"name": "Type",
"type": "option",
"value": ["Logical shift", "Arithmetic shift"]
}
];
}
/**
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
const amount = args[0],
type = args[1],
mask = type === "Logical shift" ? 0 : 0x80;
return input.map(b => {
return (b >>> amount) ^ (b & mask);
});
}
/**
* Highlight Bit shift right
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlight(pos, args) {
return pos;
}
/**
* Highlight Bit shift right in reverse
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightReverse(pos, args) {
return pos;
}
}
export default BitShiftRight;

View File

@@ -1,368 +0,0 @@
import Utils from "../Utils.js";
/**
* Bitwise operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const BitwiseOp = {
/**
* Runs bitwise operations across the input data.
*
* @private
* @param {byteArray} input
* @param {byteArray} key
* @param {function} func - The bitwise calculation to carry out
* @param {boolean} nullPreserving
* @param {string} scheme
* @returns {byteArray}
*/
_bitOp: function (input, key, func, nullPreserving, scheme) {
if (!key || !key.length) key = [0];
let result = [],
x = null,
k = null,
o = null;
for (let i = 0; i < input.length; i++) {
k = key[i % key.length];
o = input[i];
x = nullPreserving && (o === 0 || o === k) ? o : func(o, k);
result.push(x);
if (scheme &&
scheme !== "Standard" &&
!(nullPreserving && (o === 0 || o === k))) {
switch (scheme) {
case "Input differential":
key[i % key.length] = x;
break;
case "Output differential":
key[i % key.length] = o;
break;
}
}
}
return result;
},
/**
* @constant
* @default
*/
XOR_PRESERVE_NULLS: false,
/**
* @constant
* @default
*/
XOR_SCHEME: ["Standard", "Input differential", "Output differential"],
/**
* @constant
* @default
*/
KEY_FORMAT: ["Hex", "Base64", "UTF8", "Latin1"],
/**
* XOR operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runXor: function (input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option),
scheme = args[1],
nullPreserving = args[2];
return BitwiseOp._bitOp(input, key, BitwiseOp._xor, nullPreserving, scheme);
},
/**
* @constant
* @default
*/
XOR_BRUTE_KEY_LENGTH: 1,
/**
* @constant
* @default
*/
XOR_BRUTE_SAMPLE_LENGTH: 100,
/**
* @constant
* @default
*/
XOR_BRUTE_SAMPLE_OFFSET: 0,
/**
* @constant
* @default
*/
XOR_BRUTE_PRINT_KEY: true,
/**
* @constant
* @default
*/
XOR_BRUTE_OUTPUT_HEX: false,
/**
* XOR Brute Force operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runXorBrute: function (input, args) {
const keyLength = args[0],
sampleLength = args[1],
sampleOffset = args[2],
scheme = args[3],
nullPreserving = args[4],
printKey = args[5],
outputHex = args[6],
crib = args[7].toLowerCase();
let output = [],
result,
resultUtf8,
record = "";
input = input.slice(sampleOffset, sampleOffset + sampleLength);
if (ENVIRONMENT_IS_WORKER())
self.sendStatusMessage("Calculating " + Math.pow(256, keyLength) + " values...");
/**
* Converts an integer to an array of bytes expressing that number.
*
* @param {number} int
* @param {number} len - Length of the resulting array
* @returns {array}
*/
const intToByteArray = (int, len) => {
let res = Array(len).fill(0);
for (let i = len - 1; i >= 0; i--) {
res[i] = int & 0xff;
int = int >>> 8;
}
return res;
};
for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) {
if (key % 10000 === 0 && ENVIRONMENT_IS_WORKER()) {
self.sendStatusMessage("Calculating " + l + " values... " + Math.floor(key / l * 100) + "%");
}
result = BitwiseOp._bitOp(input, intToByteArray(key, keyLength), BitwiseOp._xor, nullPreserving, scheme);
resultUtf8 = Utils.byteArrayToUtf8(result);
record = "";
if (crib && resultUtf8.toLowerCase().indexOf(crib) < 0) continue;
if (printKey) record += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
if (outputHex) {
record += Utils.toHex(result);
} else {
record += Utils.printable(resultUtf8, false);
}
output.push(record);
}
return output.join("\n");
},
/**
* NOT operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runNot: function (input, args) {
return BitwiseOp._bitOp(input, null, BitwiseOp._not);
},
/**
* AND operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runAnd: function (input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._and);
},
/**
* OR operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runOr: function (input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._or);
},
/**
* ADD operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runAdd: function (input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._add);
},
/**
* SUB operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runSub: function (input, args) {
const key = Utils.convertToByteArray(args[0].string || "", args[0].option);
return BitwiseOp._bitOp(input, key, BitwiseOp._sub);
},
/**
* Bit shift left operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runBitShiftLeft: function(input, args) {
const amount = args[0];
return input.map(b => {
return (b << amount) & 0xff;
});
},
/**
* @constant
* @default
*/
BIT_SHIFT_TYPE: ["Logical shift", "Arithmetic shift"],
/**
* Bit shift right operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {byteArray}
*/
runBitShiftRight: function(input, args) {
const amount = args[0],
type = args[1],
mask = type === "Logical shift" ? 0 : 0x80;
return input.map(b => {
return (b >>> amount) ^ (b & mask);
});
},
/**
* XOR bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_xor: function (operand, key) {
return operand ^ key;
},
/**
* NOT bitwise calculation.
*
* @private
* @param {number} operand
* @returns {number}
*/
_not: function (operand, _) {
return ~operand & 0xff;
},
/**
* AND bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_and: function (operand, key) {
return operand & key;
},
/**
* OR bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_or: function (operand, key) {
return operand | key;
},
/**
* ADD bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_add: function (operand, key) {
return (operand + key) % 256;
},
/**
* SUB bitwise calculation.
*
* @private
* @param {number} operand
* @param {number} key
* @returns {number}
*/
_sub: function (operand, key) {
const result = operand - key;
return (result < 0) ? 256 + result : result;
},
};
export default BitwiseOp;

View File

@@ -0,0 +1,101 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import { Blowfish } from "../vendor/Blowfish";
import { toBase64 } from "../lib/Base64";
import { toHexFast } from "../lib/Hex";
/**
* Lookup table for Blowfish output types.
*/
const BLOWFISH_OUTPUT_TYPE_LOOKUP = {
Base64: 0, Hex: 1, String: 2, Raw: 3
};
/**
* Lookup table for Blowfish modes.
*/
const BLOWFISH_MODE_LOOKUP = {
ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
};
/**
* Blowfish Decrypt operation
*/
class BlowfishDecrypt extends Operation {
/**
* BlowfishDecrypt constructor
*/
constructor() {
super();
this.name = "Blowfish Decrypt";
this.module = "Ciphers";
this.description = "Blowfish is a symmetric-key block cipher designed in 1993 by Bruce Schneier and included in a large number of cipher suites and encryption products. AES now receives more attention.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.";
this.infoURL = "https://wikipedia.org/wiki/Blowfish_(cipher)";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "IV",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "PCBC", "CFB", "OFB", "CTR", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Hex", "Base64", "Raw"]
},
{
"name": "Output",
"type": "option",
"value": ["Raw", "Hex"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType] = args;
if (key.length === 0) throw new OperationError("Enter a key");
input = inputType === "Raw" ? Utils.strToByteArray(input) : input;
Blowfish.setIV(toBase64(iv), 0);
const result = Blowfish.decrypt(input, key, {
outputType: BLOWFISH_OUTPUT_TYPE_LOOKUP[inputType], // This actually means inputType. The library is weird.
cipherMode: BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Hex" ? toHexFast(Utils.strToByteArray(result)) : result;
}
}
export default BlowfishDecrypt;

View File

@@ -0,0 +1,102 @@
/**
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*/
import Operation from "../Operation";
import Utils from "../Utils";
import OperationError from "../errors/OperationError";
import { Blowfish } from "../vendor/Blowfish";
import { toBase64 } from "../lib/Base64";
/**
* Lookup table for Blowfish output types.
*/
const BLOWFISH_OUTPUT_TYPE_LOOKUP = {
Base64: 0, Hex: 1, String: 2, Raw: 3
};
/**
* Lookup table for Blowfish modes.
*/
const BLOWFISH_MODE_LOOKUP = {
ECB: 0, CBC: 1, PCBC: 2, CFB: 3, OFB: 4, CTR: 5
};
/**
* Blowfish Encrypt operation
*/
class BlowfishEncrypt extends Operation {
/**
* BlowfishEncrypt constructor
*/
constructor() {
super();
this.name = "Blowfish Encrypt";
this.module = "Ciphers";
this.description = "Blowfish is a symmetric-key block cipher designed in 1993 by Bruce Schneier and included in a large number of cipher suites and encryption products. AES now receives more attention.<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.";
this.infoURL = "https://wikipedia.org/wiki/Blowfish_(cipher)";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
"name": "Key",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "IV",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "PCBC", "CFB", "OFB", "CTR", "ECB"]
},
{
"name": "Input",
"type": "option",
"value": ["Raw", "Hex"]
},
{
"name": "Output",
"type": "option",
"value": ["Hex", "Base64", "Raw"]
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteArray(args[1].string, args[1].option),
[,, mode, inputType, outputType] = args;
if (key.length === 0) throw new OperationError("Enter a key");
input = Utils.convertToByteString(input, inputType);
Blowfish.setIV(toBase64(iv), 0);
const enc = Blowfish.encrypt(input, key, {
outputType: BLOWFISH_OUTPUT_TYPE_LOOKUP[outputType],
cipherMode: BLOWFISH_MODE_LOOKUP[mode]
});
return outputType === "Raw" ? Utils.byteArrayToChars(enc) : enc;
}
}
export default BlowfishEncrypt;

View File

@@ -1,423 +0,0 @@
import Utils from "../Utils.js";
/**
* Byte representation operations.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @namespace
*/
const ByteRepr = {
/**
* @constant
* @default
*/
DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF"],
/**
* @constant
* @default
*/
HEX_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "0x", "\\x", "None"],
/**
* @constant
* @default
*/
BIN_DELIM_OPTIONS: ["Space", "Comma", "Semi-colon", "Colon", "Line feed", "CRLF", "None"],
/**
* To Hex operation.
*
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
runToHex: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
return Utils.toHex(new Uint8Array(input), delim, 2);
},
/**
* From Hex operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromHex: function(input, args) {
const delim = args[0] || "Space";
return Utils.fromHex(input, delim, 2);
},
/**
* To Octal operation.
*
* @author Matt C [matt@artemisbot.uk]
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToOct: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
return input.map(val => val.toString(8)).join(delim);
},
/**
* From Octal operation.
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromOct: function(input, args) {
const delim = Utils.charRep[args[0] || "Space"];
if (input.length === 0) return [];
return input.split(delim).map(val => parseInt(val, 8));
},
/**
* @constant
* @default
*/
CHARCODE_BASE: 16,
/**
* To Charcode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runToCharcode: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
base = args[1],
output = "",
padding = 2,
ordinal;
if (base < 2 || base > 36) {
throw "Error: Base argument must be between 2 and 36";
}
const charcode = Utils.strToCharcode(input);
for (let i = 0; i < charcode.length; i++) {
ordinal = charcode[i];
if (base === 16) {
if (ordinal < 256) padding = 2;
else if (ordinal < 65536) padding = 4;
else if (ordinal < 16777216) padding = 6;
else if (ordinal < 4294967296) padding = 8;
else padding = 2;
if (padding > 2 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
output += Utils.hex(ordinal, padding) + delim;
} else {
if (ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
output += ordinal.toString(base) + delim;
}
}
return output.slice(0, -delim.length);
},
/**
* From Charcode operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromCharcode: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
base = args[1],
bites = input.split(delim),
i = 0;
if (base < 2 || base > 36) {
throw "Error: Base argument must be between 2 and 36";
}
if (base !== 16 && ENVIRONMENT_IS_WORKER()) self.setOption("attemptHighlight", false);
// Split into groups of 2 if the whole string is concatenated and
// too long to be a single character
if (bites.length === 1 && input.length > 17) {
bites = [];
for (i = 0; i < input.length; i += 2) {
bites.push(input.slice(i, i+2));
}
}
let latin1 = "";
for (i = 0; i < bites.length; i++) {
latin1 += Utils.chr(parseInt(bites[i], base));
}
return Utils.strToByteArray(latin1);
},
/**
* Highlight to hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightTo: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
len = delim === "\r\n" ? 1 : delim.length;
pos[0].start = pos[0].start * (2 + len);
pos[0].end = pos[0].end * (2 + len) - len;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
pos[0].start += 2;
pos[0].end += 2;
}
return pos;
},
/**
* Highlight from hex
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFrom: function(pos, args) {
let delim = Utils.charRep[args[0] || "Space"],
len = delim === "\r\n" ? 1 : delim.length,
width = len + 2;
// 0x and \x are added to the beginning if they are selected, so increment the positions accordingly
if (delim === "0x" || delim === "\\x") {
if (pos[0].start > 1) pos[0].start -= 2;
else pos[0].start = 0;
if (pos[0].end > 1) pos[0].end -= 2;
else pos[0].end = 0;
}
pos[0].start = pos[0].start === 0 ? 0 : Math.round(pos[0].start / width);
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / width);
return pos;
},
/**
* To Decimal operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToDecimal: function(input, args) {
const delim = Utils.charRep[args[0]];
return input.join(delim);
},
/**
* From Decimal operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromDecimal: function(input, args) {
const delim = Utils.charRep[args[0]];
let byteStr = input.split(delim), output = [];
if (byteStr[byteStr.length-1] === "")
byteStr = byteStr.slice(0, byteStr.length-1);
for (let i = 0; i < byteStr.length; i++) {
output[i] = parseInt(byteStr[i], 10);
}
return output;
},
/**
* To Binary operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToBinary: function(input, args) {
let delim = Utils.charRep[args[0] || "Space"],
output = "",
padding = 8;
for (let i = 0; i < input.length; i++) {
output += input[i].toString(2).padStart(padding, "0") + delim;
}
if (delim.length) {
return output.slice(0, -delim.length);
} else {
return output;
}
},
/**
* From Binary operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromBinary: function(input, args) {
const delimRegex = Utils.regexRep[args[0] || "Space"];
input = input.replace(delimRegex, "");
const output = [];
const byteLen = 8;
for (let i = 0; i < input.length; i += byteLen) {
output.push(parseInt(input.substr(i, byteLen), 2));
}
return output;
},
/**
* Highlight to binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightToBinary: function(pos, args) {
const delim = Utils.charRep[args[0] || "Space"];
pos[0].start = pos[0].start * (8 + delim.length);
pos[0].end = pos[0].end * (8 + delim.length) - delim.length;
return pos;
},
/**
* Highlight from binary
*
* @param {Object[]} pos
* @param {number} pos[].start
* @param {number} pos[].end
* @param {Object[]} args
* @returns {Object[]} pos
*/
highlightFromBinary: function(pos, args) {
const delim = Utils.charRep[args[0] || "Space"];
pos[0].start = pos[0].start === 0 ? 0 : Math.floor(pos[0].start / (8 + delim.length));
pos[0].end = pos[0].end === 0 ? 0 : Math.ceil(pos[0].end / (8 + delim.length));
return pos;
},
/**
* @constant
* @default
*/
HEX_CONTENT_CONVERT_WHICH: ["Only special chars", "Only special chars including spaces", "All chars"],
/**
* @constant
* @default
*/
HEX_CONTENT_SPACES_BETWEEN_BYTES: false,
/**
* To Hex Content operation.
*
* @param {byteArray} input
* @param {Object[]} args
* @returns {string}
*/
runToHexContent: function(input, args) {
const convert = args[0];
const spaces = args[1];
if (convert === "All chars") {
let result = "|" + Utils.toHex(input) + "|";
if (!spaces) result = result.replace(/ /g, "");
return result;
}
let output = "",
inHex = false,
convertSpaces = convert === "Only special chars including spaces",
b;
for (let i = 0; i < input.length; i++) {
b = input[i];
if ((b === 32 && convertSpaces) || (b < 48 && b !== 32) || (b > 57 && b < 65) || (b > 90 && b < 97) || b > 122) {
if (!inHex) {
output += "|";
inHex = true;
} else if (spaces) output += " ";
output += Utils.toHex([b]);
} else {
if (inHex) {
output += "|";
inHex = false;
}
output += Utils.chr(input[i]);
}
}
if (inHex) output += "|";
return output;
},
/**
* From Hex Content operation.
*
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
runFromHexContent: function(input, args) {
const regex = /\|([a-f\d ]{2,})\|/gi;
let output = [], m, i = 0;
while ((m = regex.exec(input))) {
// Add up to match
for (; i < m.index;)
output.push(Utils.ord(input[i++]));
// Add match
const bytes = Utils.fromHex(m[1]);
if (bytes) {
for (let a = 0; a < bytes.length;)
output.push(bytes[a++]);
} else {
// Not valid hex, print as normal
for (; i < regex.lastIndex;)
output.push(Utils.ord(input[i++]));
}
i = regex.lastIndex;
}
// Add all after final match
for (; i < input.length;)
output.push(Utils.ord(input[i++]));
return output;
},
};
export default ByteRepr;

Some files were not shown because too many files have changed in this diff Show More