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

Compare commits

...

61 Commits

Author SHA1 Message Date
n1474335
58361e58f8 5.12.0 2017-07-19 16:03:24 +00:00
n1474335
96b4361b31 Merge branch 'feature-bcd' 2017-07-19 15:55:36 +00:00
n1474335
c773edceb9 Added BCD operations with tests 2017-07-19 15:29:37 +00:00
n1474335
bcaef8ba54 5.11.7 2017-07-18 16:17:59 +00:00
n1474335
c7d0b0ccc5 Merge branch 'feature-xor-brute-scheme' 2017-07-18 16:17:13 +00:00
n1474335
38792a0f02 Added differential schemes to 'XOR Brute Force' operation 2017-07-18 16:09:22 +00:00
n1474335
cda557e1b9 Removed sessionStorage as it is no longer used and marginally affects performance 2017-07-18 14:28:51 +00:00
n1474335
8d09f7c7eb 5.11.6 2017-07-17 13:31:37 +00:00
n1474335
4845a56435 Fixed Diff test for new higlighting class 2017-07-17 13:31:28 +00:00
n1474335
f164dcdd70 Fixed Diff highlighting classes 2017-07-17 13:19:08 +00:00
n1474335
6c8da6b070 Added ellipsis to overflowing args 2017-07-13 15:11:21 +00:00
n1474335
09df753ca9 5.11.5 2017-07-12 14:34:50 +00:00
n1474335
72ec9df1b1 Fixed option naming conventions 2017-07-12 14:34:45 +00:00
n1474335
086d5272ac 5.11.4 2017-07-12 12:49:22 +00:00
n1474335
2555de7712 Fixed bug in firefox where recipes containing an = character would not load from the URL 2017-07-12 12:49:10 +00:00
n1474335
645e540c66 5.11.3 2017-07-10 11:49:48 +00:00
n1474335
d16e1a4451 Fixed bug in 'Show Base64 offsets' where highlights did not show 2017-07-10 11:49:41 +00:00
n1474335
7c5dd2bd78 5.11.2 2017-07-07 13:30:27 +00:00
n1474335
2f0121f0e4 Merge branch 'bwhitn-unzipmod' 2017-07-07 13:28:21 +00:00
n1474335
7e310a8de7 Moved file switch listener to correct block 2017-07-07 13:27:47 +00:00
n1474335
c460c2bf6b Replaced hexToByteArray with fromHex and byteArrayToHex with toHex. Switched displayFilesAsHTML operation to use template strings and introduced markup formatting method. 2017-07-07 13:23:58 +00:00
n1474335
2400de337b Merge branch 'unzipmod' of https://github.com/bwhitn/CyberChef into bwhitn-unzipmod 2017-07-03 15:28:12 +00:00
n1474335
85d41085de 5.11.1 2017-07-03 15:25:52 +00:00
n1474335
48d45d026e Merge branch 'bwhitn-filetimemod' 2017-07-03 15:25:39 +00:00
n1474335
183c57643b Tidied up changes to filetime operations and brought tests up to date 2017-07-03 15:25:14 +00:00
n1474335
e7cea889ab Merge branch 'filetimemod' of https://github.com/bwhitn/CyberChef into bwhitn-filetimemod 2017-07-03 15:19:42 +00:00
n1474335
61c799447b Improved banner CSS 2017-07-03 15:18:47 +00:00
bwhitn
ad25daf206 Allow hex and decimal format for Windows Filetime format as those are the formats they are typically represented in 2017-07-02 20:04:25 -04:00
bwhitn
4143bba89f This adds the ability to move the file data from Utils.displayFilesAsHTML to the input. 2017-07-01 00:40:22 -04:00
n1474335
8eb7d65b74 5.11.0 2017-06-28 19:56:07 +01:00
n1474335
51798553e1 Merge branch 'artemisbot-features/bifid' 2017-06-28 19:55:19 +01:00
n1474335
323928ff86 Tidied up Bifid operations 2017-06-28 19:54:34 +01:00
n1474335
fe3aeabd0a Merge branch 'features/bifid' of https://github.com/artemisbot/CyberChef into artemisbot-features/bifid 2017-06-28 19:27:42 +01:00
n1474335
ec7a55dba6 5.10.7 2017-06-27 14:13:30 +00:00
n1474335
b4fe708d70 Merge branch 'bug-display-as-html' 2017-06-27 14:13:24 +00:00
n1474335
c3469bd545 Correctly escape filenames in displayFilesAsHTML 2017-06-27 14:04:30 +00:00
n1474335
92018b761d 5.10.6 2017-06-26 21:54:15 +01:00
n1474335
bb45ff0515 Merge branch 'bug-preloader' 2017-06-26 21:53:21 +01:00
n1474335
df1405e998 Fixed mildly infuriating bug where the preloader rings overlap 2017-06-26 21:47:57 +01:00
n1474335
62ec018bb2 Update README.md 2017-06-26 16:35:51 +01:00
n1474335
14b7c4bf23 Improved support for different alphabets in 'Substitute' operation 2017-06-23 13:21:19 +00:00
n1474335
5c774a3ce2 Updated to allow delimiter to be set 2017-06-23 12:18:08 +00:00
Matt C
246480daef Fixed styling errors 2017-06-22 17:13:31 +01:00
Matt C
91c6f682e7 Added Bifid Cipher Encode & Decode
Bifid Cipher + Tests
2017-06-21 22:28:17 +01:00
n1474335
a417a6469c Added package-lock.json 2017-06-20 14:45:20 +00:00
n1474335
2821bdd52b 5.10.5 2017-06-19 16:50:07 +00:00
n1474335
d37300be39 Merge branch 'bug-astral' 2017-06-19 16:49:17 +00:00
n1474335
15b83072bb Added support for astral characters to charcode ops 2017-06-19 15:40:36 +00:00
n1474335
6d2e2259db 5.10.4 2017-06-16 15:44:42 +00:00
n1474335
5b70614212 Merge branch 'tidy-links' 2017-06-16 15:38:37 +00:00
n1474335
4363da534d Updated links to new hash variant and cleaned up About and Option panes 2017-06-16 15:36:42 +00:00
n1474335
685f7a4f00 Create CODE_OF_CONDUCT.md 2017-06-16 15:32:55 +01:00
n1474335
6e26f25418 5.10.3 2017-06-16 11:26:03 +00:00
n1474335
d8051ce9a2 Merge branch 'feature-hashurls' 2017-06-16 11:23:16 +00:00
n1474335
00e7d8a390 The recipe and input are now stored in the hash part of the URL 2017-06-16 11:04:35 +00:00
bwhitn
213ec028b8 Merge pull request #2 from gchq/master
Current
2017-06-15 19:43:22 -04:00
n1474335
61951e76ac 5.10.2 2017-06-15 14:25:03 +00:00
n1474335
47cf763b3f Merge branch 'bug-findreplace' 2017-06-15 14:23:35 +00:00
n1474335
04aac03d6e Fixed global matching for simple strings in 'Find / Replace' operation. Closes #25. 2017-06-15 14:21:30 +00:00
n1474335
3faef2c9c9 5.10.1 2017-06-13 16:31:00 +00:00
n1474335
eda17d1671 Added mixed content note to 'HTTP request' error message. 2017-06-13 16:30:55 +00:00
39 changed files with 6889 additions and 310 deletions

View File

@@ -93,6 +93,7 @@
"moment": false, "moment": false,
"COMPILE_TIME": false, "COMPILE_TIME": false,
"COMPILE_MSG": false "COMPILE_MSG": false,
"PKG_VERSION": false
} }
} }

46
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at oss@gchq.gov.uk. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -164,7 +164,8 @@ module.exports = function (grunt) {
}), }),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
COMPILE_TIME: JSON.stringify(compileTime), COMPILE_TIME: JSON.stringify(compileTime),
COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || "") COMPILE_MSG: JSON.stringify(grunt.option("compile-msg") || grunt.option("msg") || ""),
PKG_VERSION: JSON.stringify(pkg.version)
}), }),
new ExtractTextPlugin("styles.css"), new ExtractTextPlugin("styles.css"),
], ],

View File

