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

Compare commits

...

570 Commits

Author SHA1 Message Date
n1474335
ed8bd34915 9.48.0 2022-10-15 00:15:49 +01:00
n1474335
5c72791279 Updated CHANGELOG 2022-10-15 00:15:39 +01:00
n1474335
142f91425c Added 'LM Hash' opertaion 2022-10-15 00:13:39 +01:00
n1474335
d6344760ec Merge branch 'master' of https://github.com/brun0ne/CyberChef 2022-10-14 18:45:47 +01:00
n1474335
64c009f266 9.47.5 2022-10-14 16:28:10 +01:00
n1474335
a73decc792 Merge branch 'master' of https://github.com/gariev/CyberChef 2022-10-14 16:26:08 +01:00
n1474335
f332ca4617 9.47.4 2022-10-14 16:24:33 +01:00
n1474335
937791d33d Merge branch 'jwt-magic' of https://github.com/whs/CyberChef 2022-10-14 16:24:19 +01:00
n1474335
a63a130723 Merge branch 'ci/actions' of https://github.com/Fdawgs/CyberChef 2022-10-14 16:21:53 +01:00
n1474335
0f1175bf15 9.47.3 2022-10-14 16:20:38 +01:00
n1474335
e4db23f857 Removed extra comment from Raw Inflate 2022-10-14 16:20:34 +01:00
n1474335
32e7dd030e Merge branch 'master' of https://github.com/XlogicX/CyberChef 2022-10-14 16:19:32 +01:00
n1474335
e33950961e 9.47.2 2022-10-14 16:10:19 +01:00
n1474335
5d65cb419f Tidied up 'Generate all hashes' operation 2022-10-14 16:10:01 +01:00
n1474335
536053d5f9 Merge branch 'hash' of https://github.com/jl2168/CyberChef 2022-10-14 14:53:00 +01:00
n1474335
04ef095b88 9.47.1 2022-10-14 14:47:30 +01:00
n1474335
66277cd71f Added more DNS request types 2022-10-14 14:47:19 +01:00
n1474335
58f01d0464 Merge branch 'PTR-option' of https://github.com/CyberGoat/CyberChef 2022-10-14 14:14:17 +01:00
n1474335
9ba9c56361 9.47.0 2022-10-14 14:07:47 +01:00
n1474335
11902e3220 Updated CHANGELOG 2022-10-14 14:07:42 +01:00
n1474335
c3f79c4b2c Merge branch 'feature/lzma' of https://github.com/mattnotmitt/CyberChef 2022-10-14 14:03:57 +01:00
n1474335
576905e8b8 9.46.7 2022-10-14 14:01:05 +01:00
n1474335
77a3b91afe Merge branch 'ssh-ed25519' of https://github.com/cplussharp/CyberChef 2022-10-14 14:00:03 +01:00
n1474335
40b58aa144 9.46.6 2022-10-14 13:57:35 +01:00
n1474335
d5bcdc8eed Dependency fixes 2022-10-14 13:57:00 +01:00
Manatsawin Hanmongkolchai
674649ca7f Added checks to JWTDecode operation 2022-10-09 14:57:02 +07:00
XlogicX
1a9a070c3b Removal of unnecessary error condition
This situation occurs because the dependancy (zlibjs/bin/rawinflate.min.js) doesn't do a sanity check on distances going back farther than the current buffer.

For example:
DEFLATE data of '123' and then a length of 9 going back a distance of 6
ASCIIHEX: 333432869300

! infgen 3.0 output
!
last			! 1
fixed			! 01
literal '1		! 10000110
literal '2		! 01000110
literal '3		! 11000110
match 9 6		! 1 00100 1110000
infgen warning: distance too far back (6/3)
end			! 0000000
			! 0

We only have 3 characters, we shouldn't be able to seek 6 characters back. But rawinflate.min.js doesn't check for this like the infgen debug tool (and others) would. So CyberChef would happily provide this as the result:
123...123...
Where the dots are just nulls of likley empty memory preceding the actual buffer

So with the example in this source
// e.g. Input data of [8b, 1d, dc, 44]

last			! 1
fixed			! 01
literal ']		! 10110001
match 158 5		! 0 00100 11011 10000011
infgen warning: distance too far back (5/1)

This means we have a literal ']' and then we are asking for 158 more characters and to find them a distance of 5 back. This explains why the ']', why it repeats every 5, and why it is a length > 158.

This code should just be removed; it isn't justified. Being that this issue is a lack of sanity checking in a dependancy, and that this routine only catches the symptom of one of the nearly unlimited edge cases like this, AND it could filter out correct inputs, such as a recipe of this as input to RAWDEFLATE
]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX
Then getting an error with the INFLATE even though the input is actually valid.
2022-10-06 14:02:27 -04:00
XlogicX
32bee35f85 Removal of unnecessary error checking routine
This situation occurs because the dependancy (zlibjs/bin/rawinflate.min.js) doesn't do a sanity check on distances going back farther than the current buffer.

For example:
DEFLATE data of '123' and then a length of 9 going back a distance of 6
ASCIIHEX: 333432869300

! infgen 3.0 output
!
last			! 1
fixed			! 01
literal '1		! 10000110
literal '2		! 01000110
literal '3		! 11000110
match 9 6		! 1 00100 1110000
infgen warning: distance too far back (6/3)
end			! 0000000
			! 0

We only have 3 characters, we shouldn't be able to seek 6 characters back. But rawinflate.min.js doesn't check for this like the infgen debug tool (and others) would. So CyberChef would happily provide this as the result:
123...123...
Where the dots are just nulls of likley empty memory preceding the actual buffer

So with the example in this source
// e.g. Input data of [8b, 1d, dc, 44]

last			! 1
fixed			! 01
literal ']		! 10110001
match 158 5		! 0 00100 11011 10000011
infgen warning: distance too far back (5/1)

This means we have a literal ']' and then we are asking for 158 more characters and to find them a distance of 5 back. This explains why the ']', why it repeats every 5, and why it is a length > 158.

This code should just be removed; it isn't justified. Being that this issue is a lack of sanity checking in a dependancy, and that this routine only catches the symptom of one of the nearly unlimited edge cases like this, AND it could filter out correct inputs, such as a recipe of this as input to RAWDEFLATE
]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX[]OMG[]HAX
Then getting an error with the INFLATE even though the input is actually valid.
2022-10-06 13:53:32 -04:00
Frazer Smith
a68ce5a5af Update GitHub Actions 2022-10-06 13:58:01 +01:00
Igor Gariev
026e9ca9c3 Added escape sequence "\a" (audible bell, 0x07) to Utils.parseEscapedChars().
The sequece is part of C and C++ standard, as well as protocol buffer encoding.

