mirror of
https://github.com/gchq/CyberChef
synced 2025-12-05 23:53:27 +00:00
Compare commits
450 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f77633cee9 | ||
|
|
c858970573 | ||
|
|
fb3eceaee0 | ||
|
|
8e37fec8f8 | ||
|
|
ccaabfaee8 | ||
|
|
c431fb30c5 | ||
|
|
1c04848480 | ||
|
|
ca1a0797fb | ||
|
|
7b497181fd | ||
|
|
92767b1078 | ||
|
|
7c66dacc40 | ||
|
|
632277f9bd | ||
|
|
3c23ae03f5 | ||
|
|
8117926ca3 | ||
|
|
31e9d27f1a | ||
|
|
0c067d60d8 | ||
|
|
1171d6b165 | ||
|
|
18022a2a48 | ||
|
|
1400b5cca4 | ||
|
|
98b5f9cc29 | ||
|
|
e766cb009e | ||
|
|
993e276858 | ||
|
|
a762fb4df4 | ||
|
|
00781fa459 | ||
|
|
c6e7367a4e | ||
|
|
3749bb1222 | ||
|
|
f3c83b2009 | ||
|
|
d3583c31bc | ||
|
|
2f638d8c0c | ||
|
|
74e43ff65a | ||
|
|
4f0b160ed3 | ||
|
|
709b8696fc | ||
|
|
e080c5d72e | ||
|
|
9cc177a9ad | ||
|
|
9ad4e2525e | ||
|
|
60b5fe0e76 | ||
|
|
9273f97d88 | ||
|
|
e2b7ac68ef | ||
|
|
98a6baf55a | ||
|
|
b5677387f5 | ||
|
|
cd3eaa4762 | ||
|
|
9733bf65de | ||
|
|
bc433f0234 | ||
|
|
ad7283ee6f | ||
|
|
5b941358a9 | ||
|
|
f19e898c57 | ||
|
|
590ffa184d | ||
|
|
46de51512f | ||
|
|
a13f2f26e4 | ||
|
|
c9c26f6f9f | ||
|
|
787c29e42b | ||
|
|
1c0b83d833 | ||
|
|
5c767d09b0 | ||
|
|
75dba51f56 | ||
|
|
78a1827af8 | ||
|
|
9e3733b33b | ||
|
|
4ef65589e8 | ||
|
|
cf9e670309 | ||
|
|
b09f98fbb4 | ||
|
|
e43e010163 | ||
|
|
2a5cee0bd3 | ||
|
|
c962bb79f5 | ||
|
|
add745551b | ||
|
|
7b8213e1f6 | ||
|
|
2e23a33dfc | ||
|
|
bca296ee37 | ||
|
|
2dbd647868 | ||
|
|
2991e7d1fe | ||
|
|
f6f12fc193 | ||
|
|
1fac8c1cea | ||
|
|
590462e2e4 | ||
|
|
291c55befd | ||
|
|
7db1f39473 | ||
|
|
6017578964 | ||
|
|
d2174725a9 | ||
|
|
f630c499d5 | ||
|
|
cfc29ef821 | ||
|
|
83c6775038 | ||
|
|
ae1b12c120 | ||
|
|
c423de545f | ||
|
|
84011371b7 | ||
|
|
f831ec6b7e | ||
|
|
05bfa9158d | ||
|
|
649016bc85 | ||
|
|
7492b874cf | ||
|
|
9ea21af61f | ||
|
|
dd18e52993 | ||
|
|
7712ee7f35 | ||
|
|
a4a13666e6 | ||
|
|
07ef4da892 | ||
|
|
e9ca4dc9ca | ||
|
|
57bb8fbc45 | ||
|
|
9175624210 | ||
|
|
289a417dfb | ||
|
|
8379a9b275 | ||
|
|
5b1fad118f | ||
|
|
5e8985810e | ||
|
|
d2568e2a29 | ||
|
|
6dfc21ef06 | ||
|
|
1f19f2f58c | ||
|
|
1728cc7a85 | ||
|
|
fa2fc2ba33 | ||
|
|
9a33498fed | ||
|
|
a3b873fd96 | ||
|
|
97bd03799e | ||
|
|
ffaaaae2b4 | ||
|
|
ff88d30d2f | ||
|
|
88e3c2ccb2 | ||
|
|
6155634d3b | ||
|
|
5029356514 | ||
|
|
e57d5a7e75 | ||
|
|
2bbe54cdcd | ||
|
|
0e2423c390 | ||
|
|
8fadad5891 | ||
|
|
32455cd20f | ||
|
|
1e0e7f16a7 | ||
|
|
95884d77cf | ||
|
|
b69373f5e7 | ||
|
|
61e85474d3 | ||
|
|
3a9bdc58af | ||
|
|
59c1c45d78 | ||
|
|
b5f6cedd30 | ||
|
|
c879af6860 | ||
|
|
22fe5a6ae7 | ||
|
|
57714c86a6 | ||
|
|
70cd375049 | ||
|
|
e27e1dd42f | ||
|
|
8ad18bc7db | ||
|
|
5893ac1a37 | ||
|
|
83c3ab97f9 | ||
|
|
9b6be140fa | ||
|
|
a6a60392c2 | ||
|
|
ccfa0b991e | ||
|
|
73b0e68993 | ||
|
|
31a4eef001 | ||
|
|
d6e2c9a6b9 | ||
|
|
e069f5db13 | ||
|
|
96b59cf0df | ||
|
|
c1e1d4b7e3 | ||
|
|
32d869231e | ||
|
|
6f95f01dda | ||
|
|
61a1c44f26 | ||
|
|
e6c7899569 | ||
|
|
a74a14145e | ||
|
|
04022b22be | ||
|
|
4f3010691c | ||
|
|
672b477751 | ||
|
|
19360391a6 | ||
|
|
447be8af34 | ||
|
|
0989550e5c | ||
|
|
4d9b48b4d8 | ||
|
|
979652387d | ||
|
|
de84fbdd1c | ||
|
|
170e564319 | ||
|
|
530836876f | ||
|
|
1abc46058c | ||
|
|
892a3716ed | ||
|
|
5d982a9c8d | ||
|
|
6206224f1e | ||
|
|
766310e2c7 | ||
|
|
02a397d2ae | ||
|
|
4563c86acd | ||
|
|
0a59f8068e | ||
|
|
24548e3a48 | ||
|
|
f4784d49e7 | ||
|
|
14d5069c6e | ||
|
|
9fdd55c5c6 | ||
|
|
5bc523aeff | ||
|
|
3ae2e2e2c8 | ||
|
|
83e49da7f6 | ||
|
|
fe6df8778f | ||
|
|
69073c9d99 | ||
|
|
d5a0adea0c | ||
|
|
1bcb8e433d | ||
|
|
fa05cf1d78 | ||
|
|
63dff0d34d | ||
|
|
e228b197f9 | ||
|
|
4bbeb6caa3 | ||
|
|
139d25dff9 | ||
|
|
6984258404 | ||
|
|
ba66fd6546 | ||
|
|
47bbefd81f | ||
|
|
50f796049c | ||
|
|
618da545b1 | ||
|
|
21236f1938 | ||
|
|
4169a15066 | ||
|
|
6b10f61e11 | ||
|
|
83f119f7e4 | ||
|
|
041c899a35 | ||
|
|
5412fc01b3 | ||
|
|
76926d9252 | ||
|
|
3270961574 | ||
|
|
9a1ef71aec | ||
|
|
ba878925ad | ||
|
|
8d6b71bfaa | ||
|
|
b6845aa03c | ||
|
|
4a673bd92a | ||
|
|
fdffabfdd4 | ||
|
|
ba8591293b | ||
|
|
9b3aae10cf | ||
|
|
5c85c4df63 | ||
|
|
8ece2603fb | ||
|
|
1b54584820 | ||
|
|
3ce3866000 | ||
|
|
becc258b6c | ||
|
|
16c9e7119d | ||
|
|
f5888fea9c | ||
|
|
b5162c7549 | ||
|
|
1baea1da3d | ||
|
|
40899a6fe4 | ||
|
|
66c533431d | ||
|
|
74ae77f17a | ||
|
|
99eb1cced5 | ||
|
|
c880ecf3c4 | ||
|
|
c54c34d88e | ||
|
|
60b3c597a7 | ||
|
|
372ab32539 | ||
|
|
e14745a973 | ||
|
|
afffe584cf | ||
|
|
cf532f1e30 | ||
|
|
46425ba552 | ||
|
|
9f65fac4e6 | ||
|
|
af98feff51 | ||
|
|
339c741a2c | ||
|
|
d1bde23f00 | ||
|
|
be544faf0f | ||
|
|
eff77fd3bb | ||
|
|
3df57ba3dd | ||
|
|
4bae662357 | ||
|
|
357c90546e | ||
|
|
54769a90ac | ||
|
|
0550aedd54 | ||
|
|
5947ed21fc | ||
|
|
0a0949246f | ||
|
|
09c6e181fb | ||
|
|
02cf394bcd | ||
|
|
46afbf9888 | ||
|
|
7cf19d22a8 | ||
|
|
46d708360f | ||
|
|
7ec91e2366 | ||
|
|
a74ee47bf0 | ||
|
|
7b68b92498 | ||
|
|
274f3acd45 | ||
|
|
53dff8b30f | ||
|
|
9892ee273e | ||
|
|
7dccecb336 | ||
|
|
aa09da0403 | ||
|
|
98d7f1481c | ||
|
|
7d8bdbcf7e | ||
|
|
db009d3689 | ||
|
|
d7bc529a95 | ||
|
|
3f035294a6 | ||
|
|
36282e362f | ||
|
|
223353cf4d | ||
|
|
ded32da632 | ||
|
|
d6fc21cc34 | ||
|
|
b86e960456 | ||
|
|
7747bfe0f2 | ||
|
|
fabea8cc61 | ||
|
|
de4cd2eebc | ||
|
|
e16ce1d9c2 | ||
|
|
345ad741b3 | ||
|
|
e53108c493 | ||
|
|
6129378854 | ||
|
|
11a1416dcc | ||
|
|
9025538544 | ||
|
|
46929e1844 | ||
|
|
bf023cad48 | ||
|
|
f649236bad | ||
|
|
54b1454c0a | ||
|
|
a41b1c2f5e | ||
|
|
0327d7cb7a | ||
|
|
621d7c3683 | ||
|
|
ae7c3fca31 | ||
|
|
1b3295ff59 | ||
|
|
e40e7a0e4e | ||
|
|
cf5fd7cbf2 | ||
|
|
ec37a676a8 | ||
|
|
f33193e122 | ||
|
|
7c40204e4f | ||
|
|
2b2ffb3346 | ||
|
|
39b7e4ff9e | ||
|
|
a1109c43f6 | ||
|
|
887ea0cf06 | ||
|
|
3e0525ee9e | ||
|
|
e6eafc2843 | ||
|
|
3e8dea73d2 | ||
|
|
bbf19ee944 | ||
|
|
f6c8c9e76c | ||
|
|
9dba1232b7 | ||
|
|
bf14c8983f | ||
|
|
3ab95384df | ||
|
|
953a581a94 | ||
|
|
3bfddd708c | ||
|
|
13a54ec318 | ||
|
|
2781640a2a | ||
|
|
7989f119d3 | ||
|
|
2e0aa7ae87 | ||
|
|
c01ce90e06 | ||
|
|
d68c8cb845 | ||
|
|
0b5ee7c79f | ||
|
|
23cbe1c426 | ||
|
|
de727bcddc | ||
|
|
c9d9730726 | ||
|
|
a380aed878 | ||
|
|
4dafa50799 | ||
|
|
d4ae241758 | ||
|
|
88947b9d42 | ||
|
|
3c68ad1302 | ||
|
|
e2b3389da6 | ||
|
|
938385c18b | ||
|
|
5d01b06877 | ||
|
|
f007c093eb | ||
|
|
53e69835ff | ||
|
|
939208903a | ||
|
|
7526f4d7b1 | ||
|
|
616b38c6fb | ||
|
|
a302df8f91 | ||
|
|
093512a55a | ||
|
|
007224c92e | ||
|
|
738ee33959 | ||
|
|
d720a6b250 | ||
|
|
5ce3cc17bb | ||
|
|
5c35205315 | ||
|
|
10751934e4 | ||
|
|
d658f91106 | ||
|
|
fae96af17d | ||
|
|
57c1a03c4f | ||
|
|
cb8fe42c66 | ||
|
|
7f4b2574b0 | ||
|
|
fad163e0eb | ||
|
|
7ad3992bd1 | ||
|
|
e7b5c0e37c | ||
|
|
cc35127459 | ||
|
|
1c0ecd29c2 | ||
|
|
1f0fddd0e9 | ||
|
|
18c6b9bc09 | ||
|
|
2233b9a094 | ||
|
|
e0f000b913 | ||
|
|
73864e0809 | ||
|
|
cd8a85975c | ||
|
|
2f94ec20b0 | ||
|
|
09d9deae43 | ||
|
|
209fc07eac | ||
|
|
ae70cb89ed | ||
|
|
4c3324aea1 | ||
|
|
ac2fcee90f | ||
|
|
94e00115fe | ||
|
|
29255d2338 | ||
|
|
bda36e508a | ||
|
|
d2ea1273da | ||
|
|
39278cfce7 | ||
|
|
46cc48cfb9 | ||
|
|
eb4009949d | ||
|
|
57c48a4bd2 | ||
|
|
45011de494 | ||
|
|
5e51ed0a5f | ||
|
|
875802ef2a | ||
|
|
bbc255ef83 | ||
|
|
fc155ec3fc | ||
|
|
3a0c8a199a | ||
|
|
9c729c4490 | ||
|
|
19bdbd66e5 | ||
|
|
ea090f79ee | ||
|
|
1be6c54be2 | ||
|
|
a4eeb226b1 | ||
|
|
d136717636 | ||
|
|
ad0a2e6f58 | ||
|
|
0212bfb46e | ||
|
|
5e0d661542 | ||
|
|
4c5f529ef4 | ||
|
|
b765534b8b | ||
|
|
7c672c5ee9 | ||
|
|
090bf3f8ec | ||
|
|
9f4ef9cdad | ||
|
|
26fa66ef64 | ||
|
|
b69e4567c0 | ||
|
|
26b19350f2 | ||
|
|
2018b7e247 | ||
|
|
cbff4161a1 | ||
|
|
130bdfb7f2 | ||
|
|
d0c43f5aa9 | ||
|
|
f864a5f31e | ||
|
|
ff585584f6 | ||
|
|
2e0af64ac3 | ||
|
|
8a029e5147 | ||
|
|
4251089687 | ||
|
|
dbcd670ca8 | ||
|
|
cecae671d8 | ||
|
|
b4e23ac454 | ||
|
|
e7209ca085 | ||
|
|
022ef71d2c | ||
|
|
0fad891a3a | ||
|
|
47f608b502 | ||
|
|
4ab745f730 | ||
|
|
1f89ac11d2 | ||
|
|
1a5dae76c2 | ||
|
|
032c7f529a | ||
|
|
707818abcc | ||
|
|
58e8b4c618 | ||
|
|
9c0c2867dd | ||
|
|
4308c717c3 | ||
|
|
30bc8dfbe9 | ||
|
|
342b67581b | ||
|
|
75da5b650c | ||
|
|
5b6a53be3e | ||
|
|
5b5105c864 | ||
|
|
9d09146f68 | ||
|
|
1b19d20d0c | ||
|
|
0eacab5ddc | ||
|
|
0d7874bac1 | ||
|
|
5cddfafbd0 | ||
|
|
5ce133c47e | ||
|
|
53a579028c | ||
|
|
570a84b67a | ||
|
|
a68bfd7223 | ||
|
|
fd7176a445 | ||
|
|
0a06472639 | ||
|
|
3f3a7cd4f6 | ||
|
|
99415359d0 | ||
|
|
54cb2d268b | ||
|
|
0e40daecb6 | ||
|
|
21a822b082 | ||
|
|
a770d09687 | ||
|
|
27b81c4e11 | ||
|
|
8826c80e07 | ||
|
|
02e3ce7fc1 | ||
|
|
673e6aede5 | ||
|
|
14190fc533 | ||
|
|
20d0ae5304 | ||
|
|
2ba37af109 | ||
|
|
728f8e65d6 | ||
|
|
3a2580fbc2 | ||
|
|
462f619f43 | ||
|
|
ef61735f64 | ||
|
|
a2780ca056 | ||
|
|
d025c8bd9a | ||
|
|
acf38e47ba | ||
|
|
4122d4207d | ||
|
|
d550ae7d93 | ||
|
|
815a542cc1 | ||
|
|
123a0ccd70 | ||
|
|
4c737475d4 | ||
|
|
4e0d97f2c1 | ||
|
|
85906cafbb | ||
|
|
a8dc691033 | ||
|
|
4d7988b78e | ||
|
|
841e760b04 | ||
|
|
31e758ca45 | ||
|
|
f81ca3ba60 |
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 9,
|
||||
"ecmaFeatures": {
|
||||
@@ -63,7 +63,8 @@
|
||||
}],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double", {
|
||||
"avoidEscape": true
|
||||
"avoidEscape": true,
|
||||
"allowTemplateLiterals": true
|
||||
}],
|
||||
"camelcase": ["error", {
|
||||
"properties": "always"
|
||||
|
||||
33
.github/workflows/codeql.yml
vendored
Normal file
33
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: "CodeQL Analysis"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master ]
|
||||
schedule:
|
||||
- cron: '22 17 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
55
.github/workflows/master.yml
vendored
Normal file
55
.github/workflows/master.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: "Master Build, Test & Deploy"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set node version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '17.x'
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
npm run setheapsize
|
||||
|
||||
- name: Lint
|
||||
run: npx grunt lint
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
npm test
|
||||
npm run testnodeconsumer
|
||||
|
||||
- name: Production Build
|
||||
if: success()
|
||||
run: npx grunt prod
|
||||
|
||||
- name: Generate sitemap
|
||||
run: npx grunt exec:sitemap
|
||||
|
||||
# - name: UI Tests
|
||||
# if: success()
|
||||
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||
|
||||
- name: Prepare for GitHub Pages
|
||||
if: success()
|
||||
run: npx grunt copy:ghPages
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
if: success() && github.ref == 'refs/heads/master'
|
||||
uses: crazy-max/ghaction-github-pages@v2
|
||||
with:
|
||||
target_branch: gh-pages
|
||||
build_dir: ./build/prod
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
38
.github/workflows/pull_requests.yml
vendored
Normal file
38
.github/workflows/pull_requests.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: "Pull Requests"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [synchronize, opened, reopened]
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set node version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '17.x'
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
npm run setheapsize
|
||||
|
||||
- name: Lint
|
||||
run: npx grunt lint
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
npm test
|
||||
npm run testnodeconsumer
|
||||
|
||||
- name: Production Build
|
||||
if: success()
|
||||
run: npx grunt prod
|
||||
|
||||
# - name: UI Tests
|
||||
# if: success()
|
||||
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||
57
.github/workflows/releases.yml
vendored
Normal file
57
.github/workflows/releases.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: "Releases"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set node version
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '17.x'
|
||||
|
||||
- name: Install
|
||||
run: |
|
||||
npm install
|
||||
npm run setheapsize
|
||||
|
||||
- name: Lint
|
||||
run: npx grunt lint
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
npm test
|
||||
npm run testnodeconsumer
|
||||
|
||||
- name: Production Build
|
||||
if: success()
|
||||
run: npx grunt prod
|
||||
|
||||
# - name: UI Tests
|
||||
# if: success()
|
||||
# run: xvfb-run --server-args="-screen 0 1200x800x24" npx grunt testui
|
||||
|
||||
- name: Upload Release Assets
|
||||
if: success()
|
||||
id: upload-release-assets
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: build/prod/*.zip
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
body: "See the [CHANGELOG](https://github.com/gchq/CyberChef/blob/master/CHANGELOG.md) and [commit messages](https://github.com/gchq/CyberChef/commits/master) for details."
|
||||
|
||||
- name: Publish to NPM
|
||||
if: success()
|
||||
uses: JS-DevTools/npm-publish@v1
|
||||
with:
|
||||
token: ${{ secrets.NPM_TOKEN }}
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,4 +11,4 @@ src/node/config/OperationConfig.json
|
||||
src/node/index.mjs
|
||||
**/*.DS_Store
|
||||
tests/browser/output/*
|
||||
|
||||
.node-version
|
||||
|
||||
55
.travis.yml
55
.travis.yml
@@ -1,55 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- lts/dubnium
|
||||
cache: npm
|
||||
os: linux
|
||||
addons:
|
||||
chrome: stable
|
||||
install: npm install
|
||||
before_script:
|
||||
- npm install -g grunt
|
||||
- export NODE_OPTIONS=--max_old_space_size=2048
|
||||
script:
|
||||
- grunt lint
|
||||
- grunt test
|
||||
- grunt testnodeconsumer
|
||||
- grunt prod --msg="$COMPILE_MSG"
|
||||
- xvfb-run --server-args="-screen 0 1200x800x24" grunt testui
|
||||
before_deploy:
|
||||
- grunt exec:sitemap
|
||||
- grunt copy:ghPages
|
||||
deploy:
|
||||
- provider: pages
|
||||
edge: true
|
||||
token: $GITHUB_TOKEN
|
||||
local_dir: build/prod/
|
||||
target_branch: gh-pages
|
||||
on:
|
||||
repo: gchq/CyberChef
|
||||
branch: master
|
||||
- provider: releases
|
||||
edge: true
|
||||
token:
|
||||
secure: "HV1WSKv4l/0Y2bKKs1iBJocBcmLj08PCRUeEM/jTwA4jqJ8EiLHWiXtER/D5sEg2iibRVKd2OQjfrmS6bo4AiwdeVgAKmv0FtS2Jw+391N8Nd5AkEANHa5Om/IpHLTL2YRAjpJTsDpY72bMUTJIwjQA3TFJkgrpOw6KYfohOcgbxLpZ4XuNJRU3VL4Hsxdv5V9aOVmfFOmMOVPQlakXy7NgtW5POp1f2WJwgcZxylkR1CjwaqMyXmSoVl46pyH3tr5+dptsQoKSGdi6sIHGA60oDotFPcm+0ifa47wZw+vapuuDi4tdNxhrHGaDMG8xiE0WFDHwQUDlk2/+W7j9SEX0H3Em7us371JXRp56EDwEcDa34VpVkC6i8HGcHK55hnxVbMZXGf3qhOFD8wY7qMbjMRvIpucrMHBi86OfkDfv0vDj2LyvIl5APj/AX50BrE0tfH1MZbH26Jkx4NdlkcxQ14GumarmUqfmVvbX/fsoA6oUuAAE9ZgRRi3KHO4wci6KUcRfdm+XOeUkaBFsL86G3EEYIvrtBTuaypdz+Cx7nd1iPZyWMx5Y1gXnVzha4nBdV4+7l9JIsFggD8QVpw2uHXQiS1KXFjOeqA3DBD8tjMB7q26Fl2fD3jkOo4BTbQ2NrRIZUu/iL+fOmMPsyMt2qulB0yaSBCfkbEq8xrUA="
|
||||
file_glob: true
|
||||
file:
|
||||
- build/prod/*.zip
|
||||
- src/node/cjs.js
|
||||
on:
|
||||
repo: gchq/CyberChef
|
||||
tags: true
|
||||
- provider: npm
|
||||
edge: true
|
||||
email: "n1474335@gmail.com"
|
||||
api_token:
|
||||
secure: "UnDQL3Kh+GK2toL0TK3FObO0ujVssU3Eg4BBuYdjwLB81GhiGE5/DTh7THdZPOpbLo6wQeOwfZDuMeKC1OU+0Uf4NsdYFu1aq6xMO20qBQ4qUfgsyiK4Qgywj9gk0p1+OFZdGAZ/j1CNRAaF71XQIY6iV84c+SO4WoizXYrNT0Jh4sr2DA4/97G2xmJtPi0qOzYrJ09R56ZUozmqeik5G0pMRIuJRbpjS/7bZXV+N7WV0ombZc9RkUaetbabEVOLQ+Xx5YAIVq+VuEeMe9VBSnxY/FfCLmy1wJsjGzpLCyBI9nbrG4nw8Wgc2m8NfK9rcpIvBTGner9r2j60NVDkZ8kLZPrqXhq6AZMwa+oz6K5UQCqRo2RRQzSGwXxg67HY5Tcq+oNmjd+DqpPg4LZ3eGlluyP5XfG+hpSr9Ya4d8q8SrUWLxkoLHI6ZKMtoKFbTCSSQPiluW5hsZxjz3yDkkjsJw64M/EM8UyJrgaXqDklQu+7rBGKLfsK6os7RDiqjBWpQ7gwpo8HvY0O8yqEAabPz+QGkanpjcCOZCXFbSkzWxYy37RMAPu88iINVZVlZE4l+WJenCpZY95ueyy0mG9cyMSzVRPyX6A+/n4H6VMFPFjpGDLTD588ACEjY1lmHfS/eXwXJcgqPPD2gW0XdRdUheU/ssqlfCfGWQMTDXs="
|
||||
on:
|
||||
tags: true
|
||||
branch: master
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/83c143a6822e218d5b34
|
||||
on_success: change
|
||||
on_failure: always
|
||||
on_start: never
|
||||
131
CHANGELOG.md
131
CHANGELOG.md
@@ -1,7 +1,83 @@
|
||||
# Changelog
|
||||
|
||||
## Versioning
|
||||
|
||||
CyberChef uses the [semver](https://semver.org/) system to manage versioning: `<MAJOR>.<MINOR>.<PATCH>`.
|
||||
|
||||
- MAJOR version changes represent a significant change to the fundamental architecture of CyberChef and may (but don't always) make breaking changes that are not backwards compatible.
|
||||
- MINOR version changes usually mean the addition of new operations or reasonably significant new features.
|
||||
- PATCH versions are used for bug fixes and any other small tweaks that modify or improve existing capabilities.
|
||||
|
||||
All major and minor version changes will be documented in this file. Details of patch-level version changes can be found in [commit messages](https://github.com/gchq/CyberChef/commits/master).
|
||||
|
||||
|
||||
## Details
|
||||
|
||||
### [9.37.0] - 2022-03-29
|
||||
- 'SM4 Encrypt' and 'SM4 Decrypt' operations added [@swesven] | [#1189]
|
||||
- NoPadding options added for CBC and ECB modes in AES, DES and Triple DES Decrypt operations [@swesven] | [#1189]
|
||||
|
||||
### [9.36.0] - 2022-03-29
|
||||
- 'SIGABA' operation added [@hettysymes] | [#934]
|
||||
|
||||
### [9.35.0] - 2022-03-28
|
||||
- 'To Base45' and 'From Base45' operations added [@t-8ch] | [#1242]
|
||||
|
||||
### [9.34.0] - 2022-03-28
|
||||
- 'Get All Casings' operation added [@n1073645] | [#1065]
|
||||
|
||||
### [9.33.0] - 2022-03-25
|
||||
- Updated to support Node 17 [@n1474335] [@john19696] [@t-8ch] | [[#1326] [#1313] [#1244]
|
||||
- Improved CJS and ESM module support [@d98762625] | [#1037]
|
||||
|
||||
### [9.32.0] - 2021-08-18
|
||||
- 'Protobuf Encode' operation added and decode operation modified to allow decoding with full and partial schemas [@n1474335] | [dd18e52]
|
||||
|
||||
### [9.31.0] - 2021-08-10
|
||||
- 'HASSH Client Fingerprint' and 'HASSH Server Fingerprint' operations added [@n1474335] | [e9ca4dc]
|
||||
|
||||
### [9.30.0] - 2021-08-10
|
||||
- 'JA3S Fingerprint' operation added [@n1474335] | [289a417]
|
||||
|
||||
### [9.29.0] - 2021-07-28
|
||||
- 'JA3 Fingerprint' operation added [@n1474335] | [9a33498]
|
||||
|
||||
### [9.28.0] - 2021-03-26
|
||||
- 'CBOR Encode' and 'CBOR Decode' operations added [@Danh4] | [#999]
|
||||
|
||||
### [9.27.0] - 2021-02-12
|
||||
- 'Fuzzy Match' operation added [@n1474335] | [8ad18b]
|
||||
|
||||
### [9.26.0] - 2021-02-11
|
||||
- 'Get Time' operation added [@n1073645] [@n1474335] | [#1045]
|
||||
|
||||
### [9.25.0] - 2021-02-11
|
||||
- 'Extract ID3' operation added [@n1073645] [@n1474335] | [#1006]
|
||||
|
||||
### [9.24.0] - 2021-02-02
|
||||
- 'SM3' hashing function added along with more configuration options for other hashing operations [@n1073645] [@n1474335] | [#1022]
|
||||
|
||||
### [9.23.0] - 2021-02-01
|
||||
- Various RSA operations added to encrypt, decrypt, sign, verify and generate keys [@mattnotmitt] [@GCHQ77703] | [#652]
|
||||
|
||||
### [9.22.0] - 2021-02-01
|
||||
- 'Unicode Text Format' operation added [@mattnotmitt] | [#1083]
|
||||
|
||||
### [9.21.0] - 2020-06-12
|
||||
- Node API now exports `magic` operation [@d98762625] | [#1049]
|
||||
|
||||
### [9.20.0] - 2020-03-27
|
||||
- 'Parse ObjectID Timestamp' operation added [@dmfj] | [#987]
|
||||
|
||||
### [9.19.0] - 2020-03-24
|
||||
- Improvements to the 'Magic' operation, allowing it to recognise more data formats and provide more accurate results [@n1073645] [@n1474335] | [#966] [b765534b](https://github.com/gchq/CyberChef/commit/b765534b8b2a0454a5132a0a52d1d8844bcbdaaa)
|
||||
|
||||
### [9.18.0] - 2020-03-13
|
||||
- 'Convert to NATO alphabet' operation added [@MarvinJWendt] | [#674]
|
||||
|
||||
### [9.17.0] - 2020-03-13
|
||||
- 'Generate Image' operation added [@pointhi] | [#683]
|
||||
|
||||
### [9.16.0] - 2020-03-06
|
||||
- 'Colossus' operation added [@VirtualColossus] | [#917]
|
||||
|
||||
@@ -212,6 +288,27 @@ All major and minor version changes will be documented in this file. Details of
|
||||
|
||||
|
||||
|
||||
[9.37.0]: https://github.com/gchq/CyberChef/releases/tag/v9.37.0
|
||||
[9.36.0]: https://github.com/gchq/CyberChef/releases/tag/v9.36.0
|
||||
[9.35.0]: https://github.com/gchq/CyberChef/releases/tag/v9.35.0
|
||||
[9.34.0]: https://github.com/gchq/CyberChef/releases/tag/v9.34.0
|
||||
[9.33.0]: https://github.com/gchq/CyberChef/releases/tag/v9.33.0
|
||||
[9.32.0]: https://github.com/gchq/CyberChef/releases/tag/v9.32.0
|
||||
[9.31.0]: https://github.com/gchq/CyberChef/releases/tag/v9.31.0
|
||||
[9.30.0]: https://github.com/gchq/CyberChef/releases/tag/v9.30.0
|
||||
[9.29.0]: https://github.com/gchq/CyberChef/releases/tag/v9.29.0
|
||||
[9.28.0]: https://github.com/gchq/CyberChef/releases/tag/v9.28.0
|
||||
[9.27.0]: https://github.com/gchq/CyberChef/releases/tag/v9.27.0
|
||||
[9.26.0]: https://github.com/gchq/CyberChef/releases/tag/v9.26.0
|
||||
[9.25.0]: https://github.com/gchq/CyberChef/releases/tag/v9.25.0
|
||||
[9.24.0]: https://github.com/gchq/CyberChef/releases/tag/v9.24.0
|
||||
[9.23.0]: https://github.com/gchq/CyberChef/releases/tag/v9.23.0
|
||||
[9.22.0]: https://github.com/gchq/CyberChef/releases/tag/v9.22.0
|
||||
[9.21.0]: https://github.com/gchq/CyberChef/releases/tag/v9.21.0
|
||||
[9.20.0]: https://github.com/gchq/CyberChef/releases/tag/v9.20.0
|
||||
[9.19.0]: https://github.com/gchq/CyberChef/releases/tag/v9.19.0
|
||||
[9.18.0]: https://github.com/gchq/CyberChef/releases/tag/v9.18.0
|
||||
[9.17.0]: https://github.com/gchq/CyberChef/releases/tag/v9.17.0
|
||||
[9.16.0]: https://github.com/gchq/CyberChef/releases/tag/v9.16.0
|
||||
[9.15.0]: https://github.com/gchq/CyberChef/releases/tag/v9.15.0
|
||||
[9.14.0]: https://github.com/gchq/CyberChef/releases/tag/v9.14.0
|
||||
@@ -304,6 +401,21 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[@cbeuw]: https://github.com/cbeuw
|
||||
[@matthieuxyz]: https://github.com/matthieuxyz
|
||||
[@Flavsditz]: https://github.com/Flavsditz
|
||||
[@pointhi]: https://github.com/pointhi
|
||||
[@MarvinJWendt]: https://github.com/MarvinJWendt
|
||||
[@dmfj]: https://github.com/dmfj
|
||||
[@mattnotmitt]: https://github.com/mattnotmitt
|
||||
[@Danh4]: https://github.com/Danh4
|
||||
[@john19696]: https://github.com/john19696
|
||||
[@t-8ch]: https://github.com/t-8ch
|
||||
[@hettysymes]: https://github.com/hettysymes
|
||||
[@swesven]: https://github.com/swesven
|
||||
|
||||
[8ad18b]: https://github.com/gchq/CyberChef/commit/8ad18bc7db6d9ff184ba3518686293a7685bf7b7
|
||||
[9a33498]: https://github.com/gchq/CyberChef/commit/9a33498fed26a8df9c9f35f39a78a174bf50a513
|
||||
[289a417]: https://github.com/gchq/CyberChef/commit/289a417dfb5923de5e1694354ec42a08d9395bfe
|
||||
[e9ca4dc]: https://github.com/gchq/CyberChef/commit/e9ca4dc9caf98f33fd986431cd400c88082a42b8
|
||||
[dd18e52]: https://github.com/gchq/CyberChef/commit/dd18e529939078b89867297b181a584e8b2cc7da
|
||||
|
||||
[#95]: https://github.com/gchq/CyberChef/pull/299
|
||||
[#173]: https://github.com/gchq/CyberChef/pull/173
|
||||
@@ -365,10 +477,29 @@ All major and minor version changes will be documented in this file. Details of
|
||||
[#625]: https://github.com/gchq/CyberChef/pull/625
|
||||
[#627]: https://github.com/gchq/CyberChef/pull/627
|
||||
[#632]: https://github.com/gchq/CyberChef/pull/632
|
||||
[#652]: https://github.com/gchq/CyberChef/pull/652
|
||||
[#653]: https://github.com/gchq/CyberChef/pull/653
|
||||
[#674]: https://github.com/gchq/CyberChef/pull/674
|
||||
[#683]: https://github.com/gchq/CyberChef/pull/683
|
||||
[#865]: https://github.com/gchq/CyberChef/pull/865
|
||||
[#912]: https://github.com/gchq/CyberChef/pull/912
|
||||
[#917]: https://github.com/gchq/CyberChef/pull/917
|
||||
[#934]: https://github.com/gchq/CyberChef/pull/934
|
||||
[#948]: https://github.com/gchq/CyberChef/pull/948
|
||||
[#952]: https://github.com/gchq/CyberChef/pull/952
|
||||
[#965]: https://github.com/gchq/CyberChef/pull/965
|
||||
[#966]: https://github.com/gchq/CyberChef/pull/966
|
||||
[#987]: https://github.com/gchq/CyberChef/pull/987
|
||||
[#999]: https://github.com/gchq/CyberChef/pull/999
|
||||
[#1006]: https://github.com/gchq/CyberChef/pull/1006
|
||||
[#1022]: https://github.com/gchq/CyberChef/pull/1022
|
||||
[#1037]: https://github.com/gchq/CyberChef/pull/1037
|
||||
[#1045]: https://github.com/gchq/CyberChef/pull/1045
|
||||
[#1049]: https://github.com/gchq/CyberChef/pull/1049
|
||||
[#1065]: https://github.com/gchq/CyberChef/pull/1065
|
||||
[#1083]: https://github.com/gchq/CyberChef/pull/1083
|
||||
[#1189]: https://github.com/gchq/CyberChef/pull/1189
|
||||
[#1242]: https://github.com/gchq/CyberChef/pull/1242
|
||||
[#1244]: https://github.com/gchq/CyberChef/pull/1244
|
||||
[#1313]: https://github.com/gchq/CyberChef/pull/1313
|
||||
[#1326]: https://github.com/gchq/CyberChef/pull/1326
|
||||
140
Gruntfile.js
140
Gruntfile.js
@@ -6,6 +6,8 @@ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPl
|
||||
const glob = require("glob");
|
||||
const path = require("path");
|
||||
|
||||
const nodeFlags = "--experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings --no-deprecation";
|
||||
|
||||
/**
|
||||
* Grunt configuration for building the app in various formats.
|
||||
*
|
||||
@@ -36,11 +38,10 @@ module.exports = function (grunt) {
|
||||
"clean:node", "clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex"
|
||||
]);
|
||||
|
||||
grunt.registerTask("test",
|
||||
"A task which runs all the operation tests in the tests directory.",
|
||||
grunt.registerTask("configTests",
|
||||
"A task which configures config files in preparation for tests to be run. Use `npm test` to run tests.",
|
||||
[
|
||||
"clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex",
|
||||
"exec:nodeTests", "exec:opTests"
|
||||
"clean:config", "clean:nodeConfig", "exec:generateConfig", "exec:generateNodeIndex"
|
||||
]);
|
||||
|
||||
grunt.registerTask("testui",
|
||||
@@ -49,13 +50,12 @@ module.exports = function (grunt) {
|
||||
|
||||
grunt.registerTask("testnodeconsumer",
|
||||
"A task which checks whether consuming CJS and ESM apps work with the CyberChef build",
|
||||
["exec:setupNodeConsumers", "exec:testCJSNodeConsumer", "exec:testESMNodeConsumer", "exec:testESMDeepImportNodeConsumer", "exec:teardownNodeConsumers"]);
|
||||
["exec:setupNodeConsumers", "exec:testCJSNodeConsumer", "exec:testESMNodeConsumer", "exec:teardownNodeConsumers"]);
|
||||
|
||||
grunt.registerTask("default",
|
||||
"Lints the code base",
|
||||
["eslint", "exec:repoSize"]);
|
||||
|
||||
grunt.registerTask("tests", "test");
|
||||
grunt.registerTask("lint", "eslint");
|
||||
|
||||
grunt.registerTask("findModules",
|
||||
@@ -80,7 +80,6 @@ module.exports = function (grunt) {
|
||||
grunt.loadNpmTasks("grunt-contrib-watch");
|
||||
grunt.loadNpmTasks("grunt-chmod");
|
||||
grunt.loadNpmTasks("grunt-exec");
|
||||
grunt.loadNpmTasks("grunt-accessibility");
|
||||
grunt.loadNpmTasks("grunt-concurrent");
|
||||
grunt.loadNpmTasks("grunt-contrib-connect");
|
||||
grunt.loadNpmTasks("grunt-zip");
|
||||
@@ -177,7 +176,7 @@ module.exports = function (grunt) {
|
||||
// previous one fails. & would coninue on a fail
|
||||
.join("&&")
|
||||
// Windows does not support \n properly
|
||||
.replace("\n", "\\n");
|
||||
.replace(/\n/g, "\\n");
|
||||
}
|
||||
|
||||
grunt.initConfig({
|
||||
@@ -190,76 +189,46 @@ module.exports = function (grunt) {
|
||||
standalone: ["build/prod/CyberChef*.html"]
|
||||
},
|
||||
eslint: {
|
||||
options: {
|
||||
configFile: "./.eslintrc.json"
|
||||
},
|
||||
configs: ["*.{js,mjs}"],
|
||||
core: ["src/core/**/*.{js,mjs}", "!src/core/vendor/**/*", "!src/core/operations/legacy/**/*"],
|
||||
web: ["src/web/**/*.{js,mjs}", "!src/web/static/**/*"],
|
||||
node: ["src/node/**/*.{js,mjs}"],
|
||||
tests: ["tests/**/*.{js,mjs}"],
|
||||
},
|
||||
accessibility: {
|
||||
options: {
|
||||
accessibilityLevel: "WCAG2A",
|
||||
verbose: false,
|
||||
ignore: [
|
||||
"WCAG2A.Principle1.Guideline1_3.1_3_1.H42.2"
|
||||
]
|
||||
},
|
||||
test: {
|
||||
src: ["build/**/*.html"]
|
||||
}
|
||||
},
|
||||
webpack: {
|
||||
options: webpackConfig,
|
||||
web: webpackProdConf(),
|
||||
},
|
||||
"webpack-dev-server": {
|
||||
options: {
|
||||
webpack: webpackConfig,
|
||||
host: "0.0.0.0",
|
||||
port: grunt.option("port") || 8080,
|
||||
disableHostCheck: true,
|
||||
overlay: true,
|
||||
inline: false,
|
||||
clientLogLevel: "error",
|
||||
stats: {
|
||||
children: false,
|
||||
chunks: false,
|
||||
modules: false,
|
||||
entrypoints: false,
|
||||
warningsFilter: [
|
||||
/source-map/,
|
||||
/dependency is an expression/,
|
||||
/export 'default'/,
|
||||
/Can't resolve 'sodium'/
|
||||
],
|
||||
}
|
||||
},
|
||||
options: webpackConfig,
|
||||
start: {
|
||||
webpack: {
|
||||
mode: "development",
|
||||
target: "web",
|
||||
entry: Object.assign({
|
||||
main: "./src/web/index.js"
|
||||
}, moduleEntryPoints),
|
||||
resolve: {
|
||||
alias: {
|
||||
"./config/modules/OpModules.mjs": "./config/modules/Default.mjs"
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(BUILD_CONSTANTS),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "index.html",
|
||||
template: "./src/web/html/index.html",
|
||||
chunks: ["main"],
|
||||
compileTime: compileTime,
|
||||
version: pkg.version,
|
||||
})
|
||||
]
|
||||
}
|
||||
mode: "development",
|
||||
target: "web",
|
||||
entry: Object.assign({
|
||||
main: "./src/web/index.js"
|
||||
}, moduleEntryPoints),
|
||||
resolve: {
|
||||
alias: {
|
||||
"./config/modules/OpModules.mjs": "./config/modules/Default.mjs"
|
||||
}
|
||||
},
|
||||
devServer: {
|
||||
port: grunt.option("port") || 8080,
|
||||
client: {
|
||||
logging: "error",
|
||||
overlay: true
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin(BUILD_CONSTANTS),
|
||||
new HtmlWebpackPlugin({
|
||||
filename: "index.html",
|
||||
template: "./src/web/html/index.html",
|
||||
chunks: ["main"],
|
||||
compileTime: compileTime,
|
||||
version: pkg.version,
|
||||
})
|
||||
]
|
||||
}
|
||||
},
|
||||
zip: {
|
||||
@@ -304,7 +273,7 @@ module.exports = function (grunt) {
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: "build/prod/index.html",
|
||||
src: ["build/prod/index.html"],
|
||||
dest: "build/prod/index.html"
|
||||
}
|
||||
]
|
||||
@@ -326,7 +295,7 @@ module.exports = function (grunt) {
|
||||
},
|
||||
files: [
|
||||
{
|
||||
src: "build/prod/index.html",
|
||||
src: ["build/prod/index.html"],
|
||||
dest: `build/prod/CyberChef_v${pkg.version}.html`
|
||||
}
|
||||
]
|
||||
@@ -364,15 +333,15 @@ module.exports = function (grunt) {
|
||||
command: "git gc --prune=now --aggressive"
|
||||
},
|
||||
sitemap: {
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation src/web/static/sitemap.mjs > build/prod/sitemap.xml",
|
||||
command: `node ${nodeFlags} src/web/static/sitemap.mjs > build/prod/sitemap.xml`,
|
||||
sync: true
|
||||
},
|
||||
generateConfig: {
|
||||
command: chainCommands([
|
||||
"echo '\n--- Regenerating config files. ---'",
|
||||
"echo [] > src/core/config/OperationConfig.json",
|
||||
"node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateOpsIndex.mjs",
|
||||
"node --experimental-modules --no-warnings --no-deprecation src/core/config/scripts/generateConfig.mjs",
|
||||
`node ${nodeFlags} src/core/config/scripts/generateOpsIndex.mjs`,
|
||||
`node ${nodeFlags} src/core/config/scripts/generateConfig.mjs`,
|
||||
"echo '--- Config scripts finished. ---\n'"
|
||||
]),
|
||||
sync: true
|
||||
@@ -380,20 +349,14 @@ module.exports = function (grunt) {
|
||||
generateNodeIndex: {
|
||||
command: chainCommands([
|
||||
"echo '\n--- Regenerating node index ---'",
|
||||
"node --experimental-modules --no-warnings --no-deprecation src/node/config/scripts/generateNodeIndex.mjs",
|
||||
`node ${nodeFlags} src/node/config/scripts/generateNodeIndex.mjs`,
|
||||
"echo '--- Node index generated. ---\n'"
|
||||
]),
|
||||
sync: true
|
||||
},
|
||||
opTests: {
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation tests/operations/index.mjs"
|
||||
},
|
||||
browserTests: {
|
||||
command: "./node_modules/.bin/nightwatch --env prod"
|
||||
},
|
||||
nodeTests: {
|
||||
command: "node --experimental-modules --no-warnings --no-deprecation tests/node/index.mjs"
|
||||
},
|
||||
setupNodeConsumers: {
|
||||
command: chainCommands([
|
||||
"echo '\n--- Testing node consumers ---'",
|
||||
@@ -414,24 +377,27 @@ module.exports = function (grunt) {
|
||||
testCJSNodeConsumer: {
|
||||
command: chainCommands([
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings cjs-consumer.js",
|
||||
`node ${nodeFlags} cjs-consumer.js`,
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
testESMNodeConsumer: {
|
||||
command: chainCommands([
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings --experimental-modules esm-consumer.mjs",
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
testESMDeepImportNodeConsumer: {
|
||||
command: chainCommands([
|
||||
`cd ${nodeConsumerTestPath}`,
|
||||
"node --no-warnings --experimental-modules esm-deep-import-consumer.mjs",
|
||||
`node ${nodeFlags} esm-consumer.mjs`,
|
||||
]),
|
||||
stdout: false,
|
||||
},
|
||||
fixCryptoApiImports: {
|
||||
command: [
|
||||
`[[ "$OSTYPE" == "darwin"* ]]`,
|
||||
"&&",
|
||||
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i '' -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`,
|
||||
"||",
|
||||
`find ./node_modules/crypto-api/src/ \\( -type d -name .git -prune \\) -o -type f -print0 | xargs -0 sed -i -e '/\\.mjs/!s/\\(from "\\.[^"]*\\)";/\\1.mjs";/g'`
|
||||
].join(" "),
|
||||
stdout: false
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# CyberChef
|
||||
|
||||
[](https://travis-ci.org/gchq/CyberChef)
|
||||
[](https://david-dm.org/gchq/CyberChef)
|
||||
[](https://github.com/gchq/CyberChef/actions?query=workflow%3A%22Master+Build%2C+Test+%26+Deploy%22)
|
||||
[](https://lgtm.com/projects/g/gchq/CyberChef/context:javascript)
|
||||
[](https://www.npmjs.com/package/cyberchef)
|
||||
[](https://github.com/gchq/CyberChef/blob/master/LICENSE)
|
||||
[](https://gitter.im/gchq/CyberChef?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
#### *The Cyber Swiss Army Knife*
|
||||
|
||||
CyberChef is a simple, intuitive web app for carrying out all manner of "cyber" operations within a web browser. These operations include simple encoding like XOR or Base64, more complex encryption like AES, DES and Blowfish, creating binary and hexdumps, compression and decompression of data, calculating hashes and checksums, IPv6 and X.509 parsing, changing character encodings, and much more.
|
||||
CyberChef is a simple, intuitive web app for carrying out all manner of "cyber" operations within a web browser. These operations include simple encoding like XOR and Base64, more complex encryption like AES, DES and Blowfish, creating binary and hexdumps, compression and decompression of data, calculating hashes and checksums, IPv6 and X.509 parsing, changing character encodings, and much more.
|
||||
|
||||
The tool is designed to enable both technical and non-technical analysts to manipulate data in complex ways without having to deal with complex tools or algorithms. It was conceived, designed, built and incrementally improved by an analyst in their 10% innovation time over several years.
|
||||
|
||||
@@ -105,7 +105,7 @@ An installation walkthrough, how-to guides for adding new operations and themes,
|
||||
|
||||
## Licencing
|
||||
|
||||
CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/copyright-and-re-use/crown-copyright/).
|
||||
CyberChef is released under the [Apache 2.0 Licence](https://www.apache.org/licenses/LICENSE-2.0) and is covered by [Crown Copyright](https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/).
|
||||
|
||||
|
||||
[1]: https://gchq.github.io/CyberChef
|
||||
|
||||
@@ -11,6 +11,7 @@ module.exports = function(api) {
|
||||
],
|
||||
"plugins": [
|
||||
"dynamic-import-node",
|
||||
"@babel/plugin-syntax-import-assertions",
|
||||
[
|
||||
"babel-plugin-transform-builtin-extend", {
|
||||
"globals": ["Error"]
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
"start_process": true,
|
||||
"server_path": "./node_modules/.bin/chromedriver",
|
||||
"port": 9515,
|
||||
"log_path": false
|
||||
"log_path": "tests/browser/output"
|
||||
},
|
||||
"desiredCapabilities": {
|
||||
"browserName": "chrome"
|
||||
}
|
||||
},
|
||||
"enable_fail_fast": true
|
||||
},
|
||||
|
||||
"dev": {
|
||||
|
||||
33984
package-lock.json
generated
33984
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
201
package.json
201
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cyberchef",
|
||||
"version": "9.16.1",
|
||||
"version": "9.37.1",
|
||||
"description": "The Cyber Swiss Army Knife for encryption, encoding, compression and data analysis.",
|
||||
"author": "n1474335 <n1474335@gmail.com>",
|
||||
"homepage": "https://gchq.github.io/CyberChef",
|
||||
@@ -27,143 +27,154 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/gchq/CyberChef/"
|
||||
},
|
||||
"main": "src/node/cjs.js",
|
||||
"module": "src/node/index.mjs",
|
||||
"main": "src/node/wrapper.js",
|
||||
"exports": {
|
||||
"import": "./src/node/index.mjs",
|
||||
"require": "./src/node/wrapper.js"
|
||||
},
|
||||
"bugs": "https://github.com/gchq/CyberChef/issues",
|
||||
"browserslist": [
|
||||
"Chrome >= 50",
|
||||
"Firefox >= 38",
|
||||
"node >= 10"
|
||||
"node >= 16"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.5",
|
||||
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||
"@babel/preset-env": "^7.7.6",
|
||||
"autoprefixer": "^9.7.3",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.0",
|
||||
"chromedriver": "^80.0.1",
|
||||
"@babel/core": "^7.17.8",
|
||||
"@babel/eslint-parser": "^7.17.0",
|
||||
"@babel/plugin-syntax-import-assertions": "^7.16.7",
|
||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||
"@babel/preset-env": "^7.16.11",
|
||||
"@babel/runtime": "^7.17.8",
|
||||
"autoprefixer": "^10.4.4",
|
||||
"babel-loader": "^8.2.4",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"chromedriver": "^99.0.0",
|
||||
"cli-progress": "^3.10.0",
|
||||
"colors": "^1.4.0",
|
||||
"copy-webpack-plugin": "^5.0.5",
|
||||
"css-loader": "^3.2.1",
|
||||
"eslint": "^6.7.2",
|
||||
"exports-loader": "^0.7.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-accessibility": "~6.0.0",
|
||||
"copy-webpack-plugin": "^10.2.4",
|
||||
"core-js": "^3.21.1",
|
||||
"css-loader": "6.7.1",
|
||||
"eslint": "^8.12.0",
|
||||
"grunt": "^1.4.1",
|
||||
"grunt-chmod": "~1.1.1",
|
||||
"grunt-concurrent": "^3.0.0",
|
||||
"grunt-contrib-clean": "~2.0.0",
|
||||
"grunt-contrib-connect": "^2.1.0",
|
||||
"grunt-contrib-connect": "^3.0.0",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-eslint": "^22.0.0",
|
||||
"grunt-eslint": "^24.0.0",
|
||||
"grunt-exec": "~3.0.0",
|
||||
"grunt-webpack": "^3.1.3",
|
||||
"grunt-webpack": "^5.0.0",
|
||||
"grunt-zip": "^0.18.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"imports-loader": "^0.8.0",
|
||||
"mini-css-extract-plugin": "^0.8.0",
|
||||
"nightwatch": "^1.3.4",
|
||||
"node-sass": "^4.13.0",
|
||||
"postcss-css-variables": "^0.14.0",
|
||||
"postcss-import": "^12.0.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"sitemap": "^5.1.0",
|
||||
"style-loader": "^1.0.1",
|
||||
"svg-url-loader": "^3.0.3",
|
||||
"url-loader": "^3.0.0",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-bundle-analyzer": "^3.6.0",
|
||||
"webpack-dev-server": "^3.9.0",
|
||||
"webpack-node-externals": "^1.7.2",
|
||||
"worker-loader": "^2.0.0"
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"imports-loader": "^3.1.1",
|
||||
"mini-css-extract-plugin": "2.6.0",
|
||||
"nightwatch": "^2.0.10",
|
||||
"postcss": "^8.4.12",
|
||||
"postcss-css-variables": "^0.18.0",
|
||||
"postcss-import": "^14.1.0",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"prompt": "^1.2.2",
|
||||
"sass-loader": "^12.6.0",
|
||||
"sitemap": "^7.1.1",
|
||||
"terser": "^5.12.1",
|
||||
"webpack": "^5.70.0",
|
||||
"webpack-bundle-analyzer": "^4.5.0",
|
||||
"webpack-dev-server": "4.7.4",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
"worker-loader": "^3.0.8"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.7.0",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"arrive": "^2.4.1",
|
||||
"avsc": "^5.4.16",
|
||||
"avsc": "^5.7.3",
|
||||
"babel-plugin-transform-builtin-extend": "1.1.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"blakejs": "^1.1.0",
|
||||
"bootstrap": "4.4.1",
|
||||
"bootstrap-colorpicker": "^3.2.0",
|
||||
"bootstrap-material-design": "^4.1.2",
|
||||
"bson": "^4.0.2",
|
||||
"bignumber.js": "^9.0.2",
|
||||
"blakejs": "^1.2.1",
|
||||
"bootstrap": "4.6.1",
|
||||
"bootstrap-colorpicker": "^3.4.0",
|
||||
"bootstrap-material-design": "^4.1.3",
|
||||
"browserify-zlib": "^0.2.0",
|
||||
"bson": "^4.6.2",
|
||||
"buffer": "^6.0.3",
|
||||
"cbor": "8.1.0",
|
||||
"chi-squared": "^1.1.0",
|
||||
"codepage": "^1.14.0",
|
||||
"core-js": "^3.4.8",
|
||||
"codepage": "^1.15.0",
|
||||
"crypto-api": "^0.8.5",
|
||||
"crypto-js": "^3.1.9-1",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"ctph.js": "0.0.5",
|
||||
"d3": "^5.14.2",
|
||||
"d3": "7.3.0",
|
||||
"d3-hexbin": "^0.2.2",
|
||||
"diff": "^4.0.1",
|
||||
"es6-promisify": "^6.0.2",
|
||||
"escodegen": "^1.12.0",
|
||||
"esm": "^3.2.25",
|
||||
"esmangle": "^1.0.1",
|
||||
"diff": "^5.0.0",
|
||||
"es6-promisify": "^7.0.0",
|
||||
"escodegen": "^2.0.0",
|
||||
"esprima": "^4.0.1",
|
||||
"exif-parser": "^0.1.12",
|
||||
"file-saver": "^2.0.2",
|
||||
"geodesy": "^1.1.3",
|
||||
"highlight.js": "^9.16.2",
|
||||
"jimp": "^0.9.3",
|
||||
"jquery": "3.4.1",
|
||||
"file-saver": "^2.0.5",
|
||||
"flat": "^5.0.2",
|
||||
"geodesy": "1.1.3",
|
||||
"highlight.js": "^11.5.0",
|
||||
"jimp": "^0.16.1",
|
||||
"jquery": "3.6.0",
|
||||
"js-crc": "^0.2.0",
|
||||
"js-sha3": "^0.8.0",
|
||||
"jsesc": "^2.5.2",
|
||||
"jsonpath": "^1.0.2",
|
||||
"jsesc": "^3.0.2",
|
||||
"jsonpath": "^1.1.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"jsqr": "^1.2.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"kbpgp": "2.1.6",
|
||||
"jsqr": "^1.4.0",
|
||||
"jsrsasign": "^10.5.14",
|
||||
"kbpgp": "2.1.15",
|
||||
"libbzip2-wasm": "0.0.4",
|
||||
"libyara-wasm": "^1.0.1",
|
||||
"lodash": "^4.17.15",
|
||||
"loglevel": "^1.6.6",
|
||||
"libyara-wasm": "^1.1.0",
|
||||
"lodash": "^4.17.21",
|
||||
"loglevel": "^1.8.0",
|
||||
"loglevel-message-prefix": "^3.0.0",
|
||||
"markdown-it": "^10.0.0",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.27",
|
||||
"markdown-it": "^12.3.2",
|
||||
"moment": "^2.29.1",
|
||||
"moment-timezone": "^0.5.34",
|
||||
"ngeohash": "^0.6.3",
|
||||
"node-forge": "^0.9.1",
|
||||
"node-forge": "^1.3.0",
|
||||
"node-md6": "^0.1.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"nodom": "^2.4.0",
|
||||
"notepack.io": "^2.2.0",
|
||||
"notepack.io": "^2.3.0",
|
||||
"nwmatcher": "^1.4.4",
|
||||
"otp": "^0.1.3",
|
||||
"popper.js": "^1.16.0",
|
||||
"otp": "0.1.3",
|
||||
"path": "^0.12.7",
|
||||
"popper.js": "^1.16.1",
|
||||
"process": "^0.11.10",
|
||||
"protobufjs": "^6.11.2",
|
||||
"qr-image": "^3.2.0",
|
||||
"scryptsy": "^2.1.0",
|
||||
"snackbarjs": "^1.1.0",
|
||||
"sortablejs": "^1.10.1",
|
||||
"split.js": "^1.5.11",
|
||||
"ssdeep.js": "0.0.2",
|
||||
"tesseract.js": "^2.0.0-alpha.15",
|
||||
"ua-parser-js": "^0.7.20",
|
||||
"sortablejs": "^1.15.0",
|
||||
"split.js": "^1.6.5",
|
||||
"ssdeep.js": "0.0.3",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"tesseract.js": "2.1.5",
|
||||
"ua-parser-js": "^1.0.2",
|
||||
"unorm": "^1.6.0",
|
||||
"utf8": "^3.0.0",
|
||||
"vkbeautify": "^0.99.3",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "0.0.27",
|
||||
"xregexp": "^4.2.4",
|
||||
"xmldom": "^0.6.0",
|
||||
"xpath": "0.0.32",
|
||||
"xregexp": "^5.1.0",
|
||||
"zlibjs": "^0.3.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "grunt dev",
|
||||
"build": "grunt prod",
|
||||
"repl": "node src/node/repl.js",
|
||||
"test": "grunt test",
|
||||
"test-node-consumer": "grunt testnodeconsumer",
|
||||
"testui": "grunt testui",
|
||||
"start": "npx grunt dev",
|
||||
"build": "npx grunt prod",
|
||||
"repl": "node --experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings src/node/repl.mjs",
|
||||
"test": "npx grunt configTests && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation tests/node/index.mjs && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation tests/operations/index.mjs",
|
||||
"testnodeconsumer": "npx grunt testnodeconsumer",
|
||||
"testui": "npx grunt testui",
|
||||
"testuidev": "npx nightwatch --env=dev",
|
||||
"lint": "grunt lint",
|
||||
"newop": "node --experimental-modules src/core/config/scripts/newOperation.mjs"
|
||||
"lint": "npx grunt lint",
|
||||
"postinstall": "npx grunt exec:fixCryptoApiImports",
|
||||
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
|
||||
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
|
||||
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class Chef {
|
||||
*/
|
||||
async bake(input, recipeConfig, options) {
|
||||
log.debug("Chef baking");
|
||||
const startTime = new Date().getTime(),
|
||||
const startTime = Date.now(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
containsFc = recipe.containsFlowControl(),
|
||||
notUTF8 = options && "treatAsUtf8" in options && !options.treatAsUtf8;
|
||||
@@ -84,7 +84,7 @@ class Chef {
|
||||
result: await this.dish.get(returnType, notUTF8),
|
||||
type: Dish.enumLookup(this.dish.type),
|
||||
progress: progress,
|
||||
duration: new Date().getTime() - startTime,
|
||||
duration: Date.now() - startTime,
|
||||
error: error
|
||||
};
|
||||
}
|
||||
@@ -110,7 +110,7 @@ class Chef {
|
||||
silentBake(recipeConfig) {
|
||||
log.debug("Running silent bake");
|
||||
|
||||
const startTime = new Date().getTime(),
|
||||
const startTime = Date.now(),
|
||||
recipe = new Recipe(recipeConfig),
|
||||
dish = new Dish();
|
||||
|
||||
@@ -119,7 +119,7 @@ class Chef {
|
||||
} catch (err) {
|
||||
// Suppress all errors
|
||||
}
|
||||
return new Date().getTime() - startTime;
|
||||
return Date.now() - startTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +146,12 @@ class Chef {
|
||||
const func = direction === "forward" ? highlights[i].f : highlights[i].b;
|
||||
|
||||
if (typeof func == "function") {
|
||||
pos = func(pos, highlights[i].args);
|
||||
try {
|
||||
pos = func(pos, highlights[i].args);
|
||||
} catch (err) {
|
||||
// Throw away highlighting errors
|
||||
pos = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import Chef from "./Chef.mjs";
|
||||
import OperationConfig from "./config/OperationConfig.json";
|
||||
import OperationConfig from "./config/OperationConfig.json" assert {type: "json"};
|
||||
import OpModules from "./config/modules/OpModules.mjs";
|
||||
|
||||
// Add ">" to the start of all log messages in the Chef Worker
|
||||
@@ -212,7 +212,7 @@ self.loadRequiredModules = function(recipeConfig) {
|
||||
if (!(module in OpModules)) {
|
||||
log.info(`Loading ${module} module`);
|
||||
self.sendStatusMessage(`Loading ${module} module`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`);
|
||||
self.importScripts(`${self.docURL}/modules/${module}.js`); // lgtm [js/client-side-unvalidated-url-redirection]
|
||||
self.sendStatusMessage("");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,7 +207,7 @@ class Dish {
|
||||
const data = new Uint8Array(this.value.slice(0, 2048)),
|
||||
types = detectFileType(data);
|
||||
|
||||
if (!types.length || !types[0].mime || !types[0].mime === "text/plain") {
|
||||
if (!types.length || !types[0].mime || !(types[0].mime === "text/plain")) {
|
||||
return null;
|
||||
} else {
|
||||
return types[0].mime;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationConfig from "./config/OperationConfig.json";
|
||||
import OperationConfig from "./config/OperationConfig.json" assert {type: "json"};
|
||||
import OperationError from "./errors/OperationError.mjs";
|
||||
import Operation from "./Operation.mjs";
|
||||
import DishError from "./errors/DishError.mjs";
|
||||
@@ -46,7 +46,7 @@ class Recipe {
|
||||
module: OperationConfig[c.op].module,
|
||||
ingValues: c.args,
|
||||
breakpoint: c.breakpoint,
|
||||
disabled: c.disabled,
|
||||
disabled: c.disabled || c.op === "Comment",
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -170,13 +170,18 @@ class Utils {
|
||||
*
|
||||
* @param {string} str - The input string to display.
|
||||
* @param {boolean} [preserveWs=false] - Whether or not to print whitespace.
|
||||
* @param {boolean} [onlyAscii=false] - Whether or not to replace non ASCII characters.
|
||||
* @returns {string}
|
||||
*/
|
||||
static printable(str, preserveWs=false) {
|
||||
static printable(str, preserveWs=false, onlyAscii=false) {
|
||||
if (isWebEnvironment() && window.app && !window.app.options.treatAsUtf8) {
|
||||
str = Utils.byteArrayToChars(Utils.strToByteArray(str));
|
||||
}
|
||||
|
||||
if (onlyAscii) {
|
||||
return str.replace(/[^\x20-\x7f]/g, ".");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-misleading-character-class
|
||||
const re = /[\0-\x08\x0B-\x0C\x0E-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uD7FF\uE000-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/g;
|
||||
const wsRe = /[\x09-\x10\x0D\u2028\u2029]/g;
|
||||
@@ -704,8 +709,22 @@ class Utils {
|
||||
* Utils.stripHtmlTags("<div>Test</div>");
|
||||
*/
|
||||
static stripHtmlTags(htmlStr, removeScriptAndStyle=false) {
|
||||
/**
|
||||
* Recursively remove a pattern from a string until there are no more matches.
|
||||
* Avoids incomplete sanitization e.g. "aabcbc".replace(/abc/g, "") === "abc"
|
||||
*
|
||||
* @param {RegExp} pattern
|
||||
* @param {string} str
|
||||
* @returns {string}
|
||||
*/
|
||||
function recursiveRemove(pattern, str) {
|
||||
const newStr = str.replace(pattern, "");
|
||||
return newStr.length === str.length ? newStr : recursiveRemove(pattern, newStr);
|
||||
}
|
||||
|
||||
if (removeScriptAndStyle) {
|
||||
htmlStr = htmlStr.replace(/<(script|style)[^>]*>.*<\/(script|style)>/gmi, "");
|
||||
htmlStr = recursiveRemove(/<script[^>]*>.*?<\/script>/gi, htmlStr);
|
||||
htmlStr = recursiveRemove(/<style[^>]*>.*?<\/style>/gi, htmlStr);
|
||||
}
|
||||
return htmlStr.replace(/<[^>]+>/g, "");
|
||||
}
|
||||
@@ -729,11 +748,10 @@ class Utils {
|
||||
">": ">",
|
||||
'"': """,
|
||||
"'": "'", // ' not recommended because it's not in the HTML spec
|
||||
"/": "/", // forward slash is included as it helps end an HTML entity
|
||||
"`": "`"
|
||||
};
|
||||
|
||||
return str.replace(/[&<>"'/`]/g, function (match) {
|
||||
return str.replace(/[&<>"'`]/g, function (match) {
|
||||
return HTML_CHARS[match];
|
||||
});
|
||||
}
|
||||
@@ -878,7 +896,7 @@ class Utils {
|
||||
|
||||
while ((m = recipeRegex.exec(recipe))) {
|
||||
// Translate strings in args back to double-quotes
|
||||
args = m[2]
|
||||
args = m[2] // lgtm [js/incomplete-sanitization]
|
||||
.replace(/"/g, '\\"') // Escape double quotes
|
||||
.replace(/(^|,|{|:)'/g, '$1"') // Replace opening ' with "
|
||||
.replace(/([^\\]|(?:\\\\)+)'(,|:|}|$)/g, '$1"$2') // Replace closing ' with "
|
||||
@@ -1182,12 +1200,37 @@ class Utils {
|
||||
"CRLF": /\r\n/g,
|
||||
"Forward slash": /\//g,
|
||||
"Backslash": /\\/g,
|
||||
"0x with comma": /,?0x/g,
|
||||
"0x": /0x/g,
|
||||
"\\x": /\\x/g,
|
||||
"None": /\s+/g // Included here to remove whitespace when there shouldn't be any
|
||||
}[token];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate object in chunks of given size.
|
||||
*
|
||||
* @param {Iterable} iterable
|
||||
* @param {number} chunksize
|
||||
*/
|
||||
static* chunked(iterable, chunksize) {
|
||||
const iterator = iterable[Symbol.iterator]();
|
||||
while (true) {
|
||||
const res = [];
|
||||
for (let i = 0; i < chunksize; i++) {
|
||||
const next = iterator.next();
|
||||
if (next.done) {
|
||||
break;
|
||||
}
|
||||
res.push(next.value);
|
||||
}
|
||||
if (res.length) {
|
||||
yield res;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1335,7 +1378,7 @@ export function sendStatusMessage(msg) {
|
||||
self.sendStatusMessage(msg);
|
||||
else if (isWebEnvironment())
|
||||
app.alert(msg, 10000);
|
||||
else if (isNodeEnvironment())
|
||||
else if (isNodeEnvironment() && !global.TESTING)
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(msg);
|
||||
}
|
||||
|
||||
38
src/core/config/Categories.json
Executable file → Normal file
38
src/core/config/Categories.json
Executable file → Normal file
@@ -18,15 +18,17 @@
|
||||
"From Binary",
|
||||
"To Octal",
|
||||
"From Octal",
|
||||
"To Base64",
|
||||
"From Base64",
|
||||
"Show Base64 offsets",
|
||||
"To Base32",
|
||||
"From Base32",
|
||||
"To Base45",
|
||||
"From Base45",
|
||||
"To Base58",
|
||||
"From Base58",
|
||||
"To Base62",
|
||||
"From Base62",
|
||||
"To Base64",
|
||||
"From Base64",
|
||||
"Show Base64 offsets",
|
||||
"To Base85",
|
||||
"From Base85",
|
||||
"To Base",
|
||||
@@ -61,7 +63,9 @@
|
||||
"Parse TLV",
|
||||
"CSV to JSON",
|
||||
"JSON to CSV",
|
||||
"Avro to JSON"
|
||||
"Avro to JSON",
|
||||
"CBOR Encode",
|
||||
"CBOR Decode"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -79,6 +83,8 @@
|
||||
"RC2 Decrypt",
|
||||
"RC4",
|
||||
"RC4 Drop",
|
||||
"SM4 Encrypt",
|
||||
"SM4 Decrypt",
|
||||
"ROT13",
|
||||
"ROT47",
|
||||
"XOR",
|
||||
@@ -116,7 +122,8 @@
|
||||
"Multiple Bombe",
|
||||
"Typex",
|
||||
"Lorenz",
|
||||
"Colossus"
|
||||
"Colossus",
|
||||
"SIGABA"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -134,6 +141,11 @@
|
||||
"PGP Verify",
|
||||
"PGP Encrypt and Sign",
|
||||
"PGP Decrypt and Verify",
|
||||
"Generate RSA Key Pair",
|
||||
"RSA Sign",
|
||||
"RSA Verify",
|
||||
"RSA Encrypt",
|
||||
"RSA Decrypt",
|
||||
"Parse SSH Host Key"
|
||||
]
|
||||
},
|
||||
@@ -184,8 +196,13 @@
|
||||
"URL Encode",
|
||||
"URL Decode",
|
||||
"Protobuf Decode",
|
||||
"Protobuf Encode",
|
||||
"VarInt Encode",
|
||||
"VarInt Decode",
|
||||
"JA3 Fingerprint",
|
||||
"JA3S Fingerprint",
|
||||
"HASSH Client Fingerprint",
|
||||
"HASSH Server Fingerprint",
|
||||
"Format MAC addresses",
|
||||
"Change IP format",
|
||||
"Group IP addresses",
|
||||
@@ -200,8 +217,10 @@
|
||||
"ops": [
|
||||
"Encode text",
|
||||
"Decode text",
|
||||
"Unicode Text Format",
|
||||
"Remove Diacritics",
|
||||
"Unescape Unicode Characters"
|
||||
"Unescape Unicode Characters",
|
||||
"Convert to NATO alphabet"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -216,6 +235,7 @@
|
||||
"From Case Insensitive Regex",
|
||||
"Add line numbers",
|
||||
"Remove line numbers",
|
||||
"Get All Casings",
|
||||
"To Table",
|
||||
"Reverse",
|
||||
"Sort",
|
||||
@@ -231,6 +251,7 @@
|
||||
"Pad lines",
|
||||
"Find / Replace",
|
||||
"Regular expression",
|
||||
"Fuzzy Match",
|
||||
"Offset checker",
|
||||
"Hamming Distance",
|
||||
"Convert distance",
|
||||
@@ -241,6 +262,7 @@
|
||||
"Convert co-ordinate format",
|
||||
"Show on map",
|
||||
"Parse UNIX file permissions",
|
||||
"Parse ObjectID timestamp",
|
||||
"Swap endianness",
|
||||
"Parse colour code",
|
||||
"Escape string",
|
||||
@@ -259,6 +281,7 @@
|
||||
"Windows Filetime to UNIX Timestamp",
|
||||
"UNIX Timestamp to Windows Filetime",
|
||||
"Extract dates",
|
||||
"Get Time",
|
||||
"Sleep"
|
||||
]
|
||||
},
|
||||
@@ -278,6 +301,7 @@
|
||||
"JPath expression",
|
||||
"CSS selector",
|
||||
"Extract EXIF",
|
||||
"Extract ID3",
|
||||
"Extract Files"
|
||||
]
|
||||
},
|
||||
@@ -311,6 +335,7 @@
|
||||
"SHA1",
|
||||
"SHA2",
|
||||
"SHA3",
|
||||
"SM3",
|
||||
"Keccak",
|
||||
"Shake",
|
||||
"RIPEMD",
|
||||
@@ -395,6 +420,7 @@
|
||||
"ops": [
|
||||
"Render Image",
|
||||
"Play Media",
|
||||
"Generate Image",
|
||||
"Optical Character Recognition",
|
||||
"Remove EXIF",
|
||||
"Extract EXIF",
|
||||
|
||||
@@ -42,13 +42,10 @@ for (const opObj in Ops) {
|
||||
outputType: op.presentType,
|
||||
flowControl: op.flowControl,
|
||||
manualBake: op.manualBake,
|
||||
args: op.args
|
||||
args: op.args,
|
||||
checks: op.checks
|
||||
};
|
||||
|
||||
if ("patterns" in op) {
|
||||
operationConfig[op.name].patterns = op.patterns;
|
||||
}
|
||||
|
||||
if (!(op.module in modules))
|
||||
modules[op.module] = {};
|
||||
modules[op.module][op.name] = opObj;
|
||||
|
||||
@@ -121,7 +121,7 @@ prompt.get(schema, (err, result) => {
|
||||
|
||||
const moduleName = result.opName.replace(/\w\S*/g, txt => {
|
||||
return txt.charAt(0).toUpperCase() + txt.substr(1);
|
||||
}).replace(/[\s-()/./]/g, "");
|
||||
}).replace(/[\s-()./]/g, "");
|
||||
|
||||
|
||||
const template = `/**
|
||||
|
||||
@@ -17,7 +17,7 @@ class DishJSON extends DishType {
|
||||
*/
|
||||
static toArrayBuffer() {
|
||||
DishJSON.checkForValue(this.value);
|
||||
this.value = this.value ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
|
||||
this.value = this.value !== undefined ? Utils.strToArrayBuffer(JSON.stringify(this.value, null, 4)) : new ArrayBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import OperationError from "./OperationError.mjs";
|
||||
import DishError from "./DishError.mjs";
|
||||
import ExcludedOperationError from "./ExcludedOperationError";
|
||||
import ExcludedOperationError from "./ExcludedOperationError.mjs";
|
||||
|
||||
export {
|
||||
OperationError,
|
||||
|
||||
27
src/core/lib/Base45.mjs
Normal file
27
src/core/lib/Base45.mjs
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Base45 resources.
|
||||
*
|
||||
* @author Thomas Weißschuh [thomas@t-8ch.de]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Highlight to Base45
|
||||
*/
|
||||
export function highlightToBase45(pos, args) {
|
||||
pos[0].start = Math.floor(pos[0].start / 2) * 3;
|
||||
pos[0].end = Math.ceil(pos[0].end / 2) * 3;
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight from Base45
|
||||
*/
|
||||
export function highlightFromBase45(pos, args) {
|
||||
pos[0].start = Math.floor(pos[0].start / 3) * 2;
|
||||
pos[0].end = Math.ceil(pos[0].end / 3) * 2;
|
||||
return pos;
|
||||
}
|
||||
|
||||
export const ALPHABET = "0-9A-Z $%*+\\-./:";
|
||||
@@ -148,4 +148,8 @@ export const ALPHABET_OPTIONS = [
|
||||
{name: "BinHex: !-,-0-689@A-NP-VX-Z[`a-fh-mp-r", value: "!-,-0-689@A-NP-VX-Z[`a-fh-mp-r"},
|
||||
{name: "ROT13: N-ZA-Mn-za-m0-9+/=", value: "N-ZA-Mn-za-m0-9+/="},
|
||||
{name: "UNIX crypt: ./0-9A-Za-z", value: "./0-9A-Za-z"},
|
||||
{name: "Atom128: /128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", value: "/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC"},
|
||||
{name: "Megan35: 3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", value: "3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"},
|
||||
{name: "Zong22: ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", value: "ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2"},
|
||||
{name: "Hazz15: HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", value: "HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5"}
|
||||
];
|
||||
|
||||
@@ -32,9 +32,9 @@ export const ALPHABET_OPTIONS = [
|
||||
* @returns {string}
|
||||
*/
|
||||
export function alphabetName(alphabet) {
|
||||
alphabet = alphabet.replace("'", "'");
|
||||
alphabet = alphabet.replace("\"", """);
|
||||
alphabet = alphabet.replace("\\", "\");
|
||||
alphabet = alphabet.replace(/'/g, "'");
|
||||
alphabet = alphabet.replace(/"/g, """);
|
||||
alphabet = alphabet.replace(/\\/g, "\");
|
||||
let name;
|
||||
|
||||
ALPHABET_OPTIONS.forEach(function(a) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -58,6 +59,9 @@ export function toBinary(data, delim="Space", padding=8) {
|
||||
* fromBinary("00010000:00100000:00110000", "Colon");
|
||||
*/
|
||||
export function fromBinary(data, delim="Space", byteLen=8) {
|
||||
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
|
||||
throw new OperationError("Byte length must be a positive integer");
|
||||
|
||||
const delimRegex = Utils.regexRep(delim);
|
||||
data = data.replace(delimRegex, "");
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ export function bitOp (input, key, func, nullPreserving, scheme) {
|
||||
!(nullPreserving && (o === 0 || o === k))) {
|
||||
switch (scheme) {
|
||||
case "Input differential":
|
||||
key[i % key.length] = x;
|
||||
key[i % key.length] = o;
|
||||
break;
|
||||
case "Output differential":
|
||||
key[i % key.length] = o;
|
||||
key[i % key.length] = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
const crypto = {};
|
||||
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
|
||||
/* dojo-release-1.8.1/dojo/_base/lang.js.uncompressed.js */
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* @constant
|
||||
@@ -86,8 +87,8 @@ export function getScatterValues(input, recordDelimiter, fieldDelimiter, columnH
|
||||
}
|
||||
|
||||
values = values.map(row => {
|
||||
const x = parseFloat(row[0], 10),
|
||||
y = parseFloat(row[1], 10);
|
||||
const x = parseFloat(row[0]),
|
||||
y = parseFloat(row[1]);
|
||||
|
||||
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
||||
@@ -121,14 +122,14 @@ export function getScatterValuesWithColour(input, recordDelimiter, fieldDelimite
|
||||
}
|
||||
|
||||
values = values.map(row => {
|
||||
const x = parseFloat(row[0], 10),
|
||||
y = parseFloat(row[1], 10),
|
||||
const x = parseFloat(row[0]),
|
||||
y = parseFloat(row[1]),
|
||||
colour = row[2];
|
||||
|
||||
if (Number.isNaN(x)) throw new OperationError("Values must be numbers in base 10.");
|
||||
if (Number.isNaN(y)) throw new OperationError("Values must be numbers in base 10.");
|
||||
|
||||
return [x, y, colour];
|
||||
return [x, y, Utils.escapeHtml(colour)];
|
||||
});
|
||||
|
||||
return { headings, values };
|
||||
@@ -157,7 +158,7 @@ export function getSeriesValues(input, recordDelimiter, fieldDelimiter, columnHe
|
||||
values.forEach(row => {
|
||||
const serie = row[0],
|
||||
xVal = row[1],
|
||||
val = parseFloat(row[2], 10);
|
||||
val = parseFloat(row[2]);
|
||||
|
||||
if (Number.isNaN(val)) throw new OperationError("Values must be numbers in base 10.");
|
||||
|
||||
|
||||
9
src/core/lib/Crypt.mjs
Normal file
9
src/core/lib/Crypt.mjs
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Crypt resources.
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
export const cryptNotice = "WARNING: Cryptographic operations in CyberChef should not be relied upon to provide security in any situation. No guarantee is offered for their correctness. We advise you not to use keys generated from CyberChef in operational contexts.";
|
||||
@@ -468,6 +468,34 @@ export const FILE_SIGNATURES = {
|
||||
],
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Targa Image",
|
||||
extension: "tga",
|
||||
mime: "image/x-targa",
|
||||
description: "",
|
||||
signature: [
|
||||
{ // This signature is not at the beginning of the file. The extractor works backwards.
|
||||
0: 0x54,
|
||||
1: 0x52,
|
||||
2: 0x55,
|
||||
3: 0x45,
|
||||
4: 0x56,
|
||||
5: 0x49,
|
||||
6: 0x53,
|
||||
7: 0x49,
|
||||
8: 0x4f,
|
||||
9: 0x4e,
|
||||
10: 0x2d,
|
||||
11: 0x58,
|
||||
12: 0x46,
|
||||
13: 0x49,
|
||||
14: 0x4c,
|
||||
15: 0x45,
|
||||
16: 0x2e
|
||||
}
|
||||
],
|
||||
extractor: extractTARGA
|
||||
}
|
||||
],
|
||||
"Video": [
|
||||
{ // Place before webm
|
||||
@@ -780,7 +808,7 @@ export const FILE_SIGNATURES = {
|
||||
1: 0xfb
|
||||
}
|
||||
],
|
||||
extractor: null
|
||||
extractor: extractMP3
|
||||
},
|
||||
{
|
||||
name: "MPEG-4 Part 14 audio",
|
||||
@@ -1724,6 +1752,25 @@ export const FILE_SIGNATURES = {
|
||||
},
|
||||
extractor: null
|
||||
},
|
||||
{
|
||||
name: "Jar Archive",
|
||||
extension: "jar",
|
||||
mime: "application/java-archive",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x50,
|
||||
1: 0x4B,
|
||||
2: 0x03,
|
||||
3: 0x04,
|
||||
4: 0x14,
|
||||
5: 0x00,
|
||||
6: 0x08,
|
||||
7: 0x00,
|
||||
8: 0x08,
|
||||
9: 0x00
|
||||
},
|
||||
extractor: extractZIP
|
||||
},
|
||||
{
|
||||
name: "lzop compressed",
|
||||
extension: "lzop,lzo",
|
||||
@@ -1742,7 +1789,7 @@ export const FILE_SIGNATURES = {
|
||||
extractor: extractLZOP
|
||||
},
|
||||
{
|
||||
name: "Linux deb",
|
||||
name: "Linux deb package",
|
||||
extension: "deb",
|
||||
mime: "application/vnd.debian.binary-package",
|
||||
description: "",
|
||||
@@ -1755,7 +1802,7 @@ export const FILE_SIGNATURES = {
|
||||
5: 0x68,
|
||||
6: 0x3e
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractDEB
|
||||
},
|
||||
{
|
||||
name: "Apple Disk Image",
|
||||
@@ -2150,14 +2197,14 @@ export const FILE_SIGNATURES = {
|
||||
mime: "application/octet-stream",
|
||||
description: "",
|
||||
signature: {
|
||||
0: 0x6b, // keych
|
||||
0: 0x6b, // kych
|
||||
1: 0x79,
|
||||
2: 0x63,
|
||||
3: 0x68,
|
||||
4: 0x00,
|
||||
5: 0x01
|
||||
},
|
||||
extractor: null
|
||||
extractor: extractMacOSXKeychain
|
||||
},
|
||||
{
|
||||
name: "TCP Packet",
|
||||
@@ -2308,6 +2355,12 @@ export const FILE_SIGNATURES = {
|
||||
1: 0x03,
|
||||
2: 0xc6,
|
||||
3: 0x04
|
||||
},
|
||||
{
|
||||
0: 0x95,
|
||||
1: 0x05,
|
||||
2: 0x86,
|
||||
3: 0x04
|
||||
}
|
||||
],
|
||||
extractor: null
|
||||
@@ -3028,6 +3081,90 @@ export function extractICO(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TARGA extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
*/
|
||||
export function extractTARGA(bytes, offset) {
|
||||
// Need all the bytes since we do not know how far up the image goes.
|
||||
const stream = new Stream(bytes);
|
||||
stream.moveTo(offset - 8);
|
||||
|
||||
// Read in the offsets of the possible areas.
|
||||
const extensionOffset = stream.readInt(4, "le");
|
||||
const developerOffset = stream.readInt(4, "le");
|
||||
|
||||
stream.moveBackwardsBy(8);
|
||||
|
||||
/**
|
||||
* Moves backwards in the stream until it meet bytes that are the same as the amount of bytes moved.
|
||||
*
|
||||
* @param {number} sizeOfSize
|
||||
* @param {number} maxSize
|
||||
*/
|
||||
function moveBackwardsUntilSize(maxSize, sizeOfSize) {
|
||||
for (let i = 0; i < maxSize; i++) {
|
||||
stream.moveBackwardsBy(1);
|
||||
|
||||
// Read in sizeOfSize amount of bytes in.
|
||||
const size = stream.readInt(sizeOfSize, "le") - 1;
|
||||
stream.moveBackwardsBy(sizeOfSize);
|
||||
|
||||
// If the size matches.
|
||||
if (size === i)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves backwards in the stream until we meet bytes(when calculated) that are the same as the amount of bytes moved.
|
||||
*/
|
||||
function moveBackwardsUntilImageSize() {
|
||||
stream.moveBackwardsBy(5);
|
||||
|
||||
// The documentation said that 0x100000 was the largest the file could be.
|
||||
for (let i = 0; i < 0x100000; i++) {
|
||||
|
||||
// (Height * Width * pixel depth in bits)/8
|
||||
const total = (stream.readInt(2, "le") * stream.readInt(2, "le") * stream.readInt(1))/8;
|
||||
if (total === i-1)
|
||||
break;
|
||||
|
||||
stream.moveBackwardsBy(6);
|
||||
}
|
||||
}
|
||||
|
||||
if (extensionOffset || developerOffset) {
|
||||
if (extensionOffset) {
|
||||
// Size is stored in two bytes hence the maximum is 0xffff.
|
||||
moveBackwardsUntilSize(0xffff, 2);
|
||||
|
||||
// Move to where we think the start of the file is.
|
||||
stream.moveBackwardsBy(extensionOffset);
|
||||
} else if (developerOffset) {
|
||||
// Size is stored in 4 bytes hence the maxiumum is 0xffffffff.
|
||||
moveBackwardsUntilSize(0xffffffff, 4);
|
||||
|
||||
// Size is stored in byte position 6 so have to move back.
|
||||
stream.moveBackwardsBy(6);
|
||||
|
||||
// Move to where we think the start of the file is.
|
||||
stream.moveBackwardsBy(developerOffset);
|
||||
}
|
||||
} else {
|
||||
// Move backwards until size === number of bytes passed.
|
||||
moveBackwardsUntilImageSize();
|
||||
|
||||
// Move backwards over the reaminder of the header + the 5 we borrowed in moveBackwardsUntilImageSize().
|
||||
stream.moveBackwardsBy(0xc+5);
|
||||
}
|
||||
|
||||
return stream.carve(stream.position, offset+0x12);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* WAV extractor.
|
||||
*
|
||||
@@ -3048,6 +3185,79 @@ export function extractWAV(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MP3 extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {Number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractMP3(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Constants for flag byte.
|
||||
const bitRateIndexes = ["free", 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, "bad"];
|
||||
|
||||
const samplingRateFrequencyIndex = [44100, 48000, 32000, "reserved"];
|
||||
|
||||
// ID3 tag, move over it.
|
||||
if ((stream.getBytes(3).toString() === [0x49, 0x44, 0x33].toString())) {
|
||||
stream.moveTo(6);
|
||||
const tagSize = (stream.readInt(1) << 21) | (stream.readInt(1) << 14) | (stream.readInt(1) << 7) | stream.readInt(1);
|
||||
stream.moveForwardsBy(tagSize);
|
||||
} else {
|
||||
stream.moveTo(0);
|
||||
}
|
||||
|
||||
// Loop over all the frame headers in the file.
|
||||
while (stream.hasMore()) {
|
||||
|
||||
// If it has an old TAG frame at the end of it, fixed size, 128 bytes.
|
||||
if (stream.getBytes(3) === [0x54, 0x41, 0x47].toString()) {
|
||||
stream.moveForwardsBy(125);
|
||||
break;
|
||||
}
|
||||
|
||||
// If not start of frame.
|
||||
if (stream.getBytes(2).toString() !== [0xff, 0xfb].toString()) {
|
||||
stream.moveBackwardsBy(2);
|
||||
break;
|
||||
}
|
||||
|
||||
// Read flag byte.
|
||||
const flags = stream.readInt(1);
|
||||
|
||||
// Extract frame bit rate from flag byte.
|
||||
const bitRate = bitRateIndexes[flags >> 4];
|
||||
|
||||
// Extract frame sample rate from flag byte.
|
||||
const sampleRate = samplingRateFrequencyIndex[(flags & 0x0f) >> 2];
|
||||
|
||||
// Padding if the frame size is not a multiple of the bitrate.
|
||||
const padding = (flags & 0x02) >> 1;
|
||||
|
||||
// Things that are either not standard or undocumented.
|
||||
if (bitRate === "free" || bitRate === "bad" || sampleRate === "reserved") {
|
||||
stream.moveBackwardsBy(1);
|
||||
break;
|
||||
}
|
||||
|
||||
// Formula: FrameLength = (144 * BitRate / SampleRate ) + Padding
|
||||
const frameSize = Math.floor(((144 * bitRate) / sampleRate) + padding);
|
||||
|
||||
// If the next move goes past the end of the bytestream then extract the entire bytestream.
|
||||
// We assume complete frames in the above formula because there is no field that suggests otherwise.
|
||||
if ((stream.position + frameSize) > stream.length) {
|
||||
stream.moveTo(stream.length);
|
||||
break;
|
||||
} else {
|
||||
stream.moveForwardsBy(frameSize - 3);
|
||||
}
|
||||
}
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FLV extractor.
|
||||
*
|
||||
@@ -3202,6 +3412,26 @@ export function extractPListXML(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MacOS X Keychain Extactor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {number} offset
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
export function extractMacOSXKeychain(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Move to size field.
|
||||
stream.moveTo(0x14);
|
||||
|
||||
// Move forwards by size.
|
||||
stream.moveForwardsBy(stream.readInt(4));
|
||||
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* OLE2 extractor.
|
||||
*
|
||||
@@ -3448,6 +3678,37 @@ export function extractXZ(bytes, offset) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DEB extractor.
|
||||
*
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {Number} offset
|
||||
*/
|
||||
export function extractDEB(bytes, offset) {
|
||||
const stream = new Stream(bytes.slice(offset));
|
||||
|
||||
// Move past !<arch>
|
||||
stream.moveForwardsBy(8);
|
||||
while (stream.hasMore()) {
|
||||
|
||||
// Move to size field.
|
||||
stream.moveForwardsBy(48);
|
||||
let fsize= "";
|
||||
|
||||
// Convert size to a usable number.
|
||||
for (const elem of stream.getBytes(10)) {
|
||||
fsize += String.fromCharCode(elem);
|
||||
}
|
||||
fsize = parseInt(fsize.trim(), 10);
|
||||
|
||||
// Move past `\n
|
||||
stream.moveForwardsBy(2);
|
||||
stream.moveForwardsBy(fsize);
|
||||
}
|
||||
return stream.carve();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ELF extractor.
|
||||
*
|
||||
|
||||
@@ -213,7 +213,7 @@ function locatePotentialSig(buf, sig, offset) {
|
||||
export function isType(type, buf) {
|
||||
const types = detectFileType(buf);
|
||||
|
||||
if (!(types && types.length)) return false;
|
||||
if (!types.length) return false;
|
||||
|
||||
if (typeof type === "string") {
|
||||
return types.reduce((acc, t) => {
|
||||
|
||||
253
src/core/lib/FuzzyMatch.mjs
Normal file
253
src/core/lib/FuzzyMatch.mjs
Normal file
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
* LICENSE
|
||||
*
|
||||
* This software is dual-licensed to the public domain and under the following
|
||||
* license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
* publish, and distribute this file as you see fit.
|
||||
*
|
||||
* VERSION
|
||||
* 0.1.0 (2016-03-28) Initial release
|
||||
*
|
||||
* AUTHOR
|
||||
* Forrest Smith
|
||||
*
|
||||
* CONTRIBUTORS
|
||||
* J<>rgen Tjern<72> - async helper
|
||||
* Anurag Awasthi - updated to 0.2.0
|
||||
*/
|
||||
|
||||
export const DEFAULT_WEIGHTS = {
|
||||
sequentialBonus: 15, // bonus for adjacent matches
|
||||
separatorBonus: 30, // bonus if match occurs after a separator
|
||||
camelBonus: 30, // bonus if match is uppercase and prev is lower
|
||||
firstLetterBonus: 15, // bonus if the first letter is matched
|
||||
|
||||
leadingLetterPenalty: -5, // penalty applied for every letter in str before the first match
|
||||
maxLeadingLetterPenalty: -15, // maximum penalty for leading letters
|
||||
unmatchedLetterPenalty: -1
|
||||
};
|
||||
|
||||
/**
|
||||
* Does a fuzzy search to find pattern inside a string.
|
||||
* @param {string} pattern pattern to search for
|
||||
* @param {string} str string which is being searched
|
||||
* @param {boolean} global whether to search for all matches or just one
|
||||
* @returns [boolean, number] a boolean which tells if pattern was
|
||||
* found or not and a search score
|
||||
*/
|
||||
export function fuzzyMatch(pattern, str, global=false, weights=DEFAULT_WEIGHTS) {
|
||||
const recursionCount = 0;
|
||||
const recursionLimit = 10;
|
||||
const matches = [];
|
||||
const maxMatches = 256;
|
||||
|
||||
if (!global) {
|
||||
return fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
0 /* patternCurIndex */,
|
||||
0 /* strCurrIndex */,
|
||||
null /* srcMatches */,
|
||||
matches,
|
||||
maxMatches,
|
||||
0 /* nextMatch */,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
);
|
||||
}
|
||||
|
||||
// Return all matches
|
||||
let foundMatch = true,
|
||||
score,
|
||||
idxs,
|
||||
strCurrIndex = 0;
|
||||
const results = [];
|
||||
|
||||
while (foundMatch) {
|
||||
[foundMatch, score, idxs] = fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
0 /* patternCurIndex */,
|
||||
strCurrIndex,
|
||||
null /* srcMatches */,
|
||||
matches,
|
||||
maxMatches,
|
||||
0 /* nextMatch */,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
);
|
||||
if (foundMatch) results.push([foundMatch, score, [...idxs]]);
|
||||
strCurrIndex = idxs[idxs.length - 1] + 1;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive helper function
|
||||
*/
|
||||
function fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
patternCurIndex,
|
||||
strCurrIndex,
|
||||
srcMatches,
|
||||
matches,
|
||||
maxMatches,
|
||||
nextMatch,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
) {
|
||||
let outScore = 0;
|
||||
|
||||
// Return if recursion limit is reached.
|
||||
if (++recursionCount >= recursionLimit) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
// Return if we reached ends of strings.
|
||||
if (patternCurIndex === pattern.length || strCurrIndex === str.length) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
// Recursion params
|
||||
let recursiveMatch = false;
|
||||
let bestRecursiveMatches = [];
|
||||
let bestRecursiveScore = 0;
|
||||
|
||||
// Loop through pattern and str looking for a match.
|
||||
let firstMatch = true;
|
||||
while (patternCurIndex < pattern.length && strCurrIndex < str.length) {
|
||||
// Match found.
|
||||
if (
|
||||
pattern[patternCurIndex].toLowerCase() === str[strCurrIndex].toLowerCase()
|
||||
) {
|
||||
if (nextMatch >= maxMatches) {
|
||||
return [false, outScore, []];
|
||||
}
|
||||
|
||||
if (firstMatch && srcMatches) {
|
||||
matches = [...srcMatches];
|
||||
firstMatch = false;
|
||||
}
|
||||
|
||||
const [matched, recursiveScore, recursiveMatches] = fuzzyMatchRecursive(
|
||||
pattern,
|
||||
str,
|
||||
patternCurIndex,
|
||||
strCurrIndex + 1,
|
||||
matches,
|
||||
recursiveMatches,
|
||||
maxMatches,
|
||||
nextMatch,
|
||||
recursionCount,
|
||||
recursionLimit,
|
||||
weights
|
||||
);
|
||||
|
||||
if (matched) {
|
||||
// Pick best recursive score.
|
||||
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
||||
bestRecursiveMatches = [...recursiveMatches];
|
||||
bestRecursiveScore = recursiveScore;
|
||||
}
|
||||
recursiveMatch = true;
|
||||
}
|
||||
|
||||
matches[nextMatch++] = strCurrIndex;
|
||||
++patternCurIndex;
|
||||
}
|
||||
++strCurrIndex;
|
||||
}
|
||||
|
||||
const matched = patternCurIndex === pattern.length;
|
||||
|
||||
if (matched) {
|
||||
outScore = 100;
|
||||
|
||||
// Apply leading letter penalty
|
||||
let penalty = weights.leadingLetterPenalty * matches[0];
|
||||
penalty =
|
||||
penalty < weights.maxLeadingLetterPenalty ?
|
||||
weights.maxLeadingLetterPenalty :
|
||||
penalty;
|
||||
outScore += penalty;
|
||||
|
||||
// Apply unmatched penalty
|
||||
const unmatched = str.length - nextMatch;
|
||||
outScore += weights.unmatchedLetterPenalty * unmatched;
|
||||
|
||||
// Apply ordering bonuses
|
||||
for (let i = 0; i < nextMatch; i++) {
|
||||
const currIdx = matches[i];
|
||||
|
||||
if (i > 0) {
|
||||
const prevIdx = matches[i - 1];
|
||||
if (currIdx === prevIdx + 1) {
|
||||
outScore += weights.sequentialBonus;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for bonuses based on neighbor character value.
|
||||
if (currIdx > 0) {
|
||||
// Camel case
|
||||
const neighbor = str[currIdx - 1];
|
||||
const curr = str[currIdx];
|
||||
if (
|
||||
neighbor !== neighbor.toUpperCase() &&
|
||||
curr !== curr.toLowerCase()
|
||||
) {
|
||||
outScore += weights.camelBonus;
|
||||
}
|
||||
const isNeighbourSeparator = neighbor === "_" || neighbor === " ";
|
||||
if (isNeighbourSeparator) {
|
||||
outScore += weights.separatorBonus;
|
||||
}
|
||||
} else {
|
||||
// First letter
|
||||
outScore += weights.firstLetterBonus;
|
||||
}
|
||||
}
|
||||
|
||||
// Return best result
|
||||
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
||||
// Recursive score is better than "this"
|
||||
matches = bestRecursiveMatches;
|
||||
outScore = bestRecursiveScore;
|
||||
return [true, outScore, matches];
|
||||
} else if (matched) {
|
||||
// "this" score is better than recursive
|
||||
return [true, outScore, matches];
|
||||
} else {
|
||||
return [false, outScore, matches];
|
||||
}
|
||||
}
|
||||
return [false, outScore, matches];
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a list of match indexes into a list of match ranges
|
||||
*
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @param [number] matches
|
||||
* @returns [[number]]
|
||||
*/
|
||||
export function calcMatchRanges(matches) {
|
||||
const ranges = [];
|
||||
let start = matches[0],
|
||||
curr = start;
|
||||
|
||||
matches.forEach(m => {
|
||||
if (m === curr || m === curr + 1) curr = m;
|
||||
else {
|
||||
ranges.push([start, curr - start + 1]);
|
||||
start = m;
|
||||
curr = m;
|
||||
}
|
||||
});
|
||||
|
||||
ranges.push([start, curr - start + 1]);
|
||||
return ranges;
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
|
||||
/**
|
||||
@@ -100,6 +101,9 @@ export function toHexFast(data) {
|
||||
* fromHex("0a:14:1e", "Colon");
|
||||
*/
|
||||
export function fromHex(data, delim="Auto", byteLen=2) {
|
||||
if (byteLen < 1 || Math.round(byteLen) !== byteLen)
|
||||
throw new OperationError("Byte length must be a positive integer");
|
||||
|
||||
if (delim !== "None") {
|
||||
const delimRegex = delim === "Auto" ? /[^a-f\d]|(0x)/gi : Utils.regexRep(delim);
|
||||
data = data.replace(delimRegex, "");
|
||||
|
||||
@@ -26,7 +26,7 @@ export function ipv4CidrRange(cidr, includeNetworkInfo, enumerateAddresses, allo
|
||||
let output = "";
|
||||
|
||||
if (cidrRange < 0 || cidrRange > 31) {
|
||||
return "IPv4 CIDR must be less than 32";
|
||||
throw new OperationError("IPv4 CIDR must be less than 32");
|
||||
}
|
||||
|
||||
const mask = ~(0xFFFFFFFF >>> cidrRange),
|
||||
@@ -64,7 +64,7 @@ export function ipv6CidrRange(cidr, includeNetworkInfo) {
|
||||
cidrRange = parseInt(cidr[cidr.length-1], 10);
|
||||
|
||||
if (cidrRange < 0 || cidrRange > 127) {
|
||||
return "IPv6 CIDR must be less than 128";
|
||||
throw new OperationError("IPv6 CIDR must be less than 128");
|
||||
}
|
||||
|
||||
const ip1 = new Array(8),
|
||||
@@ -211,7 +211,7 @@ export function ipv4ListedRange(match, includeNetworkInfo, enumerateAddresses, a
|
||||
const network = strToIpv4(ipv4CidrList[i].split("/")[0]);
|
||||
const cidrRange = parseInt(ipv4CidrList[i].split("/")[1], 10);
|
||||
if (cidrRange < 0 || cidrRange > 31) {
|
||||
return "IPv4 CIDR must be less than 32";
|
||||
throw new OperationError("IPv4 CIDR must be less than 32");
|
||||
}
|
||||
const mask = ~(0xFFFFFFFF >>> cidrRange),
|
||||
cidrIp1 = network & mask,
|
||||
@@ -254,7 +254,7 @@ export function ipv6ListedRange(match, includeNetworkInfo) {
|
||||
const cidrRange = parseInt(ipv6CidrList[i].split("/")[1], 10);
|
||||
|
||||
if (cidrRange < 0 || cidrRange > 127) {
|
||||
return "IPv6 CIDR must be less than 128";
|
||||
throw new OperationError("IPv6 CIDR must be less than 128");
|
||||
}
|
||||
|
||||
const cidrIp1 = new Array(8),
|
||||
|
||||
24
src/core/lib/JWT.mjs
Normal file
24
src/core/lib/JWT.mjs
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* JWT resources
|
||||
*
|
||||
* @author mt3571 [mt3571@protonmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* List of the JWT algorithms that can be used
|
||||
*/
|
||||
export const JWT_ALGORITHMS = [
|
||||
"HS256",
|
||||
"HS384",
|
||||
"HS512",
|
||||
"RS256",
|
||||
"RS384",
|
||||
"RS512",
|
||||
"ES256",
|
||||
"ES384",
|
||||
"ES512",
|
||||
"None"
|
||||
];
|
||||
@@ -1,8 +1,8 @@
|
||||
import OperationConfig from "../config/OperationConfig.json";
|
||||
import OperationConfig from "../config/OperationConfig.json" assert {type: "json"};
|
||||
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import Recipe from "../Recipe.mjs";
|
||||
import Dish from "../Dish.mjs";
|
||||
import {detectFileType} from "./FileType.mjs";
|
||||
import {detectFileType, isType} from "./FileType.mjs";
|
||||
import chiSquared from "chi-squared";
|
||||
|
||||
/**
|
||||
@@ -19,31 +19,38 @@ class Magic {
|
||||
* Magic constructor.
|
||||
*
|
||||
* @param {ArrayBuffer} buf
|
||||
* @param {Object[]} [opPatterns]
|
||||
* @param {Object[]} [opCriteria]
|
||||
* @param {Object} [prevOp]
|
||||
*/
|
||||
constructor(buf, opPatterns) {
|
||||
constructor(buf, opCriteria=Magic._generateOpCriteria(), prevOp=null) {
|
||||
this.inputBuffer = new Uint8Array(buf);
|
||||
this.inputStr = Utils.arrayBufferToStr(buf);
|
||||
this.opPatterns = opPatterns || Magic._generateOpPatterns();
|
||||
this.opCriteria = opCriteria;
|
||||
this.prevOp = prevOp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds operations that claim to be able to decode the input based on regular
|
||||
* expression matches.
|
||||
* Finds operations that claim to be able to decode the input based on various criteria.
|
||||
*
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
findMatchingOps() {
|
||||
const matches = [];
|
||||
findMatchingInputOps() {
|
||||
const matches = [],
|
||||
inputEntropy = this.calcEntropy();
|
||||
|
||||
for (let i = 0; i < this.opPatterns.length; i++) {
|
||||
const pattern = this.opPatterns[i],
|
||||
regex = new RegExp(pattern.match, pattern.flags);
|
||||
this.opCriteria.forEach(check => {
|
||||
// If the input doesn't lie in the required entropy range, move on
|
||||
if (check.entropyRange &&
|
||||
(inputEntropy < check.entropyRange[0] ||
|
||||
inputEntropy > check.entropyRange[1]))
|
||||
return;
|
||||
// If the input doesn't match the pattern, move on
|
||||
if (check.pattern &&
|
||||
!check.pattern.test(this.inputStr))
|
||||
return;
|
||||
|
||||
if (regex.test(this.inputStr)) {
|
||||
matches.push(pattern);
|
||||
}
|
||||
}
|
||||
matches.push(check);
|
||||
});
|
||||
|
||||
return matches;
|
||||
}
|
||||
@@ -185,8 +192,10 @@ class Magic {
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
calcEntropy() {
|
||||
const prob = this._freqDist();
|
||||
calcEntropy(data=this.inputBuffer, standalone=false) {
|
||||
if (!standalone && this.inputEntropy) return this.inputEntropy;
|
||||
|
||||
const prob = this._freqDist(data, standalone);
|
||||
let entropy = 0,
|
||||
p;
|
||||
|
||||
@@ -195,6 +204,8 @@ class Magic {
|
||||
if (p === 0) continue;
|
||||
entropy += p * Math.log(p) / Math.log(2);
|
||||
}
|
||||
|
||||
if (!standalone) this.inputEntropy = -entropy;
|
||||
return -entropy;
|
||||
}
|
||||
|
||||
@@ -264,25 +275,59 @@ class Magic {
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the data passes output criteria for an operation check
|
||||
*
|
||||
* @param {ArrayBuffer} data
|
||||
* @param {Object} criteria
|
||||
* @returns {boolean}
|
||||
*/
|
||||
outputCheckPasses(data, criteria) {
|
||||
if (criteria.pattern) {
|
||||
const dataStr = Utils.arrayBufferToStr(data),
|
||||
regex = new RegExp(criteria.pattern, criteria.flags);
|
||||
if (!regex.test(dataStr))
|
||||
return false;
|
||||
}
|
||||
if (criteria.entropyRange) {
|
||||
const dataEntropy = this.calcEntropy(data, true);
|
||||
if (dataEntropy < criteria.entropyRange[0] || dataEntropy > criteria.entropyRange[1])
|
||||
return false;
|
||||
}
|
||||
if (criteria.mime &&
|
||||
!isType(criteria.mime, data))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Speculatively executes matching operations, recording metadata of each result.
|
||||
*
|
||||
* @param {number} [depth=0] - How many levels to try to execute
|
||||
* @param {boolean} [extLang=false] - Extensive language support (false = only check the most
|
||||
* common Internet languages)
|
||||
* common Internet languages)
|
||||
* @param {boolean} [intensive=false] - Run brute-forcing on each branch (significantly affects
|
||||
* performance)
|
||||
* performance)
|
||||
* @param {Object[]} [recipeConfig=[]] - The recipe configuration up to this point
|
||||
* @param {boolean} [useful=false] - Whether the current recipe should be scored highly
|
||||
* @param {string} [crib=null] - The regex crib provided by the user, for filtering the operation output
|
||||
* @param {string} [crib=null] - The regex crib provided by the user, for filtering the operation
|
||||
* output
|
||||
* @returns {Object[]} - A sorted list of the recipes most likely to result in correct decoding
|
||||
*/
|
||||
async speculativeExecution(depth=0, extLang=false, intensive=false, recipeConfig=[], useful=false, crib=null) {
|
||||
async speculativeExecution(
|
||||
depth=0,
|
||||
extLang=false,
|
||||
intensive=false,
|
||||
recipeConfig=[],
|
||||
useful=false,
|
||||
crib=null) {
|
||||
|
||||
// If we have reached the recursion depth, return
|
||||
if (depth < 0) return [];
|
||||
|
||||
// Find any operations that can be run on this data
|
||||
const matchingOps = this.findMatchingOps();
|
||||
|
||||
const matchingOps = this.findMatchingInputOps();
|
||||
let results = [];
|
||||
|
||||
// Record the properties of the current data
|
||||
@@ -308,17 +353,21 @@ class Magic {
|
||||
},
|
||||
output = await this._runRecipe([opConfig]);
|
||||
|
||||
// If the recipe is repeating and returning the same data, do not continue
|
||||
if (prevOp && op.op === prevOp.op && _buffersEqual(output, this.inputBuffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the recipe returned an empty buffer, do not continue
|
||||
if (_buffersEqual(output, new ArrayBuffer())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const magic = new Magic(output, this.opPatterns),
|
||||
// If the recipe is repeating and returning the same data, do not continue
|
||||
if (prevOp && op.op === prevOp.op && _buffersEqual(output, this.inputBuffer)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the output criteria for this op doesn't match the output, do not continue
|
||||
if (op.output && !this.outputCheckPasses(output, op.output))
|
||||
return;
|
||||
|
||||
const magic = new Magic(output, this.opCriteria, OperationConfig[op.op]),
|
||||
speculativeResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, intensive, [...recipeConfig, opConfig], op.useful, crib);
|
||||
|
||||
@@ -330,7 +379,7 @@ class Magic {
|
||||
const bfEncodings = await this.bruteForce();
|
||||
|
||||
await Promise.all(bfEncodings.map(async enc => {
|
||||
const magic = new Magic(enc.data, this.opPatterns),
|
||||
const magic = new Magic(enc.data, this.opCriteria, undefined),
|
||||
bfResults = await magic.speculativeExecution(
|
||||
depth-1, extLang, false, [...recipeConfig, enc.conf], false, crib);
|
||||
|
||||
@@ -345,7 +394,8 @@ class Magic {
|
||||
r.languageScores[0].probability > 0 || // Some kind of language was found
|
||||
r.fileType || // A file was found
|
||||
r.isUTF8 || // UTF-8 was found
|
||||
r.matchingOps.length // A matching op was found
|
||||
r.matchingOps.length || // A matching op was found
|
||||
r.matchesCrib // The crib matches
|
||||
)
|
||||
);
|
||||
|
||||
@@ -376,9 +426,10 @@ class Magic {
|
||||
bScore += b.entropy;
|
||||
|
||||
// A result with no recipe but matching ops suggests there are better options
|
||||
if ((!a.recipe.length && a.matchingOps.length) &&
|
||||
b.recipe.length)
|
||||
if ((!a.recipe.length && a.matchingOps.length) && b.recipe.length)
|
||||
return 1;
|
||||
if ((!b.recipe.length && b.matchingOps.length) && a.recipe.length)
|
||||
return -1;
|
||||
|
||||
return aScore - bScore;
|
||||
});
|
||||
@@ -417,14 +468,16 @@ class Magic {
|
||||
* Calculates the number of times each byte appears in the input as a percentage
|
||||
*
|
||||
* @private
|
||||
* @param {ArrayBuffer} [data]
|
||||
* @param {boolean} [standalone]
|
||||
* @returns {number[]}
|
||||
*/
|
||||
_freqDist() {
|
||||
if (this.freqDist) return this.freqDist;
|
||||
_freqDist(data=this.inputBuffer, standalone=false) {
|
||||
if (!standalone && this.freqDist) return this.freqDist;
|
||||
|
||||
const len = this.inputBuffer.length;
|
||||
const len = data.length,
|
||||
counts = new Array(256).fill(0);
|
||||
let i = len;
|
||||
const counts = new Array(256).fill(0);
|
||||
|
||||
if (!len) {
|
||||
this.freqDist = counts;
|
||||
@@ -432,13 +485,15 @@ class Magic {
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
counts[this.inputBuffer[i]]++;
|
||||
counts[data[i]]++;
|
||||
}
|
||||
|
||||
this.freqDist = counts.map(c => {
|
||||
const result = counts.map(c => {
|
||||
return c / len * 100;
|
||||
});
|
||||
return this.freqDist;
|
||||
|
||||
if (!standalone) this.freqDist = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,24 +502,29 @@ class Magic {
|
||||
* @private
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
static _generateOpPatterns() {
|
||||
const opPatterns = [];
|
||||
static _generateOpCriteria() {
|
||||
const opCriteria = [];
|
||||
|
||||
for (const op in OperationConfig) {
|
||||
if (!("patterns" in OperationConfig[op])) continue;
|
||||
if (!("checks" in OperationConfig[op]))
|
||||
continue;
|
||||
|
||||
OperationConfig[op].patterns.forEach(pattern => {
|
||||
opPatterns.push({
|
||||
OperationConfig[op].checks.forEach(check => {
|
||||
// Add to the opCriteria list.
|
||||
// Compile the regex here and cache the compiled version so we
|
||||
// don't have to keep calculating it.
|
||||
opCriteria.push({
|
||||
op: op,
|
||||
match: pattern.match,
|
||||
flags: pattern.flags,
|
||||
args: pattern.args,
|
||||
useful: pattern.useful || false
|
||||
pattern: check.pattern ? new RegExp(check.pattern, check.flags) : null,
|
||||
args: check.args,
|
||||
useful: check.useful,
|
||||
entropyRange: check.entropyRange,
|
||||
output: check.output
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return opPatterns;
|
||||
return opCriteria;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Utils from "../Utils.mjs";
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
/**
|
||||
* Protobuf lib. Contains functions to decode protobuf serialised
|
||||
@@ -32,9 +33,10 @@ class Protobuf {
|
||||
this.MSB = 0x80;
|
||||
this.VALUE = 0x7f;
|
||||
|
||||
// Declare offset and length
|
||||
// Declare offset, length, and field type object
|
||||
this.offset = 0;
|
||||
this.LENGTH = data.length;
|
||||
this.fieldTypes = {};
|
||||
}
|
||||
|
||||
// Public Functions
|
||||
@@ -76,15 +78,281 @@ class Protobuf {
|
||||
return pb._varInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode input JSON according to the given schema
|
||||
*
|
||||
* @param {Object} input
|
||||
* @param {Object []} args
|
||||
* @returns {Object}
|
||||
*/
|
||||
static encode(input, args) {
|
||||
this.updateProtoRoot(args[0]);
|
||||
if (!this.mainMessageName) {
|
||||
throw new Error("Schema Error: Schema not defined");
|
||||
}
|
||||
const message = this.parsedProto.root.nested[this.mainMessageName];
|
||||
|
||||
// Convert input into instance of message, and verify instance
|
||||
input = message.fromObject(input);
|
||||
const error = message.verify(input);
|
||||
if (error) {
|
||||
throw new Error("Input Error: " + error);
|
||||
}
|
||||
// Encode input
|
||||
const output = message.encode(input).finish();
|
||||
return new Uint8Array(output).buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse Protobuf data
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @returns {Object}
|
||||
*/
|
||||
static decode(input) {
|
||||
static decode(input, args) {
|
||||
this.updateProtoRoot(args[0]);
|
||||
this.showUnknownFields = args[1];
|
||||
this.showTypes = args[2];
|
||||
return this.mergeDecodes(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the parsedProto, throw parsing errors
|
||||
*
|
||||
* @param {string} protoText
|
||||
*/
|
||||
static updateProtoRoot(protoText) {
|
||||
try {
|
||||
this.parsedProto = protobuf.parse(protoText);
|
||||
if (this.parsedProto.package) {
|
||||
this.parsedProto.root = this.parsedProto.root.nested[this.parsedProto.package];
|
||||
}
|
||||
this.updateMainMessageName();
|
||||
} catch (error) {
|
||||
throw new Error("Schema " + error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mainMessageName to the first instance of a message defined in the schema that is not a submessage
|
||||
*
|
||||
*/
|
||||
static updateMainMessageName() {
|
||||
const messageNames = [];
|
||||
const fieldTypes = [];
|
||||
this.parsedProto.root.nestedArray.forEach(block => {
|
||||
if (block instanceof protobuf.Type) {
|
||||
messageNames.push(block.name);
|
||||
this.parsedProto.root.nested[block.name].fieldsArray.forEach(field => {
|
||||
fieldTypes.push(field.type);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (messageNames.length === 0) {
|
||||
this.mainMessageName = null;
|
||||
} else {
|
||||
// for (const name of messageNames) {
|
||||
// if (!fieldTypes.includes(name)) {
|
||||
// this.mainMessageName = name;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
this.mainMessageName = messageNames[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode input using Protobufjs package and raw methods, compare, and merge results
|
||||
*
|
||||
* @param {byteArray} input
|
||||
* @returns {Object}
|
||||
*/
|
||||
static mergeDecodes(input) {
|
||||
const pb = new Protobuf(input);
|
||||
return pb._parse();
|
||||
let rawDecode = pb._parse();
|
||||
let message;
|
||||
|
||||
if (this.showTypes) {
|
||||
rawDecode = this.showRawTypes(rawDecode, pb.fieldTypes);
|
||||
this.parsedProto.root = this.appendTypesToFieldNames(this.parsedProto.root);
|
||||
}
|
||||
|
||||
try {
|
||||
message = this.parsedProto.root.nested[this.mainMessageName];
|
||||
const packageDecode = message.toObject(message.decode(input), {
|
||||
bytes: String,
|
||||
longs: Number,
|
||||
enums: String,
|
||||
defualts: true
|
||||
});
|
||||
const output = {};
|
||||
|
||||
if (this.showUnknownFields) {
|
||||
output[message.name] = packageDecode;
|
||||
output["Unknown Fields"] = this.compareFields(rawDecode, message);
|
||||
return output;
|
||||
} else {
|
||||
return packageDecode;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
if (message) {
|
||||
throw new Error("Input " + error);
|
||||
} else {
|
||||
return rawDecode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace fieldnames with fieldname and type
|
||||
*
|
||||
* @param {Object} schemaRoot
|
||||
* @returns {Object}
|
||||
*/
|
||||
static appendTypesToFieldNames(schemaRoot) {
|
||||
for (const block of schemaRoot.nestedArray) {
|
||||
if (block instanceof protobuf.Type) {
|
||||
for (const [fieldName, fieldData] of Object.entries(block.fields)) {
|
||||
schemaRoot.nested[block.name].remove(block.fields[fieldName]);
|
||||
schemaRoot.nested[block.name].add(new protobuf.Field(`${fieldName} (${fieldData.type})`, fieldData.id, fieldData.type, fieldData.rule));
|
||||
}
|
||||
}
|
||||
}
|
||||
return schemaRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add field type to field name for fields in the raw decoded output
|
||||
*
|
||||
* @param {Object} rawDecode
|
||||
* @param {Object} fieldTypes
|
||||
* @returns {Object}
|
||||
*/
|
||||
static showRawTypes(rawDecode, fieldTypes) {
|
||||
for (const [fieldNum, value] of Object.entries(rawDecode)) {
|
||||
const fieldType = fieldTypes[fieldNum];
|
||||
let outputFieldValue;
|
||||
let outputFieldType;
|
||||
|
||||
// Submessages
|
||||
if (isNaN(fieldType)) {
|
||||
outputFieldType = 2;
|
||||
|
||||
// Repeated submessages
|
||||
if (Array.isArray(value)) {
|
||||
const fieldInstances = [];
|
||||
for (const instance of Object.keys(value)) {
|
||||
if (typeof(value[instance]) !== "string") {
|
||||
fieldInstances.push(this.showRawTypes(value[instance], fieldType));
|
||||
} else {
|
||||
fieldInstances.push(value[instance]);
|
||||
}
|
||||
}
|
||||
outputFieldValue = fieldInstances;
|
||||
|
||||
// Single submessage
|
||||
} else {
|
||||
outputFieldValue = this.showRawTypes(value, fieldType);
|
||||
}
|
||||
|
||||
// Non-submessage field
|
||||
} else {
|
||||
outputFieldType = fieldType;
|
||||
outputFieldValue = value;
|
||||
}
|
||||
|
||||
// Substitute fieldNum with field number and type
|
||||
rawDecode[`field #${fieldNum}: ${this.getTypeInfo(outputFieldType)}`] = outputFieldValue;
|
||||
delete rawDecode[fieldNum];
|
||||
}
|
||||
return rawDecode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare raw decode to package decode and return discrepancies
|
||||
*
|
||||
* @param rawDecodedMessage
|
||||
* @param schemaMessage
|
||||
* @returns {Object}
|
||||
*/
|
||||
static compareFields(rawDecodedMessage, schemaMessage) {
|
||||
// Define message data using raw decode output and schema
|
||||
const schemaFieldProperties = {};
|
||||
const schemaFieldNames = Object.keys(schemaMessage.fields);
|
||||
schemaFieldNames.forEach(field => schemaFieldProperties[schemaMessage.fields[field].id] = field);
|
||||
|
||||
// Loop over each field present in the raw decode output
|
||||
for (const fieldName in rawDecodedMessage) {
|
||||
let fieldId;
|
||||
if (isNaN(fieldName)) {
|
||||
fieldId = fieldName.match(/^field #(\d+)/)[1];
|
||||
} else {
|
||||
fieldId = fieldName;
|
||||
}
|
||||
|
||||
// Check if this field is defined in the schema
|
||||
if (fieldId in schemaFieldProperties) {
|
||||
const schemaFieldName = schemaFieldProperties[fieldId];
|
||||
|
||||
// Extract the current field data from the raw decode and schema
|
||||
const rawFieldData = rawDecodedMessage[fieldName];
|
||||
const schemaField = schemaMessage.fields[schemaFieldName];
|
||||
|
||||
// Check for repeated fields
|
||||
if (Array.isArray(rawFieldData) && !schemaField.repeated) {
|
||||
rawDecodedMessage[`(${schemaMessage.name}) ${schemaFieldName} is a repeated field`] = rawFieldData;
|
||||
}
|
||||
|
||||
// Check for submessage fields
|
||||
if (schemaField.resolvedType instanceof protobuf.Type) {
|
||||
const subMessageType = schemaMessage.fields[schemaFieldName].type;
|
||||
const schemaSubMessage = this.parsedProto.root.nested[subMessageType];
|
||||
const rawSubMessages = rawDecodedMessage[fieldName];
|
||||
let rawDecodedSubMessage = {};
|
||||
|
||||
// Squash multiple submessage instances into one submessage
|
||||
if (Array.isArray(rawSubMessages)) {
|
||||
rawSubMessages.forEach(subMessageInstance => {
|
||||
const instanceFields = Object.entries(subMessageInstance);
|
||||
instanceFields.forEach(subField => {
|
||||
rawDecodedSubMessage[subField[0]] = subField[1];
|
||||
});
|
||||
});
|
||||
} else {
|
||||
rawDecodedSubMessage = rawSubMessages;
|
||||
}
|
||||
|
||||
// Treat submessage as own message and compare its fields
|
||||
rawDecodedSubMessage = Protobuf.compareFields(rawDecodedSubMessage, schemaSubMessage);
|
||||
if (Object.entries(rawDecodedSubMessage).length !== 0) {
|
||||
rawDecodedMessage[`${schemaFieldName} (${subMessageType}) has missing fields`] = rawDecodedSubMessage;
|
||||
}
|
||||
}
|
||||
delete rawDecodedMessage[fieldName];
|
||||
}
|
||||
}
|
||||
return rawDecodedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wiretype information for input wiretype number
|
||||
*
|
||||
* @param {number} wireType
|
||||
* @returns {string}
|
||||
*/
|
||||
static getTypeInfo(wireType) {
|
||||
switch (wireType) {
|
||||
case 0:
|
||||
return "VarInt (e.g. int32, bool)";
|
||||
case 1:
|
||||
return "64-Bit (e.g. fixed64, double)";
|
||||
case 2:
|
||||
return "L-delim (e.g. string, message)";
|
||||
case 5:
|
||||
return "32-Bit (e.g. fixed32, float)";
|
||||
}
|
||||
}
|
||||
|
||||
// Private Class Functions
|
||||
@@ -143,6 +411,11 @@ class Protobuf {
|
||||
const header = this._fieldHeader();
|
||||
const type = header.type;
|
||||
const key = header.key;
|
||||
|
||||
if (typeof(this.fieldTypes[key]) !== "object") {
|
||||
this.fieldTypes[key] = type;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
// varint
|
||||
case 0:
|
||||
@@ -152,7 +425,7 @@ class Protobuf {
|
||||
return { "key": key, "value": this._uint64() };
|
||||
// length delimited
|
||||
case 2:
|
||||
return { "key": key, "value": this._lenDelim() };
|
||||
return { "key": key, "value": this._lenDelim(key) };
|
||||
// fixed 32
|
||||
case 5:
|
||||
return { "key": key, "value": this._uint32() };
|
||||
@@ -237,10 +510,10 @@ class Protobuf {
|
||||
* @returns {number}
|
||||
*/
|
||||
_uint64() {
|
||||
// Read off a Uint64
|
||||
let num = this.data[this.offset++] * 0x1000000 + (this.data[this.offset++] << 16) + (this.data[this.offset++] << 8) + this.data[this.offset++];
|
||||
num = num * 0x100000000 + this.data[this.offset++] * 0x1000000 + (this.data[this.offset++] << 16) + (this.data[this.offset++] << 8) + this.data[this.offset++];
|
||||
return num;
|
||||
// Read off a Uint64 with little-endian
|
||||
const lowerHalf = this.data[this.offset++] + (this.data[this.offset++] * 0x100) + (this.data[this.offset++] * 0x10000) + this.data[this.offset++] * 0x1000000;
|
||||
const upperHalf = this.data[this.offset++] + (this.data[this.offset++] * 0x100) + (this.data[this.offset++] * 0x10000) + this.data[this.offset++] * 0x1000000;
|
||||
return upperHalf * 0x100000000 + lowerHalf;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +522,7 @@ class Protobuf {
|
||||
* @private
|
||||
* @returns {Object|string}
|
||||
*/
|
||||
_lenDelim() {
|
||||
_lenDelim(fieldNum) {
|
||||
// Read off the field length
|
||||
const length = this._varInt();
|
||||
const fieldBytes = this.data.slice(this.offset, this.offset + length);
|
||||
@@ -258,6 +531,10 @@ class Protobuf {
|
||||
// Attempt to parse as a new Protobuf Object
|
||||
const pbObject = new Protobuf(fieldBytes);
|
||||
field = pbObject._parse();
|
||||
|
||||
// Set field types object
|
||||
this.fieldTypes[fieldNum] = {...this.fieldTypes[fieldNum], ...pbObject.fieldTypes};
|
||||
|
||||
} catch (err) {
|
||||
// Otherwise treat as bytes
|
||||
field = Utils.byteArrayToChars(fieldBytes);
|
||||
@@ -276,7 +553,7 @@ class Protobuf {
|
||||
_uint32() {
|
||||
// Use a dataview to read off the integer
|
||||
const dataview = new DataView(new Uint8Array(this.data.slice(this.offset, this.offset + 4)).buffer);
|
||||
const value = dataview.getUint32(0);
|
||||
const value = dataview.getUint32(0, true);
|
||||
this.offset += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { toHex, fromHex } from "./Hex.mjs";
|
||||
* @param {number} indent
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatDnStr (dnStr, indent) {
|
||||
export function formatDnStr(dnStr, indent) {
|
||||
const fields = dnStr.substr(1).replace(/([^\\])\//g, "$1$1/").split(/[^\\]\//);
|
||||
let output = "",
|
||||
maxKeyLen = 0,
|
||||
@@ -54,7 +54,7 @@ export function formatDnStr (dnStr, indent) {
|
||||
* @param {number} indent
|
||||
* @returns {string}
|
||||
*/
|
||||
export function formatByteStr (byteStr, length, indent) {
|
||||
export function formatByteStr(byteStr, length, indent) {
|
||||
byteStr = toHex(fromHex(byteStr), ":");
|
||||
length = length * 3;
|
||||
let output = "";
|
||||
|
||||
@@ -9,8 +9,9 @@
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import jsQR from "jsqr";
|
||||
import qr from "qr-image";
|
||||
import jimp from "jimp";
|
||||
import Utils from "../Utils.mjs";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Parses a QR code image from an image
|
||||
|
||||
17
src/core/lib/RSA.mjs
Normal file
17
src/core/lib/RSA.mjs
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* RSA resources.
|
||||
*
|
||||
* @author Matt C [me@mitt.dev]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import forge from "node-forge";
|
||||
|
||||
export const MD_ALGORITHMS = {
|
||||
"SHA-1": forge.md.sha1,
|
||||
"MD5": forge.md.md5,
|
||||
"SHA-256": forge.md.sha256,
|
||||
"SHA-384": forge.md.sha384,
|
||||
"SHA-512": forge.md.sha512,
|
||||
};
|
||||
502
src/core/lib/SIGABA.mjs
Normal file
502
src/core/lib/SIGABA.mjs
Normal file
@@ -0,0 +1,502 @@
|
||||
/**
|
||||
* Emulation of the SIGABA machine
|
||||
*
|
||||
* @author hettysymes
|
||||
* @copyright hettysymes 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* A set of randomised example SIGABA cipher/control rotors (these rotors are interchangeable). Cipher and control rotors can be referred to as C and R rotors respectively.
|
||||
*/
|
||||
export const CR_ROTORS = [
|
||||
{name: "Example 1", value: "SRGWANHPJZFXVIDQCEUKBYOLMT"},
|
||||
{name: "Example 2", value: "THQEFSAZVKJYULBODCPXNIMWRG"},
|
||||
{name: "Example 3", value: "XDTUYLEVFNQZBPOGIRCSMHWKAJ"},
|
||||
{name: "Example 4", value: "LOHDMCWUPSTNGVXYFJREQIKBZA"},
|
||||
{name: "Example 5", value: "ERXWNZQIJYLVOFUMSGHTCKPBDA"},
|
||||
{name: "Example 6", value: "FQECYHJIOUMDZVPSLKRTGWXBAN"},
|
||||
{name: "Example 7", value: "TBYIUMKZDJSOPEWXVANHLCFQGR"},
|
||||
{name: "Example 8", value: "QZUPDTFNYIAOMLEBWJXCGHKRSV"},
|
||||
{name: "Example 9", value: "CZWNHEMPOVXLKRSIDGJFYBTQAU"},
|
||||
{name: "Example 10", value: "ENPXJVKYQBFZTICAGMOHWRLDUS"}
|
||||
];
|
||||
|
||||
/**
|
||||
* A set of randomised example SIGABA index rotors (may be referred to as I rotors).
|
||||
*/
|
||||
export const I_ROTORS = [
|
||||
{name: "Example 1", value: "6201348957"},
|
||||
{name: "Example 2", value: "6147253089"},
|
||||
{name: "Example 3", value: "8239647510"},
|
||||
{name: "Example 4", value: "7194835260"},
|
||||
{name: "Example 5", value: "4873205916"}
|
||||
];
|
||||
|
||||
export const NUMBERS = "0123456789".split("");
|
||||
|
||||
/**
|
||||
* Converts a letter to uppercase (if it already isn't)
|
||||
*
|
||||
* @param {char} letter - letter to convert to uppercase
|
||||
* @returns {char}
|
||||
*/
|
||||
export function convToUpperCase(letter) {
|
||||
const charCode = letter.charCodeAt();
|
||||
if (97<=charCode && charCode<=122) {
|
||||
return String.fromCharCode(charCode-32);
|
||||
}
|
||||
return letter;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SIGABA machine consisting of the 3 rotor banks: cipher, control and index banks.
|
||||
*/
|
||||
export class SigabaMachine {
|
||||
|
||||
/**
|
||||
* SigabaMachine constructor
|
||||
*
|
||||
* @param {Object[]} cipherRotors - list of CRRotors
|
||||
* @param {Object[]} controlRotors - list of CRRotors
|
||||
* @param {object[]} indexRotors - list of IRotors
|
||||
*/
|
||||
constructor(cipherRotors, controlRotors, indexRotors) {
|
||||
this.cipherBank = new CipherBank(cipherRotors);
|
||||
this.controlBank = new ControlBank(controlRotors);
|
||||
this.indexBank = new IndexBank(indexRotors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps all the correct rotors in the machine.
|
||||
*/
|
||||
step() {
|
||||
const controlOut = this.controlBank.goThroughControl();
|
||||
const indexOut = this.indexBank.goThroughIndex(controlOut);
|
||||
this.cipherBank.step(indexOut);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a letter. A space is converted to a "Z" before encryption, and a "Z" is converted to an "X". This allows spaces to be encrypted.
|
||||
*
|
||||
* @param {char} letter - letter to encrypt
|
||||
* @returns {char}
|
||||
*/
|
||||
encryptLetter(letter) {
|
||||
letter = convToUpperCase(letter);
|
||||
if (letter === " ") {
|
||||
letter = "Z";
|
||||
} else if (letter === "Z") {
|
||||
letter = "X";
|
||||
}
|
||||
const encryptedLetter = this.cipherBank.encrypt(letter);
|
||||
this.step();
|
||||
return encryptedLetter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a letter. A letter decrypted as a "Z" is converted to a space before it is output, since spaces are converted to "Z"s before encryption.
|
||||
*
|
||||
* @param {char} letter - letter to decrypt
|
||||
* @returns {char}
|
||||
*/
|
||||
decryptLetter(letter) {
|
||||
letter = convToUpperCase(letter);
|
||||
let decryptedLetter = this.cipherBank.decrypt(letter);
|
||||
if (decryptedLetter === "Z") {
|
||||
decryptedLetter = " ";
|
||||
}
|
||||
this.step();
|
||||
return decryptedLetter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a message of one or more letters
|
||||
*
|
||||
* @param {string} msg - message to encrypt
|
||||
* @returns {string}
|
||||
*/
|
||||
encrypt(msg) {
|
||||
let ciphertext = "";
|
||||
for (const letter of msg) {
|
||||
ciphertext = ciphertext.concat(this.encryptLetter(letter));
|
||||
}
|
||||
return ciphertext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a message of one or more letters
|
||||
*
|
||||
* @param {string} msg - message to decrypt
|
||||
* @returns {string}
|
||||
*/
|
||||
decrypt(msg) {
|
||||
let plaintext = "";
|
||||
for (const letter of msg) {
|
||||
plaintext = plaintext.concat(this.decryptLetter(letter));
|
||||
}
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The cipher rotor bank consists of 5 cipher rotors in either a forward or reversed orientation.
|
||||
*/
|
||||
export class CipherBank {
|
||||
|
||||
/**
|
||||
* CipherBank constructor
|
||||
*
|
||||
* @param {Object[]} rotors - list of CRRotors
|
||||
*/
|
||||
constructor(rotors) {
|
||||
this.rotors = rotors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a letter through the cipher rotors (signal goes from left-to-right)
|
||||
*
|
||||
* @param {char} inputPos - the input position of the signal (letter to be encrypted)
|
||||
* @returns {char}
|
||||
*/
|
||||
encrypt(inputPos) {
|
||||
for (const rotor of this.rotors) {
|
||||
inputPos = rotor.crypt(inputPos, "leftToRight");
|
||||
}
|
||||
return inputPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts a letter through the cipher rotors (signal goes from right-to-left)
|
||||
*
|
||||
* @param {char} inputPos - the input position of the signal (letter to be decrypted)
|
||||
* @returns {char}
|
||||
*/
|
||||
decrypt(inputPos) {
|
||||
const revOrderedRotors = [...this.rotors].reverse();
|
||||
for (const rotor of revOrderedRotors) {
|
||||
inputPos = rotor.crypt(inputPos, "rightToLeft");
|
||||
}
|
||||
return inputPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step the cipher rotors forward according to the inputs from the index rotors
|
||||
*
|
||||
* @param {number[]} indexInputs - the inputs from the index rotors
|
||||
*/
|
||||
step(indexInputs) {
|
||||
const logicDict = {0: [0, 9], 1: [7, 8], 2: [5, 6], 3: [3, 4], 4: [1, 2]};
|
||||
const rotorsToMove = [];
|
||||
for (const key in logicDict) {
|
||||
const item = logicDict[key];
|
||||
for (const i of indexInputs) {
|
||||
if (item.includes(i)) {
|
||||
rotorsToMove.push(this.rotors[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const rotor of rotorsToMove) {
|
||||
rotor.step();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The control rotor bank consists of 5 control rotors in either a forward or reversed orientation. Signals to the control rotor bank always go from right-to-left.
|
||||
*/
|
||||
export class ControlBank {
|
||||
|
||||
/**
|
||||
* ControlBank constructor. The rotors have been reversed as signals go from right-to-left through the control rotors.
|
||||
*
|
||||
* @param {Object[]} rotors - list of CRRotors
|
||||
*/
|
||||
constructor(rotors) {
|
||||
this.rotors = [...rotors].reverse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a letter.
|
||||
*
|
||||
* @param {char} inputPos - the input position of the signal
|
||||
* @returns {char}
|
||||
*/
|
||||
crypt(inputPos) {
|
||||
for (const rotor of this.rotors) {
|
||||
inputPos = rotor.crypt(inputPos, "rightToLeft");
|
||||
}
|
||||
return inputPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the outputs of the control rotors. The inputs to the control rotors are always "F", "G", "H" and "I".
|
||||
*
|
||||
* @returns {number[]}
|
||||
*/
|
||||
getOutputs() {
|
||||
const outputs = [this.crypt("F"), this.crypt("G"), this.crypt("H"), this.crypt("I")];
|
||||
const logicDict = {1: "B", 2: "C", 3: "DE", 4: "FGH", 5: "IJK", 6: "LMNO", 7: "PQRST", 8: "UVWXYZ", 9: "A"};
|
||||
const numberOutputs = [];
|
||||
for (const key in logicDict) {
|
||||
const item = logicDict[key];
|
||||
for (const output of outputs) {
|
||||
if (item.includes(output)) {
|
||||
numberOutputs.push(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return numberOutputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps the control rotors. Only 3 of the control rotors step: one after every encryption, one after every 26, and one after every 26 squared.
|
||||
*/
|
||||
step() {
|
||||
const MRotor = this.rotors[1], FRotor = this.rotors[2], SRotor = this.rotors[3];
|
||||
// 14 is the offset of "O" from "A" - the next rotor steps once the previous rotor reaches "O"
|
||||
if (FRotor.state === 14) {
|
||||
if (MRotor.state === 14) {
|
||||
SRotor.step();
|
||||
}
|
||||
MRotor.step();
|
||||
}
|
||||
FRotor.step();
|
||||
}
|
||||
|
||||
/**
|
||||
* The goThroughControl function combines getting the outputs from the control rotor bank and then stepping them.
|
||||
*
|
||||
* @returns {number[]}
|
||||
*/
|
||||
goThroughControl() {
|
||||
const outputs = this.getOutputs();
|
||||
this.step();
|
||||
return outputs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The index rotor bank consists of 5 index rotors all placed in the forwards orientation.
|
||||
*/
|
||||
export class IndexBank {
|
||||
|
||||
/**
|
||||
* IndexBank constructor
|
||||
*
|
||||
* @param {Object[]} rotors - list of IRotors
|
||||
*/
|
||||
constructor(rotors) {
|
||||
this.rotors = rotors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a number.
|
||||
*
|
||||
* @param {number} inputPos - the input position of the signal
|
||||
* @returns {number}
|
||||
*/
|
||||
crypt(inputPos) {
|
||||
for (const rotor of this.rotors) {
|
||||
inputPos = rotor.crypt(inputPos);
|
||||
}
|
||||
return inputPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* The goThroughIndex function takes the inputs from the control rotor bank and returns the list of outputs after encryption through the index rotors.
|
||||
*
|
||||
* @param {number[]} controlInputs - inputs from the control rotors
|
||||
* @returns {number[]}
|
||||
*/
|
||||
goThroughIndex(controlInputs) {
|
||||
const outputs = [];
|
||||
for (const inp of controlInputs) {
|
||||
outputs.push(this.crypt(inp));
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotor class
|
||||
*/
|
||||
export class Rotor {
|
||||
|
||||
/**
|
||||
* Rotor constructor
|
||||
*
|
||||
* @param {number[]} wireSetting - the wirings within the rotor: mapping from left-to-right, the index of the number in the list maps onto the number at that index
|
||||
* @param {bool} rev - true if the rotor is reversed, false if it isn't
|
||||
* @param {number} key - the starting position or state of the rotor
|
||||
*/
|
||||
constructor(wireSetting, key, rev) {
|
||||
this.state = key;
|
||||
this.numMapping = this.getNumMapping(wireSetting, rev);
|
||||
this.posMapping = this.getPosMapping(rev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number mapping from the wireSetting (only different from wireSetting if rotor is reversed)
|
||||
*
|
||||
* @param {number[]} wireSetting - the wirings within the rotors
|
||||
* @param {bool} rev - true if reversed, false if not
|
||||
* @returns {number[]}
|
||||
*/
|
||||
getNumMapping(wireSetting, rev) {
|
||||
if (rev===false) {
|
||||
return wireSetting;
|
||||
} else {
|
||||
const length = wireSetting.length;
|
||||
const tempMapping = new Array(length);
|
||||
for (let i=0; i<length; i++) {
|
||||
tempMapping[wireSetting[i]] = i;
|
||||
}
|
||||
return tempMapping;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position mapping (how the position numbers map onto the numbers of the rotor)
|
||||
*
|
||||
* @param {bool} rev - true if reversed, false if not
|
||||
* @returns {number[]}
|
||||
*/
|
||||
getPosMapping(rev) {
|
||||
const length = this.numMapping.length;
|
||||
const posMapping = [];
|
||||
if (rev===false) {
|
||||
for (let i = this.state; i < this.state+length; i++) {
|
||||
let res = i%length;
|
||||
if (res<0) {
|
||||
res += length;
|
||||
}
|
||||
posMapping.push(res);
|
||||
}
|
||||
} else {
|
||||
for (let i = this.state; i > this.state-length; i--) {
|
||||
let res = i%length;
|
||||
if (res<0) {
|
||||
res += length;
|
||||
}
|
||||
posMapping.push(res);
|
||||
}
|
||||
}
|
||||
return posMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt/decrypt data. This process is identical to the rotors of cipher machines such as Enigma or Typex.
|
||||
*
|
||||
* @param {number} inputPos - the input position of the signal (the data to encrypt/decrypt)
|
||||
* @param {string} direction - one of "leftToRight" and "rightToLeft", states the direction in which the signal passes through the rotor
|
||||
* @returns {number}
|
||||
*/
|
||||
cryptNum(inputPos, direction) {
|
||||
const inpNum = this.posMapping[inputPos];
|
||||
let outNum;
|
||||
if (direction === "leftToRight") {
|
||||
outNum = this.numMapping[inpNum];
|
||||
} else if (direction === "rightToLeft") {
|
||||
outNum = this.numMapping.indexOf(inpNum);
|
||||
}
|
||||
const outPos = this.posMapping.indexOf(outNum);
|
||||
return outPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Steps the rotor. The number at position 0 will be moved to position 1 etc.
|
||||
*/
|
||||
step() {
|
||||
const lastNum = this.posMapping.pop();
|
||||
this.posMapping.splice(0, 0, lastNum);
|
||||
this.state = this.posMapping[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A CRRotor is a cipher (C) or control (R) rotor. These rotors are identical and interchangeable. A C or R rotor consists of 26 contacts, one for each letter, and may be put into either a forwards of reversed orientation.
|
||||
*/
|
||||
export class CRRotor extends Rotor {
|
||||
|
||||
/**
|
||||
* CRRotor constructor
|
||||
*
|
||||
* @param {string} wireSetting - the rotor wirings (string of letters)
|
||||
* @param {char} key - initial state of rotor
|
||||
* @param {bool} rev - true if reversed, false if not
|
||||
*/
|
||||
constructor(wireSetting, key, rev=false) {
|
||||
wireSetting = wireSetting.split("").map(CRRotor.letterToNum);
|
||||
super(wireSetting, CRRotor.letterToNum(key), rev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static function which converts a letter into its number i.e. its offset from the letter "A"
|
||||
*
|
||||
* @param {char} letter - letter to convert to number
|
||||
* @returns {number}
|
||||
*/
|
||||
static letterToNum(letter) {
|
||||
return letter.charCodeAt()-65;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static function which converts a number (a letter's offset from "A") into its letter
|
||||
*
|
||||
* @param {number} num - number to convert to letter
|
||||
* @returns {char}
|
||||
*/
|
||||
static numToLetter(num) {
|
||||
return String.fromCharCode(num+65);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts/decrypts a letter.
|
||||
*
|
||||
* @param {char} inputPos - the input position of the signal ("A" refers to position 0 etc.)
|
||||
* @param {string} direction - one of "leftToRight" and "rightToLeft"
|
||||
* @returns {char}
|
||||
*/
|
||||
crypt(inputPos, direction) {
|
||||
inputPos = CRRotor.letterToNum(inputPos);
|
||||
const outPos = this.cryptNum(inputPos, direction);
|
||||
return CRRotor.numToLetter(outPos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* An IRotor is an index rotor, which consists of 10 contacts each numbered from 0 to 9. Unlike C and R rotors, they cannot be put in the reversed orientation. The index rotors do not step at any point during encryption or decryption.
|
||||
*/
|
||||
export class IRotor extends Rotor {
|
||||
|
||||
/**
|
||||
* IRotor constructor
|
||||
*
|
||||
* @param {string} wireSetting - the rotor wirings (string of numbers)
|
||||
* @param {char} key - initial state of rotor
|
||||
*/
|
||||
constructor(wireSetting, key) {
|
||||
wireSetting = wireSetting.split("").map(Number);
|
||||
super(wireSetting, Number(key), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts a number
|
||||
*
|
||||
* @param {number} inputPos - the input position of the signal
|
||||
* @returns {number}
|
||||
*/
|
||||
crypt(inputPos) {
|
||||
return this.cryptNum(inputPos, "leftToRight");
|
||||
}
|
||||
|
||||
}
|
||||
331
src/core/lib/SM4.mjs
Normal file
331
src/core/lib/SM4.mjs
Normal file
@@ -0,0 +1,331 @@
|
||||
/**
|
||||
* Complete implementation of SM4 cipher encryption/decryption with
|
||||
* ECB, CBC, CFB, OFB, CTR block modes.
|
||||
* These modes are specified in IETF draft-ribose-cfrg-sm4-09, see:
|
||||
* https://tools.ietf.org/id/draft-ribose-cfrg-sm4-09.html
|
||||
* for details.
|
||||
*
|
||||
* Follows spec from Cryptography Standardization Technical Comittee:
|
||||
* http://www.gmbz.org.cn/upload/2018-04-04/1522788048733065051.pdf
|
||||
*
|
||||
* @author swesven
|
||||
* @copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/** Number of rounds */
|
||||
const NROUNDS = 32;
|
||||
|
||||
/** block size in bytes */
|
||||
const BLOCKSIZE = 16;
|
||||
|
||||
/** The S box, 256 8-bit values */
|
||||
const Sbox = [
|
||||
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
|
||||
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
|
||||
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
|
||||
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
|
||||
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
|
||||
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
|
||||
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
|
||||
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
|
||||
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
|
||||
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
|
||||
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
|
||||
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
|
||||
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
|
||||
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
|
||||
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
|
||||
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48
|
||||
];
|
||||
|
||||
/** "Fixed parameter CK" used in key expansion */
|
||||
const CK = [
|
||||
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
|
||||
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
|
||||
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
|
||||
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
|
||||
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
|
||||
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
|
||||
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
|
||||
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
|
||||
];
|
||||
|
||||
/** "System parameter FK" */
|
||||
const FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc];
|
||||
|
||||
/**
|
||||
* Rotating 32-bit shift left
|
||||
*
|
||||
* (Note that although JS integers are stored in doubles and thus have 53 bits,
|
||||
* the JS bitwise operations are 32-bit)
|
||||
*/
|
||||
function ROL(i, n) {
|
||||
return (i << n) | (i >>> (32 - n));
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear transformation L
|
||||
*
|
||||
* @param {integer} b - a 32 bit integer
|
||||
*/
|
||||
function transformL(b) {
|
||||
/* Replace each of the 4 bytes in b with the value at its offset in the Sbox */
|
||||
b = (Sbox[(b >>> 24) & 0xFF] << 24) | (Sbox[(b >>> 16) & 0xFF] << 16) |
|
||||
(Sbox[(b >>> 8) & 0xFF] << 8) | Sbox[b & 0xFF];
|
||||
/* circular rotate and xor */
|
||||
return b ^ ROL(b, 2) ^ ROL(b, 10) ^ ROL(b, 18) ^ ROL(b, 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Linear transformation L'
|
||||
*
|
||||
* @param {integer} b - a 32 bit integer
|
||||
*/
|
||||
function transformLprime(b) {
|
||||
/* Replace each of the 4 bytes in b with the value at its offset in the Sbox */
|
||||
b = (Sbox[(b >>> 24) & 0xFF] << 24) | (Sbox[(b >>> 16) & 0xFF] << 16) |
|
||||
(Sbox[(b >>> 8) & 0xFF] << 8) | Sbox[b & 0xFF];
|
||||
return b ^ ROL(b, 13) ^ ROL(b, 23); /* circular rotate and XOR */
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the round key
|
||||
*/
|
||||
function initSM4RoundKey(rawkey) {
|
||||
const K = rawkey.map((a, i) => a ^ FK[i]); /* K = rawkey ^ FK */
|
||||
const roundKey = [];
|
||||
for (let i = 0; i < 32; i++)
|
||||
roundKey[i] = K[i + 4] = K[i] ^ transformLprime(K[i + 1] ^ K[i + 2] ^ K[i + 3] ^ CK[i]);
|
||||
return roundKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypts/decrypts a single block X (4 32-bit values) with a prepared round key.
|
||||
*
|
||||
* @param {intArray} X - A cleartext block.
|
||||
* @param {intArray} roundKey - The round key from initSMRoundKey for encrypting (reversed for decrypting).
|
||||
* @returns {byteArray} - The cipher text.
|
||||
*/
|
||||
function encryptBlockSM4(X, roundKey) {
|
||||
for (let i = 0; i < NROUNDS; i++)
|
||||
X[i + 4] = X[i] ^ transformL(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ roundKey[i]);
|
||||
return [X[35], X[34], X[33], X[32]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes 16 bytes from an offset in an array and returns an array of 4 32-bit Big-Endian values.
|
||||
* (DataView won't work portably here as we need Big-Endian)
|
||||
*
|
||||
* @param {byteArray} bArray - the array of bytes
|
||||
* @param {integer} offset - starting offset in the array; 15 bytes must follow it.
|
||||
*/
|
||||
function bytesToInts(bArray, offs=0) {
|
||||
let offset = offs;
|
||||
const A = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
|
||||
offset += 4;
|
||||
const B = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
|
||||
offset += 4;
|
||||
const C = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
|
||||
offset += 4;
|
||||
const D = (bArray[offset] << 24) | (bArray[offset + 1] << 16) | (bArray[offset + 2] << 8) | bArray[offset + 3];
|
||||
return [A, B, C, D];
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverse of bytesToInts above; takes an array of 32-bit integers and turns it into an array of bytes.
|
||||
* Again, Big-Endian order.
|
||||
*/
|
||||
function intsToBytes(ints) {
|
||||
const bArr = [];
|
||||
for (let i = 0; i < ints.length; i++) {
|
||||
bArr.push((ints[i] >> 24) & 0xFF);
|
||||
bArr.push((ints[i] >> 16) & 0xFF);
|
||||
bArr.push((ints[i] >> 8) & 0xFF);
|
||||
bArr.push(ints[i] & 0xFF);
|
||||
}
|
||||
return bArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt using SM4 using a given block cipher mode.
|
||||
*
|
||||
* @param {byteArray} message - The clear text message; any length under 32 Gb or so.
|
||||
* @param {byteArray} key - The cipher key, 16 bytes.
|
||||
* @param {byteArray} iv - The IV or nonce, 16 bytes (not used with ECB mode)
|
||||
* @param {string} mode - The block cipher mode "CBC", "ECB", "CFB", "OFB", "CTR".
|
||||
* @param {boolean} noPadding - Don't add PKCS#7 padding if set.
|
||||
* @returns {byteArray} - The cipher text.
|
||||
*/
|
||||
export function encryptSM4(message, key, iv, mode="ECB", noPadding=false) {
|
||||
const messageLength = message.length;
|
||||
if (messageLength === 0)
|
||||
return [];
|
||||
const roundKey = initSM4RoundKey(bytesToInts(key, 0));
|
||||
|
||||
/* Pad with PKCS#7 if requested for ECB/CBC else add zeroes (which are sliced off at the end) */
|
||||
let padByte = 0;
|
||||
let nPadding = 16 - (message.length & 0xF);
|
||||
if (mode === "ECB" || mode === "CBC") {
|
||||
if (noPadding) {
|
||||
if (nPadding !== 16)
|
||||
throw new OperationError(`No padding requested in ${mode} mode but input is not a 16-byte multiple.`);
|
||||
nPadding = 0;
|
||||
} else
|
||||
padByte = nPadding;
|
||||
}
|
||||
for (let i = 0; i < nPadding; i++)
|
||||
message.push(padByte);
|
||||
|
||||
const cipherText = [];
|
||||
switch (mode) {
|
||||
case "ECB":
|
||||
for (let i = 0; i < message.length; i += BLOCKSIZE)
|
||||
Array.prototype.push.apply(cipherText, intsToBytes(encryptBlockSM4(bytesToInts(message, i), roundKey)));
|
||||
break;
|
||||
case "CBC":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < message.length; i += BLOCKSIZE) {
|
||||
const block = bytesToInts(message, i);
|
||||
block[0] ^= iv[0]; block[1] ^= iv[1];
|
||||
block[2] ^= iv[2]; block[3] ^= iv[3];
|
||||
iv = encryptBlockSM4(block, roundKey);
|
||||
Array.prototype.push.apply(cipherText, intsToBytes(iv));
|
||||
}
|
||||
break;
|
||||
case "CFB":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < message.length; i += BLOCKSIZE) {
|
||||
iv = encryptBlockSM4(iv, roundKey);
|
||||
const block = bytesToInts(message, i);
|
||||
block[0] ^= iv[0]; block[1] ^= iv[1];
|
||||
block[2] ^= iv[2]; block[3] ^= iv[3];
|
||||
Array.prototype.push.apply(cipherText, intsToBytes(block));
|
||||
iv = block;
|
||||
}
|
||||
break;
|
||||
case "OFB":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < message.length; i += BLOCKSIZE) {
|
||||
iv = encryptBlockSM4(iv, roundKey);
|
||||
const block = bytesToInts(message, i);
|
||||
block[0] ^= iv[0]; block[1] ^= iv[1];
|
||||
block[2] ^= iv[2]; block[3] ^= iv[3];
|
||||
Array.prototype.push.apply(cipherText, intsToBytes(block));
|
||||
}
|
||||
break;
|
||||
case "CTR":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < message.length; i += BLOCKSIZE) {
|
||||
let iv2 = [...iv]; /* containing the IV + counter */
|
||||
iv2[3] += (i >> 4);/* Using a 32 bit counter here. 64 Gb encrypts should be enough for everyone. */
|
||||
iv2 = encryptBlockSM4(iv2, roundKey);
|
||||
const block = bytesToInts(message, i);
|
||||
block[0] ^= iv2[0]; block[1] ^= iv2[1];
|
||||
block[2] ^= iv2[2]; block[3] ^= iv2[3];
|
||||
Array.prototype.push.apply(cipherText, intsToBytes(block));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new OperationError("Invalid block cipher mode: "+mode);
|
||||
}
|
||||
if (mode !== "ECB" && mode !== "CBC")
|
||||
return cipherText.slice(0, messageLength);
|
||||
return cipherText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt using SM4 using a given block cipher mode.
|
||||
*
|
||||
* @param {byteArray} cipherText - The ciphertext
|
||||
* @param {byteArray} key - The cipher key, 16 bytes.
|
||||
* @param {byteArray} iv - The IV or nonce, 16 bytes (not used with ECB mode)
|
||||
* @param {string} mode - The block cipher mode "CBC", "ECB", "CFB", "OFB", "CTR"
|
||||
* @param {boolean] ignorePadding - If true, ignore padding issues in ECB/CBC mode.
|
||||
* @returns {byteArray} - The cipher text.
|
||||
*/
|
||||
export function decryptSM4(cipherText, key, iv, mode="ECB", ignorePadding=false) {
|
||||
const originalLength = cipherText.length;
|
||||
if (originalLength === 0)
|
||||
return [];
|
||||
let roundKey = initSM4RoundKey(bytesToInts(key, 0));
|
||||
|
||||
if (mode === "ECB" || mode === "CBC") {
|
||||
/* Init decryption key */
|
||||
roundKey = roundKey.reverse();
|
||||
if ((originalLength & 0xF) !== 0 && !ignorePadding)
|
||||
throw new OperationError(`With ECB or CBC modes, the input must be divisible into 16 byte blocks. (${cipherText.length & 0xF} bytes extra)`);
|
||||
} else { /* Pad dummy bytes for other modes, chop them off at the end */
|
||||
while ((cipherText.length & 0xF) !== 0)
|
||||
cipherText.push(0);
|
||||
}
|
||||
|
||||
const clearText = [];
|
||||
switch (mode) {
|
||||
case "ECB":
|
||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE)
|
||||
Array.prototype.push.apply(clearText, intsToBytes(encryptBlockSM4(bytesToInts(cipherText, i), roundKey)));
|
||||
break;
|
||||
case "CBC":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
||||
const block = encryptBlockSM4(bytesToInts(cipherText, i), roundKey);
|
||||
block[0] ^= iv[0]; block[1] ^= iv[1];
|
||||
block[2] ^= iv[2]; block[3] ^= iv[3];
|
||||
Array.prototype.push.apply(clearText, intsToBytes(block));
|
||||
iv = bytesToInts(cipherText, i);
|
||||
}
|
||||
break;
|
||||
case "CFB":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
||||
iv = encryptBlockSM4(iv, roundKey);
|
||||
const block = bytesToInts(cipherText, i);
|
||||
block[0] ^= iv[0]; block[1] ^= iv[1];
|
||||
block[2] ^= iv[2]; block[3] ^= iv[3];
|
||||
Array.prototype.push.apply(clearText, intsToBytes(block));
|
||||
iv = bytesToInts(cipherText, i);
|
||||
}
|
||||
break;
|
||||
case "OFB":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
||||
iv = encryptBlockSM4(iv, roundKey);
|
||||
const block = bytesToInts(cipherText, i);
|
||||
block[0] ^= iv[0]; block[1] ^= iv[1];
|
||||
block[2] ^= iv[2]; block[3] ^= iv[3];
|
||||
Array.prototype.push.apply(clearText, intsToBytes(block));
|
||||
}
|
||||
break;
|
||||
case "CTR":
|
||||
iv = bytesToInts(iv, 0);
|
||||
for (let i = 0; i < cipherText.length; i += BLOCKSIZE) {
|
||||
let iv2 = [...iv]; /* containing the IV + counter */
|
||||
iv2[3] += (i >> 4);/* Using a 32 bit counter here. 64 Gb encrypts should be enough for everyone. */
|
||||
iv2 = encryptBlockSM4(iv2, roundKey);
|
||||
const block = bytesToInts(cipherText, i);
|
||||
block[0] ^= iv2[0]; block[1] ^= iv2[1];
|
||||
block[2] ^= iv2[2]; block[3] ^= iv2[3];
|
||||
Array.prototype.push.apply(clearText, intsToBytes(block));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new OperationError(`Invalid block cipher mode: ${mode}`);
|
||||
}
|
||||
/* Check PKCS#7 padding */
|
||||
if (mode === "ECB" || mode === "CBC") {
|
||||
if (ignorePadding)
|
||||
return clearText;
|
||||
const padByte = clearText[clearText.length - 1];
|
||||
if (padByte > 16)
|
||||
throw new OperationError("Invalid PKCS#7 padding.");
|
||||
for (let i = 0; i < padByte; i++)
|
||||
if (clearText[clearText.length -i - 1] !== padByte)
|
||||
throw new OperationError("Invalid PKCS#7 padding.");
|
||||
return clearText.slice(0, clearText.length - padByte);
|
||||
}
|
||||
return clearText.slice(0, originalLength);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ export default class Stream {
|
||||
|
||||
// Get the skip table.
|
||||
const skiptable = preprocess(val, length);
|
||||
let found = true;
|
||||
let found;
|
||||
|
||||
while (this.position < this.length) {
|
||||
// Until we hit the final element of val in the stream.
|
||||
@@ -303,11 +303,13 @@ export default class Stream {
|
||||
/**
|
||||
* Returns a slice of the stream up to the current position.
|
||||
*
|
||||
* @param {number} [start=0]
|
||||
* @param {number} [finish=this.position]
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
carve() {
|
||||
if (this.bitPos > 0) this.position++;
|
||||
return this.bytes.slice(0, this.position);
|
||||
carve(start=0, finish=this.position) {
|
||||
if (this.bitPos > 0) finish++;
|
||||
return this.bytes.slice(start, finish);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,38 @@ class A1Z26CipherDecode extends Operation {
|
||||
value: DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9] )+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9],)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9];)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9]:)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9]\\n)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([12]?[0-9]\\r\\n)+[12]?[0-9]\\s*$",
|
||||
flags: "",
|
||||
args: ["CRLF"]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
@@ -22,7 +22,7 @@ class AESDecrypt extends Operation {
|
||||
|
||||
this.name = "AES Decrypt";
|
||||
this.module = "Ciphers";
|
||||
this.description = "Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.<br><br><b>Key:</b> The following algorithms will be used based on the size of the key:<ul><li>16 bytes = AES-128</li><li>24 bytes = AES-192</li><li>32 bytes = AES-256</li></ul><br><br><b>IV:</b> The Initialization Vector should be 16 bytes long. If not entered, it will default to 16 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.<br><br><b>GCM Tag:</b> This field is ignored unless 'GCM' mode is used.";
|
||||
this.description = "Advanced Encryption Standard (AES) is a U.S. Federal Information Processing Standard (FIPS). It was selected after a 5-year process where 15 competing designs were evaluated.<br><br><b>Key:</b> The following algorithms will be used based on the size of the key:<ul><li>16 bytes = AES-128</li><li>24 bytes = AES-192</li><li>32 bytes = AES-256</li></ul><br><br><b>IV:</b> The Initialization Vector should be 16 bytes long. If not entered, it will default to 16 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.<br><br><b>GCM Tag:</b> This field is ignored unless 'GCM' mode is used.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Advanced_Encryption_Standard";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
@@ -41,8 +41,41 @@ class AESDecrypt extends Operation {
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
|
||||
"type": "argSelector",
|
||||
"value": [
|
||||
{
|
||||
name: "CBC",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "CFB",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "OFB",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "CTR",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "GCM",
|
||||
on: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "ECB",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "CBC/NoPadding",
|
||||
off: [5, 6]
|
||||
},
|
||||
{
|
||||
name: "ECB/NoPadding",
|
||||
off: [5, 6]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
@@ -59,6 +92,12 @@ class AESDecrypt extends Operation {
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
},
|
||||
{
|
||||
"name": "Additional Authenticated Data",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -73,10 +112,12 @@ class AESDecrypt extends Operation {
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
mode = args[2].substring(0, 3),
|
||||
noPadding = args[2].endsWith("NoPadding"),
|
||||
inputType = args[3],
|
||||
outputType = args[4],
|
||||
gcmTag = Utils.convertToByteString(args[5].string, args[5].option);
|
||||
gcmTag = Utils.convertToByteString(args[5].string, args[5].option),
|
||||
aad = Utils.convertToByteString(args[6].string, args[6].option);
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
@@ -90,9 +131,18 @@ The following algorithms will be used based on the size of the key:
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const decipher = forge.cipher.createDecipher("AES-" + mode, key);
|
||||
|
||||
/* Allow for a "no padding" mode */
|
||||
if (noPadding) {
|
||||
decipher.mode.unpad = function(output, options) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
decipher.start({
|
||||
iv: iv.length === 0 ? "" : iv,
|
||||
tag: gcmTag
|
||||
tag: mode === "GCM" ? gcmTag : undefined,
|
||||
additionalData: mode === "GCM" ? aad : undefined
|
||||
});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
const result = decipher.finish();
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
|
||||
/**
|
||||
@@ -41,8 +41,33 @@ class AESEncrypt extends Operation {
|
||||
},
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "GCM", "ECB"]
|
||||
"type": "argSelector",
|
||||
"value": [
|
||||
{
|
||||
name: "CBC",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "CFB",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "OFB",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "CTR",
|
||||
off: [5]
|
||||
},
|
||||
{
|
||||
name: "GCM",
|
||||
on: [5]
|
||||
},
|
||||
{
|
||||
name: "ECB",
|
||||
off: [5]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
@@ -53,6 +78,12 @@ class AESEncrypt extends Operation {
|
||||
"name": "Output",
|
||||
"type": "option",
|
||||
"value": ["Hex", "Raw"]
|
||||
},
|
||||
{
|
||||
"name": "Additional Authenticated Data",
|
||||
"type": "toggleString",
|
||||
"value": "",
|
||||
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -69,7 +100,8 @@ class AESEncrypt extends Operation {
|
||||
iv = Utils.convertToByteString(args[1].string, args[1].option),
|
||||
mode = args[2],
|
||||
inputType = args[3],
|
||||
outputType = args[4];
|
||||
outputType = args[4],
|
||||
aad = Utils.convertToByteString(args[5].string, args[5].option);
|
||||
|
||||
if ([16, 24, 32].indexOf(key.length) < 0) {
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
@@ -83,7 +115,10 @@ The following algorithms will be used based on the size of the key:
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const cipher = forge.cipher.createCipher("AES-" + mode, key);
|
||||
cipher.start({iv: iv});
|
||||
cipher.start({
|
||||
iv: iv,
|
||||
additionalData: mode === "GCM" ? aad : undefined
|
||||
});
|
||||
cipher.update(forge.util.createBuffer(input));
|
||||
cipher.finish();
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Add Text To Image operation
|
||||
|
||||
@@ -44,6 +44,48 @@ class BaconCipherDecode extends Operation {
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "0/1", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "0/1", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "A/B", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Standard (I=J and U=V)", "A/B", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "0/1", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([01]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "0/1", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "A/B", false]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*([AB]{5}\\s?)+$",
|
||||
flags: "",
|
||||
args: ["Complete", "A/B", true]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { Blowfish } from "../lib/Blowfish.mjs";
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { Blowfish } from "../lib/Blowfish.mjs";
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
import { gaussianBlur } from "../lib/ImageManipulation.mjs";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Blur Image operation
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/**
|
||||
* Emulation of the Bombe machine.
|
||||
*
|
||||
* Tested against the Bombe Rebuild at Bletchley Park's TNMOC
|
||||
* using a variety of inputs and settings to confirm correctness.
|
||||
*
|
||||
* @author s2224834
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
|
||||
@@ -33,9 +33,9 @@ class Bzip2Decompress extends Operation {
|
||||
value: false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
"match": "^\\x42\\x5a\\x68",
|
||||
"pattern": "^\\x42\\x5a\\x68",
|
||||
"flags": "",
|
||||
"args": []
|
||||
}
|
||||
|
||||
41
src/core/operations/CBORDecode.mjs
Normal file
41
src/core/operations/CBORDecode.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @author Danh4 [dan.h4@ncsc.gov.uk]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Cbor from "cbor";
|
||||
|
||||
/**
|
||||
* CBOR Decode operation
|
||||
*/
|
||||
class CBORDecode extends Operation {
|
||||
|
||||
/**
|
||||
* CBORDecode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "CBOR Decode";
|
||||
this.module = "Serialise";
|
||||
this.description = "Concise Binary Object Representation (CBOR) is a binary data serialization format loosely based on JSON. Like JSON it allows the transmission of data objects that contain name–value pairs, but in a more concise manner. This increases processing and transfer speeds at the cost of human readability. It is defined in IETF RFC 8949.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/CBOR";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
return Cbor.decodeFirstSync(Buffer.from(input).toString("hex"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CBORDecode;
|
||||
41
src/core/operations/CBOREncode.mjs
Normal file
41
src/core/operations/CBOREncode.mjs
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @author Danh4 [dan.h4@ncsc.gov.uk]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Cbor from "cbor";
|
||||
|
||||
/**
|
||||
* CBOR Encode operation
|
||||
*/
|
||||
class CBOREncode extends Operation {
|
||||
|
||||
/**
|
||||
* CBOREncode constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "CBOR Encode";
|
||||
this.module = "Serialise";
|
||||
this.description = "Concise Binary Object Representation (CBOR) is a binary data serialization format loosely based on JSON. Like JSON it allows the transmission of data objects that contain name–value pairs, but in a more concise manner. This increases processing and transfer speeds at the cost of human readability. It is defined in IETF RFC 8949.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/CBOR";
|
||||
this.inputType = "JSON";
|
||||
this.outputType = "ArrayBuffer";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JSON} input
|
||||
* @param {Object[]} args
|
||||
* @returns {ArrayBuffer}
|
||||
*/
|
||||
run(input, args) {
|
||||
return new Uint8Array(Cbor.encodeCanonical(input)).buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default CBOREncode;
|
||||
@@ -21,7 +21,7 @@ class CTPH extends Operation {
|
||||
this.name = "CTPH";
|
||||
this.module = "Crypto";
|
||||
this.description = "Context Triggered Piecewise Hashing, also called Fuzzy Hashing, can match inputs that have homologies. Such inputs have sequences of identical bytes in the same order, although bytes in between these sequences may be different in both content and length.<br><br>CTPH was originally based on the work of Dr. Andrew Tridgell and a spam email detector called SpamSum. This method was adapted by Jesse Kornblum and published at the DFRWS conference in 2006 in a paper 'Identifying Almost Identical Files Using Context Triggered Piecewise Hashing'.";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/**
|
||||
* Emulation of Colossus.
|
||||
*
|
||||
* Tested against the Colossus Rebuild at Bletchley Park's TNMOC
|
||||
* using a variety of inputs and settings to confirm correctness.
|
||||
*
|
||||
* @author VirtualColossus [martin@virtualcolossus.co.uk]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { ColossusComputer } from "../lib/Colossus.mjs";
|
||||
import { SWITCHES, VALID_ITA2 } from "../lib/Lorenz.mjs";
|
||||
@@ -125,7 +128,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "R1-Negate",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "R1-Counter",
|
||||
@@ -164,7 +168,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "R2-Negate",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "R2-Counter",
|
||||
@@ -203,7 +208,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "R3-Negate",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "R3-Counter",
|
||||
@@ -212,7 +218,8 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Negate All",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "K Rack: Addition",
|
||||
@@ -220,23 +227,28 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Add-Q1",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q2",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q3",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q4",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Q5",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add-Equals",
|
||||
@@ -246,11 +258,13 @@ class Colossus extends Operation {
|
||||
},
|
||||
{
|
||||
name: "Add-Counter1",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Add Negate All",
|
||||
type: "boolean"
|
||||
type: "boolean",
|
||||
value: false
|
||||
},
|
||||
{
|
||||
name: "Total Motor",
|
||||
|
||||
@@ -24,7 +24,7 @@ class CompareCTPHHashes extends Operation {
|
||||
this.name = "Compare CTPH hashes";
|
||||
this.module = "Crypto";
|
||||
this.description = "Compares two Context Triggered Piecewise Hashing (CTPH) fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/Context_Triggered_Piecewise_Hashing";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Context_Triggered_Piecewise_Hashing";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Number";
|
||||
this.args = [
|
||||
|
||||
@@ -24,7 +24,7 @@ class CompareSSDEEPHashes extends Operation {
|
||||
this.name = "Compare SSDEEP hashes";
|
||||
this.module = "Crypto";
|
||||
this.description = "Compares two SSDEEP fuzzy hashes to determine the similarity between them on a scale of 0 to 100.";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/Ssdeep";
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=Ssdeep";
|
||||
this.inputType = "string";
|
||||
this.outputType = "Number";
|
||||
this.args = [
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Contain Image operation
|
||||
|
||||
@@ -8,7 +8,8 @@ import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Convert Image Format operation
|
||||
@@ -88,7 +89,7 @@ class ConvertImageFormat extends Operation {
|
||||
"Sub": jimp.PNG_FILTER_SUB,
|
||||
"Up": jimp.PNG_FILTER_UP,
|
||||
"Average": jimp.PNG_FILTER_AVERAGE,
|
||||
"Paeth": jimp.PNG_FILTER_PATH // Incorrect spelling in Jimp library
|
||||
"Paeth": jimp.PNG_FILTER_PATH
|
||||
};
|
||||
|
||||
const mime = formatMap[format];
|
||||
|
||||
82
src/core/operations/ConvertToNATOAlphabet.mjs
Normal file
82
src/core/operations/ConvertToNATOAlphabet.mjs
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* @author MarvinJWendt [git@marvinjwendt.com]
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
|
||||
/**
|
||||
* Convert to NATO alphabet operation
|
||||
*/
|
||||
class ConvertToNATOAlphabet extends Operation {
|
||||
/**
|
||||
* ConvertToNATOAlphabet constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Convert to NATO alphabet";
|
||||
this.module = "Default";
|
||||
this.description = "Converts characters to their representation in the NATO phonetic alphabet.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/NATO_phonetic_alphabet";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {string}
|
||||
*/
|
||||
run(input, args) {
|
||||
return input.replace(/[a-z0-9,/.]/ig, letter => {
|
||||
return lookup[letter.toUpperCase()];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const lookup = {
|
||||
"A": "Alfa ",
|
||||
"B": "Bravo ",
|
||||
"C": "Charlie ",
|
||||
"D": "Delta ",
|
||||
"E": "Echo ",
|
||||
"F": "Foxtrot ",
|
||||
"G": "Golf ",
|
||||
"H": "Hotel ",
|
||||
"I": "India ",
|
||||
"J": "Juliett ",
|
||||
"K": "Kilo ",
|
||||
"L": "Lima ",
|
||||
"M": "Mike ",
|
||||
"N": "November ",
|
||||
"O": "Oscar ",
|
||||
"P": "Papa ",
|
||||
"Q": "Quebec ",
|
||||
"R": "Romeo ",
|
||||
"S": "Sierra ",
|
||||
"T": "Tango ",
|
||||
"U": "Uniform ",
|
||||
"V": "Victor ",
|
||||
"W": "Whiskey ",
|
||||
"X": "X-ray ",
|
||||
"Y": "Yankee ",
|
||||
"Z": "Zulu ",
|
||||
"0": "Zero ",
|
||||
"1": "One ",
|
||||
"2": "Two ",
|
||||
"3": "Three ",
|
||||
"4": "Four ",
|
||||
"5": "Five ",
|
||||
"6": "Six ",
|
||||
"7": "Seven ",
|
||||
"8": "Eight ",
|
||||
"9": "Nine ",
|
||||
",": "Comma ",
|
||||
"/": "Fraction bar ",
|
||||
".": "Full stop ",
|
||||
};
|
||||
|
||||
export default ConvertToNATOAlphabet;
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Cover Image operation
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Crop Image operation
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
/**
|
||||
* DES Decrypt operation
|
||||
@@ -22,7 +22,7 @@ class DESDecrypt extends Operation {
|
||||
|
||||
this.name = "DES Decrypt";
|
||||
this.module = "Ciphers";
|
||||
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used.";
|
||||
this.description = "DES is a previously dominant algorithm for encryption, and was published as an official U.S. Federal Information Processing Standard (FIPS). It is now considered to be insecure due to its small key size.<br><br><b>Key:</b> DES uses a key length of 8 bytes (64 bits).<br>Triple DES uses a key length of 24 bytes (192 bits).<br><br><b>IV:</b> The Initialization Vector should be 8 bytes long. If not entered, it will default to 8 null bytes.<br><br><b>Padding:</b> In CBC and ECB mode, PKCS#7 padding will be used as a default.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/Data_Encryption_Standard";
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
@@ -42,7 +42,7 @@ class DESDecrypt extends Operation {
|
||||
{
|
||||
"name": "Mode",
|
||||
"type": "option",
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "ECB"]
|
||||
"value": ["CBC", "CFB", "OFB", "CTR", "ECB", "CBC/NoPadding", "ECB/NoPadding"]
|
||||
},
|
||||
{
|
||||
"name": "Input",
|
||||
@@ -65,7 +65,9 @@ class DESDecrypt extends Operation {
|
||||
run(input, args) {
|
||||
const key = Utils.convertToByteString(args[0].string, args[0].option),
|
||||
iv = Utils.convertToByteArray(args[1].string, args[1].option),
|
||||
[,, mode, inputType, outputType] = args;
|
||||
mode = args[2].substring(0, 3),
|
||||
noPadding = args[2].endsWith("NoPadding"),
|
||||
[,,, inputType, outputType] = args;
|
||||
|
||||
if (key.length !== 8) {
|
||||
throw new OperationError(`Invalid key length: ${key.length} bytes
|
||||
@@ -83,6 +85,14 @@ Make sure you have specified the type correctly (e.g. Hex vs UTF8).`);
|
||||
input = Utils.convertToByteString(input, inputType);
|
||||
|
||||
const decipher = forge.cipher.createDecipher("DES-" + mode, key);
|
||||
|
||||
/* Allow for a "no padding" mode */
|
||||
if (noPadding) {
|
||||
decipher.mode.unpad = function(output, options) {
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
decipher.start({iv: iv});
|
||||
decipher.update(forge.util.createBuffer(input));
|
||||
const result = decipher.finish();
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
/**
|
||||
* DES Encrypt operation
|
||||
|
||||
@@ -24,6 +24,13 @@ class DechunkHTTPResponse extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^[0-9A-F]+\r\n",
|
||||
flags: "i",
|
||||
args: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,13 @@ class DecodeNetBIOSName extends Operation {
|
||||
"value": 65
|
||||
}
|
||||
];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*\\S{32}$",
|
||||
flags: "",
|
||||
args: [65]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,17 @@ class DefangIPAddresses extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "^\\s*(([0-9]{1,3}\\.){3}[0-9]{1,3}|([0-9a-f]{4}:){7}[0-9a-f]{4})\\s*$",
|
||||
flags: "i",
|
||||
args: [],
|
||||
output: {
|
||||
pattern: "^\\s*(([0-9]{1,3}\\[\\.\\]){3}[0-9]{1,3}|([0-9a-f]{4}\\[\\:\\]){7}[0-9a-f]{4})\\s*$",
|
||||
flags: "i"
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,7 +67,7 @@ class DeriveEVPKey extends Operation {
|
||||
iterations = args[2],
|
||||
hasher = args[3],
|
||||
salt = Utils.convertToByteString(args[4].string, args[4].option),
|
||||
key = CryptoJS.EvpKDF(passphrase, salt, {
|
||||
key = CryptoJS.EvpKDF(passphrase, salt, { // lgtm [js/insufficient-password-hash]
|
||||
keySize: keySize,
|
||||
hasher: CryptoJS.algo[hasher],
|
||||
iterations: iterations,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import forge from "node-forge/dist/forge.min.js";
|
||||
import forge from "node-forge";
|
||||
|
||||
/**
|
||||
* Derive PBKDF2 key operation
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Image Dither operation
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/**
|
||||
* Emulation of the Enigma machine.
|
||||
*
|
||||
* Tested against various genuine Enigma machines using a variety of inputs
|
||||
* and settings to confirm correctness.
|
||||
*
|
||||
* @author s2224834
|
||||
* @copyright Crown Copyright 2019
|
||||
* @license Apache-2.0
|
||||
|
||||
@@ -44,22 +44,22 @@ class EscapeUnicodeCharacters extends Operation {
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "\\\\u(?:[\\da-f]{4,6})",
|
||||
pattern: "\\\\u(?:[\\da-f]{4,6})",
|
||||
flags: "i",
|
||||
args: ["\\u"]
|
||||
},
|
||||
{
|
||||
match: "%u(?:[\\da-f]{4,6})",
|
||||
pattern: "%u(?:[\\da-f]{4,6})",
|
||||
flags: "i",
|
||||
args: ["%u"]
|
||||
},
|
||||
{
|
||||
match: "U\\+(?:[\\da-f]{4,6})",
|
||||
pattern: "U\\+(?:[\\da-f]{4,6})",
|
||||
flags: "i",
|
||||
args: ["U+"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ class ExtractDomains extends Operation {
|
||||
{
|
||||
"name": "Display total",
|
||||
"type": "boolean",
|
||||
"value": "Extract.DISPLAY_TOTAL"
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class ExtractEmailAddresses extends Operation {
|
||||
*/
|
||||
run(input, args) {
|
||||
const displayTotal = args[0],
|
||||
// email regex from: https://www.regextester.com/98066
|
||||
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFF-a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/ig;
|
||||
// email regex from: https://www.regextester.com/98066
|
||||
regex = /(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?\.)+[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9](?:[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9-]*[\u00A0-\uD7FF\uE000-\uFFFFa-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}\])/ig;
|
||||
return search(input, regex, null, displayTotal);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,10 +21,25 @@ class ExtractFiles extends Operation {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// Get the first extension for each signature that can be extracted
|
||||
let supportedExts = Object.keys(FILE_SIGNATURES).map(cat => {
|
||||
return FILE_SIGNATURES[cat]
|
||||
.filter(sig => sig.extractor)
|
||||
.map(sig => sig.extension.toUpperCase());
|
||||
});
|
||||
|
||||
// Flatten categories and remove duplicates
|
||||
supportedExts = [].concat(...supportedExts).unique();
|
||||
|
||||
this.name = "Extract Files";
|
||||
this.module = "Default";
|
||||
this.description = "Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:<ul><li>JPG</li><li>EXE</li><li>ZIP</li><li>PDF</li><li>PNG</li><li>BMP</li><li>FLV</li><li>RTF</li><li>DOCX, PPTX, XLSX</li><li>EPUB</li><li>GZIP</li><li>ZLIB</li><li>ELF, BIN, AXF, O, PRX, SO</li></ul>";
|
||||
this.infoURL = "https://forensicswiki.org/wiki/File_Carving";
|
||||
this.description = `Performs file carving to attempt to extract files from the input.<br><br>This operation is currently capable of carving out the following formats:
|
||||
<ul>
|
||||
<li>
|
||||
${supportedExts.join("</li><li>")}
|
||||
</li>
|
||||
</ul>`;
|
||||
this.infoURL = "https://forensicswiki.xyz/wiki/index.php?title=File_Carving";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "List<File>";
|
||||
this.presentType = "html";
|
||||
@@ -38,7 +53,7 @@ class ExtractFiles extends Operation {
|
||||
{
|
||||
name: "Ignore failed extractions",
|
||||
type: "boolean",
|
||||
value: "true"
|
||||
value: true
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
324
src/core/operations/ExtractID3.mjs
Normal file
324
src/core/operations/ExtractID3.mjs
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* @author n1073645 [n1073645@gmail.com]
|
||||
* @author n1474335 [n1474335@gmail.com]
|
||||
* @copyright Crown Copyright 2020
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
/**
|
||||
* Extract ID3 operation
|
||||
*/
|
||||
class ExtractID3 extends Operation {
|
||||
|
||||
/**
|
||||
* ExtractID3 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "Extract ID3";
|
||||
this.module = "Default";
|
||||
this.description = "This operation extracts ID3 metadata from an MP3 file.<br><br>ID3 is a metadata container most often used in conjunction with the MP3 audio file format. It allows information such as the title, artist, album, track number, and other information about the file to be stored in the file itself.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/ID3";
|
||||
this.inputType = "ArrayBuffer";
|
||||
this.outputType = "JSON";
|
||||
this.presentType = "html";
|
||||
this.args = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ArrayBuffer} input
|
||||
* @param {Object[]} args
|
||||
* @returns {JSON}
|
||||
*/
|
||||
run(input, args) {
|
||||
input = new Uint8Array(input);
|
||||
|
||||
/**
|
||||
* Extracts the ID3 header fields.
|
||||
*/
|
||||
function extractHeader() {
|
||||
if (!Array.from(input.slice(0, 3)).equals([0x49, 0x44, 0x33]))
|
||||
throw new OperationError("No valid ID3 header.");
|
||||
|
||||
const header = {
|
||||
"Type": "ID3",
|
||||
// Tag version
|
||||
"Version": input[3].toString() + "." + input[4].toString(),
|
||||
// Header version
|
||||
"Flags": input[5].toString()
|
||||
};
|
||||
|
||||
input = input.slice(6);
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the size fields to a single integer.
|
||||
*
|
||||
* @param {number} num
|
||||
* @returns {string}
|
||||
*/
|
||||
function readSize(num) {
|
||||
let result = 0;
|
||||
|
||||
// The sizes are 7 bit numbers stored in 8 bit locations
|
||||
for (let i = (num) * 7; i; i -= 7) {
|
||||
result = (result << i) | input[0];
|
||||
input = input.slice(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads frame header based on ID.
|
||||
*
|
||||
* @param {string} id
|
||||
* @returns {number}
|
||||
*/
|
||||
function readFrame(id) {
|
||||
const frame = {};
|
||||
|
||||
// Size of frame
|
||||
const size = readSize(4);
|
||||
frame.Size = size.toString();
|
||||
frame.Description = FRAME_DESCRIPTIONS[id];
|
||||
input = input.slice(2);
|
||||
|
||||
// Read data from frame
|
||||
let data = "";
|
||||
for (let i = 1; i < size; i++)
|
||||
data += String.fromCharCode(input[i]);
|
||||
frame.Data = data;
|
||||
|
||||
// Move to next Frame
|
||||
input = input.slice(size);
|
||||
|
||||
return [frame, size];
|
||||
}
|
||||
|
||||
const result = extractHeader();
|
||||
|
||||
const headerTagSize = readSize(4);
|
||||
result.Size = headerTagSize.toString();
|
||||
|
||||
const tags = {};
|
||||
let pos = 10;
|
||||
|
||||
// While the current element is in the header
|
||||
while (pos < headerTagSize) {
|
||||
|
||||
// Frame Identifier of frame
|
||||
let id = String.fromCharCode(input[0]) + String.fromCharCode(input[1]) + String.fromCharCode(input[2]);
|
||||
input = input.slice(3);
|
||||
|
||||
// If the next character is non-zero it is an identifier
|
||||
if (input[0] !== 0) {
|
||||
id += String.fromCharCode(input[0]);
|
||||
}
|
||||
input = input.slice(1);
|
||||
|
||||
if (id in FRAME_DESCRIPTIONS) {
|
||||
const [frame, size] = readFrame(id);
|
||||
tags[id] = frame;
|
||||
pos += 10 + size;
|
||||
} else if (id === "\x00\x00\x00") { // end of header
|
||||
break;
|
||||
} else {
|
||||
throw new OperationError("Unknown Frame Identifier: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
result.Tags = tags;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the extracted data in a more accessible format for web apps.
|
||||
* @param {JSON} data
|
||||
* @returns {html}
|
||||
*/
|
||||
present(data) {
|
||||
if (!data || !Object.prototype.hasOwnProperty.call(data, "Tags"))
|
||||
return JSON.stringify(data, null, 4);
|
||||
|
||||
let output = `<table class="table table-hover table-sm table-bordered table-nonfluid">
|
||||
<tr><th>Tag</th><th>Description</th><th>Data</th></tr>`;
|
||||
|
||||
for (const tagID in data.Tags) {
|
||||
const description = data.Tags[tagID].Description,
|
||||
contents = data.Tags[tagID].Data;
|
||||
output += `<tr><td>${tagID}</td><td>${Utils.escapeHtml(description)}</td><td>${Utils.escapeHtml(contents)}</td></tr>`;
|
||||
}
|
||||
output += "</table>";
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Borrowed from https://github.com/aadsm/jsmediatags
|
||||
const FRAME_DESCRIPTIONS = {
|
||||
// v2.2
|
||||
"BUF": "Recommended buffer size",
|
||||
"CNT": "Play counter",
|
||||
"COM": "Comments",
|
||||
"CRA": "Audio encryption",
|
||||
"CRM": "Encrypted meta frame",
|
||||
"ETC": "Event timing codes",
|
||||
"EQU": "Equalization",
|
||||
"GEO": "General encapsulated object",
|
||||
"IPL": "Involved people list",
|
||||
"LNK": "Linked information",
|
||||
"MCI": "Music CD Identifier",
|
||||
"MLL": "MPEG location lookup table",
|
||||
"PIC": "Attached picture",
|
||||
"POP": "Popularimeter",
|
||||
"REV": "Reverb",
|
||||
"RVA": "Relative volume adjustment",
|
||||
"SLT": "Synchronized lyric/text",
|
||||
"STC": "Synced tempo codes",
|
||||
"TAL": "Album/Movie/Show title",
|
||||
"TBP": "BPM (Beats Per Minute)",
|
||||
"TCM": "Composer",
|
||||
"TCO": "Content type",
|
||||
"TCR": "Copyright message",
|
||||
"TDA": "Date",
|
||||
"TDY": "Playlist delay",
|
||||
"TEN": "Encoded by",
|
||||
"TFT": "File type",
|
||||
"TIM": "Time",
|
||||
"TKE": "Initial key",
|
||||
"TLA": "Language(s)",
|
||||
"TLE": "Length",
|
||||
"TMT": "Media type",
|
||||
"TOA": "Original artist(s)/performer(s)",
|
||||
"TOF": "Original filename",
|
||||
"TOL": "Original Lyricist(s)/text writer(s)",
|
||||
"TOR": "Original release year",
|
||||
"TOT": "Original album/Movie/Show title",
|
||||
"TP1": "Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group",
|
||||
"TP2": "Band/Orchestra/Accompaniment",
|
||||
"TP3": "Conductor/Performer refinement",
|
||||
"TP4": "Interpreted, remixed, or otherwise modified by",
|
||||
"TPA": "Part of a set",
|
||||
"TPB": "Publisher",
|
||||
"TRC": "ISRC (International Standard Recording Code)",
|
||||
"TRD": "Recording dates",
|
||||
"TRK": "Track number/Position in set",
|
||||
"TSI": "Size",
|
||||
"TSS": "Software/hardware and settings used for encoding",
|
||||
"TT1": "Content group description",
|
||||
"TT2": "Title/Songname/Content description",
|
||||
"TT3": "Subtitle/Description refinement",
|
||||
"TXT": "Lyricist/text writer",
|
||||
"TXX": "User defined text information frame",
|
||||
"TYE": "Year",
|
||||
"UFI": "Unique file identifier",
|
||||
"ULT": "Unsychronized lyric/text transcription",
|
||||
"WAF": "Official audio file webpage",
|
||||
"WAR": "Official artist/performer webpage",
|
||||
"WAS": "Official audio source webpage",
|
||||
"WCM": "Commercial information",
|
||||
"WCP": "Copyright/Legal information",
|
||||
"WPB": "Publishers official webpage",
|
||||
"WXX": "User defined URL link frame",
|
||||
// v2.3
|
||||
"AENC": "Audio encryption",
|
||||
"APIC": "Attached picture",
|
||||
"ASPI": "Audio seek point index",
|
||||
"CHAP": "Chapter",
|
||||
"CTOC": "Table of contents",
|
||||
"COMM": "Comments",
|
||||
"COMR": "Commercial frame",
|
||||
"ENCR": "Encryption method registration",
|
||||
"EQU2": "Equalisation (2)",
|
||||
"EQUA": "Equalization",
|
||||
"ETCO": "Event timing codes",
|
||||
"GEOB": "General encapsulated object",
|
||||
"GRID": "Group identification registration",
|
||||
"IPLS": "Involved people list",
|
||||
"LINK": "Linked information",
|
||||
"MCDI": "Music CD identifier",
|
||||
"MLLT": "MPEG location lookup table",
|
||||
"OWNE": "Ownership frame",
|
||||
"PRIV": "Private frame",
|
||||
"PCNT": "Play counter",
|
||||
"POPM": "Popularimeter",
|
||||
"POSS": "Position synchronisation frame",
|
||||
"RBUF": "Recommended buffer size",
|
||||
"RVA2": "Relative volume adjustment (2)",
|
||||
"RVAD": "Relative volume adjustment",
|
||||
"RVRB": "Reverb",
|
||||
"SEEK": "Seek frame",
|
||||
"SYLT": "Synchronized lyric/text",
|
||||
"SYTC": "Synchronized tempo codes",
|
||||
"TALB": "Album/Movie/Show title",
|
||||
"TBPM": "BPM (beats per minute)",
|
||||
"TCOM": "Composer",
|
||||
"TCON": "Content type",
|
||||
"TCOP": "Copyright message",
|
||||
"TDAT": "Date",
|
||||
"TDLY": "Playlist delay",
|
||||
"TDRC": "Recording time",
|
||||
"TDRL": "Release time",
|
||||
"TDTG": "Tagging time",
|
||||
"TENC": "Encoded by",
|
||||
"TEXT": "Lyricist/Text writer",
|
||||
"TFLT": "File type",
|
||||
"TIME": "Time",
|
||||
"TIPL": "Involved people list",
|
||||
"TIT1": "Content group description",
|
||||
"TIT2": "Title/songname/content description",
|
||||
"TIT3": "Subtitle/Description refinement",
|
||||
"TKEY": "Initial key",
|
||||
"TLAN": "Language(s)",
|
||||
"TLEN": "Length",
|
||||
"TMCL": "Musician credits list",
|
||||
"TMED": "Media type",
|
||||
"TMOO": "Mood",
|
||||
"TOAL": "Original album/movie/show title",
|
||||
"TOFN": "Original filename",
|
||||
"TOLY": "Original lyricist(s)/text writer(s)",
|
||||
"TOPE": "Original artist(s)/performer(s)",
|
||||
"TORY": "Original release year",
|
||||
"TOWN": "File owner/licensee",
|
||||
"TPE1": "Lead performer(s)/Soloist(s)",
|
||||
"TPE2": "Band/orchestra/accompaniment",
|
||||
"TPE3": "Conductor/performer refinement",
|
||||
"TPE4": "Interpreted, remixed, or otherwise modified by",
|
||||
"TPOS": "Part of a set",
|
||||
"TPRO": "Produced notice",
|
||||
"TPUB": "Publisher",
|
||||
"TRCK": "Track number/Position in set",
|
||||
"TRDA": "Recording dates",
|
||||
"TRSN": "Internet radio station name",
|
||||
"TRSO": "Internet radio station owner",
|
||||
"TSOA": "Album sort order",
|
||||
"TSOP": "Performer sort order",
|
||||
"TSOT": "Title sort order",
|
||||
"TSIZ": "Size",
|
||||
"TSRC": "ISRC (international standard recording code)",
|
||||
"TSSE": "Software/Hardware and settings used for encoding",
|
||||
"TSST": "Set subtitle",
|
||||
"TYER": "Year",
|
||||
"TXXX": "User defined text information frame",
|
||||
"UFID": "Unique file identifier",
|
||||
"USER": "Terms of use",
|
||||
"USLT": "Unsychronized lyric/text transcription",
|
||||
"WCOM": "Commercial information",
|
||||
"WCOP": "Copyright/Legal information",
|
||||
"WOAF": "Official audio file webpage",
|
||||
"WOAR": "Official artist/performer webpage",
|
||||
"WOAS": "Official audio source webpage",
|
||||
"WORS": "Official internet radio station homepage",
|
||||
"WPAY": "Payment",
|
||||
"WPUB": "Publishers official webpage",
|
||||
"WXXX": "User defined URL link frame"
|
||||
};
|
||||
|
||||
export default ExtractID3;
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
import { fromBinary } from "../lib/Binary.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Extract LSB operation
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
import {RGBA_DELIM_OPTIONS} from "../lib/Delim.mjs";
|
||||
|
||||
|
||||
@@ -9,7 +9,8 @@ import OperationError from "../errors/OperationError.mjs";
|
||||
import { isImage } from "../lib/FileType.mjs";
|
||||
import { toBase64 } from "../lib/Base64.mjs";
|
||||
import { isWorkerEnvironment } from "../Utils.mjs";
|
||||
import jimp from "jimp";
|
||||
import jimplib from "jimp/es/index.js";
|
||||
const jimp = jimplib.default ? jimplib.default : jimplib;
|
||||
|
||||
/**
|
||||
* Flip Image operation
|
||||
|
||||
@@ -30,7 +30,12 @@ class FrequencyDistribution extends Operation {
|
||||
{
|
||||
"name": "Show 0%s",
|
||||
"type": "boolean",
|
||||
"value": "Entropy.FREQ_ZEROS"
|
||||
"value": true
|
||||
},
|
||||
{
|
||||
"name": "Show ASCII",
|
||||
"type": "boolean",
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -76,14 +81,14 @@ class FrequencyDistribution extends Operation {
|
||||
* @returns {html}
|
||||
*/
|
||||
present(freq, args) {
|
||||
const showZeroes = args[0];
|
||||
const [showZeroes, showAscii] = args;
|
||||
|
||||
// Print
|
||||
let output = `<canvas id='chart-area'></canvas><br>
|
||||
Total data length: ${freq.dataLength}
|
||||
Number of bytes represented: ${freq.bytesRepresented}
|
||||
Number of bytes not represented: ${256 - freq.bytesRepresented}
|
||||
|
||||
Byte Percentage
|
||||
<script>
|
||||
var canvas = document.getElementById("chart-area"),
|
||||
parentRect = canvas.parentNode.getBoundingClientRect(),
|
||||
@@ -93,16 +98,32 @@ Byte Percentage
|
||||
canvas.height = parentRect.height * 0.9;
|
||||
|
||||
CanvasComponents.drawBarChart(canvas, scores, "Byte", "Frequency %", 16, 6);
|
||||
</script>`;
|
||||
</script>
|
||||
<table class="table table-hover table-sm">
|
||||
<tr><th>Byte</th>${showAscii ? "<th>ASCII</th>" : ""}<th>Percentage</th><th></th></tr>`;
|
||||
|
||||
for (let i = 0; i < 256; i++) {
|
||||
if (freq.distribution[i] || showZeroes) {
|
||||
output += " " + Utils.hex(i, 2) + " (" +
|
||||
(freq.percentages[i].toFixed(2).replace(".00", "") + "%)").padEnd(8, " ") +
|
||||
Array(Math.ceil(freq.percentages[i])+1).join("|") + "\n";
|
||||
let c = "";
|
||||
if (showAscii) {
|
||||
if (i <= 32) {
|
||||
c = String.fromCharCode(0x2400 + i);
|
||||
} else if (i === 127) {
|
||||
c = String.fromCharCode(0x2421);
|
||||
} else {
|
||||
c = String.fromCharCode(i);
|
||||
}
|
||||
}
|
||||
const bite = `<td>${Utils.hex(i, 2)}</td>`,
|
||||
ascii = showAscii ? `<td>${c}</td>` : "",
|
||||
percentage = `<td>${(freq.percentages[i].toFixed(2).replace(".00", "") + "%").padEnd(8, " ")}</td>`,
|
||||
bars = `<td>${Array(Math.ceil(freq.percentages[i])+1).join("|")}</td>`;
|
||||
|
||||
output += `<tr>${bite}${ascii}${percentage}${bars}</tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
output += "</table>";
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ class FromBCD extends Operation {
|
||||
"value": FORMAT
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:\\d{4} ){3,}\\d{4}$",
|
||||
pattern: "^(?:\\d{4} ){3,}\\d{4}$",
|
||||
flags: "",
|
||||
args: ["8 4 2 1", true, false, "Nibbles"]
|
||||
},
|
||||
@@ -95,7 +95,7 @@ class FromBCD extends Operation {
|
||||
if (!packed) {
|
||||
// Discard each high nibble
|
||||
for (let i = 0; i < nibbles.length; i++) {
|
||||
nibbles.splice(i, 1);
|
||||
nibbles.splice(i, 1); // lgtm [js/loop-iteration-skipped-due-to-shifting]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class FromBase extends Operation {
|
||||
}
|
||||
|
||||
const number = input.replace(/\s/g, "").split(".");
|
||||
let result = new BigNumber(number[0], radix) || 0;
|
||||
let result = new BigNumber(number[0], radix);
|
||||
|
||||
if (number.length === 1) return result;
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ class FromBase32 extends Operation {
|
||||
value: true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
|
||||
pattern: "^(?:[A-Z2-7]{8})+(?:[A-Z2-7]{2}={6}|[A-Z2-7]{4}={4}|[A-Z2-7]{5}={3}|[A-Z2-7]{7}={1})?$",
|
||||
flags: "",
|
||||
args: ["A-Z2-7=", false]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -84,10 +84,10 @@ class FromBase32 extends Operation {
|
||||
chr5 = ((enc7 & 7) << 5) | enc8;
|
||||
|
||||
output.push(chr1);
|
||||
if (enc2 & 3 !== 0 || enc3 !== 32) output.push(chr2);
|
||||
if (enc4 & 15 !== 0 || enc5 !== 32) output.push(chr3);
|
||||
if (enc5 & 1 !== 0 || enc6 !== 32) output.push(chr4);
|
||||
if (enc7 & 7 !== 0 || enc8 !== 32) output.push(chr5);
|
||||
if ((enc2 & 3) !== 0 || enc3 !== 32) output.push(chr2);
|
||||
if ((enc4 & 15) !== 0 || enc5 !== 32) output.push(chr3);
|
||||
if ((enc5 & 1) !== 0 || enc6 !== 32) output.push(chr4);
|
||||
if ((enc7 & 7) !== 0 || enc8 !== 32) output.push(chr5);
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
89
src/core/operations/FromBase45.mjs
Normal file
89
src/core/operations/FromBase45.mjs
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @author Thomas Weißschuh [thomas@t-8ch.de]
|
||||
* @copyright Crown Copyright 2021
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
import {ALPHABET, highlightToBase45, highlightFromBase45} from "../lib/Base45.mjs";
|
||||
import Operation from "../Operation.mjs";
|
||||
import OperationError from "../errors/OperationError.mjs";
|
||||
import Utils from "../Utils.mjs";
|
||||
|
||||
|
||||
/**
|
||||
* From Base45 operation
|
||||
*/
|
||||
class FromBase45 extends Operation {
|
||||
|
||||
/**
|
||||
* FromBase45 constructor
|
||||
*/
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.name = "From Base45";
|
||||
this.module = "Default";
|
||||
this.description = "Base45 is a notation for encoding arbitrary byte data using a restricted set of symbols that can be conveniently used by humans and processed by computers. The high number base results in shorter strings than with the decimal or hexadecimal system. Base45 is optimized for usage with QR codes.";
|
||||
this.infoURL = "https://wikipedia.org/wiki/List_of_numeral_systems";
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [
|
||||
{
|
||||
name: "Alphabet",
|
||||
type: "string",
|
||||
value: ALPHABET
|
||||
}
|
||||
];
|
||||
|
||||
this.highlight = highlightFromBase45;
|
||||
this.highlightReverse = highlightToBase45;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @param {Object[]} args
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
if (!input) return [];
|
||||
const alphabet = Utils.expandAlphRange(args[0]);
|
||||
|
||||
const res = [];
|
||||
|
||||
for (const triple of Utils.chunked(input, 3)) {
|
||||
triple.reverse();
|
||||
let b = 0;
|
||||
for (const c of triple) {
|
||||
const idx = alphabet.indexOf(c);
|
||||
if (idx === -1) {
|
||||
throw new OperationError(`Character not in alphabet: '${c}'`);
|
||||
}
|
||||
b *= 45;
|
||||
b += idx;
|
||||
}
|
||||
|
||||
if (b > 65535) {
|
||||
throw new OperationError(`Triplet too large: '${triple.join("")}'`);
|
||||
}
|
||||
|
||||
if (triple.length > 2) {
|
||||
/**
|
||||
* The last triple may only have 2 bytes so we push the MSB when we got 3 bytes
|
||||
* Pushing MSB
|
||||
*/
|
||||
res.push(b >> 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushing LSB
|
||||
*/
|
||||
res.push(b & 0xff);
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default FromBase45;
|
||||
@@ -38,14 +38,14 @@ class FromBase58 extends Operation {
|
||||
"value": true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
pattern: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
flags: "",
|
||||
args: ["123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", false]
|
||||
},
|
||||
{
|
||||
match: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
pattern: "^[1-9A-HJ-NP-Za-km-z]{20,}$",
|
||||
flags: "",
|
||||
args: ["rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz", false]
|
||||
},
|
||||
|
||||
@@ -36,72 +36,92 @@ class FromBase64 extends Operation {
|
||||
value: true
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4})+(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d\\-_]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9-_", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+\\-]{4}){5,}(?:[A-Z\\d+\\-]{2}==|[A-Z\\d+\\-]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9+\\-=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d./]{4}){5,}(?:[A-Z\\d./]{2}==|[A-Z\\d./]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d_.]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d_.]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9_.", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d._]{4}){5,}(?:[A-Z\\d._]{2}--|[A-Z\\d._]{3}-)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["A-Za-z0-9._-", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9a-zA-Z+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[A-Z\\d+/]{4}){5,}(?:[A-Z\\d+/]{2}==|[A-Z\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["0-9A-Za-z+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
|
||||
pattern: "^[ !\"#$%&'()*+,\\-./\\d:;<=>?@A-Z[\\\\\\]^_]{20,}$",
|
||||
flags: "",
|
||||
args: [" -_", false]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d+\\-]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["+\\-0-9A-Za-z", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
|
||||
pattern: "^\\s*[!\"#$%&'()*+,\\-0-689@A-NP-VX-Z[`a-fh-mp-r]{20,}\\s*$",
|
||||
flags: "",
|
||||
args: ["!-,-0-689@A-NP-VX-Z[`a-fh-mp-r", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
|
||||
pattern: "^\\s*(?:[N-ZA-M\\d+/]{4}){5,}(?:[N-ZA-M\\d+/]{2}==|[N-ZA-M\\d+/]{3}=)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["N-ZA-Mn-za-m0-9+/=", true]
|
||||
},
|
||||
{
|
||||
match: "^\\s*[A-Z\\d./]{20,}\\s*$",
|
||||
pattern: "^\\s*[A-Z\\d./]{20,}\\s*$",
|
||||
flags: "i",
|
||||
args: ["./0-9A-Za-z", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}CC|[A-Z=\\d\\+/]{3}C)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["/128GhIoPQROSTeUbADfgHijKLM+n0pFWXY456xyzB7=39VaqrstJklmNuZvwcdEC", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}22|[A-Z=\\d\\+/]{3}2)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["ZKj9n+yf0wDVX1s/5YbdxSo=ILaUpPBCHg8uvNO4klm6iJGhQ7eFrWczAMEq3RTt2", true]
|
||||
},
|
||||
{
|
||||
pattern: "^\\s*(?:[A-Z=\\d\\+/]{4}){5,}(?:[A-Z=\\d\\+/]{2}55|[A-Z=\\d\\+/]{3}5)?\\s*$",
|
||||
flags: "i",
|
||||
args: ["HNO4klm6ij9n+J2hyf0gzA8uvwDEq3X1Q7ZKeFrWcVTts/MRGYbdxSo=ILaUpPBC5", true]
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -31,41 +31,47 @@ class FromBinary extends Operation {
|
||||
"name": "Delimiter",
|
||||
"type": "option",
|
||||
"value": BIN_DELIM_OPTIONS
|
||||
},
|
||||
{
|
||||
"name": "Byte Length",
|
||||
"type": "number",
|
||||
"value": 8,
|
||||
"min": 1
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[01]{8})+$",
|
||||
pattern: "^(?:[01]{8})+$",
|
||||
flags: "",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?: [01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?: [01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:,[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:,[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:;[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:;[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?::[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?::[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:\\n[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:\\n[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[01]{8})(?:\\r\\n[01]{8})*$",
|
||||
pattern: "^(?:[01]{8})(?:\\r\\n[01]{8})*$",
|
||||
flags: "",
|
||||
args: ["CRLF"]
|
||||
},
|
||||
@@ -78,7 +84,8 @@ class FromBinary extends Operation {
|
||||
* @returns {byteArray}
|
||||
*/
|
||||
run(input, args) {
|
||||
return fromBinary(input, args[0]);
|
||||
const byteLen = args[1] ? args[1] : 8;
|
||||
return fromBinary(input, args[0], byteLen);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,37 +36,37 @@ class FromDecimal extends Operation {
|
||||
"value": false
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?: (?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?: (?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Space", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:,(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:,(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Comma", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:;(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:;(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?::(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?::(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Colon", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["Line feed", false]
|
||||
},
|
||||
{
|
||||
match: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\r\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
pattern: "^(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5])(?:\\r\\n(?:\\d{1,2}|1\\d{2}|2[0-4]\\d|25[0-5]))*$",
|
||||
flags: "",
|
||||
args: ["CRLF", false]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,12 @@ class FromHTMLEntity extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "string";
|
||||
this.args = [];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "&(?:#\\d{2,3}|#x[\\da-f]{2}|[a-z]{2,6});",
|
||||
pattern: "&(?:#\\d{2,3}|#x[\\da-f]{2}|[a-z]{2,6});",
|
||||
flags: "i",
|
||||
args: []
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -32,49 +32,54 @@ class FromHex extends Operation {
|
||||
value: FROM_HEX_DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[\\dA-F]{2})+$",
|
||||
pattern: "^(?:[\\dA-F]{2})+$",
|
||||
flags: "i",
|
||||
args: ["None"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?: [\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?: [\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:,[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:,[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:;[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:;[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?::[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?::[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:\\n[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:\\n[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:\\r\\n[\\dA-F]{2})*$",
|
||||
pattern: "^[\\dA-F]{2}(?:\\r\\n[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["CRLF"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:0x[\\dA-F]{2})*$",
|
||||
pattern: "^(?:0x[\\dA-F]{2})+$",
|
||||
flags: "i",
|
||||
args: ["0x"]
|
||||
},
|
||||
{
|
||||
match: "^[\\dA-F]{2}(?:\\\\x[\\dA-F]{2})*$",
|
||||
pattern: "^0x[\\dA-F]{2}(?:,0x[\\dA-F]{2})*$",
|
||||
flags: "i",
|
||||
args: ["0x with comma"]
|
||||
},
|
||||
{
|
||||
pattern: "^(?:\\\\x[\\dA-F]{2})+$",
|
||||
flags: "i",
|
||||
args: ["\\x"]
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@ class FromHexContent extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
this.checks = [
|
||||
{
|
||||
pattern: "\\|([\\da-f]{2} ?)+\\|",
|
||||
flags: "i",
|
||||
args: []
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,9 +27,9 @@ class FromHexdump extends Operation {
|
||||
this.inputType = "string";
|
||||
this.outputType = "byteArray";
|
||||
this.args = [];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:(?:[\\dA-F]{4,16}h?:?)?[ \\t]*((?:[\\dA-F]{2} ){1,8}(?:[ \\t]|[\\dA-F]{2}-)(?:[\\dA-F]{2} ){1,8}|(?:[\\dA-F]{4} )*[\\dA-F]{4}|(?:[\\dA-F]{2} )*[\\dA-F]{2})[^\\n]*\\n?){2,}$",
|
||||
pattern: "^(?:(?:[\\dA-F]{4,16}h?:?)?[ \\t]*((?:[\\dA-F]{2} ){1,8}(?:[ \\t]|[\\dA-F]{2}-)(?:[\\dA-F]{2} ){1,8}|(?:[\\dA-F]{4} )*[\\dA-F]{4}|(?:[\\dA-F]{2} )*[\\dA-F]{2})[^\\n]*\\n?){2,}$",
|
||||
flags: "i",
|
||||
args: []
|
||||
},
|
||||
|
||||
@@ -37,12 +37,12 @@ class FromMorseCode extends Operation {
|
||||
"value": WORD_DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "(?:^[-. \\n]{5,}$|^[_. \\n]{5,}$|^(?:dash|dot| |\\n){5,}$)",
|
||||
pattern: "(?:^[-. \\n]{5,}$|^[_. \\n]{5,}$|^(?:dash|dot| |\\n){5,}$)",
|
||||
flags: "i",
|
||||
args: ["Space", "Line feed"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -32,37 +32,37 @@ class FromOctal extends Operation {
|
||||
"value": DELIM_OPTIONS
|
||||
}
|
||||
];
|
||||
this.patterns = [
|
||||
this.checks = [
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?: (?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?: (?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Space"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:,(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:,(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Comma"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:;(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:;(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Semi-colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?::(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?::(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Colon"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["Line feed"]
|
||||
},
|
||||
{
|
||||
match: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\r\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
pattern: "^(?:[0-7]{1,2}|[123][0-7]{2})(?:\\r\\n(?:[0-7]{1,2}|[123][0-7]{2}))*$",
|
||||
flags: "",
|
||||
args: ["CRLF"]
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user