@@ -6,9 +6,9 @@
#### *The Cyber Swiss Army Knife* #### *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 creating hexdumps, simple encoding like XOR or Base64, more complex encryption like AES, DES and Blowfish, data compression and decompression, calculating hashes and checksums, IPv6 and X.509 parsing, 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 or Base64, more complex encryption like AES, DES and Blowfish, creating binary and hexdumps, compression and decompression of data, calculating hashes and checksums, IPv6 and X.509 parsing, changing character encodings, and much more.
The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years. Every effort has been made to structure the code in a readable and extendable format, however it should be noted that the analyst is not a professional developer and the code has not been peer-reviewed for compliance with a formal specification. The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years. Every effort has been made to structure the code in a readable and extendable format, however it should be noted that the analyst is not a professional developer.
## Live demo ## Live demo
@@ -24,9 +24,9 @@ Cryptographic operations in CyberChef should not be relied upon to provide secur
There are four main areas in CyberChef: There are four main areas in CyberChef:
1. The **input** box in the top right, where you can paste, type or drag the data you want to operate on. 1. The **input** box in the top right, where you can paste, type or drag the data you want to operate on.
2. The **output** box in the bottom right, where the outcome of the specified processing will be displayed. 2. The **output** box in the bottom right, where the outcome of your processing will be displayed.
3. The **operations** list on the far left, where you can find all the operations that CyberChef is capable of in categorised lists, or by searching. 3. The **operations** list on the far left, where you can find all the operations that CyberChef is capable of in categorised lists, or by searching.
4. The **recipe** area in the middle, where you drag the operations that you want to use and specify arguments and options. 4. The **recipe** area in the middle, where you can drag the operations that you want to use and specify arguments and options.
You can use as many operations as you like in simple or complex ways. Some examples are as follows: You can use as many operations as you like in simple or complex ways. Some examples are as follows:
@@ -66,12 +66,16 @@ You can use as many operations as you like in simple or complex ways. Some examp
## Browser support ## Browser support
CyberChef is built to support Google Chrome 40+, Mozilla Firefox 35+ and Microsoft Edge 14+. CyberChef is built to support
- Google Chrome 40+
- Mozilla Firefox 35+
- Microsoft Edge 14+
## Contributing ## Contributing
An installation walkthrough, how-to guides for adding new operations, 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). An installation walkthrough, how-to guides for adding new operations and themes, descriptions of the repository structure, available data types and coding conventions can all be found in the project [wiki pages](https://github.com/gchq/CyberChef/wiki).
- Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0) - Sign the [GCHQ Contributor Licence Agreement](https://github.com/gchq/Gaffer/wiki/GCHQ-OSS-Contributor-License-Agreement-V1.0)
- Push your changes to your fork. - Push your changes to your fork.
@@ -84,10 +88,10 @@ CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/lice
[1]: https://gchq.github.io/CyberChef [1]: https://gchq.github.io/CyberChef
[2]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1 [2]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1
[3]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA [3]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA
[4]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Parse%20IPv6%20address%22%2C%22args%22%3A%5B%5D%7D%5D&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy [4]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22Parse%20IPv6%20address%22%2C%22args%22%3A%5B%5D%7D%5D&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy
[5]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22From%20Hexdump%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22Gunzip%22%2C%22args%22%3A%5B%5D%7D%5D&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu%2Fy7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb%2F3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw [5]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22From%20Hexdump%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22Gunzip%22%2C%22args%22%3A%5B%5D%7D%5D&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu%2Fy7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb%2F3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw
[6]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22From%20UNIX%20Timestamp%22%2C%22args%22%3A%5B%22Seconds%20(s)%22%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA [6]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22From%20UNIX%20Timestamp%22%2C%22args%22%3A%5B%22Seconds%20(s)%22%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA
[7]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22Conditional%20Jump%22%2C%22args%22%3A%5B%221%22%2C%222%22%2C%2210%22%5D%7D%2C%7B%22op%22%3A%22To%20Hex%22%2C%22args%22%3A%5B%22Space%22%5D%7D%2C%7B%22op%22%3A%22Return%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22To%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%5D%7D%5D&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA [7]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22Conditional%20Jump%22%2C%22args%22%3A%5B%221%22%2C%222%22%2C%2210%22%5D%7D%2C%7B%22op%22%3A%22To%20Hex%22%2C%22args%22%3A%5B%22Space%22%5D%7D%2C%7B%22op%22%3A%22Return%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22To%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%5D%7D%5D&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA
[8]: https://gchq.github.io/CyberChef/?recipe=%5B%7B%22op%22%3A%22XOR%22%2C%22args%22%3A%5B%7B%22option%22%3A%22Hex%22%2C%22string%22%3A%223a%22%7D%2Cfalse%2Cfalse%5D%7D%2C%7B%22op%22%3A%22To%20Hexdump%22%2C%22args%22%3A%5B%2216%22%2Cfalse%2Cfalse%5D%7D%5D&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4 [8]: https://gchq.github.io/CyberChef/#recipe=%5B%7B%22op%22%3A%22XOR%22%2C%22args%22%3A%5B%7B%22option%22%3A%22Hex%22%2C%22string%22%3A%223a%22%7D%2Cfalse%2Cfalse%5D%7D%2C%7B%22op%22%3A%22To%20Hexdump%22%2C%22args%22%3A%5B%2216%22%2Cfalse%2Cfalse%5D%7D%5D&input=VGhlIGFuc3dlciB0byB0aGUgdWx0aW1hdGUgcXVlc3Rpb24gb2YgbGlmZSwgdGhlIFVuaXZlcnNlLCBhbmQgZXZlcnl0aGluZyBpcyA0Mi4

5761
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "cyberchef", "name": "cyberchef",
"version": "5.10.0", "version": "5.12.0",
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.", "description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
"author": "n1474335 <n1474335@gmail.com>", "author": "n1474335 <n1474335@gmail.com>",
"homepage": "https://gchq.github.io/CyberChef", "homepage": "https://gchq.github.io/CyberChef",

View File

@@ -73,7 +73,7 @@ Ingredient.prepare = function(data, type) {
case "byteArray": case "byteArray":
if (typeof data == "string") { if (typeof data == "string") {
data = data.replace(/\s+/g, ""); data = data.replace(/\s+/g, "");
return Utils.hexToByteArray(data); return Utils.fromHex(data);
} else { } else {
return data; return data;
} }

View File

@@ -23,6 +23,16 @@ const Utils = {
* Utils.chr(97); * Utils.chr(97);
*/ */
chr: function(o) { chr: function(o) {
// Detect astral symbols
// Thanks to @mathiasbynens for this solution
// https://mathiasbynens.be/notes/javascript-unicode
if (o > 0xffff) {
o -= 0x10000;
const high = String.fromCharCode(o >>> 10 & 0x3ff | 0xd800);
o = 0xdc00 | o & 0x3ff;
return high + String.fromCharCode(o);
}
return String.fromCharCode(o); return String.fromCharCode(o);
}, },
@@ -38,6 +48,18 @@ const Utils = {
* Utils.ord('a'); * Utils.ord('a');
*/ */
ord: function(c) { ord: function(c) {
// Detect astral symbols
// Thanks to @mathiasbynens for this solution
// https://mathiasbynens.be/notes/javascript-unicode
if (c.length === 2) {
const high = c.charCodeAt(0);
const low = c.charCodeAt(1);
if (high >= 0xd800 && high < 0xdc00 &&
low >= 0xdc00 && low < 0xe000) {
return (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
}
}
return c.charCodeAt(0); return c.charCodeAt(0);
}, },
@@ -216,7 +238,7 @@ const Utils = {
str = Utils.byteArrayToChars(Utils.strToByteArray(str)); str = Utils.byteArrayToChars(Utils.strToByteArray(str));
} }
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-\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 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; const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
str = str.replace(re, "."); str = str.replace(re, ".");
@@ -259,6 +281,22 @@ const Utils = {
}, },
/**
* Escape a string containing regex control characters so that it can be safely
* used in a regex without causing unintended behaviours.
*
* @param {string} str
* @returns {string}
*
* @example
* // returns "\[example\]"
* Utils.escapeRegex("[example]");
*/
escapeRegex: function(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
},
/** /**
* Expand an alphabet range string into a list of the characters in that range. * Expand an alphabet range string into a list of the characters in that range.
* *
@@ -302,48 +340,6 @@ const Utils = {
}, },
/**
* Translates a hex string into an array of bytes.
*
* @param {string} hexStr
* @returns {byteArray}
*
* @example
* // returns [0xfe, 0x09, 0xa7]
* Utils.hexToByteArray("fe09a7");
*/
hexToByteArray: function(hexStr) {
// TODO: Handle errors i.e. input string is not hex
if (!hexStr) return [];
hexStr = hexStr.replace(/\s+/g, "");
const byteArray = [];
for (let i = 0; i < hexStr.length; i += 2) {
byteArray.push(parseInt(hexStr.substr(i, 2), 16));
}
return byteArray;
},
/**
* Translates an array of bytes to a hex string.
*
* @param {byteArray} byteArray
* @returns {string}
*
* @example
* // returns "fe09a7"
* Utils.byteArrayToHex([0xfe, 0x09, 0xa7]);
*/
byteArrayToHex: function(byteArray) {
if (!byteArray) return "";
let hexStr = "";
for (let i = 0; i < byteArray.length; i++) {
hexStr += Utils.hex(byteArray[i]) + " ";
}
return hexStr.slice(0, hexStr.length-1);
},
/** /**
* Converts a string to a byte array. * Converts a string to a byte array.
* Treats the string as UTF-8 if any values are over 255. * Treats the string as UTF-8 if any values are over 255.
@@ -396,7 +392,7 @@ const Utils = {
/** /**
* Converts a string to a charcode array * Converts a string to a unicode charcode array
* *
* @param {string} str * @param {string} str
* @returns {byteArray} * @returns {byteArray}
@@ -409,12 +405,23 @@ const Utils = {
* Utils.strToCharcode("你好"); * Utils.strToCharcode("你好");
*/ */
strToCharcode: function(str) { strToCharcode: function(str) {
const byteArray = new Array(str.length); const charcode = new Array();
let i = str.length;
while (i--) { for (let i = 0; i < str.length; i++) {
byteArray[i] = str.charCodeAt(i); let ord = str.charCodeAt(i);
// Detect and merge astral symbols
if (i < str.length - 1 && ord >= 0xd800 && ord < 0xdc00) {
const low = str[i + 1].charCodeAt(0);
if (low >= 0xdc00 && low < 0xe000) {
ord = Utils.ord(str[i] + str[++i]);
}
}
charcode.push(ord);
} }
return byteArray;
return charcode;
}, },
@@ -777,7 +784,7 @@ const Utils = {
if (removeScriptAndStyle) { if (removeScriptAndStyle) {
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, ""); htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
} }
return htmlStr.replace(/<[^>\n]+>/g, ""); return htmlStr.replace(/<[^>]+>/g, "");
}, },
@@ -894,17 +901,20 @@ const Utils = {
* @param {Object[]} files * @param {Object[]} files
* @returns {html} * @returns {html}
*/ */
displayFilesAsHTML: function(files){ displayFilesAsHTML: function(files) {
/* <NL> and <SP> used to denote newlines and spaces in HTML markup.
* If a non-html operation is used, all markup will be removed but these
* whitespace chars will remain for formatting purposes.
*/
const formatDirectory = function(file) { const formatDirectory = function(file) {
const html = "<div class='panel panel-default'>" + const html = `<div class='panel panel-default' style='white-space: normal;'>
"<div class='panel-heading' role='tab'>" + <div class='panel-heading' role='tab'>
"<h4 class='panel-title'>" + <h4 class='panel-title'>
file.fileName + <NL>${Utils.escapeHtml(file.fileName)}
// The following line is for formatting when HTML is stripped </h4>
"<span style='display: none'>\n0 bytes\n</span>" + </div>
"</h4>" + </div>`;
"</div>" +
"</div>";
return html; return html;
}; };
@@ -915,45 +925,52 @@ const Utils = {
); );
const blobUrl = URL.createObjectURL(blob); const blobUrl = URL.createObjectURL(blob);
const downloadAnchorElem = "<a href='" + blobUrl + "' " + const viewFileElem = `<a href='#collapse${i}'
"title='Download " + Utils.escapeHtml(file.fileName) + "' " + class='collapsed'
"download='" + Utils.escapeHtml(file.fileName) + "'>\u21B4</a>"; data-toggle='collapse'
aria-expanded='true'
aria-controls='collapse${i}'
title="Show/hide contents of '${Utils.escapeHtml(file.fileName)}'">&#x1f441;&#xfe0f;</a>`;
const expandFileContentsElem = "<a href='#collapse" + i + "' " + const downloadFileElem = `<a href='${blobUrl}'
"class='collapsed' " + title='Download ${Utils.escapeHtml(file.fileName)}'
"data-toggle='collapse' " + download='${Utils.escapeHtml(file.fileName)}'>&#x1f4be;</a>`;
"aria-expanded='true' " +
"aria-controls='collapse" + i + "' " +
"title=\"Show/hide contents of '" + Utils.escapeHtml(file.fileName) + "'\">&#x1F50D</a>";
const html = "<div class='panel panel-default'>" + const hexFileData = Utils.toHexFast(new Uint8Array(file.bytes));
"<div class='panel-heading' role='tab' id='heading" + i + "'>" +
"<h4 class='panel-title'>" + const switchToInputElem = `<a href='#switchFileToInput${i}'
"<div>" + class='file-switch'
Utils.escapeHtml(file.fileName) + title='Move file to input as hex'
" " + expandFileContentsElem + fileValue='${hexFileData}'>&#x21e7;</a>`;
" " + downloadAnchorElem +
"<span class='pull-right'>" + const html = `<div class='panel panel-default' style='white-space: normal;'>
// These are for formatting when stripping HTML <div class='panel-heading' role='tab' id='heading${i}'>
"<span style='display: none'>\n</span>" + <h4 class='panel-title'>
file.size.toLocaleString() + " bytes" + <div>
"<span style='display: none'>\n</span>" + ${Utils.escapeHtml(file.fileName)}<NL>
"</span>" + ${viewFileElem}<SP>
"</div>" + ${downloadFileElem}<SP>
"</h4>" + ${switchToInputElem}<SP>
"</div>" + <span class='pull-right'>
"<div id='collapse" + i + "' class='panel-collapse collapse' " + <NL>${file.size.toLocaleString()} bytes
"role='tabpanel' aria-labelledby='heading" + i + "'>" + </span>
"<div class='panel-body'>" + </div>
"<pre><code>" + Utils.escapeHtml(file.contents) + "</pre></code></div>" + </h4>
"</div>" + </div>
"</div>"; <div id='collapse${i}' class='panel-collapse collapse'
role='tabpanel' aria-labelledby='heading${i}'>
<div class='panel-body'>
<NL><NL><pre><code>${Utils.escapeHtml(file.contents)}</code></pre>
</div>
</div>
</div>`;
return html; return html;
}; };
let html = "<div style='padding: 5px;'>" + let html = `<div style='padding: 5px; white-space: normal;'>
files.length + ${files.length} file(s) found<NL>
" file(s) found</div>\n"; </div>`;
files.forEach(function(file, i) { files.forEach(function(file, i) {
if (typeof file.contents !== "undefined") { if (typeof file.contents !== "undefined") {
html += formatFile(file, i); html += formatFile(file, i);
@@ -961,14 +978,53 @@ const Utils = {
html += formatDirectory(file); html += formatDirectory(file);
} }
}); });
return html;
return html.replace(/(?:(<pre>(?:\n|.)*<\/pre>)|\s{2,})/g, "$1") // Remove whitespace from markup
.replace(/<NL>/g, "\n") // Replace <NP> with newlines
.replace(/<SP>/g, " "); // Replace <SP> with spaces
},
/**
* Parses URI parameters into a JSON object.
*
* @param {string} paramStr - The serialised query or hash section of a URI
* @returns {object}
*
* @example
* // returns {a: 'abc', b: '123'}
* Utils.parseURIParams("?a=abc&b=123")
* Utils.parseURIParams("#a=abc&b=123")
*/
parseURIParams: function(paramStr) {
if (paramStr === "") return {};
// Cut off ? or # and split on &
if (paramStr[0] === "?" ||
paramStr[0] === "#") {
paramStr = paramStr.substr(1);
}
const params = paramStr.split("&");
const result = {};
for (let i = 0; i < params.length; i++) {
const param = params[i].split("=");
if (param.length !== 2) {
result[params[i]] = true;
} else {
result[param[0]] = decodeURIComponent(param[1].replace(/\+/g, " "));
}
}
return result;
}, },
/** /**
* Actual modulo function, since % is actually the remainder function in JS. * Actual modulo function, since % is actually the remainder function in JS.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
@@ -981,7 +1037,7 @@ const Utils = {
/** /**
* Finds the greatest common divisor of two numbers. * Finds the greatest common divisor of two numbers.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}
@@ -997,7 +1053,7 @@ const Utils = {
/** /**
* Finds the modular inverse of two values. * Finds the modular inverse of two values.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {number} x * @param {number} x
* @param {number} y * @param {number} y
* @returns {number} * @returns {number}

View File

@@ -46,6 +46,8 @@ const Categories = [
"From Base58", "From Base58",
"To Base", "To Base",
"From Base", "From Base",
"To BCD",
"From BCD",
"To HTML Entity", "To HTML Entity",
"From HTML Entity", "From HTML Entity",
"URL Encode", "URL Encode",
@@ -89,6 +91,8 @@ const Categories = [
"Vigenère Decode", "Vigenère Decode",
"To Morse Code", "To Morse Code",
"From Morse Code", "From Morse Code",
"Bifid Cipher Encode",
"Bifid Cipher Decode",
"Affine Cipher Encode", "Affine Cipher Encode",
"Affine Cipher Decode", "Affine Cipher Decode",
"Atbash Cipher", "Atbash Cipher",

View File

@@ -2,6 +2,7 @@ import FlowControl from "../FlowControl.js";
import Base from "../operations/Base.js"; import Base from "../operations/Base.js";
import Base58 from "../operations/Base58.js"; import Base58 from "../operations/Base58.js";
import Base64 from "../operations/Base64.js"; import Base64 from "../operations/Base64.js";
import BCD from "../operations/BCD.js";
import BitwiseOp from "../operations/BitwiseOp.js"; import BitwiseOp from "../operations/BitwiseOp.js";
import ByteRepr from "../operations/ByteRepr.js"; import ByteRepr from "../operations/ByteRepr.js";
import CharEnc from "../operations/CharEnc.js"; import CharEnc from "../operations/CharEnc.js";
@@ -330,30 +331,25 @@ const OperationConfig = {
value: BitwiseOp.XOR_BRUTE_KEY_LENGTH value: BitwiseOp.XOR_BRUTE_KEY_LENGTH
}, },
{ {
name: "Length of sample", name: "Sample length",
type: "number", type: "number",
value: BitwiseOp.XOR_BRUTE_SAMPLE_LENGTH value: BitwiseOp.XOR_BRUTE_SAMPLE_LENGTH
}, },
{ {
name: "Offset of sample", name: "Sample offset",
type: "number", type: "number",
value: BitwiseOp.XOR_BRUTE_SAMPLE_OFFSET value: BitwiseOp.XOR_BRUTE_SAMPLE_OFFSET
}, },
{
name: "Scheme",
type: "option",
value: BitwiseOp.XOR_SCHEME
},
{ {
name: "Null preserving", name: "Null preserving",
type: "boolean", type: "boolean",
value: BitwiseOp.XOR_PRESERVE_NULLS value: BitwiseOp.XOR_PRESERVE_NULLS
}, },
{
name: "Differential",
type: "boolean",
value: BitwiseOp.XOR_DIFFERENTIAL
},
{
name: "Crib (known plaintext string)",
type: "binaryString",
value: ""
},
{ {
name: "Print key", name: "Print key",
type: "boolean", type: "boolean",
@@ -363,6 +359,11 @@ const OperationConfig = {
name: "Output as hex", name: "Output as hex",
type: "boolean", type: "boolean",
value: BitwiseOp.XOR_BRUTE_OUTPUT_HEX value: BitwiseOp.XOR_BRUTE_OUTPUT_HEX
},
{
name: "Crib (known plaintext string)",
type: "binaryString",
value: ""
} }
] ]
}, },
@@ -605,7 +606,7 @@ const OperationConfig = {
args: [] args: []
}, },
"To Hexdump": { "To Hexdump": {
description: "Creates a hexdump of the input data, displaying both the hexademinal values of each byte and an ASCII representation alongside.", description: "Creates a hexdump of the input data, displaying both the hexadecimal values of each byte and an ASCII representation alongside.",
run: Hexdump.runTo, run: Hexdump.runTo,
highlight: Hexdump.highlightTo, highlight: Hexdump.highlightTo,
highlightReverse: Hexdump.highlightFrom, highlightReverse: Hexdump.highlightFrom,
@@ -1512,6 +1513,36 @@ const OperationConfig = {
} }
] ]
}, },
"Bifid Cipher Encode": {
description: "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.",
run: Cipher.runBifidEnc,
highlight: true,
highlightReverse: true,
inputType: "string",
outputType: "string",
args: [
{
name: "Keyword",
type: "string",
value: ""
}
]
},
"Bifid Cipher Decode": {
description: "The Bifid cipher is a cipher which uses a Polybius square in conjunction with transposition, which can be fairly difficult to decipher without knowing the alphabet keyword.",
run: Cipher.runBifidDec,
highlight: true,
highlightReverse: true,
inputType: "string",
outputType: "string",
args: [
{
name: "Keyword",
type: "string",
value: ""
}
]
},
"Affine Cipher Encode": { "Affine Cipher Encode": {
description: "The Affine cipher is a type of monoalphabetic substitution cipher, wherein each letter in an alphabet is mapped to its numeric equivalent, encrypted using simple mathematical function, <code>(ax + b) % 26</code>, and converted back to a letter.", description: "The Affine cipher is a type of monoalphabetic substitution cipher, wherein each letter in an alphabet is mapped to its numeric equivalent, encrypted using simple mathematical function, <code>(ax + b) % 26</code>, and converted back to a letter.",
run: Cipher.runAffineEnc, run: Cipher.runAffineEnc,
@@ -1919,7 +1950,7 @@ const OperationConfig = {
args: [] args: []
}, },
"Find / Replace": { "Find / Replace": {
description: "Replaces all occurrences of the first string with the second.<br><br>The three match options are only relevant to regex search strings.", description: "Replaces all occurrences of the first string with the second.<br><br> Includes support for regular expressions (regex), simple strings and extended strings (which support \\n, \\r, \\t, \\b, \\f and escaped hex bytes using \\x notation, e.g. \\x00 for a null byte).",
run: StrUtils.runFindReplace, run: StrUtils.runFindReplace,
manualBake: true, manualBake: true,
inputType: "string", inputType: "string",
@@ -2271,6 +2302,11 @@ const OperationConfig = {
name: "Output units", name: "Output units",
type: "option", type: "option",
value: DateTime.UNITS value: DateTime.UNITS
},
{
name: "Input format",
type: "option",
value: DateTime.FILETIME_FORMATS
} }
] ]
}, },
@@ -2284,6 +2320,11 @@ const OperationConfig = {
name: "Input units", name: "Input units",
type: "option", type: "option",
value: DateTime.UNITS value: DateTime.UNITS
},
{
name: "Output format",
type: "option",
value: DateTime.FILETIME_FORMATS
} }
] ]
}, },
@@ -3196,8 +3237,8 @@ const OperationConfig = {
"Substitute": { "Substitute": {
description: "A substitution cipher allowing you to specify bytes to replace with other byte values. This can be used to create Caesar ciphers but is more powerful as any byte value can be substituted, not just letters, and the substitution values need not be in order.<br><br>Enter the bytes you want to replace in the Plaintext field and the bytes to replace them with in the Ciphertext field.<br><br>Non-printable bytes can be specified using string escape notation. For example, a line feed character can be written as either <code>\\n</code> or <code>\\x0a</code>.<br><br>Byte ranges can be specified using a hyphen. For example, the sequence <code>0123456789</code> can be written as <code>0-9</code>.", description: "A substitution cipher allowing you to specify bytes to replace with other byte values. This can be used to create Caesar ciphers but is more powerful as any byte value can be substituted, not just letters, and the substitution values need not be in order.<br><br>Enter the bytes you want to replace in the Plaintext field and the bytes to replace them with in the Ciphertext field.<br><br>Non-printable bytes can be specified using string escape notation. For example, a line feed character can be written as either <code>\\n</code> or <code>\\x0a</code>.<br><br>Byte ranges can be specified using a hyphen. For example, the sequence <code>0123456789</code> can be written as <code>0-9</code>.",
run: Cipher.runSubstitute, run: Cipher.runSubstitute,
inputType: "byteArray", inputType: "string",
outputType: "byteArray", outputType: "string",
args: [ args: [
{ {
name: "Plaintext", name: "Plaintext",
@@ -3467,6 +3508,64 @@ const OperationConfig = {
} }
] ]
}, },
"From BCD": {
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign.",
run: BCD.runFromBCD,
inputType: "string",
outputType: "number",
args: [
{
name: "Scheme",
type: "option",
value: BCD.ENCODING_SCHEME
},
{
name: "Packed",
type: "boolean",
value: true
},
{
name: "Signed",
type: "boolean",
value: false
},
{
name: "Input format",
type: "option",
value: BCD.FORMAT
}
]
},
"To BCD": {
description: "Binary-Coded Decimal (BCD) is a class of binary encodings of decimal numbers where each decimal digit is represented by a fixed number of bits, usually four or eight. Special bit patterns are sometimes used for a sign",
run: BCD.runToBCD,
inputType: "number",
outputType: "string",
args: [
{
name: "Scheme",
type: "option",
value: BCD.ENCODING_SCHEME
},
{
name: "Packed",
type: "boolean",
value: true
},
{
name: "Signed",
type: "boolean",
value: false
},
{
name: "Output format",
type: "option",
value: BCD.FORMAT
}
]
},
}; };
export default OperationConfig; export default OperationConfig;

214
src/core/operations/BCD.js Executable file
View File

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

View File

@@ -221,15 +221,15 @@ const Base64 = {
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset0.substr(offset0.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 2) + "</span>"; "<span class='hl3'>" + offset0.substr(offset0.length - 2) + "</span>";
} else if (len0 % 4 === 3) { } else if (len0 % 4 === 3) {
staticSection = offset0.slice(0, -2); staticSection = offset0.slice(0, -2);
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" + Utils.escapeHtml(Utils.fromBase64(staticSection, alphabet).slice(0, -1)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset0.substr(offset0.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset0.substr(offset0.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset0.substr(offset0.length - 1) + "</span>"; "<span class='hl3'>" + offset0.substr(offset0.length - 1) + "</span>";
} else { } else {
staticSection = offset0; staticSection = offset0;
offset0 = "<span data-toggle='tooltip' data-placement='top' title='" + offset0 = "<span data-toggle='tooltip' data-placement='top' title='" +
@@ -243,23 +243,23 @@ const Base64 = {
// Highlight offset 1 // Highlight offset 1
padding = "<span class='hlred'>" + offset1.substr(0, 1) + "</span>" + padding = "<span class='hl3'>" + offset1.substr(0, 1) + "</span>" +
"<span class='hlgreen'>" + offset1.substr(1, 1) + "</span>"; "<span class='hl5'>" + offset1.substr(1, 1) + "</span>";
offset1 = offset1.substr(2); offset1 = offset1.substr(2);
if (len1 % 4 === 2) { if (len1 % 4 === 2) {
staticSection = offset1.slice(0, -3); staticSection = offset1.slice(0, -3);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset1.substr(offset1.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 2) + "</span>"; "<span class='hl3'>" + offset1.substr(offset1.length - 2) + "</span>";
} else if (len1 % 4 === 3) { } else if (len1 % 4 === 3) {
staticSection = offset1.slice(0, -2); staticSection = offset1.slice(0, -2);
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AA" + staticSection, alphabet).slice(1, -1)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset1.substr(offset1.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset1.substr(offset1.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset1.substr(offset1.length - 1) + "</span>"; "<span class='hl3'>" + offset1.substr(offset1.length - 1) + "</span>";
} else { } else {
staticSection = offset1; staticSection = offset1;
offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset1 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@@ -272,23 +272,23 @@ const Base64 = {
} }
// Highlight offset 2 // Highlight offset 2
padding = "<span class='hlred'>" + offset2.substr(0, 2) + "</span>" + padding = "<span class='hl3'>" + offset2.substr(0, 2) + "</span>" +
"<span class='hlgreen'>" + offset2.substr(2, 1) + "</span>"; "<span class='hl5'>" + offset2.substr(2, 1) + "</span>";
offset2 = offset2.substr(3); offset2 = offset2.substr(3);
if (len2 % 4 === 2) { if (len2 % 4 === 2) {
staticSection = offset2.slice(0, -3); staticSection = offset2.slice(0, -3);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 3, 1) + "</span>" + "<span class='hl5'>" + offset2.substr(offset2.length - 3, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 2) + "</span>"; "<span class='hl3'>" + offset2.substr(offset2.length - 2) + "</span>";
} else if (len2 % 4 === 3) { } else if (len2 % 4 === 3) {
staticSection = offset2.slice(0, -2); staticSection = offset2.slice(0, -2);
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" + Utils.escapeHtml(Utils.fromBase64("AAA" + staticSection, alphabet).slice(2, -2)) + "'>" +
staticSection + "</span>" + staticSection + "</span>" +
"<span class='hlgreen'>" + offset2.substr(offset2.length - 2, 1) + "</span>" + "<span class='hl5'>" + offset2.substr(offset2.length - 2, 1) + "</span>" +
"<span class='hlred'>" + offset2.substr(offset2.length - 1) + "</span>"; "<span class='hl3'>" + offset2.substr(offset2.length - 1) + "</span>";
} else { } else {
staticSection = offset2; staticSection = offset2;
offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" + offset2 = padding + "<span data-toggle='tooltip' data-placement='top' title='" +
@@ -300,8 +300,8 @@ const Base64 = {
offset2 = staticSection; offset2 = staticSection;
} }
return (showVariable ? "Characters highlighted in <span class='hlgreen'>green</span> could change if the input is surrounded by more data." + return (showVariable ? "Characters highlighted in <span class='hl5'>green</span> could change if the input is surrounded by more data." +
"\nCharacters highlighted in <span class='hlred'>red</span> are for padding purposes only." + "\nCharacters highlighted in <span class='hl3'>red</span> are for padding purposes only." +
"\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." + "\nUnhighlighted characters are <span data-toggle='tooltip' data-placement='top' title='Tooltip on left'>static</span>." +
"\nHover over the static sections to see what they decode to on their own.\n" + "\nHover over the static sections to see what they decode to on their own.\n" +
"\nOffset 0: " + offset0 + "\nOffset 0: " + offset0 +

View File

@@ -36,7 +36,9 @@ const BitwiseOp = {
o = input[i]; o = input[i];
x = nullPreserving && (o === 0 || o === k) ? o : func(o, k); x = nullPreserving && (o === 0 || o === k) ? o : func(o, k);
result.push(x); result.push(x);
if (scheme !== "Standard" && !(nullPreserving && (o === 0 || o === k))) { if (scheme &&
scheme !== "Standard" &&
!(nullPreserving && (o === 0 || o === k))) {
switch (scheme) { switch (scheme) {
case "Input differential": case "Input differential":
key[i % key.length] = x; key[i % key.length] = x;
@@ -120,19 +122,19 @@ const BitwiseOp = {
* @returns {string} * @returns {string}
*/ */
runXorBrute: function (input, args) { runXorBrute: function (input, args) {
let keyLength = parseInt(args[0], 10), const keyLength = parseInt(args[0], 10),
sampleLength = args[1], sampleLength = args[1],
sampleOffset = args[2], sampleOffset = args[2],
nullPreserving = args[3], scheme = args[3],
differential = args[4], nullPreserving = args[4],
crib = args[5], printKey = args[5],
printKey = args[6], outputHex = args[6],
outputHex = args[7], crib = args[7];
regex;
let output = "", let output = "",
result, result,
resultUtf8; resultUtf8,
regex;
input = input.slice(sampleOffset, sampleOffset + sampleLength); input = input.slice(sampleOffset, sampleOffset + sampleLength);
@@ -142,14 +144,12 @@ const BitwiseOp = {
for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) { for (let key = 1, l = Math.pow(256, keyLength); key < l; key++) {
result = BitwiseOp._bitOp(input, Utils.hexToByteArray(key.toString(16)), BitwiseOp._xor, nullPreserving, differential); result = BitwiseOp._bitOp(input, Utils.fromHex(key.toString(16)), BitwiseOp._xor, nullPreserving, scheme);
resultUtf8 = Utils.byteArrayToUtf8(result); resultUtf8 = Utils.byteArrayToUtf8(result);
if (crib !== "" && resultUtf8.search(regex) === -1) continue; if (crib !== "" && resultUtf8.search(regex) === -1) continue;
if (printKey) output += "Key = " + Utils.hex(key, (2*keyLength)) + ": "; if (printKey) output += "Key = " + Utils.hex(key, (2*keyLength)) + ": ";
if (outputHex) if (outputHex) output += Utils.toHex(result) + "\n";
output += Utils.byteArrayToHex(result) + "\n"; else output += Utils.printable(resultUtf8, false) + "\n";
else
output += Utils.printable(resultUtf8, false) + "\n";
if (printKey) output += "\n"; if (printKey) output += "\n";
} }
return output; return output;

View File

@@ -58,7 +58,7 @@ const ByteRepr = {
/** /**
* To Octal operation. * To Octal operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {byteArray} input * @param {byteArray} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -72,7 +72,7 @@ const ByteRepr = {
/** /**
* From Octal operation. * From Octal operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {byteArray}
@@ -108,8 +108,9 @@ const ByteRepr = {
throw "Error: Base argument must be between 2 and 36"; throw "Error: Base argument must be between 2 and 36";
} }
for (let i = 0; i < input.length; i++) { const charcode = Utils.strToCharcode(input);
ordinal = Utils.ord(input[i]); for (let i = 0; i < charcode.length; i++) {
ordinal = charcode[i];
if (base === 16) { if (base === 16) {
if (ordinal < 256) padding = 2; if (ordinal < 256) padding = 2;

View File

@@ -407,7 +407,7 @@ const Cipher = {
/** /**
* Vigenère Encode operation. * Vigenère Encode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -454,7 +454,7 @@ const Cipher = {
/** /**
* Vigenère Decode operation. * Vigenère Decode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -508,7 +508,7 @@ const Cipher = {
/** /**
* Affine Cipher Encode operation. * Affine Cipher Encode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -540,9 +540,9 @@ const Cipher = {
/** /**
* Affine Cipher Encode operation. * Affine Cipher Decode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -584,7 +584,7 @@ const Cipher = {
/** /**
* Atbash Cipher Encode operation. * Atbash Cipher Encode operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {string} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {string} * @returns {string}
@@ -594,6 +594,159 @@ const Cipher = {
}, },
/**
* Generates a polybius square for the given keyword
*
* @private
* @author Matt C [matt@artemisbot.uk]
* @param {string} keyword - Must be upper case
* @returns {string}
*/
_genPolybiusSquare: function (keyword) {
const alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
const polArray = `${keyword}${alpha}`.split("").unique();
let polybius = [];
for (let i = 0; i < 5; i++) {
polybius[i] = polArray.slice(i*5, i*5 + 5);
}
return polybius;
},
/**
* Bifid Cipher Encode operation
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBifidEnc: function (input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
let output = "",
xCo = [],
yCo = [],
structure = [],
count = 0;
if (keyword.length > 25)
return "The alphabet keyword must be less than 25 characters.";
if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0)
return "The key must consist only of letters";
const polybius = Cipher._genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach(letter => {
let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0,
polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
xCo.push(polInd);
yCo.push(i);
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
const trans = `${yCo.join("")}${xCo.join("")}`;
structure.forEach(pos => {
if (typeof pos === "boolean") {
let coords = trans.substr(2*count, 2).split("");
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
},
/**
* Bifid Cipher Decode operation
*
* @author Matt C [matt@artemisbot.uk]
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
runBifidDec: function (input, args) {
const keywordStr = args[0].toUpperCase().replace("J", "I"),
keyword = keywordStr.split("").unique(),
alpha = "ABCDEFGHIKLMNOPQRSTUVWXYZ";
let output = "",
structure = [],
count = 0,
trans = "";
if (keyword.length > 25)
return "The alphabet keyword must be less than 25 characters.";
if (!/^[a-zA-Z]+$/.test(keywordStr) && keyword.length > 0)
return "The key must consist only of letters";
const polybius = Cipher._genPolybiusSquare(keywordStr);
input.replace("J", "I").split("").forEach((letter) => {
let alpInd = alpha.split("").indexOf(letter.toLocaleUpperCase()) >= 0,
polInd;
if (alpInd) {
for (let i = 0; i < 5; i++) {
polInd = polybius[i].indexOf(letter.toLocaleUpperCase());
if (polInd >= 0) {
trans += `${i}${polInd}`;
}
}
if (alpha.split("").indexOf(letter) >= 0) {
structure.push(true);
} else if (alpInd) {
structure.push(false);
}
} else {
structure.push(letter);
}
});
structure.forEach(pos => {
if (typeof pos === "boolean") {
let coords = [trans[count], trans[count+trans.length/2]];
output += pos ?
polybius[coords[0]][coords[1]] :
polybius[coords[0]][coords[1]].toLocaleLowerCase();
count++;
} else {
output += pos;
}
});
return output;
},
/** /**
* @constant * @constant
* @default * @default
@@ -608,23 +761,23 @@ const Cipher = {
/** /**
* Substitute operation. * Substitute operation.
* *
* @param {byteArray} input * @param {string} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {string}
*/ */
runSubstitute: function (input, args) { runSubstitute: function (input, args) {
let plaintext = Utils.strToByteArray(Utils.expandAlphRange(args[0]).join()), let plaintext = Utils.expandAlphRange(args[0]).join(),
ciphertext = Utils.strToByteArray(Utils.expandAlphRange(args[1]).join()), ciphertext = Utils.expandAlphRange(args[1]).join(),
output = [], output = "",
index = -1; index = -1;
if (plaintext.length !== ciphertext.length) { if (plaintext.length !== ciphertext.length) {
output = Utils.strToByteArray("Warning: Plaintext and Ciphertext lengths differ\n\n"); output = "Warning: Plaintext and Ciphertext lengths differ\n\n";
} }
for (let i = 0; i < input.length; i++) { for (let i = 0; i < input.length; i++) {
index = plaintext.indexOf(input[i]); index = plaintext.indexOf(input[i]);
output.push(index > -1 && index < ciphertext.length ? ciphertext[index] : input[i]); output += index > -1 && index < ciphertext.length ? ciphertext[index] : input[i];
} }
return output; return output;

View File

@@ -89,8 +89,17 @@ const DateTime = {
* @returns {string} * @returns {string}
*/ */
runFromFiletimeToUnix: function(input, args) { runFromFiletimeToUnix: function(input, args) {
let units = args[0]; let units = args[0],
input = new BigInteger(input).subtract(new BigInteger("116444736000000000")); format = args[1];
if (format === "Hex") {
input = new BigInteger(input, 16);
} else {
input = new BigInteger(input);
}
input = input.subtract(new BigInteger("116444736000000000"));
if (units === "Seconds (s)"){ if (units === "Seconds (s)"){
input = input.divide(new BigInteger("10000000")); input = input.divide(new BigInteger("10000000"));
} else if (units === "Milliseconds (ms)") { } else if (units === "Milliseconds (ms)") {
@@ -102,6 +111,7 @@ const DateTime = {
} else { } else {
throw "Unrecognised unit"; throw "Unrecognised unit";
} }
return input.toString(); return input.toString();
}, },
@@ -115,8 +125,11 @@ const DateTime = {
* @returns {string} * @returns {string}
*/ */
runToFiletimeFromUnix: function(input, args) { runToFiletimeFromUnix: function(input, args) {
let units = args[0]; let units = args[0],
format = args[1];
input = new BigInteger(input); input = new BigInteger(input);
if (units === "Seconds (s)"){ if (units === "Seconds (s)"){
input = input.multiply(new BigInteger("10000000")); input = input.multiply(new BigInteger("10000000"));
} else if (units === "Milliseconds (ms)") { } else if (units === "Milliseconds (ms)") {
@@ -128,10 +141,24 @@ const DateTime = {
} else { } else {
throw "Unrecognised unit"; throw "Unrecognised unit";
} }
return input.add(new BigInteger("116444736000000000")).toString();
input = input.add(new BigInteger("116444736000000000"));
if (format === "Hex"){
return input.toString(16);
} else {
return input.toString();
}
}, },
/**
* @constant
* @default
*/
FILETIME_FORMATS: ["Decimal", "Hex"],
/** /**
* @constant * @constant
* @default * @default

View File

@@ -91,7 +91,7 @@ const HTML = {
const bite = HTML._entityToByte[m[1]]; const bite = HTML._entityToByte[m[1]];
if (bite) { if (bite) {
output += Utils.chr(bite); output += Utils.chr(bite);
} else if (!bite && m[1][0] === "#" && m[1].length > 1 && /^#\d{1,5}$/.test(m[1])) { } else if (!bite && m[1][0] === "#" && m[1].length > 1 && /^#\d{1,6}$/.test(m[1])) {
// Numeric entity (e.g. &#10;) // Numeric entity (e.g. &#10;)
const num = m[1].slice(1, m[1].length); const num = m[1].slice(1, m[1].length);
output += Utils.chr(parseInt(num, 10)); output += Utils.chr(parseInt(num, 10));

View File

@@ -146,6 +146,7 @@ const HTTP = {
return e.toString() + return e.toString() +
"\n\nThis error could be caused by one of the following:\n" + "\n\nThis error could be caused by one of the following:\n" +
" - An invalid URL\n" + " - An invalid URL\n" +
" - Making a request to an insecure resource (HTTP) from a secure source (HTTPS)\n" +
" - Making a cross-origin request to a server which does not support CORS\n"; " - Making a cross-origin request to a server which does not support CORS\n";
}); });
}, },

View File

@@ -283,7 +283,7 @@ const IP = {
baIp.push(decimal & 255); baIp.push(decimal & 255);
break; break;
case "Hex": case "Hex":
baIp = Utils.hexToByteArray(lines[i]); baIp = Utils.fromHex(lines[i]);
break; break;
default: default:
throw "Unsupported input IP format"; throw "Unsupported input IP format";
@@ -516,7 +516,7 @@ const IP = {
"<tr><td>Destination IP address</td><td>" + IP._ipv4ToStr(dstIP) + "</td></tr>"; "<tr><td>Destination IP address</td><td>" + IP._ipv4ToStr(dstIP) + "</td></tr>";
if (ihl > 5) { if (ihl > 5) {
output += "<tr><td>Options</td><td>" + Utils.byteArrayToHex(options) + "</td></tr>"; output += "<tr><td>Options</td><td>" + Utils.toHex(options) + "</td></tr>";
} }
return output + "</table>"; return output + "</table>";

View File

@@ -135,7 +135,7 @@ const Rotate = {
/** /**
* ROT47 operation. * ROT47 operation.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @param {byteArray} input * @param {byteArray} input
* @param {Object[]} args * @param {Object[]} args
* @returns {byteArray} * @returns {byteArray}

View File

@@ -227,14 +227,16 @@ const StrUtils = {
if (type === "Regex") { if (type === "Regex") {
find = new RegExp(find, modifiers); find = new RegExp(find, modifiers);
} else if (type.indexOf("Extended") === 0) { return input.replace(find, replace);
}
if (type.indexOf("Extended") === 0) {
find = Utils.parseEscapedChars(find); find = Utils.parseEscapedChars(find);
} }
return input.replace(find, replace, modifiers); find = new RegExp(Utils.escapeRegex(find), modifiers);
// Non-standard addition of flags in the third argument. This will work in Firefox but
// probably nowhere else. The purpose is to allow global matching when the `find` parameter return input.replace(find, replace);
// is just a string.
}, },
@@ -357,9 +359,9 @@ const StrUtils = {
for (let i = 0; i < diff.length; i++) { for (let i = 0; i < diff.length; i++) {
if (diff[i].added) { if (diff[i].added) {
if (showAdded) output += "<span class='hlgreen'>" + Utils.escapeHtml(diff[i].value) + "</span>"; if (showAdded) output += "<span class='hl5'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else if (diff[i].removed) { } else if (diff[i].removed) {
if (showRemoved) output += "<span class='hlred'>" + Utils.escapeHtml(diff[i].value) + "</span>"; if (showRemoved) output += "<span class='hl3'>" + Utils.escapeHtml(diff[i].value) + "</span>";
} else { } else {
output += Utils.escapeHtml(diff[i].value); output += Utils.escapeHtml(diff[i].value);
} }
@@ -422,7 +424,7 @@ const StrUtils = {
} }
if (match && !inMatch) { if (match && !inMatch) {
outputs[s] += "<span class='hlgreen'>" + Utils.escapeHtml(samples[s][i]); outputs[s] += "<span class='hl5'>" + Utils.escapeHtml(samples[s][i]);
if (samples[s].length === i + 1) outputs[s] += "</span>"; if (samples[s].length === i + 1) outputs[s] += "</span>";
if (s === samples.length - 1) inMatch = true; if (s === samples.length - 1) inMatch = true;
} else if (!match && inMatch) { } else if (!match && inMatch) {

View File

@@ -73,7 +73,7 @@ App.prototype.loaded = function() {
}, 1000); }, 1000);
// Clear the loading message interval // Clear the loading message interval
clearInterval(window.loadingMsgInt); clearInterval(window.loadingMsgsInt);
}; };
@@ -200,13 +200,7 @@ App.prototype.silentBake = function() {
* @returns {string} * @returns {string}
*/ */
App.prototype.getInput = function() { App.prototype.getInput = function() {
const input = this.manager.input.get(); return this.manager.input.get();
// Save to session storage in case we need to restore it later
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
return input;
}; };
@@ -216,8 +210,6 @@ App.prototype.getInput = function() {
* @param {string} input - The string to set the input to * @param {string} input - The string to set the input to
*/ */
App.prototype.setInput = function(input) { App.prototype.setInput = function(input) {
sessionStorage.setItem("inputLength", input.length);
sessionStorage.setItem("input", input);
this.manager.input.set(input); this.manager.input.set(input);
}; };
@@ -399,39 +391,33 @@ App.prototype.addFavourite = function(name) {
* Checks for input and recipe in the URI parameters and loads them if present. * Checks for input and recipe in the URI parameters and loads them if present.
*/ */
App.prototype.loadURIParams = function() { App.prototype.loadURIParams = function() {
// Load query string from URI // Load query string or hash from URI (depending on which is populated)
this.queryString = (function(a) { // We prefer getting the hash by splitting the href rather than referencing
if (a === "") return {}; // location.hash as some browsers (Firefox) automatically URL decode it,
const b = {}; // which cause issues.
for (let i = 0; i < a.length; i++) { const params = window.location.search ||
const p = a[i].split("="); window.location.href.split("#")[1] ||
if (p.length !== 2) { window.location.hash;
b[a[i]] = true; this.uriParams = Utils.parseURIParams(params);
} else {
b[p[0]] = decodeURIComponent(p[1].replace(/\+/g, " "));
}
}
return b;
})(window.location.search.substr(1).split("&"));
// Pause auto-bake while loading but don't modify `this.autoBake_` // Pause auto-bake while loading but don't modify `this.autoBake_`
// otherwise `manualBake` cannot trigger. // otherwise `manualBake` cannot trigger.
this.autoBakePause = true; this.autoBakePause = true;
// Read in recipe from query string // Read in recipe from URI params
if (this.queryString.recipe) { if (this.uriParams.recipe) {
try { try {
const recipeConfig = JSON.parse(this.queryString.recipe); const recipeConfig = JSON.parse(this.uriParams.recipe);
this.setRecipeConfig(recipeConfig); this.setRecipeConfig(recipeConfig);
} catch (err) {} } catch (err) {}
} else if (this.queryString.op) { } else if (this.uriParams.op) {
// If there's no recipe, look for single operations // If there's no recipe, look for single operations
this.manager.recipe.clearRecipe(); this.manager.recipe.clearRecipe();
try { try {
this.manager.recipe.addOperation(this.queryString.op); this.manager.recipe.addOperation(this.uriParams.op);
} catch (err) { } catch (err) {
// If no exact match, search for nearest match and add that // If no exact match, search for nearest match and add that
const matchedOps = this.manager.ops.filterOperations(this.queryString.op, false); const matchedOps = this.manager.ops.filterOperations(this.uriParams.op, false);
if (matchedOps.length) { if (matchedOps.length) {
this.manager.recipe.addOperation(matchedOps[0].name); this.manager.recipe.addOperation(matchedOps[0].name);
} }
@@ -439,15 +425,15 @@ App.prototype.loadURIParams = function() {
// Populate search with the string // Populate search with the string
const search = document.getElementById("search"); const search = document.getElementById("search");
search.value = this.queryString.op; search.value = this.uriParams.op;
search.dispatchEvent(new Event("search")); search.dispatchEvent(new Event("search"));
} }
} }
// Read in input data from query string // Read in input data from URI params
if (this.queryString.input) { if (this.uriParams.input) {
try { try {
const inputData = Utils.fromBase64(this.queryString.input); const inputData = Utils.fromBase64(this.uriParams.input);
this.setInput(inputData); this.setInput(inputData);
} catch (err) {} } catch (err) {}
} }
@@ -474,9 +460,7 @@ App.prototype.nextIngId = function() {
* @returns {Object[]} * @returns {Object[]}
*/ */
App.prototype.getRecipeConfig = function() { App.prototype.getRecipeConfig = function() {
const recipeConfig = this.manager.recipe.getConfig(); return this.manager.recipe.getConfig();
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
return recipeConfig;
}; };
@@ -486,7 +470,6 @@ App.prototype.getRecipeConfig = function() {
* @param {Object[]} recipeConfig - The recipe configuration * @param {Object[]} recipeConfig - The recipe configuration
*/ */
App.prototype.setRecipeConfig = function(recipeConfig) { App.prototype.setRecipeConfig = function(recipeConfig) {
sessionStorage.setItem("recipeConfig", JSON.stringify(recipeConfig));
document.getElementById("rec-list").innerHTML = null; document.getElementById("rec-list").innerHTML = null;
for (let i = 0; i < recipeConfig.length; i++) { for (let i = 0; i < recipeConfig.length; i++) {
@@ -689,9 +672,7 @@ App.prototype.stateChange = function(e) {
* @param {event} e * @param {event} e
*/ */
App.prototype.popState = function(e) { App.prototype.popState = function(e) {
if (window.location.href.split("#")[0] !== this.lastStateUrl) { this.loadURIParams();
this.loadURIParams();
}
}; };

View File

@@ -174,20 +174,21 @@ ControlsWaiter.prototype.generateStateUrl = function(includeRecipe, includeInput
const inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding const inputStr = Utils.toBase64(this.app.getInput(), "A-Za-z0-9+/"); // B64 alphabet with no padding
includeRecipe = includeRecipe && (recipeConfig.length > 0); includeRecipe = includeRecipe && (recipeConfig.length > 0);
includeInput = includeInput && (inputStr.length > 0) && (inputStr.length < 8000); // Only inlcude input if it is less than 50KB (51200 * 4/3 as it is Base64 encoded)
includeInput = includeInput && (inputStr.length > 0) && (inputStr.length <= 68267);
const params = [ const params = [
includeRecipe ? ["recipe", recipeStr] : undefined, includeRecipe ? ["recipe", recipeStr] : undefined,
includeInput ? ["input", inputStr] : undefined, includeInput ? ["input", inputStr] : undefined,
]; ];
const query = params const hash = params
.filter(v => v) .filter(v => v)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`) .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join("&"); .join("&");
if (query) { if (hash) {
return `${link}?${query}`; return `${link}#${hash}`;
} }
return link; return link;
@@ -350,13 +351,18 @@ ControlsWaiter.prototype.loadButtonClick = function() {
/** /**
* Populates the bug report information box with useful technical info. * Populates the bug report information box with useful technical info.
*
* @param {event} e
*/ */
ControlsWaiter.prototype.supportButtonClick = function() { ControlsWaiter.prototype.supportButtonClick = function(e) {
e.preventDefault();
const reportBugInfo = document.getElementById("report-bug-info"); const reportBugInfo = document.getElementById("report-bug-info");
const saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/"); const saveLink = this.generateStateUrl(true, true, null, "https://gchq.github.io/CyberChef/");
if (reportBugInfo) { if (reportBugInfo) {
reportBugInfo.innerHTML = "* CyberChef compile time: " + COMPILE_TIME + "\n" + reportBugInfo.innerHTML = "* Version: " + PKG_VERSION + "\n" +
"* Compile time: " + COMPILE_TIME + "\n" +
"* User-Agent: \n" + navigator.userAgent + "\n" + "* User-Agent: \n" + navigator.userAgent + "\n" +
"* [Link to reproduce](" + saveLink + ")\n\n"; "* [Link to reproduce](" + saveLink + ")\n\n";
} }

View File

@@ -145,6 +145,7 @@ Manager.prototype.initialiseEventListeners = function() {
document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter)); document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter));
this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter); this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter); this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
this.addDynamicListener(".file-switch", "click", this.output.fileSwitch, this.output);
// Options // Options
document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options)); document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));

View File

@@ -57,8 +57,11 @@ OptionsWaiter.prototype.load = function(options) {
/** /**
* Handler for options click events. * Handler for options click events.
* Dispays the options pane. * Dispays the options pane.
*
* @param {event} e
*/ */
OptionsWaiter.prototype.optionsClick = function() { OptionsWaiter.prototype.optionsClick = function(e) {
e.preventDefault();
$("#options-modal").modal(); $("#options-modal").modal();
}; };

View File

@@ -167,6 +167,17 @@ OutputWaiter.prototype.undoSwitchClick = function() {
document.getElementById("undo-switch").disabled = true; document.getElementById("undo-switch").disabled = true;
}; };
/**
* Handler for file switch click events.
* Moves a files data for items created via Utils.displayFilesAsHTML to the input.
*/
OutputWaiter.prototype.fileSwitch = function(e) {
e.preventDefault();
this.switchOrigData = this.manager.input.get();
this.app.setInput(e.target.getAttribute("fileValue"));
document.getElementById("undo-switch").disabled = false;
};
/** /**
* Handler for maximise output click events. * Handler for maximise output click events.

View File

@@ -32,8 +32,12 @@
<link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" /> <link rel="icon" type="image/ico" href="<%- require('../static/images/favicon.ico') %>" />
<script type="application/javascript"> <script type="application/javascript">
"use strict";
// Load theme before the preloader is shown // Load theme before the preloader is shown
document.querySelector(":root").className = JSON.parse(localStorage.getItem("options")).theme; try {
document.querySelector(":root").className = JSON.parse(localStorage.getItem("options")).theme;
} catch (e) {}
// Define loading messages // Define loading messages
const loadingMsgs = [ const loadingMsgs = [
@@ -87,23 +91,29 @@
</div> </div>
<div id="content-wrapper"> <div id="content-wrapper">
<div id="banner"> <div id="banner">
<% if (htmlWebpackPlugin.options.inline) { %> <div class="col-md-4" style="text-align: left; padding-left: 10px;">
<span style="float: left; margin-left: 10px;">Compile time: <%= htmlWebpackPlugin.options.compileTime %></span> <% if (htmlWebpackPlugin.options.inline) { %>
<% } else { %> <span>Version <%= htmlWebpackPlugin.options.version %></span>
<a href="cyberchef.htm" style="float: left; margin-left: 10px; margin-right: 80px;" download>Download CyberChef<img aria-hidden="true" src="<%- require('../static/images/download-24x24.png') %>" alt="Download Icon"/></a> <% } else { %>
<% } %> <a href="cyberchef.htm" download>Download CyberChef<img aria-hidden="true" src="<%- require('../static/images/download-24x24.png') %>" alt="Download Icon"/></a>
<span id="notice"> <% } %>
<script type="text/javascript"> </div>
// Must be text/javascript rather than application/javascript otherwise IE won't recognise it... <div class="col-md-4" style="text-align: center;">
if (navigator.userAgent && navigator.userAgent.match(/MSIE \d\d?\./)) { <span id="notice">
document.write("Internet Explorer is not supported, please use Firefox or Chrome instead"); <script type="text/javascript">
alert("Internet Explorer is not supported, please use Firefox or Chrome instead"); // Must be text/javascript rather than application/javascript otherwise IE won't recognise it...
} if (navigator.userAgent && navigator.userAgent.match(/MSIE \d\d?\./)) {
</script> document.write("Internet Explorer is not supported, please use Firefox or Chrome instead");
<noscript>JavaScript is not enabled. Good luck.</noscript> alert("Internet Explorer is not supported, please use Firefox or Chrome instead");
</span> }
<a href="#" id="support" class="banner-right" data-toggle="modal" data-target="#support-modal">About / Support<img aria-hidden="true" src="<%- require('../static/images/help-22x22.png') %>" alt="Question Mark Icon"/></a> </script>
<a href="#" id="options" class="banner-right">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a> <noscript>JavaScript is not enabled. Good luck.</noscript>
</span>
</div>
<div class="col-md-4" style="text-align: right; padding-right: 0;">
<a href="#" id="options">Options<img aria-hidden="true" src="<%- require('../static/images/settings-22x22.png') %>" alt="Settings Icon"/></a>
<a href="#" id="support" data-toggle="modal" data-target="#support-modal">About / Support<img aria-hidden="true" src="<%- require('../static/images/help-22x22.png') %>" alt="Question Mark Icon"/></a>
</div>
</div> </div>
<div id="workspace-wrapper"> <div id="workspace-wrapper">
<div id="operations" class="split split-horizontal no-select"> <div id="operations" class="split split-horizontal no-select">
@@ -264,32 +274,32 @@
<label for="theme"> Theme (only supported in modern browsers)</label> <label for="theme"> Theme (only supported in modern browsers)</label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="update_url" id="update_url" checked /> <input type="checkbox" option="updateUrl" id="updateUrl" checked />
<label for="update_url"> Update the URL when the input or recipe changes </label> <label for="updateUrl"> Update the URL when the input or recipe changes </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="show_highlighter" id="show_highlighter" checked /> <input type="checkbox" option="showHighlighter" id="showHighlighter" checked />
<label for="show_highlighter"> Highlight selected bytes in output and input (when possible) </label> <label for="showHighlighter"> Highlight selected bytes in output and input (when possible) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="treat_as_utf8" id="treat_as_utf8" checked /> <input type="checkbox" option="treatAsUtf8" id="treatAsUtf8" checked />
<label for="treat_as_utf8"> Treat output as UTF-8 if possible </label> <label for="treatAsUtf8"> Treat output as UTF-8 if possible </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="word_wrap" id="word_wrap" checked /> <input type="checkbox" option="wordWrap" id="wordWrap" checked />
<label for="word_wrap"> Word wrap the input and output </label> <label for="wordWrap"> Word wrap the input and output </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="checkbox" option="show_errors" id="show_errors" checked /> <input type="checkbox" option="showErrors" id="showErrors" checked />
<label for="show_errors"> Operation error reporting (recommended) </label> <label for="showErrors"> Operation error reporting (recommended) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="number" option="error_timeout" id="error_timeout" /> <input type="number" option="errorTimeout" id="errorTimeout" />
<label for="error_timeout"> Operation error timeout in ms (0 for never) </label> <label for="errorTimeout"> Operation error timeout in ms (0 for never) </label>
</div> </div>
<div class="option-item"> <div class="option-item">
<input type="number" option="auto_bake_threshold" id="auto_bake_threshold"/> <input type="number" option="autoBakeThreshold" id="autoBakeThreshold"/>
<label for="auto_bake_threshold"> Auto Bake threshold in ms </label> <label for="autoBakeThreshold"> Auto Bake threshold in ms </label>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
@@ -341,7 +351,7 @@
Compile time: <%= htmlWebpackPlugin.options.compileTime %> Compile time: <%= htmlWebpackPlugin.options.compileTime %>
</p> </p>
<p>&copy; Crown Copyright 2016.</p> <p>&copy; Crown Copyright 2016.</p>
<p>Licenced under the Apache Licence, Version 2.0.</p> <p>Released under the Apache Licence, Version 2.0.</p>
<br> <br>
<br> <br>
<div> <div>
@@ -368,14 +378,14 @@
</a> </a>
</blockquote> </blockquote>
<div class="collapse" id="faq-examples"> <div class="collapse" id="faq-examples">
<p>There are well over 100 operations in CyberChef allowing you to carry simple and complex tasks easily. Here are some examples:</p> <p>There are around 200 operations in CyberChef allowing you to carry out simple and complex tasks easily. Here are some examples:</p>
<ul> <ul>
<li><a href="?recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1">Decode a Base64-encoded string</a></li> <li><a href="#recipe=%5B%7B%22op%22%3A%22From%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%2Ctrue%5D%7D%5D&input=VTI4Z2JHOXVaeUJoYm1RZ2RHaGhibXR6SUdadmNpQmhiR3dnZEdobElHWnBjMmd1">Decode a Base64-encoded string</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA">Convert a date and time to a different time zone</a></li> <li><a href="#recipe=%5B%7B%22op%22%3A%22Translate%20DateTime%20Format%22%2C%22args%22%3A%5B%22Standard%20date%20and%20time%22%2C%22DD%2FMM%2FYYYY%20HH%3Amm%3Ass%22%2C%22UTC%22%2C%22dddd%20Do%20MMMM%20YYYY%20HH%3Amm%3Ass%20Z%20z%22%2C%22Australia%2FQueensland%22%5D%7D%5D&input=MTUvMDYvMjAxNSAyMDo0NTowMA">Convert a date and time to a different time zone</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22Parse%20IPv6%20address%22%2C%22args%22%3A%5B%5D%7D%5D&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy">Parse a Teredo IPv6 address</a></li> <li><a href="#recipe=%5B%7B%22op%22%3A%22Parse%20IPv6%20address%22%2C%22args%22%3A%5B%5D%7D%5D&input=MjAwMTowMDAwOjQxMzY6ZTM3ODo4MDAwOjYzYmY6M2ZmZjpmZGQy">Parse a Teredo IPv6 address</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22From%20Hexdump%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22Gunzip%22%2C%22args%22%3A%5B%5D%7D%5D&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu%2Fy7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb%2F3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw">Convert data from a hexdump, then decompress</a></li> <li><a href="#recipe=%5B%7B%22op%22%3A%22From%20Hexdump%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22Gunzip%22%2C%22args%22%3A%5B%5D%7D%5D&input=MDAwMDAwMDAgIDFmIDhiIDA4IDAwIDEyIGJjIGYzIDU3IDAwIGZmIDBkIGM3IGMxIDA5IDAwIDIwICB8Li4uLi6881cu%2Fy7HwS4uIHwKMDAwMDAwMTAgIDA4IDA1IGQwIDU1IGZlIDA0IDJkIGQzIDA0IDFmIGNhIDhjIDQ0IDIxIDViIGZmICB8Li7QVf4uLdMuLsouRCFb%2F3wKMDAwMDAwMjAgIDYwIGM3IGQ3IDAzIDE2IGJlIDQwIDFmIDc4IDRhIDNmIDA5IDg5IDBiIDlhIDdkICB8YMfXLi6%2BQC54Sj8uLi4ufXwKMDAwMDAwMzAgIDRlIGM4IDRlIDZkIDA1IDFlIDAxIDhiIDRjIDI0IDAwIDAwIDAwICAgICAgICAgICB8TshObS4uLi5MJC4uLnw">Convert data from a hexdump, then decompress</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22From%20UNIX%20Timestamp%22%2C%22args%22%3A%5B%22Seconds%20(s)%22%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA">Display multiple timestamps as full dates</a></li> <li><a href="#recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22From%20UNIX%20Timestamp%22%2C%22args%22%3A%5B%22Seconds%20(s)%22%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA">Display multiple timestamps as full dates</a></li>
<li><a href="?recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22Conditional%20Jump%22%2C%22args%22%3A%5B%221%22%2C%222%22%2C%2210%22%5D%7D%2C%7B%22op%22%3A%22To%20Hex%22%2C%22args%22%3A%5B%22Space%22%5D%7D%2C%7B%22op%22%3A%22Return%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22To%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%5D%7D%5D&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA">Carry out different operations on data of different types</a></li> <li><a href="#recipe=%5B%7B%22op%22%3A%22Fork%22%2C%22args%22%3A%5B%22%5C%5Cn%22%2C%22%5C%5Cn%22%5D%7D%2C%7B%22op%22%3A%22Conditional%20Jump%22%2C%22args%22%3A%5B%221%22%2C%222%22%2C%2210%22%5D%7D%2C%7B%22op%22%3A%22To%20Hex%22%2C%22args%22%3A%5B%22Space%22%5D%7D%2C%7B%22op%22%3A%22Return%22%2C%22args%22%3A%5B%5D%7D%2C%7B%22op%22%3A%22To%20Base64%22%2C%22args%22%3A%5B%22A-Za-z0-9%2B%2F%3D%22%5D%7D%5D&input=U29tZSBkYXRhIHdpdGggYSAxIGluIGl0ClNvbWUgZGF0YSB3aXRoIGEgMiBpbiBpdA">Carry out different operations on data of different types</a></li>
</ul> </ul>
</div> </div>
<blockquote> <blockquote>
@@ -395,7 +405,7 @@
<div class="collapse" id="faq-fork"> <div class="collapse" id="faq-fork">
<p>Maybe you have 10 timestamps that you want to parse or 16 encoded strings that all have the same key.</p> <p>Maybe you have 10 timestamps that you want to parse or 16 encoded strings that all have the same key.</p>
<p>The 'Fork' operation (found in the 'Flow control' category) splits up the input line by line and runs all subsequent operations on each line separately. Each output is then displayed on a separate line. These delimiters can be changed, so if your inputs are separated by commas, you can change the split delimiter to a comma instead.</p> <p>The 'Fork' operation (found in the 'Flow control' category) splits up the input line by line and runs all subsequent operations on each line separately. Each output is then displayed on a separate line. These delimiters can be changed, so if your inputs are separated by commas, you can change the split delimiter to a comma instead.</p>
<p><a href='?recipe=%5B%7B"op"%3A"Fork"%2C"args"%3A%5B"%5C%5Cn"%2C"%5C%5Cn"%5D%7D%2C%7B"op"%3A"From%20UNIX%20Timestamp"%2C"args"%3A%5B"Seconds%20(s)"%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA%3D%3D'>Click here</a> for an example.</p> <p><a href='#recipe=%5B%7B"op"%3A"Fork"%2C"args"%3A%5B"%5C%5Cn"%2C"%5C%5Cn"%5D%7D%2C%7B"op"%3A"From%20UNIX%20Timestamp"%2C"args"%3A%5B"Seconds%20(s)"%5D%7D%5D&input=OTc4MzQ2ODAwCjEwMTI2NTEyMDAKMTA0NjY5NjQwMAoxMDgxMDg3MjAwCjExMTUzMDUyMDAKMTE0OTYwOTYwMA%3D%3D'>Click here</a> for an example.</p>
</div> </div>
</div> </div>
<div role="tabpanel" class="tab-pane" id="report-bug"> <div role="tabpanel" class="tab-pane" id="report-bug">
@@ -407,21 +417,25 @@
<a class="btn btn-primary" href="https://github.com/gchq/CyberChef/issues/new" role="button">Raise issue on GitHub</a> <a class="btn btn-primary" href="https://github.com/gchq/CyberChef/issues/new" role="button">Raise issue on GitHub</a>
</div> </div>
<div role="tabpanel" class="tab-pane" id="about" style="padding: 20px;"> <div role="tabpanel" class="tab-pane" id="about" style="padding: 20px;">
<h4>What</h4> <h5><strong>What</strong></h5>
<p>A simple, intuitive web app for analysing and decoding data without having to deal with complex tools or programming languages. CyberChef encourages both technical and non-technical people to explore data formats, encryption and compression.</p> <p>A simple, intuitive web app for analysing and decoding data without having to deal with complex tools or programming languages. CyberChef encourages both technical and non-technical people to explore data formats, encryption and compression.</p><br>
<h4>Why</h4> <h5><strong>Why</strong></h5>
<p>Digital data comes in all shapes, sizes and formats in the modern world CyberChef helps to make sense of this data all on one easy-to-use platform.</p> <p>Digital data comes in all shapes, sizes and formats in the modern world CyberChef helps to make sense of this data all on one easy-to-use platform.</p><br>
<h4>How</h4> <h5><strong>How</strong></h5>
<p>The interface is designed with simplicity at its heart. Complex techniques are now as trivial as drag-and-drop. Simple functions can be combined to build up a "recipe", potentially resulting in complex analysis, which can be shared with other users and used with their input.</p> <p>The interface is designed with simplicity at its heart. Complex techniques are now as trivial as drag-and-drop. Simple functions can be combined to build up a "recipe", potentially resulting in complex analysis, which can be shared with other users and used with their input.</p>
<p>For those comfortable writing code, CyberChef is a quick and efficient way to prototype solutions to a problem which can then be scripted once proven to work.</p> <p>For those comfortable writing code, CyberChef is a quick and efficient way to prototype solutions to a problem which can then be scripted once proven to work.</p><br>
<h4>Who</h4> <h5><strong>Who</strong></h5>
<p>It is expected that CyberChef will be useful for cybersecurity and antivirus companies. It should also appeal to the academic world and any individuals or companies involved in the analysis of digital data, be that software developers, analysts, mathematicians or casual puzzle solvers.</p> <p>It is expected that CyberChef will be useful for cybersecurity and antivirus companies. It should also appeal to the academic world and any individuals or companies involved in the analysis of digital data, be that software developers, analysts, mathematicians or casual puzzle solvers.</p><br>
<h4>Aim</h4> <h5><strong>Aim</strong></h5>
<p>It is hoped that by releasing CyberChef through <a href="https://github.com/gchq/cyberchef">GitHub</a>, contributions can be added which can be rolled out into future versions of the tool.</p> <p>It is hoped that by releasing CyberChef through <a href="https://github.com/gchq/CyberChef">GitHub</a>, contributions can be added which can be rolled out into future versions of the tool.</p><br>
<br> <br>
<p>There are around 150 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p> <p>There are around 150 useful operations in CyberChef for anyone working on anything vaguely Internet-related, whether you just want to convert a timestamp to a different format, decompress gzipped data, create a SHA3 hash, or parse an X.509 certificate to find out who issued it.</p>

View File

@@ -9,7 +9,7 @@
ga('create', 'UA-85682716-2', 'auto'); ga('create', 'UA-85682716-2', 'auto');
// Specifying location.pathname here overrides the default URL which would include arguments. // Specifying location.pathname here overrides the default URL which could include arguments.
// This method prevents Google Analytics from logging any recipe or input data in the URL. // This method prevents Google Analytics from logging any recipe or input data in the URL.
ga('send', 'pageview', location.pathname); ga('send', 'pageview', location.pathname);

View File

@@ -59,6 +59,7 @@
background-color: var(--arg-background); background-color: var(--arg-background);
border: 1px solid var(--arg-border-colour); border: 1px solid var(--arg-border-colour);
font-family: var(--fixed-width-font-family); font-family: var(--fixed-width-font-family);
text-overflow: ellipsis;
} }
.short-string { .short-string {

View File

@@ -10,19 +10,15 @@
position: absolute; position: absolute;
height: 30px; height: 30px;
width: 100%; width: 100%;
text-align: center;
line-height: 30px; line-height: 30px;
border-bottom: 1px solid var(--primary-border-colour); border-bottom: 1px solid var(--primary-border-colour);
color: var(--banner-font-colour); color: var(--banner-font-colour);
background-color: var(--banner-bg-colour); background-color: var(--banner-bg-colour);
} }
.banner-right {
float: right;
margin-right: 10px;
}
#banner img { #banner img {
margin-bottom: 2px; margin-bottom: 2px;
margin-left: 8px; margin-left: 8px;
padding-right: 10px;
} }

View File

@@ -14,6 +14,10 @@
margin: 10px; margin: 10px;
} }
.option-item label {
font-weight: normal;
}
.option-item input[type=number] { .option-item input[type=number] {
margin: 15px 10px; margin: 15px 10px;
width: 80px; width: 80px;

View File

@@ -37,20 +37,24 @@
#preloader:after { #preloader:after {
content: ""; content: "";
position: absolute; position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border: 3px solid transparent; border: 3px solid transparent;
border-radius: 50%; border-radius: 50%;
} }
#preloader:before { #preloader:before {
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-top-color: #e74c3c; border-top-color: #e74c3c;
animation: spin 3s linear infinite; animation: spin 3s linear infinite;
} }
#preloader:after { #preloader:after {
top: 13px;
left: 13px;
right: 13px;
bottom: 13px;
border-top-color: #f9c922; border-top-color: #f9c922;
animation: spin 1.5s linear infinite; animation: spin 1.5s linear infinite;
} }

View File

@@ -202,6 +202,11 @@ optgroup {
border-color: var(--primary-border-colour); border-color: var(--primary-border-colour);
} }
.bootstrap-switch .bootstrap-switch-handle-off.bootstrap-switch-default {
background-color: var(--primary-border-colour);
color: var(--primary-font-colour);
}
/* Sortable */ /* Sortable */

View File

@@ -12,8 +12,10 @@ import "babel-polyfill";
import TestRegister from "./TestRegister.js"; import TestRegister from "./TestRegister.js";
import "./tests/operations/Base58.js"; import "./tests/operations/Base58.js";
import "./tests/operations/BCD.js";
import "./tests/operations/ByteRepr.js"; import "./tests/operations/ByteRepr.js";
import "./tests/operations/CharEnc.js"; import "./tests/operations/CharEnc.js";
import "./tests/operations/Cipher.js";
import "./tests/operations/Code.js"; import "./tests/operations/Code.js";
import "./tests/operations/Compress.js"; import "./tests/operations/Compress.js";
import "./tests/operations/DateTime.js"; import "./tests/operations/DateTime.js";

View File

@@ -0,0 +1,103 @@
/**
* BCD tests
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "To BCD: default 0",
input: "0",
expectedOutput: "0000",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 2 1", true, false, "Nibbles"]
}
]
},
{
name: "To BCD: unpacked nibbles",
input: "1234567890",
expectedOutput: "0000 0001 0000 0010 0000 0011 0000 0100 0000 0101 0000 0110 0000 0111 0000 1000 0000 1001 0000 0000",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 2 1", false, false, "Nibbles"]
}
]
},
{
name: "To BCD: packed, signed bytes",
input: "1234567890",
expectedOutput: "00000001 00100011 01000101 01100111 10001001 00001100",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 2 1", true, true, "Bytes"]
}
]
},
{
name: "To BCD: packed, signed nibbles, 8 4 -2 -1",
input: "-1234567890",
expectedOutput: "0000 0111 0110 0101 0100 1011 1010 1001 1000 1111 0000 1101",
recipeConfig: [
{
"op": "To BCD",
"args": ["8 4 -2 -1", true, true, "Nibbles"]
}
]
},
{
name: "From BCD: default 0",
input: "0000",
expectedOutput: "0",
recipeConfig: [
{
"op": "From BCD",
"args": ["8 4 2 1", true, false, "Nibbles"]
}
]
},
{
name: "From BCD: packed, signed bytes",
input: "00000001 00100011 01000101 01100111 10001001 00001101",
expectedOutput: "-1234567890",
recipeConfig: [
{
"op": "From BCD",
"args": ["8 4 2 1", true, true, "Bytes"]
}
]
},
{
name: "From BCD: Excess-3, unpacked, unsigned",
input: "00000100 00000101 00000110 00000111 00001000 00001001 00001010 00001011 00001100 00000011",
expectedOutput: "1234567890",
recipeConfig: [
{
"op": "From BCD",
"args": ["Excess-3", false, false, "Nibbles"]
}
]
},
{
name: "BCD: raw 4 2 2 1, packed, signed",
input: "1234567890",
expectedOutput: "1234567890",
recipeConfig: [
{
"op": "To BCD",
"args": ["4 2 2 1", true, true, "Raw"]
},
{
"op": "From BCD",
"args": ["4 2 2 1", true, true, "Raw"]
}
]
},
]);

View File

@@ -1,7 +1,7 @@
/** /**
* ByteRepr tests. * ByteRepr tests.
* *
* @author Matt C [matt@artemisbot.pw] * @author Matt C [matt@artemisbot.uk]
* @copyright Crown Copyright 2017 * @copyright Crown Copyright 2017
* @license Apache-2.0 * @license Apache-2.0
*/ */

View File

@@ -0,0 +1,78 @@
/**
* Cipher tests.
*
* @author Matt C [matt@artemisbot.uk]
*
* @copyright Crown Copyright 2017
* @license Apache-2.0
*/
import TestRegister from "../../TestRegister.js";
TestRegister.addTests([
{
name: "Bifid Cipher Encode: no input",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "Bifid Cipher Encode",
"args": ["nothing"]
}
],
},
{
name: "Bifid Cipher Encode: no key",
input: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.",
expectedOutput: "Vq daqcliho rmltofvlnc qbdhlcr nt qdq Fbm-Rdkkm vuoottnoi aitp al axf tdtmvt owppkaodtx.",
recipeConfig: [
{
"op": "Bifid Cipher Encode",
"args": [""]
}
],
},
{
name: "Bifid Cipher Encode: normal",
input: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.",
expectedOutput: "Wc snpsigdd cpfrrcxnfi hikdnnp dm crc Fcb-Pdeug vueageacc vtyl sa zxm crebzp lyoeuaiwpv.",
recipeConfig: [
{
"op": "Bifid Cipher Encode",
"args": ["Schrodinger"]
}
],
},
{
name: "Bifid Cipher Decode: no input",
input: "",
expectedOutput: "",
recipeConfig: [
{
"op": "Bifid Cipher Decode",
"args": ["nothing"]
}
],
},
{
name: "Bifid Cipher Decode: no key",
input: "Vq daqcliho rmltofvlnc qbdhlcr nt qdq Fbm-Rdkkm vuoottnoi aitp al axf tdtmvt owppkaodtx.",
expectedOutput: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.",
recipeConfig: [
{
"op": "Bifid Cipher Decode",
"args": [""]
}
],
},
{
name: "Bifid Cipher Decode: normal",
input: "Wc snpsigdd cpfrrcxnfi hikdnnp dm crc Fcb-Pdeug vueageacc vtyl sa zxm crebzp lyoeuaiwpv.",
expectedOutput: "We recreate conditions similar to the Van-Allen radiation belt in our secure facilities.",
recipeConfig: [
{
"op": "Bifid Cipher Decode",
"args": ["Schrodinger"]
}
],
},
]);

View File

@@ -16,7 +16,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "Windows Filetime to UNIX Timestamp", op: "Windows Filetime to UNIX Timestamp",
args: ["Nanoseconds (ns)"], args: ["Nanoseconds (ns)", "Decimal"],
}, },
], ],
}, },
@@ -27,7 +27,7 @@ TestRegister.addTests([
recipeConfig: [ recipeConfig: [
{ {
op: "UNIX Timestamp to Windows Filetime", op: "UNIX Timestamp to Windows Filetime",
args: ["Nanoseconds (ns)"], args: ["Nanoseconds (ns)", "Decimal"],
}, },
], ],
}, },

View File

@@ -26,7 +26,7 @@ TestRegister.addTests([
{ {
name: "Diff, basic usage", name: "Diff, basic usage",
input: "testing23\n\ntesting123", input: "testing23\n\ntesting123",
expectedOutput: "testing<span class='hlgreen'>1</span>23", expectedOutput: "testing<span class='hl5'>1</span>23",
recipeConfig: [ recipeConfig: [
{ {
"op": "Diff", "op": "Diff",