- https://en.wikipedia.org/wiki/Escape_sequences_in_C
- https://en.cppreference.com/w/cpp/language/escape
- https://developers.google.com/protocol-buffers/docs/text-format-spec#string
2022-09-28 20:32:21 -07:00
BrunonDEV
f1ce67d79b Added NTLM operation
Hashing operation - MD4 on UTF16LE-encoded input
2022-09-27 23:13:22 +02:00
john19696
312be4772c rsdix 2022-09-23 11:38:15 +01:00
john19696
be97a0062e linted 2022-09-22 16:53:29 +01:00
john19696
00f0101723 author fix 2022-09-22 16:39:51 +01:00
john19696
f450240094 Parameterise All hashes 2022-09-22 16:30:36 +01:00
Ethan Block
a7b8378736 Adding PTR to possiable values for Resolver 2022-09-21 11:37:06 -04:00
Matt C
98a70c2dd2 Add tests and handle decompress returning string or array 2022-09-19 17:33:55 +01:00
Matt C
d502dd9857 Add LZMA Decompress operation 2022-09-19 14:24:09 +01:00
Matt C
1ec7033d46 Add LZMA Compress operation 2022-09-19 14:24:09 +01:00
Matt C
28ec56a27f Update libyara package to fix bug with compile messages and add support for console module 2022-09-18 16:11:04 +01:00
CPlusSharp
bf2afcd2ef Support Ed25519 SSH host key parsing 2022-09-18 12:47:55 +02:00
Matt C
8f710461da Update yara to 4.2.3 and fix output reading 0 matches 2022-09-17 23:48:11 +01:00
n1474335
c2cf535f88 Added node builder script to package.json 2022-09-16 14:37:31 +01:00
n1474335
ced9ab68fa 9.46.5 2022-09-16 14:16:42 +01:00
n1474335
cdb197a9c3 Reverted to local copies of Tesseract trainddata in order to remain self-contained. 2022-09-16 14:15:54 +01:00
Sean Marpo
c8eacb9942 Linting fixes 2022-09-09 14:45:06 -07:00
Sean Marpo
1c8e37cb64 Update tesseract, fix API for tesseract 3.0 2022-09-09 14:33:49 -07:00
n1474335
1b0ced9f9b 9.46.4 2022-09-09 21:23:09 +01:00
n1474335
7b245b084a Updated to Node v18 and removed node-sass dependency 2022-09-09 21:22:55 +01:00
n1474335
b00f64518f Merge branch 'nodejs18' of https://github.com/john19696/CyberChef 2022-09-09 20:55:18 +01:00
n1474335
c3434e894d 9.46.3 2022-09-09 20:53:37 +01:00
n1474335
dd66f728b3 Merge branch 'fix-protobuf-order' of https://github.com/oliverrahner/CyberChef 2022-09-09 20:52:36 +01:00
n1474335
e40142b8c5 9.46.2 2022-09-09 20:39:35 +01:00
n1474335
1dd1b839b8 Switched jsonpath library to jsonpath-plus. Fixes #1318 2022-09-09 20:39:28 +01:00
n1474335
d90d845f27 9.46.1 2022-09-09 16:51:38 +01:00
n1474335
8c9ad81039 Merge branch 'feat-primitive' of https://github.com/jeiea/CyberChef 2022-09-09 16:49:12 +01:00
n1474335
cef7a7b27d Lint 2022-09-09 16:44:41 +01:00
n1474335
3e715ef21a Merge branch 'fix-select' of https://github.com/IsSafrullah/CyberChef 2022-09-09 16:43:15 +01:00
n1474335
86b43b4ffa Updated README 2022-09-09 16:39:10 +01:00
IsSafrullah
65d883496b fix select when change theme 2022-09-06 03:52:42 +07:00
jeiea
69e59916e2 feat: support boolean and null in JSON to CSV 2022-08-17 02:12:39 +09:00
Philippe Arteau
475282984b Minor typos 2022-07-29 14:32:46 -04:00
Oliver Rahner
2f89130f41 fix protobuf field order 2022-07-21 16:36:15 +02:00
john19696
e9dd7eceb8 upgrade to nodejs v18 2022-07-14 14:27:59 +01:00
n1474335
037590f831 Updated CHANGELOG 2022-07-08 17:18:20 +01:00
n1474335
85496684d8 9.46.0 2022-07-08 17:17:23 +01:00
n1474335
4200ed4eb9 Tidied Cetacean ciphers 2022-07-08 17:16:35 +01:00
n1474335
6b16f11d3b Merge branch 'master' of https://github.com/valdelaseras/CyberChef 2022-07-08 17:02:06 +01:00
n1474335
683bd3e5db Updated CHANGELOG 2022-07-08 16:34:21 +01:00
n1474335
6a10e94bfd 9.45.0 2022-07-08 16:33:33 +01:00
n1474335
25086386c6 Tidied ROT8000 2022-07-08 16:33:16 +01:00
n1474335
d99ee32cc4 Merge branch 'ROT8000' of https://github.com/thomasleplus/CyberChef 2022-07-08 16:28:42 +01:00
n1474335
f1d318f229 Updated CHANGELOG 2022-07-08 16:25:59 +01:00
n1474335
a7fc455e05 9.44.0 2022-07-08 16:24:47 +01:00
n1474335
c02c4a72e4 Merge branch 'add-lz-string' of https://github.com/crespyl/CyberChef 2022-07-08 16:23:15 +01:00
n1474335
f97ce18ff9 Updated CHANGELOG 2022-07-08 16:03:42 +01:00
n1474335
dfd9afc2c4 9.43.0 2022-07-08 16:02:35 +01:00
n1474335
eb5663a1ed Tidied ROT brute forcing ops 2022-07-08 16:02:24 +01:00
n1474335
418a7962a5 Merge branch 'rot_bruteforce' of https://github.com/mikecat/CyberChef 2022-07-08 15:55:27 +01:00
n1474335
2ffce23c67 Updated CHANGELOG 2022-07-08 15:52:00 +01:00
n1474335
b828b50ccc 9.42.0 2022-07-08 15:47:42 +01:00
n1474335
a6aa40db97 Tidied LS47 operations 2022-07-08 15:47:35 +01:00
n1474335
45ede4beaf Merge branch 'LS47Cipher' of https://github.com/n1073645/CyberChef 2022-07-08 15:41:16 +01:00
n1474335
98a95c8bbf Updated CHANGELOG 2022-07-08 15:38:12 +01:00
n1474335
74bb8d92dc 9.41.0 2022-07-08 15:36:36 +01:00
n1474335
6cccc2c786 Tidied Caesar Box Cipher 2022-07-08 15:36:30 +01:00
n1474335
99a0a05947 Merge branch 'caesarBoxCipher' of https://github.com/n1073645/CyberChef 2022-07-08 15:32:56 +01:00
n1474335
94700dab89 Updated CHANGELOG 2022-07-08 15:28:39 +01:00
n1474335
c9d29c89bb 9.40.0 2022-07-08 15:27:01 +01:00
n1474335
7d4e554571 Tweaks to P-List Viewer operation 2022-07-08 15:26:33 +01:00
n1474335
2858a74cbf Merge branch 'plistViewer' of https://github.com/n1073645/CyberChef 2022-07-08 15:18:50 +01:00
n1474335
28e599a835 Merge branch 'improve-subsection-description' of https://github.com/n1073645/CyberChef 2022-07-08 15:17:31 +01:00
n1474335
1fb1d9cbb7 9.39.6 2022-07-08 15:16:00 +01:00
n1474335
2f097e5dfc Tidied up Base85 issues 2022-07-08 15:15:53 +01:00
n1474335
b71e3241be Merge branch 'master' of https://github.com/benediktwerner/CyberChef 2022-07-08 15:04:09 +01:00
n1474335
4b018bf421 9.39.5 2022-07-08 14:55:32 +01:00
n1474335
f751de896f Merge branch 'base' of https://github.com/john19696/CyberChef 2022-07-08 14:55:20 +01:00
n1474335
65aeae9c1e 9.39.4 2022-07-08 14:53:07 +01:00
n1474335
80943b0c26 Merge branch 'fix-merge' of https://github.com/n1073645/CyberChef 2022-07-08 14:52:56 +01:00
n1474335
a9657ac5c7 9.39.3 2022-07-08 14:51:08 +01:00
n1474335
6fa2e49f3a Merge branch 'webp-extractor' of https://github.com/n1073645/CyberChef 2022-07-08 14:50:57 +01:00
n1474335
50f0f70805 9.39.2 2022-07-08 14:49:50 +01:00
n1474335
fc95d82c49 Tweaked Extract Files minimum size 2022-07-08 14:49:40 +01:00
n1474335
bb6c1c54ff Merge branch 'extract-files-min-size' of https://github.com/n1073645/CyberChef 2022-07-08 13:57:06 +01:00
n1474335
c4414bd910 Fixed dropdown toggle height 2022-07-08 13:53:19 +01:00
n1073645
42c911838d Add min size to Extract Files 2022-06-17 11:18:49 +01:00
n1073645
8917eabfd1 Implemented webp extractor 2022-06-17 09:56:36 +01:00
n1073645
fc91469807 Added nesting to Merge/Fork/Subsection 2022-06-17 09:26:00 +01:00
John L
1735d9c091 remove logging 2022-06-15 15:07:39 +01:00
John L
00d754d466 lint fixes 2022-06-14 15:57:04 +01:00
John L
906727f133 Base85 improvements 2022-06-14 10:23:13 +01:00
n1073645
191d7f11f7 Improve the subsection description 2022-06-10 15:25:12 +01:00
n1474335
54fdc05e3a 9.39.1 2022-06-09 16:32:32 +01:00
n1474335
2267569c8d Fixed lint 2022-06-09 16:32:23 +01:00
n1474335
2f53ee3974 Merge branch 'fix_loop_counter' of https://github.com/sec65/CyberChef 2022-06-09 16:23:01 +01:00
n1474335
a3b846638f 9.39.0 2022-06-09 15:17:14 +01:00
n1474335
cc3033266c Updated CHANGELOG 2022-06-09 15:17:08 +01:00
n1474335
23b168515c Merge branch 'elf-info' of https://github.com/n1073645/CyberChef 2022-06-09 15:04:46 +01:00
n1073645
049690fea2 Linting modifications 2022-06-09 10:15:47 +01:00
n1073645
d3de91de85 Modify stream library to support reading until a null byte 2022-06-09 10:12:19 +01:00
n1073645
64eae37788 Added tests for ELF-Info 2022-06-09 10:02:38 +01:00
n1474335
8c71b0b8df 9.38.9 2022-06-08 18:59:36 +01:00
n1474335
2bf1ac6b9c 'Parse X.509 Certificate' Issuer and Subject name parsing improved. Closes #1365 2022-06-08 18:59:27 +01:00
n1474335
7197a434c2 9.38.8 2022-06-08 18:07:32 +01:00
n1474335
5349115b94 'JSON Beautify' operation now supports formatting, collapsing and syntax highlighting. Closes #203. 2022-06-08 18:07:11 +01:00
n1474335
4274e8f3a2 Fixed PEM wiki link 2022-06-06 15:20:31 +01:00
n1474335
7610e159a3 9.38.7 2022-06-06 14:54:27 +01:00
n1474335
9ec94434bb Fixed 'From Base64' bug adding null bytes. Closes #1362 2022-06-06 14:54:06 +01:00
n1073645
1ab444bda2 Fix tabs in tests file. 2022-06-05 18:40:46 +01:00
n1073645
3990ba774f Implemented readelf-like functionality. 2022-06-05 18:35:02 +01:00
sec65
1fea9a25a5 reset loop counter after last run 2022-06-05 17:22:42 +02:00
n1474335
3f57711c39 9.38.6 2022-06-03 22:58:23 +01:00
n1474335
dc46018757 Tidied up 'PEM to Hex' operation 2022-06-03 22:57:49 +01:00
n1474335
1464e5d5e4 Merge branch 'pem' of https://github.com/cplussharp/CyberChef 2022-06-03 22:21:37 +01:00
n1474335
95f7ed0de4 9.38.5 2022-06-03 22:13:50 +01:00
n1474335
6e7240026a Updated dependencies 2022-06-03 22:13:44 +01:00
n1474335
8bae7bf809 9.38.4 2022-06-03 21:41:44 +01:00
n1474335
b78bb2d3d6 Added 'Strict mode' to 'From Base64' operation 2022-06-03 21:41:37 +01:00
n1474335
f9a6402825 Merge branch 'strict_base64' of https://github.com/mikecat/CyberChef 2022-06-03 13:18:41 +01:00
n1474335
8ec5f3cb18 9.38.3 2022-06-03 13:15:25 +01:00
n1474335
c330394ff2 Fixed toBinary delim adjustment 2022-06-03 13:15:18 +01:00
n1474335
36e66ad5b4 Merge branch 'master' of https://github.com/michaellrowley/CyberChef 2022-06-03 13:10:59 +01:00
n1474335
a5a89efc06 9.38.2 2022-06-03 12:58:50 +01:00
n1474335
1078c37043 Merge branch 'master' of https://github.com/LukeSerne/CyberChef 2022-06-03 12:55:56 +01:00
n1474335
535c7188a8 9.38.1 2022-06-03 12:53:22 +01:00
n1474335
d6f9e216a6 Merge branch 'fix-to-base45' of https://github.com/mikecat/CyberChef 2022-06-03 12:50:45 +01:00
n1474335
7d6a879a67 Added script for updating the CHANGELOG 2022-05-31 00:20:19 +01:00
n1474335
668eac1f9e Fixed Split.js issues when resizing to around 1000px wide 2022-05-30 22:53:17 +01:00
n1474335
ff99436ce6 Fixed 'To Hex' highlighting 2022-05-30 19:43:59 +01:00
n1474335
ec577fc075 Fixed CSS for maximising output pane 2022-05-30 19:25:41 +01:00
n1474335
cc9d51b7be 9.38.0 2022-05-30 18:14:46 +01:00
n1474335
cf2b54e8c0 Update CHANGELOG 2022-05-30 18:14:41 +01:00
n1474335
a895d1d82a Added 'Parse TCP' operation 2022-05-30 18:06:15 +01:00
MikeCAT
11da4188ee fix "To Base45" ( #1351 ) 2022-05-20 11:59:04 +00:00
Luke Serné
5b68bad185 Support UTF8 encoded characters in Substitution operation
This adds support for UTF8-encoded characters in the input and the parameters.
2022-05-13 17:35:50 +02:00
n1474335
477e4a7421 9.37.3 2022-04-14 18:08:23 +01:00
n1474335
9a982f05ac Extract operations now offer built-in Sort and Unique options. Unique operation offers option to count occurances. Closes #1334. 2022-04-14 18:08:16 +01:00
n1474335
6959e2cf01 9.37.2 2022-04-14 16:57:51 +01:00
n1474335
f5fe79326a CodeQL fixes 2022-04-14 16:57:46 +01:00
n1474335
f77633cee9 9.37.1 2022-04-14 13:17:43 +01:00
n1474335
c858970573 Merge branch 'john19696-testui' 2022-04-14 13:17:32 +01:00
n1474335
fb3eceaee0 Tidied up UI tests 2022-04-14 13:16:36 +01:00
n1474335
8e37fec8f8 Merge branch 'testui' of https://github.com/john19696/CyberChef into john19696-testui 2022-04-14 11:55:56 +01:00
n1474335
ccaabfaee8 Fixed incorrect wording for RC4 Drop argument. 2022-04-14 11:55:34 +01:00
Thomas Leplus
e712af33b7 Adding ROT8000 2022-04-11 18:44:14 -07:00
John L
c431fb30c5 added logging & jsdoc 2022-04-06 10:54:20 +01:00
John L
1c04848480 added log_path 2022-04-06 10:51:52 +01:00
John L
ca1a0797fb Merge branch 'master' into testui 2022-04-04 17:54:02 +01:00
John L
7b497181fd refactor samples, and more tests 2022-04-04 17:40:58 +01:00
John L
92767b1078 Try import 2022-03-31 19:32:41 +01:00
n1474335
7c66dacc40 9.37.0 2022-03-29 18:05:55 +01:00
n1474335
632277f9bd Updated CHANGELOG 2022-03-29 18:05:47 +01:00
n1474335
3c23ae03f5 Merge branch 'swesven-master' 2022-03-29 18:02:09 +01:00
n1474335
8117926ca3 Tidied up SM4 ops and NoPadding options for AES, DES and TripleDES 2022-03-29 18:01:57 +01:00
n1474335
31e9d27f1a Merge branch 'master' of https://github.com/swesven/CyberChef into swesven-master 2022-03-29 17:30:22 +01:00
n1474335
0c067d60d8 9.36.1 2022-03-29 17:26:17 +01:00
n1474335
1171d6b165 Merge branch 'pH-T-master' 2022-03-29 17:22:50 +01:00
n1474335
18022a2a48 Merge branch 'master' of https://github.com/pH-T/CyberChef into pH-T-master 2022-03-29 17:22:34 +01:00
n1474335
1400b5cca4 9.36.0 2022-03-29 12:48:47 +01:00
n1474335
98b5f9cc29 Updated CHANGELOG 2022-03-29 12:48:43 +01:00
n1474335
e766cb009e Merge branch 'hettysymes-SIGABA' 2022-03-29 12:45:56 +01:00
n1474335
993e276858 Tidied up Bletchley ops 2022-03-29 12:45:42 +01:00
n1474335
a762fb4df4 Merge branch 'SIGABA' of https://github.com/hettysymes/CyberChef into hettysymes-SIGABA 2022-03-29 12:26:39 +01:00
n1474335
00781fa459 Fixed lint 2022-03-29 11:55:41 +01:00
n1474335
c6e7367a4e 9.35.1 2022-03-28 17:15:51 +01:00
n1474335
3749bb1222 Merge branch 'AlexAndHisScripts-master' 2022-03-28 17:15:39 +01:00
n1474335
f3c83b2009 Merge branch 'master' of https://github.com/AlexAndHisScripts/CyberChef into AlexAndHisScripts-master 2022-03-28 17:14:57 +01:00
n1474335
d3583c31bc 9.35.0 2022-03-28 16:19:55 +01:00
n1474335
2f638d8c0c Updated CHANGELOG 2022-03-28 16:19:51 +01:00
n1474335
74e43ff65a Merge branch 't-8ch-base45' 2022-03-28 16:18:02 +01:00
n1474335
4f0b160ed3 Tidied up Base45 ops 2022-03-28 16:15:37 +01:00
n1474335
709b8696fc Merge branch 'base45' of https://github.com/t-8ch/CyberChef into t-8ch-base45 2022-03-28 16:04:34 +01:00
n1474335
e080c5d72e 9.34.2 2022-03-28 15:57:00 +01:00
n1474335
9cc177a9ad Code quality improvements 2022-03-28 15:56:15 +01:00
Paul Hager
9ad4e2525e fix: GetAllCasings.mjs test 2022-03-28 16:52:59 +02:00
n1474335
60b5fe0e76 9.34.1 2022-03-28 15:42:16 +01:00
n1474335
9273f97d88 Updated dependencies 2022-03-28 15:42:11 +01:00
Paul Hager
e2b7ac68ef fix: GetAllCasings.mjs - newline bug 2022-03-28 14:46:41 +02:00
n1474335
98a6baf55a 9.34.0 2022-03-28 11:40:04 +01:00
n1474335
b5677387f5 Updated CHANGELOG 2022-03-28 11:39:57 +01:00
n1474335
cd3eaa4762 Merge branch 'n1073645-variationsOfString' 2022-03-28 11:37:42 +01:00
n1474335
9733bf65de Merge branch 'nodejs16' of https://github.com/john19696/CyberChef into john19696-nodejs16 2022-03-28 11:37:23 +01:00
n1474335
bc433f0234 9.33.1 2022-03-28 11:23:19 +01:00
n1474335
ad7283ee6f Merge branch 'Gitoffthelawn-patch-1' 2022-03-28 11:21:49 +01:00
n1474335
5b941358a9 Merge branch 'patch-1' of https://github.com/Gitoffthelawn/CyberChef into Gitoffthelawn-patch-1 2022-03-28 11:21:22 +01:00
n1474335
f19e898c57 Merge branch 'Wingless-Archangel-patch-1' 2022-03-28 11:20:29 +01:00
n1474335
590ffa184d Merge branch 'patch-1' of https://github.com/Wingless-Archangel/CyberChef into Wingless-Archangel-patch-1 2022-03-28 11:20:10 +01:00
n1474335
46de51512f Merge branch 'adamkdean-patch-1' 2022-03-28 11:15:48 +01:00
n1474335
a13f2f26e4 Merge branch 'patch-1' of https://github.com/adamkdean/CyberChef into adamkdean-patch-1 2022-03-28 11:15:28 +01:00
n1474335
c9c26f6f9f Merge branch 'eljeffeg-patch-1' 2022-03-28 11:13:44 +01:00
n1474335
787c29e42b Merge branch 'patch-1' of https://github.com/eljeffeg/CyberChef into eljeffeg-patch-1 2022-03-28 11:13:26 +01:00
n1474335
1c0b83d833 9.33.0 2022-03-28 10:58:30 +01:00
n1474335
5c767d09b0 Updated CHANGELOG 2022-03-28 10:58:25 +01:00
n1474335
75dba51f56 Improve CJS and ESM module support #1037 2022-03-28 10:52:28 +01:00
n1474335
78a1827af8 Merge branch 'john19696-nodejs16' 2022-03-25 18:33:22 +00:00
n1474335
9e3733b33b Fixed Node imports 2022-03-25 18:28:01 +00:00
n1474335
4ef65589e8 Actions can now be triggered manually 2022-03-25 15:24:21 +00:00
n1474335
cf9e670309 Updated eslint 2022-03-25 15:17:00 +00:00
n1474335
b09f98fbb4 Updated to Node 17 2022-03-25 14:59:54 +00:00
n1474335
e43e010163 Merge branch 'nodejs16' of https://github.com/john19696/CyberChef into john19696-nodejs16 2022-03-25 13:26:31 +00:00
n1073645
2a5cee0bd3 9.32.4 2022-03-23 09:31:17 +00:00
n1073645
c962bb79f5 Updated Dependencies 2022-03-23 09:28:32 +00:00
John L
add745551b WIP 2022-02-11 16:56:58 +00:00
john19696
7b8213e1f6 Merge pull request #3 from john19696/nodejs16
Nodejs16 upgrade
2022-02-11 13:04:49 +00:00
John L
2e23a33dfc Merge branch 'nodejs16' of https://github.com/john19696/CyberChef into nodejs16 2022-02-04 11:03:05 +00:00
John L
bca296ee37 GitHub actions update 2022-02-04 11:02:52 +00:00
John L
2dbd647868 nodeFlags needs quote change 2022-01-31 11:39:17 +00:00
john19696
2991e7d1fe Update Gruntfile.js
add nodeFlags
2022-01-31 10:31:19 +00:00
John L
f6f12fc193 chromedriver update 2022-01-27 17:18:31 +00:00
john19696
1fac8c1cea Merge pull request #1 from t-8ch/nodejs16
Node 16 compatibility
2022-01-21 15:05:16 +00:00
Adam K Dean
590462e2e4 Fixed Crown Copyright link
Closes #1310
2022-01-18 22:07:59 +00:00
Robin Scholtes
578a61d331 add cetacean cipher encoder and decoder operations, tests. Update .gitignore to exclude idea generated files 2022-01-17 23:37:24 +13:00
Michael Rowley
ed542582f9 Added more error-handling to ToUpperCase() 2021-12-29 19:59:48 +00:00
Michael Rowley
2574a63975 Minor adjustments 2021-12-29 19:32:39 +00:00
ElJeffe
291c55befd Spelling
Someone noticed this in another project that includes CyberChef
2021-12-14 08:47:55 -05:00
CPlusSharp
b7a978505f PEMToHex: add magic check so it gets found 2021-11-17 20:19:42 +01:00
Thomas Weißschuh
7db1f39473 base45: Implement highlighting 2021-11-09 21:12:49 +01:00
Thomas Weißschuh
6017578964 Add Base45 operations
Closes #1219

Co-developed-by: Cyril Delétré <cyril.deletre@gmail.com>
2021-11-09 21:12:44 +01:00
CPlusSharp
1dbcd2ac84 PEMtoHex: Support arbitrary PEMs
previous implementation only supported some PEMs (e.g. Certificate)

the new implementation is more general,
it just extracts the base64 between
header and footer and decodes that to hex
2021-11-07 11:21:17 +01:00
Peter Jacobs
671ae6558f Added 'LZString Decompress' and 'LZString Compress' operations 2021-10-29 15:26:31 -05:00
Alex Chambers_Jones
d2174725a9 Fixed reflected XSS described in issue 1265 2021-10-29 17:59:02 +01:00
Wingless-Archangel
f630c499d5 Update .gitignore
add .node-version file for supporting [nodeenv](https://github.com/ekalinin/nodeenv)
2021-10-29 14:41:35 +02:00
MikeCAT
e8f91316ff Added ROT13/47 Brute Force 2021-10-20 21:28:48 +09:00
MikeCAT
a7cdb095d2 Added input validation to fromBase64() 2021-10-04 22:39:16 +09:00
Thomas Weißschuh
cfc29ef821 Always use mjs imports
This is needed for Node/NPM 16 compat
2021-09-17 08:48:04 +02:00
Thomas Weißschuh
83c6775038 Support json modules
This is need for builds on Node/NPM 16
2021-09-17 08:45:56 +02:00
n1474335
ae1b12c120 9.32.3 2021-09-03 14:58:53 +01:00
n1474335
c423de545f Switch XOR input and output differential logic. Fixes #1155 2021-09-03 14:58:48 +01:00
n1474335
84011371b7 9.32.2 2021-08-26 16:51:50 +01:00
n1474335
f831ec6b7e Fixed issues in Protobuf parsing 2021-08-26 16:51:42 +01:00
n1474335
05bfa9158d 9.32.1 2021-08-19 12:06:30 +01:00
n1474335
649016bc85 Updated dependencies 2021-08-19 12:06:26 +01:00
n1474335
7492b874cf 9.32.0 2021-08-18 17:23:43 +01:00
n1474335
9ea21af61f Updated CHANGELOG 2021-08-18 17:23:38 +01:00
n1474335
dd18e52993 Protobuf operations improved to enable full and partial schema support 2021-08-18 17:22:09 +01:00
Gitoffthelawn
7712ee7f35 Improved consistency
Changed "or" to "and" because all the other sentence sections use "and".
2021-08-11 01:55:47 -07:00
n1474335
a4a13666e6 9.31.0 2021-08-10 16:51:28 +01:00
n1474335
07ef4da892 Updated CHANGELOG 2021-08-10 16:50:59 +01:00
n1474335
e9ca4dc9ca Added HASSH operations 2021-08-10 16:48:35 +01:00
n1474335
57bb8fbc45 9.30.0 2021-08-10 15:00:10 +01:00
n1474335
9175624210 Updated CHANGELOG 2021-08-10 15:00:04 +01:00
n1474335
289a417dfb Added 'JA3S Fingerprint' operation 2021-08-10 14:57:34 +01:00
n1474335
8379a9b275 Skipping UI tests in GitHub Actions 2021-08-10 14:26:33 +01:00
n1474335
5b1fad118f Fixed chromedriver path 2021-07-28 15:56:01 +01:00
n1474335
5e8985810e 9.29.2 2021-07-28 15:35:29 +01:00
n1474335
d2568e2a29 Updated dependencies 2021-07-28 15:35:24 +01:00
n1474335
6dfc21ef06 9.29.1 2021-07-28 14:58:17 +01:00
n1474335
1f19f2f58c Updated chromedriver 2021-07-28 14:58:09 +01:00
n1474335
1728cc7a85 9.29.0 2021-07-28 14:37:05 +01:00
n1474335
fa2fc2ba33 Updated CHANGELOG 2021-07-28 14:36:14 +01:00
n1474335
9a33498fed Added 'TLS JA3 Fingerprint' operation 2021-07-28 14:32:39 +01:00
n1474335
a3b873fd96 9.28.0 2021-03-26 14:09:51 +00:00
n1474335
97bd03799e Updated CHANGELOG 2021-03-26 14:09:37 +00:00
n1474335
ffaaaae2b4 Merge branch 'Danh4-issue-991' 2021-03-26 14:07:18 +00:00
n1474335
ff88d30d2f Tidied up CBOR operations 2021-03-26 14:07:02 +00:00
n1474335
88e3c2ccb2 Merge branch 'issue-991' of https://github.com/Danh4/CyberChef into Danh4-issue-991 2021-03-26 13:59:16 +00:00
swesven
6155634d3b Add the SM4 block cipher, also a no-padding option for block ciphers.
This adds an implementation of the SM4 block cipher, and operations
to encrypt and decrypt using it with CBC,ECB,CFB,OFB,CTR modes.

Also, a "no padding" option is added for AES,DES,3DES and SM4
decryption in ECB/CBC modes. This variant does not attempt to
validate the last block as being PKCS#7 padded.

This is useful, both since other padding schemes exist, and also
for decrypting data where the final block is missing.
2021-03-24 00:58:54 +01:00
n1474335
5029356514 Added link to FAQ description about output handling 2021-03-05 10:50:38 +00:00
n1474335
e57d5a7e75 9.27.6 2021-02-23 15:11:22 +00:00
n1474335
2bbe54cdcd Added further deconstruction of IPv6 Multicast Addresses in the 'Parse IPv6 Address' operation 2021-02-23 15:11:16 +00:00
n1474335
0e2423c390 9.27.5 2021-02-22 19:33:56 +00:00
n1474335
8fadad5891 AES Additional data can now be entered in a range of formats. #1011 2021-02-22 19:33:52 +00:00
n1474335
32455cd20f 9.27.4 2021-02-22 19:13:47 +00:00
n1474335
1e0e7f16a7 Added numeric validation for arguments in Binary and Hex operattions. Fixes #1178 2021-02-22 19:13:38 +00:00
n1474335
95884d77cf Extractable file formats are now listed properly in the 'Extract Files' description 2021-02-17 15:01:42 +00:00
n1474335
b69373f5e7 Fixed 'JSON to CSV' data flattening. 2021-02-16 14:48:56 +00:00
n1474335
61e85474d3 9.27.3 2021-02-16 14:36:36 +00:00
n1474335
3a9bdc58af Fixed 'JSON to CSV' handling of complex structures. Closes #637 2021-02-16 14:36:31 +00:00
n1474335
59c1c45d78 Updated dependencies 2021-02-16 14:17:09 +00:00
n1474335
b5f6cedd30 9.27.2 2021-02-16 14:12:18 +00:00
n1474335
c879af6860 Fixed 'Save recipe' URL generation issue. Closes #1176 2021-02-16 14:12:14 +00:00
n1474335
22fe5a6ae7 9.27.1 2021-02-12 17:55:36 +00:00
n1474335
57714c86a6 Escape HTML input in Fuzzy Match operation 2021-02-12 17:55:28 +00:00
n1474335
70cd375049 9.27.0 2021-02-12 13:54:52 +00:00
n1474335
e27e1dd42f Updated CHANGELOG 2021-02-12 13:53:59 +00:00
n1474335
8ad18bc7db Added 'Fuzzy Match' operation 2021-02-12 13:51:51 +00:00
n1474335
5893ac1a37 9.26.3 2021-02-12 12:12:08 +00:00
n1474335
83c3ab97f9 Merge branch 'n1073645-base64Alphabets' 2021-02-12 12:11:53 +00:00
n1474335
9b6be140fa Merge branch 'base64Alphabets' of https://github.com/n1073645/CyberChef into n1073645-base64Alphabets 2021-02-12 12:08:56 +00:00
n1474335
a6a60392c2 9.26.2 2021-02-12 12:05:03 +00:00
n1474335
ccfa0b991e Updated dependencies 2021-02-12 12:04:59 +00:00
n1474335
73b0e68993 Added code quality badge to README 2021-02-12 11:54:54 +00:00
n1474335
31a4eef001 9.26.1 2021-02-11 19:06:58 +00:00
n1474335
d6e2c9a6b9 Merge branch 'n1073645-HexdumpAsciiFix' 2021-02-11 19:06:47 +00:00
n1474335
e069f5db13 Tidied up hexdump UNIX format 2021-02-11 19:06:35 +00:00
n1474335
96b59cf0df Merge branch 'HexdumpAsciiFix' of https://github.com/n1073645/CyberChef into n1073645-HexdumpAsciiFix 2021-02-11 18:59:51 +00:00
n1474335
c1e1d4b7e3 9.26.0 2021-02-11 18:50:09 +00:00
n1474335
32d869231e Updated CHANGELOG 2021-02-11 18:50:03 +00:00
n1474335
6f95f01dda Merge branch 'n1073645-EPOCH' 2021-02-11 18:47:59 +00:00
n1474335
61a1c44f26 Renamed 'Generate Current Epoch' to 'Get Time'. It now uses the W3C High Resolution Time API and supports microsecond granularity 2021-02-11 18:47:44 +00:00
n1474335
e6c7899569 Merge branch 'EPOCH' of https://github.com/n1073645/CyberChef into n1073645-EPOCH 2021-02-11 18:08:55 +00:00
n1474335
a74a14145e 9.25.0 2021-02-11 18:03:45 +00:00
n1474335
04022b22be Updated CHANGELOG 2021-02-11 18:03:40 +00:00
n1474335
4f3010691c Merge branch 'n1073645-ID3Tags' 2021-02-11 18:01:24 +00:00
n1474335
672b477751 Extract ID3 operation now returns a JSON blob and presents an HTML table 2021-02-11 18:01:08 +00:00
n1474335
19360391a6 Merge branch 'ID3Tags' of https://github.com/n1073645/CyberChef into n1073645-ID3Tags 2021-02-11 16:16:33 +00:00
n1474335
447be8af34 9.24.8 2021-02-11 16:14:48 +00:00
n1474335
0989550e5c Merge branch 'n1073645-keychainExtractor' 2021-02-11 16:14:34 +00:00
n1474335
4d9b48b4d8 Tidied whitespace 2021-02-11 16:14:23 +00:00
n1474335
979652387d Merge branch 'keychainExtractor' of https://github.com/n1073645/CyberChef into n1073645-keychainExtractor 2021-02-11 16:12:57 +00:00
n1474335
de84fbdd1c Renamed workflows 2021-02-10 18:37:46 +00:00
n1474335
170e564319 Fixed incomplete multi-character sanitization and incomplete URL substring sanitization issues. 2021-02-10 17:41:39 +00:00
n1474335
530836876f 9.24.7 2021-02-10 13:13:25 +00:00
n1474335
1abc46058c Added a CodeQL workflow to check for bugs through code analysis. Fixed numerous bugs and implemented safeguards as already reported. 2021-02-10 13:13:19 +00:00
n1474335
892a3716ed Added Crypt lib for common resources 2021-02-09 15:00:35 +00:00
n1474335
5d982a9c8d 9.24.6 2021-02-09 14:50:17 +00:00
n1474335
6206224f1e Merge branch 'alblue-master' 2021-02-09 14:46:19 +00:00
n1474335
766310e2c7 Frequency Distribution operation now displays an HTML table 2021-02-09 14:46:04 +00:00
n1474335
02a397d2ae Merge branch 'master' of https://github.com/alblue/CyberChef into alblue-master 2021-02-09 14:30:03 +00:00
n1474335
4563c86acd 9.24.5 2021-02-09 14:24:08 +00:00
n1474335
0a59f8068e Merge branch 'aussieklutz-master' 2021-02-09 14:23:18 +00:00
n1474335
24548e3a48 Tidied up JWT tests 2021-02-09 14:23:02 +00:00
n1474335
f4784d49e7 Merge branch 'master' of https://github.com/aussieklutz/CyberChef into aussieklutz-master 2021-02-09 14:16:36 +00:00
n1474335
14d5069c6e Merge branch 'mt3571-1073-jwt-verify' 2021-02-09 14:15:12 +00:00
n1474335
9fdd55c5c6 Tidied up JWT ops 2021-02-09 14:14:59 +00:00
n1474335
5bc523aeff Merge branch '1073-jwt-verify' of https://github.com/mt3571/CyberChef into mt3571-1073-jwt-verify 2021-02-09 14:02:21 +00:00
n1474335
3ae2e2e2c8 Fixed highlighting of op names where only the description has hit 2021-02-09 11:50:20 +00:00
n1474335
83e49da7f6 Fixed description hiighlighting issue 2021-02-09 11:37:25 +00:00
n1474335
fe6df8778f Fixed year in CHANGELOG records. Closes #1168 2021-02-09 11:26:00 +00:00
Alex Blewitt
69073c9d99 Add ASCII output for frequency table
When showing results in the frequency distribution table, it's quite
helpful to see the distribution of the ASCII letters, if any. Provide an
option to show this to show the characters alongside the code.

Fixes #1169.

Signed-off-by: Alex Blewitt <alex.blewitt@gmail.com>
2021-02-07 16:22:13 +00:00
aussieklutz
d5a0adea0c Update JWTVerify.mjs 2021-02-06 18:35:46 +10:00
aussieklutz
1bcb8e433d Update JWTVerify.mjs 2021-02-06 18:10:54 +10:00
aussieklutz
fa05cf1d78 Update JWTVerify.mjs
Enabled ESRSA verification.
2021-02-06 17:58:49 +10:00
aussieklutz
63dff0d34d Update JWTVerify.mjs
Enabled validation of ECSHA256 JWT tokens in the tests
2021-02-06 17:55:44 +10:00
aussieklutz
e228b197f9 Update JWTVerify.mjs 2021-02-06 17:45:42 +10:00
aussieklutz
4bbeb6caa3 Update JWTVerify.mjs
Add expectation for working RSASHA256 test, and comment out unused privatekey.
2021-02-06 17:42:42 +10:00
aussieklutz
139d25dff9 Update JWTVerify.mjs
Update RSASHA256 test with the public key derived from the pre-existing private key, and expect a working testcase.
2021-02-06 17:40:04 +10:00
aussieklutz
6984258404 Update JWTVerify.mjs
Enable verification of RSASHA256 and 512 tokens
2021-02-06 17:27:54 +10:00
n1474335
ba66fd6546 Fixed recursive matching arguments 2021-02-05 19:04:27 +00:00
n1474335
47bbefd81f Fixed recursive scoring results in fuzzy match lib 2021-02-05 18:24:15 +00:00
n1474335
50f796049c Fixed search test 2021-02-05 18:07:20 +00:00
n1474335
618da545b1 9.24.4 2021-02-05 17:55:10 +00:00
n1474335
21236f1938 Added fuzzy search for operations 2021-02-05 17:54:57 +00:00
n1474335
4169a15066 9.24.3 2021-02-03 19:07:46 +00:00
n1474335
6b10f61e11 Moved postinstall script to a Grunt task to fix relative calls. npm scripts now use local grunt install. 2021-02-03 19:07:39 +00:00
n1474335
83f119f7e4 9.24.2 2021-02-03 17:54:57 +00:00
n1474335
041c899a35 Comments are now treated as disabled so that they do not interfere with the Dish type. Closes #1126 and #1132. Thanks to @mt3571 for the suggestion. 2021-02-03 17:54:49 +00:00
n1474335
5412fc01b3 Merge branch 'Prinzhorn-base_64_order' 2021-02-02 17:36:23 +00:00
n1474335
76926d9252 Merge branch 'base_64_order' of https://github.com/Prinzhorn/CyberChef into Prinzhorn-base_64_order 2021-02-02 17:36:10 +00:00
n1474335
3270961574 Merge branch 'n1073645-microsoftDecoderMagic' 2021-02-02 17:29:41 +00:00
n1474335
9a1ef71aec Merge branch 'microsoftDecoderMagic' of https://github.com/n1073645/CyberChef into n1073645-microsoftDecoderMagic 2021-02-02 17:29:25 +00:00
n1474335
ba878925ad Merge branch 'Prinzhorn-boolean_args' 2021-02-02 17:23:23 +00:00
n1474335
8d6b71bfaa Merge branch 'boolean_args' of https://github.com/Prinzhorn/CyberChef into Prinzhorn-boolean_args 2021-02-02 17:23:05 +00:00
n1474335
b6845aa03c 9.24.1 2021-02-02 17:18:49 +00:00
n1474335
4a673bd92a AES Decrypt now supports Additional Authenticated Data in GCM mode. Added tests for ADD at each AES size. 2021-02-02 17:18:35 +00:00
n1474335
fdffabfdd4 Merge branch 'n1073645-AESGCMAAD' 2021-02-02 16:11:03 +00:00
n1474335
ba8591293b Merge branch 'AESGCMAAD' of https://github.com/n1073645/CyberChef into n1073645-AESGCMAAD 2021-02-02 16:10:47 +00:00
n1474335
9b3aae10cf 9.24.0 2021-02-02 16:07:57 +00:00
n1474335
5c85c4df63 Updated CHANGELOG 2021-02-02 16:07:52 +00:00
n1474335
8ece2603fb Merge branch 'n1073645-SM3' 2021-02-02 16:06:49 +00:00
n1474335
1b54584820 Tweaks to various hashing functions to improve config options 2021-02-02 16:06:37 +00:00
n1474335
3ce3866000 Merge branch 'SM3' of https://github.com/n1073645/CyberChef into n1073645-SM3 2021-02-02 11:58:36 +00:00
n1474335
becc258b6c Updated CHANGELOG 2021-02-02 11:12:52 +00:00
n1474335
16c9e7119d Updated CHANGELOG 2021-02-02 11:02:23 +00:00
n1474335
f5888fea9c 9.23.1 2021-02-01 19:29:47 +00:00
n1474335
b5162c7549 Merge branch 'maqifrnswa-master' 2021-02-01 19:27:42 +00:00
n1474335
1baea1da3d Merge branch 'master' of https://github.com/maqifrnswa/CyberChef into maqifrnswa-master 2021-02-01 19:27:24 +00:00
n1474335
40899a6fe4 9.23.0 2021-02-01 19:16:03 +00:00
n1474335
66c533431d Merge branch 'mattnotmitt-rsa' 2021-02-01 19:15:46 +00:00
n1474335
74ae77f17a Tidied up and added tests for RSA operations 2021-02-01 19:15:32 +00:00
n1474335
99eb1cced5 Merge branch 'rsa' of https://github.com/mattnotmitt/CyberChef into mattnotmitt-rsa 2021-02-01 17:30:02 +00:00
n1474335
c880ecf3c4 Merge branch 'stephengroat-nvmrc' 2021-02-01 17:01:44 +00:00
n1474335
c54c34d88e Merge branch 'nvmrc' of https://github.com/stephengroat/CyberChef into stephengroat-nvmrc 2021-02-01 17:01:31 +00:00
n1474335
60b3c597a7 9.22.4 2021-02-01 16:57:48 +00:00
n1474335
372ab32539 Updated dependencies 2021-02-01 16:57:42 +00:00
n1474335
e14745a973 9.22.3 2021-02-01 16:43:19 +00:00
n1474335
afffe584cf Added flat lib for JSON to CSV op 2021-02-01 16:41:54 +00:00
n1474335
cf532f1e30 Merge branch 'n1073645-JSONTOCSV' 2021-02-01 16:35:09 +00:00
n1474335
46425ba552 Merge branch 'JSONTOCSV' of https://github.com/n1073645/CyberChef into n1073645-JSONTOCSV 2021-02-01 16:34:57 +00:00
n1474335
9f65fac4e6 Added actions for linting and testing Pull Requests 2021-02-01 16:34:12 +00:00
n1474335
af98feff51 Improved PGP keygen test 2021-02-01 16:24:47 +00:00
n1474335
339c741a2c 9.22.2 2021-02-01 16:14:38 +00:00
n1474335
d1bde23f00 Merge branch 'n1073645-datetime' 2021-02-01 16:13:57 +00:00
n1474335
be544faf0f Merge branch 'datetime' of https://github.com/n1073645/CyberChef into n1073645-datetime 2021-02-01 16:13:43 +00:00
n1474335
eff77fd3bb 9.22.1 2021-02-01 16:11:46 +00:00
n1474335
3df57ba3dd Added big and little endian options for Windows timestamp conversion ops 2021-02-01 16:11:39 +00:00
n1474335
4bae662357 Merge branch 'n1073645-FiletimeEndianness' 2021-02-01 15:56:03 +00:00
n1474335
357c90546e Merge branch 'FiletimeEndianness' of https://github.com/n1073645/CyberChef into n1073645-FiletimeEndianness 2021-02-01 15:55:42 +00:00
n1474335
54769a90ac 9.22.0 2021-02-01 15:52:34 +00:00
n1474335
0550aedd54 Merge branch 'mattnotmitt-features/unicode-format' 2021-02-01 15:51:29 +00:00
n1474335
5947ed21fc Tidied up localisation in Wikipedia URL 2021-02-01 15:51:14 +00:00
n1474335
0a0949246f Merge branch 'features/unicode-format' of https://github.com/mattnotmitt/CyberChef into mattnotmitt-features/unicode-format 2021-02-01 15:45:21 +00:00
n1474335
09c6e181fb 9.21.6 2021-02-01 14:42:32 +00:00
n1474335
02cf394bcd ROT13 tweaks 2021-02-01 14:42:00 +00:00
n1474335
46afbf9888 Merge branch 'n1073645-numberRot' 2021-02-01 14:37:44 +00:00
n1474335
7cf19d22a8 Merge branch 'numberRot' of https://github.com/n1073645/CyberChef into n1073645-numberRot 2021-02-01 14:37:30 +00:00
n1474335
46d708360f Merge branch 'xavier630-patch-1' 2021-02-01 14:32:05 +00:00
n1474335
7ec91e2366 Merge branch 'patch-1' of https://github.com/xavier630/CyberChef into xavier630-patch-1 2021-02-01 14:31:42 +00:00
n1474335
a74ee47bf0 Updated actions config 2021-02-01 14:18:17 +00:00
n1474335
7b68b92498 Updated actions config 2021-02-01 14:14:40 +00:00
n1474335
274f3acd45 Added GH Releases and NPM actions 2021-02-01 14:10:21 +00:00
n1474335
53dff8b30f 9.21.5 2021-02-01 11:10:16 +00:00
n1474335
9892ee273e Bugfix: ECC mode now works correctly in 'Generate PGP Key Pair' 2021-02-01 11:10:04 +00:00
n1474335
7dccecb336 Moved GH Pages prep to its own step 2021-01-22 17:15:58 +00:00
n1474335
aa09da0403 GitHub Actions now pushes to GitHub Pages 2021-01-22 17:13:17 +00:00
n1474335
98d7f1481c Improved UI tests 2021-01-22 16:52:37 +00:00
n1474335
7d8bdbcf7e Improved UI tests 2021-01-22 16:39:04 +00:00
n1474335
db009d3689 Improved UI tests 2021-01-22 16:25:19 +00:00
n1474335
d7bc529a95 Improved UI tests 2021-01-22 16:16:11 +00:00
n1474335
3f035294a6 Improved UI tests 2021-01-22 16:06:44 +00:00
n1474335
36282e362f Improved UI tests 2021-01-22 15:53:56 +00:00
n1474335
223353cf4d Increased UI test operation timeout 2021-01-22 15:43:18 +00:00
n1474335
ded32da632 Back to a single job 2021-01-22 15:33:15 +00:00
n1474335
d6fc21cc34 Fixed yaml 2021-01-22 15:28:46 +00:00
n1474335
b86e960456 Actions broken out into separate jobs 2021-01-22 15:27:42 +00:00
n1474335
7747bfe0f2 Fixed prod build and Actions now creates a sitemap 2021-01-22 15:17:17 +00:00
n1474335
fabea8cc61 Fix whitespace 2021-01-22 15:00:15 +00:00
n1474335
de4cd2eebc Add production build to GitHub Actions script 2021-01-22 14:59:26 +00:00
n1474335
e16ce1d9c2 Specify node version 2021-01-22 14:53:53 +00:00
n1474335
345ad741b3 Fixed sed command in postinstall script to be platform dependent 2021-01-22 14:47:33 +00:00
n1474335
e53108c493 Merge branch 'gh-actions' 2021-01-22 14:33:48 +00:00
n1474335
6129378854 Reduced Actions script to just linting and testing 2021-01-22 14:33:35 +00:00
n1474335
11a1416dcc Merge branch 'gh-actions' of https://github.com/mattnotmitt/CyberChef into mattnotmitt-gh-actions 2021-01-22 14:17:29 +00:00
n1474335
9025538544 9.21.4 2021-01-22 13:48:30 +00:00
n1474335
46929e1844 Merge branch 'mattnotmax-haversine' 2021-01-22 13:48:19 +00:00
n1474335
bf023cad48 Merge branch 'haversine' of https://github.com/mattnotmax/CyberChef into mattnotmax-haversine 2021-01-22 13:48:04 +00:00
n1474335
f649236bad 9.21.3 2021-01-22 13:45:24 +00:00
n1474335
54b1454c0a Merge branch 'n1073645-binaryLength' 2021-01-22 13:45:09 +00:00
n1474335
a41b1c2f5e Merge branch 'binaryLength' of https://github.com/n1073645/CyberChef into n1073645-binaryLength 2021-01-22 13:44:49 +00:00
n1474335
0327d7cb7a 9.21.2 2021-01-22 13:40:36 +00:00
n1474335
621d7c3683 Merge branch 'manicgiraffe-update_forensicswiki_url' 2021-01-22 13:40:24 +00:00
n1474335
ae7c3fca31 Merge branch 'update_forensicswiki_url' of https://github.com/manicgiraffe/CyberChef into manicgiraffe-update_forensicswiki_url 2021-01-22 13:39:55 +00:00
n1474335
1b3295ff59 9.21.1 2021-01-22 12:59:05 +00:00
n1474335
e40e7a0e4e Added UI tests for operations. Unfinished. 2021-01-22 12:57:21 +00:00
n1474335
cf5fd7cbf2 Updated dependencies 2021-01-22 11:04:45 +00:00
n1474335
ec37a676a8 Updated dependencies 2020-12-14 17:51:12 +00:00
n1474335
f33193e122 Updated dependencies 2020-12-14 15:32:12 +00:00
n1474335
7c40204e4f Updated dependencies 2020-12-11 17:58:23 +00:00
n1474335
2b2ffb3346 Updated dependencies 2020-12-11 16:24:39 +00:00
mattnotmax
39b7e4ff9e Correct Haversine test output 2020-12-09 21:12:26 +11:00
mattnotmax
a1109c43f6 Fix for haversine distance bug 2020-12-08 21:17:43 +11:00
mt3571
887ea0cf06 Changed an incorrect name 2020-12-01 13:49:34 +00:00
mt3571
3e0525ee9e Added in a new file to store the list of JWT algorithms that can be used, as this error was occurring due to a mismatch between what you could sign and what you could verify 2020-12-01 13:38:01 +00:00
Arthur Taggart
e6eafc2843 Updated the links to forensic wiki website 2020-11-29 14:19:42 +00:00
Xavier Downs
3e8dea73d2 Update ScanForEmbeddedFiles.mjs 2020-11-24 12:18:02 +00:00
n1073645
bbf19ee944 argument added for numbers in ROT 2020-08-24 11:24:25 +01:00
n1073645
f6c8c9e76c swap endianness argument added to Windows Filetime To Unix Timestamp 2020-08-24 10:39:18 +01:00
n1073645
9dba1232b7 byte length argument added to ToBinary 2020-08-24 09:41:05 +01:00
Matt
bf14c8983f trigger travis 2020-08-19 11:04:09 +01:00
Matt
3ab95384df Add unicode tests 2020-08-19 10:55:29 +01:00
n1073645
953a581a94 byte length arg added 2020-08-17 14:12:29 +01:00
n1073645
3bfddd708c rectify week number 2020-08-17 10:40:00 +01:00
Matt
13a54ec318 Add unicode text format operation 2020-08-13 12:36:02 +01:00
n1073645
2781640a2a JSON to CSV improvements 2020-07-29 15:27:55 +01:00
n1073645
7989f119d3 Linting Modifications 2020-07-16 09:56:30 +01:00
Scott Howard
2e0aa7ae87 Don't pad rail fence decode fixes #1069 2020-07-15 22:05:15 -04:00
n1073645
667dfd820e info url added 2020-07-06 16:46:40 +01:00
n1073645
3e3c526a62 Caesar Box Cipher Added 2020-07-06 16:35:14 +01:00
n1073645
c01ce90e06 Tests Added 2020-07-06 11:20:54 +01:00
n1073645
d68c8cb845 Casing Variations 2020-07-06 10:44:05 +01:00
Matt
0b5ee7c79f Update actions 2020-07-04 15:35:49 +01:00
Matt
23cbe1c426 Merge branch 'master' into gh-actions 2020-07-04 15:29:24 +01:00
Stephen G
de727bcddc use nvmrc file
avoids storing node version in travis to allow nvm tool for local dev
2020-06-13 14:51:37 -07:00
d98762625
c9d9730726 Updated CHANGELOG 2020-06-12 13:50:30 +01:00
d98762625
a380aed878 9.21.0 2020-06-12 12:49:39 +01:00
d98762625
4dafa50799 improve some comments, remove unused properties from magic state shim in node API 2020-06-12 12:35:33 +01:00
Benedikt Werner
f5a7db03cd Base85: Only require 15 continuous base85 chars 2020-06-10 15:50:26 +02:00
Matt
d4ae241758 Merge branch 'master' into rsa 2020-06-08 15:55:37 +01:00
hettysymes
88947b9d42 Added operation description note and modified comment formatting 2020-06-08 12:27:40 +01:00
hettysymes
3c68ad1302 Modified control rotor stepping so the next control rotor steps once the previous rotor reaches "O" and added tests 2020-06-07 17:45:17 +01:00
hettysymes
e2b3389da6 Added SIGABA simple test 2020-06-06 19:47:15 +01:00
hettysymes
938385c18b Fixed grunt lint errors 2020-06-06 19:46:42 +01:00
hettysymes
5d01b06877 Added copyright and clarified description 2020-06-06 19:46:42 +01:00
hettysymes
f007c093eb Emulation of the WW2 SIGABA machine
I have created an emulation of the SIGABA machine and have tested it against some test data from a Master's thesis by Miao Ai: https://scholarworks.sjsu.edu/cgi/viewcontent.cgi?article=1237&context=etd_projects
2020-06-06 19:46:42 +01:00
d98762625
53e69835ff Formally disallow flowcontrol operations from being used in bake recipes 2020-06-05 14:44:34 +01:00
d98762625
939208903a Allow magic in node api 2020-06-05 12:26:17 +01:00
n1073645
7526f4d7b1 Generate Epoch Time Operation Added 2020-06-01 13:47:51 +01:00
d98762625
616b38c6fb 9.20.7 2020-05-28 09:50:22 +01:00
d98762625
a302df8f91 update bootstrap 2020-05-28 09:34:57 +01:00
dependabot[bot]
093512a55a Bump jquery from 3.4.1 to 3.5.0
Bumps [jquery](https://github.com/jquery/jquery) from 3.4.1 to 3.5.0.
- [Release notes](https://github.com/jquery/jquery/releases)
- [Commits](https://github.com/jquery/jquery/compare/3.4.1...3.5.0)

Signed-off-by: dependabot[bot] <support@github.com>
2020-05-27 15:11:47 +00:00
d98762625
007224c92e 9.20.6 2020-05-27 15:59:42 +01:00
Matthieu
738ee33959 Fix bug in Normalise Unicode operation: replace nfc by nfkc 2020-05-27 15:47:40 +01:00
d98762625
d720a6b250 9.20.5 2020-05-27 15:34:45 +01:00
Matt
5ce3cc17bb Fixed tesseract issue 2020-05-27 15:00:34 +01:00
d98762625
5c35205315 9.20.4 2020-05-27 13:35:20 +01:00
d98762625
10751934e4 add uname dependency and bump chromedriver 2020-05-27 12:28:59 +01:00
d98762625
d658f91106 Merge branch 'master' of github.com:gchq/CyberChef into javascript-minify 2020-05-27 11:54:07 +01:00
Benedikt Werner
ee408f7add Base85: Update magic regexes to require 20 non-whitespace base85 chars 2020-05-22 03:30:57 +02:00
Benedikt Werner
1294d764e2 Base85: Only remove start and end markers with standard/ascii85 encoding 2020-05-22 03:30:15 +02:00
Benedikt Werner
eab1be0e2c Magic base85: Remove 'i' flag 2020-05-20 00:23:50 +02:00
Benedikt Werner
15dd9d4c93 Add magic checks for base85 2020-05-16 00:42:50 +02:00
Benedikt Werner
103ecff6a7 Base85: Ignore whitespace 2020-05-16 00:42:31 +02:00
Benedikt Werner
0182cdda69 Base85: Fix alphabetName 2020-05-16 00:42:02 +02:00
n1073645
fae96af17d Info for sm3 added 2020-04-24 14:13:55 +01:00
n1073645
57c1a03c4f Option structures added for hashing algorithms 2020-04-24 14:04:13 +01:00
Alexander Prinzhorn
cb8fe42c66 Put Base64 after Base62 2020-04-16 10:20:38 +02:00
Alexander Prinzhorn
7f4b2574b0 Use proper booleans instead of relying on truthy/falsy values 2020-04-16 09:59:43 +02:00
Matt
fad163e0eb Added tests (that can't be run) 2020-04-07 21:16:29 +01:00
Matt
7ad3992bd1 Add RSA Decrypt Operation 2020-04-07 13:31:33 +01:00
Matt
e7b5c0e37c Add RSA Encrypt Operation 2020-04-07 13:31:17 +01:00
n1073645
cc35127459 AAD for AES Added 2020-04-07 13:03:24 +01:00
Matt
1c0ecd29c2 Fix RSA operations 2020-04-07 11:45:54 +01:00
n1073645
1f0fddd0e9 Added magic signature to Microsoft Script Decoder 2020-04-07 10:33:15 +01:00
Matt
18c6b9bc09 Add RSA Verify operation 2020-04-06 15:24:22 +01:00
Matt
2233b9a094 Comment and add error handling to generate and sign 2020-04-06 15:24:06 +01:00
Matt
e0f000b913 Fixed RSA generation and added digest option to verify 2020-04-06 13:35:14 +01:00
Matt
73864e0809 Merge branch 'master' into features/rsa 2020-04-05 12:08:24 +01:00
n1073645
cd8a85975c Info and description 2020-04-02 15:59:58 +01:00
n1073645
2f94ec20b0 Added to categories 2020-04-02 15:58:03 +01:00
n1073645
09d9deae43 ID3 Extractor Added. 2020-04-02 15:43:55 +01:00
71819
209fc07eac Issue 991: Add CBOR Decode operation 2020-03-30 11:31:25 +01:00
71819
ae70cb89ed Issue 991: Add CBOR Encode operation 2020-03-30 11:31:25 +01:00
n1474335
4c3324aea1 9.20.3 2020-03-30 11:17:46 +01:00
n1474335
ac2fcee90f Merge branch 'Danh4-issue-998' 2020-03-30 11:17:38 +01:00
71819
94e00115fe Issue 998: DishJSON should only replace undefined with new ArrayBuffer not null or false 2020-03-28 20:27:59 +00:00
d98762625
29255d2338 Merge pull request #983 from n1073645/gruntSpelling
Grunt npm tests comment changed to npm test
2020-03-27 14:23:54 +00:00
n1073645
bda36e508a Regexes for magic for the new alphabets 2020-03-27 13:27:56 +00:00
n1073645
d2ea1273da Merge remote-tracking branch 'upstream/master' into base64Alphabets 2020-03-27 13:09:03 +00:00
n1474335
39278cfce7 9.20.2 2020-03-27 12:10:01 +00:00
n1474335
46cc48cfb9 Renamed Parse ObjectID Timestamp operation files 2020-03-27 12:09:57 +00:00
n1474335
eb4009949d 9.20.1 2020-03-27 12:05:41 +00:00
n1474335
57c48a4bd2 Merge branch 'n1073645-TargaExtractor' 2020-03-27 12:05:34 +00:00
n1474335
45011de494 Tidied up TARGE extractor 2020-03-27 12:05:23 +00:00
n1474335
5e51ed0a5f Merge branch 'TargaExtractor' of https://github.com/n1073645/CyberChef into n1073645-TargaExtractor 2020-03-27 12:01:46 +00:00
n1474335
875802ef2a 9.20.0 2020-03-27 12:00:39 +00:00
n1474335
bbc255ef83 Updated CHANGELOG 2020-03-27 12:00:33 +00:00
n1474335
fc155ec3fc Merge branch 'dmfj-parse-objectid-timestamp' 2020-03-27 11:56:58 +00:00
n1474335
3a0c8a199a Tidied up 'Parse ObjectID Timestamp' operation 2020-03-27 11:56:42 +00:00
n1474335
9c729c4490 Merge branch 'parse-objectid-timestamp' of https://github.com/dmfj/CyberChef into dmfj-parse-objectid-timestamp 2020-03-27 11:48:55 +00:00
n1474335
19bdbd66e5 Merge branch 'cbeuw-stacking-fix' 2020-03-27 10:19:14 +00:00
n1474335
ea090f79ee Merge branch 'stacking-fix' of https://github.com/cbeuw/CyberChef into cbeuw-stacking-fix 2020-03-27 10:17:54 +00:00
Andy Wang
1be6c54be2 Fix dropup menu being covered 2020-03-26 22:45:03 +00:00
Dominic Fitch-Jones
9f4ef9cdad Add ObjectId timestamp parser operation 2020-03-21 17:42:17 -04:00
n1073645
2e0af64ac3 PGP secring signatures and Mac OS X keyring extractor added 2020-03-17 14:53:05 +00:00
n1073645
8a029e5147 Grunt npm tests changed to npm test 2020-03-17 08:40:15 +00:00
n1073645
4251089687 Targa Image Extractor 2020-03-17 08:24:35 +00:00
n1073645
dbcd670ca8 Targa file extractor 2020-03-16 16:56:01 +00:00
n1073645
30bc8dfbe9 UNIX Format Added for ToHexdump 2020-03-13 10:38:37 +00:00
n1073645
53a579028c Added only ASCII flag to ToHexdump 2020-03-12 09:30:48 +00:00
n1073645
e91e993fb5 Update LS47.mjs 2020-02-14 13:43:30 +00:00
n1073645
e71794d362 Tests added for LS47 2020-02-14 12:28:12 +00:00
n1073645
6fd929160d Comments and linting. 2020-01-28 10:35:01 +00:00
n1073645
5cdd062ed9 Linting done 2020-01-28 09:40:03 +00:00
n1073645
0259ed8314 LS47 implemented, needs linting 2020-01-27 16:07:54 +00:00
n1073645
3a2580fbc2 Extra Base64 Alphabets 2020-01-22 10:35:11 +00:00
n1073645
d8405e5f81 Linting on PLIST viewer operation. 2019-11-25 10:37:30 +00:00
n1073645
0295d0c9b4 Tided up presentation of the PLIST 2019-11-25 10:35:45 +00:00
n1073645
8e1e1d56ca Plist viewer operation added. 2019-11-22 15:39:43 +00:00
n1073645
63bb19d48d Began implementing the PLIST viewer operation 2019-11-22 08:32:46 +00:00
n1073645
e92ed13864 PLIST viewer. 2019-11-21 12:53:44 +00:00
Jarrod Connolly
462f619f43 Update JavaScript Minify operation to support ES6. 2019-10-31 23:18:54 -07:00
Matt
123a0ccd70 Changed pages workflow 2019-09-30 14:41:14 +01:00
Matt
4c737475d4 Forgot to run prod build 2019-09-30 14:08:23 +01:00
Matt
4e0d97f2c1 Added sudo 2019-09-30 14:02:22 +01:00
Matt
85906cafbb Adjustment 2019-09-30 13:56:26 +01:00
Matt
a8dc691033 Try using github actions
Gonna monch this commit once i know it works
2019-09-30 13:51:38 +01:00
Matt
4d7988b78e Fixed RSA key generation 2019-09-30 13:12:10 +01:00
Matt
841e760b04 Merge remote-tracking branch 'upstream/master' into features/rsa 2019-09-30 11:03:41 +01:00
Matt C
31e758ca45 Attempt to make RSA key generation functional 2018-08-31 11:25:05 +01:00
GCHQ 77703
f81ca3ba60 Implement RSA generation and signing of messages 2018-08-30 22:38:01 +01:00
307 changed files with 34454 additions and 11667 deletions

View File

@@ -1,5 +1,5 @@
{
"parser": "babel-eslint",
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 9,
"ecmaFeatures": {
@@ -63,7 +63,8 @@
}],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double", {
"avoidEscape": true
"avoidEscape": true,
"allowTemplateLiterals": true
}],
"camelcase": ["error", {
"properties": "always"

33
.github/workflows/codeql.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: "CodeQL Analysis"
on:
workflow_dispatch:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '22 17 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

55
.github/workflows/master.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
name: "Master Build, Test & Deploy"
on:
workflow_dispatch:
push:
branches:
- master
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set node version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install
run: |
npm install
npm run setheapsize
- name: Lint
run: npx grunt lint
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Production Build
if: success()
run: npx grunt prod
- name: Generate sitemap
run: npx grunt exec:sitemap
# - name: UI Tests
# if: success()
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Prepare for GitHub Pages
if: success()
run: npx grunt copy:ghPages
- name: Deploy to GitHub Pages
if: success() && github.ref == 'refs/heads/master'
uses: crazy-max/ghaction-github-pages@v3
with:
target_branch: gh-pages
build_dir: ./build/prod
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

38
.github/workflows/pull_requests.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: "Pull Requests"
on:
workflow_dispatch:
pull_request:
types: [synchronize, opened, reopened]
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set node version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install
run: |
npm install
npm run setheapsize
- name: Lint
run: npx grunt lint
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Production Build
if: success()
run: npx grunt prod
# - name: UI Tests
# if: success()
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui

57
.github/workflows/releases.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: "Releases"
on:
workflow_dispatch:
push:
tags:
- 'v*'
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set node version
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install
run: |
npm install
npm run setheapsize
- name: Lint
run: npx grunt lint
- name: Unit Tests
run: |
npm test
npm run testnodeconsumer
- name: Production Build
if: success()
run: npx grunt prod
# - name: UI Tests
# if: success()
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
- name: Upload Release Assets
if: success()
id: upload-release-assets
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: build/prod/*.zip
tag: ${{ github.ref }}
overwrite: true
file_glob: true
body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details."
- name: Publish to NPM
if: success()
uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}

2
.gitignore vendored
View File

@@ -11,4 +11,4 @@ src/node/config/OperationConfig.json
src/node/index.mjs
**/*.DS_Store
tests/browser/output/*
.node-version

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18

View File

@@ -1,55 +0,0 @@
language: node_js
node_js:
- lts/dubnium
cache: npm
os: linux
addons:
chrome: stable
install: npm install
before_script:
- npm install -g grunt
- export NODE_OPTIONS=--max_old_space_size=2048
script:
- grunt lint
- npm test
- grunt testnodeconsumer
- grunt prod --msg="$COMPILE_MSG"
- xvfb-run --server-args="-screen 0 1200x800x24" grunt testui
before_deploy:
- grunt exec:sitemap
- grunt copy:ghPages
deploy:
- provider: pages
edge: true
token: $GITHUB_TOKEN
local_dir: build/prod/
target_branch: gh-pages
on:
repo: gchq/CyberChef
branch: master
- provider: releases
edge: true
token:
secure: "HV1WSKv4l/0Y2bKKs1iBJocBcmLj08PCRUeEM/jTwA4jqJ8EiLHWiXtER/D5sEg2iibRVKd2OQjfrmS6bo4AiwdeVgAKmv0FtS2Jw+391N8Nd5AkEANHa5Om/IpHLTL2YRAjpJTsDpY72bMUTJIwjQA3TFJkgrpOw6KYfohOcgbxLpZ4XuNJRU3VL4Hsxdv5V9aOVmfFOmMOVPQlakXy7NgtW5POp1f2WJwgcZxylkR1CjwaqMyXmSoVl46pyH3tr5+dptsQoKSGdi6sIHGA60oDotFPcm+0ifa47wZw+vapuuDi4tdNxhrHGaDMG8xiE0WFDHwQUDlk2/+W7j9SEX0H3Em7us371JXRp56EDwEcDa34VpVkC6i8HGcHK55hnxVbMZXGf3qhOFD8wY7qMbjMRvIpucrMHBi86OfkDfv0vDj2LyvIl5APj/AX50BrE0tfH1MZbH26Jkx4NdlkcxQ14GumarmUqfmVvbX/fsoA6oUuAAE9ZgRRi3KHO4wci6KUcRfdm+XOeUkaBFsL86G3EEYIvrtBTuaypdz+Cx7nd1iPZyWMx5Y1gXnVzha4nBdV4+7l9JIsFggD8QVpw2uHXQiS1KXFjOeqA3DBD8tjMB7q26Fl2fD3jkOo4BTbQ2NrRIZUu/iL+fOmMPsyMt2qulB0yaSBCfkbEq8xrUA="
file_glob: true
file:
- build/prod/*.zip
- src/node/cjs.js
on:
repo: gchq/CyberChef
tags: true
- provider: npm
edge: true
email: "n1474335@gmail.com"
api_token:
secure: "UnDQL3Kh+GK2toL0TK3FObO0ujVssU3Eg4BBuYdjwLB81GhiGE5/DTh7THdZPOpbLo6wQeOwfZDuMeKC1OU+0Uf4NsdYFu1aq6xMO20qBQ4qUfgsyiK4Qgywj9gk0p1+OFZdGAZ/j1CNRAaF71XQIY6iV84c+SO4WoizXYrNT0Jh4sr2DA4/97G2xmJtPi0qOzYrJ09R56ZUozmqeik5G0pMRIuJRbpjS/7bZXV+N7WV0ombZc9RkUaetbabEVOLQ+Xx5YAIVq+VuEeMe9VBSnxY/FfCLmy1wJsjGzpLCyBI9nbrG4nw8Wgc2m8NfK9rcpIvBTGner9r2j60NVDkZ8kLZPrqXhq6AZMwa+oz6K5UQCqRo2RRQzSGwXxg67HY5Tcq+oNmjd+DqpPg4LZ3eGlluyP5XfG+hpSr9Ya4d8q8SrUWLxkoLHI6ZKMtoKFbTCSSQPiluW5hsZxjz3yDkkjsJw64M/EM8UyJrgaXqDklQu+7rBGKLfsK6os7RDiqjBWpQ7gwpo8HvY0O8yqEAabPz+QGkanpjcCOZCXFbSkzWxYy37RMAPu88iINVZVlZE4l+WJenCpZY95ueyy0mG9cyMSzVRPyX6A+/n4H6VMFPFjpGDLTD588ACEjY1lmHfS/eXwXJcgqPPD2gW0XdRdUheU/ssqlfCfGWQMTDXs="
on:
tags: true
branch: master
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/83c143a6822e218d5b34
on_success: change
on_failure: always
on_start: never

View File

@@ -1,7 +1,107 @@
# Changelog
## Versioning
CyberChef uses the [semver](https://semver.org/) system to manage versioning: `<MAJOR>.<MINOR>.<PATCH>`.
- MAJOR version changes represent a significant change to the fundamental architecture of CyberChef and may (but don't always) make breaking changes that are not backwards compatible.
- MINOR version changes usually mean the addition of new operations or reasonably significant new features.
- PATCH versions are used for bug fixes and any other small tweaks that modify or improve existing capabilities.
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
## Details
### [9.48.0] - 2022-10-14
- Added 'LM Hash' and 'NT Hash' operations [@n1474335] [@brun0ne] | [#1427]
### [9.47.0] - 2022-10-14
- Added 'LZMA Decompress' and 'LZMA Compress' operations [@mattnotmitt] | [#1421]
### [9.46.0] - 2022-07-08
- Added 'Cetacean Cipher Encode' and 'Cetacean Cipher Decode' operations [@valdelaseras] | [#1308]
### [9.45.0] - 2022-07-08
- Added 'ROT8000' operation [@thomasleplus] | [#1250]
### [9.44.0] - 2022-07-08
- Added 'LZString Compress' and 'LZString Decompress' operations [@crespyl] | [#1266]
### [9.43.0] - 2022-07-08
- Added 'ROT13 Brute Force' and 'ROT47 Brute Force' operations [@mikecat] | [#1264]
### [9.42.0] - 2022-07-08
- Added 'LS47 Encrypt' and 'LS47 Decrypt' operations [@n1073645] | [#951]
### [9.41.0] - 2022-07-08
- Added 'Caesar Box Cipher' operation [@n1073645] | [#1066]
### [9.40.0] - 2022-07-08
- Added 'P-list Viewer' operation [@n1073645] | [#906]
### [9.39.0] - 2022-06-09
- Added 'ELF Info' operation [@n1073645] | [#1364]
### [9.38.0] - 2022-05-30
- Added 'Parse TCP' operation [@n1474335] | [a895d1d]
### [9.37.0] - 2022-03-29
- 'SM4 Encrypt' and 'SM4 Decrypt' operations added [@swesven] | [#1189]
- NoPadding options added for CBC and ECB modes in AES, DES and Triple DES Decrypt operations [@swesven] | [#1189]
### [9.36.0] - 2022-03-29
- 'SIGABA' operation added [@hettysymes] | [#934]
### [9.35.0] - 2022-03-28
- 'To Base45' and 'From Base45' operations added [@t-8ch] | [#1242]
### [9.34.0] - 2022-03-28
- 'Get All Casings' operation added [@n1073645] | [#1065]
### [9.33.0] - 2022-03-25
- Updated to support Node 17 [@n1474335] [@john19696] [@t-8ch] | [[#1326] [#1313] [#1244]
- Improved CJS and ESM module support [@d98762625] | [#1037]
### [9.32.0] - 2021-08-18
- 'Protobuf Encode' operation added and decode operation modified to allow decoding with full and partial schemas [@n1474335] | [dd18e52]
### [9.31.0] - 2021-08-10
- 'HASSH Client Fingerprint' and 'HASSH Server Fingerprint' operations added [@n1474335] | [e9ca4dc]
### [9.30.0] - 2021-08-10
- 'JA3S Fingerprint' operation added [@n1474335] | [289a417]
### [9.29.0] - 2021-07-28
- 'JA3 Fingerprint' operation added [@n1474335] | [9a33498]
### [9.28.0] - 2021-03-26
- 'CBOR Encode' and 'CBOR Decode' operations added [@Danh4] | [#999]
### [9.27.0] - 2021-02-12
- 'Fuzzy Match' operation added [@n1474335] | [8ad18b]
### [9.26.0] - 2021-02-11
- 'Get Time' operation added [@n1073645] [@n1474335] | [#1045]
### [9.25.0] - 2021-02-11
- 'Extract ID3' operation added [@n1073645] [@n1474335] | [#1006]
### [9.24.0] - 2021-02-02
- 'SM3' hashing function added along with more configuration options for other hashing operations [@n1073645] [@n1474335] | [#1022]
### [9.23.0] - 2021-02-01
- Various RSA operations added to encrypt, decrypt, sign, verify and generate keys [@mattnotmitt] [@GCHQ77703] | [#652]
### [9.22.0] - 2021-02-01
- 'Unicode Text Format' operation added [@mattnotmitt] | [#1083]
### [9.21.0] - 2020-06-12
- Node API now exports `magic` operation [@d98762625] | [#1049]
### [9.20.0] - 2020-03-27
- 'Parse ObjectID Timestamp' operation added [@dmfj] | [#987]
### [9.19.0] - 2020-03-24
- Improvements to the 'Magic' operation, allowing it to recognise more data formats and provide more accurate results [@n1073645] [@n1474335] | [#966] [b765534b](https://github.com/gchq/CyberChef/commit/b765534b8b2a0454a5132a0a52d1d8844bcbdaaa)
@@ -68,7 +168,7 @@ All major and minor version changes will be documented in this file. Details of
<details>
<summary>Click to expand v8 minor versions</summary>
### [8.38.0] - 2019-07-03
- 'Streebog' and 'GOST hash' operations added [@MShwed] [@n1474335] | [#530]
@@ -221,6 +321,35 @@ All major and minor version changes will be documented in this file. Details of
[9.48.0]: https://github.com/gchq/CyberChef/releases/tag/v9.48.0
[9.47.0]: https://github.com/gchq/CyberChef/releases/tag/v9.47.0
[9.46.0]: https://github.com/gchq/CyberChef/releases/tag/v9.46.0
[9.45.0]: https://github.com/gchq/CyberChef/releases/tag/v9.45.0
[9.44.0]: https://github.com/gchq/CyberChef/releases/tag/v9.44.0
[9.43.0]: https://github.com/gchq/CyberChef/releases/tag/v9.43.0
[9.42.0]: https://github.com/gchq/CyberChef/releases/tag/v9.42.0
[9.41.0]: https://github.com/gchq/CyberChef/releases/tag/v9.41.0
[9.40.0]: https://github.com/gchq/CyberChef/releases/tag/v9.40.0
[9.39.0]: https://github.com/gchq/CyberChef/releases/tag/v9.39.0
[9.38.0]: https://github.com/gchq/CyberChef/releases/tag/v9.38.0
[9.37.0]: https://github.com/gchq/CyberChef/releases/tag/v9.37.0
[9.36.0]: https://github.com/gchq/CyberChef/releases/tag/v9.36.0
[9.35.0]: https://github.com/gchq/CyberChef/releases/tag/v9.35.0
[9.34.0]: https://github.com/gchq/CyberChef/releases/tag/v9.34.0
[9.33.0]: https://github.com/gchq/CyberChef/releases/tag/v9.33.0
[9.32.0]: https://github.com/gchq/CyberChef/releases/tag/v9.32.0
[9.31.0]: https://github.com/gchq/CyberChef/releases/tag/v9.31.0
[9.30.0]: https://github.com/gchq/CyberChef/releases/tag/v9.30.0
[9.29.0]: https://github.com/gchq/CyberChef/releases/tag/v9.29.0
[9.28.0]: https://github.com/gchq/CyberChef/releases/tag/v9.28.0
[9.27.0]: https://github.com/gchq/CyberChef/releases/tag/v9.27.0
[9.26.0]: https://github.com/gchq/CyberChef/releases/tag/v9.26.0
[9.25.0]: https://github.com/gchq/CyberChef/releases/tag/v9.25.0
[9.24.0]: https://github.com/gchq/CyberChef/releases/tag/v9.24.0
[9.23.0]: https://github.com/gchq/CyberChef/releases/tag/v9.23.0
[9.22.0]: https://github.com/gchq/CyberChef/releases/tag/v9.22.0
[9.21.0]: https://github.com/gchq/CyberChef/releases/tag/v9.21.0
[9.20.0]: https://github.com/gchq/CyberChef/releases/tag/v9.20.0
[9.19.0]: https://github.com/gchq/CyberChef/releases/tag/v9.19.0
[9.18.0]: https://github.com/gchq/CyberChef/releases/tag/v9.18.0
[9.17.0]: https://github.com/gchq/CyberChef/releases/tag/v9.17.0
@@ -318,6 +447,25 @@ All major and minor version changes will be documented in this file. Details of
[@Flavsditz]: https://github.com/Flavsditz
[@pointhi]: https://github.com/pointhi
[@MarvinJWendt]: https://github.com/MarvinJWendt
[@dmfj]: https://github.com/dmfj
[@mattnotmitt]: https://github.com/mattnotmitt
[@Danh4]: https://github.com/Danh4
[@john19696]: https://github.com/john19696
[@t-8ch]: https://github.com/t-8ch
[@hettysymes]: https://github.com/hettysymes
[@swesven]: https://github.com/swesven
[@mikecat]: https://github.com/mikecat
[@crespyl]: https://github.com/crespyl
[@thomasleplus]: https://github.com/thomasleplus
[@valdelaseras]: https://github.com/valdelaseras
[@brun0ne]: https://github.com/brun0ne
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
[9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513
[289a417]: https://github.com/gchq/CyberChef/commit/289a417dfb5923de5e1694354ec42a08d9395bfe
[e9ca4dc]: https://github.com/gchq/CyberChef/commit/e9ca4dc9caf98f33fd986431cd400c88082a42b8
[dd18e52]: https://github.com/gchq/CyberChef/commit/dd18e529939078b89867297b181a584e8b2cc7da
[a895d1d]: https://github.com/gchq/CyberChef/commit/a895d1d82a2f92d440a0c5eca2bc7c898107b737
[#95]: https://github.com/gchq/CyberChef/pull/299
[#173]: https://github.com/gchq/CyberChef/pull/173
@@ -379,13 +527,40 @@ All major and minor version changes will be documented in this file. Details of
[#625]: https://github.com/gchq/CyberChef/pull/625
[#627]: https://github.com/gchq/CyberChef/pull/627
[#632]: https://github.com/gchq/CyberChef/pull/632
[#652]: https://github.com/gchq/CyberChef/pull/652
[#653]: https://github.com/gchq/CyberChef/pull/653
[#674]: https://github.com/gchq/CyberChef/pull/674
[#683]: https://github.com/gchq/CyberChef/pull/683
[#865]: https://github.com/gchq/CyberChef/pull/865
[#906]: https://github.com/gchq/CyberChef/pull/906
[#912]: https://github.com/gchq/CyberChef/pull/912
[#917]: https://github.com/gchq/CyberChef/pull/917
[#934]: https://github.com/gchq/CyberChef/pull/934
[#948]: https://github.com/gchq/CyberChef/pull/948
[#951]: https://github.com/gchq/CyberChef/pull/951
[#952]: https://github.com/gchq/CyberChef/pull/952
[#965]: https://github.com/gchq/CyberChef/pull/965
[#966]: https://github.com/gchq/CyberChef/pull/966
[#987]: https://github.com/gchq/CyberChef/pull/987
[#999]: https://github.com/gchq/CyberChef/pull/999
[#1006]: https://github.com/gchq/CyberChef/pull/1006
[#1022]: https://github.com/gchq/CyberChef/pull/1022
[#1037]: https://github.com/gchq/CyberChef/pull/1037
[#1045]: https://github.com/gchq/CyberChef/pull/1045
[#1049]: https://github.com/gchq/CyberChef/pull/1049
[#1065]: https://github.com/gchq/CyberChef/pull/1065
[#1066]: https://github.com/gchq/CyberChef/pull/1066
[#1083]: https://github.com/gchq/CyberChef/pull/1083
[#1189]: https://github.com/gchq/CyberChef/pull/1189
[#1242]: https://github.com/gchq/CyberChef/pull/1242
[#1244]: https://github.com/gchq/CyberChef/pull/1244
[#1313]: https://github.com/gchq/CyberChef/pull/1313
[#1326]: https://github.com/gchq/CyberChef/pull/1326
[#1364]: https://github.com/gchq/CyberChef/pull/1364
[#1264]: https://github.com/gchq/CyberChef/pull/1264
[#1266]: https://github.com/gchq/CyberChef/pull/1266
[#1250]: https://github.com/gchq/CyberChef/pull/1250
[#1308]: https://github.com/gchq/CyberChef/pull/1308
[#1421]: https://github.com/gchq/CyberChef/pull/1421
[#1427]: https://github.com/gchq/CyberChef/pull/1427

View File

@@ -6,6 +6,8 @@ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPl
const glob = require("glob");
const path = require("path");
const nodeFlags = "--experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings --no-deprecation";
/**
* Grunt configuration for building the app in various formats.
*
@@ -37,7 +39,7 @@ module.exports = function (grunt) {
]);
grunt.registerTask("configTests",
"A task which configures config files in preparation for tests to be run. Use `npm tests` to run tests.",
"A task which configures config files in preparation for tests to be run. Use `npm test` to run tests.",
[
"clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex"
]);
@@ -48,7 +50,7 @@ module.exports = function (grunt) {
grunt.registerTask("testnodeconsumer",
"A task which checks whether consuming CJS and ESM apps work with the CyberChef build",
["exec:setupNodeConsumers", "exec:testCJSNodeConsumer", "exec:testESMNodeConsumer", "exec:testESMDeepImportNodeConsumer", "exec:teardownNodeConsumers"]);
["exec:setupNodeConsumers", "exec:testCJSNodeConsumer", "exec:testESMNodeConsumer", "exec:teardownNodeConsumers"]);
grunt.registerTask("default",
"Lints the code base",
@@ -78,7 +80,6 @@ module.exports = function (grunt) {
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-chmod");
grunt.loadNpmTasks("grunt-exec");
grunt.loadNpmTasks("grunt-accessibility");
grunt.loadNpmTasks("grunt-concurrent");
grunt.loadNpmTasks("grunt-contrib-connect");
grunt.loadNpmTasks("grunt-zip");
@@ -175,7 +176,7 @@ module.exports = function (grunt) {
// previous one fails. & would coninue on a fail
.join("&&")
// Windows does not support \n properly
.replace("\n", "\\n");
.replace(/\n/g, "\\n");
}
grunt.initConfig({
@@ -188,76 +189,47 @@ module.exports = function (grunt) {
standalone: ["build/prod/CyberChef*.html"]
},
eslint: {
options: {
configFile: "./.eslintrc.json"
},
configs: ["*.{js,mjs}"],
core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*", "!src/core/operations/legacy/**/*"],
web: ["src/web/**/*.{js,mjs}", "!src/web/static/**/*"],
node: ["src/node/**/*.{js,mjs}"],
tests: ["tests/**/*.{js,mjs}"],
},
accessibility: {
options: {
accessibilityLevel: "WCAG2A",
verbose: false,
ignore: [
"WCAG2A.Principle1.Guideline1_3.1_3_1.H42.2"
]
},
test: {
src: ["build/**/*.html"]
}
},
webpack: {
options: webpackConfig,
web: webpackProdConf(),
},
"webpack-dev-server": {
options: {
webpack: webpackConfig,
host: "0.0.0.0",
port: grunt.option("port") || 8080,
disableHostCheck: true,
overlay: true,
inline: false,
clientLogLevel: "error",
stats: {
children: false,
chunks: false,
modules: false,
entrypoints: false,
warningsFilter: [
/source-map/,
/dependency is an expression/,
/export 'default'/,
/Can't resolve 'sodium'/
],
}
},
options: webpackConfig,
start: {
webpack: {
mode: "development",
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.mjs": "./config/modules/Default.mjs"
}
mode: "development",
target: "web",
entry: Object.assign({
main: "./src/web/index.js"
}, moduleEntryPoints),
resolve: {
alias: {
"./config/modules/OpModules.mjs": "./config/modules/Default.mjs"
}
},
devServer: {
port: grunt.option("port") || 8080,
client: {
logging: "error",
overlay: true
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/web/html/index.html",
chunks: ["main"],
compileTime: compileTime,
version: pkg.version,
})
]
}
hot: "only"
},
plugins: [
new webpack.DefinePlugin(BUILD_CONSTANTS),
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/web/html/index.html",
chunks: ["main"],
compileTime: compileTime,
version: pkg.version,
})
]
}
},
zip: {
@@ -302,7 +274,7 @@ module.exports = function (grunt) {
},
files: [
{
src: "build/prod/index.html",
src: ["build/prod/index.html"],
dest: "build/prod/index.html"
}
]
@@ -324,7 +296,7 @@ module.exports = function (grunt) {
},
files: [
{
src: "build/prod/index.html",
src: ["build/prod/index.html"],
dest: `build/prod/CyberChef_v${pkg.version}.html`
}
]
@@ -362,15 +334,15 @@ module.exports = function (grunt) {
command: "git gc --prune=now --aggressive"
},
sitemap: {
command: "node --experimental-modules --no-warnings --no-deprecation src/web/static/sitemap.mjs > build/prod/sitemap.xml",
command: `node ${nodeFlags} src/web/static/sitemap.mjs > build/prod/sitemap.xml`,
sync: true
},
generateConfig: {
command: chainCommands([
"echo '\n--- Regenerating config files. ---'",
"echo [] > src/core/config/OperationConfig.json",
"node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateOpsIndex.mjs",
"node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateConfig.mjs",
`node ${nodeFlags} src/core/config/scripts/generateOpsIndex.mjs`,
`node ${nodeFlags} src/core/config/scripts/generateConfig.mjs`,
"echo '--- Config scripts finished. ---\n'"
]),
sync: true
@@ -378,7 +350,7 @@ module.exports = function (grunt) {
generateNodeIndex: {
command: chainCommands([
"echo '\n--- Regenerating node index ---'",
"node --experimental-modules --no-warnings --no-deprecation src/node/config/scripts/generateNodeIndex.mjs",
`node ${nodeFlags} src/node/config/scripts/generateNodeIndex.mjs`,
"echo '--- Node index generated. ---\n'"
]),
sync: true
@@ -406,24 +378,27 @@ module.exports = function (grunt) {
testCJSNodeConsumer: {
command: chainCommands([
`cd ${nodeConsumerTestPath}`,
"node --no-warnings cjs-consumer.js",
`node ${nodeFlags} cjs-consumer.js`,
]),
stdout: false,
},
testESMNodeConsumer: {
command: chainCommands([
`cd ${nodeConsumerTestPath}`,
"node --no-warnings --experimental-modules esm-consumer.mjs",
]),
stdout: false,
},
testESMDeepImportNodeConsumer: {
command: chainCommands([
`cd ${nodeConsumerTestPath}`,
"node --no-warnings --experimental-modules esm-deep-import-consumer.mjs",
`node ${nodeFlags} esm-consumer.mjs`,
]),
stdout: false,
},
fixCryptoApiImports: {
command: [
`[[ "$OSTYPE" == "darwin"* ]]`,
"&&",
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i '' -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`,
"||",
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`
].join(" "),
stdout: false
}
},
});
};

View File

@@ -1,7 +1,7 @@
# CyberChef
[![Build Status](https://travis-ci.org/gchq/CyberChef.svg?branch=master)](https://travis-ci.org/gchq/CyberChef)
[![dependencies Status](https://david-dm.org/gchq/CyberChef/status.svg)](https://david-dm.org/gchq/CyberChef)
[![](https://github.com/gchq/CyberChef/workflows/Master%20Build,%20Test%20&%20Deploy/badge.svg)](https://github.com/gchq/CyberChef/actions?query=workflow%3A%22Master+Build%2C+Test+%26+Deploy%22)
[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/gchq/CyberChef.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/gchq/CyberChef/context:javascript)
[![npm](https://img.shields.io/npm/v/cyberchef.svg)](https://www.npmjs.com/package/cyberchef)
[![](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/gchq/CyberChef/blob/master/LICENSE)
[![Gitter](https://badges.gitter.im/gchq/CyberChef.svg)](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@@ -9,7 +9,7 @@
#### *The Cyber Swiss Army Knife*
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.
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 and 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.
@@ -54,7 +54,7 @@ You can use as many operations as you like in simple or complex ways. Some examp
- 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.
- 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 that 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.
@@ -66,7 +66,7 @@ You can use as many operations as you like in simple or complex ways. Some examp
- Highlighting
- When you highlight text in the input or output, the offset and length values will be displayed and, if possible, the corresponding data will be highlighted in the output or input respectively (example: [highlight the word 'question' in the input to see where it appears in the output][11]).
- Save to file and load from file
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 2GB are supported (depending on your browser), however some operations may take a very long time to run over this much data.
- You can save the output to a file at any time or load a file by dragging and dropping it into the input field. Files up to around 2GB are supported (depending on your browser), however, some operations may take a very long time to run over this much data.
- CyberChef is entirely client-side
- It should be noted that none of your recipe configuration or input (either text or files) is ever sent to the CyberChef web server - all processing is carried out within your browser, on your own computer.
- Due to this feature, CyberChef can be downloaded and run locally. You can use the link in the top left corner of the app to download a full copy of CyberChef and drop it into a virtual machine, share it with other people, or host it in a closed network.
@@ -74,10 +74,10 @@ You can use as many operations as you like in simple or complex ways. Some examp
## Deep linking
By manipulation of CyberChef's URL hash, you can change the initial settings with which the page opens.
By manipulating CyberChef's URL hash, you can change the initial settings with which the page opens.
The format is `https://gchq.github.io/CyberChef/#recipe=Operation()&input=...`
Supported arguments are `recipe`, `input` (encoded in Base64), and `theme`.
Supported arguments are `recipe`, `input` (encoded in Base64), and `theme`.
## Browser support
@@ -90,12 +90,12 @@ CyberChef is built to support
## Node.js support
CyberChef is built to fully support Node.js `v10` and partially supports `v12`. Named imports using a deep import specifier does not work in `v12`. For more information, see the Node API page in the project [wiki pages](https://github.com/gchq/CyberChef/wiki)
CyberChef is built to fully support Node.js `v16`. For more information, see the Node API page in the project [wiki pages](https://github.com/gchq/CyberChef/wiki)
## 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.
Contributing a new operation to CyberChef is super easy! The quickstart script 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).
@@ -105,7 +105,7 @@ An installation walkthrough, how-to guides for adding new operations and themes,
## Licencing
CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/copyright-and-re-use/crown-copyright/).
CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/).
[1]: https://gchq.github.io/CyberChef

View File

@@ -11,6 +11,7 @@ module.exports = function(api) {
],
"plugins": [
"dynamic-import-node",
"@babel/plugin-syntax-import-assertions",
[
"babel-plugin-transform-builtin-extend", {
"globals": ["Error"]

View File

@@ -10,11 +10,12 @@
"start_process": true,
"server_path": "./node_modules/.bin/chromedriver",
"port": 9515,
"log_path": false
"log_path": "tests/browser/output"
},
"desiredCapabilities": {
"browserName": "chrome"
}
},
"enable_fail_fast": true
},
"dev": {

30158
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "cyberchef",
"version": "9.19.1",
"version": "9.48.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef",
@@ -27,144 +27,159 @@
"type": "git",
"url": "https://github.com/gchq/CyberChef/"
},
"main": "src/node/cjs.js",
"module": "src/node/index.mjs",
"main": "src/node/wrapper.js",
"exports": {
"import": "./src/node/index.mjs",
"require": "./src/node/wrapper.js"
},
"bugs": "https://github.com/gchq/CyberChef/issues",
"browserslist": [
"Chrome >= 50",
"Firefox >= 38",
"node >= 10"
"node >= 16"
],
"devDependencies": {
"@babel/core": "^7.8.7",
"@babel/plugin-transform-runtime": "^7.8.3",
"@babel/preset-env": "^7.8.7",
"autoprefixer": "^9.7.4",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.0.6",
"babel-plugin-dynamic-import-node": "^2.3.0",
"chromedriver": "^80.0.1",
"cli-progress": "^3.6.0",
"@babel/core": "^7.18.2",
"@babel/eslint-parser": "^7.18.2",
"@babel/plugin-syntax-import-assertions": "^7.17.12",
"@babel/plugin-transform-runtime": "^7.18.2",
"@babel/preset-env": "^7.18.2",
"@babel/runtime": "^7.18.3",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"babel-plugin-dynamic-import-node": "^2.3.3",
"babel-plugin-transform-builtin-extend": "1.1.2",
"chromedriver": "^103.0.0",
"cli-progress": "^3.11.1",
"colors": "^1.4.0",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2",
"eslint": "^6.8.0",
"exports-loader": "^0.7.0",
"file-loader": "^6.0.0",
"grunt": "^1.1.0",
"grunt-accessibility": "~6.0.0",
"copy-webpack-plugin": "^11.0.0",
"core-js": "^3.22.8",
"css-loader": "6.7.1",
"eslint": "^8.16.0",
"grunt": "^1.5.3",
"grunt-chmod": "~1.1.1",
"grunt-concurrent": "^3.0.0",
"grunt-contrib-clean": "~2.0.0",
"grunt-contrib-connect": "^2.1.0",
"grunt-contrib-clean": "~2.0.1",
"grunt-contrib-connect": "^3.0.0",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-eslint": "^22.0.0",
"grunt-eslint": "^24.0.0",
"grunt-exec": "~3.0.0",
"grunt-webpack": "^3.1.3",
"grunt-webpack": "^5.0.0",
"grunt-zip": "^0.18.2",
"html-webpack-plugin": "^3.2.0",
"imports-loader": "^0.8.0",
"mini-css-extract-plugin": "^0.9.0",
"nightwatch": "^1.3.4",
"node-sass": "^4.13.1",
"postcss-css-variables": "^0.14.0",
"postcss-import": "^12.0.1",
"postcss-loader": "^3.0.0",
"prompt": "^1.0.0",
"sass-loader": "^8.0.2",
"sitemap": "^6.1.0",
"style-loader": "^1.1.3",
"svg-url-loader": "^5.0.0",
"url-loader": "^4.0.0",
"webpack": "^4.42.0",
"webpack-bundle-analyzer": "^3.6.1",
"webpack-dev-server": "^3.10.3",
"webpack-node-externals": "^1.7.2",
"worker-loader": "^2.0.0"
"html-webpack-plugin": "^5.5.0",
"imports-loader": "^4.0.0",
"mini-css-extract-plugin": "2.6.0",
"modify-source-webpack-plugin": "^3.0.0",
"nightwatch": "^2.1.7",
"postcss": "^8.4.14",
"postcss-css-variables": "^0.18.0",
"postcss-import": "^14.1.0",
"postcss-loader": "^7.0.0",
"prompt": "^1.3.0",
"sitemap": "^7.1.1",
"terser": "^5.14.0",
"webpack": "^5.73.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-dev-server": "4.9.1",
"webpack-node-externals": "^3.0.0",
"worker-loader": "^3.0.8"
},
"dependencies": {
"@babel/polyfill": "^7.8.7",
"@babel/runtime": "^7.8.7",
"@babel/polyfill": "^7.12.1",
"@blu3r4y/lzma": "^2.3.3",
"arrive": "^2.4.1",
"avsc": "^5.4.19",
"babel-plugin-transform-builtin-extend": "1.1.2",
"avsc": "^5.7.4",
"bcryptjs": "^2.4.3",
"bignumber.js": "^9.0.0",
"blakejs": "^1.1.0",
"bootstrap": "4.4.1",
"bootstrap-colorpicker": "^3.2.0",
"bootstrap-material-design": "^4.1.2",
"bson": "^4.0.3",
"bignumber.js": "^9.0.2",
"blakejs": "^1.2.1",
"bootstrap": "4.6.1",
"bootstrap-colorpicker": "^3.4.0",
"bootstrap-material-design": "^4.1.3",
"browserify-zlib": "^0.2.0",
"bson": "^4.6.4",
"buffer": "^6.0.3",
"cbor": "8.1.0",
"chi-squared": "^1.1.0",
"codepage": "^1.14.0",
"core-js": "^3.6.4",
"codepage": "^1.15.0",
"crypto-api": "^0.8.5",
"crypto-js": "^4.0.0",
"crypto-browserify": "^3.12.0",
"crypto-js": "^4.1.1",
"ctph.js": "0.0.5",
"d3": "^5.15.0",
"d3": "7.4.4",
"d3-hexbin": "^0.2.2",
"diff": "^4.0.2",
"es6-promisify": "^6.1.0",
"escodegen": "^1.14.1",
"esm": "^3.2.25",
"esmangle": "^1.0.1",
"diff": "^5.1.0",
"es6-promisify": "^7.0.0",
"escodegen": "^2.0.0",
"esprima": "^4.0.1",
"exif-parser": "^0.1.12",
"file-saver": "^2.0.2",
"geodesy": "^1.1.3",
"highlight.js": "^9.18.1",
"jimp": "^0.9.5",
"jquery": "3.4.1",
"file-saver": "^2.0.5",
"flat": "^5.0.2",
"geodesy": "1.1.3",
"highlight.js": "^11.5.1",
"jimp": "^0.16.1",
"jquery": "3.6.0",
"js-crc": "^0.2.0",
"js-sha3": "^0.8.0",
"jsesc": "^2.5.2",
"jsonpath": "^1.0.2",
"jsesc": "^3.0.2",
"json5": "^2.2.1",
"jsonpath-plus": "^7.2.0",
"jsonwebtoken": "^8.5.1",
"jsqr": "^1.2.0",
"jsrsasign": "8.0.12",
"kbpgp": "2.1.13",
"jsqr": "^1.4.0",
"jsrsasign": "^10.5.23",
"kbpgp": "2.1.15",
"libbzip2-wasm": "0.0.4",
"libyara-wasm": "^1.0.1",
"lodash": "^4.17.15",
"loglevel": "^1.6.7",
"libyara-wasm": "^1.2.1",
"lodash": "^4.17.21",
"loglevel": "^1.8.0",
"loglevel-message-prefix": "^3.0.0",
"markdown-it": "^10.0.0",
"moment": "^2.24.0",
"moment-timezone": "^0.5.28",
"lz-string": "^1.4.4",
"markdown-it": "^13.0.1",
"moment": "^2.29.3",
"moment-timezone": "^0.5.34",
"ngeohash": "^0.6.3",
"node-forge": "^0.9.1",
"node-forge": "^1.3.1",
"node-md6": "^0.1.0",
"nodom": "^2.4.0",
"notepack.io": "^2.3.0",
"notepack.io": "^3.0.1",
"ntlm": "^0.1.3",
"nwmatcher": "^1.4.4",
"otp": "^0.1.3",
"otp": "0.1.3",
"path": "^0.12.7",
"popper.js": "^1.16.1",
"process": "^0.11.10",
"protobufjs": "^6.11.3",
"qr-image": "^3.2.0",
"scryptsy": "^2.1.0",
"snackbarjs": "^1.1.0",
"sortablejs": "^1.10.2",
"split.js": "^1.5.11",
"ssdeep.js": "0.0.2",
"tesseract.js": "^2.0.2",
"ua-parser-js": "^0.7.21",
"sortablejs": "^1.15.0",
"split.js": "^1.6.5",
"ssdeep.js": "0.0.3",
"stream-browserify": "^3.0.0",
"tesseract.js": "3.0.2",
"ua-parser-js": "^1.0.2",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"vkbeautify": "^0.99.3",
"xmldom": "^0.3.0",
"xpath": "0.0.27",
"xregexp": "^4.3.0",
"xmldom": "^0.6.0",
"xpath": "0.0.32",
"xregexp": "^5.1.0",
"zlibjs": "^0.3.1"
},
"scripts": {
"start": "grunt dev",
"build": "grunt prod",
"repl": "node src/node/repl.js",
"test": "grunt configTests && node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs",
"test-node-consumer": "grunt testnodeconsumer",
"testui": "grunt testui",
"start": "npx grunt dev",
"build": "npx grunt prod",
"node": "npx grunt node",
"repl": "node --experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings src/node/repl.mjs",
"test": "npx grunt configTests && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation --openssl-legacy-provider tests/node/index.mjs && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation --openssl-legacy-provider tests/operations/index.mjs",
"testnodeconsumer": "npx grunt testnodeconsumer",
"testui": "npx grunt testui",
"testuidev": "npx nightwatch --env=dev",
"lint": "grunt lint",
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
"lint": "npx grunt lint",
"postinstall": "npx grunt exec:fixCryptoApiImports",
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs",
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
}
}

View File

@@ -7,7 +7,7 @@
*/
import Chef from "./Chef.mjs";
import OperationConfig from "./config/OperationConfig.json";
import OperationConfig from "./config/OperationConfig.json" assert {type: "json"};
import OpModules from "./config/modules/OpModules.mjs";
// Add ">" to the start of all log messages in the Chef Worker
@@ -212,7 +212,7 @@ self.loadRequiredModules = function(recipeConfig) {
if (!(module in OpModules)) {
log.info(`Loading ${module} module`);
self.sendStatusMessage(`Loading ${module} module`);
self.importScripts(`${self.docURL}/modules/${module}.js`);
self.importScripts(`${self.docURL}/modules/${module}.js`); // lgtm [js/client-side-unvalidated-url-redirection]
self.sendStatusMessage("");
}
});

View File

@@ -207,7 +207,7 @@ class Dish {
const data = new Uint8Array(this.value.slice(0, 2048)),
types = detectFileType(data);
if (!types.length || !types[0].mime || !types[0].mime === "text/plain") {
if (!types.length || !types[0].mime || !(types[0].mime === "text/plain")) {
return null;
} else {
return types[0].mime;

View File

@@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import OperationConfig from "./config/OperationConfig.json";
import OperationConfig from "./config/OperationConfig.json" assert {type: "json"};
import OperationError from "./errors/OperationError.mjs";
import Operation from "./Operation.mjs";
import DishError from "./errors/DishError.mjs";
@@ -46,7 +46,7 @@ class Recipe {
module: OperationConfig[c.op].module,
ingValues: c.args,
breakpoint: c.breakpoint,
disabled: c.disabled,
disabled: c.disabled || c.op === "Comment",
});
});
}

View File

@@ -170,13 +170,18 @@ class Utils {
*
* @param {string} str - The input string to display.
* @param {boolean} [preserveWs=false] - Whether or not to print whitespace.
* @param {boolean} [onlyAscii=false] - Whether or not to replace non ASCII characters.
* @returns {string}
*/
static printable(str, preserveWs=false) {
static printable(str, preserveWs=false, onlyAscii=false) {
if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) {
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
}
if (onlyAscii) {
return str.replace(/[^\x20-\x7f]/g, ".");
}
// eslint-disable-next-line no-misleading-character-class
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
@@ -201,7 +206,7 @@ class Utils {
* Utils.parseEscapedChars("\\n");
*/
static parseEscapedChars(str) {
return str.replace(/\\([bfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a) {
return str.replace(/\\([abfnrtv'"]|[0-3][0-7]{2}|[0-7]{1,2}|x[\da-fA-F]{2}|u[\da-fA-F]{4}|u\{[\da-fA-F]{1,6}\}|\\)/g, function(m, a) {
switch (a[0]) {
case "\\":
return "\\";
@@ -214,6 +219,8 @@ class Utils {
case "6":
case "7":
return String.fromCharCode(parseInt(a, 8));
case "a":
return String.fromCharCode(7);
case "b":
return "\b";
case "t":
@@ -704,8 +711,22 @@ class Utils {
* Utils.stripHtmlTags("<div>Test</div>");
*/
static stripHtmlTags(htmlStr, removeScriptAndStyle=false) {
/**
* Recursively remove a pattern from a string until there are no more matches.
* Avoids incomplete sanitization e.g. "aabcbc".replace(/abc/g, "") === "abc"
*
* @param {RegExp} pattern
* @param {string} str
* @returns {string}
*/
function recursiveRemove(pattern, str) {
const newStr = str.replace(pattern, "");
return newStr.length === str.length ? newStr : recursiveRemove(pattern, newStr);
}
if (removeScriptAndStyle) {
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
htmlStr = recursiveRemove(/<script[^>]*>.*?<\/script[^>]*>/gi, htmlStr);
htmlStr = recursiveRemove(/<style[^>]*>.*?<\/style[^>]*>/gi, htmlStr);
}
return htmlStr.replace(/<[^>]+>/g, "");
}
@@ -729,11 +750,10 @@ class Utils {
">": "&gt;",
'"': "&quot;",
"'": "&#x27;", // &apos; not recommended because it's not in the HTML spec
"/": "&#x2F;", // forward slash is included as it helps end an HTML entity
"`": "&#x60;"
};
return str.replace(/[&<>"'/`]/g, function (match) {
return str.replace(/[&<>"'`]/g, function (match) {
return HTML_CHARS[match];
});
}
@@ -878,7 +898,7 @@ class Utils {
while ((m = recipeRegex.exec(recipe))) {
// Translate strings in args back to double-quotes
args = m[2]
args = m[2] // lgtm [js/incomplete-sanitization]
.replace(/"/g, '\\"') // Escape double quotes
.replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
.replace(/([^\\]|(?:\\\\)+)'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
@@ -1189,6 +1209,30 @@ class Utils {
}[token];
}
/**
* Iterate object in chunks of given size.
*
* @param {Iterable} iterable
* @param {number} chunksize
*/
static* chunked(iterable, chunksize) {
const iterator = iterable[Symbol.iterator]();
while (true) {
const res = [];
for (let i = 0; i < chunksize; i++) {
const next = iterator.next();
if (next.done) {
break;
}
res.push(next.value);
}
if (res.length) {
yield res;
} else {
return;
}
}
}
}
/**

58
src/core/config/Categories.json Executable file → Normal file
View File

@@ -18,15 +18,17 @@
"From Binary",
"To Octal",
"From Octal",
"To Base64",
"From Base64",
"Show Base64 offsets",
"To Base32",
"From Base32",
"To Base45",
"From Base45",
"To Base58",
"From Base58",
"To Base62",
"From Base62",
"To Base64",
"From Base64",
"Show Base64 offsets",
"To Base85",
"From Base85",
"To Base",
@@ -61,7 +63,9 @@
"Parse TLV",
"CSV to JSON",
"JSON to CSV",
"Avro to JSON"
"Avro to JSON",
"CBOR Encode",
"CBOR Decode"
]
},
{
@@ -75,12 +79,19 @@
"DES Decrypt",
"Triple DES Encrypt",
"Triple DES Decrypt",
"LS47 Encrypt",
"LS47 Decrypt",
"RC2 Encrypt",
"RC2 Decrypt",
"RC4",
"RC4 Drop",
"SM4 Encrypt",
"SM4 Decrypt",
"ROT13",
"ROT13 Brute Force",
"ROT47",
"ROT47 Brute Force",
"ROT8000",
"XOR",
"XOR Brute Force",
"Vigenère Encode",
@@ -91,6 +102,7 @@
"Bacon Cipher Decode",
"Bifid Cipher Encode",
"Bifid Cipher Decode",
"Caesar Box Cipher",
"Affine Cipher Encode",
"Affine Cipher Decode",
"A1Z26 Cipher Encode",
@@ -100,6 +112,8 @@
"Atbash Cipher",
"CipherSaber2 Encrypt",
"CipherSaber2 Decrypt",
"Cetacean Cipher Encode",
"Cetacean Cipher Decode",
"Substitute",
"Derive PBKDF2 key",
"Derive EVP key",
@@ -116,7 +130,8 @@
"Multiple Bombe",
"Typex",
"Lorenz",
"Colossus"
"Colossus",
"SIGABA"
]
},
{
@@ -134,6 +149,11 @@
"PGP Verify",
"PGP Encrypt and Sign",
"PGP Decrypt and Verify",
"Generate RSA Key Pair",
"RSA Sign",
"RSA Verify",
"RSA Encrypt",
"RSA Decrypt",
"Parse SSH Host Key"
]
},
@@ -164,7 +184,8 @@
"Bit shift right",
"Rotate left",
"Rotate right",
"ROT13"
"ROT13",
"ROT8000"
]
},
{
@@ -178,14 +199,20 @@
"Parse IP range",
"Parse IPv6 address",
"Parse IPv4 header",
"Parse TCP",
"Parse UDP",
"Parse SSH Host Key",
"Parse URI",
"URL Encode",
"URL Decode",
"Protobuf Decode",
"Protobuf Encode",
"VarInt Encode",
"VarInt Decode",
"JA3 Fingerprint",
"JA3S Fingerprint",
"HASSH Client Fingerprint",
"HASSH Server Fingerprint",
"Format MAC addresses",
"Change IP format",
"Group IP addresses",
@@ -200,6 +227,7 @@
"ops": [
"Encode text",
"Decode text",
"Unicode Text Format",
"Remove Diacritics",
"Unescape Unicode Characters",
"Convert to NATO alphabet"
@@ -217,6 +245,7 @@
"From Case Insensitive Regex",
"Add line numbers",
"Remove line numbers",
"Get All Casings",
"To Table",
"Reverse",
"Sort",
@@ -232,6 +261,7 @@
"Pad lines",
"Find / Replace",
"Regular expression",
"Fuzzy Match",
"Offset checker",
"Hamming Distance",
"Convert distance",
@@ -242,6 +272,7 @@
"Convert co-ordinate format",
"Show on map",
"Parse UNIX file permissions",
"Parse ObjectID timestamp",
"Swap endianness",
"Parse colour code",
"Escape string",
@@ -260,6 +291,7 @@
"Windows Filetime to UNIX Timestamp",
"UNIX Timestamp to Windows Filetime",
"Extract dates",
"Get Time",
"Sleep"
]
},
@@ -279,6 +311,7 @@
"JPath expression",
"CSS selector",
"Extract EXIF",
"Extract ID3",
"Extract Files"
]
},
@@ -296,7 +329,11 @@
"Bzip2 Decompress",
"Bzip2 Compress",
"Tar",
"Untar"
"Untar",
"LZString Decompress",
"LZString Compress",
"LZMA Decompress",
"LZMA Compress"
]
},
{
@@ -312,6 +349,7 @@
"SHA1",
"SHA2",
"SHA3",
"SM3",
"Keccak",
"Shake",
"RIPEMD",
@@ -331,6 +369,8 @@
"Bcrypt compare",
"Bcrypt parse",
"Scrypt",
"NT Hash",
"LM Hash",
"Fletcher-8 Checksum",
"Fletcher-16 Checksum",
"Fletcher-32 Checksum",
@@ -388,7 +428,8 @@
"Extract RGBA",
"View Bit Plane",
"Randomize Colour Palette",
"Extract LSB"
"Extract LSB",
"ELF Info"
]
},
{
@@ -431,6 +472,7 @@
"Frequency distribution",
"Index of Coincidence",
"Chi Square",
"P-list Viewer",
"Disassemble x86",
"Pseudo-Random Number Generator",
"Generate UUID",

View File

@@ -0,0 +1,144 @@
/**
* This script updates the CHANGELOG when a new minor version is created.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
/* eslint no-console: ["off"] */
import prompt from "prompt";
import colors from "colors";
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: newMinorVersion.mjs should be run from the project root");
console.log("Example> node --experimental-modules src/core/config/scripts/newMinorVersion.mjs");
process.exit(1);
}
let changelogData = fs.readFileSync(path.join(process.cwd(), "CHANGELOG.md"), "utf8");
const lastVersion = changelogData.match(/## Details\s+### \[(\d+)\.(\d+)\.(\d+)\]/);
const newVersion = [
parseInt(lastVersion[1], 10),
parseInt(lastVersion[2], 10) + 1,
0
];
let knownContributors = changelogData.match(/^\[@([^\]]+)\]/gm);
knownContributors = knownContributors.map(c => c.slice(2, -1));
const date = (new Date()).toISOString().split("T")[0];
const schema = {
properties: {
message: {
description: "A short but descriptive summary of a feature in this version",
example: "Added 'Op name' operation",
prompt: "Feature description",
type: "string",
required: true,
},
author: {
description: "The author of the feature (only one supported, edit manually to add more)",
example: "n1474335",
prompt: "Author",
type: "string",
default: "n1474335"
},
id: {
description: "The PR number or full commit hash for this feature.",
example: "1200",
prompt: "Pull request or commit ID",
type: "string"
},
another: {
description: "y/n",
example: "y",
prompt: "Add another feature?",
type: "string",
pattern: /^[yn]$/,
}
}
};
// 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);
}
prompt.message = "";
prompt.delimiter = ":".green;
const features = [];
const authors = [];
const prIDs = [];
const commitIDs = [];
prompt.start();
const getFeature = function() {
prompt.get(schema, (err, result) => {
if (err) {
console.log("\nExiting script.");
process.exit(0);
}
features.push(result);
if (result.another === "y") {
getFeature();
} else {
let message = `### [${newVersion[0]}.${newVersion[1]}.${newVersion[2]}] - ${date}\n`;
features.forEach(feature => {
const id = feature.id.length > 10 ? feature.id.slice(0, 7) : "#" + feature.id;
message += `- ${feature.message} [@${feature.author}] | [${id}]\n`;
if (!knownContributors.includes(feature.author)) {
authors.push(`[@${feature.author}]: https://github.com/${feature.author}`);
}
if (feature.id.length > 10) {
commitIDs.push(`[${id}]: https://github.com/gchq/CyberChef/commit/${feature.id}`);
} else {
prIDs.push(`[#${feature.id}]: https://github.com/gchq/CyberChef/pull/${feature.id}`);
}
});
// Message
changelogData = changelogData.replace(/## Details\n\n/, "## Details\n\n" + message + "\n");
// Tag
const newTag = `[${newVersion[0]}.${newVersion[1]}.${newVersion[2]}]: https://github.com/gchq/CyberChef/releases/tag/v${newVersion[0]}.${newVersion[1]}.${newVersion[2]}\n`;
changelogData = changelogData.replace(/\n\n(\[\d+\.\d+\.\d+\]: https)/, "\n\n" + newTag + "$1");
// Author
authors.forEach(author => {
changelogData = changelogData.replace(/(\n\[@[^\]]+\]: https:\/\/github\.com\/[^\n]+\n)\n/, "$1" + author + "\n\n");
});
// Commit IDs
commitIDs.forEach(commitID => {
changelogData = changelogData.replace(/(\n\[[^\].]+\]: https:\/\/github.com\/gchq\/CyberChef\/commit\/[^\n]+\n)\n/, "$1" + commitID + "\n\n");
});
// PR IDs
prIDs.forEach(prID => {
changelogData = changelogData.replace(/(\n\[#[^\]]+\]: https:\/\/github.com\/gchq\/CyberChef\/pull\/[^\n]+\n)\n*$/, "$1" + prID + "\n\n");
});
fs.writeFileSync(path.join(process.cwd(), "CHANGELOG.md"), changelogData);
console.log("Written CHANGELOG.md");
}
});
};
getFeature();

View File

@@ -121,7 +121,7 @@ prompt.get(schema, (err, result) => {
const moduleName = result.opName.replace(/\w\S*/g, txt => {
return txt.charAt(0).toUpperCase() + txt.substr(1);
}).replace(/[\s-()/./]/g, "");
}).replace(/[\s-()./]/g, "");
const template = `/**

View File

@@ -17,7 +17,7 @@ class DishJSON extends DishType {
*/
static toArrayBuffer() {
DishJSON.checkForValue(this.value);
this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
this.value = this.value !== undefined ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
}
/**

View File

@@ -1,6 +1,6 @@
import OperationError from "./OperationError.mjs";
import DishError from "./DishError.mjs";
import ExcludedOperationError from "./ExcludedOperationError";
import ExcludedOperationError from "./ExcludedOperationError.mjs";
export {
OperationError,

27
src/core/lib/Base45.mjs Normal file
View File

@@ -0,0 +1,27 @@
/**
* Base45 resources.
*
* @author Thomas Weißschuh [thomas@t-8ch.de]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
/**
* Highlight to Base45
*/
export function highlightToBase45(pos, args) {
pos[0].start = Math.floor(pos[0].start / 2) * 3;
pos[0].end = Math.ceil(pos[0].end / 2) * 3;
return pos;
}
/**
* Highlight from Base45
*/
export function highlightFromBase45(pos, args) {
pos[0].start = Math.floor(pos[0].start / 3) * 2;
pos[0].end = Math.ceil(pos[0].end / 3) * 2;
return pos;
}
export const ALPHABET = "0-9A-Z $%*+\\-./:";

View File

@@ -82,15 +82,46 @@ export function toBase64(data, alphabet="A-Za-z0-9+/=") {
* // returns [72, 101, 108, 108, 111]
* fromBase64("SGVsbG8=", null, "byteArray");
*/
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true) {
export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", removeNonAlphChars=true, strictMode=false) {
if (!data) {
return returnType === "string" ? "" : [];
}
alphabet = alphabet || "A-Za-z0-9+/=";
alphabet = Utils.expandAlphRange(alphabet).join("");
// Confirm alphabet is a valid length
if (alphabet.length !== 64 && alphabet.length !== 65) { // Allow for padding
throw new OperationError(`Invalid Base64 alphabet length (${alphabet.length}): ${alphabet}`);
throw new OperationError(`Error: Base64 alphabet should be 64 characters long, or 65 with a padding character. Found ${alphabet.length}: ${alphabet}`);
}
// Remove non-alphabet characters
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
data = data.replace(re, "");
}
if (strictMode) {
// Check for incorrect lengths (even without padding)
if (data.length % 4 === 1) {
throw new OperationError(`Error: Invalid Base64 input length (${data.length}). Cannot be 4n+1, even without padding chars.`);
}
if (alphabet.length === 65) { // Padding character included
const pad = alphabet.charAt(64);
const padPos = data.indexOf(pad);
if (padPos >= 0) {
// Check that the padding character is only used at the end and maximum of twice
if (padPos < data.length - 2 || data.charAt(data.length - 1) !== pad) {
throw new OperationError(`Error: Base64 padding character (${pad}) not used in the correct place.`);
}
// Check that input is padded to the correct length
if (data.length % 4 !== 0) {
throw new OperationError("Error: Base64 not padded to a multiple of 4.");
}
}
}
}
const output = [];
@@ -98,31 +129,28 @@ export function fromBase64(data, alphabet="A-Za-z0-9+/=", returnType="string", r
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++) || "=");
// Including `|| null` forces empty strings to null so that indexOf returns -1 instead of 0
enc1 = alphabet.indexOf(data.charAt(i++) || null);
enc2 = alphabet.indexOf(data.charAt(i++) || null);
enc3 = alphabet.indexOf(data.charAt(i++) || null);
enc4 = alphabet.indexOf(data.charAt(i++) || null);
enc2 = enc2 === -1 ? 64 : enc2;
enc3 = enc3 === -1 ? 64 : enc3;
enc4 = enc4 === -1 ? 64 : enc4;
if (strictMode && (enc1 < 0 || enc2 < 0 || enc3 < 0 || enc4 < 0)) {
throw new OperationError("Error: Base64 input contains non-alphabet char(s)");
}
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output.push(chr1);
if (enc3 !== 64) {
if (chr1 >= 0 && chr1 < 256) {
output.push(chr1);
}
if (chr2 >= 0 && chr2 < 256 && enc3 !== 64) {
output.push(chr2);
}
if (enc4 !== 64) {
if (chr3 >= 0 && chr3 < 256 && enc4 !== 64) {
output.push(chr3);
}
}
@@ -148,4 +176,8 @@ export const ALPHABET_OPTIONS = [
{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"},
{name: "Atom128: /128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", value: "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC"},
{name: "Megan35: 3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", value: "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"},
{name: "Zong22: ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", value: "ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2"},
{name: "Hazz15: HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", value: "HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5"}
];

View File

@@ -1,3 +1,5 @@
import Utils from "../Utils.mjs";
/**
* Base85 resources.
*
@@ -32,13 +34,12 @@ export const ALPHABET_OPTIONS = [
* @returns {string}
*/
export function alphabetName(alphabet) {
alphabet = alphabet.replace("'", "&apos;");
alphabet = alphabet.replace("\"", "&quot;");
alphabet = alphabet.replace("\\", "&bsol;");
alphabet = escape(alphabet);
let name;
ALPHABET_OPTIONS.forEach(function(a) {
if (escape(alphabet) === escape(a.value)) name = a.name;
const expanded = Utils.expandAlphRange(a.value).join("");
if (alphabet === escape(expanded)) name = a.name;
});
return name;

View File

@@ -7,38 +7,45 @@
*/
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* Convert a byte array into a binary string.
*
* @param {Uint8Array|byteArray} data
* @param {Uint8Array|byteArray|number} data
* @param {string} [delim="Space"]
* @param {number} [padding=8]
* @returns {string}
*
* @example
* // returns "00010000 00100000 00110000"
* // returns "00001010 00010100 00011110"
* toBinary([10,20,30]);
*
* // returns "00010000 00100000 00110000"
* toBinary([10,20,30], ":");
* // returns "00001010:00010100:00011110"
* toBinary([10,20,30], "Colon");
*
* // returns "1010:10100:11110"
* toBinary([10,20,30], "Colon", 0);
*/
export function toBinary(data, delim="Space", padding=8) {
if (!data) return "";
if (data === undefined || data === null)
throw new OperationError("Unable to convert to binary: Empty input data enocuntered");
delim = Utils.charRep(delim);
let output = "";
for (let i = 0; i < data.length; i++) {
output += data[i].toString(2).padStart(padding, "0") + delim;
}
if (delim.length) {
return output.slice(0, -delim.length);
if (data.length) { // array
for (let i = 0; i < data.length; i++) {
output += data[i].toString(2).padStart(padding, "0");
if (i !== data.length - 1) output += delim;
}
} else if (typeof data === "number") { // Single value
return data.toString(2).padStart(padding, "0");
} else {
return output;
return "";
}
return output;
}
@@ -52,12 +59,15 @@ export function toBinary(data, delim="Space", padding=8) {
*
* @example
* // returns [10,20,30]
* fromBinary("00010000 00100000 00110000");
* fromBinary("00001010 00010100 00011110");
*
* // returns [10,20,30]
* fromBinary("00010000:00100000:00110000", "Colon");
* fromBinary("00001010:00010100:00011110", "Colon");
*/
export function fromBinary(data, delim="Space", byteLen=8) {
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
throw new OperationError("Byte length must be a positive integer");
const delimRegex = Utils.regexRep(delim);
data = data.replace(delimRegex, "");

View File

@@ -34,10 +34,10 @@ export function bitOp (input, key, func, nullPreserving, scheme) {
!(nullPreserving && (o === 0 || o === k))) {
switch (scheme) {
case "Input differential":
key[i % key.length] = x;
key[i % key.length] = o;
break;
case "Output differential":
key[i % key.length] = o;
key[i % key.length] = x;
break;
}
}

View File

@@ -38,7 +38,7 @@
const crypto = {};
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
/* dojo-release-1.8.1/dojo/_base/lang.js.uncompressed.js */

View File

@@ -6,6 +6,7 @@
*/
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* @constant
@@ -86,8 +87,8 @@ export function getScatterValues(input, recordDelimiter, fieldDelimiter, columnH
}
values = values.map(row => {
const x = parseFloat(row[0], 10),
y = parseFloat(row[1], 10);
const x = parseFloat(row[0]),
y = parseFloat(row[1]);
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
@@ -121,14 +122,14 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
}
values = values.map(row => {
const x = parseFloat(row[0], 10),
y = parseFloat(row[1], 10),
const x = parseFloat(row[0]),
y = parseFloat(row[1]),
colour = row[2];
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
return [x, y, colour];
return [x, y, Utils.escapeHtml(colour)];
});
return { headings, values };
@@ -157,7 +158,7 @@ export function getSeriesValues(input, recordDelimiter, fieldDelimiter, columnHe
values.forEach(row => {
const serie = row[0],
xVal = row[1],
val = parseFloat(row[2], 10);
val = parseFloat(row[2]);
if (Number.isNaN(val)) throw new OperationError("Values must be numbers in base 10.");

9
src/core/lib/Crypt.mjs Normal file
View File

@@ -0,0 +1,9 @@
/**
* Crypt resources.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
export const cryptNotice = "WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts.";

View File

@@ -12,15 +12,15 @@
*
* @param {string} input
* @param {RegExp} searchRegex
* @param {RegExp} removeRegex - A regular expression defining results to remove from the
* @param {RegExp} [removeRegex=null] - A regular expression defining results to remove from the
* final list
* @param {boolean} includeTotal - Whether or not to include the total number of results
* @param {Function} [sortBy=null] - The sorting comparison function to apply
* @param {boolean} [unique=false] - Whether to unique the results
* @returns {string}
*/
export function search (input, searchRegex, removeRegex, includeTotal) {
let output = "",
total = 0,
match;
export function search(input, searchRegex, removeRegex=null, sortBy=null, unique=false) {
let results = [];
let match;
while ((match = searchRegex.exec(input))) {
// Moves pointer when an empty string is matched (prevents infinite loop)
@@ -30,14 +30,19 @@ export function search (input, searchRegex, removeRegex, includeTotal) {
if (removeRegex && removeRegex.test(match[0]))
continue;
total++;
output += match[0] + "\n";
results.push(match[0]);
}
if (includeTotal)
output = "Total found: " + total + "\n\n" + output;
if (sortBy) {
results = results.sort(sortBy);
}
return output;
if (unique) {
results = results.unique();
}
return results;
}

View File

@@ -70,7 +70,7 @@ export const FILE_SIGNATURES = {
10: 0x42,
11: 0x50
},
extractor: null
extractor: extractWEBP
},
{
name: "Camera Image File Format",
@@ -468,6 +468,34 @@ export const FILE_SIGNATURES = {
],
extractor: null
},
{
name: "Targa Image",
extension: "tga",
mime: "image/x-targa",
description: "",
signature: [
{ // This signature is not at the beginning of the file. The extractor works backwards.
0: 0x54,
1: 0x52,
2: 0x55,
3: 0x45,
4: 0x56,
5: 0x49,
6: 0x53,
7: 0x49,
8: 0x4f,
9: 0x4e,
10: 0x2d,
11: 0x58,
12: 0x46,
13: 0x49,
14: 0x4c,
15: 0x45,
16: 0x2e
}
],
extractor: extractTARGA
}
],
"Video": [
{ // Place before webm
@@ -2169,14 +2197,14 @@ export const FILE_SIGNATURES = {
mime: "application/octet-stream",
description: "",
signature: {
0: 0x6b, // keych
0: 0x6b, // kych
1: 0x79,
2: 0x63,
3: 0x68,
4: 0x00,
5: 0x01
},
extractor: null
extractor: extractMacOSXKeychain
},
{
name: "TCP Packet",
@@ -2327,6 +2355,12 @@ export const FILE_SIGNATURES = {
1: 0x03,
2: 0xc6,
3: 0x04
},
{
0: 0x95,
1: 0x05,
2: 0x86,
3: 0x04
}
],
extractor: null
@@ -2998,6 +3032,30 @@ export function extractPNG(bytes, offset) {
}
/**
* WEBP extractor.
*
* @param {Uint8Array} bytes
* @param {number} offset
* @returns {Uint8Array}
*/
export function extractWEBP(bytes, offset) {
const stream = new Stream(bytes.slice(offset));
// Move to file size offset.
stream.moveForwardsBy(4);
// Read file size field.
const fileSize = stream.readInt(4, "le");
// Move to end of file.
// There is no need to minus 8 from the size as the size factors in the offset.
stream.moveForwardsBy(fileSize);
return stream.carve();
}
/**
* BMP extractor.
*
@@ -3047,6 +3105,90 @@ export function extractICO(bytes, offset) {
}
/**
* TARGA extractor.
*
* @param {Uint8Array} bytes
* @param {number} offset
*/
export function extractTARGA(bytes, offset) {
// Need all the bytes since we do not know how far up the image goes.
const stream = new Stream(bytes);
stream.moveTo(offset - 8);
// Read in the offsets of the possible areas.
const extensionOffset = stream.readInt(4, "le");
const developerOffset = stream.readInt(4, "le");
stream.moveBackwardsBy(8);
/**
* Moves backwards in the stream until it meet bytes that are the same as the amount of bytes moved.
*
* @param {number} sizeOfSize
* @param {number} maxSize
*/
function moveBackwardsUntilSize(maxSize, sizeOfSize) {
for (let i = 0; i < maxSize; i++) {
stream.moveBackwardsBy(1);
// Read in sizeOfSize amount of bytes in.
const size = stream.readInt(sizeOfSize, "le") - 1;
stream.moveBackwardsBy(sizeOfSize);
// If the size matches.
if (size === i)
break;
}
}
/**
* Moves backwards in the stream until we meet bytes(when calculated) that are the same as the amount of bytes moved.
*/
function moveBackwardsUntilImageSize() {
stream.moveBackwardsBy(5);
// The documentation said that 0x100000 was the largest the file could be.
for (let i = 0; i < 0x100000; i++) {
// (Height * Width * pixel depth in bits)/8
const total = (stream.readInt(2, "le") * stream.readInt(2, "le") * stream.readInt(1))/8;
if (total === i-1)
break;
stream.moveBackwardsBy(6);
}
}
if (extensionOffset || developerOffset) {
if (extensionOffset) {
// Size is stored in two bytes hence the maximum is 0xffff.
moveBackwardsUntilSize(0xffff, 2);
// Move to where we think the start of the file is.
stream.moveBackwardsBy(extensionOffset);
} else if (developerOffset) {
// Size is stored in 4 bytes hence the maxiumum is 0xffffffff.
moveBackwardsUntilSize(0xffffffff, 4);
// Size is stored in byte position 6 so have to move back.
stream.moveBackwardsBy(6);
// Move to where we think the start of the file is.
stream.moveBackwardsBy(developerOffset);
}
} else {
// Move backwards until size === number of bytes passed.
moveBackwardsUntilImageSize();
// Move backwards over the reaminder of the header + the 5 we borrowed in moveBackwardsUntilImageSize().
stream.moveBackwardsBy(0xc+5);
}
return stream.carve(stream.position, offset+0x12);
}
/**
* WAV extractor.
*
@@ -3294,6 +3436,26 @@ export function extractPListXML(bytes, offset) {
}
/**
* MacOS X Keychain Extactor.
*
* @param {Uint8Array} bytes
* @param {number} offset
* @returns {Uint8Array}
*/
export function extractMacOSXKeychain(bytes, offset) {
const stream = new Stream(bytes.slice(offset));
// Move to size field.
stream.moveTo(0x14);
// Move forwards by size.
stream.moveForwardsBy(stream.readInt(4));
return stream.carve();
}
/**
* OLE2 extractor.
*
@@ -3640,8 +3802,8 @@ function parseDEFLATE(stream) {
while (!finalBlock) {
// Read header
finalBlock = stream.readBits(1);
const blockType = stream.readBits(2);
finalBlock = stream.readBits(1, "le");
const blockType = stream.readBits(2, "le");
if (blockType === 0) {
/* No compression */
@@ -3660,16 +3822,16 @@ function parseDEFLATE(stream) {
/* Dynamic Huffman */
// Read the number of liternal and length codes
const hlit = stream.readBits(5) + 257;
const hlit = stream.readBits(5, "le") + 257;
// Read the number of distance codes
const hdist = stream.readBits(5) + 1;
const hdist = stream.readBits(5, "le") + 1;
// Read the number of code lengths
const hclen = stream.readBits(4) + 4;
const hclen = stream.readBits(4, "le") + 4;
// Parse code lengths
const codeLengths = new Uint8Array(huffmanOrder.length);
for (let i = 0; i < hclen; i++) {
codeLengths[huffmanOrder[i]] = stream.readBits(3);
codeLengths[huffmanOrder[i]] = stream.readBits(3, "le");
}
// Parse length table
@@ -3681,16 +3843,16 @@ function parseDEFLATE(stream) {
code = readHuffmanCode(stream, codeLengthsTable);
switch (code) {
case 16:
repeat = 3 + stream.readBits(2);
repeat = 3 + stream.readBits(2, "le");
while (repeat--) lengthTable[i++] = prev;
break;
case 17:
repeat = 3 + stream.readBits(3);
repeat = 3 + stream.readBits(3, "le");
while (repeat--) lengthTable[i++] = 0;
prev = 0;
break;
case 18:
repeat = 11 + stream.readBits(7);
repeat = 11 + stream.readBits(7, "le");
while (repeat--) lengthTable[i++] = 0;
prev = 0;
break;
@@ -3748,11 +3910,11 @@ function parseHuffmanBlock(stream, litTab, distTab) {
if (code < 256) continue;
// Length code
stream.readBits(lengthExtraTable[code - 257]);
stream.readBits(lengthExtraTable[code - 257], "le");
// Dist code
code = readHuffmanCode(stream, distTab);
stream.readBits(distanceExtraTable[code]);
stream.readBits(distanceExtraTable[code], "le");
}
}
@@ -3810,7 +3972,7 @@ function readHuffmanCode(stream, table) {
const [codeTable, maxCodeLength] = table;
// Read max length
const bitsBuf = stream.readBits(maxCodeLength);
const bitsBuf = stream.readBits(maxCodeLength, "le");
const codeWithLength = codeTable[bitsBuf & ((1 << maxCodeLength) - 1)];
const codeLength = codeWithLength >>> 16;

View File

@@ -213,7 +213,7 @@ function locatePotentialSig(buf, sig, offset) {
export function isType(type, buf) {
const types = detectFileType(buf);
if (!(types && types.length)) return false;
if (!types.length) return false;
if (typeof type === "string") {
return types.reduce((acc, t) => {

253
src/core/lib/FuzzyMatch.mjs Normal file
View File

@@ -0,0 +1,253 @@
/**
* LICENSE
*
* This software is dual-licensed to the public domain and under the following
* license: you are granted a perpetual, irrevocable license to copy, modify,
* publish, and distribute this file as you see fit.
*
* VERSION
* 0.1.0 (2016-03-28) Initial release
*
* AUTHOR
* Forrest Smith
*
* CONTRIBUTORS
* J<>rgen Tjern<72> - async helper
* Anurag Awasthi - updated to 0.2.0
*/
export const DEFAULT_WEIGHTS = {
sequentialBonus: 15, // bonus for adjacent matches
separatorBonus: 30, // bonus if match occurs after a separator
camelBonus: 30, // bonus if match is uppercase and prev is lower
firstLetterBonus: 15, // bonus if the first letter is matched
leadingLetterPenalty: -5, // penalty applied for every letter in str before the first match
maxLeadingLetterPenalty: -15, // maximum penalty for leading letters
unmatchedLetterPenalty: -1
};
/**
* Does a fuzzy search to find pattern inside a string.
* @param {string} pattern pattern to search for
* @param {string} str string which is being searched
* @param {boolean} global whether to search for all matches or just one
* @returns [boolean, number] a boolean which tells if pattern was
* found or not and a search score
*/
export function fuzzyMatch(pattern, str, global=false, weights=DEFAULT_WEIGHTS) {
const recursionCount = 0;
const recursionLimit = 10;
const matches = [];
const maxMatches = 256;
if (!global) {
return fuzzyMatchRecursive(
pattern,
str,
0 /* patternCurIndex */,
0 /* strCurrIndex */,
null /* srcMatches */,
matches,
maxMatches,
0 /* nextMatch */,
recursionCount,
recursionLimit,
weights
);
}
// Return all matches
let foundMatch = true,
score,
idxs,
strCurrIndex = 0;
const results = [];
while (foundMatch) {
[foundMatch, score, idxs] = fuzzyMatchRecursive(
pattern,
str,
0 /* patternCurIndex */,
strCurrIndex,
null /* srcMatches */,
matches,
maxMatches,
0 /* nextMatch */,
recursionCount,
recursionLimit,
weights
);
if (foundMatch) results.push([foundMatch, score, [...idxs]]);
strCurrIndex = idxs[idxs.length - 1] + 1;
}
return results;
}
/**
* Recursive helper function
*/
function fuzzyMatchRecursive(
pattern,
str,
patternCurIndex,
strCurrIndex,
srcMatches,
matches,
maxMatches,
nextMatch,
recursionCount,
recursionLimit,
weights
) {
let outScore = 0;
// Return if recursion limit is reached.
if (++recursionCount >= recursionLimit) {
return [false, outScore, []];
}
// Return if we reached ends of strings.
if (patternCurIndex === pattern.length || strCurrIndex === str.length) {
return [false, outScore, []];
}
// Recursion params
let recursiveMatch = false;
let bestRecursiveMatches = [];
let bestRecursiveScore = 0;
// Loop through pattern and str looking for a match.
let firstMatch = true;
while (patternCurIndex < pattern.length && strCurrIndex < str.length) {
// Match found.
if (
pattern[patternCurIndex].toLowerCase() === str[strCurrIndex].toLowerCase()
) {
if (nextMatch >= maxMatches) {
return [false, outScore, []];
}
if (firstMatch && srcMatches) {
matches = [...srcMatches];
firstMatch = false;
}
const [matched, recursiveScore, recursiveMatches] = fuzzyMatchRecursive(
pattern,
str,
patternCurIndex,
strCurrIndex + 1,
matches,
recursiveMatches,
maxMatches,
nextMatch,
recursionCount,
recursionLimit,
weights
);
if (matched) {
// Pick best recursive score.
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
bestRecursiveMatches = [...recursiveMatches];
bestRecursiveScore = recursiveScore;
}
recursiveMatch = true;
}
matches[nextMatch++] = strCurrIndex;
++patternCurIndex;
}
++strCurrIndex;
}
const matched = patternCurIndex === pattern.length;
if (matched) {
outScore = 100;
// Apply leading letter penalty
let penalty = weights.leadingLetterPenalty * matches[0];
penalty =
penalty < weights.maxLeadingLetterPenalty ?
weights.maxLeadingLetterPenalty :
penalty;
outScore += penalty;
// Apply unmatched penalty
const unmatched = str.length - nextMatch;
outScore += weights.unmatchedLetterPenalty * unmatched;
// Apply ordering bonuses
for (let i = 0; i < nextMatch; i++) {
const currIdx = matches[i];
if (i > 0) {
const prevIdx = matches[i - 1];
if (currIdx === prevIdx + 1) {
outScore += weights.sequentialBonus;
}
}
// Check for bonuses based on neighbor character value.
if (currIdx > 0) {
// Camel case
const neighbor = str[currIdx - 1];
const curr = str[currIdx];
if (
neighbor !== neighbor.toUpperCase() &&
curr !== curr.toLowerCase()
) {
outScore += weights.camelBonus;
}
const isNeighbourSeparator = neighbor === "_" || neighbor === " ";
if (isNeighbourSeparator) {
outScore += weights.separatorBonus;
}
} else {
// First letter
outScore += weights.firstLetterBonus;
}
}
// Return best result
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
// Recursive score is better than "this"
matches = bestRecursiveMatches;
outScore = bestRecursiveScore;
return [true, outScore, matches];
} else if (matched) {
// "this" score is better than recursive
return [true, outScore, matches];
} else {
return [false, outScore, matches];
}
}
return [false, outScore, matches];
}
/**
* Turns a list of match indexes into a list of match ranges
*
* @author n1474335 [n1474335@gmail.com]
* @param [number] matches
* @returns [[number]]
*/
export function calcMatchRanges(matches) {
const ranges = [];
let start = matches[0],
curr = start;
matches.forEach(m => {
if (m === curr || m === curr + 1) curr = m;
else {
ranges.push([start, curr - start + 1]);
start = m;
curr = m;
}
});
ranges.push([start, curr - start + 1]);
return ranges;
}

View File

@@ -7,6 +7,7 @@
*/
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
@@ -100,6 +101,9 @@ export function toHexFast(data) {
* fromHex("0a:14:1e", "Colon");
*/
export function fromHex(data, delim="Auto", byteLen=2) {
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
throw new OperationError("Byte length must be a positive integer");
if (delim !== "None") {
const delimRegex = delim === "Auto" ? /[^a-f\d]|(0x)/gi : Utils.regexRep(delim);
data = data.replace(delimRegex, "");

24
src/core/lib/JWT.mjs Normal file
View File

@@ -0,0 +1,24 @@
/**
* JWT resources
*
* @author mt3571 [mt3571@protonmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
/**
* List of the JWT algorithms that can be used
*/
export const JWT_ALGORITHMS = [
"HS256",
"HS384",
"HS512",
"RS256",
"RS384",
"RS512",
"ES256",
"ES384",
"ES512",
"None"
];

244
src/core/lib/LS47.mjs Normal file
View File

@@ -0,0 +1,244 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
const letters = "_abcdefghijklmnopqrstuvwxyz.0123456789,-+*/:?!'()";
const tiles = [];
/**
* Initialises the tiles with values and positions.
*/
export function initTiles() {
for (let i = 0; i < 49; i++)
tiles.push([letters.charAt(i), [Math.floor(i/7), i % 7]]);
}
/**
* Rotates the key "down".
*
* @param {string} key
* @param {number} col
* @param {number} n
* @returns {string}
*/
function rotateDown(key, col, n) {
const lines = [];
for (let i = 0; i < 7; i++)
lines.push(key.slice(i*7, (i + 1) * 7));
const lefts = [];
let mids = [];
const rights = [];
lines.forEach((element) => {
lefts.push(element.slice(0, col));
mids.push(element.charAt(col));
rights.push(element.slice(col+1));
});
n = (7 - n % 7) % 7;
mids = mids.slice(n).concat(mids.slice(0, n));
let result = "";
for (let i = 0; i < 7; i++)
result += lefts[i] + mids[i] + rights[i];
return result;
}
/**
* Rotates the key "right".
*
* @param {string} key
* @param {number} row
* @param {number} n
* @returns {string}
*/
function rotateRight(key, row, n) {
const mid = key.slice(row * 7, (row + 1) * 7);
n = (7 - n % 7) % 7;
return key.slice(0, 7 * row) + mid.slice(n) + mid.slice(0, n) + key.slice(7 * (row + 1));
}
/**
* Finds the position of a letter in the tiles.
*
* @param {string} letter
* @returns {string}
*/
function findIx(letter) {
for (let i = 0; i < tiles.length; i++)
if (tiles[i][0] === letter)
return tiles[i][1];
throw new OperationError("Letter " + letter + " is not included in LS47");
}
/**
* Derives key from the input password.
*
* @param {string} password
* @returns {string}
*/
export function deriveKey(password) {
let i = 0;
let k = letters;
for (const c of password) {
const [row, col] = findIx(c);
k = rotateDown(rotateRight(k, i, col), i, row);
i = (i + 1) % 7;
}
return k;
}
/**
* Checks the key is a valid key.
*
* @param {string} key
*/
function checkKey(key) {
if (key.length !== letters.length)
throw new OperationError("Wrong key size");
const counts = new Array();
for (let i = 0; i < letters.length; i++)
counts[letters.charAt(i)] = 0;
for (const elem of letters) {
if (letters.indexOf(elem) === -1)
throw new OperationError("Letter " + elem + " not in LS47");
counts[elem]++;
if (counts[elem] > 1)
throw new OperationError("Letter duplicated in the key");
}
}
/**
* Finds the position of a letter in they key.
*
* @param {letter} key
* @param {string} letter
* @returns {object}
*/
function findPos (key, letter) {
const index = key.indexOf(letter);
if (index >= 0 && index < 49)
return [Math.floor(index/7), index%7];
throw new OperationError("Letter " + letter + " is not in the key");
}
/**
* Returns the character at the position on the tiles.
*
* @param {string} key
* @param {object} coord
* @returns {string}
*/
function findAtPos(key, coord) {
return key.charAt(coord[1] + (coord[0] * 7));
}
/**
* Returns new position by adding two positions.
*
* @param {object} a
* @param {object} b
* @returns {object}
*/
function addPos(a, b) {
return [(a[0] + b[0]) % 7, (a[1] + b[1]) % 7];
}
/**
* Returns new position by subtracting two positions.
* Note: We have to manually do the remainder division, since JS does not
* operate correctly on negative numbers (e.g. -3 % 4 = -3 when it should be 1).
*
* @param {object} a
* @param {object} b
* @returns {object}
*/
function subPos(a, b) {
const asub = a[0] - b[0];
const bsub = a[1] - b[1];
return [asub - (Math.floor(asub/7) * 7), bsub - (Math.floor(bsub/7) * 7)];
}
/**
* Encrypts the plaintext string.
*
* @param {string} key
* @param {string} plaintext
* @returns {string}
*/
function encrypt(key, plaintext) {
checkKey(key);
let mp = [0, 0];
let ciphertext = "";
for (const p of plaintext) {
const pp = findPos(key, p);
const mix = findIx(findAtPos(key, mp));
let cp = addPos(pp, mix);
const c = findAtPos(key, cp);
ciphertext += c;
key = rotateRight(key, pp[0], 1);
cp = findPos(key, c);
key = rotateDown(key, cp[1], 1);
mp = addPos(mp, findIx(c));
}
return ciphertext;
}
/**
* Decrypts the ciphertext string.
*
* @param {string} key
* @param {string} ciphertext
* @returns {string}
*/
function decrypt(key, ciphertext) {
checkKey(key);
let mp = [0, 0];
let plaintext = "";
for (const c of ciphertext) {
let cp = findPos(key, c);
const mix = findIx(findAtPos(key, mp));
const pp = subPos(cp, mix);
const p = findAtPos(key, pp);
plaintext += p;
key = rotateRight(key, pp[0], 1);
cp = findPos(key, c);
key = rotateDown(key, cp[1], 1);
mp = addPos(mp, findIx(c));
}
return plaintext;
}
/**
* Adds padding to the input.
*
* @param {string} key
* @param {string} plaintext
* @param {string} signature
* @param {number} paddingSize
* @returns {string}
*/
export function encryptPad(key, plaintext, signature, paddingSize) {
initTiles();
checkKey(key);
let padding = "";
for (let i = 0; i < paddingSize; i++) {
padding += letters.charAt(Math.floor(Math.random() * letters.length));
}
return encrypt(key, padding+plaintext+"---"+signature);
}
/**
* Removes padding from the ouput.
*
* @param {string} key
* @param {string} ciphertext
* @param {number} paddingSize
* @returns {string}
*/
export function decryptPad(key, ciphertext, paddingSize) {
initTiles();
checkKey(key);
return decrypt(key, ciphertext).slice(paddingSize);
}

21
src/core/lib/LZString.mjs Normal file
View File

@@ -0,0 +1,21 @@
/**
* lz-string exports.
*
* @author crespyl [peter@crespyl.net]
* @copyright Peter Jacobs 2021
* @license Apache-2.0
*/
import LZString from "lz-string";
export const COMPRESSION_OUTPUT_FORMATS = ["default", "UTF16", "Base64"];
export const COMPRESSION_FUNCTIONS = {
"default": LZString.compress,
"UTF16": LZString.compressToUTF16,
"Base64": LZString.compressToBase64,
};
export const DECOMPRESSION_FUNCTIONS = {
"default": LZString.decompress,
"UTF16": LZString.decompressFromUTF16,
"Base64": LZString.decompressFromBase64,
};

View File

@@ -1,4 +1,4 @@
import OperationConfig from "../config/OperationConfig.json";
import OperationConfig from "../config/OperationConfig.json" assert {type: "json"};
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
import Recipe from "../Recipe.mjs";
import Dish from "../Dish.mjs";

View File

@@ -1,4 +1,5 @@
import Utils from "../Utils.mjs";
import protobuf from "protobufjs";
/**
* Protobuf lib. Contains functions to decode protobuf serialised
@@ -32,9 +33,10 @@ class Protobuf {
this.MSB = 0x80;
this.VALUE = 0x7f;
// Declare offset and length
// Declare offset, length, and field type object
this.offset = 0;
this.LENGTH = data.length;
this.fieldTypes = {};
}
// Public Functions
@@ -76,15 +78,281 @@ class Protobuf {
return pb._varInt();
}
/**
* Encode input JSON according to the given schema
*
* @param {Object} input
* @param {Object []} args
* @returns {Object}
*/
static encode(input, args) {
this.updateProtoRoot(args[0]);
if (!this.mainMessageName) {
throw new Error("Schema Error: Schema not defined");
}
const message = this.parsedProto.root.nested[this.mainMessageName];
// Convert input into instance of message, and verify instance
input = message.fromObject(input);
const error = message.verify(input);
if (error) {
throw new Error("Input Error: " + error);
}
// Encode input
const output = message.encode(input).finish();
return new Uint8Array(output).buffer;
}
/**
* Parse Protobuf data
*
* @param {byteArray} input
* @returns {Object}
*/
static decode(input) {
static decode(input, args) {
this.updateProtoRoot(args[0]);
this.showUnknownFields = args[1];
this.showTypes = args[2];
return this.mergeDecodes(input);
}
/**
* Update the parsedProto, throw parsing errors
*
* @param {string} protoText
*/
static updateProtoRoot(protoText) {
try {
this.parsedProto = protobuf.parse(protoText);
if (this.parsedProto.package) {
this.parsedProto.root = this.parsedProto.root.nested[this.parsedProto.package];
}
this.updateMainMessageName();
} catch (error) {
throw new Error("Schema " + error);
}
}
/**
* Set mainMessageName to the first instance of a message defined in the schema that is not a submessage
*
*/
static updateMainMessageName() {
const messageNames = [];
const fieldTypes = [];
this.parsedProto.root.nestedArray.forEach(block => {
if (block instanceof protobuf.Type) {
messageNames.push(block.name);
this.parsedProto.root.nested[block.name].fieldsArray.forEach(field => {
fieldTypes.push(field.type);
});
}
});
if (messageNames.length === 0) {
this.mainMessageName = null;
} else {
// for (const name of messageNames) {
// if (!fieldTypes.includes(name)) {
// this.mainMessageName = name;
// break;
// }
// }
this.mainMessageName = messageNames[0];
}
}
/**
* Decode input using Protobufjs package and raw methods, compare, and merge results
*
* @param {byteArray} input
* @returns {Object}
*/
static mergeDecodes(input) {
const pb = new Protobuf(input);
return pb._parse();
let rawDecode = pb._parse();
let message;
if (this.showTypes) {
rawDecode = this.showRawTypes(rawDecode, pb.fieldTypes);
this.parsedProto.root = this.appendTypesToFieldNames(this.parsedProto.root);
}
try {
message = this.parsedProto.root.nested[this.mainMessageName];
const packageDecode = message.toObject(message.decode(input), {
bytes: String,
longs: Number,
enums: String,
defaults: true
});
const output = {};
if (this.showUnknownFields) {
output[message.name] = packageDecode;
output["Unknown Fields"] = this.compareFields(rawDecode, message);
return output;
} else {
return packageDecode;
}
} catch (error) {
if (message) {
throw new Error("Input " + error);
} else {
return rawDecode;
}
}
}
/**
* Replace fieldnames with fieldname and type
*
* @param {Object} schemaRoot
* @returns {Object}
*/
static appendTypesToFieldNames(schemaRoot) {
for (const block of schemaRoot.nestedArray) {
if (block instanceof protobuf.Type) {
for (const [fieldName, fieldData] of Object.entries(block.fields)) {
schemaRoot.nested[block.name].remove(block.fields[fieldName]);
schemaRoot.nested[block.name].add(new protobuf.Field(`${fieldName} (${fieldData.type})`, fieldData.id, fieldData.type, fieldData.rule));
}
}
}
return schemaRoot;
}
/**
* Add field type to field name for fields in the raw decoded output
*
* @param {Object} rawDecode
* @param {Object} fieldTypes
* @returns {Object}
*/
static showRawTypes(rawDecode, fieldTypes) {
for (const [fieldNum, value] of Object.entries(rawDecode)) {
const fieldType = fieldTypes[fieldNum];
let outputFieldValue;
let outputFieldType;
// Submessages
if (isNaN(fieldType)) {
outputFieldType = 2;
// Repeated submessages
if (Array.isArray(value)) {
const fieldInstances = [];
for (const instance of Object.keys(value)) {
if (typeof(value[instance]) !== "string") {
fieldInstances.push(this.showRawTypes(value[instance], fieldType));
} else {
fieldInstances.push(value[instance]);
}
}
outputFieldValue = fieldInstances;
// Single submessage
} else {
outputFieldValue = this.showRawTypes(value, fieldType);
}
// Non-submessage field
} else {
outputFieldType = fieldType;
outputFieldValue = value;
}
// Substitute fieldNum with field number and type
rawDecode[`field #${fieldNum}: ${this.getTypeInfo(outputFieldType)}`] = outputFieldValue;
delete rawDecode[fieldNum];
}
return rawDecode;
}
/**
* Compare raw decode to package decode and return discrepancies
*
* @param rawDecodedMessage
* @param schemaMessage
* @returns {Object}
*/
static compareFields(rawDecodedMessage, schemaMessage) {
// Define message data using raw decode output and schema
const schemaFieldProperties = {};
const schemaFieldNames = Object.keys(schemaMessage.fields);
schemaFieldNames.forEach(field => schemaFieldProperties[schemaMessage.fields[field].id] = field);
// Loop over each field present in the raw decode output
for (const fieldName in rawDecodedMessage) {
let fieldId;
if (isNaN(fieldName)) {
fieldId = fieldName.match(/^field #(\d+)/)[1];
} else {
fieldId = fieldName;
}
// Check if this field is defined in the schema
if (fieldId in schemaFieldProperties) {
const schemaFieldName = schemaFieldProperties[fieldId];
// Extract the current field data from the raw decode and schema
const rawFieldData = rawDecodedMessage[fieldName];
const schemaField = schemaMessage.fields[schemaFieldName];
// Check for repeated fields
if (Array.isArray(rawFieldData) && !schemaField.repeated) {
rawDecodedMessage[`(${schemaMessage.name}) ${schemaFieldName} is a repeated field`] = rawFieldData;
}
// Check for submessage fields
if (schemaField.resolvedType instanceof protobuf.Type) {
const subMessageType = schemaMessage.fields[schemaFieldName].type;
const schemaSubMessage = this.parsedProto.root.nested[subMessageType];
const rawSubMessages = rawDecodedMessage[fieldName];
let rawDecodedSubMessage = {};
// Squash multiple submessage instances into one submessage
if (Array.isArray(rawSubMessages)) {
rawSubMessages.forEach(subMessageInstance => {
const instanceFields = Object.entries(subMessageInstance);
instanceFields.forEach(subField => {
rawDecodedSubMessage[subField[0]] = subField[1];
});
});
} else {
rawDecodedSubMessage = rawSubMessages;
}
// Treat submessage as own message and compare its fields
rawDecodedSubMessage = Protobuf.compareFields(rawDecodedSubMessage, schemaSubMessage);
if (Object.entries(rawDecodedSubMessage).length !== 0) {
rawDecodedMessage[`${schemaFieldName} (${subMessageType}) has missing fields`] = rawDecodedSubMessage;
}
}
delete rawDecodedMessage[fieldName];
}
}
return rawDecodedMessage;
}
/**
* Returns wiretype information for input wiretype number
*
* @param {number} wireType
* @returns {string}
*/
static getTypeInfo(wireType) {
switch (wireType) {
case 0:
return "VarInt (e.g. int32, bool)";
case 1:
return "64-Bit (e.g. fixed64, double)";
case 2:
return "L-delim (e.g. string, message)";
case 5:
return "32-Bit (e.g. fixed32, float)";
}
}
// Private Class Functions
@@ -143,6 +411,11 @@ class Protobuf {
const header = this._fieldHeader();
const type = header.type;
const key = header.key;
if (typeof(this.fieldTypes[key]) !== "object") {
this.fieldTypes[key] = type;
}
switch (type) {
// varint
case 0:
@@ -152,7 +425,7 @@ class Protobuf {
return { "key": key, "value": this._uint64() };
// length delimited
case 2:
return { "key": key, "value": this._lenDelim() };
return { "key": key, "value": this._lenDelim(key) };
// fixed 32
case 5:
return { "key": key, "value": this._uint32() };
@@ -237,10 +510,10 @@ class Protobuf {
* @returns {number}
*/
_uint64() {
// Read off a Uint64
let num = this.data[this.offset++] * 0x1000000 + (this.data[this.offset++] << 16) + (this.data[this.offset++] << 8) + this.data[this.offset++];
num = num * 0x100000000 + this.data[this.offset++] * 0x1000000 + (this.data[this.offset++] << 16) + (this.data[this.offset++] << 8) + this.data[this.offset++];
return num;
// Read off a Uint64 with little-endian
const lowerHalf = this.data[this.offset++] + (this.data[this.offset++] * 0x100) + (this.data[this.offset++] * 0x10000) + this.data[this.offset++] * 0x1000000;
const upperHalf = this.data[this.offset++] + (this.data[this.offset++] * 0x100) + (this.data[this.offset++] * 0x10000) + this.data[this.offset++] * 0x1000000;
return upperHalf * 0x100000000 + lowerHalf;
}
/**
@@ -249,7 +522,7 @@ class Protobuf {
* @private
* @returns {Object|string}
*/
_lenDelim() {
_lenDelim(fieldNum) {
// Read off the field length
const length = this._varInt();
const fieldBytes = this.data.slice(this.offset, this.offset + length);
@@ -258,6 +531,10 @@ class Protobuf {
// Attempt to parse as a new Protobuf Object
const pbObject = new Protobuf(fieldBytes);
field = pbObject._parse();
// Set field types object
this.fieldTypes[fieldNum] = {...this.fieldTypes[fieldNum], ...pbObject.fieldTypes};
} catch (err) {
// Otherwise treat as bytes
field = Utils.byteArrayToChars(fieldBytes);
@@ -276,7 +553,7 @@ class Protobuf {
_uint32() {
// Use a dataview to read off the integer
const dataview = new DataView(new Uint8Array(this.data.slice(this.offset, this.offset + 4)).buffer);
const value = dataview.getUint32(0);
const value = dataview.getUint32(0, true);
this.offset += 4;
return value;
}

47
src/core/lib/Protocol.mjs Normal file
View File

@@ -0,0 +1,47 @@
/**
* Protocol parsing functions.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import BigNumber from "bignumber.js";
import {toHexFast} from "../lib/Hex.mjs";
/**
* Recursively displays a JSON object as an HTML table
*
* @param {Object} obj
* @returns string
*/
export function objToTable(obj, nested=false) {
let html = `<table
class='table table-sm table-nonfluid ${nested ? "mb-0 table-borderless" : "table-bordered"}'
style='table-layout: fixed; ${nested ? "margin: -1px !important;" : ""}'>`;
if (!nested)
html += `<tr>
<th>Field</th>
<th>Value</th>
</tr>`;
for (const key in obj) {
html += `<tr><td style='word-wrap: break-word'>${key}</td>`;
if (typeof obj[key] === "object")
html += `<td style='padding: 0'>${objToTable(obj[key], true)}</td>`;
else
html += `<td>${obj[key]}</td>`;
html += "</tr>";
}
html += "</table>";
return html;
}
/**
* Converts bytes into a BigNumber string
* @param {Uint8Array} bs
* @returns {string}
*/
export function bytesToLargeNumber(bs) {
return BigNumber(toHexFast(bs), 16).toString();
}

View File

@@ -9,35 +9,25 @@
import { toHex, fromHex } from "./Hex.mjs";
/**
* Formats Distinguished Name (DN) strings.
* Formats Distinguished Name (DN) objects to strings.
*
* @param {string} dnStr
* @param {Object} dnObj
* @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;
export function formatDnObj(dnObj, indent) {
let output = "";
for (i = 0; i < fields.length; i++) {
if (!fields[i].length) continue;
const maxKeyLen = dnObj.array.reduce((max, item) => {
return item[0].type.length > max ? item[0].type.length : max;
}, 0);
key = fields[i].split("=")[0];
for (let i = 0; i < dnObj.array.length; i++) {
if (!dnObj.array[i].length) continue;
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";
const key = dnObj.array[i][0].type;
const value = dnObj.array[i][0].value;
const str = `${key.padEnd(maxKeyLen, " ")} = ${value}\n`;
output += str.padStart(indent + str.length, " ");
}
@@ -54,7 +44,7 @@ export function formatDnStr (dnStr, indent) {
* @param {number} indent
* @returns {string}
*/
export function formatByteStr (byteStr, length, indent) {
export function formatByteStr(byteStr, length, indent) {
byteStr = toHex(fromHex(byteStr), ":");
length = length * 3;
let output = "";

View File

@@ -9,8 +9,9 @@
import OperationError from "../errors/OperationError.mjs";
import jsQR from "jsqr";
import qr from "qr-image";
import jimp from "jimp";
import Utils from "../Utils.mjs";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Parses a QR code image from an image

17
src/core/lib/RSA.mjs Normal file
View File

@@ -0,0 +1,17 @@
/**
* RSA resources.
*
* @author Matt C [me@mitt.dev]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import forge from "node-forge";
export const MD_ALGORITHMS = {
"SHA-1": forge.md.sha1,
"MD5": forge.md.md5,
"SHA-256": forge.md.sha256,
"SHA-384": forge.md.sha384,
"SHA-512": forge.md.sha512,
};

502
src/core/lib/SIGABA.mjs Normal file
View File

@@ -0,0 +1,502 @@
/**
* Emulation of the SIGABA machine
*
* @author hettysymes
* @copyright hettysymes 2020
* @license Apache-2.0
*/
/**
* A set of randomised example SIGABA cipher/control rotors (these rotors are interchangeable). Cipher and control rotors can be referred to as C and R rotors respectively.
*/
export const CR_ROTORS = [
{name: "Example 1", value: "SRGWANHPJZFXVIDQCEUKBYOLMT"},
{name: "Example 2", value: "THQEFSAZVKJYULBODCPXNIMWRG"},
{name: "Example 3", value: "XDTUYLEVFNQZBPOGIRCSMHWKAJ"},
{name: "Example 4", value: "LOHDMCWUPSTNGVXYFJREQIKBZA"},
{name: "Example 5", value: "ERXWNZQIJYLVOFUMSGHTCKPBDA"},
{name: "Example 6", value: "FQECYHJIOUMDZVPSLKRTGWXBAN"},
{name: "Example 7", value: "TBYIUMKZDJSOPEWXVANHLCFQGR"},
{name: "Example 8", value: "QZUPDTFNYIAOMLEBWJXCGHKRSV"},
{name: "Example 9", value: "CZWNHEMPOVXLKRSIDGJFYBTQAU"},
{name: "Example 10", value: "ENPXJVKYQBFZTICAGMOHWRLDUS"}
];
/**
* A set of randomised example SIGABA index rotors (may be referred to as I rotors).
*/
export const I_ROTORS = [
{name: "Example 1", value: "6201348957"},
{name: "Example 2", value: "6147253089"},
{name: "Example 3", value: "8239647510"},
{name: "Example 4", value: "7194835260"},
{name: "Example 5", value: "4873205916"}
];
export const NUMBERS = "0123456789".split("");
/**
* Converts a letter to uppercase (if it already isn't)
*
* @param {char} letter - letter to convert to uppercase
* @returns {char}
*/
export function convToUpperCase(letter) {
const charCode = letter.charCodeAt();
if (97<=charCode && charCode<=122) {
return String.fromCharCode(charCode-32);
}
return letter;
}
/**
* The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks.
*/
export class SigabaMachine {
/**
* SigabaMachine constructor
*
* @param {Object[]} cipherRotors - list of CRRotors
* @param {Object[]} controlRotors - list of CRRotors
* @param {object[]} indexRotors - list of IRotors
*/
constructor(cipherRotors, controlRotors, indexRotors) {
this.cipherBank = new CipherBank(cipherRotors);
this.controlBank = new ControlBank(controlRotors);
this.indexBank = new IndexBank(indexRotors);
}
/**
* Steps all the correct rotors in the machine.
*/
step() {
const controlOut = this.controlBank.goThroughControl();
const indexOut = this.indexBank.goThroughIndex(controlOut);
this.cipherBank.step(indexOut);
}
/**
* Encrypts a letter. A space is converted to a "Z" before encryption, and a "Z" is converted to an "X". This allows spaces to be encrypted.
*
* @param {char} letter - letter to encrypt
* @returns {char}
*/
encryptLetter(letter) {
letter = convToUpperCase(letter);
if (letter === " ") {
letter = "Z";
} else if (letter === "Z") {
letter = "X";
}
const encryptedLetter = this.cipherBank.encrypt(letter);
this.step();
return encryptedLetter;
}
/**
* Decrypts a letter. A letter decrypted as a "Z" is converted to a space before it is output, since spaces are converted to "Z"s before encryption.
*
* @param {char} letter - letter to decrypt
* @returns {char}
*/
decryptLetter(letter) {
letter = convToUpperCase(letter);
let decryptedLetter = this.cipherBank.decrypt(letter);
if (decryptedLetter === "Z") {
decryptedLetter = " ";
}
this.step();
return decryptedLetter;
}
/**
* Encrypts a message of one or more letters
*
* @param {string} msg - message to encrypt
* @returns {string}
*/
encrypt(msg) {
let ciphertext = "";
for (const letter of msg) {
ciphertext = ciphertext.concat(this.encryptLetter(letter));
}
return ciphertext;
}
/**
* Decrypts a message of one or more letters
*
* @param {string} msg - message to decrypt
* @returns {string}
*/
decrypt(msg) {
let plaintext = "";
for (const letter of msg) {
plaintext = plaintext.concat(this.decryptLetter(letter));
}
return plaintext;
}
}
/**
* The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation.
*/
export class CipherBank {
/**
* CipherBank constructor
*
* @param {Object[]} rotors - list of CRRotors
*/
constructor(rotors) {
this.rotors = rotors;
}
/**
* Encrypts a letter through the cipher rotors (signal goes from left-to-right)
*
* @param {char} inputPos - the input position of the signal (letter to be encrypted)
* @returns {char}
*/
encrypt(inputPos) {
for (const rotor of this.rotors) {
inputPos = rotor.crypt(inputPos, "leftToRight");
}
return inputPos;
}
/**
* Decrypts a letter through the cipher rotors (signal goes from right-to-left)
*
* @param {char} inputPos - the input position of the signal (letter to be decrypted)
* @returns {char}
*/
decrypt(inputPos) {
const revOrderedRotors = [...this.rotors].reverse();
for (const rotor of revOrderedRotors) {
inputPos = rotor.crypt(inputPos, "rightToLeft");
}
return inputPos;
}
/**
* Step the cipher rotors forward according to the inputs from the index rotors
*
* @param {number[]} indexInputs - the inputs from the index rotors
*/
step(indexInputs) {
const logicDict = {0: [0, 9], 1: [7, 8], 2: [5, 6], 3: [3, 4], 4: [1, 2]};
const rotorsToMove = [];
for (const key in logicDict) {
const item = logicDict[key];
for (const i of indexInputs) {
if (item.includes(i)) {
rotorsToMove.push(this.rotors[key]);
break;
}
}
}
for (const rotor of rotorsToMove) {
rotor.step();
}
}
}
/**
* The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left.
*/
export class ControlBank {
/**
* ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors.
*
* @param {Object[]} rotors - list of CRRotors
*/
constructor(rotors) {
this.rotors = [...rotors].reverse();
}
/**
* Encrypts a letter.
*
* @param {char} inputPos - the input position of the signal
* @returns {char}
*/
crypt(inputPos) {
for (const rotor of this.rotors) {
inputPos = rotor.crypt(inputPos, "rightToLeft");
}
return inputPos;
}
/**
* Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I".
*
* @returns {number[]}
*/
getOutputs() {
const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")];
const logicDict = {1: "B", 2: "C", 3: "DE", 4: "FGH", 5: "IJK", 6: "LMNO", 7: "PQRST", 8: "UVWXYZ", 9: "A"};
const numberOutputs = [];
for (const key in logicDict) {
const item = logicDict[key];
for (const output of outputs) {
if (item.includes(output)) {
numberOutputs.push(key);
break;
}
}
}
return numberOutputs;
}
/**
* Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared.
*/
step() {
const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3];
// 14 is the offset of "O" from "A" - the next rotor steps once the previous rotor reaches "O"
if (FRotor.state === 14) {
if (MRotor.state === 14) {
SRotor.step();
}
MRotor.step();
}
FRotor.step();
}
/**
* The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them.
*
* @returns {number[]}
*/
goThroughControl() {
const outputs = this.getOutputs();
this.step();
return outputs;
}
}
/**
* The index rotor bank consists of 5 index rotors all placed in the forwards orientation.
*/
export class IndexBank {
/**
* IndexBank constructor
*
* @param {Object[]} rotors - list of IRotors
*/
constructor(rotors) {
this.rotors = rotors;
}
/**
* Encrypts a number.
*
* @param {number} inputPos - the input position of the signal
* @returns {number}
*/
crypt(inputPos) {
for (const rotor of this.rotors) {
inputPos = rotor.crypt(inputPos);
}
return inputPos;
}
/**
* The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors.
*
* @param {number[]} controlInputs - inputs from the control rotors
* @returns {number[]}
*/
goThroughIndex(controlInputs) {
const outputs = [];
for (const inp of controlInputs) {
outputs.push(this.crypt(inp));
}
return outputs;
}
}
/**
* Rotor class
*/
export class Rotor {
/**
* Rotor constructor
*
* @param {number[]} wireSetting - the wirings within the rotor: mapping from left-to-right, the index of the number in the list maps onto the number at that index
* @param {bool} rev - true if the rotor is reversed, false if it isn't
* @param {number} key - the starting position or state of the rotor
*/
constructor(wireSetting, key, rev) {
this.state = key;
this.numMapping = this.getNumMapping(wireSetting, rev);
this.posMapping = this.getPosMapping(rev);
}
/**
* Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed)
*
* @param {number[]} wireSetting - the wirings within the rotors
* @param {bool} rev - true if reversed, false if not
* @returns {number[]}
*/
getNumMapping(wireSetting, rev) {
if (rev===false) {
return wireSetting;
} else {
const length = wireSetting.length;
const tempMapping = new Array(length);
for (let i=0; i<length; i++) {
tempMapping[wireSetting[i]] = i;
}
return tempMapping;
}
}
/**
* Get the position mapping (how the position numbers map onto the numbers of the rotor)
*
* @param {bool} rev - true if reversed, false if not
* @returns {number[]}
*/
getPosMapping(rev) {
const length = this.numMapping.length;
const posMapping = [];
if (rev===false) {
for (let i = this.state; i < this.state+length; i++) {
let res = i%length;
if (res<0) {
res += length;
}
posMapping.push(res);
}
} else {
for (let i = this.state; i > this.state-length; i--) {
let res = i%length;
if (res<0) {
res += length;
}
posMapping.push(res);
}
}
return posMapping;
}
/**
* Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex.
*
* @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt)
* @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor
* @returns {number}
*/
cryptNum(inputPos, direction) {
const inpNum = this.posMapping[inputPos];
let outNum;
if (direction === "leftToRight") {
outNum = this.numMapping[inpNum];
} else if (direction === "rightToLeft") {
outNum = this.numMapping.indexOf(inpNum);
}
const outPos = this.posMapping.indexOf(outNum);
return outPos;
}
/**
* Steps the rotor. The number at position 0 will be moved to position 1 etc.
*/
step() {
const lastNum = this.posMapping.pop();
this.posMapping.splice(0, 0, lastNum);
this.state = this.posMapping[0];
}
}
/**
* A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation.
*/
export class CRRotor extends Rotor {
/**
* CRRotor constructor
*
* @param {string} wireSetting - the rotor wirings (string of letters)
* @param {char} key - initial state of rotor
* @param {bool} rev - true if reversed, false if not
*/
constructor(wireSetting, key, rev=false) {
wireSetting = wireSetting.split("").map(CRRotor.letterToNum);
super(wireSetting, CRRotor.letterToNum(key), rev);
}
/**
* Static function which converts a letter into its number i.e. its offset from the letter "A"
*
* @param {char} letter - letter to convert to number
* @returns {number}
*/
static letterToNum(letter) {
return letter.charCodeAt()-65;
}
/**
* Static function which converts a number (a letter's offset from "A") into its letter
*
* @param {number} num - number to convert to letter
* @returns {char}
*/
static numToLetter(num) {
return String.fromCharCode(num+65);
}
/**
* Encrypts/decrypts a letter.
*
* @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.)
* @param {string} direction - one of "leftToRight" and "rightToLeft"
* @returns {char}
*/
crypt(inputPos, direction) {
inputPos = CRRotor.letterToNum(inputPos);
const outPos = this.cryptNum(inputPos, direction);
return CRRotor.numToLetter(outPos);
}
}
/**
* An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption.
*/
export class IRotor extends Rotor {
/**
* IRotor constructor
*
* @param {string} wireSetting - the rotor wirings (string of numbers)
* @param {char} key - initial state of rotor
*/
constructor(wireSetting, key) {
wireSetting = wireSetting.split("").map(Number);
super(wireSetting, Number(key), false);
}
/**
* Encrypts a number
*
* @param {number} inputPos - the input position of the signal
* @returns {number}
*/
crypt(inputPos) {
return this.cryptNum(inputPos, "leftToRight");
}
}

331
src/core/lib/SM4.mjs Normal file
View File

@@ -0,0 +1,331 @@
/**
* Complete implementation of SM4 cipher encryption/decryption with
* ECB, CBC, CFB, OFB, CTR block modes.
* These modes are specified in IETF draft-ribose-cfrg-sm4-09, see:
* https://tools.ietf.org/id/draft-ribose-cfrg-sm4-09.html
* for details.
*
* Follows spec from Cryptography Standardization Technical Comittee:
* http://www.gmbz.org.cn/upload/2018-04-04/1522788048733065051.pdf
*
* @author swesven
* @copyright 2021
* @license Apache-2.0
*/
import OperationError from "../errors/OperationError.mjs";
/** Number of rounds */
const NROUNDS = 32;
/** block size in bytes */
const BLOCKSIZE = 16;
/** The S box, 256 8-bit values */
const Sbox = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
];
/** "Fixed parameter CK" used in key expansion */
const CK = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
];
/** "System parameter FK" */
const FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc];
/**
* Rotating 32-bit shift left
*
* (Note that although JS integers are stored in doubles and thus have 53 bits,
* the JS bitwise operations are 32-bit)
*/
function ROL(i, n) {
return (i << n) | (i >>> (32 - n));
}
/**
* Linear transformation L
*
* @param {integer} b - a 32 bit integer
*/
function transformL(b) {
/* Replace each of the 4 bytes in b with the value at its offset in the Sbox */
b = (Sbox[(b >>> 24) & 0xFF] << 24) | (Sbox[(b >>> 16) & 0xFF] << 16) |
(Sbox[(b >>> 8) & 0xFF] << 8) | Sbox[b & 0xFF];
/* circular rotate and xor */
return b ^ ROL(b, 2) ^ ROL(b, 10) ^ ROL(b, 18) ^ ROL(b, 24);
}
/**
* Linear transformation L'
*
* @param {integer} b - a 32 bit integer
*/
function transformLprime(b) {
/* Replace each of the 4 bytes in b with the value at its offset in the Sbox */
b = (Sbox[(b >>> 24) & 0xFF] << 24) | (Sbox[(b >>> 16) & 0xFF] << 16) |
(Sbox[(b >>> 8) & 0xFF] << 8) | Sbox[b & 0xFF];
return b ^ ROL(b, 13) ^ ROL(b, 23); /* circular rotate and XOR */
}
/**
* Initialize the round key
*/
function initSM4RoundKey(rawkey) {
const K = rawkey.map((a, i) => a ^ FK[i]); /* K = rawkey ^ FK */
const roundKey = [];
for (let i = 0; i < 32; i++)
roundKey[i] = K[i + 4] = K[i] ^ transformLprime(K[i + 1] ^ K[i + 2] ^ K[i + 3] ^ CK[i]);
return roundKey;
}
/**
* Encrypts/decrypts a single block X (4 32-bit values) with a prepared round key.
*
* @param {intArray} X - A cleartext block.
* @param {intArray} roundKey - The round key from initSMRoundKey for encrypting (reversed for decrypting).
* @returns {byteArray} - The cipher text.
*/
function encryptBlockSM4(X, roundKey) {
for (let i = 0; i < NROUNDS; i++)
X[i + 4] = X[i] ^ transformL(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ roundKey[i]);
return [X[35], X[34], X[33], X[32]];
}
/**
* Takes 16 bytes from an offset in an array and returns an array of 4 32-bit Big-Endian values.
* (DataView won't work portably here as we need Big-Endian)
*
* @param {byteArray} bArray - the array of bytes
* @param {integer} offset - starting offset in the array; 15 bytes must follow it.
*/
function bytesToInts(bArray, offs=0) {
let offset = offs;
const A = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
offset += 4;
const B = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
offset += 4;
const C = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
offset += 4;
const D = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
return [A, B, C, D];
}
/**
* Inverse of bytesToInts above; takes an array of 32-bit integers and turns it into an array of bytes.
* Again, Big-Endian order.
*/
function intsToBytes(ints) {
const bArr = [];
for (let i = 0; i < ints.length; i++) {
bArr.push((ints[i] >> 24) & 0xFF);
bArr.push((ints[i] >> 16) & 0xFF);
bArr.push((ints[i] >> 8) & 0xFF);
bArr.push(ints[i] & 0xFF);
}
return bArr;
}
/**
* Encrypt using SM4 using a given block cipher mode.
*
* @param {byteArray} message - The clear text message; any length under 32 Gb or so.
* @param {byteArray} key - The cipher key, 16 bytes.
* @param {byteArray} iv - The IV or nonce, 16 bytes (not used with ECB mode)
* @param {string} mode - The block cipher mode "CBC", "ECB", "CFB", "OFB", "CTR".
* @param {boolean} noPadding - Don't add PKCS#7 padding if set.
* @returns {byteArray} - The cipher text.
*/
export function encryptSM4(message, key, iv, mode="ECB", noPadding=false) {
const messageLength = message.length;
if (messageLength === 0)
return [];
const roundKey = initSM4RoundKey(bytesToInts(key, 0));
/* Pad with PKCS#7 if requested for ECB/CBC else add zeroes (which are sliced off at the end) */
let padByte = 0;
let nPadding = 16 - (message.length & 0xF);
if (mode === "ECB" || mode === "CBC") {
if (noPadding) {
if (nPadding !== 16)
throw new OperationError(`No padding requested in ${mode} mode but input is not a 16-byte multiple.`);
nPadding = 0;
} else
padByte = nPadding;
}
for (let i = 0; i < nPadding; i++)
message.push(padByte);
const cipherText = [];
switch (mode) {
case "ECB":
for (let i = 0; i < message.length; i += BLOCKSIZE)
Array.prototype.push.apply(cipherText, intsToBytes(encryptBlockSM4(bytesToInts(message, i), roundKey)));
break;
case "CBC":
iv = bytesToInts(iv, 0);
for (let i = 0; i < message.length; i += BLOCKSIZE) {
const block = bytesToInts(message, i);
block[0] ^= iv[0]; block[1] ^= iv[1];
block[2] ^= iv[2]; block[3] ^= iv[3];
iv = encryptBlockSM4(block, roundKey);
Array.prototype.push.apply(cipherText, intsToBytes(iv));
}
break;
case "CFB":
iv = bytesToInts(iv, 0);
for (let i = 0; i < message.length; i += BLOCKSIZE) {
iv = encryptBlockSM4(iv, roundKey);
const block = bytesToInts(message, i);
block[0] ^= iv[0]; block[1] ^= iv[1];
block[2] ^= iv[2]; block[3] ^= iv[3];
Array.prototype.push.apply(cipherText, intsToBytes(block));
iv = block;
}
break;
case "OFB":
iv = bytesToInts(iv, 0);
for (let i = 0; i < message.length; i += BLOCKSIZE) {
iv = encryptBlockSM4(iv, roundKey);
const block = bytesToInts(message, i);
block[0] ^= iv[0]; block[1] ^= iv[1];
block[2] ^= iv[2]; block[3] ^= iv[3];
Array.prototype.push.apply(cipherText, intsToBytes(block));
}
break;
case "CTR":
iv = bytesToInts(iv, 0);
for (let i = 0; i < message.length; i += BLOCKSIZE) {
let iv2 = [...iv]; /* containing the IV + counter */
iv2[3] += (i >> 4);/* Using a 32 bit counter here. 64 Gb encrypts should be enough for everyone. */
iv2 = encryptBlockSM4(iv2, roundKey);
const block = bytesToInts(message, i);
block[0] ^= iv2[0]; block[1] ^= iv2[1];
block[2] ^= iv2[2]; block[3] ^= iv2[3];
Array.prototype.push.apply(cipherText, intsToBytes(block));
}
break;
default:
throw new OperationError("Invalid block cipher mode: "+mode);
}
if (mode !== "ECB" && mode !== "CBC")
return cipherText.slice(0, messageLength);
return cipherText;
}
/**
* Decrypt using SM4 using a given block cipher mode.
*
* @param {byteArray} cipherText - The ciphertext
* @param {byteArray} key - The cipher key, 16 bytes.
* @param {byteArray} iv - The IV or nonce, 16 bytes (not used with ECB mode)
* @param {string} mode - The block cipher mode "CBC", "ECB", "CFB", "OFB", "CTR"
* @param {boolean] ignorePadding - If true, ignore padding issues in ECB/CBC mode.
* @returns {byteArray} - The cipher text.
*/
export function decryptSM4(cipherText, key, iv, mode="ECB", ignorePadding=false) {
const originalLength = cipherText.length;
if (originalLength === 0)
return [];
let roundKey = initSM4RoundKey(bytesToInts(key, 0));
if (mode === "ECB" || mode === "CBC") {
/* Init decryption key */
roundKey = roundKey.reverse();
if ((originalLength & 0xF) !== 0 && !ignorePadding)
throw new OperationError(`With ECB or CBC modes, the input must be divisible into 16 byte blocks. (${cipherText.length & 0xF} bytes extra)`);
} else { /* Pad dummy bytes for other modes, chop them off at the end */
while ((cipherText.length & 0xF) !== 0)
cipherText.push(0);
}
const clearText = [];
switch (mode) {
case "ECB":
for (let i = 0; i < cipherText.length; i += BLOCKSIZE)
Array.prototype.push.apply(clearText, intsToBytes(encryptBlockSM4(bytesToInts(cipherText, i), roundKey)));
break;
case "CBC":
iv = bytesToInts(iv, 0);
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
const block = encryptBlockSM4(bytesToInts(cipherText, i), roundKey);
block[0] ^= iv[0]; block[1] ^= iv[1];
block[2] ^= iv[2]; block[3] ^= iv[3];
Array.prototype.push.apply(clearText, intsToBytes(block));
iv = bytesToInts(cipherText, i);
}
break;
case "CFB":
iv = bytesToInts(iv, 0);
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
iv = encryptBlockSM4(iv, roundKey);
const block = bytesToInts(cipherText, i);
block[0] ^= iv[0]; block[1] ^= iv[1];
block[2] ^= iv[2]; block[3] ^= iv[3];
Array.prototype.push.apply(clearText, intsToBytes(block));
iv = bytesToInts(cipherText, i);
}
break;
case "OFB":
iv = bytesToInts(iv, 0);
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
iv = encryptBlockSM4(iv, roundKey);
const block = bytesToInts(cipherText, i);
block[0] ^= iv[0]; block[1] ^= iv[1];
block[2] ^= iv[2]; block[3] ^= iv[3];
Array.prototype.push.apply(clearText, intsToBytes(block));
}
break;
case "CTR":
iv = bytesToInts(iv, 0);
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
let iv2 = [...iv]; /* containing the IV + counter */
iv2[3] += (i >> 4);/* Using a 32 bit counter here. 64 Gb encrypts should be enough for everyone. */
iv2 = encryptBlockSM4(iv2, roundKey);
const block = bytesToInts(cipherText, i);
block[0] ^= iv2[0]; block[1] ^= iv2[1];
block[2] ^= iv2[2]; block[3] ^= iv2[3];
Array.prototype.push.apply(clearText, intsToBytes(block));
}
break;
default:
throw new OperationError(`Invalid block cipher mode: ${mode}`);
}
/* Check PKCS#7 padding */
if (mode === "ECB" || mode === "CBC") {
if (ignorePadding)
return clearText;
const padByte = clearText[clearText.length - 1];
if (padByte > 16)
throw new OperationError("Invalid PKCS#7 padding.");
for (let i = 0; i < padByte; i++)
if (clearText[clearText.length -i - 1] !== padByte)
throw new OperationError("Invalid PKCS#7 padding.");
return clearText.slice(0, clearText.length - padByte);
}
return clearText.slice(0, originalLength);
}

105
src/core/lib/Sort.mjs Normal file
View File

@@ -0,0 +1,105 @@
/**
* Sorting functions
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*
*/
/**
* Comparison operation for sorting of strings ignoring case.
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function caseInsensitiveSort(a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
}
/**
* Comparison operation for sorting of IPv4 addresses.
*
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function ipSort(a, b) {
let a_ = a.split("."),
b_ = b.split(".");
a_ = a_[0] * 0x1000000 + a_[1] * 0x10000 + a_[2] * 0x100 + a_[3] * 1;
b_ = b_[0] * 0x1000000 + b_[1] * 0x10000 + b_[2] * 0x100 + b_[3] * 1;
if (isNaN(a_) && !isNaN(b_)) return 1;
if (!isNaN(a_) && isNaN(b_)) return -1;
if (isNaN(a_) && isNaN(b_)) return a.localeCompare(b);
return a_ - b_;
}
/**
* Comparison operation for sorting of numeric values.
*
* @author Chris van Marle
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function numericSort(a, b) {
const a_ = a.split(/([^\d]+)/),
b_ = b.split(/([^\d]+)/);
for (let i = 0; i < a_.length && i < b.length; ++i) {
if (isNaN(a_[i]) && !isNaN(b_[i])) return 1; // Numbers after non-numbers
if (!isNaN(a_[i]) && isNaN(b_[i])) return -1;
if (isNaN(a_[i]) && isNaN(b_[i])) {
const ret = a_[i].localeCompare(b_[i]); // Compare strings
if (ret !== 0) return ret;
}
if (!isNaN(a_[i]) && !isNaN(b_[i])) { // Compare numbers
if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
}
}
return a.localeCompare(b);
}
/**
* Comparison operation for sorting of hexadecimal values.
*
* @author Chris van Marle
* @param {string} a
* @param {string} b
* @returns {number}
*/
export function hexadecimalSort(a, b) {
let a_ = a.split(/([^\da-f]+)/i),
b_ = b.split(/([^\da-f]+)/i);
a_ = a_.map(v => {
const t = parseInt(v, 16);
return isNaN(t) ? v : t;
});
b_ = b_.map(v => {
const t = parseInt(v, 16);
return isNaN(t) ? v : t;
});
for (let i = 0; i < a_.length && i < b.length; ++i) {
if (isNaN(a_[i]) && !isNaN(b_[i])) return 1; // Numbers after non-numbers
if (!isNaN(a_[i]) && isNaN(b_[i])) return -1;
if (isNaN(a_[i]) && isNaN(b_[i])) {
const ret = a_[i].localeCompare(b_[i]); // Compare strings
if (ret !== 0) return ret;
}
if (!isNaN(a_[i]) && !isNaN(b_[i])) { // Compare numbers
if (a_[i] - b_[i] !== 0) return a_[i] - b_[i];
}
}
return a.localeCompare(b);
}

View File

@@ -27,15 +27,17 @@ export default class Stream {
}
/**
* Get a number of bytes from the current position.
* Get a number of bytes from the current position, or all remaining bytes.
*
* @param {number} numBytes
* @param {number} [numBytes=null]
* @returns {Uint8Array}
*/
getBytes(numBytes) {
getBytes(numBytes=null) {
if (this.position > this.length) return undefined;
const newPosition = this.position + numBytes;
const newPosition = numBytes !== null ?
this.position + numBytes :
this.length;
const bytes = this.bytes.slice(this.position, newPosition);
this.position = newPosition;
this.bitPos = 0;
@@ -46,12 +48,14 @@ export default class Stream {
* Interpret the following bytes as a string, stopping at the next null byte or
* the supplied limit.
*
* @param {number} numBytes
* @param {number} [numBytes=-1]
* @returns {string}
*/
readString(numBytes) {
readString(numBytes=-1) {
if (this.position > this.length) return undefined;
if (numBytes === -1) numBytes = this.length - this.position;
let result = "";
for (let i = this.position; i < this.position + numBytes; i++) {
const currentByte = this.bytes[i];
@@ -91,34 +95,40 @@ export default class Stream {
}
/**
* Reads a number of bits from the buffer.
*
* @TODO Add endianness
* Reads a number of bits from the buffer in big or little endian.
*
* @param {number} numBits
* @param {string} [endianness="be"]
* @returns {number}
*/
readBits(numBits) {
readBits(numBits, endianness="be") {
if (this.position > this.length) return undefined;
let bitBuf = 0,
bitBufLen = 0;
// Add remaining bits from current byte
bitBuf = (this.bytes[this.position++] & bitMask(this.bitPos)) >>> this.bitPos;
bitBuf = this.bytes[this.position++] & bitMask(this.bitPos);
if (endianness !== "be") bitBuf >>>= this.bitPos;
bitBufLen = 8 - this.bitPos;
this.bitPos = 0;
// Not enough bits yet
while (bitBufLen < numBits) {
bitBuf |= this.bytes[this.position++] << bitBufLen;
if (endianness === "be")
bitBuf = (bitBuf << bitBufLen) | this.bytes[this.position++];
else
bitBuf |= this.bytes[this.position++] << bitBufLen;
bitBufLen += 8;
}
// Reverse back to numBits
if (bitBufLen > numBits) {
const excess = bitBufLen - numBits;
bitBuf &= (1 << numBits) - 1;
if (endianness === "be")
bitBuf >>>= excess;
else
bitBuf &= (1 << numBits) - 1;
bitBufLen -= excess;
this.position--;
this.bitPos = 8 - excess;
@@ -133,7 +143,9 @@ export default class Stream {
* @returns {number} The bit mask
*/
function bitMask(bitPos) {
return 256 - (1 << bitPos);
return endianness === "be" ?
(1 << (8 - bitPos)) - 1 :
256 - (1 << bitPos);
}
}
@@ -177,7 +189,7 @@ export default class Stream {
// Get the skip table.
const skiptable = preprocess(val, length);
let found = true;
let found;
while (this.position < this.length) {
// Until we hit the final element of val in the stream.
@@ -303,11 +315,13 @@ export default class Stream {
/**
* Returns a slice of the stream up to the current position.
*
* @param {number} [start=0]
* @param {number} [finish=this.position]
* @returns {Uint8Array}
*/
carve() {
if (this.bitPos > 0) this.position++;
return this.bytes.slice(0, this.position);
carve(start=0, finish=this.position) {
if (this.bitPos > 0) finish++;
return this.bytes.slice(start, finish);
}
}

View File

@@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
import OperationError from "../errors/OperationError.mjs";
/**
@@ -22,7 +22,7 @@ class AESDecrypt extends Operation {
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.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 as a default.<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";
@@ -41,8 +41,41 @@ class AESDecrypt extends Operation {
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
"type": "argSelector",
"value": [
{
name: "CBC",
off: [5, 6]
},
{
name: "CFB",
off: [5, 6]
},
{
name: "OFB",
off: [5, 6]
},
{
name: "CTR",
off: [5, 6]
},
{
name: "GCM",
on: [5, 6]
},
{
name: "ECB",
off: [5, 6]
},
{
name: "CBC/NoPadding",
off: [5, 6]
},
{
name: "ECB/NoPadding",
off: [5, 6]
}
]
},
{
"name": "Input",
@@ -59,6 +92,12 @@ class AESDecrypt extends Operation {
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
},
{
"name": "Additional Authenticated Data",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
}
];
}
@@ -73,10 +112,12 @@ class AESDecrypt extends Operation {
run(input, args) {
const key = Utils.convertToByteString(args[0].string, args[0].option),
iv = Utils.convertToByteString(args[1].string, args[1].option),
mode = args[2],
mode = args[2].substring(0, 3),
noPadding = args[2].endsWith("NoPadding"),
inputType = args[3],
outputType = args[4],
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
gcmTag = Utils.convertToByteString(args[5].string, args[5].option),
aad = Utils.convertToByteString(args[6].string, args[6].option);
if ([16, 24, 32].indexOf(key.length) < 0) {
throw new OperationError(`Invalid key length: ${key.length} bytes
@@ -90,9 +131,18 @@ The following algorithms will be used based on the size of the key:
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
/* Allow for a "no padding" mode */
if (noPadding) {
decipher.mode.unpad = function(output, options) {
return true;
};
}
decipher.start({
iv: iv.length === 0 ? "" : iv,
tag: gcmTag
tag: mode === "GCM" ? gcmTag : undefined,
additionalData: mode === "GCM" ? aad : undefined
});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();

View File

@@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
import OperationError from "../errors/OperationError.mjs";
/**
@@ -41,8 +41,33 @@ class AESEncrypt extends Operation {
},
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
"type": "argSelector",
"value": [
{
name: "CBC",
off: [5]
},
{
name: "CFB",
off: [5]
},
{
name: "OFB",
off: [5]
},
{
name: "CTR",
off: [5]
},
{
name: "GCM",
on: [5]
},
{
name: "ECB",
off: [5]
}
]
},
{
"name": "Input",
@@ -53,6 +78,12 @@ class AESEncrypt extends Operation {
"name": "Output",
"type": "option",
"value": ["Hex", "Raw"]
},
{
"name": "Additional Authenticated Data",
"type": "toggleString",
"value": "",
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
}
];
}
@@ -69,7 +100,8 @@ class AESEncrypt extends Operation {
iv = Utils.convertToByteString(args[1].string, args[1].option),
mode = args[2],
inputType = args[3],
outputType = args[4];
outputType = args[4],
aad = 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
@@ -83,7 +115,10 @@ The following algorithms will be used based on the size of the key:
input = Utils.convertToByteString(input, inputType);
const cipher = forge.cipher.createCipher("AES-" + mode, key);
cipher.start({iv: iv});
cipher.start({
iv: iv,
additionalData: mode === "GCM" ? aad : undefined
});
cipher.update(forge.util.createBuffer(input));
cipher.finish();

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Add Text To Image operation

View File

@@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
import OperationError from "../errors/OperationError.mjs";
import { Blowfish } from "../lib/Blowfish.mjs";

View File

@@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
import OperationError from "../errors/OperationError.mjs";
import { Blowfish } from "../lib/Blowfish.mjs";

View File

@@ -9,8 +9,9 @@ import OperationError from "../errors/OperationError.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import jimp from "jimp";
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Blur Image operation

View File

@@ -1,6 +1,9 @@
/**
* Emulation of the Bombe machine.
*
* Tested against the Bombe Rebuild at Bletchley Park's TNMOC
* using a variety of inputs and settings to confirm correctness.
*
* @author s2224834
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -0,0 +1,41 @@
/**
* @author Danh4 [dan.h4@ncsc.gov.uk]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Cbor from "cbor";
/**
* CBOR Decode operation
*/
class CBORDecode extends Operation {
/**
* CBORDecode constructor
*/
constructor() {
super();
this.name = "CBOR Decode";
this.module = "Serialise";
this.description = "Concise Binary Object Representation (CBOR) is a binary data serialization format loosely based on JSON. Like JSON it allows the transmission of data objects that contain namevalue pairs, but in a more concise manner. This increases processing and transfer speeds at the cost of human readability. It is defined in IETF RFC 8949.";
this.infoURL = "https://wikipedia.org/wiki/CBOR";
this.inputType = "ArrayBuffer";
this.outputType = "JSON";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {JSON}
*/
run(input, args) {
return Cbor.decodeFirstSync(Buffer.from(input).toString("hex"));
}
}
export default CBORDecode;

View File

@@ -0,0 +1,41 @@
/**
* @author Danh4 [dan.h4@ncsc.gov.uk]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Cbor from "cbor";
/**
* CBOR Encode operation
*/
class CBOREncode extends Operation {
/**
* CBOREncode constructor
*/
constructor() {
super();
this.name = "CBOR Encode";
this.module = "Serialise";
this.description = "Concise Binary Object Representation (CBOR) is a binary data serialization format loosely based on JSON. Like JSON it allows the transmission of data objects that contain namevalue pairs, but in a more concise manner. This increases processing and transfer speeds at the cost of human readability. It is defined in IETF RFC 8949.";
this.infoURL = "https://wikipedia.org/wiki/CBOR";
this.inputType = "JSON";
this.outputType = "ArrayBuffer";
this.args = [];
}
/**
* @param {JSON} input
* @param {Object[]} args
* @returns {ArrayBuffer}
*/
run(input, args) {
return new Uint8Array(Cbor.encodeCanonical(input)).buffer;
}
}
export default CBOREncode;

View File

@@ -21,7 +21,7 @@ class CTPH extends Operation {
this.name = "CTPH";
this.module = "Crypto";
this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'.";
this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
this.inputType = "string";
this.outputType = "string";
this.args = [];

View File

@@ -0,0 +1,61 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* Caesar Box Cipher operation
*/
class CaesarBoxCipher extends Operation {
/**
* CaesarBoxCipher constructor
*/
constructor() {
super();
this.name = "Caesar Box Cipher";
this.module = "Ciphers";
this.description = "Caesar Box is a transposition cipher used in the Roman Empire, in which letters of the message are written in rows in a square (or a rectangle) and then, read by column.";
this.infoURL = "https://www.dcode.fr/caesar-box-cipher";
this.inputType = "string";
this.outputType = "string";
this.args = [
{
name: "Box Height",
type: "number",
value: 1
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const tableHeight = args[0];
const tableWidth = Math.ceil(input.length / tableHeight);
while (input.indexOf(" ") !== -1)
input = input.replace(" ", "");
for (let i = 0; i < (tableHeight * tableWidth) - input.length; i++) {
input += "\x00";
}
let result = "";
for (let i = 0; i < tableHeight; i++) {
for (let j = i; j < input.length; j += tableHeight) {
if (input.charAt(j) !== "\x00") {
result += input.charAt(j);
}
}
}
return result;
}
}
export default CaesarBoxCipher;

View File

@@ -0,0 +1,63 @@
/**
* @author dolphinOnKeys [robin@weird.io]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
/**
* Cetacean Cipher Decode operation
*/
class CetaceanCipherDecode extends Operation {
/**
* CetaceanCipherDecode constructor
*/
constructor() {
super();
this.name = "Cetacean Cipher Decode";
this.module = "Ciphers";
this.description = "Decode Cetacean Cipher input. <br/><br/>e.g. <code>EEEEEEEEEeeEeEEEEEEEEEEEEeeEeEEe</code> becomes <code>hi</code>";
this.infoURL = "https://hitchhikers.fandom.com/wiki/Dolphins";
this.inputType = "string";
this.outputType = "string";
this.checks = [
{
pattern: "^(?:[eE]{16,})(?: [eE]{16,})*$",
flags: "",
args: []
}
];
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const binaryArray = [];
for (const char of input) {
if (char === " ") {
binaryArray.push(...[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]);
} else {
binaryArray.push(char === "e" ? 1 : 0);
}
}
const byteArray = [];
for (let i = 0; i < binaryArray.length; i += 16) {
byteArray.push(binaryArray.slice(i, i + 16).join(""));
}
return byteArray.map(byte =>
String.fromCharCode(parseInt(byte, 2))
).join("");
}
}
export default CetaceanCipherDecode;

View File

@@ -0,0 +1,51 @@
/**
* @author dolphinOnKeys [robin@weird.io]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import {toBinary} from "../lib/Binary.mjs";
/**
* Cetacean Cipher Encode operation
*/
class CetaceanCipherEncode extends Operation {
/**
* CetaceanCipherEncode constructor
*/
constructor() {
super();
this.name = "Cetacean Cipher Encode";
this.module = "Ciphers";
this.description = "Converts any input into Cetacean Cipher. <br/><br/>e.g. <code>hi</code> becomes <code>EEEEEEEEEeeEeEEEEEEEEEEEEeeEeEEe</code>";
this.infoURL = "https://hitchhikers.fandom.com/wiki/Dolphins";
this.inputType = "string";
this.outputType = "string";
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
const result = [];
const charArray = input.split("");
charArray.map(character => {
if (character === " ") {
result.push(character);
} else {
const binaryArray = toBinary(character.charCodeAt(0), "None", 16).split("");
result.push(binaryArray.map(str => str === "1" ? "e" : "E").join(""));
}
});
return result.join("");
}
}
export default CetaceanCipherEncode;

View File

@@ -1,6 +1,9 @@
/**
* Emulation of Colossus.
*
* Tested against the Colossus Rebuild at Bletchley Park's TNMOC
* using a variety of inputs and settings to confirm correctness.
*
* @author VirtualColossus [martin@virtualcolossus.co.uk]
* @copyright Crown Copyright 2019
* @license Apache-2.0
@@ -125,7 +128,8 @@ class Colossus extends Operation {
},
{
name: "R1-Negate",
type: "boolean"
type: "boolean",
value: false
},
{
name: "R1-Counter",
@@ -164,7 +168,8 @@ class Colossus extends Operation {
},
{
name: "R2-Negate",
type: "boolean"
type: "boolean",
value: false
},
{
name: "R2-Counter",
@@ -203,7 +208,8 @@ class Colossus extends Operation {
},
{
name: "R3-Negate",
type: "boolean"
type: "boolean",
value: false
},
{
name: "R3-Counter",
@@ -212,7 +218,8 @@ class Colossus extends Operation {
},
{
name: "Negate All",
type: "boolean"
type: "boolean",
value: false
},
{
name: "K Rack: Addition",
@@ -220,23 +227,28 @@ class Colossus extends Operation {
},
{
name: "Add-Q1",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Add-Q2",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Add-Q3",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Add-Q4",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Add-Q5",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Add-Equals",
@@ -246,11 +258,13 @@ class Colossus extends Operation {
},
{
name: "Add-Counter1",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Add Negate All",
type: "boolean"
type: "boolean",
value: false
},
{
name: "Total Motor",

View File

@@ -24,7 +24,7 @@ class CompareCTPHHashes extends Operation {
this.name = "Compare CTPH hashes";
this.module = "Crypto";
this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
this.inputType = "string";
this.outputType = "Number";
this.args = [

View File

@@ -24,7 +24,7 @@ class CompareSSDEEPHashes extends Operation {
this.name = "Compare SSDEEP hashes";
this.module = "Crypto";
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
this.infoURL = "https://forensicswiki.org/wiki/Ssdeep";
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
this.inputType = "string";
this.outputType = "Number";
this.args = [

View File

@@ -64,6 +64,7 @@ class ConditionalJump extends Operation {
jmpIndex = getLabelIndex(label, state);
if (state.numJumps >= maxJumps || jmpIndex === -1) {
state.numJumps = 0;
return state;
}
@@ -73,6 +74,8 @@ class ConditionalJump extends Operation {
if (!invert && strMatch || invert && !strMatch) {
state.progress = jmpIndex;
state.numJumps++;
} else {
state.numJumps = 0;
}
}

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Contain Image operation

View File

@@ -8,7 +8,8 @@ import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Convert Image Format operation
@@ -88,7 +89,7 @@ class ConvertImageFormat extends Operation {
"Sub": jimp.PNG_FILTER_SUB,
"Up": jimp.PNG_FILTER_UP,
"Average": jimp.PNG_FILTER_AVERAGE,
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
"Paeth": jimp.PNG_FILTER_PATH
};
const mime = formatMap[format];

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Cover Image operation

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Crop Image operation

View File

@@ -7,7 +7,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
/**
* DES Decrypt operation
@@ -22,7 +22,7 @@ class DESDecrypt extends Operation {
this.name = "DES Decrypt";
this.module = "Ciphers";
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.";
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
this.inputType = "string";
this.outputType = "string";
@@ -42,7 +42,7 @@ class DESDecrypt extends Operation {
{
"name": "Mode",
"type": "option",
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
"value": ["CBC", "CFB", "OFB", "CTR", "ECB", "CBC/NoPadding", "ECB/NoPadding"]
},
{
"name": "Input",
@@ -65,7 +65,9 @@ class DESDecrypt extends Operation {
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;
mode = args[2].substring(0, 3),
noPadding = args[2].endsWith("NoPadding"),
[,,, inputType, outputType] = args;
if (key.length !== 8) {
throw new OperationError(`Invalid key length: ${key.length} bytes
@@ -83,6 +85,14 @@ Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
input = Utils.convertToByteString(input, inputType);
const decipher = forge.cipher.createDecipher("DES-" + mode, key);
/* Allow for a "no padding" mode */
if (noPadding) {
decipher.mode.unpad = function(output, options) {
return true;
};
}
decipher.start({iv: iv});
decipher.update(forge.util.createBuffer(input));
const result = decipher.finish();

View File

@@ -7,7 +7,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
/**
* DES Encrypt operation

View File

@@ -51,10 +51,27 @@ class DNSOverHTTPS extends Operation {
value: [
"A",
"AAAA",
"TXT",
"MX",
"ANAME",
"CERT",
"CNAME",
"DNSKEY",
"NS"
"HTTPS",
"IPSECKEY",
"LOC",
"MX",
"NS",
"OPENPGPKEY",
"PTR",
"RRSIG",
"SIG",
"SOA",
"SPF",
"SRV",
"SSHFP",
"TA",
"TXT",
"URI",
"ANY"
]
},
{

View File

@@ -67,7 +67,7 @@ class DeriveEVPKey extends Operation {
iterations = args[2],
hasher = args[3],
salt = Utils.convertToByteString(args[4].string, args[4].option),
key = CryptoJS.EvpKDF(passphrase, salt, {
key = CryptoJS.EvpKDF(passphrase, salt, { // lgtm [js/insufficient-password-hash]
keySize: keySize,
hasher: CryptoJS.algo[hasher],
iterations: iterations,

View File

@@ -6,7 +6,7 @@
import Operation from "../Operation.mjs";
import Utils from "../Utils.mjs";
import forge from "node-forge/dist/forge.min.js";
import forge from "node-forge";
/**
* Derive PBKDF2 key operation

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Image Dither operation

View File

@@ -0,0 +1,913 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @copyright Crown Copyright 2022
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import Stream from "../lib/Stream.mjs";
import Utils from "../Utils.mjs";
import OperationError from "../errors/OperationError.mjs";
/**
* ELF Info operation
*/
class ELFInfo extends Operation {
/**
* ELFInfo constructor
*/
constructor() {
super();
this.name = "ELF Info";
this.module = "Default";
this.description = "Implements readelf-like functionality. This operation will extract the ELF Header, Program Headers, Section Headers and Symbol Table for an ELF file.";
this.infoURL = "https://www.wikipedia.org/wiki/Executable_and_Linkable_Format";
this.inputType = "ArrayBuffer";
this.outputType = "string";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let phoff = 0;
let phEntries = 0;
let shoff = 0;
let shEntries = 0;
let shentSize = 0;
let entry = 0;
let format = 0;
let endianness = "";
let shstrtab = 0;
let namesOffset = 0;
let symtabOffset = 0;
let symtabSize = 0;
let symtabEntSize = 0;
let strtabOffset = 0;
const align = 30;
/**
* This function reads characters until it hits a null terminator.
*
* @param {stream} stream
* @param {integer} namesOffset
* @param {integer} nameOffset
* @returns {string}
*/
function readString(stream, namesOffset, nameOffset) {
const preMove = stream.position;
stream.moveTo(namesOffset + nameOffset);
const nameResult = stream.readString();
stream.moveTo(preMove);
return nameResult;
}
/**
* This function parses and extracts relevant information from the ELF Header.
*
* @param {stream} stream
* @returns {string}
*/
function elfHeader(stream) {
/**
* The ELF Header is comprised of the following structures depending on the binary's format.
*
* e_ident - The Magic Number 0x7F,0x45,0x4c,0x46
* - Byte set to 1 or 2 to signify 32-bit or 64-bit format, respectively.
* - Byte set to 1 or 2 to signify little of big endianness, respectively.
* - Byte set to 1 for the version of ELF.
* - Byte identifying the target OS ABI.
* - Byte further identifying the OS ABI Version.
* - 7 Padding Bytes.
* e_type - 2 bytes identifying the object file type.
* e_machine - 2 bytes identifying the instruction set architecture.
* e_version - Byte set to 1 for the version of ELF.
*
* 32-bit:
* e_entry - 4 Bytes specifying the entry point.
* e_phoff - 4 Bytes specifying the offset of the Program Header Table.
* e_shoff - 4 Bytes specifying the offset of the Section Header Table.
*
* 64-bit:
* e_entry - 8 Bytes specifying the entry point.
* e_phoff - 8 Bytes specifying the offset of the Program Header Table.
* e_shoff - 8 Bytes specifying the offset of the Section Header Table.
*
* e_flags - 4 Bytes specifying processor specific flags.
* e_ehsize - 2 Bytes specifying the size of the ELF Header.
* e_phentsize - 2 Bytes specifying the size of a Program Header Table Entry.
* e_phnum - 2 Bytes specifying the number of entries in the Program Header Table.
* e_shentsize - 2 Bytes specifying the size of a Section Header Table Entry.
* e_shnum - 2 Bytes specifying the number of entries in the Section Header Table.
* e_shstrndx - 2 Bytes specifying the index of the section containing the section names in the Section Header Table.
*/
const ehResult = [];
const magic = stream.getBytes(4);
if (magic.join("") !== [0x7f, 0x45, 0x4c, 0x46].join(""))
throw new OperationError("Invalid ELF");
ehResult.push("Magic:".padEnd(align) + `${Utils.byteArrayToChars(magic)}`);
format = stream.readInt(1);
ehResult.push("Format:".padEnd(align) + `${format === 1 ? "32-bit" : "64-bit"}`);
endianness = stream.readInt(1) === 1 ? "le" : "be";
ehResult.push("Endianness:".padEnd(align) + `${endianness === "le" ? "Little" : "Big"}`);
ehResult.push("Version:".padEnd(align) + `${stream.readInt(1).toString()}`);
let ABI = "";
switch (stream.readInt(1)) {
case 0x00:
ABI = "System V";
break;
case 0x01:
ABI = "HP-UX";
break;
case 0x02:
ABI = "NetBSD";
break;
case 0x03:
ABI = "Linux";
break;
case 0x04:
ABI = "GNU Hurd";
break;
case 0x06:
ABI = "Solaris";
break;
case 0x07:
ABI = "AIX";
break;
case 0x08:
ABI = "IRIX";
break;
case 0x09:
ABI = "FreeBSD";
break;
case 0x0A:
ABI = "Tru64";
break;
case 0x0B:
ABI = "Novell Modesto";
break;
case 0x0C:
ABI = "OpenBSD";
break;
case 0x0D:
ABI = "OpenVMS";
break;
case 0x0E:
ABI = "NonStop Kernel";
break;
case 0x0F:
ABI = "AROS";
break;
case 0x10:
ABI = "Fenix OS";
break;
case 0x11:
ABI = "CloudABI";
break;
case 0x12:
ABI = "Stratus Technologies OpenVOS";
break;
default:
break;
}
ehResult.push("ABI:".padEnd(align) + ABI);
// Linux Kernel does not use ABI Version.
const abiVersion = stream.readInt(1).toString();
if (ABI !== "Linux")
ehResult.push("ABI Version:".padEnd(align) + abiVersion);
stream.moveForwardsBy(7);
let eType = "";
switch (stream.readInt(2, endianness)) {
case 0x0000:
eType = "Unknown";
break;
case 0x0001:
eType = "Relocatable File";
break;
case 0x0002:
eType = "Executable File";
break;
case 0x0003:
eType = "Shared Object";
break;
case 0x0004:
eType = "Core File";
break;
case 0xFE00:
eType = "LOOS";
break;
case 0xFEFF:
eType = "HIOS";
break;
case 0xFF00:
eType = "LOPROC";
break;
case 0xFFFF:
eType = "HIPROC";
break;
default:
break;
}
ehResult.push("Type:".padEnd(align) + eType);
let ISA = "";
switch (stream.readInt(2, endianness)) {
case 0x0000:
ISA = "No specific instruction set";
break;
case 0x0001:
ISA = "AT&T WE 32100";
break;
case 0x0002:
ISA = "SPARC";
break;
case 0x0003:
ISA = "x86";
break;
case 0x0004:
ISA = "Motorola 68000 (M68k)";
break;
case 0x0005:
ISA = "Motorola 88000 (M88k)";
break;
case 0x0006:
ISA = "Intel MCU";
break;
case 0x0007:
ISA = "Intel 80860";
break;
case 0x0008:
ISA = "MIPS";
break;
case 0x0009:
ISA = "IBM System/370";
break;
case 0x000A:
ISA = "MIPS RS3000 Little-endian";
break;
case 0x000B:
case 0x000C:
case 0x000D:
case 0x000E:
case 0x0018:
case 0x0019:
case 0x001A:
case 0x001B:
case 0x001C:
case 0x001D:
case 0x001E:
case 0x001F:
case 0x0020:
case 0x0021:
case 0x0022:
case 0x0023:
ISA = "Reserved for future use";
break;
case 0x000F:
ISA = "Hewlett-Packard PA-RISC";
break;
case 0x0011:
ISA = "Fujitsu VPP500";
break;
case 0x0012:
ISA = "Enhanced instruction set SPARC";
break;
case 0x0013:
ISA = "Intel 80960";
break;
case 0x0014:
ISA = "PowerPC";
break;
case 0x0015:
ISA = "PowerPC (64-bit)";
break;
case 0x0016:
ISA = "S390, including S390";
break;
case 0x0017:
ISA = "IBM SPU/SPC";
break;
case 0x0024:
ISA = "NEC V800";
break;
case 0x0025:
ISA = "Fujitsu FR20";
break;
case 0x0026:
ISA = "TRW RH-32";
break;
case 0x0027:
ISA = "Motorola RCE";
break;
case 0x0028:
ISA = "ARM (up to ARMv7/Aarch32)";
break;
case 0x0029:
ISA = "Digital Alpha";
break;
case 0x002A:
ISA = "SuperH";
break;
case 0x002B:
ISA = "SPARC Version 9";
break;
case 0x002C:
ISA = "Siemens TriCore embedded processor";
break;
case 0x002D:
ISA = "Argonaut RISC Core";
break;
case 0x002E:
ISA = "Hitachi H8/300";
break;
case 0x002F:
ISA = "Hitachi H8/300H";
break;
case 0x0030:
ISA = "Hitachi H8S";
break;
case 0x0031:
ISA = "Hitachi H8/500";
break;
case 0x0032:
ISA = "IA-64";
break;
case 0x0033:
ISA = "Standford MIPS-X";
break;
case 0x0034:
ISA = "Motorola ColdFire";
break;
case 0x0035:
ISA = "Motorola M68HC12";
break;
case 0x0036:
ISA = "Fujitsu MMA Multimedia Accelerator";
break;
case 0x0037:
ISA = "Siemens PCP";
break;
case 0x0038:
ISA = "Sony nCPU embedded RISC processor";
break;
case 0x0039:
ISA = "Denso NDR1 microprocessor";
break;
case 0x003A:
ISA = "Motorola Star*Core processor";
break;
case 0x003B:
ISA = "Toyota ME16 processor";
break;
case 0x003C:
ISA = "STMicroelectronics ST100 processor";
break;
case 0x003D:
ISA = "Advanced Logic Corp. TinyJ embedded processor family";
break;
case 0x003E:
ISA = "AMD x86-64";
break;
case 0x003F:
ISA = "Sony DSP Processor";
break;
case 0x0040:
ISA = "Digital Equipment Corp. PDP-10";
break;
case 0x0041:
ISA = "Digital Equipment Corp. PDP-11";
break;
case 0x0042:
ISA = "Siemens FX66 microcontroller";
break;
case 0x0043:
ISA = "STMicroelectronics ST9+ 8/16 bit microcontroller";
break;
case 0x0044:
ISA = "STMicroelectronics ST7 8-bit microcontroller";
break;
case 0x0045:
ISA = "Motorola MC68HC16 Microcontroller";
break;
case 0x0046:
ISA = "Motorola MC68HC11 Microcontroller";
break;
case 0x0047:
ISA = "Motorola MC68HC08 Microcontroller";
break;
case 0x0048:
ISA = "Motorola MC68HC05 Microcontroller";
break;
case 0x0049:
ISA = "Silicon Graphics SVx";
break;
case 0x004A:
ISA = "STMicroelectronics ST19 8-bit microcontroller";
break;
case 0x004B:
ISA = "Digital VAX";
break;
case 0x004C:
ISA = "Axis Communications 32-bit embedded processor";
break;
case 0x004D:
ISA = "Infineon Technologies 32-bit embedded processor";
break;
case 0x004E:
ISA = "Element 14 64-bit DSP Processor";
break;
case 0x004F:
ISA = "LSI Logic 16-bit DSP Processor";
break;
case 0x0050:
ISA = "Donald Knuth's educational 64-bit processor";
break;
case 0x0051:
ISA = "Harvard University machine-independent object files";
break;
case 0x0052:
ISA = "SiTera Prism";
break;
case 0x0053:
ISA = "Atmel AVR 8-bit microcontroller";
break;
case 0x0054:
ISA = "Fujitsu FR30";
break;
case 0x0055:
ISA = "Mitsubishi D10V";
break;
case 0x0056:
ISA = "Mitsubishi D30V";
break;
case 0x0057:
ISA = "NEC v850";
break;
case 0x0058:
ISA = "Mitsubishi M32R";
break;
case 0x0059:
ISA = "Matsushita MN10300";
break;
case 0x005A:
ISA = "Matsushita MN10200";
break;
case 0x005B:
ISA = "picoJava";
break;
case 0x005C:
ISA = "OpenRISC 32-bit embedded processor";
break;
case 0x005D:
ISA = "ARC Cores Tangent-A5";
break;
case 0x005E:
ISA = "Tensilica Xtensa Architecture";
break;
case 0x005F:
ISA = "Alphamosaic VideoCore processor";
break;
case 0x0060:
ISA = "Thompson Multimedia General Purpose Processor";
break;
case 0x0061:
ISA = "National Semiconductor 32000 series";
break;
case 0x0062:
ISA = "Tenor Network TPC processor";
break;
case 0x0063:
ISA = "Trebia SNP 1000 processor";
break;
case 0x0064:
ISA = "STMicroelectronics (www.st.com) ST200 microcontroller";
break;
case 0x008C:
ISA = "TMS320C6000 Family";
break;
case 0x00AF:
ISA = "MCST Elbrus e2k";
break;
case 0x00B7:
ISA = "ARM 64-bits (ARMv8/Aarch64)";
break;
case 0x00F3:
ISA = "RISC-V";
break;
case 0x00F7:
ISA = "Berkeley Packet Filter";
break;
case 0x0101:
ISA = "WDC 65C816";
break;
default:
ISA = "Unimplemented";
break;
}
ehResult.push("Instruction Set Architecture:".padEnd(align) + ISA);
ehResult.push("ELF Version:".padEnd(align) + `${stream.readInt(4, endianness)}`);
const readSize = format === 1 ? 4 : 8;
entry = stream.readInt(readSize, endianness);
phoff = stream.readInt(readSize, endianness);
shoff = stream.readInt(readSize, endianness);
ehResult.push("Entry Point:".padEnd(align) + `0x${Utils.hex(entry)}`);
ehResult.push("Entry PHOFF:".padEnd(align) + `0x${Utils.hex(phoff)}`);
ehResult.push("Entry SHOFF:".padEnd(align) + `0x${Utils.hex(shoff)}`);
const flags = stream.readInt(4, endianness);
ehResult.push("Flags:".padEnd(align) + `${Utils.bin(flags)}`);
ehResult.push("ELF Header Size:".padEnd(align) + `${stream.readInt(2, endianness)} bytes`);
ehResult.push("Program Header Size:".padEnd(align) + `${stream.readInt(2, endianness)} bytes`);
phEntries = stream.readInt(2, endianness);
ehResult.push("Program Header Entries:".padEnd(align) + phEntries);
shentSize = stream.readInt(2, endianness);
ehResult.push("Section Header Size:".padEnd(align) + shentSize + " bytes");
shEntries = stream.readInt(2, endianness);
ehResult.push("Section Header Entries:".padEnd(align) + shEntries);
shstrtab = stream.readInt(2, endianness);
ehResult.push("Section Header Names:".padEnd(align) + shstrtab);
return ehResult.join("\n");
}
/**
* This function parses and extracts relevant information from a Program Header.
*
* @param {stream} stream
* @returns {string}
*/
function programHeader(stream) {
/**
* A Program Header is comprised of the following structures depending on the binary's format.
*
* p_type - 4 Bytes identifying the type of the segment.
*
* 32-bit:
* p_offset - 4 Bytes specifying the offset of the segment.
* p_vaddr - 4 Bytes specifying the virtual address of the segment in memory.
* p_paddr - 4 Bytes specifying the physical address of the segment in memory.
* p_filesz - 4 Bytes specifying the size in bytes of the segment in the file image.
* p_memsz - 4 Bytes specifying the size in bytes of the segment in memory.
* p_flags - 4 Bytes identifying the segment dependent flags.
* p_align - 4 Bytes set to 0 or 1 for alignment or no alignment, respectively.
*
* 64-bit:
* p_flags - 4 Bytes identifying segment dependent flags.
* p_offset - 8 Bytes specifying the offset of the segment.
* p_vaddr - 8 Bytes specifying the virtual address of the segment in memory.
* p_paddr - 8 Bytes specifying the physical address of the segment in memory.
* p_filesz - 8 Bytes specifying the size in bytes of the segment in the file image.
* p_memsz - 8 Bytes specifying the size in bytes of the segment in memory.
* p_align - 8 Bytes set to 0 or 1 for alignment or no alignment, respectively.
*/
/**
* This function decodes the flags bitmask for the Program Header.
*
* @param {integer} flags
* @returns {string}
*/
function readFlags(flags) {
const result = [];
if (flags & 0x1)
result.push("Execute");
if (flags & 0x2)
result.push("Write");
if (flags & 0x4)
result.push("Read");
if (flags & 0xf0000000)
result.push("Unspecified");
return result.join(",");
}
const phResult = [];
let pType = "";
const programHeaderType = stream.readInt(4, endianness);
switch (true) {
case (programHeaderType === 0x00000000):
pType = "Unused";
break;
case (programHeaderType === 0x00000001):
pType = "Loadable Segment";
break;
case (programHeaderType === 0x00000002):
pType = "Dynamic linking information";
break;
case (programHeaderType === 0x00000003):
pType = "Interpreter Information";
break;
case (programHeaderType === 0x00000004):
pType = "Auxiliary Information";
break;
case (programHeaderType === 0x00000005):
pType = "Reserved";
break;
case (programHeaderType === 0x00000006):
pType = "Program Header Table";
break;
case (programHeaderType === 0x00000007):
pType = "Thread-Local Storage Template";
break;
case (programHeaderType >= 0x60000000 && programHeaderType <= 0x6FFFFFFF):
pType = "Reserved Inclusive Range. OS Specific";
break;
case (programHeaderType >= 0x70000000 && programHeaderType <= 0x7FFFFFFF):
pType = "Reserved Inclusive Range. Processor Specific";
break;
default:
break;
}
phResult.push("Program Header Type:".padEnd(align) + pType);
if (format === 2)
phResult.push("Flags:".padEnd(align) + readFlags(stream.readInt(4, endianness)));
const readSize = format === 1? 4 : 8;
phResult.push("Offset Of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)}`);
phResult.push("Virtual Address of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)}`);
phResult.push("Physical Address of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)}`);
phResult.push("Size of Segment:".padEnd(align) + `${stream.readInt(readSize, endianness)} bytes`);
phResult.push("Size of Segment in Memory:".padEnd(align) + `${stream.readInt(readSize, endianness)} bytes`);
if (format === 1)
phResult.push("Flags:".padEnd(align) + readFlags(stream.readInt(4, endianness)));
stream.moveForwardsBy(readSize);
return phResult.join("\n");
}
/**
* This function parses and extracts relevant information from a Section Header.
*
* @param {stream} stream
* @returns {string}
*/
function sectionHeader(stream) {
/**
* A Section Header is comprised of the following structures depending on the binary's format.
*
* sh_name - 4 Bytes identifying the offset into the .shstrtab for the name of this section.
* sh_type - 4 Bytes identifying the type of this header.
*
* 32-bit:
* sh_flags - 4 Bytes identifying section specific flags.
* sh_addr - 4 Bytes identifying the virtual address of the section in memory.
* sh_offset - 4 Bytes identifying the offset of the section in the file.
* sh_size - 4 Bytes specifying the size in bytes of the section in the file image.
* sh_link - 4 Bytes identifying the index of an associated section.
* sh_info - 4 Bytes specifying extra information about the section.
* sh_addralign - 4 Bytes containing the alignment for the section.
* sh_entsize - 4 Bytes specifying the size, in bytes, of each entry in the section.
*
* 64-bit:
* sh_flags - 8 Bytes identifying section specific flags.
* sh_addr - 8 Bytes identifying the virtual address of the section in memory.
* sh_offset - 8 Bytes identifying the offset of the section in the file.
* sh_size - 8 Bytes specifying the size in bytes of the section in the file image.
* sh_link - 4 Bytes identifying the index of an associated section.
* sh_info - 4 Bytes specifying extra information about the section.
* sh_addralign - 8 Bytes containing the alignment for the section.
* sh_entsize - 8 Bytes specifying the size, in bytes, of each entry in the section.
*/
const shResult = [];
const nameOffset = stream.readInt(4, endianness);
let type = "";
const shType = stream.readInt(4, endianness);
switch (true) {
case (shType === 0x00000001):
type = "Program Data";
break;
case (shType === 0x00000002):
type = "Symbol Table";
break;
case (shType === 0x00000003):
type = "String Table";
break;
case (shType === 0x00000004):
type = "Relocation Entries with Addens";
break;
case (shType === 0x00000005):
type = "Symbol Hash Table";
break;
case (shType === 0x00000006):
type = "Dynamic Linking Information";
break;
case (shType === 0x00000007):
type = "Notes";
break;
case (shType === 0x00000008):
type = "Program Space with No Data";
break;
case (shType === 0x00000009):
type = "Relocation Entries with no Addens";
break;
case (shType === 0x0000000A):
type = "Reserved";
break;
case (shType === 0x0000000B):
type = "Dynamic Linker Symbol Table";
break;
case (shType === 0x0000000E):
type = "Array of Constructors";
break;
case (shType === 0x0000000F):
type = "Array of Destructors";
break;
case (shType === 0x00000010):
type = "Array of pre-constructors";
break;
case (shType === 0x00000011):
type = "Section group";
break;
case (shType === 0x00000012):
type = "Extended section indices";
break;
case (shType === 0x00000013):
type = "Number of defined types";
break;
case (shType >= 0x60000000 && shType <= 0x6fffffff):
type = "OS-specific";
break;
case (shType >= 0x70000000 && shType <= 0x7fffffff):
type = "Processor-specific";
break;
case (shType >= 0x80000000 && shType <= 0x8fffffff):
type = "Application-specific";
break;
default:
type = "Unused";
break;
}
shResult.push("Type:".padEnd(align) + type);
let nameResult = "";
if (type !== "Unused") {
nameResult = readString(stream, namesOffset, nameOffset);
shResult.push("Section Name: ".padEnd(align) + nameResult);
}
const readSize = (format === 1) ? 4 : 8;
const flags = stream.readInt(readSize, endianness);
const shFlags = [];
const bitMasks = [
[0x00000001, "Writable"],
[0x00000002, "Alloc"],
[0x00000004, "Executable"],
[0x00000010, "Merge"],
[0x00000020, "Strings"],
[0x00000040, "SHT Info Link"],
[0x00000080, "Link Order"],
[0x00000100, "OS Specific Handling"],
[0x00000200, "Group"],
[0x00000400, "Thread Local Data"],
[0x0FF00000, "OS-Specific"],
[0xF0000000, "Processor Specific"],
[0x04000000, "Special Ordering (Solaris)"],
[0x08000000, "Excluded (Solaris)"]
];
bitMasks.forEach(elem => {
if (flags & elem[0])
shFlags.push(elem[1]);
});
shResult.push("Flags:".padEnd(align) + shFlags);
const vaddr = stream.readInt(readSize, endianness);
shResult.push("Section Vaddr in memory:".padEnd(align) + vaddr);
const shoffset = stream.readInt(readSize, endianness);
shResult.push("Offset of the section:".padEnd(align) + shoffset);
const secSize = stream.readInt(readSize, endianness);
shResult.push("Section Size:".padEnd(align) + secSize);
const associatedSection = stream.readInt(4, endianness);
shResult.push("Associated Section:".padEnd(align) + associatedSection);
const extraInfo = stream.readInt(4, endianness);
shResult.push("Section Extra Information:".padEnd(align) + extraInfo);
// Jump over alignment field.
stream.moveForwardsBy(readSize);
const entSize = stream.readInt(readSize, endianness);
switch (nameResult) {
case ".strtab":
strtabOffset = shoffset;
break;
case ".symtab":
symtabOffset = shoffset;
symtabSize = secSize;
symtabEntSize = entSize;
break;
default:
break;
}
return shResult.join("\n");
}
/**
* This function returns the offset of the Section Header Names Section.
*
* @param {stream} stream
*/
function getNamesOffset(stream) {
const preMove = stream.position;
stream.moveTo(shoff + (shentSize * shstrtab));
if (format === 1) {
stream.moveForwardsBy(0x10);
namesOffset = stream.readInt(4, endianness);
} else {
stream.moveForwardsBy(0x18);
namesOffset = stream.readInt(8, endianness);
}
stream.position = preMove;
}
/**
* This function returns a symbol's name from the string table.
*
* @param {stream} stream
* @returns {string}
*/
function getSymbols(stream) {
/**
* The Symbol Table is comprised of Symbol Table Entries whose structure depends on the binary's format.
*
* 32-bit:
* st_name - 4 Bytes specifying an index in the files symbol string table.
* st_value - 4 Bytes identifying the value associated with the symbol.
* st_size - 4 Bytes specifying the size associated with the symbol (this is not the size of the symbol).
* st_info - A byte specifying the type and binding of the symbol.
* st_other - A byte specifying the symbol's visibility.
* st_shndx - 2 Bytes identifying the section that this symbol is related to.
*
* 64-bit:
* st_name - 4 Bytes specifying an index in the files symbol string table.
* st_info - A byte specifying the type and binding of the symbol.
* st_other - A byte specifying the symbol's visibility.
* st_shndx - 2 Bytes identifying the section that this symbol is related to.
* st_value - 8 Bytes identifying the value associated with the symbol.
* st_size - 8 Bytes specifying the size associated with the symbol (this is not the size of the symbol).
*/
const nameOffset = stream.readInt(4, endianness);
stream.moveForwardsBy(format === 2 ? 20 : 12);
return readString(stream, strtabOffset, nameOffset);
}
input = new Uint8Array(input);
const stream = new Stream(input);
const result = ["=".repeat(align) + " ELF Header " + "=".repeat(align)];
result.push(elfHeader(stream) + "\n");
getNamesOffset(stream);
result.push("=".repeat(align) + " Program Header " + "=".repeat(align));
stream.moveTo(phoff);
for (let i = 0; i < phEntries; i++)
result.push(programHeader(stream) + "\n");
result.push("=".repeat(align) + " Section Header " + "=".repeat(align));
stream.moveTo(shoff);
for (let i = 0; i < shEntries; i++)
result.push(sectionHeader(stream) + "\n");
result.push("=".repeat(align) + " Symbol Table " + "=".repeat(align));
stream.moveTo(symtabOffset);
let elem = "";
for (let i = 0; i < (symtabSize / symtabEntSize); i++)
if ((elem = getSymbols(stream)) !== "")
result.push("Symbol Name:".padEnd(align) + elem);
return result.join("\n");
}
}
export default ELFInfo;

View File

@@ -1,6 +1,9 @@
/**
* Emulation of the Enigma machine.
*
* Tested against various genuine Enigma machines using a variety of inputs
* and settings to confirm correctness.
*
* @author s2224834
* @copyright Crown Copyright 2019
* @license Apache-2.0

View File

@@ -44,7 +44,13 @@ class ExtractDates extends Operation {
date3 = "(?:0[1-9]|1[012])[- /.](?:0[1-9]|[12][0-9]|3[01])[- /.](?:19|20)\\d\\d", // mm/dd/yyyy
regex = new RegExp(date1 + "|" + date2 + "|" + date3, "ig");
return search(input, regex, null, displayTotal);
const results = search(input, regex);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return results.join("\n");
}
}
}

View File

@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import { search, DOMAIN_REGEX } from "../lib/Extract.mjs";
import { caseInsensitiveSort } from "../lib/Sort.mjs";
/**
* Extract domains operation
@@ -25,9 +26,19 @@ class ExtractDomains extends Operation {
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": "Extract.DISPLAY_TOTAL"
name: "Display total",
type: "boolean",
value: false
},
{
name: "Sort",
type: "boolean",
value: false
},
{
name: "Unique",
type: "boolean",
value: false
}
];
}
@@ -38,8 +49,21 @@ class ExtractDomains extends Operation {
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0];
return search(input, DOMAIN_REGEX, null, displayTotal);
const [displayTotal, sort, unique] = args;
const results = search(
input,
DOMAIN_REGEX,
null,
sort ? caseInsensitiveSort : null,
unique
);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return results.join("\n");
}
}
}

View File

@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
import { caseInsensitiveSort } from "../lib/Sort.mjs";
/**
* Extract email addresses operation
@@ -25,9 +26,19 @@ class ExtractEmailAddresses extends Operation {
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
name: "Display total",
type: "boolean",
value: false
},
{
name: "Sort",
type: "boolean",
value: false
},
{
name: "Unique",
type: "boolean",
value: false
}
];
}
@@ -38,10 +49,23 @@ class ExtractEmailAddresses extends Operation {
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
// email regex from: https://www.regextester.com/98066
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ig;
return search(input, regex, null, displayTotal);
const [displayTotal, sort, unique] = args,
// email regex from: https://www.regextester.com/98066
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig;
const results = search(
input,
regex,
null,
sort ? caseInsensitiveSort : null,
unique
);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return results.join("\n");
}
}
}

View File

@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
import { caseInsensitiveSort } from "../lib/Sort.mjs";
/**
* Extract file paths operation
@@ -25,19 +26,29 @@ class ExtractFilePaths extends Operation {
this.outputType = "string";
this.args = [
{
"name": "Windows",
"type": "boolean",
"value": true
name: "Windows",
type: "boolean",
value: true
},
{
"name": "UNIX",
"type": "boolean",
"value": true
name: "UNIX",
type: "boolean",
value: true
},
{
"name": "Display total",
"type": "boolean",
"value": false
name: "Display total",
type: "boolean",
value: false
},
{
name: "Sort",
type: "boolean",
value: false
},
{
name: "Unique",
type: "boolean",
value: false
}
];
}
@@ -48,7 +59,7 @@ class ExtractFilePaths extends Operation {
* @returns {string}
*/
run(input, args) {
const [includeWinPath, includeUnixPath, displayTotal] = args,
const [includeWinPath, includeUnixPath, displayTotal, sort, unique] = args,
winDrive = "[A-Z]:\\\\",
winName = "[A-Z\\d][A-Z\\d\\- '_\\(\\)~]{0,61}",
winExt = "[A-Z\\d]{1,6}",
@@ -65,12 +76,25 @@ class ExtractFilePaths extends Operation {
filePaths = unixPath;
}
if (filePaths) {
const regex = new RegExp(filePaths, "ig");
return search(input, regex, null, displayTotal);
} else {
if (!filePaths) {
return "";
}
const regex = new RegExp(filePaths, "ig");
const results = search(
input,
regex,
null,
sort ? caseInsensitiveSort : null,
unique
);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return results.join("\n");
}
}
}

View File

@@ -21,10 +21,25 @@ class ExtractFiles extends Operation {
constructor() {
super();
// Get the first extension for each signature that can be extracted
let supportedExts = Object.keys(FILE_SIGNATURES).map(cat => {
return FILE_SIGNATURES[cat]
.filter(sig => sig.extractor)
.map(sig => sig.extension.toUpperCase());
});
// Flatten categories and remove duplicates
supportedExts = [].concat(...supportedExts).unique();
this.name = "Extract Files";
this.module = "Default";
this.description = "Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:<ul><li>JPG</li><li>EXE</li><li>ZIP</li><li>PDF</li><li>PNG</li><li>BMP</li><li>FLV</li><li>RTF</li><li>DOCX, PPTX, XLSX</li><li>EPUB</li><li>GZIP</li><li>ZLIB</li><li>ELF, BIN, AXF, O, PRX, SO</li></ul>";
this.infoURL = "https://forensicswiki.org/wiki/File_Carving";
this.description = `Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:
<ul>
<li>
${supportedExts.join("</li><li>")}
</li>
</ul>Minimum File Size can be used to prune small false positives.`;
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=File_Carving";
this.inputType = "ArrayBuffer";
this.outputType = "List<File>";
this.presentType = "html";
@@ -38,7 +53,12 @@ class ExtractFiles extends Operation {
{
name: "Ignore failed extractions",
type: "boolean",
value: "true"
value: true
},
{
name: "Minimum File Size",
type: "number",
value: 100
}
]);
}
@@ -51,6 +71,7 @@ class ExtractFiles extends Operation {
run(input, args) {
const bytes = new Uint8Array(input),
categories = [],
minSize = args.pop(1),
ignoreFailedExtractions = args.pop(1);
args.forEach((cat, i) => {
@@ -65,7 +86,9 @@ class ExtractFiles extends Operation {
const errors = [];
detectedFiles.forEach(detectedFile => {
try {
files.push(extractFile(bytes, detectedFile.fileDetails, detectedFile.offset));
const file = extractFile(bytes, detectedFile.fileDetails, detectedFile.offset);
if (file.size >= minSize)
files.push(file);
} catch (err) {
if (!ignoreFailedExtractions && err.message.indexOf("No extraction algorithm available") < 0) {
errors.push(

View File

@@ -0,0 +1,324 @@
/**
* @author n1073645 [n1073645@gmail.com]
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2020
* @license Apache-2.0
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* Extract ID3 operation
*/
class ExtractID3 extends Operation {
/**
* ExtractID3 constructor
*/
constructor() {
super();
this.name = "Extract ID3";
this.module = "Default";
this.description = "This operation extracts ID3 metadata from an MP3 file.<br><br>ID3 is a metadata container most often used in conjunction with the MP3 audio file format. It allows information such as the title, artist, album, track number, and other information about the file to be stored in the file itself.";
this.infoURL = "https://wikipedia.org/wiki/ID3";
this.inputType = "ArrayBuffer";
this.outputType = "JSON";
this.presentType = "html";
this.args = [];
}
/**
* @param {ArrayBuffer} input
* @param {Object[]} args
* @returns {JSON}
*/
run(input, args) {
input = new Uint8Array(input);
/**
* Extracts the ID3 header fields.
*/
function extractHeader() {
if (!Array.from(input.slice(0, 3)).equals([0x49, 0x44, 0x33]))
throw new OperationError("No valid ID3 header.");
const header = {
"Type": "ID3",
// Tag version
"Version": input[3].toString() + "." + input[4].toString(),
// Header version
"Flags": input[5].toString()
};
input = input.slice(6);
return header;
}
/**
* Converts the size fields to a single integer.
*
* @param {number} num
* @returns {string}
*/
function readSize(num) {
let result = 0;
// The sizes are 7 bit numbers stored in 8 bit locations
for (let i = (num) * 7; i; i -= 7) {
result = (result << i) | input[0];
input = input.slice(1);
}
return result;
}
/**
* Reads frame header based on ID.
*
* @param {string} id
* @returns {number}
*/
function readFrame(id) {
const frame = {};
// Size of frame
const size = readSize(4);
frame.Size = size.toString();
frame.Description = FRAME_DESCRIPTIONS[id];
input = input.slice(2);
// Read data from frame
let data = "";
for (let i = 1; i < size; i++)
data += String.fromCharCode(input[i]);
frame.Data = data;
// Move to next Frame
input = input.slice(size);
return [frame, size];
}
const result = extractHeader();
const headerTagSize = readSize(4);
result.Size = headerTagSize.toString();
const tags = {};
let pos = 10;
// While the current element is in the header
while (pos < headerTagSize) {
// Frame Identifier of frame
let id = String.fromCharCode(input[0]) + String.fromCharCode(input[1]) + String.fromCharCode(input[2]);
input = input.slice(3);
// If the next character is non-zero it is an identifier
if (input[0] !== 0) {
id += String.fromCharCode(input[0]);
}
input = input.slice(1);
if (id in FRAME_DESCRIPTIONS) {
const [frame, size] = readFrame(id);
tags[id] = frame;
pos += 10 + size;
} else if (id === "\x00\x00\x00") { // end of header
break;
} else {
throw new OperationError("Unknown Frame Identifier: " + id);
}
}
result.Tags = tags;
return result;
}
/**
* Displays the extracted data in a more accessible format for web apps.
* @param {JSON} data
* @returns {html}
*/
present(data) {
if (!data || !Object.prototype.hasOwnProperty.call(data, "Tags"))
return JSON.stringify(data, null, 4);
let output = `<table class="table table-hover table-sm table-bordered table-nonfluid">
<tr><th>Tag</th><th>Description</th><th>Data</th></tr>`;
for (const tagID in data.Tags) {
const description = data.Tags[tagID].Description,
contents = data.Tags[tagID].Data;
output += `<tr><td>${tagID}</td><td>${Utils.escapeHtml(description)}</td><td>${Utils.escapeHtml(contents)}</td></tr>`;
}
output += "</table>";
return output;
}
}
// Borrowed from https://github.com/aadsm/jsmediatags
const FRAME_DESCRIPTIONS = {
// v2.2
"BUF": "Recommended buffer size",
"CNT": "Play counter",
"COM": "Comments",
"CRA": "Audio encryption",
"CRM": "Encrypted meta frame",
"ETC": "Event timing codes",
"EQU": "Equalization",
"GEO": "General encapsulated object",
"IPL": "Involved people list",
"LNK": "Linked information",
"MCI": "Music CD Identifier",
"MLL": "MPEG location lookup table",
"PIC": "Attached picture",
"POP": "Popularimeter",
"REV": "Reverb",
"RVA": "Relative volume adjustment",
"SLT": "Synchronized lyric/text",
"STC": "Synced tempo codes",
"TAL": "Album/Movie/Show title",
"TBP": "BPM (Beats Per Minute)",
"TCM": "Composer",
"TCO": "Content type",
"TCR": "Copyright message",
"TDA": "Date",
"TDY": "Playlist delay",
"TEN": "Encoded by",
"TFT": "File type",
"TIM": "Time",
"TKE": "Initial key",
"TLA": "Language(s)",
"TLE": "Length",
"TMT": "Media type",
"TOA": "Original artist(s)/performer(s)",
"TOF": "Original filename",
"TOL": "Original Lyricist(s)/text writer(s)",
"TOR": "Original release year",
"TOT": "Original album/Movie/Show title",
"TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
"TP2": "Band/Orchestra/Accompaniment",
"TP3": "Conductor/Performer refinement",
"TP4": "Interpreted, remixed, or otherwise modified by",
"TPA": "Part of a set",
"TPB": "Publisher",
"TRC": "ISRC (International Standard Recording Code)",
"TRD": "Recording dates",
"TRK": "Track number/Position in set",
"TSI": "Size",
"TSS": "Software/hardware and settings used for encoding",
"TT1": "Content group description",
"TT2": "Title/Songname/Content description",
"TT3": "Subtitle/Description refinement",
"TXT": "Lyricist/text writer",
"TXX": "User defined text information frame",
"TYE": "Year",
"UFI": "Unique file identifier",
"ULT": "Unsychronized lyric/text transcription",
"WAF": "Official audio file webpage",
"WAR": "Official artist/performer webpage",
"WAS": "Official audio source webpage",
"WCM": "Commercial information",
"WCP": "Copyright/Legal information",
"WPB": "Publishers official webpage",
"WXX": "User defined URL link frame",
// v2.3
"AENC": "Audio encryption",
"APIC": "Attached picture",
"ASPI": "Audio seek point index",
"CHAP": "Chapter",
"CTOC": "Table of contents",
"COMM": "Comments",
"COMR": "Commercial frame",
"ENCR": "Encryption method registration",
"EQU2": "Equalisation (2)",
"EQUA": "Equalization",
"ETCO": "Event timing codes",
"GEOB": "General encapsulated object",
"GRID": "Group identification registration",
"IPLS": "Involved people list",
"LINK": "Linked information",
"MCDI": "Music CD identifier",
"MLLT": "MPEG location lookup table",
"OWNE": "Ownership frame",
"PRIV": "Private frame",
"PCNT": "Play counter",
"POPM": "Popularimeter",
"POSS": "Position synchronisation frame",
"RBUF": "Recommended buffer size",
"RVA2": "Relative volume adjustment (2)",
"RVAD": "Relative volume adjustment",
"RVRB": "Reverb",
"SEEK": "Seek frame",
"SYLT": "Synchronized lyric/text",
"SYTC": "Synchronized tempo codes",
"TALB": "Album/Movie/Show title",
"TBPM": "BPM (beats per minute)",
"TCOM": "Composer",
"TCON": "Content type",
"TCOP": "Copyright message",
"TDAT": "Date",
"TDLY": "Playlist delay",
"TDRC": "Recording time",
"TDRL": "Release time",
"TDTG": "Tagging time",
"TENC": "Encoded by",
"TEXT": "Lyricist/Text writer",
"TFLT": "File type",
"TIME": "Time",
"TIPL": "Involved people list",
"TIT1": "Content group description",
"TIT2": "Title/songname/content description",
"TIT3": "Subtitle/Description refinement",
"TKEY": "Initial key",
"TLAN": "Language(s)",
"TLEN": "Length",
"TMCL": "Musician credits list",
"TMED": "Media type",
"TMOO": "Mood",
"TOAL": "Original album/movie/show title",
"TOFN": "Original filename",
"TOLY": "Original lyricist(s)/text writer(s)",
"TOPE": "Original artist(s)/performer(s)",
"TORY": "Original release year",
"TOWN": "File owner/licensee",
"TPE1": "Lead performer(s)/Soloist(s)",
"TPE2": "Band/orchestra/accompaniment",
"TPE3": "Conductor/performer refinement",
"TPE4": "Interpreted, remixed, or otherwise modified by",
"TPOS": "Part of a set",
"TPRO": "Produced notice",
"TPUB": "Publisher",
"TRCK": "Track number/Position in set",
"TRDA": "Recording dates",
"TRSN": "Internet radio station name",
"TRSO": "Internet radio station owner",
"TSOA": "Album sort order",
"TSOP": "Performer sort order",
"TSOT": "Title sort order",
"TSIZ": "Size",
"TSRC": "ISRC (international standard recording code)",
"TSSE": "Software/Hardware and settings used for encoding",
"TSST": "Set subtitle",
"TYER": "Year",
"TXXX": "User defined text information frame",
"UFID": "Unique file identifier",
"USER": "Terms of use",
"USLT": "Unsychronized lyric/text transcription",
"WCOM": "Commercial information",
"WCOP": "Copyright/Legal information",
"WOAF": "Official audio file webpage",
"WOAR": "Official artist/performer webpage",
"WOAS": "Official audio source webpage",
"WORS": "Official internet radio station homepage",
"WPAY": "Payment",
"WPUB": "Publishers official webpage",
"WXXX": "User defined URL link frame"
};
export default ExtractID3;

View File

@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
import { ipSort } from "../lib/Sort.mjs";
/**
* Extract IP addresses operation
@@ -25,24 +26,34 @@ class ExtractIPAddresses extends Operation {
this.outputType = "string";
this.args = [
{
"name": "IPv4",
"type": "boolean",
"value": true
name: "IPv4",
type: "boolean",
value: true
},
{
"name": "IPv6",
"type": "boolean",
"value": false
name: "IPv6",
type: "boolean",
value: false
},
{
"name": "Remove local IPv4 addresses",
"type": "boolean",
"value": false
name: "Remove local IPv4 addresses",
type: "boolean",
value: false
},
{
"name": "Display total",
"type": "boolean",
"value": false
name: "Display total",
type: "boolean",
value: false
},
{
name: "Sort",
type: "boolean",
value: false
},
{
name: "Unique",
type: "boolean",
value: false
}
];
}
@@ -53,7 +64,7 @@ class ExtractIPAddresses extends Operation {
* @returns {string}
*/
run(input, args) {
const [includeIpv4, includeIpv6, removeLocal, displayTotal] = args,
const [includeIpv4, includeIpv6, removeLocal, displayTotal, sort, unique] = args,
ipv4 = "(?:(?:\\d|[01]?\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d|\\d)(?:\\/\\d{1,2})?",
ipv6 = "((?=.*::)(?!.*::.+::)(::)?([\\dA-F]{1,4}:(:|\\b)|){5}|([\\dA-F]{1,4}:){6})((([\\dA-F]{1,4}((?!\\3)::|:\\b|(?![\\dA-F])))|(?!\\2\\3)){2}|(((2[0-4]|1\\d|[1-9])?\\d|25[0-5])\\.?\\b){4})";
let ips = "";
@@ -66,23 +77,29 @@ class ExtractIPAddresses extends Operation {
ips = ipv6;
}
if (ips) {
const regex = new RegExp(ips, "ig");
if (!ips) return "";
if (removeLocal) {
const ten = "10\\..+",
oneninetwo = "192\\.168\\..+",
oneseventwo = "172\\.(?:1[6-9]|2\\d|3[01])\\..+",
onetwoseven = "127\\..+",
removeRegex = new RegExp("^(?:" + ten + "|" + oneninetwo +
"|" + oneseventwo + "|" + onetwoseven + ")");
const regex = new RegExp(ips, "ig");
return search(input, regex, removeRegex, displayTotal);
} else {
return search(input, regex, null, displayTotal);
}
const ten = "10\\..+",
oneninetwo = "192\\.168\\..+",
oneseventwo = "172\\.(?:1[6-9]|2\\d|3[01])\\..+",
onetwoseven = "127\\..+",
removeRegex = new RegExp("^(?:" + ten + "|" + oneninetwo +
"|" + oneseventwo + "|" + onetwoseven + ")");
const results = search(
input,
regex,
removeLocal ? removeRegex : null,
sort ? ipSort : null,
unique
);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return "";
return results.join("\n");
}
}

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
import { fromBinary } from "../lib/Binary.mjs";
import { isImage } from "../lib/FileType.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Extract LSB operation

View File

@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import { search } from "../lib/Extract.mjs";
import { hexadecimalSort } from "../lib/Sort.mjs";
/**
* Extract MAC addresses operation
@@ -25,9 +26,19 @@ class ExtractMACAddresses extends Operation {
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
name: "Display total",
type: "boolean",
value: false
},
{
name: "Sort",
type: "boolean",
value: false
},
{
name: "Unique",
type: "boolean",
value: false
}
];
}
@@ -38,10 +49,21 @@ class ExtractMACAddresses extends Operation {
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0],
regex = /[A-F\d]{2}(?:[:-][A-F\d]{2}){5}/ig;
const [displayTotal, sort, unique] = args,
regex = /[A-F\d]{2}(?:[:-][A-F\d]{2}){5}/ig,
results = search(
input,
regex,
null,
sort ? hexadecimalSort : null,
unique
);
return search(input, regex, null, displayTotal);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return results.join("\n");
}
}
}

View File

@@ -7,7 +7,8 @@
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs";

View File

@@ -6,6 +6,7 @@
import Operation from "../Operation.mjs";
import { search, URL_REGEX } from "../lib/Extract.mjs";
import { caseInsensitiveSort } from "../lib/Sort.mjs";
/**
* Extract URLs operation
@@ -25,9 +26,19 @@ class ExtractURLs extends Operation {
this.outputType = "string";
this.args = [
{
"name": "Display total",
"type": "boolean",
"value": false
name: "Display total",
type: "boolean",
value: false
},
{
name: "Sort",
type: "boolean",
value: false
},
{
name: "Unique",
type: "boolean",
value: false
}
];
}
@@ -38,8 +49,20 @@ class ExtractURLs extends Operation {
* @returns {string}
*/
run(input, args) {
const displayTotal = args[0];
return search(input, URL_REGEX, null, displayTotal);
const [displayTotal, sort, unique] = args;
const results = search(
input,
URL_REGEX,
null,
sort ? caseInsensitiveSort : null,
unique
);
if (displayTotal) {
return `Total found: ${results.length}\n\n${results.join("\n")}`;
} else {
return results.join("\n");
}
}
}

View File

@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
import { isImage } from "../lib/FileType.mjs";
import { toBase64 } from "../lib/Base64.mjs";
import { isWorkerEnvironment } from "../Utils.mjs";
import jimp from "jimp";
import jimplib from "jimp/es/index.js";
const jimp = jimplib.default ? jimplib.default : jimplib;
/**
* Flip Image operation

View File

@@ -65,12 +65,21 @@ class Fork extends Operation {
if (input)
inputs = input.split(splitDelim);
// Set to 1 as if we are here, then there is one, the current one.
let numOp = 1;
// Create subOpList for each tranche to operate on
// (all remaining operations unless we encounter a Merge)
// all remaining operations unless we encounter a Merge
for (i = state.progress + 1; i < opList.length; i++) {
if (opList[i].name === "Merge" && !opList[i].disabled) {
break;
numOp--;
if (numOp === 0 || opList[i].ingValues[0])
break;
else
// Not this Fork's Merge.
subOpList.push(opList[i]);
} else {
if (opList[i].name === "Fork" || opList[i].name === "Subsection")
numOp++;
subOpList.push(opList[i]);
}
}

View File

@@ -30,7 +30,12 @@ class FrequencyDistribution extends Operation {
{
"name": "Show 0%s",
"type": "boolean",
"value": "Entropy.FREQ_ZEROS"
"value": true
},
{
"name": "Show ASCII",
"type": "boolean",
"value": true
}
];
}
@@ -76,14 +81,14 @@ class FrequencyDistribution extends Operation {
* @returns {html}
*/
present(freq, args) {
const showZeroes = args[0];
const [showZeroes, showAscii] = args;
// Print
let output = `<canvas id='chart-area'></canvas><br>
Total data length: ${freq.dataLength}
Number of bytes represented: ${freq.bytesRepresented}
Number of bytes not represented: ${256 - freq.bytesRepresented}
Byte Percentage
<script>
var canvas = document.getElementById("chart-area"),
parentRect = canvas.parentNode.getBoundingClientRect(),
@@ -93,16 +98,32 @@ Byte Percentage
canvas.height = parentRect.height * 0.9;
CanvasComponents.drawBarChart(canvas, scores, "Byte", "Frequency %", 16, 6);
</script>`;
</script>
<table class="table table-hover table-sm">
<tr><th>Byte</th>${showAscii ? "<th>ASCII</th>" : ""}<th>Percentage</th><th></th></tr>`;
for (let i = 0; i < 256; i++) {
if (freq.distribution[i] || showZeroes) {
output += " " + Utils.hex(i, 2) + " (" +
(freq.percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
Array(Math.ceil(freq.percentages[i])+1).join("|") + "\n";
let c = "";
if (showAscii) {
if (i <= 32) {
c = String.fromCharCode(0x2400 + i);
} else if (i === 127) {
c = String.fromCharCode(0x2421);
} else {
c = String.fromCharCode(i);
}
}
const bite = `<td>${Utils.hex(i, 2)}</td>`,
ascii = showAscii ? `<td>${c}</td>` : "",
percentage = `<td>${(freq.percentages[i].toFixed(2).replace(".00", "") + "%").padEnd(8, " ")}</td>`,
bars = `<td>${Array(Math.ceil(freq.percentages[i])+1).join("|")}</td>`;
output += `<tr>${bite}${ascii}${percentage}${bars}</tr>`;
}
}
output += "</table>";
return output;
}

View File

@@ -95,7 +95,7 @@ class FromBCD extends Operation {
if (!packed) {
// Discard each high nibble
for (let i = 0; i < nibbles.length; i++) {
nibbles.splice(i, 1);
nibbles.splice(i, 1); // lgtm [js/loop-iteration-skipped-due-to-shifting]
}
}

View File

@@ -46,7 +46,7 @@ class FromBase extends Operation {
}
const number = input.replace(/\s/g, "").split(".");
let result = new BigNumber(number[0], radix) || 0;
let result = new BigNumber(number[0], radix);
if (number.length === 1) return result;

View File

@@ -84,10 +84,10 @@ class FromBase32 extends Operation {
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);
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;

View File

@@ -0,0 +1,101 @@
/**
* @author Thomas Weißschuh [thomas@t-8ch.de]
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/
import {ALPHABET, highlightToBase45, highlightFromBase45} from "../lib/Base45.mjs";
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
import Utils from "../Utils.mjs";
/**
* From Base45 operation
*/
class FromBase45 extends Operation {
/**
* FromBase45 constructor
*/
constructor() {
super();
this.name = "From Base45";
this.module = "Default";
this.description = "Base45 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. The high number base results in shorter strings than with the decimal or hexadecimal system. Base45 is optimized for usage with QR codes.";
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
this.inputType = "string";
this.outputType = "byteArray";
this.args = [
{
name: "Alphabet",
type: "string",
value: ALPHABET
},
{
name: "Remove non-alphabet chars",
type: "boolean",
value: true
},
];
this.highlight = highlightFromBase45;
this.highlightReverse = highlightToBase45;
}
/**
* @param {string} input
* @param {Object[]} args
* @returns {byteArray}
*/
run(input, args) {
if (!input) return [];
const alphabet = Utils.expandAlphRange(args[0]).join("");
const removeNonAlphChars = args[1];
const res = [];
// Remove non-alphabet characters
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
input = input.replace(re, "");
}
for (const triple of Utils.chunked(input, 3)) {
triple.reverse();
let b = 0;
for (const c of triple) {
const idx = alphabet.indexOf(c);
if (idx === -1) {
throw new OperationError(`Character not in alphabet: '${c}'`);
}
b *= 45;
b += idx;
}
if (b > 65535) {
throw new OperationError(`Triplet too large: '${triple.join("")}'`);
}
if (triple.length > 2) {
/**
* The last triple may only have 2 bytes so we push the MSB when we got 3 bytes
* Pushing MSB
*/
res.push(b >> 8);
}
/**
* Pushing LSB
*/
res.push(b & 0xff);
}
return res;
}
}
export default FromBase45;

View File

@@ -34,74 +34,99 @@ class FromBase64 extends Operation {
name: "Remove non-alphabet chars",
type: "boolean",
value: true
},
{
name: "Strict mode",
type: "boolean",
value: false
}
];
this.checks = [
{
pattern: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
flags: "i",
args: ["A-Za-z0-9+/=", true]
args: ["A-Za-z0-9+/=", true, false]
},
{
pattern: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
flags: "i",
args: ["A-Za-z0-9-_", true]
args: ["A-Za-z0-9-_", true, false]
},
{
pattern: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
flags: "i",
args: ["A-Za-z0-9+\\-=", true]
args: ["A-Za-z0-9+\\-=", true, false]
},
{
pattern: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
flags: "i",
args: ["./0-9A-Za-z=", true]
args: ["./0-9A-Za-z=", true, false]
},
{
pattern: "^\\s*[A-Z\\d_.]{20,}\\s*$",
flags: "i",
args: ["A-Za-z0-9_.", true]
args: ["A-Za-z0-9_.", true, false]
},
{
pattern: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
flags: "i",
args: ["A-Za-z0-9._-", true]
args: ["A-Za-z0-9._-", true, false]
},
{
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
flags: "i",
args: ["0-9a-zA-Z+/=", true]
args: ["0-9a-zA-Z+/=", true, false]
},
{
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
flags: "i",
args: ["0-9A-Za-z+/=", true]
args: ["0-9A-Za-z+/=", true, false]
},
{
pattern: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
flags: "",
args: [" -_", false]
args: [" -_", false, false]
},
{
pattern: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
flags: "i",
args: ["+\\-0-9A-Za-z", true]
args: ["+\\-0-9A-Za-z", true, false]
},
{
pattern: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
flags: "",
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true]
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true, false]
},
{
pattern: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
flags: "i",
args: ["N-ZA-Mn-za-m0-9+/=", true]
args: ["N-ZA-Mn-za-m0-9+/=", true, false]
},
{
pattern: "^\\s*[A-Z\\d./]{20,}\\s*$",
flags: "i",
args: ["./0-9A-Za-z", true]
args: ["./0-9A-Za-z", true, false]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
flags: "i",
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true, false]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
flags: "i",
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true, false]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
flags: "i",
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true, false]
},
{
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
flags: "i",
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true, false]
}
];
}
@@ -111,9 +136,9 @@ class FromBase64 extends Operation {
* @returns {byteArray}
*/
run(input, args) {
const [alphabet, removeNonAlphChars] = args;
const [alphabet, removeNonAlphChars, strictMode] = args;
return fromBase64(input, alphabet, "byteArray", removeNonAlphChars);
return fromBase64(input, alphabet, "byteArray", removeNonAlphChars, strictMode);
}
/**

View File

@@ -32,6 +32,40 @@ class FromBase85 extends Operation {
type: "editableOption",
value: ALPHABET_OPTIONS
},
{
name: "Remove non-alphabet chars",
type: "boolean",
value: true
},
];
this.checks = [
{
pattern:
"^\\s*(?:<~)?" + // Optional whitespace and starting marker
"[\\s!-uz]*" + // Any amount of base85 characters and whitespace
"[!-uz]{15}" + // At least 15 continoues base85 characters without whitespace
"[\\s!-uz]*" + // Any amount of base85 characters and whitespace
"(?:~>)?\\s*$", // Optional ending marker and whitespace
args: ["!-u"],
},
{
pattern:
"^" +
"[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" +
"[0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]{15}" + // At least 15 continoues base85 characters without whitespace
"[\\s0-9a-zA-Z.\\-:+=^!/*?&<>()[\\]{}@%$#]*" +
"$",
args: ["0-9a-zA-Z.\\-:+=^!/*?&<>()[]{}@%$#"],
},
{
pattern:
"^" +
"[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" +
"[0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]{15}" + // At least 15 continoues base85 characters without whitespace
"[\\s0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~]*" +
"$",
args: ["0-9A-Za-z!#$%&()*+\\-;<=>?@^_`{|}~"],
},
];
}
@@ -43,6 +77,7 @@ class FromBase85 extends Operation {
run(input, args) {
const alphabet = Utils.expandAlphRange(args[0]).join(""),
encoding = alphabetName(alphabet),
removeNonAlphChars = args[1],
result = [];
if (alphabet.length !== 85 ||
@@ -50,11 +85,18 @@ class FromBase85 extends Operation {
throw new OperationError("Alphabet must be of length 85");
}
if (input.length === 0) return [];
const matches = input.match(/<~(.+?)~>/);
// Remove delimiters if present
const matches = input.match(/^<~(.+?)~>$/);
if (matches !== null) input = matches[1];
// Remove non-alphabet characters
if (removeNonAlphChars) {
const re = new RegExp("[^" + alphabet.replace(/[[\]\\\-^$]/g, "\\$&") + "]", "g");
input = input.replace(re, "");
}
if (input.length === 0) return [];
let i = 0;
let block, blockBytes;
while (i < input.length) {
@@ -69,7 +111,7 @@ class FromBase85 extends Operation {
.map((chr, idx) => {
const digit = alphabet.indexOf(chr);
if (digit < 0 || digit > 84) {
throw `Invalid character '${chr}' at index ${idx}`;
throw `Invalid character '${chr}' at index ${i + idx}`;
}
return digit;
});

View File

@@ -31,6 +31,12 @@ class FromBinary extends Operation {
"name": "Delimiter",
"type": "option",
"value": BIN_DELIM_OPTIONS
},
{
"name": "Byte Length",
"type": "number",
"value": 8,
"min": 1
}
];
this.checks = [
@@ -78,7 +84,8 @@ class FromBinary extends Operation {
* @returns {byteArray}
*/
run(input, args) {
return fromBinary(input, args[0]);
const byteLen = args[1] ? args[1] : 8;
return fromBinary(input, args[0], byteLen);
}
/**

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